parser.ts
keybindings/parser.ts
No strong subsystem tag
204
Lines
4972
Bytes
7
Exports
1
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with general runtime concerns. It contains 204 lines, 1 detected imports, and 7 detected exports.
Important relationships
Detected exports
parseKeystrokeparseChordkeystrokeToStringchordToStringkeystrokeToDisplayStringchordToDisplayStringparseBindings
Keywords
casepartskeystrokebreakchordpushctrldisplayparsedkeystrokeshift
Detected imports
./types.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import type {
Chord,
KeybindingBlock,
ParsedBinding,
ParsedKeystroke,
} from './types.js'
/**
* Parse a keystroke string like "ctrl+shift+k" into a ParsedKeystroke.
* Supports various modifier aliases (ctrl/control, alt/opt/option/meta,
* cmd/command/super/win).
*/
export function parseKeystroke(input: string): ParsedKeystroke {
const parts = input.split('+')
const keystroke: ParsedKeystroke = {
key: '',
ctrl: false,
alt: false,
shift: false,
meta: false,
super: false,
}
for (const part of parts) {
const lower = part.toLowerCase()
switch (lower) {
case 'ctrl':
case 'control':
keystroke.ctrl = true
break
case 'alt':
case 'opt':
case 'option':
keystroke.alt = true
break
case 'shift':
keystroke.shift = true
break
case 'meta':
keystroke.meta = true
break
case 'cmd':
case 'command':
case 'super':
case 'win':
keystroke.super = true
break
case 'esc':
keystroke.key = 'escape'
break
case 'return':
keystroke.key = 'enter'
break
case 'space':
keystroke.key = ' '
break
case '↑':
keystroke.key = 'up'
break
case '↓':
keystroke.key = 'down'
break
case '←':
keystroke.key = 'left'
break
case '→':
keystroke.key = 'right'
break
default:
keystroke.key = lower
break
}
}
return keystroke
}
/**
* Parse a chord string like "ctrl+k ctrl+s" into an array of ParsedKeystrokes.
*/
export function parseChord(input: string): Chord {
// A lone space character IS the space key binding, not a separator
if (input === ' ') return [parseKeystroke('space')]
return input.trim().split(/\s+/).map(parseKeystroke)
}
/**
* Convert a ParsedKeystroke to its canonical string representation for display.
*/
export function keystrokeToString(ks: ParsedKeystroke): string {
const parts: string[] = []
if (ks.ctrl) parts.push('ctrl')
if (ks.alt) parts.push('alt')
if (ks.shift) parts.push('shift')
if (ks.meta) parts.push('meta')
if (ks.super) parts.push('cmd')
// Use readable names for display
const displayKey = keyToDisplayName(ks.key)
parts.push(displayKey)
return parts.join('+')
}
/**
* Map internal key names to human-readable display names.
*/
function keyToDisplayName(key: string): string {
switch (key) {
case 'escape':
return 'Esc'
case ' ':
return 'Space'
case 'tab':
return 'tab'
case 'enter':
return 'Enter'
case 'backspace':
return 'Backspace'
case 'delete':
return 'Delete'
case 'up':
return '↑'
case 'down':
return '↓'
case 'left':
return '←'
case 'right':
return '→'
case 'pageup':
return 'PageUp'
case 'pagedown':
return 'PageDown'
case 'home':
return 'Home'
case 'end':
return 'End'
default:
return key
}
}
/**
* Convert a Chord to its canonical string representation for display.
*/
export function chordToString(chord: Chord): string {
return chord.map(keystrokeToString).join(' ')
}
/**
* Display platform type - a subset of Platform that we care about for display.
* WSL and unknown are treated as linux for display purposes.
*/
type DisplayPlatform = 'macos' | 'windows' | 'linux' | 'wsl' | 'unknown'
/**
* Convert a ParsedKeystroke to a platform-appropriate display string.
* Uses "opt" for alt on macOS, "alt" elsewhere.
*/
export function keystrokeToDisplayString(
ks: ParsedKeystroke,
platform: DisplayPlatform = 'linux',
): string {
const parts: string[] = []
if (ks.ctrl) parts.push('ctrl')
// Alt/meta are equivalent in terminals, show platform-appropriate name
if (ks.alt || ks.meta) {
// Only macOS uses "opt", all other platforms use "alt"
parts.push(platform === 'macos' ? 'opt' : 'alt')
}
if (ks.shift) parts.push('shift')
if (ks.super) {
parts.push(platform === 'macos' ? 'cmd' : 'super')
}
// Use readable names for display
const displayKey = keyToDisplayName(ks.key)
parts.push(displayKey)
return parts.join('+')
}
/**
* Convert a Chord to a platform-appropriate display string.
*/
export function chordToDisplayString(
chord: Chord,
platform: DisplayPlatform = 'linux',
): string {
return chord.map(ks => keystrokeToDisplayString(ks, platform)).join(' ')
}
/**
* Parse keybinding blocks (from JSON config) into a flat list of ParsedBindings.
*/
export function parseBindings(blocks: KeybindingBlock[]): ParsedBinding[] {
const bindings: ParsedBinding[] = []
for (const block of blocks) {
for (const [key, action] of Object.entries(block.bindings)) {
bindings.push({
chord: parseChord(key),
action,
context: block.context,
})
}
}
return bindings
}