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(excerpt) = snapshot.excerpt(&self.excerpt_id) {
44 // Even though the anchor refers to a valid excerpt the underlying buffer might have
45 // changed. In that case, treat the anchor as if it were at the start of that
46 // excerpt.
47 if self.buffer_id == Some(excerpt.buffer_id)
48 && other.buffer_id == Some(excerpt.buffer_id)
49 {
50 let self_anchor = excerpt.clip_anchor(self.text_anchor.clone());
51 let other_anchor = excerpt.clip_anchor(other.text_anchor.clone());
52 self_anchor.cmp(&other_anchor, &excerpt.buffer)
53 } else if self.buffer_id == Some(excerpt.buffer_id) {
54 Ok(Ordering::Greater)
55 } else if other.buffer_id == Some(excerpt.buffer_id) {
56 Ok(Ordering::Less)
57 } else {
58 Ok(Ordering::Equal)
59 }
60 } else {
61 Ok(Ordering::Equal)
62 }
63 } else {
64 Ok(excerpt_id_cmp)
65 }
66 }
67
68 pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
69 if self.text_anchor.bias != Bias::Left {
70 if let Some(excerpt) = snapshot.excerpt(&self.excerpt_id) {
71 if self.buffer_id == Some(excerpt.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(&excerpt.buffer),
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(excerpt) = snapshot.excerpt(&self.excerpt_id) {
86 if self.buffer_id == Some(excerpt.buffer_id) {
87 return Self {
88 buffer_id: self.buffer_id,
89 excerpt_id: self.excerpt_id.clone(),
90 text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
91 };
92 }
93 }
94 }
95 self.clone()
96 }
97
98 pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
99 where
100 D: TextDimension + Ord + Sub<D, Output = D>,
101 {
102 snapshot.summary_for_anchor(self)
103 }
104}
105
106impl ToOffset for Anchor {
107 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize {
108 self.summary(snapshot)
109 }
110}
111
112impl ToPoint for Anchor {
113 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
114 self.summary(snapshot)
115 }
116}
117
118pub trait AnchorRangeExt {
119 fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering>;
120 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
121 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
122}
123
124impl AnchorRangeExt for Range<Anchor> {
125 fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering> {
126 Ok(match self.start.cmp(&other.start, buffer)? {
127 Ordering::Equal => other.end.cmp(&self.end, buffer)?,
128 ord @ _ => ord,
129 })
130 }
131
132 fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize> {
133 self.start.to_offset(&content)..self.end.to_offset(&content)
134 }
135
136 fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
137 self.start.to_point(&content)..self.end.to_point(&content)
138 }
139}