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, Debug, Hash, Default)]
 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 Anchor {
 20    pub const MIN: Self = Self {
 21        timestamp: clock::Lamport::MIN,
 22        offset: usize::MIN,
 23        bias: Bias::Left,
 24        buffer_id: None,
 25    };
 26
 27    pub const MAX: Self = Self {
 28        timestamp: clock::Lamport::MAX,
 29        offset: usize::MAX,
 30        bias: Bias::Right,
 31        buffer_id: None,
 32    };
 33
 34    pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
 35        let fragment_id_comparison = if self.timestamp == other.timestamp {
 36            Ordering::Equal
 37        } else {
 38            buffer
 39                .fragment_id_for_anchor(self)
 40                .cmp(buffer.fragment_id_for_anchor(other))
 41        };
 42
 43        fragment_id_comparison
 44            .then_with(|| self.offset.cmp(&other.offset))
 45            .then_with(|| self.bias.cmp(&other.bias))
 46    }
 47
 48    pub fn min(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
 49        if self.cmp(other, buffer).is_le() {
 50            *self
 51        } else {
 52            *other
 53        }
 54    }
 55
 56    pub fn max(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
 57        if self.cmp(other, buffer).is_ge() {
 58            *self
 59        } else {
 60            *other
 61        }
 62    }
 63
 64    pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
 65        match bias {
 66            Bias::Left => self.bias_left(buffer),
 67            Bias::Right => self.bias_right(buffer),
 68        }
 69    }
 70
 71    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
 72        match self.bias {
 73            Bias::Left => *self,
 74            Bias::Right => buffer.anchor_before(self),
 75        }
 76    }
 77
 78    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
 79        match self.bias {
 80            Bias::Left => buffer.anchor_after(self),
 81            Bias::Right => *self,
 82        }
 83    }
 84
 85    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
 86    where
 87        D: TextDimension,
 88    {
 89        content.summary_for_anchor(self)
 90    }
 91
 92    /// Returns true when the [`Anchor`] is located inside a visible fragment.
 93    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
 94        if *self == Anchor::MIN || *self == Anchor::MAX {
 95            true
 96        } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
 97            false
 98        } else {
 99            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
100                return false;
101            };
102            let mut fragment_cursor = buffer
103                .fragments
104                .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
105            fragment_cursor.seek(&Some(fragment_id), Bias::Left);
106            fragment_cursor
107                .item()
108                .is_some_and(|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) -> Ordering;
140    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
141}
142
143impl AnchorRangeExt for Range<Anchor> {
144    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
145        match self.start.cmp(&other.start, buffer) {
146            Ordering::Equal => other.end.cmp(&self.end, buffer),
147            ord => ord,
148        }
149    }
150
151    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
152        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
153    }
154}