duat_core/mode/cursor/
selections.rs

1use std::cell::Cell;
2
3use gapbuf::{GapBuffer, gap_buffer};
4
5pub use self::cursor::{Selection, VPoint};
6use crate::{
7    add_shifts, merging_range_by_guess_and_lazy_shift,
8    text::{Change, Point},
9};
10
11/// The list of [`Selection`]s in a [`Text`]
12///
13/// This list can contain any number of [`Selection`]s, and they
14/// should be usable in whatever order the end user may want, without
15/// breaking from, for example, modifications that should move cursors
16/// backwards or ahead. If that is not the case, report it as a bug.
17///
18/// they are primarily meant to be interacted with from the
19/// [`Handle`], with its [`edit_`] methods meant to efficiently
20/// handle a large number of [`Selection`]s in an efficient manner,
21/// although you can interact with them separately.
22///
23/// A [`Text`] will keep itself in check with regards to its
24/// [`Selections`], that is, it will automatically remove and add the
25/// [`MainCaret`] and [`ExtraCaret`] [tags] when the [`Selections`]
26/// are altered. If it fails to do that, report it as a bug.
27///
28/// [`Handle`]: crate::context::Handle
29/// [`edit_`]: crate::context::Handle::edit_all
30/// [`Text`]: crate::text::Text
31/// [`MainCaret`]: crate::text::MainCaret
32/// [`ExtraCaret`]: crate::text::ExtraCaret
33/// [tags]: crate::text::Tag
34#[derive(Clone)]
35pub struct Selections {
36    buf: GapBuffer<Selection>,
37    main_i: usize,
38    shift_state: Cell<(usize, [i32; 3])>,
39}
40
41impl Selections {
42    /// Returns a new [`Selections`]
43    pub fn new() -> Self {
44        Self {
45            buf: gap_buffer![Selection::default()],
46            main_i: 0,
47            shift_state: Cell::new((0, [0; 3])),
48        }
49    }
50
51    /// A new [`Selections`] with a set main [`Selection`]
52    pub(crate) fn new_with_main(main: Selection) -> Self {
53        Self {
54            buf: gap_buffer![main],
55            main_i: 0,
56            shift_state: Cell::new((0, [0; 3])),
57        }
58    }
59
60    ////////// Modification functions
61
62    /// Sets the main [`Selection`]
63    pub fn set_main(&mut self, new: usize) {
64        self.main_i = new.min(self.buf.len().saturating_sub(1));
65    }
66
67    /// Rotates the main [`Selection`] by an amount
68    pub fn rotate_main(&mut self, amount: i32) {
69        self.main_i = (self.main_i as i32 + amount).rem_euclid(self.buf.len() as i32) as usize
70    }
71
72    /// Removes all [`Selection`]s
73    pub fn clear(&mut self) {
74        self.buf = GapBuffer::new();
75        self.shift_state.take();
76    }
77
78    /// Removes all [`Selection`]s and adds a [default `Selection`] as
79    /// main
80    ///
81    /// [default `Selection`]: Selection::default
82    pub fn reset(&mut self) {
83        self.remove_extras();
84        self.buf[self.main_i] = Selection::default();
85    }
86
87    /// Removes all but the main [`Selection`]
88    pub fn remove_extras(&mut self) {
89        if !self.is_empty() {
90            let cursor = self.buf.remove(self.main_i);
91            let (sh_from, shift) = self.shift_state.take();
92            if sh_from <= self.main_i {
93                cursor.shift_by(shift);
94            }
95            self.buf = gap_buffer![cursor];
96        }
97        self.main_i = 0;
98    }
99
100    ////////// Querying functions
101
102    /// Gets the main [`Selection`], if there is one
103    pub fn get_main(&self) -> Option<&Selection> {
104        self.get(self.main_i)
105    }
106
107    /// Gets the `n`th [`Selection`] if there is one
108    pub fn get(&self, n: usize) -> Option<&Selection> {
109        if n >= self.len() {
110            return None;
111        }
112        let (sh_from, shift) = self.shift_state.get();
113        if n >= sh_from && shift != [0; 3] {
114            for cursor in self.buf.range(sh_from..n + 1).iter() {
115                cursor.shift_by(shift);
116            }
117            if n + 1 < self.buf.len() {
118                self.shift_state.set((n + 1, shift));
119            } else {
120                self.shift_state.take();
121            }
122        }
123
124        self.buf.get(n)
125    }
126
127    /// Iterates over all [`Selection`]s in order
128    pub fn iter(&self) -> impl Iterator<Item = (&Selection, bool)> {
129        // Since we don't know how many Selections will be iterated over, we
130        // shift all cursors, just in case.
131        let (sh_from, shift) = self.shift_state.take();
132        if shift != [0; 3] {
133            for cursor in self.buf.range(sh_from..).iter() {
134                cursor.shift_by(shift);
135            }
136        }
137        self.buf
138            .iter()
139            .enumerate()
140            .map(move |(i, cursor)| (cursor, i == self.main_i))
141    }
142
143    /// The index of the main [`Selection`]
144    pub fn main_index(&self) -> usize {
145        self.main_i
146    }
147
148    /// How many [`Selection`]s there are in the list
149    pub fn len(&self) -> usize {
150        self.buf.len()
151    }
152
153    /// Returns [`true`] when there are no [`Selection`]s
154    #[must_use]
155    pub fn is_empty(&self) -> bool {
156        self.len() == 0
157    }
158
159    ////////// Internal modification functions
160
161    /// Inserts a [`Selection`] back from editing
162    pub(crate) fn insert(
163        &mut self,
164        guess_i: usize,
165        cursor: Selection,
166        main: bool,
167    ) -> ([usize; 2], bool) {
168        let (sh_from, shift) = self.shift_state.take();
169        let sh_from = sh_from.min(self.len());
170
171        // The range of cursors that will be drained
172        let c_range = merging_range_by_guess_and_lazy_shift(
173            (&self.buf, self.buf.len()),
174            (guess_i, [cursor.start(), cursor.end_excl()]),
175            (sh_from, shift, [0; 3], Point::shift_by),
176            (Selection::start, Selection::end_excl),
177        );
178
179        // Shift all ranges that preceed the end of the cursor's range.
180        if sh_from < c_range.end && shift != [0; 3] {
181            for cursor in self.buf.range(sh_from..c_range.end).into_iter() {
182                cursor.shift_by(shift);
183            }
184        }
185
186        // Get the minimum and maximum Points in the taken range, designate
187        // those as the new Selection's bounds.
188        let (caret, anchor, last_cursor_overhangs) = {
189            let mut c_range = c_range.clone();
190            let first = c_range.next().and_then(|i| self.get(i));
191            let last = c_range.last().and_then(|i| self.get(i)).or(first);
192            let start = first
193                .map(|first| first.lazy_v_start().min(cursor.lazy_v_start()))
194                .unwrap_or(cursor.lazy_v_start());
195            let (end, last_cursor_overhangs) = if let Some(last) = last
196                && last.lazy_v_end() >= cursor.lazy_v_end()
197            {
198                (last.lazy_v_end(), true)
199            } else {
200                (cursor.lazy_v_end(), false)
201            };
202
203            if let Some(anchor) = cursor.anchor() {
204                match cursor.caret() < anchor {
205                    true => (start, Some(end), last_cursor_overhangs),
206                    false => (end, Some(start), last_cursor_overhangs),
207                }
208            } else {
209                (end, (start != end).then_some(start), last_cursor_overhangs)
210            }
211        };
212
213        let cursor = Selection::from_v(caret, anchor, cursor.change_i);
214        self.buf.splice(c_range.clone(), [cursor]);
215
216        if main {
217            self.main_i = c_range.start;
218        } else if self.main_i >= c_range.start {
219            self.main_i = (self.main_i + 1 - c_range.clone().count()).max(c_range.start)
220        }
221
222        // If there are no more Selections after this, don't set the
223        // shift_state.
224        let cursors_taken = c_range.clone().count();
225        let new_sh_from = sh_from.saturating_sub(cursors_taken).max(c_range.start) + 1;
226        if new_sh_from < self.buf.len() {
227            self.shift_state.set((new_sh_from, shift));
228        }
229
230        ([c_range.start, cursors_taken], last_cursor_overhangs)
231    }
232
233    /// Applies a [`Change`] to the [`Selection`]s list
234    pub(crate) fn apply_change(&mut self, guess_i: usize, change: Change<&str>) -> usize {
235        let (sh_from, shift) = self.shift_state.take();
236        let sh_from = sh_from.min(self.len());
237
238        // The range of cursors that will be drained
239        let c_range = merging_range_by_guess_and_lazy_shift(
240            (&self.buf, self.buf.len()),
241            (guess_i, [change.start(), change.taken_end()]),
242            (sh_from, shift, [0; 3], Point::shift_by),
243            (Selection::start, Selection::end_excl),
244        );
245
246        // Since applied changes don't remove Selections, we need to shift all
247        // Selections in the whole range. First by the original shift, in
248        // order to update them to the latest shift leve, then by the
249        // change.
250        if c_range.end > sh_from && shift != [0; 3] {
251            for cursor in self.buf.range(sh_from..c_range.end).into_iter() {
252                cursor.shift_by(shift);
253            }
254        }
255        let range = c_range.start..c_range.end.max(sh_from);
256        for cursor in self.buf.range(range).into_iter() {
257            cursor.shift_by_change(change);
258        }
259
260        let (cursors_taken, cursors_added) = {
261            let mut cursors_taken = self.buf.splice(c_range.clone(), []);
262            if let Some(first) = cursors_taken.next() {
263                let last = cursors_taken.next_back().unwrap_or(first.clone());
264                let (start, end) = (first.start(), last.end_excl());
265                let merged = Selection::new(start, (start < end).then_some(end));
266                drop(cursors_taken);
267                self.buf.insert(c_range.start, merged);
268
269                (c_range.len(), 1)
270            } else {
271                (0, 0)
272            }
273        };
274
275        let new_sh_from = sh_from.saturating_sub(cursors_taken).max(c_range.start) + cursors_added;
276        if new_sh_from < self.buf.len() {
277            self.shift_state
278                .set((new_sh_from, add_shifts(shift, change.shift())));
279        }
280
281        cursors_taken - cursors_added
282    }
283
284    /// Removes a [`Selection`], which might be brought back
285    pub(crate) fn remove(&mut self, i: usize) -> Option<(Selection, bool)> {
286        if i >= self.buf.len() {
287            return None;
288        }
289        let (sh_from, shift) = self.shift_state.get();
290
291        if i >= sh_from && shift != [0; 3] {
292            for cursor in self.buf.range(sh_from..i + 1).iter() {
293                cursor.shift_by(shift);
294            }
295            if i + 1 < self.buf.len() {
296                // i here, instead of i + 1, since this Selection is about to be
297                // removed.
298                self.shift_state.set((i, shift));
299            } else {
300                self.shift_state.take();
301            }
302        } else if i < sh_from {
303            // If I am removing before sh_from, obviously the index of the first
304            // unshifted Selection is moved back.
305            self.shift_state.set((sh_from - 1, shift));
306        }
307
308        let was_main = self.main_i == i;
309        if self.main_i >= i {
310            self.main_i = self.main_i.saturating_sub(1);
311        }
312        Some((self.buf.remove(i), was_main))
313    }
314
315    /// Ensures that there is at least one [`Selection`] on the list
316    pub(crate) fn populate(&mut self) {
317        if self.buf.is_empty() {
318            self.main_i = 0;
319            self.buf = gap_buffer![Selection::default()];
320        }
321    }
322}
323
324impl Default for Selections {
325    fn default() -> Self {
326        Self::new()
327    }
328}
329
330mod cursor {
331    use std::{cell::Cell, cmp::Ordering, ops::Range};
332
333    use bincode::{Decode, Encode};
334
335    use crate::{
336        cfg::PrintCfg,
337        text::{Change, Point, Text},
338        ui::{Caret, RawArea},
339    };
340
341    /// A cursor in the text file. This is an editing cursor, -(not
342    /// a printing cursor.
343    #[derive(Default, Clone, Encode, Decode)]
344    pub struct Selection {
345        caret: Cell<LazyVPoint>,
346        anchor: Cell<Option<LazyVPoint>>,
347        pub(in crate::mode::cursor) change_i: Option<u32>,
348    }
349
350    impl Selection {
351        /// Returns a new instance of [`Selection`].
352        pub(crate) fn new(caret: Point, anchor: Option<Point>) -> Self {
353            Self {
354                caret: Cell::new(LazyVPoint::Unknown(caret)),
355                anchor: Cell::new(anchor.map(LazyVPoint::Unknown)),
356                change_i: None,
357            }
358        }
359
360        pub(super) fn from_v(
361            caret: LazyVPoint,
362            anchor: Option<LazyVPoint>,
363            change_i: Option<u32>,
364        ) -> Self {
365            Self {
366                caret: Cell::new(caret),
367                anchor: Cell::new(anchor),
368                change_i,
369            }
370        }
371
372        /// Moves to specific, pre calculated [`Point`].
373        pub fn move_to(&mut self, p: Point, text: &Text) {
374            if p == self.caret() {
375                return;
376            }
377            let p = text.point_at(p.byte().min(text.last_point().unwrap().byte()));
378            *self.caret.get_mut() = LazyVPoint::Unknown(p);
379        }
380
381        /// Internal horizontal movement function.
382        ///
383        /// Returns the number of distance moved through.
384        pub fn move_hor(&mut self, by: i32, text: &Text) -> i32 {
385            let by = by as isize;
386            let (Some(last), false) = (text.last_point(), by == 0) else {
387                return 0;
388            };
389            let target = self.caret.get().point().char().saturating_add_signed(by);
390
391            let p = if target == 0 {
392                Point::default()
393            } else if target >= last.char() {
394                last
395            } else if by.abs() < 500 {
396                if by > 0 {
397                    let (point, _) = text
398                        .chars_fwd(self.caret())
399                        .take(by as usize + 1)
400                        .last()
401                        .unwrap();
402                    point
403                } else {
404                    let (point, _) = text
405                        .chars_rev(self.caret())
406                        .take(by.unsigned_abs())
407                        .last()
408                        .unwrap();
409                    point
410                }
411            } else {
412                text.point_at_char(target)
413            };
414
415            let moved = p.char() as i32 - self.caret.get().point().char() as i32;
416            *self.caret.get_mut() = LazyVPoint::Unknown(p);
417            moved
418        }
419
420        /// Internal vertical movement function.
421        ///
422        /// Returns the distance moved in lines.
423        pub fn move_ver(
424            &mut self,
425            by: i32,
426            text: &Text,
427            area: &impl RawArea,
428            cfg: PrintCfg,
429        ) -> i32 {
430            let by = by as isize;
431            let (Some(last), false) = (text.last_point(), by == 0) else {
432                return 0;
433            };
434
435            let (vp, moved) = {
436                let vp = self.caret.get().calculate(text, area, cfg);
437                let line_start = {
438                    let target = self.caret.get().point().line().saturating_add_signed(by);
439                    text.point_at_line(target.min(last.line()))
440                };
441
442                let mut wraps = 0;
443                let mut vcol = 0;
444
445                let (wcol, p) = area
446                    .print_iter(text.iter_fwd(line_start), cfg.new_line_as('\n'))
447                    .find_map(|(Caret { len, x, wrap }, item)| {
448                        wraps += wrap as usize;
449                        if let Some((p, char)) = item.as_real_char()
450                            && (vcol + len as u16 > vp.dvcol || char == '\n')
451                        {
452                            return Some((x as u16, p));
453                        }
454
455                        vcol += len as u16;
456                        None
457                    })
458                    .unwrap_or((0, last));
459
460                let moved = p.line() as i32 - vp.p.line() as i32;
461                let vp = vp.known(p, (p.char() - line_start.char()) as u16, vcol, wcol);
462                (vp, moved)
463            };
464
465            *self.caret.get_mut() = LazyVPoint::Known(vp);
466            moved
467        }
468
469        /// Internal vertical movement function.
470        ///
471        /// Returns the distance moved in wrapped lines.
472        pub fn move_ver_wrapped(
473            &mut self,
474            by: i32,
475            text: &Text,
476            area: &impl RawArea,
477            cfg: PrintCfg,
478        ) -> i32 {
479            if text.last_point().is_none() || by == 0 {
480                return 0;
481            };
482            let vp = self.caret.get().calculate(text, area, cfg);
483
484            let mut wraps = 0;
485
486            *self.caret.get_mut() = LazyVPoint::Known(if by > 0 {
487                let line_start = text.point_at_line(vp.p.line());
488                let mut vcol = vp.vcol;
489                let mut last = (vp.vcol, vp.wcol, vp.p);
490                let mut last_valid = (vp.vcol, vp.wcol, vp.p);
491
492                let (vcol, wcol, p) = area
493                    .print_iter(text.iter_fwd(line_start), cfg.new_line_as('\n'))
494                    .skip_while(|(_, item)| item.byte() <= self.byte())
495                    .find_map(|(Caret { x, len, wrap }, item)| {
496                        wraps += wrap as i32;
497                        if let Some((p, char)) = item.as_real_char() {
498                            if (x..x + len).contains(&(vp.dwcol as u32)) || char == '\n' {
499                                last_valid = (vcol, x as u16, p);
500                                if wraps == by {
501                                    return Some((vcol, x as u16, p));
502                                }
503                            } else if wraps > by {
504                                return Some(last);
505                            }
506                            last = (vcol, x as u16, p);
507                        }
508                        vcol += len as u16;
509                        None
510                    })
511                    .unwrap_or(last_valid);
512                vp.known(p, (p.char() - line_start.char()) as u16, vcol, wcol)
513            } else {
514                let end_points = text.points_after(vp.p).unwrap();
515                let mut just_wrapped = false;
516                let mut last_valid = (vp.wcol, vp.p);
517
518                let mut iter =
519                    area.rev_print_iter(text.iter_rev(end_points), cfg.new_line_as('\n'));
520                let wcol_and_p = iter.find_map(|(Caret { x, len, wrap }, item)| {
521                    if let Some((p, _)) = item.as_real_char() {
522                        if (x..x + len).contains(&(vp.dwcol as u32))
523                            || (just_wrapped && x + len < vp.dwcol as u32)
524                        {
525                            last_valid = (x as u16, p);
526                            if wraps == by {
527                                return Some((x as u16, p));
528                            }
529                        }
530                        just_wrapped = false;
531                    }
532                    wraps -= wrap as i32;
533                    just_wrapped |= wrap;
534                    None
535                });
536
537                if let Some((wcol, p)) = wcol_and_p {
538                    let (ccol, vcol) = iter
539                        .take_while(|(_, item)| item.as_real_char().is_none_or(|(_, c)| c != '\n'))
540                        .fold((0, 0), |(ccol, vcol), (caret, item)| {
541                            (ccol + item.is_real() as u16, vcol + caret.len as u16)
542                        });
543                    vp.known(p, ccol, vcol, wcol)
544                } else {
545                    let (wcol, p) = last_valid;
546                    let (ccol, vcol) = area
547                        .rev_print_iter(text.iter_rev(p), cfg.new_line_as('\n'))
548                        .take_while(|(_, item)| item.as_real_char().is_none_or(|(_, c)| c != '\n'))
549                        .fold((0, 0), |(ccol, vcol), (caret, item)| {
550                            (ccol + item.is_real() as u16, vcol + caret.len as u16)
551                        });
552                    vp.known(p, ccol, vcol, wcol)
553                }
554            });
555
556            wraps
557        }
558
559        pub(crate) fn shift_by_change(&self, change: Change<&str>) {
560            let (shift, taken) = (change.shift(), change.taken_end());
561            if self.caret() >= change.start() {
562                let shifted_caret = self.caret().max(taken).shift_by(shift);
563                self.caret.set(LazyVPoint::Unknown(shifted_caret));
564            }
565            if let Some(anchor) = self.anchor.get()
566                && anchor.point() >= change.start()
567            {
568                let shifted_anchor = anchor.point().max(taken).shift_by(shift);
569                self.anchor.set(Some(LazyVPoint::Unknown(shifted_anchor)));
570            }
571        }
572
573        /// Assumes tha both parts of the cursor are ahead of the
574        /// shift
575        pub(crate) fn shift_by(&self, shift: [i32; 3]) {
576            let shifted_caret = self.caret().shift_by(shift);
577            self.caret.set(LazyVPoint::Unknown(shifted_caret));
578            if let Some(anchor) = self.anchor.get() {
579                let shifted_anchor = anchor.point().shift_by(shift);
580                self.anchor.set(Some(LazyVPoint::Unknown(shifted_anchor)));
581            }
582        }
583
584        /// Sets the position of the anchor to be the same as the
585        /// current cursor position in the file
586        ///
587        /// The `anchor` and `current` act as a range of text on the
588        /// file.
589        pub fn set_anchor(&mut self) {
590            *self.anchor.get_mut() = Some(self.caret.get())
591        }
592
593        /// Unsets the anchor
594        ///
595        /// This is done so the cursor no longer has a valid
596        /// selection.
597        pub fn unset_anchor(&mut self) -> Option<Point> {
598            self.anchor.take().map(|a| a.point())
599        }
600
601        /// Switches the position of the anchor and caret
602        pub fn swap_ends(&mut self) {
603            if let Some(anchor) = self.anchor.get_mut() {
604                std::mem::swap(self.caret.get_mut(), anchor);
605            }
606        }
607
608        /// Returns the cursor's position on the screen
609        pub fn caret(&self) -> Point {
610            self.caret.get().point()
611        }
612
613        /// The anchor of this [`Selection`], if it exists
614        pub fn anchor(&self) -> Option<Point> {
615            self.anchor.get().map(|a| a.point())
616        }
617
618        /// The byte (relative to the beginning of the file) of the
619        /// caret. Indexed at 0
620        pub fn byte(&self) -> usize {
621            self.caret.get().point().byte()
622        }
623
624        /// The char (relative to the beginning of the file) of the
625        /// caret. Indexed at 0
626        pub fn char(&self) -> usize {
627            self.caret.get().point().char()
628        }
629
630        /// The line of the caret. Indexed at 0.
631        pub fn line(&self) -> usize {
632            self.caret.get().point().line()
633        }
634
635        /// Returns the range between `caret` and `anchor`.
636        ///
637        /// If `anchor` isn't set, returns an empty range on `caret`.
638        ///
639        /// A [`Selection`]'s range will also never include the last
640        /// character in a [`Text`], which must be a newline.
641        ///
642        /// # Warning
643        ///
644        /// This function will return the range that is supposed
645        /// to be replaced, if `self.is_inclusive()`, this means that
646        /// it will return one more byte at the end, i.e. start..=end.
647        pub fn range(&self, text: &Text) -> Range<usize> {
648            let [start, end] = self.point_range(text);
649            start.byte()..end.byte()
650        }
651
652        /// The starting [`Point`] of this [`Selection`]
653        pub fn start(&self) -> Point {
654            if let Some(anchor) = self.anchor.get() {
655                anchor.point().min(self.caret.get().point())
656            } else {
657                self.caret.get().point()
658            }
659        }
660
661        /// The ending [`Point`] of this [`Selection`]
662        pub fn end(&self, text: &Text) -> Point {
663            let raw = self.end_excl();
664            raw.fwd(text.char_at(raw).unwrap())
665        }
666
667        pub(crate) fn end_excl(&self) -> Point {
668            if let Some(anchor) = self.anchor.get() {
669                anchor.point().max(self.caret.get().point())
670            } else {
671                self.caret.get().point()
672            }
673        }
674
675        pub(crate) fn tag_points(&self, text: &Text) -> (Point, Option<[Point; 2]>) {
676            let caret = self.caret();
677            if let Some(anchor) = self.anchor() {
678                match anchor.cmp(&caret) {
679                    Ordering::Less => (caret, Some([anchor, caret])),
680                    Ordering::Equal => (caret, None),
681                    Ordering::Greater => {
682                        let end = anchor.fwd(text.char_at(anchor).unwrap());
683                        (caret, Some([caret, end]))
684                    }
685                }
686            } else {
687                (caret, None)
688            }
689        }
690
691        /// Returns the range between `target` and `anchor`.
692        ///
693        /// like [`Selection::range`], this function will not include
694        /// beyond the last character's [`Point`].
695        ///
696        /// If `anchor` isn't set, returns an empty range on `target`.
697        pub fn point_range(&self, text: &Text) -> [Point; 2] {
698            [self.start(), self.end(text)]
699        }
700
701        /// Sets both the desired visual column, as well as the
702        /// desired wrapped column
703        pub fn set_desired_cols(&mut self, v: usize, w: usize) {
704            let (v, w) = (v as u16, w as u16);
705            match self.caret.get_mut() {
706                LazyVPoint::Known(vp) => {
707                    vp.dvcol = v;
708                    vp.dwcol = w;
709                }
710                LazyVPoint::Unknown(p) => {
711                    *self.caret.get_mut() = LazyVPoint::Desired { p: *p, dvcol: v, dwcol: w }
712                }
713                LazyVPoint::Desired { dvcol, dwcol, .. } => (*dvcol, *dwcol) = (v, w),
714            }
715        }
716
717        /// The visual caret of this [`Selection`]
718        ///
719        /// [`VPoint`]s include a lot more information than regular
720        /// [`Point`]s, like visual distance form the left edge, what
721        /// the desired distance is, etc.
722        pub fn v_caret(&self, text: &Text, area: &impl RawArea, cfg: PrintCfg) -> VPoint {
723            let vp = self.caret.get().calculate(text, area, cfg);
724            self.caret.set(LazyVPoint::Known(vp));
725            vp
726        }
727
728        /// The visual anchor of this [`Selection`], if it exists
729        ///
730        /// [`VPoint`]s include a lot more information than regular
731        /// [`Point`]s, like visual distance form the left edge, what
732        /// the desired distance is, etc.
733        pub fn v_anchor(&self, text: &Text, area: &impl RawArea, cfg: PrintCfg) -> Option<VPoint> {
734            self.anchor.get().map(|anchor| {
735                let vp = anchor.calculate(text, area, cfg);
736                self.anchor.set(Some(LazyVPoint::Known(vp)));
737                vp
738            })
739        }
740
741        /// The visual range between the caret and anchor of this
742        /// [`Selection`]
743        ///
744        /// [`VPoint`]s include a lot more information than regular
745        /// [`Point`]s, like visual distance form the left edge, what
746        /// the desired distance is, etc.
747        pub fn v_range(&self, text: &Text, area: &impl RawArea, cfg: PrintCfg) -> [VPoint; 2] {
748            let v_caret = self.v_caret(text, area, cfg);
749            let v_anchor = self.v_anchor(text, area, cfg).unwrap_or(v_caret);
750            [v_caret.min(v_anchor), v_caret.max(v_anchor)]
751        }
752
753        /// The starting [`LazyVPoint`]
754        pub(super) fn lazy_v_start(&self) -> LazyVPoint {
755            match self.anchor.get() {
756                Some(anchor) => self.caret.get().min(anchor),
757                None => self.caret.get(),
758            }
759        }
760
761        /// The ending [`LazyVPoint`]
762        pub(super) fn lazy_v_end(&self) -> LazyVPoint {
763            match self.anchor.get() {
764                Some(anchor) => self.caret.get().max(anchor),
765                None => self.caret.get(),
766            }
767        }
768    }
769
770    /// A struct meant to minimize calculations on very large numbers
771    /// of [`Selection`]s
772    #[derive(Clone, Copy, Eq, Encode, Decode)]
773    pub(super) enum LazyVPoint {
774        Known(VPoint),
775        Unknown(Point),
776        Desired { p: Point, dvcol: u16, dwcol: u16 },
777    }
778
779    impl LazyVPoint {
780        /// The actual [`Point`]
781        fn point(self) -> Point {
782            match self {
783                LazyVPoint::Known(vp) => vp.p,
784                LazyVPoint::Unknown(p) => p,
785                LazyVPoint::Desired { p, .. } => p,
786            }
787        }
788
789        /// Calculates the [`VPoint`], to be used sparingly
790        fn calculate(self, text: &Text, area: &impl RawArea, cfg: PrintCfg) -> VPoint {
791            match self {
792                Self::Known(vp) => vp,
793                Self::Unknown(p) => VPoint::new(p, text, area, cfg),
794                Self::Desired { p, dvcol, dwcol } => {
795                    let mut vp = VPoint::new(p, text, area, cfg);
796                    vp.dvcol = dvcol;
797                    vp.dwcol = dwcol;
798                    vp
799                }
800            }
801        }
802    }
803
804    impl Default for LazyVPoint {
805        fn default() -> Self {
806            Self::Desired { p: Point::default(), dvcol: 0, dwcol: 0 }
807        }
808    }
809
810	#[allow(clippy::non_canonical_partial_ord_impl)]
811    impl PartialOrd for LazyVPoint {
812        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
813            Some(self.point().cmp(&other.point()))
814        }
815    }
816
817    impl Ord for LazyVPoint {
818        fn cmp(&self, other: &Self) -> Ordering {
819            self.partial_cmp(other).unwrap()
820        }
821    }
822
823    impl PartialEq for LazyVPoint {
824        fn eq(&self, other: &Self) -> bool {
825            self.point() == other.point()
826        }
827    }
828
829    /// A visual [`Point`], which includes more information
830    ///
831    /// Alongside the byte, char, and line of the [`Point`], this
832    /// struct has:
833    ///
834    /// - Number of [`char`]s from the left edge
835    /// - Number of visual cells from the left edge
836    /// - Desired number of visual cells from the left edge
837    /// - Number of wrapped cells from the left edge
838    /// - Desired number of wrapped cells from the left edge
839    ///
840    /// The difference between visual cells and wrapped cells is that
841    /// visual cells are essentially "The distance a [`Point`] would
842    /// be if this line were not wrapped"
843    ///
844    /// Desired cells are used when moving vertically, since when you
845    /// move a [`Selection`] up or down to a shorter line, then to a
846    /// longer one, you expect the horizontal position to hold. This
847    /// is applied both in [full line] and [wrapped line] vertical
848    /// movement.
849    ///
850    /// [full line]: crate::mode::Cursor::move_ver
851    /// [wrapped line]: crate::mode::Cursor::move_ver_wrapped
852    #[derive(Clone, Copy, Debug, Eq, Encode, Decode)]
853    pub struct VPoint {
854        p: Point,
855        // No plan to support lines that are far too long
856        ccol: u16,
857        vcol: u16,
858        dvcol: u16,
859        wcol: u16,
860        dwcol: u16,
861    }
862
863    impl VPoint {
864        /// Returns a new [`VPoint`]
865        fn new(p: Point, text: &Text, area: &impl RawArea, cfg: PrintCfg) -> Self {
866            let [start, _] = text.points_of_line(p.line());
867
868            let mut vcol = 0;
869
870            let wcol = area
871                .print_iter(text.iter_fwd(text.visual_line_start(start)), cfg)
872                .find_map(|(caret, item)| {
873                    if let Some((lhs, _)) = item.as_real_char()
874                        && lhs == p
875                    {
876                        return Some(caret.x as u16);
877                    }
878                    vcol += caret.len as u16;
879                    None
880                })
881                .unwrap_or(0);
882
883            Self {
884                p,
885                ccol: (p.char() - start.char()) as u16,
886                vcol,
887                dvcol: vcol,
888                wcol,
889                dwcol: wcol,
890            }
891        }
892
893        /// Returns a new [`VPoint`] from raw data
894        fn known(self, p: Point, ccol: u16, vcol: u16, wcol: u16) -> Self {
895            Self { p, ccol, vcol, wcol, ..self }
896        }
897
898        /// The byte index of this [`VPoint`]
899        pub fn byte(&self) -> usize {
900            self.p.byte()
901        }
902
903        /// The char index of this [`VPoint`]
904        pub fn char(&self) -> usize {
905            self.p.char()
906        }
907
908        /// The line index of this [`VPoint`]
909        pub fn line(&self) -> usize {
910            self.p.line()
911        }
912
913        /// Number of characters from the start of the line
914        pub fn char_col(&self) -> usize {
915            self.ccol as usize
916        }
917
918        /// Total space from the start of the line
919        pub fn visual_col(&self) -> usize {
920            self.vcol as usize
921        }
922
923        /// How much space there should be from the start of the line
924        pub fn desired_visual_col(&self) -> usize {
925            self.dvcol as usize
926        }
927
928        /// Total space from the left edge
929        pub fn wrapped_col(&self) -> usize {
930            self.wcol as usize
931        }
932
933        /// How much space there should be from the left edge
934        pub fn desired_wrapped_col(&self) -> usize {
935            self.dwcol as usize
936        }
937    }
938
939	#[allow(clippy::non_canonical_partial_ord_impl)]
940    impl PartialOrd for VPoint {
941        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
942            Some(self.p.cmp(&other.p))
943        }
944    }
945
946    impl Ord for VPoint {
947        fn cmp(&self, other: &Self) -> Ordering {
948            self.partial_cmp(other).unwrap()
949        }
950    }
951
952    impl PartialEq for VPoint {
953        fn eq(&self, other: &Self) -> bool {
954            self.p == other.p
955        }
956    }
957
958    impl std::fmt::Debug for Selection {
959        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
960            f.debug_struct("Selection")
961                .field("caret", &self.caret.get())
962                .field("anchor", &self.anchor.get())
963                .field("change_i", &self.change_i)
964                .finish()
965        }
966    }
967
968    impl std::fmt::Debug for LazyVPoint {
969        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
970            match self {
971                Self::Known(vp) => write!(f, "Known({:?})", vp.p),
972                Self::Unknown(p) => write!(f, "Unknown({p:?}"),
973                Self::Desired { p, .. } => write!(f, "Desired({p:?})"),
974            }
975        }
976    }
977}
978
979impl std::fmt::Debug for Selections {
980    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
981        struct DebugShiftState((usize, [i32; 3]));
982        impl std::fmt::Debug for DebugShiftState {
983            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
984                write!(f, "{:?}", self.0)
985            }
986        }
987
988        f.debug_struct("Selections")
989            .field("buf", &self.buf)
990            .field("main_i", &self.main_i)
991            .field("shift_sate", &DebugShiftState(self.shift_state.get()))
992            .finish()
993    }
994}