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 timestamp: clock::Lamport,
 15    /// The byte offset into the text inserted in the operation
 16    /// at `timestamp`.
 17    pub offset: usize,
 18    /// Whether this anchor stays attached to the character *before* or *after*
 19    /// the offset.
 20    pub bias: Bias,
 21    pub buffer_id: Option<BufferId>,
 22}
 23
 24impl Debug for Anchor {
 25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 26        if self.is_min() {
 27            return write!(f, "Anchor::min({:?})", self.buffer_id);
 28        }
 29        if self.is_max() {
 30            return write!(f, "Anchor::max({:?})", self.buffer_id);
 31        }
 32
 33        f.debug_struct("Anchor")
 34            .field("timestamp", &self.timestamp)
 35            .field("offset", &self.offset)
 36            .field("bias", &self.bias)
 37            .field("buffer_id", &self.buffer_id)
 38            .finish()
 39    }
 40}
 41
 42impl Anchor {
 43    pub const MIN: Self = Self {
 44        timestamp: clock::Lamport::MIN,
 45        offset: usize::MIN,
 46        bias: Bias::Left,
 47        buffer_id: None,
 48    };
 49
 50    pub const MAX: Self = Self {
 51        timestamp: clock::Lamport::MAX,
 52        offset: usize::MAX,
 53        bias: Bias::Right,
 54        buffer_id: None,
 55    };
 56
 57    pub fn min_for_buffer(buffer_id: BufferId) -> Self {
 58        Self {
 59            timestamp: clock::Lamport::MIN,
 60            offset: usize::MIN,
 61            bias: Bias::Left,
 62            buffer_id: Some(buffer_id),
 63        }
 64    }
 65
 66    pub fn max_for_buffer(buffer_id: BufferId) -> Self {
 67        Self {
 68            timestamp: clock::Lamport::MAX,
 69            offset: usize::MAX,
 70            bias: Bias::Right,
 71            buffer_id: Some(buffer_id),
 72        }
 73    }
 74
 75    pub fn min_min_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 76        let min = Self::min_for_buffer(buffer_id);
 77        min..min
 78    }
 79    pub fn max_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 80        let max = Self::max_for_buffer(buffer_id);
 81        max..max
 82    }
 83    pub fn min_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
 84        Self::min_for_buffer(buffer_id)..Self::max_for_buffer(buffer_id)
 85    }
 86
 87    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
 88        let fragment_id_comparison = if self.timestamp == other.timestamp {
 89            Ordering::Equal
 90        } else {
 91            buffer
 92                .fragment_id_for_anchor(self)
 93                .cmp(buffer.fragment_id_for_anchor(other))
 94        };
 95
 96        fragment_id_comparison
 97            .then_with(|| self.offset.cmp(&other.offset))
 98            .then_with(|| self.bias.cmp(&other.bias))
 99    }
100
101    pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
102        if self.cmp(other, buffer).is_le() {
103            self
104        } else {
105            other
106        }
107    }
108
109    pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
110        if self.cmp(other, buffer).is_ge() {
111            self
112        } else {
113            other
114        }
115    }
116
117    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
118        match bias {
119            Bias::Left => self.bias_left(buffer),
120            Bias::Right => self.bias_right(buffer),
121        }
122    }
123
124    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
125        match self.bias {
126            Bias::Left => *self,
127            Bias::Right => buffer.anchor_before(self),
128        }
129    }
130
131    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
132        match self.bias {
133            Bias::Left => buffer.anchor_after(self),
134            Bias::Right => *self,
135        }
136    }
137
138    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
139    where
140        D: TextDimension,
141    {
142        content.summary_for_anchor(self)
143    }
144
145    /// Returns true when the [`Anchor`] is located inside a visible fragment.
146    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
147        if self.is_min() || self.is_max() {
148            true
149        } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
150            false
151        } else {
152            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
153                return false;
154            };
155            let (.., item) = buffer
156                .fragments
157                .find::<Dimensions<Option<&Locator>, usize>, _>(
158                    &None,
159                    &Some(fragment_id),
160                    Bias::Left,
161                );
162            item.is_some_and(|fragment| fragment.visible)
163        }
164    }
165
166    pub fn is_min(&self) -> bool {
167        self.timestamp == clock::Lamport::MIN
168            && self.offset == usize::MIN
169            && self.bias == Bias::Left
170    }
171
172    pub fn is_max(&self) -> bool {
173        self.timestamp == clock::Lamport::MAX
174            && self.offset == usize::MAX
175            && self.bias == Bias::Right
176    }
177}
178
179pub trait OffsetRangeExt {
180    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
181    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
182    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
183}
184
185impl<T> OffsetRangeExt for Range<T>
186where
187    T: ToOffset,
188{
189    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
190        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
191    }
192
193    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
194        self.start.to_offset(snapshot).to_point(snapshot)
195            ..self.end.to_offset(snapshot).to_point(snapshot)
196    }
197
198    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
199        self.start.to_offset(snapshot).to_point_utf16(snapshot)
200            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
201    }
202}
203
204pub trait AnchorRangeExt {
205    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
206    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
207}
208
209impl AnchorRangeExt for Range<Anchor> {
210    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
211        match self.start.cmp(&other.start, buffer) {
212            Ordering::Equal => other.end.cmp(&self.end, buffer),
213            ord => ord,
214        }
215    }
216
217    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
218        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
219    }
220}