1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Selection {
10 pub anchor: usize,
11 pub head: usize,
12}
13
14impl Selection {
15 pub fn cursor(at: usize) -> Self {
16 Self {
17 anchor: at,
18 head: at,
19 }
20 }
21
22 pub fn collapsed(self) -> bool {
23 self.anchor == self.head
24 }
25
26 pub fn range(self) -> (usize, usize) {
28 (self.anchor.min(self.head), self.anchor.max(self.head))
29 }
30}
31
32#[derive(Debug, Clone)]
36pub struct SelectionSet {
37 primary: Selection,
38 extras: Vec<Selection>,
39}
40
41impl Default for SelectionSet {
42 fn default() -> Self {
43 Self {
44 primary: Selection::cursor(0),
45 extras: Vec::new(),
46 }
47 }
48}
49
50impl SelectionSet {
51 pub fn primary(&self) -> Selection {
52 self.primary
53 }
54
55 pub fn heads(&self) -> Vec<usize> {
57 std::iter::once(self.primary.head)
58 .chain(self.extras.iter().map(|s| s.head))
59 .collect()
60 }
61
62 pub fn extra_heads(&self) -> &[Selection] {
63 &self.extras
64 }
65
66 pub fn count(&self) -> usize {
67 1 + self.extras.len()
68 }
69
70 pub fn set_head(&mut self, head: usize) {
72 self.primary.head = head;
73 }
74
75 pub fn collapse_primary(&mut self, at: usize) {
77 self.primary = Selection::cursor(at);
78 }
79
80 pub fn stretch_primary(&mut self, anchor: usize, head: usize) {
82 self.primary = Selection { anchor, head };
83 }
84
85 pub fn toggle_extra(&mut self) {
87 if let Some(i) = self.extras.iter().position(|s| s.head == self.primary.head) {
88 self.extras.remove(i);
89 } else {
90 self.extras.push(self.primary);
91 }
92 self.normalize();
93 }
94
95 pub fn plant_extra(&mut self, at: usize) {
99 if at != self.primary.head && !self.extras.iter().any(|s| s.head == at) {
100 self.extras.push(Selection::cursor(at));
101 }
102 self.normalize();
103 }
104
105 pub fn normalize(&mut self) {
108 self.extras.sort_by_key(|s| s.head);
109 self.extras.dedup();
110 }
111
112 pub fn collapse_extras(&mut self) {
114 self.extras.clear();
115 }
116
117 pub fn set_extras(&mut self, heads: impl IntoIterator<Item = usize>) {
121 self.extras = heads.into_iter().map(Selection::cursor).collect();
122 self.normalize();
123 }
124
125 pub fn remap(&mut self, at: usize, delta: isize) {
128 let shift = |p: &mut usize| {
129 if *p >= at {
130 *p = (*p as isize + delta).max(0) as usize;
131 }
132 };
133 shift(&mut self.primary.head);
134 shift(&mut self.primary.anchor);
135 for s in &mut self.extras {
136 shift(&mut s.head);
137 shift(&mut s.anchor);
138 }
139 self.normalize();
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn invariants_hold() {
149 let mut s = SelectionSet::default();
150 s.set_head(5);
151 s.toggle_extra(); assert_eq!(s.count(), 2);
153 s.plant_extra(2);
154 s.plant_extra(2); assert_eq!(s.count(), 3);
156 assert_eq!(s.heads(), vec![5, 2, 5]);
157 s.toggle_extra(); assert_eq!(s.count(), 2);
159 assert_eq!(s.heads(), vec![5, 2]);
160 s.collapse_extras();
161 assert_eq!(s.count(), 1);
162 }
163
164 #[test]
165 fn remap_shifts_past_the_edit() {
166 let mut s = SelectionSet::default();
167 s.collapse_primary(10);
168 s.plant_extra(20);
169 s.remap(5, 3); assert_eq!(s.heads(), vec![13, 23]);
171 s.remap(5, -3);
172 assert_eq!(s.heads(), vec![10, 20]);
173 }
174}