escriba_core/
selection.rs1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::id::CaretId;
5use crate::position::Position;
6use crate::range::Range;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
11pub struct Cursor {
12 pub id: CaretId,
13 pub anchor: Position,
14 pub head: Position,
15}
16
17impl Cursor {
18 #[must_use]
19 pub const fn new(id: CaretId, anchor: Position, head: Position) -> Self {
20 Self { id, anchor, head }
21 }
22
23 #[must_use]
24 pub fn at(id: CaretId, p: Position) -> Self {
25 Self {
26 id,
27 anchor: p,
28 head: p,
29 }
30 }
31
32 #[must_use]
34 pub fn range(self) -> Range {
35 Range::new(self.anchor, self.head).normalized()
36 }
37
38 #[must_use]
39 pub const fn is_caret(self) -> bool {
40 self.anchor.line == self.head.line && self.anchor.column == self.head.column
43 }
44
45 #[must_use]
47 pub const fn extend_to(self, p: Position) -> Self {
48 Self {
49 id: self.id,
50 anchor: self.anchor,
51 head: p,
52 }
53 }
54
55 #[must_use]
57 pub const fn collapse(self) -> Self {
58 Self {
59 id: self.id,
60 anchor: self.head,
61 head: self.head,
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
69pub struct Selection {
70 carets: Vec<Cursor>,
71 primary: usize,
72}
73
74impl Selection {
75 #[must_use]
76 pub fn single(cursor: Cursor) -> Self {
77 Self {
78 carets: vec![cursor],
79 primary: 0,
80 }
81 }
82
83 #[must_use]
84 pub fn carets(&self) -> &[Cursor] {
85 &self.carets
86 }
87
88 #[must_use]
89 pub fn primary(&self) -> &Cursor {
90 &self.carets[self.primary]
91 }
92
93 pub fn add(&mut self, c: Cursor) {
94 self.carets.push(c);
95 }
96
97 pub fn map_primary(&mut self, f: impl FnOnce(Cursor) -> Cursor) {
98 let idx = self.primary;
99 self.carets[idx] = f(self.carets[idx]);
100 }
101
102 pub fn map_all(&mut self, mut f: impl FnMut(Cursor) -> Cursor) {
103 for c in &mut self.carets {
104 *c = f(*c);
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn caret_is_empty_range() {
115 let c = Cursor::at(CaretId(0), Position::new(1, 3));
116 assert!(c.is_caret());
117 assert!(c.range().is_empty());
118 }
119
120 #[test]
121 fn extend_grows_but_anchor_stays() {
122 let c = Cursor::at(CaretId(0), Position::new(0, 0));
123 let grown = c.extend_to(Position::new(0, 5));
124 assert_eq!(grown.anchor, Position::new(0, 0));
125 assert_eq!(grown.head, Position::new(0, 5));
126 }
127
128 #[test]
129 fn collapse_makes_caret() {
130 let c = Cursor::new(CaretId(0), Position::new(0, 0), Position::new(0, 5));
131 let collapsed = c.collapse();
132 assert!(collapsed.is_caret());
133 assert_eq!(collapsed.head, Position::new(0, 5));
134 }
135
136 #[test]
137 fn primary_is_first_by_default() {
138 let s = Selection::single(Cursor::at(CaretId(0), Position::new(3, 2)));
139 assert_eq!(s.primary().head, Position::new(3, 2));
140 }
141}