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        if bias == Bias::Left {
 66            self.bias_left(buffer)
 67        } else {
 68            self.bias_right(buffer)
 69        }
 70    }
 71
 72    pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
 73        if self.bias == Bias::Left {
 74            *self
 75        } else {
 76            buffer.anchor_before(self)
 77        }
 78    }
 79
 80    pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
 81        if self.bias == Bias::Right {
 82            *self
 83        } else {
 84            buffer.anchor_after(self)
 85        }
 86    }
 87
 88    pub fn summary<D>(&self, content: &BufferSnapshot) -> D
 89    where
 90        D: TextDimension,
 91    {
 92        content.summary_for_anchor(self)
 93    }
 94
 95    /// Returns true when the [`Anchor`] is located inside a visible fragment.
 96    pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
 97        if *self == Anchor::MIN || *self == Anchor::MAX {
 98            true
 99        } else if self.buffer_id != Some(buffer.remote_id) {
100            false
101        } else {
102            let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
103                return false;
104            };
105            let mut fragment_cursor = buffer
106                .fragments
107                .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
108            fragment_cursor.seek(&Some(fragment_id), Bias::Left);
109            fragment_cursor
110                .item()
111                .is_some_and(|fragment| fragment.visible)
112        }
113    }
114}
115
116pub trait OffsetRangeExt {
117    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
118    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
119    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
120}
121
122impl<T> OffsetRangeExt for Range<T>
123where
124    T: ToOffset,
125{
126    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
127        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
128    }
129
130    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
131        self.start.to_offset(snapshot).to_point(snapshot)
132            ..self.end.to_offset(snapshot).to_point(snapshot)
133    }
134
135    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
136        self.start.to_offset(snapshot).to_point_utf16(snapshot)
137            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
138    }
139}
140
141pub trait AnchorRangeExt {
142    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
143    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
144}
145
146impl AnchorRangeExt for Range<Anchor> {
147    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
148        match self.start.cmp(&other.start, buffer) {
149            Ordering::Equal => other.end.cmp(&self.end, buffer),
150            ord => ord,
151        }
152    }
153
154    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
155        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
156    }
157}