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}