anchor.rs

  1use crate::{
  2    BufferId, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint, ToPointUtf16,
  3    locator::Locator,
  4};
  5use std::{cmp::Ordering, fmt::Debug, ops::Range};
  6use sum_tree::{Bias, Dimensions};
  7
  8/// A timestamped position in a buffer.
  9#[doc(alias = "TextAnchor")]
 10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
 11pub struct Anchor {
 12    // /// The timestamp of the operation that inserted the text
 13    // /// in which this anchor is located.
 14    // pub(crate) timestamp: clock::Lamport,
 15    // we store the replica id and sequence number of the timestamp inline
 16    // to avoid the alignment of our fields from increasing the size of this struct
 17    // This saves 8 bytes, by allowing replica id, value and bias to occupy the padding
 18    pub(crate) timestamp_replica_id: clock::ReplicaId,
 19    pub(crate) timestamp_value: clock::Seq,
 20
 21    /// The byte offset into the text inserted in the operation
 22    /// at `timestamp`.
 23    pub offset: u32,
 24    /// Whether this anchor stays attached to the character *before* or *after*
 25    /// the offset.
 26    pub bias: Bias,
 27    pub buffer_id: BufferId,
 28}
 29
 30impl Debug for Anchor {
 31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 32        if self.is_min() {
 33            return write!(f, "Anchor::min({:?})", self.buffer_id);
 34        }
 35        if self.is_max() {
 36            return write!(f, "Anchor::max({:?})", self.buffer_id);
 37        }
 38
 39        f.debug_struct("Anchor")
 40            .field("timestamp", &self.timestamp())
 41            .field("offset", &self.offset)
 42            .field("bias", &self.bias)
 43            .field("buffer_id", &self.buffer_id)
 44            .finish()
 45    }
 46}
 47
 48impl Anchor {
 49    pub fn new(timestamp: clock::Lamport, offset: u32, bias: Bias, buffer_id: BufferId) -> Self {
 50        Self {
 51            timestamp_replica_id: timestamp.replica_id,
 52            timestamp_value: timestamp.value,
 53            offset,
 54            bias,
 55            buffer_id,
 56        }
 57    }
 58
 59    pub fn min_for_buffer(buffer_id: BufferId) -> Self {
 60        Self {
 61            timestamp_replica_id: clock::Lamport::MIN.replica_id,
 62            timestamp_value: clock::Lamport::MIN.value,
 63            offset: u32::MIN,
 64            bias: Bias::Left,
 65            buffer_id,
 66        }
 67    }
 68
 69    pub fn max_for_buffer(buffer_id: BufferId) -> Self {
 70        Self {
 71            timestamp_replica_id: clock::Lamport::MAX.replica_id,
 72            timestamp_value: clock::Lamport::MAX.value,
 73            offset: u32::MAX,
 74            bias: Bias::Right,
 75            buffer_id,
 76        }
 77    }
 78
 79    pub fn min_min_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 80        let min = Self::min_for_buffer(buffer_id);
 81        min..min
 82    }
 83    pub fn max_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 84        let max = Self::max_for_buffer(buffer_id);
 85        max..max
 86    }
 87    pub fn min_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 88        Self::min_for_buffer(buffer_id)..Self::max_for_buffer(buffer_id)
 89    }
 90
 91    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
 92        let fragment_id_comparison = if self.timestamp() == other.timestamp() {
 93            Ordering::Equal
 94        } else {
 95            buffer
 96                .fragment_id_for_anchor(self)
 97                .cmp(buffer.fragment_id_for_anchor(other))
 98        };
 99
100        fragment_id_comparison
101            .then_with(|| self.offset.cmp(&other.offset))
102            .then_with(|| self.bias.cmp(&other.bias))
103    }
104
105    pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
106        if self.cmp(other, buffer).is_le() {
107            self
108        } else {
109            other
110        }
111    }
112
113    pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
114        if self.cmp(other, buffer).is_ge() {
115            self
116        } else {
117            other
118        }
119    }
120
121    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
122        match bias {
123            Bias::Left => self.bias_left(buffer),
124            Bias::Right => self.bias_right(buffer),
125        }
126    }
127
128    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
129        match self.bias {
130            Bias::Left => *self,
131            Bias::Right => buffer.anchor_before(self),
132        }
133    }
134
135    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
136        match self.bias {
137            Bias::Left => buffer.anchor_after(self),
138            Bias::Right => *self,
139        }
140    }
141
142    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
143    where
144        D: TextDimension,
145    {
146        content.summary_for_anchor(self)
147    }
148
149    /// Returns true when the [`Anchor`] is located inside a visible fragment.
150    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
151        if self.is_min() || self.is_max() {
152            true
153        } else if self.buffer_id != buffer.remote_id {
154            false
155        } else {
156            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
157                return false;
158            };
159            let (.., item) = buffer
160                .fragments
161                .find::<Dimensions<Option<&Locator>, usize>, _>(
162                    &None,
163                    &Some(fragment_id),
164                    Bias::Left,
165                );
166            item.is_some_and(|fragment| fragment.visible)
167        }
168    }
169
170    pub fn is_min(&self) -> bool {
171        self.timestamp() == clock::Lamport::MIN
172            && self.offset == u32::MIN
173            && self.bias == Bias::Left
174    }
175
176    pub fn is_max(&self) -> bool {
177        self.timestamp() == clock::Lamport::MAX
178            && self.offset == u32::MAX
179            && self.bias == Bias::Right
180    }
181
182    #[inline]
183    pub fn timestamp(&self) -> clock::Lamport {
184        clock::Lamport {
185            replica_id: self.timestamp_replica_id,
186            value: self.timestamp_value,
187        }
188    }
189
190    pub fn opaque_id(&self) -> [u8; 20] {
191        let mut bytes = [0u8; 20];
192        let buffer_id: u64 = self.buffer_id.into();
193        bytes[0..8].copy_from_slice(&buffer_id.to_le_bytes());
194        bytes[8..12].copy_from_slice(&self.offset.to_le_bytes());
195        bytes[12..16].copy_from_slice(&self.timestamp_value.to_le_bytes());
196        let replica_id = self.timestamp_replica_id.as_u16();
197        bytes[16..18].copy_from_slice(&replica_id.to_le_bytes());
198        bytes[18] = self.bias as u8;
199        bytes
200    }
201}
202
203pub trait OffsetRangeExt {
204    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
205    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
206    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
207}
208
209impl<T> OffsetRangeExt for Range<T>
210where
211    T: ToOffset,
212{
213    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
214        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
215    }
216
217    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
218        self.start.to_offset(snapshot).to_point(snapshot)
219            ..self.end.to_offset(snapshot).to_point(snapshot)
220    }
221
222    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
223        self.start.to_offset(snapshot).to_point_utf16(snapshot)
224            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
225    }
226}
227
228pub trait AnchorRangeExt {
229    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
230    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
231    fn contains_anchor(&self, b: Anchor, buffer: &BufferSnapshot) -> bool;
232}
233
234impl AnchorRangeExt for Range<Anchor> {
235    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
236        match self.start.cmp(&other.start, buffer) {
237            Ordering::Equal => other.end.cmp(&self.end, buffer),
238            ord => ord,
239        }
240    }
241
242    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
243        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
244    }
245
246    fn contains_anchor(&self, other: Anchor, buffer: &BufferSnapshot) -> bool {
247        self.start.cmp(&other, buffer).is_le() && self.end.cmp(&other, buffer).is_ge()
248    }
249}