1use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint};
2use anyhow::Result;
3use std::{
4 cmp::Ordering,
5 ops::{Range, Sub},
6};
7use sum_tree::Bias;
8use text::{rope::TextDimension, Point};
9
10#[derive(Clone, Eq, PartialEq, Debug, Hash)]
11pub struct Anchor {
12 pub(crate) buffer_id: Option<usize>,
13 pub(crate) excerpt_id: ExcerptId,
14 pub(crate) text_anchor: text::Anchor,
15}
16
17impl Anchor {
18 pub fn min() -> Self {
19 Self {
20 buffer_id: None,
21 excerpt_id: ExcerptId::min(),
22 text_anchor: text::Anchor::min(),
23 }
24 }
25
26 pub fn max() -> Self {
27 Self {
28 buffer_id: None,
29 excerpt_id: ExcerptId::max(),
30 text_anchor: text::Anchor::max(),
31 }
32 }
33
34 pub fn excerpt_id(&self) -> &ExcerptId {
35 &self.excerpt_id
36 }
37
38 pub fn cmp<'a>(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Result<Ordering> {
39 let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id);
40 if excerpt_id_cmp.is_eq() {
41 if self.excerpt_id == ExcerptId::min() || self.excerpt_id == ExcerptId::max() {
42 Ok(Ordering::Equal)
43 } else if let Some((buffer_id, buffer_snapshot)) =
44 snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id)
45 {
46 // Even though the anchor refers to a valid excerpt the underlying buffer might have
47 // changed. In that case, treat the anchor as if it were at the start of that
48 // excerpt.
49 if self.buffer_id == Some(buffer_id) && other.buffer_id == Some(buffer_id) {
50 self.text_anchor.cmp(&other.text_anchor, buffer_snapshot)
51 } else if self.buffer_id == Some(buffer_id) {
52 Ok(Ordering::Greater)
53 } else if other.buffer_id == Some(buffer_id) {
54 Ok(Ordering::Less)
55 } else {
56 Ok(Ordering::Equal)
57 }
58 } else {
59 Ok(Ordering::Equal)
60 }
61 } else {
62 Ok(excerpt_id_cmp)
63 }
64 }
65
66 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
67 if self.text_anchor.bias != Bias::Left {
68 if let Some((buffer_id, buffer_snapshot)) =
69 snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id)
70 {
71 if self.buffer_id == Some(buffer_id) {
72 return Self {
73 buffer_id: self.buffer_id,
74 excerpt_id: self.excerpt_id.clone(),
75 text_anchor: self.text_anchor.bias_left(buffer_snapshot),
76 };
77 }
78 }
79 }
80 self.clone()
81 }
82
83 pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
84 if self.text_anchor.bias != Bias::Right {
85 if let Some((buffer_id, buffer_snapshot)) =
86 snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id)
87 {
88 if self.buffer_id == Some(buffer_id) {
89 return Self {
90 buffer_id: self.buffer_id,
91 excerpt_id: self.excerpt_id.clone(),
92 text_anchor: self.text_anchor.bias_right(buffer_snapshot),
93 };
94 }
95 }
96 }
97 self.clone()
98 }
99
100 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
101 where
102 D: TextDimension + Ord + Sub<D, Output = D>,
103 {
104 snapshot.summary_for_anchor(self)
105 }
106}
107
108impl ToOffset for Anchor {
109 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize {
110 self.summary(snapshot)
111 }
112}
113
114impl ToPoint for Anchor {
115 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
116 self.summary(snapshot)
117 }
118}
119
120pub trait AnchorRangeExt {
121 fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering>;
122 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
123 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
124}
125
126impl AnchorRangeExt for Range<Anchor> {
127 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering> {
128 Ok(match self.start.cmp(&other.start, buffer)? {
129 Ordering::Equal => other.end.cmp(&self.end, buffer)?,
130 ord @ _ => ord,
131 })
132 }
133
134 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize> {
135 self.start.to_offset(&content)..self.end.to_offset(&content)
136 }
137
138 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
139 self.start.to_point(&content)..self.end.to_point(&content)
140 }
141}