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;
  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.fragments.cursor::<(Option<&Locator>, usize)>(&None);
106            fragment_cursor.seek(&Some(fragment_id), Bias::Left);
107            fragment_cursor
108                .item()
109                .map_or(false, |fragment| fragment.visible)
110        }
111    }
112}
113
114pub trait OffsetRangeExt {
115    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
116    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
117    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
118}
119
120impl<T> OffsetRangeExt for Range<T>
121where
122    T: ToOffset,
123{
124    fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
125        self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
126    }
127
128    fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
129        self.start.to_offset(snapshot).to_point(snapshot)
130            ..self.end.to_offset(snapshot).to_point(snapshot)
131    }
132
133    fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
134        self.start.to_offset(snapshot).to_point_utf16(snapshot)
135            ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
136    }
137}
138
139pub trait AnchorRangeExt {
140    fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
141    fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
142}
143
144impl AnchorRangeExt for Range<Anchor> {
145    fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
146        match self.start.cmp(&other.start, buffer) {
147            Ordering::Equal => other.end.cmp(&self.end, buffer),
148            ord => ord,
149        }
150    }
151
152    fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
153        self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
154    }
155}