Implement anchor resolution using locators

Antonio Scandurra created

Change summary

crates/text/src/anchor.rs    | 589 ++++----------------------------------
crates/text/src/selection.rs |  87 ++--
crates/text/src/tests.rs     |  12 
crates/text/src/text.rs      | 370 ++++++++---------------
4 files changed, 244 insertions(+), 814 deletions(-)

Detailed changes

crates/text/src/anchor.rs 🔗

@@ -1,561 +1,96 @@
 use crate::{rope::TextDimension, Snapshot};
 
-use super::{Buffer, FromAnchor, FullOffset, Point, ToOffset};
+use super::{Buffer, ToOffset};
 use anyhow::Result;
-use std::{
-    cmp::Ordering,
-    fmt::{Debug, Formatter},
-    ops::Range,
-};
-use sum_tree::{Bias, SumTree};
+use std::{cmp::Ordering, fmt::Debug, ops::Range};
+use sum_tree::Bias;
 
 #[derive(Clone, Eq, PartialEq, Debug, Hash)]
-pub struct Anchor {
-    pub full_offset: FullOffset,
-    pub bias: Bias,
-    pub version: clock::Global,
-}
-
-#[derive(Clone)]
-pub struct AnchorMap<T> {
-    pub(crate) version: clock::Global,
-    pub(crate) bias: Bias,
-    pub(crate) entries: Vec<(FullOffset, T)>,
-}
-
-#[derive(Clone)]
-pub struct AnchorSet(pub(crate) AnchorMap<()>);
-
-#[derive(Clone)]
-pub struct AnchorRangeMap<T> {
-    pub(crate) version: clock::Global,
-    pub(crate) entries: Vec<(Range<FullOffset>, T)>,
-    pub(crate) start_bias: Bias,
-    pub(crate) end_bias: Bias,
-}
-
-#[derive(Clone)]
-pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>);
-
-#[derive(Clone)]
-pub struct AnchorRangeMultimap<T: Clone> {
-    pub(crate) entries: SumTree<AnchorRangeMultimapEntry<T>>,
-    pub(crate) version: clock::Global,
-    pub(crate) start_bias: Bias,
-    pub(crate) end_bias: Bias,
-}
-
-#[derive(Clone)]
-pub(crate) struct AnchorRangeMultimapEntry<T> {
-    pub(crate) range: FullOffsetRange,
-    pub(crate) value: T,
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct FullOffsetRange {
-    pub(crate) start: FullOffset,
-    pub(crate) end: FullOffset,
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct AnchorRangeMultimapSummary {
-    start: FullOffset,
-    end: FullOffset,
-    min_start: FullOffset,
-    max_end: FullOffset,
-    count: usize,
+pub enum Anchor {
+    Min,
+    Insertion {
+        timestamp: clock::Local,
+        offset: usize,
+        bias: Bias,
+    },
+    Max,
 }
 
 impl Anchor {
     pub fn min() -> Self {
-        Self {
-            full_offset: FullOffset(0),
-            bias: Bias::Left,
-            version: Default::default(),
-        }
+        Self::Min
     }
 
     pub fn max() -> Self {
-        Self {
-            full_offset: FullOffset::MAX,
-            bias: Bias::Right,
-            version: Default::default(),
-        }
+        Self::Max
     }
 
     pub fn cmp<'a>(&self, other: &Anchor, buffer: &Snapshot) -> Result<Ordering> {
-        if self == other {
-            return Ok(Ordering::Equal);
-        }
-
-        let offset_comparison = if self.version == other.version {
-            self.full_offset.cmp(&other.full_offset)
-        } else {
-            buffer
-                .full_offset_for_anchor(self)
-                .cmp(&buffer.full_offset_for_anchor(other))
-        };
-
-        Ok(offset_comparison.then_with(|| self.bias.cmp(&other.bias)))
-    }
-
-    pub fn bias_left(&self, buffer: &Buffer) -> Anchor {
-        if self.bias == Bias::Left {
-            self.clone()
-        } else {
-            buffer.anchor_before(self)
-        }
-    }
-
-    pub fn bias_right(&self, buffer: &Buffer) -> Anchor {
-        if self.bias == Bias::Right {
-            self.clone()
-        } else {
-            buffer.anchor_after(self)
-        }
-    }
-
-    pub fn summary<'a, D>(&self, content: &'a Snapshot) -> D
-    where
-        D: TextDimension<'a>,
-    {
-        content.summary_for_anchor(self)
-    }
-}
-
-impl<T> AnchorMap<T> {
-    pub fn version(&self) -> &clock::Global {
-        &self.version
-    }
-
-    pub fn len(&self) -> usize {
-        self.entries.len()
-    }
-
-    pub fn iter<'a, D>(&'a self, snapshot: &'a Snapshot) -> impl Iterator<Item = (D, &'a T)> + 'a
-    where
-        D: 'a + TextDimension<'a>,
-    {
-        snapshot
-            .summaries_for_anchors(
-                self.version.clone(),
-                self.bias,
-                self.entries.iter().map(|e| &e.0),
-            )
-            .zip(self.entries.iter().map(|e| &e.1))
-    }
-}
-
-impl AnchorSet {
-    pub fn version(&self) -> &clock::Global {
-        &self.0.version
-    }
-
-    pub fn len(&self) -> usize {
-        self.0.len()
-    }
-
-    pub fn iter<'a, D>(&'a self, content: &'a Snapshot) -> impl Iterator<Item = D> + 'a
-    where
-        D: 'a + TextDimension<'a>,
-    {
-        self.0.iter(content).map(|(position, _)| position)
-    }
-}
-
-impl<T> AnchorRangeMap<T> {
-    pub fn version(&self) -> &clock::Global {
-        &self.version
-    }
-
-    pub fn len(&self) -> usize {
-        self.entries.len()
-    }
-
-    pub fn from_full_offset_ranges(
-        version: clock::Global,
-        start_bias: Bias,
-        end_bias: Bias,
-        entries: Vec<(Range<FullOffset>, T)>,
-    ) -> Self {
-        Self {
-            version,
-            start_bias,
-            end_bias,
-            entries,
-        }
-    }
-
-    pub fn ranges<'a, D>(
-        &'a self,
-        content: &'a Snapshot,
-    ) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
-    where
-        D: 'a + TextDimension<'a>,
-    {
-        content
-            .summaries_for_anchor_ranges(
-                self.version.clone(),
-                self.start_bias,
-                self.end_bias,
-                self.entries.iter().map(|e| &e.0),
-            )
-            .zip(self.entries.iter().map(|e| &e.1))
-    }
-
-    pub fn intersecting_ranges<'a, D, I>(
-        &'a self,
-        range: Range<(I, Bias)>,
-        content: &'a Snapshot,
-    ) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
-    where
-        D: 'a + TextDimension<'a>,
-        I: ToOffset,
-    {
-        let range = content.anchor_at(range.start.0, range.start.1)
-            ..content.anchor_at(range.end.0, range.end.1);
-
-        let mut probe_anchor = Anchor {
-            full_offset: Default::default(),
-            bias: self.start_bias,
-            version: self.version.clone(),
-        };
-        let start_ix = self.entries.binary_search_by(|probe| {
-            probe_anchor.full_offset = probe.0.end;
-            probe_anchor.cmp(&range.start, &content).unwrap()
-        });
-
-        match start_ix {
-            Ok(start_ix) | Err(start_ix) => content
-                .summaries_for_anchor_ranges(
-                    self.version.clone(),
-                    self.start_bias,
-                    self.end_bias,
-                    self.entries[start_ix..].iter().map(|e| &e.0),
-                )
-                .zip(self.entries.iter().map(|e| &e.1)),
-        }
-    }
-
-    pub fn full_offset_ranges(&self) -> impl Iterator<Item = &(Range<FullOffset>, T)> {
-        self.entries.iter()
-    }
-
-    pub fn min_by_key<'a, D, F, K>(
-        &self,
-        content: &'a Snapshot,
-        mut extract_key: F,
-    ) -> Option<(Range<D>, &T)>
-    where
-        D: 'a + TextDimension<'a>,
-        F: FnMut(&T) -> K,
-        K: Ord,
-    {
-        self.entries
-            .iter()
-            .min_by_key(|(_, value)| extract_key(value))
-            .map(|(range, value)| (self.resolve_range(range, &content), value))
-    }
-
-    pub fn max_by_key<'a, D, F, K>(
-        &self,
-        content: &'a Snapshot,
-        mut extract_key: F,
-    ) -> Option<(Range<D>, &T)>
-    where
-        D: 'a + TextDimension<'a>,
-        F: FnMut(&T) -> K,
-        K: Ord,
-    {
-        self.entries
-            .iter()
-            .max_by_key(|(_, value)| extract_key(value))
-            .map(|(range, value)| (self.resolve_range(range, &content), value))
-    }
-
-    fn resolve_range<'a, D>(&self, range: &Range<FullOffset>, content: &'a Snapshot) -> Range<D>
-    where
-        D: 'a + TextDimension<'a>,
-    {
-        let mut anchor = Anchor {
-            full_offset: range.start,
-            bias: self.start_bias,
-            version: self.version.clone(),
-        };
-        let start = content.summary_for_anchor(&anchor);
-
-        anchor.full_offset = range.end;
-        anchor.bias = self.end_bias;
-        let end = content.summary_for_anchor(&anchor);
-
-        start..end
-    }
-}
-
-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 mut f = f.debug_map();
-        for (range, value) in &self.entries {
-            f.key(range);
-            f.value(value);
-        }
-        f.finish()
-    }
-}
-
-impl Debug for AnchorRangeSet {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        let mut f = f.debug_set();
-        for (range, _) in &self.0.entries {
-            f.entry(range);
-        }
-        f.finish()
-    }
-}
-
-impl AnchorRangeSet {
-    pub fn len(&self) -> usize {
-        self.0.len()
-    }
-
-    pub fn version(&self) -> &clock::Global {
-        self.0.version()
-    }
-
-    pub fn ranges<'a, D>(&'a self, content: &'a Snapshot) -> impl 'a + Iterator<Item = Range<Point>>
-    where
-        D: 'a + TextDimension<'a>,
-    {
-        self.0.ranges(content).map(|(range, _)| range)
-    }
-}
+        match (self, other) {
+            (Self::Min, Self::Min) => Ok(Ordering::Equal),
+            (Self::Min, _) => Ok(Ordering::Less),
+            (_, Self::Min) => Ok(Ordering::Greater),
+            (Self::Max, Self::Max) => Ok(Ordering::Equal),
+            (Self::Max, _) => Ok(Ordering::Greater),
+            (_, Self::Max) => Ok(Ordering::Less),
+            (
+                Self::Insertion {
+                    timestamp: lhs_id,
+                    bias: lhs_bias,
+                    offset: lhs_offset,
+                },
+                Self::Insertion {
+                    timestamp: rhs_id,
+                    bias: rhs_bias,
+                    offset: rhs_offset,
+                },
+            ) => {
+                let offset_comparison = if lhs_id == rhs_id {
+                    lhs_offset.cmp(&rhs_offset)
+                } else {
+                    buffer
+                        .full_offset_for_anchor(self)
+                        .cmp(&buffer.full_offset_for_anchor(other))
+                };
 
-impl<T: Clone> Default for AnchorRangeMultimap<T> {
-    fn default() -> Self {
-        Self {
-            entries: Default::default(),
-            version: Default::default(),
-            start_bias: Bias::Left,
-            end_bias: Bias::Left,
+                Ok(offset_comparison.then_with(|| lhs_bias.cmp(&rhs_bias)))
+            }
         }
     }
-}
-
-impl<T: Clone> AnchorRangeMultimap<T> {
-    pub fn version(&self) -> &clock::Global {
-        &self.version
-    }
-
-    pub fn intersecting_ranges<'a, I, O>(
-        &'a self,
-        range: Range<I>,
-        content: &'a Snapshot,
-        inclusive: bool,
-    ) -> impl Iterator<Item = (usize, Range<O>, &T)> + 'a
-    where
-        I: ToOffset,
-        O: FromAnchor,
-    {
-        let end_bias = if inclusive { Bias::Right } else { Bias::Left };
-        let range = range.start.to_full_offset(&content, Bias::Left)
-            ..range.end.to_full_offset(&content, end_bias);
-        let mut cursor = self.entries.filter::<_, usize>(
-            {
-                let mut endpoint = Anchor {
-                    full_offset: FullOffset(0),
-                    bias: Bias::Right,
-                    version: self.version.clone(),
-                };
-                move |summary: &AnchorRangeMultimapSummary| {
-                    endpoint.full_offset = summary.max_end;
-                    endpoint.bias = self.end_bias;
-                    let max_end = endpoint.to_full_offset(&content, self.end_bias);
-                    let start_cmp = range.start.cmp(&max_end);
-
-                    endpoint.full_offset = summary.min_start;
-                    endpoint.bias = self.start_bias;
-                    let min_start = endpoint.to_full_offset(&content, self.start_bias);
-                    let end_cmp = range.end.cmp(&min_start);
-
-                    if inclusive {
-                        start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
-                    } else {
-                        start_cmp == Ordering::Less && end_cmp == Ordering::Greater
-                    }
-                }
-            },
-            &(),
-        );
 
-        std::iter::from_fn({
-            let mut endpoint = Anchor {
-                full_offset: FullOffset(0),
-                bias: Bias::Left,
-                version: self.version.clone(),
-            };
-            move || {
-                if let Some(item) = cursor.item() {
-                    let ix = *cursor.start();
-                    endpoint.full_offset = item.range.start;
-                    endpoint.bias = self.start_bias;
-                    let start = O::from_anchor(&endpoint, &content);
-                    endpoint.full_offset = item.range.end;
-                    endpoint.bias = self.end_bias;
-                    let end = O::from_anchor(&endpoint, &content);
-                    let value = &item.value;
-                    cursor.next(&());
-                    Some((ix, start..end, value))
+    pub fn bias_left(&self, buffer: &Buffer) -> Anchor {
+        match self {
+            Anchor::Min => Anchor::Min,
+            Anchor::Insertion { bias, .. } => {
+                if *bias == Bias::Left {
+                    self.clone()
                 } else {
-                    None
+                    buffer.anchor_before(self)
                 }
             }
-        })
-    }
-
-    pub fn from_full_offset_ranges(
-        version: clock::Global,
-        start_bias: Bias,
-        end_bias: Bias,
-        entries: impl Iterator<Item = (Range<FullOffset>, T)>,
-    ) -> Self {
-        Self {
-            version,
-            start_bias,
-            end_bias,
-            entries: SumTree::from_iter(
-                entries.map(|(range, value)| AnchorRangeMultimapEntry {
-                    range: FullOffsetRange {
-                        start: range.start,
-                        end: range.end,
-                    },
-                    value,
-                }),
-                &(),
-            ),
+            Anchor::Max => buffer.anchor_before(self),
         }
     }
 
-    pub fn full_offset_ranges(&self) -> impl Iterator<Item = (Range<FullOffset>, &T)> {
-        self.entries
-            .cursor::<()>()
-            .map(|entry| (entry.range.start..entry.range.end, &entry.value))
-    }
-
-    pub fn filter<'a, O, F>(
-        &'a self,
-        content: &'a Snapshot,
-        mut f: F,
-    ) -> impl 'a + Iterator<Item = (usize, Range<O>, &T)>
-    where
-        O: FromAnchor,
-        F: 'a + FnMut(&'a T) -> bool,
-    {
-        let mut endpoint = Anchor {
-            full_offset: FullOffset(0),
-            bias: Bias::Left,
-            version: self.version.clone(),
-        };
-        self.entries
-            .cursor::<()>()
-            .enumerate()
-            .filter_map(move |(ix, entry)| {
-                if f(&entry.value) {
-                    endpoint.full_offset = entry.range.start;
-                    endpoint.bias = self.start_bias;
-                    let start = O::from_anchor(&endpoint, &content);
-                    endpoint.full_offset = entry.range.end;
-                    endpoint.bias = self.end_bias;
-                    let end = O::from_anchor(&endpoint, &content);
-                    Some((ix, start..end, &entry.value))
+    pub fn bias_right(&self, buffer: &Buffer) -> Anchor {
+        match self {
+            Anchor::Min => buffer.anchor_after(self),
+            Anchor::Insertion { bias, .. } => {
+                if *bias == Bias::Right {
+                    self.clone()
                 } else {
-                    None
+                    buffer.anchor_after(self)
                 }
-            })
-    }
-}
-
-impl<T: Clone> sum_tree::Item for AnchorRangeMultimapEntry<T> {
-    type Summary = AnchorRangeMultimapSummary;
-
-    fn summary(&self) -> Self::Summary {
-        AnchorRangeMultimapSummary {
-            start: self.range.start,
-            end: self.range.end,
-            min_start: self.range.start,
-            max_end: self.range.end,
-            count: 1,
-        }
-    }
-}
-
-impl Default for AnchorRangeMultimapSummary {
-    fn default() -> Self {
-        Self {
-            start: FullOffset(0),
-            end: FullOffset::MAX,
-            min_start: FullOffset::MAX,
-            max_end: FullOffset(0),
-            count: 0,
-        }
-    }
-}
-
-impl sum_tree::Summary for AnchorRangeMultimapSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, other: &Self, _: &Self::Context) {
-        self.min_start = self.min_start.min(other.min_start);
-        self.max_end = self.max_end.max(other.max_end);
-
-        #[cfg(debug_assertions)]
-        {
-            let start_comparison = self.start.cmp(&other.start);
-            assert!(start_comparison <= Ordering::Equal);
-            if start_comparison == Ordering::Equal {
-                assert!(self.end.cmp(&other.end) >= Ordering::Equal);
             }
+            Anchor::Max => Anchor::Max,
         }
-
-        self.start = other.start;
-        self.end = other.end;
-        self.count += other.count;
-    }
-}
-
-impl Default for FullOffsetRange {
-    fn default() -> Self {
-        Self {
-            start: FullOffset(0),
-            end: FullOffset::MAX,
-        }
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, AnchorRangeMultimapSummary> for usize {
-    fn add_summary(&mut self, summary: &'a AnchorRangeMultimapSummary, _: &()) {
-        *self += summary.count;
     }
-}
-
-impl<'a> sum_tree::Dimension<'a, AnchorRangeMultimapSummary> for FullOffsetRange {
-    fn add_summary(&mut self, summary: &'a AnchorRangeMultimapSummary, _: &()) {
-        self.start = summary.start;
-        self.end = summary.end;
-    }
-}
 
-impl<'a> sum_tree::SeekTarget<'a, AnchorRangeMultimapSummary, FullOffsetRange> for FullOffsetRange {
-    fn cmp(&self, cursor_location: &FullOffsetRange, _: &()) -> Ordering {
-        Ord::cmp(&self.start, &cursor_location.start)
-            .then_with(|| Ord::cmp(&cursor_location.end, &self.end))
+    pub fn summary<'a, D>(&self, content: &'a Snapshot) -> D
+    where
+        D: TextDimension<'a>,
+    {
+        content.summary_for_anchor(self)
     }
 }
 

crates/text/src/selection.rs 🔗

@@ -1,8 +1,8 @@
 use sum_tree::Bias;
 
-use crate::{rope::TextDimension, Snapshot};
+use crate::{rope::TextDimension, Anchor, Snapshot};
 
-use super::{AnchorRangeMap, Buffer, Point, ToOffset, ToPoint};
+use super::{Buffer, Point, ToOffset, ToPoint};
 use std::{cmp::Ordering, ops::Range, sync::Arc};
 
 pub type SelectionSetId = clock::Lamport;
@@ -28,7 +28,7 @@ pub struct Selection<T> {
 pub struct SelectionSet {
     pub id: SelectionSetId,
     pub active: bool,
-    pub selections: Arc<AnchorRangeMap<SelectionState>>,
+    pub selections: Arc<[Selection<Anchor>]>,
 }
 
 #[derive(Debug, Eq, PartialEq)]
@@ -98,6 +98,21 @@ impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
     }
 }
 
+impl Selection<Anchor> {
+    pub fn resolve<'a, D: 'a + TextDimension<'a>>(
+        &'a self,
+        snapshot: &'a Snapshot,
+    ) -> Selection<D> {
+        Selection {
+            id: self.id,
+            start: snapshot.summary_for_anchor(&self.start),
+            end: snapshot.summary_for_anchor(&self.end),
+            reversed: self.reversed,
+            goal: self.goal,
+        }
+    }
+}
+
 impl SelectionSet {
     pub fn len(&self) -> usize {
         self.selections.len()
@@ -105,69 +120,59 @@ impl SelectionSet {
 
     pub fn selections<'a, D>(
         &'a self,
-        content: &'a Snapshot,
+        snapshot: &'a Snapshot,
     ) -> impl 'a + Iterator<Item = Selection<D>>
     where
         D: 'a + TextDimension<'a>,
     {
-        self.selections
-            .ranges(content)
-            .map(|(range, state)| Selection {
-                id: state.id,
-                start: range.start,
-                end: range.end,
-                reversed: state.reversed,
-                goal: state.goal,
-            })
+        self.selections.iter().map(|s| s.resolve(snapshot))
     }
 
     pub fn intersecting_selections<'a, D, I>(
         &'a self,
         range: Range<(I, Bias)>,
-        content: &'a Snapshot,
+        snapshot: &'a Snapshot,
     ) -> impl 'a + Iterator<Item = Selection<D>>
     where
         D: 'a + TextDimension<'a>,
         I: 'a + ToOffset,
     {
-        self.selections
-            .intersecting_ranges(range, content)
-            .map(|(range, state)| Selection {
-                id: state.id,
-                start: range.start,
-                end: range.end,
-                reversed: state.reversed,
-                goal: state.goal,
-            })
+        let start = snapshot.anchor_at(range.start.0, range.start.1);
+        let end = snapshot.anchor_at(range.end.0, range.end.1);
+        let start_ix = match self
+            .selections
+            .binary_search_by(|probe| probe.start.cmp(&start, snapshot).unwrap())
+        {
+            Ok(ix) | Err(ix) => ix,
+        };
+        let end_ix = match self
+            .selections
+            .binary_search_by(|probe| probe.end.cmp(&end, snapshot).unwrap())
+        {
+            Ok(ix) | Err(ix) => ix,
+        };
+        self.selections[start_ix..end_ix]
+            .iter()
+            .map(|s| s.resolve(snapshot))
     }
 
-    pub fn oldest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option<Selection<D>>
+    pub fn oldest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
     where
         D: 'a + TextDimension<'a>,
     {
         self.selections
-            .min_by_key(content, |selection| selection.id)
-            .map(|(range, state)| Selection {
-                id: state.id,
-                start: range.start,
-                end: range.end,
-                reversed: state.reversed,
-                goal: state.goal,
-            })
+            .iter()
+            .min_by_key(|s| s.id)
+            .map(|s| s.resolve(snapshot))
     }
 
-    pub fn newest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option<Selection<D>>
+    pub fn newest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
     where
         D: 'a + TextDimension<'a>,
     {
         self.selections
-            .max_by_key(content, |selection| selection.id)
-            .map(|(range, state)| Selection {
-                id: state.id,
-                start: range.start,
-                end: range.end,
-                reversed: state.reversed,
-                goal: state.goal,
-            })
+            .iter()
+            .max_by_key(|s| s.id)
+            .map(|s| s.resolve(snapshot))
     }
 }

crates/text/src/tests.rs 🔗

@@ -645,11 +645,13 @@ impl Buffer {
             assert_eq!(insertion_fragment.fragment_id, fragment.id);
         }
 
-        let insertions = self.snapshot.insertions.items(&());
-        assert_eq!(
-            HashSet::from_iter(insertions.iter().map(|i| &i.fragment_id)).len(),
-            insertions.len()
-        );
+        let mut cursor = self.snapshot.fragments.cursor::<Locator>();
+        for insertion_fragment in self.snapshot.insertions.cursor::<()>() {
+            cursor.seek(&insertion_fragment.fragment_id, Bias::Left, &None);
+            let fragment = cursor.item().unwrap();
+            assert_eq!(insertion_fragment.fragment_id, fragment.id);
+            assert_eq!(insertion_fragment.split_offset, fragment.insertion_offset);
+        }
     }
 }
 

crates/text/src/text.rs 🔗

@@ -27,7 +27,7 @@ use rope::TextDimension;
 pub use rope::{Chunks, Rope, TextSummary};
 pub use selection::*;
 use std::{
-    cmp::{self, Reverse},
+    cmp::{self, Ordering},
     iter::Iterator,
     ops::{self, Deref, Range, Sub},
     str,
@@ -67,8 +67,8 @@ pub struct Transaction {
     end: clock::Global,
     edits: Vec<clock::Local>,
     ranges: Vec<Range<FullOffset>>,
-    selections_before: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
-    selections_after: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
+    selections_before: HashMap<SelectionSetId, Arc<[Selection<Anchor>]>>,
+    selections_after: HashMap<SelectionSetId, Arc<[Selection<Anchor>]>>,
     first_edit_at: Instant,
     last_edit_at: Instant,
 }
@@ -155,7 +155,7 @@ impl History {
     fn start_transaction(
         &mut self,
         start: clock::Global,
-        selections_before: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
+        selections_before: HashMap<SelectionSetId, Arc<[Selection<Anchor>]>>,
         now: Instant,
     ) {
         self.transaction_depth += 1;
@@ -175,7 +175,7 @@ impl History {
 
     fn end_transaction(
         &mut self,
-        selections_after: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
+        selections_after: HashMap<SelectionSetId, Arc<[Selection<Anchor>]>>,
         now: Instant,
     ) -> Option<&Transaction> {
         assert_ne!(self.transaction_depth, 0);
@@ -430,7 +430,7 @@ pub enum Operation {
     },
     UpdateSelections {
         set_id: SelectionSetId,
-        selections: Arc<AnchorRangeMap<SelectionState>>,
+        selections: Arc<[Selection<Anchor>]>,
         lamport_timestamp: clock::Lamport,
     },
     RemoveSelections {
@@ -1122,9 +1122,9 @@ impl Buffer {
             match op {
                 Operation::Edit(edit) => self.version.ge(&edit.version),
                 Operation::Undo { undo, .. } => self.version.ge(&undo.version),
-                Operation::UpdateSelections { selections, .. } => {
-                    self.version.ge(selections.version())
-                }
+                Operation::UpdateSelections { selections, .. } => selections
+                    .iter()
+                    .all(|s| self.can_resolve(&s.start) && self.can_resolve(&s.end)),
                 Operation::RemoveSelections { .. } => true,
                 Operation::SetActiveSelections { set_id, .. } => {
                     set_id.map_or(true, |set_id| self.selections.contains_key(&set_id))
@@ -1135,6 +1135,14 @@ impl Buffer {
         }
     }
 
+    fn can_resolve(&self, anchor: &Anchor) -> bool {
+        match anchor {
+            Anchor::Min => true,
+            Anchor::Insertion { timestamp, .. } => self.version.observed(*timestamp),
+            Anchor::Max => true,
+        }
+    }
+
     pub fn peek_undo_stack(&self) -> Option<&Transaction> {
         self.history.undo_stack.last()
     }
@@ -1280,25 +1288,22 @@ impl Buffer {
         self.selections.iter()
     }
 
-    fn build_selection_anchor_range_map<T: ToOffset>(
+    fn build_anchor_selection_set<T: ToOffset>(
         &self,
         selections: &[Selection<T>],
-    ) -> Arc<AnchorRangeMap<SelectionState>> {
-        Arc::new(self.anchor_range_map(
-            Bias::Left,
-            Bias::Left,
-            selections.iter().map(|selection| {
-                let start = selection.start.to_offset(self);
-                let end = selection.end.to_offset(self);
-                let range = start..end;
-                let state = SelectionState {
+    ) -> Arc<[Selection<Anchor>]> {
+        Arc::from(
+            selections
+                .iter()
+                .map(|selection| Selection {
                     id: selection.id,
+                    start: self.anchor_before(&selection.start),
+                    end: self.anchor_before(&selection.end),
                     reversed: selection.reversed,
                     goal: selection.goal,
-                };
-                (range, state)
-            }),
-        ))
+                })
+                .collect::<Vec<_>>(),
+        )
     }
 
     pub fn update_selection_set<T: ToOffset>(
@@ -1306,7 +1311,7 @@ impl Buffer {
         set_id: SelectionSetId,
         selections: &[Selection<T>],
     ) -> Result<Operation> {
-        let selections = self.build_selection_anchor_range_map(selections);
+        let selections = self.build_anchor_selection_set(selections);
         let set = self
             .selections
             .get_mut(&set_id)
@@ -1322,7 +1327,7 @@ impl Buffer {
     pub fn restore_selection_set(
         &mut self,
         set_id: SelectionSetId,
-        selections: Arc<AnchorRangeMap<SelectionState>>,
+        selections: Arc<[Selection<Anchor>]>,
     ) -> Result<Operation> {
         let set = self
             .selections
@@ -1337,7 +1342,7 @@ impl Buffer {
     }
 
     pub fn add_selection_set<T: ToOffset>(&mut self, selections: &[Selection<T>]) -> Operation {
-        let selections = self.build_selection_anchor_range_map(selections);
+        let selections = self.build_anchor_selection_set(selections);
         let set_id = self.lamport_clock.tick();
         self.selections.insert(
             set_id,
@@ -1675,19 +1680,81 @@ impl Snapshot {
     where
         D: TextDimension<'a>,
     {
-        let cx = Some(anchor.version.clone());
-        let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
-        cursor.seek(
-            &VersionedFullOffset::Offset(anchor.full_offset),
-            anchor.bias,
-            &cx,
-        );
-        let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-            anchor.full_offset - cursor.start().0.full_offset()
-        } else {
-            0
-        };
-        self.text_summary_for_range(0..cursor.start().1 + overshoot)
+        match anchor {
+            Anchor::Min => D::default(),
+            Anchor::Insertion {
+                timestamp,
+                offset,
+                bias,
+            } => {
+                let anchor_key = InsertionFragmentKey {
+                    timestamp: *timestamp,
+                    split_offset: *offset,
+                };
+                let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>();
+                insertion_cursor.seek(&anchor_key, *bias, &());
+                if let Some(insertion) = insertion_cursor.item() {
+                    let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
+                    if comparison == Ordering::Greater
+                        || (*bias == Bias::Left && comparison == Ordering::Equal && *offset > 0)
+                    {
+                        insertion_cursor.prev(&());
+                    }
+                } else {
+                    insertion_cursor.prev(&());
+                }
+                let insertion = insertion_cursor.item().expect("invalid insertion");
+                debug_assert_eq!(insertion.timestamp, *timestamp, "invalid insertion");
+
+                let mut fragment_cursor = self.fragments.cursor::<(Locator, usize)>();
+                fragment_cursor.seek(&insertion.fragment_id, Bias::Left, &None);
+                let fragment = fragment_cursor.item().unwrap();
+                let mut fragment_offset = fragment_cursor.start().1;
+                if fragment.visible {
+                    fragment_offset += *offset - insertion.split_offset;
+                }
+                self.text_summary_for_range(0..fragment_offset)
+            }
+            Anchor::Max => D::from_text_summary(&self.visible_text.summary()),
+        }
+    }
+
+    fn full_offset_for_anchor(&self, anchor: &Anchor) -> FullOffset {
+        match anchor {
+            Anchor::Min => Default::default(),
+            Anchor::Insertion {
+                timestamp,
+                offset,
+                bias,
+            } => {
+                let anchor_key = InsertionFragmentKey {
+                    timestamp: *timestamp,
+                    split_offset: *offset,
+                };
+                let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>();
+                insertion_cursor.seek(&anchor_key, *bias, &());
+                if let Some(insertion) = insertion_cursor.item() {
+                    let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
+                    if comparison == Ordering::Greater
+                        || (*bias == Bias::Left && comparison == Ordering::Equal && *offset > 0)
+                    {
+                        insertion_cursor.prev(&());
+                    }
+                } else {
+                    insertion_cursor.prev(&());
+                }
+                let insertion = insertion_cursor.item().expect("invalid insertion");
+                debug_assert_eq!(insertion.timestamp, *timestamp, "invalid insertion");
+
+                let mut fragment_cursor = self.fragments.cursor::<(Locator, FullOffset)>();
+                fragment_cursor.seek(&insertion.fragment_id, Bias::Left, &None);
+                fragment_cursor.start().1 + (*offset - insertion.split_offset)
+            }
+            Anchor::Max => {
+                let text = self.fragments.summary().text;
+                FullOffset(text.visible + text.deleted)
+            }
+        }
     }
 
     pub fn text_summary_for_range<'a, D, O: ToOffset>(&'a self, range: Range<O>) -> D
@@ -1699,70 +1766,6 @@ impl Snapshot {
             .summary(range.end.to_offset(self))
     }
 
-    fn summaries_for_anchors<'a, D, I>(
-        &'a self,
-        version: clock::Global,
-        bias: Bias,
-        ranges: I,
-    ) -> impl 'a + Iterator<Item = D>
-    where
-        D: 'a + TextDimension<'a>,
-        I: 'a + IntoIterator<Item = &'a FullOffset>,
-    {
-        let cx = Some(version.clone());
-        let mut summary = D::default();
-        let mut rope_cursor = self.visible_text.cursor(0);
-        let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
-        ranges.into_iter().map(move |offset| {
-            cursor.seek_forward(&VersionedFullOffset::Offset(*offset), bias, &cx);
-            let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-                *offset - cursor.start().0.full_offset()
-            } else {
-                0
-            };
-            summary.add_assign(&rope_cursor.summary(cursor.start().1 + overshoot));
-            summary.clone()
-        })
-    }
-
-    fn summaries_for_anchor_ranges<'a, D, I>(
-        &'a self,
-        version: clock::Global,
-        start_bias: Bias,
-        end_bias: Bias,
-        ranges: I,
-    ) -> impl 'a + Iterator<Item = Range<D>>
-    where
-        D: 'a + TextDimension<'a>,
-        I: 'a + IntoIterator<Item = &'a Range<FullOffset>>,
-    {
-        let cx = Some(version);
-        let mut summary = D::default();
-        let mut rope_cursor = self.visible_text.cursor(0);
-        let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
-        ranges.into_iter().map(move |range| {
-            cursor.seek_forward(&VersionedFullOffset::Offset(range.start), start_bias, &cx);
-            let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-                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(range.end), end_bias, &cx);
-            let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
-                range.end - cursor.start().0.full_offset()
-            } else {
-                0
-            };
-            summary.add_assign(&rope_cursor.summary::<D>(cursor.start().1 + overshoot));
-            let end_summary = summary.clone();
-
-            start_summary..end_summary
-        })
-    }
-
     pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
         self.anchor_at(position, Bias::Left)
     }
@@ -1772,139 +1775,22 @@ impl Snapshot {
     }
 
     pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
-        Anchor {
-            full_offset: position.to_full_offset(self, bias),
-            bias,
-            version: self.version.clone(),
-        }
-    }
-
-    pub fn anchor_map<T, E>(&self, bias: Bias, entries: E) -> AnchorMap<T>
-    where
-        E: IntoIterator<Item = (usize, T)>,
-    {
-        let version = self.version.clone();
-        let mut cursor = self.fragments.cursor::<FragmentTextSummary>();
-        let entries = entries
-            .into_iter()
-            .map(|(offset, value)| {
-                cursor.seek_forward(&offset, bias, &None);
-                let full_offset = FullOffset(cursor.start().deleted + offset);
-                (full_offset, value)
-            })
-            .collect();
-
-        AnchorMap {
-            version,
-            bias,
-            entries,
-        }
-    }
-
-    pub fn anchor_range_map<T, E>(
-        &self,
-        start_bias: Bias,
-        end_bias: Bias,
-        entries: E,
-    ) -> AnchorRangeMap<T>
-    where
-        E: IntoIterator<Item = (Range<usize>, T)>,
-    {
-        let version = self.version.clone();
-        let mut cursor = self.fragments.cursor::<FragmentTextSummary>();
-        let entries = entries
-            .into_iter()
-            .map(|(range, value)| {
-                let Range {
-                    start: start_offset,
-                    end: end_offset,
-                } = range;
-                cursor.seek_forward(&start_offset, start_bias, &None);
-                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..full_end_offset, value)
-            })
-            .collect();
-
-        AnchorRangeMap {
-            version,
-            start_bias,
-            end_bias,
-            entries,
-        }
-    }
-
-    pub fn anchor_set<E>(&self, bias: Bias, entries: E) -> AnchorSet
-    where
-        E: IntoIterator<Item = usize>,
-    {
-        AnchorSet(self.anchor_map(bias, entries.into_iter().map(|range| (range, ()))))
-    }
-
-    pub fn anchor_range_set<E>(
-        &self,
-        start_bias: Bias,
-        end_bias: Bias,
-        entries: E,
-    ) -> AnchorRangeSet
-    where
-        E: IntoIterator<Item = Range<usize>>,
-    {
-        AnchorRangeSet(self.anchor_range_map(
-            start_bias,
-            end_bias,
-            entries.into_iter().map(|range| (range, ())),
-        ))
-    }
-
-    pub fn anchor_range_multimap<T, E, O>(
-        &self,
-        start_bias: Bias,
-        end_bias: Bias,
-        entries: E,
-    ) -> AnchorRangeMultimap<T>
-    where
-        T: Clone,
-        E: IntoIterator<Item = (Range<O>, T)>,
-        O: ToOffset,
-    {
-        let mut entries = entries
-            .into_iter()
-            .map(|(range, value)| AnchorRangeMultimapEntry {
-                range: FullOffsetRange {
-                    start: range.start.to_full_offset(self, start_bias),
-                    end: range.end.to_full_offset(self, end_bias),
-                },
-                value,
-            })
-            .collect::<Vec<_>>();
-        entries.sort_unstable_by_key(|i| (i.range.start, Reverse(i.range.end)));
-        AnchorRangeMultimap {
-            entries: SumTree::from_iter(entries, &()),
-            version: self.version.clone(),
-            start_bias,
-            end_bias,
-        }
-    }
-
-    fn full_offset_for_anchor(&self, anchor: &Anchor) -> FullOffset {
-        let cx = Some(anchor.version.clone());
-        let mut cursor = self
-            .fragments
-            .cursor::<(VersionedFullOffset, FragmentTextSummary)>();
-        cursor.seek(
-            &VersionedFullOffset::Offset(anchor.full_offset),
-            anchor.bias,
-            &cx,
-        );
-        let overshoot = if cursor.item().is_some() {
-            anchor.full_offset - cursor.start().0.full_offset()
+        let offset = position.to_offset(self);
+        if bias == Bias::Left && offset == 0 {
+            Anchor::Min
+        } else if bias == Bias::Right && offset == self.len() {
+            Anchor::Max
         } else {
-            0
-        };
-        let summary = cursor.start().1;
-        FullOffset(summary.visible + summary.deleted + overshoot)
+            let mut fragment_cursor = self.fragments.cursor::<(usize, Locator)>();
+            fragment_cursor.seek(&offset, bias, &None);
+            let fragment = fragment_cursor.item().unwrap();
+            let overshoot = offset - fragment_cursor.start().0;
+            Anchor::Insertion {
+                timestamp: fragment.insertion_timestamp.local(),
+                offset: fragment.insertion_offset + overshoot,
+                bias,
+            }
+        }
     }
 
     pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
@@ -2200,10 +2086,6 @@ impl sum_tree::Summary for InsertionFragmentKey {
 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct FullOffset(pub usize);
 
-impl FullOffset {
-    const MAX: Self = FullOffset(usize::MAX);
-}
-
 impl ops::AddAssign<usize> for FullOffset {
     fn add_assign(&mut self, rhs: usize) {
         self.0 += rhs;
@@ -2239,6 +2121,12 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FullOffset {
     }
 }
 
+impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Locator {
+    fn add_summary(&mut self, summary: &FragmentSummary, _: &Option<clock::Global>) {
+        *self = summary.max_id.clone();
+    }
+}
+
 impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, FragmentTextSummary> for usize {
     fn cmp(
         &self,
@@ -2363,9 +2251,9 @@ impl ToOffset for Anchor {
     }
 }
 
-impl<'a> ToOffset for &'a Anchor {
+impl<'a, T: ToOffset> ToOffset for &'a T {
     fn to_offset(&self, content: &Snapshot) -> usize {
-        content.summary_for_anchor(self)
+        (*self).to_offset(content)
     }
 }