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, Debug, Hash, Default)]
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 Anchor {
20 pub const MIN: Self = Self {
21 timestamp: clock::Lamport::MIN,
22 offset: usize::MIN,
23 bias: Bias::Left,
24 buffer_id: None,
25 };
26
27 pub const MAX: Self = Self {
28 timestamp: clock::Lamport::MAX,
29 offset: usize::MAX,
30 bias: Bias::Right,
31 buffer_id: None,
32 };
33
34 pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
35 let fragment_id_comparison = if self.timestamp == other.timestamp {
36 Ordering::Equal
37 } else {
38 buffer
39 .fragment_id_for_anchor(self)
40 .cmp(buffer.fragment_id_for_anchor(other))
41 };
42
43 fragment_id_comparison
44 .then_with(|| self.offset.cmp(&other.offset))
45 .then_with(|| self.bias.cmp(&other.bias))
46 }
47
48 pub fn min(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
49 if self.cmp(other, buffer).is_le() {
50 *self
51 } else {
52 *other
53 }
54 }
55
56 pub fn max(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
57 if self.cmp(other, buffer).is_ge() {
58 *self
59 } else {
60 *other
61 }
62 }
63
64 pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
65 if bias == Bias::Left {
66 self.bias_left(buffer)
67 } else {
68 self.bias_right(buffer)
69 }
70 }
71
72 pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
73 if self.bias == Bias::Left {
74 *self
75 } else {
76 buffer.anchor_before(self)
77 }
78 }
79
80 pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
81 if self.bias == Bias::Right {
82 *self
83 } else {
84 buffer.anchor_after(self)
85 }
86 }
87
88 pub fn summary<D>(&self, content: &BufferSnapshot) -> D
89 where
90 D: TextDimension,
91 {
92 content.summary_for_anchor(self)
93 }
94
95 /// Returns true when the [`Anchor`] is located inside a visible fragment.
96 pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
97 if *self == Anchor::MIN || *self == Anchor::MAX {
98 true
99 } else if self.buffer_id != Some(buffer.remote_id) {
100 false
101 } else {
102 let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {
103 return false;
104 };
105 let mut fragment_cursor = buffer
106 .fragments
107 .cursor::<Dimensions<Option<&Locator>, usize>>(&None);
108 fragment_cursor.seek(&Some(fragment_id), Bias::Left);
109 fragment_cursor
110 .item()
111 .is_some_and(|fragment| fragment.visible)
112 }
113 }
114}
115
116pub trait OffsetRangeExt {
117 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
118 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
119 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
120}
121
122impl<T> OffsetRangeExt for Range<T>
123where
124 T: ToOffset,
125{
126 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
127 self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
128 }
129
130 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
131 self.start.to_offset(snapshot).to_point(snapshot)
132 ..self.end.to_offset(snapshot).to_point(snapshot)
133 }
134
135 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
136 self.start.to_offset(snapshot).to_point_utf16(snapshot)
137 ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
138 }
139}
140
141pub trait AnchorRangeExt {
142 fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
143 fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
144}
145
146impl AnchorRangeExt for Range<Anchor> {
147 fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
148 match self.start.cmp(&other.start, buffer) {
149 Ordering::Equal => other.end.cmp(&self.end, buffer),
150 ord => ord,
151 }
152 }
153
154 fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
155 self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
156 }
157}