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.normalize_selection(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        self.normalize_all_selections(retained_index)
151    }
152
153    pub fn apply_edit(&mut self, edit: &Edit, retained_index: Option<usize>) -> Option<usize> {
154        self.update_all_selections(retained_index, |selection| selection.apply_edit(edit))
155    }
156
157    pub fn add_selection(&mut self, selection: Selection) -> usize {
158        let index = match self
159            .selections
160            .binary_search_by_key(&selection.start(), |selection| selection.start())
161        {
162            Ok(index) => {
163                self.selections[index] = selection;
164                index
165            }
166            Err(index) => {
167                self.selections.insert(index, selection);
168                index
169            }
170        };
171        self.normalize_selection(index)
172    }
173
174    pub fn set_selection(&mut self, selection: Selection) {
175        self.selections.clear();
176        self.selections.push(selection);
177    }
178
179    fn normalize_selection(&mut self, index: usize) -> usize {
180        let mut index = index;
181        while index > 0 {
182            let prev_index = index - 1;
183            if !self.selections[prev_index].overlaps_with(self.selections[index]) {
184                break;
185            }
186            self.selections.remove(prev_index);
187            index -= 1;
188        }
189        while index + 1 < self.selections.len() {
190            let next_index = index + 1;
191            if !self.selections[index].overlaps_with(self.selections[next_index]) {
192                break;
193            }
194            self.selections.remove(next_index);
195        }
196        index
197    }
198
199    fn normalize_all_selections(&mut self, retained_index: Option<usize>) -> Option<usize> {
200        let mut retained_index = retained_index;
201        let mut current_index = 0;
202        while current_index + 1 < self.selections.len() {
203            let next_index = current_index + 1;
204            let current_selection = self.selections[current_index];
205            let next_selection = self.selections[next_index];
206            assert!(current_selection.start() <= next_selection.start());
207            if let Some(merged_selection) = current_selection.merge_with(next_selection) {
208                self.selections[current_index] = merged_selection;
209                self.selections.remove(next_index);
210                if let Some(retained_index) = &mut retained_index {
211                    if next_index <= *retained_index {
212                        *retained_index -= 1;
213                    }
214                }
215            } else {
216                current_index += 1;
217            }
218        }
219        retained_index
220    }
221}
222
223impl Default for SelectionSet {
224    fn default() -> Self {
225        Self {
226            selections: vec![Selection::default()],
227        }
228    }
229}
230
231impl Deref for SelectionSet {
232    type Target = [Selection];
233
234    fn deref(&self) -> &Self::Target {
235        &self.selections
236    }
237}
238
239impl<'a> IntoIterator for &'a SelectionSet {
240    type Item = &'a Selection;
241    type IntoIter = Iter<'a, Selection>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.iter()
245    }
246}
247
248#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
249pub struct Cursor {
250    pub position: Position,
251    pub affinity: Affinity,
252    pub preferred_column_index: Option<usize>,
253}
254
255impl Cursor {
256    pub fn is_at_first_line(self) -> bool {
257        self.position.line_index == 0
258    }
259
260    pub fn is_at_last_line(self, line_count: usize) -> bool {
261        self.position.line_index == line_count - 1
262    }
263
264    pub fn is_at_start_of_line(self) -> bool {
265        self.position.byte_index == 0
266    }
267
268    pub fn is_at_end_of_line(self, lines: &[String]) -> bool {
269        self.position.byte_index == lines[self.position.line_index].len()
270    }
271
272    pub fn is_at_first_row_of_line(self, layout: &Layout<'_>) -> bool {
273        let (row, _) = layout
274            .line(self.position.line_index)
275            .logical_to_grid_position(self.position.byte_index, self.affinity);
276        row == 0
277    }
278
279    pub fn is_at_last_row_of_line(self, layout: &Layout<'_>) -> bool {
280        let line = layout.line(self.position.line_index);
281        let (row, _) = line.logical_to_grid_position(self.position.byte_index, self.affinity);
282        row == line.row_count() - 1
283    }
284
285    pub fn move_left(self, lines: &[String]) -> Self {
286        if !self.is_at_start_of_line() {
287            return self.move_to_prev_grapheme(lines);
288        }
289        if !self.is_at_first_line() {
290            return self.move_to_end_of_prev_line(lines);
291        }
292        self
293    }
294
295    pub fn move_right(self, lines: &[String]) -> Self {
296        if !self.is_at_end_of_line(lines) {
297            return self.move_to_next_grapheme(lines);
298        }
299        if !self.is_at_last_line(lines.len()) {
300            return self.move_to_start_of_next_line();
301        }
302        self
303    }
304
305    pub fn move_up(self, layout: &Layout<'_>) -> Self {
306        if !self.is_at_first_row_of_line(layout) {
307            return self.move_to_prev_row_of_line(layout);
308        }
309        if !self.is_at_first_line() {
310            return self.move_to_last_row_of_prev_line(layout);
311        }
312        self.move_to_start_of_line()
313    }
314
315    pub fn move_down(self, layout: &Layout<'_>) -> Self {
316        if !self.is_at_last_row_of_line(layout) {
317            return self.move_to_next_row_of_line(layout);
318        }
319        if !self.is_at_last_line(layout.as_text().as_lines().len()) {
320            return self.move_to_first_row_of_next_line(layout);
321        }
322        self.move_to_end_of_line(layout.as_text().as_lines())
323    }
324
325    pub fn home(self, lines: &[String]) -> Self {
326        if !self.is_at_start_of_line() {
327            let indent_len = lines[self.position.line_index].indent().unwrap_or("").len();
328            if self.position.byte_index <= indent_len {
329                return self.move_to_start_of_line();
330            } else {
331                return Self {
332                    position: Position {
333                        line_index: self.position.line_index,
334                        byte_index: indent_len,
335                    },
336                    affinity: Affinity::Before,
337                    preferred_column_index: None,
338                };
339            }
340        }
341        self
342    }
343
344    pub fn end(self, lines: &[String]) -> Self {
345        if !self.is_at_end_of_line(lines) {
346            let indent_len = lines[self.position.line_index].indent().unwrap_or("").len();
347            if self.position.byte_index >= indent_len {
348                return self.move_to_end_of_line(lines);
349            } else {
350                return Self {
351                    position: Position {
352                        line_index: self.position.line_index,
353                        byte_index: indent_len,
354                    },
355                    affinity: Affinity::After,
356                    preferred_column_index: None,
357                };
358            }
359        }
360        self
361    }
362
363    pub fn move_to_end_of_line(self, lines: &[String]) -> Self {
364        let mut me = self.clone();
365        while !me.is_at_end_of_line(lines) {
366            me = me.move_to_next_grapheme(lines);
367        }
368        me
369    }
370
371    pub fn move_to_start_of_line(self) -> Self {
372        Self {
373            position: Position {
374                line_index: self.position.line_index,
375                byte_index: 0,
376            },
377            affinity: Affinity::Before,
378            preferred_column_index: None,
379        }
380    }
381    
382    pub fn move_to_file_start(self) -> Self {
383        Self {
384            position: Position {
385                line_index: 0,
386                byte_index: 0,
387            },
388            affinity: Affinity::Before,
389            preferred_column_index: None,
390        }
391    }
392
393    pub fn move_to_file_end(self, lines: &[String]) -> Self {
394        Self {
395            position: Position {
396                line_index: lines.len() - 1,
397                byte_index: lines[lines.len() - 1].len(),
398            },
399            affinity: Affinity::After,
400            preferred_column_index: None,
401        }
402    }
403
404    pub fn move_to_prev_grapheme(self, lines: &[String]) -> Self {
405        Self {
406            position: Position {
407                line_index: self.position.line_index,
408                byte_index: lines[self.position.line_index][..self.position.byte_index]
409                    .grapheme_indices()
410                    .next_back()
411                    .map(|(index, _)| index)
412                    .unwrap(),
413            },
414            affinity: Affinity::After,
415            preferred_column_index: None,
416        }
417    }
418
419    pub fn move_to_next_grapheme(self, lines: &[String]) -> Self {
420        let line = &lines[self.position.line_index];
421        Self {
422            position: Position {
423                line_index: self.position.line_index,
424                byte_index: line[self.position.byte_index..]
425                    .grapheme_indices()
426                    .nth(1)
427                    .map(|(index, _)| self.position.byte_index + index)
428                    .unwrap_or(line.len()),
429            },
430            affinity: Affinity::Before,
431            preferred_column_index: None,
432        }
433    }
434
435    pub fn move_to_end_of_prev_line(self, lines: &[String]) -> Self {
436        let prev_line_index = self.position.line_index - 1;
437        Self {
438            position: Position {
439                line_index: prev_line_index,
440                byte_index: lines[prev_line_index].len(),
441            },
442            affinity: Affinity::After,
443            preferred_column_index: None,
444        }
445    }
446
447    pub fn move_to_start_of_next_line(self) -> Self {
448        Self {
449            position: Position {
450                line_index: self.position.line_index + 1,
451                byte_index: 0,
452            },
453            affinity: Affinity::Before,
454            preferred_column_index: None,
455        }
456    }
457
458    pub fn move_to_prev_row_of_line(self, layout: &Layout<'_>) -> Self {
459        let line = layout.line(self.position.line_index);
460        let (row_index, mut column_index) =
461            line.logical_to_grid_position(self.position.byte_index, self.affinity);
462        if let Some(preferred_column_index) = self.preferred_column_index {
463            column_index = preferred_column_index;
464        }
465        let (byte_index, affinity) = line.grid_to_logical_position(row_index - 1, column_index);
466        Self {
467            position: Position {
468                line_index: self.position.line_index,
469                byte_index,
470            },
471            affinity,
472            preferred_column_index: Some(column_index),
473        }
474    }
475
476    pub fn move_to_next_row_of_line(self, layout: &Layout<'_>) -> Self {
477        let line = layout.line(self.position.line_index);
478        let (row_index, mut column_index) =
479            line.logical_to_grid_position(self.position.byte_index, self.affinity);
480        if let Some(preferred_column_index) = self.preferred_column_index {
481            column_index = preferred_column_index;
482        }
483        let (byte, affinity) = line.grid_to_logical_position(row_index + 1, column_index);
484        Self {
485            position: Position {
486                line_index: self.position.line_index,
487                byte_index: byte,
488            },
489            affinity,
490            preferred_column_index: Some(column_index),
491        }
492    }
493
494    pub fn move_to_last_row_of_prev_line(self, layout: &Layout<'_>) -> Self {
495        let line = layout.line(self.position.line_index);
496        let (_, mut column_index) =
497            line.logical_to_grid_position(self.position.byte_index, self.affinity);
498        if let Some(preferred_column_index) = self.preferred_column_index {
499            column_index = preferred_column_index;
500        }
501        let prev_line = layout.line(self.position.line_index - 1);
502        let (byte_index, affinity) =
503            prev_line.grid_to_logical_position(prev_line.row_count() - 1, column_index);
504        Self {
505            position: Position {
506                line_index: self.position.line_index - 1,
507                byte_index,
508            },
509            affinity,
510            preferred_column_index: Some(column_index),
511        }
512    }
513
514    pub fn move_to_first_row_of_next_line(self, layout: &Layout<'_>) -> Self {
515        let line = layout.line(self.position.line_index);
516        let (_, mut column_index) =
517            line.logical_to_grid_position(self.position.byte_index, self.affinity);
518        if let Some(preferred_column_index) = self.preferred_column_index {
519            column_index = preferred_column_index;
520        }
521        let next_line = layout.line(self.position.line_index + 1);
522        let (byte_index, affinity) = next_line.grid_to_logical_position(0, column_index);
523        Self {
524            position: Position {
525                line_index: self.position.line_index + 1,
526                byte_index,
527            },
528            affinity,
529            preferred_column_index: Some(column_index),
530        }
531    }
532
533
534    pub fn apply_edit(self, edit: &Edit) -> Self {
535        Self {
536            position: self.position.apply_edit(edit),
537            ..self
538        }
539    }
540}
541
542impl From<Position> for Cursor {
543    fn from(position: Position) -> Self {
544        Self {
545            position,
546            affinity: Affinity::Before,
547            preferred_column_index: None,
548        }
549    }
550}
551
552#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
553pub enum Affinity {
554    Before,
555    After,
556}
557
558impl Default for Affinity {
559    fn default() -> Self {
560        Self::Before
561    }
562}