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
257 .get_index(path_index.0 as usize)
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.path_keys.get_index_of(path_key) {
357 return PathKeyIndex(existing as u64);
358 }
359
360 PathKeyIndex(
361 Arc::make_mut(&mut snapshot.path_keys)
362 .insert_full(path_key.clone())
363 .0 as u64,
364 )
365 }
366
367 pub fn update_path_excerpts(
368 &mut self,
369 path_key: PathKey,
370 buffer: Entity<Buffer>,
371 buffer_snapshot: &BufferSnapshot,
372 to_insert: &Vec<ExcerptRange<text::Anchor>>,
373 cx: &mut Context<Self>,
374 ) -> bool {
375 let path_key_index = self.get_or_create_path_key_index(&path_key);
376 if let Some(old_path_key) = self
377 .snapshot(cx)
378 .path_for_buffer(buffer_snapshot.remote_id())
379 && old_path_key != &path_key
380 {
381 self.remove_excerpts(old_path_key.clone(), cx);
382 }
383
384 if to_insert.len() == 0 {
385 self.remove_excerpts(path_key.clone(), cx);
386
387 return false;
388 }
389 assert_eq!(self.history.transaction_depth(), 0);
390 self.sync_mut(cx);
391
392 let buffer_id = buffer_snapshot.remote_id();
393
394 let mut snapshot = self.snapshot.get_mut();
395 let mut cursor = snapshot
396 .excerpts
397 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
398 let mut new_excerpts = SumTree::new(());
399
400 let new_ranges = to_insert.clone();
401 let mut to_insert = to_insert.iter().peekable();
402 let mut patch = Patch::empty();
403 let mut added_new_excerpt = false;
404
405 new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
406
407 // handle the case where the path key used to be associated
408 // with a different buffer by removing its excerpts.
409 if let Some(excerpt) = cursor.item()
410 && &excerpt.path_key == &path_key
411 && excerpt.buffer_id != buffer_id
412 {
413 let old_buffer_id = excerpt.buffer_id;
414 self.buffers.remove(&old_buffer_id);
415 snapshot.buffers.remove(&old_buffer_id);
416 remove_diff_state(&mut snapshot.diffs, old_buffer_id);
417 self.diffs.remove(&old_buffer_id);
418 let before = cursor.position.1;
419 cursor.seek_forward(&path_key, Bias::Right);
420 let after = cursor.position.1;
421 patch.push(Edit {
422 old: before..after,
423 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
424 });
425 cx.emit(Event::BuffersRemoved {
426 removed_buffer_ids: vec![old_buffer_id],
427 });
428 }
429
430 while let Some(excerpt) = cursor.item()
431 && excerpt.path_key == path_key
432 {
433 assert_eq!(excerpt.buffer_id, buffer_id);
434 let Some(next_excerpt) = to_insert.peek() else {
435 break;
436 };
437 if &excerpt.range == *next_excerpt {
438 let before = new_excerpts.summary().len();
439 new_excerpts.update_last(
440 |prev_excerpt| {
441 if !prev_excerpt.has_trailing_newline {
442 prev_excerpt.has_trailing_newline = true;
443 patch.push(Edit {
444 old: cursor.position.1..cursor.position.1,
445 new: before..before + MultiBufferOffset(1),
446 });
447 }
448 },
449 (),
450 );
451 new_excerpts.push(excerpt.clone(), ());
452 to_insert.next();
453 cursor.next();
454 continue;
455 }
456
457 if excerpt
458 .range
459 .context
460 .start
461 .cmp(&next_excerpt.context.start, &buffer_snapshot)
462 .is_le()
463 {
464 // remove old excerpt
465 let before = cursor.position.1;
466 cursor.next();
467 let after = cursor.position.1;
468 patch.push(Edit {
469 old: before..after,
470 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
471 });
472 } else {
473 // insert new excerpt
474 let next_excerpt = to_insert.next().unwrap();
475 added_new_excerpt = true;
476 let before = new_excerpts.summary().len();
477 new_excerpts.update_last(
478 |prev_excerpt| {
479 prev_excerpt.has_trailing_newline = true;
480 },
481 (),
482 );
483 new_excerpts.push(
484 Excerpt::new(
485 path_key.clone(),
486 path_key_index,
487 &buffer_snapshot,
488 next_excerpt.clone(),
489 false,
490 ),
491 (),
492 );
493 let after = new_excerpts.summary().len();
494 patch.push_maybe_empty(Edit {
495 old: cursor.position.1..cursor.position.1,
496 new: before..after,
497 });
498 }
499 }
500
501 // remove any further trailing excerpts
502 let mut before = cursor.position.1;
503 cursor.seek_forward(&path_key, Bias::Right);
504 let after = cursor.position.1;
505 // if we removed the previous last excerpt, remove the trailing newline from the new last excerpt
506 if cursor.item().is_none() && to_insert.peek().is_none() {
507 new_excerpts.update_last(
508 |excerpt| {
509 if excerpt.has_trailing_newline {
510 before.0.0 = before
511 .0
512 .0
513 .checked_sub(1)
514 .expect("should have preceding excerpt");
515 excerpt.has_trailing_newline = false;
516 }
517 },
518 (),
519 );
520 }
521 patch.push(Edit {
522 old: before..after,
523 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
524 });
525
526 while let Some(next_excerpt) = to_insert.next() {
527 added_new_excerpt = true;
528 let before = new_excerpts.summary().len();
529 new_excerpts.update_last(
530 |prev_excerpt| {
531 prev_excerpt.has_trailing_newline = true;
532 },
533 (),
534 );
535 new_excerpts.push(
536 Excerpt::new(
537 path_key.clone(),
538 path_key_index,
539 &buffer_snapshot,
540 next_excerpt.clone(),
541 false,
542 ),
543 (),
544 );
545 let after = new_excerpts.summary().len();
546 patch.push_maybe_empty(Edit {
547 old: cursor.position.1..cursor.position.1,
548 new: before..after,
549 });
550 }
551
552 let suffix_start = cursor.position.1;
553 let suffix = cursor.suffix();
554 let changed_trailing_excerpt = suffix.is_empty();
555 if !suffix.is_empty() {
556 let before = new_excerpts.summary().len();
557 new_excerpts.update_last(
558 |prev_excerpt| {
559 if !prev_excerpt.has_trailing_newline {
560 prev_excerpt.has_trailing_newline = true;
561 patch.push(Edit {
562 old: suffix_start..suffix_start,
563 new: before..before + MultiBufferOffset(1),
564 });
565 }
566 },
567 (),
568 );
569 }
570 new_excerpts.append(suffix, ());
571 drop(cursor);
572
573 snapshot.excerpts = new_excerpts;
574 snapshot.buffers.insert(
575 buffer_id,
576 BufferStateSnapshot {
577 path_key: path_key.clone(),
578 path_key_index,
579 buffer_snapshot: buffer_snapshot.clone(),
580 },
581 );
582
583 self.buffers.entry(buffer_id).or_insert_with(|| {
584 self.buffer_changed_since_sync.replace(true);
585 buffer.update(cx, |buffer, _| {
586 buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
587 });
588 BufferState {
589 _subscriptions: [
590 cx.observe(&buffer, |_, _, cx| cx.notify()),
591 cx.subscribe(&buffer, Self::on_buffer_event),
592 ],
593 buffer: buffer.clone(),
594 }
595 });
596
597 if changed_trailing_excerpt {
598 snapshot.trailing_excerpt_update_count += 1;
599 }
600
601 let edits = Self::sync_diff_transforms(
602 &mut snapshot,
603 patch.into_inner(),
604 DiffChangeKind::BufferEdited,
605 );
606 if !edits.is_empty() {
607 self.subscriptions.publish(edits);
608 }
609
610 cx.emit(Event::Edited {
611 edited_buffer: None,
612 is_local: true,
613 });
614 cx.emit(Event::BufferRangesUpdated {
615 buffer,
616 path_key: path_key.clone(),
617 ranges: new_ranges,
618 });
619 cx.notify();
620
621 added_new_excerpt
622 }
623
624 pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
625 let snapshot = self.sync_mut(cx);
626 let Some(path) = snapshot.path_for_buffer(buffer).cloned() else {
627 return;
628 };
629 self.remove_excerpts(path, cx);
630 }
631
632 pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context<Self>) {
633 assert_eq!(self.history.transaction_depth(), 0);
634 self.sync_mut(cx);
635
636 let mut snapshot = self.snapshot.get_mut();
637 let mut cursor = snapshot
638 .excerpts
639 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
640 let mut new_excerpts = SumTree::new(());
641 new_excerpts.append(cursor.slice(&path, Bias::Left), ());
642 let mut edit_start = cursor.position.1;
643 let mut buffer_id = None;
644 if let Some(excerpt) = cursor.item()
645 && excerpt.path_key == path
646 {
647 buffer_id = Some(excerpt.buffer_id);
648 }
649 cursor.seek(&path, Bias::Right);
650 let edit_end = cursor.position.1;
651 let suffix = cursor.suffix();
652 let changed_trailing_excerpt = suffix.is_empty();
653 new_excerpts.append(suffix, ());
654
655 if let Some(buffer_id) = buffer_id {
656 snapshot.buffers.remove(&buffer_id);
657 remove_diff_state(&mut snapshot.diffs, buffer_id);
658 self.buffers.remove(&buffer_id);
659 self.diffs.remove(&buffer_id);
660 cx.emit(Event::BuffersRemoved {
661 removed_buffer_ids: vec![buffer_id],
662 })
663 }
664 drop(cursor);
665 if changed_trailing_excerpt {
666 snapshot.trailing_excerpt_update_count += 1;
667 new_excerpts.update_last(
668 |excerpt| {
669 if excerpt.has_trailing_newline {
670 excerpt.has_trailing_newline = false;
671 edit_start.0.0 = edit_start
672 .0
673 .0
674 .checked_sub(1)
675 .expect("should have at least one excerpt");
676 }
677 },
678 (),
679 )
680 }
681
682 let edit = Edit {
683 old: edit_start..edit_end,
684 new: edit_start..edit_start,
685 };
686 snapshot.excerpts = new_excerpts;
687
688 let edits =
689 Self::sync_diff_transforms(&mut snapshot, vec![edit], DiffChangeKind::BufferEdited);
690 if !edits.is_empty() {
691 self.subscriptions.publish(edits);
692 }
693
694 cx.emit(Event::Edited {
695 edited_buffer: None,
696 is_local: true,
697 });
698 cx.notify();
699 }
700}