1use std::{ops::Range, rc::Rc, sync::Arc};
2
3use gpui::{App, AppContext, Context, Entity};
4use itertools::Itertools;
5use language::{Buffer, BufferSnapshot};
6use rope::Point;
7use sum_tree::{Dimensions, SumTree};
8use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
9use util::rel_path::RelPath;
10use ztracing::instrument;
11
12use crate::{
13 Anchor, BufferState, BufferStateSnapshot, DiffChangeKind, Event, Excerpt, ExcerptOffset,
14 ExcerptRange, ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset,
15 PathKeyIndex, build_excerpt_ranges, remove_diff_state,
16};
17
18#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
19pub struct PathKey {
20 // Used by the derived PartialOrd & Ord
21 pub sort_prefix: Option<u64>,
22 pub path: Arc<RelPath>,
23}
24
25impl PathKey {
26 pub fn min() -> Self {
27 Self {
28 sort_prefix: None,
29 path: RelPath::empty().into_arc(),
30 }
31 }
32
33 pub fn sorted(sort_prefix: u64) -> Self {
34 Self {
35 sort_prefix: Some(sort_prefix),
36 path: RelPath::empty().into_arc(),
37 }
38 }
39 pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
40 Self {
41 sort_prefix: Some(sort_prefix),
42 path,
43 }
44 }
45
46 pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
47 if let Some(file) = buffer.read(cx).file() {
48 Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
49 } else {
50 Self {
51 sort_prefix: None,
52 path: RelPath::unix(&buffer.entity_id().to_string())
53 .unwrap()
54 .into_arc(),
55 }
56 }
57 }
58}
59
60impl MultiBuffer {
61 pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
62 let snapshot = self.snapshot(cx);
63 let excerpt = snapshot.excerpts_for_path(path).next()?;
64 self.buffer(excerpt.context.start.buffer_id)
65 }
66
67 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
68 let snapshot = self.snapshot(cx);
69 let excerpt = snapshot.excerpts_for_path(path).next()?;
70 let path_key_index = snapshot.path_key_index_for_buffer(excerpt.context.start.buffer_id)?;
71 Some(Anchor::in_buffer(path_key_index, excerpt.context.start))
72 }
73
74 pub fn set_excerpts_for_buffer(
75 &mut self,
76 buffer: Entity<Buffer>,
77 ranges: impl IntoIterator<Item = Range<Point>>,
78 context_line_count: u32,
79 cx: &mut Context<Self>,
80 ) -> bool {
81 let path = PathKey::for_buffer(&buffer, cx);
82 self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
83 }
84
85 /// Sets excerpts, returns `true` if at least one new excerpt was added.
86 ///
87 /// Any existing excerpts for this buffer or this path will be replaced by the provided ranges.
88 #[instrument(skip_all)]
89 pub fn set_excerpts_for_path(
90 &mut self,
91 path: PathKey,
92 buffer: Entity<Buffer>,
93 ranges: impl IntoIterator<Item = Range<Point>>,
94 context_line_count: u32,
95 cx: &mut Context<Self>,
96 ) -> bool {
97 let buffer_snapshot = buffer.read(cx).snapshot();
98 let ranges: Vec<_> = ranges.into_iter().collect();
99 let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
100
101 let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
102 let (inserted, _path_key_index) =
103 self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
104 inserted
105 }
106
107 /// Like [`Self::set_excerpts_for_path`], but expands the provided ranges to cover any overlapping existing excerpts
108 /// for the same buffer and path.
109 ///
110 /// Existing excerpts that do not overlap any of the provided ranges are discarded.
111 pub fn update_excerpts_for_path(
112 &mut self,
113 path: PathKey,
114 buffer: Entity<Buffer>,
115 ranges: impl IntoIterator<Item = Range<Point>>,
116 context_line_count: u32,
117 cx: &mut Context<Self>,
118 ) -> bool {
119 let buffer_snapshot = buffer.read(cx).snapshot();
120 let ranges: Vec<_> = ranges.into_iter().collect();
121 let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
122 let merged = self.merge_new_with_existing_excerpt_ranges(
123 &path,
124 &buffer_snapshot,
125 excerpt_ranges,
126 cx,
127 );
128
129 let (inserted, _path_key_index) =
130 self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
131 inserted
132 }
133
134 pub fn merge_new_with_existing_excerpt_ranges(
135 &self,
136 path: &PathKey,
137 buffer_snapshot: &BufferSnapshot,
138 mut excerpt_ranges: Vec<ExcerptRange<Point>>,
139 cx: &App,
140 ) -> Vec<ExcerptRange<Point>> {
141 let multibuffer_snapshot = self.snapshot(cx);
142
143 if multibuffer_snapshot.path_for_buffer(buffer_snapshot.remote_id()) == Some(path) {
144 excerpt_ranges.sort_by_key(|range| range.context.start);
145 let mut combined_ranges = Vec::new();
146 let mut new_ranges = excerpt_ranges.into_iter().peekable();
147 for existing_range in
148 multibuffer_snapshot.excerpts_for_buffer(buffer_snapshot.remote_id())
149 {
150 let existing_range = ExcerptRange {
151 context: existing_range.context.to_point(buffer_snapshot),
152 primary: existing_range.primary.to_point(buffer_snapshot),
153 };
154 while let Some(new_range) = new_ranges.peek()
155 && new_range.context.end < existing_range.context.start
156 {
157 combined_ranges.push(new_range.clone());
158 new_ranges.next();
159 }
160
161 if let Some(new_range) = new_ranges.peek()
162 && new_range.context.start <= existing_range.context.end
163 {
164 combined_ranges.push(existing_range)
165 }
166 }
167 combined_ranges.extend(new_ranges);
168 excerpt_ranges = combined_ranges;
169 }
170
171 excerpt_ranges.sort_by_key(|range| range.context.start);
172 Self::merge_excerpt_ranges(&excerpt_ranges)
173 }
174
175 pub fn set_excerpt_ranges_for_path(
176 &mut self,
177 path: PathKey,
178 buffer: Entity<Buffer>,
179 buffer_snapshot: &BufferSnapshot,
180 excerpt_ranges: Vec<ExcerptRange<Point>>,
181 cx: &mut Context<Self>,
182 ) -> bool {
183 let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
184 let (inserted, _path_key_index) =
185 self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, merged, cx);
186 inserted
187 }
188
189 pub fn set_anchored_excerpts_for_path(
190 &self,
191 path_key: PathKey,
192 buffer: Entity<Buffer>,
193 ranges: Vec<Range<text::Anchor>>,
194 context_line_count: u32,
195 cx: &Context<Self>,
196 ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
197 let buffer_snapshot = buffer.read(cx).snapshot();
198 let multi_buffer = cx.weak_entity();
199 let mut app = cx.to_async();
200 async move {
201 let snapshot = buffer_snapshot.clone();
202 let (ranges, merged_excerpt_ranges) = app
203 .background_spawn(async move {
204 let point_ranges = ranges.iter().map(|range| range.to_point(&snapshot));
205 let excerpt_ranges =
206 build_excerpt_ranges(point_ranges, context_line_count, &snapshot);
207 let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
208 (ranges, merged)
209 })
210 .await;
211
212 multi_buffer
213 .update(&mut app, move |multi_buffer, cx| {
214 let (_, path_key_index) = multi_buffer.set_merged_excerpt_ranges_for_path(
215 path_key,
216 buffer,
217 &buffer_snapshot,
218 merged_excerpt_ranges,
219 cx,
220 );
221 ranges
222 .into_iter()
223 .map(|range| Anchor::range_in_buffer(path_key_index, range))
224 .collect()
225 })
226 .ok()
227 .unwrap_or_default()
228 }
229 }
230
231 pub fn expand_excerpts(
232 &mut self,
233 anchors: impl IntoIterator<Item = Anchor>,
234 line_count: u32,
235 direction: ExpandExcerptDirection,
236 cx: &mut Context<Self>,
237 ) {
238 if line_count == 0 {
239 return;
240 }
241
242 let snapshot = self.snapshot(cx);
243 let mut sorted_anchors = anchors
244 .into_iter()
245 .filter_map(|anchor| anchor.excerpt_anchor())
246 .collect::<Vec<_>>();
247 if sorted_anchors.is_empty() {
248 return;
249 }
250 sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot));
251 let buffers = sorted_anchors.into_iter().chunk_by(|anchor| anchor.path);
252 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
253
254 for (path_index, excerpt_anchors) in &buffers {
255 let path = snapshot
256 .path_keys_by_index
257 .get(&path_index)
258 .expect("anchor from wrong multibuffer");
259
260 let mut excerpt_anchors = excerpt_anchors.peekable();
261 let mut ranges = Vec::new();
262
263 cursor.seek_forward(path, Bias::Left);
264 let Some((buffer, buffer_snapshot)) = cursor
265 .item()
266 .map(|excerpt| (excerpt.buffer(&self), excerpt.buffer_snapshot(&snapshot)))
267 else {
268 continue;
269 };
270
271 while let Some(excerpt) = cursor.item()
272 && &excerpt.path_key == path
273 {
274 let mut range = ExcerptRange {
275 context: excerpt.range.context.to_point(buffer_snapshot),
276 primary: excerpt.range.primary.to_point(buffer_snapshot),
277 };
278
279 let mut needs_expand = false;
280 while excerpt_anchors.peek().is_some_and(|anchor| {
281 excerpt
282 .range
283 .contains(&anchor.text_anchor(), buffer_snapshot)
284 }) {
285 needs_expand = true;
286 excerpt_anchors.next();
287 }
288
289 if needs_expand {
290 match direction {
291 ExpandExcerptDirection::Up => {
292 range.context.start.row =
293 range.context.start.row.saturating_sub(line_count);
294 range.context.start.column = 0;
295 }
296 ExpandExcerptDirection::Down => {
297 range.context.end.row = (range.context.end.row + line_count)
298 .min(excerpt.buffer_snapshot(&snapshot).max_point().row);
299 range.context.end.column = excerpt
300 .buffer_snapshot(&snapshot)
301 .line_len(range.context.end.row);
302 }
303 ExpandExcerptDirection::UpAndDown => {
304 range.context.start.row =
305 range.context.start.row.saturating_sub(line_count);
306 range.context.start.column = 0;
307 range.context.end.row = (range.context.end.row + line_count)
308 .min(excerpt.buffer_snapshot(&snapshot).max_point().row);
309 range.context.end.column = excerpt
310 .buffer_snapshot(&snapshot)
311 .line_len(range.context.end.row);
312 }
313 }
314 }
315
316 ranges.push(range);
317 cursor.next();
318 }
319
320 ranges.sort_by(|l, r| l.context.start.cmp(&r.context.start));
321
322 self.set_excerpt_ranges_for_path(path.clone(), buffer, buffer_snapshot, ranges, cx);
323 }
324 }
325
326 /// Sets excerpts, returns `true` if at least one new excerpt was added.
327 pub(crate) fn set_merged_excerpt_ranges_for_path<T>(
328 &mut self,
329 path: PathKey,
330 buffer: Entity<Buffer>,
331 buffer_snapshot: &BufferSnapshot,
332 new: Vec<ExcerptRange<T>>,
333 cx: &mut Context<Self>,
334 ) -> (bool, PathKeyIndex)
335 where
336 T: language::ToOffset,
337 {
338 let anchor_ranges = new
339 .into_iter()
340 .map(|r| ExcerptRange {
341 context: buffer_snapshot.anchor_before(r.context.start)
342 ..buffer_snapshot.anchor_after(r.context.end),
343 primary: buffer_snapshot.anchor_before(r.primary.start)
344 ..buffer_snapshot.anchor_after(r.primary.end),
345 })
346 .collect::<Vec<_>>();
347 let inserted =
348 self.update_path_excerpts(path.clone(), buffer, buffer_snapshot, &anchor_ranges, cx);
349 let path_key_index = self.get_or_create_path_key_index(&path);
350 (inserted, path_key_index)
351 }
352
353 pub(crate) fn get_or_create_path_key_index(&mut self, path_key: &PathKey) -> PathKeyIndex {
354 let mut snapshot = self.snapshot.borrow_mut();
355
356 if let Some(&existing) = snapshot.indices_by_path_key.get(path_key) {
357 return existing;
358 }
359
360 let index = snapshot
361 .path_keys_by_index
362 .last()
363 .map(|(index, _)| PathKeyIndex(index.0 + 1))
364 .unwrap_or(PathKeyIndex(0));
365 snapshot.path_keys_by_index.insert(index, path_key.clone());
366 snapshot.indices_by_path_key.insert(path_key.clone(), index);
367 index
368 }
369
370 pub fn update_path_excerpts(
371 &mut self,
372 path_key: PathKey,
373 buffer: Entity<Buffer>,
374 buffer_snapshot: &BufferSnapshot,
375 to_insert: &Vec<ExcerptRange<text::Anchor>>,
376 cx: &mut Context<Self>,
377 ) -> bool {
378 let path_key_index = self.get_or_create_path_key_index(&path_key);
379 if let Some(old_path_key) = self
380 .snapshot(cx)
381 .path_for_buffer(buffer_snapshot.remote_id())
382 && old_path_key != &path_key
383 {
384 self.remove_excerpts(old_path_key.clone(), cx);
385 }
386
387 if to_insert.len() == 0 {
388 self.remove_excerpts(path_key.clone(), cx);
389
390 return false;
391 }
392 assert_eq!(self.history.transaction_depth(), 0);
393 self.sync_mut(cx);
394
395 let buffer_id = buffer_snapshot.remote_id();
396
397 let mut snapshot = self.snapshot.get_mut();
398 let mut cursor = snapshot
399 .excerpts
400 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
401 let mut new_excerpts = SumTree::new(());
402
403 let new_ranges = to_insert.clone();
404 let mut to_insert = to_insert.iter().peekable();
405 let mut patch = Patch::empty();
406 let mut added_new_excerpt = false;
407
408 new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
409
410 // handle the case where the path key used to be associated
411 // with a different buffer by removing its excerpts.
412 if let Some(excerpt) = cursor.item()
413 && &excerpt.path_key == &path_key
414 && excerpt.buffer_id != buffer_id
415 {
416 let old_buffer_id = excerpt.buffer_id;
417 self.buffers.remove(&old_buffer_id);
418 snapshot.buffers.remove(&old_buffer_id);
419 remove_diff_state(&mut snapshot.diffs, old_buffer_id);
420 self.diffs.remove(&old_buffer_id);
421 let before = cursor.position.1;
422 cursor.seek_forward(&path_key, Bias::Right);
423 let after = cursor.position.1;
424 patch.push(Edit {
425 old: before..after,
426 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
427 });
428 cx.emit(Event::BuffersRemoved {
429 removed_buffer_ids: vec![old_buffer_id],
430 });
431 }
432
433 while let Some(excerpt) = cursor.item()
434 && excerpt.path_key == path_key
435 {
436 assert_eq!(excerpt.buffer_id, buffer_id);
437 let Some(next_excerpt) = to_insert.peek() else {
438 break;
439 };
440 if &excerpt.range == *next_excerpt {
441 let before = new_excerpts.summary().len();
442 new_excerpts.update_last(
443 |prev_excerpt| {
444 if !prev_excerpt.has_trailing_newline {
445 prev_excerpt.has_trailing_newline = true;
446 patch.push(Edit {
447 old: cursor.position.1..cursor.position.1,
448 new: before..before + MultiBufferOffset(1),
449 });
450 }
451 },
452 (),
453 );
454 new_excerpts.push(excerpt.clone(), ());
455 to_insert.next();
456 cursor.next();
457 continue;
458 }
459
460 if excerpt
461 .range
462 .context
463 .start
464 .cmp(&next_excerpt.context.start, &buffer_snapshot)
465 .is_le()
466 {
467 // remove old excerpt
468 let before = cursor.position.1;
469 cursor.next();
470 let after = cursor.position.1;
471 patch.push(Edit {
472 old: before..after,
473 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
474 });
475 } else {
476 // insert new excerpt
477 let next_excerpt = to_insert.next().unwrap();
478 added_new_excerpt = true;
479 let before = new_excerpts.summary().len();
480 new_excerpts.update_last(
481 |prev_excerpt| {
482 prev_excerpt.has_trailing_newline = true;
483 },
484 (),
485 );
486 new_excerpts.push(
487 Excerpt::new(
488 path_key.clone(),
489 path_key_index,
490 &buffer_snapshot,
491 next_excerpt.clone(),
492 false,
493 ),
494 (),
495 );
496 let after = new_excerpts.summary().len();
497 patch.push_maybe_empty(Edit {
498 old: cursor.position.1..cursor.position.1,
499 new: before..after,
500 });
501 }
502 }
503
504 // remove any further trailing excerpts
505 let mut before = cursor.position.1;
506 cursor.seek_forward(&path_key, Bias::Right);
507 let after = cursor.position.1;
508 // if we removed the previous last excerpt, remove the trailing newline from the new last excerpt
509 if cursor.item().is_none() && to_insert.peek().is_none() {
510 new_excerpts.update_last(
511 |excerpt| {
512 if excerpt.has_trailing_newline {
513 before.0.0 = before
514 .0
515 .0
516 .checked_sub(1)
517 .expect("should have preceding excerpt");
518 excerpt.has_trailing_newline = false;
519 }
520 },
521 (),
522 );
523 }
524 patch.push(Edit {
525 old: before..after,
526 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
527 });
528
529 while let Some(next_excerpt) = to_insert.next() {
530 added_new_excerpt = true;
531 let before = new_excerpts.summary().len();
532 new_excerpts.update_last(
533 |prev_excerpt| {
534 prev_excerpt.has_trailing_newline = true;
535 },
536 (),
537 );
538 new_excerpts.push(
539 Excerpt::new(
540 path_key.clone(),
541 path_key_index,
542 &buffer_snapshot,
543 next_excerpt.clone(),
544 false,
545 ),
546 (),
547 );
548 let after = new_excerpts.summary().len();
549 patch.push_maybe_empty(Edit {
550 old: cursor.position.1..cursor.position.1,
551 new: before..after,
552 });
553 }
554
555 let suffix_start = cursor.position.1;
556 let suffix = cursor.suffix();
557 let changed_trailing_excerpt = suffix.is_empty();
558 if !suffix.is_empty() {
559 let before = new_excerpts.summary().len();
560 new_excerpts.update_last(
561 |prev_excerpt| {
562 if !prev_excerpt.has_trailing_newline {
563 prev_excerpt.has_trailing_newline = true;
564 patch.push(Edit {
565 old: suffix_start..suffix_start,
566 new: before..before + MultiBufferOffset(1),
567 });
568 }
569 },
570 (),
571 );
572 }
573 new_excerpts.append(suffix, ());
574 drop(cursor);
575
576 snapshot.excerpts = new_excerpts;
577 snapshot.buffers.insert(
578 buffer_id,
579 BufferStateSnapshot {
580 path_key: path_key.clone(),
581 path_key_index,
582 buffer_snapshot: buffer_snapshot.clone(),
583 },
584 );
585
586 self.buffers.entry(buffer_id).or_insert_with(|| {
587 self.buffer_changed_since_sync.replace(true);
588 buffer.update(cx, |buffer, _| {
589 buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
590 });
591 BufferState {
592 _subscriptions: [
593 cx.observe(&buffer, |_, _, cx| cx.notify()),
594 cx.subscribe(&buffer, Self::on_buffer_event),
595 ],
596 buffer: buffer.clone(),
597 }
598 });
599
600 if changed_trailing_excerpt {
601 snapshot.trailing_excerpt_update_count += 1;
602 }
603
604 let edits = Self::sync_diff_transforms(
605 &mut snapshot,
606 patch.into_inner(),
607 DiffChangeKind::BufferEdited,
608 );
609 if !edits.is_empty() {
610 self.subscriptions.publish(edits);
611 }
612
613 cx.emit(Event::Edited {
614 edited_buffer: None,
615 is_local: true,
616 });
617 cx.emit(Event::BufferRangesUpdated {
618 buffer,
619 path_key: path_key.clone(),
620 ranges: new_ranges,
621 });
622 cx.notify();
623
624 added_new_excerpt
625 }
626
627 pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
628 let snapshot = self.sync_mut(cx);
629 let Some(path) = snapshot.path_for_buffer(buffer).cloned() else {
630 return;
631 };
632 self.remove_excerpts(path, cx);
633 }
634
635 pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context<Self>) {
636 assert_eq!(self.history.transaction_depth(), 0);
637 self.sync_mut(cx);
638
639 let mut snapshot = self.snapshot.get_mut();
640 let mut cursor = snapshot
641 .excerpts
642 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
643 let mut new_excerpts = SumTree::new(());
644 new_excerpts.append(cursor.slice(&path, Bias::Left), ());
645 let mut edit_start = cursor.position.1;
646 let mut buffer_id = None;
647 if let Some(excerpt) = cursor.item()
648 && excerpt.path_key == path
649 {
650 buffer_id = Some(excerpt.buffer_id);
651 }
652 cursor.seek(&path, Bias::Right);
653 let edit_end = cursor.position.1;
654 let suffix = cursor.suffix();
655 let changed_trailing_excerpt = suffix.is_empty();
656 new_excerpts.append(suffix, ());
657
658 if let Some(buffer_id) = buffer_id {
659 snapshot.buffers.remove(&buffer_id);
660 remove_diff_state(&mut snapshot.diffs, buffer_id);
661 self.buffers.remove(&buffer_id);
662 self.diffs.remove(&buffer_id);
663 cx.emit(Event::BuffersRemoved {
664 removed_buffer_ids: vec![buffer_id],
665 })
666 }
667 drop(cursor);
668 if changed_trailing_excerpt {
669 snapshot.trailing_excerpt_update_count += 1;
670 new_excerpts.update_last(
671 |excerpt| {
672 if excerpt.has_trailing_newline {
673 excerpt.has_trailing_newline = false;
674 edit_start.0.0 = edit_start
675 .0
676 .0
677 .checked_sub(1)
678 .expect("should have at least one excerpt");
679 }
680 },
681 (),
682 )
683 }
684
685 let edit = Edit {
686 old: edit_start..edit_end,
687 new: edit_start..edit_start,
688 };
689 snapshot.excerpts = new_excerpts;
690
691 let edits =
692 Self::sync_diff_transforms(&mut snapshot, vec![edit], DiffChangeKind::BufferEdited);
693 if !edits.is_empty() {
694 self.subscriptions.publish(edits);
695 }
696
697 cx.emit(Event::Edited {
698 edited_buffer: None,
699 is_local: true,
700 });
701 cx.notify();
702 }
703}