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