duat_core/mode/helper/
mod.rs

1//! A helper struct for [`Mode`]s with [`Cursors`]
2//!
3//! This struct can edit [`Text`] in a declarative way, freeing the
4//! [`Mode`]s from worrying about synchronization of the
5//! cursors and dealing with editing the text directly.
6//!
7//! [`Mode`]: super::Mode
8use std::{cell::Cell, rc::Rc};
9
10use lender::{Lender, Lending};
11
12pub use self::cursors::{Cursor, Cursors, VPoint};
13use crate::{
14    cfg::PrintCfg,
15    text::{Change, Point, Reader, RegexPattern, Searcher, Strs, Text, TextRange},
16    ui::Area,
17    widgets::{File, Widget},
18};
19
20/// The [`Cursor`] and [`Cursors`] structs
21mod cursors;
22
23/// A struct used by [`Mode`]s to edit [`Text`]
24///
25/// You will want to use this struct when editing [`Widget`]s
26/// with [`Cursors`]. For example, let's say you want to create an
27/// mode for the [`File`] widget:
28///
29/// ```rust
30/// # use duat_core::{mode::{EditHelper, Mode, KeyEvent, Cursors}, ui::Ui, widgets::File};
31/// /// A very basic example Mode.
32/// #[derive(Clone)]
33/// struct PlacesCharactersAndMoves;
34///
35/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
36///     type Widget = File;
37///     /* ... */
38/// #   fn send_key(&mut self, key: KeyEvent, widget: &mut File, area: &U::Area) {
39/// #       todo!();
40/// #   }
41/// # }
42/// ```
43///
44/// In order to modify the widget, you must implement the
45/// [`Mode::send_key`] method. In it, you receive the following:
46///
47/// - The [key].
48/// - A [`&mut Self::Widget`].
49/// - An [`Area`], which you can resize and modify it in other ways.
50/// - The current [`Cursors`] of the widget, these should be modified
51///   by the [`EditHelper`].
52///
53/// In a [`Mode`] without cursors, you'd probably want to run
54/// [`Cursors::clear`], in order to make sure there are no cursors.
55///
56/// ```rust
57/// # use duat_core::{
58/// #     mode::{key, Cursors, EditHelper, Mode, KeyCode, KeyEvent}, ui::Ui, widgets::File,
59/// # };
60/// # #[derive(Clone)]
61/// # struct PlacesCharactersAndMoves;
62/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
63/// #   type Widget = File;
64///     /* ... */
65///     fn send_key(&mut self, key: KeyEvent, widget: &mut File, area: &U::Area) {
66///         match key {
67///             // actions based on the key pressed
68///             key!(KeyCode::Char('c')) => {
69///                 /* Do something when the character 'c' is typed. */
70///             }
71///             /* Matching the rest of the keys */
72/// #           _ => todo!()
73///         }
74///     }
75/// # }
76/// ```
77///
78/// (You can use the [`key!`] macro in order to match [`KeyEvent`]s).
79///
80/// With the `EditHelper`, you can modify [`Text`] in a simplified
81/// way. This is done by two actions, [editing] and [moving]. You
82/// can only do one of these on any number of cursors at the same
83/// time.
84///
85/// ```rust
86/// # use duat_core::{
87/// #     mode::{ key, Cursors, EditHelper, Mode, KeyCode, KeyEvent, KeyMod}, ui::Ui, widgets::File,
88/// # };
89/// # #[derive(Clone)]
90/// # struct PlacesCharactersAndMoves;
91/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
92/// #   type Widget = File;
93///     /* ... */
94///     fn send_key(&mut self, key: KeyEvent, file: &mut File, area: &U::Area) {
95///         let mut helper = EditHelper::new(file, area);
96///         
97///         match key {
98///             key!(KeyCode::Char(c)) => {
99///                 helper.edit_many(.., |e| e.insert('c'));
100///                 helper.move_many(.., |mut m| { m.move_hor(1); });
101///             },
102///             key!(KeyCode::Right, KeyMod::SHIFT) => {
103///                 helper.move_many(.., |mut m| {
104///                     if m.anchor().is_none() {
105///                         m.set_anchor()
106///                     }
107///                     m.move_hor(1);
108///                 })
109///             }
110///             key!(KeyCode::Right) => {
111///                 helper.move_many(.., |mut m| {
112///                     m.unset_anchor();
113///                     m.move_hor(1);
114///                 })
115///             }
116///             /* Predictable remaining implementations */
117/// #           _ => todo!()
118///         }
119///     }
120/// # }
121/// ```
122///
123/// [`Mode`]: super::Mode
124/// [`Text`]: crate::text::Text
125/// [`PromptLine`]: crate::widgets::PromptLine
126/// [`&mut Self::Widget`]: super::Mode::Widget
127/// [`Mode::send_key`]: super::Mode::send_key
128/// [key]: super::KeyEvent
129/// [`Self::Widget`]: super::Mode::Widget
130/// [`Some(cursors)`]: Some
131/// [`Ui::Area`]: crate::ui::Ui::Area
132/// [commands]: crate::cmd
133/// [`key!`]: super::key
134/// [`KeyEvent`]: super::KeyEvent
135/// [editing]: Editor
136/// [moving]: Mover
137pub struct EditHelper<'a, W: Widget<A::Ui>, A: Area, S> {
138    widget: &'a mut W,
139    area: &'a A,
140    inc_searcher: S,
141}
142
143impl<'a, W: Widget<A::Ui>, A: Area> EditHelper<'a, W, A, ()> {
144    /// Returns a new instance of [`EditHelper`]
145    pub fn new(widget: &'a mut W, area: &'a A) -> Self {
146        widget.text_mut().enable_cursors();
147        widget.cursors_mut().unwrap().populate();
148        EditHelper { widget, area, inc_searcher: () }
149    }
150}
151
152impl<W: Widget<A::Ui>, A: Area, S> EditHelper<'_, W, A, S> {
153    ////////// Editing functions
154
155    /// Edits the nth [`Cursor`] in the [`Text`]
156    ///
157    /// Once dropped, the [`Cursor`] in this [`Editor`] will be added
158    /// back to the list of [`Cursor`]s, unless it is [destroyed]
159    ///
160    /// If you want to edit on the main cursor, see [`edit_main`], if
161    /// you want to edit on many [`Cursor`]s, see [`edit_iter`].
162    ///
163    /// Just like all other `edit` methods, this one will populate the
164    /// [`Cursors`], so if there are no [`Cursor`]s, it will create
165    /// one at [`Point::default`].
166    ///
167    /// [destroyed]: Editor::destroy
168    /// [`edit_main`]: Self::edit_main
169    /// [`edit_iter`]: Self::edit_iter
170    pub fn edit_nth(&mut self, n: usize) -> Editor<W, A, S> {
171        let cursors = self.widget.cursors_mut().unwrap();
172        cursors.populate();
173        let Some((cursor, was_main)) = cursors.remove(n) else {
174            panic!("Cursor index {n} out of bounds");
175        };
176
177        Editor::new(
178            cursor,
179            n,
180            was_main,
181            self.widget,
182            self.area,
183            None,
184            &mut self.inc_searcher,
185        )
186    }
187
188    /// Edits the main [`Cursor`] in the [`Text`]
189    ///
190    /// Once dropped, the [`Cursor`] in this [`Editor`] will be added
191    /// back to the list of [`Cursor`]s, unless it is [destroyed]
192    ///
193    /// If you want to edit on the `nth` cursor, see [`edit_nth`],
194    /// same for [`edit_last`], if you want to edit on many
195    /// [`Cursor`]s, see [`edit_iter`].
196    ///
197    /// Just like all other `edit` methods, this one will populate the
198    /// [`Cursors`], so if there are no [`Cursor`]s, it will create
199    /// one at [`Point::default`].
200    ///
201    /// [destroyed]: Editor::destroy
202    /// [`edit_nth`]: Self::edit_nth
203    /// [`edit_last`]: Self::edit_last
204    /// [`edit_iter`]: Self::edit_iter
205    pub fn edit_main(&mut self) -> Editor<W, A, S> {
206        self.edit_nth(self.cursors().main_index())
207    }
208
209    /// Edits the last [`Cursor`] in the [`Text`]
210    ///
211    /// Once dropped, the [`Cursor`] in this [`Editor`] will be added
212    /// back to the list of [`Cursor`]s, unless it is [destroyed]
213    ///
214    /// If you want to edit on the `nth` cursor, see [`edit_nth`],
215    /// same for [`edit_main`], if you want to edit on many
216    /// [`Cursor`]s, see [`edit_iter`].
217    ///
218    /// Just like all other `edit` methods, this one will populate the
219    /// [`Cursors`], so if there are no [`Cursor`]s, it will create
220    /// one at [`Point::default`].
221    ///
222    /// [destroyed]: Editor::destroy
223    /// [`edit_nth`]: Self::edit_nth
224    /// [`edit_main`]: Self::edit_main
225    /// [`edit_iter`]: Self::edit_iter
226    pub fn edit_last(&mut self) -> Editor<W, A, S> {
227        self.edit_nth(self.cursors().len().saturating_sub(1))
228    }
229
230    /// A [`Lender`] over all [`Editor`]s of the [`Text`]
231    ///
232    /// This lets you easily iterate over all [`Cursor`]s, without
233    /// having to worry about insertion affecting the order at which
234    /// they are edited (like what repeated calls to [`edit_nth`]
235    /// would do)
236    ///
237    /// Note however that you can't use a [`Lender`] (also known as a
238    /// lending iterator) in a `for` loop, but you should be able
239    /// to just `while let Some(e) = editors.next() {}` or
240    /// `helper.edit_iter().for_each(|_| {})` instead.
241    ///
242    /// Just like all other `edit` methods, this one will populate the
243    /// [`Cursors`], so if there are no [`Cursor`]s, it will create
244    /// one at [`Point::default`].
245    ///
246    /// [`edit_nth`]: Self::edit_nth
247    pub fn edit_iter<'b>(&'b mut self) -> EditIter<'b, W, A, S> {
248        self.cursors_mut().populate();
249        EditIter {
250            next_i: Rc::new(Cell::new(0)),
251            widget: self.widget,
252            area: self.area,
253            inc_searcher: &mut self.inc_searcher,
254        }
255    }
256
257    ////////// Getter functions
258
259    /// A shared reference to the [`Widget`]
260    pub fn widget(&self) -> &W {
261        self.widget
262    }
263
264    /// A mutable reference to the [`Widget`]
265    pub fn widget_mut(&mut self) -> &mut W {
266        self.widget
267    }
268
269    /// A shared reference to the [`Widget`]
270    pub fn text(&self) -> &Text {
271        self.widget.text()
272    }
273
274    /// A mutable reference to the [`Widget`]
275    pub fn text_mut(&mut self) -> &mut Text {
276        self.widget.text_mut()
277    }
278
279    /// A shared reference to the [`Widget`]
280    pub fn cursors(&self) -> &Cursors {
281        self.widget.text().cursors().unwrap()
282    }
283
284    /// A mutable reference to the [`Widget`]
285    pub fn cursors_mut(&mut self) -> &mut Cursors {
286        self.widget.text_mut().cursors_mut().unwrap()
287    }
288
289    /// Undoes the last moment in the history, if there is one
290    pub fn undo(&mut self) {
291        self.widget.text_mut().undo();
292    }
293
294    /// Redoes the last moment in the history, if there is one
295    pub fn redo(&mut self) {
296        self.widget.text_mut().redo();
297    }
298
299    /// Finishes the current moment and adds a new one to the history
300    pub fn new_moment(&mut self) {
301        self.widget.text_mut().new_moment();
302    }
303
304    /// The [`PrintCfg`] in use
305    pub fn cfg(&self) -> PrintCfg {
306        self.widget.print_cfg()
307    }
308}
309
310impl<'a, A> EditHelper<'a, File, A, Searcher>
311where
312    A: Area,
313{
314    /// Returns a new instance of [`EditHelper`]
315    pub fn new_inc(widget: &'a mut File, area: &'a A, searcher: Searcher) -> Self {
316        let cfg = widget.print_cfg();
317        if let Some(c) = widget.cursors_mut() {
318            c.populate()
319        }
320        widget.text_mut().remove_cursors(area, cfg);
321
322        EditHelper { widget, area, inc_searcher: searcher }
323    }
324}
325
326/// A cursor that can edit [`Text`], but can't alter selections
327///
328/// This struct will be used only inside functions passed to the
329/// [`edit_*`] family of methods from the [`EditHelper`].
330///
331/// To make edits, you can use two different functions. You can either
332/// [`replace`] or you can [`insert`]. The former will completely
333/// replace the [`Cursor`]'s selection, while the latter will only
334/// place the edit before the position of the `caret`, which could be
335/// either in the start or the end of the selection.
336///
337/// ```rust
338/// # use duat_core::{mode::EditHelper, ui::Area, widgets::File};
339/// # fn test<S>(helper: &mut EditHelper<File, impl Area, S>) {
340/// helper.edit_main(|e| {
341///     e.replace("my replacement");
342///     e.insert(" and my edit");
343/// });
344/// helper.move_main(|mut m| {
345///     m.move_hor(" and my edit".chars().count() as i32);
346///     m.set_anchor();
347///     m.move_hor(-("my replacement and my edit".chars().count() as i32));
348///     let sel: String = m.selection().into_iter().collect();
349///     assert_eq!(sel, "my replacement and my edit".to_string());
350/// });
351/// # }
352/// ```
353///
354/// [`edit_*`]: EditHelper::edit_nth
355/// [`replace`]: Editor::replace
356/// [`insert`]: Editor::insert
357pub struct Editor<'a, W: Widget<A::Ui>, A: Area, S> {
358    initial: Cursor,
359    cursor: Cursor,
360    n: usize,
361    was_main: bool,
362    widget: &'a mut W,
363    area: &'a A,
364    next_i: Option<Rc<Cell<usize>>>,
365    inc_searcher: &'a mut S,
366}
367
368impl<'a, W: Widget<A::Ui>, A: Area, S> Editor<'a, W, A, S> {
369    /// Returns a new instance of [`Editor`]
370    fn new(
371        cursor: Cursor,
372        n: usize,
373        was_main: bool,
374        widget: &'a mut W,
375        area: &'a A,
376        next_i: Option<Rc<Cell<usize>>>,
377        searcher: &'a mut S,
378    ) -> Self {
379        Self {
380            initial: cursor.clone(),
381            cursor,
382            n,
383            was_main,
384            widget,
385            area,
386            next_i,
387            inc_searcher: searcher,
388        }
389    }
390
391    ////////// Text editing
392
393    /// Replaces the entire selection with new text
394    ///
395    /// If the `caret` is behind the `anchor` (or in the same spot),
396    /// after replacing the selection, the `caret` will be placed on
397    /// the start of the selection, while the `anchor` will be placed
398    /// on the new end. If it is ahead, it will be placed ahead.
399    ///
400    /// If there is no selection, then this has the same effect as
401    /// [`insert`]. If you wish to append to the `caret` instead, see
402    /// [`append`].
403    ///
404    /// [`insert`]: Self::insert
405    /// [`append`]: Self::append
406    pub fn replace(&mut self, edit: impl ToString) {
407        let change = {
408            let edit = edit.to_string();
409            let [p0, p1] = self.cursor.point_range(self.widget.text());
410            Change::new(edit, [p0, p1], self.widget.text())
411        };
412
413        let added_len = change.added_text().len();
414        let end = change.added_end();
415
416        self.edit(change);
417
418        let caret_was_on_start = self.set_caret_on_end();
419
420        self.move_to(end);
421        if added_len > 0 {
422            self.move_hor(-1);
423        }
424        if caret_was_on_start {
425            self.set_caret_on_start();
426        }
427    }
428
429    /// Inserts new text directly behind the `caret`
430    ///
431    /// If the `anchor` is ahead of the `caret`, it will move forwards
432    /// by the number of chars in the new text.
433    ///
434    /// If you wish to replace the selected text, see [`replace`], if
435    /// you want to append after the `caret` instead, see [`append`]
436    ///
437    /// [`replace`]: Self::replace
438    /// [`append`]: Self::append
439    pub fn insert(&mut self, edit: impl ToString) {
440        let range = [self.cursor.caret(), self.cursor.caret()];
441        let change = Change::new(edit.to_string(), range, self.widget.text());
442        let (added, taken) = (change.added_end(), change.taken_end());
443
444        self.edit(change);
445
446        if let Some(anchor) = self.cursor.anchor()
447            && anchor > self.cursor.caret()
448        {
449            let new_anchor = anchor + added - taken;
450            self.cursor.swap_ends();
451            self.cursor.move_to(new_anchor, self.widget.text());
452            self.cursor.swap_ends();
453        }
454    }
455
456    /// Appends new text directly after the `caret`
457    ///
458    /// If the `anchor` is ahead of the `caret`, it will move forwards
459    /// by the number of chars in the new text.
460    ///
461    /// If you wish to replace the selected text, see [`replace`], if
462    /// you want to insert before the `caret` instead, see [`insert`]
463    ///
464    /// [`replace`]: Self::replace
465    /// [`insert`]: Self::insert
466    pub fn append(&mut self, edit: impl ToString) {
467        let caret = self.cursor.caret();
468        let p = caret.fwd(self.widget.text().char_at(caret).unwrap());
469        let change = Change::new(edit.to_string(), [p, p], self.widget.text());
470        let (added, taken) = (change.added_end(), change.taken_end());
471
472        self.edit(change);
473
474        if let Some(anchor) = self.cursor.anchor()
475            && anchor > p
476        {
477            let new_anchor = anchor + added - taken;
478            self.cursor.swap_ends();
479            self.cursor.move_to(new_anchor, self.widget.text());
480            self.cursor.swap_ends();
481        }
482    }
483
484    /// Edits the file with a [`Change`]
485    fn edit(&mut self, change: Change<String>) {
486        let text = self.widget.text_mut();
487        let (change_i, cursors_taken) =
488            text.apply_change(self.cursor.change_i.map(|i| i as usize), change);
489        self.cursor.change_i = change_i.map(|i| i as u32);
490
491        // The Change may have happened before the index of the next curossr,
492        // so we need to account for that.
493        if let Some((change_i, cursors_taken)) = change_i.zip(cursors_taken)
494            && let Some(next_i) = self.next_i.as_ref()
495            && change_i <= next_i.get()
496        {
497            next_i.set(next_i.get().saturating_sub(cursors_taken));
498        }
499    }
500
501    ////////// Movement functions
502
503    /// Moves the cursor horizontally. May cause vertical movement
504    ///
505    /// Returns the distance moved in chars.
506    pub fn move_hor(&mut self, count: i32) -> i32 {
507        self.cursor.move_hor(count, self.widget.text())
508    }
509
510    /// Moves the cursor vertically. May cause horizontal movement
511    ///
512    /// Returns the distance moved in lines.
513    pub fn move_ver(&mut self, count: i32) -> i32 {
514        self.cursor.move_ver(
515            count,
516            self.widget.text(),
517            self.area,
518            self.widget.print_cfg(),
519        )
520    }
521
522    /// Moves the cursor vertically. May cause horizontal movement
523    ///
524    /// Returns the distance moved in wrapped lines.
525    pub fn move_ver_wrapped(&mut self, count: i32) {
526        self.cursor.move_ver_wrapped(
527            count,
528            self.widget.text(),
529            self.area,
530            self.widget.print_cfg(),
531        );
532    }
533
534    /// Moves the cursor to a [`Point`]
535    ///
536    /// - If the position isn't valid, it will move to the "maximum"
537    ///   position allowed.
538    pub fn move_to(&mut self, point: Point) {
539        self.cursor.move_to(point, self.widget.text());
540    }
541
542    /// Moves the cursor to a `line` and a `column`
543    ///
544    /// - If the coords isn't valid, it will move to the "maximum"
545    ///   position allowed.
546    pub fn move_to_coords(&mut self, line: usize, col: usize) {
547        let at = self
548            .text()
549            .point_at_line(line.min(self.text().len().line()));
550        let (point, _) = self.text().chars_fwd(at).take(col + 1).last().unwrap();
551        self.move_to(point);
552    }
553
554    /// Returns and takes the anchor of the [`Cursor`].
555    pub fn unset_anchor(&mut self) -> Option<Point> {
556        self.cursor.unset_anchor()
557    }
558
559    /// Sets the `anchor` to the current `caret`
560    pub fn set_anchor(&mut self) {
561        self.cursor.set_anchor()
562    }
563
564    /// Swaps the position of the `caret` and `anchor`
565    pub fn swap_ends(&mut self) {
566        self.cursor.swap_ends();
567    }
568
569    /// Sets the caret of the [`Cursor`] on the start of the
570    /// selection
571    ///
572    /// Returns `true` if a swap occurred
573    pub fn set_caret_on_start(&mut self) -> bool {
574        if let Some(anchor) = self.anchor()
575            && anchor < self.caret()
576        {
577            self.swap_ends();
578            true
579        } else {
580            false
581        }
582    }
583
584    /// Sets the caret of the [`Cursor`] on the end of the
585    /// selection
586    ///
587    /// Returns `true` if a swap occurred
588    pub fn set_caret_on_end(&mut self) -> bool {
589        if let Some(anchor) = self.anchor()
590            && anchor > self.caret()
591        {
592            self.swap_ends();
593            true
594        } else {
595            false
596        }
597    }
598
599    ////////// Cursor meta manipulation
600
601    /// Resets the [`Cursor`] to how it was before being modified
602    pub fn reset(&mut self) {
603        self.cursor = self.initial.clone();
604    }
605
606    /// Copies the current [`Cursor`] in place
607    ///
608    /// This will leave an additional [`Cursor`] with the current
609    /// selection. Do note that normal intersection rules apply, so if
610    /// at the end of the movement, this cursor intersects with any
611    /// other, they will be merged into one.
612    ///
613    /// When this [`Editor`] is dropped, like with normal [`Editor`]s,
614    /// its [`Cursor`] will be added to the [`Cursors`], unless you
615    /// [destroy] it.
616    ///
617    /// [destroy]: Self::destroy
618    pub fn copy(&mut self) -> Editor<W, A, S> {
619        Editor::new(
620            self.cursor.clone(),
621            self.n,
622            false,
623            self.widget,
624            self.area,
625            self.next_i.clone(),
626            self.inc_searcher,
627        )
628    }
629
630    /// Destroys the current [`Cursor`]
631    ///
632    /// Will not destroy it if it is the last [`Cursor`] left
633    ///
634    /// If this was the main cursor, the main cursor will now be the
635    /// cursor immediately behind it.
636    pub fn destroy(mut self) {
637        // If it is 1, it is actually 2, because this Cursor is also part of
638        // that list.
639        if !self.widget.cursors().unwrap().is_empty() {
640            // Rc<Cell> needs to be manually dropped to reduce its counter.
641            self.next_i.take();
642            if self.was_main {
643                self.widget.cursors_mut().unwrap().rotate_main(-1);
644            }
645            // The destructor is what inserts the Cursor back into the list, so
646            // don't run it.
647            std::mem::forget(self);
648        } else {
649            // Just to be explicit.
650            drop(self);
651        }
652    }
653
654    /// Sets the "desired visual column"
655    ///
656    /// The desired visual column determines at what point in a line
657    /// the caret will be placed when moving [up and down] through
658    /// lines of varying lengths.
659    ///
660    /// Will also set the "desired wrapped visual column", which is
661    /// the same thing but used when moving vertically in a [wrapped]
662    /// fashion.
663    ///
664    /// [up and down]: Mover::move_ver
665    /// [wrapped]: Mover::move_ver_wrapped
666    pub fn set_desired_vcol(&mut self, x: usize) {
667        self.cursor.set_desired_cols(x, x);
668    }
669
670    ////////// Iteration functions
671
672    /// Iterates over the [`char`]s
673    ///
674    /// This iteration will begin on the `caret`. It will also include
675    /// the [`Point`] of each `char`
676    pub fn chars_fwd(&self) -> impl Iterator<Item = (Point, char)> + '_ {
677        self.widget.text().chars_fwd(self.caret())
678    }
679
680    /// Iterates over the [`char`]s, in reverse
681    ///
682    /// This iteration will begin on the `caret`. It will also include
683    /// the [`Point`] of each `char`
684    pub fn chars_rev(&self) -> impl Iterator<Item = (Point, char)> + '_ {
685        self.widget.text().chars_rev(self.caret())
686    }
687
688    /// Searches the [`Text`] for a regex
689    ///
690    /// The search will begin on the `caret`, and returns the bounding
691    /// [`Point`]s, alongside the match. If an `end` is provided,
692    /// the search will stop at the given [`Point`].
693    ///
694    /// # Panics
695    ///
696    /// If the regex is not valid, this method will panic.
697    ///
698    /// ```rust
699    /// # use duat_core::{mode::EditHelper, ui::Area, widgets::File};
700    /// fn search_nth_paren<S>(
701    ///     helper: &mut EditHelper<File, impl Area, S>,
702    ///     n: usize,
703    /// ) {
704    ///     helper.move_many(.., |mut m| {
705    ///         let mut nth = m.search_fwd('(', None).nth(n);
706    ///         if let Some([p0, p1]) = nth {
707    ///             m.move_to(p0);
708    ///             m.set_anchor();
709    ///             m.move_to(p1);
710    ///         }
711    ///     })
712    /// }
713    /// ```
714    pub fn search_fwd<R: RegexPattern>(
715        &mut self,
716        pat: R,
717        end: Option<Point>,
718    ) -> impl Iterator<Item = R::Match> + '_ {
719        let start = self.cursor.caret().byte();
720        let text = self.widget.text_mut();
721        match end {
722            Some(end) => text.search_fwd(pat, start..end.byte()).unwrap(),
723            None => {
724                let end = text.len().byte();
725                text.search_fwd(pat, start..end).unwrap()
726            }
727        }
728    }
729
730    /// Searches the [`Text`] for a regex, in reverse
731    ///
732    /// The search will begin on the `caret`, and returns the bounding
733    /// [`Point`]s, alongside the match. If a `start` is provided,
734    /// the search will stop at the given [`Point`].
735    ///
736    /// # Panics
737    ///
738    /// If the regex is not valid, this method will panic.
739    ///
740    /// ```rust
741    /// # use duat_core::{mode::EditHelper, ui::Area, widgets::File};
742    /// fn search_nth_rev<S>(
743    ///     helper: &mut EditHelper<File, impl Area, S>,
744    ///     n: usize,
745    ///     s: &str,
746    /// ) {
747    ///     helper.move_many(.., |mut m| {
748    ///         let mut nth = m.search_rev(s, None).nth(n);
749    ///         if let Some([p0, p1]) = nth {
750    ///             m.move_to(p0);
751    ///             m.set_anchor();
752    ///             m.move_to(p1);
753    ///         }
754    ///     })
755    /// }
756    /// ```
757    pub fn search_rev<R: RegexPattern>(
758        &mut self,
759        pat: R,
760        start: Option<Point>,
761    ) -> impl Iterator<Item = R::Match> + '_ {
762        let end = self.cursor.caret().byte();
763        let start = start.unwrap_or_default();
764        let text = self.widget.text_mut();
765        text.search_rev(pat, start.byte()..end).unwrap()
766    }
767
768    /// Wether the current selection matches a regex pattern
769    pub fn matches<R: RegexPattern>(&mut self, pat: R) -> bool {
770        let range = self.cursor.range(self.widget.text());
771        self.widget.text_mut().matches(pat, range).unwrap()
772    }
773
774    ////////// Text queries
775
776    /// Returns the [`char`] in the `caret`
777    pub fn char(&self) -> char {
778        self.text().char_at(self.cursor.caret()).unwrap()
779    }
780
781    /// Returns the [`char`] at a given [`Point`]
782    pub fn char_at(&self, p: Point) -> Option<char> {
783        self.text().char_at(p)
784    }
785
786    /// Returns the [`Cursor`]'s selection
787    ///
788    /// The reason why this return value is `IntoIter<&str, 2>` is
789    /// because the [`Text`] utilizes an underlying [`GapBuffer`]
790    /// to store the characters. This means that the text is
791    /// always separated into two distinct chunks.
792    ///
793    /// If this [`Cursor`]'s selection happens to be entirely
794    /// within one of these chunks, the other `&str` will just be
795    /// empty.
796    ///
797    /// [`GapBuffer`]: gapbuf::GapBuffer
798    pub fn selection(&self) -> Strs {
799        let range = self.cursor.range(self.text());
800        self.text().strs(range)
801    }
802
803    pub fn contiguous_in(&mut self, range: impl TextRange) -> &str {
804        self.widget.text_mut().contiguous(range)
805    }
806
807    /// Returns the length of the [`Text`], in [`Point`]
808    pub fn len(&self) -> Point {
809        self.text().len()
810    }
811
812    /// Returns the position of the last [`char`] if there is one
813    pub fn last_point(&self) -> Option<Point> {
814        self.text().last_point()
815    }
816
817    /// An [`Iterator`] over the lines in a given [range]
818    ///
819    /// [range]: TextRange
820    pub fn lines_on(
821        &mut self,
822        range: impl TextRange,
823    ) -> impl DoubleEndedIterator<Item = (usize, &'_ str)> + '_ {
824        self.widget.text_mut().lines(range)
825    }
826
827    /// Gets the current level of indentation
828    pub fn indent(&self) -> usize {
829        self.widget
830            .text()
831            .indent(self.caret(), self.area, self.cfg())
832    }
833
834    /// Gets the indentation level on the given [`Point`]
835    pub fn indent_on(&self, p: Point) -> usize {
836        self.widget.text().indent(p, self.area, self.cfg())
837    }
838
839    /// Gets a [`Reader`]'s [public facing API], if it exists
840    ///
841    /// [public facing API]: Reader::PublicReader
842    pub fn get_reader<R: Reader>(&mut self) -> Option<R::PublicReader<'_>> {
843        self.widget.text_mut().get_reader::<R>()
844    }
845
846    ////////// Cursor queries
847
848    /// Returns the `caret`
849    pub fn caret(&self) -> Point {
850        self.cursor.caret()
851    }
852
853    /// Returns the `anchor`
854    pub fn anchor(&self) -> Option<Point> {
855        self.cursor.anchor()
856    }
857
858    pub fn range(&self) -> [Point; 2] {
859        self.cursor.point_range(self.text())
860    }
861
862    pub fn v_caret(&self) -> VPoint {
863        self.cursor
864            .v_caret(self.widget.text(), self.area, self.widget.print_cfg())
865    }
866
867    pub fn v_anchor(&self) -> Option<VPoint> {
868        self.cursor
869            .v_anchor(self.widget.text(), self.area, self.widget.print_cfg())
870    }
871
872    /// Returns `true` if the `anchor` exists before the `caret`
873    pub fn anchor_is_start(&self) -> bool {
874        self.anchor().is_none_or(|anchor| anchor < self.caret())
875    }
876
877    /// Whether or not this is the main [`Cursor`]
878    pub fn is_main(&self) -> bool {
879        self.was_main
880    }
881
882    pub fn text(&self) -> &Text {
883        self.widget.text()
884    }
885
886    /// The [`PrintCfg`] in use
887    pub fn cfg(&self) -> PrintCfg {
888        self.widget.print_cfg()
889    }
890}
891
892/// Incremental search functions, only available on [`IncSearcher`]s
893///
894/// [`IncSearcher`]: crate::mode::IncSearcher
895impl<W: Widget<A::Ui>, A: Area> Editor<'_, W, A, Searcher> {
896    /// Search incrementally from an [`IncSearch`] request
897    ///
898    /// This will match the Regex pattern from the current position of
899    /// the caret. if `end` is [`Some`], the search will end at the
900    /// requested [`Point`].
901    ///
902    /// [`IncSearch`]: crate::mode::IncSearch
903    pub fn search_inc_fwd(&mut self, end: Option<Point>) -> impl Iterator<Item = [Point; 2]> + '_ {
904        self.inc_searcher
905            .search_fwd(self.widget.text_mut(), (self.cursor.caret(), end))
906    }
907
908    /// Search incrementally from an [`IncSearch`] request in reverse
909    ///
910    /// This will match the Regex pattern from the current position of
911    /// the caret in reverse. if `start` is [`Some`], the search will
912    /// end at the requested [`Point`].
913    ///
914    /// [`IncSearch`]: crate::mode::IncSearch
915    pub fn search_inc_rev(
916        &mut self,
917        start: Option<Point>,
918    ) -> impl Iterator<Item = [Point; 2]> + '_ {
919        self.inc_searcher
920            .search_rev(self.widget.text_mut(), (start, self.cursor.caret()))
921    }
922
923    /// Whether the [`Cursor`]'s selection matches the [`IncSearch`]
924    /// request
925    ///
926    /// [`IncSearch`]: crate::mode::IncSearch
927    pub fn matches_inc(&mut self) -> bool {
928        let range = self.cursor.range(self.widget.text());
929        self.inc_searcher
930            .matches(self.widget.text_mut().contiguous(range))
931    }
932}
933
934// SAFETY: In theory, it should be impossible to maintain a reference
935// to W after it has dropped, since EditHelper would be mutably
936// borrowing from said W, and you can only get an Editor from
937// EditHelper. Thus. the only thing which may have been dropped is the
938// Cursors within, which are accounted for.
939// Also, since this is created by an EditHelper, it is created after
940// it, and thus is dropped before it, making its &mut Cursor reference
941// valid.
942unsafe impl<#[may_dangle] 'a, W: Widget<A::Ui> + 'a, A: Area + 'a, S: 'a> Drop
943    for Editor<'a, W, A, S>
944{
945    fn drop(&mut self) {
946        let Some(cursors) = self.widget.cursors_mut() else {
947            return;
948        };
949        let cursor = std::mem::take(&mut self.cursor);
950        let ([inserted_i, cursors_taken], last_cursor_overhangs) =
951            cursors.insert(self.n, cursor, self.was_main);
952
953        if let Some(next_i) = self.next_i.as_ref()
954            && inserted_i <= next_i.get()
955        {
956            let go_to_next = !last_cursor_overhangs as usize;
957            next_i.set(next_i.get().saturating_sub(cursors_taken).max(inserted_i) + go_to_next)
958        }
959    }
960}
961
962pub struct EditIter<'a, W: Widget<A::Ui>, A: Area, S> {
963    next_i: Rc<Cell<usize>>,
964    widget: &'a mut W,
965    area: &'a A,
966    inc_searcher: &'a mut S,
967}
968
969impl<'a, 'lend, W: Widget<A::Ui>, A: Area, S> Lending<'lend> for EditIter<'a, W, A, S> {
970    type Lend = Editor<'lend, W, A, S>;
971}
972
973impl<'a, W: Widget<A::Ui>, A: Area, S> Lender for EditIter<'a, W, A, S> {
974    fn next<'lend>(&'lend mut self) -> Option<<Self as Lending<'lend>>::Lend> {
975        let current_i = self.next_i.get();
976        let (cursor, was_main) = self.widget.cursors_mut().unwrap().remove(current_i)?;
977
978        Some(Editor::new(
979            cursor,
980            current_i,
981            was_main,
982            self.widget,
983            self.area,
984            Some(self.next_i.clone()),
985            self.inc_searcher,
986        ))
987    }
988}