@optique/core 1.1.12 → 1.1.13-dev.2823
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 +96 -19
- package/dist/completion.js +96 -19
- package/dist/internal/parser.d.cts +8 -0
- package/dist/internal/parser.d.ts +8 -0
- package/dist/modifiers.cjs +1 -0
- package/dist/modifiers.js +1 -0
- package/dist/suggestion.cjs +9 -4
- package/dist/suggestion.d.cts +1 -1
- package/dist/suggestion.d.ts +1 -1
- package/dist/suggestion.js +9 -4
- package/dist/valueparser.cjs +2 -1
- package/dist/valueparser.js +2 -1
- package/package.json +2 -2
package/dist/completion.cjs
CHANGED
|
@@ -40,6 +40,12 @@ function validateProgramName(programName) {
|
|
|
40
40
|
function encodePattern(pattern) {
|
|
41
41
|
return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
42
42
|
}
|
|
43
|
+
/** Encodes optional literal insertion metadata without changing old directives. */
|
|
44
|
+
function encodeFilePrefix(suggestion) {
|
|
45
|
+
const prefix = suggestion.prefix ?? "";
|
|
46
|
+
if (prefix === "" || !(suggestion.pattern ?? "").startsWith(prefix) || sanitizeForTransport(prefix) !== prefix) return "";
|
|
47
|
+
return `:${encodePattern(prefix)}`;
|
|
48
|
+
}
|
|
43
49
|
function encodeExtensions(extensions) {
|
|
44
50
|
return extensions?.map((ext) => ext.replace(/^\./, "")).join(",") ?? "";
|
|
45
51
|
}
|
|
@@ -69,13 +75,32 @@ function _${programName} () {
|
|
|
69
75
|
COMPREPLY=()
|
|
70
76
|
local current="\${COMP_WORDS[COMP_CWORD]}"
|
|
71
77
|
local prev=("\${COMP_WORDS[@]:1:COMP_CWORD-1}")
|
|
78
|
+
local __prefix_aware=0
|
|
72
79
|
while IFS= read -r line; do
|
|
73
80
|
if [[ "$line" == __FILE__:* ]]; then
|
|
74
81
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
75
|
-
|
|
82
|
+
local insertion_prefix
|
|
83
|
+
IFS=':' read -r _ type extensions pattern hidden insertion_prefix <<< "$line"
|
|
76
84
|
extensions="\${extensions//,\\./,}"; extensions="\${extensions#.}"
|
|
77
85
|
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
78
86
|
|
|
87
|
+
insertion_prefix="\${insertion_prefix//%3A/:}"; insertion_prefix="\${insertion_prefix//%25/%}"
|
|
88
|
+
local __saved_current="$current"
|
|
89
|
+
local -a __saved_reply=()
|
|
90
|
+
if [[ -n "$insertion_prefix" && "$pattern" == "$insertion_prefix"* && "$current" == "$insertion_prefix"* ]]; then
|
|
91
|
+
pattern="\${pattern#"$insertion_prefix"}"
|
|
92
|
+
current="\${current#"$insertion_prefix"}"
|
|
93
|
+
__prefix_aware=1
|
|
94
|
+
else
|
|
95
|
+
insertion_prefix=""
|
|
96
|
+
fi
|
|
97
|
+
# Inserted keys are not filesystem paths. Once present, keep earlier
|
|
98
|
+
# replies out of later file filters; retain legacy-only aggregation.
|
|
99
|
+
if [[ "$__prefix_aware" == "1" ]]; then
|
|
100
|
+
__saved_reply=("\${COMPREPLY[@]}")
|
|
101
|
+
COMPREPLY=()
|
|
102
|
+
fi
|
|
103
|
+
|
|
79
104
|
# Save and adjust glob/shell options for safe file completion
|
|
80
105
|
local __dotglob_was_set=0 __failglob_was_set=0 __noglob_was_set=0
|
|
81
106
|
local __globignore_was_set=0 __saved_globignore="\${GLOBIGNORE-}"
|
|
@@ -274,6 +299,16 @@ function _${programName} () {
|
|
|
274
299
|
done
|
|
275
300
|
COMPREPLY=("\${filtered[@]}")
|
|
276
301
|
fi
|
|
302
|
+
if [[ -n "$insertion_prefix" ]]; then
|
|
303
|
+
local __prefix_index
|
|
304
|
+
for __prefix_index in "\${!COMPREPLY[@]}"; do
|
|
305
|
+
COMPREPLY[$__prefix_index]="$insertion_prefix\${COMPREPLY[$__prefix_index]}"
|
|
306
|
+
done
|
|
307
|
+
fi
|
|
308
|
+
if [[ "$__prefix_aware" == "1" ]]; then
|
|
309
|
+
COMPREPLY=("\${__saved_reply[@]}" "\${COMPREPLY[@]}")
|
|
310
|
+
fi
|
|
311
|
+
current="$__saved_current"
|
|
277
312
|
else
|
|
278
313
|
# Regular literal completion
|
|
279
314
|
COMPREPLY+=("$line")
|
|
@@ -293,7 +328,7 @@ complete -F _${programName} -- ${programName}
|
|
|
293
328
|
const extensions = encodeExtensions(suggestion.extensions);
|
|
294
329
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
295
330
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
296
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}`;
|
|
331
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}`;
|
|
297
332
|
}
|
|
298
333
|
i++;
|
|
299
334
|
}
|
|
@@ -343,9 +378,20 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
343
378
|
|
|
344
379
|
if [[ "\$value" == __FILE__:* ]]; then
|
|
345
380
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
346
|
-
local type extensions pattern hidden
|
|
347
|
-
IFS=':' read -r _ type extensions pattern hidden <<< "\$value"
|
|
348
|
-
|
|
381
|
+
local type extensions pattern hidden insertion_prefix
|
|
382
|
+
IFS=':' read -r _ type extensions pattern hidden insertion_prefix <<< "\$value"
|
|
383
|
+
local __decoded_pattern="\$pattern"
|
|
384
|
+
__decoded_pattern="\${__decoded_pattern//\\%3A/:}"; __decoded_pattern="\${__decoded_pattern//\\%25/%}"
|
|
385
|
+
insertion_prefix="\${insertion_prefix//\\%3A/:}"; insertion_prefix="\${insertion_prefix//\\%25/%}"
|
|
386
|
+
local __saved_prefix="\$PREFIX" __saved_iprefix="\$IPREFIX"
|
|
387
|
+
if [[ -n "\$insertion_prefix" && "\$__decoded_pattern" == "\$insertion_prefix"* && "\$PREFIX" == "\$insertion_prefix"* ]]; then
|
|
388
|
+
pattern="\${__decoded_pattern#"\$insertion_prefix"}"
|
|
389
|
+
PREFIX="\${PREFIX#"\$insertion_prefix"}"
|
|
390
|
+
IPREFIX="\$IPREFIX\$insertion_prefix"
|
|
391
|
+
else
|
|
392
|
+
# Preserve legacy decoding for directives without active metadata.
|
|
393
|
+
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
394
|
+
fi
|
|
349
395
|
has_file_completion=1
|
|
350
396
|
|
|
351
397
|
# Native completion helpers expect normal zsh glob grouping even if
|
|
@@ -364,7 +410,6 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
364
410
|
# _directories enumerate the pattern's directory instead of the
|
|
365
411
|
# current word. If the user has already typed beyond the pattern,
|
|
366
412
|
# keep PREFIX unchanged for incremental narrowing.
|
|
367
|
-
local __saved_prefix="\$PREFIX"
|
|
368
413
|
if [[ -n "\$pattern" ]]; then
|
|
369
414
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
370
415
|
local __norm_prefix="\${PREFIX#./}"
|
|
@@ -421,6 +466,7 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
421
466
|
|
|
422
467
|
# Restore PREFIX and glob_dots to their previous state
|
|
423
468
|
PREFIX="\$__saved_prefix"
|
|
469
|
+
IPREFIX="\$__saved_iprefix"
|
|
424
470
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
425
471
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
426
472
|
else
|
|
@@ -463,7 +509,7 @@ fi
|
|
|
463
509
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
464
510
|
const description = suggestion.description == null ? "" : sanitizeForTransport(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
465
511
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
466
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\0${description}\0`;
|
|
512
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\0${description}\0`;
|
|
467
513
|
}
|
|
468
514
|
}
|
|
469
515
|
};
|
|
@@ -501,6 +547,21 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
501
547
|
set -l extensions $parts[3]
|
|
502
548
|
set -l pattern (string replace -a '%25' '%' -- (string replace -a '%3A' ':' -- $parts[4]))
|
|
503
549
|
set -l hidden $parts[5]
|
|
550
|
+
set -l insertion_prefix ""
|
|
551
|
+
if test (count $parts) -ge 6
|
|
552
|
+
set insertion_prefix (string replace -a '%25' '%' -- (string replace -a '%3A' ':' -- $parts[6]))
|
|
553
|
+
end
|
|
554
|
+
set -l saved_current "$current"
|
|
555
|
+
set -l prefix_length (string length -- "$insertion_prefix")
|
|
556
|
+
if test $prefix_length -gt 0
|
|
557
|
+
and test (string sub -l $prefix_length -- "$pattern") = "$insertion_prefix"
|
|
558
|
+
and test (string sub -l $prefix_length -- "$current") = "$insertion_prefix"
|
|
559
|
+
set -l start (math $prefix_length + 1)
|
|
560
|
+
set pattern (string sub -s $start -- "$pattern")
|
|
561
|
+
set current (string sub -s $start -- "$current")
|
|
562
|
+
else
|
|
563
|
+
set insertion_prefix ""
|
|
564
|
+
end
|
|
504
565
|
|
|
505
566
|
# When a pattern is specified, use it as the glob base instead
|
|
506
567
|
# of the current word. If the user has already typed beyond the
|
|
@@ -722,8 +783,9 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
722
783
|
|
|
723
784
|
# Output file completions
|
|
724
785
|
for item in $items
|
|
725
|
-
echo $item
|
|
786
|
+
echo "$insertion_prefix$item"
|
|
726
787
|
end
|
|
788
|
+
set current "$saved_current"
|
|
727
789
|
else
|
|
728
790
|
# Regular literal completion - split by tab
|
|
729
791
|
set -l parts (string split \\t -- $line)
|
|
@@ -753,7 +815,7 @@ complete -c ${programName} -f -a '(${functionName})'
|
|
|
753
815
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
754
816
|
const description = suggestion.description == null ? "" : sanitizeForTransport(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
755
817
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
756
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
818
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t${description}`;
|
|
757
819
|
}
|
|
758
820
|
i++;
|
|
759
821
|
}
|
|
@@ -866,6 +928,10 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
866
928
|
let pattern = ($parts | get 3 | str replace -a '%3A' ':' | str replace -a '%25' '%')
|
|
867
929
|
let hidden = ($parts | get 4) == '1'
|
|
868
930
|
|
|
931
|
+
let insertion_prefix = if ($parts | length) > 5 {
|
|
932
|
+
$parts | get 5 | str replace -a '%3A' ':' | str replace -a '%25' '%'
|
|
933
|
+
} else { "" }
|
|
934
|
+
|
|
869
935
|
# Extract prefix from the last argument if it exists
|
|
870
936
|
let prefix = if ($final_args | length) > 0 {
|
|
871
937
|
$final_args | last
|
|
@@ -873,6 +939,11 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
873
939
|
""
|
|
874
940
|
}
|
|
875
941
|
|
|
942
|
+
let insertion_prefix = if ($insertion_prefix | is-not-empty) and ($pattern | str starts-with $insertion_prefix) and ($prefix | str starts-with $insertion_prefix) { $insertion_prefix } else { "" }
|
|
943
|
+
let prefix_length = ($insertion_prefix | str length)
|
|
944
|
+
let pattern = if $prefix_length > 0 { $pattern | str substring $prefix_length.. } else { $pattern }
|
|
945
|
+
let prefix = if $prefix_length > 0 { $prefix | str substring $prefix_length.. } else { $prefix }
|
|
946
|
+
|
|
876
947
|
# When a pattern is specified, use it as the glob base instead of
|
|
877
948
|
# the user-typed prefix. If the user has already typed beyond the
|
|
878
949
|
# pattern (e.g., pattern="src/" and prefix="src/ma"), keep the
|
|
@@ -1018,7 +1089,7 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1018
1089
|
} else {
|
|
1019
1090
|
$dir_prefix + ($item.name | path basename)
|
|
1020
1091
|
}
|
|
1021
|
-
{ value: $name }
|
|
1092
|
+
{ value: ($insertion_prefix + $name) }
|
|
1022
1093
|
}
|
|
1023
1094
|
} else {
|
|
1024
1095
|
# Regular literal completion - split by tab
|
|
@@ -1075,7 +1146,7 @@ ${functionName}-external
|
|
|
1075
1146
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
1076
1147
|
const description = suggestion.description == null ? "" : sanitizeForTransport(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
1077
1148
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
1078
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
1149
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t${description}`;
|
|
1079
1150
|
}
|
|
1080
1151
|
i++;
|
|
1081
1152
|
}
|
|
@@ -1142,11 +1213,17 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1142
1213
|
if (\$line -match '^__FILE__:') {
|
|
1143
1214
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
1144
1215
|
\$directive = (\$line -split "\`t")[0]
|
|
1145
|
-
\$parts = \$directive -split ':',
|
|
1216
|
+
\$parts = \$directive -split ':', 6
|
|
1146
1217
|
\$type = \$parts[1]
|
|
1147
1218
|
\$extensions = \$parts[2]
|
|
1148
1219
|
\$pattern = \$parts[3] -replace '%3A', ':' -replace '%25', '%'
|
|
1149
1220
|
\$hidden = \$parts[4] -eq '1'
|
|
1221
|
+
\$insertionPrefix = if (\$parts.Length -gt 5) { \$parts[5] -replace '%3A', ':' -replace '%25', '%' } else { '' }
|
|
1222
|
+
\$matchingWord = \$wordToComplete
|
|
1223
|
+
if (\$insertionPrefix -and \$pattern.StartsWith(\$insertionPrefix, [System.StringComparison]::Ordinal) -and \$matchingWord.StartsWith(\$insertionPrefix, [System.StringComparison]::Ordinal)) {
|
|
1224
|
+
\$pattern = \$pattern.Substring(\$insertionPrefix.Length)
|
|
1225
|
+
\$matchingWord = \$matchingWord.Substring(\$insertionPrefix.Length)
|
|
1226
|
+
} else { \$insertionPrefix = '' }
|
|
1150
1227
|
|
|
1151
1228
|
# When a pattern is specified, use it as the file matching
|
|
1152
1229
|
# prefix instead of the current word. If the user has
|
|
@@ -1155,7 +1232,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1155
1232
|
# comparing so that Windows backslashes match forward slashes
|
|
1156
1233
|
# in the transported pattern.
|
|
1157
1234
|
\$normalizedPattern = if (\$pattern) { \$pattern.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1158
|
-
\$normalizedWord = if (\$
|
|
1235
|
+
\$normalizedWord = if (\$matchingWord) { \$matchingWord.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1159
1236
|
\$comparison = if (\$IsWindows) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
1160
1237
|
# For wildcard patterns, compare only the directory prefix
|
|
1161
1238
|
\$comparePattern = \$normalizedPattern
|
|
@@ -1167,12 +1244,12 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1167
1244
|
\$comparePattern = ''
|
|
1168
1245
|
}
|
|
1169
1246
|
}
|
|
1170
|
-
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$
|
|
1171
|
-
\$
|
|
1247
|
+
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$matchingWord -ne \$pattern) {
|
|
1248
|
+
\$matchingWord
|
|
1172
1249
|
} elseif (\$pattern) {
|
|
1173
1250
|
\$pattern
|
|
1174
|
-
} elseif (\$
|
|
1175
|
-
\$
|
|
1251
|
+
} elseif (\$matchingWord) {
|
|
1252
|
+
\$matchingWord
|
|
1176
1253
|
} else { '' }
|
|
1177
1254
|
|
|
1178
1255
|
# Use -Force to include hidden files when requested, or when
|
|
@@ -1247,7 +1324,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1247
1324
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1248
1325
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
1249
1326
|
[System.Management.Automation.CompletionResult]::new(
|
|
1250
|
-
\$completionText,
|
|
1327
|
+
(\$insertionPrefix + \$completionText),
|
|
1251
1328
|
\$completionText,
|
|
1252
1329
|
'ParameterValue',
|
|
1253
1330
|
\$itemType
|
|
@@ -1287,7 +1364,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1287
1364
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
1288
1365
|
const description = suggestion.description == null ? "" : sanitizeForTransport(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
1289
1366
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
1290
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t[file]\t${description}`;
|
|
1367
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t[file]\t${description}`;
|
|
1291
1368
|
}
|
|
1292
1369
|
i++;
|
|
1293
1370
|
}
|
package/dist/completion.js
CHANGED
|
@@ -40,6 +40,12 @@ function validateProgramName(programName) {
|
|
|
40
40
|
function encodePattern(pattern) {
|
|
41
41
|
return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
42
42
|
}
|
|
43
|
+
/** Encodes optional literal insertion metadata without changing old directives. */
|
|
44
|
+
function encodeFilePrefix(suggestion) {
|
|
45
|
+
const prefix = suggestion.prefix ?? "";
|
|
46
|
+
if (prefix === "" || !(suggestion.pattern ?? "").startsWith(prefix) || sanitizeForTransport(prefix) !== prefix) return "";
|
|
47
|
+
return `:${encodePattern(prefix)}`;
|
|
48
|
+
}
|
|
43
49
|
function encodeExtensions(extensions) {
|
|
44
50
|
return extensions?.map((ext) => ext.replace(/^\./, "")).join(",") ?? "";
|
|
45
51
|
}
|
|
@@ -69,13 +75,32 @@ function _${programName} () {
|
|
|
69
75
|
COMPREPLY=()
|
|
70
76
|
local current="\${COMP_WORDS[COMP_CWORD]}"
|
|
71
77
|
local prev=("\${COMP_WORDS[@]:1:COMP_CWORD-1}")
|
|
78
|
+
local __prefix_aware=0
|
|
72
79
|
while IFS= read -r line; do
|
|
73
80
|
if [[ "$line" == __FILE__:* ]]; then
|
|
74
81
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
75
|
-
|
|
82
|
+
local insertion_prefix
|
|
83
|
+
IFS=':' read -r _ type extensions pattern hidden insertion_prefix <<< "$line"
|
|
76
84
|
extensions="\${extensions//,\\./,}"; extensions="\${extensions#.}"
|
|
77
85
|
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
78
86
|
|
|
87
|
+
insertion_prefix="\${insertion_prefix//%3A/:}"; insertion_prefix="\${insertion_prefix//%25/%}"
|
|
88
|
+
local __saved_current="$current"
|
|
89
|
+
local -a __saved_reply=()
|
|
90
|
+
if [[ -n "$insertion_prefix" && "$pattern" == "$insertion_prefix"* && "$current" == "$insertion_prefix"* ]]; then
|
|
91
|
+
pattern="\${pattern#"$insertion_prefix"}"
|
|
92
|
+
current="\${current#"$insertion_prefix"}"
|
|
93
|
+
__prefix_aware=1
|
|
94
|
+
else
|
|
95
|
+
insertion_prefix=""
|
|
96
|
+
fi
|
|
97
|
+
# Inserted keys are not filesystem paths. Once present, keep earlier
|
|
98
|
+
# replies out of later file filters; retain legacy-only aggregation.
|
|
99
|
+
if [[ "$__prefix_aware" == "1" ]]; then
|
|
100
|
+
__saved_reply=("\${COMPREPLY[@]}")
|
|
101
|
+
COMPREPLY=()
|
|
102
|
+
fi
|
|
103
|
+
|
|
79
104
|
# Save and adjust glob/shell options for safe file completion
|
|
80
105
|
local __dotglob_was_set=0 __failglob_was_set=0 __noglob_was_set=0
|
|
81
106
|
local __globignore_was_set=0 __saved_globignore="\${GLOBIGNORE-}"
|
|
@@ -274,6 +299,16 @@ function _${programName} () {
|
|
|
274
299
|
done
|
|
275
300
|
COMPREPLY=("\${filtered[@]}")
|
|
276
301
|
fi
|
|
302
|
+
if [[ -n "$insertion_prefix" ]]; then
|
|
303
|
+
local __prefix_index
|
|
304
|
+
for __prefix_index in "\${!COMPREPLY[@]}"; do
|
|
305
|
+
COMPREPLY[$__prefix_index]="$insertion_prefix\${COMPREPLY[$__prefix_index]}"
|
|
306
|
+
done
|
|
307
|
+
fi
|
|
308
|
+
if [[ "$__prefix_aware" == "1" ]]; then
|
|
309
|
+
COMPREPLY=("\${__saved_reply[@]}" "\${COMPREPLY[@]}")
|
|
310
|
+
fi
|
|
311
|
+
current="$__saved_current"
|
|
277
312
|
else
|
|
278
313
|
# Regular literal completion
|
|
279
314
|
COMPREPLY+=("$line")
|
|
@@ -293,7 +328,7 @@ complete -F _${programName} -- ${programName}
|
|
|
293
328
|
const extensions = encodeExtensions(suggestion.extensions);
|
|
294
329
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
295
330
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
296
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}`;
|
|
331
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}`;
|
|
297
332
|
}
|
|
298
333
|
i++;
|
|
299
334
|
}
|
|
@@ -343,9 +378,20 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
343
378
|
|
|
344
379
|
if [[ "\$value" == __FILE__:* ]]; then
|
|
345
380
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
346
|
-
local type extensions pattern hidden
|
|
347
|
-
IFS=':' read -r _ type extensions pattern hidden <<< "\$value"
|
|
348
|
-
|
|
381
|
+
local type extensions pattern hidden insertion_prefix
|
|
382
|
+
IFS=':' read -r _ type extensions pattern hidden insertion_prefix <<< "\$value"
|
|
383
|
+
local __decoded_pattern="\$pattern"
|
|
384
|
+
__decoded_pattern="\${__decoded_pattern//\\%3A/:}"; __decoded_pattern="\${__decoded_pattern//\\%25/%}"
|
|
385
|
+
insertion_prefix="\${insertion_prefix//\\%3A/:}"; insertion_prefix="\${insertion_prefix//\\%25/%}"
|
|
386
|
+
local __saved_prefix="\$PREFIX" __saved_iprefix="\$IPREFIX"
|
|
387
|
+
if [[ -n "\$insertion_prefix" && "\$__decoded_pattern" == "\$insertion_prefix"* && "\$PREFIX" == "\$insertion_prefix"* ]]; then
|
|
388
|
+
pattern="\${__decoded_pattern#"\$insertion_prefix"}"
|
|
389
|
+
PREFIX="\${PREFIX#"\$insertion_prefix"}"
|
|
390
|
+
IPREFIX="\$IPREFIX\$insertion_prefix"
|
|
391
|
+
else
|
|
392
|
+
# Preserve legacy decoding for directives without active metadata.
|
|
393
|
+
pattern="\${pattern//%3A/:}"; pattern="\${pattern//%25/%}"
|
|
394
|
+
fi
|
|
349
395
|
has_file_completion=1
|
|
350
396
|
|
|
351
397
|
# Native completion helpers expect normal zsh glob grouping even if
|
|
@@ -364,7 +410,6 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
364
410
|
# _directories enumerate the pattern's directory instead of the
|
|
365
411
|
# current word. If the user has already typed beyond the pattern,
|
|
366
412
|
# keep PREFIX unchanged for incremental narrowing.
|
|
367
|
-
local __saved_prefix="\$PREFIX"
|
|
368
413
|
if [[ -n "\$pattern" ]]; then
|
|
369
414
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
370
415
|
local __norm_prefix="\${PREFIX#./}"
|
|
@@ -421,6 +466,7 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
421
466
|
|
|
422
467
|
# Restore PREFIX and glob_dots to their previous state
|
|
423
468
|
PREFIX="\$__saved_prefix"
|
|
469
|
+
IPREFIX="\$__saved_iprefix"
|
|
424
470
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
425
471
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
426
472
|
else
|
|
@@ -463,7 +509,7 @@ fi
|
|
|
463
509
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
464
510
|
const description = suggestion.description == null ? "" : sanitizeForTransport(formatMessage(suggestion.description, { colors: false }));
|
|
465
511
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
466
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\0${description}\0`;
|
|
512
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\0${description}\0`;
|
|
467
513
|
}
|
|
468
514
|
}
|
|
469
515
|
};
|
|
@@ -501,6 +547,21 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
501
547
|
set -l extensions $parts[3]
|
|
502
548
|
set -l pattern (string replace -a '%25' '%' -- (string replace -a '%3A' ':' -- $parts[4]))
|
|
503
549
|
set -l hidden $parts[5]
|
|
550
|
+
set -l insertion_prefix ""
|
|
551
|
+
if test (count $parts) -ge 6
|
|
552
|
+
set insertion_prefix (string replace -a '%25' '%' -- (string replace -a '%3A' ':' -- $parts[6]))
|
|
553
|
+
end
|
|
554
|
+
set -l saved_current "$current"
|
|
555
|
+
set -l prefix_length (string length -- "$insertion_prefix")
|
|
556
|
+
if test $prefix_length -gt 0
|
|
557
|
+
and test (string sub -l $prefix_length -- "$pattern") = "$insertion_prefix"
|
|
558
|
+
and test (string sub -l $prefix_length -- "$current") = "$insertion_prefix"
|
|
559
|
+
set -l start (math $prefix_length + 1)
|
|
560
|
+
set pattern (string sub -s $start -- "$pattern")
|
|
561
|
+
set current (string sub -s $start -- "$current")
|
|
562
|
+
else
|
|
563
|
+
set insertion_prefix ""
|
|
564
|
+
end
|
|
504
565
|
|
|
505
566
|
# When a pattern is specified, use it as the glob base instead
|
|
506
567
|
# of the current word. If the user has already typed beyond the
|
|
@@ -722,8 +783,9 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
722
783
|
|
|
723
784
|
# Output file completions
|
|
724
785
|
for item in $items
|
|
725
|
-
echo $item
|
|
786
|
+
echo "$insertion_prefix$item"
|
|
726
787
|
end
|
|
788
|
+
set current "$saved_current"
|
|
727
789
|
else
|
|
728
790
|
# Regular literal completion - split by tab
|
|
729
791
|
set -l parts (string split \\t -- $line)
|
|
@@ -753,7 +815,7 @@ complete -c ${programName} -f -a '(${functionName})'
|
|
|
753
815
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
754
816
|
const description = suggestion.description == null ? "" : sanitizeForTransport(formatMessage(suggestion.description, { colors: false }));
|
|
755
817
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
756
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
818
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t${description}`;
|
|
757
819
|
}
|
|
758
820
|
i++;
|
|
759
821
|
}
|
|
@@ -866,6 +928,10 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
866
928
|
let pattern = ($parts | get 3 | str replace -a '%3A' ':' | str replace -a '%25' '%')
|
|
867
929
|
let hidden = ($parts | get 4) == '1'
|
|
868
930
|
|
|
931
|
+
let insertion_prefix = if ($parts | length) > 5 {
|
|
932
|
+
$parts | get 5 | str replace -a '%3A' ':' | str replace -a '%25' '%'
|
|
933
|
+
} else { "" }
|
|
934
|
+
|
|
869
935
|
# Extract prefix from the last argument if it exists
|
|
870
936
|
let prefix = if ($final_args | length) > 0 {
|
|
871
937
|
$final_args | last
|
|
@@ -873,6 +939,11 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
873
939
|
""
|
|
874
940
|
}
|
|
875
941
|
|
|
942
|
+
let insertion_prefix = if ($insertion_prefix | is-not-empty) and ($pattern | str starts-with $insertion_prefix) and ($prefix | str starts-with $insertion_prefix) { $insertion_prefix } else { "" }
|
|
943
|
+
let prefix_length = ($insertion_prefix | str length)
|
|
944
|
+
let pattern = if $prefix_length > 0 { $pattern | str substring $prefix_length.. } else { $pattern }
|
|
945
|
+
let prefix = if $prefix_length > 0 { $prefix | str substring $prefix_length.. } else { $prefix }
|
|
946
|
+
|
|
876
947
|
# When a pattern is specified, use it as the glob base instead of
|
|
877
948
|
# the user-typed prefix. If the user has already typed beyond the
|
|
878
949
|
# pattern (e.g., pattern="src/" and prefix="src/ma"), keep the
|
|
@@ -1018,7 +1089,7 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1018
1089
|
} else {
|
|
1019
1090
|
$dir_prefix + ($item.name | path basename)
|
|
1020
1091
|
}
|
|
1021
|
-
{ value: $name }
|
|
1092
|
+
{ value: ($insertion_prefix + $name) }
|
|
1022
1093
|
}
|
|
1023
1094
|
} else {
|
|
1024
1095
|
# Regular literal completion - split by tab
|
|
@@ -1075,7 +1146,7 @@ ${functionName}-external
|
|
|
1075
1146
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
1076
1147
|
const description = suggestion.description == null ? "" : sanitizeForTransport(formatMessage(suggestion.description, { colors: false }));
|
|
1077
1148
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
1078
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
|
|
1149
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t${description}`;
|
|
1079
1150
|
}
|
|
1080
1151
|
i++;
|
|
1081
1152
|
}
|
|
@@ -1142,11 +1213,17 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1142
1213
|
if (\$line -match '^__FILE__:') {
|
|
1143
1214
|
# Parse file completion directive: __FILE__:type:extensions:pattern:hidden
|
|
1144
1215
|
\$directive = (\$line -split "\`t")[0]
|
|
1145
|
-
\$parts = \$directive -split ':',
|
|
1216
|
+
\$parts = \$directive -split ':', 6
|
|
1146
1217
|
\$type = \$parts[1]
|
|
1147
1218
|
\$extensions = \$parts[2]
|
|
1148
1219
|
\$pattern = \$parts[3] -replace '%3A', ':' -replace '%25', '%'
|
|
1149
1220
|
\$hidden = \$parts[4] -eq '1'
|
|
1221
|
+
\$insertionPrefix = if (\$parts.Length -gt 5) { \$parts[5] -replace '%3A', ':' -replace '%25', '%' } else { '' }
|
|
1222
|
+
\$matchingWord = \$wordToComplete
|
|
1223
|
+
if (\$insertionPrefix -and \$pattern.StartsWith(\$insertionPrefix, [System.StringComparison]::Ordinal) -and \$matchingWord.StartsWith(\$insertionPrefix, [System.StringComparison]::Ordinal)) {
|
|
1224
|
+
\$pattern = \$pattern.Substring(\$insertionPrefix.Length)
|
|
1225
|
+
\$matchingWord = \$matchingWord.Substring(\$insertionPrefix.Length)
|
|
1226
|
+
} else { \$insertionPrefix = '' }
|
|
1150
1227
|
|
|
1151
1228
|
# When a pattern is specified, use it as the file matching
|
|
1152
1229
|
# prefix instead of the current word. If the user has
|
|
@@ -1155,7 +1232,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1155
1232
|
# comparing so that Windows backslashes match forward slashes
|
|
1156
1233
|
# in the transported pattern.
|
|
1157
1234
|
\$normalizedPattern = if (\$pattern) { \$pattern.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1158
|
-
\$normalizedWord = if (\$
|
|
1235
|
+
\$normalizedWord = if (\$matchingWord) { \$matchingWord.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1159
1236
|
\$comparison = if (\$IsWindows) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
1160
1237
|
# For wildcard patterns, compare only the directory prefix
|
|
1161
1238
|
\$comparePattern = \$normalizedPattern
|
|
@@ -1167,12 +1244,12 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1167
1244
|
\$comparePattern = ''
|
|
1168
1245
|
}
|
|
1169
1246
|
}
|
|
1170
|
-
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$
|
|
1171
|
-
\$
|
|
1247
|
+
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$matchingWord -ne \$pattern) {
|
|
1248
|
+
\$matchingWord
|
|
1172
1249
|
} elseif (\$pattern) {
|
|
1173
1250
|
\$pattern
|
|
1174
|
-
} elseif (\$
|
|
1175
|
-
\$
|
|
1251
|
+
} elseif (\$matchingWord) {
|
|
1252
|
+
\$matchingWord
|
|
1176
1253
|
} else { '' }
|
|
1177
1254
|
|
|
1178
1255
|
# Use -Force to include hidden files when requested, or when
|
|
@@ -1247,7 +1324,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1247
1324
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1248
1325
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
1249
1326
|
[System.Management.Automation.CompletionResult]::new(
|
|
1250
|
-
\$completionText,
|
|
1327
|
+
(\$insertionPrefix + \$completionText),
|
|
1251
1328
|
\$completionText,
|
|
1252
1329
|
'ParameterValue',
|
|
1253
1330
|
\$itemType
|
|
@@ -1287,7 +1364,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1287
1364
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
1288
1365
|
const description = suggestion.description == null ? "" : sanitizeForTransport(formatMessage(suggestion.description, { colors: false }));
|
|
1289
1366
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
1290
|
-
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t[file]\t${description}`;
|
|
1367
|
+
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}${encodeFilePrefix(suggestion)}\t[file]\t${description}`;
|
|
1291
1368
|
}
|
|
1292
1369
|
i++;
|
|
1293
1370
|
}
|
|
@@ -637,6 +637,14 @@ type Suggestion = {
|
|
|
637
637
|
* The current prefix/pattern for fallback when native completion is unavailable.
|
|
638
638
|
*/
|
|
639
639
|
readonly pattern?: string;
|
|
640
|
+
/**
|
|
641
|
+
* Literal text at the start of `pattern` that is preserved on insertion
|
|
642
|
+
* but excluded from native filesystem matching, such as `output=`.
|
|
643
|
+
* Shell adapters ignore it if it does not lead both the pattern and
|
|
644
|
+
* the current word. Regenerate installed scripts to use this field.
|
|
645
|
+
* @since 1.1.13
|
|
646
|
+
*/
|
|
647
|
+
readonly prefix?: string;
|
|
640
648
|
/**
|
|
641
649
|
* The type of file system entries to complete.
|
|
642
650
|
*/
|
|
@@ -637,6 +637,14 @@ type Suggestion = {
|
|
|
637
637
|
* The current prefix/pattern for fallback when native completion is unavailable.
|
|
638
638
|
*/
|
|
639
639
|
readonly pattern?: string;
|
|
640
|
+
/**
|
|
641
|
+
* Literal text at the start of `pattern` that is preserved on insertion
|
|
642
|
+
* but excluded from native filesystem matching, such as `output=`.
|
|
643
|
+
* Shell adapters ignore it if it does not lead both the pattern and
|
|
644
|
+
* the current word. Regenerate installed scripts to use this field.
|
|
645
|
+
* @since 1.1.13
|
|
646
|
+
*/
|
|
647
|
+
readonly prefix?: string;
|
|
640
648
|
/**
|
|
641
649
|
* The type of file system entries to complete.
|
|
642
650
|
*/
|
package/dist/modifiers.cjs
CHANGED
|
@@ -1311,6 +1311,7 @@ function multiple(parser, options = {}) {
|
|
|
1311
1311
|
"file",
|
|
1312
1312
|
suggestion.type,
|
|
1313
1313
|
suggestion.pattern ?? "",
|
|
1314
|
+
suggestion.prefix ?? "",
|
|
1314
1315
|
suggestion.includeHidden === true,
|
|
1315
1316
|
suggestion.extensions == null ? "" : suggestion.extensions.toSorted().join("\0"),
|
|
1316
1317
|
description
|
package/dist/modifiers.js
CHANGED
|
@@ -1311,6 +1311,7 @@ function multiple(parser, options = {}) {
|
|
|
1311
1311
|
"file",
|
|
1312
1312
|
suggestion.type,
|
|
1313
1313
|
suggestion.pattern ?? "",
|
|
1314
|
+
suggestion.prefix ?? "",
|
|
1314
1315
|
suggestion.includeHidden === true,
|
|
1315
1316
|
suggestion.extensions == null ? "" : suggestion.extensions.toSorted().join("\0"),
|
|
1316
1317
|
description
|
package/dist/suggestion.cjs
CHANGED
|
@@ -257,22 +257,27 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
257
257
|
*
|
|
258
258
|
* For literal suggestions, the text itself is used as the key.
|
|
259
259
|
* For file suggestions, a composite key is created from the type,
|
|
260
|
-
* extensions, and
|
|
260
|
+
* extensions, pattern, and insertion prefix.
|
|
261
261
|
*
|
|
262
262
|
* @param suggestion The suggestion to create a key for
|
|
263
263
|
* @returns A string key that uniquely identifies this suggestion
|
|
264
264
|
* @internal
|
|
265
265
|
*/
|
|
266
266
|
function getSuggestionKey(suggestion) {
|
|
267
|
-
if (suggestion.kind === "literal") return suggestion.text
|
|
268
|
-
return `
|
|
267
|
+
if (suggestion.kind === "literal") return `literal:${suggestion.text}`;
|
|
268
|
+
return `file:${JSON.stringify([
|
|
269
|
+
suggestion.type,
|
|
270
|
+
suggestion.extensions?.toSorted().join(",") ?? "",
|
|
271
|
+
suggestion.pattern ?? "",
|
|
272
|
+
suggestion.prefix ?? ""
|
|
273
|
+
])}`;
|
|
269
274
|
}
|
|
270
275
|
/**
|
|
271
276
|
* Removes duplicate suggestions from an array while preserving order.
|
|
272
277
|
*
|
|
273
278
|
* Suggestions are considered duplicates if they have the same key:
|
|
274
279
|
* - Literal suggestions: same text
|
|
275
|
-
* - File suggestions: same type, extensions, and
|
|
280
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
276
281
|
*
|
|
277
282
|
* The first occurrence of each unique suggestion is kept. For file
|
|
278
283
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
package/dist/suggestion.d.cts
CHANGED
|
@@ -159,7 +159,7 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
159
159
|
*
|
|
160
160
|
* Suggestions are considered duplicates if they have the same key:
|
|
161
161
|
* - Literal suggestions: same text
|
|
162
|
-
* - File suggestions: same type, extensions, and
|
|
162
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
163
163
|
*
|
|
164
164
|
* The first occurrence of each unique suggestion is kept. For file
|
|
165
165
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
package/dist/suggestion.d.ts
CHANGED
|
@@ -159,7 +159,7 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
159
159
|
*
|
|
160
160
|
* Suggestions are considered duplicates if they have the same key:
|
|
161
161
|
* - Literal suggestions: same text
|
|
162
|
-
* - File suggestions: same type, extensions, and
|
|
162
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
163
163
|
*
|
|
164
164
|
* The first occurrence of each unique suggestion is kept. For file
|
|
165
165
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
package/dist/suggestion.js
CHANGED
|
@@ -257,22 +257,27 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
257
257
|
*
|
|
258
258
|
* For literal suggestions, the text itself is used as the key.
|
|
259
259
|
* For file suggestions, a composite key is created from the type,
|
|
260
|
-
* extensions, and
|
|
260
|
+
* extensions, pattern, and insertion prefix.
|
|
261
261
|
*
|
|
262
262
|
* @param suggestion The suggestion to create a key for
|
|
263
263
|
* @returns A string key that uniquely identifies this suggestion
|
|
264
264
|
* @internal
|
|
265
265
|
*/
|
|
266
266
|
function getSuggestionKey(suggestion) {
|
|
267
|
-
if (suggestion.kind === "literal") return suggestion.text
|
|
268
|
-
return `
|
|
267
|
+
if (suggestion.kind === "literal") return `literal:${suggestion.text}`;
|
|
268
|
+
return `file:${JSON.stringify([
|
|
269
|
+
suggestion.type,
|
|
270
|
+
suggestion.extensions?.toSorted().join(",") ?? "",
|
|
271
|
+
suggestion.pattern ?? "",
|
|
272
|
+
suggestion.prefix ?? ""
|
|
273
|
+
])}`;
|
|
269
274
|
}
|
|
270
275
|
/**
|
|
271
276
|
* Removes duplicate suggestions from an array while preserving order.
|
|
272
277
|
*
|
|
273
278
|
* Suggestions are considered duplicates if they have the same key:
|
|
274
279
|
* - Literal suggestions: same text
|
|
275
|
-
* - File suggestions: same type, extensions, and
|
|
280
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
276
281
|
*
|
|
277
282
|
* The first occurrence of each unique suggestion is kept. For file
|
|
278
283
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
package/dist/valueparser.cjs
CHANGED
|
@@ -602,7 +602,8 @@ function prefixKeyValueSuggestion(suggestion, prefix, suffix = "") {
|
|
|
602
602
|
const text$1 = `${prefix}${textValue}${suffix}`;
|
|
603
603
|
if (suggestion.kind === "file" && suffix === "") return {
|
|
604
604
|
...suggestion,
|
|
605
|
-
pattern: text$1
|
|
605
|
+
pattern: text$1,
|
|
606
|
+
prefix: `${prefix}${suggestion.prefix ?? ""}`
|
|
606
607
|
};
|
|
607
608
|
return suggestion.description == null ? {
|
|
608
609
|
kind: "literal",
|
package/dist/valueparser.js
CHANGED
|
@@ -602,7 +602,8 @@ function prefixKeyValueSuggestion(suggestion, prefix, suffix = "") {
|
|
|
602
602
|
const text$1 = `${prefix}${textValue}${suffix}`;
|
|
603
603
|
if (suggestion.kind === "file" && suffix === "") return {
|
|
604
604
|
...suggestion,
|
|
605
|
-
pattern: text$1
|
|
605
|
+
pattern: text$1,
|
|
606
|
+
prefix: `${prefix}${suggestion.prefix ?? ""}`
|
|
606
607
|
};
|
|
607
608
|
return suggestion.description == null ? {
|
|
608
609
|
kind: "literal",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.13-dev.2823",
|
|
4
4
|
"description": "Type-safe combinatorial command-line interface parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
},
|
|
197
197
|
"sideEffects": false,
|
|
198
198
|
"devDependencies": {
|
|
199
|
-
"@optique/env": "1.1.
|
|
199
|
+
"@optique/env": "1.1.13-dev.2823+d4c34d2f",
|
|
200
200
|
"@types/node": "^24.0.0",
|
|
201
201
|
"fast-check": "^4.7.0",
|
|
202
202
|
"tsdown": "^0.13.0",
|