#![allow(clippy::uninlined_format_args)]
use {
super::super::*,
crate::session_state::{ChangeType, VimSessionState},
reovim_driver_command::{Command, CommandContext, CommandHandler},
reovim_driver_session::{BufferApi, SessionRuntime, api::ExtensionApi},
};
use {
crate::session_state::{LastChange, OperatorType},
reovim_driver_command::ArgValue,
reovim_driver_session::{
ClientId, ExtensionMap, Session, Window, WindowLayout, api::CommandExecutor,
testing::StubExecutor,
},
reovim_kernel::{
api::{
ModeStack,
v1::{
BufferId, HistoryRing, Jumplist, KernelContext, MarkBank, ModeId, ModuleId,
RegisterBank,
},
},
testing::{create_test_context, setup_buffer},
},
};
fn test_mode() -> ModeId {
ModeId::new(ModuleId::new("vim"), "normal")
}
struct TestState {
session: Session,
mode_stack: ModeStack,
windows: WindowLayout,
extensions: ExtensionMap,
compositor: Option<Box<dyn reovim_driver_layout::RootCompositor>>,
tabs: reovim_driver_session::TabPageSet,
registers: RegisterBank,
clipboard_history: HistoryRing,
local_marks: MarkBank,
jumplist: Jumplist,
active_buffer: Option<BufferId>,
terminal_size: (u16, u16),
}
impl TestState {
fn with_window(buffer_id: BufferId, mode: ModeId) -> Self {
let session = Session::new(ClientId::new(1), mode.clone()); let mode_stack = ModeStack::new(mode);
let mut windows = WindowLayout::empty();
let extensions = ExtensionMap::new();
windows.add(Window::with_buffer(buffer_id));
Self {
session,
mode_stack,
windows,
extensions,
compositor: None,
tabs: reovim_driver_session::TabPageSet::new(),
registers: RegisterBank::new(),
clipboard_history: HistoryRing::new(),
local_marks: MarkBank::new(),
jumplist: Jumplist::new(),
active_buffer: None,
terminal_size: (80, 24),
}
}
fn runtime<'a>(
&'a mut self,
kernel: &'a KernelContext,
executor: &'a dyn CommandExecutor,
) -> SessionRuntime<'a> {
SessionRuntime::new(
&mut self.session,
reovim_driver_session::ClientContext {
mode_stack: &mut self.mode_stack,
windows: &mut self.windows,
extensions: &mut self.extensions,
compositor: &mut self.compositor,
tabs: &mut self.tabs,
registers: &mut self.registers,
clipboard_history: &mut self.clipboard_history,
local_marks: &mut self.local_marks,
jumplist: &mut self.jumplist,
active_buffer: &mut self.active_buffer,
terminal_size: &mut self.terminal_size,
},
kernel,
executor,
)
}
}
#[test]
fn test_dot_repeat_command_id() {
let cmd = DotRepeat;
assert_eq!(cmd.id().name(), "dot-repeat");
assert_eq!(cmd.description(), "Repeat last change (.)");
}
#[test]
fn test_dot_repeat_without_vim_state_returns_success() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello world");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
}
#[test]
fn test_dot_repeat_without_last_change_returns_success() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello world");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
runtime.ext_mut::<VimSessionState>();
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
}
#[test]
fn test_dot_repeat_insert_text() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "hello".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert!(content.is_some());
assert_eq!(content.unwrap(), "hello");
}
#[test]
fn test_dot_repeat_insert_with_original_count() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "ab".to_string(),
},
count: Some(3),
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert!(content.is_some());
assert_eq!(content.unwrap(), "ababab");
}
#[test]
fn test_dot_repeat_count_overrides_original() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "x".to_string(),
},
count: Some(5),
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
args.set("count", ArgValue::Count(2));
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert!(content.is_some());
assert_eq!(content.unwrap(), "xx");
}
#[test]
fn test_last_change_stores_operator_motion() {
let last_change = LastChange {
change_type: ChangeType::OperatorMotion {
operator: OperatorType::Delete,
linewise: false,
},
count: Some(3),
register: Some('a'),
keys: Vec::new(),
};
assert!(matches!(last_change.change_type, ChangeType::OperatorMotion { .. }));
assert_eq!(last_change.count, Some(3));
assert_eq!(last_change.register, Some('a'));
}
#[test]
fn test_last_change_stores_operator_textobj() {
let last_change = LastChange {
change_type: ChangeType::OperatorTextObject {
operator: OperatorType::Change,
linewise: false,
},
count: None,
register: None,
keys: Vec::new(),
};
assert!(matches!(last_change.change_type, ChangeType::OperatorTextObject { .. }));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_last_change_stores_insert() {
let last_change = LastChange {
change_type: ChangeType::Insert {
text: "hello world".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
};
match &last_change.change_type {
ChangeType::Insert { text } => {
assert_eq!(text, "hello world");
}
_ => panic!("Expected Insert variant"),
}
}
#[test]
fn test_dot_repeat_no_buffer_returns_error() {
let kernel = create_test_context();
let mode = test_mode();
let mut session = Session::new(ClientId::new(1), mode.clone()); let executor = StubExecutor;
let mut mode_stack = ModeStack::new(mode);
let mut windows = WindowLayout::empty();
let mut extensions = ExtensionMap::new();
let mut compositor = None;
let mut tabs = reovim_driver_session::TabPageSet::new();
let mut registers = RegisterBank::new();
let mut clipboard_history = HistoryRing::new();
let mut local_marks = MarkBank::new();
let mut jumplist = Jumplist::new();
let mut active_buffer = None;
let mut terminal_size = (80u16, 24u16);
let mut runtime = SessionRuntime::new(
&mut session,
reovim_driver_session::ClientContext {
mode_stack: &mut mode_stack,
windows: &mut windows,
extensions: &mut extensions,
compositor: &mut compositor,
tabs: &mut tabs,
registers: &mut registers,
clipboard_history: &mut clipboard_history,
local_marks: &mut local_marks,
jumplist: &mut jumplist,
active_buffer: &mut active_buffer,
terminal_size: &mut terminal_size,
},
&kernel,
&executor,
);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "test".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let args = CommandContext::new();
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_error());
}
#[test]
fn test_dot_repeat_command_debug() {
let debug = format!("{:?}", DotRepeat);
assert!(debug.contains("DotRepeat"));
}
#[test]
fn test_dot_repeat_command_default() {
let _ = DotRepeat;
}
#[test]
fn test_dot_repeat_command_clone() {
let cmd = DotRepeat;
let _ = cmd;
}
#[test]
fn test_dot_repeat_insert_empty_text() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: String::new(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert_eq!(content.unwrap(), "hello");
}
#[test]
fn test_dot_repeat_insert_with_no_count() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "abc".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert_eq!(content.unwrap(), "abc");
}
#[test]
fn test_dot_repeat_operator_motion_stub() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello world");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::OperatorMotion {
operator: OperatorType::Delete,
linewise: false,
},
count: Some(1),
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
}
#[test]
fn test_dot_repeat_operator_textobj_stub() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello world");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::OperatorTextObject {
operator: OperatorType::Change,
linewise: true,
},
count: None,
register: Some('a'),
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
}
#[test]
fn test_dot_repeat_insert_with_newline() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "line1\nline2".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
}
#[test]
fn test_dot_repeat_count_overrides_none() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "");
let mut state = TestState::with_window(buffer_id, test_mode());
let executor = StubExecutor;
let mut runtime = state.runtime(&kernel, &executor);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "x".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
args.set("count", ArgValue::Count(3));
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_success());
let content = runtime.buffer_content(buffer_id);
assert_eq!(content.unwrap(), "xxx");
}
#[test]
fn test_dot_repeat_insert_no_active_window() {
let kernel = create_test_context();
let buffer_id = setup_buffer(&kernel, "hello");
let mode = test_mode();
let mut session = Session::new(ClientId::new(1), mode.clone());
let executor = StubExecutor;
let mut mode_stack = ModeStack::new(mode);
let mut windows = WindowLayout::empty(); let mut extensions = ExtensionMap::new();
let mut compositor = None;
let mut tabs = reovim_driver_session::TabPageSet::new();
let mut registers = RegisterBank::new();
let mut clipboard_history = HistoryRing::new();
let mut local_marks = MarkBank::new();
let mut jumplist = Jumplist::new();
let mut active_buffer = None;
let mut terminal_size = (80u16, 24u16);
let mut runtime = SessionRuntime::new(
&mut session,
reovim_driver_session::ClientContext {
mode_stack: &mut mode_stack,
windows: &mut windows,
extensions: &mut extensions,
compositor: &mut compositor,
tabs: &mut tabs,
registers: &mut registers,
clipboard_history: &mut clipboard_history,
local_marks: &mut local_marks,
jumplist: &mut jumplist,
active_buffer: &mut active_buffer,
terminal_size: &mut terminal_size,
},
&kernel,
&executor,
);
{
let vim = runtime.ext_mut::<VimSessionState>();
vim.last_change = Some(LastChange {
change_type: ChangeType::Insert {
text: "abc".to_string(),
},
count: None,
register: None,
keys: Vec::new(),
});
}
let mut args = CommandContext::new();
args.set_buffer_id(buffer_id);
let result = DotRepeat.execute(&mut runtime, &args);
assert!(result.is_error());
}