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