duat_core/mode/helper/
cursors.rs

1use std::ops::RangeBounds;
2
3use gapbuf::{GapBuffer, gap_buffer};
4use serde::{Deserialize, Serialize, de::Visitor, ser::SerializeSeq};
5
6pub use self::cursor::Cursor;
7use crate::{
8    cfg::PrintCfg,
9    text::{Point, Text},
10    ui::Area,
11};
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct Cursors {
15    buf: CursorGapBuffer,
16    main_i: usize,
17    is_incl: bool,
18}
19
20impl Cursors {
21    ////////// Definition functions
22    pub fn new_excl() -> Self {
23        Self {
24            buf: CursorGapBuffer(gap_buffer![Cursor::default()]),
25            main_i: 0,
26            is_incl: false,
27        }
28    }
29
30    pub fn new_incl() -> Self {
31        Self {
32            buf: CursorGapBuffer(gap_buffer![Cursor::default()]),
33            main_i: 0,
34            is_incl: true,
35        }
36    }
37
38    pub fn reset_on_byte(&mut self, b: usize, text: &Text, area: &impl Area, cfg: PrintCfg) {
39        let point = text.point_at(b.min(text.len().byte()));
40        let cursor = Cursor::new(point, text, area, cfg);
41        self.buf = CursorGapBuffer(gap_buffer![cursor]);
42        self.main_i = 0;
43    }
44
45    pub fn make_excl(&mut self) {
46        self.is_incl = false;
47    }
48
49    pub fn make_incl(&mut self) {
50        self.is_incl = true;
51    }
52
53    ////////// Insertion functions
54    pub fn insert_from_parts(
55        &mut self,
56        guess_i: usize,
57        point: Point,
58        range: usize,
59        text: &Text,
60        area: &impl Area,
61        cfg: PrintCfg,
62    ) -> usize {
63        let mut cursor = Cursor::new(point, text, area, cfg);
64        let range = match self.is_incl {
65            true => range.saturating_sub(1),
66            false => range,
67        };
68        if range > 0 {
69            cursor.set_anchor();
70            cursor.move_hor(range as i32, text, area, cfg);
71        }
72        self.insert(guess_i, false, cursor)
73    }
74
75    pub(super) fn insert(&mut self, guess_i: usize, was_main: bool, cursor: Cursor) -> usize {
76        // The range of cursors that will be drained
77        let c_range = if let Some(prev_i) = guess_i.checked_sub(1)
78            && let Some(prev) = self.get(prev_i)
79            && prev.start() <= cursor.start()
80            && cursor.start_v() <= prev.end_v()
81        {
82            prev_i..guess_i
83        } else {
84            let buf = self.buf.range(..);
85            match binary_search_by_key(buf, cursor.start_v(), |c| c.start_v()) {
86                Ok(i) => i..i + 1,
87                Err(i) => {
88                    if let Some(prev_i) = i.checked_sub(1)
89                        && let Some(prev) = self.buf.get(prev_i)
90                        && cursor.start_v() <= prev.end_v()
91                    {
92                        prev_i..i
93                    } else {
94                        i..i
95                    }
96                }
97            }
98        };
99
100        // This block determines how far ahead this cursor will merge
101        let c_range = if self
102            .get(c_range.end)
103            .is_none_or(|c| cursor.end_v() < c.start_v())
104        {
105            c_range
106        } else {
107            let buf = self.buf.range(..);
108            match binary_search_by_key(buf, cursor.end_v(), |c| c.start_v()) {
109                Ok(i) => c_range.start..i + 1,
110                Err(i) => {
111                    if let Some(prev) = self.buf.get(i)
112                        && prev.start_v() < cursor.end_v()
113                    {
114                        c_range.start..i + 1
115                    } else {
116                        c_range.start..i
117                    }
118                }
119            }
120        };
121
122        let mut c_range_iter = c_range.clone();
123        let first = c_range_iter.next().and_then(|i| self.get(i));
124        let last = c_range_iter.last().and_then(|i| self.get(i));
125        let start = first
126            .map(|c| c.start_v().min(cursor.start_v()))
127            .unwrap_or(cursor.start_v());
128        let end = last
129            .map(|c| c.end_v().max(cursor.end_v()))
130            .unwrap_or(cursor.end_v());
131
132        let (caret, anchor) = if let Some(anchor) = cursor.anchor() {
133            match cursor.caret() < anchor {
134                true => (start, Some(end)),
135                false => (end, Some(start)),
136            }
137        } else {
138            (end, (start != end).then_some(start))
139        };
140
141        let cursor = Cursor::from_v(caret, anchor, cursor.change_i);
142        self.buf.drain(c_range.clone());
143        self.buf.insert(c_range.start, cursor);
144
145        if was_main {
146            self.main_i = c_range.start;
147        } else if self.main_i > c_range.start {
148            self.main_i = (self.main_i - c_range.clone().count()).max(c_range.start)
149        }
150
151        c_range.start
152    }
153
154    pub fn rotate_main(&mut self, amount: i32) {
155        self.main_i = (self.main_i as i32 + amount).rem_euclid(self.buf.len() as i32) as usize
156    }
157
158    pub fn remove_extras(&mut self) {
159        if !self.is_empty() {
160            let cursor = self.buf[self.main_i];
161            self.buf = CursorGapBuffer(gap_buffer![cursor]);
162        }
163        self.main_i = 0;
164    }
165
166    /// The main [`Cursor`] in use
167    ///
168    /// # Panics
169    ///
170    /// Will panic if there are no [`Cursor`]s
171    pub fn main(&self) -> &Cursor {
172        &self.buf[self.main_i]
173    }
174
175    pub fn get_main(&self) -> Option<Cursor> {
176        self.get(self.main_i)
177    }
178
179    pub fn get(&self, i: usize) -> Option<Cursor> {
180        self.buf.get(i).cloned()
181    }
182
183    pub fn iter(&self) -> impl Iterator<Item = (&Cursor, bool)> {
184        self.buf
185            .iter()
186            .enumerate()
187            .map(move |(index, cursor)| (cursor, index == self.main_i))
188    }
189
190    pub fn main_index(&self) -> usize {
191        self.main_i
192    }
193
194    pub fn len(&self) -> usize {
195        self.buf.len()
196    }
197
198    #[must_use]
199    pub fn is_empty(&self) -> bool {
200        self.len() == 0
201    }
202
203    pub fn is_incl(&self) -> bool {
204        self.is_incl
205    }
206
207    pub fn clear(&mut self) {
208        self.buf = CursorGapBuffer(GapBuffer::new())
209    }
210
211    pub fn reset(&mut self) {
212        self.remove_extras();
213        self.buf[self.main_i] = Cursor::default();
214    }
215
216    pub(super) fn remove(&mut self, i: usize) -> Option<(Cursor, bool)> {
217        (i < self.buf.len()).then(|| {
218            let was_main = self.main_i == i;
219            if self.main_i >= i {
220                self.main_i = self.main_i.saturating_sub(1);
221            }
222            (self.buf.remove(i), was_main)
223        })
224    }
225
226    pub(crate) fn shift_by(
227        &mut self,
228        from: usize,
229        shift: (i32, i32, i32),
230        text: &Text,
231        area: &impl Area,
232        cfg: PrintCfg,
233    ) {
234        for cursor in self.buf.iter_mut().skip(from) {
235            cursor.shift_by(shift, text, area, cfg);
236        }
237    }
238
239    pub(super) fn drain(
240        &mut self,
241        range: impl RangeBounds<usize>,
242    ) -> impl Iterator<Item = (Cursor, bool)> + '_ {
243        let orig_main = self.main_i;
244        self.main_i = 0;
245        self.buf
246            .drain(range)
247            .enumerate()
248            .map(move |(i, c)| match i == orig_main {
249                true => (c, true),
250                false => (c, false),
251            })
252    }
253
254    pub(super) fn populate(&mut self) {
255        if self.buf.0.is_empty() {
256            self.main_i = 0;
257            self.buf.0 = gap_buffer![Cursor::default()];
258        }
259    }
260}
261
262impl Default for Cursors {
263    fn default() -> Self {
264        Self::new_excl()
265    }
266}
267
268mod cursor {
269    use std::ops::Range;
270
271    use serde::{Deserialize, Serialize};
272
273    use crate::{
274        cfg::{IterCfg, PrintCfg},
275        text::{Point, Text},
276        ui::{Area, Caret},
277    };
278
279    /// A cursor in the text file. This is an editing cursor, -(not
280    /// a printing cursor.
281    #[derive(Default, Clone, Copy, Debug, Serialize, Deserialize)]
282    pub struct Cursor {
283        caret: VPoint,
284        anchor: Option<VPoint>,
285        pub(in crate::mode::helper) change_i: Option<usize>,
286    }
287
288    impl Cursor {
289        /// Returns a new instance of [`Cursor`].
290        pub(super) fn new(point: Point, text: &Text, area: &impl Area, cfg: PrintCfg) -> Self {
291            Self {
292                caret: VPoint::new(point, text, area, cfg),
293                // This should be fine.
294                anchor: None,
295                change_i: None,
296            }
297        }
298
299        pub(super) fn from_v(
300            caret: VPoint,
301            anchor: Option<VPoint>,
302            change_i: Option<usize>,
303        ) -> Self {
304            Self { caret, anchor, change_i }
305        }
306
307        /// Moves to specific, pre calculated [`Point`].
308        pub fn move_to(&mut self, point: Point, text: &Text, area: &impl Area, cfg: PrintCfg) {
309            if point == self.caret() {
310                return;
311            }
312            let Some(last) = text.last_point() else {
313                self.anchor = None;
314                self.caret = VPoint::default();
315                return;
316            };
317            self.caret = VPoint::new(point.min(last), text, area, cfg);
318        }
319
320        /// Internal horizontal movement function.
321        pub fn move_hor(&mut self, by: i32, text: &Text, area: &impl Area, cfg: PrintCfg) {
322            let by = by as isize;
323            let (Some(last), false) = (text.last_point(), by == 0) else {
324                return;
325            };
326            let target = self.caret.point.char().saturating_add_signed(by);
327
328            let point = if target == 0 {
329                Point::default()
330            } else if target >= last.char() {
331                last
332            } else if by.abs() < 500 {
333                if by > 0 {
334                    let (point, _) = text
335                        .chars_fwd(self.caret())
336                        .take(by as usize + 1)
337                        .last()
338                        .unwrap();
339                    point
340                } else {
341                    let (point, _) = text
342                        .chars_rev(self.caret())
343                        .take(by.unsigned_abs())
344                        .last()
345                        .unwrap();
346                    point
347                }
348            } else {
349                text.point_at_char(target)
350            };
351
352            self.caret = VPoint::new(point, text, area, cfg);
353        }
354
355        /// Internal vertical movement function.
356        pub fn move_ver(&mut self, by: i32, text: &Text, area: &impl Area, cfg: PrintCfg) {
357            let by = by as isize;
358            let (Some(last), false) = (text.last_point(), by == 0) else {
359                return;
360            };
361            let cfg = IterCfg::new(cfg).dont_wrap();
362            let dcol = self.caret.dcol;
363
364            let point = {
365                let target = self.caret.line().saturating_add_signed(by).min(last.line());
366                let point = text.point_at_line(target);
367
368                area.print_iter(text.iter_fwd(point), cfg)
369                    .filter_map(|(caret, item)| Some(caret).zip(item.as_real_char()))
370                    .find_map(|(Caret { x, len, .. }, (p, char))| {
371                        (p.line() == target && (x + len > dcol || char == '\n')).then_some(p)
372                    })
373                    .unwrap_or(last)
374            };
375
376            self.caret.point = point;
377            self.caret.vcol = vcol(point, text, area, cfg)
378        }
379
380        /// Internal vertical movement function.
381        pub fn move_ver_wrapped(&mut self, by: i32, text: &Text, area: &impl Area, cfg: PrintCfg) {
382            if text.last_point().is_none() || by == 0 {
383                return;
384            };
385            let cfg = IterCfg::new(cfg);
386            let dwcol = self.caret.dwcol;
387
388            let mut wraps = 0;
389            let mut last_valid = self.caret();
390            let mut new_wrap = false;
391
392            let point = if by > 0 {
393                let line_start = text.visual_line_start(self.caret.point);
394
395                area.print_iter(text.iter_fwd(line_start), cfg)
396                    .skip_while(|(_, item)| item.byte() <= self.byte())
397                    .filter_map(|(caret, item)| {
398                        wraps += caret.wrap as i32;
399                        Some((caret, wraps)).zip(item.as_real_char())
400                    })
401                    .find_map(|((Caret { x, len, wrap }, wraps), (p, char))| {
402                        new_wrap |= wrap;
403                        if x + len > dwcol || char == '\n' {
404                            if new_wrap {
405                                new_wrap = false;
406                                last_valid = p;
407                            }
408                            (wraps == by).then_some(p)
409                        } else {
410                            None
411                        }
412                    })
413            } else {
414                let end = text.points_after(self.caret.point).unwrap();
415
416                area.rev_print_iter(text.iter_rev(end), cfg)
417                    .filter_map(|(Caret { x, wrap, .. }, item)| {
418                        let old_wraps = wraps;
419                        wraps -= wrap as i32;
420                        Some((x, old_wraps, wrap)).zip(item.as_real_char())
421                    })
422                    .find_map(|((x, wraps, wrap), (p, _))| {
423                        if dwcol >= x || wrap {
424                            if new_wrap {
425                                new_wrap = false;
426                                last_valid = p;
427                            }
428                            (wraps == by).then_some(p)
429                        } else {
430                            None
431                        }
432                    })
433            };
434
435            self.caret.point = point.unwrap_or(last_valid);
436            self.caret.vcol = vcol(self.caret.point, text, area, cfg.dont_wrap())
437        }
438
439        pub(crate) fn shift_by(
440            &mut self,
441            shift: (i32, i32, i32),
442            text: &Text,
443            area: &impl Area,
444            cfg: PrintCfg,
445        ) {
446            let shifted_caret = self.caret().shift_by(shift);
447            self.move_to(shifted_caret, text, area, cfg);
448            if let Some(anchor) = self.anchor() {
449                let shifted_anchor = anchor.shift_by(shift);
450                self.swap_ends();
451                self.move_to(shifted_anchor, text, area, cfg);
452                self.swap_ends();
453            }
454        }
455
456        /// Sets the position of the anchor to be the same as the
457        /// current cursor position in the file
458        ///
459        /// The `anchor` and `current` act as a range of text on the
460        /// file.
461        pub fn set_anchor(&mut self) {
462            self.anchor = Some(self.caret)
463        }
464
465        /// Unsets the anchor
466        ///
467        /// This is done so the cursor no longer has a valid
468        /// selection.
469        pub fn unset_anchor(&mut self) -> Option<Point> {
470            self.anchor.take().map(|a| a.point)
471        }
472
473        /// Switches the position of the anchor and caret
474        pub fn swap_ends(&mut self) {
475            if let Some(anchor) = self.anchor.as_mut() {
476                std::mem::swap(&mut self.caret, anchor);
477            }
478        }
479
480        /// Returns the cursor's position on the screen
481        pub fn caret(&self) -> Point {
482            self.caret.point
483        }
484
485        pub fn anchor(&self) -> Option<Point> {
486            self.anchor.map(|a| a.point)
487        }
488
489        /// The byte (relative to the beginning of the file) of the
490        /// caret. Indexed at 0
491        pub fn byte(&self) -> usize {
492            self.caret.byte()
493        }
494
495        /// The char (relative to the beginning of the file) of the
496        /// caret. Indexed at 0
497        pub fn char(&self) -> usize {
498            self.caret.char()
499        }
500
501        /// The line of the caret. Indexed at 0.
502        pub fn line(&self) -> usize {
503            self.caret.line()
504        }
505
506        /// The column of the caret. Indexed at 0
507        pub fn vcol(&self) -> usize {
508            self.caret.vcol()
509        }
510
511        pub fn anchor_vcol(&self) -> Option<usize> {
512            self.anchor.map(|a| a.vcol())
513        }
514
515        pub fn desired_vcol(&self) -> usize {
516            self.caret.dcol as usize
517        }
518
519        pub fn desired_anchor_vcol(&self) -> Option<usize> {
520            self.anchor.map(|a| a.dcol as usize)
521        }
522
523        /// Returns the range between `target` and `anchor`.
524        ///
525        /// If `anchor` isn't set, returns an empty range on `target`.
526        ///
527        /// A [`Cursor`]'s range will also never include the last
528        /// character in a [`Text`], which must be a newline.
529        ///
530        /// # Warning
531        ///
532        /// This function will return the range that is supposed
533        /// to be replaced, if `self.is_inclusive()`, this means that
534        /// it will return one more byte at the end, i.e. start..=end.
535        pub fn range(&self, is_inclusive: bool, text: &Text) -> Range<usize> {
536            let anchor = self.anchor.unwrap_or(self.caret);
537            let (start, end) = if anchor < self.caret {
538                (anchor.byte(), self.caret.byte())
539            } else {
540                (self.caret.byte(), anchor.byte())
541            };
542
543            let last = text.last_point();
544            if let Some(last) = last {
545                let go_further = is_inclusive && last.byte() > end;
546                start..if go_further { end + 1 } else { end }
547            } else {
548                0..0
549            }
550        }
551
552        /// The starting [`Point`] of this [`Cursor`]
553        pub fn start(&self) -> Point {
554            if let Some(anchor) = self.anchor {
555                anchor.point.min(self.caret.point)
556            } else {
557                self.caret.point
558            }
559        }
560
561        /// Returns the range between `target` and `anchor`.
562        ///
563        /// like [`Cursor::range`], this function will not include
564        /// beyond the last character's [`Point`].
565        ///
566        /// If `anchor` isn't set, returns an empty range on `target`.
567        pub fn point_range(&self, is_incl: bool, text: &Text) -> (Point, Point) {
568            let anchor = self.anchor.unwrap_or(self.caret);
569            let mut end = self.caret.point.max(anchor.point);
570            if is_incl
571                && end.byte() + 1 != text.len().byte()
572                && let Some(char) = text.char_at(end)
573            {
574                end = end.fwd(char)
575            }
576            (self.caret.point.min(anchor.point), end)
577        }
578
579        /// Sets the desired visual column
580        ///
581        /// The desired visual column determines at what point in a
582        /// line the caret will be placed when moving up and
583        /// down through lines of varying lengths.
584        pub fn set_desired_v_col(&mut self, x: usize) {
585            self.caret.dcol = x as u32;
586        }
587
588        /// Sets the desired wrapped visual column
589        ///
590        /// The desired wrapped visual column determines at what point
591        /// in a line the caret will be placed when moving up
592        /// and down through wrapped lines of varying lengths.
593        pub fn set_desired_wrapped_v_col(&mut self, x: usize) {
594            self.caret.dwcol = x as u32;
595        }
596
597        pub(super) fn start_v(&self) -> VPoint {
598            match self.anchor {
599                Some(anchor) => self.caret.min(anchor),
600                None => self.caret,
601            }
602        }
603
604        pub(super) fn end_v(&self) -> VPoint {
605            match self.anchor {
606                Some(anchor) => self.caret.max(anchor),
607                None => self.caret,
608            }
609        }
610    }
611
612    impl std::fmt::Display for Cursor {
613        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614            write!(
615                f,
616                "{}:{}, {}",
617                self.caret.line() + 1,
618                self.caret.vcol() + 1,
619                self.caret.dcol
620            )
621        }
622    }
623
624    #[derive(Default, Clone, Copy, Eq, Debug, Serialize, Deserialize)]
625    pub struct VPoint {
626        point: Point,
627        vcol: u32,
628        dcol: u32,
629        dwcol: u32,
630    }
631
632    impl PartialOrd for VPoint {
633        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
634            Some(self.point.cmp(&other.point))
635        }
636    }
637
638    impl Ord for VPoint {
639        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
640            self.partial_cmp(other).unwrap()
641        }
642    }
643
644    impl PartialEq for VPoint {
645        fn eq(&self, other: &Self) -> bool {
646            self.point == other.point
647        }
648    }
649
650    impl VPoint {
651        fn new(point: Point, text: &Text, area: &impl Area, cfg: PrintCfg) -> Self {
652            let cfg = IterCfg::new(cfg);
653            let dwcol = vcol(point, text, area, cfg);
654            let vcol = vcol(point, text, area, cfg.dont_wrap());
655            Self { point, vcol, dcol: vcol, dwcol }
656        }
657
658        pub fn byte(&self) -> usize {
659            self.point.byte()
660        }
661
662        pub fn char(&self) -> usize {
663            self.point.char()
664        }
665
666        pub fn line(&self) -> usize {
667            self.point.line()
668        }
669
670        pub fn vcol(&self) -> usize {
671            self.vcol as usize
672        }
673    }
674
675    fn vcol(point: Point, text: &Text, area: &impl Area, cfg: IterCfg) -> u32 {
676        if let Some(after) = text.points_after(point) {
677            area.rev_print_iter(text.iter_rev(after), cfg)
678                .find_map(|(caret, item)| item.part.is_char().then_some(caret.x))
679                .unwrap_or(0)
680        } else {
681            area.rev_print_iter(text.iter_rev(text.len()), cfg)
682                .find_map(|(caret, item)| item.part.is_char().then_some(caret.x + caret.len))
683                .unwrap_or(0)
684        }
685    }
686}
687
688/// Binary searching by keys for [`GapBuffer`]s
689fn binary_search_by_key<K>(
690    buf: gapbuf::Range<Cursor>,
691    key: K,
692    f: impl Fn(&Cursor) -> K,
693) -> Result<usize, usize>
694where
695    K: PartialEq + Eq + PartialOrd + Ord,
696{
697    let mut size = buf.len();
698    let mut left = 0;
699    let mut right = size;
700
701    while left < right {
702        let mid = left + size / 2;
703
704        let k = f(&buf[mid]);
705
706        match k.cmp(&key) {
707            std::cmp::Ordering::Less => left = mid + 1,
708            std::cmp::Ordering::Equal => return Ok(mid),
709            std::cmp::Ordering::Greater => right = mid,
710        }
711
712        size = right - left;
713    }
714
715    Err(left)
716}
717
718#[derive(Clone)]
719struct CursorGapBuffer(GapBuffer<Cursor>);
720
721impl std::ops::Deref for CursorGapBuffer {
722    type Target = GapBuffer<Cursor>;
723
724    fn deref(&self) -> &Self::Target {
725        &self.0
726    }
727}
728
729impl std::ops::DerefMut for CursorGapBuffer {
730    fn deref_mut(&mut self) -> &mut Self::Target {
731        &mut self.0
732    }
733}
734
735impl std::fmt::Debug for CursorGapBuffer {
736    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737        std::fmt::Debug::fmt(&self.0, f)
738    }
739}
740
741impl Serialize for CursorGapBuffer {
742    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
743    where
744        S: serde::Serializer,
745    {
746        let mut ser_gapbuf = serializer.serialize_seq(Some(self.0.len()))?;
747
748        for cursor in self.0.iter() {
749            ser_gapbuf.serialize_element(cursor)?;
750        }
751        ser_gapbuf.end()
752    }
753}
754
755impl<'de> Deserialize<'de> for CursorGapBuffer {
756    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
757    where
758        D: serde::Deserializer<'de>,
759    {
760        struct GapBufferVisitor;
761
762        impl<'v> Visitor<'v> for GapBufferVisitor {
763            type Value = CursorGapBuffer;
764
765            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
766                write!(formatter, "This visitor expected a sequence of Cursors")
767            }
768
769            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
770            where
771                A: serde::de::SeqAccess<'v>,
772            {
773                let mut buf = if let Some(len) = seq.size_hint() {
774                    GapBuffer::with_capacity(len)
775                } else {
776                    GapBuffer::new()
777                };
778
779                while let Some(cursor) = seq.next_element()? {
780                    buf.push_back(cursor);
781                }
782
783                Ok(CursorGapBuffer(buf))
784            }
785        }
786
787        deserializer.deserialize_seq(GapBufferVisitor)
788    }
789}