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