Skip to content

"Ctrl+Esc" never matches "Ctrl+Escape" (nor Up/UpArrow, Return/Enter, Del/Delete, 1/D1): only modifier aliases are canonicalized #162

Description

@matt-edmondson

What's wrong

Note canonicalizes modifier aliases only (Keybinding/Models/MusicalTypes.cs:67-72, CanonicalizeKey):

"CONTROL" => "CTRL",
"WIN" or "WINDOWS" or "CMD" or "COMMAND" => "META",
_ => key

Every other key is stored exactly as typed, only uppercased, and chord equality compares those stored names. So the same physical key can be written several ways that never match each other.

Nothing in the library defines the canonical spelling either. Keybinding/Models/SpecialKeys.cs declares a public SpecialKeys enum (Escape, ArrowUp, PageUp, F1, ...), but no code references it.

Failure scenario

  • A user config binds "Ctrl+Esc", which is stored as notes {CTRL, ESC}.
  • The host builds chords from System.ConsoleKey via ToString(), so Escape becomes "Escape" and the chord is {CTRL, ESCAPE}.
  • ExecuteChord(Chord.Parse("Ctrl+Escape")) returns null. The binding shows in GetAllChords() but never fires, and nothing reports the mismatch.

Other spellings of the same key miss in the same way:

  • Up / ArrowUp (the SpecialKeys name) / UpArrow (the ConsoleKey name)
  • Return / Enter
  • Del / Delete
  • PgUp / PageUp
  • Ins / Insert
  • Ctrl+1 versus ConsoleKey's D1
  • the modifiers Option / Alt and Super / Meta

Why it matters

#107 fixed exactly this kind of silent mismatch for modifiers. Non-modifier keys are where hosts, config files and users disagree most, and the library gives them no canonical name to converge on.

Suggested fix / acceptance criteria

  • Extend CanonicalizeKey with an alias table that maps each key to one canonical name. At minimum:
    • Esc/Escape, Return/Enter, Del/Delete, Ins/Insert, PgUp/PageUp, PgDn/PageDown
    • Up/UpArrow/ArrowUp and the other arrow keys
    • D0–D9 → 0–9
    • Option → ALT, Super → META
  • Base the canonical names on SpecialKeys, or remove that enum if it is not meant to be public API.
  • Optionally, add Note.FromConsoleKey(ConsoleKey) / Chord.FromConsoleKeyInfo(ConsoleKeyInfo) so hosts get canonical chords without writing their own mapping.
  • Tests:
    • Chord.Parse("Ctrl+Esc") == Chord.Parse("Ctrl+Escape")
    • Chord.Parse("Up") == Chord.Parse("UpArrow")
    • A stored profile that uses an alias still matches after it is loaded.

Activity

  1. matt-edmondson commented on Sep 30, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions