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