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