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