73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import type { Command, Option } from "commander"
|
|
|
|
/** Generate fish completions dynamically from the Commander program tree. */
|
|
export function action(program: Command) {
|
|
const lines: string[] = [
|
|
"# Fish completions for sandlot (auto-generated)",
|
|
"# Install: sandlot completions > ~/.config/fish/completions/sandlot.fish",
|
|
"",
|
|
"complete -c sandlot -f",
|
|
"",
|
|
"function __sandlot_sessions",
|
|
` command sandlot list --json 2>/dev/null | string match -r '"branch":\\s*"[^"]+"' | string replace -r '.*"branch":\\s*"([^"]+)".*' '$1'`,
|
|
"end",
|
|
"",
|
|
]
|
|
|
|
const branchCommands: string[] = []
|
|
|
|
for (const cmd of program.commands) {
|
|
const name = cmd.name()
|
|
const desc = cmd.description()
|
|
const subs = cmd.commands as Command[]
|
|
|
|
if (subs.length > 0) {
|
|
// Parent command (e.g. "vm") — register it plus its subcommands
|
|
lines.push(`complete -c sandlot -n __fish_use_subcommand -a ${name} -d ${esc(desc)}`)
|
|
const subNames = subs.filter(s => !(s as any)._hidden).map(s => s.name())
|
|
const guard = `"__fish_seen_subcommand_from ${name}; and not __fish_seen_subcommand_from ${subNames.join(" ")}"`
|
|
for (const sub of subs) {
|
|
if ((sub as any)._hidden) continue
|
|
lines.push(`complete -c sandlot -n ${guard} -a ${sub.name()} -d ${esc(sub.description())}`)
|
|
}
|
|
// Options on subcommands
|
|
for (const sub of subs) {
|
|
if ((sub as any)._hidden) continue
|
|
emitOptions(lines, `${name} ${sub.name()}`, sub.options)
|
|
}
|
|
} else {
|
|
// Leaf command
|
|
lines.push(`complete -c sandlot -n __fish_use_subcommand -a ${name} -d ${esc(desc)}`)
|
|
emitOptions(lines, name, cmd.options)
|
|
|
|
// Track commands that accept a <branch> / [branch] argument
|
|
const hasBranch = cmd.registeredArguments.some(a => a.name() === "branch")
|
|
if (hasBranch) branchCommands.push(name)
|
|
}
|
|
}
|
|
|
|
// Session completions for all branch-taking commands
|
|
if (branchCommands.length > 0) {
|
|
lines.push("")
|
|
lines.push(`complete -c sandlot -n "__fish_seen_subcommand_from ${branchCommands.join(" ")}" -xa "(__sandlot_sessions)"`)
|
|
}
|
|
|
|
lines.push("")
|
|
process.stdout.write(lines.join("\n") + "\n")
|
|
}
|
|
|
|
function esc(s: string): string {
|
|
return `"${s.replace(/"/g, '\\"')}"`
|
|
}
|
|
|
|
function emitOptions(lines: string[], cmdName: string, options: readonly Option[]) {
|
|
for (const opt of options) {
|
|
const parts = [`complete -c sandlot -n "__fish_seen_subcommand_from ${cmdName}"`]
|
|
if (opt.short) parts.push(`-s ${opt.short.replace("-", "")}`)
|
|
if (opt.long) parts.push(`-l ${opt.long.replace("--", "")}`)
|
|
parts.push(`-d ${esc(opt.description)}`)
|
|
if (opt.required) parts.push("-r")
|
|
lines.push(parts.join(" "))
|
|
}
|
|
}
|