Skip to main content

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