|
| 1 | +import omelette from "omelette"; |
| 2 | + |
| 3 | +import { |
| 4 | + findCommand, |
| 5 | + getCategories, |
| 6 | + getCommandsByCategory, |
| 7 | +} from "./commands/registry.js"; |
| 8 | +import { OUTPUT_FORMATS } from "./output.js"; |
| 9 | +import { describeCommand } from "./parser.js"; |
| 10 | +import { FLAG_DEFS } from "./router.js"; |
| 11 | +import { COMMAND_NAME, KEYS_SUBCOMMANDS } from "./utils/command-info.js"; |
| 12 | + |
| 13 | +export const COMPLETION_SUBCOMMANDS = ["install", "uninstall"] as const; |
| 14 | + |
| 15 | +const SPECIAL_CATEGORIES: Record<string, readonly string[]> = { |
| 16 | + keys: KEYS_SUBCOMMANDS, |
| 17 | + completion: COMPLETION_SUBCOMMANDS, |
| 18 | +}; |
| 19 | + |
| 20 | +const topLevelCompletions = [ |
| 21 | + ...getCategories(), |
| 22 | + ...Object.keys(SPECIAL_CATEGORIES), |
| 23 | +]; |
| 24 | + |
| 25 | +const globalFlagCompletions = FLAG_DEFS.flatMap((d) => d.aliases); |
| 26 | + |
| 27 | +const completion = omelette(COMMAND_NAME); |
| 28 | + |
| 29 | +completion.on("complete", (_fragment, { before, reply, line }) => { |
| 30 | + const parts = line.trim().split(" ").slice(1); |
| 31 | + // Trailing space means the current word is complete — user wants next arg |
| 32 | + const depth = line.endsWith(" ") ? parts.length + 1 : parts.length; |
| 33 | + const [category, action] = parts; |
| 34 | + |
| 35 | + if (depth <= 1) { |
| 36 | + reply([...topLevelCompletions, "--help", "--version"]); |
| 37 | + return; |
| 38 | + } |
| 39 | + |
| 40 | + if (depth === 2) { |
| 41 | + const special = category ? SPECIAL_CATEGORIES[category] : undefined; |
| 42 | + if (special) { |
| 43 | + reply([...special]); |
| 44 | + return; |
| 45 | + } |
| 46 | + if (category) { |
| 47 | + reply([...getCommandsByCategory(category).map((c) => c.name), "--help"]); |
| 48 | + return; |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + if (category && action) { |
| 53 | + if (before === "--output") { |
| 54 | + reply([...OUTPUT_FORMATS]); |
| 55 | + return; |
| 56 | + } |
| 57 | + |
| 58 | + if (before === "--key") { |
| 59 | + import("./key-manager.js") |
| 60 | + .then(({ getKeyManager }) => getKeyManager().listKeys()) |
| 61 | + .then((keys) => reply(keys.map((k) => k.name))) |
| 62 | + .catch(() => reply([])); |
| 63 | + return; |
| 64 | + } |
| 65 | + |
| 66 | + const cmd = findCommand(category, action); |
| 67 | + if (cmd) { |
| 68 | + const cmdFlags = describeCommand(cmd).map((f) => `--${f.name}`); |
| 69 | + reply([...cmdFlags, ...globalFlagCompletions]); |
| 70 | + return; |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + reply([]); |
| 75 | +}); |
| 76 | + |
| 77 | +export { completion }; |
0 commit comments