1use sum_tree::Bias;
2
3use crate::{rope::TextDimension, Anchor, Snapshot};
4
5use super::{Buffer, Point, ToOffset, ToPoint};
6use std::{cmp::Ordering, ops::Range, sync::Arc};
7
8pub type SelectionSetId = clock::Lamport;
9pub type SelectionsVersion = usize;
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
12pub enum SelectionGoal {
13 None,
14 Column(u32),
15 ColumnRange { start: u32, end: u32 },
16}
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct Selection<T> {
20 pub id: usize,
21 pub start: T,
22 pub end: T,
23 pub reversed: bool,
24 pub goal: SelectionGoal,
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct SelectionSet {
29 pub id: SelectionSetId,
30 pub active: bool,
31 pub selections: Arc<[Selection<Anchor>]>,
32}
33
34#[derive(Debug, Eq, PartialEq)]
35pub struct SelectionState {
36 pub id: usize,
37 pub reversed: bool,
38 pub goal: SelectionGoal,
39}
40
41impl<T: Clone> Selection<T> {
42 pub fn head(&self) -> T {
43 if self.reversed {
44 self.start.clone()
45 } else {
46 self.end.clone()
47 }
48 }
49
50 pub fn tail(&self) -> T {
51 if self.reversed {
52 self.end.clone()
53 } else {
54 self.start.clone()
55 }
56 }
57}
58
59impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
60 pub fn is_empty(&self) -> bool {
61 self.start == self.end
62 }
63
64 pub fn set_head(&mut self, head: T) {
65 if head.cmp(&self.tail()) < Ordering::Equal {
66 if !self.reversed {
67 self.end = self.start;
68 self.reversed = true;
69 }
70 self.start = head;
71 } else {
72 if self.reversed {
73 self.start = self.end;
74 self.reversed = false;
75 }
76 self.end = head;
77 }
78 }
79
80 pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
81 let start = self.start.to_point(buffer);
82 let end = self.end.to_point(buffer);
83 if self.reversed {
84 end..start
85 } else {
86 start..end
87 }
88 }
89
90 pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
91 let start = self.start.to_offset(buffer);
92 let end = self.end.to_offset(buffer);
93 if self.reversed {
94 end..start
95 } else {
96 start..end
97 }
98 }
99}
100
101impl Selection<Anchor> {
102 pub fn resolve<'a, D: 'a + TextDimension<'a>>(
103 &'a self,
104 snapshot: &'a Snapshot,
105 ) -> Selection<D> {
106 Selection {
107 id: self.id,
108 start: snapshot.summary_for_anchor(&self.start),
109 end: snapshot.summary_for_anchor(&self.end),
110 reversed: self.reversed,
111 goal: self.goal,
112 }
113 }
114}
115
116impl SelectionSet {
117 pub fn len(&self) -> usize {
118 self.selections.len()
119 }
120
121 pub fn selections<'a, D>(
122 &'a self,
123 snapshot: &'a Snapshot,
124 ) -> impl 'a + Iterator<Item = Selection<D>>
125 where
126 D: 'a + TextDimension<'a>,
127 {
128 let anchors = self
129 .selections
130 .iter()
131 .flat_map(|selection| [&selection.start, &selection.end].into_iter());
132 let mut positions = snapshot.summaries_for_anchors::<D, _>(anchors);
133 self.selections.iter().map(move |selection| Selection {
134 start: positions.next().unwrap(),
135 end: positions.next().unwrap(),
136 goal: selection.goal,
137 reversed: selection.reversed,
138 id: selection.id,
139 })
140 }
141
142 pub fn intersecting_selections<'a, D, I>(
143 &'a self,
144 range: Range<(I, Bias)>,
145 snapshot: &'a Snapshot,
146 ) -> impl 'a + Iterator<Item = Selection<D>>
147 where
148 D: 'a + TextDimension<'a>,
149 I: 'a + ToOffset,
150 {
151 let start = snapshot.anchor_at(range.start.0, range.start.1);
152 let end = snapshot.anchor_at(range.end.0, range.end.1);
153 let start_ix = match self
154 .selections
155 .binary_search_by(|probe| probe.end.cmp(&start, snapshot).unwrap())
156 {
157 Ok(ix) | Err(ix) => ix,
158 };
159 let end_ix = match self
160 .selections
161 .binary_search_by(|probe| probe.start.cmp(&end, snapshot).unwrap())
162 {
163 Ok(ix) | Err(ix) => ix,
164 };
165 self.selections[start_ix..end_ix]
166 .iter()
167 .map(|s| s.resolve(snapshot))
168 }
169
170 pub fn oldest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
171 where
172 D: 'a + TextDimension<'a>,
173 {
174 self.selections
175 .iter()
176 .min_by_key(|s| s.id)
177 .map(|s| s.resolve(snapshot))
178 }
179
180 pub fn newest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
181 where
182 D: 'a + TextDimension<'a>,
183 {
184 self.selections
185 .iter()
186 .max_by_key(|s| s.id)
187 .map(|s| s.resolve(snapshot))
188 }
189}