anchor.rs

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