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 fits(&self, doc: &Document) -> bool {
134 let ok = |p: ByteOffset| p <= doc.len() && doc.is_char_boundary(p);
135 !self.ranges.is_empty()
136 && self.primary < self.ranges.len()
137 && self.ranges.iter().all(|s| ok(s.anchor) && ok(s.head))
138 && self.ranges.windows(2).all(|w| w[0].end() < w[1].start())
139 && self
140 .rect
141 .as_ref()
142 .is_none_or(|r| r.start_line.max(r.end_line) < doc.line_count())
143 }
144
145 pub fn primary(&self) -> &Selection {
146 &self.ranges[self.primary]
147 }
148
149 pub fn primary_index(&self) -> usize {
150 self.primary
151 }
152
153 pub fn len(&self) -> usize {
154 self.ranges.len()
155 }
156
157 pub fn is_empty(&self) -> bool {
158 false
159 }
160
161 pub fn rect(&self) -> Option<&Rectangle> {
162 self.rect.as_ref()
163 }
164
165 pub fn with_rect(mut self, rect: Option<Rectangle>) -> Self {
166 self.rect = rect;
167 self
168 }
169
170 pub fn map(&self, mut f: impl FnMut(&Selection) -> Selection) -> Self {
172 Self::new(self.ranges.iter().map(&mut f).collect(), self.primary)
173 }
174
175 fn normalize(&mut self) {
179 let primary = self.primary;
180 let mut indexed: Vec<(usize, Selection)> = std::mem::take(&mut self.ranges)
181 .into_iter()
182 .enumerate()
183 .collect();
184 indexed.sort_by_key(|(_, s)| (s.start(), s.end()));
185
186 let mut out: Vec<Selection> = Vec::with_capacity(indexed.len());
187 let mut new_primary = 0;
188 let mut leader: Option<(usize, Selection)> = None;
190 for (i, sel) in indexed {
191 match out.last_mut() {
192 Some(last) if sel.start() <= last.end() => {
193 let (start, end) = (last.start(), last.end().max(sel.end()));
194 let (lead_idx, lead) = match leader.take() {
195 Some(l) if l.0 == primary || i != primary => l,
196 _ => (i, sel),
197 };
198 *last = if lead.is_backward() {
199 Selection {
200 anchor: end,
201 head: start,
202 goal: lead.goal,
203 }
204 } else {
205 Selection {
206 anchor: start,
207 head: end,
208 goal: lead.goal,
209 }
210 };
211 if i == primary {
212 new_primary = out.len() - 1;
213 }
214 leader = Some((lead_idx, lead));
215 }
216 _ => {
217 if i == primary {
218 new_primary = out.len();
219 }
220 leader = Some((i, sel.clone()));
221 out.push(sel);
222 }
223 }
224 }
225 self.ranges = out;
226 self.primary = new_primary;
227 }
228
229 pub fn validate(&self, doc: &Document) -> Result<(), String> {
231 if self.ranges.is_empty() || self.primary >= self.ranges.len() {
232 return Err(format!("bad primary/len: {self:?}"));
233 }
234 for s in &self.ranges {
235 for p in [s.anchor, s.head] {
236 if p > doc.len() || !doc.is_char_boundary(p) {
237 return Err(format!("{p:?} out of range or not a char boundary"));
238 }
239 }
240 }
241 for w in self.ranges.windows(2) {
242 if w[0].end() >= w[1].start() {
243 return Err(format!(
244 "unsorted, overlapping or touching: {:?}",
245 self.ranges
246 ));
247 }
248 }
249 Ok(())
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 fn sel(a: usize, h: usize) -> Selection {
258 Selection::new(ByteOffset(a), ByteOffset(h))
259 }
260
261 #[test]
262 fn normalize_sorts_and_merges() {
263 let set = SelectionSet::new(vec![sel(10, 12), sel(0, 2), sel(5, 5), sel(5, 5)], 0);
264 assert_eq!(set.ranges(), &[sel(0, 2), sel(5, 5), sel(10, 12)]);
265 assert_eq!(set.primary(), &sel(10, 12));
266 }
267
268 #[test]
269 fn touching_selections_merge_keeping_primary_direction() {
270 let set = SelectionSet::new(vec![sel(0, 3), sel(6, 3)], 1);
271 assert_eq!(set.ranges(), &[sel(6, 0)]);
272 assert_eq!(set.primary_index(), 0);
273
274 let set = SelectionSet::new(vec![sel(0, 3), sel(6, 3), sel(9, 9)], 2);
275 assert_eq!(set.ranges(), &[sel(0, 6), sel(9, 9)]);
276 assert_eq!(set.primary_index(), 1);
277 }
278
279 #[test]
280 fn caret_inside_selection_merges() {
281 let set = SelectionSet::new(vec![sel(2, 8), sel(4, 4)], 1);
282 assert_eq!(set.ranges(), &[sel(2, 8)]);
283 assert_eq!(set.primary_index(), 0);
284 }
285}