1use crate::{
2 BufferId, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint, ToPointUtf16,
3 locator::Locator,
4};
5use std::{cmp::Ordering, fmt::Debug, ops::Range};
6use sum_tree::{Bias, Dimensions};
7
8/// A timestamped position in a buffer
9#[derive(Copy, Clone, Eq, PartialEq, Hash)]
10pub struct Anchor {
11 pub timestamp: clock::Lamport,
12 /// The byte offset in the buffer
13 pub offset: usize,
14 /// Describes which character the anchor is biased towards
15 pub bias: Bias,
16 pub buffer_id: Option<BufferId>,
17}
18
19impl Debug for Anchor {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 if *self == Self::MIN {
22 return f.write_str("Anchor::MIN");
23 }
24 if *self == Self::MAX {
25 return f.write_str("Anchor::MAX");
26 }
27
28 f.debug_struct("Anchor")
29 .field("timestamp", &self.timestamp)
30 .field("offset", &self.offset)
31 .field("bias", &self.bias)
32 .field("buffer_id", &self.buffer_id)
33 .finish()
34 }
35}
36
37impl Anchor {
38 pub const MIN: Self = Self {
39 timestamp: clock::Lamport::MIN,
40 offset: usize::MIN,
41 bias: Bias::Left,
42 buffer_id: None,
43 };
44
45 pub const MAX: Self = Self {
46 timestamp: clock::Lamport::MAX,
47 offset: usize::MAX,
48 bias: Bias::Right,
49 buffer_id: None,
50 };
51
52 pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
53 let fragment_id_comparison = if self.timestamp == other.timestamp {
54 Ordering::Equal
55 } else {
56 buffer
57 .fragment_id_for_anchor(self)
58 .cmp(buffer.fragment_id_for_anchor(other))
59 };
60
61 fragment_id_comparison
62 .then_with(|| self.offset.cmp(&other.offset))
63 .then_with(|| self.bias.cmp(&other.bias))
64 }
65
66 pub fn min<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
67 if self.cmp(other, buffer).is_le() {
68 self
69 } else {
70 other
71 }
72 }
73
74 pub fn max<'a>(&'a self, other: &'a Self, buffer: &BufferSnapshot) -> &'a Self {
75 if self.cmp(other, buffer).is_ge() {
76 self
77 } else {
78 other
79 }
80 }
81
82 pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
83 match bias {
84 Bias::Left => self.bias_left(buffer),
85 Bias::Right => self.bias_right(buffer),
86 }
87 }
88
89 pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
90 match self.bias {
91 Bias::Left => *self,
92 Bias::Right => buffer.anchor_before(self),
93 }
94 }
95
96 pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
97 match self.bias {
98 Bias::Left => buffer.anchor_after(self),
99 Bias::Right => *self,
100 }
101 }
102
103 pub fn summary<D>(&self, content: &BufferSnapshot) -> D
104 where
105 D: TextDimension,
106 {
107 content.summary_for_anchor(self)
108 }
109
110 /// Returns true when the [`Anchor`] is located inside a visible fragment.
111 pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
112 if *self == Anchor::MIN || *self == Anchor::MAX {
113 true
114 } else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
115 false
116 } else {
117 let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
118 return false;
119 };
120 let (.., item) = buffer
121 .fragments
122 .find::<Dimensions<Option<&Locator>, usize>, _>(
123 &None,
124 &Some(fragment_id),
125 Bias::Left,
126 );
127 item.is_some_and(|fragment| fragment.visible)
128 }
129 }
130}
131
132pub trait OffsetRangeExt {
133 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
134 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
135 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
136}
137
138impl<T> OffsetRangeExt for Range<T>
139where
140 T: ToOffset,
141{
142 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
143 self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
144 }
145
146 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
147 self.start.to_offset(snapshot).to_point(snapshot)
148 ..self.end.to_offset(snapshot).to_point(snapshot)
149 }
150
151 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
152 self.start.to_offset(snapshot).to_point_utf16(snapshot)
153 ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
154 }
155}
156
157pub trait AnchorRangeExt {
158 fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
159 fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
160}
161
162impl AnchorRangeExt for Range<Anchor> {
163 fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
164 match self.start.cmp(&other.start, buffer) {
165 Ordering::Equal => other.end.cmp(&self.end, buffer),
166 ord => ord,
167 }
168 }
169
170 fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
171 self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
172 }
173}