@optique/core 1.0.15-dev.2854 → 1.0.15-dev.2880
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
CHANGED
|
@@ -120,7 +120,7 @@ function _${programName} () {
|
|
|
120
120
|
esac
|
|
121
121
|
fi
|
|
122
122
|
done
|
|
123
|
-
local __tilde_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)
|
|
123
|
+
local __tilde_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)?$'
|
|
124
124
|
local __tilde_glob_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)?$'
|
|
125
125
|
local prev=("\${COMP_WORDS[@]:1:COMP_CWORD-1}")
|
|
126
126
|
# Bash splits unquoted '=' at its default word breaks. Reassemble only
|
|
@@ -353,8 +353,9 @@ function _${programName} () {
|
|
|
353
353
|
|
|
354
354
|
# path() echoes the raw shell word as its pattern. Once dequoted,
|
|
355
355
|
# use the literal current-word prefix instead of the escaped pattern.
|
|
356
|
-
if [[
|
|
357
|
-
|
|
356
|
+
if [[ "$pattern" == "$__raw_file_current" && "$__file_current" != "$__raw_file_current" ]] ||
|
|
357
|
+
{ [[ -n "$__reply_prefix" ]] && [ "$pattern" = "$__file_current" ] &&
|
|
358
|
+
{ [[ "$pattern" != *[\*\?]* ]] || [ "$__raw_file_current" != "$__file_current" ]; }; }; then
|
|
358
359
|
pattern=""
|
|
359
360
|
fi
|
|
360
361
|
|
|
@@ -388,7 +389,8 @@ function _${programName} () {
|
|
|
388
389
|
# However, if the user has already typed beyond the pattern (e.g.,
|
|
389
390
|
# pattern="src/" and current="src/ma"), preserve the typed suffix
|
|
390
391
|
# for incremental filtering.
|
|
391
|
-
local __from_pattern=0
|
|
392
|
+
local __from_pattern=0 __filter_pattern=0 __pattern_prefix=""
|
|
393
|
+
local __pattern_tilde_prefix="" __pattern_tilde_expanded=""
|
|
392
394
|
if [[ -n "$pattern" ]]; then
|
|
393
395
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
394
396
|
local __norm_current="\${__file_current#./}"
|
|
@@ -400,10 +402,27 @@ function _${programName} () {
|
|
|
400
402
|
__compare_pattern="\${__compare_pattern%%[\*\?]*}"
|
|
401
403
|
[[ "$__compare_pattern" == */* ]] && __compare_pattern="\${__compare_pattern%/*}/" || __compare_pattern=""
|
|
402
404
|
fi
|
|
405
|
+
local __extends_pattern=0
|
|
403
406
|
if [[ ( -n "$__compare_pattern" || -n "$__norm_current" ) && \${#__norm_current} -ge \${#__compare_pattern} && "\${__norm_current:0:\${#__compare_pattern}}" == "$__compare_pattern" && "$__file_current" != "$pattern" ]]; then
|
|
407
|
+
__extends_pattern=1
|
|
408
|
+
fi
|
|
409
|
+
if (( __extends_pattern )) && [[ "$__norm_pattern" != *[\*\?]* ]]; then
|
|
404
410
|
# User has typed beyond or an equivalent form of the pattern
|
|
405
411
|
true
|
|
406
412
|
else
|
|
413
|
+
# Wildcards keep their file filter while the typed prefix narrows
|
|
414
|
+
# the resulting paths. Save the expanded prefix before using pattern.
|
|
415
|
+
if [[ "$__norm_pattern" == *[\*\?]* ]]; then
|
|
416
|
+
__filter_pattern=1
|
|
417
|
+
__pattern_prefix="$__glob_current"
|
|
418
|
+
# An unquoted value equal to its wildcard is a complete search
|
|
419
|
+
# pattern, not a literal filename prefix to filter against.
|
|
420
|
+
if [ "$__file_current" = "$pattern" ] && [ "$__raw_file_current" = "$__file_current" ]; then
|
|
421
|
+
__pattern_prefix=""
|
|
422
|
+
fi
|
|
423
|
+
__pattern_tilde_prefix="$__tilde_prefix"
|
|
424
|
+
__pattern_tilde_expanded="$__tilde_expanded"
|
|
425
|
+
fi
|
|
407
426
|
__from_pattern=1
|
|
408
427
|
# Reset tilde state from the current-word expansion so that a
|
|
409
428
|
# non-tilde pattern is not rewritten through stale tilde state
|
|
@@ -432,36 +451,93 @@ function _${programName} () {
|
|
|
432
451
|
# This runs after tilde expansion so that paths like ~/.config/ are
|
|
433
452
|
# checked against the expanded path, not the literal ~ string.
|
|
434
453
|
local __inside_hidden_path=0
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
454
|
+
local __hidden_directory_pattern='/[.]([^./][^/]*|[.][^/]+)/'
|
|
455
|
+
if [[ "/\${__glob_current%/}/" =~ $__hidden_directory_pattern ||
|
|
456
|
+
"/\${__pattern_prefix%/}/" =~ $__hidden_directory_pattern ]]; then
|
|
457
|
+
__inside_hidden_path=1
|
|
458
|
+
fi
|
|
438
459
|
# Also check if the current prefix explicitly targets hidden entries
|
|
439
460
|
# (e.g., user typed "." or ".e" to complete .env)
|
|
440
461
|
local __prefix_targets_hidden=0
|
|
441
462
|
local __prefix_base="\${__glob_current##*/}"
|
|
463
|
+
if [[ -n "$__pattern_prefix" ]]; then __prefix_base="\${__pattern_prefix##*/}"; fi
|
|
442
464
|
[[ "$__prefix_base" == .* ]] && __prefix_targets_hidden=1
|
|
443
465
|
if [[ "$hidden" == "1" || "$__inside_hidden_path" == "1" || "$__prefix_targets_hidden" == "1" ]]; then shopt -s dotglob; fi
|
|
444
466
|
|
|
445
467
|
# Pre-expand file candidates. When the glob base came from the
|
|
446
468
|
# program's pattern and contains wildcard characters (* or ?),
|
|
447
|
-
#
|
|
469
|
+
# expand it as data with shell globbing. Empty IFS avoids splitting paths;
|
|
470
|
+
# NUL records preserve spaces and newlines without requiring mapfile.
|
|
471
|
+
# Shell expansion respects dotglob on older Bash versions as well.
|
|
448
472
|
# Otherwise append * to treat it as a prefix.
|
|
449
|
-
#
|
|
450
|
-
#
|
|
473
|
+
# Only * and ? are wildcard syntax; escape brackets, backslashes, and
|
|
474
|
+
# extended-glob operators so other pattern characters stay literal.
|
|
451
475
|
local -a __candidates=()
|
|
452
476
|
if [[ "$__from_pattern" == "1" && "$__glob_current" == *[\*\?]* ]]; then
|
|
453
|
-
|
|
477
|
+
local __pathname_glob="" __glob_char __glob_index
|
|
478
|
+
for ((__glob_index=0; __glob_index<\${#__glob_current}; __glob_index++)); do
|
|
479
|
+
__glob_char="\${__glob_current:__glob_index:1}"
|
|
480
|
+
case "$__glob_char" in
|
|
481
|
+
'['|']'|'\\'|'('|')'|'|'|'+'|'@'|'!')
|
|
482
|
+
__pathname_glob+="\\\\$__glob_char" ;;
|
|
483
|
+
*) __pathname_glob+="$__glob_char" ;;
|
|
484
|
+
esac
|
|
485
|
+
done
|
|
486
|
+
[[ "$type" == "directory" ]] && __pathname_glob="\${__pathname_glob%/}"
|
|
487
|
+
local __glob_dir="\${__pathname_glob%/*}"
|
|
488
|
+
[[ "$__glob_dir" == "$__pathname_glob" ]] && __glob_dir="."
|
|
489
|
+
[[ -z "$__glob_dir" ]] && __glob_dir="/"
|
|
490
|
+
local __navigation_dir="" __basename_glob="\${__pathname_glob##*/}"
|
|
491
|
+
# Once an offered directory is entered, keep the basename filter but
|
|
492
|
+
# enumerate there. Only descendants of matching pattern bases qualify.
|
|
493
|
+
if [[ "$__pattern_prefix" == */* ]]; then
|
|
494
|
+
local __typed_dir="\${__pattern_prefix%/*}" __base_dir __norm_base __norm_dir
|
|
495
|
+
# Resolve directory ancestry in subshells, preserving the typed spelling
|
|
496
|
+
# for insertion. The trailing slash also preserves newline-ending names.
|
|
497
|
+
if __norm_dir=$(CDPATH= builtin cd -P -- "$__typed_dir" >/dev/null 2>&1 && printf '%s/' "\${PWD%/}"); then
|
|
498
|
+
while IFS= read -r -d '' __base_dir; do
|
|
499
|
+
[[ -d "$__base_dir" ]] || continue
|
|
500
|
+
__norm_base=$(CDPATH= builtin cd -P -- "$__base_dir" >/dev/null 2>&1 && printf '%s/' "\${PWD%/}") || continue
|
|
501
|
+
if [ "\${__norm_dir:0:\${#__norm_base}}" = "$__norm_base" ]; then
|
|
502
|
+
__navigation_dir="$__typed_dir"
|
|
503
|
+
break
|
|
504
|
+
fi
|
|
505
|
+
done < <(IFS=; printf '%s\\0' $__glob_dir)
|
|
506
|
+
fi
|
|
507
|
+
fi
|
|
508
|
+
local __candidate
|
|
509
|
+
while IFS= read -r -d '' __candidate; do
|
|
510
|
+
if [[ -e "$__candidate" || -L "$__candidate" ]]; then
|
|
511
|
+
[[ -d "$__candidate" ]] && __candidate="\${__candidate%/}"
|
|
512
|
+
__candidates+=("$__candidate")
|
|
513
|
+
fi
|
|
514
|
+
done < <(
|
|
515
|
+
IFS=
|
|
516
|
+
if [[ -n "$__navigation_dir" ]]; then
|
|
517
|
+
printf '%s\\0' "$__navigation_dir"/$__basename_glob
|
|
518
|
+
else
|
|
519
|
+
printf '%s\\0' $__pathname_glob
|
|
520
|
+
fi
|
|
521
|
+
)
|
|
454
522
|
# For file/any modes, also include directories from the base
|
|
455
523
|
# directory for navigation even when the glob itself does not
|
|
456
524
|
# match directory names. Skip this for directory mode so that
|
|
457
525
|
# the pattern's basename filter is respected.
|
|
458
526
|
if [[ "$type" != "directory" ]]; then
|
|
459
|
-
local __glob_dir="\${__glob_current%/*}"
|
|
460
|
-
[[ "$__glob_dir" == "$__glob_current" ]] && __glob_dir="."
|
|
461
527
|
local __d
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
528
|
+
local __directory_pattern="\${__glob_dir%/}/*/"
|
|
529
|
+
while IFS= read -r -d '' __d; do
|
|
530
|
+
[[ -d "$__d" ]] || continue
|
|
531
|
+
__d="\${__d%/}"
|
|
532
|
+
__candidates+=("$__d")
|
|
533
|
+
done < <(
|
|
534
|
+
IFS=
|
|
535
|
+
if [[ -n "$__navigation_dir" ]]; then
|
|
536
|
+
printf '%s\\0' "$__navigation_dir"/*/
|
|
537
|
+
else
|
|
538
|
+
printf '%s\\0' $__directory_pattern
|
|
539
|
+
fi
|
|
540
|
+
)
|
|
465
541
|
fi
|
|
466
542
|
else
|
|
467
543
|
__candidates=("$__glob_current"*)
|
|
@@ -472,6 +548,26 @@ function _${programName} () {
|
|
|
472
548
|
fi
|
|
473
549
|
fi
|
|
474
550
|
|
|
551
|
+
if (( __filter_pattern )); then
|
|
552
|
+
local __nocasematch_was_set=0
|
|
553
|
+
shopt -q nocasematch && __nocasematch_was_set=1
|
|
554
|
+
if shopt -q nocaseglob; then shopt -s nocasematch; else shopt -u nocasematch; fi
|
|
555
|
+
local -a __filtered_candidates=()
|
|
556
|
+
for __candidate in "\${__candidates[@]}"; do
|
|
557
|
+
if [[ "$__candidate" == "$__pattern_prefix"* ||
|
|
558
|
+
"\${__candidate#./}" == "\${__pattern_prefix#./}"* ]]; then
|
|
559
|
+
if [[ "$__pattern_prefix" == ./* && "$__candidate" != ./* ]]; then
|
|
560
|
+
__candidate="./$__candidate"
|
|
561
|
+
elif [[ "$__pattern_prefix" != ./* && "$__pattern_prefix" != . ]]; then
|
|
562
|
+
__candidate="\${__candidate#./}"
|
|
563
|
+
fi
|
|
564
|
+
__filtered_candidates+=("$__candidate")
|
|
565
|
+
fi
|
|
566
|
+
done
|
|
567
|
+
if (( __nocasematch_was_set )); then shopt -s nocasematch; else shopt -u nocasematch; fi
|
|
568
|
+
__candidates=("\${__filtered_candidates[@]}")
|
|
569
|
+
fi
|
|
570
|
+
|
|
475
571
|
# Generate file completions based on type
|
|
476
572
|
case "$type" in
|
|
477
573
|
file)
|
|
@@ -533,6 +629,10 @@ function _${programName} () {
|
|
|
533
629
|
esac
|
|
534
630
|
|
|
535
631
|
# Restore tilde prefix in completion results
|
|
632
|
+
if (( __filter_pattern )); then
|
|
633
|
+
__tilde_prefix="$__pattern_tilde_prefix"
|
|
634
|
+
__tilde_expanded="$__pattern_tilde_expanded"
|
|
635
|
+
fi
|
|
536
636
|
if [[ -n "$__tilde_prefix" &&
|
|
537
637
|
( -z "$__quote" || $__file_quote_offset -gt 0 ) ]]; then
|
|
538
638
|
local __i
|
|
@@ -632,6 +732,31 @@ function _${programName} () {
|
|
|
632
732
|
COMPREPLY+=("\${__word:\${#__remove}}")
|
|
633
733
|
fi
|
|
634
734
|
done
|
|
735
|
+
if (( \${#COMPREPLY[@]} > 1 )) && command -v sort >/dev/null 2>&1; then
|
|
736
|
+
# Sort shell-escaped keys, not filenames: each key occupies one line even
|
|
737
|
+
# when a candidate contains tabs or newlines. Keep the first occurrence's
|
|
738
|
+
# index, then restore the original order without evaluating escaped data.
|
|
739
|
+
# This works without associative arrays or NUL-aware sort on Bash 3.2.
|
|
740
|
+
local -a __unique_replies=()
|
|
741
|
+
local __index __key __last_key=""
|
|
742
|
+
while IFS= read -r __index; do
|
|
743
|
+
__unique_replies+=("\${COMPREPLY[__index]}")
|
|
744
|
+
done < <(
|
|
745
|
+
for ((__index=0; __index<\${#COMPREPLY[@]}; __index++)); do
|
|
746
|
+
printf '%q\\t%d\\n' "\${COMPREPLY[__index]}" "$__index"
|
|
747
|
+
done |
|
|
748
|
+
LC_ALL=C sort -t $'\\t' -k1,1 -k2,2n |
|
|
749
|
+
{
|
|
750
|
+
while IFS=$'\\t' read -r __key __index; do
|
|
751
|
+
if [ "$__key" != "$__last_key" ]; then
|
|
752
|
+
printf '%s\\n' "$__index"
|
|
753
|
+
__last_key="$__key"
|
|
754
|
+
fi
|
|
755
|
+
done
|
|
756
|
+
} | LC_ALL=C sort -n
|
|
757
|
+
)
|
|
758
|
+
COMPREPLY=("\${__unique_replies[@]}")
|
|
759
|
+
fi
|
|
635
760
|
if [[ $__file_mode == 1 && $(type -t compopt) == builtin ]]; then
|
|
636
761
|
compopt -o filenames 2>/dev/null || true
|
|
637
762
|
fi
|
|
@@ -657,7 +782,7 @@ fi
|
|
|
657
782
|
}
|
|
658
783
|
for (const [index, { prefix, file }] of joined.files.entries()) {
|
|
659
784
|
if (index > 0) yield "\n";
|
|
660
|
-
yield `__JOINED_FILE__:${encodePattern(prefix)}:${file.type}:${encodeExtensions(file.extensions)}
|
|
785
|
+
yield `__JOINED_FILE__:${encodePattern(prefix)}:${file.type}:${encodeExtensions(file.extensions)}:${encodePattern(sanitizeForTransport(file.pattern ?? ""))}:${file.includeHidden ? "1" : "0"}`;
|
|
661
786
|
}
|
|
662
787
|
} else if (suggestion.kind === "literal") yield encodeBashLiteral(suggestion.text);
|
|
663
788
|
else {
|
|
@@ -683,6 +808,10 @@ const zsh = {
|
|
|
683
808
|
return `#compdef ${programName}
|
|
684
809
|
function ${functionName} () {
|
|
685
810
|
local current="\$words[CURRENT]"
|
|
811
|
+
# Let zsh match the suffix when completing in the middle of a word.
|
|
812
|
+
if [[ -o complete_in_word ]]; then
|
|
813
|
+
current="\$PREFIX"
|
|
814
|
+
fi
|
|
686
815
|
local -a prev
|
|
687
816
|
# Extract previous arguments, skipping empty ones
|
|
688
817
|
prev=()
|
|
@@ -730,6 +859,8 @@ function ${functionName} () {
|
|
|
730
859
|
local __was_sh_glob=0
|
|
731
860
|
[[ -o sh_glob ]] && __was_sh_glob=1
|
|
732
861
|
unsetopt sh_glob
|
|
862
|
+
local __was_extended_glob=0
|
|
863
|
+
[[ -o extended_glob ]] && __was_extended_glob=1
|
|
733
864
|
|
|
734
865
|
# Enable glob_dots when hidden files are requested so that
|
|
735
866
|
# _files and _directories include dot-prefixed entries
|
|
@@ -742,7 +873,34 @@ function ${functionName} () {
|
|
|
742
873
|
# current word. If the user has already typed beyond the pattern,
|
|
743
874
|
# keep PREFIX unchanged for incremental narrowing.
|
|
744
875
|
local __saved_prefix="\$PREFIX"
|
|
745
|
-
|
|
876
|
+
local __initial_file_pattern="" __initial_directory="" __initial_leaf=""
|
|
877
|
+
if [[ ( "\$type" == "file" || "\$type" == "any" || "\$type" == "directory" ) &&
|
|
878
|
+
-z "\$current\$PREFIX\$SUFFIX" &&
|
|
879
|
+
-z "\${compstate[pattern_match]}" &&
|
|
880
|
+
"\$pattern" != /* && "\$pattern" != "~"* ]]; then
|
|
881
|
+
if [[ "\$pattern" == */* ]]; then
|
|
882
|
+
__initial_directory="\${pattern%/*}/"
|
|
883
|
+
fi
|
|
884
|
+
__initial_leaf="\${pattern##*/}"
|
|
885
|
+
# Native -g filters are split as shell words and use extended glob
|
|
886
|
+
# syntax. Keep unsupported filename forms on the existing path.
|
|
887
|
+
if [[ "\$__initial_leaf" == *[\*\?]* &&
|
|
888
|
+
"\$__initial_directory" != *[\*\?]* &&
|
|
889
|
+
"\$__initial_directory" != *["[]()"]* &&
|
|
890
|
+
"\$__initial_leaf" != *["[]"]* &&
|
|
891
|
+
"\$__initial_leaf" != *'\\'* &&
|
|
892
|
+
"\$__initial_leaf" != *[[:space:]]* &&
|
|
893
|
+
"\$__initial_leaf" != *["#~^()|<>;&"]* &&
|
|
894
|
+
"\$__initial_leaf" != *"'"* && "\$__initial_leaf" != *'"'* ]]; then
|
|
895
|
+
__initial_file_pattern="\$__initial_leaf"
|
|
896
|
+
# Only the literal directory belongs in PREFIX. Quote it so
|
|
897
|
+
# spaces and shell metacharacters remain literal path text.
|
|
898
|
+
if [[ -n "\$__initial_directory" ]]; then
|
|
899
|
+
PREFIX="\${(q)__initial_directory}"
|
|
900
|
+
fi
|
|
901
|
+
fi
|
|
902
|
+
fi
|
|
903
|
+
if [[ -n "\$pattern" && -z "\$__initial_file_pattern" ]]; then
|
|
746
904
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
747
905
|
local __norm_prefix="\${PREFIX#./}"
|
|
748
906
|
local __norm_pattern="\${pattern#./}"
|
|
@@ -767,15 +925,84 @@ function ${functionName} () {
|
|
|
767
925
|
ext_pattern="*.\$extensions"
|
|
768
926
|
fi
|
|
769
927
|
fi
|
|
928
|
+
# _wanted/_next_label write to the named explanation parameter.
|
|
929
|
+
# Keep it local for every file directive, including directory/any.
|
|
930
|
+
local -a expl=()
|
|
770
931
|
# Use zsh's native file completion
|
|
771
932
|
case "\$type" in
|
|
772
|
-
file)
|
|
773
|
-
if [[ -n "\$
|
|
774
|
-
#
|
|
775
|
-
#
|
|
776
|
-
#
|
|
777
|
-
|
|
778
|
-
|
|
933
|
+
file|any)
|
|
934
|
+
if [[ "\$type" == "file" || -n "\$__initial_file_pattern" ]]; then
|
|
935
|
+
# Regular-file suggestions retain symlinked files; initial
|
|
936
|
+
# any globs use a non-directory qualifier instead.
|
|
937
|
+
local file_pattern="\${ext_pattern:-*}(#q-.)"
|
|
938
|
+
if [[ -n "\$__initial_file_pattern" ]]; then
|
|
939
|
+
local __file_qualifier="-."
|
|
940
|
+
[[ "\$type" == "any" ]] && __file_qualifier="^-/"
|
|
941
|
+
local -a __initial_file_patterns=()
|
|
942
|
+
if [[ -n "\$extensions" ]]; then
|
|
943
|
+
setopt extended_glob
|
|
944
|
+
local __initial_extension
|
|
945
|
+
for __initial_extension in "\${(@s:,:)extensions}"; do
|
|
946
|
+
__initial_file_patterns+=("(\${__initial_file_pattern}~^*.\${__initial_extension})(#q\${__file_qualifier})")
|
|
947
|
+
done
|
|
948
|
+
else
|
|
949
|
+
__initial_file_patterns=("\${__initial_file_pattern}(#q\${__file_qualifier})")
|
|
950
|
+
fi
|
|
951
|
+
file_pattern="\${(j: :)__initial_file_patterns}"
|
|
952
|
+
fi
|
|
953
|
+
local file_subtree_pattern="**/*(#q/N)"
|
|
954
|
+
local -a file_subtree=() file_recursive_patterns=()
|
|
955
|
+
local -a file_tags=(files directories)
|
|
956
|
+
local file_recursive_pattern file_directory file_prefix file_tag
|
|
957
|
+
if [[ -n "\$__initial_file_pattern" || -z "\$ext_pattern" ]] &&
|
|
958
|
+
zstyle -t ":completion:\${curcontext}:" list-dirs-first; then
|
|
959
|
+
file_tags=(directories files)
|
|
960
|
+
fi
|
|
961
|
+
_tags "\${file_tags[@]}"
|
|
962
|
+
while _tags; do
|
|
963
|
+
local file_matched=0
|
|
964
|
+
for file_tag in "\${file_tags[@]}"; do
|
|
965
|
+
if [[ "\$file_tag" == directories ]]; then
|
|
966
|
+
while _next_label directories expl directory; do
|
|
967
|
+
_path_files -/ "\${expl[@]}" && file_matched=1
|
|
968
|
+
done
|
|
969
|
+
else
|
|
970
|
+
while _next_label files expl file; do
|
|
971
|
+
local -a file_ignore=() file_ignore_args=()
|
|
972
|
+
# Explicit globs bypass automatic fignore. Apply it only
|
|
973
|
+
# when the tag has not supplied its own ignore patterns.
|
|
974
|
+
if [[ -z "\$__initial_file_pattern" && -z "\$ext_pattern" && -z "\$_comp_no_ignore" && -n "\$FIGNORE" ]] && (( \${#_comp_ignore[@]} == 0 )); then
|
|
975
|
+
file_ignore=("?*\${^fignore[@]}")
|
|
976
|
+
file_ignore_args=(-F file_ignore)
|
|
977
|
+
fi
|
|
978
|
+
if _path_files -g "\${file_pattern}" "\${expl[@]}" "\${file_ignore_args[@]}"; then
|
|
979
|
+
file_matched=1
|
|
980
|
+
elif [[ ( -n "\$__initial_file_pattern" || -z "\$ext_pattern" ) && "\$PREFIX\$SUFFIX" != */* ]] &&
|
|
981
|
+
zstyle -a ":completion:\${curcontext}:files" recursive-files file_recursive_patterns; then
|
|
982
|
+
# Match _files' recursive fallback, retaining the same
|
|
983
|
+
# file filter and tag-specific ignore options.
|
|
984
|
+
for file_recursive_pattern in "\${file_recursive_patterns[@]}"; do
|
|
985
|
+
if [[ "\$PWD/" = \${~file_recursive_pattern} ]]; then
|
|
986
|
+
if (( \${#file_subtree[@]} == 0 )); then
|
|
987
|
+
file_subtree=(\${~file_subtree_pattern})
|
|
988
|
+
fi
|
|
989
|
+
for file_directory in "\${file_subtree[@]}"; do
|
|
990
|
+
file_prefix="\$PREFIX"
|
|
991
|
+
PREFIX="\$file_directory/\$PREFIX"
|
|
992
|
+
_path_files -g "\${file_pattern}" "\${expl[@]}" "\${file_ignore_args[@]}" && file_matched=1
|
|
993
|
+
PREFIX="\$file_prefix"
|
|
994
|
+
done
|
|
995
|
+
break
|
|
996
|
+
fi
|
|
997
|
+
done
|
|
998
|
+
fi
|
|
999
|
+
done
|
|
1000
|
+
fi
|
|
1001
|
+
done
|
|
1002
|
+
(( file_matched )) && break
|
|
1003
|
+
done
|
|
1004
|
+
elif [[ -n "\$ext_pattern" ]]; then
|
|
1005
|
+
local file_pattern="\${ext_pattern}(#q^-/)"
|
|
779
1006
|
_wanted files expl file _path_files -g "\${file_pattern}"
|
|
780
1007
|
_wanted directories expl directory _path_files -/
|
|
781
1008
|
else
|
|
@@ -783,21 +1010,17 @@ function ${functionName} () {
|
|
|
783
1010
|
fi
|
|
784
1011
|
;;
|
|
785
1012
|
directory)
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if [[ -n "\$ext_pattern" ]]; then
|
|
790
|
-
local file_pattern="\${ext_pattern}(#q^-/)"
|
|
791
|
-
_wanted files expl file _path_files -g "\${file_pattern}"
|
|
792
|
-
_wanted directories expl directory _path_files -/
|
|
1013
|
+
if [[ -n "\$__initial_file_pattern" ]]; then
|
|
1014
|
+
# -/ would add every directory alongside the filtered glob.
|
|
1015
|
+
_wanted directories expl directory _path_files -g "\${__initial_file_pattern}(#q-/)"
|
|
793
1016
|
else
|
|
794
|
-
|
|
1017
|
+
_directories
|
|
795
1018
|
fi
|
|
796
1019
|
;;
|
|
797
1020
|
esac
|
|
798
|
-
|
|
799
1021
|
# Restore PREFIX and glob_dots to their previous state
|
|
800
1022
|
PREFIX="\$__saved_prefix"
|
|
1023
|
+
if [[ "\$__was_extended_glob" == "1" ]]; then setopt extended_glob; else unsetopt extended_glob; fi
|
|
801
1024
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
802
1025
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
803
1026
|
else
|
|
@@ -1551,8 +1774,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1551
1774
|
let all_items = (do $list_entries $ls_pattern)
|
|
1552
1775
|
let dirs = $all_items | where type == dir
|
|
1553
1776
|
let files = $all_items | where type == file | where {|f|
|
|
1554
|
-
|
|
1555
|
-
|
|
1777
|
+
$ext_list | any {|ext|
|
|
1778
|
+
$f.name | path basename | str ends-with ('.' + $ext)
|
|
1779
|
+
}
|
|
1556
1780
|
}
|
|
1557
1781
|
$dirs | append $files
|
|
1558
1782
|
}
|
|
@@ -1568,8 +1792,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1568
1792
|
let all_items = (do $list_entries $ls_pattern)
|
|
1569
1793
|
let dirs = $all_items | where type == dir
|
|
1570
1794
|
let files = $all_items | where type == file | where {|f|
|
|
1571
|
-
|
|
1572
|
-
|
|
1795
|
+
$ext_list | any {|ext|
|
|
1796
|
+
$f.name | path basename | str ends-with ('.' + $ext)
|
|
1797
|
+
}
|
|
1573
1798
|
}
|
|
1574
1799
|
$dirs | append $files
|
|
1575
1800
|
}
|
|
@@ -1737,6 +1962,15 @@ const pwsh = {
|
|
|
1737
1962
|
Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
1738
1963
|
param(\$wordToComplete, \$commandAst, \$cursorPosition)
|
|
1739
1964
|
|
|
1965
|
+
# CompletionText is shell syntax; display labels remain unquoted.
|
|
1966
|
+
\$quoteArgument = {
|
|
1967
|
+
param([string]\$value)
|
|
1968
|
+
if (\$value -cmatch '\\A[a-zA-Z0-9_./:\\\\-]+\\z' -and \$value -cnotmatch '\\A-(?!-).*[.:]' -and \$value -cnotmatch '\\A-?(?:[0-9]|\\.[0-9])') {
|
|
1969
|
+
return \$value
|
|
1970
|
+
}
|
|
1971
|
+
return "'" + [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent(\$value) + "'"
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1740
1974
|
# Extract arguments from AST (handles quoted strings properly)
|
|
1741
1975
|
\$arguments = @()
|
|
1742
1976
|
\$commandElements = \$commandAst.CommandElements
|
|
@@ -1750,8 +1984,11 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1750
1984
|
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1751
1985
|
# wordToComplete has synthesized delimiters and decoded escapes.
|
|
1752
1986
|
# Only the AST retains the literal value and original quote type.
|
|
1987
|
+
# PSReadLine leaves the cursor before a directory's closing quote.
|
|
1753
1988
|
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst] -and
|
|
1754
|
-
\$element.Extent.EndOffset -eq \$cursorPosition
|
|
1989
|
+
(\$element.Extent.EndOffset -eq \$cursorPosition -or
|
|
1990
|
+
(\$element.Extent.EndOffset -eq (\$cursorPosition + 1) -and
|
|
1991
|
+
\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')))) {
|
|
1755
1992
|
if (\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')) {
|
|
1756
1993
|
\$valuePrefix = \$element.Value
|
|
1757
1994
|
\$quoteType = \$element.StringConstantType
|
|
@@ -1784,7 +2021,7 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1784
2021
|
} elseif (\$quoteType -eq 'DoubleQuoted') {
|
|
1785
2022
|
'"' + (\$text -replace '[\\x60\$"\\u201c-\\u201e]', '\`\$0') + '"'
|
|
1786
2023
|
} else {
|
|
1787
|
-
\$text
|
|
2024
|
+
& \$quoteArgument \$text
|
|
1788
2025
|
}
|
|
1789
2026
|
}
|
|
1790
2027
|
|
|
@@ -1889,7 +2126,10 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1889
2126
|
\$extList = (\$extensions -split ',') -replace '^\\.',''
|
|
1890
2127
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue |
|
|
1891
2128
|
Where-Object {
|
|
1892
|
-
\$
|
|
2129
|
+
\$fileName = \$_.Name
|
|
2130
|
+
\$_.PSIsContainer -or (@(\$extList | Where-Object {
|
|
2131
|
+
\$fileName.EndsWith('.' + \$_, [System.StringComparison]::Ordinal)
|
|
2132
|
+
}).Count -gt 0)
|
|
1893
2133
|
}
|
|
1894
2134
|
} else {
|
|
1895
2135
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue
|
|
@@ -1904,7 +2144,10 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1904
2144
|
\$extList = (\$extensions -split ',') -replace '^\\.',''
|
|
1905
2145
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue |
|
|
1906
2146
|
Where-Object {
|
|
1907
|
-
\$
|
|
2147
|
+
\$fileName = \$_.Name
|
|
2148
|
+
\$_.PSIsContainer -or (@(\$extList | Where-Object {
|
|
2149
|
+
\$fileName.EndsWith('.' + \$_, [System.StringComparison]::Ordinal)
|
|
2150
|
+
}).Count -gt 0)
|
|
1908
2151
|
}
|
|
1909
2152
|
} else {
|
|
1910
2153
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue
|
|
@@ -1936,15 +2179,32 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1936
2179
|
\$items | ForEach-Object {
|
|
1937
2180
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1938
2181
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
2182
|
+
\$resultType = if (\$_.PSIsContainer) { 'ProviderContainer' } else { 'ParameterValue' }
|
|
2183
|
+
# PSReadLine recognizes the platform separator for containers.
|
|
2184
|
+
# Keep the display label's existing slash convention.
|
|
2185
|
+
\$directorySuffix = [IO.Path]::DirectorySeparatorChar
|
|
2186
|
+
\$pathText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)\$directorySuffix" } else { \$completionText }
|
|
1939
2187
|
# Quoting disables native tilde expansion. A home pattern
|
|
1940
2188
|
# supplied by the parser must insert the discovered path.
|
|
1941
2189
|
\$replacementText = if (\$null -ne \$quoteType -and -not \$prefixFromInput -and \$homePattern) {
|
|
1942
|
-
if (\$_.PSIsContainer) { \$_.FullName +
|
|
1943
|
-
} else { \$
|
|
2190
|
+
if (\$_.PSIsContainer) { \$_.FullName + \$directorySuffix } else { \$_.FullName }
|
|
2191
|
+
} else { \$pathText }
|
|
2192
|
+
# Quoting suppresses native tilde expansion. Keep safe
|
|
2193
|
+
# ~/ paths bare; otherwise use the enumerated full path.
|
|
2194
|
+
\$insertionText = if (\$null -ne \$quoteType) {
|
|
2195
|
+
& \$quoteCompletion \$replacementText
|
|
2196
|
+
} elseif (\$completionText -cmatch '\\A~/[a-zA-Z0-9_./:\\\\-]*\\z') {
|
|
2197
|
+
\$pathText
|
|
2198
|
+
} else {
|
|
2199
|
+
\$argumentText = if (\$completionText.StartsWith('~/') -or \$completionText.StartsWith('~\\')) {
|
|
2200
|
+
if (\$_.PSIsContainer) { \$_.FullName + \$directorySuffix } else { \$_.FullName }
|
|
2201
|
+
} else { \$pathText }
|
|
2202
|
+
& \$quoteArgument \$argumentText
|
|
2203
|
+
}
|
|
1944
2204
|
[System.Management.Automation.CompletionResult]::new(
|
|
1945
|
-
|
|
2205
|
+
\$insertionText,
|
|
1946
2206
|
\$completionText,
|
|
1947
|
-
|
|
2207
|
+
\$resultType,
|
|
1948
2208
|
\$itemType
|
|
1949
2209
|
)
|
|
1950
2210
|
}
|
package/dist/completion.js
CHANGED
|
@@ -120,7 +120,7 @@ function _${programName} () {
|
|
|
120
120
|
esac
|
|
121
121
|
fi
|
|
122
122
|
done
|
|
123
|
-
local __tilde_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)
|
|
123
|
+
local __tilde_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)?$'
|
|
124
124
|
local __tilde_glob_pattern='^(~[a-zA-Z0-9_.+-]*)(/.*)?$'
|
|
125
125
|
local prev=("\${COMP_WORDS[@]:1:COMP_CWORD-1}")
|
|
126
126
|
# Bash splits unquoted '=' at its default word breaks. Reassemble only
|
|
@@ -353,8 +353,9 @@ function _${programName} () {
|
|
|
353
353
|
|
|
354
354
|
# path() echoes the raw shell word as its pattern. Once dequoted,
|
|
355
355
|
# use the literal current-word prefix instead of the escaped pattern.
|
|
356
|
-
if [[
|
|
357
|
-
|
|
356
|
+
if [[ "$pattern" == "$__raw_file_current" && "$__file_current" != "$__raw_file_current" ]] ||
|
|
357
|
+
{ [[ -n "$__reply_prefix" ]] && [ "$pattern" = "$__file_current" ] &&
|
|
358
|
+
{ [[ "$pattern" != *[\*\?]* ]] || [ "$__raw_file_current" != "$__file_current" ]; }; }; then
|
|
358
359
|
pattern=""
|
|
359
360
|
fi
|
|
360
361
|
|
|
@@ -388,7 +389,8 @@ function _${programName} () {
|
|
|
388
389
|
# However, if the user has already typed beyond the pattern (e.g.,
|
|
389
390
|
# pattern="src/" and current="src/ma"), preserve the typed suffix
|
|
390
391
|
# for incremental filtering.
|
|
391
|
-
local __from_pattern=0
|
|
392
|
+
local __from_pattern=0 __filter_pattern=0 __pattern_prefix=""
|
|
393
|
+
local __pattern_tilde_prefix="" __pattern_tilde_expanded=""
|
|
392
394
|
if [[ -n "$pattern" ]]; then
|
|
393
395
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
394
396
|
local __norm_current="\${__file_current#./}"
|
|
@@ -400,10 +402,27 @@ function _${programName} () {
|
|
|
400
402
|
__compare_pattern="\${__compare_pattern%%[\*\?]*}"
|
|
401
403
|
[[ "$__compare_pattern" == */* ]] && __compare_pattern="\${__compare_pattern%/*}/" || __compare_pattern=""
|
|
402
404
|
fi
|
|
405
|
+
local __extends_pattern=0
|
|
403
406
|
if [[ ( -n "$__compare_pattern" || -n "$__norm_current" ) && \${#__norm_current} -ge \${#__compare_pattern} && "\${__norm_current:0:\${#__compare_pattern}}" == "$__compare_pattern" && "$__file_current" != "$pattern" ]]; then
|
|
407
|
+
__extends_pattern=1
|
|
408
|
+
fi
|
|
409
|
+
if (( __extends_pattern )) && [[ "$__norm_pattern" != *[\*\?]* ]]; then
|
|
404
410
|
# User has typed beyond or an equivalent form of the pattern
|
|
405
411
|
true
|
|
406
412
|
else
|
|
413
|
+
# Wildcards keep their file filter while the typed prefix narrows
|
|
414
|
+
# the resulting paths. Save the expanded prefix before using pattern.
|
|
415
|
+
if [[ "$__norm_pattern" == *[\*\?]* ]]; then
|
|
416
|
+
__filter_pattern=1
|
|
417
|
+
__pattern_prefix="$__glob_current"
|
|
418
|
+
# An unquoted value equal to its wildcard is a complete search
|
|
419
|
+
# pattern, not a literal filename prefix to filter against.
|
|
420
|
+
if [ "$__file_current" = "$pattern" ] && [ "$__raw_file_current" = "$__file_current" ]; then
|
|
421
|
+
__pattern_prefix=""
|
|
422
|
+
fi
|
|
423
|
+
__pattern_tilde_prefix="$__tilde_prefix"
|
|
424
|
+
__pattern_tilde_expanded="$__tilde_expanded"
|
|
425
|
+
fi
|
|
407
426
|
__from_pattern=1
|
|
408
427
|
# Reset tilde state from the current-word expansion so that a
|
|
409
428
|
# non-tilde pattern is not rewritten through stale tilde state
|
|
@@ -432,36 +451,93 @@ function _${programName} () {
|
|
|
432
451
|
# This runs after tilde expansion so that paths like ~/.config/ are
|
|
433
452
|
# checked against the expanded path, not the literal ~ string.
|
|
434
453
|
local __inside_hidden_path=0
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
454
|
+
local __hidden_directory_pattern='/[.]([^./][^/]*|[.][^/]+)/'
|
|
455
|
+
if [[ "/\${__glob_current%/}/" =~ $__hidden_directory_pattern ||
|
|
456
|
+
"/\${__pattern_prefix%/}/" =~ $__hidden_directory_pattern ]]; then
|
|
457
|
+
__inside_hidden_path=1
|
|
458
|
+
fi
|
|
438
459
|
# Also check if the current prefix explicitly targets hidden entries
|
|
439
460
|
# (e.g., user typed "." or ".e" to complete .env)
|
|
440
461
|
local __prefix_targets_hidden=0
|
|
441
462
|
local __prefix_base="\${__glob_current##*/}"
|
|
463
|
+
if [[ -n "$__pattern_prefix" ]]; then __prefix_base="\${__pattern_prefix##*/}"; fi
|
|
442
464
|
[[ "$__prefix_base" == .* ]] && __prefix_targets_hidden=1
|
|
443
465
|
if [[ "$hidden" == "1" || "$__inside_hidden_path" == "1" || "$__prefix_targets_hidden" == "1" ]]; then shopt -s dotglob; fi
|
|
444
466
|
|
|
445
467
|
# Pre-expand file candidates. When the glob base came from the
|
|
446
468
|
# program's pattern and contains wildcard characters (* or ?),
|
|
447
|
-
#
|
|
469
|
+
# expand it as data with shell globbing. Empty IFS avoids splitting paths;
|
|
470
|
+
# NUL records preserve spaces and newlines without requiring mapfile.
|
|
471
|
+
# Shell expansion respects dotglob on older Bash versions as well.
|
|
448
472
|
# Otherwise append * to treat it as a prefix.
|
|
449
|
-
#
|
|
450
|
-
#
|
|
473
|
+
# Only * and ? are wildcard syntax; escape brackets, backslashes, and
|
|
474
|
+
# extended-glob operators so other pattern characters stay literal.
|
|
451
475
|
local -a __candidates=()
|
|
452
476
|
if [[ "$__from_pattern" == "1" && "$__glob_current" == *[\*\?]* ]]; then
|
|
453
|
-
|
|
477
|
+
local __pathname_glob="" __glob_char __glob_index
|
|
478
|
+
for ((__glob_index=0; __glob_index<\${#__glob_current}; __glob_index++)); do
|
|
479
|
+
__glob_char="\${__glob_current:__glob_index:1}"
|
|
480
|
+
case "$__glob_char" in
|
|
481
|
+
'['|']'|'\\'|'('|')'|'|'|'+'|'@'|'!')
|
|
482
|
+
__pathname_glob+="\\\\$__glob_char" ;;
|
|
483
|
+
*) __pathname_glob+="$__glob_char" ;;
|
|
484
|
+
esac
|
|
485
|
+
done
|
|
486
|
+
[[ "$type" == "directory" ]] && __pathname_glob="\${__pathname_glob%/}"
|
|
487
|
+
local __glob_dir="\${__pathname_glob%/*}"
|
|
488
|
+
[[ "$__glob_dir" == "$__pathname_glob" ]] && __glob_dir="."
|
|
489
|
+
[[ -z "$__glob_dir" ]] && __glob_dir="/"
|
|
490
|
+
local __navigation_dir="" __basename_glob="\${__pathname_glob##*/}"
|
|
491
|
+
# Once an offered directory is entered, keep the basename filter but
|
|
492
|
+
# enumerate there. Only descendants of matching pattern bases qualify.
|
|
493
|
+
if [[ "$__pattern_prefix" == */* ]]; then
|
|
494
|
+
local __typed_dir="\${__pattern_prefix%/*}" __base_dir __norm_base __norm_dir
|
|
495
|
+
# Resolve directory ancestry in subshells, preserving the typed spelling
|
|
496
|
+
# for insertion. The trailing slash also preserves newline-ending names.
|
|
497
|
+
if __norm_dir=$(CDPATH= builtin cd -P -- "$__typed_dir" >/dev/null 2>&1 && printf '%s/' "\${PWD%/}"); then
|
|
498
|
+
while IFS= read -r -d '' __base_dir; do
|
|
499
|
+
[[ -d "$__base_dir" ]] || continue
|
|
500
|
+
__norm_base=$(CDPATH= builtin cd -P -- "$__base_dir" >/dev/null 2>&1 && printf '%s/' "\${PWD%/}") || continue
|
|
501
|
+
if [ "\${__norm_dir:0:\${#__norm_base}}" = "$__norm_base" ]; then
|
|
502
|
+
__navigation_dir="$__typed_dir"
|
|
503
|
+
break
|
|
504
|
+
fi
|
|
505
|
+
done < <(IFS=; printf '%s\\0' $__glob_dir)
|
|
506
|
+
fi
|
|
507
|
+
fi
|
|
508
|
+
local __candidate
|
|
509
|
+
while IFS= read -r -d '' __candidate; do
|
|
510
|
+
if [[ -e "$__candidate" || -L "$__candidate" ]]; then
|
|
511
|
+
[[ -d "$__candidate" ]] && __candidate="\${__candidate%/}"
|
|
512
|
+
__candidates+=("$__candidate")
|
|
513
|
+
fi
|
|
514
|
+
done < <(
|
|
515
|
+
IFS=
|
|
516
|
+
if [[ -n "$__navigation_dir" ]]; then
|
|
517
|
+
printf '%s\\0' "$__navigation_dir"/$__basename_glob
|
|
518
|
+
else
|
|
519
|
+
printf '%s\\0' $__pathname_glob
|
|
520
|
+
fi
|
|
521
|
+
)
|
|
454
522
|
# For file/any modes, also include directories from the base
|
|
455
523
|
# directory for navigation even when the glob itself does not
|
|
456
524
|
# match directory names. Skip this for directory mode so that
|
|
457
525
|
# the pattern's basename filter is respected.
|
|
458
526
|
if [[ "$type" != "directory" ]]; then
|
|
459
|
-
local __glob_dir="\${__glob_current%/*}"
|
|
460
|
-
[[ "$__glob_dir" == "$__glob_current" ]] && __glob_dir="."
|
|
461
527
|
local __d
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
528
|
+
local __directory_pattern="\${__glob_dir%/}/*/"
|
|
529
|
+
while IFS= read -r -d '' __d; do
|
|
530
|
+
[[ -d "$__d" ]] || continue
|
|
531
|
+
__d="\${__d%/}"
|
|
532
|
+
__candidates+=("$__d")
|
|
533
|
+
done < <(
|
|
534
|
+
IFS=
|
|
535
|
+
if [[ -n "$__navigation_dir" ]]; then
|
|
536
|
+
printf '%s\\0' "$__navigation_dir"/*/
|
|
537
|
+
else
|
|
538
|
+
printf '%s\\0' $__directory_pattern
|
|
539
|
+
fi
|
|
540
|
+
)
|
|
465
541
|
fi
|
|
466
542
|
else
|
|
467
543
|
__candidates=("$__glob_current"*)
|
|
@@ -472,6 +548,26 @@ function _${programName} () {
|
|
|
472
548
|
fi
|
|
473
549
|
fi
|
|
474
550
|
|
|
551
|
+
if (( __filter_pattern )); then
|
|
552
|
+
local __nocasematch_was_set=0
|
|
553
|
+
shopt -q nocasematch && __nocasematch_was_set=1
|
|
554
|
+
if shopt -q nocaseglob; then shopt -s nocasematch; else shopt -u nocasematch; fi
|
|
555
|
+
local -a __filtered_candidates=()
|
|
556
|
+
for __candidate in "\${__candidates[@]}"; do
|
|
557
|
+
if [[ "$__candidate" == "$__pattern_prefix"* ||
|
|
558
|
+
"\${__candidate#./}" == "\${__pattern_prefix#./}"* ]]; then
|
|
559
|
+
if [[ "$__pattern_prefix" == ./* && "$__candidate" != ./* ]]; then
|
|
560
|
+
__candidate="./$__candidate"
|
|
561
|
+
elif [[ "$__pattern_prefix" != ./* && "$__pattern_prefix" != . ]]; then
|
|
562
|
+
__candidate="\${__candidate#./}"
|
|
563
|
+
fi
|
|
564
|
+
__filtered_candidates+=("$__candidate")
|
|
565
|
+
fi
|
|
566
|
+
done
|
|
567
|
+
if (( __nocasematch_was_set )); then shopt -s nocasematch; else shopt -u nocasematch; fi
|
|
568
|
+
__candidates=("\${__filtered_candidates[@]}")
|
|
569
|
+
fi
|
|
570
|
+
|
|
475
571
|
# Generate file completions based on type
|
|
476
572
|
case "$type" in
|
|
477
573
|
file)
|
|
@@ -533,6 +629,10 @@ function _${programName} () {
|
|
|
533
629
|
esac
|
|
534
630
|
|
|
535
631
|
# Restore tilde prefix in completion results
|
|
632
|
+
if (( __filter_pattern )); then
|
|
633
|
+
__tilde_prefix="$__pattern_tilde_prefix"
|
|
634
|
+
__tilde_expanded="$__pattern_tilde_expanded"
|
|
635
|
+
fi
|
|
536
636
|
if [[ -n "$__tilde_prefix" &&
|
|
537
637
|
( -z "$__quote" || $__file_quote_offset -gt 0 ) ]]; then
|
|
538
638
|
local __i
|
|
@@ -632,6 +732,31 @@ function _${programName} () {
|
|
|
632
732
|
COMPREPLY+=("\${__word:\${#__remove}}")
|
|
633
733
|
fi
|
|
634
734
|
done
|
|
735
|
+
if (( \${#COMPREPLY[@]} > 1 )) && command -v sort >/dev/null 2>&1; then
|
|
736
|
+
# Sort shell-escaped keys, not filenames: each key occupies one line even
|
|
737
|
+
# when a candidate contains tabs or newlines. Keep the first occurrence's
|
|
738
|
+
# index, then restore the original order without evaluating escaped data.
|
|
739
|
+
# This works without associative arrays or NUL-aware sort on Bash 3.2.
|
|
740
|
+
local -a __unique_replies=()
|
|
741
|
+
local __index __key __last_key=""
|
|
742
|
+
while IFS= read -r __index; do
|
|
743
|
+
__unique_replies+=("\${COMPREPLY[__index]}")
|
|
744
|
+
done < <(
|
|
745
|
+
for ((__index=0; __index<\${#COMPREPLY[@]}; __index++)); do
|
|
746
|
+
printf '%q\\t%d\\n' "\${COMPREPLY[__index]}" "$__index"
|
|
747
|
+
done |
|
|
748
|
+
LC_ALL=C sort -t $'\\t' -k1,1 -k2,2n |
|
|
749
|
+
{
|
|
750
|
+
while IFS=$'\\t' read -r __key __index; do
|
|
751
|
+
if [ "$__key" != "$__last_key" ]; then
|
|
752
|
+
printf '%s\\n' "$__index"
|
|
753
|
+
__last_key="$__key"
|
|
754
|
+
fi
|
|
755
|
+
done
|
|
756
|
+
} | LC_ALL=C sort -n
|
|
757
|
+
)
|
|
758
|
+
COMPREPLY=("\${__unique_replies[@]}")
|
|
759
|
+
fi
|
|
635
760
|
if [[ $__file_mode == 1 && $(type -t compopt) == builtin ]]; then
|
|
636
761
|
compopt -o filenames 2>/dev/null || true
|
|
637
762
|
fi
|
|
@@ -657,7 +782,7 @@ fi
|
|
|
657
782
|
}
|
|
658
783
|
for (const [index, { prefix, file }] of joined.files.entries()) {
|
|
659
784
|
if (index > 0) yield "\n";
|
|
660
|
-
yield `__JOINED_FILE__:${encodePattern(prefix)}:${file.type}:${encodeExtensions(file.extensions)}
|
|
785
|
+
yield `__JOINED_FILE__:${encodePattern(prefix)}:${file.type}:${encodeExtensions(file.extensions)}:${encodePattern(sanitizeForTransport(file.pattern ?? ""))}:${file.includeHidden ? "1" : "0"}`;
|
|
661
786
|
}
|
|
662
787
|
} else if (suggestion.kind === "literal") yield encodeBashLiteral(suggestion.text);
|
|
663
788
|
else {
|
|
@@ -683,6 +808,10 @@ const zsh = {
|
|
|
683
808
|
return `#compdef ${programName}
|
|
684
809
|
function ${functionName} () {
|
|
685
810
|
local current="\$words[CURRENT]"
|
|
811
|
+
# Let zsh match the suffix when completing in the middle of a word.
|
|
812
|
+
if [[ -o complete_in_word ]]; then
|
|
813
|
+
current="\$PREFIX"
|
|
814
|
+
fi
|
|
686
815
|
local -a prev
|
|
687
816
|
# Extract previous arguments, skipping empty ones
|
|
688
817
|
prev=()
|
|
@@ -730,6 +859,8 @@ function ${functionName} () {
|
|
|
730
859
|
local __was_sh_glob=0
|
|
731
860
|
[[ -o sh_glob ]] && __was_sh_glob=1
|
|
732
861
|
unsetopt sh_glob
|
|
862
|
+
local __was_extended_glob=0
|
|
863
|
+
[[ -o extended_glob ]] && __was_extended_glob=1
|
|
733
864
|
|
|
734
865
|
# Enable glob_dots when hidden files are requested so that
|
|
735
866
|
# _files and _directories include dot-prefixed entries
|
|
@@ -742,7 +873,34 @@ function ${functionName} () {
|
|
|
742
873
|
# current word. If the user has already typed beyond the pattern,
|
|
743
874
|
# keep PREFIX unchanged for incremental narrowing.
|
|
744
875
|
local __saved_prefix="\$PREFIX"
|
|
745
|
-
|
|
876
|
+
local __initial_file_pattern="" __initial_directory="" __initial_leaf=""
|
|
877
|
+
if [[ ( "\$type" == "file" || "\$type" == "any" || "\$type" == "directory" ) &&
|
|
878
|
+
-z "\$current\$PREFIX\$SUFFIX" &&
|
|
879
|
+
-z "\${compstate[pattern_match]}" &&
|
|
880
|
+
"\$pattern" != /* && "\$pattern" != "~"* ]]; then
|
|
881
|
+
if [[ "\$pattern" == */* ]]; then
|
|
882
|
+
__initial_directory="\${pattern%/*}/"
|
|
883
|
+
fi
|
|
884
|
+
__initial_leaf="\${pattern##*/}"
|
|
885
|
+
# Native -g filters are split as shell words and use extended glob
|
|
886
|
+
# syntax. Keep unsupported filename forms on the existing path.
|
|
887
|
+
if [[ "\$__initial_leaf" == *[\*\?]* &&
|
|
888
|
+
"\$__initial_directory" != *[\*\?]* &&
|
|
889
|
+
"\$__initial_directory" != *["[]()"]* &&
|
|
890
|
+
"\$__initial_leaf" != *["[]"]* &&
|
|
891
|
+
"\$__initial_leaf" != *'\\'* &&
|
|
892
|
+
"\$__initial_leaf" != *[[:space:]]* &&
|
|
893
|
+
"\$__initial_leaf" != *["#~^()|<>;&"]* &&
|
|
894
|
+
"\$__initial_leaf" != *"'"* && "\$__initial_leaf" != *'"'* ]]; then
|
|
895
|
+
__initial_file_pattern="\$__initial_leaf"
|
|
896
|
+
# Only the literal directory belongs in PREFIX. Quote it so
|
|
897
|
+
# spaces and shell metacharacters remain literal path text.
|
|
898
|
+
if [[ -n "\$__initial_directory" ]]; then
|
|
899
|
+
PREFIX="\${(q)__initial_directory}"
|
|
900
|
+
fi
|
|
901
|
+
fi
|
|
902
|
+
fi
|
|
903
|
+
if [[ -n "\$pattern" && -z "\$__initial_file_pattern" ]]; then
|
|
746
904
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
747
905
|
local __norm_prefix="\${PREFIX#./}"
|
|
748
906
|
local __norm_pattern="\${pattern#./}"
|
|
@@ -767,15 +925,84 @@ function ${functionName} () {
|
|
|
767
925
|
ext_pattern="*.\$extensions"
|
|
768
926
|
fi
|
|
769
927
|
fi
|
|
928
|
+
# _wanted/_next_label write to the named explanation parameter.
|
|
929
|
+
# Keep it local for every file directive, including directory/any.
|
|
930
|
+
local -a expl=()
|
|
770
931
|
# Use zsh's native file completion
|
|
771
932
|
case "\$type" in
|
|
772
|
-
file)
|
|
773
|
-
if [[ -n "\$
|
|
774
|
-
#
|
|
775
|
-
#
|
|
776
|
-
#
|
|
777
|
-
|
|
778
|
-
|
|
933
|
+
file|any)
|
|
934
|
+
if [[ "\$type" == "file" || -n "\$__initial_file_pattern" ]]; then
|
|
935
|
+
# Regular-file suggestions retain symlinked files; initial
|
|
936
|
+
# any globs use a non-directory qualifier instead.
|
|
937
|
+
local file_pattern="\${ext_pattern:-*}(#q-.)"
|
|
938
|
+
if [[ -n "\$__initial_file_pattern" ]]; then
|
|
939
|
+
local __file_qualifier="-."
|
|
940
|
+
[[ "\$type" == "any" ]] && __file_qualifier="^-/"
|
|
941
|
+
local -a __initial_file_patterns=()
|
|
942
|
+
if [[ -n "\$extensions" ]]; then
|
|
943
|
+
setopt extended_glob
|
|
944
|
+
local __initial_extension
|
|
945
|
+
for __initial_extension in "\${(@s:,:)extensions}"; do
|
|
946
|
+
__initial_file_patterns+=("(\${__initial_file_pattern}~^*.\${__initial_extension})(#q\${__file_qualifier})")
|
|
947
|
+
done
|
|
948
|
+
else
|
|
949
|
+
__initial_file_patterns=("\${__initial_file_pattern}(#q\${__file_qualifier})")
|
|
950
|
+
fi
|
|
951
|
+
file_pattern="\${(j: :)__initial_file_patterns}"
|
|
952
|
+
fi
|
|
953
|
+
local file_subtree_pattern="**/*(#q/N)"
|
|
954
|
+
local -a file_subtree=() file_recursive_patterns=()
|
|
955
|
+
local -a file_tags=(files directories)
|
|
956
|
+
local file_recursive_pattern file_directory file_prefix file_tag
|
|
957
|
+
if [[ -n "\$__initial_file_pattern" || -z "\$ext_pattern" ]] &&
|
|
958
|
+
zstyle -t ":completion:\${curcontext}:" list-dirs-first; then
|
|
959
|
+
file_tags=(directories files)
|
|
960
|
+
fi
|
|
961
|
+
_tags "\${file_tags[@]}"
|
|
962
|
+
while _tags; do
|
|
963
|
+
local file_matched=0
|
|
964
|
+
for file_tag in "\${file_tags[@]}"; do
|
|
965
|
+
if [[ "\$file_tag" == directories ]]; then
|
|
966
|
+
while _next_label directories expl directory; do
|
|
967
|
+
_path_files -/ "\${expl[@]}" && file_matched=1
|
|
968
|
+
done
|
|
969
|
+
else
|
|
970
|
+
while _next_label files expl file; do
|
|
971
|
+
local -a file_ignore=() file_ignore_args=()
|
|
972
|
+
# Explicit globs bypass automatic fignore. Apply it only
|
|
973
|
+
# when the tag has not supplied its own ignore patterns.
|
|
974
|
+
if [[ -z "\$__initial_file_pattern" && -z "\$ext_pattern" && -z "\$_comp_no_ignore" && -n "\$FIGNORE" ]] && (( \${#_comp_ignore[@]} == 0 )); then
|
|
975
|
+
file_ignore=("?*\${^fignore[@]}")
|
|
976
|
+
file_ignore_args=(-F file_ignore)
|
|
977
|
+
fi
|
|
978
|
+
if _path_files -g "\${file_pattern}" "\${expl[@]}" "\${file_ignore_args[@]}"; then
|
|
979
|
+
file_matched=1
|
|
980
|
+
elif [[ ( -n "\$__initial_file_pattern" || -z "\$ext_pattern" ) && "\$PREFIX\$SUFFIX" != */* ]] &&
|
|
981
|
+
zstyle -a ":completion:\${curcontext}:files" recursive-files file_recursive_patterns; then
|
|
982
|
+
# Match _files' recursive fallback, retaining the same
|
|
983
|
+
# file filter and tag-specific ignore options.
|
|
984
|
+
for file_recursive_pattern in "\${file_recursive_patterns[@]}"; do
|
|
985
|
+
if [[ "\$PWD/" = \${~file_recursive_pattern} ]]; then
|
|
986
|
+
if (( \${#file_subtree[@]} == 0 )); then
|
|
987
|
+
file_subtree=(\${~file_subtree_pattern})
|
|
988
|
+
fi
|
|
989
|
+
for file_directory in "\${file_subtree[@]}"; do
|
|
990
|
+
file_prefix="\$PREFIX"
|
|
991
|
+
PREFIX="\$file_directory/\$PREFIX"
|
|
992
|
+
_path_files -g "\${file_pattern}" "\${expl[@]}" "\${file_ignore_args[@]}" && file_matched=1
|
|
993
|
+
PREFIX="\$file_prefix"
|
|
994
|
+
done
|
|
995
|
+
break
|
|
996
|
+
fi
|
|
997
|
+
done
|
|
998
|
+
fi
|
|
999
|
+
done
|
|
1000
|
+
fi
|
|
1001
|
+
done
|
|
1002
|
+
(( file_matched )) && break
|
|
1003
|
+
done
|
|
1004
|
+
elif [[ -n "\$ext_pattern" ]]; then
|
|
1005
|
+
local file_pattern="\${ext_pattern}(#q^-/)"
|
|
779
1006
|
_wanted files expl file _path_files -g "\${file_pattern}"
|
|
780
1007
|
_wanted directories expl directory _path_files -/
|
|
781
1008
|
else
|
|
@@ -783,21 +1010,17 @@ function ${functionName} () {
|
|
|
783
1010
|
fi
|
|
784
1011
|
;;
|
|
785
1012
|
directory)
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if [[ -n "\$ext_pattern" ]]; then
|
|
790
|
-
local file_pattern="\${ext_pattern}(#q^-/)"
|
|
791
|
-
_wanted files expl file _path_files -g "\${file_pattern}"
|
|
792
|
-
_wanted directories expl directory _path_files -/
|
|
1013
|
+
if [[ -n "\$__initial_file_pattern" ]]; then
|
|
1014
|
+
# -/ would add every directory alongside the filtered glob.
|
|
1015
|
+
_wanted directories expl directory _path_files -g "\${__initial_file_pattern}(#q-/)"
|
|
793
1016
|
else
|
|
794
|
-
|
|
1017
|
+
_directories
|
|
795
1018
|
fi
|
|
796
1019
|
;;
|
|
797
1020
|
esac
|
|
798
|
-
|
|
799
1021
|
# Restore PREFIX and glob_dots to their previous state
|
|
800
1022
|
PREFIX="\$__saved_prefix"
|
|
1023
|
+
if [[ "\$__was_extended_glob" == "1" ]]; then setopt extended_glob; else unsetopt extended_glob; fi
|
|
801
1024
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
802
1025
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
803
1026
|
else
|
|
@@ -1551,8 +1774,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1551
1774
|
let all_items = (do $list_entries $ls_pattern)
|
|
1552
1775
|
let dirs = $all_items | where type == dir
|
|
1553
1776
|
let files = $all_items | where type == file | where {|f|
|
|
1554
|
-
|
|
1555
|
-
|
|
1777
|
+
$ext_list | any {|ext|
|
|
1778
|
+
$f.name | path basename | str ends-with ('.' + $ext)
|
|
1779
|
+
}
|
|
1556
1780
|
}
|
|
1557
1781
|
$dirs | append $files
|
|
1558
1782
|
}
|
|
@@ -1568,8 +1792,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
1568
1792
|
let all_items = (do $list_entries $ls_pattern)
|
|
1569
1793
|
let dirs = $all_items | where type == dir
|
|
1570
1794
|
let files = $all_items | where type == file | where {|f|
|
|
1571
|
-
|
|
1572
|
-
|
|
1795
|
+
$ext_list | any {|ext|
|
|
1796
|
+
$f.name | path basename | str ends-with ('.' + $ext)
|
|
1797
|
+
}
|
|
1573
1798
|
}
|
|
1574
1799
|
$dirs | append $files
|
|
1575
1800
|
}
|
|
@@ -1737,6 +1962,15 @@ const pwsh = {
|
|
|
1737
1962
|
Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
1738
1963
|
param(\$wordToComplete, \$commandAst, \$cursorPosition)
|
|
1739
1964
|
|
|
1965
|
+
# CompletionText is shell syntax; display labels remain unquoted.
|
|
1966
|
+
\$quoteArgument = {
|
|
1967
|
+
param([string]\$value)
|
|
1968
|
+
if (\$value -cmatch '\\A[a-zA-Z0-9_./:\\\\-]+\\z' -and \$value -cnotmatch '\\A-(?!-).*[.:]' -and \$value -cnotmatch '\\A-?(?:[0-9]|\\.[0-9])') {
|
|
1969
|
+
return \$value
|
|
1970
|
+
}
|
|
1971
|
+
return "'" + [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent(\$value) + "'"
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1740
1974
|
# Extract arguments from AST (handles quoted strings properly)
|
|
1741
1975
|
\$arguments = @()
|
|
1742
1976
|
\$commandElements = \$commandAst.CommandElements
|
|
@@ -1750,8 +1984,11 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1750
1984
|
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1751
1985
|
# wordToComplete has synthesized delimiters and decoded escapes.
|
|
1752
1986
|
# Only the AST retains the literal value and original quote type.
|
|
1987
|
+
# PSReadLine leaves the cursor before a directory's closing quote.
|
|
1753
1988
|
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst] -and
|
|
1754
|
-
\$element.Extent.EndOffset -eq \$cursorPosition
|
|
1989
|
+
(\$element.Extent.EndOffset -eq \$cursorPosition -or
|
|
1990
|
+
(\$element.Extent.EndOffset -eq (\$cursorPosition + 1) -and
|
|
1991
|
+
\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')))) {
|
|
1755
1992
|
if (\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')) {
|
|
1756
1993
|
\$valuePrefix = \$element.Value
|
|
1757
1994
|
\$quoteType = \$element.StringConstantType
|
|
@@ -1784,7 +2021,7 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1784
2021
|
} elseif (\$quoteType -eq 'DoubleQuoted') {
|
|
1785
2022
|
'"' + (\$text -replace '[\\x60\$"\\u201c-\\u201e]', '\`\$0') + '"'
|
|
1786
2023
|
} else {
|
|
1787
|
-
\$text
|
|
2024
|
+
& \$quoteArgument \$text
|
|
1788
2025
|
}
|
|
1789
2026
|
}
|
|
1790
2027
|
|
|
@@ -1889,7 +2126,10 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1889
2126
|
\$extList = (\$extensions -split ',') -replace '^\\.',''
|
|
1890
2127
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue |
|
|
1891
2128
|
Where-Object {
|
|
1892
|
-
\$
|
|
2129
|
+
\$fileName = \$_.Name
|
|
2130
|
+
\$_.PSIsContainer -or (@(\$extList | Where-Object {
|
|
2131
|
+
\$fileName.EndsWith('.' + \$_, [System.StringComparison]::Ordinal)
|
|
2132
|
+
}).Count -gt 0)
|
|
1893
2133
|
}
|
|
1894
2134
|
} else {
|
|
1895
2135
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue
|
|
@@ -1904,7 +2144,10 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1904
2144
|
\$extList = (\$extensions -split ',') -replace '^\\.',''
|
|
1905
2145
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue |
|
|
1906
2146
|
Where-Object {
|
|
1907
|
-
\$
|
|
2147
|
+
\$fileName = \$_.Name
|
|
2148
|
+
\$_.PSIsContainer -or (@(\$extList | Where-Object {
|
|
2149
|
+
\$fileName.EndsWith('.' + \$_, [System.StringComparison]::Ordinal)
|
|
2150
|
+
}).Count -gt 0)
|
|
1908
2151
|
}
|
|
1909
2152
|
} else {
|
|
1910
2153
|
\$items = Get-ChildItem @forceParam -Path \$globPath -ErrorAction SilentlyContinue
|
|
@@ -1936,15 +2179,32 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1936
2179
|
\$items | ForEach-Object {
|
|
1937
2180
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1938
2181
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
2182
|
+
\$resultType = if (\$_.PSIsContainer) { 'ProviderContainer' } else { 'ParameterValue' }
|
|
2183
|
+
# PSReadLine recognizes the platform separator for containers.
|
|
2184
|
+
# Keep the display label's existing slash convention.
|
|
2185
|
+
\$directorySuffix = [IO.Path]::DirectorySeparatorChar
|
|
2186
|
+
\$pathText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)\$directorySuffix" } else { \$completionText }
|
|
1939
2187
|
# Quoting disables native tilde expansion. A home pattern
|
|
1940
2188
|
# supplied by the parser must insert the discovered path.
|
|
1941
2189
|
\$replacementText = if (\$null -ne \$quoteType -and -not \$prefixFromInput -and \$homePattern) {
|
|
1942
|
-
if (\$_.PSIsContainer) { \$_.FullName +
|
|
1943
|
-
} else { \$
|
|
2190
|
+
if (\$_.PSIsContainer) { \$_.FullName + \$directorySuffix } else { \$_.FullName }
|
|
2191
|
+
} else { \$pathText }
|
|
2192
|
+
# Quoting suppresses native tilde expansion. Keep safe
|
|
2193
|
+
# ~/ paths bare; otherwise use the enumerated full path.
|
|
2194
|
+
\$insertionText = if (\$null -ne \$quoteType) {
|
|
2195
|
+
& \$quoteCompletion \$replacementText
|
|
2196
|
+
} elseif (\$completionText -cmatch '\\A~/[a-zA-Z0-9_./:\\\\-]*\\z') {
|
|
2197
|
+
\$pathText
|
|
2198
|
+
} else {
|
|
2199
|
+
\$argumentText = if (\$completionText.StartsWith('~/') -or \$completionText.StartsWith('~\\')) {
|
|
2200
|
+
if (\$_.PSIsContainer) { \$_.FullName + \$directorySuffix } else { \$_.FullName }
|
|
2201
|
+
} else { \$pathText }
|
|
2202
|
+
& \$quoteArgument \$argumentText
|
|
2203
|
+
}
|
|
1944
2204
|
[System.Management.Automation.CompletionResult]::new(
|
|
1945
|
-
|
|
2205
|
+
\$insertionText,
|
|
1946
2206
|
\$completionText,
|
|
1947
|
-
|
|
2207
|
+
\$resultType,
|
|
1948
2208
|
\$itemType
|
|
1949
2209
|
)
|
|
1950
2210
|
}
|
|
@@ -16,10 +16,13 @@ function joinedFileSuggestion(prefix, file, value) {
|
|
|
16
16
|
text: prefix + (file.pattern ?? ""),
|
|
17
17
|
description: file.description
|
|
18
18
|
};
|
|
19
|
-
|
|
19
|
+
joinedFiles.set(literal, {
|
|
20
20
|
files: [{
|
|
21
21
|
prefix,
|
|
22
|
-
file
|
|
22
|
+
file: file.pattern === value && !/[?*]/.test(value) ? {
|
|
23
|
+
...file,
|
|
24
|
+
pattern: void 0
|
|
25
|
+
} : file
|
|
23
26
|
}],
|
|
24
27
|
literal: false
|
|
25
28
|
});
|
|
@@ -15,10 +15,13 @@ function joinedFileSuggestion(prefix, file, value) {
|
|
|
15
15
|
text: prefix + (file.pattern ?? ""),
|
|
16
16
|
description: file.description
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
joinedFiles.set(literal, {
|
|
19
19
|
files: [{
|
|
20
20
|
prefix,
|
|
21
|
-
file
|
|
21
|
+
file: file.pattern === value && !/[?*]/.test(value) ? {
|
|
22
|
+
...file,
|
|
23
|
+
pattern: void 0
|
|
24
|
+
} : file
|
|
22
25
|
}],
|
|
23
26
|
literal: false
|
|
24
27
|
});
|