Skip to content

A Command built from a CommandId with surrounding whitespace registers but can never be found, bound or unregistered #123

Description

@matt-edmondson

What's wrong

The two Command constructors store the id differently:

  • Command(string id, …) trims the id.
  • Command(CommandId id, …) (Keybinding/Models/Command.cs:18-23) stores Id = id as given.

CommandRegistry.RegisterCommand keys its dictionary on command.Id untrimmed (Keybinding/Services/CommandRegistry.cs, TryAdd(command.Id, …)). But IsCommandRegistered, GetCommand and UnregisterCommand all trim the id they are given before looking it up. So a command whose CommandId has leading or trailing whitespace is stored under a key that no lookup can ever produce.

CommandId's documented pattern (^[a-z0-9]+(\.[a-z0-9]+)*$) is not enforced either, so CommandId.Create(" file.save ") succeeds.

Reproduction

This was executed as a scratch MSTest:

reg.RegisterCommand(new Command(CommandId.Create(" file.save "), CommandName.Create("Save"))); // true
reg.IsCommandRegistered("file.save");                 // False
reg.IsCommandRegistered(" file.save ");               // False (lookup trims, stored key doesn't)
reg.GetAllCommands();                                 // contains "[ file.save ]"
svc.BindChord(" file.save ", Chord.Parse("Ctrl+S"));  // False
reg.UnregisterCommand(" file.save ");                 // False

After a save and reload, the repository rebuilds the command with the string constructor, so the id comes back trimmed. The in-memory state and the reloaded state disagree.

Why it matters

Command ids often come from configuration files or plugin manifests, where stray whitespace is common. The registration looks successful and the command shows up in listings, but it can never be bound, executed or removed, and nothing reports an error.

Suggested fix / acceptance criteria

  • Normalize in one place: trim in the Command(CommandId, …) constructor, or key CommandRegistry on command.Id.ToString().Trim().
  • new Command(CommandId.Create(" file.save "), …) then behaves exactly like new Command(" file.save ", …).
  • Enforcing CommandId's regex is not recommended as the fix: the similar NoteName pattern would reject + and ,, which the Ctrl++ / Ctrl+, support relies on.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    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