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, RefMut},
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, Parser},
19    opts::PrintOpts,
20    text::{Change, Lines, Point, RegexPattern, Searcher, Strs, Text, TextIndex, TextRange},
21    ui::{Area, Widget},
22};
23
24/// The [`Selection`] and [`Selections`] structs
25mod selections;
26
27/// A selection that can edit [`Text`], but can't alter selections
28///
29/// This struct will be used only inside functions passed to the
30/// [`edit_*`] family of methods from the [`Handle`].
31///
32/// To make edits, you can use three different functions. You can,
33/// those being [`replace`], [`insert`], and [`append`]. [`replace`]
34/// will completely replace the [`Selection`]'s selection. [`insert`]
35/// will place text behind the `caret`, and [`append`] will place it
36/// after the `caret`.
37///
38/// You can also move the [`Selection`]'s selection in many different
39/// ways, which are described below, in the `impl` section for this
40/// struct.
41///
42/// ```rust
43/// # duat_core::doc_duat!(duat);
44/// # use duat::prelude::*;
45/// # fn test(mut pa: Pass, handle: &mut Handle) {
46/// let sel: String = handle.edit_main(&mut pa, |mut c| {
47///     c.set_anchor();
48///     c.set_caret_on_end();
49///     c.replace("my replacement");
50///     c.append(" and my edit");
51///
52///     c.swap_ends();
53///     c.insert("This is ");
54///     c.swap_ends();
55///
56///     c.move_hor(" and my edit".chars().count() as i32);
57///     c.set_anchor();
58///     c.move_hor(-("This is my replacement and my edit".chars().count() as i32));
59///     c.selection().into_iter().collect()
60/// });
61///
62/// assert_eq!(&sel, "This is my replacement and my edit");
63/// # }
64/// ```
65///
66/// [`edit_*`]: crate::context::Handle::edit_nth
67/// [`Handle`]: crate::context::Handle
68/// [`replace`]: Cursor::replace
69/// [`insert`]: Cursor::insert
70/// [`append`]: Cursor::append
71pub struct Cursor<'a, W: Widget + ?Sized = crate::buffer::Buffer, S = ()> {
72    initial: Selection,
73    selection: Selection,
74    n: usize,
75    was_main: bool,
76    widget: &'a mut W,
77    area: &'a Area,
78    next_i: Option<Rc<Cell<usize>>>,
79    inc_searcher: &'a mut S,
80    is_copy: bool,
81}
82
83impl<'a, W: Widget + ?Sized, S> Cursor<'a, W, S> {
84    /// Returns a new instance of [`Cursor`]
85    pub(crate) fn new(
86        (selection, n, was_main): (Selection, usize, bool),
87        (widget, area): (&'a mut W, &'a Area),
88        next_i: Option<Rc<Cell<usize>>>,
89        searcher: &'a mut S,
90        is_copy: bool,
91    ) -> Self {
92        Self {
93            initial: selection.clone(),
94            selection,
95            n,
96            was_main,
97            widget,
98            area,
99            next_i,
100            inc_searcher: searcher,
101            is_copy,
102        }
103    }
104
105    ////////// Text editing
106
107    /// Replaces the entire selection with new text
108    ///
109    /// If there is a selection, then it is treated as _inclusive_,
110    /// therefore, a selection where `caret == anchor` will remove the
111    /// character where the caret is. If there is no selection, then
112    /// this has the same effect as [`insert`]. If you wish to
113    /// append to the `caret` instead, see [`append`].
114    ///
115    /// After replacing the sele tion, if the `caret` is behind the
116    /// `anchor` (or in the same spot), it will be placed on the start
117    /// of the selection, while the `anchor` will be placed on the
118    /// new end. If it is ahead, it will be placed ahead.
119    ///
120    /// [`insert`]: Self::insert
121    /// [`append`]: Self::append
122    pub fn replace(&mut self, edit: impl ToString) {
123        let change = {
124            let edit = edit.to_string();
125            let range = self.selection.point_range(self.widget.text());
126            let (p0, p1) = (range.start, range.end);
127            let p1 = if self.anchor().is_some() { p1 } else { p0 };
128            Change::new(edit, p0..p1, self.widget.text())
129        };
130
131        // Disconsider null changes.
132        if change.added_str().len() < 10 && change.added_str() == change.taken_str() {
133            return;
134        }
135
136        let (start, end) = (change.start(), change.added_end());
137
138        self.edit(change.clone());
139
140        let anchor_was_on_start = self.anchor_is_start();
141        self.move_to(start..end);
142        if !anchor_was_on_start {
143            self.set_caret_on_start();
144        }
145    }
146
147    /// Inserts new text directly behind the `caret`
148    ///
149    /// If the `anchor` is ahead of the `caret`, it will move forwards
150    /// by the number of chars in the new text.
151    ///
152    /// If you wish to replace the selected text, see [`replace`], if
153    /// you want to append after the `caret` instead, see [`append`]
154    ///
155    /// [`replace`]: Self::replace
156    /// [`append`]: Self::append
157    pub fn insert(&mut self, edit: impl ToString) {
158        let range = self.selection.caret()..self.selection.caret();
159        let change = Change::new(edit.to_string(), range, self.widget.text());
160        let (added, taken) = (change.added_end(), change.taken_end());
161
162        self.edit(change);
163
164        if let Some(anchor) = self.selection.anchor()
165            && anchor > self.selection.caret()
166        {
167            let new_anchor = anchor + added - taken;
168            self.selection.swap_ends();
169            self.selection.move_to(new_anchor, self.widget.text());
170            self.selection.swap_ends();
171        }
172    }
173
174    /// Appends new text directly after the `caret`
175    ///
176    /// If the `anchor` is ahead of the `caret`, it will move forwards
177    /// by the number of chars in the new text.
178    ///
179    /// If you wish to replace the selected text, see [`replace`], if
180    /// you want to insert before the `caret` instead, see [`insert`]
181    ///
182    /// [`replace`]: Self::replace
183    /// [`insert`]: Self::insert
184    pub fn append(&mut self, edit: impl ToString) {
185        let caret = self.selection.caret();
186        let p = caret.fwd(self.widget.text().char_at(caret).unwrap());
187        let change = Change::new(edit.to_string(), p..p, self.widget.text());
188        let (added, taken) = (change.added_end(), change.taken_end());
189
190        self.edit(change);
191
192        if let Some(anchor) = self.selection.anchor()
193            && anchor > p
194        {
195            let new_anchor = anchor + added - taken;
196            self.selection.swap_ends();
197            self.selection.move_to(new_anchor, self.widget.text());
198            self.selection.swap_ends();
199        }
200    }
201
202    /// Edits the buffer with a [`Change`]
203    fn edit(&mut self, change: Change<'static, String>) {
204        let text = self.widget.text_mut();
205        let (change_i, selections_taken) =
206            text.apply_change(self.selection.change_i.map(|i| i as usize), change);
207        self.selection.change_i = change_i.map(|i| i as u32);
208
209        // The Change may have happened before the index of the next curossr,
210        // so we need to account for that.
211        if let Some(change_i) = change_i
212            && let Some(next_i) = self.next_i.as_ref()
213            && change_i <= next_i.get()
214        {
215            next_i.set(next_i.get().saturating_sub(selections_taken));
216        }
217    }
218
219    ////////// Movement functions
220
221    /// Moves the selection horizontally. May cause vertical movement
222    ///
223    /// Returns the distance moved in chars.
224    #[track_caller]
225    pub fn move_hor(&mut self, count: i32) -> i32 {
226        self.selection.move_hor(count, self.widget.text())
227    }
228
229    /// Moves the selection vertically. May cause horizontal movement
230    ///
231    /// Returns the distance moved in lines.
232    #[track_caller]
233    pub fn move_ver(&mut self, count: i32) -> i32 {
234        self.selection.move_ver(
235            count,
236            self.widget.text(),
237            self.area,
238            self.widget.get_print_opts(),
239        )
240    }
241
242    /// Moves the selection vertically a number of wrapped lines. May
243    /// cause horizontal movement
244    ///
245    /// Returns the distance moved in wrapped lines.
246    #[track_caller]
247    pub fn move_ver_wrapped(&mut self, count: i32) {
248        self.selection.move_ver_wrapped(
249            count,
250            self.widget.text(),
251            self.area,
252            self.widget.get_print_opts(),
253        );
254    }
255
256    /// Moves the selection to a [`Point`] or a [range] of [`Point`]s
257    ///
258    /// If you give it just a [`Point`], it will move the caret,
259    /// without affecting the anchor. If you give it a [range] of
260    /// [`Point`]s, the anchor will be placed at the start, while the
261    /// caret will be placed at the end of said [range]. You can flip
262    /// those positions with a function like [`swap_ends`].
263    ///
264    /// If a [`Point`] is not valid, it will be corrected and clamped
265    /// to the lenght of the [`Text`].
266    ///
267    /// [range]: std::ops::RangeBounds
268    /// [`swap_ends`]: Self::swap_ends
269    #[track_caller]
270    pub fn move_to(&mut self, point_or_points: impl CaretOrRange) {
271        point_or_points.move_to(self);
272    }
273
274    /// Moves the selection to [`Point::default`], i.c., the start of
275    /// the [`Text`]
276    #[track_caller]
277    pub fn move_to_start(&mut self) {
278        self.selection.move_to(Point::default(), self.widget.text());
279    }
280
281    /// Moves the selection to a `line` and a `column`
282    ///
283    /// - If the coords isn't valid, it will move to the "maximum"
284    ///   position allowed.
285    #[track_caller]
286    pub fn move_to_coords(&mut self, line: usize, col: usize) {
287        let range = self
288            .text()
289            .line_range(line.min(self.text().last_point().line()));
290        let (p, _) = self
291            .text()
292            .chars_fwd(range.clone())
293            .unwrap()
294            .take(col + 1)
295            .last()
296            .unzip();
297        self.move_to(p.unwrap_or(range.end));
298    }
299
300    /// Moves to a column on the current line
301    #[track_caller]
302    pub fn move_to_col(&mut self, col: usize) {
303        let line = self.text().point_at_line(self.caret().line()).line();
304        self.move_to_coords(line, col);
305    }
306
307    /// Returns and takes the anchor of the [`Selection`].
308    pub fn unset_anchor(&mut self) -> Option<Point> {
309        self.selection.unset_anchor()
310    }
311
312    /// Sets the `anchor` to the current `caret`
313    pub fn set_anchor(&mut self) {
314        self.selection.set_anchor()
315    }
316
317    /// Sets the `anchor` if it was not already set
318    ///
319    /// Returns `true` if the anchor was set by this command.
320    pub fn set_anchor_if_needed(&mut self) -> bool {
321        if self.anchor().is_none() {
322            self.selection.set_anchor();
323            true
324        } else {
325            false
326        }
327    }
328
329    /// Swaps the position of the `caret` and `anchor`
330    pub fn swap_ends(&mut self) {
331        self.selection.swap_ends();
332    }
333
334    /// Sets the caret of the [`Selection`] on the start of the
335    /// selection
336    ///
337    /// Returns `true` if a swap occurred
338    pub fn set_caret_on_start(&mut self) -> bool {
339        if let Some(anchor) = self.anchor()
340            && anchor < self.caret()
341        {
342            self.swap_ends();
343            true
344        } else {
345            false
346        }
347    }
348
349    /// Sets the caret of the [`Selection`] on the end of the
350    /// selection
351    ///
352    /// Returns `true` if a swap occurred
353    pub fn set_caret_on_end(&mut self) -> bool {
354        if let Some(anchor) = self.anchor()
355            && anchor > self.caret()
356        {
357            self.swap_ends();
358            true
359        } else {
360            false
361        }
362    }
363
364    ////////// Selection meta manipulation
365
366    /// Resets the [`Selection`] to how it was before being modified
367    pub fn reset(&mut self) {
368        self.selection = self.initial.clone();
369    }
370
371    /// Copies the current [`Selection`] in place
372    ///
373    /// This will leave an additional [`Selection`] with the current
374    /// selection. Do note that normal intersection rules apply, so if
375    /// at the end of the movement, this selection intersects with any
376    /// other, they will be merged into one.
377    ///
378    /// When this [`Cursor`] is dropped, like with normal [`Cursor`]s,
379    /// its [`Selection`] will be added to the [`Selections`], unless
380    /// you [destroy] it.
381    ///
382    /// [destroy]: Self::destroy
383    pub fn copy(&mut self) -> Cursor<'_, W, S> {
384        Cursor::new(
385            (self.selection.clone(), self.n, false),
386            (self.widget, self.area),
387            self.next_i.clone(),
388            self.inc_searcher,
389            true,
390        )
391    }
392
393    /// Destroys the current [`Selection`]
394    ///
395    /// Will not destroy it if it is the last [`Selection`] left
396    ///
397    /// If this was the main selection, the main selection will now be
398    /// the selection immediately behind it.
399    pub fn destroy(mut self) {
400        // If it is 1, it is actually 2, because this Selection is also part
401        // of that list.
402        if !self.widget.text().selections().is_empty() || self.is_copy {
403            // Rc<Cell> needs to be manually dropped to reduce its counter.
404            self.next_i.take();
405            if self.was_main {
406                self.widget.text_mut().selections_mut().rotate_main(-1);
407            }
408            // The destructor is what inserts the Selection back into the list, so
409            // don't run it.
410            std::mem::forget(self);
411        } else {
412            // Just to be explicit.
413            drop(self);
414        }
415    }
416
417    /// Sets the "desired visual column"
418    ///
419    /// The desired visual column determines at what point in a line
420    /// the caret will be placed when moving [up and down] through
421    /// lines of varying lengths.
422    ///
423    /// Will also set the "desired wrapped visual column", which is
424    /// the same thing but used when moving vertically in a [wrapped]
425    /// fashion.
426    ///
427    /// [up and down]: Cursor::move_ver
428    /// [wrapped]: Cursor::move_ver_wrapped
429    pub fn set_desired_vcol(&mut self, x: usize) {
430        self.selection.set_desired_cols(x, x);
431    }
432
433    ////////// Iteration functions
434
435    /// Iterates over the [`char`]s
436    ///
437    /// This iteration will begin on the `caret`. It will also include
438    /// the [`Point`] of each `char`
439    pub fn chars_fwd(&self) -> impl Iterator<Item = (Point, char)> + '_ {
440        self.widget.text().chars_fwd(self.caret()..).unwrap()
441    }
442
443    /// Iterates over the [`char`]s, in reverse
444    ///
445    /// This iteration will begin on the `caret`. It will also include
446    /// the [`Point`] of each `char`
447    pub fn chars_rev(&self) -> impl Iterator<Item = (Point, char)> {
448        self.widget.text().chars_rev(..self.caret()).unwrap()
449    }
450
451    /// Wether the current selection matches a regex pattern
452    #[track_caller]
453    pub fn matches<R: RegexPattern>(&self, pat: R) -> bool {
454        let range = self.selection.byte_range(self.widget.text());
455        match self.widget.text().matches(pat, range) {
456            Ok(result) => result,
457            Err(err) => panic!("{err}"),
458        }
459    }
460
461    /// Searches the [`Text`] for a regex
462    ///
463    /// The search will begin on the `caret` and returns the
464    /// [`Range<usize>`] for the match, where the bounding `usize`s
465    /// represents a byte index, and can be directly used in, for
466    /// example, [`Cursor::move_to`].
467    ///
468    /// ```rust
469    /// # duat_core::doc_duat!(duat);
470    /// # use duat::prelude::*;
471    /// fn search_nth(pa: &mut Pass, handle: &Handle, n: usize, pat: &str) {
472    ///     handle.edit_all(pa, |mut c| {
473    ///         let mut nth = c.search_fwd(pat).nth(n);
474    ///         if let Some(range) = nth {
475    ///             c.move_to(range);
476    ///         }
477    ///     })
478    /// }
479    /// ```
480    ///
481    /// # Panics
482    ///
483    /// If the regex is not valid, this method will panic.
484    #[track_caller]
485    pub fn search_fwd<R: RegexPattern>(&self, pat: R) -> impl Iterator<Item = R::Match> + '_ {
486        let start = self.selection.caret();
487        let text = self.widget.text();
488        match text.search_fwd(pat, start..text.len()) {
489            Ok(iter) => iter,
490            Err(err) => panic!("{err}"),
491        }
492    }
493
494    /// Searches the [`Text`] for a regex, with an upper limit
495    ///
496    /// The search will begin on the `caret` and returns the
497    /// [`Range<usize>`] for the match, where the bounding `usize`s
498    /// represents a byte index, and can be directly used in, for
499    /// example, [`Cursor::move_to`].
500    ///
501    /// ```rust
502    /// # duat_core::doc_duat!(duat);
503    /// # use duat::prelude::*;
504    /// fn find_within(pa: &mut Pass, handle: &Handle, pat: &str) {
505    ///     handle.edit_all(pa, |mut c| {
506    ///         c.set_caret_on_start();
507    ///         let mut range = c.search_fwd_until(pat, c.range().end).next();
508    ///         if let Some(range) = range {
509    ///             c.move_to(range);
510    ///         }
511    ///     })
512    /// }
513    /// ```
514    ///
515    /// # Panics
516    ///
517    /// If the regex is not valid, this method will panic.
518    #[track_caller]
519    pub fn search_fwd_until<R: RegexPattern>(
520        &self,
521        pat: R,
522        until: impl TextIndex,
523    ) -> impl Iterator<Item = R::Match> + '_ {
524        let start = self.selection.caret();
525        let text = self.widget.text();
526        match text.search_fwd(pat, start.byte()..until.to_byte_index()) {
527            Ok(iter) => iter,
528            Err(err) => panic!("{err}"),
529        }
530    }
531
532    /// Searches the [`Text`] for a regex, skipping the caret's
533    /// character
534    ///
535    /// The search will begin one character afer the `caret` and
536    /// returns the [`Range<usize>`] for the match, where the
537    /// bounding `usize`s represents a byte index, and can be
538    /// directly used in, for example, [`Cursor::move_to`].
539    ///
540    /// ```rust
541    /// # duat_core::doc_duat!(duat);
542    /// # use duat::prelude::*;
543    /// fn next_paren_match(pa: &mut Pass, handle: &Handle) {
544    ///     handle.edit_all(pa, |mut c| {
545    ///         let mut start_count = 0;
546    ///         let mut start_bound = None;
547    ///         let end_bound = c.search_fwd_excl([r"\(", r"\)"]).find(|(id, range)| {
548    ///             start_count += ((*id == 0) as u32).saturating_sub((*id == 1) as u32);
549    ///             start_bound = (*id == 0 && start_count == 0).then_some(range.clone());
550    ///             start_count == 0 && *id == 1
551    ///         });
552    ///
553    ///         if let (Some(start), Some((_, end))) = (start_bound, end_bound) {
554    ///             c.move_to(start.start..end.end);
555    ///         }
556    ///     })
557    /// }
558    /// ```
559    ///
560    /// # Panics
561    ///
562    /// If the regex is not valid, this method will panic.
563    #[track_caller]
564    pub fn search_fwd_excl<R: RegexPattern>(&self, pat: R) -> impl Iterator<Item = R::Match> + '_ {
565        let start = self.selection.caret();
566        let text = self.widget.text();
567        match text.search_fwd(
568            pat,
569            start.byte() + self.char().len_utf8()..text.len().byte(),
570        ) {
571            Ok(iter) => iter,
572            Err(err) => panic!("{err}"),
573        }
574    }
575
576    /// Searches the [`Text`] for a regex, with an upper limit and
577    /// skipping the caret's character
578    ///
579    /// The search will begin one character afer the `caret` and
580    /// returns the [`Range<usize>`] for the match, where the
581    /// bounding `usize`s represents a byte index, and can be
582    /// directly used in, for example, [`Cursor::move_to`].
583    ///
584    /// ```rust
585    /// # duat_core::doc_duat!(duat);
586    /// # use duat::prelude::*;
587    /// # #[derive(Clone, Copy)]
588    /// # struct Insert;
589    /// # impl Mode for Insert {
590    /// #     type Widget = Buffer;
591    /// #     fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle<Self::Widget>) {}
592    /// # }
593    /// #[derive(Clone, Copy)]
594    /// enum Action {
595    ///     Move,
596    ///     Select,
597    ///     Delete,
598    ///     Change,
599    /// }
600    ///
601    /// fn f_key_from_vim(pa: &mut Pass, handle: &Handle, char: char, n: usize, action: Action) {
602    ///     handle.edit_all(pa, |mut c| {
603    ///         let line_end = c.search_fwd('\n').next().unwrap();
604    ///         let mut nth = c.search_fwd_excl_until(char, line_end.start).nth(n);
605    ///
606    ///         match (nth, action) {
607    ///             (Some(range), Action::Move) => {
608    ///                 c.unset_anchor();
609    ///                 c.move_to(range.start);
610    ///             }
611    ///             (Some(range), Action::Select) => {
612    ///                 c.set_anchor_if_needed();
613    ///                 c.move_to(range.start);
614    ///             }
615    ///             (Some(range), Action::Delete | Action::Change) => {
616    ///                 c.set_anchor_if_needed();
617    ///                 c.move_to(range.start);
618    ///                 c.replace("");
619    ///             }
620    ///             _ => {}
621    ///         }
622    ///     });
623    ///
624    ///     if let Action::Change = action {
625    ///         mode::set(Insert);
626    ///     }
627    /// }
628    /// ```
629    ///
630    /// # Panics
631    ///
632    /// If the regex is not valid, this method will panic.
633    #[track_caller]
634    pub fn search_fwd_excl_until<R: RegexPattern>(
635        &self,
636        pat: R,
637        until: impl TextIndex,
638    ) -> impl Iterator<Item = R::Match> + '_ {
639        let start = self.selection.caret();
640        let text = self.widget.text();
641        match text.search_fwd(
642            pat,
643            start.byte() + self.char().len_utf8()..until.to_byte_index(),
644        ) {
645            Ok(iter) => iter,
646            Err(err) => panic!("{err}"),
647        }
648    }
649
650    /// Searches the [`Text`] for a regex, in reverse
651    ///
652    /// The search will begin on the `caret` and returns the
653    /// [`Range<usize>`] for the match, where the bounding `usize`s
654    /// represents a byte index, and can be directly used in, for
655    /// example, [`Cursor::move_to`].
656    ///
657    /// ```rust
658    /// # duat_core::doc_duat!(duat);
659    /// # use duat::prelude::*;
660    /// fn remove_prefix(pa: &mut Pass, handle: &Handle) {
661    ///     let prefix_pat = format!(r"{}*\z", handle.opts(pa).word_chars_regex());
662    ///     handle.edit_all(pa, |mut c| {
663    ///         let prefix_range = c.search_rev(&prefix_pat).next();
664    ///         if let Some(range) = prefix_range {
665    ///             c.move_to(range);
666    ///             c.replace("");
667    ///         }
668    ///     })
669    /// }
670    /// ```
671    ///
672    /// # Panics
673    ///
674    /// If the regex is not valid, this method will panic.
675    #[track_caller]
676    pub fn search_rev<R: RegexPattern>(&self, pat: R) -> impl Iterator<Item = R::Match> + '_ {
677        let end = self.selection.caret();
678        let text = self.widget.text();
679        match text.search_rev(pat, Point::default()..end) {
680            Ok(iter) => iter,
681            Err(err) => panic!("{err}"),
682        }
683    }
684
685    /// Searches the [`Text`] for a regex, in reverse, until a given
686    /// point
687    ///
688    /// The search will begin on the `caret` and returns the
689    /// [`Range<usize>`] for the match, where the bounding `usize`s
690    /// represents a byte index, and can be directly used in, for
691    /// example, [`Cursor::move_to`].
692    ///
693    /// ```rust
694    /// # duat_core::doc_duat!(duat);
695    /// # use duat::prelude::*;
696    /// fn search_before_but_after_prev(pa: &mut Pass, handle: &Handle, pat: &str) {
697    ///     let mut last_end = 0;
698    ///     handle.edit_all(pa, |mut c| {
699    ///         let mut nth = c.search_rev_until(pat, last_end).next();
700    ///         if let Some(range) = nth {
701    ///             c.move_to(range)
702    ///         }
703    ///         last_end = c.range().end.byte();
704    ///     })
705    /// }
706    /// ```
707    ///
708    /// # Panics
709    ///
710    /// If the regex is not valid, this method will panic.
711    #[track_caller]
712    pub fn search_rev_until<R: RegexPattern>(
713        &self,
714        pat: R,
715        until: impl TextIndex,
716    ) -> impl Iterator<Item = R::Match> + '_ {
717        let end = self.selection.caret();
718        let start = until.to_byte_index();
719        let text = self.widget.text();
720        match text.search_rev(pat, start..end.byte()) {
721            Ok(iter) => iter,
722            Err(err) => panic!("{err}"),
723        }
724    }
725
726    /// Searches the [`Text`] for a regex, in reverse, including the
727    /// caret's character
728    ///
729    /// The search will begin on the character right after the `caret`
730    /// and returns the [`Range<usize>`] for the match, where the
731    /// bounding `usize`s represents a byte index, and can be
732    /// directly used in, for example, [`Cursor::move_to`].
733    ///
734    /// ```rust
735    /// # duat_core::doc_duat!(duat);
736    /// # use duat::prelude::*;
737    /// fn last_word_in_selection(pa: &mut Pass, handle: &Handle) {
738    ///     let word_pat = format!(r"{}+", handle.opts(pa).word_chars_regex());
739    ///     handle.edit_all(pa, |mut c| {
740    ///         c.set_caret_on_end();
741    ///         let mut nth = c.search_rev_incl(&word_pat).next();
742    ///         if let Some(range) = nth {
743    ///             c.move_to(range)
744    ///         } else {
745    ///             c.reset()
746    ///         }
747    ///     })
748    /// }
749    /// ```
750    ///
751    /// # Panics
752    ///
753    /// If the regex is not valid, this method will panic.
754    #[track_caller]
755    pub fn search_rev_incl<R: RegexPattern>(&self, pat: R) -> impl Iterator<Item = R::Match> + '_ {
756        let end = self.selection.caret();
757        let text = self.widget.text();
758        match text.search_rev(pat, ..end.byte() + self.char().len_utf8()) {
759            Ok(iter) => iter,
760            Err(err) => panic!("{err}"),
761        }
762    }
763
764    /// Searches the [`Text`] for a regex, in reverse, including the
765    /// caret's character, until a given point
766    ///
767    /// The search will begin on the character right after the `caret`
768    /// and returns the [`Range<usize>`] for the match, where the
769    /// bounding `usize`s represents a byte index, and can be
770    /// directly used in, for example, [`Cursor::move_to`].
771    ///
772    /// ```rust
773    /// # duat_core::doc_duat!(duat);
774    /// # use duat::prelude::*;
775    /// fn last_word_limited_to_selection(pa: &mut Pass, handle: &Handle) {
776    ///     let word_pat = format!(r"{}+", handle.opts(pa).word_chars_regex());
777    ///     handle.edit_all(pa, |mut c| {
778    ///         c.set_caret_on_end();
779    ///         let start = c.range().start;
780    ///         let mut nth = c.search_rev_incl_until(&word_pat, start).next();
781    ///         if let Some(range) = nth {
782    ///             c.move_to(range)
783    ///         } else {
784    ///             c.reset()
785    ///         }
786    ///     })
787    /// }
788    /// ```
789    ///
790    /// # Panics
791    ///
792    /// If the regex is not valid, this method will panic.
793    #[track_caller]
794    pub fn search_rev_incl_until<R: RegexPattern>(
795        &self,
796        pat: R,
797        until: impl TextIndex,
798    ) -> impl Iterator<Item = R::Match> + '_ {
799        let end = self.selection.caret();
800        let start = until.to_byte_index();
801        let text = self.widget.text();
802        match text.search_rev(pat, start..end.byte() + self.char().len_utf8()) {
803            Ok(iter) => iter,
804            Err(err) => panic!("{err}"),
805        }
806    }
807
808    ////////// Text queries
809
810    /// Returns the [`char`] in the `caret`
811    pub fn char(&self) -> char {
812        self.text()
813            .char_at(self.selection.caret())
814            .unwrap_or_else(|| panic!("{:#?}\n{:#?}", self.selection.caret(), self.text()))
815    }
816
817    /// Returns the [`char`] at a given [`Point`]
818    pub fn char_at(&self, i: impl TextIndex) -> Option<char> {
819        self.text().char_at(i)
820    }
821
822    /// Returns the [`Selection`]'s selection
823    ///
824    /// The reason why this return value is `IntoIter<&str, 2>` is
825    /// because the [`Text`] utilizes an underlying [`GapBuffer`]
826    /// to store the characters. This means that the text is
827    /// always separated into two distinct chunks.
828    ///
829    /// If this [`Selection`]'s selection happens to be entirely
830    /// within one of these chunks, the other `&str` will just be
831    /// empty.
832    ///
833    /// [`GapBuffer`]: gapbuf::GapBuffer
834    pub fn selection(&self) -> Strs<'_> {
835        let range = self.selection.byte_range(self.text());
836        self.text().strs(range).unwrap()
837    }
838
839    /// Returns the [`Strs`] for the given [`TextRange`]
840    ///
841    /// [`GapBuffer`]: gapbuf::GapBuffer
842    pub fn strs(&self, range: impl TextRange) -> Option<Strs<'_>> {
843        self.widget.text().strs(range)
844    }
845
846    /// Returns the length of the [`Text`], in [`Point`]
847    pub fn len(&self) -> Point {
848        self.text().len()
849    }
850
851    /// Returns the position of the last [`char`] if there is one
852    pub fn last_point(&self) -> Point {
853        self.text().last_point()
854    }
855
856    /// An [`Iterator`] over the lines the `Cursor`'s range
857    pub fn lines(&self) -> Lines<'_> {
858        self.widget.text().lines(self.range())
859    }
860
861    /// An [`Iterator`] over the lines in a given [range]
862    ///
863    /// [range]: TextRange
864    pub fn lines_on(&self, range: impl TextRange) -> Lines<'_> {
865        self.widget.text().lines(range)
866    }
867
868    /// Gets the current level of indentation
869    pub fn indent(&self) -> usize {
870        self.widget
871            .text()
872            .indent(self.caret(), self.area, self.opts())
873    }
874
875    /// Gets the indentation level on the given [`Point`]
876    pub fn indent_on(&self, p: Point) -> usize {
877        self.widget.text().indent(p, self.area, self.opts())
878    }
879
880    ////////// Selection queries
881
882    /// Returns the `caret`
883    pub fn caret(&self) -> Point {
884        self.selection.caret()
885    }
886
887    /// Returns the `anchor`
888    pub fn anchor(&self) -> Option<Point> {
889        self.selection.anchor()
890    }
891
892    /// The [`Point`] range of the [`Selection`]
893    ///
894    /// This is an _inclusive_ range (not Rust's [`RangeInclusive`]
895    /// however), this means that, even if there is no anchor, the
896    /// lenght of this range will always be at least 1.
897    ///
898    /// If you want an exclusive range, see [`Cursor::range_excl`]
899    ///
900    /// [`RangeInclusive`]: std::ops::RangeInclusive
901    pub fn range(&self) -> Range<Point> {
902        self.selection.point_range(self.text())
903    }
904
905    /// An exclusive [`Point`] range of the [`Selection`]
906    ///
907    /// If you wish for an inclusive range, whose length is always
908    /// greater than or equal to 1, see [`RangeInclusive`].
909    pub fn range_excl(&self) -> Range<Point> {
910        self.selection.point_range_excl()
911    }
912
913    /// The [`VPoint`] range of the [`Selection`]
914    ///
915    /// Use only if you need the things that the [`VPoint`] provides,
916    /// in order to preven extraneous calculations
917    pub fn v_caret(&self) -> VPoint {
918        self.selection
919            .v_caret(self.widget.text(), self.area, self.widget.get_print_opts())
920    }
921
922    /// The [`VPoint`] of the anchor, if it exists
923    ///
924    /// Use only if you need the things that the [`VPoint`] provides,
925    /// in order to preven extraneous calculations
926    pub fn v_anchor(&self) -> Option<VPoint> {
927        self.selection
928            .v_anchor(self.widget.text(), self.area, self.widget.get_print_opts())
929    }
930
931    /// Returns `true` if the `anchor` exists before the `caret`
932    pub fn anchor_is_start(&self) -> bool {
933        self.anchor().is_none_or(|anchor| anchor <= self.caret())
934    }
935
936    /// Whether or not this is the main [`Selection`]
937    pub fn is_main(&self) -> bool {
938        self.was_main
939    }
940
941    /// The [`Text`] of the [`Widget`]
942    pub fn text(&self) -> &Text {
943        self.widget.text()
944    }
945
946    /// The [`PrintOpts`] in use
947    pub fn opts(&self) -> PrintOpts {
948        self.widget.get_print_opts()
949    }
950}
951
952impl<S> Cursor<'_, Buffer, S> {
953    /// Reads the [`Bytes`] and a [`Parser`]
954    ///
955    /// [`Bytes`]: crate::text::Bytes
956    pub fn read_parser<Rd: Parser, Ret>(&self, read: impl FnOnce(&Rd) -> Ret) -> Option<Ret> {
957        self.widget.read_parser(read)
958    }
959}
960
961/// Incremental search functions, only available on [`IncSearcher`]s
962///
963/// [`IncSearcher`]: https://docs.rs/duat/latest/duat/modes/struct.IncSearcher.html
964impl<W: Widget + ?Sized> Cursor<'_, W, Searcher> {
965    /// Search incrementally from an [`IncSearch`] request
966    ///
967    /// This will match the Regex pattern from the current position of
968    /// the caret. if `end` is [`Some`], the search will end at the
969    /// requested [`Point`].
970    ///
971    /// [`IncSearch`]: https://docs.rs/duat/latest/duat/modes/struct.IncSearch.html
972    pub fn search_inc_fwd(
973        &mut self,
974        end: Option<Point>,
975    ) -> impl Iterator<Item = Range<usize>> + '_ {
976        let range = if let Some(end) = end {
977            (self.selection.caret()..end).to_range(self.text().len().byte())
978        } else {
979            (self.selection.caret()..).to_range(self.text().len().byte())
980        };
981        self.inc_searcher.search_fwd(self.widget.text(), range)
982    }
983
984    /// Search incrementally from an [`IncSearch`] request in reverse
985    ///
986    /// This will match the Regex pattern from the current position of
987    /// the caret in reverse. if `start` is [`Some`], the search will
988    /// end at the requested [`Point`].
989    ///
990    /// [`IncSearch`]: https://docs.rs/duat/latest/duat/modes/struct.IncSearch.html
991    pub fn search_inc_rev(
992        &mut self,
993        start: Option<Point>,
994    ) -> impl Iterator<Item = Range<usize>> + '_ {
995        let range = if let Some(start) = start {
996            (start..self.selection.caret()).to_range(self.text().len().byte())
997        } else {
998            (..self.selection.caret()).to_range(self.text().len().byte())
999        };
1000        self.inc_searcher.search_rev(self.widget.text(), range)
1001    }
1002
1003    /// Whether the [`Selection`]'s selection matches the
1004    /// [`IncSearch`] request
1005    ///
1006    /// [`IncSearch`]: https://docs.rs/duat/latest/duat/modes/struct.IncSearch.html
1007    pub fn matches_inc(&mut self) -> bool {
1008        let range = self.selection.byte_range(self.widget.text());
1009        self.inc_searcher
1010            .matches(self.widget.text().strs(range).unwrap().to_string().as_str())
1011    }
1012}
1013
1014// SAFETY: In theory, it should be impossible to maintain a reference
1015// to W after it has dropped, since the Handle would be mutably
1016// borrowing from said W, and you can only get a Cursor from Handles.
1017// Thus, the only thing which may have been dropped is the Selections
1018// within, which are accounted for.
1019unsafe impl<#[may_dangle] 'a, W: Widget + ?Sized + 'a, S: 'a> Drop for Cursor<'a, W, S> {
1020    fn drop(&mut self) {
1021        let selection = std::mem::take(&mut self.selection);
1022        let ([inserted_i, selections_taken], last_selection_overhangs) = self
1023            .widget
1024            .text_mut()
1025            .selections_mut()
1026            .insert(self.n, selection, self.was_main);
1027
1028        if let Some(next_i) = self.next_i.as_ref()
1029            && inserted_i <= next_i.get()
1030        {
1031            let go_to_next = !last_selection_overhangs as usize;
1032            next_i.set(
1033                next_i
1034                    .get()
1035                    .saturating_sub(selections_taken)
1036                    .max(inserted_i)
1037                    + go_to_next,
1038            )
1039        }
1040    }
1041}
1042
1043impl<'a, W: Widget + ?Sized, S> std::fmt::Debug for Cursor<'a, W, S> {
1044    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1045        f.debug_struct("Cursor")
1046            .field("selection", &self.selection)
1047            .finish_non_exhaustive()
1048    }
1049}
1050
1051/// An [`Iterator`] overf all [`Cursor`]s
1052pub struct Cursors<'a, W: Widget + ?Sized, S> {
1053    next_i: Rc<Cell<usize>>,
1054    widget: &'a mut W,
1055    area: &'a Area,
1056    inc_searcher: RefMut<'a, S>,
1057}
1058
1059impl<'a, W: Widget + ?Sized, S> Cursors<'a, W, S> {
1060    /// Creates a new [`Cursors`]
1061    pub(crate) fn new(
1062        next_i: usize,
1063        widget: &'a mut W,
1064        area: &'a Area,
1065        inc_searcher: RefMut<'a, S>,
1066    ) -> Self {
1067        Self {
1068            next_i: Rc::new(Cell::new(next_i)),
1069            widget,
1070            area,
1071            inc_searcher,
1072        }
1073    }
1074}
1075
1076impl<'a, 'lend, W: Widget + ?Sized, S> Lending<'lend> for Cursors<'a, W, S> {
1077    type Lend = Cursor<'lend, W, S>;
1078}
1079
1080impl<'a, W: Widget + ?Sized, S> Lender for Cursors<'a, W, S> {
1081    fn next<'lend>(&'lend mut self) -> Option<<Self as Lending<'lend>>::Lend> {
1082        let current_i = self.next_i.get();
1083        let (selection, was_main) = self.widget.text_mut().selections_mut().remove(current_i)?;
1084
1085        Some(Cursor::new(
1086            (selection, current_i, was_main),
1087            (self.widget, self.area),
1088            Some(self.next_i.clone()),
1089            &mut self.inc_searcher,
1090            false,
1091        ))
1092    }
1093}
1094
1095/// A position that a [`Cursor`] can move to
1096///
1097/// This will come either in the form of [`Point`]s or byte indices
1098/// (in the form of `usize`). It can be a single value, like
1099/// `Point::default()` or `3`, in which case only the [caret] will
1100/// move, not affecting the [anchor].
1101///
1102/// Or it could be a [range], like `p1..p2` or `..=1000`, in which
1103/// case the caret will be placed at the end, while the anchor will be
1104/// placed at the start.
1105///
1106/// [caret]: Cursor::caret
1107/// [anchor]: Cursor::anchor
1108/// [range]: std::ops::RangeBounds
1109pub trait CaretOrRange {
1110    /// Internal movement function for monomorphization
1111    #[doc(hidden)]
1112    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>);
1113}
1114
1115impl CaretOrRange for Point {
1116    #[track_caller]
1117    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1118        cursor.selection.move_to(self, cursor.widget.text());
1119    }
1120}
1121
1122impl CaretOrRange for Range<Point> {
1123    #[track_caller]
1124    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1125        assert!(
1126            self.start <= self.end,
1127            "slice index start is larger than end"
1128        );
1129
1130        cursor.selection.move_to(self.start, cursor.widget.text());
1131        if self.start < self.end {
1132            cursor.set_anchor();
1133            cursor.selection.move_to(self.end, cursor.widget.text());
1134            if self.end < cursor.widget.text().len() {
1135                cursor.move_hor(-1);
1136            }
1137        } else {
1138            cursor.unset_anchor();
1139        }
1140    }
1141}
1142
1143impl CaretOrRange for RangeInclusive<Point> {
1144    #[track_caller]
1145    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1146        assert!(
1147            self.start() <= self.end(),
1148            "slice index start is larger than end"
1149        );
1150
1151        cursor
1152            .selection
1153            .move_to(*self.start(), cursor.widget.text());
1154        cursor.set_anchor();
1155        cursor.selection.move_to(*self.end(), cursor.widget.text());
1156    }
1157}
1158
1159impl CaretOrRange for RangeFrom<Point> {
1160    #[track_caller]
1161    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1162        cursor.selection.move_to(self.start, cursor.widget.text());
1163        if self.start < cursor.text().len() {
1164            cursor.set_anchor();
1165            cursor
1166                .selection
1167                .move_to(cursor.widget.text().len(), cursor.widget.text());
1168            cursor.move_hor(-1);
1169        } else {
1170            cursor.unset_anchor();
1171        }
1172    }
1173}
1174
1175impl CaretOrRange for RangeTo<Point> {
1176    #[track_caller]
1177    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1178        cursor.move_to_start();
1179        if Point::default() < self.end {
1180            cursor.set_anchor();
1181            cursor.selection.move_to(self.end, cursor.widget.text());
1182            cursor.move_hor(-1);
1183        } else {
1184            cursor.unset_anchor();
1185        }
1186    }
1187}
1188
1189impl CaretOrRange for RangeToInclusive<Point> {
1190    #[track_caller]
1191    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1192        cursor.move_to_start();
1193        cursor.set_anchor();
1194        cursor.selection.move_to(self.end, cursor.widget.text());
1195    }
1196}
1197
1198impl CaretOrRange for usize {
1199    #[track_caller]
1200    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1201        cursor.selection.move_to(
1202            cursor.widget.text().point_at_byte(self),
1203            cursor.widget.text(),
1204        )
1205    }
1206}
1207
1208impl CaretOrRange for Range<usize> {
1209    #[track_caller]
1210    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1211        assert!(
1212            self.start <= self.end,
1213            "slice index start is larger than end"
1214        );
1215
1216        cursor.selection.move_to(
1217            cursor.widget.text().point_at_byte(self.start),
1218            cursor.widget.text(),
1219        );
1220        if self.start < self.end {
1221            cursor.set_anchor();
1222            cursor.selection.move_to(
1223                cursor.widget.text().point_at_byte(self.end),
1224                cursor.widget.text(),
1225            );
1226            if self.end < cursor.widget.text().len().byte() {
1227                cursor.move_hor(-1);
1228            }
1229        } else {
1230            cursor.unset_anchor();
1231        }
1232    }
1233}
1234
1235impl CaretOrRange for RangeInclusive<usize> {
1236    #[track_caller]
1237    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1238        assert!(
1239            self.start() <= self.end(),
1240            "slice index start is larger than end"
1241        );
1242
1243        cursor.selection.move_to(
1244            cursor.widget.text().point_at_byte(*self.start()),
1245            cursor.widget.text(),
1246        );
1247        cursor.set_anchor();
1248        cursor.selection.move_to(
1249            cursor.widget.text().point_at_byte(*self.end()),
1250            cursor.widget.text(),
1251        );
1252    }
1253}
1254
1255impl CaretOrRange for RangeFrom<usize> {
1256    #[track_caller]
1257    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1258        cursor.selection.move_to(
1259            cursor.widget.text().point_at_byte(self.start),
1260            cursor.widget.text(),
1261        );
1262        if self.start < cursor.text().len().byte() {
1263            cursor.set_anchor();
1264            cursor
1265                .selection
1266                .move_to(cursor.widget.text().len(), cursor.widget.text());
1267            cursor.move_hor(-1);
1268        } else {
1269            cursor.unset_anchor();
1270        }
1271    }
1272}
1273
1274impl CaretOrRange for RangeTo<usize> {
1275    #[track_caller]
1276    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1277        cursor.move_to_start();
1278        if self.end > 0 {
1279            cursor.set_anchor();
1280            cursor.selection.move_to(
1281                cursor.widget.text().point_at_byte(self.end),
1282                cursor.widget.text(),
1283            );
1284            cursor.move_hor(-1);
1285        } else {
1286            cursor.unset_anchor();
1287        }
1288    }
1289}
1290
1291impl CaretOrRange for RangeToInclusive<usize> {
1292    #[track_caller]
1293    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1294        cursor.move_to_start();
1295        cursor.set_anchor();
1296        cursor.selection.move_to(
1297            cursor.widget.text().point_at_byte(self.end),
1298            cursor.widget.text(),
1299        );
1300    }
1301}
1302
1303impl CaretOrRange for RangeFull {
1304    #[track_caller]
1305    fn move_to<W: Widget + ?Sized, S>(self, cursor: &mut Cursor<'_, W, S>) {
1306        cursor.move_to_start();
1307        if cursor.text().len() > Point::default() {
1308            cursor.set_anchor();
1309            cursor
1310                .selection
1311                .move_to(cursor.widget.text().len(), cursor.widget.text());
1312        }
1313    }
1314}