rustyline/
undo.rs

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