start fixing randomized test

Cole Miller and Conrad Irwin created

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

Change summary

crates/agent_ui/src/agent_diff.rs             |   2 
crates/agent_ui/src/inline_assistant.rs       |   7 
crates/diagnostics/src/diagnostics.rs         |   2 
crates/editor/src/editor_tests.rs             |   2 
crates/editor/src/inlays/inlay_hints.rs       |   2 
crates/editor/src/split.rs                    |   8 
crates/multi_buffer/src/multi_buffer.rs       | 226 +++++++-------------
crates/multi_buffer/src/multi_buffer_tests.rs | 152 ++++---------
crates/multi_buffer/src/path_key.rs           |  33 +-
crates/text/src/anchor.rs                     |   5 
10 files changed, 170 insertions(+), 269 deletions(-)

Detailed changes

crates/agent_ui/src/agent_diff.rs 🔗

@@ -212,7 +212,7 @@ impl AgentDiffPane {
 
         self.multibuffer.update(cx, |multibuffer, cx| {
             for path in paths_to_delete {
-                multibuffer.remove_excerpts_for_path(path, cx);
+                multibuffer.remove_excerpts(path, cx);
             }
         });
 

crates/agent_ui/src/inline_assistant.rs 🔗

@@ -442,10 +442,9 @@ impl InlineAssistant {
         let newest_selection = newest_selection.unwrap();
 
         let mut codegen_ranges = Vec::new();
-        for (buffer, buffer_range, excerpt_id) in
-            snapshot.ranges_to_buffer_ranges(selections.iter().map(|selection| {
-                snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end)
-            }))
+        for (buffer, buffer_range, excerpt_id) in selections
+            .iter()
+            .map(|selection| selection.range_to_buffer_range(selection.start..selection.end))
         {
             let anchor_range = Anchor::range_in_buffer(
                 excerpt_id,

crates/diagnostics/src/diagnostics.rs 🔗

@@ -329,7 +329,7 @@ impl ProjectDiagnosticsEditor {
                 continue;
             }
             self.multibuffer.update(cx, |b, cx| {
-                b.remove_excerpts_for_path(PathKey::for_buffer(&buffer, cx), cx);
+                b.remove_excerpts(PathKey::for_buffer(&buffer, cx), cx);
             });
         }
     }

crates/editor/src/editor_tests.rs 🔗

@@ -18550,7 +18550,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
     // Remove some excerpts.
     leader.update(cx, |leader, cx| {
         leader.buffer.update(cx, |multibuffer, cx| {
-            multibuffer.remove_excerpts_for_path(
+            multibuffer.remove_excerpts(
                 PathKey::with_sort_prefix(1, rel_path("b.txt").into_arc()),
                 cx,
             );

crates/editor/src/inlays/inlay_hints.rs 🔗

@@ -3060,7 +3060,7 @@ let c = 3;"#
         editor
             .update(cx, |editor, _, cx| {
                 editor.buffer().update(cx, |multibuffer, cx| {
-                    multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx);
+                    multibuffer.remove_excerpts(PathKey::sorted(1), cx);
                 })
             })
             .unwrap();

crates/editor/src/split.rs 🔗

@@ -1074,7 +1074,7 @@ impl SplittableEditor {
     pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
         let Some(lhs) = &self.lhs else {
             self.rhs_multibuffer.update(cx, |rhs_multibuffer, cx| {
-                rhs_multibuffer.remove_excerpts_for_path(path, cx);
+                rhs_multibuffer.remove_excerpts(path, cx);
             });
             return;
         };
@@ -1095,10 +1095,10 @@ impl SplittableEditor {
         }
 
         self.rhs_multibuffer.update(cx, |rhs_multibuffer, cx| {
-            rhs_multibuffer.remove_excerpts_for_path(path.clone(), cx);
+            rhs_multibuffer.remove_excerpts(path.clone(), cx);
         });
         lhs.multibuffer.update(cx, |lhs_multibuffer, cx| {
-            lhs_multibuffer.remove_excerpts_for_path(path, cx);
+            lhs_multibuffer.remove_excerpts(path, cx);
         });
     }
 
@@ -1142,7 +1142,7 @@ impl SplittableEditor {
                     rhs_multibuffer.excerpts_for_path(&path).collect();
                 let Some(excerpt_id) = rhs_excerpt_ids.first().copied() else {
                     lhs.multibuffer.update(cx, |lhs_multibuffer, lhs_cx| {
-                        lhs_multibuffer.remove_excerpts_for_path(path, lhs_cx);
+                        lhs_multibuffer.remove_excerpts(path, lhs_cx);
                     });
                     continue;
                 };

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -51,9 +51,9 @@ use std::{
     sync::{Arc, OnceLock},
     time::Duration,
 };
-use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap};
+use sum_tree::{Bias, Cursor, Dimension, Dimensions, Item as _, SumTree, TreeMap};
 use text::{
-    BufferId, Edit, LineIndent, TextSummary,
+    AnchorRangeExt as _, BufferId, Edit, LineIndent, TextSummary,
     subscription::{Subscription, Topic},
 };
 use theme::SyntaxTheme;
@@ -98,12 +98,12 @@ pub struct PathKeyIndex(u64);
 pub struct ExcerptInfo {
     path_key_index: PathKeyIndex,
     buffer_id: BufferId,
-    range: ExcerptRange<text::Anchor>,
+    range: Range<text::Anchor>,
 }
 
 impl ExcerptInfo {
     pub fn end_anchor(&self) -> Anchor {
-        Anchor::in_buffer(self.path_key_index, self.range.context.end)
+        Anchor::in_buffer(self.path_key_index, self.range.end)
     }
 }
 
@@ -1883,27 +1883,17 @@ impl MultiBuffer {
         let snapshot = buffer.read(cx).snapshot();
         let text_anchor = snapshot.anchor_after(&point);
         for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
-            if excerpt.range.contains(&text_anchor, &snapshot) {
-                found = Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor));
-                break;
-            }
-            if excerpt
-                .range
-                .context
-                .start
-                .cmp(&text_anchor, &snapshot)
-                .is_gt()
-            {
+            if excerpt.range.start.cmp(&text_anchor, &snapshot).is_gt() {
                 found = Some(Anchor::in_buffer(
                     excerpt.path_key_index,
-                    excerpt.range.context.start,
+                    excerpt.range.start,
                 ));
                 break;
+            } else if excerpt.range.end.cmp(&text_anchor, &snapshot).is_ge() {
+                found = Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor));
+                break;
             }
-            found = Some(Anchor::in_buffer(
-                excerpt.path_key_index,
-                excerpt.range.context.end,
-            ));
+            found = Some(Anchor::in_buffer(excerpt.path_key_index, excerpt.range.end));
         }
 
         found
@@ -1918,7 +1908,7 @@ impl MultiBuffer {
     ) -> Option<Anchor> {
         let snapshot = buffer.read(cx).snapshot();
         for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
-            if excerpt.range.contains(&anchor, &snapshot) {
+            if excerpt.range.contains_anchor(anchor, &snapshot) {
                 return Some(Anchor::in_buffer(excerpt.path_key_index, anchor));
             }
         }
@@ -2509,7 +2499,6 @@ 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);
@@ -2588,10 +2577,6 @@ 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 {
@@ -2622,15 +2607,14 @@ impl MultiBuffer {
         }
 
         paths_to_edit.sort_unstable_by_key(|(path, _, _)| path.clone());
+        dbg!(&paths_to_edit);
 
         let mut edits = Vec::new();
         let mut new_excerpts = SumTree::default();
         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);
             let buffer_id = buffer.remote_id();
 
@@ -2642,42 +2626,44 @@ impl MultiBuffer {
                 },
             );
 
-            let new_excerpt = if let Some(prev_version) = prev_version {
-                edits.extend(
-                    buffer
-                        .edits_since_in_range::<usize>(
-                            &prev_version,
-                            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);
-                            let old_start = excerpt_old_start + edit.old.start;
-                            let old_end = excerpt_old_start + edit.old.end;
-                            let new_start = excerpt_new_start + edit.new.start;
-                            let new_end = excerpt_new_start + edit.new.end;
-                            Edit {
-                                old: old_start..old_end,
-                                new: new_start..new_end,
-                            }
-                        }),
-                );
+            if let Some(prev_version) = &prev_version {
+                while let Some(old_excerpt) = cursor.item()
+                    && &old_excerpt.path_key == &path
+                {
+                    edits.extend(
+                        buffer
+                            .edits_since_in_range::<usize>(
+                                prev_version,
+                                old_excerpt.range.context.clone(),
+                            )
+                            .map(|edit| {
+                                let excerpt_old_start = cursor.start().len();
+                                let excerpt_new_start =
+                                    ExcerptDimension(new_excerpts.summary().text.len);
+                                let old_start = excerpt_old_start + edit.old.start;
+                                let old_end = excerpt_old_start + edit.old.end;
+                                let new_start = excerpt_new_start + edit.new.start;
+                                let new_end = excerpt_new_start + edit.new.end;
+                                Edit {
+                                    old: old_start..old_end,
+                                    new: new_start..new_end,
+                                }
+                            }),
+                    );
 
-                Excerpt::new(
-                    path,
-                    old_excerpt.path_key_index,
-                    &buffer.snapshot(),
-                    old_excerpt.range.clone(),
-                    old_excerpt.has_trailing_newline,
-                )
+                    let excerpt = Excerpt::new(
+                        old_excerpt.path_key.clone(),
+                        old_excerpt.path_key_index,
+                        &buffer.snapshot(),
+                        old_excerpt.range.clone(),
+                        old_excerpt.has_trailing_newline,
+                    );
+                    new_excerpts.push(excerpt, ());
+                    cursor.next();
+                }
             } else {
-                old_excerpt.clone()
+                new_excerpts.append(cursor.slice(&path, Bias::Right), ());
             };
-
-            new_excerpts.push(new_excerpt, ());
-            cursor.next();
         }
         new_excerpts.append(cursor.suffix(), ());
 
@@ -2692,7 +2678,6 @@ impl MultiBuffer {
         excerpt_edits: Vec<text::Edit<ExcerptOffset>>,
         change_kind: DiffChangeKind,
     ) -> Vec<Edit<MultiBufferOffset>> {
-        dbg!(&excerpt_edits);
         if excerpt_edits.is_empty() {
             return vec![];
         }
@@ -3336,7 +3321,7 @@ impl MultiBuffer {
                     .path_key
                     .clone();
                 log::info!("Removing excerpts {:?}", path_key);
-                self.remove_excerpts_for_path(path_key, cx);
+                self.remove_excerpts(path_key, cx);
             }
         }
     }
@@ -3665,72 +3650,35 @@ impl MultiBufferSnapshot {
         result
     }
 
-    pub fn ranges_to_buffer_ranges<T: ToOffset>(
-        &self,
-        ranges: impl Iterator<Item = Range<T>>,
-    ) -> impl Iterator<Item = (&BufferSnapshot, Range<BufferOffset>)> {
-        ranges.flat_map(|range| {
-            self.range_to_buffer_ranges((Bound::Included(range.start), Bound::Included(range.end)))
-                .into_iter()
-        })
-    }
-
     // todo!() can we make this not RangeBounds
-    pub fn range_to_buffer_ranges<R, T>(
+    pub fn range_to_buffer_ranges<T: ToOffset>(
         &self,
-        range: R,
-    ) -> Vec<(&BufferSnapshot, Range<BufferOffset>)>
-    where
-        R: RangeBounds<T>,
-        T: ToOffset,
-    {
+        range: Range<T>,
+    ) -> Vec<(&BufferSnapshot, Range<BufferOffset>)> {
         self.range_to_buffer_ranges_with_context(range)
             .into_iter()
             .map(|(buffer, range, _context)| (buffer, range))
             .collect()
     }
 
-    pub fn range_to_buffer_ranges_with_context<R, T>(
+    pub fn range_to_buffer_ranges_with_context<T: ToOffset>(
         &self,
-        range: R,
-    ) -> Vec<(&BufferSnapshot, Range<BufferOffset>, Range<text::Anchor>)>
-    where
-        R: RangeBounds<T>,
-        T: ToOffset,
-    {
-        let start = match range.start_bound() {
-            Bound::Included(start) => start.to_offset(self),
-            Bound::Excluded(_) => panic!("excluded start bound not supported"),
-            Bound::Unbounded => MultiBufferOffset::ZERO,
-        };
-        let end_bound = match range.end_bound() {
-            Bound::Included(end) => Bound::Included(end.to_offset(self)),
-            Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
-            Bound::Unbounded => Bound::Unbounded,
-        };
-        let bounds = (Bound::Included(start), end_bound);
-
+        range: Range<T>,
+    ) -> Vec<(&BufferSnapshot, Range<BufferOffset>, Range<text::Anchor>)> {
         let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
+        let start = range.start.to_offset(self);
+        let end = range.end.to_offset(self);
         cursor.seek(&start);
 
         let mut result: Vec<(&BufferSnapshot, Range<BufferOffset>, Range<text::Anchor>)> =
             Vec::new();
-        let mut last_excerpt = None;
         while let Some(region) = cursor.region() {
-            let dominated_by_end_bound = match end_bound {
-                Bound::Included(end) => region.range.start > end,
-                Bound::Excluded(end) => region.range.start >= end,
-                Bound::Unbounded => false,
-            };
-            if dominated_by_end_bound {
+            if region.range.start >= end {
                 break;
             }
             if region.is_main_buffer {
                 let start_overshoot = start.saturating_sub(region.range.start);
-                let end_offset = match end_bound {
-                    Bound::Included(end) | Bound::Excluded(end) => end,
-                    Bound::Unbounded => region.range.end,
-                };
+                let end_offset = end;
                 let end_overshoot = end_offset.saturating_sub(region.range.start);
                 let start = region
                     .buffer_range
@@ -3741,35 +3689,34 @@ impl MultiBufferSnapshot {
                     .end
                     .min(region.buffer_range.start + end_overshoot);
                 let context = region.excerpt.range.context.clone();
-                if last_excerpt
-                    .as_ref()
-                    .is_some_and(|prev_excerpt| prev_excerpt == &region.excerpt.info())
-                    && let Some(prev) = result
-                        .last_mut()
-                        .filter(|(_, prev_range, _)| prev_range.end == start)
-                {
+                if let Some(prev) = result.last_mut().filter(|(prev_buffer, prev_range, _)| {
+                    prev_buffer.remote_id() == region.buffer.remote_id() && prev_range.end == start
+                }) {
                     prev.1.end = end;
                 } else {
                     result.push((region.buffer, start..end, context));
-                    // todo!() is there a better way to do this?
-                    last_excerpt = Some(region.excerpt.info())
                 }
             }
             cursor.next();
         }
 
-        if let Some(excerpt) = cursor.excerpt() {
-            let dominated_by_prev_excerpt =
-                last_excerpt.is_some_and(|last_excerpt| last_excerpt == excerpt.info());
-            if !dominated_by_prev_excerpt && excerpt.text_summary.len == 0 {
-                let excerpt_position = self.len();
-                let buffer_snapshot = excerpt.buffer_snapshot(self);
-                if bounds.contains(&excerpt_position) {
-                    let buffer_offset =
-                        BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot));
-                    let context = excerpt.range.context.clone();
-                    result.push((buffer_snapshot, buffer_offset..buffer_offset, context));
-                }
+        // Handle empty trailing excerpt, which doesn't have a region
+        if let Some(excerpt) = cursor.excerpt()
+            && excerpt.text_summary.len == 0
+            && end == self.len()
+        {
+            let buffer_snapshot = excerpt.buffer_snapshot(self);
+
+            let buffer_offset =
+                BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot));
+            let context = excerpt.range.context.clone();
+            if result
+                .last_mut()
+                .is_none_or(|(prev_buffer, prev_range, _)| {
+                    prev_buffer.remote_id() != excerpt.buffer_id || prev_range.end != buffer_offset
+                })
+            {
+                result.push((buffer_snapshot, buffer_offset..buffer_offset, context));
             }
         }
 
@@ -4231,7 +4178,6 @@ 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 {
@@ -5073,7 +5019,6 @@ impl MultiBufferSnapshot {
 
         let mut summaries = Vec::new();
         while let Some(anchor) = anchors.peek() {
-            dbg!(&anchor);
             let target = anchor.seek_target(self);
             let excerpt_anchor = match anchor {
                 Anchor::Min => {
@@ -6476,7 +6421,7 @@ impl MultiBufferSnapshot {
             .to_multi_buffer_debug_ranges(self)
             .into_iter()
             .flat_map(|range| {
-                self.range_to_buffer_ranges(range.start..=range.end)
+                self.range_to_buffer_ranges(range)
                     .into_iter()
                     .map(|(buffer, range)| {
                         buffer.anchor_after(range.start)..buffer.anchor_before(range.end)
@@ -6653,8 +6598,7 @@ where
         }
 
         self.excerpts.seek(&excerpt_position, Bias::Right);
-        if self.excerpts.item().is_none() && dbg!(excerpt_position) == *self.excerpts.start() {
-            dbg!("NONE");
+        if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() {
             self.excerpts.prev();
         }
     }
@@ -6823,7 +6767,6 @@ 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,
@@ -6956,12 +6899,11 @@ impl Excerpt {
         range: ExcerptRange<text::Anchor>,
         has_trailing_newline: bool,
     ) -> Self {
-        dbg!(&path_key, &range, has_trailing_newline);
         Excerpt {
             path_key,
             path_key_index,
             buffer_id: buffer_snapshot.remote_id(),
-            max_buffer_row: range.context.end.to_point(&buffer_snapshot).row,
+            max_buffer_row: dbg!(range.context.end.to_point(&buffer_snapshot).row),
             text_summary: buffer_snapshot.text_summary_for_range::<TextSummary, _>(
                 range.context.to_offset(&buffer_snapshot),
             ),
@@ -6988,7 +6930,7 @@ impl Excerpt {
         ExcerptInfo {
             path_key_index: self.path_key_index,
             buffer_id: self.buffer_id,
-            range: self.range.clone(),
+            range: self.range.context.clone(),
         }
     }
 
@@ -7615,13 +7557,11 @@ 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 dbg!(self.point) == dbg!(self.cursor.diff_transforms.end().output_dimension.0)
-            {
+            } else if self.point == 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 🔗

@@ -319,7 +319,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
     );
 
     let snapshot = multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx);
+        multibuffer.remove_excerpts(PathKey::sorted(1), cx);
         multibuffer.snapshot(cx)
     });
 
@@ -875,11 +875,11 @@ fn test_expand_excerpts(cx: &mut App) {
     multibuffer.update(cx, |multibuffer, cx| {
         let line_zero = multibuffer.snapshot(cx).anchor_before(Point::new(0, 0));
 
-        multibuffer.expand_excerpts_with_paths(
+        multibuffer.expand_excerpts(
             multibuffer
                 .snapshot(cx)
                 .excerpts()
-                .map(|(_, info)| Anchor::in_buffer(info.path_key_index, info.range.context.end)),
+                .map(|(_, info)| Anchor::in_buffer(info.path_key_index, info.range.end)),
             1,
             ExpandExcerptDirection::UpAndDown,
             cx,
@@ -1109,7 +1109,6 @@ 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");
@@ -2294,7 +2293,7 @@ impl ReferenceExcerpt {
         ExcerptInfo {
             path_key_index: self.path_key_index,
             buffer_id: self.buffer.read(cx).remote_id(),
-            range: ExcerptRange::new(self.range.clone()),
+            range: self.range.clone(),
         }
     }
 }
@@ -2345,7 +2344,8 @@ impl ReferenceMultibuffer {
                 path_key: path_key.clone(),
                 path_key_index,
                 buffer: buffer.clone(),
-                range: buffer_snapshot.anchor_range_around(range.context),
+                range: buffer_snapshot.anchor_before(range.context.start)
+                    ..buffer_snapshot.anchor_after(range.context.end),
             }),
         );
     }
@@ -2669,7 +2669,7 @@ impl ReferenceMultibuffer {
                             expand_info: expand_direction.zip(region.excerpt_info.clone()).map(
                                 |(direction, excerpt_info)| ExpandInfo {
                                     direction,
-                                    excerpt_range: excerpt_info.range.context,
+                                    excerpt_range: excerpt_info.range,
                                 },
                             ),
                         }
@@ -2787,8 +2787,8 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
         let mut seen_ranges = Vec::default();
 
         for (buf, info) in snapshot.excerpts() {
-            let start = info.range.context.start.to_point(buf);
-            let end = info.range.context.end.to_point(buf);
+            let start = info.range.start.to_point(buf);
+            let end = info.range.end.to_point(buf);
             seen_ranges.push(start..end);
 
             if let Some(last_end) = last_end.take() {
@@ -2865,10 +2865,10 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                         })
                         .collect::<Vec<_>>();
                     log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
-                    multibuffer.expand_excerpts_with_paths(
-                        excerpts.iter().map(|info| {
-                            Anchor::in_buffer(info.path_key_index, info.range.context.end)
-                        }),
+                    multibuffer.expand_excerpts(
+                        excerpts
+                            .iter()
+                            .map(|info| Anchor::in_buffer(info.path_key_index, info.range.end)),
                         line_count,
                         ExpandExcerptDirection::UpAndDown,
                         cx,
@@ -3160,6 +3160,17 @@ fn check_multibuffer(
     );
 
     log::info!("Multibuffer content:\n{}", actual_diff);
+    dbg!(
+        snapshot
+            .excerpts()
+            .map(|(buffer_snapshot, excerpt)| {
+                (
+                    buffer_snapshot.remote_id(),
+                    excerpt.range.to_point(&buffer_snapshot),
+                )
+            })
+            .collect::<Vec<_>>()
+    );
 
     assert_eq!(
         actual_row_infos.len(),
@@ -3182,6 +3193,27 @@ fn check_multibuffer(
             start_row
         );
     }
+    // dbg!(&expected_row_infos);
+
+    // dbg!(
+    //     snapshot
+    //         .excerpts()
+    //         .map(|(buffer_snapshot, excerpt)| {
+    //             (
+    //                 buffer_snapshot.remote_id(),
+    //                 excerpt.range.to_point(&buffer_snapshot),
+    //             )
+    //         })
+    //         .collect::<Vec<_>>()
+    // );
+    // dbg!(&expected_row_infos);
+    // dbg!(
+    //     reference
+    //         .excerpts
+    //         .iter()
+    //         .map(|excerpt| { excerpt.range.to_point(&excerpt.buffer.read(cx).snapshot()) })
+    //         .collect::<Vec<_>>()
+    // );
 
     assert_eq!(
         snapshot.widest_line_number(),
@@ -3623,7 +3655,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
     });
     cx.run_until_parked();
 
-    dbg!("BEFORE");
     let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
         multibuffer.set_all_diff_hunks_expanded(cx);
@@ -3646,12 +3677,10 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
         multibuffer
     });
 
-    dbg!("BEFORE");
     let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
         (multibuffer.snapshot(cx), multibuffer.subscribe())
     });
 
-    dbg!("BEFORE");
     assert_new_snapshot(
         &multibuffer,
         &mut snapshot,
@@ -3668,7 +3697,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
         ),
     );
 
-    dbg!("BEFORE");
     let anchor_1 = multibuffer.read_with(cx, |multibuffer, cx| {
         multibuffer
             .buffer_anchor_to_anchor(
@@ -3678,11 +3706,9 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
             )
             .unwrap()
     });
-    dbg!("BEFORE");
     let point_1 = snapshot.summaries_for_anchors::<Point, _>([&anchor_1])[0];
     assert_eq!(point_1, Point::new(0, 0));
 
-    dbg!("BEFORE");
     let anchor_2 = multibuffer.read_with(cx, |multibuffer, cx| {
         multibuffer
             .buffer_anchor_to_anchor(
@@ -3692,9 +3718,7 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
             )
             .unwrap()
     });
-    dbg!("BEFORE");
     let point_2 = snapshot.summaries_for_anchors::<Point, _>([&anchor_2])[0];
-    dbg!("AFTER");
     assert_eq!(point_2, Point::new(3, 0));
 }
 
@@ -4181,7 +4205,7 @@ fn assert_excerpts_match(
     multibuffer.read_with(cx, |multibuffer, cx| {
         for (buffer, info) in multibuffer.snapshot(cx).excerpts() {
             output.push_str("-----\n");
-            output.extend(buffer.text_for_range(info.range.context));
+            output.extend(buffer.text_for_range(info.range));
             if !output.ends_with('\n') {
                 output.push('\n');
             }
@@ -4974,9 +4998,7 @@ fn test_excerpts_containment_functions(cx: &mut App) {
 }
 
 #[gpui::test]
-fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
-    use std::ops::Bound;
-
+fn test_range_to_buffer_ranges(cx: &mut App) {
     let buffer_1 = cx.new(|cx| Buffer::local("aaa\nbbb", cx));
     let buffer_2 = cx.new(|cx| Buffer::local("ccc", cx));
 
@@ -5011,52 +5033,6 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         "Half-open range ending at excerpt start should EXCLUDE that excerpt"
     );
     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!(
-        ranges_inclusive.len(),
-        2,
-        "Inclusive range ending at excerpt start should INCLUDE that excerpt"
-    );
-    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));
-    assert_eq!(
-        ranges_unbounded.len(),
-        2,
-        "Unbounded end should include all excerpts"
-    );
-    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()),
-        Bound::Excluded(excerpt_2_start),
-    ));
-    assert_eq!(
-        ranges_excluded_end.len(),
-        1,
-        "Excluded end bound should exclude excerpt starting at that point"
-    );
-    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()
@@ -5096,36 +5072,10 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
         snapshot_trailing.range_to_buffer_ranges_with_context(Point::zero()..max_point);
     assert_eq!(
         ranges_half_open_max.len(),
-        1,
-        "Half-open range to max_point should EXCLUDE trailing empty excerpt at max_point"
-    );
-    assert_eq!(ranges_half_open_max[0].2, te_excerpt_1_info.range.context);
-
-    let ranges_inclusive_max =
-        snapshot_trailing.range_to_buffer_ranges_with_context(Point::zero()..=max_point);
-    assert_eq!(
-        ranges_inclusive_max.len(),
         2,
-        "Inclusive range to max_point should INCLUDE trailing empty excerpt"
-    );
-    assert_eq!(ranges_inclusive_max[0].2, te_excerpt_1_info.range.context);
-    assert_eq!(ranges_inclusive_max[1].2, te_excerpt_2_info.range.context);
-
-    let ranges_unbounded_trailing = snapshot_trailing
-        .range_to_buffer_ranges_with_context((Bound::Included(Point::zero()), Bound::Unbounded));
-    assert_eq!(
-        ranges_unbounded_trailing.len(),
-        2,
-        "Unbounded end should include trailing empty excerpt"
-    );
-    assert_eq!(
-        ranges_unbounded_trailing[0].2,
-        te_excerpt_1_info.range.context
-    );
-    assert_eq!(
-        ranges_unbounded_trailing[1].2,
-        te_excerpt_2_info.range.context
+        "Should include trailing empty excerpts"
     );
+    assert_eq!(ranges_half_open_max[0].2, te_excerpt_1_info.range);
 }
 
 #[gpui::test]
@@ -5177,8 +5127,8 @@ fn test_cannot_seek_backward_after_excerpt_replacement(cx: &mut TestAppContext)
         let e_b2_info = excerpt_infos[1].clone();
         let e_b3_info = excerpt_infos[2].clone();
 
-        let anchor_b2 = Anchor::in_buffer(e_b2_info.path_key_index, e_b2_info.range.context.start);
-        let anchor_b3 = Anchor::in_buffer(e_b3_info.path_key_index, e_b3_info.range.context.start);
+        let anchor_b2 = Anchor::in_buffer(e_b2_info.path_key_index, e_b2_info.range.start);
+        let anchor_b3 = Anchor::in_buffer(e_b3_info.path_key_index, e_b3_info.range.start);
         (anchor_b2, anchor_b3)
     });
 

crates/multi_buffer/src/path_key.rs 🔗

@@ -69,7 +69,7 @@ impl MultiBuffer {
         let excerpt = snapshot.excerpts_for_path(path).next()?;
         Some(Anchor::in_buffer(
             excerpt.path_key_index,
-            excerpt.range.context.start,
+            excerpt.range.start,
         ))
     }
 
@@ -178,7 +178,7 @@ impl MultiBuffer {
         }
     }
 
-    pub(super) fn expand_excerpts_with_paths(
+    pub fn expand_excerpts(
         &mut self,
         anchors: impl IntoIterator<Item = Anchor>,
         line_count: u32,
@@ -199,6 +199,11 @@ impl MultiBuffer {
                 .path_keys_by_index
                 .get(&path_index)
                 .expect("anchor from wrong multibuffer");
+
+            let mut excerpt_anchors = excerpt_anchors.peekable();
+            let mut ranges = Vec::new();
+
+            cursor.seek_forward(path, Bias::Left);
             let Some((buffer, buffer_snapshot)) = cursor
                 .item()
                 .map(|excerpt| (excerpt.buffer(&self), excerpt.buffer_snapshot(&snapshot)))
@@ -206,10 +211,6 @@ impl MultiBuffer {
                 continue;
             };
 
-            let mut excerpt_anchors = excerpt_anchors.peekable();
-            let mut ranges = Vec::new();
-
-            cursor.seek_forward(path, Bias::Left);
             while let Some(excerpt) = cursor.item()
                 && &excerpt.path_key == path
             {
@@ -324,11 +325,11 @@ impl MultiBuffer {
             .path_for_buffer(buffer_snapshot.remote_id())
             && old_path_key != &path_key
         {
-            self.remove_excerpts_for_path(old_path_key.clone(), cx);
+            self.remove_excerpts(old_path_key.clone(), cx);
         }
 
         if to_insert.len() == 0 {
-            self.remove_excerpts_for_path(path_key.clone(), cx);
+            self.remove_excerpts(path_key.clone(), cx);
 
             return (false, path_key_index);
         }
@@ -406,7 +407,10 @@ impl MultiBuffer {
                         path_key_index,
                         &buffer_snapshot,
                         next_excerpt.clone(),
-                        to_insert.peek().is_some() || cursor.item().is_some(),
+                        to_insert.peek().is_some()
+                            || cursor
+                                .item()
+                                .is_some_and(|item| item.path_key_index != path_key_index),
                     ),
                     (),
                 );
@@ -437,7 +441,10 @@ impl MultiBuffer {
                     path_key_index,
                     &buffer_snapshot,
                     next_excerpt.clone(),
-                    to_insert.peek().is_some() || cursor.item().is_some(),
+                    to_insert.peek().is_some()
+                        || cursor
+                            .item()
+                            .is_some_and(|item| item.path_key_index != path_key_index),
                 ),
                 (),
             );
@@ -481,7 +488,7 @@ impl MultiBuffer {
 
         let edits = Self::sync_diff_transforms(
             &mut snapshot,
-            dbg!(patch.into_inner()),
+            patch.into_inner(),
             DiffChangeKind::BufferEdited,
         );
         if !edits.is_empty() {
@@ -506,10 +513,10 @@ impl MultiBuffer {
         let Some(path) = snapshot.path_for_buffer(buffer).cloned() else {
             return;
         };
-        self.remove_excerpts_for_path(path, cx);
+        self.remove_excerpts(path, cx);
     }
 
-    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
+    pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context<Self>) {
         assert_eq!(self.history.transaction_depth(), 0);
         self.sync_mut(cx);
 

crates/text/src/anchor.rs 🔗

@@ -216,6 +216,7 @@ where
 pub trait AnchorRangeExt {
     fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
     fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
+    fn contains_anchor(&self, b: Anchor, buffer: &BufferSnapshot) -> bool;
 }
 
 impl AnchorRangeExt for Range<Anchor> {
@@ -229,4 +230,8 @@ impl AnchorRangeExt for Range<Anchor> {
     fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
         self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
     }
+
+    fn contains_anchor(&self, other: Anchor, buffer: &BufferSnapshot) -> bool {
+        self.start.cmp(&other, buffer).is_le() && self.end.cmp(&other, buffer).is_ge()
+    }
 }