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;
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.fragments.cursor::<(Option<&Locator>, usize)>(&None);
106 fragment_cursor.seek(&Some(fragment_id), Bias::Left);
107 fragment_cursor
108 .item()
109 .map_or(false, |fragment| fragment.visible)
110 }
111 }
112}
113
114pub trait OffsetRangeExt {
115 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
116 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
117 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
118}
119
120impl<T> OffsetRangeExt for Range<T>
121where
122 T: ToOffset,
123{
124 fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
125 self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
126 }
127
128 fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
129 self.start.to_offset(snapshot).to_point(snapshot)
130 ..self.end.to_offset(snapshot).to_point(snapshot)
131 }
132
133 fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
134 self.start.to_offset(snapshot).to_point_utf16(snapshot)
135 ..self.end.to_offset(snapshot).to_point_utf16(snapshot)
136 }
137}
138
139pub trait AnchorRangeExt {
140 fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering;
141 fn overlaps(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> bool;
142}
143
144impl AnchorRangeExt for Range<Anchor> {
145 fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Ordering {
146 match self.start.cmp(&other.start, buffer) {
147 Ordering::Equal => other.end.cmp(&self.end, buffer),
148 ord => ord,
149 }
150 }
151
152 fn overlaps(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> bool {
153 self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt()
154 }
155}