#![allow(clippy::doc_markdown)]
use {
reovim_driver_input::{
KeyCode, KeyEvent, KeyLookupState, KeySequence, KeymapQuery, ModeKeyResolver, ModeState,
ModeTransition, PopResult, ResolveInput, ResolveResult,
},
reovim_kernel::api::v1::ModeId,
};
use {super::super::window::*, crate::modes::VimMode};
fn key(c: char) -> KeyEvent {
KeyEvent::new(KeyCode::Char(c))
}
fn test_state() -> ModeState {
ModeState::new(VimMode::WINDOW_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::WINDOW_ID;
ResolveInput::new(&EMPTY_KEYS, &MODE, keymap)
}
#[test]
fn test_new_resolver() {
let resolver = VimWindowResolver::new();
assert_eq!(resolver.mode_id(), &VimMode::WINDOW_ID);
}
#[test]
fn test_mode_id() {
let resolver = VimWindowResolver::new();
assert_eq!(resolver.mode_id().name(), "window");
}
#[test]
fn test_inherits_from_none() {
let resolver = VimWindowResolver::new();
assert!(resolver.inherits_from().is_none());
}
#[test]
fn test_unbound_key_not_handled() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&key('x'), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
fn test_escape_not_handled_without_binding() {
let resolver = VimWindowResolver::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_default_impl() {
let resolver = VimWindowResolver::default();
assert_eq!(resolver.mode_id(), &VimMode::WINDOW_ID);
}
struct ExactOnlyKeymap {
cmd: &'static str,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for ExactOnlyKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactOnly(reovim_kernel::api::v1::CommandId::new(
reovim_kernel::api::v1::ModuleId::new("test"),
self.cmd,
))
}
}
struct PrefixOnlyKeymap;
#[cfg_attr(coverage_nightly, coverage(off))]
impl KeymapQuery for PrefixOnlyKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::PrefixOnly
}
}
struct ExactWithLongerKeymap {
cmd: &'static str,
}
#[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"),
self.cmd,
),
}
}
}
fn resolve_input_with<T: KeymapQuery>(keymap: &T) -> ResolveInput<'_> {
static EMPTY_KEYS: KeySequence = KeySequence::new();
static MODE: ModeId = VimMode::WINDOW_ID;
ResolveInput::new(&EMPTY_KEYS, &MODE, keymap)
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_exact_only_command_pops_with_execute() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactOnlyKeymap { cmd: "focus-left" };
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('h'), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::ExecuteCommand { command, .. }),
}) => {
assert_eq!(command.name(), "focus-left");
}
_ => panic!("Expected Pop with ExecuteCommand, got {result:?}"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_exact_with_longer_pops_with_execute() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactWithLongerKeymap {
cmd: "split-horizontal",
};
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('s'), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::ExecuteCommand { command, .. }),
}) => {
assert_eq!(command.name(), "split-horizontal");
}
_ => panic!("Expected Pop with ExecuteCommand, got {result:?}"),
}
}
#[test]
fn test_prefix_only_returns_pending() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = PrefixOnlyKeymap;
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('z'), &mut state, &input);
assert!(matches!(result, ResolveResult::Pending));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_escape_binding_pops_cancelled() {
struct CancelKeymap;
impl KeymapQuery for CancelKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactOnly(crate::ids::CANCEL_TO_NORMAL)
}
}
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = CancelKeymap;
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Escape), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::Cancelled),
}) => {}
_ => panic!("Expected Pop with Cancelled, got {result:?}"),
}
}
#[test]
fn test_reset_is_noop() {
let mut resolver = VimWindowResolver::new();
resolver.reset();
assert_eq!(resolver.mode_id(), &VimMode::WINDOW_ID);
}
#[test]
fn test_multiple_keys_independent() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactOnlyKeymap { cmd: "focus-down" };
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('j'), &mut state, &input);
assert!(matches!(result, ResolveResult::ModeTransition(ModeTransition::Pop { .. })));
let result = resolver.resolve_with_keymap(&key('k'), &mut state, &input);
assert!(matches!(result, ResolveResult::ModeTransition(ModeTransition::Pop { .. })));
}
#[test]
fn test_not_found_returns_not_handled() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
let result = resolver.resolve_with_keymap(&key('z'), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_cancel_with_exact_with_longer() {
struct CancelWithLongerKeymap;
impl KeymapQuery for CancelWithLongerKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactWithLonger {
exact: crate::ids::CANCEL_TO_NORMAL,
}
}
}
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = CancelWithLongerKeymap;
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&KeyEvent::new(KeyCode::Escape), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::Cancelled),
}) => {}
_ => panic!("Expected Pop with Cancelled, got {result:?}"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_execute_command_args_empty() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactOnlyKeymap {
cmd: "close-window",
};
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('c'), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::ExecuteCommand { args, .. }),
}) => {
assert!(args.is_empty());
}
_ => panic!("Expected Pop with ExecuteCommand, got {result:?}"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_exact_with_longer_command_name() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactWithLongerKeymap {
cmd: "split-vertical",
};
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('v'), &mut state, &input);
match result {
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::ExecuteCommand { command, .. }),
}) => {
assert_eq!(command.name(), "split-vertical");
}
_ => panic!("Expected Pop with ExecuteCommand, got {result:?}"),
}
}
#[test]
fn test_different_key_chars_same_result() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = ExactOnlyKeymap { cmd: "focus-right" };
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('l'), &mut state, &input);
assert!(matches!(result, ResolveResult::ModeTransition(ModeTransition::Pop { .. })));
}
#[test]
fn test_mode_id_module() {
let resolver = VimWindowResolver::new();
assert_eq!(resolver.mode_id().module().as_str(), "vim");
}
#[test]
fn test_not_found_different_keys() {
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = NotFoundKeymap;
let input = resolve_input(&keymap);
for c in ['a', 'b', 'c', '1', '2', '3'] {
let result = resolver.resolve_with_keymap(&key(c), &mut state, &input);
assert!(matches!(result, ResolveResult::NotHandled), "key '{c}' should be not handled");
}
}
#[test]
fn test_const_new() {
const RESOLVER: VimWindowResolver = VimWindowResolver::new();
assert_eq!(RESOLVER.mode_id().name(), "window");
}
#[test]
fn test_reset_then_resolve() {
let mut resolver = VimWindowResolver::new();
resolver.reset();
let mut state = test_state();
let keymap = ExactOnlyKeymap { cmd: "focus-up" };
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('k'), &mut state, &input);
assert!(matches!(result, ResolveResult::ModeTransition(ModeTransition::Pop { .. })));
}
#[test]
fn test_cancel_command_exact_only() {
struct CancelExactKeymap;
impl KeymapQuery for CancelExactKeymap {
fn query(&self, _mode: &ModeId, _keys: &KeySequence) -> KeyLookupState {
KeyLookupState::ExactOnly(crate::ids::CANCEL_TO_NORMAL)
}
}
let resolver = VimWindowResolver::new();
let mut state = test_state();
let keymap = CancelExactKeymap;
let input = resolve_input_with(&keymap);
let result = resolver.resolve_with_keymap(&key('q'), &mut state, &input);
assert!(matches!(
result,
ResolveResult::ModeTransition(ModeTransition::Pop {
result: Some(PopResult::Cancelled),
})
));
}