mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 07:33:46 -06:00
f66fff57f3
Co-authored-by: Peter Steinberger <steipete@macos.shared>
94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import type { Command, Option } from "commander";
|
|
|
|
type ShellCompletionValueChoice = {
|
|
flags: string[];
|
|
choices: string[];
|
|
requiresValue: boolean;
|
|
};
|
|
|
|
export type ShellCompletionContext = {
|
|
command: Command;
|
|
pathVariants: string[][];
|
|
completions: string[];
|
|
valueOptions: string[];
|
|
valueChoices: ShellCompletionValueChoice[];
|
|
};
|
|
|
|
type ShellCompletionCommandTree = {
|
|
root: ShellCompletionContext;
|
|
descendants: ShellCompletionContext[];
|
|
};
|
|
|
|
function completionFlags(option: Option): string[] {
|
|
return [option.short, option.long].filter((flag): flag is string => Boolean(flag));
|
|
}
|
|
|
|
function commandNameVariants(command: Command): string[] {
|
|
return [command.name(), ...command.aliases()];
|
|
}
|
|
|
|
export function collectShellCompletionCommandTree(program: Command): ShellCompletionCommandTree {
|
|
const descendants: ShellCompletionContext[] = [];
|
|
|
|
const visit = (
|
|
command: Command,
|
|
pathVariants: string[][],
|
|
inheritedValueOptions: readonly string[],
|
|
inheritedValueChoices: readonly ShellCompletionValueChoice[],
|
|
): ShellCompletionContext => {
|
|
const ownOptionFlags = new Set(command.options.flatMap(completionFlags));
|
|
const context: ShellCompletionContext = {
|
|
command,
|
|
pathVariants,
|
|
completions: [
|
|
...command.commands.flatMap(commandNameVariants),
|
|
...command.options.flatMap(completionFlags),
|
|
],
|
|
valueOptions: [
|
|
...new Set([
|
|
...inheritedValueOptions,
|
|
...command.options.flatMap((option) =>
|
|
option.required || option.optional ? completionFlags(option) : [],
|
|
),
|
|
]),
|
|
],
|
|
valueChoices: [
|
|
...inheritedValueChoices.flatMap(({ flags, ...choice }) => {
|
|
const inheritedFlags = flags.filter((flag) => !ownOptionFlags.has(flag));
|
|
return inheritedFlags.length > 0 ? [{ flags: inheritedFlags, ...choice }] : [];
|
|
}),
|
|
...command.options.flatMap((option) =>
|
|
option.argChoices?.length
|
|
? [
|
|
{
|
|
flags: completionFlags(option),
|
|
choices: [...option.argChoices],
|
|
requiresValue: option.required,
|
|
},
|
|
]
|
|
: [],
|
|
),
|
|
],
|
|
};
|
|
|
|
if (pathVariants[0]?.length) {
|
|
descendants.push(context);
|
|
}
|
|
|
|
for (const child of command.commands) {
|
|
visit(
|
|
child,
|
|
pathVariants.flatMap((parents) =>
|
|
commandNameVariants(child).map((name) => parents.concat(name)),
|
|
),
|
|
context.valueOptions,
|
|
context.valueChoices,
|
|
);
|
|
}
|
|
|
|
return context;
|
|
};
|
|
|
|
return { root: visit(program, [[]], [], []), descendants };
|
|
}
|