#![allow(clippy::doc_markdown)]
use {
reovim_driver_input::{
KeyCode, KeyEvent, KeyLookupState, KeySequence, KeymapQuery, ModeKeyResolver, ModeState,
Modifiers, ResolveInput, ResolveResult,
},
reovim_kernel::api::v1::ModeId,
};
use {super::super::commandline::*, crate::modes::VimMode};
fn key(c: char) -> KeyEvent {
KeyEvent::new(KeyCode::Char(c))
}
fn key_with_mod(c: char, modifiers: Modifiers) -> KeyEvent {
KeyEvent::with_modifiers(KeyCode::Char(c), modifiers)
}
fn test_state() -> ModeState {
ModeState::new(VimMode::COMMANDLINE_ID)
}
struct NotFoundKeymap;
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for NotFoundKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::NotFound
}
}
fn resolve_input(keymap: &impl KeymapQuery) -> ResolveInput<'_> {
static EMPTY_KEYS: KeySequence = KeySequence::new();
static MODE: ModeId = VimMode::COMMANDLINE_ID;
ResolveInput::new(&EMPTY_KEYS, &MODE, keymap)
}
#[test]
fn test_new_resolver() {
let resolver = VimCommandLineResolver::new();
assert_eq!(resolver.mode_id(), &VimMode::COMMANDLINE_ID);
}
#[test]
fn test_insert_character() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&key('w'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: 'w', .. }));
let result = resolver.resolve_with_keymap(&key('q'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: 'q', .. }));
let result = resolver.resolve_with_keymap(&key(' '), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: ' ', .. }));
}
#[test]
fn test_escape_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Escape), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_enter_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Enter), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_backspace_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result =
resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Backspace), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_ctrl_char_not_inserted() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result =
resolver.resolve_with_keymap(&key_with_mod('c', Modifiers::CTRL), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_mode_id() {
let resolver = VimCommandLineResolver::new();
assert_eq!(resolver.mode_id().name(), "command");
}
#[test]
fn test_inherits_from() {
let resolver = VimCommandLineResolver::new();
assert!(resolver.inherits_from().is_none());
}
#[test]
fn test_default_impl() {
let resolver = VimCommandLineResolver::default();
assert_eq!(resolver.mode_id(), &VimMode::COMMANDLINE_ID);
}
#[test]
fn test_reset_is_noop() {
let mut resolver = VimCommandLineResolver::new();
resolver.reset();
assert_eq!(resolver.mode_id(), &VimMode::COMMANDLINE_ID);
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
fn test_insert_digits() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
for c in '0'..='9' {
let result = resolver.resolve_with_keymap(&key(c), &mut state, &input);
assert!(
matches!(result, ResolveResult::InsertChar { char: ch, .. } if ch == c),
"digit '{c}' should be insertable"
);
}
}
#[test]
fn test_insert_special_symbols() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&key('/'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: '/', .. }));
let result = resolver.resolve_with_keymap(&key('!'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: '!', .. }));
let result = resolver.resolve_with_keymap(&key('.'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: '.', .. }));
}
#[test]
fn test_alt_char_not_inserted() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result =
resolver.resolve_with_keymap(&key_with_mod('a', Modifiers::ALT), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_tab_not_inserted() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Tab), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_arrow_keys_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Up), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Down), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
struct ExactKeymap;
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for ExactKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactOnly(reovim_kernel::api::v1::CommandId::new(
reovim_kernel::api::v1::ModuleId::new("test"),
"test-cmd",
))
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_escape_executes_with_keymap_binding() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = ExactKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Escape), &mut state, &input);
match result {
ResolveResult::Execute(cmd, _) => {
assert_eq!(cmd.name(), "test-cmd");
}
_ => panic!("expected Execute, got {result:?}"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_enter_executes_with_keymap_binding() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = ExactKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Enter), &mut state, &input);
match result {
ResolveResult::Execute(cmd, _) => {
assert_eq!(cmd.name(), "test-cmd");
}
_ => panic!("expected Execute, got {result:?}"),
}
}
struct PrefixKeymap;
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for PrefixKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::PrefixOnly
}
}
#[test]
fn test_prefix_only_returns_pending() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = PrefixKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Escape), &mut state, &input);
assert!(matches!(result, ResolveResult::Pending));
}
#[test]
fn test_delete_key_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Delete), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_mode_id_module() {
let resolver = VimCommandLineResolver::new();
assert_eq!(resolver.mode_id().module().as_str(), "vim");
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
fn test_insert_uppercase_chars() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
for c in 'A'..='Z' {
let result = resolver.resolve_with_keymap(&key(c), &mut state, &input);
assert!(
matches!(result, ResolveResult::InsertChar { char: ch, .. } if ch == c),
"uppercase '{c}' should be insertable"
);
}
}
#[test]
fn test_insert_unicode_char() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result =
resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Char('\u{00e9}')), &mut state, &input);
assert!(matches!(
result,
ResolveResult::InsertChar {
char: '\u{00e9}',
..
}
));
}
#[test]
fn test_ctrl_shift_not_inserted() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(
&key_with_mod('a', Modifiers::CTRL | Modifiers::SHIFT),
&mut state,
&input,
);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_home_key_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Home), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_end_key_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::End), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
struct ExactWithLongerKeymap;
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for ExactWithLongerKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactWithLonger {
exact: reovim_kernel::api::v1::CommandId::new(
reovim_kernel::api::v1::ModuleId::new("test"),
"test-cmd",
),
}
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_exact_with_longer_executes() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = ExactWithLongerKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Enter), &mut state, &input);
match result {
ResolveResult::Execute(cmd, _) => {
assert_eq!(cmd.name(), "test-cmd");
}
_ => panic!("expected Execute, got {result:?}"),
}
}
#[test]
fn test_const_new() {
const RESOLVER: VimCommandLineResolver = VimCommandLineResolver::new();
assert_eq!(RESOLVER.mode_id().name(), "command");
}
#[test]
fn test_shift_char_is_insertable() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result =
resolver.resolve_with_keymap(&key_with_mod('A', Modifiers::SHIFT), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: 'A', .. }));
}
#[test]
fn test_reset_then_resolve() {
let mut resolver = VimCommandLineResolver::new();
resolver.reset();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&key('a'), &mut state, &input);
assert!(matches!(result, ResolveResult::InsertChar { char: 'a', .. }));
}
#[test]
fn test_function_keys_not_handled() {
let resolver = VimCommandLineResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
for code in [KeyCode::F(1), KeyCode::F(5), KeyCode::F(12)] {
let result = resolver.resolve_with_keymap(&KeyEvent::new(code), &mut state, &input);
assert!(
matches!(result, ResolveResult::NotHandled),
"Function key {code:?} should not be handled"
);
}
}