kiro_editor/
editor.rs

1use crate::error::Result;
2use crate::highlight::Highlighting;
3use crate::input::{InputSeq, KeySeq};
4use crate::language::Language;
5use crate::prompt::{self, Prompt, PromptResult};
6use crate::screen::Screen;
7use crate::status_bar::StatusBar;
8use crate::text_buffer::{CursorDir, Lines, TextBuffer};
9use std::io::Write;
10use std::path::Path;
11
12enum EditStep {
13    Continue(InputSeq),
14    Quit,
15}
16
17impl EditStep {
18    fn continues(&self) -> bool {
19        match self {
20            EditStep::Continue(_) => true,
21            EditStep::Quit => false,
22        }
23    }
24}
25
26pub struct Editor<I: Iterator<Item = Result<InputSeq>>, W: Write> {
27    input: I,       // Escape sequences stream represented as Iterator
28    quitting: bool, // After first Ctrl-Q
29    hl: Highlighting,
30    screen: Screen<W>,
31    bufs: Vec<TextBuffer>,
32    buf_idx: usize,
33    status_bar: StatusBar,
34}
35
36impl<I, W> Editor<I, W>
37where
38    I: Iterator<Item = Result<InputSeq>>,
39    W: Write,
40{
41    fn with_buf(
42        buf: TextBuffer,
43        mut input: I,
44        output: W,
45        window_size: Option<(usize, usize)>,
46    ) -> Result<Editor<I, W>> {
47        let screen = Screen::new(window_size, &mut input, output)?;
48        let status_bar = StatusBar::from_buffer(&buf, (1, 1));
49        Ok(Editor {
50            input,
51            quitting: false,
52            hl: Highlighting::default(),
53            screen,
54            bufs: vec![buf],
55            buf_idx: 0,
56            status_bar,
57        })
58    }
59
60    pub fn new(input: I, output: W, window_size: Option<(usize, usize)>) -> Result<Editor<I, W>> {
61        Self::with_buf(TextBuffer::empty(), input, output, window_size)
62    }
63
64    pub fn with_lines<S: AsRef<str>, L: Iterator<Item = S>>(
65        lines: L,
66        input: I,
67        output: W,
68        window_size: Option<(usize, usize)>,
69    ) -> Result<Editor<I, W>> {
70        Self::with_buf(TextBuffer::with_lines(lines)?, input, output, window_size)
71    }
72
73    pub fn open<P: AsRef<Path>>(
74        mut input: I,
75        output: W,
76        window_size: Option<(usize, usize)>,
77        paths: &[P],
78    ) -> Result<Editor<I, W>> {
79        if paths.is_empty() {
80            return Self::new(input, output, window_size);
81        }
82        let screen = Screen::new(window_size, &mut input, output)?;
83        let bufs: Vec<_> = paths.iter().map(TextBuffer::open).collect::<Result<_>>()?;
84        let hl = Highlighting::new(bufs[0].lang(), bufs[0].rows());
85        let status_bar = StatusBar::from_buffer(&bufs[0], (1, bufs.len()));
86        Ok(Editor {
87            input,
88            quitting: false,
89            hl,
90            screen,
91            bufs,
92            buf_idx: 0,
93            status_bar,
94        })
95    }
96
97    pub fn buf(&self) -> &TextBuffer {
98        &self.bufs[self.buf_idx]
99    }
100
101    fn buf_mut(&mut self) -> &mut TextBuffer {
102        &mut self.bufs[self.buf_idx]
103    }
104
105    fn refresh_status_bar(&mut self) {
106        self.status_bar
107            .set_buf_pos((self.buf_idx + 1, self.bufs.len()));
108        self.status_bar.update_from_buf(&self.bufs[self.buf_idx]);
109    }
110
111    fn render_screen(&mut self) -> Result<()> {
112        self.refresh_status_bar();
113        self.screen
114            .render(&self.bufs[self.buf_idx], &mut self.hl, &self.status_bar)?;
115        self.status_bar.redraw = false;
116        Ok(())
117    }
118
119    fn will_reset_scroll(&mut self) {
120        self.screen.set_dirty_start(0);
121        self.screen.rowoff = 0;
122        self.screen.coloff = 0;
123    }
124
125    fn will_reset_screen(&mut self) {
126        self.screen.set_dirty_start(self.screen.rowoff);
127        self.screen.unset_message();
128        self.status_bar.redraw = true;
129    }
130
131    fn open_buffer(&mut self) -> Result<()> {
132        if let PromptResult::Input(input) = self.prompt::<prompt::NoAction>(
133            "Open: {} (Empty name for new text buffer, ^G or ESC to cancel)",
134            false,
135        )? {
136            let buf = if input.is_empty() {
137                TextBuffer::empty()
138            } else {
139                TextBuffer::open(input)?
140            };
141            self.hl = Highlighting::new(buf.lang(), buf.rows());
142            self.bufs.push(buf);
143            self.buf_idx = self.bufs.len() - 1;
144            self.will_reset_scroll();
145        }
146        Ok(())
147    }
148
149    fn switch_buffer(&mut self, idx: usize) {
150        let len = self.bufs.len();
151        if len == 1 {
152            self.screen.set_info_message("No other buffer is opened");
153            return;
154        }
155
156        debug_assert!(idx < len);
157        self.buf_idx = idx;
158        let buf = self.buf();
159
160        // XXX: Should we put Highlighting instance in TextBuffer rather than Editor?
161        // Then we don't need to recreate Highlighting instance for each buffer switch.
162        self.hl = Highlighting::new(buf.lang(), buf.rows());
163        self.will_reset_scroll();
164    }
165
166    fn next_buffer(&mut self) {
167        self.switch_buffer(if self.buf_idx == self.bufs.len() - 1 {
168            0
169        } else {
170            self.buf_idx + 1
171        });
172    }
173
174    fn previous_buffer(&mut self) {
175        self.switch_buffer(if self.buf_idx == 0 {
176            self.bufs.len() - 1
177        } else {
178            self.buf_idx - 1
179        });
180    }
181
182    fn prompt<A: prompt::Action>(
183        &mut self,
184        prompt: &str,
185        empty_is_cancel: bool,
186    ) -> Result<PromptResult> {
187        Prompt::new(
188            &mut self.screen,
189            &mut self.bufs[self.buf_idx],
190            &mut self.hl,
191            &mut self.status_bar,
192            empty_is_cancel,
193        )
194        .run::<A, _, _>(prompt, &mut self.input)
195    }
196
197    fn save(&mut self) -> Result<()> {
198        let mut create = false;
199        if !self.buf().has_file() {
200            let template = "Save as: {} (^G or ESC to cancel)";
201            if let PromptResult::Input(input) = self.prompt::<prompt::NoAction>(template, true)? {
202                let prev_lang = self.buf().lang();
203                self.buf_mut().set_file(input);
204                self.hl.lang_changed(self.buf().lang());
205                if prev_lang != self.buf().lang() {
206                    // Render entire screen since highglight updated
207                    self.screen.set_dirty_start(self.screen.rowoff);
208                }
209                create = true;
210            }
211        }
212
213        match self.buf_mut().save() {
214            Ok(msg) => self.screen.set_info_message(msg),
215            Err(msg) => {
216                self.screen.set_error_message(msg);
217                if create {
218                    self.buf_mut().set_unnamed();
219                }
220            }
221        }
222
223        Ok(())
224    }
225
226    fn find(&mut self) -> Result<()> {
227        let template = "Search: {} (^F or ^N or RIGHT to forward, ^B or ^P or LEFT to back, ^G or ESC to cancel)";
228        self.prompt::<prompt::TextSearch>(template, true)?;
229        Ok(())
230    }
231
232    fn show_help(&mut self) -> Result<()> {
233        self.screen.render_help()?;
234
235        // Consume any key
236        while let Some(seq) = self.input.next() {
237            if self.screen.maybe_resize(&mut self.input)? {
238                self.screen.render_help()?;
239                self.status_bar.redraw = true;
240            }
241            if seq?.key != KeySeq::Unidentified {
242                break;
243            }
244        }
245
246        // Redraw screen after closing help
247        self.screen.set_dirty_start(self.screen.rowoff);
248        Ok(())
249    }
250
251    fn handle_quit(&mut self, s: InputSeq) -> EditStep {
252        let modified = self.bufs.iter().any(|b| b.modified());
253        if !modified || self.quitting {
254            EditStep::Quit
255        } else {
256            self.quitting = true;
257            self.screen.set_error_message(
258                "At least one file has unsaved changes! Press ^Q again to quit or ^S to save",
259            );
260            EditStep::Continue(s)
261        }
262    }
263
264    fn handle_not_mapped(&mut self, seq: &InputSeq) {
265        self.screen
266            .set_error_message(format!("Key '{}' not mapped", seq));
267    }
268
269    fn process_keypress(&mut self, s: InputSeq) -> Result<EditStep> {
270        use KeySeq::*;
271
272        let rowoff = self.screen.rowoff;
273        let rows = self.screen.rows();
274        let prev_cursor = self.buf().cursor();
275
276        match &s {
277            InputSeq {
278                key: Unidentified, ..
279            } => return Ok(EditStep::Continue(s)),
280            InputSeq { key, alt: true, .. } => match key {
281                Key(b'v') => self.buf_mut().move_cursor_page(CursorDir::Up, rowoff, rows),
282                Key(b'f') => self.buf_mut().move_cursor_by_word(CursorDir::Right),
283                Key(b'b') => self.buf_mut().move_cursor_by_word(CursorDir::Left),
284                Key(b'n') => self.buf_mut().move_cursor_paragraph(CursorDir::Down),
285                Key(b'p') => self.buf_mut().move_cursor_paragraph(CursorDir::Up),
286                Key(b'x') => self.previous_buffer(),
287                Key(b'<') => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Up),
288                Key(b'>') => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Down),
289                LeftKey => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Left),
290                RightKey => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Right),
291                _ => self.handle_not_mapped(&s),
292            },
293            InputSeq {
294                key, ctrl: true, ..
295            } => match key {
296                Key(b'p') => self.buf_mut().move_cursor_one(CursorDir::Up),
297                Key(b'b') => self.buf_mut().move_cursor_one(CursorDir::Left),
298                Key(b'n') => self.buf_mut().move_cursor_one(CursorDir::Down),
299                Key(b'f') => self.buf_mut().move_cursor_one(CursorDir::Right),
300                Key(b'v') => self
301                    .buf_mut()
302                    .move_cursor_page(CursorDir::Down, rowoff, rows),
303                Key(b'a') => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Left),
304                Key(b'e') => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Right),
305                Key(b'd') => self.buf_mut().delete_right_char(),
306                Key(b'g') => self.find()?,
307                Key(b'h') => self.buf_mut().delete_char(),
308                Key(b'k') => self.buf_mut().delete_until_end_of_line(),
309                Key(b'j') => self.buf_mut().delete_until_head_of_line(),
310                Key(b'w') => self.buf_mut().delete_word(),
311                Key(b'l') => {
312                    self.screen.set_dirty_start(self.screen.rowoff); // Clear
313                    self.screen.unset_message();
314                    self.status_bar.redraw = true;
315                }
316                Key(b's') => self.save()?,
317                Key(b'i') => self.buf_mut().insert_tab(),
318                Key(b'm') => self.buf_mut().insert_line(),
319                Key(b'o') => self.open_buffer()?,
320                Key(b'?') => self.show_help()?,
321                Key(b'x') => self.next_buffer(),
322                Key(b']') => self
323                    .buf_mut()
324                    .move_cursor_page(CursorDir::Down, rowoff, rows),
325                Key(b'u') => {
326                    if !self.buf_mut().undo() {
327                        self.screen.set_info_message("No older change");
328                    }
329                }
330                Key(b'r') => {
331                    if !self.buf_mut().redo() {
332                        self.screen.set_info_message("Buffer is already newest");
333                    }
334                }
335                LeftKey => self.buf_mut().move_cursor_by_word(CursorDir::Left),
336                RightKey => self.buf_mut().move_cursor_by_word(CursorDir::Right),
337                DownKey => self.buf_mut().move_cursor_paragraph(CursorDir::Down),
338                UpKey => self.buf_mut().move_cursor_paragraph(CursorDir::Up),
339                Key(b'q') => return Ok(self.handle_quit(s)),
340                _ => self.handle_not_mapped(&s),
341            },
342            InputSeq { key, .. } => match key {
343                Key(0x1b) => self.buf_mut().move_cursor_page(CursorDir::Up, rowoff, rows), // Clash with Ctrl-[
344                Key(0x08) => self.buf_mut().delete_char(), // Backspace
345                Key(0x7f) => self.buf_mut().delete_char(), // Delete key is mapped to \x1b[3~
346                Key(b'\r') => self.buf_mut().insert_line(),
347                Key(b) if !b.is_ascii_control() => self.buf_mut().insert_char(*b as char),
348                Utf8Key(c) => self.buf_mut().insert_char(*c),
349                UpKey => self.buf_mut().move_cursor_one(CursorDir::Up),
350                LeftKey => self.buf_mut().move_cursor_one(CursorDir::Left),
351                DownKey => self.buf_mut().move_cursor_one(CursorDir::Down),
352                RightKey => self.buf_mut().move_cursor_one(CursorDir::Right),
353                PageUpKey => self.buf_mut().move_cursor_page(CursorDir::Up, rowoff, rows),
354                PageDownKey => self
355                    .buf_mut()
356                    .move_cursor_page(CursorDir::Down, rowoff, rows),
357                HomeKey => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Left),
358                EndKey => self.buf_mut().move_cursor_to_buffer_edge(CursorDir::Right),
359                DeleteKey => self.buf_mut().delete_right_char(),
360                Cursor(_, _) => unreachable!(),
361                _ => self.handle_not_mapped(&s),
362            },
363        }
364
365        if let Some(line) = self.buf_mut().finish_edit() {
366            self.hl.needs_update = true;
367            self.screen.set_dirty_start(line);
368        }
369        if self.buf().cursor() != prev_cursor {
370            self.screen.cursor_moved = true;
371        }
372        self.quitting = false;
373        Ok(EditStep::Continue(s))
374    }
375
376    fn step(&mut self) -> Result<EditStep> {
377        let seq = if let Some(seq) = self.input.next() {
378            seq?
379        } else {
380            return Ok(EditStep::Quit);
381        };
382
383        if self.screen.maybe_resize(&mut self.input)? {
384            self.will_reset_screen();
385        }
386
387        let step = self.process_keypress(seq)?;
388
389        if step.continues() {
390            self.render_screen()?;
391        }
392
393        Ok(step)
394    }
395
396    pub fn first_paint(&mut self) -> Result<Edit<'_, I, W>> {
397        if self.buf().is_scratch() {
398            self.screen.render_welcome(&self.status_bar)?;
399            self.status_bar.redraw = false;
400        } else {
401            self.render_screen()?;
402        }
403        Ok(Edit { editor: self })
404    }
405
406    pub fn edit(&mut self) -> Result<()> {
407        // Map Iterator<Result<T>> to Iterator<Result<()>> for .collect()
408        self.first_paint()?.try_for_each(|r| r.map(|_| ()))
409    }
410
411    pub fn lines(&self) -> Lines<'_> {
412        self.buf().lines()
413    }
414
415    pub fn screen(&self) -> &'_ Screen<W> {
416        &self.screen
417    }
418
419    pub fn lang(&self) -> Language {
420        self.buf().lang()
421    }
422
423    pub fn set_lang(&mut self, lang: Language) {
424        let buf = self.buf_mut();
425        if buf.lang() == lang {
426            return;
427        }
428        buf.set_lang(lang);
429        self.hl = Highlighting::new(lang, buf.rows());
430    }
431}
432
433pub struct Edit<'a, I, W>
434where
435    I: Iterator<Item = Result<InputSeq>>,
436    W: Write,
437{
438    editor: &'a mut Editor<I, W>,
439}
440
441impl<'a, I, W> Edit<'a, I, W>
442where
443    I: Iterator<Item = Result<InputSeq>>,
444    W: Write,
445{
446    pub fn editor(&self) -> &'_ Editor<I, W> {
447        self.editor
448    }
449}
450
451impl<'a, I, W> Iterator for Edit<'a, I, W>
452where
453    I: Iterator<Item = Result<InputSeq>>,
454    W: Write,
455{
456    type Item = Result<InputSeq>;
457
458    fn next(&mut self) -> Option<Self::Item> {
459        match self.editor.step() {
460            Ok(EditStep::Continue(seq)) => Some(Ok(seq)),
461            Ok(EditStep::Quit) => None,
462            Err(err) => Some(Err(err)),
463        }
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use crate::editor::Editor;
470    use crate::error::Result;
471    use crate::input::{InputSeq, KeySeq};
472    use crate::language::Language;
473    use std::fs::File;
474    use std::io::{self, BufRead, BufReader, Write};
475
476    use KeySeq::*;
477
478    struct DummyInputs(Vec<InputSeq>);
479
480    impl Iterator for DummyInputs {
481        type Item = Result<InputSeq>;
482
483        fn next(&mut self) -> Option<Self::Item> {
484            if self.0.is_empty() {
485                None
486            } else {
487                Some(Ok(self.0.remove(0)))
488            }
489        }
490    }
491
492    struct Discard;
493
494    impl Write for Discard {
495        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
496            Ok(buf.len())
497        }
498
499        fn flush(&mut self) -> io::Result<()> {
500            Ok(())
501        }
502    }
503
504    fn key(c: char) -> InputSeq {
505        InputSeq::new(Key(c as u8))
506    }
507
508    fn ctrl(c: char) -> InputSeq {
509        InputSeq::ctrl(Key(c as u8))
510    }
511
512    fn sp(k: KeySeq) -> InputSeq {
513        if let Key(_) = k {
514            assert!(false, "{:?}", k);
515        }
516        InputSeq::new(k)
517    }
518
519    fn utf8(c: char) -> InputSeq {
520        InputSeq::new(Utf8Key(c))
521    }
522
523    #[test]
524    fn empty_buffer() {
525        let input = DummyInputs(vec![InputSeq::ctrl(Key(b'q'))]);
526        let mut editor = Editor::new(input, Discard, Some((80, 24))).unwrap();
527        editor.edit().unwrap();
528
529        assert!(editor.screen().rows() > 0);
530        assert!(editor.screen().cols() > 0);
531        assert_eq!(editor.lines().collect::<Vec<_>>(), vec![""]);
532
533        let msg = editor.screen().message_text();
534        assert_eq!(msg, "Ctrl-? for help");
535    }
536
537    #[test]
538    fn write_to_empty_buffer() {
539        let input = DummyInputs(vec![key('a'), key('b'), key('c'), ctrl('q'), ctrl('q')]);
540        let mut editor = Editor::new(input, Discard, Some((80, 24))).unwrap();
541        editor.edit().unwrap();
542
543        let lines = editor.lines().collect::<Vec<_>>();
544        assert_eq!(lines, vec!["abc"]);
545
546        let msg = editor.screen().message_text();
547        assert!(
548            msg.contains("At least one file has unsaved changes!"),
549            "{}",
550            msg
551        );
552    }
553
554    #[test]
555    fn edit_step_by_step() {
556        let keys = vec![key('a'), key('b'), key('c'), ctrl('q'), ctrl('q')];
557        let input = DummyInputs(keys.clone());
558        let mut editor = Editor::new(input, Discard, Some((80, 24))).unwrap();
559        let mut editing = editor.first_paint().unwrap();
560
561        let mut keys = keys.iter();
562        let mut xs = [1, 2, 3, 3].iter();
563        while let Some(res) = editing.next() {
564            let key = res.unwrap();
565            let (x, y) = editing.editor().buf().cursor();
566            assert_eq!(y, 0);
567            assert_eq!(*xs.next().unwrap(), x);
568            assert_eq!(keys.next().unwrap(), &key);
569        }
570
571        let mut lines = editor.lines();
572        assert_eq!(lines.len(), 1);
573        let line = lines.next().unwrap();
574        assert_eq!(line, "abc");
575    }
576
577    #[test]
578    fn move_cursor_down() {
579        let input = DummyInputs(vec![
580            key('a'),
581            sp(DownKey),
582            key('b'),
583            sp(DownKey),
584            key('c'),
585            ctrl('q'),
586            ctrl('q'),
587        ]);
588        let mut editor = Editor::new(input, Discard, Some((80, 24))).unwrap();
589        editor.edit().unwrap();
590
591        assert!(editor.screen().rows() > 0);
592        assert!(editor.screen().cols() > 0);
593
594        let lines = editor.lines().collect::<Vec<_>>();
595        assert_eq!(lines, vec!["a", "b", "c"]);
596    }
597
598    #[test]
599    fn open_file() {
600        let input = DummyInputs(vec![ctrl('q')]);
601
602        let this_file = file!();
603        let mut editor = Editor::open(input, Discard, Some((80, 24)), &[this_file]).unwrap();
604        editor.edit().unwrap();
605
606        let f = BufReader::new(File::open(this_file).unwrap());
607        for (i, (expected, actual)) in f.lines().zip(editor.lines()).enumerate() {
608            assert_eq!(expected.unwrap(), actual, "Line: {}", i + 1);
609        }
610
611        assert_eq!(editor.lang(), Language::Rust);
612    }
613
614    #[test]
615    fn message_bar_squashed() {
616        let input = DummyInputs(vec![ctrl('l'), sp(Unidentified), ctrl('q')]);
617        let mut buf = Vec::new();
618        let mut editor = Editor::new(input, &mut buf, Some((80, 24))).unwrap();
619        editor.edit().unwrap();
620
621        let msg = editor.screen().message_text();
622        assert_eq!(msg, "");
623    }
624
625    #[test]
626    fn undo_modified() {
627        let input = DummyInputs(vec![
628            key('a'),
629            key('b'),
630            key('c'),
631            ctrl('m'),
632            ctrl('u'),
633            ctrl('u'),
634            ctrl('q'),
635            ctrl('q'),
636        ]);
637        let mut editor = Editor::new(input, Discard, Some((80, 24))).unwrap();
638        editor.edit().unwrap();
639
640        let lines = editor.lines().collect::<Vec<_>>();
641        assert_eq!(lines, vec![""]);
642
643        assert!(!editor.bufs[0].modified());
644    }
645
646    macro_rules! test_text_edit {
647    ($title:ident, $title_undo:ident, $title_redo:ident {
648        before: $before:expr,
649        input: [$($input:expr,)+],
650        after: $after:expr,
651        cursor: $cursor:expr,
652    }) => {
653        #[test]
654        fn $title() {
655            let input = DummyInputs(vec![$($input,)+]);
656
657            let mut editor = Editor::with_lines(
658                $before.lines().skip(1), // .skip(1) for first empty line
659                input,
660                Discard,
661                Some((80, 24)),
662            ).unwrap();
663            editor.edit().unwrap();
664
665            let actual = editor.lines().collect::<Vec<_>>();
666            let expected = $after.lines().skip(1).collect::<Vec<_>>(); // .skip(1) for first empty line
667
668            assert_eq!(expected.len(), actual.len(), "expected='{:?}' actual='{:?}'", expected, actual);
669
670            for (idx, (actual_line, expected_line)) in actual.iter().zip(expected.iter()).enumerate() {
671                assert_eq!(
672                    expected_line,
673                    actual_line,
674                    "Line {} mismatch! expected='{:?} actual='{:?}'", idx+1, expected, actual,
675                );
676            }
677
678            assert_eq!(editor.buf().cursor(), $cursor)
679        }
680
681        #[test]
682        fn $title_undo() {
683            let mut input = vec![$($input,)+];
684            for _ in 0..input.len() {
685                input.push(ctrl('u')); // Add undo input to rollback all changes
686            }
687            let input = DummyInputs(input);
688
689            let mut editor = Editor::with_lines(
690                $before.lines().skip(1), // .skip(1) for first empty line
691                input,
692                Discard,
693                Some((80, 24)),
694            ).unwrap();
695            editor.edit().unwrap();
696
697            // After enough undo operations, buffer must be the same buffer as init
698            let actual = editor.lines().collect::<Vec<_>>();
699            let expected = $before.lines().skip(1).collect::<Vec<_>>(); // .skip(1) for first empty line
700
701            assert_eq!(expected.len(), actual.len(), "expected='{:?}' actual='{:?}'", expected, actual);
702
703            for (idx, (actual_line, expected_line)) in actual.iter().zip(expected.iter()).enumerate() {
704                assert_eq!(
705                    expected_line,
706                    actual_line,
707                    "Line {} mismatch! expected='{:?} actual='{:?}'", idx+1, expected, actual,
708                );
709            }
710        }
711
712        #[test]
713        fn $title_redo() {
714            let mut input = vec![$($input,)+];
715            let len = input.len();
716            for _ in 0..len {
717                input.push(ctrl('u')); // Add undo input to rollback all changes
718            }
719            for _ in 0..len {
720                input.push(ctrl('r')); // Add redo input to rollback all changes
721            }
722            let input = DummyInputs(input);
723
724            let mut editor = Editor::with_lines(
725                $before.lines().skip(1), // .skip(1) for first empty line
726                input,
727                Discard,
728                Some((80, 24)),
729            ).unwrap();
730            editor.edit().unwrap();
731
732            // After enough undo and redo operations
733            let actual = editor.lines().collect::<Vec<_>>();
734            let expected = $after.lines().skip(1).collect::<Vec<_>>(); // .skip(1) for first empty line
735
736            assert_eq!(expected.len(), actual.len(), "expected='{:?}' actual='{:?}'", expected, actual);
737
738            for (idx, (actual_line, expected_line)) in actual.iter().zip(expected.iter()).enumerate() {
739                assert_eq!(
740                    expected_line,
741                    actual_line,
742                    "Line {} mismatch! expected='{:?} actual='{:?}'", idx+1, expected, actual,
743                );
744            }
745        }
746    }
747}
748
749    test_text_edit!(
750        insert_char,
751        insert_char_undo,
752        insert_char_redo {
753            before: "",
754            input: [
755                key('a'),
756                key('b'),
757                sp(DownKey),
758                key('c'), // Insert first char to new line
759                key('\r'),
760                key('d'),
761                key('e'),
762            ],
763            after: "
764ab
765c
766de",
767            cursor: (2, 2),
768        }
769    );
770
771    test_text_edit!(
772        delete_char,
773        delete_char_undo,
774        delete_char_redo {
775            before: "
776abc
777def
778
779gh",
780            input: [
781                key('\x08'), // Do nothing (0x08 means backspace)
782                sp(EndKey),
783                key('\x08'), // Delete c
784                key('\x08'), // Delete b
785                sp(DownKey),
786                sp(DownKey),
787                key('\x08'), // Remove empty line
788                key('\x08'), // Remove f
789                ctrl('v'),   // Move to end of buffer
790                key('\x08'), // Do nothing
791                sp(UpKey),
792                sp(RightKey),
793                key('\x08'), // Delete g
794                key('\x08'), // Delete a line
795                key('\x08'), // Delete e
796            ],
797            after: "
798a
799dh",
800            cursor: (1, 1),
801        }
802    );
803
804    test_text_edit!(
805        insert_tab,
806        insert_tab_undo,
807        insert_tab_redo {
808            before: "
809
810ab
811cd
812ef",
813            input: [
814                ctrl('i'),
815                sp(DownKey),
816                sp(HomeKey),
817                ctrl('i'),
818                sp(DownKey),
819                sp(HomeKey),
820                sp(RightKey),
821                ctrl('i'),
822                sp(DownKey),
823                sp(EndKey),
824                ctrl('i'),
825            ],
826            after: "
827	
828	ab
829c	d
830ef	",
831            cursor: (3, 3),
832        }
833    );
834
835    test_text_edit!(
836        insert_line,
837        insert_line_undo,
838        insert_line_redo {
839            before: "
840
841ab
842cd",
843            input: [
844                key('\r'), // insert line at empty line
845                sp(DownKey),
846                key('\r'), // insert line at head of line
847                sp(RightKey),
848                key('\r'), // insert line at middle of line
849                sp(EndKey),
850                key('\r'), // insert line at end of line
851                ctrl('v'), // move to end of buffer
852                key('\r'), // insert new line
853                key('\r'), // insert new line
854            ],
855            after: "
856
857
858
859a
860b
861
862cd
863
864
865",
866            cursor: (0, 8),
867        }
868    );
869
870    test_text_edit!(
871        delete_right_char,
872        delete_right_char_undo,
873        delete_right_char_redo {
874            before: "
875abc
876
877g",
878            input: [
879                sp(DeleteKey), // Delete a
880                sp(RightKey),
881                sp(DeleteKey), // Delete c
882                sp(DownKey),
883                sp(DeleteKey), // Delete empty line
884                ctrl('v'),     // Move to end of buffer
885                sp(DeleteKey), // Do nothing
886                sp(UpKey),
887                sp(EndKey),
888                sp(DeleteKey), // Do nothing at end of last line
889            ],
890            after: "
891b
892g",
893            cursor: (1, 1),
894        }
895    );
896
897    test_text_edit!(
898        delete_until_end_of_line,
899        delete_until_end_of_line_undo,
900        delete_until_end_of_line_redo {
901            before: "
902ab
903cd
904ef
905g
906
907h",
908            input: [
909                ctrl('k'), // Delete at head of line
910                sp(DownKey),
911                sp(RightKey),
912                ctrl('k'), // Delete at middle of line
913                sp(DownKey),
914                sp(RightKey),
915                ctrl('k'), // Delete at end of line
916                sp(DownKey),
917                ctrl('k'), // Delete at empty line
918                ctrl('v'), // Move to end of buffer
919                ctrl('k'), // Do nothing at end of buffer
920                ctrl('k'), // Do nothing at end of buffer
921            ],
922            after: "
923
924c
925efg
926h",
927            cursor: (0, 4),
928        }
929    );
930
931    test_text_edit!(
932        delete_until_head_of_line,
933        delete_until_head_of_line_undo,
934        delete_until_head_of_line_redo {
935            before: "
936ab
937cd
938ef
939gh
940
941i",
942            input: [
943                ctrl('j'), // Do nothing at head of buffer
944                ctrl('j'), // Do nothing at head of buffer
945                sp(RightKey),
946                ctrl('j'), // Delete at middle of line
947                sp(DownKey),
948                sp(EndKey),
949                ctrl('j'), // Delete at end of line
950                sp(DownKey),
951                sp(DownKey),
952                ctrl('j'), // Delete at head of line
953                sp(DownKey),
954                ctrl('j'), // Delete at empty line
955                ctrl('v'), // End of buffer
956                ctrl('j'), // Do nothing at end of buffer
957                ctrl('j'), // Do nothing at end of buffer
958            ],
959            after: "
960b
961
962efgh
963i",
964            cursor: (0, 4),
965        }
966    );
967
968    test_text_edit!(
969        delete_word,
970        delete_word_undo,
971        delete_word_redo {
972            before: "
973abc def ghi
974jkl mno pqr
975
976s",
977            input: [
978                ctrl('w'), // Do nothing at head of buffer
979                ctrl('w'), // Do nothing at head of buffer
980                sp(EndKey),
981                sp(LeftKey),
982                sp(LeftKey),
983                sp(LeftKey),
984                sp(LeftKey),
985                ctrl('w'), // Delete 'def' (end of word)
986                sp(LeftKey),
987                sp(LeftKey),
988                ctrl('w'), // Delete 'ab' (middle of word)
989                sp(EndKey),
990                sp(LeftKey),
991                sp(LeftKey),
992                ctrl('w'), // Delete 'g' (middle of word)
993                sp(DownKey),
994                sp(EndKey),
995                sp(LeftKey),
996                sp(LeftKey),
997                sp(LeftKey),
998                ctrl('w'), // Delete 'mno '
999                ctrl('w'), // Delete 'jkl '
1000                sp(DownKey),
1001                ctrl('w'), // Do nothing at empty line
1002                ctrl('w'), // Do nothing at empty line
1003                ctrl('v'), // End of buffer
1004                ctrl('w'), // Do nothing at end of buffer
1005                ctrl('w'), // Do nothing at end of buffer
1006            ],
1007            after: "
1008c  hi
1009pqr
1010
1011s",
1012            cursor: (0, 4),
1013        }
1014    );
1015
1016    test_text_edit!(
1017        insert_utf8_char,
1018        insert_utf8_char_undo,
1019        insert_utf8_char_redo {
1020            before: "",
1021            input: [
1022                utf8('あ'),
1023                utf8('い'),
1024                sp(DownKey),
1025                utf8('う'), // Insert first char to new line
1026                key('\r'),
1027                utf8('え'),
1028                utf8('お'),
1029            ],
1030            after: "
1031あい
10321033えお",
1034            cursor: (2, 2),
1035        }
1036    );
1037
1038    test_text_edit!(
1039        delete_utf8_char,
1040        delete_utf8_char_undo,
1041        delete_utf8_char_redo {
1042            before: "
1043あいう
1044えおか
1045
1046きく",
1047            input: [
1048                key('\x08'), // Do nothing (0x08 means backspace)
1049                sp(EndKey),
1050                key('\x08'), // Delete c
1051                key('\x08'), // Delete b
1052                sp(DownKey),
1053                sp(DownKey),
1054                key('\x08'), // Remove empty line
1055                key('\x08'), // Remove f
1056                ctrl('v'),   // Move to end of buffer
1057                key('\x08'), // Do nothing
1058                sp(UpKey),
1059                sp(RightKey),
1060                key('\x08'), // Delete g
1061                key('\x08'), // Delete a line
1062                key('\x08'), // Delete e
1063            ],
1064            after: "
10651066えく",
1067            cursor: (1, 1),
1068        }
1069    );
1070
1071    test_text_edit!(
1072        insert_tab_utf8,
1073        insert_tab_utf8_undo,
1074        insert_tab_utf8_redo {
1075            before: "
1076
1077あい
1078うえ
1079おか",
1080            input: [
1081                ctrl('i'),
1082                sp(DownKey),
1083                sp(HomeKey),
1084                ctrl('i'),
1085                sp(DownKey),
1086                sp(HomeKey),
1087                sp(RightKey),
1088                ctrl('i'),
1089                sp(DownKey),
1090                sp(EndKey),
1091                ctrl('i'),
1092            ],
1093            after: "
1094	
1095	あい
1096う	え
1097おか	",
1098            cursor: (3, 3),
1099        }
1100    );
1101
1102    test_text_edit!(
1103        insert_line_utf8,
1104        insert_line_utf8_undo,
1105        insert_line_utf8_redo {
1106            before: "
1107
1108あい
1109うえ",
1110            input: [
1111                key('\r'), // insert line at empty line
1112                sp(DownKey),
1113                key('\r'), // insert line at head of line
1114                sp(RightKey),
1115                key('\r'), // insert line at middle of line
1116                sp(EndKey),
1117                key('\r'), // insert line at end of line
1118                ctrl('v'), // move to end of buffer
1119                key('\r'), // insert new line
1120                key('\r'), // insert new line
1121            ],
1122            after: "
1123
1124
1125
112611271128
1129うえ
1130
1131
1132",
1133            cursor: (0, 8),
1134        }
1135    );
1136
1137    test_text_edit!(
1138        delete_right_utf8_char,
1139        delete_right_utf8_char_undo,
1140        delete_right_utf8_char_redo {
1141            before: "
1142あいう
1143
1144え",
1145            input: [
1146                sp(DeleteKey), // Delete a
1147                sp(RightKey),
1148                sp(DeleteKey), // Delete c
1149                sp(DownKey),
1150                sp(DeleteKey), // Delete empty line
1151                ctrl('v'),     // Move to end of buffer
1152                sp(DeleteKey), // Do nothing
1153                sp(UpKey),
1154                sp(EndKey),
1155                sp(DeleteKey), // Do nothing at end of last line
1156            ],
1157            after: "
11581159え",
1160            cursor: (1, 1),
1161        }
1162    );
1163
1164    test_text_edit!(
1165        delete_until_end_of_line_utf8,
1166        delete_until_end_of_line_utf8_undo,
1167        delete_until_end_of_line_utf8_redo {
1168            before: "
1169あい
1170うえ
1171おか
11721173
1174く",
1175            input: [
1176                ctrl('k'), // Delete at head of line
1177                sp(DownKey),
1178                sp(RightKey),
1179                ctrl('k'), // Delete at middle of line
1180                sp(DownKey),
1181                sp(RightKey),
1182                ctrl('k'), // Delete at end of line
1183                sp(DownKey),
1184                ctrl('k'), // Delete at empty line
1185                ctrl('v'), // Move to end of buffer
1186                ctrl('k'), // Do nothing at end of buffer
1187                ctrl('k'), // Do nothing at end of buffer
1188            ],
1189            after: "
1190
11911192おかき
1193く",
1194            cursor: (0, 4),
1195        }
1196    );
1197
1198    test_text_edit!(
1199        delete_until_head_of_line_utf8,
1200        delete_until_head_of_line_utf8_undo,
1201        delete_until_head_of_line_utf8_redo {
1202            before: "
1203あい
1204うえ
1205おか
1206きく
1207
1208け",
1209            input: [
1210                ctrl('j'), // Do nothing at head of buffer
1211                ctrl('j'), // Do nothing at head of buffer
1212                sp(RightKey),
1213                ctrl('j'), // Delete at middle of line
1214                sp(DownKey),
1215                sp(EndKey),
1216                ctrl('j'), // Delete at end of line
1217                sp(DownKey),
1218                sp(DownKey),
1219                ctrl('j'), // Delete at head of line
1220                sp(DownKey),
1221                ctrl('j'), // Delete at empty line
1222                ctrl('v'), // End of buffer
1223                ctrl('j'), // Do nothing at end of buffer
1224                ctrl('j'), // Do nothing at end of buffer
1225            ],
1226            after: "
12271228
1229おかきく
1230け",
1231            cursor: (0, 4),
1232        }
1233    );
1234
1235    test_text_edit!(
1236        delete_utf8_word,
1237        delete_utf8_word_undo,
1238        delete_utf8_word_redo {
1239            before: "
1240あいう えおか きくけ
1241こさし すせそ たちつ
1242
1243て",
1244            input: [
1245                ctrl('w'), // Do nothing at head of buffer
1246                ctrl('w'), // Do nothing at head of buffer
1247                sp(EndKey),
1248                sp(LeftKey),
1249                sp(LeftKey),
1250                sp(LeftKey),
1251                sp(LeftKey),
1252                ctrl('w'), // Delete 'def' (end of word)
1253                sp(LeftKey),
1254                sp(LeftKey),
1255                ctrl('w'), // Delete 'ab' (middle of word)
1256                sp(EndKey),
1257                sp(LeftKey),
1258                sp(LeftKey),
1259                ctrl('w'), // Delete 'g' (middle of word)
1260                sp(DownKey),
1261                sp(EndKey),
1262                sp(LeftKey),
1263                sp(LeftKey),
1264                sp(LeftKey),
1265                ctrl('w'), // Delete 'mno '
1266                ctrl('w'), // Delete 'jkl '
1267                sp(DownKey),
1268                ctrl('w'), // Do nothing at empty line
1269                ctrl('w'), // Do nothing at empty line
1270                ctrl('v'), // End of buffer
1271                ctrl('w'), // Do nothing at end of buffer
1272                ctrl('w'), // Do nothing at end of buffer
1273            ],
1274            after: "
1275う  くけ
1276たちつ
1277
1278て",
1279            cursor: (0, 4),
1280        }
1281    );
1282}