makepad_code_editor/
selection.rs

1use {
2    crate::{
3        layout::Layout,
4        str::StrExt,
5        text::{Edit, Length, Position},
6    },
7    std::{ops, ops::Deref, slice::Iter},
8};
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, Hash, Eq)]
11pub struct Selection {
12    pub cursor: Cursor,
13    pub anchor: Position,
14}
15
16impl Selection {
17    pub fn is_empty(self) -> bool {
18        self.anchor == self.cursor.position
19    }
20
21    pub fn overlaps_with(self, other: Self) -> bool {
22        if self.is_empty() || other.is_empty() {
23            self.end() >= other.start()
24        } else {
25            self.end() > other.start()
26        }
27    }
28
29    pub fn start(self) -> Position {
30        self.cursor.position.min(self.anchor)
31    }
32
33    pub fn start_affinity(self) -> Affinity {
34        if self.anchor < self.cursor.position {
35            Affinity::After
36        } else {
37            self.cursor.affinity
38        }
39    }
40
41    pub fn end(self) -> Position {
42        self.cursor.position.max(self.anchor)
43    }
44
45    pub fn end_affinity(self) -> Affinity {
46        if self.cursor.position < self.anchor {
47            Affinity::Before
48        } else {
49            self.cursor.affinity
50        }
51    }
52
53    pub fn length(self) -> Length {
54        self.end() - self.start()
55    }
56
57    pub fn line_range(self) -> ops::Range<usize> {
58        if self.anchor <= self.cursor.position {
59            self.anchor.line_index..self.cursor.position.line_index + 1
60        } else {
61            self.cursor.position.line_index..if self.anchor.byte_index == 0 {
62                self.anchor.line_index
63            } else {
64                self.anchor.line_index + 1
65            }
66        }
67    }
68
69    pub fn update_cursor(self, f: impl FnOnce(Cursor) -> Cursor) -> Self {
70        Self {
71            cursor: f(self.cursor),
72            ..self
73        }
74    }
75
76    pub fn reset_anchor(self) -> Self {
77        Self {
78            anchor: self.cursor.position,
79            ..self
80        }
81    }
82
83    pub fn merge_with(self, other: Self) -> Option<Self> {
84        if self.overlaps_with(other) {
85            Some(if self.anchor <= self.cursor.position {
86                Selection {
87                    anchor: self.anchor,
88                    cursor: other.cursor,
89                }
90            } else {
91                Selection {
92                    anchor: other.anchor,
93                    cursor: self.cursor,
94                }
95            })
96        } else {
97            None
98        }
99    }
100
101    pub fn apply_edit(self, edit: &Edit) -> Self {
102        Self {
103            anchor: self.anchor.apply_edit(edit),
104            cursor: self.cursor.apply_edit(edit),
105            ..self
106        }
107    }
108}
109
110impl From<Cursor> for Selection {
111    fn from(cursor: Cursor) -> Self {
112        Self {
113            cursor,
114            anchor: cursor.position,
115        }
116    }
117}
118
119#[derive(Clone, Debug, Eq, Hash, PartialEq)]
120pub struct SelectionSet {
121    selections: Vec<Selection>,
122}
123
124impl SelectionSet {
125    pub fn new() -> Self {
126        Self::default()
127    }
128
129    pub fn as_selections(&self) -> &[Selection] {
130        &self.selections
131    }
132
133    pub fn update_selection(
134        &mut self,
135        index: usize,
136        f: impl FnOnce(Selection) -> Selection,
137    ) -> usize {
138        self.selections[index] = f(self.selections[index]);
139        self.remove_overlapping_selections(index)
140    }
141
142    pub fn update_all_selections(
143        &mut self,
144        retained_index: Option<usize>,
145        mut f: impl FnMut(Selection) -> Selection,
146    ) -> Option<usize> {
147        for selection in &mut self.selections {
148            *selection = f(*selection);
149        }
150        let mut index = retained_index;
151        let mut current_index = 0;
152        while current_index + 1 < self.selections.len() {
153            let next_index = current_index + 1;
154            let current_selection = self.selections[current_index];
155            let next_selection = self.selections[next_index];
156            assert!(current_selection.start() <= next_selection.start());
157            if let Some(merged_selection) = current_selection.merge_with(next_selection) {
158                self.selections[current_index] = merged_selection;
159                self.selections.remove(next_index);
160                if let Some(index) = &mut index {
161                    if next_index <= *index {
162                        *index -= 1;
163                    }
164                }
165            } else {
166                current_index += 1;
167            }
168        }
169        index
170    }
171
172    pub fn apply_edit(&mut self, edit: &Edit) {
173        for selection in &mut self.selections {
174            *selection = selection.apply_edit(edit);
175        }
176    }
177
178    pub fn add_selection(&mut self, selection: Selection) -> usize {
179        let index = match self
180            .selections
181            .binary_search_by_key(&selection.start(), |selection| selection.start())
182        {
183            Ok(index) => {
184                self.selections[index] = selection;
185                index
186            }
187            Err(index) => {
188                self.selections.insert(index, selection);
189                index
190            }
191        };
192        self.remove_overlapping_selections(index)
193    }
194
195    pub fn set_selection(&mut self, selection: Selection) {
196        self.selections.clear();
197        self.selections.push(selection);
198    }
199
200    fn remove_overlapping_selections(&mut self, index: usize) -> usize {
201        let mut index = index;
202        while index > 0 {
203            let prev_index = index - 1;
204            if !self.selections[prev_index].overlaps_with(self.selections[index]) {
205                break;
206            }
207            self.selections.remove(prev_index);
208            index -= 1;
209        }
210        while index + 1 < self.selections.len() {
211            let next_index = index + 1;
212            if !self.selections[index].overlaps_with(self.selections[next_index]) {
213                break;
214            }
215            self.selections.remove(next_index);
216        }
217        index
218    }
219}
220
221impl Default for SelectionSet {
222    fn default() -> Self {
223        Self {
224            selections: vec![Selection::default()],
225        }
226    }
227}
228
229impl Deref for SelectionSet {
230    type Target = [Selection];
231
232    fn deref(&self) -> &Self::Target {
233        &self.selections
234    }
235}
236
237impl<'a> IntoIterator for &'a SelectionSet {
238    type Item = &'a Selection;
239    type IntoIter = Iter<'a, Selection>;
240
241    fn into_iter(self) -> Self::IntoIter {
242        self.iter()
243    }
244}
245
246#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
247pub struct Cursor {
248    pub position: Position,
249    pub affinity: Affinity,
250    pub preferred_column_index: Option<usize>,
251}
252
253impl Cursor {
254    pub fn is_at_first_line(self) -> bool {
255        self.position.line_index == 0
256    }
257
258    pub fn is_at_last_line(self, line_count: usize) -> bool {
259        self.position.line_index == line_count - 1
260    }
261
262    pub fn is_at_start_of_line(self) -> bool {
263        self.position.byte_index == 0
264    }
265
266    pub fn is_at_end_of_line(self, lines: &[String]) -> bool {
267        self.position.byte_index == lines[self.position.line_index].len()
268    }
269
270    pub fn is_at_first_row_of_line(self, layout: &Layout<'_>) -> bool {
271        let (row, _) = layout
272            .line(self.position.line_index)
273            .logical_to_grid_position(self.position.byte_index, self.affinity);
274        row == 0
275    }
276
277    pub fn is_at_last_row_of_line(self, layout: &Layout<'_>) -> bool {
278        let line = layout.line(self.position.line_index);
279        let (row, _) = line.logical_to_grid_position(self.position.byte_index, self.affinity);
280        row == line.row_count() - 1
281    }
282
283    pub fn move_left(self, lines: &[String]) -> Self {
284        if !self.is_at_start_of_line() {
285            return self.move_to_prev_grapheme(lines);
286        }
287        if !self.is_at_first_line() {
288            return self.move_to_end_of_prev_line(lines);
289        }
290        self
291    }
292
293    pub fn move_right(self, lines: &[String]) -> Self {
294        if !self.is_at_end_of_line(lines) {
295            return self.move_to_next_grapheme(lines);
296        }
297        if !self.is_at_last_line(lines.len()) {
298            return self.move_to_start_of_next_line();
299        }
300        self
301    }
302
303    pub fn move_up(self, layout: &Layout<'_>) -> Self {
304        if !self.is_at_first_row_of_line(layout) {
305            return self.move_to_prev_row_of_line(layout);
306        }
307        if !self.is_at_first_line() {
308            return self.move_to_last_row_of_prev_line(layout);
309        }
310        self
311    }
312
313    pub fn move_down(self, layout: &Layout<'_>) -> Self {
314        if !self.is_at_last_row_of_line(layout) {
315            return self.move_to_next_row_of_line(layout);
316        }
317        if !self.is_at_last_line(layout.as_text().as_lines().len()) {
318            return self.move_to_first_row_of_next_line(layout);
319        }
320        self
321    }
322
323    pub fn move_to_prev_grapheme(self, lines: &[String]) -> Self {
324        Self {
325            position: Position {
326                line_index: self.position.line_index,
327                byte_index: lines[self.position.line_index][..self.position.byte_index]
328                    .grapheme_indices()
329                    .next_back()
330                    .map(|(index, _)| index)
331                    .unwrap(),
332            },
333            affinity: Affinity::After,
334            preferred_column_index: None,
335        }
336    }
337
338    pub fn move_to_next_grapheme(self, lines: &[String]) -> Self {
339        let line = &lines[self.position.line_index];
340        Self {
341            position: Position {
342                line_index: self.position.line_index,
343                byte_index: line[self.position.byte_index..]
344                    .grapheme_indices()
345                    .nth(1)
346                    .map(|(index, _)| self.position.byte_index + index)
347                    .unwrap_or(line.len()),
348            },
349            affinity: Affinity::Before,
350            preferred_column_index: None,
351        }
352    }
353
354    pub fn move_to_end_of_prev_line(self, lines: &[String]) -> Self {
355        let prev_line_index = self.position.line_index - 1;
356        Self {
357            position: Position {
358                line_index: prev_line_index,
359                byte_index: lines[prev_line_index].len(),
360            },
361            affinity: Affinity::After,
362            preferred_column_index: None,
363        }
364    }
365
366    pub fn move_to_start_of_next_line(self) -> Self {
367        Self {
368            position: Position {
369                line_index: self.position.line_index + 1,
370                byte_index: 0,
371            },
372            affinity: Affinity::Before,
373            preferred_column_index: None,
374        }
375    }
376
377    pub fn move_to_prev_row_of_line(self, layout: &Layout<'_>) -> Self {
378        let line = layout.line(self.position.line_index);
379        let (row_index, mut column_index) =
380            line.logical_to_grid_position(self.position.byte_index, self.affinity);
381        if let Some(preferred_column_index) = self.preferred_column_index {
382            column_index = preferred_column_index;
383        }
384        let (byte_index, affinity) = line.grid_to_logical_position(row_index - 1, column_index);
385        Self {
386            position: Position {
387                line_index: self.position.line_index,
388                byte_index,
389            },
390            affinity,
391            preferred_column_index: Some(column_index),
392        }
393    }
394
395    pub fn move_to_next_row_of_line(self, layout: &Layout<'_>) -> Self {
396        let line = layout.line(self.position.line_index);
397        let (row_index, mut column_index) =
398            line.logical_to_grid_position(self.position.byte_index, self.affinity);
399        if let Some(preferred_column_index) = self.preferred_column_index {
400            column_index = preferred_column_index;
401        }
402        let (byte, affinity) = line.grid_to_logical_position(row_index + 1, column_index);
403        Self {
404            position: Position {
405                line_index: self.position.line_index,
406                byte_index: byte,
407            },
408            affinity,
409            preferred_column_index: Some(column_index),
410        }
411    }
412
413    pub fn move_to_last_row_of_prev_line(self, layout: &Layout<'_>) -> Self {
414        let line = layout.line(self.position.line_index);
415        let (_, mut column_index) =
416            line.logical_to_grid_position(self.position.byte_index, self.affinity);
417        if let Some(preferred_column_index) = self.preferred_column_index {
418            column_index = preferred_column_index;
419        }
420        let prev_line = layout.line(self.position.line_index - 1);
421        let (byte_index, affinity) =
422            prev_line.grid_to_logical_position(prev_line.row_count() - 1, column_index);
423        Self {
424            position: Position {
425                line_index: self.position.line_index - 1,
426                byte_index,
427            },
428            affinity,
429            preferred_column_index: Some(column_index),
430        }
431    }
432
433    pub fn move_to_first_row_of_next_line(self, layout: &Layout<'_>) -> Self {
434        let line = layout.line(self.position.line_index);
435        let (_, mut column_index) =
436            line.logical_to_grid_position(self.position.byte_index, self.affinity);
437        if let Some(preferred_column_index) = self.preferred_column_index {
438            column_index = preferred_column_index;
439        }
440        let next_line = layout.line(self.position.line_index + 1);
441        let (byte_index, affinity) = next_line.grid_to_logical_position(0, column_index);
442        Self {
443            position: Position {
444                line_index: self.position.line_index + 1,
445                byte_index,
446            },
447            affinity,
448            preferred_column_index: Some(column_index),
449        }
450    }
451
452    pub fn apply_edit(self, edit: &Edit) -> Self {
453        Self {
454            position: self.position.apply_edit(edit),
455            ..self
456        }
457    }
458}
459
460impl From<Position> for Cursor {
461    fn from(position: Position) -> Self {
462        Self {
463            position,
464            affinity: Affinity::Before,
465            preferred_column_index: None,
466        }
467    }
468}
469
470#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
471pub enum Affinity {
472    Before,
473    After,
474}
475
476impl Default for Affinity {
477    fn default() -> Self {
478        Self::Before
479    }
480}