What's wrong
There are two independent parsers for the same strings:
KeybindingService.ParsePhrase and ParseChord (Keybinding/Services/KeybindingService.cs:188-227)
Chord.Parse and Phrase.Parse (Keybinding/Models/MusicalTypes.cs:312-336 and :439-451)
Each tokenizes with KeyStringTokenizer and builds Notes by hand. They produce the same result today only because both copies have been patched by hand:
The copies still differ in exception messages and paramName. The notes.Count == 0 guard at KeybindingService.cs:226 is dead code.
Why it matters
Every new parsing rule has to be added twice, for example the key-name equivalences in #166. Forgetting one copy reintroduces a #107-style bug: a binding that parses one way through the service and another way through the model, so it silently never matches.
Suggested fix
- Make
KeybindingService.ParseChord return Chord.Parse(chordString).
- Make
ParsePhrase return Phrase.Parse(phraseString).
- Delete the duplicated loops and the dead guard.
Acceptance criteria
- Only one implementation of chord and phrase parsing remains.
- A test asserts that, over the existing alias, separator and invalid inputs, both entry points return equal values and throw the same exception types.
What's wrong
There are two independent parsers for the same strings:
KeybindingService.ParsePhraseandParseChord(Keybinding/Services/KeybindingService.cs:188-227)Chord.ParseandPhrase.Parse(Keybinding/Models/MusicalTypes.cs:312-336and:439-451)Each tokenizes with
KeyStringTokenizerand buildsNotes by hand. They produce the same result today only because both copies have been patched by hand:KeybindingService.ParseChordskipped the alias normalization thatChord.Parsehad, so "Control+S" never matched "Ctrl+S".The copies still differ in exception messages and
paramName. Thenotes.Count == 0guard atKeybindingService.cs:226is dead code.Why it matters
Every new parsing rule has to be added twice, for example the key-name equivalences in #166. Forgetting one copy reintroduces a #107-style bug: a binding that parses one way through the service and another way through the model, so it silently never matches.
Suggested fix
KeybindingService.ParseChordreturnChord.Parse(chordString).ParsePhrasereturnPhrase.Parse(phraseString).Acceptance criteria