404 lines · 12.9 KB · Rust
RawDownload
use std::ops::Range; use gpui::Entity;use language::Buffer;use multi_buffer::{Anchor, MultiBufferOffset, MultiBufferSnapshot, ToOffset as _};use project::{Project, bookmark_store::BookmarkStore};use rope::Point;use text::Bias;use ui::{Context, Window};use util::ResultExt as _;use workspace::{Workspace, searchable::Direction}; use crate::display_map::DisplayRow;use crate::{    EditBookmark, Editor, GoToNextBookmark, GoToPreviousBookmark, MultibufferSelectionMode,    SelectionEffects, ToggleBookmark, ToggleBookmarkWithLabel, ViewBookmarks, scroll::Autoscroll,}; #[derive(Clone, Debug)]struct BookmarkTarget {    buffer: Entity<Buffer>,    anchor: Anchor,    buffer_anchor: text::Anchor,} impl Editor {    fn bookmark_exists_for_target(        bookmark_store: &Entity<BookmarkStore>,        target: &BookmarkTarget,        cx: &mut Context<Self>,    ) -> bool {        bookmark_store.update(cx, |bookmark_store, cx| {            bookmark_store                .find_bookmark(&target.buffer, target.buffer_anchor, cx)                .is_some()        })    }     pub fn set_show_bookmarks(&mut self, show_bookmarks: bool, cx: &mut Context<Self>) {        self.show_bookmarks = Some(show_bookmarks);        cx.notify();    }     pub fn toggle_bookmark(        &mut self,        _: &ToggleBookmark,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.toggle_bookmark_impl(false, window, cx);    }     pub fn toggle_bookmark_with_label(        &mut self,        _: &ToggleBookmarkWithLabel,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.toggle_bookmark_impl(true, window, cx);    }     fn toggle_bookmark_impl(        &mut self,        with_label: bool,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(bookmark_store) = self.bookmark_store.clone() else {            return;        };        let Some(project) = self.project() else {            return;        };         let snapshot = self.snapshot(window, cx);        let multi_buffer_snapshot = snapshot.buffer_snapshot();         let mut selections = self.selections.all::<Point>(&snapshot.display_snapshot);        selections.sort_by_key(|s| s.head());        selections.dedup_by_key(|s| s.head().row);         let mut exist_targets: Vec<BookmarkTarget> = vec![];        let mut absent_targets: Vec<BookmarkTarget> = vec![];         for selection in &selections {            let head = selection.head();            let multibuffer_anchor = multi_buffer_snapshot.anchor_before(Point::new(head.row, 0));             if let Some((buffer_anchor, _)) =                multi_buffer_snapshot.anchor_to_buffer_anchor(multibuffer_anchor)            {                let buffer_id = buffer_anchor.buffer_id;                if let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) {                    let target = BookmarkTarget {                        buffer,                        anchor: multibuffer_anchor,                        buffer_anchor,                    };                     if Self::bookmark_exists_for_target(&bookmark_store, &target, cx) {                        exist_targets.push(target);                    } else {                        absent_targets.push(target);                    }                }            }        }         if absent_targets.is_empty() {            // All cursors are on existing bookmarks, remove all bookmarks.            self.toggle_bookmarks(exist_targets, String::new(), cx);        } else if with_label {            // Only add new ones (prompting for a label) and leave existing ones unchanged.            self.add_toggle_bookmark_blocks(absent_targets, bookmark_store, window, cx);        } else {            // Only add new (unnamed) bookmarks and leave existing ones unchanged.            self.toggle_bookmarks(absent_targets, String::new(), cx);        }         cx.notify();    }     pub fn toggle_bookmark_at_row(&mut self, row: DisplayRow, cx: &mut Context<Self>) {        let display_snapshot = self.display_snapshot(cx);        let point = display_snapshot.display_point_to_point(row.as_display_point(), Bias::Left);        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let anchor = buffer_snapshot.anchor_before(point);         self.toggle_bookmark_at_anchor(anchor, cx);    }     pub fn toggle_bookmark_at_anchor(&mut self, anchor: Anchor, cx: &mut Context<Self>) {        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let Some((position, _)) = buffer_snapshot.anchor_to_buffer_anchor(anchor) else {            return;        };        let Some(buffer) = self.buffer.read(cx).buffer(position.buffer_id) else {            return;        };         let Some(bookmark_store) = self.bookmark_store.clone() else {            return;        };         bookmark_store.update(cx, |bookmark_store, cx| {            bookmark_store.toggle_bookmark(buffer, position, String::new(), cx);        });         cx.notify();    }     pub fn edit_bookmark(&mut self, _: &EditBookmark, window: &mut Window, cx: &mut Context<Self>) {        let snapshot = self.snapshot(window, cx);        let multi_buffer_snapshot = snapshot.buffer_snapshot();        let selection = self            .selections            .newest::<Point>(&snapshot.display_snapshot)            .head();        let anchor = multi_buffer_snapshot.anchor_before(Point::new(selection.row, 0));        self.edit_bookmark_at_anchor(anchor, window, cx);    }     pub fn edit_bookmark_at_anchor(        &mut self,        anchor: Anchor,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(bookmark_store) = self.bookmark_store.clone() else {            return;        };        let Some(project) = self.project() else {            return;        };         let editor_buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let Some((buffer_anchor, _)) = editor_buffer_snapshot.anchor_to_buffer_anchor(anchor)        else {            return;        };        let Some(buffer) = project.read(cx).buffer_for_id(buffer_anchor.buffer_id, cx) else {            return;        };        let Some(label) = bookmark_store.update(cx, |store, cx| {            store                .find_bookmark(&buffer, buffer_anchor, cx)                .map(|bookmark| bookmark.label.clone())        }) else {            return;        };         self.add_edit_bookmark_block(            BookmarkTarget {                anchor,                buffer,                buffer_anchor,            },            &label,            bookmark_store,            window,            cx,        );    }     fn add_edit_bookmark_block(        &mut self,        target: BookmarkTarget,        label: &str,        bookmark_store: Entity<BookmarkStore>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.add_edit_block(            target.anchor,            label,            "Enter bookmark label (Optional)",            Some(Box::new(move |label, _, cx| {                bookmark_store.update(cx, |store, cx| {                    store.edit_bookmark(&target.buffer, target.buffer_anchor, label, cx)                });            })),            None,            window,            cx,        );    }     fn add_toggle_bookmark_blocks(        &mut self,        targets: Vec<BookmarkTarget>,        bookmark_store: Entity<BookmarkStore>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        for target in targets {            let bookmark_store = bookmark_store.clone();            self.add_edit_block(                target.anchor,                "",                "Enter bookmark label (Optional)",                Some(Box::new(move |label: String, _, cx| {                    bookmark_store.update(cx, |store, cx| {                        store.toggle_bookmark(target.buffer, target.buffer_anchor, label, cx);                    });                })),                None,                window,                cx,            );        }    }     fn toggle_bookmarks(        &mut self,        targets: Vec<BookmarkTarget>,        label: String,        cx: &mut Context<Self>,    ) {        if let Some(bookmark_store) = self.bookmark_store.clone() {            bookmark_store.update(cx, |store, cx| {                for target in targets {                    store.toggle_bookmark(target.buffer, target.buffer_anchor, label.clone(), cx);                }            });        }    }     pub fn go_to_next_bookmark(        &mut self,        _: &GoToNextBookmark,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.go_to_bookmark_impl(Direction::Next, window, cx);    }     pub fn go_to_previous_bookmark(        &mut self,        _: &GoToPreviousBookmark,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.go_to_bookmark_impl(Direction::Prev, window, cx);    }     fn go_to_bookmark_impl(        &mut self,        direction: Direction,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(project) = &self.project else {            return;        };        let Some(bookmark_store) = &self.bookmark_store else {            return;        };         let selection = self            .selections            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));        let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);         let mut all_bookmarks = Self::bookmarks_in_range(            MultiBufferOffset(0)..multi_buffer_snapshot.len(),            &multi_buffer_snapshot,            project,            bookmark_store,            cx,        );        all_bookmarks.sort_by_key(|a| a.to_offset(&multi_buffer_snapshot));         let anchor = match direction {            Direction::Next => all_bookmarks                .iter()                .find(|anchor| anchor.to_offset(&multi_buffer_snapshot) > selection.head())                .or_else(|| all_bookmarks.first()),            Direction::Prev => all_bookmarks                .iter()                .rfind(|anchor| anchor.to_offset(&multi_buffer_snapshot) < selection.head())                .or_else(|| all_bookmarks.last()),        }        .cloned();         if let Some(anchor) = anchor {            self.unfold_ranges(&[anchor..anchor], true, false, cx);            self.change_selections(                SelectionEffects::scroll(Autoscroll::center()),                window,                cx,                |s| {                    s.select_anchor_ranges([anchor..anchor]);                },            );        }    }     pub fn view_bookmarks(        workspace: &mut Workspace,        _: &ViewBookmarks,        window: &mut Window,        cx: &mut Context<Workspace>,    ) {        let bookmark_store = workspace.project().read(cx).bookmark_store();        cx.spawn_in(window, async move |workspace, cx| {            let Some(locations) = BookmarkStore::all_bookmark_locations(bookmark_store, cx)                .await                .log_err()            else {                return;            };             workspace                .update_in(cx, |workspace, window, cx| {                    Editor::open_locations_in_multibuffer(                        workspace,                        locations,                        "Bookmarks".into(),                        false,                        false,                        MultibufferSelectionMode::First,                        window,                        cx,                    );                })                .log_err();        })        .detach();    }     fn bookmarks_in_range(        range: Range<MultiBufferOffset>,        multi_buffer_snapshot: &MultiBufferSnapshot,        project: &Entity<Project>,        bookmark_store: &Entity<BookmarkStore>,        cx: &mut Context<Self>,    ) -> Vec<Anchor> {        multi_buffer_snapshot            .range_to_buffer_ranges(range)            .into_iter()            .flat_map(|(buffer_snapshot, buffer_range, _excerpt_range)| {                let Some(buffer) = project                    .read(cx)                    .buffer_for_id(buffer_snapshot.remote_id(), cx)                else {                    return Vec::new();                };                bookmark_store                    .update(cx, |store, cx| {                        store.bookmarks_for_buffer(                            buffer,                            buffer_snapshot.anchor_before(buffer_range.start)                                ..buffer_snapshot.anchor_after(buffer_range.end),                            &buffer_snapshot,                            cx,                        )                    })                    .into_iter()                    .filter_map(|bookmark| multi_buffer_snapshot.anchor_in_buffer(bookmark.anchor))                    .collect::<Vec<_>>()            })            .collect()    }}