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