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 let excerpt_insertion_result = 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 /// Expand each new merged range to encompass any overlapping existing
371 /// excerpt, then re-merge. This turns "partial overlap where the union
372 /// equals the existing range" into an exact match, avoiding unnecessary
373 /// remove+insert churn that floods the wrap map with edits.
374 fn expand_new_ranges_to_existing(
375 &self,
376 path: &PathKey,
377 buffer_snapshot: &BufferSnapshot,
378 mut new: Vec<ExcerptRange<Point>>,
379 counts: Vec<usize>,
380 cx: &App,
381 ) -> (Vec<ExcerptRange<Point>>, Vec<usize>) {
382 let existing = self.excerpts_by_path.get(path).cloned().unwrap_or_default();
383 if existing.is_empty() || new.is_empty() {
384 return (new, counts);
385 }
386
387 let snapshot = self.snapshot(cx);
388 let buffer_id = buffer_snapshot.remote_id();
389 let existing_ranges: Vec<Range<Point>> = existing
390 .iter()
391 .filter_map(|&id| {
392 let excerpt = snapshot.excerpt(id)?;
393 (excerpt.buffer_id == buffer_id)
394 .then(|| excerpt.range.context.to_point(buffer_snapshot))
395 })
396 .collect();
397
398 let mut changed = false;
399 for new_range in &mut new {
400 for existing_range in &existing_ranges {
401 if new_range.context.start <= existing_range.end
402 && new_range.context.end >= existing_range.start
403 {
404 let expanded_start = new_range.context.start.min(existing_range.start);
405 let expanded_end = new_range.context.end.max(existing_range.end);
406 if expanded_start != new_range.context.start
407 || expanded_end != new_range.context.end
408 {
409 new_range.context.start = expanded_start;
410 new_range.context.end = expanded_end;
411 changed = true;
412 }
413 }
414 }
415 }
416
417 if !changed {
418 return (new, counts);
419 }
420
421 let mut result_ranges: Vec<ExcerptRange<Point>> = Vec::new();
422 let mut result_counts: Vec<usize> = Vec::new();
423 for (range, count) in new.into_iter().zip(counts) {
424 if let Some(last) = result_ranges.last_mut() {
425 if last.context.end >= range.context.start
426 || last.context.end.row + 1 == range.context.start.row
427 {
428 last.context.end = last.context.end.max(range.context.end);
429 *result_counts.last_mut().unwrap() += count;
430 continue;
431 }
432 }
433 result_ranges.push(range);
434 result_counts.push(count);
435 }
436
437 (result_ranges, result_counts)
438 }
439
440 fn update_path_excerpts(
441 &mut self,
442 path_key: PathKey,
443 buffer: Entity<Buffer>,
444 buffer_snapshot: &BufferSnapshot,
445 to_insert: &Vec<ExcerptRange<text::Anchor>>,
446 cx: &mut Context<Self>,
447 ) -> bool {
448 let path_key_index = self.get_or_create_path_key_index(&path_key);
449 if let Some(old_path_key) = self
450 .snapshot(cx)
451 .path_for_buffer(buffer_snapshot.remote_id())
452 && old_path_key != &path_key
453 {
454 self.remove_excerpts(old_path_key.clone(), cx);
455 }
456
457 if to_insert.len() == 0 {
458 self.remove_excerpts(path_key.clone(), cx);
459
460 return false;
461 }
462 assert_eq!(self.history.transaction_depth(), 0);
463 self.sync_mut(cx);
464
465 let buffer_id = buffer_snapshot.remote_id();
466
467 let mut snapshot = self.snapshot.get_mut();
468 let mut cursor = snapshot
469 .excerpts
470 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
471 let mut new_excerpts = SumTree::new(());
472
473 let new_ranges = to_insert.clone();
474 let mut to_insert = to_insert.iter().peekable();
475 let mut patch = Patch::empty();
476 let mut added_new_excerpt = false;
477
478 new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
479
480 // handle the case where the path key used to be associated
481 // with a different buffer by removing its excerpts.
482 if let Some(excerpt) = cursor.item()
483 && &excerpt.path_key == &path_key
484 && excerpt.buffer_id != buffer_id
485 {
486 let old_buffer_id = excerpt.buffer_id;
487 self.buffers.remove(&old_buffer_id);
488 snapshot.buffers.remove(&old_buffer_id);
489 remove_diff_state(&mut snapshot.diffs, old_buffer_id);
490 self.diffs.remove(&old_buffer_id);
491 let before = cursor.position.1;
492 cursor.seek_forward(&path_key, Bias::Right);
493 let after = cursor.position.1;
494 patch.push(Edit {
495 old: before..after,
496 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
497 });
498 cx.emit(Event::BuffersRemoved {
499 removed_buffer_ids: vec![old_buffer_id],
500 });
501 }
502
503 while let Some(excerpt) = cursor.item()
504 && excerpt.path_key == path_key
505 {
506 assert_eq!(excerpt.buffer_id, buffer_id);
507 let Some(next_excerpt) = to_insert.peek() else {
508 break;
509 };
510 if &excerpt.range == *next_excerpt {
511 let before = new_excerpts.summary().len();
512 new_excerpts.update_last(
513 |prev_excerpt| {
514 if !prev_excerpt.has_trailing_newline {
515 prev_excerpt.has_trailing_newline = true;
516 patch.push(Edit {
517 old: cursor.position.1..cursor.position.1,
518 new: before..before + MultiBufferOffset(1),
519 });
520 }
521 },
522 (),
523 );
524 new_excerpts.push(excerpt.clone(), ());
525 to_insert.next();
526 cursor.next();
527 continue;
528 }
529
530 if excerpt
531 .range
532 .context
533 .start
534 .cmp(&next_excerpt.context.start, &buffer_snapshot)
535 .is_le()
536 {
537 // remove old excerpt
538 let before = cursor.position.1;
539 cursor.next();
540 let after = cursor.position.1;
541 patch.push(Edit {
542 old: before..after,
543 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
544 });
545 } else {
546 // insert new excerpt
547 let next_excerpt = to_insert.next().unwrap();
548 added_new_excerpt = true;
549 let before = new_excerpts.summary().len();
550 new_excerpts.update_last(
551 |prev_excerpt| {
552 prev_excerpt.has_trailing_newline = true;
553 },
554 (),
555 );
556 new_excerpts.push(
557 Excerpt::new(
558 path_key.clone(),
559 path_key_index,
560 &buffer_snapshot,
561 next_excerpt.clone(),
562 false,
563 ),
564 (),
565 );
566 let after = new_excerpts.summary().len();
567 patch.push_maybe_empty(Edit {
568 old: cursor.position.1..cursor.position.1,
569 new: before..after,
570 });
571 }
572 }
573
574 // remove any further trailing excerpts
575 let mut before = cursor.position.1;
576 cursor.seek_forward(&path_key, Bias::Right);
577 let after = cursor.position.1;
578 // if we removed the previous last excerpt, remove the trailing newline from the new last excerpt
579 if cursor.item().is_none() && to_insert.peek().is_none() {
580 new_excerpts.update_last(
581 |excerpt| {
582 if excerpt.has_trailing_newline {
583 before.0.0 = before
584 .0
585 .0
586 .checked_sub(1)
587 .expect("should have preceding excerpt");
588 excerpt.has_trailing_newline = false;
589 }
590 },
591 (),
592 );
593 }
594 patch.push(Edit {
595 old: before..after,
596 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
597 });
598
599 while let Some(next_excerpt) = to_insert.next() {
600 added_new_excerpt = true;
601 let before = new_excerpts.summary().len();
602 new_excerpts.update_last(
603 |prev_excerpt| {
604 prev_excerpt.has_trailing_newline = true;
605 },
606 (),
607 );
608 new_excerpts.push(
609 Excerpt::new(
610 path_key.clone(),
611 path_key_index,
612 &buffer_snapshot,
613 next_excerpt.clone(),
614 false,
615 ),
616 (),
617 );
618 let after = new_excerpts.summary().len();
619 patch.push_maybe_empty(Edit {
620 old: cursor.position.1..cursor.position.1,
621 new: before..after,
622 });
623 }
624
625 let suffix_start = cursor.position.1;
626 let suffix = cursor.suffix();
627 let changed_trailing_excerpt = suffix.is_empty();
628 if !suffix.is_empty() {
629 let before = new_excerpts.summary().len();
630 new_excerpts.update_last(
631 |prev_excerpt| {
632 if !prev_excerpt.has_trailing_newline {
633 prev_excerpt.has_trailing_newline = true;
634 patch.push(Edit {
635 old: suffix_start..suffix_start,
636 new: before..before + MultiBufferOffset(1),
637 });
638 }
639 },
640 (),
641 );
642 }
643 new_excerpts.append(suffix, ());
644 drop(cursor);
645
646 snapshot.excerpts = new_excerpts;
647 snapshot.buffers.insert(
648 buffer_id,
649 BufferStateSnapshot {
650 path_key: path_key.clone(),
651 path_key_index,
652 buffer_snapshot: buffer_snapshot.clone(),
653 },
654 );
655
656 self.buffers.entry(buffer_id).or_insert_with(|| {
657 self.buffer_changed_since_sync.replace(true);
658 buffer.update(cx, |buffer, _| {
659 buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
660 });
661 BufferState {
662 _subscriptions: [
663 cx.observe(&buffer, |_, _, cx| cx.notify()),
664 cx.subscribe(&buffer, Self::on_buffer_event),
665 ],
666 buffer: buffer.clone(),
667 }
668 });
669
670 if changed_trailing_excerpt {
671 snapshot.trailing_excerpt_update_count += 1;
672 }
673
674 let edits = Self::sync_diff_transforms(
675 &mut snapshot,
676 patch.into_inner(),
677 DiffChangeKind::BufferEdited,
678 );
679 if !edits.is_empty() {
680 self.subscriptions.publish(edits);
681 }
682
683 cx.emit(Event::Edited {
684 edited_buffer: None,
685 is_local: true,
686 });
687 cx.emit(Event::BufferRangesUpdated {
688 buffer,
689 path_key: path_key.clone(),
690 ranges: new_ranges,
691 });
692 cx.notify();
693
694 added_new_excerpt
695 }
696
697 pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
698 let snapshot = self.sync_mut(cx);
699 let Some(path) = snapshot.path_for_buffer(buffer).cloned() else {
700 return;
701 };
702 self.remove_excerpts(path, cx);
703 }
704
705 pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context<Self>) {
706 assert_eq!(self.history.transaction_depth(), 0);
707 self.sync_mut(cx);
708
709 let mut snapshot = self.snapshot.get_mut();
710 let mut cursor = snapshot
711 .excerpts
712 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
713 let mut new_excerpts = SumTree::new(());
714 new_excerpts.append(cursor.slice(&path, Bias::Left), ());
715 let mut edit_start = cursor.position.1;
716 let mut buffer_id = None;
717 if let Some(excerpt) = cursor.item()
718 && excerpt.path_key == path
719 {
720 buffer_id = Some(excerpt.buffer_id);
721 }
722 cursor.seek(&path, Bias::Right);
723 let edit_end = cursor.position.1;
724 let suffix = cursor.suffix();
725 let changed_trailing_excerpt = suffix.is_empty();
726 new_excerpts.append(suffix, ());
727
728 if let Some(buffer_id) = buffer_id {
729 snapshot.buffers.remove(&buffer_id);
730 remove_diff_state(&mut snapshot.diffs, buffer_id);
731 self.buffers.remove(&buffer_id);
732 self.diffs.remove(&buffer_id);
733 cx.emit(Event::BuffersRemoved {
734 removed_buffer_ids: vec![buffer_id],
735 })
736 }
737 drop(cursor);
738 if changed_trailing_excerpt {
739 snapshot.trailing_excerpt_update_count += 1;
740 new_excerpts.update_last(
741 |excerpt| {
742 if excerpt.has_trailing_newline {
743 excerpt.has_trailing_newline = false;
744 edit_start.0.0 = edit_start
745 .0
746 .0
747 .checked_sub(1)
748 .expect("should have at least one excerpt");
749 }
750 },
751 (),
752 )
753 }
754
755 let edit = Edit {
756 old: edit_start..edit_end,
757 new: edit_start..edit_start,
758 };
759 snapshot.excerpts = new_excerpts;
760
761 let edits =
762 Self::sync_diff_transforms(&mut snapshot, vec![edit], DiffChangeKind::BufferEdited);
763 if !edits.is_empty() {
764 self.subscriptions.publish(edits);
765 }
766
767 cx.emit(Event::Edited {
768 edited_buffer: None,
769 is_local: true,
770 });
771 cx.notify();
772 }
773}