duat_core/mode/helper/
cursors.rs

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