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    pub timestamp: clock::Lamport,
 12    /// The byte offset in the buffer
 13    pub offset: usize,
 14    /// Describes which character the anchor is biased towards
 15    pub bias: Bias,
 16    pub buffer_id: Option<BufferId>,
 17}
 18
 19impl Debug for Anchor {
 20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 21        if *self == Self::MIN {
 22            return f.write_str("Anchor::MIN");
 23        }
 24        if *self == Self::MAX {
 25            return f.write_str("Anchor::MAX");
 26        }
 27
 28        f.debug_struct("Anchor")
 29            .field("timestamp", &self.timestamp)
 30            .field("offset", &self.offset)
 31            .field("bias", &self.bias)
 32            .field("buffer_id", &self.buffer_id)
 33            .finish()
 34    }
 35}
 36
 37impl Anchor {
 38    pub const MIN: Self = Self {
 39        timestamp: clock::Lamport::MIN,
 40        offset: usize::MIN,
 41        bias: Bias::Left,
 42        buffer_id: None,
 43    };
 44
 45    pub const MAX: Self = Self {
 46        timestamp: clock::Lamport::MAX,
 47        offset: usize::MAX,
 48        bias: Bias::Right,
 49        buffer_id: None,
 50    };
 51
 52    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
 53        let fragment_id_comparison = if self.timestamp == other.timestamp {
 54            Ordering::Equal
 55        } else {
 56            buffer
 57                .fragment_id_for_anchor(self)
 58                .cmp(buffer.fragment_id_for_anchor(other))
 59        };
 60
 61        fragment_id_comparison
 62            .then_with(|| self.offset.cmp(&other.offset))
 63            .then_with(|| self.bias.cmp(&other.bias))
 64    }
 65
 66    pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
 67        if self.cmp(other, buffer).is_le() {
 68            self
 69        } else {
 70            other
 71        }
 72    }
 73
 74    pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
 75        if self.cmp(other, buffer).is_ge() {
 76            self
 77        } else {
 78            other
 79        }
 80    }
 81
 82    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
 83        match bias {
 84            Bias::Left => self.bias_left(buffer),
 85            Bias::Right => self.bias_right(buffer),
 86        }
 87    }
 88
 89    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
 90        match self.bias {
 91            Bias::Left => *self,
 92            Bias::Right => buffer.anchor_before(self),
 93        }
 94    }
 95
 96    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
 97        match self.bias {
 98            Bias::Left => buffer.anchor_after(self),
 99            Bias::Right => *self,
100        }
101    }
102
103    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
104    where
105        D: TextDimension,
106    {
107        content.summary_for_anchor(self)
108    }
109
110    /// Returns true when the [`Anchor`] is located inside a visible fragment.
111    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
112        if *self == Anchor::MIN || *self == Anchor::MAX {
113            true
114        } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
115            false
116        } else {
117            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
118                return false;
119            };
120            let (.., item) = buffer
121                .fragments
122                .find::<Dimensions<Option<&Locator>, usize>, _>(
123                    &None,
124                    &Some(fragment_id),
125                    Bias::Left,
126                );
127            item.is_some_and(|fragment| fragment.visible)
128        }
129    }
130}
131
132pub trait OffsetRangeExt {
133    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
134    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
135    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
136}
137
138impl<T> OffsetRangeExt for Range<T>
139where
140    T: ToOffset,
141{
142    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
143        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
144    }
145
146    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
147        self.start.to_offset(snapshot).to_point(snapshot)
148            ..self.end.to_offset(snapshot).to_point(snapshot)
149    }
150
151    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
152        self.start.to_offset(snapshot).to_point_utf16(snapshot)
153            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
154    }
155}
156
157pub trait AnchorRangeExt {
158    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
159    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
160}
161
162impl AnchorRangeExt for Range<Anchor> {
163    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
164        match self.start.cmp(&other.start, buffer) {
165            Ordering::Equal => other.end.cmp(&self.end, buffer),
166            ord => ord,
167        }
168    }
169
170    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
171        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
172    }
173}