From 8ceb54fbc2f879e0e80f58c18761bd54db07e5f7 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:25:40 +0000 Subject: zsh-3.1.5-pws-15 --- Completion/Core/_compalso | 12 +- Completion/Core/_multi_parts | 287 ++++++++++++---------------- Completion/Core/_path_files | 443 +++++++++++++++++-------------------------- Completion/Core/_sep_parts | 78 +++----- Completion/Core/compinit | 72 ++++--- 5 files changed, 374 insertions(+), 518 deletions(-) (limited to 'Completion/Core') diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso index 52fb08f05..61649901e 100644 --- a/Completion/Core/_compalso +++ b/Completion/Core/_compalso @@ -1,15 +1,17 @@ #autoload -# This searches $1 in the array for normal completions and calls the result. +# This searches $* in the array for normal completions and calls the result. # It is used to include completions for another command or special context # into the list generated by the calling function. # For example the function for `-subscript-' could call this as in # `_compalso -math-' to get the completions that would be generated for a # mathematical context. -local tmp ret=1 +local i tmp -tmp="$_comps[$1]" -[[ -z "$tmp" ]] || "$tmp" && ret=0 +for i; do + tmp="$_comps[$1]" + [[ -z "$tmp" ]] || "$tmp" && return 0 +done -return ret +return 1 diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts index ab9438494..b49c41e22 100644 --- a/Completion/Core/_multi_parts +++ b/Completion/Core/_multi_parts @@ -7,15 +7,8 @@ # 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 menu origflags mflags - -_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 +typeset -U tmp2 # Get the options. @@ -41,193 +34,159 @@ else matches=( "${(@P)2}" ) fi -# Now build the pattern from what we have on the line. We also save -# the original string in `orig'. +# 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 [[ $#compstate[pattern_match] -ne 0 ]]; then - if [[ "${compstate[pattern_match]-*}" = \** ]]; then - str="${PREFIX}*${SUFFIX}*" - else - str="${PREFIX}${SUFFIX}" - fi -else - patstr="${PREFIX:q}*${SUFFIX:q}*" -fi -orig="${PREFIX}${SUFFIX}" +pre="$PREFIX" +suf="$SUFFIX" +orig="$PREFIX$SUFFIX" -[[ $compstate[insert] = *menu || -n "$_comp_correct" || +# Special handling for menucompletion? + +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || ( $#compstate[pattern_match] -ne 0 && "$orig" != "${orig:q}" ) ]] && menu=yes -matchflags="" -_match_pattern _path_files patstr matchflags -origflags="$matchflags" -[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - -patstr="${${patstr//$sep/*$sep}//\*##/*}" - -# 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. - if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi - - pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}" - tmp1=( "${(@M)matches:#${~mflags}${orig%%${sep}*}${sep}${~pat}}" ) +compadd -O tmp1 -M "r:|${sep}=* r:|=*" - "$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 - # (and also remove all words not matching the string at all), and - # set `patstr' and `orig' to the next component. + # Get the prefix and suffix for matching. - tmp1="${orig%%${sep}*}${sep}" - pref="$pref$tmp1" - matches=("${(@)${(@)${(@M)matches:#${tmp1}*}#$tmp1}:#}") - 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 -if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" -else - mflags="$matchflags" -fi -tmp1=( "${(@M)matches:#${~mflags}${~pat}}" ) - -if (( $#tmp1 )); then - - # There are words that are matched, put them into `matches' and then - # move all unambiguous components from the beginning into `pref'. + if [[ "$pre" = *${sep}* ]]; then + PREFIX="${pre%%${sep}*}" + SUFFIX="" + else + PREFIX="${pre}" + SUFFIX="${suf%%${sep}*}" + fi - matches=( "$tmp1[@]" ) - while [[ "$matches[1]" = *${sep}* ]]; do + # 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'. - # 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=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" ) - tmp1="${matches[1]%%${sep}*}${sep}" - tmp2=( "${(@)matches:#${tmp1}*}" ) - (( $#tmp2 )) && break + if (( $#tmp1 )); then + npref="${PREFIX}${SUFFIX}${sep}" + else + # No exact match, see how many strings match what's on the line. - # All matches have the same prefix, put it into `pref' and remove - # it from the matches. + tmp2=( "${(@)matches%%${sep}*}" ) + compadd -O tmp1 - "$tmp2[@]" - pref="$pref$tmp1" - matches=( "${(@)${(@)matches#$tmp1}:#}" ) + if [[ $#tmp1 -eq 1 ]]; then - if [[ "$orig" = *${sep}* ]]; then - orig="${orig#*${sep}}" - else - orig='' - fi - done + # 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. - # 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" -I "$ISUFFIX" -S '' - \ - "${pref}${orig}" - elif [[ -n "$menu" ]]; then - if [[ "$orig" = *${sep}* ]]; then - orig="${sep}${orig#*${sep}}" - else - orig='' - fi - for i in "$matches[@]" ; do - if [[ "$i" = *${sep}* ]]; then - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" -s "$orig" - "${i%%${sep}*}${sep}" + if [[ "$pre$suf" = *${sep}* ]]; then + npref="${tmp1[1]}${sep}" else - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" -s "$orig" - "${i%%${sep}*}" + matches=( "${(@M)matches:#${tmp1[1]}*}" ) + tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" ) + + if (( $#tmp2 )); then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" -qS "$sep" - "$tmp1[1]" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" - "$tmp1[1]" + fi + return 1 fi - done - else - for i in "$matches[@]" ; do - if [[ "$i" = *${sep}* ]]; then - compadd -U -i "$IPREFIX" -I "$ISUFFIX" -p "$pref" -s "${i#*${sep}}" \ - "$group[@]" "$expl[@]" -M "r:|${sep:q}=*" - "${i%%${sep}*}${sep}" + 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:|=*" - "$matches[@]" + + 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 -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" -s "${sep}${tmp2#*${sep}}" - "$tmp1[@]" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" - "$tmp1[@]" + fi else - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" - "$i" + # 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 -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' -p "$pref" -s "${i#*${sep}}" - "${i%%${sep}*}${sep}" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' -p "$pref" - "$i" + fi + done 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. - - if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" + return 0 else - mflags="$matchflags" - fi - tmp1=( "${(@M)matches:#${~mflags}${~patstr%%${sep}*}${sep}*}" ) - (( $#tmp1 )) || break - - # Then we check if there are words that have a different prefix. + # 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 + [[ "$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. - - compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}" + if [[ -n "$suf" ]]; then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -s "$suf" - "$pref$pre" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' - "$pref$pre$suf" + fi return 0 fi + fi - # All words have the same prefix, so add it to `pref' again and - # try the next component. + # 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'. - pref="$pref${tmp1[1]%%${sep}*}${sep}" - matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" ) - orig="${orig#*${sep}}" - patstr="${patstr#*${sep}}" - done + matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" ) + pref="$pref$npref" - # Finally, add the unambiguous prefix and the rest of the string - # from the line. + # Now we set `pre' and `suf' to their new values. - compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" - "$orig" -fi + if [[ "$pre" = *${sep}* ]]; then + pre="${pre#*${sep}}" + elif [[ "$suf" = *${sep}* ]]; then + 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. -# This sets the return value to indicate that we added matches (or not). + [[ -n "$pref" && "$orig" != "$pref" ]] && + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' - "$pref" -[[ nm -ne compstate[nmatches] ]] + return + fi +done diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index 54b04a368..21837ade2 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -7,10 +7,9 @@ # the last have the same syntax and meaning as for `compgen' or # `compadd', respectively. The `-F ' option may be used to give # a list of suffixes either by giving the name of an array or -# literally by giving them in a string surrounded by -# parentheses. Files with one of the suffixes thus given are treated -# like files with one of the suffixes in the `fignore' array in normal -# completion. +# literally by giving them in a string surrounded by parentheses. Files +# with one of the suffixes thus given are treated like files with one +# of the suffixes in the `fignore' array in normal completion. # # This function supports one configuration key: # @@ -18,22 +17,13 @@ # If this is set to a non-empty string, the partially typed path # from the line will be expanded as far as possible even if trailing # pathname components can not be completed. -# -# -# This function uses the helper functions `_match_test' and `_match_pattern'. - -# First see if we should generate matches for the global matcher in use. - -_match_test _path_files || return 1 -# Yes, so... +local linepath realpath donepath prepath testpath exppath +local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf +local pats ignore group expl addpfx addsfx remsfx +local nm=$compstate[nmatches] menu -local nm str linepath realpath donepath patstr prepath testpath rest -local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt -local addpfx addsfx expl orig ostr nm=$compstate[nmatches] menu remsfx patlast -local origflags mflags tmp3 tmp4 exppaths - -typeset -U prepaths +typeset -U prepaths exppaths setopt localoptions nullglob rcexpandparam extendedglob unsetopt markdirs globsubst shwordsplit nounset @@ -109,35 +99,27 @@ if [[ "$sopt" = - ]]; then fi fi -# str holds the whole string from the command line with a `*' between -# the prefix and the suffix. Then we see if we will do menucompletion. +# We get the prefix and the suffix from the line and save the whole +# original string. Then we see if we will do menucompletion. -if [[ $#compstate[pattern_match] -ne 0 ]]; then - if [[ "${compstate[pattern_match]-*}" = \** ]]; then - str="${PREFIX}*${SUFFIX}" - else - str="${PREFIX}${SUFFIX}" - fi -else - str="${PREFIX:q}*${SUFFIX:q}" - [[ "$str" = \\\~* ]] && str="$str[2,-1]" -fi +pre="$PREFIX" +suf="$SUFFIX" orig="${PREFIX}${SUFFIX}" -[[ $compstate[insert] = *menu || -n "$_comp_correct" || +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || ( $#compstate[pattern_match] -ne 0 && "${orig#\~}" != "${${orig#\~}:q}" ) ]] && menu=yes - # We will first try normal completion called with `compgen', but only if we -# weren't given a `-F', `-r', or `-R' option. +# weren't given a `-F', `-r', or `-R' option or we are in the string. -if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then +if [[ -z "$suf" && $#ignore -eq 0 && $#remsfx -eq 0 && + -z "$_comp_correct" ]]; then # First build an array containing the `-W' option, if there is any and we # want to use it. We don't want to use it if the string from the command line # is a absolute path or relative to the current directory. - if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then + if [[ -z "$prepaths[1]" || "$pre[1]" = [~/] || "$pre" = (.|..)/* ]]; then tmp1=() else tmp1=(-W "( $prepaths )") @@ -145,7 +127,6 @@ if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then # Now call compgen. - nm=$compstate[nmatches] if [[ -z "$gopt" ]]; then compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt else @@ -157,13 +138,13 @@ if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then [[ compstate[nmatches] -eq nm ]] || return 0 fi -# No `-F' option, so we want to use `fignore'. +# If given no `-F' option, we want to use `fignore'. (( $#ignore )) || ignore=(-F fignore) # Now let's have a closer look at the string to complete. -if [[ "$str[1]" = \~ ]]; then +if [[ "$pre[1]" = \~ ]]; then # It begins with `~', so remember anything before the first slash to be able # to report it to the completion code. Also get an expanded version of it # (in `realpath'), so that we can generate the matches. Then remove that @@ -171,10 +152,10 @@ if [[ "$str[1]" = \~ ]]; then # paths and make sure that the loop below is run only once with an empty # prefix path by setting `prepaths'. - linepath="${str%%/*}/" + linepath="${pre%%/*}/" eval realpath\=$linepath [[ "$realpath" = "$linepath" ]] && return 1 - str="${str#*/}" + pre="${pre#*/}" orig="${orig#*/}" donepath='' prepaths=( '' ) @@ -185,12 +166,12 @@ else linepath='' realpath='' - if [[ "$str[1]" = / ]]; then + if [[ "$pre[1]" = / ]]; then # If it is a absolut path name, we remove the first slash and put it in # `donepath' meaning that we treat it as the path that was already handled. # Also, we don't use the paths from `-W'. - str="$str[2,-1]" + pre="$pre[2,-1]" orig="$orig[2,-1]" donepath='/' prepaths=( '' ) @@ -198,278 +179,204 @@ else # The common case, we just use the string as it is, unless it begins with # `./' or `../' in which case we don't use the paths from `-W'. - [[ "$str" = (.|..)/* ]] && prepaths=( '' ) + [[ "$pre" = (.|..)/* ]] && prepaths=( '' ) donepath='' fi fi -# Now build the glob pattern by calling `_match_pattern'. -patstr="$str" -matchflags="" -_match_pattern _path_files patstr matchflags -origflags="$matchflags" -[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - -# We almost expect the pattern to have changed `..' into `*.*.', `/.' into -# `/*.', and probably to contain two or more consecutive `*'s. Since these -# have special meaning for globbing, we remove them. But before that, we -# add the pattern for matching any characters before a slash. - -patstr="$patstr:gs-/-*/-:gs/*.*./../:gs/**/*/:gs-.*/-./-" - -# We take the last pathname component from the pattern and store it in -# `patlast', replacing `*'s in it with patterns that match any character -# but not slashes. Later we will generate matches using `patstr' with the -# patterns we were given (like `*.c') appended to it, producing all matching -# files. These filenames are then compared to `patlast' and all names not -# matching that will be removed. All this is needed to be able to correctly -# support `completeinword' as otherwise we would have something like `a*x' -# from the line (the `*' was inserted above) and appending the `-g' pattern -# `*.tex' would yield `a*x*.tex' which is not what we want. - -if [[ "$patstr" = */* ]]; then - if [[ -n "$_comp_correct" && "${#orig##*/}" -le _comp_correct ]]; then - patlast="*/${origflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}" - else - patlast="*/${matchflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}" - fi - patstr="${patstr%/*}/" -else - if [[ -n "$_comp_correct" && "$#orig" -le _comp_correct ]]; then - patlast="${origflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}" - else - patlast="${matchflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}" - fi - patstr="" -fi - -# First we skip over all pathname components in `str' which really exist in -# the file-system, so that `/usr/lib/l' doesn't offer you `lib' and -# `lib5'. Pathname components skipped this way are taken from `orig' and added -# to `donepath'. +# Now we generate the matches. First we loop over all prefix paths given +# with the `-W' option. -while [[ "$orig" = */* ]] do - tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}${^~pats} ) - tmp1=("${(@M)tmp1:#$~patlast}") - [[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break - donepath="$donepath${orig%%/*}/" - orig="${orig#*/}" - patstr="${patstr#*/}" -done +for prepath in "$prepaths[@]"; do -# Finally, generate the matches. First we loop over all the paths from `-W'. -# Note that in this loop `str' is used as a modifyable version of `patstr' -# and `testpath' is a modifyable version of `donepath'. + # Get local copies of the prefix, suffix, and the prefix path to use + # in the following loop, which walks through the pathname components + # in the string from the line. -for prepath in "$prepaths[@]"; do - str="$patstr" + tpre="$pre" + tsuf="$suf" testpath="$donepath" - ostr="$orig" - [[ -z "$prepath" || "$prepath[-1]" = / ]] || prepath="${prepath}/" + tmp1=( "$prepath$realpath$donepath" ) - # The second loop tests the components of the path in `str' to get the - # possible matches. + while true; do - while [[ "$str" = */* ]] do - # `rest' is the pathname after the first slash that is left. In `tmp1' - # we get the globbing matches for the pathname component currently - # handled. + # Skip over `./' and `../'. + + if [[ "$tpre" = (.|..)/* ]]; then + tmp1=( ${^tmp1}${tpre%%/*}/ ) + tpre="${tpre#*/}" + continue + fi - if [[ -n "$_comp_correct" && "${#ostr%%/*}" -le _comp_correct ]]; then - mflags="$origflags" + # Get the prefix and suffix for matching. + + if [[ "$tpre" = */* ]]; then + PREFIX="${tpre%%/*}" + SUFFIX="" else - mflags="$matchflags" + PREFIX="${tpre}" + SUFFIX="${tsuf%%/*}" fi - rest="${str#*/}" - tmp1="${prepath}${realpath}${testpath}${~mflags}${str%%/*}(-/)" - tmp1=( $~tmp1 ) - if [[ $#tmp1 -eq 0 ]]; then - # If this didn't produce any matches, we don't need to test this path - # any further, so continue with the next `-W' path, if any. + # Get the matching files by globbing. - continue 2 - elif [[ $#tmp1 -gt 1 ]]; then - # If it produced more than one match, we want to remove those which - # don't have possible following pathname components matching the - # rest of the string we are completing. (The case with only one - # match is handled below.) - # In `collect' we will collect those of the produced pathnames that - # have a matching possible path-suffix. In `suffixes' we build an - # array containing strings build from the rest of the string to - # complete and the glob patterns we were given as arguments. - - collect=() - suffixes=( $rest$^pats ) - suffixes=( "${(@)suffixes:gs.**.*.}" ) - - if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi + if [[ "$tpre$tsuf" = */* ]]; then + tmp1=( ${^tmp1}*(D/) ) + else + tmp1=( ${^tmp1}${^~pats} ) + fi + + if [[ -n "$PREFIX$SUFFIX" ]]; then + # See which of them match what's on the line. - # In the loop the prefixes from the `tmp1' array produced above and - # the suffixes we just built are used to produce possible matches - # via globbing. - - for i in "$tmp1[@]" ; do - tmp2=( ${~i}/${~mflags}${~suffixes} ) - tmp2=("${(@M)tmp2:#$~patlast}") - [[ $#tmp2 -ne 0 ]] && collect=( $collect $i ) - done - - # If this test showed that none of the matches from the glob in `tmp1' - # has a possible sub-path matching what's on the line, we add the - # matches found in `tmp1' and otherwise give up and continue with the - # next `-W' path. - - if [[ $#collect -eq 0 ]]; then - # Before giving, we first try to get the longest expandable path- - # prefix, though. The result is stored in `exppaths' - - tmp2=() - tmp3="$rest" - tmp4="${ostr##*/}" - ostr="${ostr%/*}" - while [[ "$tmp3" = */* ]]; do - tmp2=( ${^tmp1}/${~mflags}${~tmp3} ) - if [[ $#tmp2 -eq 1 ]]; then - exppaths=( "$exppaths[@]" "${tmp2[1]}${tmp4}" ) - exppaths=( "${(@)exppaths#${prepath}${realpath}}" ) - break; - fi - tmp3="${tmp3%/*}" - tmp4="${ostr##*/}/${tmp4}" - ostr="${ostr%/*}" - done + compadd -O tmp2 "$ignore[@]" - "${(@)tmp1##*/}" + + # If no file matches, save the expanded path and continue with + # the outer loop. + + if [[ $#tmp2 -eq 0 ]]; then + [[ "$tmp1[1]" = */* ]] && + exppaths=( "$exppaths[@]" ${^tmp1%/*}/${tpre}${tsuf} ) continue 2 - elif [[ $#collect -ne 1 ]]; then - # If we have more than one possible match, this means that the - # pathname component currently handled is ambiguous, so we give - # it to the completion code. - # First we build the full path prefix in `tmp1'. + fi - tmp1="$prepath$realpath$testpath" + # Remove all files that weren't matched. - # Now produce all matching pathnames in `collect'. + if [[ "$tmp1[1]" = */* ]]; then + tmp1=( "${(@M)tmp1:#*/(${(j:|:)~${(@)tmp2:q}})}" ) + else + tmp1=( "${(@M)tmp1:#(${(j:|:)~${(@)tmp2:q}})}" ) + fi + elif (( ! $#tmp1 )); then + [[ "$tmp1[1]" = */* ]] && + exppaths=( "$exppaths[@]" ${^tmp1%/*}/${tpre}${tsuf} ) + continue 2 + fi - collect=( ${~collect}/${~matchflags}${~suffixes} ) - collect=("${(@M)collect:#$~patlast}") + # Step over to the next component, if any. - # And then remove the common path prefix from all these matches. + if [[ "$tpre" = */* ]]; then + tpre="${tpre#*/}" + elif [[ "$tsuf" = */* ]]; then + tpre="${tsuf#*/}" + tsuf="" + else + break + fi - collect=( ${collect#$tmp1} ) + # There are more components, so add a slash to the files we are + # collecting. - # Finally, we add all these matches with the common (unexpanded) - # pathprefix (the `-p' option), the path-prefix (the `-W' option) - # to allow the completion code to test file type, and the path- - # suffix (the `-s' option). We also tell the completion code that - # these are file names and that `fignore' should be used as usual - # (the `-f' and `-F' options). + tmp1=( ${^tmp1}/ ) + done - if [[ -n "$menu" ]]; then - compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" -p "$linepath${testpath:q}" \ - -s "/${ostr#*/}" \ - -W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}" - else - for i in $collect; do - compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \ - -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${${i%%/*}:q}" - done - fi + # The next loop searches the first ambiguous component. - # We have just finished handling all the matches from above, so we - # can continue with the next `-W' path. + tmp3="$pre$suf" + tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" ) - continue 2 - fi + while true; do - # We reach this point if only one of the path prefixes in `tmp1' - # has a existing path-suffix matching the string from the line. - # In this case we accept this match and continue with the next - # path-name component. + # First we check if some of the files match the original string + # for this component. If there are some we remove all other + # names. This avoid having `foo' complete to `foo' and `foobar'. - tmp1=( "$collect[1]" ) - elif [[ -n "$_comp_correct" && "$mflags" = "$matchflags" ]]; then + if [[ "$tmp3" = */* ]]; then + tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" ) + if (( $#tmp4 )); then + tmp1=( "$tmp4[@]" ) + fi + fi - # If we got only one match with auto-correction and if we get none - # without correction, stop now. + # Next we see if this component is ambiguous. - tmp2="${prepath}${realpath}${testpath}${~origflags}${str%%/*}(-/)" - tmp2=( $~tmp2 ) + if [[ "$tmp3" = */* ]]; then + tmp4=( "${(@)tmp1:#${tmp1[1]%%/*}/*}" ) + else + tmp4=( "${(@)tmp1:#${tmp1[1]}}" ) + fi - if [[ $#tmp1 -ne $#tmp2 ]]; then - compadd -QU "$addpfx[@]" -S '' "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -s "/${ostr#*/}" \ - - "${${tmp1#${prepath}${realpath}${testpath}}:q}" - continue 2 + if (( $#tmp4 )); then + + # It is. For menucompletion we now add the possible completions + # for this component with the unambigous prefix we have built + # and the rest of the string from the line as the suffix. + # For normal completion we add the rests of the filenames + # collected as the suffixes to make the completion code expand + # it as far as possible. + + if [[ -n $menu ]]; then + if [[ "$tmp3" = */* ]]; then + compadd -Uf -p "$linepath$testpath" -s "/${tmp3#*/}" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "${(@)tmp1%%/*}" + else + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" + fi + else + if [[ "$tmp3" = */* ]]; then + for i in "$tmp1[@]"; do + compadd -Uf -p "$linepath$testpath" -s "/${i#*/}" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "${i%%/*}" + done + else + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" + fi fi + tmp4=- + break fi - # This is also reached if the first globbing produced only one match - # in this case we just continue with the next pathname component, too. - tmp1="$tmp1[1]" - testpath="$testpath${tmp1##*/}/" - str="$rest" - ostr="${ostr#*/}" - done + # If we have checked all components, we stop now and add the + # strings collected after the loop. - # We are here if all pathname components except the last one (which is still - # not tested) are unambiguous. So we add matches with the full path prefix, - # no path suffix, the `-W' we are currently handling, all the matches we - # can produce in this directory, if any. + if [[ "$tmp3" != */* ]]; then + tmp4="" + break + fi - if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi - tmp1="$prepath$realpath$testpath" - suffixes=( $str$^pats ) - suffixes=( "${(@)suffixes:gs.**.*.}" ) - tmp2=( ${~tmp1}${~matchflags}${~suffixes} ) - if [[ "$tmp1" = */* && "$patlast" != \*/* ]]; then - tmp2=("${(@M)tmp2:#*${~patlast}}") - else - tmp2=("${(@M)tmp2:#$~patlast}") - fi - if [[ $#tmp2 -eq 0 ]]; then - # No match, insert the expanded path and add the original tail. + # Otherwise we add the unambiguous component to `testpath' and + # take it from the filenames. - [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]" - [[ -z "$testpath" && "$linepath[-1]" = / ]] && linepath="$linepath[1,-2]" - [[ -n "$ostr" && -n "$linepath$testpath" ]] && ostr="/$ostr" + testpath="${testpath}${tmp1[1]%%/*}/" + tmp1=( "${(@)tmp1#*/}" ) - # But only if something changed. - [[ "${PREFIX}${SUFFIX}" = $linepath$testpath$ostr(|/) ]] && return 1 + tmp3="${tmp3#*/}" + done - compadd -QU -S '' "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" -f - "$linepath${testpath:q}$ostr" - else - compadd -QU "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -f "$ignore[@]" \ - -W "$prepath$realpath$testpath" - "${(@)${(@)tmp2#$tmp1}:q}" + if [[ -z "$tmp4" ]]; then + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" fi done -# If no matches were found but we have expanded paths which are different -# from the original string, use them. +# If we are configured to expand paths as far as possible and we collected +# expanded paths that are different from the string on the line, we add +# them as possible matches. exppaths=( "${(@)exppaths:#$orig}" ) if [[ -n "$compconfig[path_expand]" && - nm -eq compstate[nmatches] && $#exppaths -ne 0 ]]; then - compadd -UQ -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" - \ - "${linepath}${(@)^exppaths}" + $#exppaths -ne 0 && nm -eq compstate[nmatches] ]]; then + compadd -U -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" - \ + "${(@)exppaths}" fi -# This sets the return value to indicate that we added matches (or not). - -[[ nm -ne compstate[nmatches] ]] +[[ nm -eq compstate[nmatches] ]] diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts index fc927163b..b906a3042 100644 --- a/Completion/Core/_sep_parts +++ b/Completion/Core/_sep_parts @@ -9,24 +9,17 @@ # # _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 - # Get the options. group=() @@ -42,7 +35,7 @@ shift OPTIND-1 # Get the string from the line. str="$PREFIX$SUFFIX" -[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q" +SUFFIX="" prefix="" # Walk through the arguments to find the longest unambiguous prefix. @@ -56,61 +49,49 @@ 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}*}" + 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)" - - [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//" - - test="${matchflags}${test}" - testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) - testarr=( "${(@)testarr:#}" ) + PREFIX="$str" + 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=("") @@ -118,26 +99,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)" - - [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//" - - 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. @@ -146,26 +119,31 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do tmparr=( ${=arr[2,-2]} ) arr=tmparr fi - tmparr=( "${(@M)${(@P)arr}:#${~test}*}" ) - tmparr=( "${(@)tmparr:#}" ) + + compadd -O tmparr - "${(@P)arr}" + suffixes=("${(@)^suffixes[@]}${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") # 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") # Add the matches for each of the suffixes. + for i in "$suffixes[@]"; do compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \ -i "$IPREFIX" -I "$ISUFFIX" -p "$prefix" -s "$i" - "$testarr[@]" diff --git a/Completion/Core/compinit b/Completion/Core/compinit index 19a0fd1ad..20b1262ec 100644 --- a/Completion/Core/compinit +++ b/Completion/Core/compinit @@ -1,25 +1,23 @@ # Initialisation for new style completion. This mainly contains some helper -# function and aliases. Everything else is split into different files in this -# directory that will automatically be made autoloaded (see the end of this -# file). +# functions and aliases. Everything else is split into different files that +# will automatically be made autoloaded (see the end of this file). # The names of the files that will be considered for autoloading have to -# start with a underscores (like `_setopt). +# start with an underscores (like `_setopt). # The first line of these files will be read and has to say what should be # done with its contents: # -# `#defcomp ' -# if the first line looks like this, the file is -# autoloaded as a function and that function will -# be called to generate the matches when completing -# for one of the commands whose is given +# `#compdef ' +# If the first line looks like this, the file is autoloaded as a +# function and that function will be called to generate the matches +# when completing for one of the commands whose are given. # -# `#defpatcomp ' -# this defines a function that should be called to generate -# matches for commands whose name matches ; note -# that only one pattern may be given +# `#compdef -p ' +# This defines a function that should be called to generate matches +# for commands whose name matches . Note that only one pattern +# may be given. # -# `#defkeycomp