blob: 935b13c74aa4703bbdd0c2900de7b120189222bf (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
#autoload
# This gets two arguments, a separator (which should be only one
# character) and an array. As usual, the array may be given by it's
# name or literal as in `(foo bar baz)' (words separated by spaces in
# parentheses).
# The parts of words from the array that are separated by the
# separator character are then completed independently.
local sep matches pref npref i tmp1 group expl menu pre suf opre osuf cpre
local opts sopts match
typeset -U tmp2
# Get the options.
group=()
expl=()
opts=()
sopts=()
while getopts "J:V:X:P:S:r:R:qM:12n" opt; do
case "$opt" in
[JV]) group=("-$opt" "$OPTARG");;
X) expl=(-X "$OPTARG");;
P) opts=( "$opts[@]" -P "$OPTARG")
sopts=( "$sopts[@]" -P "$OPTARG");;
[SrR]) sopts=( "$sopts[@]" -P "$OPTARG");;
[q12n]) sopts=( "$sopts[@]" "-$opt");;
M) match="$OPTARG";;
esac
done
shift OPTIND-1
# Get the arguments, first the separator, then the array. The array is
# stored in `matches'. Further on this array will always contain those
# words from the original array that still match everything we have
# tried to match while we walk through the string from the line.
sep="$1"
if [[ "${2[1]}" = '(' ]]; then
matches=( ${2[2,-2]} )
else
matches=( "${(@P)2}" )
fi
# In `pre' and `suf' we will hold the prefix and the suffix from the
# line while we walk through them. The original string are used
# temporarily for matching.
pre="$PREFIX"
suf="$SUFFIX"
opre="$PREFIX"
osuf="$SUFFIX"
orig="$PREFIX$SUFFIX"
# Special handling for menucompletion?
[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
( $#compstate[pattern_match] -ne 0 &&
"$orig" != "${orig:q}" ) ]] && menu=yes
# In `pref' we collect the unambiguous prefix path.
pref=''
# If the string from the line matches at least one of the strings,
# we use only the matching strings.
compadd -O tmp1 -M "r:|${sep}=* r:|=* $match" - "$matches[@]"
(( $#tmp1 )) && matches=( "$tmp1[@]" )
while true; do
# Get the prefix and suffix for matching.
if [[ "$pre" = *${sep}* ]]; then
PREFIX="${pre%%${sep}*}"
SUFFIX=""
else
PREFIX="${pre}"
SUFFIX="${suf%%${sep}*}"
fi
# Check if the component for some of the possible matches is equal
# to the string from the line. If there are such strings, we directly
# use the stuff from the line. This avoids having `foo' complete to
# both `foo' and `foobar'.
tmp1=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" )
if (( $#tmp1 )); then
npref="${PREFIX}${SUFFIX}${sep}"
else
# No exact match, see how many strings match what's on the line.
builtin compadd -O tmp1 - "${(@)matches%%${sep}*}"
[[ $#tmp1 -eq 0 && -n "$_comp_correct" ]] &&
compadd -O tmp1 - "${(@)matches%%${sep}*}"
tmp2=( "$tmp1[@]" )
tmp1=( "$tmp2[@]" )
if [[ $#tmp1 -eq 1 ]]; then
# Only one match. If there are still separators from the line
# we just accept this component. Otherwise we insert what we
# have collected, probably giving it a separator character
# as a suffix.
if [[ "$pre$suf" = *${sep}* ]]; then
npref="${tmp1[1]}${sep}"
else
matches=( "${(@M)matches:#${tmp1[1]}*}" )
tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" )
PREFIX="${cpre}${pre}"
SUFFIX="$suf"
if (( $#tmp2 )); then
compadd "$group[@]" "$expl[@]" -p "$pref" -qS "$sep" "$opts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$tmp1[1]"
else
compadd "$group[@]" "$expl[@]" -p "$pref" "$sopts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$tmp1[1]"
fi
return 0
fi
elif (( $#tmp1 )); then
# More than one match. First we get all strings that match the
# rest from the line.
PREFIX="$pre"
SUFFIX="$suf"
compadd -O matches -M "r:|${sep}=* r:|=* $match" - "$matches[@]"
if [[ "$pre" = *${sep}* ]]; then
PREFIX="${cpre}${pre%%${sep}*}"
SUFFIX="${sep}${pre#*${sep}}${suf}"
else
PREFIX="${cpre}${pre}"
SUFFIX="$suf"
fi
if [[ -n "$menu" ]]; then
# With menucompletion we just add matches for the matching
# components with the prefix we collected and the rest from the
# line as a suffix.
tmp2="$pre$suf"
if [[ "$tmp2" = *${sep}* ]]; then
compadd "$group[@]" "$expl[@]" "$sopts[@]" \
-p "$pref" -s "${sep}${tmp2#*${sep}}" \
-M "r:|${sep}=* r:|=* $match" - "$tmp1[@]"
else
compadd "$group[@]" "$expl[@]" -p "$pref" "$sopts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$tmp1[@]"
fi
else
# With normal completion we add all matches one-by-one with
# the unmatched part as a suffix. This will insert the longest
# unambiguous string for all matching strings.
for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do
if [[ "$i" = *${sep}* ]]; then
compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
-p "$pref" -s "${i#*${sep}}" \
-M "r:|${sep}=* r:|=* $match" - "${i%%${sep}*}${sep}"
else
compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \
-M "r:|${sep}=* r:|=* $match" - "$i"
fi
done
fi
return 0
else
# We are here if no string matched what's on the line. In this
# case we insert the expanded prefix we collected if it differs
# from the original string from the line.
[[ "$orig" = "$pref$pre$suf" ]] && return 1
PREFIX="${cpre}${pre}"
SUFFIX="$suf"
if [[ -n "$suf" ]]; then
compadd "$group[@]" "$expl[@]" -s "$suf" "$sopts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$pref$pre"
else
compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$pref$pre"
fi
return 0
fi
fi
# We just accepted and/or expanded a component from the line. We
# remove it from the matches (using only those that have a least
# the skipped string) and ad it the `pref'.
matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" )
pref="$pref$npref"
# Now we set `pre' and `suf' to their new values.
if [[ "$pre" = *${sep}* ]]; then
cpre="${cpre}${pre%%${sep}*}${sep}"
pre="${pre#*${sep}}"
elif [[ "$suf" = *${sep}* ]]; then
cpre="${cpre}${pre}${suf%%${sep}*}${sep}"
pre="${suf#*${sep}}"
suf=""
else
# The string from the line is fully handled. If we collected an
# unambiguous prefix and that differs from the original string,
# we insert it.
PREFIX="${opre}${osuf}"
SUFFIX=""
[[ -n "$pref" && "$orig" != "$pref" ]] &&
compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
-M "r:|${sep}=* r:|=* $match" - "$pref"
return
fi
done
|