Newtype MultibufferPoint

Lukas Wirth created

Change summary

crates/multi_buffer/src/anchor.rs             |  24 
crates/multi_buffer/src/multi_buffer.rs       | 499 +++++++++++++-------
crates/multi_buffer/src/multi_buffer_tests.rs | 338 ++++++++++---
crates/multi_buffer/src/transaction.rs        |  33 
4 files changed, 613 insertions(+), 281 deletions(-)

Detailed changes

crates/multi_buffer/src/anchor.rs 🔗

@@ -1,3 +1,5 @@
+use crate::MultiBufferPoint;
+
 use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint};
 use language::{OffsetUtf16, Point, TextDimension};
 use std::{
@@ -12,6 +14,7 @@ pub struct Anchor {
     pub buffer_id: Option<BufferId>,
     pub excerpt_id: ExcerptId,
     pub text_anchor: text::Anchor,
+    /// Some if this anchor points to within a deleted diff hunk.
     pub diff_base_anchor: Option<text::Anchor>,
 }
 
@@ -160,13 +163,6 @@ impl Anchor {
         *self
     }
 
-    pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
-    where
-        D: TextDimension + Ord + Sub<D, Output = D>,
-    {
-        snapshot.summary_for_anchor(self)
-    }
-
     pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
         if *self == Anchor::min() || *self == Anchor::max() {
             true
@@ -183,19 +179,19 @@ impl Anchor {
 
 impl ToOffset for Anchor {
     fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        self.summary(snapshot)
+        snapshot.summary_for_anchor(self)
     }
     fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
-        self.summary(snapshot)
+        snapshot.summary_for_anchor(self)
     }
 }
 
 impl ToPoint for Anchor {
-    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
-        self.summary(snapshot)
+    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferPoint {
+        snapshot.summary_for_anchor(self)
     }
     fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
-        self.summary(snapshot)
+        snapshot.summary_for_anchor(self)
     }
 }
 
@@ -204,7 +200,7 @@ pub trait AnchorRangeExt {
     fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
     fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
     fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
-    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
+    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferPoint>;
 }
 
 impl AnchorRangeExt for Range<Anchor> {
@@ -227,7 +223,7 @@ impl AnchorRangeExt for Range<Anchor> {
         self.start.to_offset(content)..self.end.to_offset(content)
     }
 
-    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
+    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferPoint> {
         self.start.to_point(content)..self.end.to_point(content)
     }
 }

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -43,7 +43,7 @@ use std::{
     io,
     iter::{self, FromIterator},
     mem,
-    ops::{Range, RangeBounds, Sub},
+    ops::{self, AddAssign, Range, RangeBounds, Sub},
     rc::Rc,
     str,
     sync::Arc,
@@ -170,7 +170,91 @@ impl MultiBufferDiffHunk {
     }
 }
 
-pub type MultiBufferPoint = Point;
+/// A zero-indexed point in a multibuffer consisting of a row and column.
+#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
+pub struct MultiBufferPoint(Point);
+
+impl fmt::Debug for MultiBufferPoint {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_fmt(format_args!(
+            "MultiBufferPoint({}, {})",
+            self.row().0,
+            self.column()
+        ))
+    }
+}
+
+impl ops::Add for MultiBufferPoint {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self::Output {
+        MultiBufferPoint(self.0 + other.0)
+    }
+}
+
+impl ops::AddAssign for MultiBufferPoint {
+    fn add_assign(&mut self, other: Self) {
+        self.0 += other.0;
+    }
+}
+
+impl ops::Sub for MultiBufferPoint {
+    type Output = Self;
+
+    fn sub(self, other: Self) -> Self::Output {
+        MultiBufferPoint(self.0 - other.0)
+    }
+}
+
+impl MultiBufferPoint {
+    pub const MAX: Self = Self(Point::MAX);
+
+    pub fn new(row: MultiBufferRow, column: u32) -> Self {
+        Self(Point::new(row.0, column))
+    }
+
+    pub fn zero() -> Self {
+        Self::new(MultiBufferRow(0), 0)
+    }
+
+    pub fn is_zero(&self) -> bool {
+        self.0.is_zero()
+    }
+
+    pub fn row(self) -> MultiBufferRow {
+        MultiBufferRow(self.0.row)
+    }
+
+    pub fn column(self) -> u32 {
+        self.0.column
+    }
+
+    pub fn row_mut(&mut self) -> &mut u32 {
+        &mut self.0.row
+    }
+
+    pub fn column_mut(&mut self) -> &mut u32 {
+        &mut self.0.column
+    }
+
+    pub fn from_point(point: Point) -> Self {
+        Self(point)
+    }
+
+    // pub fn to_point(self, map: &DisplaySnapshot) -> Point {
+    //     map.display_point_to_point(self, Bias::Left)
+    // }
+
+    // pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
+    //     let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
+    //     let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
+    //     let fold_point = map.tab_snapshot().to_fold_point(tab_point, bias).0;
+    //     let inlay_point = fold_point.to_inlay_point(map.fold_snapshot());
+    //     map.inlay_snapshot()
+    //         .to_buffer_offset(map.inlay_snapshot().to_offset(inlay_point))
+    // }
+}
+
 type ExcerptOffset = TypedOffset<Excerpt>;
 type ExcerptPoint = TypedPoint<Excerpt>;
 
@@ -191,13 +275,42 @@ impl std::ops::Add<usize> for MultiBufferRow {
     }
 }
 
+impl std::ops::Sub<usize> for MultiBufferRow {
+    type Output = Self;
+
+    fn sub(self, rhs: usize) -> Self::Output {
+        MultiBufferRow(self.0 - rhs as u32)
+    }
+}
+impl std::ops::Add for MultiBufferRow {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self::Output {
+        MultiBufferRow(self.0 + rhs.0)
+    }
+}
+
+impl std::ops::Sub<Self> for MultiBufferRow {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        MultiBufferRow(self.0 - rhs.0)
+    }
+}
+
+impl fmt::Display for MultiBufferRow {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
 pub trait ToOffset: 'static + fmt::Debug {
     fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
     fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;
 }
 
 pub trait ToPoint: 'static + fmt::Debug {
-    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
+    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferPoint;
     fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;
 }
 
@@ -369,7 +482,7 @@ struct Excerpt {
 #[derive(Clone)]
 pub struct MultiBufferExcerpt<'a> {
     excerpt: &'a Excerpt,
-    diff_transforms: sum_tree::Cursor<'a, 'static, DiffTransform, DiffTransforms<usize>>,
+    diff_transforms: sum_tree::Cursor<'a, 'static, DiffTransform, DiffTransforms<usize, usize>>,
     offset: usize,
     excerpt_offset: ExcerptDimension<usize>,
     buffer_offset: usize,
@@ -418,10 +531,10 @@ pub struct DiffTransformSummary {
 
 #[derive(Clone)]
 pub struct MultiBufferRows<'a> {
-    point: Point,
+    point: MultiBufferPoint,
     is_empty: bool,
     is_singleton: bool,
-    cursor: MultiBufferCursor<'a, Point>,
+    cursor: MultiBufferCursor<'a, Point, MultiBufferPoint>,
 }
 
 pub struct MultiBufferChunks<'a> {
@@ -437,7 +550,7 @@ pub struct MultiBufferChunks<'a> {
 }
 
 pub struct ReversedMultiBufferChunks<'a> {
-    cursor: MultiBufferCursor<'a, usize>,
+    cursor: MultiBufferCursor<'a, usize, usize>,
     current_chunks: Option<rope::Chunks<'a>>,
     start: usize,
     offset: usize,
@@ -445,7 +558,7 @@ pub struct ReversedMultiBufferChunks<'a> {
 
 pub struct MultiBufferBytes<'a> {
     range: Range<usize>,
-    cursor: MultiBufferCursor<'a, usize>,
+    cursor: MultiBufferCursor<'a, usize, usize>,
     excerpt_bytes: Option<text::Bytes<'a>>,
     has_trailing_newline: bool,
     chunk: &'a [u8],
@@ -457,19 +570,33 @@ pub struct ReversedMultiBufferBytes<'a> {
     chunk: &'a [u8],
 }
 
-#[derive(Clone)]
-struct DiffTransforms<D> {
-    output_dimension: OutputDimension<D>,
-    excerpt_dimension: ExcerptDimension<D>,
+struct DiffTransforms<LBD, MBD> {
+    output_dimension: OutputDimension<MBD>,
+    excerpt_dimension: ExcerptDimension<LBD>,
+}
+
+impl<LBD, MBD> Clone for DiffTransforms<LBD, MBD>
+where
+    ExcerptDimension<LBD>: Clone,
+    OutputDimension<MBD>: Clone,
+{
+    fn clone(&self) -> Self {
+        Self {
+            output_dimension: self.output_dimension.clone(),
+            excerpt_dimension: self.excerpt_dimension.clone(),
+        }
+    }
 }
 
-impl<'a, D: TextDimension> Dimension<'a, DiffTransformSummary> for DiffTransforms<D> {
+impl<'a, LBD, MBD> Dimension<'a, DiffTransformSummary> for DiffTransforms<LBD, MBD>
+where
+    ExcerptDimension<LBD>: Dimension<'a, DiffTransformSummary>,
+    OutputDimension<MBD>: Dimension<'a, DiffTransformSummary>,
+{
     fn zero(cx: <DiffTransformSummary as sum_tree::Summary>::Context<'_>) -> Self {
         Self {
             output_dimension: OutputDimension::zero(cx),
-            excerpt_dimension: <ExcerptDimension<D> as Dimension<'a, DiffTransformSummary>>::zero(
-                cx,
-            ),
+            excerpt_dimension: ExcerptDimension::zero(cx),
         }
     }
 
@@ -484,21 +611,21 @@ impl<'a, D: TextDimension> Dimension<'a, DiffTransformSummary> for DiffTransform
 }
 
 #[derive(Clone)]
-struct MultiBufferCursor<'a, D: TextDimension> {
-    excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension<D>>,
-    diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms<D>>,
+struct MultiBufferCursor<'a, LBD, MBD> {
+    excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension<LBD>>,
+    diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms<LBD, MBD>>,
     diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
-    cached_region: Option<MultiBufferRegion<'a, D>>,
+    cached_region: Option<MultiBufferRegion<'a, LBD, MBD>>,
 }
 
 #[derive(Clone)]
-struct MultiBufferRegion<'a, D: TextDimension> {
+struct MultiBufferRegion<'a, LBD, MBD> {
     buffer: &'a BufferSnapshot,
     is_main_buffer: bool,
     diff_hunk_status: Option<DiffHunkStatus>,
     excerpt: &'a Excerpt,
-    buffer_range: Range<D>,
-    range: Range<D>,
+    buffer_range: Range<LBD>,
+    range: Range<MBD>,
     has_trailing_newline: bool,
 }
 
@@ -854,7 +981,7 @@ impl MultiBuffer {
     ) -> (HashMap<BufferId, Vec<BufferEdit>>, Vec<ExcerptId>) {
         let mut buffer_edits: HashMap<BufferId, Vec<BufferEdit>> = Default::default();
         let mut edited_excerpt_ids = Vec::new();
-        let mut cursor = snapshot.cursor::<usize>();
+        let mut cursor = snapshot.cursor::<usize, usize>();
         for (ix, (range, new_text)) in edits.into_iter().enumerate() {
             let original_indent_column = original_indent_columns.get(ix).copied().flatten();
 
@@ -1036,7 +1163,7 @@ impl MultiBuffer {
         space_above: bool,
         space_below: bool,
         cx: &mut Context<Self>,
-    ) -> Point {
+    ) -> MultiBufferPoint {
         let multibuffer_point = position.to_point(&self.read(cx));
         let (buffer, buffer_point, _) = self.point_to_buffer_point(multibuffer_point, cx).unwrap();
         self.start_transaction(cx);
@@ -1044,7 +1171,7 @@ impl MultiBuffer {
             buffer.insert_empty_line(buffer_point, space_above, space_below, cx)
         });
         self.end_transaction(cx);
-        multibuffer_point + (empty_line_start - buffer_point)
+        multibuffer_point + MultiBufferPoint(empty_line_start - buffer_point)
     }
 
     pub fn set_active_selections(
@@ -1395,14 +1522,18 @@ impl MultiBuffer {
         excerpts
     }
 
-    pub fn excerpt_ranges_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<Range<Point>> {
+    pub fn excerpt_ranges_for_buffer(
+        &self,
+        buffer_id: BufferId,
+        cx: &App,
+    ) -> Vec<Range<MultiBufferPoint>> {
         let snapshot = self.read(cx);
         let mut excerpts = snapshot
             .excerpts
             .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(());
         let mut diff_transforms = snapshot
             .diff_transforms
-            .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(());
+            .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<MultiBufferPoint>>>(());
         diff_transforms.next();
         let locators = self
             .buffers
@@ -1420,11 +1551,13 @@ impl MultiBuffer {
 
                 diff_transforms.seek_forward(&excerpt_start, Bias::Left);
                 let overshoot = excerpt_start.0 - diff_transforms.start().0.0;
-                let start = diff_transforms.start().1.0 + overshoot;
+                let mut start = diff_transforms.start().1.0;
+                start.add_distance(overshoot);
 
                 diff_transforms.seek_forward(&excerpt_end, Bias::Right);
                 let overshoot = excerpt_end.0 - diff_transforms.start().0.0;
-                let end = diff_transforms.start().1.0 + overshoot;
+                let mut end = diff_transforms.start().1.0;
+                end.add_distance(overshoot);
 
                 result.push(start..end)
             }
@@ -1458,7 +1591,7 @@ impl MultiBuffer {
         let snapshot = self.read(cx);
         let offset = position.to_offset(&snapshot);
 
-        let mut cursor = snapshot.cursor::<usize>();
+        let mut cursor = snapshot.cursor::<usize, usize>();
         cursor.seek(&offset);
         cursor
             .excerpt()
@@ -2008,8 +2141,8 @@ impl MultiBuffer {
         let mut cursor = snapshot.diff_transforms.cursor::<usize>(());
         for range in ranges {
             let range = range.to_point(&snapshot);
-            let start = snapshot.point_to_offset(Point::new(range.start.row, 0));
-            let end = snapshot.point_to_offset(Point::new(range.end.row + 1, 0));
+            let start = snapshot.point_to_offset(MultiBufferPoint::new(range.start.row(), 0));
+            let end = snapshot.point_to_offset(MultiBufferPoint::new(range.end.row() + 1, 0));
             let start = start.saturating_sub(1);
             let end = snapshot.len().min(end + 1);
             cursor.seek(&start, Bias::Right);
@@ -2028,7 +2161,7 @@ impl MultiBuffer {
 
     pub fn expand_or_collapse_diff_hunks_inner(
         &mut self,
-        ranges: impl IntoIterator<Item = (Range<Point>, ExcerptId)>,
+        ranges: impl IntoIterator<Item = (Range<MultiBufferPoint>, ExcerptId)>,
         expand: bool,
         cx: &mut Context<Self>,
     ) {
@@ -2092,8 +2225,8 @@ impl MultiBuffer {
             let end_excerpt_id = range.end.excerpt_id;
             let range = range.to_point(&snapshot);
             let mut peek_end = range.end;
-            if range.end.row < snapshot.max_row().0 {
-                peek_end = Point::new(range.end.row + 1, 0);
+            if range.end.row() < snapshot.max_row() {
+                peek_end = MultiBufferPoint::new(range.end.row() + 1, 0);
             };
             (range.start..peek_end, end_excerpt_id)
         });
@@ -3070,7 +3203,7 @@ impl MultiBufferSnapshot {
     }
 
     fn reversed_chunks_in_range(&self, range: Range<usize>) -> ReversedMultiBufferChunks<'_> {
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&range.end);
         let current_chunks = cursor.region().as_ref().map(|region| {
             let start_overshoot = range.start.saturating_sub(region.range.start);
@@ -3098,8 +3231,10 @@ impl MultiBufferSnapshot {
     }
 
     pub fn is_line_blank(&self, row: MultiBufferRow) -> bool {
-        self.text_for_range(Point::new(row.0, 0)..Point::new(row.0, self.line_len(row)))
-            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
+        self.text_for_range(
+            MultiBufferPoint::new(row, 0)..MultiBufferPoint::new(row, self.line_len(row)),
+        )
+        .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
     }
 
     pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
@@ -3125,32 +3260,35 @@ impl MultiBufferSnapshot {
         range: Range<T>,
     ) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
         let query_range = range.start.to_point(self)..range.end.to_point(self);
-        self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
-            let diff = self.diffs.get(&buffer.remote_id())?;
-            let buffer_start = buffer.anchor_before(buffer_range.start);
-            let buffer_end = buffer.anchor_after(buffer_range.end);
-            Some(
-                diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
-                    .filter_map(|hunk| {
-                        if hunk.is_created_file() && !self.all_diff_hunks_expanded {
-                            return None;
-                        }
-                        Some((hunk.range.clone(), hunk))
-                    }),
-            )
-        })
-        .filter_map(move |(range, hunk, excerpt)| {
+        self.lift_buffer_metadata::<Point, _, _, _>(
+            query_range.clone(),
+            move |buffer, buffer_range| {
+                let diff = self.diffs.get(&buffer.remote_id())?;
+                let buffer_start = buffer.anchor_before(buffer_range.start);
+                let buffer_end = buffer.anchor_after(buffer_range.end);
+                Some(
+                    diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
+                        .filter_map(|hunk| {
+                            if hunk.is_created_file() && !self.all_diff_hunks_expanded {
+                                return None;
+                            }
+                            Some((hunk.range.clone(), hunk))
+                        }),
+                )
+            },
+        )
+        .filter_map(move |(range, _, hunk, excerpt)| {
             if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
             {
                 return None;
             }
-            let end_row = if range.end.column == 0 {
-                range.end.row
+            let end_row = if range.end.column() == 0 {
+                range.end.row()
             } else {
-                range.end.row + 1
+                range.end.row() + 1
             };
             Some(MultiBufferDiffHunk {
-                row_range: MultiBufferRow(range.start.row)..MultiBufferRow(end_row),
+                row_range: range.start.row()..end_row,
                 buffer_id: excerpt.buffer_id,
                 excerpt_id: excerpt.id,
                 buffer_range: hunk.buffer_range.clone(),
@@ -3165,7 +3303,7 @@ impl MultiBufferSnapshot {
         range: Range<T>,
     ) -> impl Iterator<Item = ExcerptId> + '_ {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&range.start);
         std::iter::from_fn(move || {
             let region = cursor.region()?;
@@ -3182,7 +3320,7 @@ impl MultiBufferSnapshot {
         range: Range<T>,
     ) -> impl Iterator<Item = BufferId> + '_ {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&range.start);
         std::iter::from_fn(move || {
             let region = cursor.region()?;
@@ -3210,7 +3348,7 @@ impl MultiBufferSnapshot {
         let start = range.start.to_offset(self);
         let end = range.end.to_offset(self);
 
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&start);
 
         let mut result: Vec<(&BufferSnapshot, Range<usize>, ExcerptId)> = Vec::new();
@@ -3249,7 +3387,7 @@ impl MultiBufferSnapshot {
         let start = range.start.to_offset(self);
         let end = range.end.to_offset(self);
 
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&start);
 
         std::iter::from_fn(move || {
@@ -3295,18 +3433,19 @@ impl MultiBufferSnapshot {
     ///
     /// The returned iterator yields each of these metadata items, paired with its range in
     /// multi-buffer coordinates.
-    fn lift_buffer_metadata<'a, D, M, I>(
+    fn lift_buffer_metadata<'a, LBD, MBD, M, I>(
         &'a self,
-        query_range: Range<D>,
-        get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range<D>) -> Option<I>,
-    ) -> impl Iterator<Item = (Range<D>, M, &'a Excerpt)> + 'a
+        query_range: Range<MBD>,
+        get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range<LBD>) -> Option<I>,
+    ) -> impl Iterator<Item = (Range<MBD>, Range<LBD>, M, &'a Excerpt)> + 'a
     where
-        I: Iterator<Item = (Range<D>, M)> + 'a,
-        D: TextDimension + Ord + Sub<D, Output = D>,
+        LBD: LangBufferDimension,
+        MBD: MultiBufferDimension<Distance = LBD::Distance> + 'a,
+        I: Iterator<Item = (Range<LBD>, M)> + 'a,
     {
-        let max_position = D::from_text_summary(&self.text_summary());
+        let max_position = MBD::max_position(self);
         let mut current_excerpt_metadata: Option<(ExcerptId, I)> = None;
-        let mut cursor = self.cursor::<D>();
+        let mut cursor = self.cursor::<LBD, MBD>();
 
         // Find the excerpt and buffer offset where the given range ends.
         cursor.seek(&query_range.end);
@@ -3315,11 +3454,11 @@ impl MultiBufferSnapshot {
             if region.is_main_buffer {
                 let mut buffer_end = region.buffer_range.start;
                 let overshoot = if query_range.end > region.range.start {
-                    query_range.end - region.range.start
+                    query_range.end.distance_to(region.range.start)
                 } else {
-                    D::default()
+                    LBD::Distance::default()
                 };
-                buffer_end.add_assign(&overshoot);
+                buffer_end.add_distance(overshoot);
                 range_end = Some((region.excerpt.id, buffer_end));
                 break;
             }
@@ -3329,7 +3468,7 @@ impl MultiBufferSnapshot {
         cursor.seek(&query_range.start);
 
         if let Some(region) = cursor.region().filter(|region| !region.is_main_buffer)
-            && region.range.start > D::zero(())
+            && region.range.start > MBD::default()
         {
             cursor.prev()
         }
@@ -3353,14 +3492,14 @@ impl MultiBufferSnapshot {
                     if region.is_main_buffer {
                         buffer_start = region.buffer_range.start;
                         if query_range.start > region.range.start {
-                            let overshoot = query_range.start - region.range.start;
-                            buffer_start.add_assign(&overshoot);
+                            let overshoot = query_range.start.distance_to(region.range.start);
+                            buffer_start.add_distance(overshoot);
                         }
                         buffer_start = buffer_start.min(region.buffer_range.end);
                     } else {
                         buffer_start = cursor.main_buffer_position()?;
                     };
-                    let mut buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
+                    let mut buffer_end = excerpt.range.context.end.summary::<LBD>(&excerpt.buffer);
                     if let Some((end_excerpt_id, end_buffer_offset)) = range_end
                         && excerpt.id == end_excerpt_id
                     {
@@ -3378,7 +3517,7 @@ impl MultiBufferSnapshot {
                 {
                     // Find the multibuffer regions that contain the start and end of
                     // the metadata item's range.
-                    if metadata_buffer_range.start > D::default() {
+                    if metadata_buffer_range.start > LBD::default() {
                         while let Some(region) = cursor.region() {
                             if region.is_main_buffer
                                 && (region.buffer_range.end >= metadata_buffer_range.start
@@ -3407,8 +3546,9 @@ impl MultiBufferSnapshot {
                     if start_region.is_main_buffer
                         && metadata_buffer_range.start > region_buffer_start
                     {
-                        start_position
-                            .add_assign(&(metadata_buffer_range.start - region_buffer_start));
+                        start_position.add_distance(
+                            metadata_buffer_range.start.distance_to(region_buffer_start),
+                        );
                         start_position = start_position.min(start_region.range.end);
                     }
 
@@ -3418,14 +3558,20 @@ impl MultiBufferSnapshot {
                         debug_assert!(end_region.is_main_buffer);
                         let region_buffer_start = end_region.buffer_range.start;
                         if metadata_buffer_range.end > region_buffer_start {
-                            end_position
-                                .add_assign(&(metadata_buffer_range.end - region_buffer_start));
+                            end_position.add_distance(
+                                metadata_buffer_range.end.distance_to(region_buffer_start),
+                            );
                         }
                         end_position = end_position.min(end_region.range.end);
                     }
 
                     if start_position <= query_range.end && end_position >= query_range.start {
-                        return Some((start_position..end_position, metadata, excerpt));
+                        return Some((
+                            start_position..end_position,
+                            metadata_buffer_range,
+                            metadata,
+                            excerpt,
+                        ));
                     }
                 }
                 // When there are no more metadata items for this excerpt, move to the next excerpt.
@@ -3445,7 +3591,8 @@ impl MultiBufferSnapshot {
     pub fn diff_hunk_before<T: ToOffset>(&self, position: T) -> Option<MultiBufferRow> {
         let offset = position.to_offset(self);
 
-        let mut cursor = self.cursor::<DimensionPair<usize, Point>>();
+        let mut cursor =
+            self.cursor::<usize, DimensionPair<usize, OutputDimension<MultiBufferPoint>>>();
         cursor.seek(&DimensionPair {
             key: offset,
             value: None,
@@ -3474,7 +3621,7 @@ impl MultiBufferSnapshot {
                 let start =
                     Anchor::in_buffer(excerpt.id, excerpt.buffer_id, hunk.buffer_range.start)
                         .to_point(self);
-                return Some(MultiBufferRow(start.row));
+                return Some(start.row());
             }
         }
 
@@ -3492,7 +3639,7 @@ impl MultiBufferSnapshot {
             };
             let start = Anchor::in_buffer(excerpt.id, excerpt.buffer_id, hunk.buffer_range.start)
                 .to_point(self);
-            return Some(MultiBufferRow(start.row));
+            return Some(start.row());
         }
     }
 
@@ -3595,7 +3742,7 @@ impl MultiBufferSnapshot {
 
     pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes<'_> {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut excerpts = self.cursor::<usize>();
+        let mut excerpts = self.cursor::<usize, usize>();
         excerpts.seek(&range.start);
 
         let mut chunk;
@@ -3644,10 +3791,10 @@ impl MultiBufferSnapshot {
     }
 
     pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> {
-        let mut cursor = self.cursor::<Point>();
-        cursor.seek(&Point::new(start_row.0, 0));
+        let mut cursor = self.cursor::<Point, MultiBufferPoint>();
+        cursor.seek(&MultiBufferPoint::new(start_row, 0));
         let mut result = MultiBufferRows {
-            point: Point::new(0, 0),
+            point: MultiBufferPoint::new(MultiBufferRow(0), 0),
             is_empty: self.excerpts.is_empty(),
             is_singleton: self.is_singleton(),
             cursor,
@@ -3681,7 +3828,7 @@ impl MultiBufferSnapshot {
         self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset)
     }
 
-    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
+    pub fn clip_point(&self, point: MultiBufferPoint, bias: Bias) -> MultiBufferPoint {
         self.clip_dimension(point, bias, text::BufferSnapshot::clip_point)
     }
 
@@ -3695,7 +3842,7 @@ impl MultiBufferSnapshot {
         })
     }
 
-    pub fn offset_to_point(&self, offset: usize) -> Point {
+    pub fn offset_to_point(&self, offset: usize) -> MultiBufferPoint {
         self.convert_dimension(offset, text::BufferSnapshot::offset_to_point)
     }
 
@@ -3703,19 +3850,19 @@ impl MultiBufferSnapshot {
         self.convert_dimension(offset, text::BufferSnapshot::offset_to_point_utf16)
     }
 
-    pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
+    pub fn point_to_point_utf16(&self, point: MultiBufferPoint) -> PointUtf16 {
         self.convert_dimension(point, text::BufferSnapshot::point_to_point_utf16)
     }
 
-    pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
+    pub fn point_utf16_to_point(&self, point: PointUtf16) -> MultiBufferPoint {
         self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_point)
     }
 
-    pub fn point_to_offset(&self, point: Point) -> usize {
+    pub fn point_to_offset(&self, point: MultiBufferPoint) -> usize {
         self.convert_dimension(point, text::BufferSnapshot::point_to_offset)
     }
 
-    pub fn point_to_offset_utf16(&self, point: Point) -> OffsetUtf16 {
+    pub fn point_to_offset_utf16(&self, point: MultiBufferPoint) -> OffsetUtf16 {
         self.convert_dimension(point, text::BufferSnapshot::point_to_offset_utf16)
     }
 
@@ -3735,61 +3882,66 @@ impl MultiBufferSnapshot {
         self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset_utf16)
     }
 
-    fn clip_dimension<D>(
-        &self,
-        position: D,
+    fn clip_dimension<'a, LBD, MBD>(
+        &'a self,
+        position: MBD,
         bias: Bias,
-        clip_buffer_position: fn(&text::BufferSnapshot, D, Bias) -> D,
-    ) -> D
+        clip_buffer_position: fn(&text::BufferSnapshot, LBD, Bias) -> LBD,
+    ) -> MBD
     where
-        D: TextDimension + Ord + Sub<D, Output = D>,
+        LBD: LangBufferDimension,
+        MBD: MultiBufferDimension<Distance = LBD::Distance>,
     {
-        let mut cursor = self.cursor();
+        let mut cursor = self.cursor::<LBD, MBD>();
         cursor.seek(&position);
         if let Some(region) = cursor.region() {
             if position >= region.range.end {
                 return region.range.end;
             }
-            let overshoot = position - region.range.start;
+            let overshoot = position.distance_to(region.range.start);
             let mut buffer_position = region.buffer_range.start;
-            buffer_position.add_assign(&overshoot);
+            buffer_position.add_distance(overshoot);
             let clipped_buffer_position =
                 clip_buffer_position(region.buffer, buffer_position, bias);
             let mut position = region.range.start;
-            position.add_assign(&(clipped_buffer_position - region.buffer_range.start));
+            position.add_distance(clipped_buffer_position.distance_to(region.buffer_range.start));
             position
         } else {
-            D::from_text_summary(&self.text_summary())
+            MBD::max_position(self)
         }
     }
 
-    fn convert_dimension<D1, D2>(
-        &self,
-        key: D1,
-        convert_buffer_dimension: fn(&text::BufferSnapshot, D1) -> D2,
-    ) -> D2
+    fn convert_dimension<'a, MBD, LBD, MBD2, LBD2>(
+        &'a self,
+        position: MBD,
+        convert_buffer_dimension: fn(&text::BufferSnapshot, LBD) -> LBD2,
+    ) -> MBD2
     where
-        D1: TextDimension + Ord + Sub<D1, Output = D1>,
-        D2: TextDimension + Ord + Sub<D2, Output = D2>,
+        MBD: MultiBufferDimension<Distance = LBD::Distance>,
+        LBD: LangBufferDimension,
+        MBD2: MultiBufferDimension<Distance = LBD2>,
+        LBD2: LangBufferDimension<Distance = LBD2>,
     {
-        let mut cursor = self.cursor::<DimensionPair<D1, D2>>();
-        cursor.seek(&DimensionPair { key, value: None });
+        let mut cursor = self.cursor::<DimensionPair<LBD, LBD2>, DimensionPair<MBD, MBD2>>();
+        cursor.seek(&DimensionPair {
+            key: position,
+            value: None,
+        });
         if let Some(region) = cursor.region() {
-            if key >= region.range.end.key {
+            if position >= region.range.end.key {
                 return region.range.end.value.unwrap();
             }
-            let start_key = region.range.start.key;
-            let start_value = region.range.start.value.unwrap();
-            let buffer_start_key = region.buffer_range.start.key;
-            let buffer_start_value = region.buffer_range.start.value.unwrap();
-            let mut buffer_key = buffer_start_key;
-            buffer_key.add_assign(&(key - start_key));
-            let buffer_value = convert_buffer_dimension(region.buffer, buffer_key);
-            let mut result = start_value;
-            result.add_assign(&(buffer_value - buffer_start_value));
+            let region_start = region.range.start.key;
+            let mut buffer_position = region.buffer_range.start.key;
+            buffer_position.add_distance(position.distance_to(region_start));
+            let buffer_position_in_2 = convert_buffer_dimension(region.buffer, buffer_position);
+            let mut result = region.range.start.value.unwrap();
+            result.add_distance(
+                buffer_position_in_2.distance_to(region.buffer_range.start.value.unwrap()),
+            );
             result
         } else {
-            D2::from_text_summary(&self.text_summary())
+            MBD2::max_position(&self)
         }
     }
 
@@ -3798,7 +3950,7 @@ impl MultiBufferSnapshot {
         point: T,
     ) -> Option<(&BufferSnapshot, usize)> {
         let offset = point.to_offset(self);
-        let mut cursor = self.cursor::<usize>();
+        let mut cursor = self.cursor::<usize, usize>();
         cursor.seek(&offset);
         let region = cursor.region()?;
         let overshoot = offset - region.range.start;
@@ -3829,13 +3981,14 @@ impl MultiBufferSnapshot {
 
     pub fn point_to_buffer_point(
         &self,
-        point: Point,
+        point: MultiBufferPoint,
     ) -> Option<(&BufferSnapshot, Point, ExcerptId)> {
-        let mut cursor = self.cursor::<Point>();
+        let mut cursor = self.cursor::<Point, MultiBufferPoint>();
         cursor.seek(&point);
         let region = cursor.region()?;
-        let overshoot = point - region.range.start;
-        let buffer_point = region.buffer_range.start + overshoot;
+        let overshoot = point.distance_to(region.range.start);
+        let mut buffer_point = region.buffer_range.start;
+        buffer_point.add_distance(overshoot);
         let excerpt = cursor.excerpt()?;
         if buffer_point == region.buffer.max_point() + Point::new(1, 0)
             && region.has_trailing_newline
@@ -3850,37 +4003,38 @@ impl MultiBufferSnapshot {
 
     pub fn suggested_indents(
         &self,
-        rows: impl IntoIterator<Item = u32>,
+        rows: impl IntoIterator<Item = MultiBufferRow>,
         cx: &App,
     ) -> BTreeMap<MultiBufferRow, IndentSize> {
         let mut result = BTreeMap::new();
 
         let mut rows_for_excerpt = Vec::new();
-        let mut cursor = self.cursor::<Point>();
+        let mut cursor = self.cursor::<Point, MultiBufferPoint>();
         let mut rows = rows.into_iter().peekable();
         let mut prev_row = u32::MAX;
         let mut prev_language_indent_size = IndentSize::default();
 
         while let Some(row) = rows.next() {
-            cursor.seek(&Point::new(row, 0));
+            cursor.seek(&MultiBufferPoint::new(row, 0));
             let Some(region) = cursor.region() else {
                 continue;
             };
 
             // Retrieve the language and indent size once for each disjoint region being indented.
-            let single_indent_size = if row.saturating_sub(1) == prev_row {
+            let single_indent_size = if row.0.saturating_sub(1) == prev_row {
                 prev_language_indent_size
             } else {
+                // FIXME these are mutlibuffer rows used for lang buffers??
                 region
                     .buffer
-                    .language_indent_size_at(Point::new(row, 0), cx)
+                    .language_indent_size_at(Point::new(row.0, 0), cx)
             };
             prev_language_indent_size = single_indent_size;
-            prev_row = row;
+            prev_row = row.0;
 
             let start_buffer_row = region.buffer_range.start.row;
-            let start_multibuffer_row = region.range.start.row;
-            let end_multibuffer_row = region.range.end.row;
+            let start_multibuffer_row = region.range.start.row();
+            let end_multibuffer_row = region.range.end.row();
 
             rows_for_excerpt.push(row);
             while let Some(next_row) = rows.peek().copied() {
@@ -3894,13 +4048,13 @@ impl MultiBufferSnapshot {
 
             let buffer_rows = rows_for_excerpt
                 .drain(..)
-                .map(|row| start_buffer_row + row - start_multibuffer_row);
+                .map(|row| start_buffer_row + row.0 - start_multibuffer_row.0);
             let buffer_indents = region
                 .buffer
                 .suggested_indents(buffer_rows, single_indent_size);
             let multibuffer_indents = buffer_indents.into_iter().map(|(row, indent)| {
                 (
-                    MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
+                    MultiBufferRow(start_multibuffer_row.0 + row - start_buffer_row),
                     indent,
                 )
             });
@@ -3935,12 +4089,12 @@ impl MultiBufferSnapshot {
         let mut indent = self.indent_size_for_line(row).chars().collect::<String>();
 
         if self.language_settings(cx).extend_comment_on_newline
-            && let Some(language_scope) = self.language_scope_at(Point::new(row.0, 0))
+            && let Some(language_scope) = self.language_scope_at(MultiBufferPoint::new(row, 0))
         {
             let delimiters = language_scope.line_comment_prefixes();
             for delimiter in delimiters {
                 if *self
-                    .chars_at(Point::new(row.0, indent.len() as u32))
+                    .chars_at(MultiBufferPoint::new(row, indent.len() as u32))
                     .take(delimiter.chars().count())
                     .collect::<String>()
                     .as_str()
@@ -3992,12 +4146,13 @@ impl MultiBufferSnapshot {
         &self,
         row: MultiBufferRow,
     ) -> Option<(&BufferSnapshot, Range<Point>)> {
-        let mut cursor = self.cursor::<Point>();
-        let point = Point::new(row.0, 0);
+        let mut cursor = self.cursor::<Point, MultiBufferPoint>();
+        let point = MultiBufferPoint::new(row, 0);
         cursor.seek(&point);
         let region = cursor.region()?;
-        let overshoot = point.min(region.range.end) - region.range.start;
-        let buffer_point = region.buffer_range.start + overshoot;
+        let overshoot = point.min(region.range.end).distance_to(region.range.start);
+        let mut buffer_point = region.buffer_range.start;
+        buffer_point.add_distance(overshoot);
         if buffer_point.row > region.buffer_range.end.row {
             return None;
         }
@@ -4007,8 +4162,8 @@ impl MultiBufferSnapshot {
         Some((region.buffer, line_start..line_end))
     }
 
-    pub fn max_point(&self) -> Point {
-        self.text_summary().lines
+    pub fn max_point(&self) -> MultiBufferPoint {
+        MultiBufferPoint(self.text_summary().lines)
     }
 
     pub fn max_row(&self) -> MultiBufferRow {
@@ -4117,7 +4272,7 @@ impl MultiBufferSnapshot {
         result
     }
 
-    fn text_summary_for_excerpt_offset_range<D>(&self, mut range: Range<ExcerptOffset>) -> D
+    fn text_summary_for_excerpt_offset_range<'a, D>(&self, mut range: Range<ExcerptOffset>) -> D
     where
         D: TextDimension,
     {
@@ -4169,24 +4324,28 @@ impl MultiBufferSnapshot {
         summary
     }
 
-    pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
+    pub fn summary_for_anchor<MBD>(&self, anchor: &Anchor) -> MBD
     where
-        D: TextDimension + Ord + Sub<D, Output = D>,
+        MBD: MultiBufferDimension<
+            LangBufferDimension : LangBufferDimension,
+            Distance = <<MBD as MultiBufferDimension>::LangBufferDimension as LangBufferDimension>::Distance,
+        >,
     {
         self.summaries_for_anchors([anchor])[0]
     }
 
-    fn resolve_summary_for_anchor<D>(
+    fn resolve_summary_for_anchor<LBD, MBD>(
         &self,
         anchor: &Anchor,
-        excerpt_position: D,
+        excerpt_position: LBD,
         diff_transforms: &mut Cursor<
             DiffTransform,
-            Dimensions<ExcerptDimension<D>, OutputDimension<D>>,
+            Dimensions<ExcerptDimension<LBD>, OutputDimension<MBD>>,
         >,
-    ) -> D
+    ) -> MBD
     where
-        D: TextDimension + Ord + Sub<D, Output = D>,
+        LBD: LangBufferDimension,
+        MBD: MultiBufferDimension<Distance = LBD::Distance>,
     {
         loop {
             let transform_end_position = diff_transforms.end().0.0;
@@ -4213,10 +4372,11 @@ impl MultiBufferSnapshot {
                         if base_text_offset >= base_text_byte_range.start
                             && base_text_offset <= base_text_byte_range.end
                         {
-                            let position_in_hunk = base_text.text_summary_for_range::<D, _>(
-                                base_text_byte_range.start..base_text_offset,
-                            );
-                            position.add_assign(&position_in_hunk);
+                            let position_in_hunk = base_text
+                                .text_summary_for_range::<MBD::Distance, _>(
+                                    base_text_byte_range.start..base_text_offset,
+                                );
+                            position.add_distance(position_in_hunk);
                         } else if at_transform_end {
                             diff_transforms.next();
                             continue;
@@ -4228,8 +4388,8 @@ impl MultiBufferSnapshot {
                         diff_transforms.next();
                         continue;
                     }
-                    let overshoot = excerpt_position - diff_transforms.start().0.0;
-                    position.add_assign(&overshoot);
+                    let overshoot = excerpt_position.distance_to(diff_transforms.start().0.0);
+                    position.add_distance(overshoot);
                 }
             }
 
@@ -4274,16 +4434,21 @@ impl MultiBufferSnapshot {
         excerpt_id
     }
 
-    pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
+    pub fn summaries_for_anchors<'a, MBD, I>(&'a self, anchors: I) -> Vec<MBD>
     where
-        D: TextDimension + Ord + Sub<D, Output = D>,
+        MBD: MultiBufferDimension<
+            LangBufferDimension : LangBufferDimension,
+            Distance = <<MBD as MultiBufferDimension>::LangBufferDimension as LangBufferDimension>::Distance,
+        >,
         I: 'a + IntoIterator<Item = &'a Anchor>,
     {
         let mut anchors = anchors.into_iter().peekable();
         let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
         let mut diff_transforms_cursor = self
             .diff_transforms
-            .cursor::<Dimensions<ExcerptDimension<D>, OutputDimension<D>>>(());
+            .cursor::<Dimensions<ExcerptDimension<MBD::LangBufferDimension>, OutputDimension<MBD>>>(
+                (),
+            );
         diff_transforms_cursor.next();
 
         let mut summaries = Vec::new();

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -213,7 +213,11 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
     );
 
     assert_eq!(
-        boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(0), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(4), 2),
+            &snapshot
+        ),
         &[
             (MultiBufferRow(0), "bbbb\nccccc".to_string(), true),
             (MultiBufferRow(2), "ddd\neeee".to_string(), false),
@@ -221,35 +225,67 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
         ]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(0), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(2), 0),
+            &snapshot
+        ),
         &[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(1), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(1), 5),
+            &snapshot
+        ),
         &[]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(1), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(2), 0),
+            &snapshot
+        ),
         &[]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(1), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(4), 0),
+            &snapshot
+        ),
         &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(1), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(4), 0),
+            &snapshot
+        ),
         &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(2), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(3), 0),
+            &snapshot
+        ),
         &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(4), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(4), 2),
+            &snapshot
+        ),
         &[(MultiBufferRow(4), "jj".to_string(), true)]
     );
     assert_eq!(
-        boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
+        boundaries_in_range(
+            MultiBufferPoint::new(MultiBufferRow(4), 2)
+                ..MultiBufferPoint::new(MultiBufferRow(4), 2),
+            &snapshot
+        ),
         &[]
     );
 
@@ -288,24 +324,24 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
 
     let snapshot = multibuffer.read(cx).snapshot(cx);
     assert_eq!(
-        snapshot.clip_point(Point::new(0, 5), Bias::Left),
-        Point::new(0, 4)
+        snapshot.clip_point(MultiBufferPoint::new(MultiBufferRow(0), 5), Bias::Left),
+        MultiBufferPoint::new(MultiBufferRow(0), 4)
     );
     assert_eq!(
-        snapshot.clip_point(Point::new(0, 5), Bias::Right),
-        Point::new(0, 4)
+        snapshot.clip_point(MultiBufferPoint::new(MultiBufferRow(0), 5), Bias::Right),
+        MultiBufferPoint::new(MultiBufferRow(0), 4)
     );
     assert_eq!(
-        snapshot.clip_point(Point::new(5, 1), Bias::Right),
-        Point::new(5, 1)
+        snapshot.clip_point(MultiBufferPoint::new(MultiBufferRow(5), 1), Bias::Right),
+        MultiBufferPoint::new(MultiBufferRow(5), 1)
     );
     assert_eq!(
-        snapshot.clip_point(Point::new(5, 2), Bias::Right),
-        Point::new(5, 2)
+        snapshot.clip_point(MultiBufferPoint::new(MultiBufferRow(5), 2), Bias::Right),
+        MultiBufferPoint::new(MultiBufferRow(5), 2)
     );
     assert_eq!(
-        snapshot.clip_point(Point::new(5, 3), Bias::Right),
-        Point::new(5, 2)
+        snapshot.clip_point(MultiBufferPoint::new(MultiBufferRow(5), 3), Bias::Right),
+        MultiBufferPoint::new(MultiBufferRow(5), 2)
     );
 
     let snapshot = multibuffer.update(cx, |multibuffer, cx| {
@@ -327,7 +363,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
     );
 
     fn boundaries_in_range(
-        range: Range<Point>,
+        range: Range<MultiBufferPoint>,
         snapshot: &MultiBufferSnapshot,
     ) -> Vec<(MultiBufferRow, String, bool)> {
         snapshot
@@ -358,8 +394,12 @@ fn test_diff_boundary_anchors(cx: &mut TestAppContext) {
     multibuffer.update(cx, |multibuffer, cx| multibuffer.add_diff(diff, cx));
 
     let (before, after) = multibuffer.update(cx, |multibuffer, cx| {
-        let before = multibuffer.snapshot(cx).anchor_before(Point::new(1, 0));
-        let after = multibuffer.snapshot(cx).anchor_after(Point::new(1, 0));
+        let before = multibuffer
+            .snapshot(cx)
+            .anchor_before(MultiBufferPoint::new(MultiBufferRow(1), 0));
+        let after = multibuffer
+            .snapshot(cx)
+            .anchor_after(MultiBufferPoint::new(MultiBufferRow(1), 0));
         multibuffer.set_all_diff_hunks_expanded(cx);
         (before, after)
     });
@@ -381,11 +421,20 @@ fn test_diff_boundary_anchors(cx: &mut TestAppContext) {
 
     multibuffer.update(cx, |multibuffer, cx| {
         let snapshot = multibuffer.snapshot(cx);
-        assert_eq!(before.to_point(&snapshot), Point::new(1, 0));
-        assert_eq!(after.to_point(&snapshot), Point::new(2, 0));
         assert_eq!(
-            vec![Point::new(1, 0), Point::new(2, 0),],
-            snapshot.summaries_for_anchors::<Point, _>(&[before, after]),
+            before.to_point(&snapshot),
+            MultiBufferPoint::new(MultiBufferRow(1), 0)
+        );
+        assert_eq!(
+            after.to_point(&snapshot),
+            MultiBufferPoint::new(MultiBufferRow(2), 0)
+        );
+        assert_eq!(
+            vec![
+                MultiBufferPoint::new(MultiBufferRow(1), 0),
+                MultiBufferPoint::new(MultiBufferRow(2), 0),
+            ],
+            snapshot.summaries_for_anchors::<MultiBufferPoint, _>(&[before, after]),
         )
     })
 }
@@ -426,19 +475,22 @@ fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
 
     assert_eq!(
         snapshot
-            .diff_hunks_in_range(Point::new(1, 0)..Point::MAX)
+            .diff_hunks_in_range(MultiBufferPoint::new(MultiBufferRow(1), 0)..MultiBufferPoint::MAX)
             .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0)
             .collect::<Vec<_>>(),
         vec![1..3, 4..6, 7..8]
     );
 
-    assert_eq!(snapshot.diff_hunk_before(Point::new(1, 1)), None,);
     assert_eq!(
-        snapshot.diff_hunk_before(Point::new(7, 0)),
+        snapshot.diff_hunk_before(MultiBufferPoint::new(MultiBufferRow(1), 1)),
+        None,
+    );
+    assert_eq!(
+        snapshot.diff_hunk_before(MultiBufferPoint::new(MultiBufferRow(7), 0)),
         Some(MultiBufferRow(4))
     );
     assert_eq!(
-        snapshot.diff_hunk_before(Point::new(4, 0)),
+        snapshot.diff_hunk_before(MultiBufferPoint::new(MultiBufferRow(4), 0)),
         Some(MultiBufferRow(1))
     );
 
@@ -461,11 +513,11 @@ fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
     );
 
     assert_eq!(
-        snapshot.diff_hunk_before(Point::new(2, 0)),
+        snapshot.diff_hunk_before(MultiBufferPoint::new(MultiBufferRow(2), 0)),
         Some(MultiBufferRow(1)),
     );
     assert_eq!(
-        snapshot.diff_hunk_before(Point::new(4, 0)),
+        snapshot.diff_hunk_before(MultiBufferPoint::new(MultiBufferRow(4), 0)),
         Some(MultiBufferRow(2))
     );
 }
@@ -508,7 +560,15 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
 
     // Insert a newline within an insertion hunk
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.edit([(Point::new(2, 0)..Point::new(2, 0), "__\n__")], None, cx);
+        multibuffer.edit(
+            [(
+                MultiBufferPoint::new(MultiBufferRow(2), 0)
+                    ..MultiBufferPoint::new(MultiBufferRow(2), 0),
+                "__\n__",
+            )],
+            None,
+            cx,
+        );
     });
     assert_new_snapshot(
         &multibuffer,
@@ -531,7 +591,15 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
 
     // Delete the newline before a deleted hunk.
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.edit([(Point::new(5, 4)..Point::new(6, 0), "")], None, cx);
+        multibuffer.edit(
+            [(
+                MultiBufferPoint::new(MultiBufferRow(5), 4)
+                    ..MultiBufferPoint::new(MultiBufferRow(6), 0),
+                "",
+            )],
+            None,
+            cx,
+        );
     });
     assert_new_snapshot(
         &multibuffer,
@@ -573,7 +641,15 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
     // Cannot (yet) insert at the beginning of a deleted hunk.
     // (because it would put the newline in the wrong place)
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.edit([(Point::new(6, 0)..Point::new(6, 0), "\n")], None, cx);
+        multibuffer.edit(
+            [(
+                MultiBufferPoint::new(MultiBufferRow(6), 0)
+                    ..MultiBufferPoint::new(MultiBufferRow(6), 0),
+                "\n",
+            )],
+            None,
+            cx,
+        );
     });
     assert_new_snapshot(
         &multibuffer,
@@ -596,7 +672,15 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
 
     // Replace a range that ends in a deleted hunk.
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.edit([(Point::new(5, 2)..Point::new(6, 2), "fty-")], None, cx);
+        multibuffer.edit(
+            [(
+                MultiBufferPoint::new(MultiBufferRow(5), 2)
+                    ..MultiBufferPoint::new(MultiBufferRow(6), 2),
+                "fty-",
+            )],
+            None,
+            cx,
+        );
     });
     assert_new_snapshot(
         &multibuffer,
@@ -746,7 +830,9 @@ fn test_expand_excerpts(cx: &mut App) {
     drop(snapshot);
 
     multibuffer.update(cx, |multibuffer, cx| {
-        let line_zero = multibuffer.snapshot(cx).anchor_before(Point::new(0, 0));
+        let line_zero = multibuffer
+            .snapshot(cx)
+            .anchor_before(MultiBufferPoint::new(MultiBufferRow(0), 0));
         multibuffer.expand_excerpts(
             multibuffer.excerpt_ids(),
             1,
@@ -754,7 +840,7 @@ fn test_expand_excerpts(cx: &mut App) {
             cx,
         );
         let snapshot = multibuffer.snapshot(cx);
-        let line_two = snapshot.anchor_before(Point::new(2, 0));
+        let line_two = snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(2), 0));
         assert_eq!(line_two.cmp(&line_zero, &snapshot), cmp::Ordering::Greater);
     });
 
@@ -816,9 +902,12 @@ async fn test_set_anchored_excerpts_for_path(cx: &mut TestAppContext) {
             .map(|range| range.to_point(&snapshot_1))
             .collect::<Vec<_>>(),
         vec![
-            Point::new(2, 2)..Point::new(3, 2),
-            Point::new(6, 1)..Point::new(6, 3),
-            Point::new(11, 0)..Point::new(11, 0),
+            MultiBufferPoint::new(MultiBufferRow(2), 2)
+                ..MultiBufferPoint::new(MultiBufferRow(3), 2),
+            MultiBufferPoint::new(MultiBufferRow(6), 1)
+                ..MultiBufferPoint::new(MultiBufferRow(6), 3),
+            MultiBufferPoint::new(MultiBufferRow(11), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(11), 0),
         ]
     );
     let anchor_ranges_2 = multibuffer
@@ -839,8 +928,10 @@ async fn test_set_anchored_excerpts_for_path(cx: &mut TestAppContext) {
             .map(|range| range.to_point(&snapshot_2))
             .collect::<Vec<_>>(),
         vec![
-            Point::new(16, 1)..Point::new(17, 1),
-            Point::new(22, 0)..Point::new(22, 2)
+            MultiBufferPoint::new(MultiBufferRow(16), 1)
+                ..MultiBufferPoint::new(MultiBufferRow(17), 1),
+            MultiBufferPoint::new(MultiBufferRow(22), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(22), 2)
         ]
     );
 
@@ -920,7 +1011,10 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
     assert_eq!(snapshot.text(), "a\nb\nc\n");
 
     let hunk = snapshot
-        .diff_hunks_in_range(Point::new(1, 1)..Point::new(1, 1))
+        .diff_hunks_in_range(
+            MultiBufferPoint::new(MultiBufferRow(1), 1)
+                ..MultiBufferPoint::new(MultiBufferRow(1), 1),
+        )
         .next()
         .unwrap();
 
@@ -1252,7 +1346,9 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
 
     // Expand the first diff hunk
     multibuffer.update(cx, |multibuffer, cx| {
-        let position = multibuffer.read(cx).anchor_before(Point::new(2, 2));
+        let position = multibuffer
+            .read(cx)
+            .anchor_before(MultiBufferPoint::new(MultiBufferRow(2), 2));
         multibuffer.expand_diff_hunks(vec![position..position], cx)
     });
     assert_new_snapshot(
@@ -1274,8 +1370,12 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
 
     // Expand the second diff hunk
     multibuffer.update(cx, |multibuffer, cx| {
-        let start = multibuffer.read(cx).anchor_before(Point::new(4, 0));
-        let end = multibuffer.read(cx).anchor_before(Point::new(5, 0));
+        let start = multibuffer
+            .read(cx)
+            .anchor_before(MultiBufferPoint::new(MultiBufferRow(4), 0));
+        let end = multibuffer
+            .read(cx)
+            .anchor_before(MultiBufferPoint::new(MultiBufferRow(5), 0));
         multibuffer.expand_diff_hunks(vec![start..end], cx)
     });
     assert_new_snapshot(
@@ -1438,7 +1538,8 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) {
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.expand_diff_hunks(
             vec![
-                snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_before(Point::new(2, 0)),
+                snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(2), 0))
+                    ..snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(2), 0)),
             ],
             cx,
         );
@@ -1489,8 +1590,10 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) {
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.expand_diff_hunks(
             vec![
-                snapshot.anchor_before(Point::new(4, 0))..snapshot.anchor_before(Point::new(4, 0)),
-                snapshot.anchor_before(Point::new(4, 2))..snapshot.anchor_before(Point::new(4, 2)),
+                snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(4), 0))
+                    ..snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(4), 0)),
+                snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(4), 2))
+                    ..snapshot.anchor_before(MultiBufferPoint::new(MultiBufferRow(4), 2)),
             ],
             cx,
         );
@@ -2048,13 +2151,41 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
     );
 
     let buffer_ids_by_range = [
-        (Point::new(0, 0)..Point::new(0, 0), &[id_1] as &[_]),
-        (Point::new(0, 0)..Point::new(2, 0), &[id_1]),
-        (Point::new(2, 0)..Point::new(2, 0), &[id_1]),
-        (Point::new(3, 0)..Point::new(3, 0), &[id_1]),
-        (Point::new(8, 0)..Point::new(9, 0), &[id_1]),
-        (Point::new(8, 0)..Point::new(10, 0), &[id_1, id_2]),
-        (Point::new(9, 0)..Point::new(9, 0), &[id_2]),
+        (
+            MultiBufferPoint::new(MultiBufferRow(0), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(0), 0),
+            &[id_1] as &[_],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(0), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(2), 0),
+            &[id_1],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(2), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(2), 0),
+            &[id_1],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(3), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(3), 0),
+            &[id_1],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(8), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(9), 0),
+            &[id_1],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(8), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(10), 0),
+            &[id_1, id_2],
+        ),
+        (
+            MultiBufferPoint::new(MultiBufferRow(9), 0)
+                ..MultiBufferPoint::new(MultiBufferRow(9), 0),
+            &[id_2],
+        ),
     ];
     for (range, buffer_ids) in buffer_ids_by_range {
         assert_eq!(
@@ -3006,16 +3137,32 @@ fn test_history(cx: &mut App) {
         let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
         multibuffer.edit(
             [
-                (Point::new(0, 0)..Point::new(0, 0), "A"),
-                (Point::new(1, 0)..Point::new(1, 0), "A"),
+                (
+                    MultiBufferPoint::new(MultiBufferRow(0), 0)
+                        ..MultiBufferPoint::new(MultiBufferRow(0), 0),
+                    "A",
+                ),
+                (
+                    MultiBufferPoint::new(MultiBufferRow(1), 0)
+                        ..MultiBufferPoint::new(MultiBufferRow(1), 0),
+                    "A",
+                ),
             ],
             None,
             cx,
         );
         multibuffer.edit(
             [
-                (Point::new(0, 1)..Point::new(0, 1), "B"),
-                (Point::new(1, 1)..Point::new(1, 1), "B"),
+                (
+                    MultiBufferPoint::new(MultiBufferRow(0), 1)
+                        ..MultiBufferPoint::new(MultiBufferRow(0), 1),
+                    "B",
+                ),
+                (
+                    MultiBufferPoint::new(MultiBufferRow(1), 1)
+                        ..MultiBufferPoint::new(MultiBufferRow(1), 1),
+                    "B",
+                ),
             ],
             None,
             cx,
@@ -3025,7 +3172,7 @@ fn test_history(cx: &mut App) {
 
         // Verify edited ranges for transaction 1
         assert_eq!(
-            multibuffer.edited_ranges_for_transaction(transaction_1, cx),
+            multibuffer.edited_ranges_for_transaction::<MultiBufferPoint>(transaction_1, cx),
             &[
                 Point::new(0, 0)..Point::new(0, 2),
                 Point::new(1, 0)..Point::new(1, 2)
@@ -3275,12 +3422,12 @@ fn test_summaries_for_anchors(cx: &mut TestAppContext) {
     let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
 
     let anchor_1 = Anchor::in_buffer(ids[0], id_1, text::Anchor::MIN);
-    let point_1 = snapshot.summaries_for_anchors::<Point, _>([&anchor_1])[0];
-    assert_eq!(point_1, Point::new(0, 0));
+    let point_1 = snapshot.summaries_for_anchors::<MultiBufferPoint, _>([&anchor_1])[0];
+    assert_eq!(point_1, MultiBufferPoint::new(MultiBufferRow(0), 0));
 
     let anchor_2 = Anchor::in_buffer(ids[1], id_2, text::Anchor::MIN);
-    let point_2 = snapshot.summaries_for_anchors::<Point, _>([&anchor_2])[0];
-    assert_eq!(point_2, Point::new(3, 0));
+    let point_2 = snapshot.summaries_for_anchors::<MultiBufferPoint, _>([&anchor_2])[0];
+    assert_eq!(point_2, MultiBufferPoint::new(MultiBufferRow(3), 0));
 }
 
 #[gpui::test]
@@ -3316,19 +3463,26 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
         ),
     );
 
-    assert_eq!(snapshot.max_point(), Point::new(2, 0));
+    assert_eq!(
+        snapshot.max_point(),
+        MultiBufferPoint::new(MultiBufferRow(2), 0)
+    );
     assert_eq!(snapshot.len(), 8);
 
     assert_eq!(
         snapshot
-            .dimensions_from_points::<Point>([Point::new(2, 0)])
+            .dimensions_from_points::<Point>([MultiBufferPoint::new(MultiBufferRow(2), 0)])
             .collect::<Vec<_>>(),
         vec![Point::new(2, 0)]
     );
 
-    let (_, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
+    let (_, translated_offset) = snapshot
+        .point_to_buffer_offset(MultiBufferPoint::new(MultiBufferRow(2), 0))
+        .unwrap();
     assert_eq!(translated_offset, "one\n".len());
-    let (_, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
+    let (_, translated_point, _) = snapshot
+        .point_to_buffer_point(MultiBufferPoint::new(MultiBufferRow(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.
@@ -3360,16 +3514,20 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
 
     assert_eq!(
         snapshot
-            .dimensions_from_points::<Point>([Point::new(2, 0)])
+            .dimensions_from_points::<Point>([MultiBufferPoint::new(MultiBufferRow(2), 0)])
             .collect::<Vec<_>>(),
         vec![Point::new(2, 0)]
     );
 
     let buffer_1_id = buffer_1.read_with(cx, |buffer_1, _| buffer_1.remote_id());
-    let (buffer, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
+    let (buffer, translated_offset) = snapshot
+        .point_to_buffer_offset(MultiBufferPoint::new(MultiBufferRow(2), 0))
+        .unwrap();
     assert_eq!(buffer.remote_id(), buffer_1_id);
     assert_eq!(translated_offset, "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(MultiBufferPoint::new(MultiBufferRow(2), 0))
+        .unwrap();
     assert_eq!(buffer.remote_id(), buffer_1_id);
     assert_eq!(translated_point, Point::new(1, 0));
 }
@@ -3532,12 +3690,12 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
             "clip_offset({offset:?}, Right)"
         );
         assert_eq!(
-            snapshot.offset_to_point(clipped_left),
+            snapshot.offset_to_point(clipped_left).0,
             text.offset_to_point(clipped_left),
             "offset_to_point({clipped_left})"
         );
         assert_eq!(
-            snapshot.offset_to_point(clipped_right),
+            snapshot.offset_to_point(clipped_right).0,
             text.offset_to_point(clipped_right),
             "offset_to_point({clipped_right})"
         );
@@ -3556,32 +3714,34 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
         left_anchors.push(anchor_before);
         right_anchors.push(anchor_after);
         offsets.push(clipped_left);
-        points.push(text.offset_to_point(clipped_left));
+        points.push(MultiBufferPoint::from_point(
+            text.offset_to_point(clipped_left),
+        ));
     }
 
     for row in 0..text.max_point().row {
         for column in 0..text.line_len(row) + 1 {
-            let point = Point { row, column };
+            let point = MultiBufferPoint::from_point(Point { row, column });
             let clipped_left = snapshot.clip_point(point, Bias::Left);
             let clipped_right = snapshot.clip_point(point, Bias::Right);
             assert_eq!(
-                clipped_left,
-                text.clip_point(point, Bias::Left),
+                clipped_left.0,
+                text.clip_point(point.0, Bias::Left),
                 "clip_point({point:?}, Left)"
             );
             assert_eq!(
-                clipped_right,
-                text.clip_point(point, Bias::Right),
+                clipped_right.0,
+                text.clip_point(point.0, Bias::Right),
                 "clip_point({point:?}, Right)"
             );
             assert_eq!(
                 snapshot.point_to_offset(clipped_left),
-                text.point_to_offset(clipped_left),
+                text.point_to_offset(clipped_left.0),
                 "point_to_offset({clipped_left:?})"
             );
             assert_eq!(
                 snapshot.point_to_offset(clipped_right),
-                text.point_to_offset(clipped_right),
+                text.point_to_offset(clipped_right.0),
                 "point_to_offset({clipped_right:?})"
             );
         }
@@ -3593,7 +3753,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
         "left_anchors <-> offsets"
     );
     assert_eq!(
-        snapshot.summaries_for_anchors::<Point, _>(&left_anchors),
+        snapshot.summaries_for_anchors::<MultiBufferPoint, _>(&left_anchors),
         points,
         "left_anchors <-> points"
     );
@@ -3603,7 +3763,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
         "right_anchors <-> offsets"
     );
     assert_eq!(
-        snapshot.summaries_for_anchors::<Point, _>(&right_anchors),
+        snapshot.summaries_for_anchors::<MultiBufferPoint, _>(&right_anchors),
         points,
         "right_anchors <-> points"
     );
@@ -3637,13 +3797,13 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
 }
 
 fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
-    let max_row = snapshot.max_point().row;
+    let max_row = snapshot.max_point().row();
     let buffer_id = snapshot.excerpts().next().unwrap().1.remote_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)
+        .line_indents_in_row_range(0..max_row.0 + 1)
         .collect::<Vec<_>>();
-    for start_row in 0..snapshot.max_point().row {
+    for start_row in 0..snapshot.max_point().row().0 {
         pretty_assertions::assert_eq!(
             snapshot
                 .line_indents(MultiBufferRow(start_row), |_| true)
@@ -3657,7 +3817,7 @@ fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
     line_indents.reverse();
     pretty_assertions::assert_eq!(
         snapshot
-            .reversed_line_indents(MultiBufferRow(max_row), |_| true)
+            .reversed_line_indents(max_row, |_| true)
             .map(|(row, indent, _)| (row.0, indent))
             .collect::<Vec<_>>(),
         &line_indents[..],

crates/multi_buffer/src/transaction.rs 🔗

@@ -2,13 +2,13 @@ use gpui::{App, Context, Entity};
 use language::{self, Buffer, TextDimension, TransactionId};
 use std::{
     collections::HashMap,
-    ops::{Range, Sub},

+    ops::Range,

     time::{Duration, Instant},
 };
 use sum_tree::Bias;
 use text::BufferId;
 
-use crate::BufferState;

+use crate::{BufferState, LangBufferDimension, MultiBufferDimension};

 
 use super::{Event, ExcerptSummary, MultiBuffer};
 
@@ -314,13 +314,13 @@ impl MultiBuffer {
         }
     }
 
-    pub fn edited_ranges_for_transaction<D>(

+    pub fn edited_ranges_for_transaction<MBD>(

         &self,
         transaction_id: TransactionId,
         cx: &App,
-    ) -> Vec<Range<D>>

+    ) -> Vec<Range<MBD::LangBufferDimension>>

     where
-        D: TextDimension + Ord + Sub<D, Output = D>,

+        MBD: MultiBufferDimension<LangBufferDimension: LangBufferDimension>,

     {
         let Some(transaction) = self.history.transaction(transaction_id) else {
             return Vec::new();
@@ -336,24 +336,35 @@ impl MultiBuffer {
             };
 
             let buffer = buffer_state.buffer.read(cx);
-            for range in buffer.edited_ranges_for_transaction_id::<D>(*buffer_transaction) {

+            for range in buffer

+                .edited_ranges_for_transaction_id::<MBD::LangBufferDimension>(*buffer_transaction)

+            {

                 for excerpt_id in &buffer_state.excerpts {
                     cursor.seek(excerpt_id, Bias::Left);
                     if let Some(excerpt) = cursor.item()
                         && excerpt.locator == *excerpt_id
                     {
-                        let excerpt_buffer_start = excerpt.range.context.start.summary::<D>(buffer);

-                        let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(buffer);

+                        let excerpt_buffer_start = excerpt

+                            .range

+                            .context

+                            .start

+                            .summary::<MBD::LangBufferDimension>(buffer);

+                        let excerpt_buffer_end = excerpt

+                            .range

+                            .context

+                            .end

+                            .summary::<MBD::LangBufferDimension>(buffer);

                         let excerpt_range = excerpt_buffer_start..excerpt_buffer_end;
                         if excerpt_range.contains(&range.start)
                             && excerpt_range.contains(&range.end)
                         {
-                            let excerpt_start = D::from_text_summary(&cursor.start().text);

+                            let excerpt_start =

+                                MBD::LangBufferDimension::from_text_summary(&cursor.start().text);

 
                             let mut start = excerpt_start;
-                            start.add_assign(&(range.start - excerpt_buffer_start));

+                            start.add_distance(range.start.distance_to(excerpt_buffer_start));

                             let mut end = excerpt_start;
-                            end.add_assign(&(range.end - excerpt_buffer_start));

+                            end.add_distance(range.end.distance_to(excerpt_buffer_start));

 
                             ranges.push(start..end);
                             break;