Parser
Parse, normalize, and validate key binding strings.
Parse, normalize, and validate key binding strings. This module converts human-readable bindings like Ctrl+Shift+S into structured objects for matching.
import {
parseKeyBind,
normalizeKeyBind,
validateKeyBind,
keyboardEventToDescriptor,
KEY_ALIASES,
MODIFIER_KEYS,
} from '@gentleduck/vim/parser'import {
parseKeyBind,
normalizeKeyBind,
validateKeyBind,
keyboardEventToDescriptor,
KEY_ALIASES,
MODIFIER_KEYS,
} from '@gentleduck/vim/parser'Types
ParsedKeyBind
The structured result of parsing a key binding string.
interface ParsedKeyBind {
/** The non-modifier key, lowercased (e.g. 's', 'space', 'enter') */
key: string
ctrl: boolean
shift: boolean
alt: boolean
meta: boolean
/** Sorted array of active modifier names */
modifiers: Array<'ctrl' | 'alt' | 'meta' | 'shift'>
}interface ParsedKeyBind {
/** The non-modifier key, lowercased (e.g. 's', 'space', 'enter') */
key: string
ctrl: boolean
shift: boolean
alt: boolean
meta: boolean
/** Sorted array of active modifier names */
modifiers: Array<'ctrl' | 'alt' | 'meta' | 'shift'>
}ValidationResult
interface ValidationResult {
valid: boolean
warnings: string[]
errors: string[]
}interface ValidationResult {
valid: boolean
warnings: string[]
errors: string[]
}Functions
parseKeyBind(binding, platform?)
Parses a key binding string into its structured components. Resolves the Mod key based on the current or specified platform.
function parseKeyBind(binding: string, platform?: Platform): ParsedKeyBindfunction parseKeyBind(binding: string, platform?: Platform): ParsedKeyBindThrows if the binding is empty, has multiple non-modifier keys, or has no non-modifier key.
Examples:
parseKeyBind('ctrl+shift+s')
// { key: 's', ctrl: true, shift: true, alt: false, meta: false, modifiers: ['ctrl', 'shift'] }
parseKeyBind('Mod+S', 'mac')
// { key: 's', ctrl: false, shift: false, alt: false, meta: true, modifiers: ['meta'] }
parseKeyBind('space')
// { key: 'space', ctrl: false, shift: false, alt: false, meta: false, modifiers: [] }parseKeyBind('ctrl+shift+s')
// { key: 's', ctrl: true, shift: true, alt: false, meta: false, modifiers: ['ctrl', 'shift'] }
parseKeyBind('Mod+S', 'mac')
// { key: 's', ctrl: false, shift: false, alt: false, meta: true, modifiers: ['meta'] }
parseKeyBind('space')
// { key: 'space', ctrl: false, shift: false, alt: false, meta: false, modifiers: [] }This function throws on invalid input. If you want to validate without throwing, use validateKeyBind() instead.
normalizeKeyBind(binding, platform?)
Returns the canonical string form of a binding: modifiers in alphabetical order, all lowercase, joined by +.
function normalizeKeyBind(binding: string, platform?: Platform): stringfunction normalizeKeyBind(binding: string, platform?: Platform): stringExamples:
normalizeKeyBind('Shift+Mod+s', 'mac') // 'meta+shift+s'
normalizeKeyBind('Ctrl+K') // 'ctrl+k'
normalizeKeyBind('Command+Alt+Z') // 'alt+meta+z' (on any platform, since Command is an alias for meta)normalizeKeyBind('Shift+Mod+s', 'mac') // 'meta+shift+s'
normalizeKeyBind('Ctrl+K') // 'ctrl+k'
normalizeKeyBind('Command+Alt+Z') // 'alt+meta+z' (on any platform, since Command is an alias for meta)Use this when you need to compare two bindings for equality:
normalizeKeyBind(bindingA) === normalizeKeyBind(bindingB)normalizeKeyBind(bindingA) === normalizeKeyBind(bindingB)validateKeyBind(binding)
Validates a key binding string without throwing. Returns errors and warnings.
function validateKeyBind(binding: string): ValidationResultfunction validateKeyBind(binding: string): ValidationResultExamples:
validateKeyBind('ctrl+k')
// { valid: true, warnings: [], errors: [] }
validateKeyBind('ctrl+k+j')
// { valid: false, warnings: [], errors: ['Multiple non-modifier keys found'] }
validateKeyBind('shift+shift+a')
// { valid: false, warnings: [], errors: ["Duplicate modifier: 'shift'"] }
validateKeyBind('alt+n')
// { valid: true, warnings: ['Alt+letter combinations may not work on macOS due to special characters'], errors: [] }validateKeyBind('ctrl+k')
// { valid: true, warnings: [], errors: [] }
validateKeyBind('ctrl+k+j')
// { valid: false, warnings: [], errors: ['Multiple non-modifier keys found'] }
validateKeyBind('shift+shift+a')
// { valid: false, warnings: [], errors: ["Duplicate modifier: 'shift'"] }
validateKeyBind('alt+n')
// { valid: true, warnings: ['Alt+letter combinations may not work on macOS due to special characters'], errors: [] }Use this before storing user-defined bindings to catch problems early.
Alt+letter combinations may produce special characters on macOS, making them unreliable for shortcuts. The validator will warn about this.
keyboardEventToDescriptor(event)
Builds a canonical key descriptor string from a KeyboardEvent. Returns null for pure modifier key presses.
function keyboardEventToDescriptor(e: KeyboardEvent): string | nullfunction keyboardEventToDescriptor(e: KeyboardEvent): string | nullExample:
document.addEventListener('keydown', (e) => {
const desc = keyboardEventToDescriptor(e)
if (desc) console.log('Key descriptor:', desc) // e.g. 'ctrl+k'
})document.addEventListener('keydown', (e) => {
const desc = keyboardEventToDescriptor(e)
if (desc) console.log('Key descriptor:', desc) // e.g. 'ctrl+k'
})Constants
KEY_ALIASES
A record mapping raw key names to their canonical forms.
const KEY_ALIASES: Record<string, string> = {
' ': 'space',
escape: 'esc',
control: 'ctrl',
cmd: 'meta',
command: 'meta',
opt: 'alt',
option: 'alt',
}const KEY_ALIASES: Record<string, string> = {
' ': 'space',
escape: 'esc',
control: 'ctrl',
cmd: 'meta',
command: 'meta',
opt: 'alt',
option: 'alt',
}MODIFIER_KEYS
The set of recognized modifier key names.
const MODIFIER_KEYS: ReadonlySet<string> = new Set(['ctrl', 'alt', 'meta', 'shift'])const MODIFIER_KEYS: ReadonlySet<string> = new Set(['ctrl', 'alt', 'meta', 'shift'])