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;
13use text::BufferId;
14use util::debug_panic;
15
16/// A multibuffer anchor derived from an anchor into a specific excerpted buffer.
17#[derive(Clone, Copy, Eq, PartialEq, Hash)]
18pub(crate) struct ExcerptAnchor {
19 /// The position within the excerpt's underlying buffer. This is a stable
20 /// reference that remains valid as the buffer text is edited.
21 pub(crate) timestamp: clock::Lamport,
22
23 /// The byte offset into the text inserted in the operation
24 /// at `timestamp`.
25 pub(crate) offset: u32,
26 /// Whether this anchor stays attached to the character *before* or *after*
27 /// the offset.
28 pub(crate) bias: Bias,
29 pub(crate) buffer_id: BufferId,
30 /// Refers to the path key that the buffer had when this anchor was created,
31 /// so that ordering is stable when the path key for a buffer changes
32 pub(crate) path: PathKeyIndex,
33 /// When present, indicates this anchor points into deleted text within an
34 /// expanded diff hunk. The anchor references a position in the diff base
35 /// (original) text rather than the current buffer text. This is used when
36 /// displaying inline diffs where deleted lines are shown.
37 pub(crate) diff_base_anchor: Option<text::Anchor>,
38}
39
40/// A stable reference to a position within a [`MultiBuffer`](super::MultiBuffer).
41///
42/// Unlike simple offsets, anchors remain valid as the text is edited, automatically
43/// adjusting to reflect insertions and deletions around them.
44#[derive(Clone, Copy, Eq, PartialEq, Hash)]
45pub enum Anchor {
46 Min,
47 Excerpt(ExcerptAnchor),
48 Max,
49}
50
51impl std::fmt::Debug for ExcerptAnchor {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("Anchor")
54 .field("text_anchor", &self.text_anchor())
55 .field("diff_base_anchor", &self.diff_base_anchor)
56 .finish()
57 }
58}
59
60// todo!() should this take a lifetime?
61pub(crate) enum AnchorSeekTarget {
62 Min,
63 Max,
64 Excerpt {
65 path_key: PathKey,
66 anchor: ExcerptAnchor,
67 // None when the buffer no longer exists in the multibuffer
68 snapshot: Option<BufferSnapshot>,
69 },
70}
71
72impl std::fmt::Debug for Anchor {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 Anchor::Min => write!(f, "Anchor::Min"),
76 Anchor::Max => write!(f, "Anchor::Max"),
77 Anchor::Excerpt(excerpt_anchor) => write!(f, "{excerpt_anchor:?}"),
78 }
79 }
80}
81
82impl From<ExcerptAnchor> for Anchor {
83 fn from(anchor: ExcerptAnchor) -> Self {
84 Anchor::Excerpt(anchor)
85 }
86}
87
88impl ExcerptAnchor {
89 pub(crate) fn text_anchor(&self) -> text::Anchor {
90 text::Anchor::new(self.timestamp, self.offset, self.bias, Some(self.buffer_id))
91 }
92
93 pub(crate) fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {
94 self.diff_base_anchor = Some(diff_base_anchor);
95 self
96 }
97
98 fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
99 let Some(self_path_key) = snapshot.path_keys_by_index.get(&self.path) else {
100 panic!("anchor's path was never added to multibuffer")
101 };
102 let Some(other_path_key) = snapshot.path_keys_by_index.get(&other.path) else {
103 panic!("anchor's path was never added to multibuffer")
104 };
105
106 if self_path_key.cmp(other_path_key) != Ordering::Equal {
107 return self_path_key.cmp(other_path_key);
108 }
109
110 // in the case that you removed the buffer containing self,
111 // and added the buffer containing other with the same path key
112 // (ordering is arbitrary but consistent)
113 if self.buffer_id != other.buffer_id {
114 return self.buffer_id.cmp(&other.buffer_id);
115 }
116
117 let Some(buffer) = snapshot.buffer_for_path(&self_path_key) else {
118 return Ordering::Equal;
119 };
120 let text_cmp = self.text_anchor().cmp(&other.text_anchor(), buffer);
121 if text_cmp != Ordering::Equal {
122 return text_cmp;
123 }
124
125 if (self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some())
126 && let Some(base_text) = snapshot
127 .diffs
128 .get(&self.buffer_id)
129 .map(|diff| diff.base_text())
130 {
131 let self_anchor = self.diff_base_anchor.filter(|a| a.is_valid(base_text));
132 let other_anchor = other.diff_base_anchor.filter(|a| a.is_valid(base_text));
133 return match (self_anchor, other_anchor) {
134 (Some(a), Some(b)) => a.cmp(&b, base_text),
135 (Some(_), None) => match other.text_anchor().bias {
136 Bias::Left => Ordering::Greater,
137 Bias::Right => Ordering::Less,
138 },
139 (None, Some(_)) => match self.text_anchor().bias {
140 Bias::Left => Ordering::Less,
141 Bias::Right => Ordering::Greater,
142 },
143 (None, None) => Ordering::Equal,
144 };
145 }
146
147 Ordering::Equal
148 }
149
150 fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Self {
151 if self.bias == Bias::Left {
152 return *self;
153 }
154 let Some(buffer) = snapshot.buffer_for_id(self.buffer_id) else {
155 return *self;
156 };
157 let text_anchor = self.text_anchor().bias_left(&buffer);
158 let ret = Self::in_buffer(self.path, text_anchor);
159 if let Some(diff_base_anchor) = self.diff_base_anchor {
160 if let Some(diff) = snapshot.diffs.get(&self.buffer_id)
161 && diff_base_anchor.is_valid(&diff.base_text())
162 {
163 ret.with_diff_base_anchor(diff_base_anchor.bias_left(diff.base_text()))
164 } else {
165 ret.with_diff_base_anchor(diff_base_anchor)
166 }
167 } else {
168 ret
169 }
170 }
171
172 fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Self {
173 if self.bias == Bias::Right {
174 return *self;
175 }
176 let Some(buffer) = snapshot.buffer_for_id(self.buffer_id) else {
177 return *self;
178 };
179 let text_anchor = self.text_anchor().bias_right(&buffer);
180 let ret = Self::in_buffer(self.path, text_anchor);
181 if let Some(diff_base_anchor) = self.diff_base_anchor {
182 if let Some(diff) = snapshot.diffs.get(&self.buffer_id)
183 && diff_base_anchor.is_valid(&diff.base_text())
184 {
185 ret.with_diff_base_anchor(diff_base_anchor.bias_right(diff.base_text()))
186 } else {
187 ret.with_diff_base_anchor(diff_base_anchor)
188 }
189 } else {
190 ret
191 }
192 }
193
194 pub(crate) fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
195 let Some(buffer_id) = text_anchor.buffer_id else {
196 panic!("text_anchor must have a buffer_id");
197 };
198 ExcerptAnchor {
199 path,
200 diff_base_anchor: None,
201 timestamp: text_anchor.timestamp(),
202 buffer_id,
203 offset: text_anchor.offset,
204 bias: text_anchor.bias,
205 }
206 }
207
208 fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
209 let Some(target) = snapshot.try_anchor_seek_target(*self) else {
210 return false;
211 };
212 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
213 cursor.seek(&target, Bias::Left);
214 let Some(excerpt) = cursor.item() else {
215 return false;
216 };
217 excerpt.buffer.remote_id() == self.buffer_id
218 && excerpt
219 .range
220 .context
221 .start
222 .cmp(&self.text_anchor(), &excerpt.buffer)
223 .is_le()
224 && excerpt
225 .range
226 .context
227 .end
228 .cmp(&self.text_anchor(), &excerpt.buffer)
229 .is_ge()
230 }
231
232 pub(crate) fn seek_target(&self, snapshot: &MultiBufferSnapshot) -> AnchorSeekTarget {
233 let path_key = snapshot.path_for_anchor(*self);
234 let buffer = snapshot.buffer_for_path(&path_key).cloned();
235 AnchorSeekTarget::Excerpt {
236 path_key,
237 anchor: *self,
238 snapshot: buffer,
239 }
240 }
241}
242
243impl Anchor {
244 pub fn min() -> Self {
245 Self::Min
246 }
247
248 pub fn max() -> Self {
249 Self::Max
250 }
251
252 pub fn is_min(&self) -> bool {
253 matches!(self, Self::Min)
254 }
255
256 pub fn is_max(&self) -> bool {
257 matches!(self, Self::Max)
258 }
259
260 pub fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self {
261 Self::Excerpt(ExcerptAnchor::in_buffer(path, text_anchor))
262 }
263
264 pub fn range_in_buffer(path: PathKeyIndex, range: Range<text::Anchor>) -> Range<Self> {
265 Self::in_buffer(path, range.start)..Self::in_buffer(path, range.end)
266 }
267
268 pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
269 match (self, other) {
270 (Anchor::Min, Anchor::Min) => return Ordering::Equal,
271 (Anchor::Max, Anchor::Max) => return Ordering::Equal,
272 (Anchor::Min, _) => return Ordering::Less,
273 (Anchor::Max, _) => return Ordering::Greater,
274 (_, Anchor::Max) => return Ordering::Less,
275 (_, Anchor::Min) => return Ordering::Greater,
276 (Anchor::Excerpt(self_excerpt_anchor), Anchor::Excerpt(other_excerpt_anchor)) => {
277 self_excerpt_anchor.cmp(other_excerpt_anchor, snapshot)
278 }
279 }
280 }
281
282 pub fn bias(&self) -> Bias {
283 match self {
284 Anchor::Min => Bias::Left,
285 Anchor::Max => Bias::Right,
286 Anchor::Excerpt(anchor) => anchor.bias,
287 }
288 }
289
290 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
291 match self {
292 Anchor::Min => *self,
293 Anchor::Max => snapshot.anchor_before(snapshot.max_point()),
294 Anchor::Excerpt(anchor) => Anchor::Excerpt(anchor.bias_left(snapshot)),
295 }
296 }
297
298 pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
299 match self {
300 Anchor::Max => *self,
301 Anchor::Min => snapshot.anchor_after(Point::zero()),
302 Anchor::Excerpt(anchor) => Anchor::Excerpt(anchor.bias_right(snapshot)),
303 }
304 }
305
306 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
307 where
308 D: MultiBufferDimension
309 + Ord
310 + Sub<Output = D::TextDimension>
311 + Sub<D::TextDimension, Output = D>
312 + AddAssign<D::TextDimension>
313 + Add<D::TextDimension, Output = D>,
314 D::TextDimension: Sub<Output = D::TextDimension> + Ord,
315 {
316 snapshot.summary_for_anchor(self)
317 }
318
319 pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
320 match self {
321 Anchor::Min | Anchor::Max => true,
322 Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.is_valid(snapshot),
323 }
324 }
325
326 pub(crate) fn seek_target(&self, snapshot: &MultiBufferSnapshot) -> AnchorSeekTarget {
327 match self {
328 Anchor::Min => AnchorSeekTarget::Min,
329 Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.seek_target(snapshot),
330 Anchor::Max => AnchorSeekTarget::Max,
331 }
332 }
333
334 pub(crate) fn excerpt_anchor(&self) -> Option<ExcerptAnchor> {
335 match self {
336 Anchor::Min | Anchor::Max => None,
337 Anchor::Excerpt(excerpt_anchor) => Some(*excerpt_anchor),
338 }
339 }
340
341 pub(crate) fn try_seek_target(&self, arg: &MultiBufferSnapshot) -> Option<AnchorSeekTarget> {
342 todo!()
343 match self {
344 Anchor::Min => AnchorSeekTarget::Min,
345 Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.seek_target(snapshot),
346 Anchor::Max => AnchorSeekTarget::Max,
347 }
348 }
349}
350
351impl ToOffset for Anchor {
352 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
353 self.summary(snapshot)
354 }
355 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
356 self.summary(snapshot)
357 }
358}
359
360impl ToPoint for Anchor {
361 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
362 self.summary(snapshot)
363 }
364 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
365 self.summary(snapshot)
366 }
367}
368
369pub trait AnchorRangeExt {
370 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
371 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
372 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
373 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset>;
374 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
375}
376
377impl AnchorRangeExt for Range<Anchor> {
378 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
379 match self.start.cmp(&other.start, buffer) {
380 Ordering::Equal => other.end.cmp(&self.end, buffer),
381 ord => ord,
382 }
383 }
384
385 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
386 self.start.cmp(&other.start, buffer).is_le() && other.end.cmp(&self.end, buffer).is_le()
387 }
388
389 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
390 self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
391 }
392
393 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset> {
394 self.start.to_offset(content)..self.end.to_offset(content)
395 }
396
397 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
398 self.start.to_point(content)..self.end.to_point(content)
399 }
400}