chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
|
||||
import type { KeyBinding } from '../packages/cli/src/ui/key/keyBindings.js';
|
||||
import {
|
||||
commandCategories,
|
||||
commandDescriptions,
|
||||
defaultKeyBindingConfig,
|
||||
Command,
|
||||
getPlatformUndoBindings,
|
||||
getPlatformRedoBindings,
|
||||
} from '../packages/cli/src/ui/key/keyBindings.js';
|
||||
import {
|
||||
formatWithPrettier,
|
||||
injectBetweenMarkers,
|
||||
normalizeForCompare,
|
||||
} from './utils/autogen.js';
|
||||
|
||||
const START_MARKER = '<!-- KEYBINDINGS-AUTOGEN:START -->';
|
||||
const END_MARKER = '<!-- KEYBINDINGS-AUTOGEN:END -->';
|
||||
const OUTPUT_RELATIVE_PATH = ['docs', 'reference', 'keyboard-shortcuts.md'];
|
||||
|
||||
import { formatKeyBinding } from '../packages/cli/src/ui/key/keybindingUtils.js';
|
||||
|
||||
export interface KeybindingDocCommand {
|
||||
command: string;
|
||||
description: string;
|
||||
bindings: readonly KeyBinding[];
|
||||
}
|
||||
|
||||
export interface KeybindingDocSection {
|
||||
title: string;
|
||||
commands: readonly KeybindingDocCommand[];
|
||||
}
|
||||
|
||||
export async function main(argv = process.argv.slice(2)) {
|
||||
const checkOnly = argv.includes('--check');
|
||||
|
||||
const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
);
|
||||
const docPath = path.join(repoRoot, ...OUTPUT_RELATIVE_PATH);
|
||||
|
||||
const sections = buildDefaultDocSections();
|
||||
const generatedBlock = renderDocumentation(sections);
|
||||
const currentDoc = await readFile(docPath, 'utf8');
|
||||
const injectedDoc = injectBetweenMarkers({
|
||||
document: currentDoc,
|
||||
startMarker: START_MARKER,
|
||||
endMarker: END_MARKER,
|
||||
newContent: generatedBlock,
|
||||
paddingBefore: '\n\n',
|
||||
paddingAfter: '\n',
|
||||
});
|
||||
const updatedDoc = await formatWithPrettier(injectedDoc, docPath);
|
||||
|
||||
if (normalizeForCompare(updatedDoc) === normalizeForCompare(currentDoc)) {
|
||||
if (!checkOnly) {
|
||||
console.log('Keybinding documentation already up to date.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkOnly) {
|
||||
console.error(
|
||||
'Keybinding documentation is out of date. Run `npm run docs:keybindings` to regenerate.',
|
||||
);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
await writeFile(docPath, updatedDoc, 'utf8');
|
||||
console.log('Keybinding documentation regenerated.');
|
||||
}
|
||||
|
||||
export function buildDefaultDocSections(): readonly KeybindingDocSection[] {
|
||||
return commandCategories.map((category) => ({
|
||||
title: category.title,
|
||||
commands: category.commands.map((command) => {
|
||||
// For UNDO and REDO, we want to show all platform variants in the docs
|
||||
if (command === Command.UNDO) {
|
||||
return {
|
||||
command: command,
|
||||
description: commandDescriptions[command],
|
||||
bindings: getMergedPlatformBindings(getPlatformUndoBindings),
|
||||
};
|
||||
}
|
||||
if (command === Command.REDO) {
|
||||
return {
|
||||
command: command,
|
||||
description: commandDescriptions[command],
|
||||
bindings: getMergedPlatformBindings(getPlatformRedoBindings),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
command: command,
|
||||
description: commandDescriptions[command],
|
||||
bindings: defaultKeyBindingConfig.get(command) ?? [],
|
||||
};
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
function getMergedPlatformBindings(
|
||||
getBindings: (platform: string) => readonly KeyBinding[],
|
||||
): readonly KeyBinding[] {
|
||||
const win32 = getBindings('win32');
|
||||
const darwin = getBindings('darwin');
|
||||
const linux = getBindings('linux');
|
||||
|
||||
const all = [...win32, ...darwin, ...linux];
|
||||
const seen = new Set<string>();
|
||||
const unique: KeyBinding[] = [];
|
||||
|
||||
for (const b of all) {
|
||||
const key = `${b.name}-${b.ctrl}-${b.shift}-${b.alt}-${b.cmd}`;
|
||||
if (!seen.has(key)) {
|
||||
seen.add(key);
|
||||
unique.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
return unique;
|
||||
}
|
||||
|
||||
export function renderDocumentation(
|
||||
sections: readonly KeybindingDocSection[],
|
||||
): string {
|
||||
const renderedSections = sections.map((section) => {
|
||||
const rows = section.commands.map((command) => {
|
||||
const formattedBindings = formatBindings(command.bindings);
|
||||
const keysCell = formattedBindings.join('<br />');
|
||||
return `| \`${command.command}\` | ${command.description} | ${keysCell} |`;
|
||||
});
|
||||
|
||||
return [
|
||||
`#### ${section.title}`,
|
||||
'',
|
||||
'| Command | Action | Keys |',
|
||||
'| --- | --- | --- |',
|
||||
...rows,
|
||||
].join('\n');
|
||||
});
|
||||
|
||||
return renderedSections.join('\n\n');
|
||||
}
|
||||
|
||||
function formatBindings(bindings: readonly KeyBinding[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
const results: string[] = [];
|
||||
|
||||
for (const binding of bindings) {
|
||||
const label = formatKeyBinding(binding, 'default');
|
||||
if (label && !seen.has(label)) {
|
||||
seen.add(label);
|
||||
results.push(`\`${label}\``);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
if (process.argv[1]) {
|
||||
const entryUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
||||
if (entryUrl === import.meta.url) {
|
||||
await main();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user