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;
10
11#[derive(Clone, Copy, Eq, PartialEq, Hash)]
12pub struct Anchor {
13 pub excerpt_id: ExcerptId,
14 pub text_anchor: text::Anchor,
15 pub diff_base_anchor: Option<text::Anchor>,
16}
17
18impl std::fmt::Debug for Anchor {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 if self.is_min() {
21 return write!(f, "Anchor::min({:?})", self.text_anchor.buffer_id);
22 }
23 if self.is_max() {
24 return write!(f, "Anchor::max({:?})", self.text_anchor.buffer_id);
25 }
26
27 f.debug_struct("Anchor")
28 .field("excerpt_id", &self.excerpt_id)
29 .field("text_anchor", &self.text_anchor)
30 .field("diff_base_anchor", &self.diff_base_anchor)
31 .finish()
32 }
33}
34
35impl Anchor {
36 pub fn with_diff_base_anchor(self, diff_base_anchor: text::Anchor) -> Self {
37 Self {
38 diff_base_anchor: Some(diff_base_anchor),
39 ..self
40 }
41 }
42
43 pub fn in_buffer(excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Self {
44 Self {
45 excerpt_id,
46 text_anchor,
47 diff_base_anchor: None,
48 }
49 }
50
51 pub fn range_in_buffer(excerpt_id: ExcerptId, range: Range<text::Anchor>) -> Range<Self> {
52 Self::in_buffer(excerpt_id, range.start)..Self::in_buffer(excerpt_id, range.end)
53 }
54
55 pub fn min() -> Self {
56 Self {
57 excerpt_id: ExcerptId::min(),
58 text_anchor: text::Anchor::MIN,
59 diff_base_anchor: None,
60 }
61 }
62
63 pub fn max() -> Self {
64 Self {
65 excerpt_id: ExcerptId::max(),
66 text_anchor: text::Anchor::MAX,
67 diff_base_anchor: None,
68 }
69 }
70
71 pub fn is_min(&self) -> bool {
72 self.excerpt_id == ExcerptId::min()
73 && self.text_anchor.is_min()
74 && self.diff_base_anchor.is_none()
75 }
76
77 pub fn is_max(&self) -> bool {
78 self.excerpt_id == ExcerptId::max()
79 && self.text_anchor.is_max()
80 && self.diff_base_anchor.is_none()
81 }
82
83 pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
84 if self == other {
85 return Ordering::Equal;
86 }
87
88 let self_excerpt_id = snapshot.latest_excerpt_id(self.excerpt_id);
89 let other_excerpt_id = snapshot.latest_excerpt_id(other.excerpt_id);
90
91 let excerpt_id_cmp = self_excerpt_id.cmp(&other_excerpt_id, snapshot);
92 if excerpt_id_cmp.is_ne() {
93 return excerpt_id_cmp;
94 }
95 if self_excerpt_id == ExcerptId::max()
96 && self.text_anchor.is_max()
97 && self.text_anchor.is_max()
98 && self.diff_base_anchor.is_none()
99 && other.diff_base_anchor.is_none()
100 {
101 return Ordering::Equal;
102 }
103 if let Some(excerpt) = snapshot.excerpt(self_excerpt_id) {
104 let text_cmp = self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer);
105 if text_cmp.is_ne() {
106 return text_cmp;
107 }
108 if (self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some())
109 && let Some(base_text) = snapshot
110 .diffs
111 .get(&excerpt.buffer_id)
112 .map(|diff| diff.base_text())
113 {
114 let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a));
115 let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a));
116 return match (self_anchor, other_anchor) {
117 (Some(a), Some(b)) => a.cmp(&b, base_text),
118 (Some(_), None) => match other.text_anchor.bias {
119 Bias::Left => Ordering::Greater,
120 Bias::Right => Ordering::Less,
121 },
122 (None, Some(_)) => match self.text_anchor.bias {
123 Bias::Left => Ordering::Less,
124 Bias::Right => Ordering::Greater,
125 },
126 (None, None) => Ordering::Equal,
127 };
128 }
129 }
130 Ordering::Equal
131 }
132
133 pub fn bias(&self) -> Bias {
134 self.text_anchor.bias
135 }
136
137 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
138 if self.text_anchor.bias != Bias::Left
139 && let Some(excerpt) = snapshot.excerpt(self.excerpt_id)
140 {
141 return Self {
142 excerpt_id: excerpt.id,
143 text_anchor: self.text_anchor.bias_left(&excerpt.buffer),
144 diff_base_anchor: self.diff_base_anchor.map(|a| {
145 if let Some(base_text) = snapshot
146 .diffs
147 .get(&excerpt.buffer_id)
148 .map(|diff| diff.base_text())
149 && a.buffer_id == Some(base_text.remote_id())
150 {
151 return a.bias_left(base_text);
152 }
153 a
154 }),
155 };
156 }
157 *self
158 }
159
160 pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
161 if self.text_anchor.bias != Bias::Right
162 && let Some(excerpt) = snapshot.excerpt(self.excerpt_id)
163 {
164 return Self {
165 excerpt_id: excerpt.id,
166 text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
167 diff_base_anchor: self.diff_base_anchor.map(|a| {
168 if let Some(base_text) = snapshot
169 .diffs
170 .get(&excerpt.buffer_id)
171 .map(|diff| diff.base_text())
172 && a.buffer_id == Some(base_text.remote_id())
173 {
174 return a.bias_right(base_text);
175 }
176 a
177 }),
178 };
179 }
180 *self
181 }
182
183 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
184 where
185 D: MultiBufferDimension
186 + Ord
187 + Sub<Output = D::TextDimension>
188 + AddAssign<D::TextDimension>,
189 D::TextDimension: Sub<Output = D::TextDimension> + Ord,
190 {
191 snapshot.summary_for_anchor(self)
192 }
193
194 pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
195 if self.is_min() || self.is_max() {
196 true
197 } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
198 (self.text_anchor == excerpt.range.context.start
199 || self.text_anchor == excerpt.range.context.end
200 || self.text_anchor.is_valid(&excerpt.buffer))
201 && excerpt.contains(self)
202 } else {
203 false
204 }
205 }
206}
207
208impl ToOffset for Anchor {
209 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
210 self.summary(snapshot)
211 }
212 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
213 self.summary(snapshot)
214 }
215}
216
217impl ToPoint for Anchor {
218 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
219 self.summary(snapshot)
220 }
221 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> rope::PointUtf16 {
222 self.summary(snapshot)
223 }
224}
225
226pub trait AnchorRangeExt {
227 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
228 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
229 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
230 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset>;
231 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
232}
233
234impl AnchorRangeExt for Range<Anchor> {
235 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
236 match self.start.cmp(&other.start, buffer) {
237 Ordering::Equal => other.end.cmp(&self.end, buffer),
238 ord => ord,
239 }
240 }
241
242 fn includes(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
243 self.start.cmp(&other.start, buffer).is_le() && other.end.cmp(&self.end, buffer).is_le()
244 }
245
246 fn overlaps(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool {
247 self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le()
248 }
249
250 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferOffset> {
251 self.start.to_offset(content)..self.end.to_offset(content)
252 }
253
254 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
255 self.start.to_point(content)..self.end.to_point(content)
256 }
257}