WIP: Start converting SelectionSet to use AnchorRangeMap

Nathan Sobo created

Change summary

crates/buffer/src/anchor.rs    | 33 ++++++++++++
crates/buffer/src/lib.rs       | 19 +-----
crates/buffer/src/selection.rs | 94 ++++++++++++++++++------------------
crates/clock/src/lib.rs        |  6 ++
crates/rpc/proto/zed.proto     | 12 ++-
5 files changed, 97 insertions(+), 67 deletions(-)

Detailed changes

crates/buffer/src/anchor.rs 🔗

@@ -2,7 +2,11 @@ use crate::Point;
 
 use super::{Buffer, Content};
 use anyhow::Result;
-use std::{cmp::Ordering, ops::Range};
+use std::{
+    cmp::Ordering,
+    fmt::{Debug, Formatter},
+    ops::Range,
+};
 use sum_tree::Bias;
 
 #[derive(Clone, Eq, PartialEq, Debug, Hash)]
@@ -108,6 +112,14 @@ impl AnchorSet {
 }
 
 impl<T> AnchorRangeMap<T> {
+    pub fn from_raw(version: clock::Global, entries: Vec<(Range<(usize, Bias)>, T)>) -> Self {
+        Self { version, entries }
+    }
+
+    pub fn raw_entries(&self) -> &[(Range<(usize, Bias)>, T)] {
+        &self.entries
+    }
+
     pub fn to_point_ranges<'a>(
         &'a self,
         content: impl Into<Content<'a>> + 'a,
@@ -123,6 +135,25 @@ impl<T> AnchorRangeMap<T> {
     }
 }
 
+impl<T: PartialEq> PartialEq for AnchorRangeMap<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.version == other.version && self.entries == other.entries
+    }
+}
+
+impl<T: Eq> Eq for AnchorRangeMap<T> {}
+
+impl<T: Debug> Debug for AnchorRangeMap<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        let f = f.debug_map();
+        for (range, value) in &self.entries {
+            f.key(range);
+            f.value(value);
+        }
+        f.finish()
+    }
+}
+
 impl AnchorRangeSet {
     pub fn to_point_ranges<'a>(
         &'a self,

crates/buffer/src/lib.rs 🔗

@@ -408,7 +408,7 @@ pub enum Operation {
     },
     UpdateSelections {
         set_id: SelectionSetId,
-        selections: Arc<[Selection]>,
+        selections: Arc<AnchorRangeMap<SelectionState>>,
         lamport_timestamp: clock::Lamport,
     },
     RemoveSelections {
@@ -502,12 +502,7 @@ impl Buffer {
             selections: self
                 .selections
                 .iter()
-                .map(|(set_id, set)| proto::SelectionSet {
-                    replica_id: set_id.replica_id as u32,
-                    lamport_timestamp: set_id.value,
-                    selections: set.selections.iter().map(Into::into).collect(),
-                    is_active: set.active,
-                })
+                .map(|(set_id, set)| set.into())
                 .collect(),
         }
     }
@@ -1123,11 +1118,7 @@ impl Buffer {
                 Operation::Edit(edit) => self.version >= edit.version,
                 Operation::Undo { undo, .. } => self.version >= undo.version,
                 Operation::UpdateSelections { selections, .. } => {
-                    selections.iter().all(|selection| {
-                        let contains_start = self.version >= selection.start.version;
-                        let contains_end = self.version >= selection.end.version;
-                        contains_start && contains_end
-                    })
+                    self.version >= *selections.version()
                 }
                 Operation::RemoveSelections { .. } => true,
                 Operation::SetActiveSelections { set_id, .. } => {
@@ -1262,14 +1253,14 @@ impl Buffer {
     pub fn update_selection_set(
         &mut self,
         set_id: SelectionSetId,
-        selections: impl Into<Arc<[Selection]>>,
+        selections: &[Selection],
     ) -> Result<Operation> {
         let selections = selections.into();
         let set = self
             .selections
             .get_mut(&set_id)
             .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
-        set.selections = selections.clone();
+        set.selections = todo!();
         Ok(Operation::UpdateSelections {
             set_id,
             selections,

crates/buffer/src/selection.rs 🔗

@@ -1,13 +1,7 @@
-use crate::{Anchor, Buffer, Point, ToOffset as _, ToPoint as _};
-use anyhow::anyhow;
+use crate::{Anchor, AnchorRangeMap, Buffer, Point, ToOffset as _, ToPoint as _};
 use rpc::proto;
-use std::{
-    cmp::Ordering,
-    convert::{TryFrom, TryInto},
-    mem,
-    ops::Range,
-    sync::Arc,
-};
+use std::{cmp::Ordering, mem, ops::Range, sync::Arc};
+use sum_tree::Bias;
 
 pub type SelectionSetId = clock::Lamport;
 pub type SelectionsVersion = usize;
@@ -32,7 +26,14 @@ pub struct Selection {
 pub struct SelectionSet {
     pub id: SelectionSetId,
     pub active: bool,
-    pub selections: Arc<[Selection]>,
+    pub selections: Arc<AnchorRangeMap<SelectionState>>,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct SelectionState {
+    pub id: usize,
+    pub reversed: bool,
+    pub goal: SelectionGoal,
 }
 
 impl Selection {
@@ -89,53 +90,52 @@ impl Selection {
     }
 }
 
-impl<'a> Into<proto::Selection> for &'a Selection {
-    fn into(self) -> proto::Selection {
-        proto::Selection {
-            id: self.id as u64,
-            start: Some((&self.start).into()),
-            end: Some((&self.end).into()),
-            reversed: self.reversed,
+impl<'a> Into<proto::SelectionSet> for &'a SelectionSet {
+    fn into(self) -> proto::SelectionSet {
+        let version = self.selections.version();
+        let entries = self.selections.raw_entries();
+        proto::SelectionSet {
+            replica_id: self.id.replica_id as u32,
+            lamport_timestamp: self.id.value as u32,
+            is_active: self.active,
+            version: version.into(),
+            selections: entries
+                .iter()
+                .map(|(range, state)| proto::Selection {
+                    id: state.id as u64,
+                    start: range.start.0 as u64,
+                    end: range.end.0 as u64,
+                    reversed: state.reversed,
+                })
+                .collect(),
         }
     }
 }
 
-impl TryFrom<proto::Selection> for Selection {
-    type Error = anyhow::Error;
-
-    fn try_from(selection: proto::Selection) -> Result<Self, Self::Error> {
-        Ok(Selection {
-            id: selection.id as usize,
-            start: selection
-                .start
-                .ok_or_else(|| anyhow!("missing selection start"))?
-                .try_into()?,
-            end: selection
-                .end
-                .ok_or_else(|| anyhow!("missing selection end"))?
-                .try_into()?,
-            reversed: selection.reversed,
-            goal: SelectionGoal::None,
-        })
-    }
-}
-
-impl TryFrom<proto::SelectionSet> for SelectionSet {
-    type Error = anyhow::Error;
-
-    fn try_from(set: proto::SelectionSet) -> Result<Self, Self::Error> {
-        Ok(Self {
+impl From<proto::SelectionSet> for SelectionSet {
+    fn from(set: proto::SelectionSet) -> Self {
+        Self {
             id: clock::Lamport {
                 replica_id: set.replica_id as u16,
                 value: set.lamport_timestamp,
             },
             active: set.is_active,
-            selections: Arc::from(
+            selections: Arc::new(AnchorRangeMap::from_raw(
+                set.version.into(),
                 set.selections
                     .into_iter()
-                    .map(TryInto::try_into)
-                    .collect::<Result<Vec<Selection>, _>>()?,
-            ),
-        })
+                    .map(|selection| {
+                        let range = (selection.start as usize, Bias::Left)
+                            ..(selection.end as usize, Bias::Right);
+                        let state = SelectionState {
+                            id: selection.id as usize,
+                            reversed: selection.reversed,
+                            goal: SelectionGoal::None,
+                        };
+                        (range, state)
+                    })
+                    .collect(),
+            )),
+        }
     }
 }

crates/clock/src/lib.rs 🔗

@@ -86,6 +86,12 @@ impl<'a> From<&'a Global> for Vec<rpc::proto::VectorClockEntry> {
     }
 }
 
+impl From<Global> for Vec<rpc::proto::VectorClockEntry> {
+    fn from(version: Global) -> Self {
+        (&version).into()
+    }
+}
+
 impl Global {
     pub fn new() -> Self {
         Self::default()

crates/rpc/proto/zed.proto 🔗

@@ -233,14 +233,15 @@ message Buffer {
 message SelectionSet {
     uint32 replica_id = 1;
     uint32 lamport_timestamp = 2;
-    repeated Selection selections = 3;
-    bool is_active = 4;
+    bool is_active = 3;
+    repeated VectorClockEntry version = 4;
+    repeated Selection selections = 5;
 }
 
 message Selection {
     uint64 id = 1;
-    Anchor start = 2;
-    Anchor end = 3;
+    uint64 start = 2;
+    uint64 end = 3;
     bool reversed = 4;
 }
 
@@ -291,7 +292,8 @@ message Operation {
         uint32 replica_id = 1;
         uint32 local_timestamp = 2;
         uint32 lamport_timestamp = 3;
-        repeated Selection selections = 4;
+        repeated VectorClockEntry version = 4;
+        repeated Selection selections = 5;
     }
 
     message RemoveSelections {