1use crate::{
2 ExcerptSummary, MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey,
3 PathKeyIndex, find_diff_state,
4};
5
6use super::{MultiBufferSnapshot, ToOffset, ToPoint};
7use language::{BufferSnapshot, Point};
8use std::{
9 cmp::Ordering,
10 ops::{Add, AddAssign, Range, Sub},
11};
12use sum_tree::Bias;
13use text::BufferId;
14
15/// A multibuffer anchor derived from an anchor into a specific excerpted buffer.
16#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
17pub struct ExcerptAnchor {
18 pub(crate) text_anchor: text::Anchor,
19 pub(crate) path: PathKeyIndex,
20 pub(crate) diff_base_anchor: Option<text::Anchor>,
21}
22
23/// A stable reference to a position within a [`MultiBuffer`](super::MultiBuffer).
24///
25/// Unlike simple offsets, anchors remain valid as the text is edited, automatically
26/// adjusting to reflect insertions and deletions around them.
27#[derive(Clone, Copy, Eq, PartialEq, Hash)]
28pub enum Anchor {
29 /// An anchor that always resolves to the start of the multibuffer.
30 Min,
31 /// An anchor that's attached to a specific excerpted buffer.
32 Excerpt(ExcerptAnchor),
33 /// An anchor that always resolves to the end of the multibuffer.
34 Max,
35}
36
37pub(crate) enum AnchorSeekTarget<'a> {
38 // buffer no longer exists at its original path key in the multibuffer
39 Missing {
40 path_key: &'a PathKey,
41 },
42 // we have excerpts for the buffer at the expected path key
43 Excerpt {
44 path_key: &'a PathKey,
45 path_key_index: PathKeyIndex,
46 anchor: text::Anchor,
47 snapshot: &'a BufferSnapshot,
48 },
49 // no excerpts and it's a min or max anchor
50 Empty,
51}
52
53impl std::fmt::Debug for AnchorSeekTarget<'_> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Excerpt {
57 path_key,
58 path_key_index: _,
59 anchor,
60 snapshot: _,
61 } => f
62 .debug_struct("Excerpt")
63 .field("path_key", path_key)
64 .field("anchor", anchor)
65 .finish(),
66 Self::Missing { path_key } => f
67 .debug_struct("Missing")
68 .field("path_key", path_key)
69 .finish(),
70 Self::Empty => f.debug_struct("Empty").finish(),
71 }
72 }
73}
74
75impl std::fmt::Debug for Anchor {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 Anchor::Min => write!(f, "Anchor::Min"),
79 Anchor::Max => write!(f, "Anchor::Max"),
80 Anchor::Excerpt(excerpt_anchor) => write!(f, "{excerpt_anchor:?}"),
81 }
82 }
83}
84
85impl From<ExcerptAnchor> for Anchor {
86 fn from(anchor: ExcerptAnchor) -> Self {
87 Anchor::Excerpt(anchor)
88 }
89}
90
91impl ExcerptAnchor {
92 pub(crate) fn buffer_id(&self) -> BufferId {
93 self.text_anchor.buffer_id
94 }
95
96 pub(crate) fn text_anchor(&self) -> text::Anchor {
97 self.text_anchor
98 }
99
100 pub(crate) fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {
101 self.diff_base_anchor = Some(diff_base_anchor);
102 self
103 }
104
105 pub(crate) fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
106 let Some(self_path_key) = snapshot.path_keys_by_index.get(&self.path) else {
107 panic!("anchor's path was never added to multibuffer")
108 };
109 let Some(other_path_key) = snapshot.path_keys_by_index.get(&other.path) else {
110 panic!("anchor's path was never added to multibuffer")
111 };
112
113 if self_path_key.cmp(other_path_key) != Ordering::Equal {
114 return self_path_key.cmp(other_path_key);
115 }
116
117 // in the case that you removed the buffer containing self,
118 // and added the buffer containing other with the same path key
119 // (ordering is arbitrary but consistent)
120 if self.text_anchor.buffer_id != other.text_anchor.buffer_id {
121 return self.text_anchor.buffer_id.cmp(&other.text_anchor.buffer_id);
122 }
123
124 // two anchors into the same buffer at the same path
125 // TODO(cole) buffer_for_path is slow
126 let Some(buffer) = snapshot
127 .buffer_for_path(&self_path_key)
128 .filter(|buffer| buffer.remote_id() == self.text_anchor.buffer_id)
129 else {
130 // buffer no longer exists at the original path (which may have been reused for a different buffer),
131 // so no way to compare the anchors
132 return Ordering::Equal;
133 };
134 let text_cmp = self.text_anchor().cmp(&other.text_anchor(), buffer);
135 if text_cmp != Ordering::Equal {
136 return text_cmp;
137 }
138
139 if (self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some())
140 && let Some(base_text) = find_diff_state(&snapshot.diffs, self.text_anchor.buffer_id)
141 .map(|diff| diff.base_text())
142 {
143 let self_anchor = self.diff_base_anchor.filter(|a| a.is_valid(base_text));
144 let other_anchor = other.diff_base_anchor.filter(|a| a.is_valid(base_text));
145 return match (self_anchor, other_anchor) {
146 (Some(a), Some(b)) => a.cmp(&b, base_text),
147 (Some(_), None) => match other.text_anchor().bias {
148 Bias::Left => Ordering::Greater,
149 Bias::Right => Ordering::Less,
150 },
151 (None, Some(_)) => match self.text_anchor().bias {
152 Bias::Left => Ordering::Less,
153 Bias::Right => Ordering::Greater,
154 },
155 (None, None) => Ordering::Equal,
156 };
157 }
158
159 Ordering::Equal
160 }
161
162 fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Self {
163 if self.text_anchor.bias == Bias::Left {
164 return *self;
165 }
166 let Some(buffer) = snapshot.buffer_for_id(self.text_anchor.buffer_id) else {
167 return *self;
168 };
169 let text_anchor = self.text_anchor().bias_left(&buffer);
170 let ret = Self::in_buffer(self.path, text_anchor);
171 if let Some(diff_base_anchor) = self.diff_base_anchor {
172 if let Some(diff) = find_diff_state(&snapshot.diffs, self.text_anchor.buffer_id)
173 && diff_base_anchor.is_valid(&diff.base_text())
174 {
175 ret.with_diff_base_anchor(diff_base_anchor.bias_left(diff.base_text()))
176 } else {
177 ret.with_diff_base_anchor(diff_base_anchor)
178 }
179 } else {
180 ret
181 }
182 }
183
184 fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Self {
185 if self.text_anchor.bias == Bias::Right {
186 return *self;
187 }
188 let Some(buffer) = snapshot.buffer_for_id(self.text_anchor.buffer_id) else {
189 return *self;
190 };
191 let text_anchor = self.text_anchor().bias_right(&buffer);
192 let ret = Self::in_buffer(self.path, text_anchor);
193 if let Some(diff_base_anchor) = self.diff_base_anchor {
194 if let Some(diff) = find_diff_state(&snapshot.diffs, self.text_anchor.buffer_id)
195 && diff_base_anchor.is_valid(&diff.base_text())
196 {
197 ret.with_diff_base_anchor(diff_base_anchor.bias_right(diff.base_text()))
198 } else {
199 ret.with_diff_base_anchor(diff_base_anchor)
200 }
201 } else {
202 ret
203 }
204 }
205
206 #[track_caller]
207 pub(crate) fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
208 ExcerptAnchor {
209 path,
210 diff_base_anchor: None,
211 text_anchor,
212 }
213 }
214
215 fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
216 let Some(target) = self.try_seek_target(snapshot) else {
217 return false;
218 };
219 let Some(buffer_snapshot) = snapshot.buffer_for_id(self.buffer_id()) else {
220 return false;
221 };
222 // Early check to avoid invalid comparisons when seeking
223 if !buffer_snapshot.can_resolve(&self.text_anchor) {
224 return false;
225 }
226 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
227 cursor.seek(&target, Bias::Left);
228 let Some(excerpt) = cursor.item() else {
229 return false;
230 };
231 let is_valid = self.text_anchor == excerpt.range.context.start
232 || self.text_anchor == excerpt.range.context.end
233 || self.text_anchor.is_valid(&buffer_snapshot);
234 is_valid
235 && excerpt
236 .range
237 .context
238 .start
239 .cmp(&self.text_anchor(), buffer_snapshot)
240 .is_le()
241 && excerpt
242 .range
243 .context
244 .end
245 .cmp(&self.text_anchor(), buffer_snapshot)
246 .is_ge()
247 }
248
249 pub(crate) fn seek_target<'a>(
250 &self,
251 snapshot: &'a MultiBufferSnapshot,
252 ) -> AnchorSeekTarget<'a> {
253 self.try_seek_target(snapshot)
254 .expect("anchor is from different multi-buffer")
255 }
256
257 pub(crate) fn try_seek_target<'a>(
258 &self,
259 snapshot: &'a MultiBufferSnapshot,
260 ) -> Option<AnchorSeekTarget<'a>> {
261 let path_key = snapshot.try_path_for_anchor(*self)?;
262
263 let Some(state) = snapshot
264 .buffers
265 .get(&self.buffer_id())
266 .filter(|state| &state.path_key == path_key)
267 else {
268 return Some(AnchorSeekTarget::Missing { path_key });
269 };
270
271 Some(AnchorSeekTarget::Excerpt {
272 path_key,
273 path_key_index: self.path,
274 anchor: self.text_anchor(),
275 snapshot: &state.buffer_snapshot,
276 })
277 }
278}
279
280impl ToOffset for ExcerptAnchor {
281 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
282 Anchor::from(*self).to_offset(snapshot)
283 }
284
285 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
286 Anchor::from(*self).to_offset_utf16(snapshot)
287 }
288}
289
290impl ToPoint for ExcerptAnchor {
291 fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point {
292 Anchor::from(*self).to_point(snapshot)
293 }
294
295 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
296 Anchor::from(*self).to_point_utf16(snapshot)
297 }
298}
299
300impl Anchor {
301 pub fn is_min(&self) -> bool {
302 matches!(self, Self::Min)
303 }
304
305 pub fn is_max(&self) -> bool {
306 matches!(self, Self::Max)
307 }
308
309 pub(crate) fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
310 Self::Excerpt(ExcerptAnchor::in_buffer(path, text_anchor))
311 }
312
313 pub(crate) fn range_in_buffer(path: PathKeyIndex, range: Range<text::Anchor>) -> Range<Self> {
314 Self::in_buffer(path, range.start)..Self::in_buffer(path, range.end)
315 }
316
317 pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
318 match (self, other) {
319 (Anchor::Min, Anchor::Min) => return Ordering::Equal,
320 (Anchor::Max, Anchor::Max) => return Ordering::Equal,
321 (Anchor::Min, _) => return Ordering::Less,
322 (Anchor::Max, _) => return Ordering::Greater,
323 (_, Anchor::Max) => return Ordering::Less,
324 (_, Anchor::Min) => return Ordering::Greater,
325 (Anchor::Excerpt(self_excerpt_anchor), Anchor::Excerpt(other_excerpt_anchor)) => {
326 self_excerpt_anchor.cmp(other_excerpt_anchor, snapshot)
327 }
328 }
329 }
330
331 pub fn bias(&self) -> Bias {
332 match self {
333 Anchor::Min => Bias::Left,
334 Anchor::Max => Bias::Right,
335 Anchor::Excerpt(anchor) => anchor.text_anchor.bias,
336 }
337 }
338
339 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
340 match self {
341 Anchor::Min => *self,
342 Anchor::Max => snapshot.anchor_before(snapshot.max_point()),
343 Anchor::Excerpt(anchor) => Anchor::Excerpt(anchor.bias_left(snapshot)),
344 }
345 }
346
347 pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
348 match self {
349 Anchor::Max => *self,
350 Anchor::Min => snapshot.anchor_after(Point::zero()),
351 Anchor::Excerpt(anchor) => Anchor::Excerpt(anchor.bias_right(snapshot)),
352 }
353 }
354
355 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
356 where
357 D: MultiBufferDimension
358 + Ord
359 + Sub<Output = D::TextDimension>
360 + Sub<D::TextDimension, Output = D>
361 + AddAssign<D::TextDimension>
362 + Add<D::TextDimension, Output = D>,
363 D::TextDimension: Sub<Output = D::TextDimension> + Ord,
364 {
365 snapshot.summary_for_anchor(self)
366 }
367
368 pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
369 match self {
370 Anchor::Min | Anchor::Max => true,
371 Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.is_valid(snapshot),
372 }
373 }
374
375 fn to_excerpt_anchor(&self, snapshot: &MultiBufferSnapshot) -> Option<ExcerptAnchor> {
376 match self {
377 Anchor::Min => {
378 let excerpt = snapshot.excerpts.first()?;
379
380 Some(ExcerptAnchor {
381 text_anchor: excerpt.range.context.start,
382 path: excerpt.path_key_index,
383 diff_base_anchor: None,
384 })
385 }
386 Anchor::Excerpt(excerpt_anchor) => Some(*excerpt_anchor),
387 Anchor::Max => {
388 let excerpt = snapshot.excerpts.last()?;
389
390 Some(ExcerptAnchor {
391 text_anchor: excerpt.range.context.end,
392 path: excerpt.path_key_index,
393 diff_base_anchor: None,
394 })
395 }
396 }
397 }
398
399 pub(crate) fn seek_target<'a>(
400 &self,
401 snapshot: &'a MultiBufferSnapshot,
402 ) -> AnchorSeekTarget<'a> {
403 let Some(excerpt_anchor) = self.to_excerpt_anchor(snapshot) else {
404 return AnchorSeekTarget::Empty;
405 };
406
407 excerpt_anchor.seek_target(snapshot)
408 }
409
410 pub(crate) fn excerpt_anchor(&self) -> Option<ExcerptAnchor> {
411 match self {
412 Anchor::Min | Anchor::Max => None,
413 Anchor::Excerpt(excerpt_anchor) => Some(*excerpt_anchor),
414 }
415 }
416
417 pub(crate) fn text_anchor(&self) -> Option<text::Anchor> {
418 match self {
419 Anchor::Min | Anchor::Max => None,
420 Anchor::Excerpt(excerpt_anchor) => Some(excerpt_anchor.text_anchor()),
421 }
422 }
423
424 pub fn opaque_id(&self) -> Option<[u8; 20]> {
425 self.text_anchor().map(|a| a.opaque_id())
426 }
427
428 /// Note: anchor_to_buffer_anchor is probably what you want
429 pub fn raw_text_anchor(&self) -> Option<text::Anchor> {
430 match self {
431 Anchor::Min | Anchor::Max => None,
432 Anchor::Excerpt(excerpt_anchor) => Some(excerpt_anchor.text_anchor),
433 }
434 }
435
436 pub(crate) fn try_seek_target<'a>(
437 &self,
438 snapshot: &'a MultiBufferSnapshot,
439 ) -> Option<AnchorSeekTarget<'a>> {
440 let Some(excerpt_anchor) = self.to_excerpt_anchor(snapshot) else {
441 return Some(AnchorSeekTarget::Empty);
442 };
443 excerpt_anchor.try_seek_target(snapshot)
444 }
445
446 /// Returns the text anchor for this anchor.
447 /// Panics if the anchor is from a different buffer.
448 pub fn text_anchor_in(&self, buffer: &BufferSnapshot) -> text::Anchor {
449 match self {
450 Anchor::Min => text::Anchor::min_for_buffer(buffer.remote_id()),
451 Anchor::Excerpt(excerpt_anchor) => {
452 let text_anchor = excerpt_anchor.text_anchor;
453 assert_eq!(text_anchor.buffer_id, buffer.remote_id());
454 text_anchor
455 }
456 Anchor::Max => text::Anchor::max_for_buffer(buffer.remote_id()),
457 }
458 }
459
460 pub fn diff_base_anchor(&self) -> Option<text::Anchor> {
461 self.excerpt_anchor()?.diff_base_anchor
462 }
463
464 #[cfg(any(test, feature = "test-support"))]
465 pub fn expect_text_anchor(&self) -> text::Anchor {
466 self.excerpt_anchor().unwrap().text_anchor
467 }
468
469 pub fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {
470 match &mut self {
471 Anchor::Min | Anchor::Max => {}
472 Anchor::Excerpt(excerpt_anchor) => {
473 excerpt_anchor.diff_base_anchor = Some(diff_base_anchor);
474 }
475 }
476 self
477 }
478}
479
480impl ToOffset for Anchor {
481 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
482 self.summary(snapshot)
483 }
484 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
485 self.summary(snapshot)
486 }
487}
488
489impl ToPoint for Anchor {
490 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
491 self.summary(snapshot)
492 }
493 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
494 self.summary(snapshot)
495 }
496}
497
498pub trait AnchorRangeExt {
499 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
500 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
501 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
502 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset>;
503 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
504}
505
506impl AnchorRangeExt for Range<Anchor> {
507 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
508 match self.start.cmp(&other.start, buffer) {
509 Ordering::Equal => other.end.cmp(&self.end, buffer),
510 ord => ord,
511 }
512 }
513
514 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
515 self.start.cmp(&other.start, buffer).is_le() && other.end.cmp(&self.end, buffer).is_le()
516 }
517
518 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
519 self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
520 }
521
522 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset> {
523 self.start.to_offset(content)..self.end.to_offset(content)
524 }
525
526 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
527 self.start.to_point(content)..self.end.to_point(content)
528 }
529}