Skip to main content

typ_buffer/
selection.rs

1//! Cursors and selections.
2//!
3//! There is no single-cursor type. A caret is an empty selection, and the
4//! editor always holds a `Selections` — with one entry in the common case.
5//! Adding multi-cursor later would mean rewriting every editing path twice:
6//! once to add the concept, once to undo what the single-cursor assumption
7//! baked in.
8
9use crate::position::Position;
10
11/// A range of text with a fixed `anchor` and a moving `head`.
12///
13/// The head is where the cursor is drawn and where typing happens. Extending
14/// moves the head and leaves the anchor, which is what makes shift+arrow grow
15/// and shrink from the end the user expects.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct Selection {
18    pub anchor: Position,
19    pub head: Position,
20}
21
22impl Selection {
23    pub fn caret(at: Position) -> Self {
24        Self {
25            anchor: at,
26            head: at,
27        }
28    }
29
30    pub fn is_empty(&self) -> bool {
31        self.anchor == self.head
32    }
33
34    /// The endpoints in document order, regardless of which way it was made.
35    pub fn range(&self) -> (Position, Position) {
36        if self.anchor <= self.head {
37            (self.anchor, self.head)
38        } else {
39            (self.head, self.anchor)
40        }
41    }
42
43    /// Half-open: the start is inside, the end is not.
44    ///
45    /// That is what makes two selections which merely touch — one ending where
46    /// the next begins — stay separate instead of merging.
47    pub fn contains(&self, pos: Position) -> bool {
48        let (start, end) = self.range();
49        pos >= start && pos < end
50    }
51}
52
53impl Default for Selection {
54    fn default() -> Self {
55        Self::caret(Position::default())
56    }
57}
58
59/// A non-empty, document-ordered, non-overlapping set of selections.
60///
61/// Every mutating method ends by restoring those invariants, so no editing
62/// code has to defend against an out-of-order or overlapping set.
63#[derive(Debug, Clone)]
64pub struct Selections {
65    list: Vec<Selection>,
66    /// Index into `list`, retargeted after each sort so the selection the user
67    /// is steering stays the one they added.
68    primary: usize,
69}
70
71impl Default for Selections {
72    fn default() -> Self {
73        Self {
74            list: vec![Selection::default()],
75            primary: 0,
76        }
77    }
78}
79
80impl Selections {
81    pub fn single(selection: Selection) -> Self {
82        Self {
83            list: vec![selection],
84            primary: 0,
85        }
86    }
87
88    /// Always at least 1 — the type's invariant.
89    ///
90    /// No `is_empty` to pair with it: it could only ever return false, and a
91    /// method that is a constant invites callers to branch on something that
92    /// never varies. The invariant is the API.
93    #[allow(clippy::len_without_is_empty)]
94    pub fn len(&self) -> usize {
95        self.list.len()
96    }
97
98    pub fn primary(&self) -> Selection {
99        self.list[self.primary]
100    }
101
102    pub fn iter(&self) -> impl Iterator<Item = &Selection> {
103        self.list.iter()
104    }
105
106    /// Replace everything with one selection.
107    pub fn set_single(&mut self, selection: Selection) {
108        self.list = vec![selection];
109        self.primary = 0;
110    }
111
112    /// Add a selection and make it primary.
113    pub fn push(&mut self, selection: Selection) {
114        self.list.push(selection);
115        self.primary = self.list.len() - 1;
116        self.normalize();
117    }
118
119    /// Rewrite every selection, then restore the invariants.
120    pub fn map_in_place(&mut self, mut f: impl FnMut(Selection) -> Selection) {
121        for selection in &mut self.list {
122            *selection = f(*selection);
123        }
124        self.normalize();
125    }
126
127    /// Drop every selection but the primary, and reduce it to its head.
128    pub fn collapse_to_heads(&mut self) {
129        let head = self.primary().head;
130        self.set_single(Selection::caret(head));
131    }
132
133    fn normalize(&mut self) {
134        let primary = self.list[self.primary];
135        self.list.sort_by_key(|s| s.range());
136
137        let mut merged: Vec<Selection> = Vec::with_capacity(self.list.len());
138        for selection in self.list.drain(..) {
139            match merged.last_mut() {
140                Some(previous) if overlaps(*previous, selection) => {
141                    *previous = union(*previous, selection);
142                }
143                _ => merged.push(selection),
144            }
145        }
146        self.list = merged;
147
148        // The primary may have been merged into a larger selection, so look for
149        // whichever one now covers where it was rather than trusting an index.
150        self.primary = self
151            .list
152            .iter()
153            .position(|s| *s == primary || covers(*s, primary))
154            .unwrap_or(0);
155    }
156}
157
158fn overlaps(a: Selection, b: Selection) -> bool {
159    let (_, a_end) = a.range();
160    let (b_start, _) = b.range();
161    if a_end > b_start {
162        // Strictly greater, so selections that only touch stay separate — the
163        // same rule as `Selection::contains` being half-open.
164        return true;
165    }
166    // Two carets at the same position are one cursor, not two. Half-open
167    // ranges alone would keep them apart, because an empty range never
168    // strictly contains anything — and the consequence is typing inserting
169    // twice at the same place.
170    a.is_empty() && b.is_empty() && a_end == b_start
171}
172
173fn union(a: Selection, b: Selection) -> Selection {
174    let (a_start, a_end) = a.range();
175    let (b_start, b_end) = b.range();
176    Selection {
177        anchor: a_start.min(b_start),
178        head: a_end.max(b_end),
179    }
180}
181
182fn covers(outer: Selection, inner: Selection) -> bool {
183    let (o_start, o_end) = outer.range();
184    let (i_start, i_end) = inner.range();
185    o_start <= i_start && i_end <= o_end
186}