mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-20 05:53:52 +01:00
202 lines
6.1 KiB
Plaintext
202 lines
6.1 KiB
Plaintext
|
#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 patstr orig matchflags pref i tmp1 tmp2 nm
|
||
|
local group expl
|
||
|
|
||
|
_match_test _multi_parts || return 1
|
||
|
|
||
|
# Save the current number of matches to be able to return if we added
|
||
|
# matches or not.
|
||
|
|
||
|
nm=$compstate[nmatches]
|
||
|
|
||
|
# Get the options.
|
||
|
|
||
|
group=()
|
||
|
expl=()
|
||
|
while getopts "J:V:X:" opt; do
|
||
|
case "$opt" in
|
||
|
[JV]) group=("-$opt" "$OPTARG");;
|
||
|
X) expl=(-X "$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
|
||
|
|
||
|
# Now build the pattern from what we have on the line. We also save
|
||
|
# the original string in `orig'. The `eval' is used to replace our
|
||
|
# separator character by `*<sep>'.
|
||
|
|
||
|
if [[ -o globcomplete ]]; then
|
||
|
patstr="${PREFIX}*${SUFFIX}*"
|
||
|
else
|
||
|
patstr="${PREFIX:q}*${SUFFIX:q}*"
|
||
|
fi
|
||
|
orig="${PREFIX}${SUFFIX}"
|
||
|
|
||
|
matchflags=""
|
||
|
_match_pattern _path_files patstr matchflags
|
||
|
[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
|
||
|
|
||
|
patstr="${${patstr//$sep/*$sep}//\*##/*}"
|
||
|
#eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/"
|
||
|
|
||
|
# First we will skip over those parts of the matches for which we have
|
||
|
# exact substrings on the line. In `pref' we will build the
|
||
|
# unambiguous prefix string.
|
||
|
|
||
|
pref=''
|
||
|
while [[ "$orig" = *${sep}* ]] do
|
||
|
|
||
|
# First build the pattern to use, then collect all strings from
|
||
|
# `matches' that match the prefix we have and the exact substring in
|
||
|
# the array `tmp1'.
|
||
|
|
||
|
pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}${patstr##*${sep}}"
|
||
|
tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" )
|
||
|
|
||
|
# If there are no words matching the exact substring, stop.
|
||
|
|
||
|
(( $#tmp1 )) || break
|
||
|
|
||
|
# Otherwise add the part to the prefix, remove it from the matches
|
||
|
# (which will also remove all words not matching the string at all),
|
||
|
# and set `patstr' and `orig' to the next component.
|
||
|
|
||
|
pref="$pref${orig%%${sep}*}${sep}"
|
||
|
matches=( "${(@)${(@)matches#${orig%%${sep}*}${sep}}:#}" )
|
||
|
orig="${orig#*${sep}}"
|
||
|
patstr="${patstr#*${sep}}"
|
||
|
done
|
||
|
|
||
|
# Now we get all the words that still match in `tmp1'.
|
||
|
|
||
|
if [[ "$patstr" = *${sep}* ]]; then
|
||
|
tmp1="${patstr%${sep}*}${sep}"
|
||
|
pat="${tmp1//\*/[^${sep}]#}${patstr##*${sep}}"
|
||
|
else
|
||
|
pat="$patstr"
|
||
|
fi
|
||
|
tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" )
|
||
|
|
||
|
if (( $#tmp1 )); then
|
||
|
|
||
|
# There are words that are matched, put them int `matches' and then
|
||
|
# move all unambiguous components from the beginning into `pref'.
|
||
|
|
||
|
matches=( "$tmp1[@]" )
|
||
|
while [[ "$matches[1]" = *${sep}* ]]; do
|
||
|
|
||
|
# We just take the first component of the first match and see if
|
||
|
# there are other matches with a different prefix (these are
|
||
|
# collected in `tmp2'). If there are any, we give up.
|
||
|
|
||
|
tmp1="${matches[1]%%${sep}*}${sep}"
|
||
|
tmp2=( "${(@)matches:#${tmp1}*}" )
|
||
|
(( $#tmp2 )) && break
|
||
|
|
||
|
# All matches have the same prefix, but it into `pref' and remove
|
||
|
# it from the matches.
|
||
|
|
||
|
pref="$pref$tmp1"
|
||
|
matches=( "${(@)${(@)matches#$tmp1}:#}" )
|
||
|
|
||
|
if [[ "$orig" = *${sep}* ]]; then
|
||
|
orig="${orig#*${sep}}"
|
||
|
else
|
||
|
orig=''
|
||
|
fi
|
||
|
done
|
||
|
|
||
|
# Now we can tell the completion code about the things we
|
||
|
# found. Strings that have a separator will be added with a suffix.
|
||
|
|
||
|
if [[ -z "$orig" && "$PREFIX$SUFFIX" != "$pref$orig" ]]; then
|
||
|
compadd -QU "$group[@]" "$expl[@]" -i "$IPREFIX" -S '' - "${pref}${orig}"
|
||
|
elif [[ $compstate[insert] = *menu ]]; then
|
||
|
for i in "$matches[@]" ; do
|
||
|
if [[ "$i" = *${sep}* ]]; then
|
||
|
compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
|
||
|
-p "$pref" -qS "$sep" - "${i%%${sep}*}"
|
||
|
else
|
||
|
compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
|
||
|
-p "$pref" - "${i%%${sep}*}"
|
||
|
fi
|
||
|
done
|
||
|
else
|
||
|
for i in "$matches[@]" ; do
|
||
|
if [[ "$i" = *${sep}* ]]; then
|
||
|
compadd -U -i "$IPREFIX" -p "$pref" -s "${sep}${i#*${sep}}" \
|
||
|
"$group[@]" "$expl[@]" -M "r:|${sep}=*" - "${i%%${sep}*}"
|
||
|
else
|
||
|
compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i"
|
||
|
fi
|
||
|
done
|
||
|
fi
|
||
|
elif [[ "$patstr" = *${sep}* ]]; then
|
||
|
|
||
|
# We had no words matching the string from the line. But we want to
|
||
|
# be friendly and at least expand the prefix as far as we can. So we
|
||
|
# will loop through the rest of the string from the line and test
|
||
|
# the components one by one.
|
||
|
|
||
|
while [[ "$patstr" = *${sep}* ]]; do
|
||
|
|
||
|
# First we get all words matching at least this component in
|
||
|
# `tmp1'. If there are none, we give up.
|
||
|
|
||
|
tmp1=( "${(@M)matches:#${~matchflags}${~patstr%%${sep}*}${sep}*}" )
|
||
|
(( $#tmp1 )) || break
|
||
|
|
||
|
# Then we check if there are words that have a different prefix.
|
||
|
|
||
|
tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" )
|
||
|
if (( $#tmp2 )); then
|
||
|
|
||
|
# There are words with another prefix, so we have found an
|
||
|
# ambiguous component. So we just give all possible prefixes to
|
||
|
# the completion code together with our prefix and the rest of
|
||
|
# the string from the line as the suffix.
|
||
|
|
||
|
compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" \
|
||
|
-s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
|
||
|
return 0
|
||
|
fi
|
||
|
|
||
|
# All words have the same prefix, so add it to `pref' again and
|
||
|
# try the next component.
|
||
|
|
||
|
pref="$pref${tmp1[1]%%${sep}*}${sep}"
|
||
|
matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" )
|
||
|
orig="${orig#*${sep}}"
|
||
|
patstr="${patstr#*${sep}}"
|
||
|
done
|
||
|
|
||
|
# Finally, add the unambiguous prefix and the rest of the string
|
||
|
# from the line.
|
||
|
|
||
|
compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig"
|
||
|
fi
|
||
|
|
||
|
# This sets the return value to indicate that we added matches (or not).
|
||
|
|
||
|
[[ nm -ne compstate[nmatches] ]]
|