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}