Allow a single start/end bias per `AnchorRangeMap`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/buffer/src/anchor.rs  | 37 +++++++++++++++++++------------------
crates/buffer/src/lib.rs     | 27 ++++++++++++---------------
crates/language/src/proto.rs | 17 ++++++++++++-----
3 files changed, 43 insertions(+), 38 deletions(-)

Detailed changes

crates/buffer/src/anchor.rs 🔗

@@ -29,7 +29,9 @@ pub struct AnchorSet(pub(crate) AnchorMap<()>);
 #[derive(Clone)]
 pub struct AnchorRangeMap<T> {
     pub(crate) version: clock::Global,
-    pub(crate) entries: Vec<(Range<(FullOffset, Bias)>, T)>,
+    pub(crate) entries: Vec<(Range<FullOffset>, T)>,
+    pub(crate) start_bias: Bias,
+    pub(crate) end_bias: Bias,
 }
 
 #[derive(Clone)]
@@ -174,9 +176,16 @@ impl<T> AnchorRangeMap<T> {
 
     pub fn from_full_offset_ranges(
         version: clock::Global,
-        entries: Vec<(Range<(FullOffset, Bias)>, T)>,
+        start_bias: Bias,
+        end_bias: Bias,
+        entries: Vec<(Range<FullOffset>, T)>,
     ) -> Self {
-        Self { version, entries }
+        Self {
+            version,
+            start_bias,
+            end_bias,
+            entries,
+        }
     }
 
     pub fn ranges<'a, D>(
@@ -190,10 +199,8 @@ impl<T> AnchorRangeMap<T> {
         content.summaries_for_anchor_ranges(self)
     }
 
-    pub fn full_offset_ranges(&self) -> impl Iterator<Item = (Range<FullOffset>, &T)> {
-        self.entries
-            .iter()
-            .map(|(range, value)| (range.start.0..range.end.0, value))
+    pub fn full_offset_ranges(&self) -> impl Iterator<Item = &(Range<FullOffset>, T)> {
+        self.entries.iter()
     }
 
     pub fn min_by_key<'a, C, D, F, K>(
@@ -232,25 +239,19 @@ impl<T> AnchorRangeMap<T> {
             .map(|(range, value)| (self.resolve_range(range, &content), value))
     }
 
-    fn resolve_range<'a, D>(
-        &self,
-        range: &Range<(FullOffset, Bias)>,
-        content: &Content<'a>,
-    ) -> Range<D>
+    fn resolve_range<'a, D>(&self, range: &Range<FullOffset>, content: &Content<'a>) -> Range<D>
     where
         D: 'a + TextDimension<'a>,
     {
-        let (start, start_bias) = range.start;
         let mut anchor = Anchor {
-            full_offset: start,
-            bias: start_bias,
+            full_offset: range.start,
+            bias: self.start_bias,
             version: self.version.clone(),
         };
         let start = content.summary_for_anchor(&anchor);
 
-        let (end, end_bias) = range.end;
-        anchor.full_offset = end;
-        anchor.bias = end_bias;
+        anchor.full_offset = range.end;
+        anchor.bias = self.end_bias;
         let end = content.summary_for_anchor(&anchor);
 
         start..end

crates/buffer/src/lib.rs 🔗

@@ -1818,27 +1818,22 @@ impl<'a> Content<'a> {
         let mut rope_cursor = self.visible_text.cursor(0);
         let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
         map.entries.iter().map(move |(range, value)| {
-            let Range {
-                start: (start_offset, start_bias),
-                end: (end_offset, end_bias),
-            } = range;
-
             cursor.seek_forward(
-                &VersionedFullOffset::Offset(*start_offset),
-                *start_bias,
+                &VersionedFullOffset::Offset(range.start),
+                map.start_bias,
                 &cx,
             );
             let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-                *start_offset - cursor.start().0.full_offset()
+                range.start - cursor.start().0.full_offset()
             } else {
                 0
             };
             summary.add_assign(&rope_cursor.summary::<D>(cursor.start().1 + overshoot));
             let start_summary = summary.clone();
 
-            cursor.seek_forward(&VersionedFullOffset::Offset(*end_offset), *end_bias, &cx);
+            cursor.seek_forward(&VersionedFullOffset::Offset(range.end), map.end_bias, &cx);
             let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-                *end_offset - cursor.start().0.full_offset()
+                range.end - cursor.start().0.full_offset()
             } else {
                 0
             };
@@ -1901,14 +1896,16 @@ impl<'a> Content<'a> {
                 let full_start_offset = FullOffset(cursor.start().deleted + start_offset);
                 cursor.seek_forward(&end_offset, end_bias, &None);
                 let full_end_offset = FullOffset(cursor.start().deleted + end_offset);
-                (
-                    (full_start_offset, start_bias)..(full_end_offset, end_bias),
-                    value,
-                )
+                (full_start_offset..full_end_offset, value)
             })
             .collect();
 
-        AnchorRangeMap { version, entries }
+        AnchorRangeMap {
+            version,
+            start_bias,
+            end_bias,
+            entries,
+        }
     }
 
     pub fn anchor_set<E>(&self, bias: Bias, entries: E) -> AnchorSet

crates/language/src/proto.rs 🔗

@@ -194,8 +194,8 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
                     .selections
                     .iter()
                     .map(|selection| {
-                        let range = (FullOffset(selection.start as usize), Bias::Left)
-                            ..(FullOffset(selection.end as usize), Bias::Right);
+                        let range = FullOffset(selection.start as usize)
+                            ..FullOffset(selection.end as usize);
                         let state = SelectionState {
                             id: selection.id as usize,
                             reversed: selection.reversed,
@@ -204,7 +204,12 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
                         (range, state)
                     })
                     .collect();
-                let selections = AnchorRangeMap::from_full_offset_ranges(version, entries);
+                let selections = AnchorRangeMap::from_full_offset_ranges(
+                    version,
+                    Bias::Left,
+                    Bias::Left,
+                    entries,
+                );
 
                 Operation::Buffer(buffer::Operation::UpdateSelections {
                     set_id: clock::Lamport {
@@ -276,11 +281,13 @@ pub fn deserialize_selection_set(set: proto::SelectionSet) -> SelectionSet {
         active: set.is_active,
         selections: Arc::new(AnchorRangeMap::from_full_offset_ranges(
             set.version.into(),
+            Bias::Left,
+            Bias::Left,
             set.selections
                 .into_iter()
                 .map(|selection| {
-                    let range = (FullOffset(selection.start as usize), Bias::Left)
-                        ..(FullOffset(selection.end as usize), Bias::Left);
+                    let range =
+                        FullOffset(selection.start as usize)..FullOffset(selection.end as usize);
                     let state = SelectionState {
                         id: selection.id as usize,
                         reversed: selection.reversed,