duat_core/text/
history.rs

1//! The history for a [`Text`]
2//!
3//! The [`History`] is composed of [`Moment`]s, each having a list of
4//! [`Change`]s. Whenever you [`undo`]/[`redo`], you are
5//! undoing/redoing a whole [`Moment`], with all of its [`Change`]s,
6//! all at once.
7//!
8//! This permits Vim style undoing (one [`Change`] per [`Moment`]) as
9//! well as Kakoune style undoing (multiple [`Change`]s per
10//! [`Moment`]).
11//!
12//! [`undo`]: Text::undo
13//! [`redo`]: Text::redo
14use std::{marker::PhantomData, ops::Range, sync::Arc};
15
16use bincode::{BorrowDecode, Decode, Encode};
17use parking_lot::Mutex;
18
19use super::{Point, Text};
20use crate::{
21    text::Bytes,
22    utils::{add_shifts as add, merging_range_by_guess_and_lazy_shift},
23};
24
25/// The history of edits, contains all moments
26#[derive(Default, Debug)]
27pub struct History {
28    new_moment: Moment,
29    moments: Vec<Moment>,
30    cur_moment: usize,
31    fetcher_moments: Arc<Mutex<Vec<FetcherState>>>,
32    saved_moment: Option<usize>,
33}
34
35impl History {
36    /// Adds a [`Change`] to the [`History`]
37    pub fn apply_change(
38        &mut self,
39        guess_i: Option<usize>,
40        change: Change<'static, String>,
41    ) -> usize {
42        let mut remote = self.fetcher_moments.lock();
43        for state in remote.iter_mut() {
44            state.add_change(change.clone());
45        }
46
47        self.new_moment.add_change(guess_i, change)
48    }
49
50    /// Declares that the current moment is complete and starts a
51    /// new one
52    pub fn new_moment(&mut self) {
53        if self.new_moment.is_empty() {
54            return;
55        }
56
57        self.moments.truncate(self.cur_moment);
58
59        if let Some(saved_moment) = self.saved_moment
60            && saved_moment >= self.cur_moment
61        {
62            self.saved_moment = None;
63        }
64
65        self.moments.push(std::mem::take(&mut self.new_moment));
66        self.cur_moment += 1;
67    }
68
69    /// Redoes the next [`Moment`], returning its [`Change`]s
70    ///
71    /// Applying these [`Change`]s in the order that they're given
72    /// will result in a correct redoing.
73    pub(super) fn move_forward(
74        &mut self,
75    ) -> Option<(impl ExactSizeIterator<Item = Change<'_>>, bool)> {
76        self.new_moment();
77        if self.cur_moment == self.moments.len() {
78            None
79        } else {
80            let mut remote = self.fetcher_moments.lock();
81            self.cur_moment += 1;
82
83            let iter = self.moments[self.cur_moment - 1]
84                .changes()
85                .inspect(move |change| {
86                    for state in remote.iter_mut() {
87                        state.add_change(change.to_string_change());
88                    }
89                });
90
91            let is_saved = self.saved_moment.is_some_and(|m| m == self.cur_moment);
92            Some((iter, is_saved))
93        }
94    }
95
96    /// Undoes a [`Moment`], returning its reversed [`Change`]s
97    ///
98    /// These [`Change`]s will already be shifted corectly, such that
99    /// applying them in sequential order, without further
100    /// modifications, will result in a correct undoing.
101    pub(super) fn move_backwards(
102        &mut self,
103    ) -> Option<(impl ExactSizeIterator<Item = Change<'_>>, bool)> {
104        self.new_moment();
105        if self.cur_moment == 0 {
106            None
107        } else {
108            let mut remote = self.fetcher_moments.lock();
109            self.cur_moment -= 1;
110
111            let mut shift = [0; 3];
112            let iter = self.moments[self.cur_moment].changes().map(move |change| {
113                let mut change = change.reverse();
114                change.shift_by(shift);
115                shift = add(shift, change.shift());
116
117                for state in remote.iter_mut() {
118                    state.add_change(change.to_string_change());
119                }
120
121                change
122            });
123
124            let is_saved = self.saved_moment.is_some_and(|m| m == self.cur_moment);
125            Some((iter, is_saved))
126        }
127    }
128
129    /// Returns a new [`MomentFetcher`]
130    pub(crate) fn new_fetcher(&self) -> MomentFetcher {
131        let mut remote = self.fetcher_moments.lock();
132        remote.push(FetcherState::Alive(Moment::default()));
133        MomentFetcher {
134            list: self.fetcher_moments.clone(),
135            index: remote.len() - 1,
136        }
137    }
138
139    /// Prepare this `History` for reloading
140    pub(super) fn prepare_for_reloading(&mut self) {
141        self.fetcher_moments = Arc::default();
142    }
143
144    /// Declares that the current state of the [`Text`] was saved on
145    /// disk
146    pub(super) fn declare_saved(&mut self) {
147        self.saved_moment = Some(self.cur_moment)
148    }
149}
150
151impl Clone for History {
152    fn clone(&self) -> Self {
153        Self {
154            new_moment: self.new_moment.clone(),
155            moments: self.moments.clone(),
156            cur_moment: self.cur_moment,
157            fetcher_moments: Arc::default(),
158            saved_moment: self.saved_moment,
159        }
160    }
161}
162
163/// A moment in history, which may contain changes, or may just
164/// contain selections
165///
166/// It also contains information about how to print the buffer, so
167/// that going back in time is less jarring.
168#[derive(Clone, Default, Debug, Encode, Decode)]
169pub struct Moment {
170    changes: Vec<Change<'static, String>>,
171    shift_state: (usize, [i32; 3]),
172}
173
174impl Moment {
175    /// First try to merge this change with as many changes as
176    /// possible, then add it in
177    fn add_change(&mut self, guess_i: Option<usize>, mut change: Change<'static, String>) -> usize {
178        let new_shift = change.shift();
179        let (from, shift) = self.shift_state;
180
181        // The range of changes that will be drained
182        let m_range = merging_range_by_guess_and_lazy_shift(
183            (&self.changes, self.changes.len()),
184            (guess_i.unwrap_or(0), [change.start(), change.taken_end()]),
185            (from, shift, [0; 3], Point::shift_by),
186            (Change::start, Change::added_end),
187        );
188
189        // If sh_from < c_range.end, I need to shift the changes between the
190        // two, so that they match the shifting of the changes before sh_from
191        if from < m_range.end && shift != [0; 3] {
192            for change in &mut self.changes[from..m_range.end] {
193                change.shift_by(shift);
194            }
195        // Otherwise, the shifting will happen in reverse, and `from`
196        // will be moved backwards until the point where m_range ends.
197        // This is better for localized Changes.
198        } else if from > m_range.end && shift != [0; 3] {
199            for change in &mut self.changes[m_range.end..from] {
200                change.shift_by(shift.map(|i| -i));
201            }
202        }
203
204        for c in self.changes.drain(m_range.clone()).rev() {
205            change.try_merge(c);
206        }
207
208        // Don't add empty Changes
209        let (from, shift) = if !(change.added_str() == "" && change.taken_str() == "") {
210            self.changes.insert(m_range.start, change);
211            (m_range.start + 1, add(shift, new_shift))
212        } else {
213            (m_range.start, shift)
214        };
215
216        if from < self.changes.len() {
217            self.shift_state = (from, shift);
218        } else {
219            self.shift_state = (0, [0; 3]);
220        }
221
222        m_range.start
223    }
224
225    /// An [`Iterator`] over the [`Change`]s in this [`Moment`]
226    ///
227    /// These may represent forward or backwards [`Change`]s, forward
228    /// for newly introduced [`Change`]s and backwards when undoing.
229    pub fn changes(&self) -> impl ExactSizeIterator<Item = Change<'_>> + '_ {
230        let (from, shift) = self.shift_state;
231        self.changes.iter().enumerate().map(move |(i, change)| {
232            let mut change = change.as_ref();
233            if i >= from {
234                change.shift_by(shift);
235            }
236            change
237        })
238    }
239
240    /// Returns the number of [`Change`]s in this [`Moment`]
241    ///
242    /// Can be `0`, since, in the case of [`Parser`]s, a call for
243    /// [`parse`] is always sent, in order to update [`Buffer`]s
244    /// every time they are printed.
245    ///
246    /// [`Parser`]: crate::buffer::Parser
247    /// [`parse`]: crate::buffer::Parser::parse
248    /// [`Buffer`]: crate::buffer::Buffer
249    pub fn len(&self) -> usize {
250        self.changes.len()
251    }
252
253    /// Wether there are any [`Change`]s in this [`Moment`]
254    ///
255    /// This can happen when creating a [`Moment::default`].
256    #[must_use]
257    pub fn is_empty(&self) -> bool {
258        self.len() == 0
259    }
260}
261
262/// A change in a buffer, with a start, taken text, and added text
263#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
264pub struct Change<'s, S = &'s str> {
265    start: [i32; 3],
266    added: S,
267    taken: S,
268    added_end: [i32; 3],
269    taken_end: [i32; 3],
270    _ghost: PhantomData<&'s str>,
271}
272
273impl Change<'static, String> {
274    /// Returns a new [Change].
275    pub fn new(edit: impl ToString, range: Range<Point>, text: &Text) -> Self {
276        let added = {
277            let edit = edit.to_string();
278            // A '\n' must be kept at the end, no matter what.
279            if range.start == text.len() && !edit.ends_with('\n') || range.end == text.len() {
280                edit + "\n"
281            } else {
282                edit
283            }
284        };
285
286        let taken = text.strs(range.clone()).unwrap().collect();
287        let added_end = add(range.start.as_signed(), Point::len_of(&added).as_signed());
288        Change {
289            start: range.start.as_signed(),
290            added,
291            taken,
292            added_end,
293            taken_end: range.end.as_signed(),
294            _ghost: PhantomData,
295        }
296    }
297
298    /// Returns a copyable [`Change`]
299    pub fn as_ref(&self) -> Change<'_, &str> {
300        Change {
301            start: self.start,
302            added: &self.added,
303            taken: &self.taken,
304            added_end: self.added_end,
305            taken_end: self.taken_end,
306            _ghost: PhantomData,
307        }
308    }
309
310    /// In this function, it is assumed that `self` happened
311    /// _after_ `newer`
312    ///
313    /// If the merger fails, the older [`Change`] will be returned;
314    pub fn try_merge(&mut self, mut older: Self) {
315        if has_start_of(older.added_range(), self.taken_range()) {
316            let fixed_end = older.added_end().min(self.taken_end());
317
318            let start = sub(self.start, older.start);
319            let end = sub(fixed_end.as_signed(), older.start);
320            let range = start[0] as usize..end[0] as usize;
321            older.added.replace_range(range, &self.added);
322
323            let range = (fixed_end.byte() - self.start[0] as usize)..;
324            older.taken.push_str(&self.taken[range]);
325
326            *self = older;
327        } else if has_start_of(self.taken_range(), older.added_range()) {
328            let fixed_end = self.taken_end().min(older.added_end());
329
330            let start = sub(older.start, self.start);
331            let end = sub(fixed_end.as_signed(), self.start);
332            let range = start[0] as usize..end[0] as usize;
333            self.taken.replace_range(range, &older.taken);
334
335            let range = (fixed_end.byte() - older.start[0] as usize)..;
336
337            self.added.push_str(&older.added[range]);
338        } else {
339            unreachable!("Changes chosen that don't interact");
340        }
341        self.added_end = add(self.start, Point::len_of(&self.added).as_signed());
342        self.taken_end = add(self.start, Point::len_of(&self.taken).as_signed());
343    }
344}
345
346impl<'a> Change<'a> {
347    /// Creates a [`Change<String>`] from a [`Change<&str>`]
348    pub fn to_string_change(&self) -> Change<'static, String> {
349        Change {
350            start: self.start,
351            added: self.added.to_string(),
352            taken: self.taken.to_string(),
353            added_end: self.added_end,
354            taken_end: self.taken_end,
355            _ghost: PhantomData,
356        }
357    }
358
359    /// Returns a new copyable [`Change`] from an insertion.
360    pub fn str_insert(added_str: &'a str, start: Point) -> Self {
361        Self {
362            start: start.as_signed(),
363            added: added_str,
364            taken: "",
365            added_end: (start + Point::len_of(added_str)).as_signed(),
366            taken_end: start.as_signed(),
367            _ghost: PhantomData,
368        }
369    }
370
371    pub(super) fn remove_last_nl(len: Point) -> Change<'static> {
372        Change {
373            start: len.rev('\n').as_signed(),
374            added: "",
375            taken: "\n",
376            added_end: len.rev('\n').as_signed(),
377            taken_end: len.as_signed(),
378            _ghost: PhantomData,
379        }
380    }
381}
382
383impl<'s, S: std::borrow::Borrow<str>> Change<'s, S> {
384    /// Returns a reversed version of this [`Change`]
385    pub fn reverse(self) -> Self {
386        Self {
387            start: self.start,
388            added: self.taken,
389            taken: self.added,
390            added_end: self.taken_end,
391            taken_end: self.added_end,
392            _ghost: PhantomData,
393        }
394    }
395
396    /// Gets a [`Range<Point>`], from the start to the end of the
397    /// affected lines
398    ///
399    /// For example, if you make an edit that transforms lines `1..=3`
400    /// to lines `1..=5`, this function will return a [`Range`] that
401    /// starts at the beginning of line 1, and ends at the end of line
402    /// 5.
403    ///
404    /// > [!NOTE]
405    /// >
406    /// > This end of this range will come _after_ the last `\n`,
407    /// > which means that, in that example, said point would have a
408    /// > [`Point::line`] value equal to 6, _not_ 5, since it
409    /// > represents both the end of line 5, and the beginning of line
410    /// > 6.
411    pub fn line_range(&self, bytes: &Bytes) -> Range<Point> {
412        let start = bytes.point_at_line(self.start[2] as usize);
413        let end_range = bytes.line_range(self.added_end[2] as usize);
414        start..end_range.end
415    }
416
417    /// The [`Point`] at the start of the change
418    pub fn start(&self) -> Point {
419        to_point(self.start)
420    }
421
422    /// Returns the end of the [`Change`], before it was applied
423    pub fn taken_end(&self) -> Point {
424        to_point(self.taken_end)
425    }
426
427    /// Returns the end of the [`Change`], after it was applied
428    pub fn added_end(&self) -> Point {
429        to_point(self.added_end)
430    }
431
432    /// Returns the taken [`Range`]
433    pub fn taken_range(&self) -> Range<Point> {
434        self.start()..self.taken_end()
435    }
436
437    /// Returns the added [`Range`]
438    pub fn added_range(&self) -> Range<Point> {
439        self.start()..self.added_end()
440    }
441
442    /// The text that was taken on this [`Change`]
443    pub fn added_str(&self) -> &str {
444        self.added.borrow()
445    }
446
447    /// The text that was added by this [`Change`]
448    pub fn taken_str(&self) -> &str {
449        self.taken.borrow()
450    }
451
452    /// The total shift caused by this [`Change`]
453    pub fn shift(&self) -> [i32; 3] {
454        [
455            self.added_end[0] - self.taken_end[0],
456            self.added_end[1] - self.taken_end[1],
457            self.added_end[2] - self.taken_end[2],
458        ]
459    }
460
461    /// Shifts the [`Change`] by a "signed point"
462    pub(crate) fn shift_by(&mut self, shift: [i32; 3]) {
463        self.start = add(self.start, shift);
464        self.added_end = add(self.added_end, shift);
465        self.taken_end = add(self.taken_end, shift);
466    }
467}
468
469impl Encode for Change<'static, String> {
470    fn encode<E: bincode::enc::Encoder>(
471        &self,
472        encoder: &mut E,
473    ) -> Result<(), bincode::error::EncodeError> {
474        Encode::encode(&self.start, encoder)?;
475        Encode::encode(&self.added, encoder)?;
476        Encode::encode(&self.taken, encoder)?;
477        Encode::encode(&self.added_end, encoder)?;
478        Encode::encode(&self.taken_end, encoder)?;
479        Ok(())
480    }
481}
482
483impl<Context> Decode<Context> for Change<'static, String> {
484    fn decode<D: bincode::de::Decoder<Context = Context>>(
485        decoder: &mut D,
486    ) -> Result<Self, bincode::error::DecodeError> {
487        Ok(Self {
488            start: Decode::decode(decoder)?,
489            added: Decode::decode(decoder)?,
490            taken: Decode::decode(decoder)?,
491            added_end: Decode::decode(decoder)?,
492            taken_end: Decode::decode(decoder)?,
493            _ghost: PhantomData,
494        })
495    }
496}
497
498impl<'de, Context> BorrowDecode<'de, Context> for Change<'static, String> {
499    fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
500        decoder: &mut D,
501    ) -> Result<Self, bincode::error::DecodeError> {
502        Ok(Self {
503            start: Decode::decode(decoder)?,
504            added: Decode::decode(decoder)?,
505            taken: Decode::decode(decoder)?,
506            added_end: Decode::decode(decoder)?,
507            taken_end: Decode::decode(decoder)?,
508            _ghost: PhantomData,
509        })
510    }
511}
512
513/// If `lhs` contains the start of `rhs`
514fn has_start_of(lhs: Range<Point>, rhs: Range<Point>) -> bool {
515    lhs.start <= rhs.start && rhs.start <= lhs.end
516}
517
518/// Can fetch the latest [`Moment`]
519///
520/// This struct is used to track the [`Change`]s throughout a
521/// [`Text`].
522#[derive(Debug)]
523pub(crate) struct MomentFetcher {
524    list: Arc<Mutex<Vec<FetcherState>>>,
525    index: usize,
526}
527
528impl MomentFetcher {
529    /// Gets the latest [`Moment`], emptying the list of [`Change`]s
530    pub(crate) fn get_moment(&self) -> Moment {
531        self.list.lock()[self.index].take().expect(
532            "This should only return None if the MomentFetcher has been dropped, at which point \
533             calling this function should not be possible.",
534        )
535    }
536}
537
538// This is done in order to prevent unnecessary updates to a Moment
539// that will never be read, since its reader is gone.
540impl Drop for MomentFetcher {
541    fn drop(&mut self) {
542        self.list.lock()[self.index] = FetcherState::Dead;
543    }
544}
545
546/// The state of a [`MomentFetcher`], can be alive or dead
547///
548/// This is used to prevent updates to [`Moment`]s that will never be
549/// read.
550#[derive(Clone, Debug, Encode, Decode)]
551enum FetcherState {
552    Alive(Moment),
553    Dead,
554}
555
556impl FetcherState {
557    /// Takes the [`Moment`] from this [`FetcherState`] if it's still
558    /// alive, that is
559    fn take(&mut self) -> Option<Moment> {
560        match self {
561            FetcherState::Alive(moment) => Some(std::mem::take(moment)),
562            FetcherState::Dead => None,
563        }
564    }
565
566    /// Adds a [`Change`] to this [`FetcherState`]
567    fn add_change(&mut self, change: Change<'static, String>) {
568        match self {
569            FetcherState::Alive(moment) => {
570                moment.add_change(None, change);
571            }
572            FetcherState::Dead => {}
573        }
574    }
575}
576
577/// Subtracts two `i32` arrays
578fn sub(lhs: [i32; 3], rhs: [i32; 3]) -> [i32; 3] {
579    [lhs[0] - rhs[0], lhs[1] - rhs[1], lhs[2] - rhs[2]]
580}
581
582/// Converts an `[i32; 3]` to a [`Point`]
583fn to_point(signed: [i32; 3]) -> Point {
584    Point::from_raw(signed[0] as usize, signed[1] as usize, signed[2] as usize)
585}