refactor: unify core actions and Ex command aliases

This commit is contained in:
2026-10-10 21:57:33 +02:00
parent 4e9378816e
commit 3f05287d79
9 changed files with 536 additions and 203 deletions
+109 -4
View File
@@ -869,6 +869,113 @@ end
);
}
#[test]
fn generated_core_actions_execute_through_ruby_configuration() {
use crate::{
editor::{Input, Mode},
session::EditorSession,
};
let fixture = Fixture::new();
let config = fixture.file(
"config.rb",
r#"Vamp.configure do |config|
config.clipboard_mode = "separate"
config.bind "Q", config.core.quit
config.bind "F", config.core.force_quit
config.bind "W", config.core.save
config.bind "X", config.core.save_and_quit
config.bind "E", config.core.edit
config.bind "B", config.core.buffer
config.bind "P", config.core.plugins
config.bind "L", config.core.buffers
config.bind "N", config.core.buffer_next
config.bind "V", config.core.buffer_previous
config.bind "D", config.core.buffer_delete
config.bind "Z", config.core.buffer_force_delete
end"#,
);
for (key, dirty, quit, saved) in [
('Q', false, true, false),
('Q', true, false, false),
('F', true, true, false),
('W', true, false, true),
('X', true, true, true),
] {
let buffer = fixture.file("buffer.txt", "base");
let mut session =
EditorSession::open(Some(buffer.clone()), vec!["text".into()], Some(&config))
.unwrap();
assert!(session.editor().startup_diagnostic().is_none());
if dirty {
for input in [Input::Char('i'), Input::Char('!'), Input::Escape] {
session.handle(input);
}
}
session.handle(Input::Char(key));
assert_eq!(session.editor().should_quit(), quit, "{key}");
assert_eq!(
std::fs::read_to_string(&buffer).unwrap(),
if saved { "!base" } else { "base" },
"{key}"
);
assert_eq!(session.editor().dirty(), dirty && !saved, "{key}");
}
let buffer = fixture.file("buffer.txt", "base");
let second = fixture.file("second.txt", "second");
let mut session =
EditorSession::open(Some(buffer), vec!["text".into()], Some(&config)).unwrap();
assert!(session.editor().startup_diagnostic().is_none());
session.handle(Input::Char('E'));
assert_eq!(session.editor().mode(), Mode::Command);
assert_eq!(session.editor().command_line(), "edit ");
for ch in second.to_string_lossy().chars() {
session.handle(Input::Char(ch));
}
session.handle(Input::Enter);
assert_eq!(session.editor().lines(), &["second"]);
session.handle(Input::Char('B'));
assert_eq!(session.editor().mode(), Mode::Command);
assert_eq!(session.editor().command_line(), "buffer ");
session.handle(Input::Char('1'));
session.handle(Input::Enter);
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().lines(), &["base"]);
session.handle(Input::Char('P'));
assert!(session.editor().message().contains("text"));
session.handle(Input::Char('L'));
assert!(session.editor().message().contains("buffer.txt"));
assert!(session.editor().message().contains("second.txt"));
session.handle(Input::Char('N'));
assert_eq!(session.editor().buffer_id(), 2);
session.handle(Input::Char('V'));
assert_eq!(session.editor().buffer_id(), 1);
session.handle(Input::Char('D'));
assert!(!session.editor().has_buffer(1));
assert_eq!(session.editor().buffer_id(), 2);
for input in [Input::Char('i'), Input::Char('!'), Input::Escape] {
session.handle(input);
}
session.handle(Input::Char('D'));
assert!(session.editor().has_buffer(2));
session.handle(Input::Char('Z'));
assert!(!session.editor().has_buffer(2));
assert_eq!(std::fs::read_to_string(second).unwrap(), "second");
}
#[test]
fn raw_builtin_aliases_are_not_external_binding_commands() {
let fixture = Fixture::new();
for name in ["q", "core.quit"] {
let config = fixture.file(
"config.rb",
&format!("Vamp.configure {{ |config| config.bind 'K', '{name}' }}"),
);
assert!(load_config(&config).is_err(), "{name}");
}
}
#[test]
fn config_resolves_relative_paths_and_rejects_invalid_types_and_core() {
let fixture = Fixture::new();
@@ -895,10 +1002,8 @@ end
.contains("must be a String")
);
for target in [
"config.core.lsp_hover",
"config.core.lsp_definition",
"config.core.lsp_diagnostics",
"config.core.lsp.hvoer",
"7",
"{}",
"config.core.lsp",
"config.core.lsp.hover.tap { |action| action.name = 'core.delete' }",
"config.core.lsp.tap { |namespace| namespace.define_singleton_method(:bogus) { namespace.hover } }.bogus",