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}