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