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<'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 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}