import { RootCommand, type Complete } from "@bomb.sh/tab"; import { COMMAND_MANIFEST, type CommandCompleter } from "../command-manifest.ts"; import { loadConfig } from "../config.ts"; import { AMEND_AREA_IDS } from "../setup.ts"; export const COMPLETION_SHELLS = ["zsh", "bash", "fish", "powershell"] as const; export type CompletionShell = (typeof COMPLETION_SHELLS)[number]; function suggestions(kind: CommandCompleter, complete: Complete): void { try { if (kind === "project") { for (const project of loadConfig().projects) complete(project.name, "configured project"); return; } if (kind === "area") { for (const area of AMEND_AREA_IDS) complete(area, "setup area"); return; } for (const shell of COMPLETION_SHELLS) complete(shell, "supported shell"); } catch { // Completion runs on every TAB press. A missing or invalid config must make // completion empty, never turn an interactive shell into an error surface. } } export function completionRoot(): RootCommand { const root = new RootCommand(); for (const entry of COMMAND_MANIFEST) { const command = root.command(entry.name, entry.description); for (const flag of entry.flags) { const name = flag.name.replace(/^--/, ""); const alias = flag.alias?.replace(/^-+/, ""); const completer = flag.completer; const complete = completer === undefined ? undefined : ((add: Complete) => suggestions(completer, add)); if (flag.takesValue) { command.option(name, flag.description, complete ?? (() => undefined), alias); } else if (alias !== undefined) { command.option(name, flag.description, alias); } else { command.option(name, flag.description); } } for (const [index, positional] of (entry.positionals ?? []).entries()) { const subcommands = index === 0 ? entry.subcommands : undefined; command.argument( positional.name, positional.completer === undefined && subcommands === undefined ? undefined : (complete) => { for (const subcommand of subcommands ?? []) { complete(subcommand.name, subcommand.description); } if (positional.completer !== undefined) suggestions(positional.completer, complete); }, positional.variadic, ); } } return root; } function shellFrom(value: string | undefined): CompletionShell { if ((COMPLETION_SHELLS as readonly string[]).includes(value ?? "")) { return value as CompletionShell; } throw new Error(`complete expects one of: ${COMPLETION_SHELLS.join(", ")}`); } export function printCompletionScript(shell: CompletionShell): void { completionRoot().setup("omp-conductor", "omp-conductor", shell); } export function renderCompletionScript(shell: CompletionShell): string { const lines: string[] = []; const original = console.log; console.log = (...args: unknown[]) => lines.push(args.map(String).join(" ")); try { printCompletionScript(shell); } finally { console.log = original; } return `${lines.join("\n")}\n`; } export function completeCommand(args: string[]): void { if (args[0] === "--") { const request = args[1] === "omp-conductor" ? args.slice(2) : args.slice(1); completionRoot().parse(request); return; } printCompletionScript(shellFrom(args[0])); }