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