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::ops::Range;
15
16use serde::{
17    Deserialize, Serialize,
18    de::{self, Visitor},
19    ser::SerializeStruct,
20};
21
22use super::{Point, Text};
23
24/// The history of edits, contains all moments
25#[derive(Default, Debug, Clone, Serialize, Deserialize)]
26pub struct History {
27    moments: Vec<Moment>,
28    cur_moment: usize,
29    new_moment: Option<(Moment, SyncState)>,
30    /// Used to update ranges on the File
31    unproc_moment: Option<(Moment, SyncState)>,
32}
33
34impl History {
35    /// Creates a new [`History`]
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Adds a [`Change`] without moving the ones ahead to comply
41    ///
42    /// # Safety
43    ///
44    /// This function should only be used by the [`EditHelper`], as it
45    /// is expected that, after using it, you will call
46    /// [`shift_range`] in order to keep the [`Change`]s ahead synced
47    /// with the one being added.
48    ///
49    /// [`EditHelper`]: crate::mode::EditHelper
50    pub fn add_change(&mut self, guess_i: Option<usize>, change: Change<String>) -> usize {
51        let (unproc_moment, unproc_ss) = self.unproc_moment.get_or_insert_default();
52        unproc_moment.add_change(guess_i, change.clone(), unproc_ss);
53
54        let (new_moment, new_ss) = self.new_moment.get_or_insert_default();
55        new_moment.add_change(guess_i, change, new_ss)
56    }
57
58    /// Declares that the current moment is complete and starts a
59    /// new one
60    pub fn new_moment(&mut self) {
61        let Some((mut new_moment, mut new_ss)) = self.new_moment.take() else {
62            return;
63        };
64        finish_synchronizing(&mut new_moment, &mut new_ss);
65
66        self.moments.truncate(self.cur_moment);
67        self.moments.push(new_moment);
68        self.cur_moment += 1;
69    }
70
71    /// Redoes the next [`Moment`], returning its [`Change`]s
72    ///
73    /// Applying these [`Change`]s in the order that they're given
74    /// will result in a correct redoing.
75    pub fn move_forward(&mut self) -> Option<Vec<Change<&str>>> {
76        self.new_moment();
77        if self.cur_moment == self.moments.len() {
78            None
79        } else {
80            self.cur_moment += 1;
81            Some(
82                self.moments[self.cur_moment - 1]
83                    .0
84                    .iter()
85                    .map(|c| c.as_ref())
86                    .collect(),
87            )
88        }
89    }
90
91    /// Undoes a [`Moment`], returning its reversed [`Change`]s
92    ///
93    /// These [`Change`]s will already be shifted corectly, such that
94    /// applying them in sequential order, without further
95    /// modifications, will result in a correct undoing.
96    pub fn move_backwards(&mut self) -> Option<Vec<Change<&str>>> {
97        self.new_moment();
98        if self.cur_moment == 0 {
99            None
100        } else {
101            self.cur_moment -= 1;
102
103            let mut shift = (0, 0, 0);
104            let iter = self.moments[self.cur_moment].0.iter().map(move |c| {
105                let mut change = c.as_ref();
106                change.shift_by(shift);
107
108                shift.0 += change.taken_end().byte() as i32 - change.added_end().byte() as i32;
109                shift.1 += change.taken_end().char() as i32 - change.added_end().char() as i32;
110                shift.2 += change.taken_end().line() as i32 - change.added_end().line() as i32;
111
112                change.reverse()
113            });
114            Some(iter.collect())
115        }
116    }
117
118    pub fn unprocessed_changes(&mut self) -> Option<Vec<Change<String>>> {
119        self.unproc_moment
120            .take()
121            .map(|(c, _)| c.0.into_iter().collect())
122    }
123
124    pub fn has_unprocessed_changes(&self) -> bool {
125        self.unproc_moment.as_ref().is_some()
126    }
127}
128
129/// A moment in history, which may contain changes, or may just
130/// contain selections
131///
132/// It also contains information about how to print the file, so that
133/// going back in time is less jarring.
134#[derive(Default, Debug, Clone, Serialize, Deserialize)]
135pub struct Moment(Vec<Change<String>>);
136
137impl Moment {
138    /// First try to merge this change with as many changes as
139    /// possible, then add it in
140    ///
141    /// # Safety
142    ///
143    /// This function, unlike [`add_change`], does not shift the
144    /// [`Change`]s ahead of the inserted position, this means that
145    /// one must be careful to do it themselves. This is done safely
146    /// in the [`EditHelper`].
147    ///
148    /// # Returns
149    ///
150    /// - The index where the change was inserted;
151    /// - The number of changes that were added or subtracted during
152    ///   its insertion.
153    ///
154    /// [`add_change`]: Moment::add_change
155    /// [`EditHelper`]: crate::mode::EditHelper
156    fn add_change(
157        &mut self,
158        guess_i: Option<usize>,
159        change: Change<String>,
160        ss: &mut SyncState,
161    ) -> usize {
162        let (shift, sh_from) = (ss.shift, ss.sh_from);
163        let b = change.added_end().byte() as i32 - change.taken_end().byte() as i32;
164        let c = change.added_end().char() as i32 - change.taken_end().char() as i32;
165        let l = change.added_end().line() as i32 - change.taken_end().line() as i32;
166
167        let (change_i, sh_from) = match self.add_change_inner(guess_i, change, shift, sh_from) {
168            Ok((change_i, sh_from)) => (change_i, sh_from),
169            // A failure can happen if we are inserting before the `sh_from`, which would violate
170            // the strict ordering of changes.
171            // In order to remedy that, we synchronize the remaining changes and do it again with a
172            // fresh SyncState. This should never fail.
173            Err((guess_i, change)) => {
174                finish_synchronizing(self, ss);
175                *ss = SyncState::default();
176                self.add_change_inner(Some(guess_i), change, (0, 0, 0), 0)
177                    .unwrap()
178            }
179        };
180        ss.sh_from = sh_from;
181        ss.shift.0 += b;
182        ss.shift.1 += c;
183        ss.shift.2 += l;
184
185        change_i
186    }
187
188    /// Inner insertion of a [`Change`]
189    fn add_change_inner(
190        &mut self,
191        guess_i: Option<usize>,
192        mut change: Change<String>,
193        shift: (i32, i32, i32),
194        sh_from: usize,
195    ) -> Result<(usize, usize), (usize, Change<String>)> {
196        let sh = |n: usize| if sh_from <= n { shift } else { (0, 0, 0) };
197
198        // The range of changes that will be drained
199        let c_range = if let Some(guess_i) = guess_i
200            && let Some(c) = self.0.get(guess_i)
201            && c.start.shift_by(sh(guess_i)) <= change.start
202            && change.start <= c.added_end().shift_by(sh(guess_i))
203        {
204            // If we intersect the initial change, include it
205            guess_i..guess_i + 1
206        } else {
207            let f = |i: usize, c: &Change<String>| c.start.shift_by(sh(i));
208            match binary_search_by_key_and_index(&self.0, change.start, f) {
209                // Same thing here
210                Ok(i) => i..i + 1,
211                Err(i) => {
212                    // This is if we intersect the added part
213                    if let Some(older_i) = i.checked_sub(1)
214                        && change.start <= self.0[older_i].added_end().shift_by(sh(older_i))
215                    {
216                        older_i..i
217                    // And here is if we intersect nothing on the
218                    // start, no changes drained.
219                    } else {
220                        i..i
221                    }
222                }
223            }
224        };
225
226        if c_range.start < sh_from {
227            return Err((c_range.start, change));
228        }
229
230        // This block determines how far ahead this change will merge
231        let c_range = if self
232            .0
233            .get(c_range.end)
234            .is_none_or(|c| change.taken_end() < c.start.shift_by(sh(c_range.end)))
235        {
236            // If there is no change ahead, or it doesn't intersec, don't merge
237            c_range
238        } else {
239            let start_i = c_range.start + 1;
240            // Otherwise search ahead for another change to be merged
241            let f = |i: usize, c: &Change<String>| c.start.shift_by(sh(start_i + i));
242            match binary_search_by_key_and_index(&self.0[start_i..], change.taken_end(), f) {
243                Ok(i) => c_range.start..start_i + i + 1,
244                Err(i) => {
245                    if let Some(older) = self.0.get(start_i + i)
246                        && older.start.shift_by(sh(start_i + i)) <= change.taken_end()
247                    {
248                        c_range.start..start_i + i + 1
249                    } else {
250                        c_range.start..start_i + i
251                    }
252                }
253            }
254            // It will always be included
255        };
256
257        // Shift all ranges that preceed the end of the range.
258        if shift != (0, 0, 0) && sh_from < c_range.end {
259            for change in &mut self.0[sh_from..c_range.end] {
260                change.shift_by(shift);
261            }
262        }
263
264        for c in self.0.drain(c_range.clone()).rev() {
265            change.try_merge(c);
266        }
267
268        let sh_from = if !(change.added_text() == "" && change.taken_text() == "") {
269            self.0.insert(c_range.start, change);
270            c_range.start + 1
271        } else {
272            c_range.start
273        };
274
275        Ok((c_range.start, sh_from))
276    }
277
278    pub fn changes(&self) -> &[Change<String>] {
279        &self.0
280    }
281}
282
283/// A change in a file, with a start, taken text, and added text
284#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
285pub struct Change<S: AsRef<str>> {
286    start: Point,
287    added: S,
288    taken: S,
289}
290
291impl Change<String> {
292    /// Returns a new [Change].
293    pub fn new(edit: impl ToString, (start, end): (Point, Point), text: &Text) -> Self {
294        let added = edit.to_string();
295        let taken: String = text.strs_in(start.byte()..end.byte()).collect();
296        Change { start, added, taken }
297    }
298
299    /// Returns a copyable [`Change`]
300    pub fn as_ref(&self) -> Change<&str> {
301        Change {
302            start: self.start,
303            added: &self.added,
304            taken: &self.taken,
305        }
306    }
307
308    /// In this function, it is assumed that `self` happened
309    /// _after_ `newer`
310    ///
311    /// If the merger fails, the older [`Change`] will be returned;
312    pub fn try_merge(&mut self, mut older: Self) {
313        if has_start_of(older.added_range(), self.taken_range()) {
314            let fixed_end = older.added_end().min(self.taken_end());
315
316            let start = self.start - older.start;
317            let end = fixed_end - older.start;
318            let range = start.byte()..end.byte();
319            older.added.replace_range(range, &self.added);
320
321            let range = (fixed_end.byte() - self.start.byte())..;
322            older.taken.push_str(&self.taken[range]);
323
324            *self = older;
325        } else if has_start_of(self.taken_range(), older.added_range()) {
326            let fixed_end = self.taken_end().min(older.added_end());
327
328            let start = older.start - self.start;
329            let end = fixed_end - self.start;
330            let range = start.byte()..end.byte();
331            self.taken.replace_range(range, &older.taken);
332
333            let range = (fixed_end.byte() - older.start.byte())..;
334            self.added.push_str(&older.added[range]);
335        } else {
336            unreachable!("Files chosen that don't interact");
337        }
338    }
339}
340
341impl<'a> Change<&'a str> {
342    /// Returns a new copyable [`Change`] from an insertion.
343    pub fn str_insert(added_text: &'a str, start: Point) -> Self {
344        Self { start, added: added_text, taken: "" }
345    }
346}
347
348impl<S: AsRef<str>> Change<S> {
349    /// Returns a reversed version of this [`Change`]
350    pub fn reverse(self) -> Change<S> {
351        Change {
352            start: self.start,
353            added: self.taken,
354            taken: self.added,
355        }
356    }
357
358    /// The [`Point`] at the start of the change
359    pub fn start(&self) -> Point {
360        self.start
361    }
362
363    /// Shifts the [`Change`] by a "signed point"
364    pub(crate) fn shift_by(&mut self, shift: (i32, i32, i32)) {
365        self.start = self.start.shift_by(shift);
366    }
367
368    /// Returns the end of the [`Change`], before it was applied
369    pub fn taken_end(&self) -> Point {
370        self.start + Point::len_of(&self.taken)
371    }
372
373    /// Returns the end of the [`Change`], after it was applied
374    pub fn added_end(&self) -> Point {
375        self.start + Point::len_of(&self.added)
376    }
377
378    /// Returns the taken [`Range`]
379    pub fn taken_range(&self) -> Range<usize> {
380        self.start.byte()..self.taken_end().byte()
381    }
382
383    /// Returns the added [`Range`]
384    pub fn added_range(&self) -> Range<usize> {
385        self.start.byte()..self.added_end().byte()
386    }
387
388    /// The text that was taken on this [`Change`]
389    pub fn added_text(&self) -> &str {
390        self.added.as_ref()
391    }
392
393    /// The text that was added by this [`Change`]
394    pub fn taken_text(&self) -> &str {
395        self.taken.as_ref()
396    }
397
398    /// The difference in chars of the added and taken texts
399    pub fn chars_diff(&self) -> i32 {
400        self.added.as_ref().chars().count() as i32 - self.taken.as_ref().chars().count() as i32
401    }
402}
403
404impl Copy for Change<&str> {}
405
406impl Serialize for Change<String> {
407    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
408    where
409        S: serde::Serializer,
410    {
411        let mut ser_change = serializer.serialize_struct("Change", 3)?;
412
413        ser_change.serialize_field("start", &self.start)?;
414        ser_change.serialize_field("added", &self.added)?;
415        ser_change.serialize_field("taken", &self.taken)?;
416
417        ser_change.end()
418    }
419}
420
421impl<'de> Deserialize<'de> for Change<String> {
422    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
423    where
424        D: serde::Deserializer<'de>,
425    {
426        static FIELDS: [&str; 3] = ["start", "added", "taken"];
427        struct ChangeVisitor;
428
429        impl<'de> Visitor<'de> for ChangeVisitor {
430            type Value = Change<String>;
431
432            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
433                formatter.write_str("struct Change")
434            }
435
436            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
437            where
438                A: de::SeqAccess<'de>,
439            {
440                let start = seq
441                    .next_element()?
442                    .ok_or(de::Error::invalid_length(0, &self))?;
443                let added = seq
444                    .next_element()?
445                    .ok_or(de::Error::invalid_length(1, &self))?;
446                let taken = seq
447                    .next_element()?
448                    .ok_or(de::Error::invalid_length(2, &self))?;
449                Ok(Change { start, added, taken })
450            }
451        }
452
453        deserializer.deserialize_struct("Change", &FIELDS, ChangeVisitor)
454    }
455}
456
457/// If `lhs` contains the start of`rhs`
458fn has_start_of(lhs: Range<usize>, rhs: Range<usize>) -> bool {
459    lhs.start <= rhs.start && rhs.start <= lhs.end
460}
461
462#[derive(Default, Debug, Clone, Serialize, Deserialize)]
463struct SyncState {
464    shift: (i32, i32, i32),
465    sh_from: usize,
466}
467
468/// Binary searching by key taking into account the index
469fn binary_search_by_key_and_index<T, K>(
470    vec: &[T],
471    key: K,
472    f: impl Fn(usize, &T) -> K,
473) -> std::result::Result<usize, usize>
474where
475    K: PartialEq + Eq + PartialOrd + Ord,
476{
477    let mut size = vec.len();
478    let mut left = 0;
479    let mut right = size;
480
481    while left < right {
482        let mid = left + size / 2;
483
484        let k = f(mid, &vec[mid]);
485
486        match k.cmp(&key) {
487            std::cmp::Ordering::Less => left = mid + 1,
488            std::cmp::Ordering::Equal => return Ok(mid),
489            std::cmp::Ordering::Greater => right = mid,
490        }
491
492        size = right - left;
493    }
494
495    Err(left)
496}
497
498/// Finish synchronizing the unsynchronized changes
499fn finish_synchronizing(moment: &mut Moment, ss: &mut SyncState) {
500    if ss.shift != (0, 0, 0) {
501        // You can't get to this point without having at least one moment.
502        for change in &mut moment.0[ss.sh_from..] {
503            change.shift_by(ss.shift);
504        }
505    }
506}