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, quitting: bool, 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 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 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 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 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); 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), Key(0x08) => self.buf_mut().delete_char(), Key(0x7f) => self.buf_mut().delete_char(), 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 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), 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<_>>(); 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')); }
687 let input = DummyInputs(input);
688
689 let mut editor = Editor::with_lines(
690 $before.lines().skip(1), input,
692 Discard,
693 Some((80, 24)),
694 ).unwrap();
695 editor.edit().unwrap();
696
697 let actual = editor.lines().collect::<Vec<_>>();
699 let expected = $before.lines().skip(1).collect::<Vec<_>>(); 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')); }
719 for _ in 0..len {
720 input.push(ctrl('r')); }
722 let input = DummyInputs(input);
723
724 let mut editor = Editor::with_lines(
725 $before.lines().skip(1), input,
727 Discard,
728 Some((80, 24)),
729 ).unwrap();
730 editor.edit().unwrap();
731
732 let actual = editor.lines().collect::<Vec<_>>();
734 let expected = $after.lines().skip(1).collect::<Vec<_>>(); 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'), 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'), sp(EndKey),
783 key('\x08'), key('\x08'), sp(DownKey),
786 sp(DownKey),
787 key('\x08'), key('\x08'), ctrl('v'), key('\x08'), sp(UpKey),
792 sp(RightKey),
793 key('\x08'), key('\x08'), key('\x08'), ],
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'), sp(DownKey),
846 key('\r'), sp(RightKey),
848 key('\r'), sp(EndKey),
850 key('\r'), ctrl('v'), key('\r'), key('\r'), ],
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), sp(RightKey),
881 sp(DeleteKey), sp(DownKey),
883 sp(DeleteKey), ctrl('v'), sp(DeleteKey), sp(UpKey),
887 sp(EndKey),
888 sp(DeleteKey), ],
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'), sp(DownKey),
911 sp(RightKey),
912 ctrl('k'), sp(DownKey),
914 sp(RightKey),
915 ctrl('k'), sp(DownKey),
917 ctrl('k'), ctrl('v'), ctrl('k'), ctrl('k'), ],
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'), ctrl('j'), sp(RightKey),
946 ctrl('j'), sp(DownKey),
948 sp(EndKey),
949 ctrl('j'), sp(DownKey),
951 sp(DownKey),
952 ctrl('j'), sp(DownKey),
954 ctrl('j'), ctrl('v'), ctrl('j'), ctrl('j'), ],
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'), ctrl('w'), sp(EndKey),
981 sp(LeftKey),
982 sp(LeftKey),
983 sp(LeftKey),
984 sp(LeftKey),
985 ctrl('w'), sp(LeftKey),
987 sp(LeftKey),
988 ctrl('w'), sp(EndKey),
990 sp(LeftKey),
991 sp(LeftKey),
992 ctrl('w'), sp(DownKey),
994 sp(EndKey),
995 sp(LeftKey),
996 sp(LeftKey),
997 sp(LeftKey),
998 ctrl('w'), ctrl('w'), sp(DownKey),
1001 ctrl('w'), ctrl('w'), ctrl('v'), ctrl('w'), ctrl('w'), ],
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('う'), key('\r'),
1027 utf8('え'),
1028 utf8('お'),
1029 ],
1030 after: "
1031あい
1032う
1033えお",
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'), sp(EndKey),
1050 key('\x08'), key('\x08'), sp(DownKey),
1053 sp(DownKey),
1054 key('\x08'), key('\x08'), ctrl('v'), key('\x08'), sp(UpKey),
1059 sp(RightKey),
1060 key('\x08'), key('\x08'), key('\x08'), ],
1064 after: "
1065あ
1066えく",
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'), sp(DownKey),
1113 key('\r'), sp(RightKey),
1115 key('\r'), sp(EndKey),
1117 key('\r'), ctrl('v'), key('\r'), key('\r'), ],
1122 after: "
1123
1124
1125
1126あ
1127い
1128
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), sp(RightKey),
1148 sp(DeleteKey), sp(DownKey),
1150 sp(DeleteKey), ctrl('v'), sp(DeleteKey), sp(UpKey),
1154 sp(EndKey),
1155 sp(DeleteKey), ],
1157 after: "
1158い
1159え",
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おか
1172き
1173
1174く",
1175 input: [
1176 ctrl('k'), sp(DownKey),
1178 sp(RightKey),
1179 ctrl('k'), sp(DownKey),
1181 sp(RightKey),
1182 ctrl('k'), sp(DownKey),
1184 ctrl('k'), ctrl('v'), ctrl('k'), ctrl('k'), ],
1189 after: "
1190
1191う
1192おかき
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'), ctrl('j'), sp(RightKey),
1213 ctrl('j'), sp(DownKey),
1215 sp(EndKey),
1216 ctrl('j'), sp(DownKey),
1218 sp(DownKey),
1219 ctrl('j'), sp(DownKey),
1221 ctrl('j'), ctrl('v'), ctrl('j'), ctrl('j'), ],
1226 after: "
1227い
1228
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'), ctrl('w'), sp(EndKey),
1248 sp(LeftKey),
1249 sp(LeftKey),
1250 sp(LeftKey),
1251 sp(LeftKey),
1252 ctrl('w'), sp(LeftKey),
1254 sp(LeftKey),
1255 ctrl('w'), sp(EndKey),
1257 sp(LeftKey),
1258 sp(LeftKey),
1259 ctrl('w'), sp(DownKey),
1261 sp(EndKey),
1262 sp(LeftKey),
1263 sp(LeftKey),
1264 sp(LeftKey),
1265 ctrl('w'), ctrl('w'), sp(DownKey),
1268 ctrl('w'), ctrl('w'), ctrl('v'), ctrl('w'), ctrl('w'), ],
1274 after: "
1275う くけ
1276たちつ
1277
1278て",
1279 cursor: (0, 4),
1280 }
1281 );
1282}