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 rc::Rc,
11};
12
13use lender::{Lender, Lending};
14
15pub use self::selections::{Selection, Selections, VPoint};
16use crate::{
17 cfg::PrintCfg,
18 file::{File, Reader},
19 text::{Bytes, Change, Lines, Point, RegexPattern, Searcher, Strs, Text, TextRange},
20 ui::{RawArea, Ui, Widget},
21};
22
23/// The [`Selection`] and [`Selections`] structs
24mod selections;
25
26/// A selection that can edit [`Text`], but can't alter selections
27///
28/// This struct will be used only inside functions passed to the
29/// [`edit_*`] family of methods from the [`Handle`].
30///
31/// To make edits, you can use three different functions. You can,
32/// those being [`replace`], [`insert`], and [`append`]. [`replace`]
33/// will completely replace the [`Selection`]'s selection. [`insert`]
34/// will place text behind the `caret`, and [`append`] will place it
35/// after the `caret`.
36///
37/// You can also move the [`Selection`]'s selection in many different
38/// ways, which are described below, in the `impl` section for this
39/// struct.
40///
41/// ```rust
42/// # use duat_core::prelude::*;
43/// # fn test<U: Ui, S>(mut pa: Pass, handle: &mut Handle<File<U>, U, S>) {
44/// let sel: String = handle.edit_main(&mut pa, |mut c| {
45/// c.set_anchor();
46/// c.set_caret_on_end();
47/// c.replace("my replacement");
48/// c.append(" and my edit");
49///
50/// c.swap_ends();
51/// c.insert("This is ");
52/// c.swap_ends();
53///
54/// c.move_hor(" and my edit".chars().count() as i32);
55/// c.set_anchor();
56/// c.move_hor(-("This is my replacement and my edit".chars().count() as i32));
57/// c.selection().into_iter().collect()
58/// });
59///
60/// assert_eq!(&sel, "This is my replacement and my edit");
61/// # }
62/// ```
63///
64/// [`edit_*`]: crate::context::Handle::edit_nth
65/// [`Handle`]: crate::context::Handle
66/// [`replace`]: Cursor::replace
67/// [`insert`]: Cursor::insert
68/// [`append`]: Cursor::append
69pub struct Cursor<'a, W: Widget<A::Ui>, A: RawArea, S> {
70 initial: Selection,
71 selection: Selection,
72 n: usize,
73 was_main: bool,
74 widget: &'a mut W,
75 area: &'a A,
76 next_i: Option<Rc<Cell<usize>>>,
77 inc_searcher: &'a mut S,
78}
79
80impl<'a, W: Widget<A::Ui>, A: RawArea, S> Cursor<'a, W, A, S> {
81 /// Returns a new instance of [`Cursor`]
82 pub(crate) fn new(
83 selection: Selection,
84 n: usize,
85 was_main: bool,
86 widget: &'a mut W,
87 area: &'a A,
88 next_i: Option<Rc<Cell<usize>>>,
89 searcher: &'a mut S,
90 ) -> Self {
91 Self {
92 initial: selection.clone(),
93 selection,
94 n,
95 was_main,
96 widget,
97 area,
98 next_i,
99 inc_searcher: searcher,
100 }
101 }
102
103 ////////// Text editing
104
105 /// Replaces the entire selection with new text
106 ///
107 /// If the `caret` is behind the `anchor` (or in the same spot),
108 /// after replacing the selection, the `caret` will be placed on
109 /// the start of the selection, while the `anchor` will be placed
110 /// on the new end. If it is ahead, it will be placed ahead.
111 ///
112 /// If there is no selection, then this has the same effect as
113 /// [`insert`]. If you wish to append to the `caret` instead, see
114 /// [`append`].
115 ///
116 /// [`insert`]: Self::insert
117 /// [`append`]: Self::append
118 pub fn replace(&mut self, edit: impl ToString) {
119 let change = {
120 let edit = edit.to_string();
121 let [p0, p1] = self.selection.point_range(self.widget.text());
122 Change::new(edit, [p0, p1], self.widget.text())
123 };
124
125 let added_len = change.added_text().len();
126 let end = change.added_end();
127
128 self.edit(change);
129
130 let caret_was_on_start = self.set_caret_on_end();
131
132 self.move_to(end);
133 if added_len > 0 {
134 self.move_hor(-1);
135 }
136 if caret_was_on_start {
137 self.set_caret_on_start();
138 }
139 }
140
141 /// Inserts new text directly behind the `caret`
142 ///
143 /// If the `anchor` is ahead of the `caret`, it will move forwards
144 /// by the number of chars in the new text.
145 ///
146 /// If you wish to replace the selected text, see [`replace`], if
147 /// you want to append after the `caret` instead, see [`append`]
148 ///
149 /// [`replace`]: Self::replace
150 /// [`append`]: Self::append
151 pub fn insert(&mut self, edit: impl ToString) {
152 let range = [self.selection.caret(), self.selection.caret()];
153 let change = Change::new(edit.to_string(), range, self.widget.text());
154 let (added, taken) = (change.added_end(), change.taken_end());
155
156 self.edit(change);
157
158 if let Some(anchor) = self.selection.anchor()
159 && anchor > self.selection.caret()
160 {
161 let new_anchor = anchor + added - taken;
162 self.selection.swap_ends();
163 self.selection.move_to(new_anchor, self.widget.text());
164 self.selection.swap_ends();
165 }
166 }
167
168 /// Appends new text directly after the `caret`
169 ///
170 /// If the `anchor` is ahead of the `caret`, it will move forwards
171 /// by the number of chars in the new text.
172 ///
173 /// If you wish to replace the selected text, see [`replace`], if
174 /// you want to insert before the `caret` instead, see [`insert`]
175 ///
176 /// [`replace`]: Self::replace
177 /// [`insert`]: Self::insert
178 pub fn append(&mut self, edit: impl ToString) {
179 let caret = self.selection.caret();
180 let p = caret.fwd(self.widget.text().char_at(caret).unwrap());
181 let change = Change::new(edit.to_string(), [p, p], self.widget.text());
182 let (added, taken) = (change.added_end(), change.taken_end());
183
184 self.edit(change);
185
186 if let Some(anchor) = self.selection.anchor()
187 && anchor > p
188 {
189 let new_anchor = anchor + added - taken;
190 self.selection.swap_ends();
191 self.selection.move_to(new_anchor, self.widget.text());
192 self.selection.swap_ends();
193 }
194 }
195
196 /// Edits the file with a [`Change`]
197 fn edit(&mut self, change: Change<String>) {
198 let text = self.widget.text_mut();
199 let (change_i, selections_taken) =
200 text.apply_change(self.selection.change_i.map(|i| i as usize), change);
201 self.selection.change_i = change_i.map(|i| i as u32);
202
203 // The Change may have happened before the index of the next curossr,
204 // so we need to account for that.
205 if let Some((change_i, selections_taken)) = change_i.zip(selections_taken)
206 && let Some(next_i) = self.next_i.as_ref()
207 && change_i <= next_i.get()
208 {
209 next_i.set(next_i.get().saturating_sub(selections_taken));
210 }
211 }
212
213 ////////// Movement functions
214
215 /// Moves the selection horizontally. May cause vertical movement
216 ///
217 /// Returns the distance moved in chars.
218 pub fn move_hor(&mut self, count: i32) -> i32 {
219 self.selection.move_hor(count, self.widget.text())
220 }
221
222 /// Moves the selection vertically. May cause horizontal movement
223 ///
224 /// Returns the distance moved in lines.
225 pub fn move_ver(&mut self, count: i32) -> i32 {
226 self.selection.move_ver(
227 count,
228 self.widget.text(),
229 self.area,
230 self.widget.print_cfg(),
231 )
232 }
233
234 /// Moves the selection vertically a number of wrapped lines. May
235 /// cause horizontal movement
236 ///
237 /// Returns the distance moved in wrapped lines.
238 pub fn move_ver_wrapped(&mut self, count: i32) {
239 self.selection.move_ver_wrapped(
240 count,
241 self.widget.text(),
242 self.area,
243 self.widget.print_cfg(),
244 );
245 }
246
247 /// Moves the selection to a [`Point`]
248 ///
249 /// - If the position isn't valid, it will move to the "maximum"
250 /// position allowed.
251 pub fn move_to(&mut self, point: Point) {
252 self.selection.move_to(point, self.widget.text());
253 }
254
255 /// Moves the selection to [`Point::default`], i.e., the start of
256 /// the [`Text`]
257 pub fn move_to_start(&mut self) {
258 self.selection.move_to(Point::default(), self.widget.text());
259 }
260
261 /// Moves the selection to a `line` and a `column`
262 ///
263 /// - If the coords isn't valid, it will move to the "maximum"
264 /// position allowed.
265 pub fn move_to_coords(&mut self, line: usize, col: usize) {
266 let at = self
267 .text()
268 .point_at_line(line.min(self.text().len().line()));
269 let (point, _) = self.text().chars_fwd(at).take(col + 1).last().unwrap();
270 self.move_to(point);
271 }
272
273 /// Returns and takes the anchor of the [`Selection`].
274 pub fn unset_anchor(&mut self) -> Option<Point> {
275 self.selection.unset_anchor()
276 }
277
278 /// Sets the `anchor` to the current `caret`
279 pub fn set_anchor(&mut self) {
280 self.selection.set_anchor()
281 }
282
283 /// Sets the `anchor` if it was not already set
284 pub fn set_anchor_if_needed(&mut self) {
285 if self.anchor().is_none() {
286 self.selection.set_anchor()
287 }
288 }
289
290 /// Swaps the position of the `caret` and `anchor`
291 pub fn swap_ends(&mut self) {
292 self.selection.swap_ends();
293 }
294
295 /// Sets the caret of the [`Selection`] on the start of the
296 /// selection
297 ///
298 /// Returns `true` if a swap occurred
299 pub fn set_caret_on_start(&mut self) -> bool {
300 if let Some(anchor) = self.anchor()
301 && anchor < self.caret()
302 {
303 self.swap_ends();
304 true
305 } else {
306 false
307 }
308 }
309
310 /// Sets the caret of the [`Selection`] on the end of the
311 /// selection
312 ///
313 /// Returns `true` if a swap occurred
314 pub fn set_caret_on_end(&mut self) -> bool {
315 if let Some(anchor) = self.anchor()
316 && anchor > self.caret()
317 {
318 self.swap_ends();
319 true
320 } else {
321 false
322 }
323 }
324
325 ////////// Selection meta manipulation
326
327 /// Resets the [`Selection`] to how it was before being modified
328 pub fn reset(&mut self) {
329 self.selection = self.initial.clone();
330 }
331
332 /// Copies the current [`Selection`] in place
333 ///
334 /// This will leave an additional [`Selection`] with the current
335 /// selection. Do note that normal intersection rules apply, so if
336 /// at the end of the movement, this selection intersects with any
337 /// other, they will be merged into one.
338 ///
339 /// When this [`Cursor`] is dropped, like with normal [`Cursor`]s,
340 /// its [`Selection`] will be added to the [`Selections`], unless
341 /// you [destroy] it.
342 ///
343 /// [destroy]: Self::destroy
344 pub fn copy(&mut self) -> Cursor<W, A, S> {
345 Cursor::new(
346 self.selection.clone(),
347 self.n,
348 false,
349 self.widget,
350 self.area,
351 self.next_i.clone(),
352 self.inc_searcher,
353 )
354 }
355
356 /// Destroys the current [`Selection`]
357 ///
358 /// Will not destroy it if it is the last [`Selection`] left
359 ///
360 /// If this was the main selection, the main selection will now be
361 /// the selection immediately behind it.
362 pub fn destroy(mut self) {
363 // If it is 1, it is actually 2, because this Selection is also part
364 // of that list.
365 if !self.widget.text().selections().unwrap().is_empty() {
366 // Rc<Cell> needs to be manually dropped to reduce its counter.
367 self.next_i.take();
368 if self.was_main {
369 self.widget
370 .text_mut()
371 .selections_mut()
372 .unwrap()
373 .rotate_main(-1);
374 }
375 // The destructor is what inserts the Selection back into the list, so
376 // don't run it.
377 std::mem::forget(self);
378 } else {
379 // Just to be explicit.
380 drop(self);
381 }
382 }
383
384 /// Sets the "desired visual column"
385 ///
386 /// The desired visual column determines at what point in a line
387 /// the caret will be placed when moving [up and down] through
388 /// lines of varying lengths.
389 ///
390 /// Will also set the "desired wrapped visual column", which is
391 /// the same thing but used when moving vertically in a [wrapped]
392 /// fashion.
393 ///
394 /// [up and down]: Cursor::move_ver
395 /// [wrapped]: Cursor::move_ver_wrapped
396 pub fn set_desired_vcol(&mut self, x: usize) {
397 self.selection.set_desired_cols(x, x);
398 }
399
400 ////////// Iteration functions
401
402 /// Iterates over the [`char`]s
403 ///
404 /// This iteration will begin on the `caret`. It will also include
405 /// the [`Point`] of each `char`
406 pub fn chars_fwd(&self) -> impl Iterator<Item = (Point, char)> + '_ {
407 self.widget.text().chars_fwd(self.caret())
408 }
409
410 /// Iterates over the [`char`]s, in reverse
411 ///
412 /// This iteration will begin on the `caret`. It will also include
413 /// the [`Point`] of each `char`
414 pub fn chars_rev(&self) -> impl Iterator<Item = (Point, char)> + '_ {
415 self.widget.text().chars_rev(self.caret())
416 }
417
418 /// Searches the [`Text`] for a regex
419 ///
420 /// The search will begin on the `caret`, and returns the bounding
421 /// [`Point`]s, alongside the match. If an `end` is provided,
422 /// the search will stop at the given [`Point`].
423 ///
424 /// # Panics
425 ///
426 /// If the regex is not valid, this method will panic.
427 ///
428 /// ```rust
429 /// # use duat_core::prelude::*;
430 /// fn search_nth_paren<U: Ui, S>(pa: &mut Pass, handle: &mut Handle<File<U>, U, S>, n: usize) {
431 /// handle.edit_all(pa, |mut e| {
432 /// let mut nth = e.search_fwd('(', None).nth(n);
433 /// if let Some([p0, p1]) = nth {
434 /// e.move_to(p0);
435 /// e.set_anchor();
436 /// e.move_to(p1);
437 /// }
438 /// })
439 /// }
440 /// ```
441 pub fn search_fwd<R: RegexPattern>(
442 &mut self,
443 pat: R,
444 end: Option<Point>,
445 ) -> impl Iterator<Item = R::Match> + '_ {
446 let start = self.selection.caret().byte();
447 let text = self.widget.text_mut();
448 match end {
449 Some(end) => text.search_fwd(pat, start..end.byte()).unwrap(),
450 None => {
451 let end = text.len().byte();
452 text.search_fwd(pat, start..end).unwrap()
453 }
454 }
455 }
456
457 /// Searches the [`Text`] for a regex, in reverse
458 ///
459 /// The search will begin on the `caret`, and returns the bounding
460 /// [`Point`]s, alongside the match. If a `start` is provided,
461 /// the search will stop at the given [`Point`].
462 ///
463 /// # Panics
464 ///
465 /// If the regex is not valid, this method will panic.
466 ///
467 /// ```rust
468 /// # use duat_core::prelude::*;
469 /// fn search_nth_paren<U: Ui, S>(
470 /// pa: &mut Pass,
471 /// handle: &mut Handle<File<U>, U, S>,
472 /// s: &str,
473 /// n: usize,
474 /// ) {
475 /// handle.edit_all(pa, |mut e| {
476 /// let mut nth = e.search_rev(s, None).nth(n);
477 /// if let Some([p0, p1]) = nth {
478 /// e.move_to(p0);
479 /// e.set_anchor();
480 /// e.move_to(p1);
481 /// }
482 /// })
483 /// }
484 /// ```
485 pub fn search_rev<R: RegexPattern>(
486 &mut self,
487 pat: R,
488 start: Option<Point>,
489 ) -> impl Iterator<Item = R::Match> + '_ {
490 let end = self.selection.caret().byte();
491 let start = start.unwrap_or_default();
492 let text = self.widget.text_mut();
493 text.search_rev(pat, start.byte()..end).unwrap()
494 }
495
496 /// Wether the current selection matches a regex pattern
497 pub fn matches<R: RegexPattern>(&mut self, pat: R) -> bool {
498 let range = self.selection.range(self.widget.text());
499 self.widget.text_mut().matches(pat, range).unwrap()
500 }
501
502 ////////// Text queries
503
504 /// Returns the [`char`] in the `caret`
505 pub fn char(&self) -> char {
506 self.text().char_at(self.selection.caret()).unwrap()
507 }
508
509 /// Returns the [`char`] at a given [`Point`]
510 pub fn char_at(&self, p: Point) -> Option<char> {
511 self.text().char_at(p)
512 }
513
514 /// Returns the [`Selection`]'s selection
515 ///
516 /// The reason why this return value is `IntoIter<&str, 2>` is
517 /// because the [`Text`] utilizes an underlying [`GapBuffer`]
518 /// to store the characters. This means that the text is
519 /// always separated into two distinct chunks.
520 ///
521 /// If this [`Selection`]'s selection happens to be entirely
522 /// within one of these chunks, the other `&str` will just be
523 /// empty.
524 ///
525 /// [`GapBuffer`]: gapbuf::GapBuffer
526 pub fn selection(&self) -> Strs {
527 let range = self.selection.range(self.text());
528 self.text().strs(range)
529 }
530
531 /// Shifts the gap within the [`GapBuffer`] in order to return a
532 /// contiguous `&str`
533 ///
534 /// [`GapBuffer`]: gapbuf::GapBuffer
535 pub fn contiguous_in(&mut self, range: impl TextRange) -> &str {
536 self.widget.text_mut().contiguous(range)
537 }
538
539 /// Returns the length of the [`Text`], in [`Point`]
540 pub fn len(&self) -> Point {
541 self.text().len()
542 }
543
544 /// Returns the position of the last [`char`] if there is one
545 pub fn last_point(&self) -> Option<Point> {
546 self.text().last_point()
547 }
548
549 /// An [`Iterator`] over the lines in a given [range]
550 ///
551 /// [range]: TextRange
552 pub fn lines_on(&mut self, range: impl TextRange) -> Lines {
553 self.widget.text_mut().lines(range)
554 }
555
556 /// Gets the current level of indentation
557 pub fn indent(&self) -> usize {
558 self.widget
559 .text()
560 .indent(self.caret(), self.area, self.cfg())
561 }
562
563 /// Gets the indentation level on the given [`Point`]
564 pub fn indent_on(&self, p: Point) -> usize {
565 self.widget.text().indent(p, self.area, self.cfg())
566 }
567
568 ////////// Selection queries
569
570 /// Returns the `caret`
571 pub fn caret(&self) -> Point {
572 self.selection.caret()
573 }
574
575 /// Returns the `anchor`
576 pub fn anchor(&self) -> Option<Point> {
577 self.selection.anchor()
578 }
579
580 /// The [`Point`] range of the [`Selection`]
581 pub fn range(&self) -> [Point; 2] {
582 self.selection.point_range(self.text())
583 }
584
585 /// The [`VPoint`] range of the [`Selection`]
586 ///
587 /// Use only if you need the things that the [`VPoint`] provides,
588 /// in order to preven extraneous calculations
589 pub fn v_caret(&self) -> VPoint {
590 self.selection
591 .v_caret(self.widget.text(), self.area, self.widget.print_cfg())
592 }
593
594 /// The [`VPoint`] of the anchor, if it exists
595 ///
596 /// Use only if you need the things that the [`VPoint`] provides,
597 /// in order to preven extraneous calculations
598 pub fn v_anchor(&self) -> Option<VPoint> {
599 self.selection
600 .v_anchor(self.widget.text(), self.area, self.widget.print_cfg())
601 }
602
603 /// Returns `true` if the `anchor` exists before the `caret`
604 pub fn anchor_is_start(&self) -> bool {
605 self.anchor().is_none_or(|anchor| anchor < self.caret())
606 }
607
608 /// Whether or not this is the main [`Selection`]
609 pub fn is_main(&self) -> bool {
610 self.was_main
611 }
612
613 /// The [`Text`] of the [`Widget`]
614 pub fn text(&self) -> &Text {
615 self.widget.text()
616 }
617
618 /// The [`PrintCfg`] in use
619 pub fn cfg(&self) -> PrintCfg {
620 self.widget.print_cfg()
621 }
622}
623
624impl<U: Ui, S> Cursor<'_, File<U>, U::Area, S> {
625 /// Reads the [`Bytes`] and a [`Reader`]
626 pub fn read_bytes_and_reader<R: Reader<U>, Ret>(
627 &self,
628 f: impl FnOnce(&Bytes, &R) -> Ret,
629 ) -> Option<Ret> {
630 // SAFETY: Since the creation of an Cursor requires the use of a &mut
631 // Pass, It should be safe to read a Reader, since it cannot be a
632 // File.
633 self.widget.get_reader().map(|reader| unsafe {
634 reader.read_unsafe(|reader| f(self.widget.text().bytes(), reader))
635 })
636 }
637}
638
639/// Incremental search functions, only available on [`IncSearcher`]s
640///
641/// [`IncSearcher`]: https://docs.rs/duat-utils/latest/duat_utils/modes/struct.IncSearcher.html
642impl<W: Widget<A::Ui>, A: RawArea> Cursor<'_, W, A, Searcher> {
643 /// Search incrementally from an [`IncSearch`] request
644 ///
645 /// This will match the Regex pattern from the current position of
646 /// the caret. if `end` is [`Some`], the search will end at the
647 /// requested [`Point`].
648 ///
649 /// [`IncSearch`]: https://docs.rs/duat-utils/latest/duat_utils/modes/struct.IncSearch.html
650 pub fn search_inc_fwd(&mut self, end: Option<Point>) -> impl Iterator<Item = [Point; 2]> + '_ {
651 let range = if let Some(end) = end {
652 (self.selection.caret()..end).to_range(self.text().len().byte())
653 } else {
654 (self.selection.caret()..).to_range(self.text().len().byte())
655 };
656 self.inc_searcher.search_fwd(self.widget.text_mut(), range)
657 }
658
659 /// Search incrementally from an [`IncSearch`] request in reverse
660 ///
661 /// This will match the Regex pattern from the current position of
662 /// the caret in reverse. if `start` is [`Some`], the search will
663 /// end at the requested [`Point`].
664 ///
665 /// [`IncSearch`]: https://docs.rs/duat-utils/latest/duat_utils/modes/struct.IncSearch.html
666 pub fn search_inc_rev(
667 &mut self,
668 start: Option<Point>,
669 ) -> impl Iterator<Item = [Point; 2]> + '_ {
670 let range = if let Some(start) = start {
671 (start..self.selection.caret()).to_range(self.text().len().byte())
672 } else {
673 (..self.selection.caret()).to_range(self.text().len().byte())
674 };
675 self.inc_searcher.search_rev(self.widget.text_mut(), range)
676 }
677
678 /// Whether the [`Selection`]'s selection matches the
679 /// [`IncSearch`] request
680 ///
681 /// [`IncSearch`]: https://docs.rs/duat-utils/latest/duat_utils/modes/struct.IncSearch.html
682 pub fn matches_inc(&mut self) -> bool {
683 let range = self.selection.range(self.widget.text());
684 self.inc_searcher
685 .matches(self.widget.text_mut().contiguous(range))
686 }
687}
688
689// SAFETY: In theory, it should be impossible to maintain a reference
690// to W after it has dropped, since the Handle would be mutably
691// borrowing from said W, and you can only get a Cursor from Handles.
692// Thus, the only thing which may have been dropped is the Selections
693// within, which are accounted for.
694unsafe impl<#[may_dangle] 'a, W: Widget<A::Ui> + 'a, A: RawArea + 'a, S: 'a> Drop
695 for Cursor<'a, W, A, S>
696{
697 fn drop(&mut self) {
698 let Some(selections) = self.widget.text_mut().selections_mut() else {
699 return;
700 };
701 let selection = std::mem::take(&mut self.selection);
702 let ([inserted_i, selections_taken], last_selection_overhangs) =
703 selections.insert(self.n, selection, self.was_main);
704
705 if let Some(next_i) = self.next_i.as_ref()
706 && inserted_i <= next_i.get()
707 {
708 let go_to_next = !last_selection_overhangs as usize;
709 next_i.set(
710 next_i
711 .get()
712 .saturating_sub(selections_taken)
713 .max(inserted_i)
714 + go_to_next,
715 )
716 }
717 }
718}
719
720/// An [`Iterator`] overf all [`Cursor`]s
721pub struct Cursors<'a, W: Widget<A::Ui>, A: RawArea, S> {
722 next_i: Rc<Cell<usize>>,
723 widget: RefMut<'a, W>,
724 area: &'a A,
725 inc_searcher: RefMut<'a, S>,
726}
727
728impl<'a, W: Widget<A::Ui>, A: RawArea, S> Cursors<'a, W, A, S> {
729 /// Creates a new [`Cursors`]
730 pub(crate) fn new(
731 next_i: usize,
732 widget: RefMut<'a, W>,
733 area: &'a A,
734 inc_searcher: RefMut<'a, S>,
735 ) -> Self {
736 Self {
737 next_i: Rc::new(Cell::new(next_i)),
738 widget,
739 area,
740 inc_searcher,
741 }
742 }
743}
744
745impl<'a, 'lend, W: Widget<A::Ui>, A: RawArea, S> Lending<'lend> for Cursors<'a, W, A, S> {
746 type Lend = Cursor<'lend, W, A, S>;
747}
748
749impl<'a, W: Widget<A::Ui>, A: RawArea, S> Lender for Cursors<'a, W, A, S> {
750 fn next<'lend>(&'lend mut self) -> Option<<Self as Lending<'lend>>::Lend> {
751 let current_i = self.next_i.get();
752 let (selection, was_main) = self
753 .widget
754 .text_mut()
755 .selections_mut()
756 .unwrap()
757 .remove(current_i)?;
758
759 Some(Cursor::new(
760 selection,
761 current_i,
762 was_main,
763 &mut self.widget,
764 self.area,
765 Some(self.next_i.clone()),
766 &mut self.inc_searcher,
767 ))
768 }
769}