From 7bfb8d4f2e05d3f05a511e81da4e0f600be0a275 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 22 Jul 2026 21:19:21 -0500 Subject: [PATCH] vt, input: translate \0 as Ctrl+Space There exists an ambiguous control character in standard 7-bit ASCII terminals, `\0`, which is the traditional encoding for Ctrl+Space and Ctrl+Shift+2 (or Ctrl+@ on US-104 keyboards). ``` Space 0x20 0b00100000 @ 0x40 0b01000000 ^^^^^ control character bits 0b00000 ``` Anyway, edit didn't support it. We also had a bug in the Windows KEY_EVENT parser where we would disregard otherwise acceptable inputs such as `\0` with a scancode indicating that it came from a keypress (such as, well, Ctrl+Space). --- crates/edit/src/input.rs | 1 + crates/edit/src/sys/windows.rs | 3 ++- crates/edit/src/vt.rs | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/edit/src/input.rs b/crates/edit/src/input.rs index a0dfd840844..a3dc243a118 100644 --- a/crates/edit/src/input.rs +++ b/crates/edit/src/input.rs @@ -340,6 +340,7 @@ impl<'input> Iterator for Stream<'_, '_, 'input> { let key = ch as u32 | 0x40; return Some(Input::Keyboard(kbmod::CTRL | InputKey::new(key))); } + ' ' => return Some(Input::Keyboard(kbmod::CTRL | vk::SPACE)), '\x7f' => return Some(Input::Keyboard(vk::BACK)), _ => {} }, diff --git a/crates/edit/src/sys/windows.rs b/crates/edit/src/sys/windows.rs index f4a36e82eb7..98a100835d6 100644 --- a/crates/edit/src/sys/windows.rs +++ b/crates/edit/src/sys/windows.rs @@ -335,7 +335,8 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Duration) -> Option { let event = unsafe { &inp.Event.KeyEvent }; let ch = unsafe { event.uChar.UnicodeChar }; - if event.bKeyDown != 0 && ch != 0 { + let sc = event.wVirtualScanCode; + if event.bKeyDown != 0 && (ch != 0 || sc == 0) { utf16_buf[utf16_buf_len] = MaybeUninit::new(ch); utf16_buf_len += 1; } diff --git a/crates/edit/src/vt.rs b/crates/edit/src/vt.rs index 451d1a213ea..f661fac188f 100644 --- a/crates/edit/src/vt.rs +++ b/crates/edit/src/vt.rs @@ -172,7 +172,12 @@ impl<'input> Stream<'_, 'input> { self.parser.state = State::Esc; self.off += 1; } - c @ (0x00..0x20 | 0x7f) => { + 0x00 => { + // Ctrl+Space and Ctrl+Shift+2 both produce ^@, or \0. + self.off += 1; + return Some(Token::Ctrl(' ')); + } + c @ (0x01..0x20 | 0x7f) => { self.off += 1; return Some(Token::Ctrl(c as char)); }