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