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)]
 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<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
 49        if self.cmp(other, buffer).is_le() {
 50            self
 51        } else {
 52            other
 53        }
 54    }
 55
 56    pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a 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 (.., item) = buffer
103                .fragments
104                .find::<Dimensions<Option<&Locator>, usize>, _>(
105                    &None,
106                    &Some(fragment_id),
107                    Bias::Left,
108                );
109            item.is_some_and(|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}