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}