anchor.rs

  1use crate::{MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16};
  2
  3use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint};
  4use language::Point;
  5use std::{
  6    cmp::Ordering,
  7    ops::{AddAssign, Range, Sub},
  8};
  9use sum_tree::Bias;
 10use text::BufferId;
 11
 12#[derive(Clone, Copy, Eq, PartialEq, Hash)]
 13pub struct Anchor {
 14    /// Invariant: If buffer id is `None`, excerpt id must be `ExcerptId::min()` or `ExcerptId::max()`.
 15    pub buffer_id: Option<BufferId>,
 16    pub excerpt_id: ExcerptId,
 17    pub text_anchor: text::Anchor,
 18    pub diff_base_anchor: Option<text::Anchor>,
 19}
 20
 21impl std::fmt::Debug for Anchor {
 22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 23        if *self == Self::min() {
 24            return f.write_str("Anchor::MIN");
 25        }
 26        if *self == Self::max() {
 27            return f.write_str("Anchor::MAX");
 28        }
 29
 30        f.debug_struct("Anchor")
 31            .field("buffer_id", &self.buffer_id)
 32            .field("excerpt_id", &self.excerpt_id)
 33            .field("text_anchor", &self.text_anchor)
 34            .field("diff_base_anchor", &self.diff_base_anchor)
 35            .finish()
 36    }
 37}
 38
 39impl Anchor {
 40    pub fn with_diff_base_anchor(self, diff_base_anchor: text::Anchor) -> Self {
 41        Self {
 42            diff_base_anchor: Some(diff_base_anchor),
 43            ..self
 44        }
 45    }
 46
 47    pub fn in_buffer(
 48        excerpt_id: ExcerptId,
 49        buffer_id: BufferId,
 50        text_anchor: text::Anchor,
 51    ) -> Self {
 52        debug_assert!(
 53            text_anchor.buffer_id.is_none_or(|id| id == buffer_id),
 54            "buffer id does not match the one in the text anchor: {buffer_id:?} {text_anchor:?}",
 55        );
 56        Self {
 57            buffer_id: Some(buffer_id),
 58            excerpt_id,
 59            text_anchor,
 60            diff_base_anchor: None,
 61        }
 62    }
 63
 64    pub fn range_in_buffer(
 65        excerpt_id: ExcerptId,
 66        buffer_id: BufferId,
 67        range: Range<text::Anchor>,
 68    ) -> Range<Self> {
 69        Self::in_buffer(excerpt_id, buffer_id, range.start)
 70            ..Self::in_buffer(excerpt_id, buffer_id, range.end)
 71    }
 72
 73    pub fn min() -> Self {
 74        Self {
 75            buffer_id: None,
 76            excerpt_id: ExcerptId::min(),
 77            text_anchor: text::Anchor::MIN,
 78            diff_base_anchor: None,
 79        }
 80    }
 81
 82    pub fn max() -> Self {
 83        Self {
 84            buffer_id: None,
 85            excerpt_id: ExcerptId::max(),
 86            text_anchor: text::Anchor::MAX,
 87            diff_base_anchor: None,
 88        }
 89    }
 90
 91    pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
 92        if self == other {
 93            return Ordering::Equal;
 94        }
 95
 96        let self_excerpt_id = snapshot.latest_excerpt_id(self.excerpt_id);
 97        let other_excerpt_id = snapshot.latest_excerpt_id(other.excerpt_id);
 98
 99        let excerpt_id_cmp = self_excerpt_id.cmp(&other_excerpt_id, snapshot);
100        if excerpt_id_cmp.is_ne() {
101            return excerpt_id_cmp;
102        }
103        if self_excerpt_id == ExcerptId::max()
104            && self.text_anchor == text::Anchor::MAX
105            && self.text_anchor == text::Anchor::MAX
106            && self.diff_base_anchor.is_none()
107            && other.diff_base_anchor.is_none()
108        {
109            return Ordering::Equal;
110        }
111        if let Some(excerpt) = snapshot.excerpt(self_excerpt_id) {
112            let text_cmp = self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer);
113            if text_cmp.is_ne() {
114                return text_cmp;
115            }
116            if (self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some())
117                && let Some(base_text) = snapshot
118                    .diffs
119                    .get(&excerpt.buffer_id)
120                    .map(|diff| diff.base_text())
121            {
122                let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a));
123                let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a));
124                return match (self_anchor, other_anchor) {
125                    (Some(a), Some(b)) => a.cmp(&b, base_text),
126                    (Some(_), None) => match other.text_anchor.bias {
127                        Bias::Left => Ordering::Greater,
128                        Bias::Right => Ordering::Less,
129                    },
130                    (None, Some(_)) => match self.text_anchor.bias {
131                        Bias::Left => Ordering::Less,
132                        Bias::Right => Ordering::Greater,
133                    },
134                    (None, None) => Ordering::Equal,
135                };
136            }
137        }
138        Ordering::Equal
139    }
140
141    pub fn bias(&self) -> Bias {
142        self.text_anchor.bias
143    }
144
145    pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
146        if self.text_anchor.bias != Bias::Left
147            && let Some(excerpt) = snapshot.excerpt(self.excerpt_id)
148        {
149            return Self {
150                buffer_id: Some(excerpt.buffer_id),
151                excerpt_id: excerpt.id,
152                text_anchor: self.text_anchor.bias_left(&excerpt.buffer),
153                diff_base_anchor: self.diff_base_anchor.map(|a| {
154                    if let Some(base_text) = snapshot
155                        .diffs
156                        .get(&excerpt.buffer_id)
157                        .map(|diff| diff.base_text())
158                        && a.buffer_id == Some(base_text.remote_id())
159                    {
160                        return a.bias_left(base_text);
161                    }
162                    a
163                }),
164            };
165        }
166        *self
167    }
168
169    pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
170        if self.text_anchor.bias != Bias::Right
171            && let Some(excerpt) = snapshot.excerpt(self.excerpt_id)
172        {
173            return Self {
174                buffer_id: Some(excerpt.buffer_id),
175                excerpt_id: excerpt.id,
176                text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
177                diff_base_anchor: self.diff_base_anchor.map(|a| {
178                    if let Some(base_text) = snapshot
179                        .diffs
180                        .get(&excerpt.buffer_id)
181                        .map(|diff| diff.base_text())
182                        && a.buffer_id == Some(base_text.remote_id())
183                    {
184                        return a.bias_right(base_text);
185                    }
186                    a
187                }),
188            };
189        }
190        *self
191    }
192
193    pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
194    where
195        D: MultiBufferDimension
196            + Ord
197            + Sub<Output = D::TextDimension>
198            + AddAssign<D::TextDimension>,
199        D::TextDimension: Sub<Output = D::TextDimension> + Ord,
200    {
201        snapshot.summary_for_anchor(self)
202    }
203
204    pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
205        if *self == Anchor::min() || self.excerpt_id == ExcerptId::max() {
206            !snapshot.is_empty()
207        } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
208            (self.text_anchor == excerpt.range.context.start
209                || self.text_anchor == excerpt.range.context.end
210                || self.text_anchor.is_valid(&excerpt.buffer))
211                && excerpt.contains(self)
212        } else {
213            false
214        }
215    }
216}
217
218impl ToOffset for Anchor {
219    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
220        self.summary(snapshot)
221    }
222    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
223        self.summary(snapshot)
224    }
225}
226
227impl ToPoint for Anchor {
228    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
229        self.summary(snapshot)
230    }
231    fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
232        self.summary(snapshot)
233    }
234}
235
236pub trait AnchorRangeExt {
237    fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
238    fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
239    fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
240    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset>;
241    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
242}
243
244impl AnchorRangeExt for Range<Anchor> {
245    fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
246        match self.start.cmp(&other.start, buffer) {
247            Ordering::Equal => other.end.cmp(&self.end, buffer),
248            ord => ord,
249        }
250    }
251
252    fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
253        self.start.cmp(&other.start, buffer).is_le() && other.end.cmp(&self.end, buffer).is_le()
254    }
255
256    fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
257        self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
258    }
259
260    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset> {
261        self.start.to_offset(content)..self.end.to_offset(content)
262    }
263
264    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
265        self.start.to_point(content)..self.end.to_point(content)
266    }
267}