@optique/core 1.0.15-dev.2883 → 1.0.15-dev.2887
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 +299 -13
- package/dist/completion.js +299 -13
- package/package.json +1 -1
package/dist/completion.cjs
CHANGED
|
@@ -808,10 +808,24 @@ const zsh = {
|
|
|
808
808
|
const fixedGlobFunction = "__optique-fixed-glob-" + Array.from(programName, (character) => character.charCodeAt(0).toString(16).padStart(2, "0")).join("");
|
|
809
809
|
return `#compdef ${programName}
|
|
810
810
|
function ${functionName} () {
|
|
811
|
-
|
|
811
|
+
# Completion fields omit surrounding quotes, unlike words[CURRENT].
|
|
812
|
+
# Single-quoted fields are already literal; other fields retain escapes.
|
|
813
|
+
local current="\$IPREFIX\$PREFIX\$SUFFIX\$ISUFFIX"
|
|
814
|
+
local __logical_prefix="\$IPREFIX\$PREFIX"
|
|
812
815
|
# Let zsh match the suffix when completing in the middle of a word.
|
|
813
816
|
if [[ -o complete_in_word ]]; then
|
|
814
|
-
current="\$
|
|
817
|
+
current="\$__logical_prefix"
|
|
818
|
+
fi
|
|
819
|
+
if [[ "\$compstate[quote]" == '\"' ]]; then
|
|
820
|
+
# Restore the omitted quoting context before removing shell escapes.
|
|
821
|
+
# In double quotes, a backslash before an ordinary character is literal.
|
|
822
|
+
current="\$QIPREFIX\$current\${QISUFFIX:-\$compstate[quote]}"
|
|
823
|
+
__logical_prefix="\$QIPREFIX\$__logical_prefix\$compstate[quote]"
|
|
824
|
+
current="\${(Q)current}"
|
|
825
|
+
__logical_prefix="\${(Q)__logical_prefix}"
|
|
826
|
+
elif [[ "\$compstate[quote]" != "'" ]]; then
|
|
827
|
+
current="\${(Q)current}"
|
|
828
|
+
__logical_prefix="\${(Q)__logical_prefix}"
|
|
815
829
|
fi
|
|
816
830
|
local -a prev
|
|
817
831
|
# Extract previous arguments, skipping empty ones
|
|
@@ -819,7 +833,7 @@ function ${functionName} () {
|
|
|
819
833
|
local i
|
|
820
834
|
for (( i=2; i < CURRENT; i++ )); do
|
|
821
835
|
if [[ -n "\$words[i]" ]]; then
|
|
822
|
-
prev+=("\$words[i]")
|
|
836
|
+
prev+=("\${(Q)words[i]}")
|
|
823
837
|
fi
|
|
824
838
|
done
|
|
825
839
|
|
|
@@ -906,16 +920,20 @@ function ${functionName} () {
|
|
|
906
920
|
fi
|
|
907
921
|
fi
|
|
908
922
|
fi
|
|
909
|
-
|
|
923
|
+
local __saved_pattern_match="\$compstate[pattern_match]"
|
|
924
|
+
local __pattern_match_changed=0
|
|
925
|
+
# path() echoes current as its pattern. Keep zsh's escaped PREFIX
|
|
926
|
+
# in that case instead of reinterpreting logical text as shell syntax.
|
|
927
|
+
if [[ -n "\$pattern" && "\$pattern" != "\$current" && -z "\$__initial_file_pattern" ]]; then
|
|
910
928
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
911
|
-
local __norm_prefix="\${
|
|
929
|
+
local __norm_prefix="\${__logical_prefix#./}"
|
|
912
930
|
local __norm_pattern="\${pattern#./}"
|
|
913
931
|
local __compare_pattern="\$__norm_pattern"
|
|
914
932
|
if [[ "\$__compare_pattern" == *[\*\?]* ]]; then
|
|
915
933
|
__compare_pattern="\${__compare_pattern%%[\*\?]*}"
|
|
916
934
|
[[ "\$__compare_pattern" == */* ]] && __compare_pattern="\${__compare_pattern%/*}/" || __compare_pattern=""
|
|
917
935
|
fi
|
|
918
|
-
if [[ ( -n "\$__compare_pattern" || -n "\$__norm_prefix" ) && \${#__norm_prefix} -ge \${#__compare_pattern} && "\${__norm_prefix[1,\${#__compare_pattern}]}" == "\$__compare_pattern" && "\$
|
|
936
|
+
if [[ ( -n "\$__compare_pattern" || -n "\$__norm_prefix" ) && \${#__norm_prefix} -ge \${#__compare_pattern} && "\${__norm_prefix[1,\${#__compare_pattern}]}" == "\$__compare_pattern" && "\$__logical_prefix" != "\$pattern" ]]; then
|
|
919
937
|
# Keep the typed prefix, but retain a fixed file glob separately.
|
|
920
938
|
if [[ "\$type" != "directory" && "\$__norm_pattern" == *[\*\?]* ]]; then
|
|
921
939
|
__fixed_glob=1
|
|
@@ -981,7 +999,243 @@ function ${functionName} () {
|
|
|
981
999
|
if [[ "\$__fixed_was_glob_dots" == "0" && "\${PREFIX##*/}" != .* ]]; then unsetopt glob_dots; fi
|
|
982
1000
|
fi
|
|
983
1001
|
else
|
|
984
|
-
|
|
1002
|
+
local __native_pattern="\$pattern"
|
|
1003
|
+
# Initial globs use the conservative native filters above.
|
|
1004
|
+
# Retained input and protected markers still need pattern matching.
|
|
1005
|
+
if [[ -n "\$compstate[context]" && "\$pattern" == *[\\*\\?\\[]* &&
|
|
1006
|
+
( -n "\$current" || "\$pattern" == *'$'* || "\$pattern" == *'\`'* ) ]]; then
|
|
1007
|
+
compstate[pattern_match]='*'
|
|
1008
|
+
__pattern_match_changed=1
|
|
1009
|
+
fi
|
|
1010
|
+
# Native helpers expand substitutions, tilde names and executable
|
|
1011
|
+
# glob qualifiers. Input-derived patterns must keep these literal.
|
|
1012
|
+
# Ordinary fixed glob/tilde patterns retain their native behavior.
|
|
1013
|
+
local __tilde_name="\${pattern#\\~}"
|
|
1014
|
+
__tilde_name="\${__tilde_name%%/*}"
|
|
1015
|
+
local -a match mbegin mend
|
|
1016
|
+
local __code_qualifier=0
|
|
1017
|
+
local __fixed_qualifier_base=""
|
|
1018
|
+
local __safe_qualifiers='([-/F.@=p*rwxAIERWXsStUG^MTNDn,]|[ugdl]<->|[amc]([Mwhmsd]|)([-+]|)<->|L([kKmMgGtTpP]|)([-+]|)<->|[oO][nLlamcdN])##'
|
|
1019
|
+
local __qualifier_pattern="\$pattern" __qualifier
|
|
1020
|
+
while _have_glob_qual "\$__qualifier_pattern" complete; do
|
|
1021
|
+
__qualifier="\${match[5]#\\#q}"
|
|
1022
|
+
__qualifier_pattern="\$match[2]"
|
|
1023
|
+
# A numeric sense (+1) is not a function qualifier (+name).
|
|
1024
|
+
# Accept only bounded attribute/sort syntax in this exception.
|
|
1025
|
+
# Expand the pattern at runtime after unsetting SH_GLOB.
|
|
1026
|
+
if [[ "\$__qualifier" == \${~__safe_qualifiers} ]]; then
|
|
1027
|
+
__fixed_qualifier_base="\$__qualifier_pattern"
|
|
1028
|
+
elif [[ "\$__qualifier" == *[e+]* ]]; then
|
|
1029
|
+
__code_qualifier=1
|
|
1030
|
+
fi
|
|
1031
|
+
done
|
|
1032
|
+
# Partial overlaps with complete backend classes/groups remain
|
|
1033
|
+
# syntax. A complete input-provided expression can be literal.
|
|
1034
|
+
local -a __syntax_starts=() __syntax_ends=() __group_stack=()
|
|
1035
|
+
local -i __scan_index __class_begin=0 __member_begin=0
|
|
1036
|
+
local __scan_char __nested_class=""
|
|
1037
|
+
for (( __scan_index=1; __scan_index <= \${#pattern}; __scan_index++ )); do
|
|
1038
|
+
__scan_char="\${pattern[__scan_index]}"
|
|
1039
|
+
if [[ "\$__scan_char" == '\\' ]]; then
|
|
1040
|
+
(( __scan_index++ ))
|
|
1041
|
+
elif (( __class_begin )); then
|
|
1042
|
+
if [[ "\$__scan_char" == '[' && "\${pattern[__scan_index+1]}" == [:.=] ]]; then
|
|
1043
|
+
__nested_class="\${pattern[__scan_index+1]}"
|
|
1044
|
+
elif [[ -n "\$__nested_class" && "\$__scan_char" == "\$__nested_class" && "\${pattern[__scan_index+1]}" == ']' ]]; then
|
|
1045
|
+
(( __scan_index++ ))
|
|
1046
|
+
__nested_class=""
|
|
1047
|
+
elif [[ -z "\$__nested_class" && "\$__scan_char" == ']' ]] && (( __scan_index > __member_begin )); then
|
|
1048
|
+
__syntax_starts+=("\$__class_begin")
|
|
1049
|
+
__syntax_ends+=("\$__scan_index")
|
|
1050
|
+
__class_begin=0
|
|
1051
|
+
elif (( __scan_index == __member_begin )) &&
|
|
1052
|
+
[[ "\$__scan_char" == '!' || "\$__scan_char" == '^' ]] &&
|
|
1053
|
+
[[ "\${pattern[__scan_index+1]}" != ']' || "\${pattern[__scan_index+2,-1]}" == *']'* ]]; then
|
|
1054
|
+
(( __member_begin++ ))
|
|
1055
|
+
fi
|
|
1056
|
+
elif [[ "\$__scan_char" == '[' ]]; then
|
|
1057
|
+
__class_begin=\$__scan_index
|
|
1058
|
+
__member_begin=\$(( __scan_index + 1 ))
|
|
1059
|
+
elif [[ "\$__scan_char" == '(' ]]; then
|
|
1060
|
+
__group_stack+=("\$__scan_index")
|
|
1061
|
+
elif [[ "\$__scan_char" == ')' ]] && (( \${#__group_stack} )); then
|
|
1062
|
+
__syntax_starts+=("\${__group_stack[-1]}")
|
|
1063
|
+
__syntax_ends+=("\$__scan_index")
|
|
1064
|
+
__group_stack[-1]=()
|
|
1065
|
+
fi
|
|
1066
|
+
done
|
|
1067
|
+
# Search retained input only where it cannot split fixed syntax.
|
|
1068
|
+
local __input_part="\$current"
|
|
1069
|
+
local __backend_prefix="\$pattern" __backend_suffix=""
|
|
1070
|
+
local __search_pattern __candidate_prefix
|
|
1071
|
+
local -i __search_offset __overlap_start __overlap_end __range_index __syntax_overlap
|
|
1072
|
+
local -i __fixed_tilde_end=0
|
|
1073
|
+
local __tilde_expression='([+-]|[+-]<->|[[:alnum:]_.-]#)'
|
|
1074
|
+
if [[ "\$pattern" == '~'* &&
|
|
1075
|
+
"\$__tilde_name" == \${~__tilde_expression} ]]; then
|
|
1076
|
+
__fixed_tilde_end=\$(( \${#__tilde_name} + 1 ))
|
|
1077
|
+
fi
|
|
1078
|
+
while [[ -n "\$__input_part" ]]; do
|
|
1079
|
+
__search_pattern="\$pattern"
|
|
1080
|
+
__search_offset=0
|
|
1081
|
+
while [[ "\$__search_pattern" == *"\$__input_part"* ]]; do
|
|
1082
|
+
__candidate_prefix="\${__search_pattern%%"\$__input_part"*}"
|
|
1083
|
+
__overlap_start=\$(( __search_offset + \${#__candidate_prefix} + 1 ))
|
|
1084
|
+
__overlap_end=\$(( __overlap_start + \${#__input_part} - 1 ))
|
|
1085
|
+
__syntax_overlap=0
|
|
1086
|
+
# Leading home/directory-stack/named-directory syntax belongs
|
|
1087
|
+
# to the fixed pattern, even when the input overlaps it.
|
|
1088
|
+
if (( __fixed_tilde_end && __overlap_start <= __fixed_tilde_end )); then
|
|
1089
|
+
__syntax_overlap=1
|
|
1090
|
+
fi
|
|
1091
|
+
for (( __range_index=1; __range_index <= \${#__syntax_starts}; __range_index++ )); do
|
|
1092
|
+
if (( __overlap_start <= __syntax_ends[__range_index] &&
|
|
1093
|
+
__overlap_end >= __syntax_starts[__range_index] &&
|
|
1094
|
+
( __overlap_start > __syntax_starts[__range_index] ||
|
|
1095
|
+
__overlap_end < __syntax_ends[__range_index] ) )); then
|
|
1096
|
+
__syntax_overlap=1
|
|
1097
|
+
break
|
|
1098
|
+
fi
|
|
1099
|
+
done
|
|
1100
|
+
# Every position in a recognized fixed attribute qualifier
|
|
1101
|
+
# is backend syntax, including its closing parenthesis.
|
|
1102
|
+
if [[ "\$__fixed_qualifier_base" == *[\\*\\?\\]] ]] &&
|
|
1103
|
+
(( __overlap_end > \${#__fixed_qualifier_base} )); then
|
|
1104
|
+
__syntax_overlap=1
|
|
1105
|
+
fi
|
|
1106
|
+
if (( ! __syntax_overlap )); then
|
|
1107
|
+
if (( __overlap_start == 1 )); then
|
|
1108
|
+
__backend_prefix=""
|
|
1109
|
+
else
|
|
1110
|
+
__backend_prefix="\${pattern[1,__overlap_start-1]}"
|
|
1111
|
+
fi
|
|
1112
|
+
__backend_suffix="\${pattern[__overlap_end+1,-1]}"
|
|
1113
|
+
break 2
|
|
1114
|
+
fi
|
|
1115
|
+
__search_offset=\$__overlap_start
|
|
1116
|
+
__search_pattern="\${pattern[__overlap_start+1,-1]}"
|
|
1117
|
+
done
|
|
1118
|
+
__input_part="\${__input_part[2,-1]}"
|
|
1119
|
+
done
|
|
1120
|
+
local __quoted_input="\${(q)__input_part}"
|
|
1121
|
+
# Pattern matching removes quoting differently from normal
|
|
1122
|
+
# completion. Character classes keep these characters literal.
|
|
1123
|
+
if [[ -n "\$compstate[pattern_match]" ]]; then
|
|
1124
|
+
__quoted_input="\${(b)__input_part}"
|
|
1125
|
+
__quoted_input="\${__quoted_input//\\\$/[\\\$]}"
|
|
1126
|
+
__quoted_input="\${__quoted_input//\\\`/[\\\`]}"
|
|
1127
|
+
fi
|
|
1128
|
+
if [[ "\$pattern" == *'$'* || "\$pattern" == *'\`'* ||
|
|
1129
|
+
( "\$pattern" == '~'* && "\$__tilde_name" == *[^[:alnum:]_.+-]* ) ]] ||
|
|
1130
|
+
(( __code_qualifier )); then
|
|
1131
|
+
__native_pattern="\${(q)pattern}"
|
|
1132
|
+
local __backend_pattern="\$__backend_prefix\$__backend_suffix"
|
|
1133
|
+
local __backend_tilde="\${__backend_pattern#\\~}"
|
|
1134
|
+
__backend_tilde="\${__backend_tilde%%/*}"
|
|
1135
|
+
local __unsafe_backend_qualifier=0
|
|
1136
|
+
__qualifier_pattern="\$__backend_pattern"
|
|
1137
|
+
while _have_glob_qual "\$__qualifier_pattern" complete; do
|
|
1138
|
+
__qualifier="\${match[5]#\\#q}"
|
|
1139
|
+
__qualifier_pattern="\$match[2]"
|
|
1140
|
+
if [[ "\$__qualifier" != \${~__safe_qualifiers} ]]; then
|
|
1141
|
+
__unsafe_backend_qualifier=1
|
|
1142
|
+
fi
|
|
1143
|
+
done
|
|
1144
|
+
# Retain trusted wildcards and safe attribute qualifiers when
|
|
1145
|
+
# all executable syntax belongs to the quoted input.
|
|
1146
|
+
if [[ -n "\$__input_part" &&
|
|
1147
|
+
"\$__backend_pattern" != *'$'* &&
|
|
1148
|
+
"\$__backend_pattern" != *'\`'* &&
|
|
1149
|
+
( "\$__backend_pattern" != '~'* ||
|
|
1150
|
+
"\$__backend_tilde" != *[^[:alnum:]_.+-]* ) ]] &&
|
|
1151
|
+
(( ! __unsafe_backend_qualifier )); then
|
|
1152
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1153
|
+
elif [[ "\$__backend_pattern" != *'$('* &&
|
|
1154
|
+
"\$__backend_pattern" != *'\${'* &&
|
|
1155
|
+
"\$__backend_pattern" != *'$['* &&
|
|
1156
|
+
( "\$pattern" != '~'* ||
|
|
1157
|
+
"\$__tilde_name" != *[^[:alnum:]_.+-]* ) ]] &&
|
|
1158
|
+
(( ! __unsafe_backend_qualifier )); then
|
|
1159
|
+
# Fixed patterns also need literal substitution markers,
|
|
1160
|
+
# without quoting their wildcards or safe qualifiers.
|
|
1161
|
+
local __dollar='\\\$'
|
|
1162
|
+
local __backtick='\\\`'
|
|
1163
|
+
if [[ -n "\$compstate[pattern_match]" ]]; then
|
|
1164
|
+
__dollar='[\$]'
|
|
1165
|
+
__backtick='[\`]'
|
|
1166
|
+
fi
|
|
1167
|
+
# Existing glob classes already protect their markers.
|
|
1168
|
+
local -a __backend_parts=()
|
|
1169
|
+
local __part __escaped __char __class_kind
|
|
1170
|
+
local -i __index __in_class __class_start
|
|
1171
|
+
for __part in "\$__backend_prefix" "\$__backend_suffix"; do
|
|
1172
|
+
__escaped=""
|
|
1173
|
+
__in_class=0
|
|
1174
|
+
__class_kind=""
|
|
1175
|
+
for (( __index=1; __index <= \${#__part}; __index++ )); do
|
|
1176
|
+
__char="\${__part[__index]}"
|
|
1177
|
+
if [[ "\$__char" == '\\' ]]; then
|
|
1178
|
+
__escaped+="\$__char\${__part[__index+1]}"
|
|
1179
|
+
(( __index++ ))
|
|
1180
|
+
continue
|
|
1181
|
+
elif (( __in_class )); then
|
|
1182
|
+
if [[ "\$__char" == '[' && "\${__part[__index+1]}" == [:.=] ]]; then
|
|
1183
|
+
__class_kind="\${__part[__index+1]}"
|
|
1184
|
+
elif [[ -n "\$__class_kind" && "\$__char" == "\$__class_kind" && "\${__part[__index+1]}" == ']' ]]; then
|
|
1185
|
+
__escaped+="\$__char]"
|
|
1186
|
+
(( __index++ ))
|
|
1187
|
+
__class_kind=""
|
|
1188
|
+
continue
|
|
1189
|
+
elif [[ -z "\$__class_kind" && "\$__char" == ']' ]] && (( __index > __class_start )); then
|
|
1190
|
+
__in_class=0
|
|
1191
|
+
elif (( __index == __class_start )) && [[ "\$__char" == '!' || "\$__char" == '^' ]]; then
|
|
1192
|
+
(( __class_start++ ))
|
|
1193
|
+
fi
|
|
1194
|
+
elif [[ "\$__char" == '[' ]]; then
|
|
1195
|
+
__in_class=1
|
|
1196
|
+
__class_start=$(( __index + 1 ))
|
|
1197
|
+
elif [[ "\$__char" == '$' ]]; then
|
|
1198
|
+
__char="\$__dollar"
|
|
1199
|
+
elif [[ "\$__char" == '\`' ]]; then
|
|
1200
|
+
__char="\$__backtick"
|
|
1201
|
+
fi
|
|
1202
|
+
__escaped+="\$__char"
|
|
1203
|
+
done
|
|
1204
|
+
__backend_parts+=("\$__escaped")
|
|
1205
|
+
done
|
|
1206
|
+
__backend_prefix="\${__backend_parts[1]}"
|
|
1207
|
+
__backend_suffix="\${__backend_parts[2]}"
|
|
1208
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1209
|
+
fi
|
|
1210
|
+
else
|
|
1211
|
+
# Quote retained metacharacters, including segments made only
|
|
1212
|
+
# of them. Wildcard-only overlap remains backend syntax.
|
|
1213
|
+
if [[ "\$__input_part" == *[\\\\\\(\\)\\[\\#\\^\\|\\~\\<\\>\\*\\?]* &&
|
|
1214
|
+
"\$__input_part" != [\\*\\?]## ]]; then
|
|
1215
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1216
|
+
fi
|
|
1217
|
+
fi
|
|
1218
|
+
PREFIX="\$__native_pattern"
|
|
1219
|
+
# Match simple fixed tilde globs through native -g filters. The
|
|
1220
|
+
# pattern matcher otherwise treats the leading ~ as glob syntax,
|
|
1221
|
+
# even after _path_files has expanded it for filesystem lookup.
|
|
1222
|
+
if (( __fixed_tilde_end )) && [[ -z "\$__input_part" &&
|
|
1223
|
+
"\$__native_pattern" == "\$pattern" && "\$pattern" == */* ]]; then
|
|
1224
|
+
__initial_directory="\${pattern%/*}/"
|
|
1225
|
+
__initial_leaf="\${pattern##*/}"
|
|
1226
|
+
if [[ "\$__initial_leaf" == *[\*\?]* &&
|
|
1227
|
+
"\$__initial_directory" != *[\*\?]* &&
|
|
1228
|
+
"\$__initial_directory" != *["[]()"]* &&
|
|
1229
|
+
"\$__initial_leaf" != *["[]()#~^|<>;&"]* &&
|
|
1230
|
+
"\$__initial_leaf" != *'\\'* &&
|
|
1231
|
+
"\$__initial_leaf" != *[[:space:]]* &&
|
|
1232
|
+
"\$__initial_leaf" != *"'"* && "\$__initial_leaf" != *'"'* ]]; then
|
|
1233
|
+
__initial_file_pattern="\$__initial_leaf"
|
|
1234
|
+
PREFIX="\$__initial_directory"
|
|
1235
|
+
compstate[pattern_match]=""
|
|
1236
|
+
__pattern_match_changed=1
|
|
1237
|
+
fi
|
|
1238
|
+
fi
|
|
985
1239
|
fi
|
|
986
1240
|
fi
|
|
987
1241
|
|
|
@@ -1021,6 +1275,21 @@ function ${functionName} () {
|
|
|
1021
1275
|
fi
|
|
1022
1276
|
file_pattern="\${(j: :)__initial_file_patterns}"
|
|
1023
1277
|
fi
|
|
1278
|
+
local file_pattern_prefix="\$PREFIX"
|
|
1279
|
+
local -a match mbegin mend
|
|
1280
|
+
# Native qualifier copying concatenates flags with the explicit
|
|
1281
|
+
# regular-file group. Keep backend attributes in their own group.
|
|
1282
|
+
if [[ -n "\$compstate[pattern_match]" && -z "\$SUFFIX" ]]; then
|
|
1283
|
+
local file_qualifier file_qualifiers=""
|
|
1284
|
+
while _have_glob_qual "\$file_pattern_prefix" complete; do
|
|
1285
|
+
file_qualifier="\${match[5]#\\#q}"
|
|
1286
|
+
file_pattern_prefix="\$match[2]"
|
|
1287
|
+
file_qualifiers="(#q\$file_qualifier)\$file_qualifiers"
|
|
1288
|
+
done
|
|
1289
|
+
if [[ -n "\$file_qualifiers" ]]; then
|
|
1290
|
+
file_pattern+="\$file_qualifiers"
|
|
1291
|
+
fi
|
|
1292
|
+
fi
|
|
1024
1293
|
local file_subtree_pattern="**/*(#q/N)"
|
|
1025
1294
|
local -a file_subtree=() file_recursive_patterns=()
|
|
1026
1295
|
local -a file_tags=(files directories)
|
|
@@ -1034,10 +1303,17 @@ function ${functionName} () {
|
|
|
1034
1303
|
local file_matched=0
|
|
1035
1304
|
for file_tag in "\${file_tags[@]}"; do
|
|
1036
1305
|
if [[ "\$file_tag" == directories ]]; then
|
|
1306
|
+
local file_tag_prefix="\$PREFIX"
|
|
1307
|
+
PREFIX="\$file_pattern_prefix"
|
|
1037
1308
|
while _next_label directories expl directory; do
|
|
1309
|
+
# File qualifiers constrain final results, not the
|
|
1310
|
+
# directories needed to navigate to those results.
|
|
1038
1311
|
_path_files -/ "\${expl[@]}" && file_matched=1
|
|
1039
1312
|
done
|
|
1313
|
+
PREFIX="\$file_tag_prefix"
|
|
1040
1314
|
else
|
|
1315
|
+
local file_tag_prefix="\$PREFIX"
|
|
1316
|
+
PREFIX="\$file_pattern_prefix"
|
|
1041
1317
|
while _next_label files expl file; do
|
|
1042
1318
|
local -a file_ignore=() file_ignore_args=()
|
|
1043
1319
|
# Explicit globs bypass automatic fignore. Apply it only
|
|
@@ -1068,6 +1344,7 @@ function ${functionName} () {
|
|
|
1068
1344
|
done
|
|
1069
1345
|
fi
|
|
1070
1346
|
done
|
|
1347
|
+
PREFIX="\$file_tag_prefix"
|
|
1071
1348
|
fi
|
|
1072
1349
|
done
|
|
1073
1350
|
(( file_matched )) && break
|
|
@@ -1096,6 +1373,9 @@ function ${functionName} () {
|
|
|
1096
1373
|
# Restore PREFIX and glob_dots to their previous state
|
|
1097
1374
|
PREFIX="\$__saved_prefix"
|
|
1098
1375
|
if [[ "\$__was_extended_glob" == "1" ]]; then setopt extended_glob; else unsetopt extended_glob; fi
|
|
1376
|
+
if (( __pattern_match_changed )); then
|
|
1377
|
+
compstate[pattern_match]="\$__saved_pattern_match"
|
|
1378
|
+
fi
|
|
1099
1379
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
1100
1380
|
if [[ "\$__was_sh_word_split" == "1" ]]; then setopt sh_word_split; else unsetopt sh_word_split; fi
|
|
1101
1381
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
@@ -1114,17 +1394,23 @@ function ${functionName} () {
|
|
|
1114
1394
|
# Prepare completion with descriptions for _describe
|
|
1115
1395
|
local -a matches
|
|
1116
1396
|
local -i i
|
|
1397
|
+
local __match
|
|
1117
1398
|
for (( i=1; i <= \${#completions[@]}; i++ )); do
|
|
1118
|
-
# _describe
|
|
1119
|
-
|
|
1120
|
-
|
|
1399
|
+
# _describe uses backslashes to escape its colon separator.
|
|
1400
|
+
__match="\${completions[i]//\\\\/\\\\\\\\}"
|
|
1401
|
+
__match="\${__match//:/\\\\:}"
|
|
1121
1402
|
if [[ -n "\${descriptions[i]}" ]]; then
|
|
1122
|
-
matches+=("\$
|
|
1403
|
+
matches+=("\$__match:\${descriptions[i]}")
|
|
1123
1404
|
else
|
|
1124
|
-
matches+=("\$
|
|
1405
|
+
matches+=("\$__match")
|
|
1125
1406
|
fi
|
|
1126
1407
|
done
|
|
1127
|
-
|
|
1408
|
+
if [[ -n "\$QISUFFIX" ]]; then
|
|
1409
|
+
# A closing quote already exists; do not append a space inside it.
|
|
1410
|
+
_describe 'commands' matches -S ''
|
|
1411
|
+
else
|
|
1412
|
+
_describe 'commands' matches
|
|
1413
|
+
fi
|
|
1128
1414
|
fi
|
|
1129
1415
|
}
|
|
1130
1416
|
|
package/dist/completion.js
CHANGED
|
@@ -808,10 +808,24 @@ const zsh = {
|
|
|
808
808
|
const fixedGlobFunction = "__optique-fixed-glob-" + Array.from(programName, (character) => character.charCodeAt(0).toString(16).padStart(2, "0")).join("");
|
|
809
809
|
return `#compdef ${programName}
|
|
810
810
|
function ${functionName} () {
|
|
811
|
-
|
|
811
|
+
# Completion fields omit surrounding quotes, unlike words[CURRENT].
|
|
812
|
+
# Single-quoted fields are already literal; other fields retain escapes.
|
|
813
|
+
local current="\$IPREFIX\$PREFIX\$SUFFIX\$ISUFFIX"
|
|
814
|
+
local __logical_prefix="\$IPREFIX\$PREFIX"
|
|
812
815
|
# Let zsh match the suffix when completing in the middle of a word.
|
|
813
816
|
if [[ -o complete_in_word ]]; then
|
|
814
|
-
current="\$
|
|
817
|
+
current="\$__logical_prefix"
|
|
818
|
+
fi
|
|
819
|
+
if [[ "\$compstate[quote]" == '\"' ]]; then
|
|
820
|
+
# Restore the omitted quoting context before removing shell escapes.
|
|
821
|
+
# In double quotes, a backslash before an ordinary character is literal.
|
|
822
|
+
current="\$QIPREFIX\$current\${QISUFFIX:-\$compstate[quote]}"
|
|
823
|
+
__logical_prefix="\$QIPREFIX\$__logical_prefix\$compstate[quote]"
|
|
824
|
+
current="\${(Q)current}"
|
|
825
|
+
__logical_prefix="\${(Q)__logical_prefix}"
|
|
826
|
+
elif [[ "\$compstate[quote]" != "'" ]]; then
|
|
827
|
+
current="\${(Q)current}"
|
|
828
|
+
__logical_prefix="\${(Q)__logical_prefix}"
|
|
815
829
|
fi
|
|
816
830
|
local -a prev
|
|
817
831
|
# Extract previous arguments, skipping empty ones
|
|
@@ -819,7 +833,7 @@ function ${functionName} () {
|
|
|
819
833
|
local i
|
|
820
834
|
for (( i=2; i < CURRENT; i++ )); do
|
|
821
835
|
if [[ -n "\$words[i]" ]]; then
|
|
822
|
-
prev+=("\$words[i]")
|
|
836
|
+
prev+=("\${(Q)words[i]}")
|
|
823
837
|
fi
|
|
824
838
|
done
|
|
825
839
|
|
|
@@ -906,16 +920,20 @@ function ${functionName} () {
|
|
|
906
920
|
fi
|
|
907
921
|
fi
|
|
908
922
|
fi
|
|
909
|
-
|
|
923
|
+
local __saved_pattern_match="\$compstate[pattern_match]"
|
|
924
|
+
local __pattern_match_changed=0
|
|
925
|
+
# path() echoes current as its pattern. Keep zsh's escaped PREFIX
|
|
926
|
+
# in that case instead of reinterpreting logical text as shell syntax.
|
|
927
|
+
if [[ -n "\$pattern" && "\$pattern" != "\$current" && -z "\$__initial_file_pattern" ]]; then
|
|
910
928
|
# Normalize leading ./ so that ./src/ma matches pattern src/
|
|
911
|
-
local __norm_prefix="\${
|
|
929
|
+
local __norm_prefix="\${__logical_prefix#./}"
|
|
912
930
|
local __norm_pattern="\${pattern#./}"
|
|
913
931
|
local __compare_pattern="\$__norm_pattern"
|
|
914
932
|
if [[ "\$__compare_pattern" == *[\*\?]* ]]; then
|
|
915
933
|
__compare_pattern="\${__compare_pattern%%[\*\?]*}"
|
|
916
934
|
[[ "\$__compare_pattern" == */* ]] && __compare_pattern="\${__compare_pattern%/*}/" || __compare_pattern=""
|
|
917
935
|
fi
|
|
918
|
-
if [[ ( -n "\$__compare_pattern" || -n "\$__norm_prefix" ) && \${#__norm_prefix} -ge \${#__compare_pattern} && "\${__norm_prefix[1,\${#__compare_pattern}]}" == "\$__compare_pattern" && "\$
|
|
936
|
+
if [[ ( -n "\$__compare_pattern" || -n "\$__norm_prefix" ) && \${#__norm_prefix} -ge \${#__compare_pattern} && "\${__norm_prefix[1,\${#__compare_pattern}]}" == "\$__compare_pattern" && "\$__logical_prefix" != "\$pattern" ]]; then
|
|
919
937
|
# Keep the typed prefix, but retain a fixed file glob separately.
|
|
920
938
|
if [[ "\$type" != "directory" && "\$__norm_pattern" == *[\*\?]* ]]; then
|
|
921
939
|
__fixed_glob=1
|
|
@@ -981,7 +999,243 @@ function ${functionName} () {
|
|
|
981
999
|
if [[ "\$__fixed_was_glob_dots" == "0" && "\${PREFIX##*/}" != .* ]]; then unsetopt glob_dots; fi
|
|
982
1000
|
fi
|
|
983
1001
|
else
|
|
984
|
-
|
|
1002
|
+
local __native_pattern="\$pattern"
|
|
1003
|
+
# Initial globs use the conservative native filters above.
|
|
1004
|
+
# Retained input and protected markers still need pattern matching.
|
|
1005
|
+
if [[ -n "\$compstate[context]" && "\$pattern" == *[\\*\\?\\[]* &&
|
|
1006
|
+
( -n "\$current" || "\$pattern" == *'$'* || "\$pattern" == *'\`'* ) ]]; then
|
|
1007
|
+
compstate[pattern_match]='*'
|
|
1008
|
+
__pattern_match_changed=1
|
|
1009
|
+
fi
|
|
1010
|
+
# Native helpers expand substitutions, tilde names and executable
|
|
1011
|
+
# glob qualifiers. Input-derived patterns must keep these literal.
|
|
1012
|
+
# Ordinary fixed glob/tilde patterns retain their native behavior.
|
|
1013
|
+
local __tilde_name="\${pattern#\\~}"
|
|
1014
|
+
__tilde_name="\${__tilde_name%%/*}"
|
|
1015
|
+
local -a match mbegin mend
|
|
1016
|
+
local __code_qualifier=0
|
|
1017
|
+
local __fixed_qualifier_base=""
|
|
1018
|
+
local __safe_qualifiers='([-/F.@=p*rwxAIERWXsStUG^MTNDn,]|[ugdl]<->|[amc]([Mwhmsd]|)([-+]|)<->|L([kKmMgGtTpP]|)([-+]|)<->|[oO][nLlamcdN])##'
|
|
1019
|
+
local __qualifier_pattern="\$pattern" __qualifier
|
|
1020
|
+
while _have_glob_qual "\$__qualifier_pattern" complete; do
|
|
1021
|
+
__qualifier="\${match[5]#\\#q}"
|
|
1022
|
+
__qualifier_pattern="\$match[2]"
|
|
1023
|
+
# A numeric sense (+1) is not a function qualifier (+name).
|
|
1024
|
+
# Accept only bounded attribute/sort syntax in this exception.
|
|
1025
|
+
# Expand the pattern at runtime after unsetting SH_GLOB.
|
|
1026
|
+
if [[ "\$__qualifier" == \${~__safe_qualifiers} ]]; then
|
|
1027
|
+
__fixed_qualifier_base="\$__qualifier_pattern"
|
|
1028
|
+
elif [[ "\$__qualifier" == *[e+]* ]]; then
|
|
1029
|
+
__code_qualifier=1
|
|
1030
|
+
fi
|
|
1031
|
+
done
|
|
1032
|
+
# Partial overlaps with complete backend classes/groups remain
|
|
1033
|
+
# syntax. A complete input-provided expression can be literal.
|
|
1034
|
+
local -a __syntax_starts=() __syntax_ends=() __group_stack=()
|
|
1035
|
+
local -i __scan_index __class_begin=0 __member_begin=0
|
|
1036
|
+
local __scan_char __nested_class=""
|
|
1037
|
+
for (( __scan_index=1; __scan_index <= \${#pattern}; __scan_index++ )); do
|
|
1038
|
+
__scan_char="\${pattern[__scan_index]}"
|
|
1039
|
+
if [[ "\$__scan_char" == '\\' ]]; then
|
|
1040
|
+
(( __scan_index++ ))
|
|
1041
|
+
elif (( __class_begin )); then
|
|
1042
|
+
if [[ "\$__scan_char" == '[' && "\${pattern[__scan_index+1]}" == [:.=] ]]; then
|
|
1043
|
+
__nested_class="\${pattern[__scan_index+1]}"
|
|
1044
|
+
elif [[ -n "\$__nested_class" && "\$__scan_char" == "\$__nested_class" && "\${pattern[__scan_index+1]}" == ']' ]]; then
|
|
1045
|
+
(( __scan_index++ ))
|
|
1046
|
+
__nested_class=""
|
|
1047
|
+
elif [[ -z "\$__nested_class" && "\$__scan_char" == ']' ]] && (( __scan_index > __member_begin )); then
|
|
1048
|
+
__syntax_starts+=("\$__class_begin")
|
|
1049
|
+
__syntax_ends+=("\$__scan_index")
|
|
1050
|
+
__class_begin=0
|
|
1051
|
+
elif (( __scan_index == __member_begin )) &&
|
|
1052
|
+
[[ "\$__scan_char" == '!' || "\$__scan_char" == '^' ]] &&
|
|
1053
|
+
[[ "\${pattern[__scan_index+1]}" != ']' || "\${pattern[__scan_index+2,-1]}" == *']'* ]]; then
|
|
1054
|
+
(( __member_begin++ ))
|
|
1055
|
+
fi
|
|
1056
|
+
elif [[ "\$__scan_char" == '[' ]]; then
|
|
1057
|
+
__class_begin=\$__scan_index
|
|
1058
|
+
__member_begin=\$(( __scan_index + 1 ))
|
|
1059
|
+
elif [[ "\$__scan_char" == '(' ]]; then
|
|
1060
|
+
__group_stack+=("\$__scan_index")
|
|
1061
|
+
elif [[ "\$__scan_char" == ')' ]] && (( \${#__group_stack} )); then
|
|
1062
|
+
__syntax_starts+=("\${__group_stack[-1]}")
|
|
1063
|
+
__syntax_ends+=("\$__scan_index")
|
|
1064
|
+
__group_stack[-1]=()
|
|
1065
|
+
fi
|
|
1066
|
+
done
|
|
1067
|
+
# Search retained input only where it cannot split fixed syntax.
|
|
1068
|
+
local __input_part="\$current"
|
|
1069
|
+
local __backend_prefix="\$pattern" __backend_suffix=""
|
|
1070
|
+
local __search_pattern __candidate_prefix
|
|
1071
|
+
local -i __search_offset __overlap_start __overlap_end __range_index __syntax_overlap
|
|
1072
|
+
local -i __fixed_tilde_end=0
|
|
1073
|
+
local __tilde_expression='([+-]|[+-]<->|[[:alnum:]_.-]#)'
|
|
1074
|
+
if [[ "\$pattern" == '~'* &&
|
|
1075
|
+
"\$__tilde_name" == \${~__tilde_expression} ]]; then
|
|
1076
|
+
__fixed_tilde_end=\$(( \${#__tilde_name} + 1 ))
|
|
1077
|
+
fi
|
|
1078
|
+
while [[ -n "\$__input_part" ]]; do
|
|
1079
|
+
__search_pattern="\$pattern"
|
|
1080
|
+
__search_offset=0
|
|
1081
|
+
while [[ "\$__search_pattern" == *"\$__input_part"* ]]; do
|
|
1082
|
+
__candidate_prefix="\${__search_pattern%%"\$__input_part"*}"
|
|
1083
|
+
__overlap_start=\$(( __search_offset + \${#__candidate_prefix} + 1 ))
|
|
1084
|
+
__overlap_end=\$(( __overlap_start + \${#__input_part} - 1 ))
|
|
1085
|
+
__syntax_overlap=0
|
|
1086
|
+
# Leading home/directory-stack/named-directory syntax belongs
|
|
1087
|
+
# to the fixed pattern, even when the input overlaps it.
|
|
1088
|
+
if (( __fixed_tilde_end && __overlap_start <= __fixed_tilde_end )); then
|
|
1089
|
+
__syntax_overlap=1
|
|
1090
|
+
fi
|
|
1091
|
+
for (( __range_index=1; __range_index <= \${#__syntax_starts}; __range_index++ )); do
|
|
1092
|
+
if (( __overlap_start <= __syntax_ends[__range_index] &&
|
|
1093
|
+
__overlap_end >= __syntax_starts[__range_index] &&
|
|
1094
|
+
( __overlap_start > __syntax_starts[__range_index] ||
|
|
1095
|
+
__overlap_end < __syntax_ends[__range_index] ) )); then
|
|
1096
|
+
__syntax_overlap=1
|
|
1097
|
+
break
|
|
1098
|
+
fi
|
|
1099
|
+
done
|
|
1100
|
+
# Every position in a recognized fixed attribute qualifier
|
|
1101
|
+
# is backend syntax, including its closing parenthesis.
|
|
1102
|
+
if [[ "\$__fixed_qualifier_base" == *[\\*\\?\\]] ]] &&
|
|
1103
|
+
(( __overlap_end > \${#__fixed_qualifier_base} )); then
|
|
1104
|
+
__syntax_overlap=1
|
|
1105
|
+
fi
|
|
1106
|
+
if (( ! __syntax_overlap )); then
|
|
1107
|
+
if (( __overlap_start == 1 )); then
|
|
1108
|
+
__backend_prefix=""
|
|
1109
|
+
else
|
|
1110
|
+
__backend_prefix="\${pattern[1,__overlap_start-1]}"
|
|
1111
|
+
fi
|
|
1112
|
+
__backend_suffix="\${pattern[__overlap_end+1,-1]}"
|
|
1113
|
+
break 2
|
|
1114
|
+
fi
|
|
1115
|
+
__search_offset=\$__overlap_start
|
|
1116
|
+
__search_pattern="\${pattern[__overlap_start+1,-1]}"
|
|
1117
|
+
done
|
|
1118
|
+
__input_part="\${__input_part[2,-1]}"
|
|
1119
|
+
done
|
|
1120
|
+
local __quoted_input="\${(q)__input_part}"
|
|
1121
|
+
# Pattern matching removes quoting differently from normal
|
|
1122
|
+
# completion. Character classes keep these characters literal.
|
|
1123
|
+
if [[ -n "\$compstate[pattern_match]" ]]; then
|
|
1124
|
+
__quoted_input="\${(b)__input_part}"
|
|
1125
|
+
__quoted_input="\${__quoted_input//\\\$/[\\\$]}"
|
|
1126
|
+
__quoted_input="\${__quoted_input//\\\`/[\\\`]}"
|
|
1127
|
+
fi
|
|
1128
|
+
if [[ "\$pattern" == *'$'* || "\$pattern" == *'\`'* ||
|
|
1129
|
+
( "\$pattern" == '~'* && "\$__tilde_name" == *[^[:alnum:]_.+-]* ) ]] ||
|
|
1130
|
+
(( __code_qualifier )); then
|
|
1131
|
+
__native_pattern="\${(q)pattern}"
|
|
1132
|
+
local __backend_pattern="\$__backend_prefix\$__backend_suffix"
|
|
1133
|
+
local __backend_tilde="\${__backend_pattern#\\~}"
|
|
1134
|
+
__backend_tilde="\${__backend_tilde%%/*}"
|
|
1135
|
+
local __unsafe_backend_qualifier=0
|
|
1136
|
+
__qualifier_pattern="\$__backend_pattern"
|
|
1137
|
+
while _have_glob_qual "\$__qualifier_pattern" complete; do
|
|
1138
|
+
__qualifier="\${match[5]#\\#q}"
|
|
1139
|
+
__qualifier_pattern="\$match[2]"
|
|
1140
|
+
if [[ "\$__qualifier" != \${~__safe_qualifiers} ]]; then
|
|
1141
|
+
__unsafe_backend_qualifier=1
|
|
1142
|
+
fi
|
|
1143
|
+
done
|
|
1144
|
+
# Retain trusted wildcards and safe attribute qualifiers when
|
|
1145
|
+
# all executable syntax belongs to the quoted input.
|
|
1146
|
+
if [[ -n "\$__input_part" &&
|
|
1147
|
+
"\$__backend_pattern" != *'$'* &&
|
|
1148
|
+
"\$__backend_pattern" != *'\`'* &&
|
|
1149
|
+
( "\$__backend_pattern" != '~'* ||
|
|
1150
|
+
"\$__backend_tilde" != *[^[:alnum:]_.+-]* ) ]] &&
|
|
1151
|
+
(( ! __unsafe_backend_qualifier )); then
|
|
1152
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1153
|
+
elif [[ "\$__backend_pattern" != *'$('* &&
|
|
1154
|
+
"\$__backend_pattern" != *'\${'* &&
|
|
1155
|
+
"\$__backend_pattern" != *'$['* &&
|
|
1156
|
+
( "\$pattern" != '~'* ||
|
|
1157
|
+
"\$__tilde_name" != *[^[:alnum:]_.+-]* ) ]] &&
|
|
1158
|
+
(( ! __unsafe_backend_qualifier )); then
|
|
1159
|
+
# Fixed patterns also need literal substitution markers,
|
|
1160
|
+
# without quoting their wildcards or safe qualifiers.
|
|
1161
|
+
local __dollar='\\\$'
|
|
1162
|
+
local __backtick='\\\`'
|
|
1163
|
+
if [[ -n "\$compstate[pattern_match]" ]]; then
|
|
1164
|
+
__dollar='[\$]'
|
|
1165
|
+
__backtick='[\`]'
|
|
1166
|
+
fi
|
|
1167
|
+
# Existing glob classes already protect their markers.
|
|
1168
|
+
local -a __backend_parts=()
|
|
1169
|
+
local __part __escaped __char __class_kind
|
|
1170
|
+
local -i __index __in_class __class_start
|
|
1171
|
+
for __part in "\$__backend_prefix" "\$__backend_suffix"; do
|
|
1172
|
+
__escaped=""
|
|
1173
|
+
__in_class=0
|
|
1174
|
+
__class_kind=""
|
|
1175
|
+
for (( __index=1; __index <= \${#__part}; __index++ )); do
|
|
1176
|
+
__char="\${__part[__index]}"
|
|
1177
|
+
if [[ "\$__char" == '\\' ]]; then
|
|
1178
|
+
__escaped+="\$__char\${__part[__index+1]}"
|
|
1179
|
+
(( __index++ ))
|
|
1180
|
+
continue
|
|
1181
|
+
elif (( __in_class )); then
|
|
1182
|
+
if [[ "\$__char" == '[' && "\${__part[__index+1]}" == [:.=] ]]; then
|
|
1183
|
+
__class_kind="\${__part[__index+1]}"
|
|
1184
|
+
elif [[ -n "\$__class_kind" && "\$__char" == "\$__class_kind" && "\${__part[__index+1]}" == ']' ]]; then
|
|
1185
|
+
__escaped+="\$__char]"
|
|
1186
|
+
(( __index++ ))
|
|
1187
|
+
__class_kind=""
|
|
1188
|
+
continue
|
|
1189
|
+
elif [[ -z "\$__class_kind" && "\$__char" == ']' ]] && (( __index > __class_start )); then
|
|
1190
|
+
__in_class=0
|
|
1191
|
+
elif (( __index == __class_start )) && [[ "\$__char" == '!' || "\$__char" == '^' ]]; then
|
|
1192
|
+
(( __class_start++ ))
|
|
1193
|
+
fi
|
|
1194
|
+
elif [[ "\$__char" == '[' ]]; then
|
|
1195
|
+
__in_class=1
|
|
1196
|
+
__class_start=$(( __index + 1 ))
|
|
1197
|
+
elif [[ "\$__char" == '$' ]]; then
|
|
1198
|
+
__char="\$__dollar"
|
|
1199
|
+
elif [[ "\$__char" == '\`' ]]; then
|
|
1200
|
+
__char="\$__backtick"
|
|
1201
|
+
fi
|
|
1202
|
+
__escaped+="\$__char"
|
|
1203
|
+
done
|
|
1204
|
+
__backend_parts+=("\$__escaped")
|
|
1205
|
+
done
|
|
1206
|
+
__backend_prefix="\${__backend_parts[1]}"
|
|
1207
|
+
__backend_suffix="\${__backend_parts[2]}"
|
|
1208
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1209
|
+
fi
|
|
1210
|
+
else
|
|
1211
|
+
# Quote retained metacharacters, including segments made only
|
|
1212
|
+
# of them. Wildcard-only overlap remains backend syntax.
|
|
1213
|
+
if [[ "\$__input_part" == *[\\\\\\(\\)\\[\\#\\^\\|\\~\\<\\>\\*\\?]* &&
|
|
1214
|
+
"\$__input_part" != [\\*\\?]## ]]; then
|
|
1215
|
+
__native_pattern="\$__backend_prefix\$__quoted_input\$__backend_suffix"
|
|
1216
|
+
fi
|
|
1217
|
+
fi
|
|
1218
|
+
PREFIX="\$__native_pattern"
|
|
1219
|
+
# Match simple fixed tilde globs through native -g filters. The
|
|
1220
|
+
# pattern matcher otherwise treats the leading ~ as glob syntax,
|
|
1221
|
+
# even after _path_files has expanded it for filesystem lookup.
|
|
1222
|
+
if (( __fixed_tilde_end )) && [[ -z "\$__input_part" &&
|
|
1223
|
+
"\$__native_pattern" == "\$pattern" && "\$pattern" == */* ]]; then
|
|
1224
|
+
__initial_directory="\${pattern%/*}/"
|
|
1225
|
+
__initial_leaf="\${pattern##*/}"
|
|
1226
|
+
if [[ "\$__initial_leaf" == *[\*\?]* &&
|
|
1227
|
+
"\$__initial_directory" != *[\*\?]* &&
|
|
1228
|
+
"\$__initial_directory" != *["[]()"]* &&
|
|
1229
|
+
"\$__initial_leaf" != *["[]()#~^|<>;&"]* &&
|
|
1230
|
+
"\$__initial_leaf" != *'\\'* &&
|
|
1231
|
+
"\$__initial_leaf" != *[[:space:]]* &&
|
|
1232
|
+
"\$__initial_leaf" != *"'"* && "\$__initial_leaf" != *'"'* ]]; then
|
|
1233
|
+
__initial_file_pattern="\$__initial_leaf"
|
|
1234
|
+
PREFIX="\$__initial_directory"
|
|
1235
|
+
compstate[pattern_match]=""
|
|
1236
|
+
__pattern_match_changed=1
|
|
1237
|
+
fi
|
|
1238
|
+
fi
|
|
985
1239
|
fi
|
|
986
1240
|
fi
|
|
987
1241
|
|
|
@@ -1021,6 +1275,21 @@ function ${functionName} () {
|
|
|
1021
1275
|
fi
|
|
1022
1276
|
file_pattern="\${(j: :)__initial_file_patterns}"
|
|
1023
1277
|
fi
|
|
1278
|
+
local file_pattern_prefix="\$PREFIX"
|
|
1279
|
+
local -a match mbegin mend
|
|
1280
|
+
# Native qualifier copying concatenates flags with the explicit
|
|
1281
|
+
# regular-file group. Keep backend attributes in their own group.
|
|
1282
|
+
if [[ -n "\$compstate[pattern_match]" && -z "\$SUFFIX" ]]; then
|
|
1283
|
+
local file_qualifier file_qualifiers=""
|
|
1284
|
+
while _have_glob_qual "\$file_pattern_prefix" complete; do
|
|
1285
|
+
file_qualifier="\${match[5]#\\#q}"
|
|
1286
|
+
file_pattern_prefix="\$match[2]"
|
|
1287
|
+
file_qualifiers="(#q\$file_qualifier)\$file_qualifiers"
|
|
1288
|
+
done
|
|
1289
|
+
if [[ -n "\$file_qualifiers" ]]; then
|
|
1290
|
+
file_pattern+="\$file_qualifiers"
|
|
1291
|
+
fi
|
|
1292
|
+
fi
|
|
1024
1293
|
local file_subtree_pattern="**/*(#q/N)"
|
|
1025
1294
|
local -a file_subtree=() file_recursive_patterns=()
|
|
1026
1295
|
local -a file_tags=(files directories)
|
|
@@ -1034,10 +1303,17 @@ function ${functionName} () {
|
|
|
1034
1303
|
local file_matched=0
|
|
1035
1304
|
for file_tag in "\${file_tags[@]}"; do
|
|
1036
1305
|
if [[ "\$file_tag" == directories ]]; then
|
|
1306
|
+
local file_tag_prefix="\$PREFIX"
|
|
1307
|
+
PREFIX="\$file_pattern_prefix"
|
|
1037
1308
|
while _next_label directories expl directory; do
|
|
1309
|
+
# File qualifiers constrain final results, not the
|
|
1310
|
+
# directories needed to navigate to those results.
|
|
1038
1311
|
_path_files -/ "\${expl[@]}" && file_matched=1
|
|
1039
1312
|
done
|
|
1313
|
+
PREFIX="\$file_tag_prefix"
|
|
1040
1314
|
else
|
|
1315
|
+
local file_tag_prefix="\$PREFIX"
|
|
1316
|
+
PREFIX="\$file_pattern_prefix"
|
|
1041
1317
|
while _next_label files expl file; do
|
|
1042
1318
|
local -a file_ignore=() file_ignore_args=()
|
|
1043
1319
|
# Explicit globs bypass automatic fignore. Apply it only
|
|
@@ -1068,6 +1344,7 @@ function ${functionName} () {
|
|
|
1068
1344
|
done
|
|
1069
1345
|
fi
|
|
1070
1346
|
done
|
|
1347
|
+
PREFIX="\$file_tag_prefix"
|
|
1071
1348
|
fi
|
|
1072
1349
|
done
|
|
1073
1350
|
(( file_matched )) && break
|
|
@@ -1096,6 +1373,9 @@ function ${functionName} () {
|
|
|
1096
1373
|
# Restore PREFIX and glob_dots to their previous state
|
|
1097
1374
|
PREFIX="\$__saved_prefix"
|
|
1098
1375
|
if [[ "\$__was_extended_glob" == "1" ]]; then setopt extended_glob; else unsetopt extended_glob; fi
|
|
1376
|
+
if (( __pattern_match_changed )); then
|
|
1377
|
+
compstate[pattern_match]="\$__saved_pattern_match"
|
|
1378
|
+
fi
|
|
1099
1379
|
if [[ "\$__was_sh_glob" == "1" ]]; then setopt sh_glob; else unsetopt sh_glob; fi
|
|
1100
1380
|
if [[ "\$__was_sh_word_split" == "1" ]]; then setopt sh_word_split; else unsetopt sh_word_split; fi
|
|
1101
1381
|
if [[ "\$__was_glob_dots" == "1" ]]; then setopt glob_dots; else unsetopt glob_dots; fi
|
|
@@ -1114,17 +1394,23 @@ function ${functionName} () {
|
|
|
1114
1394
|
# Prepare completion with descriptions for _describe
|
|
1115
1395
|
local -a matches
|
|
1116
1396
|
local -i i
|
|
1397
|
+
local __match
|
|
1117
1398
|
for (( i=1; i <= \${#completions[@]}; i++ )); do
|
|
1118
|
-
# _describe
|
|
1119
|
-
|
|
1120
|
-
|
|
1399
|
+
# _describe uses backslashes to escape its colon separator.
|
|
1400
|
+
__match="\${completions[i]//\\\\/\\\\\\\\}"
|
|
1401
|
+
__match="\${__match//:/\\\\:}"
|
|
1121
1402
|
if [[ -n "\${descriptions[i]}" ]]; then
|
|
1122
|
-
matches+=("\$
|
|
1403
|
+
matches+=("\$__match:\${descriptions[i]}")
|
|
1123
1404
|
else
|
|
1124
|
-
matches+=("\$
|
|
1405
|
+
matches+=("\$__match")
|
|
1125
1406
|
fi
|
|
1126
1407
|
done
|
|
1127
|
-
|
|
1408
|
+
if [[ -n "\$QISUFFIX" ]]; then
|
|
1409
|
+
# A closing quote already exists; do not append a space inside it.
|
|
1410
|
+
_describe 'commands' matches -S ''
|
|
1411
|
+
else
|
|
1412
|
+
_describe 'commands' matches
|
|
1413
|
+
fi
|
|
1128
1414
|
fi
|
|
1129
1415
|
}
|
|
1130
1416
|
|