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