1use std::ops::Range;
5
6use serde::{Deserialize, Serialize};
7
8use crate::position::{ByteOffset, VisualCol};
9use crate::text::Document;
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Selection {
13 pub anchor: ByteOffset,
14 pub head: ByteOffset,
15 pub goal: Option<VisualCol>,
17}
18
19impl Selection {
20 pub fn caret(pos: ByteOffset) -> Self {
21 Self::new(pos, pos)
22 }
23
24 pub fn new(anchor: ByteOffset, head: ByteOffset) -> Self {
25 Self {
26 anchor,
27 head,
28 goal: None,
29 }
30 }
31
32 pub fn with_goal(mut self, goal: Option<VisualCol>) -> Self {
33 self.goal = goal;
34 self
35 }
36
37 pub fn start(&self) -> ByteOffset {
38 self.anchor.min(self.head)
39 }
40
41 pub fn end(&self) -> ByteOffset {
42 self.anchor.max(self.head)
43 }
44
45 pub fn range(&self) -> Range<ByteOffset> {
46 self.start()..self.end()
47 }
48
49 pub fn is_empty(&self) -> bool {
50 self.anchor == self.head
51 }
52
53 fn is_backward(&self) -> bool {
54 self.head < self.anchor
55 }
56}
57
58#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63pub struct Rectangle {
64 pub start_line: usize,
65 pub end_line: usize,
66 pub start_col: VisualCol,
67 pub end_col: VisualCol,
68}
69
70impl Rectangle {
71 pub fn top(&self) -> usize {
72 self.start_line.min(self.end_line)
73 }
74
75 pub fn bottom(&self) -> usize {
76 self.start_line.max(self.end_line)
77 }
78
79 pub fn left(&self) -> VisualCol {
80 self.start_col.min(self.end_col)
81 }
82
83 pub fn right(&self) -> VisualCol {
84 self.start_col.max(self.end_col)
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
90pub struct SelectionSet {
91 ranges: Vec<Selection>,
92 primary: usize,
93 rect: Option<Rectangle>,
94}
95
96impl SelectionSet {
97 pub fn single(sel: Selection) -> Self {
98 Self {
99 ranges: vec![sel],
100 primary: 0,
101 rect: None,
102 }
103 }
104
105 pub fn caret(pos: ByteOffset) -> Self {
106 Self::single(Selection::caret(pos))
107 }
108
109 pub fn new(ranges: Vec<Selection>, primary: usize) -> Self {
112 if ranges.is_empty() {
113 return Self::caret(ByteOffset(0));
114 }
115 let primary = primary.min(ranges.len() - 1);
116 let mut set = Self {
117 ranges,
118 primary,
119 rect: None,
120 };
121 set.normalize();
122 set
123 }
124
125 pub fn ranges(&self) -> &[Selection] {
126 &self.ranges
127 }
128
129 pub fn primary(&self) -> &Selection {
130 &self.ranges[self.primary]
131 }
132
133 pub fn primary_index(&self) -> usize {
134 self.primary
135 }
136
137 pub fn len(&self) -> usize {
138 self.ranges.len()
139 }
140
141 pub fn is_empty(&self) -> bool {
142 false
143 }
144
145 pub fn rect(&self) -> Option<&Rectangle> {
146 self.rect.as_ref()
147 }
148
149 pub fn with_rect(mut self, rect: Option<Rectangle>) -> Self {
150 self.rect = rect;
151 self
152 }
153
154 pub fn map(&self, mut f: impl FnMut(&Selection) -> Selection) -> Self {
156 Self::new(self.ranges.iter().map(&mut f).collect(), self.primary)
157 }
158
159 fn normalize(&mut self) {
163 let primary = self.primary;
164 let mut indexed: Vec<(usize, Selection)> = std::mem::take(&mut self.ranges)
165 .into_iter()
166 .enumerate()
167 .collect();
168 indexed.sort_by_key(|(_, s)| (s.start(), s.end()));
169
170 let mut out: Vec<Selection> = Vec::with_capacity(indexed.len());
171 let mut new_primary = 0;
172 let mut leader: Option<(usize, Selection)> = None;
174 for (i, sel) in indexed {
175 match out.last_mut() {
176 Some(last) if sel.start() <= last.end() => {
177 let (start, end) = (last.start(), last.end().max(sel.end()));
178 let (lead_idx, lead) = match leader.take() {
179 Some(l) if l.0 == primary || i != primary => l,
180 _ => (i, sel),
181 };
182 *last = if lead.is_backward() {
183 Selection {
184 anchor: end,
185 head: start,
186 goal: lead.goal,
187 }
188 } else {
189 Selection {
190 anchor: start,
191 head: end,
192 goal: lead.goal,
193 }
194 };
195 if i == primary {
196 new_primary = out.len() - 1;
197 }
198 leader = Some((lead_idx, lead));
199 }
200 _ => {
201 if i == primary {
202 new_primary = out.len();
203 }
204 leader = Some((i, sel.clone()));
205 out.push(sel);
206 }
207 }
208 }
209 self.ranges = out;
210 self.primary = new_primary;
211 }
212
213 pub fn validate(&self, doc: &Document) -> Result<(), String> {
215 if self.ranges.is_empty() || self.primary >= self.ranges.len() {
216 return Err(format!("bad primary/len: {self:?}"));
217 }
218 for s in &self.ranges {
219 for p in [s.anchor, s.head] {
220 if p > doc.len() || !doc.is_char_boundary(p) {
221 return Err(format!("{p:?} out of range or not a char boundary"));
222 }
223 }
224 }
225 for w in self.ranges.windows(2) {
226 if w[0].end() >= w[1].start() {
227 return Err(format!(
228 "unsorted, overlapping or touching: {:?}",
229 self.ranges
230 ));
231 }
232 }
233 Ok(())
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 fn sel(a: usize, h: usize) -> Selection {
242 Selection::new(ByteOffset(a), ByteOffset(h))
243 }
244
245 #[test]
246 fn normalize_sorts_and_merges() {
247 let set = SelectionSet::new(vec![sel(10, 12), sel(0, 2), sel(5, 5), sel(5, 5)], 0);
248 assert_eq!(set.ranges(), &[sel(0, 2), sel(5, 5), sel(10, 12)]);
249 assert_eq!(set.primary(), &sel(10, 12));
250 }
251
252 #[test]
253 fn touching_selections_merge_keeping_primary_direction() {
254 let set = SelectionSet::new(vec![sel(0, 3), sel(6, 3)], 1);
255 assert_eq!(set.ranges(), &[sel(6, 0)]);
256 assert_eq!(set.primary_index(), 0);
257
258 let set = SelectionSet::new(vec![sel(0, 3), sel(6, 3), sel(9, 9)], 2);
259 assert_eq!(set.ranges(), &[sel(0, 6), sel(9, 9)]);
260 assert_eq!(set.primary_index(), 1);
261 }
262
263 #[test]
264 fn caret_inside_selection_merges() {
265 let set = SelectionSet::new(vec![sel(2, 8), sel(4, 4)], 1);
266 assert_eq!(set.ranges(), &[sel(2, 8)]);
267 assert_eq!(set.primary_index(), 0);
268 }
269}