bookmarks.rs

  1use std::ops::Range;
  2
  3use gpui::Entity;
  4use multi_buffer::{Anchor, MultiBufferOffset, MultiBufferSnapshot, ToOffset as _};
  5use project::{Project, bookmark_store::BookmarkStore};
  6use rope::Point;
  7use text::Bias;
  8use ui::{Context, Window};
  9use util::ResultExt as _;
 10use workspace::{Workspace, searchable::Direction};
 11
 12use crate::display_map::DisplayRow;
 13use crate::{
 14    Editor, GoToNextBookmark, GoToPreviousBookmark, MultibufferSelectionMode, SelectionEffects,
 15    ToggleBookmark, ViewBookmarks, scroll::Autoscroll,
 16};
 17
 18impl Editor {
 19    pub fn set_show_bookmarks(&mut self, show_bookmarks: bool, cx: &mut Context<Self>) {
 20        self.show_bookmarks = Some(show_bookmarks);
 21        cx.notify();
 22    }
 23
 24    pub fn toggle_bookmark(
 25        &mut self,
 26        _: &ToggleBookmark,
 27        window: &mut Window,
 28        cx: &mut Context<Self>,
 29    ) {
 30        let Some(bookmark_store) = self.bookmark_store.clone() else {
 31            return;
 32        };
 33        let Some(project) = self.project() else {
 34            return;
 35        };
 36
 37        let snapshot = self.snapshot(window, cx);
 38        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 39
 40        let mut selections = self.selections.all::<Point>(&snapshot.display_snapshot);
 41        selections.sort_by_key(|s| s.head());
 42        selections.dedup_by_key(|s| s.head().row);
 43
 44        for selection in &selections {
 45            let head = selection.head();
 46            let multibuffer_anchor = multi_buffer_snapshot.anchor_before(Point::new(head.row, 0));
 47
 48            if let Some((buffer_anchor, _)) =
 49                multi_buffer_snapshot.anchor_to_buffer_anchor(multibuffer_anchor)
 50            {
 51                let buffer_id = buffer_anchor.buffer_id;
 52                if let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) {
 53                    bookmark_store.update(cx, |store, cx| {
 54                        store.toggle_bookmark(buffer, buffer_anchor, cx);
 55                    });
 56                }
 57            }
 58        }
 59
 60        cx.notify();
 61    }
 62
 63    pub fn toggle_bookmark_at_row(&mut self, row: DisplayRow, cx: &mut Context<Self>) {
 64        let Some(bookmark_store) = &self.bookmark_store else {
 65            return;
 66        };
 67        let display_snapshot = self.display_snapshot(cx);
 68        let point = display_snapshot.display_point_to_point(row.as_display_point(), Bias::Left);
 69        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 70        let anchor = buffer_snapshot.anchor_before(point);
 71
 72        let Some((position, _)) = buffer_snapshot.anchor_to_buffer_anchor(anchor) else {
 73            return;
 74        };
 75        let Some(buffer) = self.buffer.read(cx).buffer(position.buffer_id) else {
 76            return;
 77        };
 78
 79        bookmark_store.update(cx, |bookmark_store, cx| {
 80            bookmark_store.toggle_bookmark(buffer, position, cx);
 81        });
 82
 83        cx.notify();
 84    }
 85
 86    pub fn toggle_bookmark_at_anchor(&mut self, anchor: Anchor, cx: &mut Context<Self>) {
 87        let Some(bookmark_store) = &self.bookmark_store else {
 88            return;
 89        };
 90        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 91        let Some((position, _)) = buffer_snapshot.anchor_to_buffer_anchor(anchor) else {
 92            return;
 93        };
 94        let Some(buffer) = self.buffer.read(cx).buffer(position.buffer_id) else {
 95            return;
 96        };
 97
 98        bookmark_store.update(cx, |bookmark_store, cx| {
 99            bookmark_store.toggle_bookmark(buffer, position, cx);
100        });
101
102        cx.notify();
103    }
104
105    pub fn go_to_next_bookmark(
106        &mut self,
107        _: &GoToNextBookmark,
108        window: &mut Window,
109        cx: &mut Context<Self>,
110    ) {
111        self.go_to_bookmark_impl(Direction::Next, window, cx);
112    }
113
114    pub fn go_to_previous_bookmark(
115        &mut self,
116        _: &GoToPreviousBookmark,
117        window: &mut Window,
118        cx: &mut Context<Self>,
119    ) {
120        self.go_to_bookmark_impl(Direction::Prev, window, cx);
121    }
122
123    fn go_to_bookmark_impl(
124        &mut self,
125        direction: Direction,
126        window: &mut Window,
127        cx: &mut Context<Self>,
128    ) {
129        let Some(project) = &self.project else {
130            return;
131        };
132        let Some(bookmark_store) = &self.bookmark_store else {
133            return;
134        };
135
136        let selection = self
137            .selections
138            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
139        let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
140
141        let mut all_bookmarks = Self::bookmarks_in_range(
142            MultiBufferOffset(0)..multi_buffer_snapshot.len(),
143            &multi_buffer_snapshot,
144            project,
145            bookmark_store,
146            cx,
147        );
148        all_bookmarks.sort_by_key(|a| a.to_offset(&multi_buffer_snapshot));
149
150        let anchor = match direction {
151            Direction::Next => all_bookmarks
152                .iter()
153                .find(|anchor| anchor.to_offset(&multi_buffer_snapshot) > selection.head())
154                .or_else(|| all_bookmarks.first()),
155            Direction::Prev => all_bookmarks
156                .iter()
157                .rfind(|anchor| anchor.to_offset(&multi_buffer_snapshot) < selection.head())
158                .or_else(|| all_bookmarks.last()),
159        }
160        .cloned();
161
162        if let Some(anchor) = anchor {
163            self.unfold_ranges(&[anchor..anchor], true, false, cx);
164            self.change_selections(
165                SelectionEffects::scroll(Autoscroll::center()),
166                window,
167                cx,
168                |s| {
169                    s.select_anchor_ranges([anchor..anchor]);
170                },
171            );
172        }
173    }
174
175    pub fn view_bookmarks(
176        workspace: &mut Workspace,
177        _: &ViewBookmarks,
178        window: &mut Window,
179        cx: &mut Context<Workspace>,
180    ) {
181        let bookmark_store = workspace.project().read(cx).bookmark_store();
182        cx.spawn_in(window, async move |workspace, cx| {
183            let Some(locations) = BookmarkStore::all_bookmark_locations(bookmark_store, cx)
184                .await
185                .log_err()
186            else {
187                return;
188            };
189
190            workspace
191                .update_in(cx, |workspace, window, cx| {
192                    Editor::open_locations_in_multibuffer(
193                        workspace,
194                        locations,
195                        "Bookmarks".into(),
196                        false,
197                        false,
198                        MultibufferSelectionMode::First,
199                        window,
200                        cx,
201                    );
202                })
203                .log_err();
204        })
205        .detach();
206    }
207
208    fn bookmarks_in_range(
209        range: Range<MultiBufferOffset>,
210        multi_buffer_snapshot: &MultiBufferSnapshot,
211        project: &Entity<Project>,
212        bookmark_store: &Entity<BookmarkStore>,
213        cx: &mut Context<Self>,
214    ) -> Vec<Anchor> {
215        multi_buffer_snapshot
216            .range_to_buffer_ranges(range)
217            .into_iter()
218            .flat_map(|(buffer_snapshot, buffer_range, _excerpt_range)| {
219                let Some(buffer) = project
220                    .read(cx)
221                    .buffer_for_id(buffer_snapshot.remote_id(), cx)
222                else {
223                    return Vec::new();
224                };
225                bookmark_store
226                    .update(cx, |store, cx| {
227                        store.bookmarks_for_buffer(
228                            buffer,
229                            buffer_snapshot.anchor_before(buffer_range.start)
230                                ..buffer_snapshot.anchor_after(buffer_range.end),
231                            &buffer_snapshot,
232                            cx,
233                        )
234                    })
235                    .into_iter()
236                    .filter_map(|bookmark| {
237                        multi_buffer_snapshot.anchor_in_buffer(bookmark.anchor())
238                    })
239                    .collect::<Vec<_>>()
240            })
241            .collect()
242    }
243}