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};
10use ztracing::instrument;
11
12use crate::{
13 Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
14};
15
16#[derive(Debug, Clone)]
17pub struct PathExcerptInsertResult {
18 pub excerpt_ids: Vec<ExcerptId>,
19 pub added_new_excerpt: bool,
20}
21
22#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
23pub struct PathKey {
24 // Used by the derived PartialOrd & Ord
25 pub sort_prefix: Option<u64>,
26 pub path: Arc<RelPath>,
27}
28
29impl PathKey {
30 pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
31 Self {
32 sort_prefix: Some(sort_prefix),
33 path,
34 }
35 }
36
37 pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
38 if let Some(file) = buffer.read(cx).file() {
39 Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
40 } else {
41 Self {
42 sort_prefix: None,
43 path: RelPath::unix(&buffer.entity_id().to_string())
44 .unwrap()
45 .into_arc(),
46 }
47 }
48 }
49}
50
51impl MultiBuffer {
52 pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {
53 self.excerpts_by_path.keys()
54 }
55
56 pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
57 self.excerpts_by_path
58 .get(path)
59 .map(|excerpts| excerpts.as_slice())
60 .unwrap_or_default()
61 .iter()
62 .copied()
63 }
64
65 pub fn path_for_excerpt(&self, excerpt: ExcerptId) -> Option<PathKey> {
66 self.paths_by_excerpt.get(&excerpt).cloned()
67 }
68
69 pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
70 if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
71 self.remove_excerpts(to_remove, cx)
72 }
73 }
74
75 pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
76 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
77 let snapshot = self.read(cx);
78 let excerpt = snapshot.excerpt(*excerpt_id)?;
79 self.buffer(excerpt.buffer_id)
80 }
81
82 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
83 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
84 let snapshot = self.read(cx);
85 let excerpt = snapshot.excerpt(*excerpt_id)?;
86 Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
87 }
88
89 /// Sets excerpts, returns `true` if at least one new excerpt was added.
90 #[instrument(skip_all)]
91 pub fn set_excerpts_for_path(
92 &mut self,
93 path: PathKey,
94 buffer: Entity<Buffer>,
95 ranges: impl IntoIterator<Item = Range<Point>>,
96 context_line_count: u32,
97 cx: &mut Context<Self>,
98 ) -> (Vec<Range<Anchor>>, bool) {
99 let buffer_snapshot = buffer.read(cx).snapshot();
100 let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
101
102 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
103 self.set_merged_excerpt_ranges_for_path(
104 path,
105 buffer,
106 excerpt_ranges,
107 &buffer_snapshot,
108 new,
109 counts,
110 cx,
111 )
112 }
113
114 pub fn set_excerpt_ranges_for_path(
115 &mut self,
116 path: PathKey,
117 buffer: Entity<Buffer>,
118 buffer_snapshot: &BufferSnapshot,
119 excerpt_ranges: Vec<ExcerptRange<Point>>,
120 cx: &mut Context<Self>,
121 ) -> (Vec<Range<Anchor>>, bool) {
122 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
123 self.set_merged_excerpt_ranges_for_path(
124 path,
125 buffer,
126 excerpt_ranges,
127 buffer_snapshot,
128 new,
129 counts,
130 cx,
131 )
132 }
133
134 pub fn set_anchored_excerpts_for_path(
135 &self,
136 path_key: PathKey,
137 buffer: Entity<Buffer>,
138 ranges: Vec<Range<text::Anchor>>,
139 context_line_count: u32,
140 cx: &Context<Self>,
141 ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
142 let buffer_snapshot = buffer.read(cx).snapshot();
143 let multi_buffer = cx.weak_entity();
144 let mut app = cx.to_async();
145 async move {
146 let snapshot = buffer_snapshot.clone();
147 let (excerpt_ranges, new, counts) = app
148 .background_spawn(async move {
149 let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
150 let excerpt_ranges =
151 build_excerpt_ranges(ranges, context_line_count, &snapshot);
152 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
153 (excerpt_ranges, new, counts)
154 })
155 .await;
156
157 multi_buffer
158 .update(&mut app, move |multi_buffer, cx| {
159 let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
160 path_key,
161 buffer,
162 excerpt_ranges,
163 &buffer_snapshot,
164 new,
165 counts,
166 cx,
167 );
168 ranges
169 })
170 .ok()
171 .unwrap_or_default()
172 }
173 }
174
175 pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
176 self.remove_excerpts(
177 self.excerpts_for_buffer(buffer, cx)
178 .into_iter()
179 .map(|(excerpt, _)| excerpt),
180 cx,
181 );
182 }
183
184 pub(super) fn expand_excerpts_with_paths(
185 &mut self,
186 ids: impl IntoIterator<Item = ExcerptId>,
187 line_count: u32,
188 direction: ExpandExcerptDirection,
189 cx: &mut Context<Self>,
190 ) {
191 let mut sorted_ids: Vec<ExcerptId> = ids.into_iter().collect();
192 sorted_ids.sort_by(|a, b| {
193 let path_a = self.paths_by_excerpt.get(a);
194 let path_b = self.paths_by_excerpt.get(b);
195 path_a.cmp(&path_b)
196 });
197 let grouped = sorted_ids
198 .into_iter()
199 .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
200 .into_iter()
201 .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
202 .collect::<Vec<_>>();
203 let snapshot = self.snapshot(cx);
204
205 for (path, ids) in grouped.into_iter() {
206 let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
207 continue;
208 };
209
210 let ids_to_expand = HashSet::from_iter(ids);
211 let mut excerpt_id_ = None;
212 let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
213 let excerpt = snapshot.excerpt(*excerpt_id)?;
214 let excerpt_id = excerpt.id;
215 if excerpt_id_.is_none() {
216 excerpt_id_ = Some(excerpt_id);
217 }
218
219 let mut context = excerpt.range.context.to_point(&excerpt.buffer);
220 if ids_to_expand.contains(&excerpt_id) {
221 match direction {
222 ExpandExcerptDirection::Up => {
223 context.start.row = context.start.row.saturating_sub(line_count);
224 context.start.column = 0;
225 }
226 ExpandExcerptDirection::Down => {
227 context.end.row =
228 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
229 context.end.column = excerpt.buffer.line_len(context.end.row);
230 }
231 ExpandExcerptDirection::UpAndDown => {
232 context.start.row = context.start.row.saturating_sub(line_count);
233 context.start.column = 0;
234 context.end.row =
235 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
236 context.end.column = excerpt.buffer.line_len(context.end.row);
237 }
238 }
239 }
240
241 Some(ExcerptRange {
242 context,
243 primary: excerpt.range.primary.to_point(&excerpt.buffer),
244 })
245 });
246 let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
247 for range in expanded_ranges {
248 if let Some(last_range) = merged_ranges.last_mut()
249 && last_range.context.end >= range.context.start
250 {
251 last_range.context.end = range.context.end;
252 continue;
253 }
254 merged_ranges.push(range)
255 }
256 let Some(excerpt_id) = excerpt_id_ else {
257 continue;
258 };
259 let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
260 continue;
261 };
262
263 let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
264 continue;
265 };
266
267 let buffer_snapshot = buffer.read(cx).snapshot();
268 self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
269 }
270 }
271
272 /// Sets excerpts, returns `true` if at least one new excerpt was added.
273 fn set_merged_excerpt_ranges_for_path(
274 &mut self,
275 path: PathKey,
276 buffer: Entity<Buffer>,
277 ranges: Vec<ExcerptRange<Point>>,
278 buffer_snapshot: &BufferSnapshot,
279 new: Vec<ExcerptRange<Point>>,
280 counts: Vec<usize>,
281 cx: &mut Context<Self>,
282 ) -> (Vec<Range<Anchor>>, bool) {
283 let insert_result = self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
284
285 let mut result = Vec::new();
286 let mut ranges = ranges.into_iter();
287 for (excerpt_id, range_count) in insert_result
288 .excerpt_ids
289 .into_iter()
290 .zip(counts.into_iter())
291 {
292 for range in ranges.by_ref().take(range_count) {
293 let range = Anchor::range_in_buffer(
294 excerpt_id,
295 buffer_snapshot.anchor_before(&range.primary.start)
296 ..buffer_snapshot.anchor_after(&range.primary.end),
297 );
298 result.push(range)
299 }
300 }
301 (result, insert_result.added_new_excerpt)
302 }
303
304 pub fn update_path_excerpts(
305 &mut self,
306 path: PathKey,
307 buffer: Entity<Buffer>,
308 buffer_snapshot: &BufferSnapshot,
309 new: Vec<ExcerptRange<Point>>,
310 cx: &mut Context<Self>,
311 ) -> PathExcerptInsertResult {
312 let mut insert_after = self
313 .excerpts_by_path
314 .range(..path.clone())
315 .next_back()
316 .and_then(|(_, value)| value.last().copied())
317 .unwrap_or(ExcerptId::min());
318
319 let existing = self
320 .excerpts_by_path
321 .get(&path)
322 .cloned()
323 .unwrap_or_default();
324 let mut new_iter = new.into_iter().peekable();
325 let mut existing_iter = existing.into_iter().peekable();
326
327 let mut excerpt_ids = Vec::new();
328 let mut to_remove = Vec::new();
329 let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
330 let mut added_a_new_excerpt = false;
331 let snapshot = self.snapshot(cx);
332
333 let mut next_excerpt_id =
334 if let Some(last_entry) = self.snapshot.get_mut().excerpt_ids.last() {
335 last_entry.id.0 + 1
336 } else {
337 1
338 };
339
340 let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
341
342 let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
343 excerpts_cursor.next();
344
345 loop {
346 let existing = if let Some(&existing_id) = existing_iter.peek() {
347 let locator = snapshot.excerpt_locator_for_id(existing_id);
348 excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
349 if let Some(excerpt) = excerpts_cursor.item() {
350 if excerpt.buffer_id != buffer_snapshot.remote_id() {
351 to_remove.push(existing_id);
352 existing_iter.next();
353 continue;
354 }
355 Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
356 } else {
357 None
358 }
359 } else {
360 None
361 };
362
363 let new = new_iter.peek();
364 // Try to merge the next new range or existing excerpt into the last
365 // queued insert.
366 if let Some((last_id, last)) = to_insert.last_mut() {
367 // Next new range overlaps the last queued insert: absorb it by
368 // extending the insert's end.
369 if let Some(new) = new
370 && last.context.end >= new.context.start
371 {
372 last.context.end = last.context.end.max(new.context.end);
373 excerpt_ids.push(*last_id);
374 new_iter.next();
375 continue;
376 }
377 // Next existing excerpt overlaps the last queued insert: absorb
378 // it by extending the insert's end, and record the existing
379 // excerpt as replaced so anchors in it resolve to the new one.
380 if let Some((existing_id, existing_range)) = &existing
381 && last.context.end >= existing_range.start
382 {
383 last.context.end = last.context.end.max(existing_range.end);
384 to_remove.push(*existing_id);
385 Arc::make_mut(&mut self.snapshot.get_mut().replaced_excerpts)
386 .insert(*existing_id, *last_id);
387 existing_iter.next();
388 continue;
389 }
390 }
391
392 match (new, existing) {
393 (None, None) => break,
394
395 // No more new ranges; remove the remaining existing excerpt.
396 (None, Some((existing_id, _))) => {
397 existing_iter.next();
398 to_remove.push(existing_id);
399 }
400
401 // No more existing excerpts; queue the new range for insertion.
402 (Some(_), None) => {
403 added_a_new_excerpt = true;
404 let new_id = next_excerpt_id();
405 excerpt_ids.push(new_id);
406 to_insert.push((new_id, new_iter.next().unwrap()));
407 }
408
409 // Existing excerpt ends before the new range starts, so it
410 // has no corresponding new range and must be removed. Flush
411 // pending inserts and advance `insert_after` past it so that
412 // future inserts receive locators *after* this excerpt's
413 // locator, preserving forward ordering.
414 (Some(new), Some((_, existing_range)))
415 if existing_range.end < new.context.start =>
416 {
417 self.insert_excerpts_with_ids_after(
418 insert_after,
419 buffer.clone(),
420 mem::take(&mut to_insert),
421 cx,
422 );
423 insert_after = existing_iter.next().unwrap();
424 to_remove.push(insert_after);
425 }
426 // New range ends before the existing excerpt starts, so the
427 // new range has no corresponding existing excerpt. Queue it
428 // for insertion at the current `insert_after` position
429 // (before the existing excerpt), which is the correct
430 // spatial ordering.
431 (Some(new), Some((_, existing_range)))
432 if existing_range.start > new.context.end =>
433 {
434 let new_id = next_excerpt_id();
435 excerpt_ids.push(new_id);
436 to_insert.push((new_id, new_iter.next().unwrap()));
437 }
438 // Exact match: keep the existing excerpt in place, flush
439 // any pending inserts before it, and use it as the new
440 // `insert_after` anchor.
441 (Some(new), Some((_, existing_range)))
442 if existing_range.start == new.context.start
443 && existing_range.end == new.context.end =>
444 {
445 self.insert_excerpts_with_ids_after(
446 insert_after,
447 buffer.clone(),
448 mem::take(&mut to_insert),
449 cx,
450 );
451 insert_after = existing_iter.next().unwrap();
452 excerpt_ids.push(insert_after);
453 new_iter.next();
454 }
455
456 // Partial overlap: replace the existing excerpt with a new
457 // one whose range is the union of both, and record the
458 // replacement so that anchors in the old excerpt resolve to
459 // the new one.
460 (Some(_), Some((_, existing_range))) => {
461 let existing_id = existing_iter.next().unwrap();
462 let new_id = next_excerpt_id();
463 Arc::make_mut(&mut self.snapshot.get_mut().replaced_excerpts)
464 .insert(existing_id, new_id);
465 to_remove.push(existing_id);
466 let mut range = new_iter.next().unwrap();
467 range.context.start = range.context.start.min(existing_range.start);
468 range.context.end = range.context.end.max(existing_range.end);
469 excerpt_ids.push(new_id);
470 to_insert.push((new_id, range));
471 }
472 };
473 }
474
475 self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
476 // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
477 to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
478 self.remove_excerpts(to_remove, cx);
479
480 if excerpt_ids.is_empty() {
481 self.excerpts_by_path.remove(&path);
482 } else {
483 let snapshot = &*self.snapshot.get_mut();
484 let excerpt_ids = excerpt_ids
485 .iter()
486 .dedup()
487 .cloned()
488 // todo(lw): There is a logic bug somewhere that causes excerpt_ids to not necessarily be in order by locator
489 .sorted_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id))
490 .collect();
491 for &excerpt_id in &excerpt_ids {
492 self.paths_by_excerpt.insert(excerpt_id, path.clone());
493 }
494 self.excerpts_by_path.insert(path, excerpt_ids);
495 }
496
497 PathExcerptInsertResult {
498 excerpt_ids,
499 added_new_excerpt: added_a_new_excerpt,
500 }
501 }
502}