anchor.rs

  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}