use super::*;
#[test]
fn test_basic_motion() {
let mut editor = VimLineEditor::new();
let text = "hello world";
editor.handle_key(Key::char('l'), text);
assert_eq!(editor.cursor(), 1);
editor.handle_key(Key::char('w'), text);
assert_eq!(editor.cursor(), 6);
editor.handle_key(Key::char('$'), text);
assert_eq!(editor.cursor(), 10);
editor.handle_key(Key::char('0'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn test_mode_switching() {
let mut editor = VimLineEditor::new();
let text = "hello";
assert_eq!(editor.mode(), Mode::Normal);
editor.handle_key(Key::char('i'), text);
assert_eq!(editor.mode(), Mode::Insert);
editor.handle_key(Key::code(KeyCode::Escape), text);
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_delete_word() {
let mut editor = VimLineEditor::new();
let text = "hello world";
editor.handle_key(Key::char('d'), text);
editor.handle_key(Key::char('w'), text);
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_insert_char() {
let mut editor = VimLineEditor::new();
let text = "";
editor.handle_key(Key::char('i'), text);
let result = editor.handle_key(Key::char('x'), text);
assert_eq!(result.edits.len(), 1);
match &result.edits[0] {
TextEdit::Insert { at, text } => {
assert_eq!(*at, 0);
assert_eq!(text, "x");
}
_ => panic!("Expected Insert"),
}
}
#[test]
fn test_visual_mode() {
let mut editor = VimLineEditor::new();
let text = "hello world";
editor.handle_key(Key::char('v'), text);
assert_eq!(editor.mode(), Mode::Visual);
editor.handle_key(Key::char('w'), text);
let sel = editor.selection().unwrap();
assert_eq!(sel.start, 0);
assert!(sel.end > 0);
}
#[test]
fn test_backspace_ascii() {
let mut editor = VimLineEditor::new();
let mut text = String::from("abc");
editor.handle_key(Key::char('i'), &text);
editor.handle_key(Key::code(KeyCode::End), &text);
assert_eq!(editor.cursor(), 3);
let result = editor.handle_key(Key::code(KeyCode::Backspace), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "ab");
assert_eq!(editor.cursor(), 2);
}
#[test]
fn test_backspace_unicode() {
let mut editor = VimLineEditor::new();
let mut text = String::from("a😀b");
editor.handle_key(Key::char('i'), &text);
editor.handle_key(Key::code(KeyCode::End), &text);
editor.handle_key(Key::code(KeyCode::Left), &text); assert_eq!(editor.cursor(), 5);
let result = editor.handle_key(Key::code(KeyCode::Backspace), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "ab");
assert_eq!(editor.cursor(), 1);
}
#[test]
fn test_yank_and_paste() {
let mut editor = VimLineEditor::new();
let mut text = String::from("hello world");
editor.handle_key(Key::char('y'), &text);
let result = editor.handle_key(Key::char('w'), &text);
assert!(result.yanked.is_some());
assert_eq!(result.yanked.unwrap(), "hello ");
editor.handle_key(Key::char('$'), &text);
let result = editor.handle_key(Key::char('p'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "hello worldhello ");
}
#[test]
fn test_visual_mode_delete() {
let mut editor = VimLineEditor::new();
let mut text = String::from("hello world");
editor.handle_key(Key::char('v'), &text);
assert_eq!(editor.mode(), Mode::Visual);
editor.handle_key(Key::char('e'), &text);
let result = editor.handle_key(Key::char('d'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, " world");
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_operator_pending_escape() {
let mut editor = VimLineEditor::new();
let text = "hello world";
editor.handle_key(Key::char('d'), text);
assert!(matches!(editor.mode(), Mode::OperatorPending(_)));
editor.handle_key(Key::code(KeyCode::Escape), text);
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_replace_char() {
let mut editor = VimLineEditor::new();
let mut text = String::from("hello");
editor.handle_key(Key::char('r'), &text);
assert_eq!(editor.mode(), Mode::ReplaceChar);
let result = editor.handle_key(Key::char('x'), &text);
assert_eq!(editor.mode(), Mode::Normal);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "xello");
}
#[test]
fn test_replace_char_escape() {
let mut editor = VimLineEditor::new();
let text = "hello";
editor.handle_key(Key::char('r'), text);
assert_eq!(editor.mode(), Mode::ReplaceChar);
editor.handle_key(Key::code(KeyCode::Escape), text);
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_cw_no_trailing_space() {
let mut editor = VimLineEditor::new();
let mut text = String::from("hello world");
editor.handle_key(Key::char('c'), &text);
let result = editor.handle_key(Key::char('w'), &text);
assert_eq!(editor.mode(), Mode::Insert);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, " world");
}
#[test]
fn test_dw_includes_trailing_space() {
let mut editor = VimLineEditor::new();
let mut text = String::from("hello world");
editor.handle_key(Key::char('d'), &text);
let result = editor.handle_key(Key::char('w'), &text);
assert_eq!(editor.mode(), Mode::Normal);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "world");
}
#[test]
fn test_paste_at_empty_buffer() {
let mut editor = VimLineEditor::new();
let yank_text = String::from("test");
editor.handle_key(Key::char('y'), &yank_text);
editor.handle_key(Key::char('w'), &yank_text);
let mut text = String::new();
editor.set_cursor(0, &text);
let result = editor.handle_key(Key::char('p'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "test");
}
#[test]
fn test_dollar_cursor_on_last_char() {
let mut editor = VimLineEditor::new();
let text = "abc";
editor.handle_key(Key::char('$'), text);
assert_eq!(editor.cursor(), 2);
let text = "x";
editor.set_cursor(0, text);
editor.handle_key(Key::char('$'), text);
assert_eq!(editor.cursor(), 0); }
#[test]
fn test_x_delete_last_char_moves_cursor_left() {
let mut editor = VimLineEditor::new();
let mut text = String::from("abc");
editor.handle_key(Key::char('$'), &text);
assert_eq!(editor.cursor(), 2);
let result = editor.handle_key(Key::char('x'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "ab");
assert_eq!(editor.cursor(), 1); }
#[test]
fn test_x_delete_middle_char_cursor_stays() {
let mut editor = VimLineEditor::new();
let mut text = String::from("abc");
editor.handle_key(Key::char('l'), &text);
assert_eq!(editor.cursor(), 1);
let result = editor.handle_key(Key::char('x'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "ac");
assert_eq!(editor.cursor(), 1);
}
#[test]
fn test_percent_bracket_matching() {
let mut editor = VimLineEditor::new();
let text = "(hello world)";
assert_eq!(editor.cursor(), 0);
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 12);
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn test_percent_nested_brackets() {
let mut editor = VimLineEditor::new();
let text = "([{<>}])";
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 7);
editor.set_cursor(1, text);
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 6);
editor.set_cursor(2, text);
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 5);
editor.set_cursor(3, text);
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), 4); }
#[test]
fn test_percent_on_non_bracket() {
let mut editor = VimLineEditor::new();
let text = "hello";
let orig_cursor = editor.cursor();
editor.handle_key(Key::char('%'), text);
assert_eq!(editor.cursor(), orig_cursor);
}
#[test]
fn test_command_line_enter_clean() {
let text = String::from("preserved");
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.set_cursor(0, &text);
let result = editor.handle_key(Key::char(':'), &text);
assert_eq!(editor.mode(), Mode::CommandLine);
assert_eq!(editor.command_line_buffer(), Some(""));
assert_eq!(editor.command_line_cursor(), 0);
assert!(result.edits.is_empty(), "no edits should touch main text");
assert!(result.action.is_none());
assert_eq!(editor.status(), "COMMAND");
assert_eq!(text, "preserved");
}
#[test]
fn test_command_line_typing_submit_escape() {
let text = String::from("main");
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.handle_key(Key::char(':'), &text);
for c in "ir".chars() {
let r = editor.handle_key(Key::char(c), &text);
assert!(r.edits.is_empty(), "char '{}' must not edit main text", c);
assert!(r.action.is_none(), "char '{}' must not emit an action", c);
}
assert_eq!(editor.command_line_buffer(), Some("ir"));
assert_eq!(editor.command_line_cursor(), 2);
let submit = editor.handle_key(Key::code(KeyCode::Enter), &text);
match submit.action {
Some(Action::SubmitCommand(s)) => assert_eq!(s, "ir"),
other => panic!("expected SubmitCommand(\"ir\"), got {:?}", other),
}
assert!(submit.edits.is_empty(), "submit must not edit main text");
assert_eq!(editor.mode(), Mode::Normal);
assert_eq!(editor.command_line_buffer(), None);
editor.handle_key(Key::char(':'), &text);
editor.handle_key(Key::char('q'), &text);
let esc = editor.handle_key(Key::code(KeyCode::Escape), &text);
assert!(esc.action.is_none());
assert!(esc.edits.is_empty());
assert_eq!(editor.mode(), Mode::Normal);
assert_eq!(editor.command_line_buffer(), None);
assert_eq!(text, "main");
}
#[test]
fn test_command_line_disabled_by_default() {
let text = String::from("hello");
let mut editor = VimLineEditor::new();
let result = editor.handle_key(Key::char(':'), &text);
assert_eq!(editor.mode(), Mode::Normal);
assert!(result.edits.is_empty());
assert!(result.action.is_none());
assert_eq!(editor.command_line_buffer(), None);
}
#[test]
fn test_command_line_does_not_steal_r_replace() {
let mut text = String::from("abc");
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.handle_key(Key::char('r'), &text);
assert_eq!(editor.mode(), Mode::ReplaceChar);
let result = editor.handle_key(Key::char(':'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, ":bc");
assert_eq!(editor.mode(), Mode::Normal);
}
#[test]
fn test_command_line_does_not_steal_d_operator() {
let text = String::from("hello");
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.handle_key(Key::char('d'), &text);
assert!(matches!(editor.mode(), Mode::OperatorPending(_)));
let result = editor.handle_key(Key::char(':'), &text);
assert_eq!(editor.mode(), Mode::Normal);
assert!(result.edits.is_empty());
assert!(result.action.is_none());
assert_eq!(editor.command_line_buffer(), None);
}
#[test]
fn test_command_line_insert_mode_literal_colon_unchanged() {
let mut text = String::new();
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.handle_key(Key::char('i'), &text);
let result = editor.handle_key(Key::char(':'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, ":");
assert_eq!(editor.mode(), Mode::Insert);
}
#[test]
fn test_command_line_backspace_and_arrows() {
let text = String::from("ignored");
let mut editor = VimLineEditor::new().with_command_mode(true);
editor.handle_key(Key::char(':'), &text);
for c in "abd".chars() {
editor.handle_key(Key::char(c), &text);
}
assert_eq!(editor.command_line_buffer(), Some("abd"));
editor.handle_key(Key::code(KeyCode::Backspace), &text);
assert_eq!(editor.command_line_buffer(), Some("ab"));
assert_eq!(editor.command_line_cursor(), 2);
editor.handle_key(Key::code(KeyCode::Left), &text);
assert_eq!(editor.command_line_cursor(), 1);
editor.handle_key(Key::char('X'), &text);
assert_eq!(editor.command_line_buffer(), Some("aXb"));
editor.handle_key(Key::code(KeyCode::Home), &text);
assert_eq!(editor.command_line_cursor(), 0);
editor.handle_key(Key::code(KeyCode::End), &text);
assert_eq!(editor.command_line_cursor(), 3);
assert_eq!(text, "ignored");
}
#[test]
fn normal_k_on_single_line_emits_history_prev() {
let mut editor = VimLineEditor::new();
let text = "hello";
let result = editor.handle_key(Key::char('k'), text);
assert_eq!(result.action, Some(Action::HistoryPrev));
}
#[test]
fn normal_j_on_single_line_emits_history_next() {
let mut editor = VimLineEditor::new();
let text = "hello";
let result = editor.handle_key(Key::char('j'), text);
assert_eq!(result.action, Some(Action::HistoryNext));
}
#[test]
fn normal_kj_in_middle_of_multiline_moves_between_lines() {
let mut editor = VimLineEditor::new();
let text = "alpha\nbeta\ngamma";
editor.set_cursor(6, text);
let result = editor.handle_key(Key::char('k'), text);
assert!(
result.action.is_none(),
"k off-boundary is a motion, not history"
);
assert_eq!(editor.cursor(), 0, "moved up to 'a' of \"alpha\"");
let result = editor.handle_key(Key::char('k'), text);
assert_eq!(result.action, Some(Action::HistoryPrev));
editor.set_cursor(6, text);
let result = editor.handle_key(Key::char('j'), text);
assert!(
result.action.is_none(),
"j off-boundary is a motion, not history"
);
assert_eq!(editor.cursor(), 11, "moved down to 'g' of \"gamma\"");
let result = editor.handle_key(Key::char('j'), text);
assert_eq!(result.action, Some(Action::HistoryNext));
}
#[test]
fn normal_arrows_still_emit_history_unchanged() {
let mut editor = VimLineEditor::new();
let text = "alpha\nbeta\ngamma";
editor.set_cursor(6, text);
let up = editor.handle_key(Key::code(KeyCode::Up), text);
assert_eq!(up.action, Some(Action::HistoryPrev));
let down = editor.handle_key(Key::code(KeyCode::Down), text);
assert_eq!(down.action, Some(Action::HistoryNext));
}
#[test]
fn visual_kj_remains_motion_only() {
let mut editor = VimLineEditor::new();
let text = "one\ntwo";
editor.handle_key(Key::char('v'), text);
let result = editor.handle_key(Key::char('j'), text);
assert!(result.action.is_none());
assert_eq!(editor.cursor(), 4);
}
fn walk(text: &str, keys: &str) -> usize {
let mut editor = VimLineEditor::new();
editor.set_cursor(0, text);
for c in keys.chars() {
editor.handle_key(Key::char(c), text);
}
editor.cursor()
}
#[test]
fn w_stops_on_punctuation_run() {
let text = "foo,bar";
assert_eq!(walk(text, "w"), 3, "w from f lands on the comma");
assert_eq!(walk(text, "ww"), 4, "second w lands on b of bar");
}
#[test]
fn w_walks_foo_comma_space_bar_baz() {
let text = "foo, bar baz";
assert_eq!(walk(text, "w"), 3, "on comma");
assert_eq!(walk(text, "ww"), 5, "on b of bar");
assert_eq!(walk(text, "www"), 9, "on b of baz");
}
#[test]
fn w_past_open_paren_lands_on_arg() {
let text = "foo(bar)";
assert_eq!(walk(text, "w"), 3, "on (");
assert_eq!(walk(text, "ww"), 4, "on b of bar");
assert_eq!(walk(text, "www"), 7, "on )");
}
#[test]
fn w_on_colon_dash_punctuation() {
let text = ":-x";
assert_eq!(walk(text, "w"), 2, "w lands on x");
assert_eq!(walk(text, "ww"), 2, "second w stays clamped on x");
}
#[test]
fn b_walks_back_through_punctuation() {
let text = "foo,bar";
let mut editor = VimLineEditor::new();
editor.set_cursor(4, text);
editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 3, "b lands on comma");
editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 0, "b lands on f");
}
#[test]
fn b_from_within_word_goes_to_start() {
let text = "hello world";
let mut editor = VimLineEditor::new();
editor.set_cursor(3, text); editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn e_lands_on_last_char_of_word() {
let text = "foo,bar";
assert_eq!(walk(text, "e"), 2, "on last o of foo");
assert_eq!(walk(text, "ee"), 3, "on comma");
assert_eq!(walk(text, "eee"), 6, "on last r of bar");
}
#[test]
fn e_skips_leading_whitespace() {
let text = "foo bar";
let mut editor = VimLineEditor::new();
editor.set_cursor(3, text); editor.handle_key(Key::char('e'), text);
assert_eq!(editor.cursor(), 6, "on r of bar");
}
#[test]
fn w_at_end_of_buffer_lands_on_last_char() {
let text = "foo";
assert_eq!(walk(text, "w"), 2, "lands on last 'o', not past it");
assert_eq!(walk(text, "ww"), 2, "stays clamped on the last char");
}
#[test]
fn b_at_start_of_buffer_stays_put() {
let text = "foo";
let mut editor = VimLineEditor::new();
editor.set_cursor(0, text);
editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn b_from_eob_skips_trailing_punctuation() {
let text = "foo,";
let mut editor = VimLineEditor::new();
editor.set_cursor(text.len(), text);
assert_eq!(editor.cursor(), text.len());
editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn b_from_eob_skips_trailing_punctuation_in_phrase() {
let text = "foo bar,";
let mut editor = VimLineEditor::new();
editor.set_cursor(text.len(), text);
assert_eq!(editor.cursor(), text.len());
editor.handle_key(Key::char('b'), text);
assert_eq!(editor.cursor(), 4, "lands on start of bar, not the comma");
}
#[test]
fn e_at_end_of_buffer_stays_put() {
let text = "foo";
let mut editor = VimLineEditor::new();
editor.set_cursor(text.len(), text);
editor.handle_key(Key::char('e'), text);
assert_eq!(editor.cursor(), 2, "clamped onto last 'o', not past it");
}
#[test]
fn word_uppercase_w_skips_punctuation_to_next_space_token() {
let text = "foo,bar";
assert_eq!(walk(text, "W"), text.len() - 1);
}
#[test]
fn word_uppercase_wbe_on_mixed_text() {
let text = "foo, bar baz";
assert_eq!(walk(text, "W"), 5, "W lands on b of bar");
assert_eq!(walk(text, "WW"), 9, "W lands on b of baz");
let mut editor = VimLineEditor::new();
editor.set_cursor(9, text);
editor.handle_key(Key::char('B'), text);
assert_eq!(editor.cursor(), 5, "B lands on bar");
editor.handle_key(Key::char('B'), text);
assert_eq!(editor.cursor(), 0, "B lands on foo,");
assert_eq!(walk(text, "E"), 3, "E lands on comma");
assert_eq!(walk(text, "EE"), 7, "E lands on r of bar");
}
#[test]
fn word_uppercase_w_from_whitespace_lands_on_next_token() {
let text = "foo bar";
let mut editor = VimLineEditor::new();
editor.set_cursor(3, text); editor.handle_key(Key::char('W'), text);
assert_eq!(editor.cursor(), 4, "on b of bar");
}
#[test]
fn cw_on_punctuation_only_changes_punct_run() {
let mut editor = VimLineEditor::new();
let mut text = String::from("foo,bar");
editor.handle_key(Key::char('c'), &text);
let result = editor.handle_key(Key::char('w'), &text);
assert_eq!(editor.mode(), Mode::Insert);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, ",bar");
}
#[test]
fn c_uppercase_word_behaves_like_c_uppercase_e() {
let mut editor = VimLineEditor::new();
let mut text = String::from("foo, bar");
editor.handle_key(Key::char('c'), &text);
let result = editor.handle_key(Key::char('W'), &text);
assert_eq!(editor.mode(), Mode::Insert);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, " bar");
}
#[test]
fn d_uppercase_w_deletes_through_whitespace() {
let mut editor = VimLineEditor::new();
let mut text = String::from("foo, bar");
editor.handle_key(Key::char('d'), &text);
let result = editor.handle_key(Key::char('W'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "bar");
}
#[test]
fn visual_uppercase_w_extends_selection() {
let mut editor = VimLineEditor::new();
let text = "foo, bar baz";
editor.handle_key(Key::char('v'), text);
editor.handle_key(Key::char('W'), text);
let sel = editor.selection().unwrap();
assert_eq!(sel.start, 0);
assert_eq!(sel.end, 6, "selection spans `foo, ` up to bar");
}
#[test]
fn normal_l_at_last_char_does_not_overshoot() {
let text = "abc";
let mut editor = VimLineEditor::new();
editor.set_cursor(2, text); editor.handle_key(Key::char('l'), text);
assert_eq!(editor.cursor(), 2, "l stays clamped on the last char");
editor.handle_key(Key::code(KeyCode::Right), text);
assert_eq!(editor.cursor(), 2, "Right stays clamped on the last char");
}
#[test]
fn normal_e_from_last_char_stays_put() {
let text = "foo";
let mut editor = VimLineEditor::new();
editor.set_cursor(2, text); editor.handle_key(Key::char('e'), text);
assert_eq!(editor.cursor(), 2, "e on last word-end stays on last char");
}
#[test]
fn normal_uppercase_e_at_end_lands_on_last_char() {
let text = "foo bar";
assert_eq!(walk(text, "E"), 2, "E lands on end of first WORD");
assert_eq!(walk(text, "EE"), 6, "EE lands on end of last WORD");
assert_eq!(walk(text, "EEE"), 6);
}
#[test]
fn normal_w_clamps_on_multibyte_last_char() {
let text = "a\u{1F600}"; let mut editor = VimLineEditor::new();
editor.set_cursor(0, text);
editor.handle_key(Key::char('w'), text);
assert_eq!(editor.cursor(), 1, "clamped to start of the emoji");
assert!(text.is_char_boundary(editor.cursor()));
}
#[test]
fn normal_w_on_empty_buffer_stays_at_zero() {
let text = "";
let mut editor = VimLineEditor::new();
editor.handle_key(Key::char('w'), text);
assert_eq!(editor.cursor(), 0);
}
#[test]
fn dw_on_last_word_still_deletes_whole_word() {
let mut editor = VimLineEditor::new();
let mut text = String::from("foo bar");
editor.set_cursor(4, &text);
editor.handle_key(Key::char('d'), &text);
let result = editor.handle_key(Key::char('w'), &text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut text);
}
assert_eq!(text, "foo ", "dw deleted the whole last word 'bar'");
assert_eq!(editor.mode(), Mode::Normal);
assert!(editor.cursor() <= 4);
}
#[test]
fn append_at_last_char_keeps_past_end_for_insert() {
let text = "abc";
let mut editor = VimLineEditor::new();
editor.set_cursor(2, text); editor.handle_key(Key::char('a'), text);
assert_eq!(editor.mode(), Mode::Insert);
assert_eq!(editor.cursor(), 3, "append sits past the last char");
}
#[test]
fn insert_mode_cursor_can_rest_at_len() {
let text = "abc";
let mut editor = VimLineEditor::new();
editor.handle_key(Key::char('A'), text);
assert_eq!(editor.mode(), Mode::Insert);
assert_eq!(editor.cursor(), 3);
editor.handle_key(Key::code(KeyCode::Right), text);
assert_eq!(editor.cursor(), 3);
}