1use crate::{
2 ExcerptSummary, MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16, PathKeyIndex,
3};
4
5use super::{MultiBufferSnapshot, ToOffset, ToPoint};
6use language::Point;
7use std::{
8 cmp::Ordering,
9 ops::{Add, AddAssign, Range, Sub},
10};
11use sum_tree::Bias;
12use text::BufferId;
13use util::debug_panic;
14
15/// A stable reference to a position within a [`MultiBuffer`](super::MultiBuffer).
16///
17/// Unlike simple offsets, anchors remain valid as the text is edited, automatically
18/// adjusting to reflect insertions and deletions around them.
19#[derive(Clone, Copy, Eq, PartialEq, Hash)]
20pub enum Anchor {
21 Min,
22 Max,
23 Text {
24 /// The position within the excerpt's underlying buffer. This is a stable
25 /// reference that remains valid as the buffer text is edited.
26 timestamp: clock::Lamport,
27
28 /// The byte offset into the text inserted in the operation
29 /// at `timestamp`.
30 offset: u32,
31 /// Whether this anchor stays attached to the character *before* or *after*
32 /// the offset.
33 bias: Bias,
34 buffer_id: BufferId,
35 /// Refers to the path key that the buffer had when this anchor was created,
36 /// so that ordering is stable when the path key for a buffer changes
37 path: PathKeyIndex,
38 /// When present, indicates this anchor points into deleted text within an
39 /// expanded diff hunk. The anchor references a position in the diff base
40 /// (original) text rather than the current buffer text. This is used when
41 /// displaying inline diffs where deleted lines are shown.
42 diff_base_anchor: Option<text::Anchor>,
43 },
44}
45
46impl std::fmt::Debug for Anchor {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 if self.is_min() {
49 return write!(f, "Anchor::Min");
50 }
51 if self.is_max() {
52 return write!(f, "Anchor::Max");
53 }
54
55 f.debug_struct("Anchor")
56 .field("text_anchor", &self.text_anchor().unwrap())
57 .field("diff_base_anchor", &self.diff_base_anchor())
58 .finish()
59 }
60}
61
62impl Anchor {
63 pub fn text_anchor(&self) -> Option<(PathKeyIndex, text::Anchor, Option<text::Anchor>)> {
64 match self {
65 Self::Min | Self::Max => None,
66 Self::Text {
67 timestamp,
68 offset,
69 bias,
70 buffer_id,
71 path,
72 diff_base_anchor,
73 ..
74 } => Some((
75 *path,
76 text::Anchor::new(*timestamp, *offset, *bias, Some(*buffer_id)),
77 *diff_base_anchor,
78 )),
79 }
80 }
81
82 pub fn diff_base_anchor(&self) -> Option<text::Anchor> {
83 match self {
84 Self::Min | Self::Max => None,
85 Self::Text {
86 diff_base_anchor, ..
87 } => *diff_base_anchor,
88 }
89 }
90
91 pub fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {
92 match &mut self {
93 Self::Min | Self::Max => {
94 debug_panic!("with_diff_base_anchor called on min or max anchor");
95 }
96 Self::Text {
97 diff_base_anchor: self_diff_base_anchor,
98 ..
99 } => {
100 *self_diff_base_anchor = Some(diff_base_anchor);
101 }
102 };
103 self
104 }
105
106 pub fn text(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
107 let Some(buffer_id) = text_anchor.buffer_id else {
108 panic!("text_anchor must have a buffer_id");
109 };
110 Self::Text {
111 path,
112 diff_base_anchor: None,
113 timestamp: text_anchor.timestamp(),
114 buffer_id,
115 offset: text_anchor.offset,
116 bias: text_anchor.bias,
117 }
118 }
119
120 pub fn range_in_buffer(path: PathKeyIndex, range: Range<text::Anchor>) -> Range<Self> {
121 Self::text(path, range.start)..Self::text(path, range.end)
122 }
123
124 pub fn bias(&self) -> Bias {
125 match self {
126 Self::Min => Bias::Left,
127 Self::Max => Bias::Right,
128 Self::Text { bias, .. } => *bias,
129 }
130 }
131
132 pub fn min() -> Self {
133 Self::Min
134 }
135
136 pub fn max() -> Self {
137 Self::Max
138 }
139
140 pub fn is_min(&self) -> bool {
141 matches!(self, Self::Min)
142 }
143
144 pub fn is_max(&self) -> bool {
145 matches!(self, Self::Max)
146 }
147
148 pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
149 let (self_text_anchor, self_path, other_text_anchor, other_path) = match (self, other) {
150 (Anchor::Min, Anchor::Min) => return Ordering::Equal,
151 (Anchor::Max, Anchor::Max) => return Ordering::Equal,
152 (Anchor::Min, _) => return Ordering::Less,
153 (Anchor::Max, _) => return Ordering::Greater,
154 (_, Anchor::Max) => return Ordering::Less,
155 (_, Anchor::Min) => return Ordering::Greater,
156 (
157 Anchor::Text {
158 path: self_path, ..
159 },
160 Anchor::Text {
161 path: other_path, ..
162 },
163 ) => (
164 self.text_anchor().unwrap(),
165 self_path,
166 other.text_anchor().unwrap(),
167 other_path,
168 ),
169 };
170 let self_buffer_id = self_text_anchor.buffer_id.unwrap();
171 let other_buffer_id = other_text_anchor.buffer_id.unwrap();
172
173 let Some(self_path_key) = snapshot.path_keys_by_index.get(&self_path) else {
174 panic!("anchor's path was never added to multibuffer")
175 };
176 let Some(other_path_key) = snapshot.path_keys_by_index.get(&other_path) else {
177 panic!("anchor's path was never added to multibuffer")
178 };
179
180 if self_path_key.cmp(other_path_key) != Ordering::Equal {
181 return self_path_key.cmp(other_path_key);
182 }
183
184 // in the case that you removed the buffer contianing self,
185 // and added the buffer containing other with the same path key
186 // (ordering is arbitrary but consistent)
187 if self_buffer_id != other_buffer_id {
188 return self_buffer_id.cmp(&other_buffer_id);
189 }
190
191 let Some(buffer) = snapshot.buffer_for_path(&self_path_key) else {
192 return Ordering::Equal;
193 };
194 let text_cmp = self_text_anchor.cmp(&other_text_anchor, buffer);
195 if text_cmp != Ordering::Equal {
196 return text_cmp;
197 }
198
199 if (self.diff_base_anchor().is_some() || other.diff_base_anchor().is_some())
200 && let Some(base_text) = snapshot
201 .diffs
202 .get(&self_buffer_id)
203 .map(|diff| diff.base_text())
204 {
205 let self_anchor = self.diff_base_anchor().filter(|a| a.is_valid(base_text));
206 let other_anchor = other.diff_base_anchor().filter(|a| a.is_valid(base_text));
207 return match (self_anchor, other_anchor) {
208 (Some(a), Some(b)) => a.cmp(&b, base_text),
209 (Some(_), None) => match other_text_anchor.bias {
210 Bias::Left => Ordering::Greater,
211 Bias::Right => Ordering::Less,
212 },
213 (None, Some(_)) => match self_text_anchor.bias {
214 Bias::Left => Ordering::Less,
215 Bias::Right => Ordering::Greater,
216 },
217 (None, None) => Ordering::Equal,
218 };
219 }
220
221 Ordering::Equal
222 }
223
224 pub fn bias(&self) -> Bias {
225 match self {
226 Anchor::Min => Bias::Left,
227 Anchor::Max => Bias::Right,
228 Anchor::Text { bias, .. } => *bias,
229 }
230 }
231
232 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
233 match self {
234 Anchor::Min => *self,
235 Anchor::Max => snapshot.anchor_before(snapshot.max_point()),
236 Anchor::Text {
237 path,
238 bias,
239 buffer_id,
240 ..
241 } => {
242 if *bias == Bias::Left {
243 return *self;
244 }
245 let Some(buffer) = snapshot.buffer_for_id(*buffer_id) else {
246 return *self;
247 };
248 let text_anchor = self.text_anchor().unwrap().bias_left(&buffer);
249 let ret = Self::text(*path, text_anchor);
250 if let Some(diff_base_anchor) = self.diff_base_anchor() {
251 if let Some(diff) = snapshot.diffs.get(&buffer_id)
252 && diff_base_anchor.is_valid(&diff.base_text())
253 {
254 ret.with_diff_base_anchor(diff_base_anchor.bias_left(diff.base_text()))
255 } else {
256 ret.with_diff_base_anchor(diff_base_anchor)
257 }
258 } else {
259 ret
260 }
261 }
262 }
263 }
264
265 pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
266 match self {
267 Anchor::Min => *self,
268 Anchor::Max => snapshot.anchor_after(Point::zero()),
269 Anchor::Text {
270 path,
271 bias,
272 buffer_id,
273 ..
274 } => {
275 if *bias == Bias::Right {
276 return *self;
277 }
278 let Some(buffer) = snapshot.buffer_for_id(*buffer_id) else {
279 return *self;
280 };
281 let text_anchor = self.text_anchor().unwrap().bias_right(&buffer);
282 let ret = Self::text(*path, text_anchor);
283 if let Some(diff_base_anchor) = self.diff_base_anchor() {
284 if let Some(diff) = snapshot.diffs.get(&buffer_id)
285 && diff_base_anchor.is_valid(&diff.base_text())
286 {
287 ret.with_diff_base_anchor(diff_base_anchor.bias_right(diff.base_text()))
288 } else {
289 ret.with_diff_base_anchor(diff_base_anchor)
290 }
291 } else {
292 ret
293 }
294 }
295 }
296 }
297
298 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
299 where
300 D: MultiBufferDimension
301 + Ord
302 + Sub<Output = D::TextDimension>
303 + Sub<D::TextDimension, Output = D>
304 + AddAssign<D::TextDimension>
305 + Add<D::TextDimension, Output = D>,
306 D::TextDimension: Sub<Output = D::TextDimension> + Ord,
307 {
308 snapshot.summary_for_anchor(self)
309 }
310
311 pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
312 let Some(text_anchor) = self.text_anchor() else {
313 return true;
314 };
315 let Some(buffer_id) = text_anchor.buffer_id else {
316 debug_panic!("missing buffer_id for anchor");
317 return false;
318 };
319
320 let Some(target) = snapshot.anchor_seek_target(*self) else {
321 return false;
322 };
323 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
324 cursor.seek(&target, Bias::Left);
325 let Some(excerpt) = cursor.item() else {
326 return false;
327 };
328 excerpt.buffer.remote_id() == buffer_id
329 && excerpt
330 .range
331 .context
332 .start
333 .cmp(&text_anchor, &excerpt.buffer)
334 .is_le()
335 && excerpt
336 .range
337 .context
338 .end
339 .cmp(&text_anchor, &excerpt.buffer)
340 .is_ge()
341 }
342}
343
344impl ToOffset for Anchor {
345 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
346 self.summary(snapshot)
347 }
348 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
349 self.summary(snapshot)
350 }
351}
352
353impl ToPoint for Anchor {
354 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
355 self.summary(snapshot)
356 }
357 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
358 self.summary(snapshot)
359 }
360}
361
362pub trait AnchorRangeExt {
363 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
364 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
365 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
366 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset>;
367 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
368}
369
370impl AnchorRangeExt for Range<Anchor> {
371 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
372 match self.start.cmp(&other.start, buffer) {
373 Ordering::Equal => other.end.cmp(&self.end, buffer),
374 ord => ord,
375 }
376 }
377
378 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
379 self.start.cmp(&other.start, buffer).is_le() && other.end.cmp(&self.end, buffer).is_le()
380 }
381
382 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
383 self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
384 }
385
386 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset> {
387 self.start.to_offset(content)..self.end.to_offset(content)
388 }
389
390 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
391 self.start.to_point(content)..self.end.to_point(content)
392 }
393}