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: Option<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 const MIN: Self = Self {
 50        timestamp_replica_id: clock::Lamport::MIN.replica_id,
 51        timestamp_value: clock::Lamport::MIN.value,
 52        offset: u32::MIN,
 53        bias: Bias::Left,
 54        buffer_id: None,
 55    };
 56
 57    pub const MAX: Self = Self {
 58        timestamp_replica_id: clock::Lamport::MAX.replica_id,
 59        timestamp_value: clock::Lamport::MAX.value,
 60        offset: u32::MAX,
 61        bias: Bias::Right,
 62        buffer_id: None,
 63    };
 64
 65    pub fn new(
 66        timestamp: clock::Lamport,
 67        offset: u32,
 68        bias: Bias,
 69        buffer_id: Option<BufferId>,
 70    ) -> Self {
 71        Self {
 72            timestamp_replica_id: timestamp.replica_id,
 73            timestamp_value: timestamp.value,
 74            offset,
 75            bias,
 76            buffer_id,
 77        }
 78    }
 79
 80    pub fn min_for_buffer(buffer_id: BufferId) -> Self {
 81        Self {
 82            timestamp_replica_id: clock::Lamport::MIN.replica_id,
 83            timestamp_value: clock::Lamport::MIN.value,
 84            offset: u32::MIN,
 85            bias: Bias::Left,
 86            buffer_id: Some(buffer_id),
 87        }
 88    }
 89
 90    pub fn max_for_buffer(buffer_id: BufferId) -> Self {
 91        Self {
 92            timestamp_replica_id: clock::Lamport::MAX.replica_id,
 93            timestamp_value: clock::Lamport::MAX.value,
 94            offset: u32::MAX,
 95            bias: Bias::Right,
 96            buffer_id: Some(buffer_id),
 97        }
 98    }
 99
100    pub fn min_min_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
101        let min = Self::min_for_buffer(buffer_id);
102        min..min
103    }
104    pub fn max_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
105        let max = Self::max_for_buffer(buffer_id);
106        max..max
107    }
108    pub fn min_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
109        Self::min_for_buffer(buffer_id)..Self::max_for_buffer(buffer_id)
110    }
111
112    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
113        let fragment_id_comparison = if self.timestamp() == other.timestamp() {
114            Ordering::Equal
115        } else {
116            buffer
117                .fragment_id_for_anchor(self)
118                .cmp(buffer.fragment_id_for_anchor(other))
119        };
120
121        fragment_id_comparison
122            .then_with(|| self.offset.cmp(&other.offset))
123            .then_with(|| self.bias.cmp(&other.bias))
124    }
125
126    pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
127        if self.cmp(other, buffer).is_le() {
128            self
129        } else {
130            other
131        }
132    }
133
134    pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
135        if self.cmp(other, buffer).is_ge() {
136            self
137        } else {
138            other
139        }
140    }
141
142    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
143        match bias {
144            Bias::Left => self.bias_left(buffer),
145            Bias::Right => self.bias_right(buffer),
146        }
147    }
148
149    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
150        match self.bias {
151            Bias::Left => *self,
152            Bias::Right => buffer.anchor_before(self),
153        }
154    }
155
156    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
157        match self.bias {
158            Bias::Left => buffer.anchor_after(self),
159            Bias::Right => *self,
160        }
161    }
162
163    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
164    where
165        D: TextDimension,
166    {
167        content.summary_for_anchor(self)
168    }
169
170    /// Returns true when the [`Anchor`] is located inside a visible fragment.
171    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
172        if self.is_min() || self.is_max() {
173            true
174        } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
175            false
176        } else {
177            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
178                return false;
179            };
180            let (.., item) = buffer
181                .fragments
182                .find::<Dimensions<Option<&Locator>, usize>, _>(
183                    &None,
184                    &Some(fragment_id),
185                    Bias::Left,
186                );
187            item.is_some_and(|fragment| fragment.visible)
188        }
189    }
190
191    pub fn is_min(&self) -> bool {
192        self.timestamp() == clock::Lamport::MIN
193            && self.offset == u32::MIN
194            && self.bias == Bias::Left
195    }
196
197    pub fn is_max(&self) -> bool {
198        self.timestamp() == clock::Lamport::MAX
199            && self.offset == u32::MAX
200            && self.bias == Bias::Right
201    }
202
203    #[inline]
204    pub fn timestamp(&self) -> clock::Lamport {
205        clock::Lamport {
206            replica_id: self.timestamp_replica_id,
207            value: self.timestamp_value,
208        }
209    }
210}
211
212pub trait OffsetRangeExt {
213    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
214    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
215    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
216}
217
218impl<T> OffsetRangeExt for Range<T>
219where
220    T: ToOffset,
221{
222    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
223        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
224    }
225
226    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
227        self.start.to_offset(snapshot).to_point(snapshot)
228            ..self.end.to_offset(snapshot).to_point(snapshot)
229    }
230
231    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
232        self.start.to_offset(snapshot).to_point_utf16(snapshot)
233            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
234    }
235}
236
237pub trait AnchorRangeExt {
238    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
239    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
240}
241
242impl AnchorRangeExt for Range<Anchor> {
243    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
244        match self.start.cmp(&other.start, buffer) {
245            Ordering::Equal => other.end.cmp(&self.end, buffer),
246            ord => ord,
247        }
248    }
249
250    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
251        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
252    }
253}