#![allow(clippy::equatable_if_let)]
use {
reovim_driver_command_types::ArgValue,
reovim_driver_input::{
KeyCode, KeyEvent, KeyLookupState, ModeTransition, Modifiers, PopResult,
},
reovim_kernel::api::v1::{CommandId, ModuleId, Position},
};
use {super::super::operator_common::*, crate::modes::VimMode};
fn key(c: char) -> KeyEvent {
KeyEvent::new(KeyCode::Char(c))
}
#[test]
fn test_is_escape() {
assert!(is_escape(&KeyEvent::new(KeyCode::Escape)));
assert!(is_escape(&KeyEvent::with_modifiers(KeyCode::Char('['), Modifiers::CTRL)));
assert!(!is_escape(&key('d')));
}
#[test]
fn test_is_count_digit() {
assert!(is_count_digit(&key('1'), false));
assert!(is_count_digit(&key('9'), false));
assert!(!is_count_digit(&key('0'), false));
assert!(is_count_digit(&key('0'), true));
assert!(!is_count_digit(
&KeyEvent::with_modifiers(KeyCode::Char('3'), Modifiers::CTRL),
false
));
}
#[test]
fn test_accumulate_count() {
assert_eq!(accumulate_count(&key('3'), None), Some(3));
assert_eq!(accumulate_count(&key('5'), Some(3)), Some(35));
assert_eq!(accumulate_count(&key('0'), Some(35)), Some(350));
}
#[test]
fn test_is_line_operator_key() {
assert!(is_line_operator_key(&key('d'), OperatorType::Delete));
assert!(!is_line_operator_key(&key('y'), OperatorType::Delete));
assert!(is_line_operator_key(&key('y'), OperatorType::Yank));
assert!(is_line_operator_key(&key('c'), OperatorType::Change));
}
#[test]
fn test_is_linewise_motion() {
use reovim_kernel::api::v1::ModuleId;
let editor = ModuleId::new("editor");
let motions = ModuleId::new("motions");
assert!(is_linewise_motion(&CommandId::new(editor.clone(), "cursor-down")));
assert!(is_linewise_motion(&CommandId::new(editor.clone(), "cursor-up")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "document-start")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "document-end")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "whole-line")));
assert!(!is_linewise_motion(&CommandId::new(editor.clone(), "cursor-left")));
assert!(!is_linewise_motion(&CommandId::new(editor, "cursor-right")));
assert!(!is_linewise_motion(&CommandId::new(motions, "word-forward")));
}
#[test]
fn test_operator_state_new() {
let state = OperatorState::new(OperatorType::Delete);
assert_eq!(state.operator, OperatorType::Delete);
assert!(state.start_position.is_none());
assert!(state.operator_count.is_none());
assert!(state.motion_count.is_none());
assert!(!state.initialized, "new state should not be initialized");
}
#[test]
fn test_operator_state_with_context() {
let state = OperatorState::with_context(OperatorType::Delete, Some(3), Some('a'));
assert_eq!(state.operator, OperatorType::Delete);
assert_eq!(state.operator_count, Some(3));
assert_eq!(state.register, Some('a'));
assert!(state.initialized, "with_context should be pre-initialized");
}
#[test]
fn test_operator_state_reset() {
let mut state = OperatorState::with_context(OperatorType::Yank, Some(2), Some('b'));
state.start_position = Some(Position::new(5, 10));
state.motion_count = Some(3);
state.push_key(KeyEvent::new(KeyCode::Char('w')));
state.reset();
assert!(state.start_position.is_none());
assert!(state.operator_count.is_none());
assert!(state.motion_count.is_none());
assert!(state.register.is_none());
assert!(state.pending_keys.is_empty());
assert!(!state.initialized, "reset should clear initialized flag");
}
#[test]
fn test_operator_state_effective_count() {
let mut state = OperatorState::new(OperatorType::Yank);
assert_eq!(state.effective_count(), 1);
state.operator_count = Some(2);
assert_eq!(state.effective_count(), 2);
state.motion_count = Some(3);
assert_eq!(state.effective_count(), 6); }
#[test]
fn test_operator_state_explicit_count() {
let mut state = OperatorState::new(OperatorType::Delete);
assert_eq!(state.explicit_count(), None);
state.operator_count = Some(2);
assert_eq!(state.explicit_count(), Some(2));
state.operator_count = None;
state.motion_count = Some(3);
assert_eq!(state.explicit_count(), Some(3));
state.operator_count = Some(2);
state.motion_count = Some(3);
assert_eq!(state.explicit_count(), Some(6)); }
#[test]
fn test_operator_type_mode_id() {
assert_eq!(OperatorType::Delete.mode_id(), VimMode::DELETE_ID);
assert_eq!(OperatorType::Yank.mode_id(), VimMode::YANK_ID);
assert_eq!(OperatorType::Change.mode_id(), VimMode::CHANGE_ID);
}
#[test]
fn test_operator_type_operator_id() {
let delete_id = OperatorType::Delete.operator_id();
assert_eq!(delete_id.name(), "delete");
assert_eq!(delete_id.module().as_str(), "vim");
let yank_id = OperatorType::Yank.operator_id();
assert_eq!(yank_id.name(), "yank");
let change_id = OperatorType::Change.operator_id();
assert_eq!(change_id.name(), "change");
}
#[test]
fn test_operator_type_line_key() {
assert_eq!(OperatorType::Delete.line_key(), 'd');
assert_eq!(OperatorType::Yank.line_key(), 'y');
assert_eq!(OperatorType::Change.line_key(), 'c');
}
#[test]
fn test_operator_type_display_name() {
assert_eq!(OperatorType::Delete.display_name(), "DELETE");
assert_eq!(OperatorType::Yank.display_name(), "YANK");
assert_eq!(OperatorType::Change.display_name(), "CHANGE");
}
#[test]
fn test_operator_type_copy_clone() {
let op = OperatorType::Delete;
let copied = op;
#[allow(clippy::clone_on_copy)]
let cloned = op.clone();
assert_eq!(op, copied);
assert_eq!(op, cloned);
}
#[test]
fn test_is_escape_regular_chars() {
assert!(!is_escape(&key('a')));
assert!(!is_escape(&key('['))); assert!(!is_escape(&KeyEvent::new(KeyCode::Enter)));
assert!(!is_escape(&KeyEvent::new(KeyCode::Backspace)));
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_is_linewise_motion_paragraph() {
let motions = ModuleId::new("motions");
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "paragraph-forward")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "paragraph-backward")));
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_is_linewise_motion_screen() {
let motions = ModuleId::new("motions");
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "screen-top")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "screen-middle")));
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "screen-bottom")));
}
#[test]
fn test_is_linewise_motion_next_prev_line() {
let motions = ModuleId::new("motions");
assert!(is_linewise_motion(&CommandId::new(motions.clone(), "next-line")));
assert!(is_linewise_motion(&CommandId::new(motions, "prev-line")));
}
#[test]
fn test_is_linewise_motion_characterwise_motions() {
let motions = ModuleId::new("motions");
assert!(!is_linewise_motion(&CommandId::new(motions.clone(), "word-forward-big")));
assert!(!is_linewise_motion(&CommandId::new(motions.clone(), "word-backward")));
assert!(!is_linewise_motion(&CommandId::new(motions.clone(), "word-end")));
assert!(!is_linewise_motion(&CommandId::new(motions.clone(), "line-start")));
assert!(!is_linewise_motion(&CommandId::new(motions, "line-end")));
}
#[test]
fn test_is_inclusive_motion_inclusive() {
let motions = ModuleId::new("motions");
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "line-end")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "word-end")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "word-end-big")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "find-char")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "find-char-back")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "till-char")));
assert!(is_inclusive_motion(&CommandId::new(motions.clone(), "till-char-back")));
assert!(is_inclusive_motion(&CommandId::new(motions, "match-bracket")));
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_is_inclusive_motion_exclusive() {
let motions = ModuleId::new("motions");
let editor = ModuleId::new("editor");
assert!(!is_inclusive_motion(&CommandId::new(motions.clone(), "word-forward")));
assert!(!is_inclusive_motion(&CommandId::new(motions.clone(), "word-backward")));
assert!(!is_inclusive_motion(&CommandId::new(motions.clone(), "line-start")));
assert!(!is_inclusive_motion(&CommandId::new(editor.clone(), "cursor-left")));
assert!(!is_inclusive_motion(&CommandId::new(editor, "cursor-right")));
}
#[test]
fn test_is_word_forward_motion() {
let motions = ModuleId::new("motions");
assert!(is_word_forward_motion(&CommandId::new(motions.clone(), "word-forward")));
assert!(is_word_forward_motion(&CommandId::new(motions.clone(), "word-forward-big")));
assert!(!is_word_forward_motion(&CommandId::new(motions.clone(), "word-backward")));
assert!(!is_word_forward_motion(&CommandId::new(motions.clone(), "word-end")));
assert!(!is_word_forward_motion(&CommandId::new(motions, "cursor-down")));
}
#[test]
fn test_is_line_operator_key_with_modifiers() {
let key_ctrl_d = KeyEvent::with_modifiers(KeyCode::Char('d'), Modifiers::CTRL);
assert!(!is_line_operator_key(&key_ctrl_d, OperatorType::Delete));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_build_operator_execute_delete() {
let start = Position::new(0, 0);
let end = Position::new(0, 5);
let result = build_operator_execute(OperatorType::Delete, start, end, false, None, None);
match result {
PopResult::ExecuteCommand { command, args } => {
assert_eq!(command.name(), "delete");
assert_eq!(command.module().as_str(), "vim");
assert!(args.contains_key("linewise"));
assert!(args.contains_key("range_start"));
assert!(args.contains_key("range_end"));
assert!(args.contains_key("count"));
assert!(!args.contains_key("register"));
}
_ => panic!("Expected ExecuteCommand"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_build_operator_execute_with_count_and_register() {
let start = Position::new(1, 0);
let end = Position::new(3, 0);
let result = build_operator_execute(OperatorType::Yank, start, end, true, Some(3), Some('a'));
match result {
PopResult::ExecuteCommand { command, args } => {
assert_eq!(command.name(), "yank");
if let Some(ArgValue::Bool(linewise)) = args.get("linewise") {
assert!(linewise);
} else {
panic!("Expected linewise Bool");
}
if let Some(ArgValue::Count(count)) = args.get("count") {
assert_eq!(*count, 3);
} else {
panic!("Expected count");
}
if let Some(ArgValue::Register(reg)) = args.get("register") {
assert_eq!(*reg, 'a');
} else {
panic!("Expected register");
}
}
_ => panic!("Expected ExecuteCommand"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_build_operator_execute_change() {
let start = Position::new(0, 5);
let end = Position::new(0, 10);
let result = build_operator_execute(OperatorType::Change, start, end, false, Some(1), None);
match result {
PopResult::ExecuteCommand { command, .. } => {
assert_eq!(command.name(), "change");
assert_eq!(command.module().as_str(), "vim");
}
_ => panic!("Expected ExecuteCommand"),
}
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_build_cancelled() {
let transition = build_cancelled();
match transition {
ModeTransition::Pop { result } => {
assert!(result.is_some());
if let Some(PopResult::Cancelled) = result {
} else {
panic!("Expected PopResult::Cancelled");
}
}
_ => panic!("Expected ModeTransition::Pop"),
}
}
#[test]
fn test_apply_keymap_policy_exact_only() {
let cmd = CommandId::new(ModuleId::new("test"), "cursor-down");
let lookup = KeyLookupState::ExactOnly(cmd);
let action = apply_keymap_policy(&lookup);
assert!(matches!(action, KeymapAction::Execute(_)));
}
#[test]
fn test_apply_keymap_policy_exact_with_longer() {
let cmd = CommandId::new(ModuleId::new("test"), "delete");
let lookup = KeyLookupState::ExactWithLonger { exact: cmd };
let action = apply_keymap_policy(&lookup);
assert!(matches!(action, KeymapAction::Execute(_)));
}
#[test]
fn test_apply_keymap_policy_prefix_only() {
let lookup = KeyLookupState::PrefixOnly;
let action = apply_keymap_policy(&lookup);
assert!(matches!(action, KeymapAction::Pending));
}
#[test]
fn test_apply_keymap_policy_not_found() {
let lookup = KeyLookupState::NotFound;
let action = apply_keymap_policy(&lookup);
assert!(matches!(action, KeymapAction::Cancel));
}
#[test]
fn test_operator_state_set_start_position() {
let mut state = OperatorState::new(OperatorType::Delete);
assert!(state.start_position.is_none());
state.set_start_position(Position::new(5, 10));
assert_eq!(state.start_position, Some(Position::new(5, 10)));
}
#[test]
fn test_operator_state_has_motion_count() {
let mut state = OperatorState::new(OperatorType::Yank);
assert!(!state.has_motion_count());
state.motion_count = Some(3);
assert!(state.has_motion_count());
}
#[test]
fn test_operator_state_accumulate_motion_count() {
let mut state = OperatorState::new(OperatorType::Delete);
state.accumulate_motion_count(&key('3'));
assert_eq!(state.motion_count, Some(3));
state.accumulate_motion_count(&key('5'));
assert_eq!(state.motion_count, Some(35));
state.accumulate_motion_count(&key('0'));
assert_eq!(state.motion_count, Some(350));
}
#[test]
fn test_operator_state_take_motion_count() {
let mut state = OperatorState::new(OperatorType::Delete);
state.motion_count = Some(5);
assert_eq!(state.take_motion_count(), Some(5));
assert!(state.motion_count.is_none());
}
#[test]
fn test_operator_state_push_and_keys() {
let mut state = OperatorState::new(OperatorType::Delete);
assert!(state.keys().is_empty());
state.push_key(key('d'));
state.push_key(key('w'));
assert_eq!(state.keys().len(), 2);
}
#[test]
fn test_operator_state_clear_keys() {
let mut state = OperatorState::new(OperatorType::Delete);
state.push_key(key('x'));
assert!(!state.keys().is_empty());
state.clear_keys();
assert!(state.keys().is_empty());
}
#[test]
fn test_operator_state_effective_count_motion_only() {
let mut state = OperatorState::new(OperatorType::Delete);
state.motion_count = Some(5);
assert_eq!(state.effective_count(), 5);
}
#[test]
fn test_operator_state_effective_count_both() {
let mut state = OperatorState::new(OperatorType::Delete);
state.operator_count = Some(3);
state.motion_count = Some(4);
assert_eq!(state.effective_count(), 12);
}
#[test]
fn test_accumulate_count_non_digit() {
let result = accumulate_count(&key('a'), Some(5));
assert_eq!(result, Some(5));
}
#[test]
fn test_accumulate_count_from_none() {
let result = accumulate_count(&key('7'), None);
assert_eq!(result, Some(7));
}
#[test]
fn test_keymap_action_debug() {
let action = KeymapAction::Pending;
let debug = format!("{action:?}");
assert!(debug.contains("Pending"));
let action = KeymapAction::Cancel;
let debug = format!("{action:?}");
assert!(debug.contains("Cancel"));
}