sum_tree: Do not implement Dimension on tuples, use new Dimensions wrapper instead (#35482)

Piotr Osiewicz created

This is a bit of a readability improvement IMHO; I often find myself
confused when dealing when dimension pairs, as there's no easy way to
jump to the implementation of a dimension for tuples to remind myself
for the n-th time how exactly that impl works. Now it should be possible
to jump directly to that impl.

Another bonus is that Dimension supports 3-ary tuples as well - by using
a () as a default value of a 3rd dimension.


Release Notes:

- N/A

Change summary

Cargo.lock                                     |  1 
crates/channel/src/channel_chat.rs             | 10 ++-
crates/copilot/Cargo.toml                      |  1 
crates/copilot/src/copilot.rs                  |  3 
crates/editor/src/display_map/block_map.rs     | 42 ++++++++--------
crates/editor/src/display_map/fold_map.rs      | 43 +++++++++++-----
crates/editor/src/display_map/inlay_map.rs     | 50 ++++++++++++--------
crates/editor/src/display_map/wrap_map.rs      | 38 ++++++++++----
crates/gpui/src/elements/list.rs               |  6 +-
crates/language/src/syntax_map.rs              | 11 ++--
crates/multi_buffer/src/multi_buffer.rs        | 47 ++++++++++--------
crates/notifications/src/notification_store.rs |  6 +
crates/project/src/lsp_store.rs                |  5 +
crates/rope/src/rope.rs                        | 25 +++++----
crates/sum_tree/src/sum_tree.rs                | 27 ++++------
crates/text/src/anchor.rs                      |  6 +
crates/text/src/text.rs                        | 24 ++++++--
crates/worktree/src/worktree.rs                | 11 +++-
18 files changed, 215 insertions(+), 141 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3562,6 +3562,7 @@ dependencies = [
  "serde",
  "serde_json",
  "settings",
+ "sum_tree",
  "task",
  "theme",
  "ui",

crates/channel/src/channel_chat.rs 🔗

@@ -13,7 +13,7 @@ use std::{
     ops::{ControlFlow, Range},
     sync::Arc,
 };
-use sum_tree::{Bias, SumTree};
+use sum_tree::{Bias, Dimensions, SumTree};
 use time::OffsetDateTime;
 use util::{ResultExt as _, TryFutureExt, post_inc};
 
@@ -331,7 +331,9 @@ impl ChannelChat {
                 .update(&mut cx, |chat, cx| {
                     if let Some(first_id) = chat.first_loaded_message_id() {
                         if first_id <= message_id {
-                            let mut cursor = chat.messages.cursor::<(ChannelMessageId, Count)>(&());
+                            let mut cursor = chat
+                                .messages
+                                .cursor::<Dimensions<ChannelMessageId, Count>>(&());
                             let message_id = ChannelMessageId::Saved(message_id);
                             cursor.seek(&message_id, Bias::Left);
                             return ControlFlow::Break(
@@ -587,7 +589,9 @@ impl ChannelChat {
                 .map(|m| m.nonce)
                 .collect::<HashSet<_>>();
 
-            let mut old_cursor = self.messages.cursor::<(ChannelMessageId, Count)>(&());
+            let mut old_cursor = self
+                .messages
+                .cursor::<Dimensions<ChannelMessageId, Count>>(&());
             let mut new_messages = old_cursor.slice(&first_message.id, Bias::Left);
             let start_ix = old_cursor.start().1.0;
             let removed_messages = old_cursor.slice(&last_message.id, Bias::Right);

crates/copilot/Cargo.toml 🔗

@@ -46,6 +46,7 @@ project.workspace = true
 serde.workspace = true
 serde_json.workspace = true
 settings.workspace = true
+sum_tree.workspace = true
 task.workspace = true
 ui.workspace = true
 util.workspace = true

crates/copilot/src/copilot.rs 🔗

@@ -39,6 +39,7 @@ use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
+use sum_tree::Dimensions;
 use util::{ResultExt, fs::remove_matching};
 use workspace::Workspace;
 
@@ -239,7 +240,7 @@ impl RegisteredBuffer {
                         let new_snapshot = new_snapshot.clone();
                         async move {
                             new_snapshot
-                                .edits_since::<(PointUtf16, usize)>(&old_version)
+                                .edits_since::<Dimensions<PointUtf16, usize>>(&old_version)
                                 .map(|edit| {
                                     let edit_start = edit.new.start.0;
                                     let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);

crates/editor/src/display_map/block_map.rs 🔗

@@ -22,7 +22,7 @@ use std::{
         atomic::{AtomicUsize, Ordering::SeqCst},
     },
 };
-use sum_tree::{Bias, SumTree, Summary, TreeMap};
+use sum_tree::{Bias, Dimensions, SumTree, Summary, TreeMap};
 use text::{BufferId, Edit};
 use ui::ElementId;
 
@@ -416,7 +416,7 @@ struct TransformSummary {
 }
 
 pub struct BlockChunks<'a> {
-    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
+    transforms: sum_tree::Cursor<'a, Transform, Dimensions<BlockRow, WrapRow>>,
     input_chunks: wrap_map::WrapChunks<'a>,
     input_chunk: Chunk<'a>,
     output_row: u32,
@@ -426,7 +426,7 @@ pub struct BlockChunks<'a> {
 
 #[derive(Clone)]
 pub struct BlockRows<'a> {
-    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
+    transforms: sum_tree::Cursor<'a, Transform, Dimensions<BlockRow, WrapRow>>,
     input_rows: wrap_map::WrapRows<'a>,
     output_row: BlockRow,
     started: bool,
@@ -970,7 +970,7 @@ impl BlockMapReader<'_> {
                 .unwrap_or(self.wrap_snapshot.max_point().row() + 1),
         );
 
-        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(&());
         cursor.seek(&start_wrap_row, Bias::Left);
         while let Some(transform) = cursor.item() {
             if cursor.start().0 > end_wrap_row {
@@ -1292,7 +1292,7 @@ impl BlockSnapshot {
     ) -> BlockChunks<'a> {
         let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
 
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&BlockRow(rows.start), Bias::Right);
         let transform_output_start = cursor.start().0.0;
         let transform_input_start = cursor.start().1.0;
@@ -1324,9 +1324,9 @@ impl BlockSnapshot {
     }
 
     pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&start_row, Bias::Right);
-        let (output_start, input_start) = cursor.start();
+        let Dimensions(output_start, input_start, _) = cursor.start();
         let overshoot = if cursor
             .item()
             .map_or(false, |transform| transform.block.is_none())
@@ -1441,14 +1441,14 @@ impl BlockSnapshot {
     }
 
     pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&range.start, Bias::Right);
 
         let mut longest_row = range.start;
         let mut longest_row_chars = 0;
         if let Some(transform) = cursor.item() {
             if transform.block.is_none() {
-                let (output_start, input_start) = cursor.start();
+                let Dimensions(output_start, input_start, _) = cursor.start();
                 let overshoot = range.start.0 - output_start.0;
                 let wrap_start_row = input_start.0 + overshoot;
                 let wrap_end_row = cmp::min(
@@ -1474,7 +1474,7 @@ impl BlockSnapshot {
 
             if let Some(transform) = cursor.item() {
                 if transform.block.is_none() {
-                    let (output_start, input_start) = cursor.start();
+                    let Dimensions(output_start, input_start, _) = cursor.start();
                     let overshoot = range.end.0 - output_start.0;
                     let wrap_start_row = input_start.0;
                     let wrap_end_row = input_start.0 + overshoot;
@@ -1492,10 +1492,10 @@ impl BlockSnapshot {
     }
 
     pub(super) fn line_len(&self, row: BlockRow) -> u32 {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&BlockRow(row.0), Bias::Right);
         if let Some(transform) = cursor.item() {
-            let (output_start, input_start) = cursor.start();
+            let Dimensions(output_start, input_start, _) = cursor.start();
             let overshoot = row.0 - output_start.0;
             if transform.block.is_some() {
                 0
@@ -1510,13 +1510,13 @@ impl BlockSnapshot {
     }
 
     pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&row, Bias::Right);
         cursor.item().map_or(false, |t| t.block.is_some())
     }
 
     pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&row, Bias::Right);
         let Some(transform) = cursor.item() else {
             return false;
@@ -1528,7 +1528,7 @@ impl BlockSnapshot {
         let wrap_point = self
             .wrap_snapshot
             .make_wrap_point(Point::new(row.0, 0), Bias::Left);
-        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(&());
         cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
         cursor.item().map_or(false, |transform| {
             transform
@@ -1539,7 +1539,7 @@ impl BlockSnapshot {
     }
 
     pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&BlockRow(point.row), Bias::Right);
 
         let max_input_row = WrapRow(self.transforms.summary().input_rows);
@@ -1549,8 +1549,8 @@ impl BlockSnapshot {
 
         loop {
             if let Some(transform) = cursor.item() {
-                let (output_start_row, input_start_row) = cursor.start();
-                let (output_end_row, input_end_row) = cursor.end();
+                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
+                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
                 let output_start = Point::new(output_start_row.0, 0);
                 let input_start = Point::new(input_start_row.0, 0);
                 let input_end = Point::new(input_end_row.0, 0);
@@ -1599,13 +1599,13 @@ impl BlockSnapshot {
     }
 
     pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
-        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(&());
         cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
         if let Some(transform) = cursor.item() {
             if transform.block.is_some() {
                 BlockPoint::new(cursor.start().1.0, 0)
             } else {
-                let (input_start_row, output_start_row) = cursor.start();
+                let Dimensions(input_start_row, output_start_row, _) = cursor.start();
                 let input_start = Point::new(input_start_row.0, 0);
                 let output_start = Point::new(output_start_row.0, 0);
                 let input_overshoot = wrap_point.0 - input_start;
@@ -1617,7 +1617,7 @@ impl BlockSnapshot {
     }
 
     pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
-        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(&());
         cursor.seek(&BlockRow(block_point.row), Bias::Right);
         if let Some(transform) = cursor.item() {
             match transform.block.as_ref() {

crates/editor/src/display_map/fold_map.rs 🔗

@@ -17,7 +17,7 @@ use std::{
     sync::Arc,
     usize,
 };
-use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary, TreeMap};
+use sum_tree::{Bias, Cursor, Dimensions, FilterCursor, SumTree, Summary, TreeMap};
 use ui::IntoElement as _;
 use util::post_inc;
 
@@ -98,7 +98,9 @@ impl FoldPoint {
     }
 
     pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
-        let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
+        let mut cursor = snapshot
+            .transforms
+            .cursor::<Dimensions<FoldPoint, InlayPoint>>(&());
         cursor.seek(&self, Bias::Right);
         let overshoot = self.0 - cursor.start().0.0;
         InlayPoint(cursor.start().1.0 + overshoot)
@@ -107,7 +109,7 @@ impl FoldPoint {
     pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
         let mut cursor = snapshot
             .transforms
-            .cursor::<(FoldPoint, TransformSummary)>(&());
+            .cursor::<Dimensions<FoldPoint, TransformSummary>>(&());
         cursor.seek(&self, Bias::Right);
         let overshoot = self.0 - cursor.start().1.output.lines;
         let mut offset = cursor.start().1.output.len;
@@ -567,8 +569,9 @@ impl FoldMap {
                 let mut old_transforms = self
                     .snapshot
                     .transforms
-                    .cursor::<(InlayOffset, FoldOffset)>(&());
-                let mut new_transforms = new_transforms.cursor::<(InlayOffset, FoldOffset)>(&());
+                    .cursor::<Dimensions<InlayOffset, FoldOffset>>(&());
+                let mut new_transforms =
+                    new_transforms.cursor::<Dimensions<InlayOffset, FoldOffset>>(&());
 
                 for mut edit in inlay_edits {
                     old_transforms.seek(&edit.old.start, Bias::Left);
@@ -651,7 +654,9 @@ impl FoldSnapshot {
     pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
         let mut summary = TextSummary::default();
 
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<FoldPoint, InlayPoint>>(&());
         cursor.seek(&range.start, Bias::Right);
         if let Some(transform) = cursor.item() {
             let start_in_transform = range.start.0 - cursor.start().0.0;
@@ -700,7 +705,9 @@ impl FoldSnapshot {
     }
 
     pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<InlayPoint, FoldPoint>>(&());
         cursor.seek(&point, Bias::Right);
         if cursor.item().map_or(false, |t| t.is_fold()) {
             if bias == Bias::Left || point == cursor.start().0 {
@@ -734,7 +741,9 @@ impl FoldSnapshot {
         }
 
         let fold_point = FoldPoint::new(start_row, 0);
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<FoldPoint, InlayPoint>>(&());
         cursor.seek(&fold_point, Bias::Left);
 
         let overshoot = fold_point.0 - cursor.start().0.0;
@@ -816,7 +825,9 @@ impl FoldSnapshot {
         language_aware: bool,
         highlights: Highlights<'a>,
     ) -> FoldChunks<'a> {
-        let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
+        let mut transform_cursor = self
+            .transforms
+            .cursor::<Dimensions<FoldOffset, InlayOffset>>(&());
         transform_cursor.seek(&range.start, Bias::Right);
 
         let inlay_start = {
@@ -871,7 +882,9 @@ impl FoldSnapshot {
     }
 
     pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
-        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<FoldPoint, InlayPoint>>(&());
         cursor.seek(&point, Bias::Right);
         if let Some(transform) = cursor.item() {
             let transform_start = cursor.start().0.0;
@@ -1196,7 +1209,7 @@ impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
 
 #[derive(Clone)]
 pub struct FoldRows<'a> {
-    cursor: Cursor<'a, Transform, (FoldPoint, InlayPoint)>,
+    cursor: Cursor<'a, Transform, Dimensions<FoldPoint, InlayPoint>>,
     input_rows: InlayBufferRows<'a>,
     fold_point: FoldPoint,
 }
@@ -1313,7 +1326,7 @@ impl DerefMut for ChunkRendererContext<'_, '_> {
 }
 
 pub struct FoldChunks<'a> {
-    transform_cursor: Cursor<'a, Transform, (FoldOffset, InlayOffset)>,
+    transform_cursor: Cursor<'a, Transform, Dimensions<FoldOffset, InlayOffset>>,
     inlay_chunks: InlayChunks<'a>,
     inlay_chunk: Option<(InlayOffset, InlayChunk<'a>)>,
     inlay_offset: InlayOffset,
@@ -1448,7 +1461,7 @@ impl FoldOffset {
     pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
         let mut cursor = snapshot
             .transforms
-            .cursor::<(FoldOffset, TransformSummary)>(&());
+            .cursor::<Dimensions<FoldOffset, TransformSummary>>(&());
         cursor.seek(&self, Bias::Right);
         let overshoot = if cursor.item().map_or(true, |t| t.is_fold()) {
             Point::new(0, (self.0 - cursor.start().0.0) as u32)
@@ -1462,7 +1475,9 @@ impl FoldOffset {
 
     #[cfg(test)]
     pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
-        let mut cursor = snapshot.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
+        let mut cursor = snapshot
+            .transforms
+            .cursor::<Dimensions<FoldOffset, InlayOffset>>(&());
         cursor.seek(&self, Bias::Right);
         let overshoot = self.0 - cursor.start().0.0;
         InlayOffset(cursor.start().1.0 + overshoot)

crates/editor/src/display_map/inlay_map.rs 🔗

@@ -10,7 +10,7 @@ use std::{
     ops::{Add, AddAssign, Range, Sub, SubAssign},
     sync::Arc,
 };
-use sum_tree::{Bias, Cursor, SumTree};
+use sum_tree::{Bias, Cursor, Dimensions, SumTree};
 use text::{Patch, Rope};
 use ui::{ActiveTheme, IntoElement as _, ParentElement as _, Styled as _, div};
 
@@ -235,14 +235,14 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
 
 #[derive(Clone)]
 pub struct InlayBufferRows<'a> {
-    transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
+    transforms: Cursor<'a, Transform, Dimensions<InlayPoint, Point>>,
     buffer_rows: MultiBufferRows<'a>,
     inlay_row: u32,
     max_buffer_row: MultiBufferRow,
 }
 
 pub struct InlayChunks<'a> {
-    transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
+    transforms: Cursor<'a, Transform, Dimensions<InlayOffset, usize>>,
     buffer_chunks: CustomHighlightsChunks<'a>,
     buffer_chunk: Option<Chunk<'a>>,
     inlay_chunks: Option<text::Chunks<'a>>,
@@ -551,7 +551,9 @@ impl InlayMap {
         } else {
             let mut inlay_edits = Patch::default();
             let mut new_transforms = SumTree::default();
-            let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>(&());
+            let mut cursor = snapshot
+                .transforms
+                .cursor::<Dimensions<usize, InlayOffset>>(&());
             let mut buffer_edits_iter = buffer_edits.iter().peekable();
             while let Some(buffer_edit) = buffer_edits_iter.next() {
                 new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left), &());
@@ -770,20 +772,20 @@ impl InlaySnapshot {
     pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
         let mut cursor = self
             .transforms
-            .cursor::<(InlayOffset, (InlayPoint, usize))>(&());
+            .cursor::<Dimensions<InlayOffset, InlayPoint, usize>>(&());
         cursor.seek(&offset, Bias::Right);
         let overshoot = offset.0 - cursor.start().0.0;
         match cursor.item() {
             Some(Transform::Isomorphic(_)) => {
-                let buffer_offset_start = cursor.start().1.1;
+                let buffer_offset_start = cursor.start().2;
                 let buffer_offset_end = buffer_offset_start + overshoot;
                 let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
                 let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
-                InlayPoint(cursor.start().1.0.0 + (buffer_end - buffer_start))
+                InlayPoint(cursor.start().1.0 + (buffer_end - buffer_start))
             }
             Some(Transform::Inlay(inlay)) => {
                 let overshoot = inlay.text.offset_to_point(overshoot);
-                InlayPoint(cursor.start().1.0.0 + overshoot)
+                InlayPoint(cursor.start().1.0 + overshoot)
             }
             None => self.max_point(),
         }
@@ -800,26 +802,26 @@ impl InlaySnapshot {
     pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
         let mut cursor = self
             .transforms
-            .cursor::<(InlayPoint, (InlayOffset, Point))>(&());
+            .cursor::<Dimensions<InlayPoint, InlayOffset, Point>>(&());
         cursor.seek(&point, Bias::Right);
         let overshoot = point.0 - cursor.start().0.0;
         match cursor.item() {
             Some(Transform::Isomorphic(_)) => {
-                let buffer_point_start = cursor.start().1.1;
+                let buffer_point_start = cursor.start().2;
                 let buffer_point_end = buffer_point_start + overshoot;
                 let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
                 let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
-                InlayOffset(cursor.start().1.0.0 + (buffer_offset_end - buffer_offset_start))
+                InlayOffset(cursor.start().1.0 + (buffer_offset_end - buffer_offset_start))
             }
             Some(Transform::Inlay(inlay)) => {
                 let overshoot = inlay.text.point_to_offset(overshoot);
-                InlayOffset(cursor.start().1.0.0 + overshoot)
+                InlayOffset(cursor.start().1.0 + overshoot)
             }
             None => self.len(),
         }
     }
     pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(&());
         cursor.seek(&point, Bias::Right);
         match cursor.item() {
             Some(Transform::Isomorphic(_)) => {
@@ -831,7 +833,9 @@ impl InlaySnapshot {
         }
     }
     pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<InlayOffset, usize>>(&());
         cursor.seek(&offset, Bias::Right);
         match cursor.item() {
             Some(Transform::Isomorphic(_)) => {
@@ -844,7 +848,9 @@ impl InlaySnapshot {
     }
 
     pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
-        let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<usize, InlayOffset>>(&());
         cursor.seek(&offset, Bias::Left);
         loop {
             match cursor.item() {
@@ -877,7 +883,7 @@ impl InlaySnapshot {
         }
     }
     pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
-        let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(&());
         cursor.seek(&point, Bias::Left);
         loop {
             match cursor.item() {
@@ -911,7 +917,7 @@ impl InlaySnapshot {
     }
 
     pub fn clip_point(&self, mut point: InlayPoint, mut bias: Bias) -> InlayPoint {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(&());
         cursor.seek(&point, Bias::Left);
         loop {
             match cursor.item() {
@@ -1008,7 +1014,9 @@ impl InlaySnapshot {
     pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
         let mut summary = TextSummary::default();
 
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<InlayOffset, usize>>(&());
         cursor.seek(&range.start, Bias::Right);
 
         let overshoot = range.start.0 - cursor.start().0.0;
@@ -1056,7 +1064,7 @@ impl InlaySnapshot {
     }
 
     pub fn row_infos(&self, row: u32) -> InlayBufferRows<'_> {
-        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
+        let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(&());
         let inlay_point = InlayPoint::new(row, 0);
         cursor.seek(&inlay_point, Bias::Left);
 
@@ -1098,7 +1106,9 @@ impl InlaySnapshot {
         language_aware: bool,
         highlights: Highlights<'a>,
     ) -> InlayChunks<'a> {
-        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<InlayOffset, usize>>(&());
         cursor.seek(&range.start, Bias::Right);
 
         let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);

crates/editor/src/display_map/wrap_map.rs 🔗

@@ -9,7 +9,7 @@ use multi_buffer::{MultiBufferSnapshot, RowInfo};
 use smol::future::yield_now;
 use std::sync::LazyLock;
 use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
-use sum_tree::{Bias, Cursor, SumTree};
+use sum_tree::{Bias, Cursor, Dimensions, SumTree};
 use text::Patch;
 
 pub use super::tab_map::TextSummary;
@@ -55,7 +55,7 @@ pub struct WrapChunks<'a> {
     input_chunk: Chunk<'a>,
     output_position: WrapPoint,
     max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
+    transforms: Cursor<'a, Transform, Dimensions<WrapPoint, TabPoint>>,
     snapshot: &'a WrapSnapshot,
 }
 
@@ -66,7 +66,7 @@ pub struct WrapRows<'a> {
     output_row: u32,
     soft_wrapped: bool,
     max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
+    transforms: Cursor<'a, Transform, Dimensions<WrapPoint, TabPoint>>,
 }
 
 impl WrapRows<'_> {
@@ -598,7 +598,9 @@ impl WrapSnapshot {
     ) -> WrapChunks<'a> {
         let output_start = WrapPoint::new(rows.start, 0);
         let output_end = WrapPoint::new(rows.end, 0);
-        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut transforms = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         transforms.seek(&output_start, Bias::Right);
         let mut input_start = TabPoint(transforms.start().1.0);
         if transforms.item().map_or(false, |t| t.is_isomorphic()) {
@@ -626,7 +628,9 @@ impl WrapSnapshot {
     }
 
     pub fn line_len(&self, row: u32) -> u32 {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left);
         if cursor
             .item()
@@ -651,7 +655,9 @@ impl WrapSnapshot {
         let start = WrapPoint::new(rows.start, 0);
         let end = WrapPoint::new(rows.end, 0);
 
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         cursor.seek(&start, Bias::Right);
         if let Some(transform) = cursor.item() {
             let start_in_transform = start.0 - cursor.start().0.0;
@@ -721,7 +727,9 @@ impl WrapSnapshot {
     }
 
     pub fn row_infos(&self, start_row: u32) -> WrapRows<'_> {
-        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut transforms = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left);
         let mut input_row = transforms.start().1.row();
         if transforms.item().map_or(false, |t| t.is_isomorphic()) {
@@ -741,7 +749,9 @@ impl WrapSnapshot {
     }
 
     pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         cursor.seek(&point, Bias::Right);
         let mut tab_point = cursor.start().1.0;
         if cursor.item().map_or(false, |t| t.is_isomorphic()) {
@@ -759,7 +769,9 @@ impl WrapSnapshot {
     }
 
     pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
-        let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<TabPoint, WrapPoint>>(&());
         cursor.seek(&point, Bias::Right);
         WrapPoint(cursor.start().1.0 + (point.0 - cursor.start().0.0))
     }
@@ -784,7 +796,9 @@ impl WrapSnapshot {
 
         *point.column_mut() = 0;
 
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         cursor.seek(&point, Bias::Right);
         if cursor.item().is_none() {
             cursor.prev();
@@ -804,7 +818,9 @@ impl WrapSnapshot {
     pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
         point.0 += Point::new(1, 0);
 
-        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
+        let mut cursor = self
+            .transforms
+            .cursor::<Dimensions<WrapPoint, TabPoint>>(&());
         cursor.seek(&point, Bias::Right);
         while let Some(transform) = cursor.item() {
             if transform.is_isomorphic() && cursor.start().1.column() == 0 {

crates/gpui/src/elements/list.rs 🔗

@@ -16,7 +16,7 @@ use crate::{
 use collections::VecDeque;
 use refineable::Refineable as _;
 use std::{cell::RefCell, ops::Range, rc::Rc};
-use sum_tree::{Bias, SumTree};
+use sum_tree::{Bias, Dimensions, SumTree};
 
 /// Construct a new list element
 pub fn list(state: ListState) -> List {
@@ -371,14 +371,14 @@ impl ListState {
             return None;
         }
 
-        let mut cursor = state.items.cursor::<(Count, Height)>(&());
+        let mut cursor = state.items.cursor::<Dimensions<Count, Height>>(&());
         cursor.seek(&Count(scroll_top.item_ix), Bias::Right);
 
         let scroll_top = cursor.start().1.0 + scroll_top.offset_in_item;
 
         cursor.seek_forward(&Count(ix), Bias::Right);
         if let Some(&ListItem::Measured { size, .. }) = cursor.item() {
-            let &(Count(count), Height(top)) = cursor.start();
+            let &Dimensions(Count(count), Height(top), _) = cursor.start();
             if count == ix {
                 let top = bounds.top() + top - scroll_top;
                 return Some(Bounds::from_corners(

crates/language/src/syntax_map.rs 🔗

@@ -17,7 +17,7 @@ use std::{
     sync::Arc,
 };
 use streaming_iterator::StreamingIterator;
-use sum_tree::{Bias, SeekTarget, SumTree};
+use sum_tree::{Bias, Dimensions, SeekTarget, SumTree};
 use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint};
 use tree_sitter::{Node, Query, QueryCapture, QueryCaptures, QueryCursor, QueryMatches, Tree};
 
@@ -285,7 +285,7 @@ impl SyntaxSnapshot {
 
     pub fn interpolate(&mut self, text: &BufferSnapshot) {
         let edits = text
-            .anchored_edits_since::<(usize, Point)>(&self.interpolated_version)
+            .anchored_edits_since::<Dimensions<usize, Point>>(&self.interpolated_version)
             .collect::<Vec<_>>();
         self.interpolated_version = text.version().clone();
 
@@ -333,7 +333,8 @@ impl SyntaxSnapshot {
             };
 
             let Some(layer) = cursor.item() else { break };
-            let (start_byte, start_point) = layer.range.start.summary::<(usize, Point)>(text);
+            let Dimensions(start_byte, start_point, _) =
+                layer.range.start.summary::<Dimensions<usize, Point>>(text);
 
             // Ignore edits that end before the start of this layer, and don't consider them
             // for any subsequent layers at this same depth.
@@ -562,8 +563,8 @@ impl SyntaxSnapshot {
             }
 
             let Some(step) = step else { break };
-            let (step_start_byte, step_start_point) =
-                step.range.start.summary::<(usize, Point)>(text);
+            let Dimensions(step_start_byte, step_start_point, _) =
+                step.range.start.summary::<Dimensions<usize, Point>>(text);
             let step_end_byte = step.range.end.to_offset(text);
 
             let mut old_layer = cursor.item();

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -43,7 +43,7 @@ use std::{
     sync::Arc,
     time::{Duration, Instant},
 };
-use sum_tree::{Bias, Cursor, Dimension, SumTree, Summary, TreeMap};
+use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, Summary, TreeMap};
 use text::{
     BufferId, Edit, LineIndent, TextSummary,
     locator::Locator,
@@ -474,7 +474,7 @@ pub struct MultiBufferRows<'a> {
 
 pub struct MultiBufferChunks<'a> {
     excerpts: Cursor<'a, Excerpt, ExcerptOffset>,
-    diff_transforms: Cursor<'a, DiffTransform, (usize, ExcerptOffset)>,
+    diff_transforms: Cursor<'a, DiffTransform, Dimensions<usize, ExcerptOffset>>,
     diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
     diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,
     buffer_chunk: Option<Chunk<'a>>,
@@ -2120,10 +2120,10 @@ impl MultiBuffer {
         let buffers = self.buffers.borrow();
         let mut excerpts = snapshot
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptDimension<Point>)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(&());
         let mut diff_transforms = snapshot
             .diff_transforms
-            .cursor::<(ExcerptDimension<Point>, OutputDimension<Point>)>(&());
+            .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(&());
         diff_transforms.next();
         let locators = buffers
             .get(&buffer_id)
@@ -2281,7 +2281,7 @@ impl MultiBuffer {
         let mut new_excerpts = SumTree::default();
         let mut cursor = snapshot
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
         let mut edits = Vec::new();
         let mut excerpt_ids = ids.iter().copied().peekable();
         let mut removed_buffer_ids = Vec::new();
@@ -2492,7 +2492,7 @@ impl MultiBuffer {
         for locator in &buffer_state.excerpts {
             let mut cursor = snapshot
                 .excerpts
-                .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+                .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
             cursor.seek_forward(&Some(locator), Bias::Left);
             if let Some(excerpt) = cursor.item() {
                 if excerpt.locator == *locator {
@@ -2845,7 +2845,7 @@ impl MultiBuffer {
         let mut new_excerpts = SumTree::default();
         let mut cursor = snapshot
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
         let mut edits = Vec::<Edit<ExcerptOffset>>::new();
 
         let prefix = cursor.slice(&Some(locator), Bias::Left);
@@ -2921,7 +2921,7 @@ impl MultiBuffer {
         let mut new_excerpts = SumTree::default();
         let mut cursor = snapshot
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
         let mut edits = Vec::<Edit<ExcerptOffset>>::new();
 
         for locator in &locators {
@@ -3067,7 +3067,7 @@ impl MultiBuffer {
         let mut new_excerpts = SumTree::default();
         let mut cursor = snapshot
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
 
         for (locator, buffer, buffer_edited) in excerpts_to_edit {
             new_excerpts.append(cursor.slice(&Some(locator), Bias::Left), &());
@@ -3135,7 +3135,7 @@ impl MultiBuffer {
         let mut excerpts = snapshot.excerpts.cursor::<ExcerptOffset>(&());
         let mut old_diff_transforms = snapshot
             .diff_transforms
-            .cursor::<(ExcerptOffset, usize)>(&());
+            .cursor::<Dimensions<ExcerptOffset, usize>>(&());
         let mut new_diff_transforms = SumTree::default();
         let mut old_expanded_hunks = HashSet::default();
         let mut output_edits = Vec::new();
@@ -3260,7 +3260,7 @@ impl MultiBuffer {
         &self,
         edit: &Edit<TypedOffset<Excerpt>>,
         excerpts: &mut Cursor<Excerpt, TypedOffset<Excerpt>>,
-        old_diff_transforms: &mut Cursor<DiffTransform, (TypedOffset<Excerpt>, usize)>,
+        old_diff_transforms: &mut Cursor<DiffTransform, Dimensions<TypedOffset<Excerpt>, usize>>,
         new_diff_transforms: &mut SumTree<DiffTransform>,
         end_of_current_insert: &mut Option<(TypedOffset<Excerpt>, DiffTransformHunkInfo)>,
         old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
@@ -4713,7 +4713,9 @@ impl MultiBufferSnapshot {
         O: ToOffset,
     {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut cursor = self.diff_transforms.cursor::<(usize, ExcerptOffset)>(&());
+        let mut cursor = self
+            .diff_transforms
+            .cursor::<Dimensions<usize, ExcerptOffset>>(&());
         cursor.seek(&range.start, Bias::Right);
 
         let Some(first_transform) = cursor.item() else {
@@ -4867,7 +4869,10 @@ impl MultiBufferSnapshot {
         &self,
         anchor: &Anchor,
         excerpt_position: D,
-        diff_transforms: &mut Cursor<DiffTransform, (ExcerptDimension<D>, OutputDimension<D>)>,
+        diff_transforms: &mut Cursor<
+            DiffTransform,
+            Dimensions<ExcerptDimension<D>, OutputDimension<D>>,
+        >,
     ) -> D
     where
         D: TextDimension + Ord + Sub<D, Output = D>,
@@ -4927,7 +4932,7 @@ impl MultiBufferSnapshot {
     fn excerpt_offset_for_anchor(&self, anchor: &Anchor) -> ExcerptOffset {
         let mut cursor = self
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptOffset)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(&());
         let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
 
         cursor.seek(&Some(locator), Bias::Left);
@@ -4971,7 +4976,7 @@ impl MultiBufferSnapshot {
         let mut cursor = self.excerpts.cursor::<ExcerptSummary>(&());
         let mut diff_transforms_cursor = self
             .diff_transforms
-            .cursor::<(ExcerptDimension<D>, OutputDimension<D>)>(&());
+            .cursor::<Dimensions<ExcerptDimension<D>, OutputDimension<D>>>(&());
         diff_transforms_cursor.next();
 
         let mut summaries = Vec::new();
@@ -5201,7 +5206,9 @@ impl MultiBufferSnapshot {
 
         // Find the given position in the diff transforms. Determine the corresponding
         // offset in the excerpts, and whether the position is within a deleted hunk.
-        let mut diff_transforms = self.diff_transforms.cursor::<(usize, ExcerptOffset)>(&());
+        let mut diff_transforms = self
+            .diff_transforms
+            .cursor::<Dimensions<usize, ExcerptOffset>>(&());
         diff_transforms.seek(&offset, Bias::Right);
 
         if offset == diff_transforms.start().0 && bias == Bias::Left {
@@ -5250,7 +5257,7 @@ impl MultiBufferSnapshot {
 
         let mut excerpts = self
             .excerpts
-            .cursor::<(ExcerptOffset, Option<ExcerptId>)>(&());
+            .cursor::<Dimensions<ExcerptOffset, Option<ExcerptId>>>(&());
         excerpts.seek(&excerpt_offset, Bias::Right);
         if excerpts.item().is_none() && excerpt_offset == excerpts.start().0 && bias == Bias::Left {
             excerpts.prev();
@@ -5341,7 +5348,7 @@ impl MultiBufferSnapshot {
         let start_locator = self.excerpt_locator_for_id(id);
         let mut excerpts = self
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptDimension<usize>)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<usize>>>(&());
         excerpts.seek(&Some(start_locator), Bias::Left);
         excerpts.prev();
 
@@ -6242,14 +6249,14 @@ impl MultiBufferSnapshot {
     pub fn range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<Point>> {
         let mut cursor = self
             .excerpts
-            .cursor::<(Option<&Locator>, ExcerptDimension<Point>)>(&());
+            .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(&());
         let locator = self.excerpt_locator_for_id(excerpt_id);
         if cursor.seek(&Some(locator), Bias::Left) {
             let start = cursor.start().1.clone();
             let end = cursor.end().1;
             let mut diff_transforms = self
                 .diff_transforms
-                .cursor::<(ExcerptDimension<Point>, OutputDimension<Point>)>(&());
+                .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(&());
             diff_transforms.seek(&start, Bias::Left);
             let overshoot = start.0 - diff_transforms.start().0.0;
             let start = diff_transforms.start().1.0 + overshoot;

crates/notifications/src/notification_store.rs 🔗

@@ -6,7 +6,7 @@ use db::smol::stream::StreamExt;
 use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, Task};
 use rpc::{Notification, TypedEnvelope, proto};
 use std::{ops::Range, sync::Arc};
-use sum_tree::{Bias, SumTree};
+use sum_tree::{Bias, Dimensions, SumTree};
 use time::OffsetDateTime;
 use util::ResultExt;
 
@@ -360,7 +360,9 @@ impl NotificationStore {
         is_new: bool,
         cx: &mut Context<NotificationStore>,
     ) {
-        let mut cursor = self.notifications.cursor::<(NotificationId, Count)>(&());
+        let mut cursor = self
+            .notifications
+            .cursor::<Dimensions<NotificationId, Count>>(&());
         let mut new_notifications = SumTree::default();
         let mut old_range = 0..0;
 

crates/project/src/lsp_store.rs 🔗

@@ -96,6 +96,7 @@ use std::{
     sync::Arc,
     time::{Duration, Instant},
 };
+use sum_tree::Dimensions;
 use text::{Anchor, BufferId, LineEnding, OffsetRangeExt};
 use url::Url;
 use util::{
@@ -7253,7 +7254,9 @@ impl LspStore {
 
             let build_incremental_change = || {
                 buffer
-                    .edits_since::<(PointUtf16, usize)>(previous_snapshot.snapshot.version())
+                    .edits_since::<Dimensions<PointUtf16, usize>>(
+                        previous_snapshot.snapshot.version(),
+                    )
                     .map(|edit| {
                         let edit_start = edit.new.start.0;
                         let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);

crates/rope/src/rope.rs 🔗

@@ -12,7 +12,7 @@ use std::{
     ops::{self, AddAssign, Range},
     str,
 };
-use sum_tree::{Bias, Dimension, SumTree};
+use sum_tree::{Bias, Dimension, Dimensions, SumTree};
 
 pub use chunk::ChunkSlice;
 pub use offset_utf16::OffsetUtf16;
@@ -282,7 +282,7 @@ impl Rope {
         if offset >= self.summary().len {
             return self.summary().len_utf16;
         }
-        let mut cursor = self.chunks.cursor::<(usize, OffsetUtf16)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<usize, OffsetUtf16>>(&());
         cursor.seek(&offset, Bias::Left);
         let overshoot = offset - cursor.start().0;
         cursor.start().1
@@ -295,7 +295,7 @@ impl Rope {
         if offset >= self.summary().len_utf16 {
             return self.summary().len;
         }
-        let mut cursor = self.chunks.cursor::<(OffsetUtf16, usize)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<OffsetUtf16, usize>>(&());
         cursor.seek(&offset, Bias::Left);
         let overshoot = offset - cursor.start().0;
         cursor.start().1
@@ -308,7 +308,7 @@ impl Rope {
         if offset >= self.summary().len {
             return self.summary().lines;
         }
-        let mut cursor = self.chunks.cursor::<(usize, Point)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<usize, Point>>(&());
         cursor.seek(&offset, Bias::Left);
         let overshoot = offset - cursor.start().0;
         cursor.start().1
@@ -321,7 +321,7 @@ impl Rope {
         if offset >= self.summary().len {
             return self.summary().lines_utf16();
         }
-        let mut cursor = self.chunks.cursor::<(usize, PointUtf16)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<usize, PointUtf16>>(&());
         cursor.seek(&offset, Bias::Left);
         let overshoot = offset - cursor.start().0;
         cursor.start().1
@@ -334,7 +334,7 @@ impl Rope {
         if point >= self.summary().lines {
             return self.summary().lines_utf16();
         }
-        let mut cursor = self.chunks.cursor::<(Point, PointUtf16)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<Point, PointUtf16>>(&());
         cursor.seek(&point, Bias::Left);
         let overshoot = point - cursor.start().0;
         cursor.start().1
@@ -347,7 +347,7 @@ impl Rope {
         if point >= self.summary().lines {
             return self.summary().len;
         }
-        let mut cursor = self.chunks.cursor::<(Point, usize)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<Point, usize>>(&());
         cursor.seek(&point, Bias::Left);
         let overshoot = point - cursor.start().0;
         cursor.start().1
@@ -368,7 +368,7 @@ impl Rope {
         if point >= self.summary().lines_utf16() {
             return self.summary().len;
         }
-        let mut cursor = self.chunks.cursor::<(PointUtf16, usize)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<PointUtf16, usize>>(&());
         cursor.seek(&point, Bias::Left);
         let overshoot = point - cursor.start().0;
         cursor.start().1
@@ -381,7 +381,7 @@ impl Rope {
         if point.0 >= self.summary().lines_utf16() {
             return self.summary().lines;
         }
-        let mut cursor = self.chunks.cursor::<(PointUtf16, Point)>(&());
+        let mut cursor = self.chunks.cursor::<Dimensions<PointUtf16, Point>>(&());
         cursor.seek(&point.0, Bias::Left);
         let overshoot = Unclipped(point.0 - cursor.start().0);
         cursor.start().1
@@ -1168,16 +1168,17 @@ pub trait TextDimension:
     fn add_assign(&mut self, other: &Self);
 }
 
-impl<D1: TextDimension, D2: TextDimension> TextDimension for (D1, D2) {
+impl<D1: TextDimension, D2: TextDimension> TextDimension for Dimensions<D1, D2, ()> {
     fn from_text_summary(summary: &TextSummary) -> Self {
-        (
+        Dimensions(
             D1::from_text_summary(summary),
             D2::from_text_summary(summary),
+            (),
         )
     }
 
     fn from_chunk(chunk: ChunkSlice) -> Self {
-        (D1::from_chunk(chunk), D2::from_chunk(chunk))
+        Dimensions(D1::from_chunk(chunk), D2::from_chunk(chunk), ())
     }
 
     fn add_assign(&mut self, other: &Self) {

crates/sum_tree/src/sum_tree.rs 🔗

@@ -101,37 +101,32 @@ impl<'a, T: Summary> Dimension<'a, T> for () {
     fn add_summary(&mut self, _: &'a T, _: &T::Context) {}
 }
 
-impl<'a, T: Summary, D1: Dimension<'a, T>, D2: Dimension<'a, T>> Dimension<'a, T> for (D1, D2) {
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Dimensions<D1, D2, D3 = ()>(pub D1, pub D2, pub D3);
+
+impl<'a, T: Summary, D1: Dimension<'a, T>, D2: Dimension<'a, T>, D3: Dimension<'a, T>>
+    Dimension<'a, T> for Dimensions<D1, D2, D3>
+{
     fn zero(cx: &T::Context) -> Self {
-        (D1::zero(cx), D2::zero(cx))
+        Dimensions(D1::zero(cx), D2::zero(cx), D3::zero(cx))
     }
 
     fn add_summary(&mut self, summary: &'a T, cx: &T::Context) {
         self.0.add_summary(summary, cx);
         self.1.add_summary(summary, cx);
+        self.2.add_summary(summary, cx);
     }
 }
 
-impl<'a, S, D1, D2> SeekTarget<'a, S, (D1, D2)> for D1
-where
-    S: Summary,
-    D1: SeekTarget<'a, S, D1> + Dimension<'a, S>,
-    D2: Dimension<'a, S>,
-{
-    fn cmp(&self, cursor_location: &(D1, D2), cx: &S::Context) -> Ordering {
-        self.cmp(&cursor_location.0, cx)
-    }
-}
-
-impl<'a, S, D1, D2, D3> SeekTarget<'a, S, ((D1, D2), D3)> for D1
+impl<'a, S, D1, D2, D3> SeekTarget<'a, S, Dimensions<D1, D2, D3>> for D1
 where
     S: Summary,
     D1: SeekTarget<'a, S, D1> + Dimension<'a, S>,
     D2: Dimension<'a, S>,
     D3: Dimension<'a, S>,
 {
-    fn cmp(&self, cursor_location: &((D1, D2), D3), cx: &S::Context) -> Ordering {
-        self.cmp(&cursor_location.0.0, cx)
+    fn cmp(&self, cursor_location: &Dimensions<D1, D2, D3>, cx: &S::Context) -> Ordering {
+        self.cmp(&cursor_location.0, cx)
     }
 }
 

crates/text/src/anchor.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     locator::Locator,
 };
 use std::{cmp::Ordering, fmt::Debug, ops::Range};
-use sum_tree::Bias;
+use sum_tree::{Bias, Dimensions};
 
 /// A timestamped position in a buffer
 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
@@ -102,7 +102,9 @@ impl Anchor {
             let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
                 return false;
             };
-            let mut fragment_cursor = buffer.fragments.cursor::<(Option<&Locator>, usize)>(&None);
+            let mut fragment_cursor = buffer
+                .fragments
+                .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
             fragment_cursor.seek(&Some(fragment_id), Bias::Left);
             fragment_cursor
                 .item()

crates/text/src/text.rs 🔗

@@ -37,7 +37,7 @@ use std::{
 };
 pub use subscription::*;
 pub use sum_tree::Bias;
-use sum_tree::{FilterCursor, SumTree, TreeMap, TreeSet};
+use sum_tree::{Dimensions, FilterCursor, SumTree, TreeMap, TreeSet};
 use undo_map::UndoMap;
 
 #[cfg(any(test, feature = "test-support"))]
@@ -1071,7 +1071,9 @@ impl Buffer {
         let mut insertion_offset = 0;
         let mut new_ropes =
             RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
-        let mut old_fragments = self.fragments.cursor::<(VersionedFullOffset, usize)>(&cx);
+        let mut old_fragments = self
+            .fragments
+            .cursor::<Dimensions<VersionedFullOffset, usize>>(&cx);
         let mut new_fragments =
             old_fragments.slice(&VersionedFullOffset::Offset(ranges[0].start), Bias::Left);
         new_ropes.append(new_fragments.summary().text);
@@ -1298,7 +1300,9 @@ impl Buffer {
         self.snapshot.undo_map.insert(undo);
 
         let mut edits = Patch::default();
-        let mut old_fragments = self.fragments.cursor::<(Option<&Locator>, usize)>(&None);
+        let mut old_fragments = self
+            .fragments
+            .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
         let mut new_fragments = SumTree::new(&None);
         let mut new_ropes =
             RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
@@ -1561,7 +1565,9 @@ impl Buffer {
         D: TextDimension,
     {
         // get fragment ranges
-        let mut cursor = self.fragments.cursor::<(Option<&Locator>, usize)>(&None);
+        let mut cursor = self
+            .fragments
+            .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
         let offset_ranges = self
             .fragment_ids_for_edits(edit_ids.into_iter())
             .into_iter()
@@ -2232,7 +2238,9 @@ impl BufferSnapshot {
     {
         let anchors = anchors.into_iter();
         let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(&());
-        let mut fragment_cursor = self.fragments.cursor::<(Option<&Locator>, usize)>(&None);
+        let mut fragment_cursor = self
+            .fragments
+            .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
         let mut text_cursor = self.visible_text.cursor(0);
         let mut position = D::zero(&());
 
@@ -2318,7 +2326,9 @@ impl BufferSnapshot {
                 );
             };
 
-            let mut fragment_cursor = self.fragments.cursor::<(Option<&Locator>, usize)>(&None);
+            let mut fragment_cursor = self
+                .fragments
+                .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
             fragment_cursor.seek(&Some(&insertion.fragment_id), Bias::Left);
             let fragment = fragment_cursor.item().unwrap();
             let mut fragment_offset = fragment_cursor.start().1;
@@ -2476,7 +2486,7 @@ impl BufferSnapshot {
         };
         let mut cursor = self
             .fragments
-            .cursor::<(Option<&Locator>, FragmentTextSummary)>(&None);
+            .cursor::<Dimensions<Option<&Locator>, FragmentTextSummary>>(&None);
 
         let start_fragment_id = self.fragment_id_for_anchor(&range.start);
         cursor.seek(&Some(start_fragment_id), Bias::Left);

crates/worktree/src/worktree.rs 🔗

@@ -62,7 +62,7 @@ use std::{
     },
     time::{Duration, Instant},
 };
-use sum_tree::{Bias, Edit, KeyedItem, SeekTarget, SumTree, Summary, TreeMap, TreeSet};
+use sum_tree::{Bias, Dimensions, Edit, KeyedItem, SeekTarget, SumTree, Summary, TreeMap, TreeSet};
 use text::{LineEnding, Rope};
 use util::{
     ResultExt,
@@ -3566,10 +3566,15 @@ impl<'a> sum_tree::Dimension<'a, PathSummary<GitSummary>> for GitSummary {
     }
 }
 
-impl<'a> sum_tree::SeekTarget<'a, PathSummary<GitSummary>, (TraversalProgress<'a>, GitSummary)>
+impl<'a>
+    sum_tree::SeekTarget<'a, PathSummary<GitSummary>, Dimensions<TraversalProgress<'a>, GitSummary>>
     for PathTarget<'_>
 {
-    fn cmp(&self, cursor_location: &(TraversalProgress<'a>, GitSummary), _: &()) -> Ordering {
+    fn cmp(
+        &self,
+        cursor_location: &Dimensions<TraversalProgress<'a>, GitSummary>,
+        _: &(),
+    ) -> Ordering {
         self.cmp_path(&cursor_location.0.max_path)
     }
 }