feat(editor): edit the : command line with Ctrl-W / Cmd-Backspace

Handle DeleteWord (Ctrl-W) and DeleteLine (Cmd-Backspace) in Command mode:
Ctrl-W drops the previous word, Cmd-Backspace clears the line. Neither exits to
Normal on empty (unlike Backspace), so command editing stays on the line.
This commit is contained in:
Julien Calixte
2026-07-11 19:30:39 +02:00
parent 5d9591e5ea
commit 1d7448ba75

View File

@@ -431,7 +431,20 @@ impl Editor {
self.cmdline.clear();
self.mode = Mode::Normal;
}
// Word/line deletes and Tab aren't meaningful on a short command line.
Key::DeleteWord => {
// Readline Ctrl-W: drop trailing spaces, then the word before the
// caret — editing the `:` command line while typing it. Unlike
// Backspace, emptying the line does not cancel back to Normal.
while self.cmdline.ends_with(' ') {
self.cmdline.pop();
}
while !self.cmdline.is_empty() && !self.cmdline.ends_with(' ') {
self.cmdline.pop();
}
}
// Cmd+Backspace: clear the whole command line, staying in Command.
Key::DeleteLine => self.cmdline.clear(),
// Tab isn't meaningful on a short command line.
_ => {}
}
Effect::None
@@ -1758,6 +1771,42 @@ mod tests {
assert_eq!(e.draw(true).bytes().len(), display::FB_BYTES);
}
// ---- Command-line editing (Ctrl-W / Cmd-Backspace while typing `:`) ----
#[test]
fn ctrl_w_deletes_the_last_word_of_the_command_line() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
for c in "sync now".chars() {
e.handle(Key::Char(c));
}
e.handle(Key::DeleteWord);
assert_eq!(e.cmdline, "sync ");
assert_eq!(e.mode(), Mode::Command); // stays on the command line
}
#[test]
fn ctrl_w_on_a_one_word_command_does_not_cancel() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
e.handle(Key::Char('w'));
e.handle(Key::DeleteWord);
assert_eq!(e.cmdline, "");
assert_eq!(e.mode(), Mode::Command); // unlike Backspace, does not exit
}
#[test]
fn cmd_backspace_clears_the_command_line() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
for c in "fmt".chars() {
e.handle(Key::Char(c));
}
e.handle(Key::DeleteLine);
assert_eq!(e.cmdline, "");
assert_eq!(e.mode(), Mode::Command);
}
#[test]
fn text_getter_reflects_edits() {
let e = typed("hello");