@optique/core 0.5.0 → 0.6.0-dev.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/completion.cjs +513 -0
- package/dist/completion.d.cts +51 -0
- package/dist/completion.d.ts +51 -0
- package/dist/completion.js +510 -0
- package/dist/constructs.cjs +140 -0
- package/dist/constructs.js +140 -0
- package/dist/doc.cjs +1 -0
- package/dist/doc.js +1 -0
- package/dist/facade.cjs +115 -37
- package/dist/facade.d.cts +26 -0
- package/dist/facade.d.ts +26 -0
- package/dist/facade.js +117 -39
- package/dist/index.cjs +7 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/modifiers.cjs +24 -0
- package/dist/modifiers.js +24 -0
- package/dist/parser.cjs +46 -0
- package/dist/parser.d.cts +88 -1
- package/dist/parser.d.ts +88 -1
- package/dist/parser.js +46 -1
- package/dist/primitives.cjs +92 -0
- package/dist/primitives.js +92 -0
- package/dist/valueparser.cjs +249 -0
- package/dist/valueparser.d.cts +10 -0
- package/dist/valueparser.d.ts +10 -0
- package/dist/valueparser.js +249 -0
- package/package.json +9 -1
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import { formatMessage } from "./message.js";
|
|
2
|
+
|
|
3
|
+
//#region src/completion.ts
|
|
4
|
+
/**
|
|
5
|
+
* The Bash shell completion generator.
|
|
6
|
+
* @since 0.6.0
|
|
7
|
+
*/
|
|
8
|
+
const bash = {
|
|
9
|
+
name: "bash",
|
|
10
|
+
generateScript(programName, args = []) {
|
|
11
|
+
const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).join(" ");
|
|
12
|
+
return `
|
|
13
|
+
function _${programName} () {
|
|
14
|
+
COMPREPLY=()
|
|
15
|
+
local current="\${COMP_WORDS[COMP_CWORD]}"
|
|
16
|
+
local prev=("\${COMP_WORDS[@]:1:COMP_CWORD-1}")
|
|
17
|
+
while IFS= read -r line; do
|
|
18
|
+
if [[ "$line" == __FILE__:* ]]; then
|
|
19
|
+
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
20
|
+
IFS=':' read -r _ type extensions pattern hidden <<< "$line"
|
|
21
|
+
|
|
22
|
+
# Generate file completions based on type
|
|
23
|
+
case "$type" in
|
|
24
|
+
file)
|
|
25
|
+
# Complete files only
|
|
26
|
+
if [[ -n "$extensions" ]]; then
|
|
27
|
+
# Complete with extension filtering
|
|
28
|
+
local ext_pattern="\${extensions//,/|}"
|
|
29
|
+
for file in "$current"*; do
|
|
30
|
+
[[ -e "$file" && "$file" =~ \\.($ext_pattern)$ ]] && COMPREPLY+=("$file")
|
|
31
|
+
done
|
|
32
|
+
else
|
|
33
|
+
# Complete files only, exclude directories
|
|
34
|
+
while IFS= read -r -d '' item; do
|
|
35
|
+
[[ -f "$item" ]] && COMPREPLY+=("$item")
|
|
36
|
+
done < <(compgen -f -z -- "$current")
|
|
37
|
+
fi
|
|
38
|
+
;;
|
|
39
|
+
directory)
|
|
40
|
+
# Complete directories only
|
|
41
|
+
while IFS= read -r -d '' dir; do
|
|
42
|
+
COMPREPLY+=("$dir/")
|
|
43
|
+
done < <(compgen -d -z -- "$current")
|
|
44
|
+
;;
|
|
45
|
+
any)
|
|
46
|
+
# Complete both files and directories
|
|
47
|
+
if [[ -n "$extensions" ]]; then
|
|
48
|
+
# Files with extension filtering + directories
|
|
49
|
+
# Files with extension filtering
|
|
50
|
+
local ext_pattern="\${extensions//,/|}"
|
|
51
|
+
for item in "$current"*; do
|
|
52
|
+
if [[ -d "$item" ]]; then
|
|
53
|
+
COMPREPLY+=("$item/")
|
|
54
|
+
elif [[ -f "$item" && "$item" =~ \\.($ext_pattern)$ ]]; then
|
|
55
|
+
COMPREPLY+=("$item")
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
else
|
|
59
|
+
# Complete files and directories, add slash to directories
|
|
60
|
+
while IFS= read -r -d '' item; do
|
|
61
|
+
if [[ -d "$item" ]]; then
|
|
62
|
+
COMPREPLY+=("$item/")
|
|
63
|
+
else
|
|
64
|
+
COMPREPLY+=("$item")
|
|
65
|
+
fi
|
|
66
|
+
done < <(compgen -f -z -- "$current")
|
|
67
|
+
fi
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
|
|
71
|
+
# Filter out hidden files unless requested
|
|
72
|
+
if [[ "$hidden" != "1" && "$current" != .* ]]; then
|
|
73
|
+
local filtered=()
|
|
74
|
+
for item in "\${COMPREPLY[@]}"; do
|
|
75
|
+
[[ "$(basename "$item")" != .* ]] && filtered+=("$item")
|
|
76
|
+
done
|
|
77
|
+
COMPREPLY=("\${filtered[@]}")
|
|
78
|
+
fi
|
|
79
|
+
else
|
|
80
|
+
# Regular literal completion
|
|
81
|
+
COMPREPLY+=("$line")
|
|
82
|
+
fi
|
|
83
|
+
done < <(${programName} ${escapedArgs} "\${prev[@]}" "$current" 2>/dev/null)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
complete -F _${programName} ${programName}
|
|
87
|
+
`;
|
|
88
|
+
},
|
|
89
|
+
*encodeSuggestions(suggestions) {
|
|
90
|
+
let i = 0;
|
|
91
|
+
for (const suggestion of suggestions) {
|
|
92
|
+
if (i > 0) yield "\n";
|
|
93
|
+
if (suggestion.kind === "literal") yield `${suggestion.text}`;
|
|
94
|
+
else {
|
|
95
|
+
const extensions = suggestion.extensions?.join(",") || "";
|
|
96
|
+
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
97
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}`;
|
|
98
|
+
}
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The Zsh shell completion generator.
|
|
105
|
+
* @since 0.6.0
|
|
106
|
+
*/
|
|
107
|
+
const zsh = {
|
|
108
|
+
name: "zsh",
|
|
109
|
+
generateScript(programName, args = []) {
|
|
110
|
+
const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).join(" ");
|
|
111
|
+
return `
|
|
112
|
+
function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
113
|
+
local current="\$words[CURRENT]"
|
|
114
|
+
local -a prev
|
|
115
|
+
# Extract previous arguments, skipping empty ones
|
|
116
|
+
prev=()
|
|
117
|
+
local i
|
|
118
|
+
for (( i=2; i < CURRENT; i++ )); do
|
|
119
|
+
if [[ -n "\$words[i]" ]]; then
|
|
120
|
+
prev+=("\$words[i]")
|
|
121
|
+
fi
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
# Call the completion function and capture output
|
|
125
|
+
local output
|
|
126
|
+
if (( \${#prev[@]} == 0 )); then
|
|
127
|
+
output=\$(${programName} ${escapedArgs} "\$current" 2>/dev/null)
|
|
128
|
+
else
|
|
129
|
+
output=\$(${programName} ${escapedArgs} "\${prev[@]}" "\$current" 2>/dev/null)
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Split output into lines and process each line
|
|
133
|
+
local -a completions descriptions
|
|
134
|
+
local line value desc
|
|
135
|
+
local has_file_completion=0
|
|
136
|
+
|
|
137
|
+
while IFS= read -r line; do
|
|
138
|
+
if [[ -n "\$line" ]]; then
|
|
139
|
+
# Split by null character - first part is value, second is description
|
|
140
|
+
value=\${line%%\$'\\0'*}
|
|
141
|
+
desc=\${line#*\$'\\0'}
|
|
142
|
+
desc=\${desc%%\$'\\0'*}
|
|
143
|
+
|
|
144
|
+
if [[ "\$value" == __FILE__:* ]]; then
|
|
145
|
+
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
146
|
+
local type extensions pattern hidden
|
|
147
|
+
IFS=':' read -r _ type extensions pattern hidden <<< "\$value"
|
|
148
|
+
has_file_completion=1
|
|
149
|
+
|
|
150
|
+
# Use zsh's native file completion
|
|
151
|
+
case "\$type" in
|
|
152
|
+
file)
|
|
153
|
+
if [[ -n "\$extensions" ]]; then
|
|
154
|
+
# Complete files with extension filtering
|
|
155
|
+
local ext_pattern="*.(\\$\{extensions//,/|\})"
|
|
156
|
+
_files -g "\\$ext_pattern"
|
|
157
|
+
else
|
|
158
|
+
_files -g "*"
|
|
159
|
+
fi
|
|
160
|
+
;;
|
|
161
|
+
directory)
|
|
162
|
+
_directories
|
|
163
|
+
;;
|
|
164
|
+
any)
|
|
165
|
+
if [[ -n "\$extensions" ]]; then
|
|
166
|
+
# Complete both files and directories, with extension filtering for files
|
|
167
|
+
local ext_pattern="*.(\\$\{extensions//,/|\})"
|
|
168
|
+
_files -g "\\$ext_pattern" && _directories
|
|
169
|
+
else
|
|
170
|
+
_files
|
|
171
|
+
fi
|
|
172
|
+
;;
|
|
173
|
+
esac
|
|
174
|
+
|
|
175
|
+
# Note: zsh's _files and _directories handle hidden file filtering automatically
|
|
176
|
+
# based on the completion context and user settings
|
|
177
|
+
else
|
|
178
|
+
# Regular literal completion
|
|
179
|
+
if [[ -n "\$value" ]]; then
|
|
180
|
+
completions+=("\$value")
|
|
181
|
+
descriptions+=("\$desc")
|
|
182
|
+
fi
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
done <<< "\$output"
|
|
186
|
+
|
|
187
|
+
# Add literal completions with descriptions if we have any
|
|
188
|
+
if (( \${#completions[@]} > 0 )); then
|
|
189
|
+
# Prepare completion with descriptions for _describe
|
|
190
|
+
local -a matches
|
|
191
|
+
local -i i
|
|
192
|
+
for (( i=1; i <= \${#completions[@]}; i++ )); do
|
|
193
|
+
if [[ -n "\${descriptions[i]}" ]]; then
|
|
194
|
+
matches+=("\${completions[i]}:\${descriptions[i]}")
|
|
195
|
+
else
|
|
196
|
+
matches+=("\${completions[i]}")
|
|
197
|
+
fi
|
|
198
|
+
done
|
|
199
|
+
_describe 'commands' matches
|
|
200
|
+
fi
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
|
|
204
|
+
`;
|
|
205
|
+
},
|
|
206
|
+
*encodeSuggestions(suggestions) {
|
|
207
|
+
for (const suggestion of suggestions) if (suggestion.kind === "literal") {
|
|
208
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
209
|
+
yield `${suggestion.text}\0${description}\0`;
|
|
210
|
+
} else {
|
|
211
|
+
const extensions = suggestion.extensions?.join(",") || "";
|
|
212
|
+
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
213
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
214
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}\0${description}\0`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* The fish shell completion generator.
|
|
220
|
+
* @since 0.6.0
|
|
221
|
+
*/
|
|
222
|
+
const fish = {
|
|
223
|
+
name: "fish",
|
|
224
|
+
generateScript(programName, args = []) {
|
|
225
|
+
const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "\\'")}'`).join(" ");
|
|
226
|
+
const functionName = `__${programName.replace(/[^a-zA-Z0-9]/g, "_")}_complete`;
|
|
227
|
+
return `
|
|
228
|
+
function ${functionName}
|
|
229
|
+
set -l tokens (commandline -poc)
|
|
230
|
+
set -l current (commandline -ct)
|
|
231
|
+
|
|
232
|
+
# Extract previous arguments (skip the command name)
|
|
233
|
+
set -l prev
|
|
234
|
+
set -l count (count $tokens)
|
|
235
|
+
if test $count -gt 1
|
|
236
|
+
set prev $tokens[2..$count]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Call completion command and capture output
|
|
240
|
+
${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current 2>/dev/null)\n` : ` set -l output (${programName} $prev $current 2>/dev/null)\n`}
|
|
241
|
+
# Process each line of output
|
|
242
|
+
for line in $output
|
|
243
|
+
if string match -q '__FILE__:*' -- $line
|
|
244
|
+
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
245
|
+
set -l parts (string split ':' -- $line)
|
|
246
|
+
set -l type $parts[2]
|
|
247
|
+
set -l extensions $parts[3]
|
|
248
|
+
set -l pattern $parts[4]
|
|
249
|
+
set -l hidden $parts[5]
|
|
250
|
+
|
|
251
|
+
# Generate file completions based on type
|
|
252
|
+
set -l items
|
|
253
|
+
switch $type
|
|
254
|
+
case file
|
|
255
|
+
# Complete files only
|
|
256
|
+
for item in $current*
|
|
257
|
+
if test -f $item
|
|
258
|
+
set -a items $item
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
case directory
|
|
262
|
+
# Complete directories only
|
|
263
|
+
for item in $current*
|
|
264
|
+
if test -d $item
|
|
265
|
+
set -a items $item/
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
case any
|
|
269
|
+
# Complete both files and directories
|
|
270
|
+
for item in $current*
|
|
271
|
+
if test -d $item
|
|
272
|
+
set -a items $item/
|
|
273
|
+
else if test -f $item
|
|
274
|
+
set -a items $item
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Filter by extensions if specified
|
|
280
|
+
if test -n "$extensions" -a "$type" != directory
|
|
281
|
+
set -l filtered
|
|
282
|
+
set -l ext_list (string split ',' -- $extensions)
|
|
283
|
+
for item in $items
|
|
284
|
+
# Skip directories, they don't have extensions
|
|
285
|
+
if string match -q '*/' -- $item
|
|
286
|
+
set -a filtered $item
|
|
287
|
+
continue
|
|
288
|
+
end
|
|
289
|
+
# Check if file matches any extension
|
|
290
|
+
for ext in $ext_list
|
|
291
|
+
if string match -q "*.$ext" -- $item
|
|
292
|
+
set -a filtered $item
|
|
293
|
+
break
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
set items $filtered
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Filter out hidden files unless requested
|
|
301
|
+
if test "$hidden" != "1" -a (string sub -l 1 -- $current) != "."
|
|
302
|
+
set -l filtered
|
|
303
|
+
for item in $items
|
|
304
|
+
set -l basename (basename $item)
|
|
305
|
+
if not string match -q '.*' -- $basename
|
|
306
|
+
set -a filtered $item
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
set items $filtered
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Output file completions
|
|
313
|
+
for item in $items
|
|
314
|
+
echo $item
|
|
315
|
+
end
|
|
316
|
+
else
|
|
317
|
+
# Regular literal completion - split by tab
|
|
318
|
+
set -l parts (string split \\t -- $line)
|
|
319
|
+
if test (count $parts) -ge 2
|
|
320
|
+
# value\tdescription format
|
|
321
|
+
echo $parts[1]\\t$parts[2]
|
|
322
|
+
else
|
|
323
|
+
# Just value
|
|
324
|
+
echo $line
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
complete -c ${programName} -f -a '(${functionName})'
|
|
331
|
+
`;
|
|
332
|
+
},
|
|
333
|
+
*encodeSuggestions(suggestions) {
|
|
334
|
+
let i = 0;
|
|
335
|
+
for (const suggestion of suggestions) {
|
|
336
|
+
if (i > 0) yield "\n";
|
|
337
|
+
if (suggestion.kind === "literal") {
|
|
338
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
339
|
+
yield `${suggestion.text}\t${description}`;
|
|
340
|
+
} else {
|
|
341
|
+
const extensions = suggestion.extensions?.join(",") || "";
|
|
342
|
+
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
343
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
344
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}\t${description}`;
|
|
345
|
+
}
|
|
346
|
+
i++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* The PowerShell completion generator.
|
|
352
|
+
* @since 0.6.0
|
|
353
|
+
*/
|
|
354
|
+
const pwsh = {
|
|
355
|
+
name: "pwsh",
|
|
356
|
+
generateScript(programName, args = []) {
|
|
357
|
+
const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "''")}'`).join(", ");
|
|
358
|
+
return `
|
|
359
|
+
Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
360
|
+
param(\$wordToComplete, \$commandAst, \$cursorPosition)
|
|
361
|
+
|
|
362
|
+
# Extract arguments from AST (handles quoted strings properly)
|
|
363
|
+
\$arguments = @()
|
|
364
|
+
\$commandElements = \$commandAst.CommandElements
|
|
365
|
+
|
|
366
|
+
# Determine the range of elements to extract
|
|
367
|
+
# Exclude the last element if it matches wordToComplete (partial input case)
|
|
368
|
+
\$maxIndex = \$commandElements.Count - 1
|
|
369
|
+
if (\$commandElements.Count -gt 1) {
|
|
370
|
+
\$lastElement = \$commandElements[\$commandElements.Count - 1]
|
|
371
|
+
\$lastText = if (\$lastElement -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
|
|
372
|
+
\$lastElement.Value
|
|
373
|
+
} else {
|
|
374
|
+
\$lastElement.Extent.Text
|
|
375
|
+
}
|
|
376
|
+
if (\$lastText -eq \$wordToComplete) {
|
|
377
|
+
\$maxIndex = \$commandElements.Count - 2
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for (\$i = 1; \$i -le \$maxIndex; \$i++) {
|
|
382
|
+
\$element = \$commandElements[\$i]
|
|
383
|
+
|
|
384
|
+
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
|
|
385
|
+
\$arguments += \$element.Value
|
|
386
|
+
} else {
|
|
387
|
+
\$arguments += \$element.Extent.Text
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
# Build arguments array for completion command
|
|
392
|
+
\$completionArgs = @()
|
|
393
|
+
${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
394
|
+
` : ""} \$completionArgs += \$arguments
|
|
395
|
+
\$completionArgs += \$wordToComplete
|
|
396
|
+
|
|
397
|
+
# Call completion command and capture output
|
|
398
|
+
try {
|
|
399
|
+
\$output = & ${programName} \$completionArgs 2>\$null
|
|
400
|
+
if (-not \$output) { return }
|
|
401
|
+
|
|
402
|
+
# Parse tab-separated output and create CompletionResult objects
|
|
403
|
+
\$output -split "\`n" | ForEach-Object {
|
|
404
|
+
\$line = \$_.Trim()
|
|
405
|
+
if (-not \$line) { return }
|
|
406
|
+
|
|
407
|
+
if (\$line -match '^__FILE__:') {
|
|
408
|
+
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
409
|
+
\$parts = \$line -split ':', 5
|
|
410
|
+
\$type = \$parts[1]
|
|
411
|
+
\$extensions = \$parts[2]
|
|
412
|
+
\$pattern = \$parts[3]
|
|
413
|
+
\$hidden = \$parts[4] -eq '1'
|
|
414
|
+
|
|
415
|
+
# Determine current prefix for file matching
|
|
416
|
+
\$prefix = if (\$wordToComplete) { \$wordToComplete } else { '' }
|
|
417
|
+
|
|
418
|
+
# Get file system items based on type
|
|
419
|
+
\$items = @()
|
|
420
|
+
switch (\$type) {
|
|
421
|
+
'file' {
|
|
422
|
+
if (\$extensions) {
|
|
423
|
+
# Filter by extensions
|
|
424
|
+
\$extList = \$extensions -split ','
|
|
425
|
+
\$items = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
426
|
+
Where-Object {
|
|
427
|
+
\$ext = \$_.Extension
|
|
428
|
+
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
\$items = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
'directory' {
|
|
435
|
+
\$items = Get-ChildItem -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
436
|
+
}
|
|
437
|
+
'any' {
|
|
438
|
+
if (\$extensions) {
|
|
439
|
+
# Get directories and filtered files
|
|
440
|
+
\$dirs = Get-ChildItem -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
441
|
+
\$extList = \$extensions -split ','
|
|
442
|
+
\$files = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
443
|
+
Where-Object {
|
|
444
|
+
\$ext = \$_.Extension
|
|
445
|
+
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
446
|
+
}
|
|
447
|
+
\$items = \$dirs + \$files
|
|
448
|
+
} else {
|
|
449
|
+
\$items = Get-ChildItem -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# Filter hidden files unless requested
|
|
455
|
+
if (-not \$hidden) {
|
|
456
|
+
\$items = \$items | Where-Object { -not \$_.Attributes.HasFlag([System.IO.FileAttributes]::Hidden) }
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
# Create completion results for files
|
|
460
|
+
\$items | ForEach-Object {
|
|
461
|
+
\$completionText = if (\$_.PSIsContainer) { "\$(\$_.Name)/" } else { \$_.Name }
|
|
462
|
+
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
463
|
+
[System.Management.Automation.CompletionResult]::new(
|
|
464
|
+
\$completionText,
|
|
465
|
+
\$completionText,
|
|
466
|
+
'ParameterValue',
|
|
467
|
+
\$itemType
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
# Parse literal completion: text\\tlistItemText\\tdescription
|
|
472
|
+
\$parts = \$line -split "\`t", 3
|
|
473
|
+
\$completionText = \$parts[0]
|
|
474
|
+
\$listItemText = if (\$parts.Length -gt 1 -and \$parts[1]) { \$parts[1] } else { \$completionText }
|
|
475
|
+
\$toolTip = if (\$parts.Length -gt 2 -and \$parts[2]) { \$parts[2] } else { \$completionText }
|
|
476
|
+
|
|
477
|
+
[System.Management.Automation.CompletionResult]::new(
|
|
478
|
+
\$completionText,
|
|
479
|
+
\$listItemText,
|
|
480
|
+
'ParameterValue',
|
|
481
|
+
\$toolTip
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
# Silently ignore errors
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
`;
|
|
490
|
+
},
|
|
491
|
+
*encodeSuggestions(suggestions) {
|
|
492
|
+
let i = 0;
|
|
493
|
+
for (const suggestion of suggestions) {
|
|
494
|
+
if (i > 0) yield "\n";
|
|
495
|
+
if (suggestion.kind === "literal") {
|
|
496
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
497
|
+
yield `${suggestion.text}\t${suggestion.text}\t${description}`;
|
|
498
|
+
} else {
|
|
499
|
+
const extensions = suggestion.extensions?.join(",") || "";
|
|
500
|
+
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
501
|
+
const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
|
|
502
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}\t[file]\t${description}`;
|
|
503
|
+
}
|
|
504
|
+
i++;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
//#endregion
|
|
510
|
+
export { bash, fish, pwsh, zsh };
|
package/dist/constructs.cjs
CHANGED
|
@@ -74,6 +74,33 @@ function or(...args) {
|
|
|
74
74
|
success: false
|
|
75
75
|
};
|
|
76
76
|
},
|
|
77
|
+
suggest(context, prefix) {
|
|
78
|
+
const suggestions = [];
|
|
79
|
+
if (context.state == null) for (const parser of parsers) {
|
|
80
|
+
const parserSuggestions = parser.suggest({
|
|
81
|
+
...context,
|
|
82
|
+
state: parser.initialState
|
|
83
|
+
}, prefix);
|
|
84
|
+
suggestions.push(...parserSuggestions);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const [index, parserResult] = context.state;
|
|
88
|
+
if (parserResult.success) {
|
|
89
|
+
const parserSuggestions = parsers[index].suggest({
|
|
90
|
+
...context,
|
|
91
|
+
state: parserResult.next.state
|
|
92
|
+
}, prefix);
|
|
93
|
+
suggestions.push(...parserSuggestions);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const seen = /* @__PURE__ */ new Set();
|
|
97
|
+
return suggestions.filter((suggestion) => {
|
|
98
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
99
|
+
if (seen.has(key)) return false;
|
|
100
|
+
seen.add(key);
|
|
101
|
+
return true;
|
|
102
|
+
});
|
|
103
|
+
},
|
|
77
104
|
getDocFragments(state, _defaultValue) {
|
|
78
105
|
let description;
|
|
79
106
|
let fragments;
|
|
@@ -182,6 +209,33 @@ function longestMatch(...args) {
|
|
|
182
209
|
success: false
|
|
183
210
|
};
|
|
184
211
|
},
|
|
212
|
+
suggest(context, prefix) {
|
|
213
|
+
const suggestions = [];
|
|
214
|
+
if (context.state == null) for (const parser of parsers) {
|
|
215
|
+
const parserSuggestions = parser.suggest({
|
|
216
|
+
...context,
|
|
217
|
+
state: parser.initialState
|
|
218
|
+
}, prefix);
|
|
219
|
+
suggestions.push(...parserSuggestions);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
const [index, parserResult] = context.state;
|
|
223
|
+
if (parserResult.success) {
|
|
224
|
+
const parserSuggestions = parsers[index].suggest({
|
|
225
|
+
...context,
|
|
226
|
+
state: parserResult.next.state
|
|
227
|
+
}, prefix);
|
|
228
|
+
suggestions.push(...parserSuggestions);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const seen = /* @__PURE__ */ new Set();
|
|
232
|
+
return suggestions.filter((suggestion) => {
|
|
233
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
234
|
+
if (seen.has(key)) return false;
|
|
235
|
+
seen.add(key);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
},
|
|
185
239
|
getDocFragments(state, _defaultValue) {
|
|
186
240
|
let description;
|
|
187
241
|
let fragments;
|
|
@@ -302,6 +356,24 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
302
356
|
value: result
|
|
303
357
|
};
|
|
304
358
|
},
|
|
359
|
+
suggest(context, prefix) {
|
|
360
|
+
const suggestions = [];
|
|
361
|
+
for (const [field, parser] of parserPairs) {
|
|
362
|
+
const fieldState = context.state && typeof context.state === "object" && field in context.state ? context.state[field] : parser.initialState;
|
|
363
|
+
const fieldSuggestions = parser.suggest({
|
|
364
|
+
...context,
|
|
365
|
+
state: fieldState
|
|
366
|
+
}, prefix);
|
|
367
|
+
suggestions.push(...fieldSuggestions);
|
|
368
|
+
}
|
|
369
|
+
const seen = /* @__PURE__ */ new Set();
|
|
370
|
+
return suggestions.filter((suggestion) => {
|
|
371
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
372
|
+
if (seen.has(key)) return false;
|
|
373
|
+
seen.add(key);
|
|
374
|
+
return true;
|
|
375
|
+
});
|
|
376
|
+
},
|
|
305
377
|
getDocFragments(state, defaultValue) {
|
|
306
378
|
const fragments = parserPairs.flatMap(([field, p]) => {
|
|
307
379
|
const fieldState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
@@ -412,6 +484,25 @@ function tuple(labelOrParsers, maybeParsers) {
|
|
|
412
484
|
value: result
|
|
413
485
|
};
|
|
414
486
|
},
|
|
487
|
+
suggest(context, prefix) {
|
|
488
|
+
const suggestions = [];
|
|
489
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
490
|
+
const parser = parsers[i];
|
|
491
|
+
const parserState = context.state && Array.isArray(context.state) ? context.state[i] : parser.initialState;
|
|
492
|
+
const parserSuggestions = parser.suggest({
|
|
493
|
+
...context,
|
|
494
|
+
state: parserState
|
|
495
|
+
}, prefix);
|
|
496
|
+
suggestions.push(...parserSuggestions);
|
|
497
|
+
}
|
|
498
|
+
const seen = /* @__PURE__ */ new Set();
|
|
499
|
+
return suggestions.filter((suggestion) => {
|
|
500
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
501
|
+
if (seen.has(key)) return false;
|
|
502
|
+
seen.add(key);
|
|
503
|
+
return true;
|
|
504
|
+
});
|
|
505
|
+
},
|
|
415
506
|
getDocFragments(state, defaultValue) {
|
|
416
507
|
const fragments = parsers.flatMap((p, i) => {
|
|
417
508
|
const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
@@ -523,6 +614,35 @@ function merge(...args) {
|
|
|
523
614
|
value: object$1
|
|
524
615
|
};
|
|
525
616
|
},
|
|
617
|
+
suggest(context, prefix) {
|
|
618
|
+
const suggestions = [];
|
|
619
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
620
|
+
const parser = parsers[i];
|
|
621
|
+
let parserState;
|
|
622
|
+
if (parser.initialState === void 0) {
|
|
623
|
+
const key = `__parser_${i}`;
|
|
624
|
+
if (context.state && typeof context.state === "object" && key in context.state) parserState = context.state[key];
|
|
625
|
+
else parserState = void 0;
|
|
626
|
+
} else if (parser.initialState && typeof parser.initialState === "object") if (context.state && typeof context.state === "object") {
|
|
627
|
+
const extractedState = {};
|
|
628
|
+
for (const field in parser.initialState) extractedState[field] = field in context.state ? context.state[field] : parser.initialState[field];
|
|
629
|
+
parserState = extractedState;
|
|
630
|
+
} else parserState = parser.initialState;
|
|
631
|
+
else parserState = parser.initialState;
|
|
632
|
+
const parserSuggestions = parser.suggest({
|
|
633
|
+
...context,
|
|
634
|
+
state: parserState
|
|
635
|
+
}, prefix);
|
|
636
|
+
suggestions.push(...parserSuggestions);
|
|
637
|
+
}
|
|
638
|
+
const seen = /* @__PURE__ */ new Set();
|
|
639
|
+
return suggestions.filter((suggestion) => {
|
|
640
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
641
|
+
if (seen.has(key)) return false;
|
|
642
|
+
seen.add(key);
|
|
643
|
+
return true;
|
|
644
|
+
});
|
|
645
|
+
},
|
|
526
646
|
getDocFragments(state, _defaultValue) {
|
|
527
647
|
const fragments = parsers.flatMap((p) => {
|
|
528
648
|
const parserState = p.initialState === void 0 ? { kind: "unavailable" } : state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
@@ -641,6 +761,25 @@ function concat(...parsers) {
|
|
|
641
761
|
value: results
|
|
642
762
|
};
|
|
643
763
|
},
|
|
764
|
+
suggest(context, prefix) {
|
|
765
|
+
const suggestions = [];
|
|
766
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
767
|
+
const parser = parsers[i];
|
|
768
|
+
const parserState = context.state && Array.isArray(context.state) ? context.state[i] : parser.initialState;
|
|
769
|
+
const parserSuggestions = parser.suggest({
|
|
770
|
+
...context,
|
|
771
|
+
state: parserState
|
|
772
|
+
}, prefix);
|
|
773
|
+
suggestions.push(...parserSuggestions);
|
|
774
|
+
}
|
|
775
|
+
const seen = /* @__PURE__ */ new Set();
|
|
776
|
+
return suggestions.filter((suggestion) => {
|
|
777
|
+
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
778
|
+
if (seen.has(key)) return false;
|
|
779
|
+
seen.add(key);
|
|
780
|
+
return true;
|
|
781
|
+
});
|
|
782
|
+
},
|
|
644
783
|
getDocFragments(state, _defaultValue) {
|
|
645
784
|
const fragments = parsers.flatMap((p, index) => {
|
|
646
785
|
const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
@@ -719,6 +858,7 @@ function group(label, parser) {
|
|
|
719
858
|
initialState: parser.initialState,
|
|
720
859
|
parse: (context) => parser.parse(context),
|
|
721
860
|
complete: (state) => parser.complete(state),
|
|
861
|
+
suggest: (context, prefix) => parser.suggest(context, prefix),
|
|
722
862
|
getDocFragments: (state, defaultValue) => {
|
|
723
863
|
const { description, fragments } = parser.getDocFragments(state, defaultValue);
|
|
724
864
|
const allEntries = [];
|