@optique/core 0.10.7-dev.485 → 1.0.0-dev.1109
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/README.md +4 -6
- package/dist/annotations.cjs +209 -1
- package/dist/annotations.d.cts +78 -1
- package/dist/annotations.d.ts +78 -1
- package/dist/annotations.js +201 -1
- package/dist/completion.cjs +186 -50
- package/dist/completion.js +186 -50
- package/dist/constructs.cjs +310 -78
- package/dist/constructs.d.cts +525 -644
- package/dist/constructs.d.ts +525 -644
- package/dist/constructs.js +311 -79
- package/dist/context.cjs +43 -3
- package/dist/context.d.cts +113 -5
- package/dist/context.d.ts +113 -5
- package/dist/context.js +41 -3
- package/dist/dependency.cjs +172 -66
- package/dist/dependency.d.cts +22 -2
- package/dist/dependency.d.ts +22 -2
- package/dist/dependency.js +172 -66
- package/dist/doc.cjs +46 -1
- package/dist/doc.d.cts +24 -0
- package/dist/doc.d.ts +24 -0
- package/dist/doc.js +46 -1
- package/dist/facade.cjs +702 -322
- package/dist/facade.d.cts +124 -190
- package/dist/facade.d.ts +124 -190
- package/dist/facade.js +703 -323
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/message.cjs +7 -4
- package/dist/message.js +7 -4
- package/dist/mode-dispatch.cjs +23 -1
- package/dist/mode-dispatch.d.cts +55 -0
- package/dist/mode-dispatch.d.ts +55 -0
- package/dist/mode-dispatch.js +21 -1
- package/dist/modifiers.cjs +210 -55
- package/dist/modifiers.js +211 -56
- package/dist/parser.cjs +80 -47
- package/dist/parser.d.cts +18 -3
- package/dist/parser.d.ts +18 -3
- package/dist/parser.js +82 -50
- package/dist/primitives.cjs +102 -37
- package/dist/primitives.d.cts +81 -24
- package/dist/primitives.d.ts +81 -24
- package/dist/primitives.js +103 -39
- package/dist/usage.cjs +88 -6
- package/dist/usage.d.cts +51 -13
- package/dist/usage.d.ts +51 -13
- package/dist/usage.js +85 -7
- package/dist/valueparser.cjs +371 -99
- package/dist/valueparser.d.cts +56 -7
- package/dist/valueparser.d.ts +56 -7
- package/dist/valueparser.js +371 -99
- package/package.json +10 -1
package/dist/completion.cjs
CHANGED
|
@@ -5,16 +5,17 @@ const require_message = require('./message.cjs');
|
|
|
5
5
|
* A regular expression pattern for valid program names that can be safely
|
|
6
6
|
* interpolated into shell scripts.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* The first character must be alphanumeric or underscore. Subsequent
|
|
9
|
+
* characters may also include hyphens and dots:
|
|
9
10
|
* - Letters (a-z, A-Z)
|
|
10
11
|
* - Numbers (0-9)
|
|
11
12
|
* - Underscore (_)
|
|
12
|
-
* - Hyphen (-)
|
|
13
|
-
* - Dot (.)
|
|
13
|
+
* - Hyphen (-) — not as the first character
|
|
14
|
+
* - Dot (.) — not as the first character
|
|
14
15
|
*
|
|
15
16
|
* @internal
|
|
16
17
|
*/
|
|
17
|
-
const SAFE_PROGRAM_NAME_PATTERN = /^[a-zA-Z0-9_.-]
|
|
18
|
+
const SAFE_PROGRAM_NAME_PATTERN = /^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/;
|
|
18
19
|
/**
|
|
19
20
|
* Validates a program name for safe use in shell scripts.
|
|
20
21
|
*
|
|
@@ -27,7 +28,17 @@ const SAFE_PROGRAM_NAME_PATTERN = /^[a-zA-Z0-9_.-]+$/;
|
|
|
27
28
|
* @internal
|
|
28
29
|
*/
|
|
29
30
|
function validateProgramName(programName) {
|
|
30
|
-
if (!SAFE_PROGRAM_NAME_PATTERN.test(programName)) throw new Error(`Invalid program name for shell completion: "${programName}". Program names must contain only alphanumeric characters, underscores, hyphens, and dots.`);
|
|
31
|
+
if (!SAFE_PROGRAM_NAME_PATTERN.test(programName)) throw new Error(`Invalid program name for shell completion: "${programName}". Program names must start with an alphanumeric character or underscore, and contain only alphanumeric characters, underscores, hyphens, and dots.`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Percent-encodes colons and percent signs in a pattern string so that it
|
|
35
|
+
* can be safely embedded in the colon-delimited `__FILE__` transport format.
|
|
36
|
+
* @param pattern The raw pattern string.
|
|
37
|
+
* @returns The encoded pattern string.
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
function encodePattern(pattern) {
|
|
41
|
+
return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
31
42
|
}
|
|
32
43
|
/**
|
|
33
44
|
* The Bash shell completion generator.
|
|
@@ -47,6 +58,47 @@ function _${programName} () {
|
|
|
47
58
|
if [[ "$line" == __FILE__:* ]]; then
|
|
48
59
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
49
60
|
IFS=':' read -r _ type extensions pattern hidden <<< "$line"
|
|
61
|
+
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
62
|
+
|
|
63
|
+
# Save and adjust glob/shell options for safe file completion
|
|
64
|
+
local __dotglob_was_set=0 __failglob_was_set=0 __noglob_was_set=0
|
|
65
|
+
local __globignore_was_set=0 __saved_globignore="\${GLOBIGNORE-}"
|
|
66
|
+
[[ \${GLOBIGNORE+x} == x ]] && __globignore_was_set=1
|
|
67
|
+
shopt -q dotglob && __dotglob_was_set=1
|
|
68
|
+
shopt -q failglob && __failglob_was_set=1
|
|
69
|
+
[[ $- == *f* ]] && __noglob_was_set=1
|
|
70
|
+
# Unset GLOBIGNORE before enabling dotglob because unsetting
|
|
71
|
+
# GLOBIGNORE implicitly clears dotglob in Bash
|
|
72
|
+
shopt -u failglob 2>/dev/null
|
|
73
|
+
set +f
|
|
74
|
+
unset GLOBIGNORE
|
|
75
|
+
|
|
76
|
+
# Expand tilde prefix for file globbing
|
|
77
|
+
local __glob_current="$current" __tilde_prefix="" __tilde_expanded=""
|
|
78
|
+
if [[ "$current" =~ ^(~[a-zA-Z0-9_.+-]*)(/.*)$ ]]; then
|
|
79
|
+
__tilde_prefix="\${BASH_REMATCH[1]}"
|
|
80
|
+
eval "__tilde_expanded=\$__tilde_prefix" 2>/dev/null || true
|
|
81
|
+
if [[ -n "$__tilde_expanded" && "$__tilde_expanded" != "$__tilde_prefix" ]]; then
|
|
82
|
+
__glob_current="\${__tilde_expanded}\${current#\$__tilde_prefix}"
|
|
83
|
+
else
|
|
84
|
+
__tilde_prefix=""
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Enable dotglob when hidden files are requested, or when the user
|
|
89
|
+
# is already navigating inside a hidden directory (e.g., ~/.config/nvim/)
|
|
90
|
+
# This runs after tilde expansion so that paths like ~/.config/ are
|
|
91
|
+
# checked against the expanded path, not the literal ~ string.
|
|
92
|
+
local __inside_hidden_path=0
|
|
93
|
+
case "/\${__glob_current%/}/" in
|
|
94
|
+
*/.[!.]*/*|*/..?*/*) __inside_hidden_path=1 ;;
|
|
95
|
+
esac
|
|
96
|
+
# Also check if the current prefix explicitly targets hidden entries
|
|
97
|
+
# (e.g., user typed "." or ".e" to complete .env)
|
|
98
|
+
local __prefix_targets_hidden=0
|
|
99
|
+
local __prefix_base="\${__glob_current##*/}"
|
|
100
|
+
[[ "$__prefix_base" == .* ]] && __prefix_targets_hidden=1
|
|
101
|
+
if [[ "$hidden" == "1" || "$__inside_hidden_path" == "1" || "$__prefix_targets_hidden" == "1" ]]; then shopt -s dotglob; fi
|
|
50
102
|
|
|
51
103
|
# Generate file completions based on type
|
|
52
104
|
case "$type" in
|
|
@@ -55,21 +107,21 @@ function _${programName} () {
|
|
|
55
107
|
if [[ -n "$extensions" ]]; then
|
|
56
108
|
# Complete with extension filtering
|
|
57
109
|
local ext_pattern="\${extensions//,/|}"
|
|
58
|
-
for file in "$
|
|
59
|
-
[[ -
|
|
110
|
+
for file in "$__glob_current"*; do
|
|
111
|
+
[[ -f "$file" && "$file" =~ \\.($ext_pattern)$ ]] && COMPREPLY+=("$file")
|
|
60
112
|
done
|
|
61
113
|
else
|
|
62
114
|
# Complete files only, exclude directories
|
|
63
|
-
|
|
115
|
+
for item in "$__glob_current"*; do
|
|
64
116
|
[[ -f "$item" ]] && COMPREPLY+=("$item")
|
|
65
|
-
done
|
|
117
|
+
done
|
|
66
118
|
fi
|
|
67
119
|
;;
|
|
68
120
|
directory)
|
|
69
121
|
# Complete directories only
|
|
70
|
-
|
|
71
|
-
COMPREPLY+=("$dir/")
|
|
72
|
-
done
|
|
122
|
+
for dir in "$__glob_current"*; do
|
|
123
|
+
[[ -d "$dir" ]] && COMPREPLY+=("$dir/")
|
|
124
|
+
done
|
|
73
125
|
;;
|
|
74
126
|
any)
|
|
75
127
|
# Complete both files and directories
|
|
@@ -77,31 +129,50 @@ function _${programName} () {
|
|
|
77
129
|
# Files with extension filtering + directories
|
|
78
130
|
# Files with extension filtering
|
|
79
131
|
local ext_pattern="\${extensions//,/|}"
|
|
80
|
-
for item in "$
|
|
132
|
+
for item in "$__glob_current"*; do
|
|
81
133
|
if [[ -d "$item" ]]; then
|
|
82
134
|
COMPREPLY+=("$item/")
|
|
83
|
-
elif [[ -
|
|
135
|
+
elif [[ ( -e "$item" || -L "$item" ) && "$item" =~ \\.($ext_pattern)$ ]]; then
|
|
84
136
|
COMPREPLY+=("$item")
|
|
85
137
|
fi
|
|
86
138
|
done
|
|
87
139
|
else
|
|
88
140
|
# Complete files and directories, add slash to directories
|
|
89
|
-
|
|
141
|
+
for item in "$__glob_current"*; do
|
|
90
142
|
if [[ -d "$item" ]]; then
|
|
91
143
|
COMPREPLY+=("$item/")
|
|
92
|
-
|
|
144
|
+
# Use -e || -L to include non-regular files (sockets, FIFOs, dangling symlinks)
|
|
145
|
+
elif [[ -e "$item" || -L "$item" ]]; then
|
|
93
146
|
COMPREPLY+=("$item")
|
|
94
147
|
fi
|
|
95
|
-
done
|
|
148
|
+
done
|
|
96
149
|
fi
|
|
97
150
|
;;
|
|
98
151
|
esac
|
|
99
152
|
|
|
153
|
+
# Restore tilde prefix in completion results
|
|
154
|
+
if [[ -n "$__tilde_prefix" ]]; then
|
|
155
|
+
local __i
|
|
156
|
+
for __i in "\${!COMPREPLY[@]}"; do
|
|
157
|
+
COMPREPLY[\$__i]="\${COMPREPLY[\$__i]/#\$__tilde_expanded/\$__tilde_prefix}"
|
|
158
|
+
done
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Restore glob/shell options
|
|
162
|
+
# Restore GLOBIGNORE before dotglob because assigning GLOBIGNORE
|
|
163
|
+
# implicitly enables dotglob in Bash
|
|
164
|
+
if [[ "$__globignore_was_set" == "1" ]]; then GLOBIGNORE="$__saved_globignore"; fi
|
|
165
|
+
if [[ "$__dotglob_was_set" == "0" ]]; then shopt -u dotglob; else shopt -s dotglob; fi
|
|
166
|
+
if [[ "$__failglob_was_set" == "1" ]]; then shopt -s failglob; fi
|
|
167
|
+
if [[ "$__noglob_was_set" == "1" ]]; then set -f; fi
|
|
168
|
+
|
|
100
169
|
# Filter out hidden files unless requested
|
|
101
|
-
if [[ "$hidden" != "1" && "$
|
|
170
|
+
if [[ "$hidden" != "1" && "$__inside_hidden_path" == "0" && "$__prefix_targets_hidden" == "0" ]]; then
|
|
102
171
|
local filtered=()
|
|
172
|
+
local __name
|
|
103
173
|
for item in "\${COMPREPLY[@]}"; do
|
|
104
|
-
|
|
174
|
+
__name="\${item%/}"; __name="\${__name##*/}"
|
|
175
|
+
[[ "$__name" != .* ]] && filtered+=("$item")
|
|
105
176
|
done
|
|
106
177
|
COMPREPLY=("\${filtered[@]}")
|
|
107
178
|
fi
|
|
@@ -123,7 +194,8 @@ complete -F _${programName} ${programName}
|
|
|
123
194
|
else {
|
|
124
195
|
const extensions = suggestion.extensions?.join(",") || "";
|
|
125
196
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
126
|
-
|
|
197
|
+
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
198
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}`;
|
|
127
199
|
}
|
|
128
200
|
i++;
|
|
129
201
|
}
|
|
@@ -175,6 +247,7 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
175
247
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
176
248
|
local type extensions pattern hidden
|
|
177
249
|
IFS=':' read -r _ type extensions pattern hidden <<< "\$value"
|
|
250
|
+
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
178
251
|
has_file_completion=1
|
|
179
252
|
|
|
180
253
|
# Use zsh's native file completion
|
|
@@ -182,8 +255,8 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
182
255
|
file)
|
|
183
256
|
if [[ -n "\$extensions" ]]; then
|
|
184
257
|
# Complete files with extension filtering
|
|
185
|
-
local ext_pattern="*.(
|
|
186
|
-
_files -g "
|
|
258
|
+
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
259
|
+
_files -g "\$ext_pattern"
|
|
187
260
|
else
|
|
188
261
|
_files -g "*"
|
|
189
262
|
fi
|
|
@@ -194,8 +267,8 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
194
267
|
any)
|
|
195
268
|
if [[ -n "\$extensions" ]]; then
|
|
196
269
|
# Complete both files and directories, with extension filtering for files
|
|
197
|
-
local ext_pattern="*.(
|
|
198
|
-
_files -g "
|
|
270
|
+
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
271
|
+
_files -g "\$ext_pattern" && _directories
|
|
199
272
|
else
|
|
200
273
|
_files
|
|
201
274
|
fi
|
|
@@ -241,7 +314,8 @@ compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
|
|
|
241
314
|
const extensions = suggestion.extensions?.join(",") || "";
|
|
242
315
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
243
316
|
const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
|
|
244
|
-
|
|
317
|
+
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
318
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\0${description}\0`;
|
|
245
319
|
}
|
|
246
320
|
}
|
|
247
321
|
};
|
|
@@ -273,10 +347,11 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
273
347
|
for line in $output
|
|
274
348
|
if string match -q '__FILE__:*' -- $line
|
|
275
349
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
276
|
-
set -l
|
|
350
|
+
set -l directive (string split \\t -- $line)[1]
|
|
351
|
+
set -l parts (string split ':' -- $directive)
|
|
277
352
|
set -l type $parts[2]
|
|
278
353
|
set -l extensions $parts[3]
|
|
279
|
-
set -l pattern $parts[4]
|
|
354
|
+
set -l pattern (string replace -a '%25' '%' -- (string replace -a '%3A' ':' -- $parts[4]))
|
|
280
355
|
set -l hidden $parts[5]
|
|
281
356
|
|
|
282
357
|
# Generate file completions based on type
|
|
@@ -289,6 +364,21 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
289
364
|
set -a items $item
|
|
290
365
|
end
|
|
291
366
|
end
|
|
367
|
+
# Fish's * glob does not match dotfiles; add them
|
|
368
|
+
# explicitly when the basename is empty (i.e., $current
|
|
369
|
+
# is "" or ends with "/"), because only then are * and
|
|
370
|
+
# .* complementary. When a non-empty basename is present
|
|
371
|
+
# (e.g., "foo"), foo* already covers foo.txt, so foo.*
|
|
372
|
+
# would just produce duplicates.
|
|
373
|
+
if test "$hidden" = "1"
|
|
374
|
+
if test -z "$current"; or string match -q '*/' -- "$current"
|
|
375
|
+
for item in $current.*
|
|
376
|
+
if test -f $item
|
|
377
|
+
set -a items $item
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
292
382
|
case directory
|
|
293
383
|
# Complete directories only
|
|
294
384
|
for item in $current*
|
|
@@ -296,6 +386,15 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
296
386
|
set -a items $item/
|
|
297
387
|
end
|
|
298
388
|
end
|
|
389
|
+
if test "$hidden" = "1"
|
|
390
|
+
if test -z "$current"; or string match -q '*/' -- "$current"
|
|
391
|
+
for item in $current.*
|
|
392
|
+
if test -d $item
|
|
393
|
+
set -a items $item/
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
299
398
|
case any
|
|
300
399
|
# Complete both files and directories
|
|
301
400
|
for item in $current*
|
|
@@ -305,6 +404,17 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
305
404
|
set -a items $item
|
|
306
405
|
end
|
|
307
406
|
end
|
|
407
|
+
if test "$hidden" = "1"
|
|
408
|
+
if test -z "$current"; or string match -q '*/' -- "$current"
|
|
409
|
+
for item in $current.*
|
|
410
|
+
if test -d $item
|
|
411
|
+
set -a items $item/
|
|
412
|
+
else if test -f $item
|
|
413
|
+
set -a items $item
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
|
308
418
|
end
|
|
309
419
|
|
|
310
420
|
# Filter by extensions if specified
|
|
@@ -372,7 +482,8 @@ complete -c ${programName} -f -a '(${functionName})'
|
|
|
372
482
|
const extensions = suggestion.extensions?.join(",") || "";
|
|
373
483
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
374
484
|
const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
|
|
375
|
-
|
|
485
|
+
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
486
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
376
487
|
}
|
|
377
488
|
i++;
|
|
378
489
|
}
|
|
@@ -478,10 +589,11 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
478
589
|
$output | lines | each {|line|
|
|
479
590
|
if ($line | str starts-with '__FILE__:') {
|
|
480
591
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
481
|
-
let
|
|
592
|
+
let directive = ($line | split row "\t" | first)
|
|
593
|
+
let parts = ($directive | split row ':')
|
|
482
594
|
let type = ($parts | get 1)
|
|
483
595
|
let extensions = ($parts | get 2)
|
|
484
|
-
let pattern = ($parts | get 3)
|
|
596
|
+
let pattern = ($parts | get 3 | str replace -a '%3A' ':' | str replace -a '%25' '%')
|
|
485
597
|
let hidden = ($parts | get 4) == '1'
|
|
486
598
|
|
|
487
599
|
# Extract prefix from the last argument if it exists
|
|
@@ -493,31 +605,33 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
493
605
|
|
|
494
606
|
# Generate file completions based on type
|
|
495
607
|
# Use current directory if prefix is empty
|
|
496
|
-
|
|
608
|
+
# Note: into glob is required so that ls expands wildcards from a variable
|
|
609
|
+
let ls_pattern = if ($prefix | is-empty) { "." } else { ($prefix + "*" | into glob) }
|
|
497
610
|
|
|
611
|
+
# Use ls -a to include hidden files when requested
|
|
498
612
|
let items = try {
|
|
499
613
|
match $type {
|
|
500
614
|
"file" => {
|
|
501
615
|
if ($extensions | is-empty) {
|
|
502
|
-
ls $ls_pattern | where type == file
|
|
616
|
+
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file
|
|
503
617
|
} else {
|
|
504
618
|
let ext_list = ($extensions | split row ',')
|
|
505
|
-
ls $ls_pattern | where type == file | where {|f|
|
|
619
|
+
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file | where {|f|
|
|
506
620
|
let ext = ($f.name | path parse | get extension)
|
|
507
621
|
$ext in $ext_list
|
|
508
622
|
}
|
|
509
623
|
}
|
|
510
624
|
},
|
|
511
625
|
"directory" => {
|
|
512
|
-
ls $ls_pattern | where type == dir
|
|
626
|
+
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == dir
|
|
513
627
|
},
|
|
514
628
|
"any" => {
|
|
515
629
|
if ($extensions | is-empty) {
|
|
516
|
-
ls $ls_pattern
|
|
630
|
+
if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }
|
|
517
631
|
} else {
|
|
518
632
|
let ext_list = ($extensions | split row ',')
|
|
519
|
-
let dirs = ls $ls_pattern | where type == dir
|
|
520
|
-
let files = ls $ls_pattern | where type == file | where {|f|
|
|
633
|
+
let dirs = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == dir
|
|
634
|
+
let files = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file | where {|f|
|
|
521
635
|
let ext = ($f.name | path parse | get extension)
|
|
522
636
|
$ext in $ext_list
|
|
523
637
|
}
|
|
@@ -539,12 +653,22 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
539
653
|
}
|
|
540
654
|
}
|
|
541
655
|
|
|
656
|
+
# Extract directory prefix to preserve in completion text
|
|
657
|
+
let dir_prefix = if ($prefix | is-empty) {
|
|
658
|
+
""
|
|
659
|
+
} else if ($prefix | str ends-with "/") {
|
|
660
|
+
$prefix
|
|
661
|
+
} else {
|
|
662
|
+
let parsed = ($prefix | path parse)
|
|
663
|
+
if ($parsed.parent | is-empty) { "" } else if ($parsed.parent | str ends-with "/") { $parsed.parent } else { $parsed.parent + "/" }
|
|
664
|
+
}
|
|
665
|
+
|
|
542
666
|
# Format file completions
|
|
543
667
|
$filtered | each {|item|
|
|
544
668
|
let name = if $item.type == dir {
|
|
545
|
-
($item.name | path basename) + "/"
|
|
669
|
+
$dir_prefix + ($item.name | path basename) + "/"
|
|
546
670
|
} else {
|
|
547
|
-
$item.name | path basename
|
|
671
|
+
$dir_prefix + ($item.name | path basename)
|
|
548
672
|
}
|
|
549
673
|
{ value: $name }
|
|
550
674
|
}
|
|
@@ -602,7 +726,8 @@ ${functionName}-external
|
|
|
602
726
|
const extensions = suggestion.extensions?.join(",") || "";
|
|
603
727
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
604
728
|
const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
|
|
605
|
-
|
|
729
|
+
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
730
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
606
731
|
}
|
|
607
732
|
i++;
|
|
608
733
|
}
|
|
@@ -668,15 +793,19 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
668
793
|
|
|
669
794
|
if (\$line -match '^__FILE__:') {
|
|
670
795
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
671
|
-
\$
|
|
796
|
+
\$directive = (\$line -split "\`t")[0]
|
|
797
|
+
\$parts = \$directive -split ':', 5
|
|
672
798
|
\$type = \$parts[1]
|
|
673
799
|
\$extensions = \$parts[2]
|
|
674
|
-
\$pattern = \$parts[3]
|
|
800
|
+
\$pattern = \$parts[3] -replace '%3A', ':' -replace '%25', '%'
|
|
675
801
|
\$hidden = \$parts[4] -eq '1'
|
|
676
802
|
|
|
677
803
|
# Determine current prefix for file matching
|
|
678
804
|
\$prefix = if (\$wordToComplete) { \$wordToComplete } else { '' }
|
|
679
805
|
|
|
806
|
+
# Use -Force to include hidden files when requested
|
|
807
|
+
\$forceParam = if (\$hidden) { @{Force = \$true} } else { @{} }
|
|
808
|
+
|
|
680
809
|
# Get file system items based on type
|
|
681
810
|
\$items = @()
|
|
682
811
|
switch (\$type) {
|
|
@@ -684,31 +813,31 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
684
813
|
if (\$extensions) {
|
|
685
814
|
# Filter by extensions
|
|
686
815
|
\$extList = \$extensions -split ','
|
|
687
|
-
\$items = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
816
|
+
\$items = Get-ChildItem @forceParam -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
688
817
|
Where-Object {
|
|
689
818
|
\$ext = \$_.Extension
|
|
690
819
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
691
820
|
}
|
|
692
821
|
} else {
|
|
693
|
-
\$items = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
822
|
+
\$items = Get-ChildItem @forceParam -File -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
694
823
|
}
|
|
695
824
|
}
|
|
696
825
|
'directory' {
|
|
697
|
-
\$items = Get-ChildItem -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
826
|
+
\$items = Get-ChildItem @forceParam -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
698
827
|
}
|
|
699
828
|
'any' {
|
|
700
829
|
if (\$extensions) {
|
|
701
830
|
# Get directories and filtered files
|
|
702
|
-
\$dirs = Get-ChildItem -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
831
|
+
\$dirs = Get-ChildItem @forceParam -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
703
832
|
\$extList = \$extensions -split ','
|
|
704
|
-
\$files = Get-ChildItem -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
833
|
+
\$files = Get-ChildItem @forceParam -File -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
705
834
|
Where-Object {
|
|
706
835
|
\$ext = \$_.Extension
|
|
707
836
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
708
837
|
}
|
|
709
838
|
\$items = \$dirs + \$files
|
|
710
839
|
} else {
|
|
711
|
-
\$items = Get-ChildItem -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
840
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
712
841
|
}
|
|
713
842
|
}
|
|
714
843
|
}
|
|
@@ -718,9 +847,15 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
718
847
|
\$items = \$items | Where-Object { -not \$_.Attributes.HasFlag([System.IO.FileAttributes]::Hidden) }
|
|
719
848
|
}
|
|
720
849
|
|
|
850
|
+
# Extract directory prefix to preserve in completion text
|
|
851
|
+
\$dirPrefix = if (\$prefix -and (\$prefix.Contains('/') -or \$prefix.Contains('\\'))) {
|
|
852
|
+
\$slashIdx = [Math]::Max(\$prefix.LastIndexOf('/'), \$prefix.LastIndexOf('\\'))
|
|
853
|
+
\$prefix.Substring(0, \$slashIdx + 1)
|
|
854
|
+
} else { '' }
|
|
855
|
+
|
|
721
856
|
# Create completion results for files
|
|
722
857
|
\$items | ForEach-Object {
|
|
723
|
-
\$completionText = if (\$_.PSIsContainer) { "\$(\$_.Name)/" } else { \$_.Name }
|
|
858
|
+
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
724
859
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
725
860
|
[System.Management.Automation.CompletionResult]::new(
|
|
726
861
|
\$completionText,
|
|
@@ -761,7 +896,8 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
761
896
|
const extensions = suggestion.extensions?.join(",") || "";
|
|
762
897
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
763
898
|
const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
|
|
764
|
-
|
|
899
|
+
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
900
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t[file]\t${description}`;
|
|
765
901
|
}
|
|
766
902
|
i++;
|
|
767
903
|
}
|