anchor.rs

  1use crate::{
  2    locator::Locator, BufferId, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset,
  3    ToPoint, ToPointUtf16,
  4};
  5use anyhow::Result;
  6use std::{cmp::Ordering, fmt::Debug, ops::Range};
  7use sum_tree::Bias;
  8
  9/// A timestamped position in a buffer
 10#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
 11pub struct Anchor {
 12    pub timestamp: clock::Lamport,
 13    /// The byte offset in the buffer
 14    pub offset: usize,
 15    /// Describes which character the anchor is biased towards
 16    pub bias: Bias,
 17    pub buffer_id: Option<BufferId>,
 18}
 19
 20impl Anchor {
 21    pub const MIN: Self = Self {
 22        timestamp: clock::Lamport::MIN,
 23        offset: usize::MIN,
 24        bias: Bias::Left,
 25        buffer_id: None,
 26    };
 27
 28    pub const MAX: Self = Self {
 29        timestamp: clock::Lamport::MAX,
 30        offset: usize::MAX,
 31        bias: Bias::Right,
 32        buffer_id: None,
 33    };
 34
 35    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
 36        let fragment_id_comparison = if self.timestamp == other.timestamp {
 37            Ordering::Equal
 38        } else {
 39            buffer
 40                .fragment_id_for_anchor(self)
 41                .cmp(buffer.fragment_id_for_anchor(other))
 42        };
 43
 44        fragment_id_comparison
 45            .then_with(|| self.offset.cmp(&other.offset))
 46            .then_with(|| self.bias.cmp(&other.bias))
 47    }
 48
 49    pub fn min(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
 50        if self.cmp(other, buffer).is_le() {
 51            *self
 52        } else {
 53            *other
 54        }
 55    }
 56
 57    pub fn max(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
 58        if self.cmp(other, buffer).is_ge() {
 59            *self
 60        } else {
 61            *other
 62        }
 63    }
 64
 65    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
 66        if bias == Bias::Left {
 67            self.bias_left(buffer)
 68        } else {
 69            self.bias_right(buffer)
 70        }
 71    }
 72
 73    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
 74        if self.bias == Bias::Left {
 75            *self
 76        } else {
 77            buffer.anchor_before(self)
 78        }
 79    }
 80
 81    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
 82        if self.bias == Bias::Right {
 83            *self
 84        } else {
 85            buffer.anchor_after(self)
 86        }
 87    }
 88
 89    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
 90    where
 91        D: TextDimension,
 92    {
 93        content.summary_for_anchor(self)
 94    }
 95
 96    /// Returns true when the [`Anchor`] is located inside a visible fragment.
 97    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
 98        if *self == Anchor::MIN || *self == Anchor::MAX {
 99            true
100        } else if self.buffer_id != Some(buffer.remote_id) {
101            false
102        } else {
103            let fragment_id = buffer.fragment_id_for_anchor(self);
104            let mut fragment_cursor = buffer.fragments.cursor::<(Option<&Locator>, usize)>();
105            fragment_cursor.seek(&Some(fragment_id), Bias::Left, &None);
106            fragment_cursor
107                .item()
108                .map_or(false, |fragment| fragment.visible)
109        }
110    }
111}
112
113pub trait OffsetRangeExt {
114    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
115    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
116    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
117}
118
119impl<T> OffsetRangeExt for Range<T>
120where
121    T: ToOffset,
122{
123    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
124        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
125    }
126
127    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
128        self.start.to_offset(snapshot).to_point(snapshot)
129            ..self.end.to_offset(snapshot).to_point(snapshot)
130    }
131
132    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
133        self.start.to_offset(snapshot).to_point_utf16(snapshot)
134            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
135    }
136}
137
138pub trait AnchorRangeExt {
139    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering>;
140}
141
142impl AnchorRangeExt for Range<Anchor> {
143    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering> {
144        Ok(match self.start.cmp(&other.start, buffer) {
145            Ordering::Equal => other.end.cmp(&self.end, buffer),
146            ord => ord,
147        })
148    }
149}