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#[doc(alias = "TextAnchor")]
10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
11pub struct Anchor {
12 // /// The timestamp of the operation that inserted the text
13 // /// in which this anchor is located.
14 // pub(crate) timestamp: clock::Lamport,
15 // we store the replica id and sequence number of the timestamp inline
16 // to avoid the alignment of our fields from increasing the size of this struct
17 // This saves 8 bytes, by allowing replica id, value and bias to occupy the padding
18 pub(crate) timestamp_replica_id: clock::ReplicaId,
19 pub(crate) timestamp_value: clock::Seq,
20
21 /// The byte offset into the text inserted in the operation
22 /// at `timestamp`.
23 pub offset: u32,
24 /// Whether this anchor stays attached to the character *before* or *after*
25 /// the offset.
26 pub bias: Bias,
27 pub buffer_id: Option<BufferId>,
28}
29
30impl Debug for Anchor {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 if self.is_min() {
33 return write!(f, "Anchor::min({:?})", self.buffer_id);
34 }
35 if self.is_max() {
36 return write!(f, "Anchor::max({:?})", self.buffer_id);
37 }
38
39 f.debug_struct("Anchor")
40 .field("timestamp", &self.timestamp())
41 .field("offset", &self.offset)
42 .field("bias", &self.bias)
43 .field("buffer_id", &self.buffer_id)
44 .finish()
45 }
46}
47
48impl Anchor {
49 pub const MIN: Self = Self {
50 timestamp_replica_id: clock::Lamport::MIN.replica_id,
51 timestamp_value: clock::Lamport::MIN.value,
52 offset: u32::MIN,
53 bias: Bias::Left,
54 buffer_id: None,
55 };
56
57 pub const MAX: Self = Self {
58 timestamp_replica_id: clock::Lamport::MAX.replica_id,
59 timestamp_value: clock::Lamport::MAX.value,
60 offset: u32::MAX,
61 bias: Bias::Right,
62 buffer_id: None,
63 };
64
65 pub fn new(
66 timestamp: clock::Lamport,
67 offset: u32,
68 bias: Bias,
69 buffer_id: Option<BufferId>,
70 ) -> Self {
71 Self {
72 timestamp_replica_id: timestamp.replica_id,
73 timestamp_value: timestamp.value,
74 offset,
75 bias,
76 buffer_id,
77 }
78 }
79
80 pub fn min_for_buffer(buffer_id: BufferId) -> Self {
81 Self {
82 timestamp_replica_id: clock::Lamport::MIN.replica_id,
83 timestamp_value: clock::Lamport::MIN.value,
84 offset: u32::MIN,
85 bias: Bias::Left,
86 buffer_id: Some(buffer_id),
87 }
88 }
89
90 pub fn max_for_buffer(buffer_id: BufferId) -> Self {
91 Self {
92 timestamp_replica_id: clock::Lamport::MAX.replica_id,
93 timestamp_value: clock::Lamport::MAX.value,
94 offset: u32::MAX,
95 bias: Bias::Right,
96 buffer_id: Some(buffer_id),
97 }
98 }
99
100 pub fn min_min_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
101 let min = Self::min_for_buffer(buffer_id);
102 min..min
103 }
104 pub fn max_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
105 let max = Self::max_for_buffer(buffer_id);
106 max..max
107 }
108 pub fn min_max_range_for_buffer(buffer_id: BufferId) -> std::ops::Range<Self> {
109 Self::min_for_buffer(buffer_id)..Self::max_for_buffer(buffer_id)
110 }
111
112 pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
113 let fragment_id_comparison = if self.timestamp() == other.timestamp() {
114 Ordering::Equal
115 } else {
116 buffer
117 .fragment_id_for_anchor(self)
118 .cmp(buffer.fragment_id_for_anchor(other))
119 };
120
121 fragment_id_comparison
122 .then_with(|| self.offset.cmp(&other.offset))
123 .then_with(|| self.bias.cmp(&other.bias))
124 }
125
126 pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
127 if self.cmp(other, buffer).is_le() {
128 self
129 } else {
130 other
131 }
132 }
133
134 pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
135 if self.cmp(other, buffer).is_ge() {
136 self
137 } else {
138 other
139 }
140 }
141
142 pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
143 match bias {
144 Bias::Left => self.bias_left(buffer),
145 Bias::Right => self.bias_right(buffer),
146 }
147 }
148
149 pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
150 match self.bias {
151 Bias::Left => *self,
152 Bias::Right => buffer.anchor_before(self),
153 }
154 }
155
156 pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
157 match self.bias {
158 Bias::Left => buffer.anchor_after(self),
159 Bias::Right => *self,
160 }
161 }
162
163 pub fn summary<D>(&self, content: &BufferSnapshot) -> D
164 where
165 D: TextDimension,
166 {
167 content.summary_for_anchor(self)
168 }
169
170 /// Returns true when the [`Anchor`] is located inside a visible fragment.
171 pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
172 if self.is_min() || self.is_max() {
173 true
174 } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
175 false
176 } else {
177 let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
178 return false;
179 };
180 let (.., item) = buffer
181 .fragments
182 .find::<Dimensions<Option<&Locator>, usize>, _>(
183 &None,
184 &Some(fragment_id),
185 Bias::Left,
186 );
187 item.is_some_and(|fragment| fragment.visible)
188 }
189 }
190
191 pub fn is_min(&self) -> bool {
192 self.timestamp() == clock::Lamport::MIN
193 && self.offset == u32::MIN
194 && self.bias == Bias::Left
195 }
196
197 pub fn is_max(&self) -> bool {
198 self.timestamp() == clock::Lamport::MAX
199 && self.offset == u32::MAX
200 && self.bias == Bias::Right
201 }
202
203 #[inline]
204 pub fn timestamp(&self) -> clock::Lamport {
205 clock::Lamport {
206 replica_id: self.timestamp_replica_id,
207 value: self.timestamp_value,
208 }
209 }
210}
211
212pub trait OffsetRangeExt {
213 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
214 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
215 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
216}
217
218impl<T> OffsetRangeExt for Range<T>
219where
220 T: ToOffset,
221{
222 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
223 self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
224 }
225
226 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
227 self.start.to_offset(snapshot).to_point(snapshot)
228 ..self.end.to_offset(snapshot).to_point(snapshot)
229 }
230
231 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
232 self.start.to_offset(snapshot).to_point_utf16(snapshot)
233 ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
234 }
235}
236
237pub trait AnchorRangeExt {
238 fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
239 fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
240}
241
242impl AnchorRangeExt for Range<Anchor> {
243 fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
244 match self.start.cmp(&other.start, buffer) {
245 Ordering::Equal => other.end.cmp(&self.end, buffer),
246 ord => ord,
247 }
248 }
249
250 fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
251 self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
252 }
253}