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