rustyline/
undo.rs

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