wip fixing tests

Cole Miller and Conrad Irwin created

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/multi_buffer/src/anchor.rs             |   3 
crates/multi_buffer/src/multi_buffer.rs       |  82 +++++++----
crates/multi_buffer/src/multi_buffer_tests.rs | 145 +++++++++++---------
crates/multi_buffer/src/path_key.rs           |  66 ++++++--
crates/multi_buffer/src/transaction.rs        |   5 
crates/text/src/patch.rs                      |   3 
crates/text/src/text.rs                       |   2 
7 files changed, 184 insertions(+), 122 deletions(-)

Detailed changes

crates/multi_buffer/src/anchor.rs 🔗

@@ -14,7 +14,7 @@ use text::BufferId;
 
 /// A multibuffer anchor derived from an anchor into a specific excerpted buffer.
 #[derive(Clone, Copy, Eq, PartialEq, Hash)]
-pub(crate) struct ExcerptAnchor {
+pub struct ExcerptAnchor {
     /// The position within the excerpt's underlying buffer. This is a stable
     /// reference that remains valid as the buffer text is edited.
     pub(crate) timestamp: clock::Lamport,
@@ -190,6 +190,7 @@ impl ExcerptAnchor {
         }
     }
 
+    #[track_caller]
     pub(crate) fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
         let Some(buffer_id) = text_anchor.buffer_id else {
             panic!("text_anchor must have a buffer_id");

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -101,6 +101,12 @@ pub struct ExcerptInfo {
     range: ExcerptRange<text::Anchor>,
 }
 
+impl ExcerptInfo {
+    pub fn end_anchor(&self) -> Anchor {
+        Anchor::in_buffer(self.path_key_index, self.range.context.end)
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Event {
     BufferUpdated {
@@ -624,7 +630,6 @@ struct BufferStateSnapshot {
 pub struct MultiBufferSnapshot {
     excerpts: SumTree<Excerpt>,
     buffers: TreeMap<BufferId, BufferStateSnapshot>,
-    path_keys_by_buffer: TreeMap<BufferId, PathKey>,
     path_keys_by_index: TreeMap<PathKeyIndex, PathKey>,
     diffs: TreeMap<BufferId, DiffStateSnapshot>,
     diff_transforms: SumTree<DiffTransform>,
@@ -1749,7 +1754,6 @@ impl MultiBuffer {
             show_deleted_hunks: _,
             use_extended_diff_range: _,
             show_headers: _,
-            path_keys_by_buffer: _,
             path_keys_by_index: _,
             buffers,
         } = self.snapshot.get_mut();
@@ -1800,12 +1804,13 @@ impl MultiBuffer {
             .cursor::<Dimensions<ExcerptPoint, OutputDimension<Point>>>(());
         diff_transforms.next();
         let mut result = Vec::new();
-        let Some(path_key) = snapshot.path_keys_by_buffer.get(&buffer_id) else {
+        let Some(buffer_state) = snapshot.buffers.get(&buffer_id) else {
             return result;
         };
+        let path_key = buffer_state.path_key.clone();
 
         while let Some(excerpt) = excerpts.item()
-            && &excerpt.path_key == path_key
+            && excerpt.path_key == path_key
         {
             let excerpt_start = excerpts.start().1;
             let excerpt_end = excerpt_start + excerpt.text_summary.lines;
@@ -2456,7 +2461,7 @@ impl MultiBuffer {
         let snapshot = self.snapshot.borrow().clone();
         let ranges = ranges.iter().map(move |range| {
             let excerpt_end = snapshot
-                .excerpt_at(range.end)
+                .excerpt_containing(range.end..range.end)
                 .map(|excerpt| excerpt.end_anchor().into())
                 .unwrap_or(Anchor::Max);
             let range = range.to_point(&snapshot);
@@ -2500,6 +2505,7 @@ impl MultiBuffer {
             return snapshot;
         }
         let edits = Self::sync_from_buffer_changes(snapshot, &self.buffers, &self.diffs, cx);
+        dbg!(&edits);
 
         if !edits.is_empty() {
             self.subscriptions.publish(edits);
@@ -2518,7 +2524,6 @@ impl MultiBuffer {
             excerpts,
             diffs: buffer_diff,
             buffers: buffer_snapshots,
-            path_keys_by_buffer,
             path_keys_by_index: _,
             diff_transforms: _,
             non_text_state_update_count,
@@ -2579,13 +2584,15 @@ impl MultiBuffer {
 
             let buffer_edited =
                 current_version.changed_since(last_snapshot.buffer_snapshot.version());
+            dbg!(
+                &current_version,
+                last_snapshot.buffer_snapshot.version().clone()
+            );
             let buffer_non_text_state_updated = non_text_state_update_count
                 > last_snapshot.buffer_snapshot.non_text_state_update_count();
-            if (buffer_edited || buffer_non_text_state_updated)
-                && let Some(path_key) = path_keys_by_buffer.get(&buffer.remote_id())
-            {
+            if buffer_edited || buffer_non_text_state_updated {
                 paths_to_edit.push((
-                    path_key.clone(),
+                    last_snapshot.path_key.clone(),
                     buffer_state.buffer.clone(),
                     if buffer_edited {
                         Some(last_snapshot.buffer_snapshot.version().clone())
@@ -2617,6 +2624,7 @@ impl MultiBuffer {
         let mut cursor = excerpts.cursor::<ExcerptSummary>(());
 
         for (path, buffer, prev_version) in paths_to_edit {
+            dbg!(&prev_version, buffer.read(cx).version());
             new_excerpts.append(cursor.slice(&path, Bias::Left), ());
             let old_excerpt = cursor.item().unwrap();
             let buffer = buffer.read(cx);
@@ -2635,9 +2643,10 @@ impl MultiBuffer {
                     buffer
                         .edits_since_in_range::<usize>(
                             &prev_version,
-                            old_excerpt.range.context.clone(),
+                            dbg!(old_excerpt.range.context.clone()),
                         )
                         .map(|edit| {
+                            dbg!(&edit);
                             let excerpt_old_start = cursor.start().len();
                             let excerpt_new_start =
                                 ExcerptDimension(new_excerpts.summary().text.len);
@@ -2679,6 +2688,7 @@ impl MultiBuffer {
         excerpt_edits: Vec<text::Edit<ExcerptOffset>>,
         change_kind: DiffChangeKind,
     ) -> Vec<Edit<MultiBufferOffset>> {
+        dbg!(&excerpt_edits);
         if excerpt_edits.is_empty() {
             return vec![];
         }
@@ -3121,7 +3131,7 @@ impl MultiBuffer {
     pub fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
         let snapshot = self.snapshot(cx);
         let excerpt_end = snapshot
-            .excerpt_at(range.end)
+            .excerpt_containing(range.end..range.end)
             .map(|excerpt| excerpt.end_anchor().into())
             .unwrap_or(Anchor::Max);
         let point_range = range.to_point(&snapshot);
@@ -3316,10 +3326,11 @@ impl MultiBuffer {
                 let path_key = self
                     .snapshot
                     .borrow()
-                    .path_keys_by_buffer
+                    .buffers
                     .get(&buffer_ids.choose(rng).unwrap())
-                    .cloned()
-                    .unwrap();
+                    .unwrap()
+                    .path_key
+                    .clone();
                 log::info!("Removing excerpts {:?}", path_key);
                 self.remove_excerpts_for_path(path_key, cx);
             }
@@ -3588,7 +3599,8 @@ impl MultiBufferSnapshot {
             let mut same_buffer_anchors =
                 anchors.peeking_take_while(|a| a.buffer_id.is_some_and(|b| buffer_id == b));
 
-            if let Some(path) = self.path_keys_by_buffer.get(&buffer_id) {
+            if let Some(buffer) = self.buffers.get(&buffer_id) {
+                let path = &buffer.path_key;
                 let Some(mut next) = same_buffer_anchors.next() else {
                     continue 'anchors;
                 };
@@ -3664,6 +3676,7 @@ impl MultiBufferSnapshot {
         })
     }
 
+    // todo!() can we make this not RangeBounds
     pub fn range_to_buffer_ranges<R, T>(
         &self,
         range: R,
@@ -4219,6 +4232,7 @@ impl MultiBufferSnapshot {
     }
 
     pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> {
+        dbg!(self.diff_transforms.is_empty());
         let mut cursor = self.cursor::<Point, Point>();
         cursor.seek(&Point::new(start_row.0, 0));
         let mut result = MultiBufferRows {
@@ -5184,12 +5198,13 @@ impl MultiBufferSnapshot {
     }
 
     fn excerpts_for_buffer(&self, buffer_id: BufferId) -> impl Iterator<Item = &Excerpt> {
-        if let Some(path_key) = self.path_keys_by_buffer.get(&buffer_id) {
+        if let Some(buffer_state) = self.buffers.get(&buffer_id) {
+            let path_key = buffer_state.path_key.clone();
             let mut cursor = self.excerpts.cursor::<PathKey>(());
-            cursor.seek_forward(path_key, Bias::Left);
+            cursor.seek_forward(&path_key, Bias::Left);
             Some(iter::from_fn(move || {
                 let excerpt = cursor.item()?;
-                if &excerpt.path_key != path_key {
+                if excerpt.path_key != path_key {
                     return None;
                 }
                 cursor.next();
@@ -6262,7 +6277,7 @@ impl MultiBufferSnapshot {
     }
 
     pub fn path_for_buffer(&self, buffer_id: BufferId) -> Option<&PathKey> {
-        self.path_keys_by_buffer.get(&buffer_id)
+        Some(&self.buffers.get(&buffer_id)?.path_key)
     }
 
     pub fn path_key_index_for_buffer(&self, buffer_id: BufferId) -> Option<PathKeyIndex> {
@@ -6271,8 +6286,8 @@ impl MultiBufferSnapshot {
     }
 
     pub fn first_excerpt_for_buffer(&self, buffer_id: BufferId) -> Option<&Excerpt> {
-        let path_key = self.path_keys_by_buffer.get(&buffer_id)?;
-        self.first_excerpt_for_path(&path_key)
+        let path_key = &self.buffers.get(&buffer_id)?.path_key;
+        self.first_excerpt_for_path(path_key)
     }
 
     pub fn first_excerpt_for_path(&self, path_key: &PathKey) -> Option<&Excerpt> {
@@ -6283,7 +6298,7 @@ impl MultiBufferSnapshot {
     }
 
     pub fn buffer_for_id(&self, id: BufferId) -> Option<&BufferSnapshot> {
-        self.buffer_for_path(self.path_keys_by_buffer.get(&id)?)
+        self.buffer_for_path(self.path_for_buffer(id)?)
     }
 
     fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option<PathKey> {
@@ -6327,11 +6342,6 @@ impl MultiBufferSnapshot {
         // }
     }
 
-    // todo!() sort out excerpt_containing etc.
-    fn excerpt_at(&self, _position: impl ToOffset) -> Option<&Excerpt> {
-        todo!()
-    }
-
     /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
     pub fn excerpt_containing<T: ToOffset>(
         &self,
@@ -6545,7 +6555,8 @@ impl MultiBufferSnapshot {
     fn check_invariants(&self) {
         let excerpts = self.excerpts.items(());
 
-        let all_buffer_path_keys = HashSet::from_iter(self.path_keys_by_buffer.values().cloned());
+        let all_buffer_path_keys =
+            HashSet::from_iter(self.buffers.values().map(|b| b.path_key.clone()));
         let all_excerpt_path_keys = HashSet::from_iter(excerpts.iter().map(|e| e.path_key.clone()));
 
         for (ix, excerpt) in excerpts.iter().enumerate() {
@@ -6644,7 +6655,8 @@ where
         }
 
         self.excerpts.seek(&excerpt_position, Bias::Right);
-        if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() {
+        if self.excerpts.item().is_none() && dbg!(excerpt_position) == *self.excerpts.start() {
+            dbg!("NONE");
             self.excerpts.prev();
         }
     }
@@ -6813,6 +6825,7 @@ where
 
     fn build_region(&self) -> Option<MultiBufferRegion<'a, MBD, BD>> {
         let excerpt = self.excerpts.item()?;
+        dbg!("GOT EXCERPT");
         match self.diff_transforms.item()? {
             DiffTransform::DeletedHunk {
                 buffer_id,
@@ -6945,6 +6958,7 @@ impl Excerpt {
         range: ExcerptRange<text::Anchor>,
         has_trailing_newline: bool,
     ) -> Self {
+        dbg!(&path_key, &range, has_trailing_newline);
         Excerpt {
             path_key,
             path_key_index,
@@ -7059,11 +7073,11 @@ impl Excerpt {
     }
 
     fn start_anchor(&self) -> ExcerptAnchor {
-        todo!()
+        ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.start)
     }
 
     fn end_anchor(&self) -> ExcerptAnchor {
-        todo!()
+        ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.end)
     }
 }
 
@@ -7604,11 +7618,13 @@ impl Iterator for MultiBufferRows<'_> {
         }
 
         let mut region = self.cursor.region()?.clone();
+        dbg!("REGION");
         while self.point >= region.range.end {
             self.cursor.next();
             if let Some(next_region) = self.cursor.region() {
                 region = next_region.clone();
-            } else if self.point == self.cursor.diff_transforms.end().output_dimension.0 {
+            } else if dbg!(self.point) == dbg!(self.cursor.diff_transforms.end().output_dimension.0)
+            {
                 let multibuffer_row = MultiBufferRow(self.point.row);
                 let last_excerpt = self
                     .cursor

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -1109,6 +1109,7 @@ fn test_singleton_multibuffer_anchors(cx: &mut App) {
         buffer.edit([(0..0, "X")], None, cx);
         buffer.edit([(5..5, "Y")], None, cx);
     });
+    dbg!("-------");
     let new_snapshot = multibuffer.read(cx).snapshot(cx);
 
     assert_eq!(old_snapshot.text(), "abcd");
@@ -2279,7 +2280,7 @@ struct ReferenceExcerpt {
     range: Range<text::Anchor>,
 }
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 struct ReferenceRegion {
     buffer_id: Option<BufferId>,
     range: Range<usize>,
@@ -2293,7 +2294,7 @@ impl ReferenceExcerpt {
         ExcerptInfo {
             path_key_index: self.path_key_index,
             buffer_id: self.buffer.read(cx).remote_id(),
-            range: self.range.clone(),
+            range: ExcerptRange::new(self.range.clone()),
         }
     }
 }
@@ -2321,32 +2322,6 @@ impl ReferenceMultibuffer {
         }
     }
 
-    fn remove_excerpt(&mut self, info: ExcerptInfo, cx: &App) {
-        let ix = self
-            .excerpts
-            .iter()
-            .position(|excerpt| excerpt.info(cx) == info)
-            .unwrap();
-        let excerpt = self.excerpts.remove(ix);
-        let buffer = excerpt.buffer.read(cx);
-        let buffer_id = buffer.remote_id();
-        log::info!(
-            "Removing excerpt {}: {:?}",
-            ix,
-            buffer
-                .text_for_range(excerpt.range.to_offset(buffer))
-                .collect::<String>(),
-        );
-        if !self
-            .excerpts
-            .iter()
-            .any(|excerpt| excerpt.buffer.read(cx).remote_id() == buffer_id)
-        {
-            self.diffs.remove(&buffer_id);
-            self.inverted_diffs.remove(&buffer_id);
-        }
-    }
-
     fn set_excerpts(
         &mut self,
         path_key: PathKey,
@@ -2631,7 +2606,7 @@ impl ReferenceMultibuffer {
                     .iter()
                     .position(|region| region.range.contains(&ix))
                     .map_or(RowInfo::default(), |region_ix| {
-                        let region = &regions[region_ix];
+                        let region = regions[region_ix].clone();
                         let buffer_row = region.buffer_range.as_ref().map(|buffer_range| {
                             buffer_range.start.row
                                 + text[region.range.start..ix].matches('\n').count() as u32
@@ -2639,7 +2614,7 @@ impl ReferenceMultibuffer {
                         let main_buffer = self
                             .excerpts
                             .iter()
-                            .find(|e| e.info(cx) == region.excerpt_info.unwrap())
+                            .find(|e| e.info(cx) == region.excerpt_info.clone().unwrap())
                             .map(|e| e.buffer.clone());
                         let is_excerpt_start = region_ix == 0
                             || &regions[region_ix - 1].excerpt_info != &region.excerpt_info
@@ -2811,9 +2786,9 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
         let mut last_end = None;
         let mut seen_ranges = Vec::default();
 
-        for (_, buf, range) in snapshot.excerpts() {
-            let start = range.context.start.to_point(buf);
-            let end = range.context.end.to_point(buf);
+        for (buf, info) in snapshot.excerpts() {
+            let start = info.range.context.start.to_point(buf);
+            let end = info.range.context.end.to_point(buf);
             seen_ranges.push(start..end);
 
             if let Some(last_end) = last_end.take() {
@@ -3228,23 +3203,30 @@ fn check_multibuffer(
             .unwrap()
             + 1
     );
-    let reference_ranges = reference
-        .excerpts
-        .iter()
-        .map(|excerpt| {
-            (
-                excerpt.id,
-                excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()),
-            )
-        })
-        .collect::<HashMap<_, _>>();
+    // let reference_ranges = reference
+    //     .excerpts
+    //     .iter()
+    //     .map(|excerpt| {
+    //         (
+    //             excerpt.info(cx),
+    //             excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()),
+    //         )
+    //     })
+    //     .collect::<Vec<_>>();
     for i in 0..snapshot.len().0 {
+        // todo!() this seems not useful
         let excerpt = snapshot
             .excerpt_containing(MultiBufferOffset(i)..MultiBufferOffset(i))
             .unwrap();
+        let reference_range = reference
+            .excerpts
+            .iter()
+            .find(|reference_excerpt| reference_excerpt.info(cx) == excerpt.excerpt.info())
+            .map(|excerpt| excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()))
+            .expect("corresponding excerpt should exist in reference multibuffer");
         assert_eq!(
-            excerpt.buffer_range().start.0..excerpt.buffer_range().end.0,
-            reference_ranges[&excerpt.id()]
+            excerpt.buffer_range(&snapshot).start.0..excerpt.buffer_range(&snapshot).end.0,
+            reference_range
         );
     }
 
@@ -3641,7 +3623,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
     });
     cx.run_until_parked();
 
-    let mut ids = vec![];
     let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
         multibuffer.set_all_diff_hunks_expanded(cx);
@@ -3661,7 +3642,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
         );
         multibuffer.add_diff(diff_1.clone(), cx);
         multibuffer.add_diff(diff_2.clone(), cx);
-        ids = multibuffer.excerpt_ids();
         multibuffer
     });
 
@@ -3685,11 +3665,27 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
         ),
     );
 
-    let anchor_1 = Anchor::text(ids[0], text::Anchor::MIN);
+    let anchor_1 = multibuffer.read_with(cx, |multibuffer, cx| {
+        multibuffer
+            .buffer_anchor_to_anchor(
+                &buffer_1,
+                text::Anchor::min_for_buffer(buffer_1.read(cx).remote_id()),
+                cx,
+            )
+            .unwrap()
+    });
     let point_1 = snapshot.summaries_for_anchors::<Point, _>([&anchor_1])[0];
     assert_eq!(point_1, Point::new(0, 0));
 
-    let anchor_2 = Anchor::text(ids[1], text::Anchor::MIN);
+    let anchor_2 = multibuffer.read_with(cx, |multibuffer, cx| {
+        multibuffer
+            .buffer_anchor_to_anchor(
+                &buffer_2,
+                text::Anchor::min_for_buffer(buffer_2.read(cx).remote_id()),
+                cx,
+            )
+            .unwrap()
+    });
     let point_2 = snapshot.summaries_for_anchors::<Point, _>([&anchor_2])[0];
     assert_eq!(point_2, Point::new(3, 0));
 }
@@ -3748,7 +3744,7 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
 
     let (_, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
     assert_eq!(translated_offset.0, "one\n".len());
-    let (_, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
+    let (_, translated_point) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
     assert_eq!(translated_point, Point::new(1, 0));
 
     // The same, for an excerpt that's not at the end of the multibuffer.
@@ -3791,7 +3787,7 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
     let (buffer, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
     assert_eq!(buffer.remote_id(), buffer_1_id);
     assert_eq!(translated_offset.0, "one\n".len());
-    let (buffer, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
+    let (buffer, translated_point) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
     assert_eq!(buffer.remote_id(), buffer_1_id);
     assert_eq!(translated_point, Point::new(1, 0));
 }
@@ -3831,6 +3827,7 @@ fn format_diff(
             };
             let expand = info
                 .expand_info
+                .as_ref()
                 .map(|expand_info| match expand_info.direction {
                     ExpandExcerptDirection::Up => " [↑]",
                     ExpandExcerptDirection::Down => " [↓]",
@@ -4174,9 +4171,9 @@ fn assert_excerpts_match(
 ) {
     let mut output = String::new();
     multibuffer.read_with(cx, |multibuffer, cx| {
-        for (_, buffer, range) in multibuffer.snapshot(cx).excerpts() {
+        for (buffer, info) in multibuffer.snapshot(cx).excerpts() {
             output.push_str("-----\n");
-            output.extend(buffer.text_for_range(range.context));
+            output.extend(buffer.text_for_range(info.range.context));
             if !output.ends_with('\n') {
                 output.push('\n');
             }
@@ -4396,7 +4393,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
 
 fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
     let max_row = snapshot.max_point().row;
-    let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id();
+    let buffer_id = snapshot.excerpts().next().unwrap().1.buffer_id;
     let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text());
     let mut line_indents = text
         .line_indents_in_row_range(0..max_row + 1)
@@ -4584,7 +4581,7 @@ fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) {
     let mut diffs = Vec::new();
 
     multibuffer.update(cx, |multibuffer, cx| {
-        for buffer_id in multibuffer.excerpt_buffer_ids() {
+        for buffer_id in multibuffer.all_buffer_ids() {
             if rng.random_bool(0.7) {
                 if let Some(buffer_handle) = multibuffer.buffer(buffer_id) {
                     let buffer_text = buffer_handle.read(cx).text();
@@ -4976,7 +4973,7 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
     let buffer_2 = cx.new(|cx| Buffer::local("ccc", cx));
 
     let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
-    let (excerpt_1_id, excerpt_2_id) = multibuffer.update(cx, |multibuffer, cx| {
+    multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.set_excerpts_for_path(
             PathKey::sorted(0),
             buffer_1.clone(),
@@ -4992,10 +4989,6 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
             0,
             cx,
         );
-
-        let excerpt_ids = multibuffer.excerpt_ids();
-
-        (excerpt_ids[0], excerpt_ids[1])
     });
 
     let snapshot = multibuffer.read(cx).snapshot(cx);
@@ -5009,7 +5002,7 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         1,
         "Half-open range ending at excerpt start should EXCLUDE that excerpt"
     );
-    assert_eq!(ranges_half_open[0].2, excerpt_1_id);
+    assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7));
 
     let ranges_inclusive = snapshot.range_to_buffer_ranges(Point::zero()..=excerpt_2_start);
     assert_eq!(
@@ -5017,8 +5010,16 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         2,
         "Inclusive range ending at excerpt start should INCLUDE that excerpt"
     );
-    assert_eq!(ranges_inclusive[0].2, excerpt_1_id);
-    assert_eq!(ranges_inclusive[1].2, excerpt_2_id);
+    assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7));
+    assert_eq!(
+        ranges_half_open[0].0.remote_id(),
+        buffer_1.read(cx).remote_id()
+    );
+    assert_eq!(ranges_half_open[1].1, BufferOffset(0)..BufferOffset(0));
+    assert_eq!(
+        ranges_half_open[1].0.remote_id(),
+        buffer_2.read(cx).remote_id()
+    );
 
     let ranges_unbounded =
         snapshot.range_to_buffer_ranges((Bound::Included(Point::zero()), Bound::Unbounded));
@@ -5027,8 +5028,16 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         2,
         "Unbounded end should include all excerpts"
     );
-    assert_eq!(ranges_unbounded[0].2, excerpt_1_id);
-    assert_eq!(ranges_unbounded[1].2, excerpt_2_id);
+    assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7));
+    assert_eq!(
+        ranges_half_open[0].0.remote_id(),
+        buffer_1.read(cx).remote_id()
+    );
+    assert_eq!(ranges_half_open[1].1, BufferOffset(0)..BufferOffset(3));
+    assert_eq!(
+        ranges_half_open[1].0.remote_id(),
+        buffer_2.read(cx).remote_id()
+    );
 
     let ranges_excluded_end = snapshot.range_to_buffer_ranges((
         Bound::Included(Point::zero()),
@@ -5039,7 +5048,11 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         1,
         "Excluded end bound should exclude excerpt starting at that point"
     );
-    assert_eq!(ranges_excluded_end[0].2, excerpt_1_id);
+    assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7));
+    assert_eq!(
+        ranges_half_open[0].0.remote_id(),
+        buffer_1.read(cx).remote_id()
+    );
 
     let buffer_empty = cx.new(|cx| Buffer::local("", cx));
     let multibuffer_trailing_empty = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));

crates/multi_buffer/src/path_key.rs 🔗

@@ -278,8 +278,10 @@ impl MultiBuffer {
         let anchor_ranges = new
             .into_iter()
             .map(|r| ExcerptRange {
-                context: buffer_snapshot.anchor_range_around(r.context),
-                primary: buffer_snapshot.anchor_range_around(r.primary),
+                context: buffer_snapshot.anchor_before(r.context.start)
+                    ..buffer_snapshot.anchor_after(r.context.end),
+                primary: buffer_snapshot.anchor_before(r.primary.start)
+                    ..buffer_snapshot.anchor_after(r.primary.end),
             })
             .collect::<Vec<_>>();
         self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx)
@@ -335,26 +337,13 @@ impl MultiBuffer {
 
         let buffer_id = buffer_snapshot.remote_id();
 
-        self.buffers.entry(buffer_id).or_insert_with(|| {
-            self.buffer_changed_since_sync.replace(true);
-            buffer.update(cx, |buffer, _| {
-                buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
-            });
-            BufferState {
-                _subscriptions: [
-                    cx.observe(&buffer, |_, _, cx| cx.notify()),
-                    cx.subscribe(&buffer, Self::on_buffer_event),
-                ],
-                buffer: buffer.clone(),
-            }
-        });
-
         let mut snapshot = self.snapshot.get_mut();
         let mut cursor = snapshot
             .excerpts
             .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
         let mut new_excerpts = SumTree::new(());
 
+        let new_ranges = to_insert.clone();
         let mut to_insert = to_insert.iter().peekable();
         let mut patch = Patch::empty();
         let mut added_new_excerpt = false;
@@ -410,18 +399,19 @@ impl MultiBuffer {
                 let next_excerpt = to_insert.next().unwrap();
                 added_new_excerpt = true;
                 let before = new_excerpts.summary().len();
+                new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ());
                 new_excerpts.push(
                     Excerpt::new(
                         path_key.clone(),
                         path_key_index,
                         &buffer_snapshot,
                         next_excerpt.clone(),
-                        to_insert.peek().is_some(),
+                        to_insert.peek().is_some() || cursor.item().is_some(),
                     ),
                     (),
                 );
                 let after = new_excerpts.summary().len();
-                patch.push(Edit {
+                patch.push_maybe_empty(Edit {
                     old: cursor.position.1..cursor.position.1,
                     new: before..after,
                 });
@@ -437,6 +427,27 @@ impl MultiBuffer {
             new: new_excerpts.summary().len()..new_excerpts.summary().len(),
         });
 
+        while let Some(next_excerpt) = to_insert.next() {
+            added_new_excerpt = true;
+            let before = new_excerpts.summary().len();
+            new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ());
+            new_excerpts.push(
+                Excerpt::new(
+                    path_key.clone(),
+                    path_key_index,
+                    &buffer_snapshot,
+                    next_excerpt.clone(),
+                    to_insert.peek().is_some() || cursor.item().is_some(),
+                ),
+                (),
+            );
+            let after = new_excerpts.summary().len();
+            patch.push_maybe_empty(Edit {
+                old: cursor.position.1..cursor.position.1,
+                new: before..after,
+            });
+        }
+
         let suffix = cursor.suffix();
         let changed_trailing_excerpt = suffix.is_empty();
         new_excerpts.append(suffix, ());
@@ -449,13 +460,28 @@ impl MultiBuffer {
                 buffer_snapshot: buffer_snapshot.clone(),
             },
         );
+
+        self.buffers.entry(buffer_id).or_insert_with(|| {
+            self.buffer_changed_since_sync.replace(true);
+            buffer.update(cx, |buffer, _| {
+                buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
+            });
+            BufferState {
+                _subscriptions: [
+                    cx.observe(&buffer, |_, _, cx| cx.notify()),
+                    cx.subscribe(&buffer, Self::on_buffer_event),
+                ],
+                buffer: buffer.clone(),
+            }
+        });
+
         if changed_trailing_excerpt {
             snapshot.trailing_excerpt_update_count += 1;
         }
 
         let edits = Self::sync_diff_transforms(
             &mut snapshot,
-            patch.into_inner(),
+            dbg!(patch.into_inner()),
             DiffChangeKind::BufferEdited,
         );
         if !edits.is_empty() {
@@ -468,7 +494,7 @@ impl MultiBuffer {
         cx.emit(Event::BufferUpdated {
             buffer,
             path_key: path_key.clone(),
-            ranges: to_insert,
+            ranges: new_ranges,
         });
         cx.notify();
 

crates/multi_buffer/src/transaction.rs 🔗

@@ -333,6 +333,7 @@ impl MultiBuffer {
             let Some(excerpt) = snapshot.first_excerpt_for_buffer(*buffer_id) else {
                 continue;
             };
+            let buffer_snapshot = buffer.read(cx).snapshot();

 
             for range in buffer
                 .read(cx)
@@ -340,11 +341,11 @@ impl MultiBuffer {
             {
                 buffer_anchors.push(Anchor::in_buffer(
                     excerpt.path_key_index,
-                    excerpt.buffer_snapshot.anchor_at(range.start, Bias::Left),

+                    buffer_snapshot.anchor_at(range.start, Bias::Left),

                 ));
                 buffer_anchors.push(Anchor::in_buffer(
                     excerpt.path_key_index,
-                    excerpt.buffer_snapshot.anchor_at(range.end, Bias::Right),

+                    buffer_snapshot.anchor_at(range.end, Bias::Right),

                 ));
             }
         }

crates/text/src/patch.rs 🔗

@@ -56,7 +56,10 @@ where
         if edit.is_empty() {
             return;
         }
+        self.push_maybe_empty(edit);
+    }
 
+    pub fn push_maybe_empty(&mut self, edit: Edit<T>) {
         if let Some(last) = self.0.last_mut() {
             if last.old.end >= edit.old.start {
                 last.old.end = edit.old.end;

crates/text/src/text.rs 🔗

@@ -2509,11 +2509,13 @@ impl BufferSnapshot {
     }
 
     /// Returns an anchor range for the given input position range that is anchored to the text in the range.
+    /// todo!() this name seems misleading
     pub fn anchor_range_around<T: ToOffset>(&self, position: Range<T>) -> Range<Anchor> {
         self.anchor_after(position.start)..self.anchor_before(position.end)
     }
 
     /// Returns an anchor range for the given input position range that is anchored to the text before and after.
+    /// todo!() this name seems misleading
     pub fn anchor_range_between<T: ToOffset>(&self, position: Range<T>) -> Range<Anchor> {
         self.anchor_before(position.start)..self.anchor_after(position.end)
     }