rustyline/
undo.rs

1//! Undo API
2use std::fmt::Debug;
3
4use crate::keymap::RepeatCount;
5use crate::line_buffer::{ChangeListener, DeleteListener, Direction, LineBuffer, NoListener};
6use log::debug;
7use unicode_segmentation::UnicodeSegmentation;
8
9enum Change {
10    Begin,
11    End,
12    Insert {
13        idx: usize,
14        text: String,
15    }, // QuotedInsert, SelfInsert, Yank
16    Delete {
17        idx: usize,
18        text: String,
19    }, /* BackwardDeleteChar, BackwardKillWord, DeleteChar,
20        * KillLine, KillWholeLine, KillWord,
21        * UnixLikeDiscard, ViDeleteTo */
22    Replace {
23        idx: usize,
24        old: String,
25        new: String,
26    }, /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords,
27        * UpcaseWord, YankPop */
28}
29
30impl Change {
31    fn undo(&self, line: &mut LineBuffer) {
32        match *self {
33            Change::Begin | Change::End => {
34                unreachable!();
35            }
36            Change::Insert { idx, ref text } => {
37                line.delete_range(idx..idx + text.len(), &mut NoListener);
38            }
39            Change::Delete { idx, ref text } => {
40                line.insert_str(idx, text, &mut NoListener);
41                line.set_pos(idx + text.len());
42            }
43            Change::Replace {
44                idx,
45                ref old,
46                ref new,
47            } => {
48                line.replace(idx..idx + new.len(), old, &mut NoListener);
49            }
50        }
51    }
52
53    #[cfg(test)]
54    fn redo(&self, line: &mut LineBuffer) {
55        match *self {
56            Change::Begin | Change::End => {
57                unreachable!();
58            }
59            Change::Insert { idx, ref text } => {
60                line.insert_str(idx, text, &mut NoListener);
61            }
62            Change::Delete { idx, ref text } => {
63                line.delete_range(idx..idx + text.len(), &mut NoListener);
64            }
65            Change::Replace {
66                idx,
67                ref old,
68                ref new,
69            } => {
70                line.replace(idx..idx + old.len(), new, &mut NoListener);
71            }
72        }
73    }
74
75    fn insert_seq(&self, indx: usize) -> bool {
76        if let Change::Insert { idx, ref text } = *self {
77            idx + text.len() == indx
78        } else {
79            false
80        }
81    }
82
83    fn delete_seq(&self, indx: usize, len: usize) -> bool {
84        if let Change::Delete { idx, .. } = *self {
85            // delete or backspace
86            idx == indx || idx == indx + len
87        } else {
88            false
89        }
90    }
91
92    fn replace_seq(&self, indx: usize) -> bool {
93        if let Change::Replace { idx, ref new, .. } = *self {
94            idx + new.len() == indx
95        } else {
96            false
97        }
98    }
99}
100
101/// Undo manager
102pub struct Changeset {
103    undo_group_level: u32,
104    undos: Vec<Change>, // undoable changes
105    redos: Vec<Change>, // undone changes, redoable
106}
107
108impl Changeset {
109    pub(crate) fn new() -> Self {
110        Self {
111            undo_group_level: 0,
112            undos: Vec::new(),
113            redos: Vec::new(),
114        }
115    }
116
117    pub(crate) fn begin(&mut self) -> usize {
118        debug!(target: "rustyline", "Changeset::begin");
119        self.redos.clear();
120        let mark = self.undos.len();
121        self.undos.push(Change::Begin);
122        self.undo_group_level += 1;
123        mark
124    }
125
126    /// Returns `true` when changes happen between the last call to `begin` and
127    /// this `end`.
128    pub(crate) fn end(&mut self) -> bool {
129        debug!(target: "rustyline", "Changeset::end");
130        self.redos.clear();
131        let mut touched = false;
132        while self.undo_group_level > 0 {
133            self.undo_group_level -= 1;
134            if let Some(&Change::Begin) = self.undos.last() {
135                // empty Begin..End
136                self.undos.pop();
137            } else {
138                self.undos.push(Change::End);
139                touched = true;
140            }
141        }
142        touched
143    }
144
145    fn insert_char(idx: usize, c: char) -> Change {
146        let mut text = String::new();
147        text.push(c);
148        Change::Insert { idx, text }
149    }
150
151    pub(crate) fn insert(&mut self, idx: usize, c: char) {
152        debug!(target: "rustyline", "Changeset::insert({}, {:?})", idx, c);
153        self.redos.clear();
154        if !c.is_alphanumeric() || !self.undos.last().map_or(false, |lc| lc.insert_seq(idx)) {
155            self.undos.push(Self::insert_char(idx, c));
156            return;
157        }
158        // merge consecutive char insertions when char is alphanumeric
159        let mut last_change = self.undos.pop().unwrap();
160        if let Change::Insert { ref mut text, .. } = last_change {
161            text.push(c);
162        } else {
163            unreachable!();
164        }
165        self.undos.push(last_change);
166    }
167
168    pub(crate) fn insert_str<S: AsRef<str> + Into<String> + Debug>(
169        &mut self,
170        idx: usize,
171        string: S,
172    ) {
173        debug!(target: "rustyline", "Changeset::insert_str({}, {:?})", idx, string);
174        self.redos.clear();
175        if string.as_ref().is_empty() {
176            return;
177        }
178        self.undos.push(Change::Insert {
179            idx,
180            text: string.into(),
181        });
182    }
183
184    pub(crate) fn delete<S: AsRef<str> + Into<String> + Debug>(&mut self, indx: usize, string: S) {
185        debug!(target: "rustyline", "Changeset::delete({}, {:?})", indx, string);
186        self.redos.clear();
187        if string.as_ref().is_empty() {
188            return;
189        }
190
191        if !Self::single_char(string.as_ref())
192            || !self
193                .undos
194                .last()
195                .map_or(false, |lc| lc.delete_seq(indx, string.as_ref().len()))
196        {
197            self.undos.push(Change::Delete {
198                idx: indx,
199                text: string.into(),
200            });
201            return;
202        }
203        // merge consecutive char deletions when char is alphanumeric
204        let mut last_change = self.undos.pop().unwrap();
205        if let Change::Delete {
206            ref mut idx,
207            ref mut text,
208        } = last_change
209        {
210            if *idx == indx {
211                text.push_str(string.as_ref());
212            } else {
213                text.insert_str(0, string.as_ref());
214                *idx = indx;
215            }
216        } else {
217            unreachable!();
218        }
219        self.undos.push(last_change);
220    }
221
222    fn single_char(s: &str) -> bool {
223        let mut graphemes = s.graphemes(true);
224        graphemes.next().map_or(false, |grapheme| {
225            grapheme.chars().all(char::is_alphanumeric)
226        }) && graphemes.next().is_none()
227    }
228
229    pub(crate) fn replace<S: AsRef<str> + Into<String> + Debug>(
230        &mut self,
231        indx: usize,
232        old_: S,
233        new_: S,
234    ) {
235        debug!(target: "rustyline", "Changeset::replace({}, {:?}, {:?})", indx, old_, new_);
236        self.redos.clear();
237
238        if !self.undos.last().map_or(false, |lc| lc.replace_seq(indx)) {
239            self.undos.push(Change::Replace {
240                idx: indx,
241                old: old_.into(),
242                new: new_.into(),
243            });
244            return;
245        }
246
247        // merge consecutive char replacements
248        let mut last_change = self.undos.pop().unwrap();
249        if let Change::Replace {
250            ref mut old,
251            ref mut new,
252            ..
253        } = last_change
254        {
255            old.push_str(old_.as_ref());
256            new.push_str(new_.as_ref());
257        } else {
258            unreachable!();
259        }
260        self.undos.push(last_change);
261    }
262
263    pub(crate) fn undo(&mut self, line: &mut LineBuffer, n: RepeatCount) -> bool {
264        debug!(target: "rustyline", "Changeset::undo");
265        let mut count = 0;
266        let mut waiting_for_begin = 0;
267        let mut undone = false;
268        while let Some(change) = self.undos.pop() {
269            match change {
270                Change::Begin => {
271                    waiting_for_begin -= 1;
272                }
273                Change::End => {
274                    waiting_for_begin += 1;
275                }
276                _ => {
277                    change.undo(line);
278                    undone = true;
279                }
280            };
281            self.redos.push(change);
282            if waiting_for_begin <= 0 {
283                count += 1;
284                if count >= n {
285                    break;
286                }
287            }
288        }
289        undone
290    }
291
292    pub(crate) fn truncate(&mut self, len: usize) {
293        debug!(target: "rustyline", "Changeset::truncate({})", len);
294        self.undos.truncate(len);
295    }
296
297    #[cfg(test)]
298    pub(crate) fn redo(&mut self, line: &mut LineBuffer) -> bool {
299        let mut waiting_for_end = 0;
300        let mut redone = false;
301        while let Some(change) = self.redos.pop() {
302            match change {
303                Change::Begin => {
304                    waiting_for_end += 1;
305                }
306                Change::End => {
307                    waiting_for_end -= 1;
308                }
309                _ => {
310                    change.redo(line);
311                    redone = true;
312                }
313            };
314            self.undos.push(change);
315            if waiting_for_end <= 0 {
316                break;
317            }
318        }
319        redone
320    }
321
322    pub(crate) fn last_insert(&self) -> Option<String> {
323        for change in self.undos.iter().rev() {
324            match change {
325                Change::Insert { ref text, .. } => return Some(text.clone()),
326                Change::Replace { ref new, .. } => return Some(new.clone()),
327                Change::End => {
328                    continue;
329                }
330                _ => {
331                    return None;
332                }
333            }
334        }
335        None
336    }
337}
338
339impl DeleteListener for Changeset {
340    fn delete(&mut self, idx: usize, string: &str, _: Direction) {
341        self.delete(idx, string);
342    }
343}
344impl ChangeListener for Changeset {
345    fn insert_char(&mut self, idx: usize, c: char) {
346        self.insert(idx, c);
347    }
348
349    fn insert_str(&mut self, idx: usize, string: &str) {
350        self.insert_str(idx, string);
351    }
352
353    fn replace(&mut self, idx: usize, old: &str, new: &str) {
354        self.replace(idx, old, new);
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::Changeset;
361    use crate::line_buffer::{LineBuffer, NoListener};
362
363    #[test]
364    fn test_insert_chars() {
365        let mut cs = Changeset::new();
366        cs.insert(0, 'H');
367        cs.insert(1, 'i');
368        assert_eq!(1, cs.undos.len());
369        assert_eq!(0, cs.redos.len());
370        cs.insert(0, ' ');
371        assert_eq!(2, cs.undos.len());
372    }
373
374    #[test]
375    fn test_insert_strings() {
376        let mut cs = Changeset::new();
377        cs.insert_str(0, "Hello");
378        cs.insert_str(5, ", ");
379        assert_eq!(2, cs.undos.len());
380        assert_eq!(0, cs.redos.len());
381    }
382
383    #[test]
384    fn test_undo_insert() {
385        let mut buf = LineBuffer::init("", 0);
386        buf.insert_str(0, "Hello", &mut NoListener);
387        buf.insert_str(5, ", world!", &mut NoListener);
388        let mut cs = Changeset::new();
389        assert_eq!(buf.as_str(), "Hello, world!");
390
391        cs.insert_str(5, ", world!");
392
393        cs.undo(&mut buf, 1);
394        assert_eq!(0, cs.undos.len());
395        assert_eq!(1, cs.redos.len());
396        assert_eq!(buf.as_str(), "Hello");
397
398        cs.redo(&mut buf);
399        assert_eq!(1, cs.undos.len());
400        assert_eq!(0, cs.redos.len());
401        assert_eq!(buf.as_str(), "Hello, world!");
402    }
403
404    #[test]
405    fn test_undo_delete() {
406        let mut buf = LineBuffer::init("", 0);
407        buf.insert_str(0, "Hello", &mut NoListener);
408        let mut cs = Changeset::new();
409        assert_eq!(buf.as_str(), "Hello");
410
411        cs.delete(5, ", world!");
412
413        cs.undo(&mut buf, 1);
414        assert_eq!(buf.as_str(), "Hello, world!");
415
416        cs.redo(&mut buf);
417        assert_eq!(buf.as_str(), "Hello");
418    }
419
420    #[test]
421    fn test_delete_chars() {
422        let mut buf = LineBuffer::init("", 0);
423        buf.insert_str(0, "Hlo", &mut NoListener);
424
425        let mut cs = Changeset::new();
426        cs.delete(1, "e");
427        cs.delete(1, "l");
428        assert_eq!(1, cs.undos.len());
429
430        cs.undo(&mut buf, 1);
431        assert_eq!(buf.as_str(), "Hello");
432    }
433
434    #[test]
435    fn test_backspace_chars() {
436        let mut buf = LineBuffer::init("", 0);
437        buf.insert_str(0, "Hlo", &mut NoListener);
438
439        let mut cs = Changeset::new();
440        cs.delete(2, "l");
441        cs.delete(1, "e");
442        assert_eq!(1, cs.undos.len());
443
444        cs.undo(&mut buf, 1);
445        assert_eq!(buf.as_str(), "Hello");
446    }
447
448    #[test]
449    fn test_undo_replace() {
450        let mut buf = LineBuffer::init("", 0);
451        buf.insert_str(0, "Hello, world!", &mut NoListener);
452        let mut cs = Changeset::new();
453        assert_eq!(buf.as_str(), "Hello, world!");
454
455        buf.replace(1..5, "i", &mut NoListener);
456        assert_eq!(buf.as_str(), "Hi, world!");
457        cs.replace(1, "ello", "i");
458
459        cs.undo(&mut buf, 1);
460        assert_eq!(buf.as_str(), "Hello, world!");
461
462        cs.redo(&mut buf);
463        assert_eq!(buf.as_str(), "Hi, world!");
464    }
465
466    #[test]
467    fn test_last_insert() {
468        let mut cs = Changeset::new();
469        cs.begin();
470        cs.delete(0, "Hello");
471        cs.insert_str(0, "Bye");
472        cs.end();
473        let insert = cs.last_insert();
474        assert_eq!(Some("Bye".to_owned()), insert);
475    }
476
477    #[test]
478    fn test_end() {
479        let mut cs = Changeset::new();
480        cs.begin();
481        assert!(!cs.end());
482        cs.begin();
483        cs.insert_str(0, "Hi");
484        assert!(cs.end());
485    }
486}