termesh_editor/
selection.rs1use crate::change::{Assoc, ChangeSet};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct Range {
16 pub anchor: usize,
17 pub head: usize,
18}
19
20impl Range {
21 pub fn new(anchor: usize, head: usize) -> Self {
22 Self { anchor, head }
23 }
24
25 pub fn point(at: usize) -> Self {
27 Self { anchor: at, head: at }
28 }
29
30 pub fn is_empty(&self) -> bool {
31 self.anchor == self.head
32 }
33
34 pub fn start(&self) -> usize {
35 self.anchor.min(self.head)
36 }
37
38 pub fn end(&self) -> usize {
39 self.anchor.max(self.head)
40 }
41
42 pub fn len(&self) -> usize {
43 self.end() - self.start()
44 }
45
46 pub fn map(&self, changes: &ChangeSet) -> Self {
51 Self {
52 anchor: changes.map_pos(self.anchor, Assoc::After),
53 head: changes.map_pos(self.head, Assoc::After),
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Selection {
61 ranges: Vec<Range>,
62 primary: usize,
63}
64
65impl Selection {
66 pub fn single(range: Range) -> Self {
67 Self { ranges: vec![range], primary: 0 }
68 }
69
70 pub fn point(at: usize) -> Self {
71 Self::single(Range::point(at))
72 }
73
74 pub fn ranges(&self) -> &[Range] {
75 &self.ranges
76 }
77
78 pub fn primary(&self) -> Range {
80 self.ranges[self.primary]
81 }
82
83 pub fn map(&self, changes: &ChangeSet) -> Self {
85 Self { ranges: self.ranges.iter().map(|r| r.map(changes)).collect(), primary: self.primary }
86 }
87}
88
89impl Default for Selection {
90 fn default() -> Self {
91 Self::point(0)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn a_cursor_is_an_empty_range() {
101 let c = Range::point(4);
102 assert!(c.is_empty());
103 assert_eq!(c.len(), 0);
104 assert_eq!((c.start(), c.end()), (4, 4));
105 }
106
107 #[test]
108 fn a_backwards_range_keeps_its_direction_but_orders_its_bounds() {
109 let r = Range::new(9, 3);
110 assert_eq!((r.start(), r.end()), (3, 9));
111 assert_eq!(r.len(), 6);
112 assert_eq!(r.head, 3, "direction is information; it must survive");
113 }
114
115 #[test]
116 fn an_edit_before_the_cursor_pushes_it_along() {
117 let cursor = Range::point(10);
118 let insert = ChangeSet::replace(20, 0, 0, "abc");
119 assert_eq!(cursor.map(&insert), Range::point(13));
120 }
121
122 #[test]
123 fn an_edit_after_the_cursor_leaves_it_alone() {
124 let cursor = Range::point(4);
125 let insert = ChangeSet::replace(20, 10, 10, "abc");
126 assert_eq!(cursor.map(&insert), Range::point(4));
127 }
128
129 #[test]
130 fn typing_at_the_cursor_carries_it_forward() {
131 let cursor = Range::point(5);
134 let typed = ChangeSet::replace(10, 5, 5, "x");
135 assert_eq!(cursor.map(&typed), Range::point(6));
136 }
137
138 #[test]
139 fn a_selection_spanning_a_deletion_collapses_onto_it() {
140 let selection = Range::new(3, 9);
141 let deleted = ChangeSet::replace(20, 2, 12, "");
142 assert_eq!(selection.map(&deleted), Range::new(2, 2));
143 }
144
145 #[test]
146 fn every_range_in_a_selection_maps() {
147 let sel = Selection { ranges: vec![Range::point(1), Range::point(8)], primary: 1 };
148 let mapped = sel.map(&ChangeSet::replace(20, 0, 0, "xx"));
149 assert_eq!(mapped.ranges(), [Range::point(3), Range::point(10)]);
150 assert_eq!(mapped.primary(), Range::point(10), "the primary index survives");
151 }
152
153 #[test]
154 fn a_default_selection_is_a_cursor_at_the_start() {
155 assert_eq!(Selection::default().primary(), Range::point(0));
156 }
157}