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