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}