chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -289,6 +289,10 @@ const SYMBOL_KEYS = new Set([
"?",
]);
const SHIFTED_SYMBOL_KEYS = new Set("~!@#$%^&*()_+|{}:<>?");
const MODIFIER_NAMES = ["shift", "alt", "ctrl", "super"] as const;
const MODIFIERS = {
shift: 1,
alt: 2,
@@ -365,6 +369,17 @@ function normalizeShiftedLetterIdentityCodepoint(codepoint: number, modifier: nu
return codepoint;
}
function getLogicalShiftedSymbolCandidate(
codepoint: number | undefined,
modifier: number,
allowedSymbols: ReadonlySet<string>,
): { codepoint: number; modifier: number } | undefined {
if (codepoint === undefined || (modifier & MODIFIERS.shift) === 0) return undefined;
const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
if (!allowedSymbols.has(String.fromCharCode(normalizedCodepoint))) return undefined;
return { codepoint: normalizedCodepoint, modifier: modifier & ~MODIFIERS.shift };
}
const LEGACY_KEY_SEQUENCES = {
up: ["\x1b[A", "\x1bOA"],
down: ["\x1b[B", "\x1bOB"],
@@ -656,9 +671,6 @@ function matchesKittySequence(data: string, expectedCodepoint: number, expectedM
const actualMod = parsed.modifier & ~LOCK_MASK;
const expectedMod = expectedModifier & ~LOCK_MASK;
// Check if modifiers match
if (actualMod !== expectedMod) return false;
const normalizedCodepoint = normalizeShiftedLetterIdentityCodepoint(
normalizeKittyFunctionalCodepoint(parsed.codepoint),
parsed.modifier,
@@ -668,8 +680,22 @@ function matchesKittySequence(data: string, expectedCodepoint: number, expectedM
expectedModifier,
);
// Primary match: codepoint matches directly after normalizing functional keys
if (normalizedCodepoint === normalizedExpectedCodepoint) return true;
// Physical match: primary codepoint and every reported modifier match.
if (actualMod === expectedMod && normalizedCodepoint === normalizedExpectedCodepoint) return true;
// Logical match: Kitty reports symbols such as "+" as Shift plus the physical "=" key.
// Bindings name the produced symbol, so consume that implicit Shift while preserving
// Ctrl, Alt, and Super. The physical "shift+=" spelling remains valid through the match above.
const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(parsed.shiftedKey, actualMod, SYMBOL_KEYS);
if (
logicalShiftedSymbol &&
(logicalShiftedSymbol.modifier & ~LOCK_MASK) === expectedMod &&
logicalShiftedSymbol.codepoint === normalizeKittyFunctionalCodepoint(expectedCodepoint)
) {
return true;
}
if (actualMod !== expectedMod) return false;
// Alternate match: use base layout key for non-Latin keyboard layouts.
// This allows Ctrl+С (Cyrillic) to match Ctrl+c (Latin) when terminal reports
@@ -764,13 +790,19 @@ function isDigitKey(key: string): boolean {
}
function matchesPrintableModifyOtherKeys(data: string, expectedKeycode: number, expectedModifier: number): boolean {
if (expectedModifier === 0) return false;
const parsed = parseModifyOtherKeysSequence(data);
if (!parsed || parsed.modifier !== expectedModifier) return false;
return (
if (!parsed) return false;
const physicalMatch =
parsed.modifier === expectedModifier &&
normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) ===
normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier)
normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
if (physicalMatch) return true;
const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(
parsed.codepoint,
parsed.modifier,
SHIFTED_SYMBOL_KEYS,
);
return logicalShiftedSymbol?.modifier === expectedModifier && logicalShiftedSymbol.codepoint === expectedKeycode;
}
function formatKeyNameWithModifiers(keyName: string, modifier: number): string | undefined {
@@ -788,15 +820,22 @@ function formatKeyNameWithModifiers(keyName: string, modifier: number): string |
function parseKeyId(
keyId: string,
): { key: string; ctrl: boolean; shift: boolean; alt: boolean; super: boolean } | null {
const parts = keyId.toLowerCase().split("+");
const key = parts[parts.length - 1];
if (!key) return null;
let key = keyId.toLowerCase();
const modifiers = new Set<(typeof MODIFIER_NAMES)[number]>();
while (true) {
const modifier = MODIFIER_NAMES.find((name) => key.startsWith(`${name}+`));
if (!modifier) break;
if (modifiers.has(modifier)) return null;
modifiers.add(modifier);
key = key.slice(modifier.length + 1);
}
if (!key || (key.includes("+") && key !== "+")) return null;
return {
key,
ctrl: parts.includes("ctrl"),
shift: parts.includes("shift"),
alt: parts.includes("alt"),
super: parts.includes("super"),
ctrl: modifiers.has("ctrl"),
shift: modifiers.has("shift"),
alt: modifiers.has("alt"),
super: modifiers.has("super"),
};
}
@@ -1197,7 +1236,9 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
}
// Check both raw char and Kitty sequence (needed for release events)
return data === key || matchesKittySequence(data, codepoint, 0);
return (
data === key || matchesKittySequence(data, codepoint, 0) || matchesPrintableModifyOtherKeys(data, codepoint, 0)
);
}
return false;
@@ -1251,12 +1292,25 @@ function formatParsedKey(codepoint: number, modifier: number, baseLayoutKey?: nu
export function parseKey(data: string): string | undefined {
const kitty = parseKittySequence(data);
if (kitty) {
return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(kitty.shiftedKey, kitty.modifier, SYMBOL_KEYS);
return formatParsedKey(
logicalShiftedSymbol?.codepoint ?? kitty.codepoint,
logicalShiftedSymbol?.modifier ?? kitty.modifier,
kitty.baseLayoutKey,
);
}
const modifyOtherKeys = parseModifyOtherKeysSequence(data);
if (modifyOtherKeys) {
return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(
modifyOtherKeys.codepoint,
modifyOtherKeys.modifier,
SHIFTED_SYMBOL_KEYS,
);
return formatParsedKey(
logicalShiftedSymbol?.codepoint ?? modifyOtherKeys.codepoint,
logicalShiftedSymbol?.modifier ?? modifyOtherKeys.modifier,
);
}
// Mode-aware legacy sequences