From f9e65c2b1f448cd27c597ec727b63bebde2fbdff Mon Sep 17 00:00:00 2001 From: Sven Wischnowsky Date: Fri, 19 May 2000 08:24:16 +0000 Subject: don't use $match for something different than (#b)ackrefs (11464) --- Completion/Core/_approximate | 2 +- Completion/Core/_files | 2 +- Completion/Core/_multi_parts | 350 +++++++++++++++++++++++++------------------ Completion/Core/_sep_parts | 112 +++++++------- 4 files changed, 262 insertions(+), 204 deletions(-) (limited to 'Completion/Core') diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate index 47fc9ce23..9d294e8c4 100644 --- a/Completion/Core/_approximate +++ b/Completion/Core/_approximate @@ -10,7 +10,7 @@ [[ _matcher_num -gt 1 || "${#:-$PREFIX$SUFFIX}" -le 1 ]] && return 1 -local _comp_correct _correct_expl comax cfgacc redef +local _comp_correct _correct_expl comax cfgacc redef match local oldcontext="${curcontext}" opm="$compstate[pattern_match]" if [[ "$1" = -a* ]]; then diff --git a/Completion/Core/_files b/Completion/Core/_files index b1d06e23a..391d570a5 100644 --- a/Completion/Core/_files +++ b/Completion/Core/_files @@ -1,6 +1,6 @@ #autoload -local opts tmp glob pat pats expl tag i def descr end ign ret=1 +local opts tmp glob pat pats expl tag i def descr end ign ret=1 match zparseopts -a opts \ '/=tmp' 'f=tmp' 'g+:-=tmp' q n 1 2 P: S: r: R: W: X+: M+: F: J+: V+: diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts index 1f51d2f6d..bdfa26fc3 100644 --- a/Completion/Core/_multi_parts +++ b/Completion/Core/_multi_parts @@ -7,27 +7,22 @@ # 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] +local sep matches pref npref i tmp1 group expl menu pre suf opre osuf cpre +local opts sopts matcher imm +typeset -U tmp2 # 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 +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 `matches'. Further on this array will always contain those @@ -36,166 +31,237 @@ shift OPTIND-1 sep="$1" if [[ "${2[1]}" = '(' ]]; then - matches=( ${2[2,-2]} ) + 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 `*'. +# 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. -if [[ -o globcomplete ]]; then - patstr="${PREFIX}*${SUFFIX}*" -else - patstr="${PREFIX:q}*${SUFFIX:q}*" -fi -orig="${PREFIX}${SUFFIX}" +pre="$PREFIX" +suf="$SUFFIX" +opre="$PREFIX" +osuf="$SUFFIX" +orig="$PREFIX$SUFFIX" -matchflags="" -_match_pattern _path_files patstr matchflags -[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" +# Special handling for menucompletion? -patstr="${${patstr//$sep/*$sep}//\*##/*}" -#eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/" +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || + ( $#compstate[pattern_match] -ne 0 && + "$orig" != "${orig:q}" ) ]] && menu=yes -# 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. +# In `pref' we collect the unambiguous prefix path. 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'. +# If the string from the line matches at least one of the strings, +# we use only the matching strings. - pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}${patstr##*${sep}}" - tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" ) +compadd -O tmp1 -M "r:|${sep}=* r:|=* $matcher" - "$matches[@]" - # If there are no words matching the exact substring, stop. +(( $#tmp1 )) && matches=( "$tmp1[@]" ) - (( $#tmp1 )) || break +while true; do - # 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 + # Get the prefix and suffix for matching. -# Now we get all the words that still match in `tmp1'. + if [[ "$pre" = *${sep}* ]]; then + PREFIX="${pre%%${sep}*}" + SUFFIX="" + else + PREFIX="${pre}" + SUFFIX="${suf%%${sep}*}" + fi -if [[ "$patstr" = *${sep}* ]]; then - tmp1="${patstr%${sep}*}${sep}" - pat="${tmp1//\*/[^${sep}]#}${patstr##*${sep}}" -else - pat="$patstr" -fi -tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" ) + # 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 (( $#tmp1 )); then + if [[ -n "$PREFIX$SUFFIX" || "$pre" = ${sep}* ]]; then + tmp1=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" ) + else + tmp1=() + fi - # There are words that are matched, put them int `matches' and then - # move all unambiguous components from the beginning into `pref'. + if (( $#tmp1 )); then + npref="${PREFIX}${SUFFIX}${sep}" + else + # No exact match, see how many strings match what's on the line. - matches=( "$tmp1[@]" ) - while [[ "$matches[1]" = *${sep}* ]]; do + builtin compadd -O tmp1 - "${(@)matches%%${sep}*}" - # 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 -eq 0 && -n "$_comp_correct" ]] && + compadd -O tmp1 - "${(@)matches%%${sep}*}" - tmp1="${matches[1]%%${sep}*}${sep}" - tmp2=( "${(@)matches:#${tmp1}*}" ) - (( $#tmp2 )) && break + tmp2=( "$tmp1[@]" ) - # All matches have the same prefix, but it into `pref' and remove - # it from the matches. + if [[ $#tmp1 -eq 1 ]]; then - pref="$pref$tmp1" - matches=( "${(@)${(@)matches#$tmp1}:#}" ) + # 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 [[ "$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}*}" + if [[ "$pre$suf" = *${sep}* ]]; then + npref="${tmp1[1]}${sep}" else - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i" + 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[@]" "$opts[@]" \ + -M "r:|${sep}=* r:|=* $matcher" - "$pref$matches[1]" + else + tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" ) + + if (( $#tmp2 )); 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 - done - fi -elif [[ "$patstr" = *${sep}* ]]; then + elif (( $#tmp1 )); then + local ret=1 - # 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. + # More than one match. First we get all strings that match the + # rest from the line. - while [[ "$patstr" = *${sep}* ]]; do + PREFIX="$pre" + SUFFIX="$suf" + compadd -O matches -M "r:|${sep}=* r:|=* $matcher" - "$matches[@]" - # 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 + if [[ "$pre" = *${sep}* ]]; then + PREFIX="${cpre}${pre%%${sep}*}" + SUFFIX="${sep}${pre#*${sep}}${suf}" + else + PREFIX="${cpre}${pre}" + SUFFIX="$suf" + fi - # Then we check if there are words that have a different prefix. + 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 + for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do + case "$i" in + *${sep}) + compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \ + -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0 + ;; + ${sep}*) + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \ + -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "$sep" && ret=0 + ;; + *${sep}*) + compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \ + -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0 + ;; + *) + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "$i" && ret=0 + ;; + esac + done + 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[@]" "$opts[@]" \ + -p "$pref" -s "${i#*${sep}}" \ + -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0 + else + compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \ + -M "r:|${sep}=* r:|=* $matcher" - "$i" && ret=0 + fi + done + 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. - tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" ) - if (( $#tmp2 )); then + { ! zstyle -t ":completion:${curcontext}:" expand prefix || + [[ "$orig" = "$pref$pre$suf" ]] } && return 1 - # 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. + PREFIX="${cpre}${pre}" + SUFFIX="$suf" - compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" \ - -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}" - return 0 + 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 - # 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 + # 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'. - # Finally, add the unambiguous prefix and the rest of the string - # from the line. + matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" ) + pref="$pref$npref" - compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig" -fi + # Now we set `pre' and `suf' to their new values. -# This sets the return value to indicate that we added matches (or not). - -[[ nm -ne compstate[nmatches] ]] + 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} ]]; 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 diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts index c1cda2b9a..0f9ef0fc4 100644 --- a/Completion/Core/_sep_parts +++ b/Completion/Core/_sep_parts @@ -9,40 +9,34 @@ # # _sep_parts '(foo bar)' @ hosts # -# This will make this function complete the strings in the array -# `friends'. If the string on the line contains a `@', the substring -# after it will be completed from the array `hosts'. Of course more -# arrays may be given, each preceded by another separator string. +# This will make this function complete the strings `foo' and `bar'. +# If the string on the line contains a `@', the substring after it +# will be completed from the array `hosts'. Of course more arrays +# may be given, each preceded by another separator string. # # This function understands the `-J group', `-V group', and # `-X explanation' options. -# -# This function does part of the matching itself and calls the functions -# `_match_test' and `_match_pattern' for this. local str arr sep test testarr tmparr prefix suffixes matchers autosuffix -local matchflags opt group expl nm=$compstate[nmatches] - -# Test if we should use this function for the global matcher in use. - -_match_test _sep_parts || return 1 +local matchflags opt group expl nm=$compstate[nmatches] opre osuf opts matcher # 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 +zparseopts -D -a opts \ + 'J+:=group' 'V+:=group' P: F: S: r: R: q 1 2 n 'X+:=expl' 'M+:=matcher' + +if (( $#matcher )); then + matcher="${matcher[2]}" +else + matcher='' +fi # Get the string from the line. +opre="$PREFIX" +osuf="$SUFFIX" str="$PREFIX$SUFFIX" -[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q" +SUFFIX="" prefix="" # Walk through the arguments to find the longest unambiguous prefix. @@ -56,59 +50,53 @@ while [[ $# -gt 1 ]]; do tmparr=( ${=arr[2,-2]} ) arr=tmparr fi - # Is the separator on the line? - [[ "$str" != *${sep}* ]] && break - # Build a pattern matching the possible matches and get all these - # matches in an array. + # Is the separator on the line? - test="${str%%${sep}*}" - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 + [[ "$str" != *${sep}* ]] && break - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + # Get the matching array elements. - test="${matchflags}${test}" - testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) - testarr=( "${(@)testarr:#}" ) + PREFIX="${str%%(|\\)${sep}*}" + builtin compadd -O testarr - "${(@P)arr}" + [[ $#testarr -eq 0 && -n "$_comp_correct" ]] && + compadd -O testarr - "${(@P)arr}" # If there are no matches we give up. If there is more than one # match, this is the part we will complete. + (( $#testarr )) || return 1 [[ $#testarr -gt 1 ]] && break # Only one match, add it to the prefix and skip over it in `str', # continuing with the next array and separator. + prefix="${prefix}${testarr[1]}${sep}" str="${str#*${sep}}" shift 2 done # Get the array to work upon. + arr="$1" if [[ "$arr[1]" == '(' ]]; then tmparr=( ${=arr[2,-2]} ) arr=tmparr fi + if [[ $# -le 1 || "$str" != *${2}* ]]; then # No more separators, build the matches. - test="$str" - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 - - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - - test="${matchflags}${test}" - testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) - testarr=( "${(@)testarr:#}" ) + PREFIX="$str" + builtin compadd -O testarr - "${(@P)arr}" + [[ $#testarr -eq 0 && -n "$_comp_correct" ]] && + compadd -O testarr - "${(@P)arr}" fi [[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1 # Now we build the suffixes to give to the completion code. + shift matchers=() suffixes=("") @@ -116,23 +104,18 @@ autosuffix=() while [[ $# -gt 0 && "$str" == *${1}* ]]; do # Remove anything up to the the suffix. + str="${str#*${1}}" # Again, we get the string from the line up to the next separator # and build a pattern from it. + if [[ $# -gt 2 ]]; then - test="${str%%${3}*}" + PREFIX="${str%%${3}*}" else - test="$str" + PREFIX="$str" fi - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 - - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - test="${matchflags}${test}" - # We incrementally add suffixes by appending to them the seperators # and the strings from the next array that match the pattern we built. @@ -141,29 +124,38 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do tmparr=( ${=arr[2,-2]} ) arr=tmparr fi - tmparr=( "${(@M)${(@P)arr}:#${~test}*}" ) - tmparr=( "${(@)tmparr:#}" ) - suffixes=("${(@)^suffixes[@]}${1}${(@)^tmparr}") + + builtin compadd -O tmparr - "${(@P)arr}" + [[ $#tmparr -eq 0 && -n "$_comp_correct" ]] && + compadd -O tmparr - "${(@P)arr}" + + suffixes=("${(@)^suffixes[@]}${(q)1}${(@)^tmparr}") # We want the completion code to generate the most specific suffix # for us, so we collect matching specifications that allow partial # word matching before the separators on the fly. + matchers=("$matchers[@]" "r:|${1:q}=*") shift 2 done # If we were given at least one more separator we make the completion # code offer it by appending it as a autoremovable suffix. -(( $# )) && autosuffix=(-qS "$1") + +(( $# )) && autosuffix=(-qS "${(q)1}") # If we have collected matching specifications, we build an array # from it that can be used as arguments to `compadd'. -[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") + +[[ $#matchers+$#matcher -gt 0 ]] && matchers=(-M "$matchers $matcher") # Add the matches for each of the suffixes. + +PREFIX="$pre" +SUFFIX="$suf" for i in "$suffixes[@]"; do - compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \ - -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]" + compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" "$opts[@]" \ + -i "$IPREFIX" -I "$ISUFFIX" -p "$prefix" -s "$i" - "$testarr[@]" done # This sets the return value to indicate that we added matches (or not). -- cgit 1.4.1