1use std::{mem, ops::Range, sync::Arc};
2
3use collections::HashSet;
4use gpui::{App, AppContext, Context, Entity};
5use itertools::Itertools;
6use language::{Buffer, BufferSnapshot};
7use rope::Point;
8use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};
9use util::{post_inc, rel_path::RelPath};
10
11use crate::{
12 Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
13};
14
15#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
16pub struct PathKey {
17 // Used by the derived PartialOrd & Ord
18 pub sort_prefix: Option<u64>,
19 pub path: Arc<RelPath>,
20}
21
22impl PathKey {
23 pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
24 Self {
25 sort_prefix: Some(sort_prefix),
26 path,
27 }
28 }
29
30 pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
31 if let Some(file) = buffer.read(cx).file() {
32 Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
33 } else {
34 Self {
35 sort_prefix: None,
36 path: RelPath::unix(&buffer.entity_id().to_string())
37 .unwrap()
38 .into_arc(),
39 }
40 }
41 }
42}
43
44impl MultiBuffer {
45 pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
46 self.excerpts_by_path.keys().cloned()
47 }
48
49 pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
50 if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
51 self.remove_excerpts(to_remove, cx)
52 }
53 if let Some(follower) = &self.follower {
54 follower.update(cx, |follower, cx| {
55 follower.remove_excerpts_for_path(path, cx);
56 });
57 }
58 }
59
60 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
61 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
62 let snapshot = self.read(cx);
63 let excerpt = snapshot.excerpt(*excerpt_id)?;
64 Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
65 }
66
67 pub fn excerpt_paths(&self) -> impl Iterator<Item = &PathKey> {
68 self.excerpts_by_path.keys()
69 }
70
71 /// Sets excerpts, returns `true` if at least one new excerpt was added.
72 pub fn set_excerpts_for_path(
73 &mut self,
74 path: PathKey,
75 buffer: Entity<Buffer>,
76 ranges: impl IntoIterator<Item = Range<Point>>,
77 context_line_count: u32,
78 cx: &mut Context<Self>,
79 ) -> (Vec<Range<Anchor>>, bool) {
80 let buffer_snapshot = buffer.read(cx).snapshot();
81 let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
82
83 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
84 self.set_merged_excerpt_ranges_for_path(
85 path,
86 buffer,
87 excerpt_ranges,
88 &buffer_snapshot,
89 new,
90 counts,
91 cx,
92 )
93 }
94
95 pub fn set_excerpt_ranges_for_path(
96 &mut self,
97 path: PathKey,
98 buffer: Entity<Buffer>,
99 buffer_snapshot: &BufferSnapshot,
100 excerpt_ranges: Vec<ExcerptRange<Point>>,
101 cx: &mut Context<Self>,
102 ) -> (Vec<Range<Anchor>>, bool) {
103 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
104 self.set_merged_excerpt_ranges_for_path(
105 path,
106 buffer,
107 excerpt_ranges,
108 buffer_snapshot,
109 new,
110 counts,
111 cx,
112 )
113 }
114
115 pub fn set_anchored_excerpts_for_path(
116 &self,
117 path_key: PathKey,
118 buffer: Entity<Buffer>,
119 ranges: Vec<Range<text::Anchor>>,
120 context_line_count: u32,
121 cx: &Context<Self>,
122 ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
123 let buffer_snapshot = buffer.read(cx).snapshot();
124 let multi_buffer = cx.weak_entity();
125 let mut app = cx.to_async();
126 async move {
127 let snapshot = buffer_snapshot.clone();
128 let (excerpt_ranges, new, counts) = app
129 .background_spawn(async move {
130 let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
131 let excerpt_ranges =
132 build_excerpt_ranges(ranges, context_line_count, &snapshot);
133 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
134 (excerpt_ranges, new, counts)
135 })
136 .await;
137
138 multi_buffer
139 .update(&mut app, move |multi_buffer, cx| {
140 let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
141 path_key,
142 buffer,
143 excerpt_ranges,
144 &buffer_snapshot,
145 new,
146 counts,
147 cx,
148 );
149 ranges
150 })
151 .ok()
152 .unwrap_or_default()
153 }
154 }
155
156 pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
157 self.remove_excerpts(
158 self.excerpts_for_buffer(buffer, cx)
159 .into_iter()
160 .map(|(excerpt, _)| excerpt),
161 cx,
162 );
163 }
164
165 pub(super) fn expand_excerpts_with_paths(
166 &mut self,
167 ids: impl IntoIterator<Item = ExcerptId>,
168 line_count: u32,
169 direction: ExpandExcerptDirection,
170 cx: &mut Context<Self>,
171 ) {
172 let grouped = ids
173 .into_iter()
174 .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
175 .into_iter()
176 .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
177 .collect::<Vec<_>>();
178 let snapshot = self.snapshot(cx);
179
180 for (path, ids) in grouped.into_iter() {
181 let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
182 continue;
183 };
184
185 let ids_to_expand = HashSet::from_iter(ids);
186 let mut excerpt_id_ = None;
187 let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
188 let excerpt = snapshot.excerpt(*excerpt_id)?;
189 let excerpt_id = excerpt.id;
190 if excerpt_id_.is_none() {
191 excerpt_id_ = Some(excerpt_id);
192 }
193
194 let mut context = excerpt.range.context.to_point(&excerpt.buffer);
195 if ids_to_expand.contains(&excerpt_id) {
196 match direction {
197 ExpandExcerptDirection::Up => {
198 context.start.row = context.start.row.saturating_sub(line_count);
199 context.start.column = 0;
200 }
201 ExpandExcerptDirection::Down => {
202 context.end.row =
203 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
204 context.end.column = excerpt.buffer.line_len(context.end.row);
205 }
206 ExpandExcerptDirection::UpAndDown => {
207 context.start.row = context.start.row.saturating_sub(line_count);
208 context.start.column = 0;
209 context.end.row =
210 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
211 context.end.column = excerpt.buffer.line_len(context.end.row);
212 }
213 }
214 }
215
216 Some(ExcerptRange {
217 context,
218 primary: excerpt.range.primary.to_point(&excerpt.buffer),
219 })
220 });
221 let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
222 for range in expanded_ranges {
223 if let Some(last_range) = merged_ranges.last_mut()
224 && last_range.context.end >= range.context.start
225 {
226 last_range.context.end = range.context.end;
227 continue;
228 }
229 merged_ranges.push(range)
230 }
231 let Some(excerpt_id) = excerpt_id_ else {
232 continue;
233 };
234 let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
235 continue;
236 };
237
238 let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
239 continue;
240 };
241
242 let buffer_snapshot = buffer.read(cx).snapshot();
243 self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
244 }
245 }
246
247 /// Sets excerpts, returns `true` if at least one new excerpt was added.
248 fn set_merged_excerpt_ranges_for_path(
249 &mut self,
250 path: PathKey,
251 buffer: Entity<Buffer>,
252 ranges: Vec<ExcerptRange<Point>>,
253 buffer_snapshot: &BufferSnapshot,
254 new: Vec<ExcerptRange<Point>>,
255 counts: Vec<usize>,
256 cx: &mut Context<Self>,
257 ) -> (Vec<Range<Anchor>>, bool) {
258 let (excerpt_ids, added_a_new_excerpt) =
259 self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
260
261 let mut result = Vec::new();
262 let mut ranges = ranges.into_iter();
263 for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
264 for range in ranges.by_ref().take(range_count) {
265 let range = Anchor::range_in_buffer(
266 excerpt_id,
267 buffer_snapshot.anchor_before(&range.primary.start)
268 ..buffer_snapshot.anchor_after(&range.primary.end),
269 );
270 result.push(range)
271 }
272 }
273 (result, added_a_new_excerpt)
274 }
275
276 fn update_path_excerpts(
277 &mut self,
278 path: PathKey,
279 buffer: Entity<Buffer>,
280 buffer_snapshot: &BufferSnapshot,
281 new: Vec<ExcerptRange<Point>>,
282 cx: &mut Context<Self>,
283 ) -> (Vec<ExcerptId>, bool) {
284 let mut insert_after = self
285 .excerpts_by_path
286 .range(..path.clone())
287 .next_back()
288 .and_then(|(_, value)| value.last().copied())
289 .unwrap_or(ExcerptId::min());
290
291 let existing = self
292 .excerpts_by_path
293 .get(&path)
294 .cloned()
295 .unwrap_or_default();
296 let mut new_iter = new.into_iter().peekable();
297 let mut existing_iter = existing.into_iter().peekable();
298
299 let mut excerpt_ids = Vec::new();
300 let mut to_remove = Vec::new();
301 let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
302 let mut added_a_new_excerpt = false;
303 let snapshot = self.snapshot(cx);
304
305 let mut next_excerpt_id =
306 // is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
307 if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
308 last_entry.id.0 + 1
309 } else {
310 1
311 };
312
313 let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
314
315 let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
316 excerpts_cursor.next();
317
318 loop {
319 let existing = if let Some(&existing_id) = existing_iter.peek() {
320 let locator = snapshot.excerpt_locator_for_id(existing_id);
321 excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
322 if let Some(excerpt) = excerpts_cursor.item() {
323 if excerpt.buffer_id != buffer_snapshot.remote_id() {
324 to_remove.push(existing_id);
325 existing_iter.next();
326 continue;
327 }
328 Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
329 } else {
330 None
331 }
332 } else {
333 None
334 };
335
336 let new = new_iter.peek();
337 if let Some((last_id, last)) = to_insert.last_mut() {
338 if let Some(new) = new
339 && last.context.end >= new.context.start
340 {
341 last.context.end = last.context.end.max(new.context.end);
342 excerpt_ids.push(*last_id);
343 new_iter.next();
344 continue;
345 }
346 if let Some((existing_id, existing_range)) = &existing
347 && last.context.end >= existing_range.start
348 {
349 last.context.end = last.context.end.max(existing_range.end);
350 to_remove.push(*existing_id);
351 self.snapshot
352 .get_mut()
353 .replaced_excerpts
354 .insert(*existing_id, *last_id);
355 existing_iter.next();
356 continue;
357 }
358 }
359
360 match (new, existing) {
361 (None, None) => break,
362 (None, Some((existing_id, _))) => {
363 existing_iter.next();
364 to_remove.push(existing_id);
365 continue;
366 }
367 (Some(_), None) => {
368 added_a_new_excerpt = true;
369 let new_id = next_excerpt_id();
370 excerpt_ids.push(new_id);
371 to_insert.push((new_id, new_iter.next().unwrap()));
372 continue;
373 }
374 (Some(new), Some((_, existing_range))) => {
375 if existing_range.end < new.context.start {
376 let existing_id = existing_iter.next().unwrap();
377 to_remove.push(existing_id);
378 continue;
379 } else if existing_range.start > new.context.end {
380 let new_id = next_excerpt_id();
381 excerpt_ids.push(new_id);
382 to_insert.push((new_id, new_iter.next().unwrap()));
383 continue;
384 }
385
386 if existing_range.start == new.context.start
387 && existing_range.end == new.context.end
388 {
389 self.insert_excerpts_with_ids_after(
390 insert_after,
391 buffer.clone(),
392 mem::take(&mut to_insert),
393 cx,
394 );
395 insert_after = existing_iter.next().unwrap();
396 excerpt_ids.push(insert_after);
397 new_iter.next();
398 } else {
399 let existing_id = existing_iter.next().unwrap();
400 let new_id = next_excerpt_id();
401 self.snapshot
402 .get_mut()
403 .replaced_excerpts
404 .insert(existing_id, new_id);
405 to_remove.push(existing_id);
406 let mut range = new_iter.next().unwrap();
407 range.context.start = range.context.start.min(existing_range.start);
408 range.context.end = range.context.end.max(existing_range.end);
409 excerpt_ids.push(new_id);
410 to_insert.push((new_id, range));
411 }
412 }
413 };
414 }
415
416 self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
417 // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
418 to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
419 self.remove_excerpts(to_remove, cx);
420
421 if excerpt_ids.is_empty() {
422 self.excerpts_by_path.remove(&path);
423 } else {
424 for excerpt_id in &excerpt_ids {
425 self.paths_by_excerpt.insert(*excerpt_id, path.clone());
426 }
427 let snapshot = &*self.snapshot.get_mut();
428 let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect();
429 excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
430 self.excerpts_by_path.insert(path, excerpt_ids);
431 }
432
433 (excerpt_ids, added_a_new_excerpt)
434 }
435}