Make Ctrl+/ resolve the same on every terminal, not just kitty - #2934
Merged
Conversation
sinelaw
force-pushed
the
claude/input-parser-key-regression-crm8qa
branch
from
August 9, 2026 14:45
7aa7ab6 to
5594868
Compare
A terminal without the kitty keyboard protocol has one byte for this chord and sends 0x1F for Ctrl+/, Ctrl+7 and Ctrl+_ alike, so the parser has to pick a spelling. It derived the whole 0x1C..=0x1F range arithmetically and landed on Ctrl+_, which no keymap binds — so `ctrl+/` (toggle-comment in the default keymap, undo in the emacs one) fired under kitty, where the chord arrives as `CSI 47;5u` and decodes to Ctrl+/, and nowhere else. Report 0x1F as Ctrl+/: the key people actually press for it, and the one a kitty terminal already reports, so both kinds of terminal resolve to the same binding. 0x1C/0x1D/0x1E keep `\`, `]` and `^` — those are the keys bound to them today, and `ctrl+]` and `ctrl+\` are live bindings, so re-canonicalising them would break working chords. Also add `/` to the PTY encoder's control-byte table. It was missing, so Ctrl+/ aimed at a terminal panel fell through to the plain-character path and the child process saw a literal `/` — already wrong under kitty, and wrong everywhere once the parser reports the chord this way. Fixes #2933 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VmiRyUD1gAUkdiVTQoaaa
sinelaw
force-pushed
the
claude/input-parser-key-regression-crm8qa
branch
from
August 9, 2026 18:19
e82bd3b to
b1a3ebc
Compare
On a layout where `/` needs Shift (German, French, Spanish, ...) there is no `Ctrl+/` for a terminal to report. kitty sends the physical chord plus the character it types -- `CSI 55:47;6u`: base 55 (`7`), shifted 47 (`/`), Ctrl+Shift -- and the parser reported only the base, so the chord landed on `ctrl+shift+7`. `default.json` binds that to `set_bookmark`, so the keystroke users press for "comment this line" silently set a bookmark. Reporting the typed character instead is no fix: it would break every US `Ctrl+Shift+<digit>` binding, which are keyed on the digit. So the parser stops choosing. `KeyPress` carries the physical chord plus the layout character when the two disagree, and `Editor::handle_key_press` tries the layout reading first, using it only when the keymap actually binds it. A US layout is byte-for-byte unaffected -- nothing binds `ctrl+&`, so `Ctrl+Shift+7` still reaches `set_bookmark` -- while a German layout resolves the same keystroke to `ctrl+/`. One chord can only mean one thing, so where a keymap binds both readings this is a documented precedence, not a merge: the layout reading wins and the physical chord is unreachable from that key. `KeyPress` derefs to `KeyEvent`, so existing `.code`/`.modifiers` call sites are unchanged; the parser's `Event` mirrors crossterm's variants so the layout character survives to the keymap. Refs #2933 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VmiRyUD1gAUkdiVTQoaaa
The browser sends a chord and never a layout's shifted codepoint, so these are plain key presses with no layout character — but they still have to be the type `maybe_dismiss_wave_animation` now takes. The `web` feature is off by default, so a default-feature check misses this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VmiRyUD1gAUkdiVTQoaaa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2933
The bug
A terminal without the kitty keyboard protocol has one byte for this chord: it sends
0x1Ffor Ctrl+/, Ctrl+7 and Ctrl+_ alike.byte_to_keycodederived the whole0x1C..=0x1Frange arithmetically onto\ ] ^ _, so the byte came out as Ctrl+_ — which no keymap binds.Under kitty the same chord arrives as
CSI 47;5uand decodes straight to Ctrl+/, which is exactly whyctrl+/(toggle-comment in the default keymap, undo in the emacs one) worked there and nowhere else.The fix
Parser —
0x1Fnow reports as Ctrl+/: the key people actually press for it, and the one a kitty terminal already reports, so both kinds of terminal resolve to the same binding.0x1C/0x1D/0x1Edeliberately keep\,],^. Those are the keys bound to them today (ctrl+]is goto-matching-bracket,ctrl+\is toggle-keyboard-capture in the macOS keymap), so re-canonicalising them would break working chords.0x1Fwas the only one of the four whose spelling was wrong.PTY —
control_bytehad no/arm, so Ctrl+/ aimed at a terminal panel fell through to the plain-character path and the child saw a literal/(repro C in #2933). Already wrong under kitty; would have become universal once the parser reports the chord this way.That's the whole change: two source lines plus their tests.
Verification
Every test was checked to fail without its fix, by reverting each hunk in isolation:
0x1F→ Ctrl+/, legacy/kitty agreement)0x1F)[47](/) vs[31]The e2e tests drive the raw bytes a real terminal sends through
InputParserinto the harness and assert on rendered output (CONTRIBUTING #2), followingcsi_u_session_input.rs.other_separator_bytes_keep_their_keyspasses either way by design and is labelled a characterization test — it pins0x1C–0x1Eagainst future churn.Also verified by hand in tmux against a running editor: raw
0x1Ftoggles the comment and round-trips, andESC [ 47;5udoes the same.What this PR deliberately does not touch
An earlier revision of this branch also reworked
terminal_key_equivalentsand added aninput.key_aliasesconfig table to make the aliasing tunable. That's been dropped.The alias table existed because the parser used to report this byte as something no keymap binds (
Ctrl+7in the crossterm era,Ctrl+_after the rewrite), so an alias was the only thing makingctrl+/resolve. Canonicalising the parser on the spelling keymaps actually use removes that need. A scan of all five shipped keymaps found only two bindings spelled with an alternate form, and neither benefits:default.json'sctrl+shift+7can't be encoded distinctly on a legacy terminal and matches directly on kitty, andmacos.json'sctrl+-matches directly on kitty too.The one open question is whether macOS terminals really emit
0x1Ffor Ctrl+-, which I could not find a source for. If they do, the fix is one explicit line inmacos.jsonrather than a configurable alias layer.