Skip to content

Reject malformed key chords containing empty segments #906

Description

@benvinegar

Problem

The shared key-chord parser removes empty segments after splitting a chord on +. As a result, malformed bindings are silently interpreted as different valid shortcuts:

s+       → s
+s       → s
ctrl++s  → ctrl+s
ctrl+s+  → ctrl+s

For example, parseKeyChord("s+") currently returns the same parsed chord as parseKeyChord("s"), and the malformed chord matches a real s key event.

The behavior comes from filtering empty tokens before the parser validates the modifier and base-key structure in src/extension-api/keys.ts.

Why this matters

This grammar is shared by user [keybindings], extension registerCommand bindings, Hunk's own command table, and the public matchesKey helper. A typo can therefore claim and execute a real shortcut instead of being rejected and surfaced as an unusable chord.

This is also inconsistent with the parser's existing behavior for other malformed chords such as ctlr+s, f13, and ctrl+, which are rejected rather than normalized.

Expected behavior

Key chords with a missing component around + should be rejected. The intentional literal plus-key binding must remain valid:

+       → literal plus key
 +      → literal plus key, with surrounding whitespace tolerated

Whitespace around valid components, such as ctrl + s, should also continue to work.

Acceptance criteria

  • s+, +s, ctrl++s, and ctrl+s+ are rejected by parseKeyChord.
  • Literal + and whitespace-padded + continue to parse as the plus key.
  • Valid spaced chords such as ctrl + s continue to parse.
  • A malformed user binding is reported and omitted without discarding valid sibling chords or claiming the normalized shortcut.
  • Extension command registration inherits the stricter shared parser behavior.
  • Add focused parser and keymap regression tests.
  • Add a patch Changeset for hunkdiff.
Implementation plan

implementation-plan.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions