import { ReadlineKeymap, type ReadlineKeymapName } from "./keymap.js"; const bindingPattern = /^"((?:\\.|[^"\\])*)":\s*(.+)$/; const macroPattern = /^"((?:\\.|[^"\\])*)"$/; /** A source-located error from canonical `bind -p` output. */ export class InputrcParseError extends Error { constructor( readonly line: number, message: string, ) { super(`Inputrc parse error on line ${line}: ${message}`); this.name = "InputrcParseError"; } } /** * Parse the stable inputrc-like output emitted by `bind -m -p`. * This is intentionally narrower than the portable user-inputrc parser: Bash * has already resolved includes and conditionals before producing this form. */ export function parseCanonicalKeymap( name: ReadlineKeymapName, source: string, ): ReadlineKeymap { let keymap = ReadlineKeymap.empty(name); for (const [index, line] of source.split("\n").entries()) { const trimmed = line.trim(); if (trimmed.length === 0 || trimmed.startsWith("#")) continue; const binding = trimmed.match(bindingPattern); if (binding === null) { throw new InputrcParseError( index + 1, "expected a quoted key sequence binding", ); } const sequence = binding[1]; const value = binding[2]?.trim(); if (sequence === undefined || value === undefined || value.length === 0) { throw new InputrcParseError( index + 1, "binding is missing a key sequence or value", ); } const macro = value.match(macroPattern); keymap = keymap.withBinding( sequence, macro === null ? { kind: "command", command: value } : { kind: "macro", expansion: macro[1] ?? "" }, ); } return keymap; }