duat_core/mode/cursor/
mod.rs

1//! A helper struct for [`Mode`]s with [`Selections`]
2//!
3//! This struct can edit [`Text`] in a declarative way, freeing the
4//! [`Mode`]s from worrying about synchronization of the
5//! selections and dealing with editing the text directly.
6//!
7//! [`Mode`]: super::Mode
8use std::{
9    cell::Cell,
10    ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
11    rc::Rc,
12};
13
14use lender::{Lender, Lending};
15
16pub use self::selections::{Selection, Selections, VPoint};
17use crate::{
18    buffer::{Buffer, BufferId, Change},
19    opts::PrintOpts,
20    text::{Lines, Matches, Point, RegexHaystack, RegexPattern, Strs, Text, TextIndex, TextRange},
21    ui::{Area, Widget},
22};
23
24/// The [`Selection`] and [`Selections`] structs
25mod selections;
26
27macro_rules! sel {
28    ($cursor:expr) => {
29        $cursor.selections[$cursor.sels_i]
30            .as_ref()
31            .unwrap()
32            .selection
33    };
34}
35
36macro_rules! sel_mut {
37    ($cursor:expr) => {{
38        let mod_sel = $cursor.selections[$cursor.sels_i].as_mut().unwrap();
39        mod_sel.has_changed = true;
40        &mut mod_sel.selection
41    }};
42}
43
44/// A selection that can edit [`Text`], but can't alter selections
45///
46/// This struct will be used only inside functions passed to the
47/// [`edit_*`] family of methods from the [`Handle`].
48///
49/// To make edits, you can use three different functions. You can,
50/// those being [`replace`], [`insert`], and [`append`]. [`replace`]
51/// will completely replace the [`Selection`]'s selection. [`insert`]
52/// will place text behind the `caret`, and [`append`] will place it
53/// after the `caret`.
54///
55/// You can also move the [`Selection`]'s selection in many different
56/// ways, which are described below, in the `impl` section for this
57/// struct.
58///
59/// ```rust
60/// # duat_core::doc_duat!(duat);
61/// # use duat::prelude::*;
62/// # fn test(mut pa: Pass, handle: &mut Handle) {
63/// let sel = handle.edit_main(&mut pa, |mut c| {
64///     c.set_anchor();
65///     c.set_caret_on_end();
66///     c.replace("my replacement");
67///     c.append(" and my edit");
68///
69///     c.swap_ends();
70///     c.insert("This is ");
71///     c.swap_ends();
72///
73///     c.move_hor(" and my edit".chars().count() as i32);
74///     c.set_anchor();
75///     c.move_hor(-("This is my replacement and my edit".chars().count() as i32));
76///     c.selection().to_string()
77/// });
78///
79/// assert_eq!(&sel, "This is my replacement and my edit");
80/// # }
81/// ```
82///
83/// [`edit_*`]: crate::context::Handle::edit_nth
84/// [`Handle`]: crate::context::Handle
85/// [`replace`]: Cursor::replace
86/// [`insert`]: Cursor::insert
87/// [`append`]: Cursor::append
88pub struct Cursor<'c, W: Widget + ?Sized = crate::buffer::Buffer> {
89    selections: &'c mut Vec<Option<ModSelection>>,
90    sels_i: usize,
91    initial: Selection,
92    widget: &'c mut W,
93    area: &'c Area,
94    next_i: Option<Rc<Cell<usize>>>,
95}
96
97impl<'c, W: Widget + ?Sized> Cursor<'c, W> {
98    /// Returns a new instance of [`Cursor`]
99    pub(crate) fn new(
100        selections: &'c mut Vec<Option<ModSelection>>,
101        sels_i: usize,
102        (widget, area): (&'c mut W, &'c Area),
103        next_i: Option<Rc<Cell<usize>>>,
104    ) -> Self {
105        let initial = selections[sels_i].as_ref().unwrap().selection.clone();
106        Self {
107            selections,
108            sels_i,
109            initial,
110            widget,
111            area,
112            next_i,
113        }
114    }
115
116    ////////// Text editing
117
118    /// Replaces the entire selection with new text
119    ///
120    /// If there is a selection, then it is treated as _inclusive_,
121    /// therefore, a selection where `caret == anchor` will remove the
122    /// character where the caret is. If there is no selection, then
123    /// this has the same effect as [`insert`]. If you wish to
124    /// append to the `caret` instead, see [`append`].
125    ///
126    /// After replacing the sele tion, if the `caret` is behind the
127    /// `anchor` (or in the same spot), it will be placed on the start
128    /// of the selection, while the `anchor` will be placed on the
129    /// new end. If it is ahead, it will be placed ahead.
130    ///
131    /// [`insert`]: Self::insert
132    /// [`append`]: Self::append
133    pub fn replace(&mut self, edit: impl ToString) {
134        let change = {
135            let edit = edit.to_string();
136            let range = sel!(self).point_range(self.widget.text());
137            let (p0, p1) = (range.start, range.end);
138            let p1 = if self.anchor().is_some() { p1 } else { p0 };
139            Change::new(edit, p0..p1, self.widget.text())
140        };
141
142        // Disconsider null changes.
143        if change.added_str().len() < 10 && change.added_str() == change.taken_str() {
144            return;
145        }
146
147        let (start, end) = (change.start(), change.added_end());
148
149        self.edit(change.clone());
150
151        let anchor_was_on_start = self.anchor_is_start();
152        self.move_to(start..end);
153        if !anchor_was_on_start {
154            self.set_caret_on_start();
155        }
156    }
157
158    /// Inserts new text directly behind the `caret`
159    ///
160    /// If the `anchor` is ahead of the `caret`, it will move forwards
161    /// by the number of chars in the new text.
162    ///
163    /// If you wish to replace the selected text, see [`replace`], if
164    /// you want to append after the `caret` instead, see [`append`]
165    ///
166    /// [`replace`]: Self::replace
167    /// [`append`]: Self::append
168    pub fn insert(&mut self, edit: impl ToString) {
169        let caret_point = sel!(self).caret();
170        let range = caret_point..caret_point;
171        let change = Change::new(edit.to_string(), range, self.widget.text());
172        let (added, taken) = (change.added_end(), change.taken_end());
173
174        self.edit(change);
175
176        if let Some(anchor) = sel!(self).anchor()
177            && anchor > sel!(self).caret()
178        {
179            let new_anchor = anchor + added - taken;
180            sel_mut!(self).swap_ends();
181            sel_mut!(self).move_to(new_anchor, self.widget.text());
182            sel_mut!(self).swap_ends();
183        }
184    }
185
186    /// Appends new text directly after the `caret`
187    ///
188    /// If the `anchor` is ahead of the `caret`, it will move forwards
189    /// by the number of chars in the new text.
190    ///
191    /// If you wish to replace the selected text, see [`replace`], if
192    /// you want to insert before the `caret` instead, see [`insert`]
193    ///
194    /// [`replace`]: Self::replace
195    /// [`insert`]: Self::insert
196    pub fn append(&mut self, edit: impl ToString) {
197        let caret = sel!(self).caret();
198        let after = caret.fwd(self.widget.text().char_at(caret).unwrap());
199        let change = Change::new(edit.to_string(), after..after, self.widget.text());
200        let (added, taken) = (change.added_end(), change.taken_end());
201
202        self.edit(change);
203
204        if let Some(anchor) = sel!(self).anchor()
205            && anchor > after
206        {
207            let new_anchor = anchor + added - taken;
208            sel_mut!(self).swap_ends();
209            sel_mut!(self).move_to(new_anchor, self.widget.text());
210            sel_mut!(self).swap_ends();
211        }
212    }
213
214    /// Edits the buffer with a [`Change`]
215    fn edit(&mut self, change: Change<'static, String>) {
216        let mut text = self.widget.text_mut();
217        let (change_i, selections_taken) =
218            text.apply_change(sel!(self).change_i.map(|i| i as usize), change);
219        sel_mut!(self).change_i = change_i.map(|i| i as u32);
220
221        // The Change may have happened before the index of the next curossr,
222        // so we need to account for that.
223        if let Some(change_i) = change_i
224            && let Some(next_i) = self.next_i.as_ref()
225            && change_i <= next_i.get()
226        {
227            next_i.set(next_i.get().saturating_sub(selections_taken));
228        }
229    }
230
231    ////////// Movement functions
232
233    /// Moves the selection horizontally. May cause vertical movement
234    ///
235    /// Returns the distance traveled, in character indices
236    #[track_caller]
237    pub fn move_hor(&mut self, by: i32) -> i32 {
238        if by == 0 {
239            return 0;
240        }
241        let moved = sel_mut!(self).move_hor(by, self.widget.text());
242        if moved != 0 {
243            self.widget.text_mut().add_record_for(sel!(self).caret());
244        }
245        moved
246    }
247
248    /// Moves the selection vertically. May cause horizontal movement
249    ///
250    /// Returns the distance moved in lines.
251    #[track_caller]
252    pub fn move_ver(&mut self, by: i32) -> i32 {
253        if by == 0 {
254            return 0;
255        }
256        let line_count = sel_mut!(self).move_ver(
257            by,
258            self.widget.text(),
259            self.area,
260            self.widget.get_print_opts(),
261        );
262        if line_count != 0 {
263            self.widget.text_mut().add_record_for(sel!(self).caret());
264        }
265        line_count
266    }
267
268    /// Moves the selection vertically a number of wrapped lines. May
269    /// cause horizontal movement
270    ///
271    /// Returns the distance moved in wrapped lines.
272    #[track_caller]
273    pub fn move_ver_wrapped(&mut self, count: i32) {
274        if count == 0 {
275            return;
276        }
277        sel_mut!(self).move_ver_wrapped(
278            count,
279            self.widget.text(),
280            self.area,
281            self.widget.get_print_opts(),
282        );
283    }
284
285    /// Moves the selection to a [`Point`] or a [range] of [`Point`]s
286    ///
287    /// If you give it just a [`Point`], it will move the caret,
288    /// without affecting the anchor. If you give it a [range] of
289    /// [`Point`]s, the anchor will be placed at the start, while the
290    /// caret will be placed at the end of said [range]. You can flip
291    /// those positions with a function like [`swap_ends`].
292    ///
293    /// If a [`Point`] is not valid, it will be corrected and clamped
294    /// to the lenght of the [`Text`].
295    ///
296    /// [range]: std::ops::RangeBounds
297    /// [`swap_ends`]: Self::swap_ends
298    #[track_caller]
299    pub fn move_to(&mut self, point_or_points: impl CaretOrRange) {
300        point_or_points.move_to(self);
301        for point in [Some(sel!(self).caret()), sel!(self).anchor()]
302            .into_iter()
303            .flatten()
304        {
305            self.widget.text_mut().add_record_for(point);
306        }
307    }
308
309    /// Moves the selection to [`Point::default`], i.c., the start of
310    /// the [`Text`]
311    #[track_caller]
312    pub fn move_to_start(&mut self) {
313        sel_mut!(self).move_to(Point::default(), self.widget.text());
314    }
315
316    /// Moves the selection to a `line` and a `column`
317    ///
318    /// - If the coords isn't valid, it will move to the "maximum"
319    ///   position allowed.
320    #[track_caller]
321    pub fn move_to_coords(&mut self, line: usize, col: usize) {
322        let range = self
323            .text()
324            .line_range(line.min(self.text().last_point().line()));
325        let byte = self
326            .text()
327            .chars_fwd(range.clone())
328            .unwrap()
329            .map(|(byte, _)| byte)
330            .take(col + 1)
331            .last();
332        self.move_to(byte.unwrap_or(range.end.byte() - 1));
333    }
334
335    /// Moves to a column on the current line
336    #[track_caller]
337    pub fn move_to_col(&mut self, col: usize) {
338        let line = self.text().point_at_line(self.caret().line()).line();
339        self.move_to_coords(line, col);
340    }
341
342    /// Returns and takes the anchor of the [`Selection`], if there
343    /// was one
344    pub fn unset_anchor(&mut self) -> Option<Point> {
345        sel_mut!(self).unset_anchor()
346    }
347
348    /// Sets the `anchor` to the current `caret`
349    pub fn set_anchor(&mut self) {
350        sel_mut!(self).set_anchor()
351    }
352
353    /// Sets the `anchor` if it was not already set
354    ///
355    /// Returns `true` if the anchor was set by this command.
356    pub fn set_anchor_if_needed(&mut self) -> bool {
357        if self.anchor().is_none() {
358            sel_mut!(self).set_anchor();
359            true
360        } else {
361            false
362        }
363    }
364
365    /// Swaps the position of the `caret` and `anchor`
366    pub fn swap_ends(&mut self) {
367        sel_mut!(self).swap_ends();
368    }
369
370    /// Sets the caret of the [`Selection`] on the start of the
371    /// selection
372    ///
373    /// Returns `true` if a swap occurred
374    pub fn set_caret_on_start(&mut self) -> bool {
375        if let Some(anchor) = self.anchor()
376            && anchor < self.caret()
377        {
378            self.swap_ends();
379            true
380        } else {
381            false
382        }
383    }
384
385    /// Sets the caret of the [`Selection`] on the end of the
386    /// selection
387    ///
388    /// Returns `true` if a swap occurred
389    pub fn set_caret_on_end(&mut self) -> bool {
390        if let Some(anchor) = self.anchor()
391            && anchor > self.caret()
392        {
393            self.swap_ends();
394            true
395        } else {
396            false
397        }
398    }
399
400    ////////// Selection meta manipulation
401
402    /// Resets the [`Selection`] to how it was before being modified
403    pub fn reset(&mut self) {
404        *sel_mut!(self) = self.initial.clone();
405    }
406
407    /// Copies the current [`Selection`] in place
408    ///
409    /// This will leave an additional [`Selection`] with the current
410    /// selection. Do note that normal intersection rules apply, so if
411    /// at the end of the movement, this selection intersects with any
412    /// other, they will be merged into one.
413    ///
414    /// When this [`Cursor`] is dropped, like with normal [`Cursor`]s,
415    /// its [`Selection`] will be added to the [`Selections`], unless
416    /// you [destroy] it.
417    ///
418    /// [destroy]: Self::destroy
419    pub fn copy(&mut self) -> Cursor<'_, W> {
420        let copy = self.selections[self.sels_i].clone().unwrap();
421        self.selections
422            .push(Some(ModSelection { was_main: false, ..copy }));
423        
424        let sels_i = self.selections.len() - 1;
425        Cursor::new(
426            self.selections,
427            sels_i,
428            (self.widget, self.area),
429            self.next_i.clone(),
430        )
431    }
432
433    /// Destroys the current [`Selection`]
434    ///
435    /// Will not destroy it if it is the last [`Selection`] left
436    ///
437    /// If this was the main selection, the main selection will now be
438    /// the selection immediately behind it.
439    pub fn destroy(self) {
440        // If there are other Selections in the list, or other copies still
441        // lying around, the Cursor Selection can be destroyed.
442        if self.widget.text().selections().is_empty()
443            && self.selections.iter().flatten().count() <= 1
444        {
445            return;
446        }
447
448        if self.selections[self.sels_i].as_ref().unwrap().was_main {
449            self.widget.text_mut().selections_mut().rotate_main(-1);
450        }
451
452        self.selections[self.sels_i] = None;
453    }
454
455    /// Sets the "desired visual column"
456    ///
457    /// The desired visual column determines at what point in a line
458    /// the caret will be placed when moving [up and down] through
459    /// lines of varying lengths.
460    ///
461    /// Will also set the "desired wrapped visual column", which is
462    /// the same thing but used when moving vertically in a [wrapped]
463    /// fashion.
464    ///
465    /// [up and down]: Cursor::move_ver
466    /// [wrapped]: Cursor::move_ver_wrapped
467    pub fn set_desired_vcol(&mut self, x: usize) {
468        sel_mut!(self).set_desired_cols(x, x);
469    }
470
471    ////////// Iteration functions
472
473    /// Iterates over the [`char`]s
474    ///
475    /// Each [`char`] will be accompanied by a byte index, which is
476    /// the position where said character starts, e.g. `0` for the
477    /// first character
478    pub fn chars_fwd(&self) -> impl Iterator<Item = (usize, char)> + '_ {
479        self.widget.text().chars_fwd(self.caret()..).unwrap()
480    }
481
482    /// Iterates over the [`char`]s, in reverse
483    ///
484    /// Each [`char`] will be accompanied by a byte index, which is
485    /// the position where said character starts, e.g. `0` for the
486    /// first character
487    pub fn chars_rev(&self) -> impl Iterator<Item = (usize, char)> {
488        self.widget.text().chars_rev(..self.caret()).unwrap()
489    }
490
491    /// Wether the current selection matches a regex pattern
492    #[track_caller]
493    pub fn matches_pat<R: RegexPattern>(&self, pat: R) -> bool {
494        let range = sel!(self).byte_range(self.widget.text());
495        match self.widget.text().strs(range).unwrap().matches_pat(pat) {
496            Ok(result) => result,
497            Err(err) => panic!("{err}"),
498        }
499    }
500
501    /// Returns an [`Iterator`] over the matches of a [`RegexPattern`]
502    ///
503    /// This `Iterator` normally covers the entire range of the
504    /// [`Text`], however, there are methods that you can use to
505    /// narrow it down to ranges relative to the `Cursor`'s [`caret`].
506    ///
507    /// For example, [`CursorMatches::from_caret`] will narrow the
508    /// searched range from the beginning of the caret's `char` all
509    /// the way until the end of the [`Text`].
510    ///
511    /// This `Iterator` also implements [`DoubleEndedIterator`], which
512    /// means you can search in reverse as well.
513    ///
514    /// [`caret`]: Self::caret
515    #[track_caller]
516    pub fn search<R: RegexPattern>(&self, pat: R) -> CursorMatches<'_, R> {
517        let text = self.widget.text();
518        let caret = self.caret();
519        CursorMatches {
520            text_byte_len: text.len().byte(),
521            caret_range: caret.byte()..caret.fwd(self.char()).byte(),
522            matches: text.search(pat),
523        }
524    }
525
526    ////////// Text queries
527
528    /// Returns the [`char`] in the `caret`
529    pub fn char(&self) -> char {
530        self.text().char_at(sel!(self).caret()).unwrap()
531    }
532
533    /// Returns the [`char`] at a given [`Point`]
534    pub fn char_at(&self, i: impl TextIndex) -> Option<char> {
535        self.text().char_at(i)
536    }
537
538    /// Returns the [`Selection`]'s selection
539    ///
540    /// The reason why this return value is `IntoIter<&str, 2>` is
541    /// because the [`Text`] utilizes an underlying [`GapBuffer`]
542    /// to store the characters. This means that the text is
543    /// always separated into two distinct chunks.
544    ///
545    /// If this [`Selection`]'s selection happens to be entirely
546    /// within one of these chunks, the other `&str` will just be
547    /// empty.
548    ///
549    /// [`GapBuffer`]: gapbuf::GapBuffer
550    pub fn selection(&self) -> Strs<'_> {
551        let range = sel!(self).byte_range(self.text());
552        self.text().strs(range).unwrap()
553    }
554
555    /// Returns the [`Strs`] for the given [`TextRange`]
556    ///
557    /// [`GapBuffer`]: gapbuf::GapBuffer
558    pub fn strs(&self, range: impl TextRange) -> Option<Strs<'_>> {
559        self.widget.text().strs(range)
560    }
561
562    /// Returns the length of the [`Text`], in [`Point`]
563    pub fn len(&self) -> Point {
564        self.text().len()
565    }
566
567    /// Returns the position of the last [`char`] if there is one
568    pub fn last_point(&self) -> Point {
569        self.text().last_point()
570    }
571
572    /// An [`Iterator`] over the lines the `Cursor`'s range
573    pub fn lines(&self) -> Lines<'_> {
574        self.widget.text().lines(self.range())
575    }
576
577    /// An [`Iterator`] over the lines in a given [range]
578    ///
579    /// [range]: TextRange
580    pub fn lines_on(&self, range: impl TextRange) -> Lines<'_> {
581        self.widget.text().lines(range)
582    }
583
584    /// Gets the current level of indentation
585    pub fn indent(&self) -> usize {
586        self.widget
587            .text()
588            .indent(self.caret(), self.area, self.opts())
589    }
590
591    /// Gets the indentation level on the given [`Point`]
592    pub fn indent_on(&self, p: Point) -> usize {
593        self.widget.text().indent(p, self.area, self.opts())
594    }
595
596    ////////// Selection queries
597
598    /// Returns the `caret`
599    pub fn caret(&self) -> Point {
600        sel!(self).caret()
601    }
602
603    /// Returns the `anchor`
604    pub fn anchor(&self) -> Option<Point> {
605        sel!(self).anchor()
606    }
607
608    /// The [`Point`] range of the [`Selection`]
609    ///
610    /// This is an _inclusive_ range (not Rust's [`RangeInclusive`]
611    /// however), this means that, even if there is no anchor, the
612    /// lenght of this range will always be at least 1.
613    ///
614    /// If you want an exclusive range, see [`Cursor::range_excl`]
615    ///
616    /// [`RangeInclusive`]: std::ops::RangeInclusive
617    pub fn range(&self) -> Range<Point> {
618        sel!(self).point_range(self.text())
619    }
620
621    /// An exclusive [`Point`] range of the [`Selection`]
622    ///
623    /// If you wish for an inclusive range, whose length is always
624    /// greater than or equal to 1, see [`RangeInclusive`].
625    pub fn range_excl(&self) -> Range<Point> {
626        sel!(self).point_range_excl()
627    }
628
629    /// The [`VPoint`] range of the [`Selection`]
630    ///
631    /// Use only if you need the things that the [`VPoint`] provides,
632    /// in order to preven extraneous calculations
633    pub fn v_caret(&self) -> VPoint {
634        sel!(self).v_caret(self.widget.text(), self.area, self.widget.get_print_opts())
635    }
636
637    /// The [`VPoint`] of the anchor, if it exists
638    ///
639    /// Use only if you need the things that the [`VPoint`] provides,
640    /// in order to preven extraneous calculations
641    pub fn v_anchor(&self) -> Option<VPoint> {
642        sel!(self).v_anchor(self.widget.text(), self.area, self.widget.get_print_opts())
643    }
644
645    /// Returns `true` if the `anchor` exists before the `caret`
646    pub fn anchor_is_start(&self) -> bool {
647        self.anchor().is_none_or(|anchor| anchor <= self.caret())
648    }
649
650    /// Whether or not this is the main [`Selection`]
651    pub fn is_main(&self) -> bool {
652        self.selections[self.sels_i].as_ref().unwrap().was_main
653    }
654
655    /// The [`Text`] of the [`Widget`]
656    pub fn text(&self) -> &Text {
657        self.widget.text()
658    }
659
660    /// The [`PrintOpts`] in use
661    pub fn opts(&self) -> PrintOpts {
662        self.widget.get_print_opts()
663    }
664}
665
666impl Cursor<'_, Buffer> {
667    /// A unique identifier for this [`Buffer`]
668    ///
669    /// This is more robust than identifying it by its path or name,
670    /// or even [`PathKind`], since those could change, but this
671    /// cannot.
672    ///
673    /// [`PathKind`]: crate::buffer::PathKind
674    pub fn buffer_id(&self) -> BufferId {
675        self.widget.buffer_id()
676    }
677}
678
679impl<'a, W: Widget + ?Sized> std::fmt::Debug for Cursor<'a, W> {
680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
681        f.debug_struct("Cursor")
682            .field("selection", &sel!(self))
683            .finish_non_exhaustive()
684    }
685}
686
687/// An [`Iterator`] over the matches of a [`RegexPattern`]
688///
689/// This `Iterator` comes from searching from a [`Cursor`]. Because of
690/// that, it has methods for added convenience of search. The methods
691/// [`to_caret`], [`to_caret_incl`], [`from_caret`] and
692/// [`from_caret_excl`] will change the [`Range`] of searching to one
693/// starting or ending on the `Cursor`'s [`caret`].
694///
695/// [`to_caret`]: CursorMatches::to_caret
696/// [`to_caret_incl`]: CursorMatches::to_caret_incl
697/// [`from_caret`]: CursorMatches::from_caret
698/// [`from_caret_excl`]: CursorMatches::from_caret_excl
699/// [`caret`]: Cursor::caret
700pub struct CursorMatches<'c, R: RegexPattern> {
701    text_byte_len: usize,
702    caret_range: Range<usize>,
703    matches: Matches<'c, R>,
704}
705
706impl<'c, R: RegexPattern> CursorMatches<'c, R> {
707    /// Changes the [`TextRange`] to search on
708    ///
709    /// This _will_ reset the [`Iterator`], if it was returning
710    /// [`None`] before, it might start returning [`Some`] again if
711    /// the pattern exists in the specified [`Range`]
712    pub fn range(self, range: impl TextRange) -> Self {
713        Self {
714            matches: self.matches.range(range),
715            ..self
716        }
717    }
718
719    /// Searches over a range from the start of the caret to the end
720    /// of the [`Text`]
721    ///
722    /// ```rust
723    /// # duat_core::doc_duat!(duat);
724    /// # use duat::prelude::*;
725    /// fn search_nth(pa: &mut Pass, handle: &Handle, n: usize, pat: &str) {
726    ///     handle.edit_all(pa, |mut c| {
727    ///         let mut nth = c.search(pat).from_caret().nth(n);
728    ///         if let Some(range) = nth {
729    ///             c.move_to(range);
730    ///         }
731    ///     })
732    /// }
733    /// ```
734    #[allow(clippy::wrong_self_convention)]
735    pub fn from_caret(self) -> Self {
736        Self {
737            matches: self
738                .matches
739                .range(self.caret_range.start..self.text_byte_len),
740            ..self
741        }
742    }
743
744    /// Searches over a range from the end of the caret to the end
745    /// of the [`Text`]
746    ///
747    /// ```rust
748    /// # duat_core::doc_duat!(duat);
749    /// # use duat::prelude::*;
750    /// fn next_paren_match(pa: &mut Pass, handle: &Handle) {
751    ///     handle.edit_all(pa, |mut c| {
752    ///         let mut start_count = 0;
753    ///         let mut start_bound = None;
754    ///         let end_bound = c
755    ///             .search([r"\(", r"\)"])
756    ///             .from_caret_excl()
757    ///             .find(|(id, range)| {
758    ///                 start_count += ((*id == 0) as u32).saturating_sub((*id == 1) as u32);
759    ///                 start_bound = (*id == 0 && start_count == 0).then_some(range.clone());
760    ///                 start_count == 0 && *id == 1
761    ///             });
762    ///
763    ///         if let (Some(start), Some((_, end))) = (start_bound, end_bound) {
764    ///             c.move_to(start.start..end.end);
765    ///         }
766    ///     })
767    /// }
768    /// ```
769    #[allow(clippy::wrong_self_convention)]
770    pub fn from_caret_excl(self) -> Self {
771        Self {
772            matches: self.matches.range(self.caret_range.end..self.text_byte_len),
773            ..self
774        }
775    }
776
777    /// Searches over a range from the start of the [`Text`] to the
778    /// start of the caret's char
779    ///
780    /// ```rust
781    /// # duat_core::doc_duat!(duat);
782    /// # use duat::prelude::*;
783    /// fn remove_prefix(pa: &mut Pass, handle: &Handle) {
784    ///     let prefix_pat = format!(r"{}*\z", handle.opts(pa).word_chars_regex());
785    ///     handle.edit_all(pa, |mut c| {
786    ///         let prefix_range = c.search(&prefix_pat).to_caret().rev().next();
787    ///         if let Some(range) = prefix_range {
788    ///             c.move_to(range);
789    ///             c.replace("");
790    ///         }
791    ///     })
792    /// }
793    /// ```
794    #[allow(clippy::wrong_self_convention)]
795    pub fn to_caret(self) -> Self {
796        Self {
797            matches: self.matches.range(0..self.caret_range.start),
798            ..self
799        }
800    }
801
802    /// Searches over a range from the start of the [`Text`] to the
803    /// end of the caret's char
804    ///
805    /// ```rust
806    /// # duat_core::doc_duat!(duat);
807    /// # use duat::prelude::*;
808    /// fn last_word_in_selection(pa: &mut Pass, handle: &Handle) {
809    ///     let word_pat = format!(r"{}+", handle.opts(pa).word_chars_regex());
810    ///     handle.edit_all(pa, |mut c| {
811    ///         c.set_caret_on_end();
812    ///         let mut nth = c.search(&word_pat).to_caret_incl().rev().next();
813    ///         if let Some(range) = nth {
814    ///             c.move_to(range)
815    ///         } else {
816    ///             c.reset()
817    ///         }
818    ///     })
819    /// }
820    /// ```
821    #[allow(clippy::wrong_self_convention)]
822    pub fn to_caret_incl(self) -> Self {
823        Self {
824            matches: self.matches.range(0..self.caret_range.end),
825            ..self
826        }
827    }
828}
829
830impl<'c, R: RegexPattern> Iterator for CursorMatches<'c, R> {
831    type Item = R::Match;
832
833    fn next(&mut self) -> Option<Self::Item> {
834        self.matches.next()
835    }
836}
837
838impl<'c, R: RegexPattern> DoubleEndedIterator for CursorMatches<'c, R> {
839    fn next_back(&mut self) -> Option<Self::Item> {
840        self.matches.next_back()
841    }
842}
843
844/// An [`Iterator`] overf all [`Cursor`]s
845pub struct Cursors<'c, W: Widget + ?Sized> {
846    current: Vec<Option<ModSelection>>,
847    next_i: Rc<Cell<usize>>,
848    widget: &'c mut W,
849    area: &'c Area,
850}
851
852impl<'c, W: Widget + ?Sized> Cursors<'c, W> {
853    /// Creates a new [`Cursors`]
854    pub(crate) fn new(next_i: usize, widget: &'c mut W, area: &'c Area) -> Self {
855        Self {
856            current: Vec::new(),
857            next_i: Rc::new(Cell::new(next_i)),
858            widget,
859            area,
860        }
861    }
862}
863
864impl<'a, 'lend, W: Widget + ?Sized> Lending<'lend> for Cursors<'a, W> {
865    type Lend = Cursor<'lend, W>;
866}
867
868impl<'a, W: Widget + ?Sized> Lender for Cursors<'a, W> {
869    fn next<'lend>(&'lend mut self) -> Option<<Self as Lending<'lend>>::Lend> {
870        reinsert_selections(
871            self.current.drain(..).flatten(),
872            self.widget,
873            Some(&self.next_i),
874        );
875
876        let current_i = self.next_i.get();
877        let (selection, was_main) = self.widget.text_mut().selections_mut().remove(current_i)?;
878        self.current
879            .push(Some(ModSelection::new(selection, current_i, was_main)));
880
881        Some(Cursor::new(
882            &mut self.current,
883            0,
884            (self.widget, self.area),
885            Some(self.next_i.clone()),
886        ))
887    }
888}
889
890impl<'a, W: Widget + ?Sized> Drop for Cursors<'a, W> {
891    fn drop(&mut self) {
892        reinsert_selections(
893            self.current.drain(..).flatten(),
894            self.widget,
895            Some(&self.next_i),
896        );
897    }
898}
899
900/// Reinsert edited [`Selections`]
901#[inline]
902pub(crate) fn reinsert_selections(
903    selections: impl Iterator<Item = ModSelection>,
904    widget: &mut (impl Widget + ?Sized),
905    next_i: Option<&Cell<usize>>,
906) {
907    for mod_sel in selections {
908        let ([inserted_i, selections_taken], last_selection_overhangs) = widget
909            .text_mut()
910            .selections_mut()
911            .insert(mod_sel.index, mod_sel.selection, mod_sel.was_main);
912
913        if let Some(next_i) = next_i
914            && inserted_i <= next_i.get()
915        {
916            let go_to_next = !last_selection_overhangs as usize;
917            next_i.set(
918                next_i
919                    .get()
920                    .saturating_sub(selections_taken)
921                    .max(inserted_i)
922                    + go_to_next,
923            )
924        }
925    }
926}
927
928/// A struct representing the temporary state of a [`Selection`] in a
929/// [`Cursor`]
930#[derive(Clone, Debug)]
931pub(crate) struct ModSelection {
932    selection: Selection,
933    index: usize,
934    was_main: bool,
935    has_changed: bool,
936}
937
938impl ModSelection {
939    /// Returns a new `ModSelection`
940    pub(crate) fn new(selection: Selection, index: usize, was_main: bool) -> Self {
941        Self {
942            selection,
943            index,
944            was_main,
945            has_changed: false,
946        }
947    }
948}
949
950/// A position that a [`Cursor`] can move to
951///
952/// This will come either in the form of [`Point`]s or byte indices
953/// (in the form of `usize`). It can be a single value, like
954/// `Point::default()` or `3`, in which case only the [caret] will
955/// move, not affecting the [anchor].
956///
957/// Or it could be a [range], like `p1..p2` or `..=1000`, in which
958/// case the caret will be placed at the end, while the anchor will be
959/// placed at the start.
960///
961/// [caret]: Cursor::caret
962/// [anchor]: Cursor::anchor
963/// [range]: std::ops::RangeBounds
964pub trait CaretOrRange {
965    /// Internal movement function for monomorphization
966    #[doc(hidden)]
967    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>);
968}
969
970impl CaretOrRange for Point {
971    #[track_caller]
972    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
973        sel_mut!(cursor).move_to(self, cursor.widget.text());
974    }
975}
976
977impl CaretOrRange for usize {
978    #[track_caller]
979    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
980        sel_mut!(cursor).move_to(
981            cursor.widget.text().point_at_byte(self),
982            cursor.widget.text(),
983        )
984    }
985}
986
987impl<Idx: TextIndex> CaretOrRange for Range<Idx> {
988    #[track_caller]
989    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
990        let range = self.start.to_byte_index()..self.end.to_byte_index();
991        assert!(
992            range.start <= range.end,
993            "slice index start is larger than end"
994        );
995
996        sel_mut!(cursor).move_to(range.start, cursor.widget.text());
997        if range.start < range.end {
998            cursor.set_anchor();
999            sel_mut!(cursor).move_to(range.end, cursor.widget.text());
1000            if range.end < cursor.widget.text().len().byte() {
1001                cursor.move_hor(-1);
1002            }
1003        } else {
1004            cursor.unset_anchor();
1005        }
1006    }
1007}
1008
1009impl<Idx: TextIndex> CaretOrRange for RangeInclusive<Idx> {
1010    #[track_caller]
1011    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
1012        let range = self.start().to_byte_index()..=self.end().to_byte_index();
1013        assert!(
1014            range.start() <= range.end(),
1015            "slice index start is larger than end"
1016        );
1017
1018        sel_mut!(cursor).move_to(*range.start(), cursor.widget.text());
1019        cursor.set_anchor();
1020        sel_mut!(cursor).move_to(*range.end(), cursor.widget.text());
1021    }
1022}
1023
1024impl<Idx: TextIndex> CaretOrRange for RangeFrom<Idx> {
1025    #[track_caller]
1026    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
1027        let start = self.start.to_byte_index();
1028        sel_mut!(cursor).move_to(start, cursor.widget.text());
1029        if start < cursor.text().len().byte() {
1030            cursor.set_anchor();
1031            sel_mut!(cursor).move_to(cursor.widget.text().len(), cursor.widget.text());
1032            cursor.move_hor(-1);
1033        } else {
1034            cursor.unset_anchor();
1035        }
1036    }
1037}
1038
1039impl<Idx: TextIndex> CaretOrRange for RangeTo<Idx> {
1040    #[track_caller]
1041    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
1042        let end = self
1043            .end
1044            .to_byte_index()
1045            .min(cursor.text().last_point().byte());
1046        cursor.move_to_start();
1047        if 0 < end {
1048            cursor.set_anchor();
1049            sel_mut!(cursor).move_to(end, cursor.widget.text());
1050            cursor.move_hor(-1);
1051        } else {
1052            cursor.unset_anchor();
1053        }
1054    }
1055}
1056
1057impl<Idx: TextIndex> CaretOrRange for RangeToInclusive<Idx> {
1058    #[track_caller]
1059    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
1060        cursor.move_to_start();
1061        cursor.set_anchor();
1062        sel_mut!(cursor).move_to(self.end, cursor.widget.text());
1063    }
1064}
1065
1066impl CaretOrRange for RangeFull {
1067    #[track_caller]
1068    fn move_to<W: Widget + ?Sized>(self, cursor: &mut Cursor<'_, W>) {
1069        cursor.move_to_start();
1070        if cursor.text().len() > Point::default() {
1071            cursor.set_anchor();
1072            sel_mut!(cursor).move_to(cursor.widget.text().len(), cursor.widget.text());
1073        }
1074    }
1075}