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