-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathcompletion.ts
More file actions
137 lines (121 loc) · 3.93 KB
/
completion.ts
File metadata and controls
137 lines (121 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* Shell tab-completion support for opencli.
*
* Provides:
* - Shell script generators for bash, zsh, and fish
* - Dynamic completion logic that returns candidates for the current cursor position
*/
import { getRegistry } from './registry.js';
import { CliError } from './errors.js';
// ── Dynamic completion logic ───────────────────────────────────────────────
/**
* Built-in (non-dynamic) top-level commands.
*/
const BUILTIN_COMMANDS = [
'list',
'validate',
'verify',
'explore',
'probe', // alias for explore
'synthesize',
'generate',
'cascade',
'doctor',
'setup',
'completion',
];
/**
* Return completion candidates given the current command-line words and cursor index.
*
* @param words - The argv after 'opencli' (words[0] is the first arg, e.g. site name)
* @param cursor - 1-based position of the word being completed (1 = first arg)
*/
export function getCompletions(words: string[], cursor: number): string[] {
const registry = [...getRegistry().values()];
const externalAliases = new Set<string>();
for (const cmd of registry) {
if (cmd.execution === 'external-binary') {
for (const alias of cmd.aliases ?? []) externalAliases.add(alias);
}
}
// cursor === 1 → completing the first argument (site name or built-in command)
if (cursor <= 1) {
const sites = new Set<string>();
for (const cmd of registry) {
sites.add(cmd.site);
}
return [...BUILTIN_COMMANDS, ...sites, ...externalAliases].sort();
}
const site = words[0];
// If the first word is a built-in command, no further completion
if (BUILTIN_COMMANDS.includes(site) || externalAliases.has(site)) {
return [];
}
// cursor === 2 → completing the sub-command name under a site
if (cursor === 2) {
const subcommands: string[] = [];
for (const cmd of registry) {
if (cmd.site === site) {
subcommands.push(cmd.name);
}
}
return subcommands.sort();
}
// cursor >= 3 → no further completion
return [];
}
// ── Shell script generators ────────────────────────────────────────────────
export function bashCompletionScript(): string {
return `# Bash completion for opencli
# Add to ~/.bashrc: eval "$(opencli completion bash)"
_opencli_completions() {
local cur words cword
_get_comp_words_by_ref -n : cur words cword
local completions
completions=$(opencli --get-completions --cursor "$cword" "\${words[@]:1}" 2>/dev/null)
COMPREPLY=( $(compgen -W "$completions" -- "$cur") )
__ltrim_colon_completions "$cur"
}
complete -F _opencli_completions opencli
`;
}
export function zshCompletionScript(): string {
return `# Zsh completion for opencli
# Add to ~/.zshrc: eval "$(opencli completion zsh)"
_opencli() {
local -a completions
local cword=$((CURRENT - 1))
completions=(\${(f)"$(opencli --get-completions --cursor "$cword" "\${words[@]:1}" 2>/dev/null)"})
compadd -a completions
}
compdef _opencli opencli
`;
}
export function fishCompletionScript(): string {
return `# Fish completion for opencli
# Add to ~/.config/fish/config.fish: opencli completion fish | source
complete -c opencli -f -a '(
set -l tokens (commandline -cop)
set -l cursor (count (commandline -cop))
opencli --get-completions --cursor $cursor $tokens[2..] 2>/dev/null
)'
`;
}
/**
* Print the completion script for the requested shell.
*/
export function printCompletionScript(shell: string): void {
switch (shell) {
case 'bash':
process.stdout.write(bashCompletionScript());
break;
case 'zsh':
process.stdout.write(zshCompletionScript());
break;
case 'fish':
process.stdout.write(fishCompletionScript());
break;
default:
throw new CliError('UNSUPPORTED_SHELL', `Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
}
}