Skip to content

ExecuteChord returns null when the first binding for a chord belongs to an unregistered command, even if a registered command shares the chord #116

Description

@matt-edmondson

What's wrong

KeybindingService.ExecuteChord (Keybinding/Services/KeybindingService.cs, ~line 227) resolves the chord with FindCommandByChord (~line 280), which takes FirstOrDefault over the active profile's bindings. Only after picking that single match does it check whether the command is registered. Two things make that first match stale:

  • BindChord does no conflict check, so two commands can share a chord.
  • CommandRegistry.UnregisterCommand leaves the command's bindings in profiles.

Repro

registry.RegisterCommand(a); registry.RegisterCommand(b);
service.BindChord("a", Chord.Parse("Ctrl+S"));   // true
service.BindChord("b", Chord.Parse("Ctrl+S"));   // true
service.FindCommandByChord(Chord.Parse("Ctrl+S")); // "a"

registry.UnregisterCommand("a");
service.ExecuteChord(Chord.Parse("Ctrl+S"));      // null
service.FindCommandByChord(Chord.Parse("Ctrl+S")); // still "a"

Expected: ExecuteChord runs b, the only registered command bound to the chord.

Reproduced against the current main build.

Why it matters

Apps that unregister commands (plugins unloading, context-specific commands) end up with shortcuts that silently do nothing, even though another live command is bound to the same keys. There is no error or log to explain why the key stopped working.

Suggested fix

  • In ExecuteChord, filter matching bindings by IsCommandRegistered before choosing one.
  • Separately, and optionally: remove a command's bindings when it is unregistered, and/or report conflicts from BindChord (reject, or expose a GetConflicts API).

Acceptance criteria

  • The repro executes b.
  • A regression test covering an unregistered command sharing a chord with a registered one.

Activity

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

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions