feat: make keybindings configurable
This commit is contained in:
+100
-4
@@ -48,13 +48,16 @@ impl EditorSession {
|
||||
Err(error) => notices.push(format!("{}: {error}", path.display())),
|
||||
}
|
||||
}
|
||||
for (key, command) in config.bindings {
|
||||
if let Err(error) = editor.bind(key, command) {
|
||||
notices.push(format!("binding {key:?}: {error}"));
|
||||
for binding in config.bindings {
|
||||
if let Err(error) = editor.bind(&binding.mode, &binding.key, binding.command) {
|
||||
notices.push(format!(
|
||||
"binding {} {:?}: {error}",
|
||||
binding.mode, binding.key
|
||||
));
|
||||
}
|
||||
}
|
||||
if notices.is_empty() {
|
||||
editor.set_message("i: insert • Esc: normal • :w save • :q quit • :plugins");
|
||||
editor.set_message("Ready • --no-config uses default keybindings");
|
||||
} else {
|
||||
editor.set_message(notices.join(" | "));
|
||||
}
|
||||
@@ -318,6 +321,99 @@ end
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_bindings_preserve_modal_editing_history_and_literal_paste() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.healthy();
|
||||
let mut session = fixture.open(
|
||||
r#"
|
||||
Vamp.configure do |config|
|
||||
config.plugin "healthy.rb"
|
||||
config.bind "n", "core.insert_first_nonblank"
|
||||
config.bind "i", nil
|
||||
config.bind "<C-r>", "core.redo"
|
||||
config.bind "<C-c>", "core.normal", mode: "insert"
|
||||
config.bind "<Esc>", nil, mode: "insert"
|
||||
config.bind "!", "core.newline", mode: "insert"
|
||||
config.bind "X", nil, mode: "insert"
|
||||
config.bind ":", nil
|
||||
config.bind "<Space>", "core.command"
|
||||
config.bind "<Enter>", nil, mode: "command"
|
||||
config.bind "<C-x>", "core.submit", mode: "command"
|
||||
config.bind "<C-c>", "core.normal", mode: "command"
|
||||
config.bind "<C-h>", "core.backspace", mode: "command"
|
||||
config.bind "g", "core.undo"
|
||||
config.bind "g", "healthy"
|
||||
config.bind "n", "core.missing"
|
||||
config.bind "n", "core.undo", mode: "missing"
|
||||
config.bind "<Unknown>", "core.undo"
|
||||
config.bind "<C-f>", "healthy", mode: "insert"
|
||||
end
|
||||
"#,
|
||||
);
|
||||
let message = session.editor().message();
|
||||
for error in [
|
||||
"Unknown action for NORMAL: core.missing",
|
||||
"Unknown binding mode: missing",
|
||||
"Invalid binding key",
|
||||
"Ex/plugin commands can only be bound in Normal mode",
|
||||
] {
|
||||
assert!(message.contains(error), "{message}");
|
||||
}
|
||||
session.handle(Input::Char('i'));
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Normal);
|
||||
session.handle(Input::Char('n'));
|
||||
for ch in "界Xab!".chars() {
|
||||
session.handle(Input::Char(ch));
|
||||
}
|
||||
assert_eq!(session.editor().lines(), &["界ab", ""]);
|
||||
session.handle(Input::Escape);
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Insert);
|
||||
session.handle(Input::Paste("X!".into()));
|
||||
assert_eq!(session.editor().lines(), &["界ab", "X!"]);
|
||||
session.handle(Input::Ctrl('C'));
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Normal);
|
||||
session.handle(Input::Char('u'));
|
||||
assert_eq!(session.editor().lines(), &["界ab", ""]);
|
||||
session.handle(Input::Char('u'));
|
||||
assert_eq!(session.editor().lines(), &[String::new()]);
|
||||
session.handle(Input::Ctrl('r'));
|
||||
session.handle(Input::Ctrl('r'));
|
||||
assert_eq!(session.editor().lines(), &["界ab", "X!"]);
|
||||
session.handle(Input::Char(':'));
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Normal);
|
||||
session.handle(Input::Char(' '));
|
||||
for ch in "w界".chars() {
|
||||
session.handle(Input::Char(ch));
|
||||
}
|
||||
session.handle(Input::Backspace);
|
||||
assert_eq!(session.editor().command_line(), "w");
|
||||
session.handle(Input::Enter);
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Command);
|
||||
session.handle(Input::Ctrl('x'));
|
||||
assert_eq!(
|
||||
fs::read_to_string(fixture.0.join("buffer.txt")).unwrap(),
|
||||
"界ab\nX!"
|
||||
);
|
||||
session.handle(Input::Char(' '));
|
||||
session.handle(Input::Char('q'));
|
||||
session.handle(Input::Ctrl('c'));
|
||||
assert_eq!(session.editor().mode(), crate::editor::Mode::Normal);
|
||||
assert!(session.editor().command_line().is_empty());
|
||||
assert!(!session.editor().should_quit());
|
||||
session.handle(Input::Char('g'));
|
||||
assert_eq!(session.editor().lines(), &["healthy "]);
|
||||
session.handle(Input::Char(' '));
|
||||
session.handle(Input::Char('q'));
|
||||
session.handle(Input::Ctrl('x'));
|
||||
assert!(!session.editor().should_quit());
|
||||
session.handle(Input::Char(' '));
|
||||
session.handle(Input::Char('q'));
|
||||
session.handle(Input::Char('!'));
|
||||
session.handle(Input::Ctrl('x'));
|
||||
assert!(session.editor().should_quit());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken_config_falls_back_to_core_without_failing_the_session() {
|
||||
let fixture = Fixture::new();
|
||||
|
||||
Reference in New Issue
Block a user