diff options
Diffstat (limited to 'Completion/Base')
-rw-r--r-- | Completion/Base/Utility/_multi_parts | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/Completion/Base/Utility/_multi_parts b/Completion/Base/Utility/_multi_parts new file mode 100644 index 000000000..6f6ef12f2 --- /dev/null +++ b/Completion/Base/Utility/_multi_parts @@ -0,0 +1,254 @@ +#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 pref npref i tmp2 group expl menu pre suf opre osuf cpre +local opts sopts matcher imm +typeset -U tmp1 matches + +# Get the options. + +zparseopts -D -a sopts \ + 'J+:=group' 'V+:=group' 'X+:=expl' 'P:=opts' 'F:=opts' \ + S: r: R: q 1 2 n f 'M+:=matcher' 'i=imm' + +sopts=( "$sopts[@]" "$opts[@]" ) +if (( $#matcher )); then + matcher="${matcher[2]}" +else + matcher= +fi + +# Get the arguments, first the separator, then the array. The array is +# stored in `tmp1'. Further on the array `matches' 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 + tmp1=( ${=2[2,-2]} ) +else + tmp1=( "${(@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 matches -M "r:|${sep}=* r:|=* $matcher" -a tmp1 + +(( $#matches )) || 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'. + + if [[ -n "$PREFIX$SUFFIX" || "$pre" = ${sep}* ]]; then + tmp1=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" ) + else + tmp1=() + fi + + 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}*}:#}" + + 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]}*}" ) + + PREFIX="${cpre}${pre}" + SUFFIX="$suf" + + if [[ $#imm -ne 0 && $#matches -eq 1 ]] || + zstyle -t ":completion:${curcontext}:" expand suffix; then + compadd "$group[@]" "$expl[@]" "$sopts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$pref$matches[1]" + else + if (( $matches[(I)${tmp1[1]}${sep}*] )); then + compadd "$group[@]" "$expl[@]" -p "$pref" -r "$sep" -S "$sep" "$opts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$tmp1[1]" + else + compadd "$group[@]" "$expl[@]" -p "$pref" "$sopts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$tmp1[1]" + fi + fi + return + fi + elif (( $#tmp1 )); then + local ret=1 + + # 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:|=* $matcher" -a matches + + if [[ "$pre" = *${sep}* ]]; then + PREFIX="${cpre}${pre%%${sep}*}" + SUFFIX="${sep}${pre#*${sep}}${suf}" + else + PREFIX="${cpre}${pre}" + SUFFIX="$suf" + fi + + matches=( "${(@M)matches:#(${(j:|:)~tmp1})*}" ) + + if ! zstyle -t ":completion:${curcontext}:" expand suffix || + [[ -n "$menu" || -z "$compstate[insert]" ]]; then + + # With menucompletion we add only the ambiguous component with + # the prefix collected and a spearator for the matches that + # have more components. + + tmp2="$pre$suf" + if [[ "$tmp2" = *${sep}* ]]; then + tmp2=(-s "${sep}${tmp2#*${sep}}") + else + tmp2=() + fi + + + compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \ + -p "$pref" "$tmp2[@]" -M "r:|${sep}=* r:|=* $matcher" - \ + "${(@)${(@)${(@M)matches:#*${sep}}%%${sep}*}:#}" && ret=0 + (( $matches[(I)${sep}*] )) && + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \ + -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "$sep" && ret=0 + compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \ + -p "$pref" "$tmp2[@]" -M "r:|${sep}=* r:|=* $matcher" - \ + "${(@)${(@)${(@M)matches:#*?${sep}?*}%%${sep}*}:#}" && ret=0 + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" "$tmp2[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - \ + "${(@)matches:#*${sep}*}" && ret=0 + 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. + + compadd "$group[@]" "$expl[@]" "$opts[@]" \ + -p "$pref" -s "${i#*${sep}}" \ + -M "r:|${sep}=* r:|=* $matcher" - \ + "${(@)${(@)${(@M)matches:#*${sep}*}%%${sep}*}:#}" && ret=0 + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - \ + "${(@)matches:#*${sep}*}" && ret=0 + fi + return ret + 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. + + { ! zstyle -t ":completion:${curcontext}:" expand prefix || + [[ "$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:|=* $matcher" - "$pref$pre" + else + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$pref$pre" + fi + return + 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="" + + if [[ -n "$pref" && "$orig" != "$pref" ]]; then + if [[ "$pref" = *${sep}*${sep} ]]; then + compadd "$group[@]" "$expl[@]" "$opts[@]" \ + -p "${pref%${sep}*${sep}}${sep}" -S "$sep" \ + -M "r:|${sep}=* r:|=* $matcher" - "${${pref%${sep}}##*${sep}}" + + elif [[ "$pref" = *${sep}* ]]; then + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \ + -p "${pref%${sep}*}${sep}" \ + -M "r:|${sep}=* r:|=* $matcher" - "${pref##*${sep}}" + else + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$pref" + fi + fi + return + fi +done |