diff --git a/crates/clock/src/clock.rs b/crates/clock/src/clock.rs index 6e8b460861..2632aecce5 100644 --- a/crates/clock/src/clock.rs +++ b/crates/clock/src/clock.rs @@ -21,6 +21,15 @@ pub struct Lamport { } impl Local { + pub const MIN: Self = Self { + replica_id: ReplicaId::MIN, + value: Seq::MIN, + }; + pub const MAX: Self = Self { + replica_id: ReplicaId::MAX, + value: Seq::MAX, + }; + pub fn new(replica_id: ReplicaId) -> Self { Self { replica_id, diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 1123bd2104..a781c9f887 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,88 +1,57 @@ -use crate::{rope::TextDimension, Snapshot}; - -use super::{Buffer, ToOffset}; +use super::{rope::TextDimension, Buffer, Point, Snapshot, ToOffset}; use anyhow::Result; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; #[derive(Clone, Eq, PartialEq, Debug, Hash)] -pub enum Anchor { - Min, - Insertion { - timestamp: clock::Local, - offset: usize, - bias: Bias, - }, - Max, +pub struct Anchor { + pub timestamp: clock::Local, + pub offset: usize, + pub bias: Bias, } impl Anchor { pub fn min() -> Self { - Self::Min - } - - pub fn max() -> Self { - Self::Max - } - - pub fn cmp<'a>(&self, other: &Anchor, buffer: &Snapshot) -> Result { - 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)) - }; - - Ok(offset_comparison.then_with(|| lhs_bias.cmp(&rhs_bias))) - } + Self { + timestamp: clock::Local::MIN, + offset: usize::MIN, + bias: Bias::Left, } } + pub fn max() -> Self { + Self { + timestamp: clock::Local::MAX, + offset: usize::MAX, + bias: Bias::Right, + } + } + + pub fn cmp<'a>(&self, other: &Anchor, buffer: &Snapshot) -> Result { + let offset_comparison = if self.timestamp == other.timestamp { + self.offset.cmp(&other.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 { - match self { - Anchor::Min => Anchor::Min, - Anchor::Insertion { bias, .. } => { - if *bias == Bias::Left { - self.clone() - } else { - buffer.anchor_before(self) - } - } - Anchor::Max => buffer.anchor_before(self), + if self.bias == Bias::Left { + self.clone() + } else { + buffer.anchor_before(self) } } 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 { - buffer.anchor_after(self) - } - } - Anchor::Max => Anchor::Max, + if self.bias == Bias::Right { + self.clone() + } else { + buffer.anchor_after(self) } } @@ -97,6 +66,7 @@ impl Anchor { pub trait AnchorRangeExt { fn cmp(&self, b: &Range, buffer: &Snapshot) -> Result; fn to_offset(&self, content: &Snapshot) -> Range; + fn to_point(&self, content: &Snapshot) -> Range; } impl AnchorRangeExt for Range { @@ -110,4 +80,8 @@ impl AnchorRangeExt for Range { fn to_offset(&self, content: &Snapshot) -> Range { self.start.to_offset(&content)..self.end.to_offset(&content) } + + fn to_point(&self, content: &Snapshot) -> Range { + self.start.summary::(&content)..self.end.summary::(&content) + } } diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 5f54c4b8b9..b896aa687e 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1136,11 +1136,9 @@ impl Buffer { } fn can_resolve(&self, anchor: &Anchor) -> bool { - match anchor { - Anchor::Min => true, - Anchor::Insertion { timestamp, .. } => self.version.observed(*timestamp), - Anchor::Max => true, - } + *anchor == Anchor::min() + || *anchor == Anchor::max() + || self.version.observed(anchor.timestamp) } pub fn peek_undo_stack(&self) -> Option<&Transaction> { @@ -1680,80 +1678,74 @@ impl Snapshot { where D: TextDimension<'a>, { - 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::(); - 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 { + if *anchor == Anchor::min() { + D::default() + } else if *anchor == Anchor::max() { + D::from_text_summary(&self.visible_text.summary()) + } else { + let anchor_key = InsertionFragmentKey { + timestamp: anchor.timestamp, + split_offset: anchor.offset, + }; + let mut insertion_cursor = self.insertions.cursor::(); + insertion_cursor.seek(&anchor_key, anchor.bias, &()); + if let Some(insertion) = insertion_cursor.item() { + let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key); + if comparison == Ordering::Greater + || (anchor.bias == Bias::Left + && comparison == Ordering::Equal + && anchor.offset > 0) + { 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) + } else { + insertion_cursor.prev(&()); } - Anchor::Max => D::from_text_summary(&self.visible_text.summary()), + let insertion = insertion_cursor.item().expect("invalid insertion"); + debug_assert_eq!(insertion.timestamp, anchor.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 += anchor.offset - insertion.split_offset; + } + self.text_summary_for_range(0..fragment_offset) } } 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::(); - 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 { + if *anchor == Anchor::min() { + Default::default() + } else if *anchor == Anchor::max() { + let text = self.fragments.summary().text; + FullOffset(text.visible + text.deleted) + } else { + let anchor_key = InsertionFragmentKey { + timestamp: anchor.timestamp, + split_offset: anchor.offset, + }; + let mut insertion_cursor = self.insertions.cursor::(); + insertion_cursor.seek(&anchor_key, anchor.bias, &()); + if let Some(insertion) = insertion_cursor.item() { + let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key); + if comparison == Ordering::Greater + || (anchor.bias == Bias::Left + && comparison == Ordering::Equal + && anchor.offset > 0) + { insertion_cursor.prev(&()); } - let insertion = insertion_cursor.item().expect("invalid insertion"); - debug_assert_eq!(insertion.timestamp, *timestamp, "invalid insertion"); + } else { + insertion_cursor.prev(&()); + } + let insertion = insertion_cursor.item().expect("invalid insertion"); + debug_assert_eq!(insertion.timestamp, anchor.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) - } + let mut fragment_cursor = self.fragments.cursor::<(Locator, FullOffset)>(); + fragment_cursor.seek(&insertion.fragment_id, Bias::Left, &None); + fragment_cursor.start().1 + (anchor.offset - insertion.split_offset) } } @@ -1777,15 +1769,15 @@ impl Snapshot { pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { let offset = position.to_offset(self); if bias == Bias::Left && offset == 0 { - Anchor::Min + Anchor::min() } else if bias == Bias::Right && offset == self.len() { - Anchor::Max + Anchor::max() } else { 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 { + Anchor { timestamp: fragment.insertion_timestamp.local(), offset: fragment.insertion_offset + overshoot, bias,