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