From: martin f. krafft Date: Wed, 16 Jul 2008 14:14:59 +0000 (+0200) Subject: add temp hack for #486785 X-Git-Url: https://git.madduck.net/etc/zsh.git/commitdiff_plain/d337e355e4c6c2d779c5ab3f964b84805223305d add temp hack for #486785 --- diff --git a/.zsh/func/_path_files b/.zsh/func/_path_files new file mode 100644 index 0000000..a4aac8a --- /dev/null +++ b/.zsh/func/_path_files @@ -0,0 +1,743 @@ +#autoload + +# TEMPORARY HACK for #486785 + +# Utility function for in-path completion. This allows `/u/l/b' +# to complete to `/usr/local/bin'. + +local linepath realpath donepath prepath testpath exppath skips skipped +local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre +local pats haspats ignore pfx pfxsfx sopt gopt opt sdirs ignpar cfopt listsfx +local nm=$compstate[nmatches] menu matcher mopts sort mid accex fake +local listfiles listopts tmpdisp +local -a match mbegin mend + +typeset -U prepaths exppaths + +exppaths=() + +# Get the options. + +zparseopts -a mopts \ + 'P:=pfx' 'S:=pfxsfx' 'q=pfxsfx' 'r:=pfxsfx' 'R:=pfxsfx' \ + 'W:=prepaths' 'F:=ignore' 'M+:=matcher' \ + J+: V+: X+: 1 2 n 'f=tmp1' '/=tmp1' 'g+:-=tmp1' + +sopt="-${(@j::M)${(@)tmp1#-}#?}" +(( $tmp1[(I)-[/g]*] )) && haspats=yes +(( $tmp1[(I)-g*] )) && gopt=yes +if (( $tmp1[(I)-/] )); then + pats="${(@)${(@M)tmp1:#-g*}#-g}" + pats=( '*(-/)' ${${(z):-x $pats}[2,-1]} ) +else + pats="${(@)${(@M)tmp1:#-g*}#-g}" + pats=( ${${(z):-x $pats}[2,-1]} ) +fi +pats=( "${(@)pats:# #}" ) + +if (( $#pfx )); then + compset -P "$pfx[2]" || pfxsfx=( "$pfx[@]" "$pfxsfx[@]" ) +fi + +if (( $#prepaths )); then + tmp1="${prepaths[2]}" + if [[ "$tmp1[1]" = '(' ]]; then + prepaths=( ${^=tmp1[2,-2]%/}/ ) + elif [[ "$tmp1[1]" = '/' ]]; then + prepaths=( "${tmp1%/}/" ) + else + prepaths=( ${(P)^tmp1%/}/ ) + (( ! $#prepaths )) && prepaths=( ${tmp1%/}/ ) + fi + (( ! $#prepaths )) && prepaths=( '' ) +else + prepaths=( '' ) +fi + +if (( $#ignore )); then + if [[ "${ignore[2]}" = \(* ]]; then + ignore=( ${=ignore[2][2,-2]} ) + else + ignore=( ${(P)ignore[2]} ) + fi +fi + +# If we were given no file selection option, we behave as if we were given +# a `-f'. + +if [[ "$sopt" = -(f|) ]]; then + if [[ -z "$gopt" ]]; then + sopt='-f' + pats=('*') + else + unset sopt + fi +fi + +if (( ! $mopts[(I)-[JVX]] )); then + local expl + + if [[ -z "$gopt" && "$sopt" = -/ ]]; then + _description directories expl directory + else + _description files expl file + fi + tmp1=$expl[(I)-M*] + if (( tmp1 )); then + if (( $#matcher )); then + matcher[2]="$matcher[2] $expl[1+tmp1]" + else + matcher=(-M "$expl[1+tmp1]") + fi + fi + mopts=( "$mopts[@]" "$expl[@]" ) +fi + +# If given no `-F' option, we may want to use $fignore, turned into patterns. + +[[ -z "$_comp_no_ignore" && $#ignore -eq 0 && + ( -z $gopt || "$pats" = \ #\*\ # ) && -n $FIGNORE ]] && + ignore=( "?*${^fignore[@]}" ) + +if (( $#ignore )); then + _comp_ignore=( "$_comp_ignore[@]" "$ignore[@]" ) + (( $mopts[(I)-F] )) || mopts=( "$mopts[@]" -F _comp_ignore ) +fi + +if [[ $#matcher -eq 0 && -o nocaseglob ]]; then + # If globbing is case insensitive and there's no matcher, + # do case-insensitive matching. + matcher=( -M 'm:{a-zA-Z}={A-Za-z}' ) +fi + +if (( $#matcher )); then + # Add the current matcher to the options to compadd. + mopts=( "$mopts[@]" "$matcher[@]" ) +fi + +if zstyle -s ":completion:${curcontext}:" file-sort tmp1; then + case "$tmp1" in + *size*) sort=oL;; + *links*) sort=ol;; + *(time|date|modi)*) sort=om;; + *access*) sort=oa;; + *(inode|change)*) sort=oc;; + *) sort=on;; + esac + [[ "$tmp1" = *rev* ]] && sort[1]=O + + if [[ "$sort" = on ]]; then + sort= + else + mopts=( "${(@)mopts/#-J/-V}" ) + + tmp2=() + for tmp1 in "$pats[@]"; do + if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then + tmp2=( "$tmp2[@]" "${match[1]}(#q${sort}${match[2]})" ) + elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then + tmp2=( "$tmp2[@]" "${match[1]}((${sort}${match[2][3,-1]}" ) + elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then + tmp2=( "$tmp2[@]" "${match[1]}(${sort}${match[2][2,-1]}" ) + else + tmp2=( "$tmp2[@]" "${tmp1}(${sort})" ) + fi + done + pats=( "$tmp2[@]" ) + fi +fi + +# Check if we have to skip over sequences of slashes. The value of $skips +# is used below to match the pathname components we always have to accept +# immediately. + +if zstyle -t ":completion:${curcontext}:paths" squeeze-slashes; then + skips='((.|..|)/)##' +else + skips='((.|..)/)##' +fi + +zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs +zstyle -t ":completion:${curcontext}:paths" list-suffixes && + listsfx=yes + +[[ "$pats" = ((|*[[:blank:]])\*(|[[:blank:]]*|\([^[:blank:]]##\))|*\([^[:blank:]]#/[^[:blank:]]#\)*) ]] && + sopt=$sopt/ + +zstyle -a ":completion:${curcontext}:paths" accept-exact accex +zstyle -a ":completion:${curcontext}:" fake-files fake + +zstyle -s ":completion:${curcontext}:" ignore-parents ignpar + +if [[ -n "$compstate[pattern_match]" && + ( ( -z "$SUFFIX" && "$PREFIX" = (|*[^\$])\([^\|\~]##\) ) || + "$SUFFIX" = (|*[^\$])\([^\|\~]##\) ) ]]; then + # Copy all glob qualifiers from the line to + # the patterns used when generating matches + if [[ "$SUFFIX" = *\([^\|\~]##\) ]]; then + tmp3="${${(M)SUFFIX%\([^\|\~]##\)}[2,-2]}" + SUFFIX="${SUFFIX%\($tmp3\)}" + else + tmp3="${${(M)PREFIX%\([^\|\~]##\)}[2,-2]}" + PREFIX="${PREFIX%\($tmp3\)}" + fi + tmp2=() + for tmp1 in "$pats[@]"; do + if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then + tmp2=( "$tmp2[@]" "${match[1]}(#q${tmp3}${match[2]})" ) + elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then + tmp2=( "$tmp2[@]" "${match[1]}((${tmp3}${match[2][3,-1]}" ) + elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then + tmp2=( "$tmp2[@]" "${match[1]}(${tmp3}${match[2][2,-1]}" ) + else + tmp2=( "$tmp2[@]" "${tmp1}(${tmp3})" ) + fi + done + pats=( "$tmp2[@]" ) +fi + +# We get the prefix and the suffix from the line and save the whole +# original string. Then we see if we will do menu completion. + +pre="$PREFIX" +suf="$SUFFIX" +opre="$PREFIX" +osuf="$SUFFIX" +orig="${PREFIX}${SUFFIX}" +eorig="$orig" + +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || + ( -n "$compstate[pattern_match]" && + "${orig#\~}" != (|*[^\\])[][*?#~^\|\<\>]* ) ]] && menu=yes +[[ -n "$_comp_correct" ]] && cfopt=- + +# Now let's have a closer look at the string to complete. + +if [[ "$pre" = [^][*?#^\|\<\>\\]#(\`[^\`]#\`|\$)*/* && "$compstate[quote]" != \' ]]; then + + # If there is a parameter expansion in the word from the line, we try + # to complete the beast by expanding the prefix and completing anything + # after the first slash after the parameter expansion. + # This fails for things like `f/$foo/b/' where the first `f' is + # meant as a partial path. + + linepath="${(M)pre##*\$[^/]##/}" + eval 'realpath=${(e)~linepath}' 2>/dev/null + [[ -z "$realpath" || "$realpath" = "$linepath" ]] && return 1 + pre="${pre#${linepath}}" + i='[^/]' + i="${#linepath//$i}" + orig="${orig[1,(in:i:)/][1,-2]}" + donepath= + prepaths=( '' ) +elif [[ "$pre[1]" = \~ && -z "$compstate[quote]" ]]; 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 + # prefix from the string to complete, set `donepath' to build the correct + # paths and make sure that the loop below is run only once with an empty + # prefix path by setting `prepaths'. + + linepath="${pre[2,-1]%%/*}" + if [[ -z "$linepath" ]]; then + realpath="${HOME%/}/" + elif [[ "$linepath" = ([-+]|)[0-9]## ]]; then + if [[ "$linepath" != [-+]* ]]; then + tmp1="$linepath" + else + if [[ "$linepath" = -* ]]; then + tmp1=$(( $#dirstack $linepath )) + else + tmp1=$linepath[2,-1] + fi + [[ -o pushdminus ]] && tmp1=$(( $#dirstack - $tmp1 )) + fi + if (( ! tmp1 )); then + realpath=$PWD/ + elif [[ tmp1 -le $#dirstack ]]; then + realpath=$dirstack[tmp1]/ + else + _message 'not enough directory stack entries' + return 1 + fi + elif [[ "$linepath" = [-+] ]]; then + realpath=${~:-\~$linepath}/ + else + eval "realpath=~${linepath}/" 2>/dev/null + if [[ -z "$realpath" ]]; then + _message "unknown user \`$linepath'" + return 1 + fi + fi + linepath="~${linepath}/" + [[ "$realpath" = "$linepath" ]] && return 1 + pre="${pre#*/}" + orig="${orig#*/}" + donepath= + prepaths=( '' ) +else + # If the string does not start with a `~' we don't remove a prefix from the + # string. + + linepath= + realpath= + + if zstyle -s ":completion:${curcontext}:" preserve-prefix tmp1 && + [[ -n "$tmp1" && "$pre" = (#b)(${~tmp1})* ]]; then + + pre="$pre[${#match[1]}+1,-1]" + orig="$orig[${#match[1]}+1,-1]" + donepath="$match[1]" + prepaths=( '' ) + + elif [[ "$pre[1]" = / ]]; then + # If it is a absolute 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'. + + pre="$pre[2,-1]" + orig="$orig[2,-1]" + donepath='/' + prepaths=( '' ) + 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'. + + [[ "$pre" = (.|..)/* ]] && prepaths=( '' ) + donepath= + fi +fi + +# Now we generate the matches. First we loop over all prefix paths given +# with the `-W' option. + +for prepath in "$prepaths[@]"; do + + # 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. + + skipped= + cpre= + tpre="$pre" + tsuf="$suf" + testpath="$donepath" + + tmp2="${(M)tpre##${~skips}}" + tpre="${tpre#$tmp2}" + + tmp1=( "$prepath$realpath$donepath$tmp2" ) + + while true; do + + # Get the prefix and suffix for matching. + + if [[ "$tpre" = */* ]]; then + PREFIX="${tpre%%/*}" + SUFFIX= + else + PREFIX="${tpre}" + SUFFIX="${tsuf%%/*}" + fi + + # Force auto-mounting. There might be a better way... + # Commented out in the hope that `pws non-canonical hack' + # down below does this for us. Can be uncommented if it + # doesn't. + + # : ${^tmp1}/${PREFIX}${SUFFIX}/.(/) + + # Get the matching files by globbing. + + tmp2=( "$tmp1[@]" ) + + # Look for glob qualifiers. + # Extra nastiness to be careful about a quoted parenthesis. + # The initial tests look for parentheses with zero or an + # even number of backslashes in front. + # The later test looks for an outstanding quote. + if [[ ( -o bareglobqual && \ + "$tpre/$tsuf" = (#b)((*[^\\]|)(\\\\)#\()([^\)]#) || \ + -o extendedglob && \ + "$tpre/$tsuf" = (#b)((*[^\\]|)(\\\\)#"(#q")([^\)]#) \ + ) && -z $compstate[quote] ]]; then + compset -p ${#match[1]} + _globquals + elif [[ "$tpre$tsuf" = */* ]]; then + compfiles -P$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake + elif [[ "$sopt" = *[/f]* ]]; then + compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake "$pats[@]" + else + compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" '' fake "$pats[@]" + fi + tmp1=( $~tmp1 ) 2> /dev/null + + if [[ -n "$PREFIX$SUFFIX" ]]; then + # See which of them match what's on the line. + + # pws non-canonical hack which seems to work so far... + # if we didn't match by globbing, check that there is + # something to match by explicit name. This is for + # `clever' filing systems where names pop into existence + # when referenced. + if (( ! $#tmp1 )); then + for tmp3 in "$tmp2[@]"; do + if [[ -n $tmp3 && $tmp3 != */ ]]; then + tmp3+=/ + fi + if [[ -e "$tmp3${(Q)PREFIX}${(Q)SUFFIX}" ]] then + tmp1+=("$tmp3${(Q)PREFIX}${(Q)SUFFIX}") + fi + done + fi + + if (( ! $#tmp1 )); then + tmp2=( ${^tmp2}/$PREFIX$SUFFIX ) + elif [[ "$tmp1[1]" = */* ]]; then + if [[ -n "$_comp_correct" ]]; then + tmp2=( "$tmp1[@]" ) + builtin compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}" + + if [[ $#tmp1 -eq 0 ]]; then + tmp1=( "$tmp2[@]" ) + compadd -D tmp1 "$matcher[@]" - "${(@)tmp2:t}" + fi + else + tmp2=( "$tmp1[@]" ) + compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}" + fi + else + tmp2=( '' ) + compadd -D tmp1 "$matcher[@]" -a tmp1 + fi + + # If no file matches, save the expanded path and continue with + # the outer loop. + + if (( ! $#tmp1 )); then + if [[ "$tmp2[1]" = */* ]]; then + tmp2=( "${(@)tmp2#${prepath}${realpath}}" ) + if [[ "$tmp2[1]" = */* ]]; then + tmp2=( "${(@)tmp2:h}" ) + compquote tmp2 + if [[ "$tmp2" = */ ]]; then + exppaths=( "$exppaths[@]" ${^tmp2}${tpre}${tsuf} ) + else + exppaths=( "$exppaths[@]" ${^tmp2}/${tpre}${tsuf} ) + fi + elif [[ ${tpre}${tsuf} = */* ]]; then + exppaths=( "$exppaths[@]" ${tpre}${tsuf} ) + + ### this once was in an `else' (not `elif') + fi + fi + continue 2 + fi + elif (( ! $#tmp1 )); then + # A little extra hack: if we were completing `foo/' and `foo' + # contains no files, this will normally produce no matches and other + # completers might think that's it's their time now. But if the next + # completer is _correct or something like that, this will result in + # an attempt to correct a valid directory name. So we just add the + # original string in such a case so that the command line doesn't + # change but other completers still think there are matches. + # We do this only if we weren't given a `-g' or `-/' option because + # otherwise this would keep `_files' from completing all filenames + # if none of the patterns match. + + if [[ -z "$tpre$tsuf" && -n "$pre$suf" ]]; then + pfxsfx=(-S '' "$pfxsfx[@]") + ### Don't remember what the break was good for. We explicitly + ### execute this only when there are no matches in the directory, + ### so why continue? + ### + ### tmp1=( "$tmp2[@]" ) + ### break + elif [[ -n "$haspats" && -z "$tpre$tsuf$suf" && "$pre" = */ ]]; then + PREFIX="${opre}" + SUFFIX="${osuf}" + compadd -nQS '' - "$linepath$donepath$orig" + tmp4=- + fi + continue 2 + fi + + if [[ -n "$ignpar" && -z "$_comp_no_ignore" && + "$tpre$tsuf" != */* && $#tmp1 -ne 0 && + ( "$ignpar" != *dir* || "$pats" = '*(-/)' ) && + ( "$ignpar" != *..* || "$tmp1[1]" = *../* ) ]]; then + + compfiles -i tmp1 _comp_ignore "$ignpar" "$prepath$realpath$donepath" + + (( $#_comp_ignore && $mopts[(I)-F] )) || + mopts=( "$mopts[@]" -F _comp_ignore ) + fi + + # Step over to the next component, if any. + + if [[ "$tpre" = */* ]]; then + tpre="${tpre#*/}" + elif [[ "$tsuf" = */* ]]; then + tpre="${tsuf#*/}" + tsuf= + else + break + fi + + # There are more components, so skip over the next components and make a + # slash be added. + + tmp1=( ${tmp1//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} ) + tmp2="${(M)tpre##((.|..|)/)##}" + if [[ -n "$tmp2" ]]; then + skipped="/$tmp2" + tpre="${tpre#$tmp2}" + else + skipped=/ + fi + done + + # The next loop searches the first ambiguous component. + + tmp3="$pre$suf" + tpre="$pre" + tsuf="$suf" + [[ -n "${prepath}${realpath}${testpath}" ]] && + tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" ) + + while true; do + + # 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 avoids having `foo' complete to `foo' and `foobar'. + # The return value is non-zero if the component is ambiguous. + + compfiles -r tmp1 "${(Q)tmp3}" + tmp4=$? + + if [[ "$tpre" = */* ]]; then + tmp2="${cpre}${tpre%%/*}" + PREFIX="${donepath}${linepath}${tmp2}" + SUFFIX="/${tpre#*/}${tsuf#*/}" + else + tmp2="${cpre}${tpre}" + PREFIX="${donepath}${linepath}${tmp2}" + SUFFIX="${tsuf}" + fi + + # This once tested `|| [[ -n "$compstate[pattern_match]" && + # "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]' but it should now be smart + # enough to handle multiple components with patterns. + + if (( tmp4 )); then + # It is. For menu completion we now add the possible completions + # for this component with the unambiguous 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. + + tmp2="$testpath" + if [[ -n "$linepath" ]]; then + compquote -p tmp2 tmp1 + elif [[ -n "$tmp2" ]]; then + compquote -p tmp1 + compquote tmp2 + else + compquote tmp1 tmp2 + fi + + if [[ -z "$_comp_correct" && + "$compstate[pattern_match]" = \* && -n "$listsfx" && + "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then + PREFIX="$opre" + SUFFIX="$osuf" + fi + + # This once tested `-n $menu ||' but our menu-completion expert says + # that's not what we want. + + if [[ -z "$compstate[insert]" ]] || + { ! zstyle -t ":completion:${curcontext}:paths" expand suffix && + [[ -z "$listsfx" && + ( -n "$_comp_correct" || + -z "$compstate[pattern_match]" || "$SUFFIX" != */* || + "${SUFFIX#*/}" = (|*[^\\])[][*?#~^\|\<\>]* ) ]] }; then + # We have not been told to insert the match, so we are + # listing, or something. + (( tmp4 )) && zstyle -t ":completion:${curcontext}:paths" ambiguous && + compstate[to_end]= + if [[ "$tmp3" = */* ]]; then + if [[ -z "$listsfx" || "$tmp3" != */?* ]]; then + # I think this means we are expanding some directory + # back up the path. + tmp1=("${(@)tmp1%%/*}") + _list_files tmp1 "$prepath$realpath$testpath" + compadd -Qf "$mopts[@]" -p "$linepath$tmp2" -s "/${tmp3#*/}" \ + -W "$prepath$realpath$testpath" \ + "$pfxsfx[@]" -M "r:|/=* r:|=*" \ + $listopts \ + -a tmp1 + else + # Same with a non-empty suffix + tmp1=("${(@)^tmp1%%/*}/${tmp3#*/}") + _list_files tmp1 "$prepath$realpath$testpath" + compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \ + -W "$prepath$realpath$testpath" \ + "$pfxsfx[@]" -M "r:|/=* r:|=*" \ + $listopts \ + -a tmp1 + fi + else + _list_files tmp1 "$prepath$realpath$testpath" + compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \ + -W "$prepath$realpath$testpath" \ + "$pfxsfx[@]" -M "r:|/=* r:|=*" \ + $listopts \ + -a tmp1 + fi + else + # We are inserting the match into the command line. + if [[ "$tmp3" = */* ]]; then + tmp4=( -Qf "$mopts[@]" -p "$linepath$tmp2" + -W "$prepath$realpath$testpath" + "$pfxsfx[@]" -M "r:|/=* r:|=*" ) + if [[ -z "$listsfx" ]]; then + for i in "$tmp1[@]"; do + tmpdisp=("${i%%/*}") + _list_files tmpdisp "$prepath$realpath$testpath" + compadd "$tmp4[@]" -s "/${i#*/}" $listopts - "$tmpdisp" + done + else + [[ -n "$compstate[pattern_match]" ]] && SUFFIX="${SUFFIX:s./.*/}*" + + for i in "$tmp1[@]"; do + _list_files i "$prepath$realpath$testpath" + compadd "$tmp4[@]" $listopts - "$i" + done + fi + else + _list_files tmp1 "$prepath$realpath$testpath" + compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \ + -W "$prepath$realpath$testpath" \ + "$pfxsfx[@]" -M "r:|/=* r:|=*" \ + $listopts \ + -a tmp1 + fi + fi + tmp4=- + break + fi + + # If we have checked all components, we stop now and add the + # strings collected after the loop. + + if [[ "$tmp3" != */* ]]; then + tmp4= + break + fi + + # Otherwise we add the unambiguous component to `testpath' and + # take it from the filenames. + + testpath="${testpath}${tmp1[1]%%/*}/" + + tmp3="${tmp3#*/}" + + if [[ "$tpre" = */* ]]; then + if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" && + "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then + cpre="${cpre}${tmp1[1]%%/*}/" + else + cpre="${cpre}${tpre%%/*}/" + fi + tpre="${tpre#*/}" + elif [[ "$tsuf" = */* ]]; then + [[ "$tsuf" != /* ]] && mid="$testpath" + if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" && + "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then + cpre="${cpre}${tmp1[1]%%/*}/" + else + cpre="${cpre}${tpre}/" + fi + tpre="${tsuf#*/}" + tsuf= + else + tpre= + tsuf= + fi + + tmp1=( "${(@)tmp1#*/}" ) + done + + if [[ -z "$tmp4" ]]; then + if [[ "$mid" = */ ]]; then + PREFIX="${opre}" + SUFFIX="${osuf}" + + tmp4="${testpath#${mid}}" + tmp3="${mid%/*/}" + tmp2="${${mid%/}##*/}" + if [[ -n "$linepath" ]]; then + compquote -p tmp3 + else + compquote tmp3 + fi + compquote tmp4 tmp2 tmp1 + for i in "$tmp1[@]"; do + _list_files tmp2 "$prepath$realpath${mid%/*/}" + compadd -Qf "$mopts[@]" -p "$linepath$tmp3/" -s "/$tmp4$i" \ + -W "$prepath$realpath${mid%/*/}/" \ + "$pfxsfx[@]" -M "r:|/=* r:|=*" $listopts - "$tmp2" + done + else + if [[ "$osuf" = */* ]]; then + PREFIX="${opre}${osuf}" + SUFFIX= + else + PREFIX="${opre}" + SUFFIX="${osuf}" + fi + tmp4="$testpath" + if [[ -n "$linepath" ]]; then + compquote -p tmp4 tmp1 + elif [[ -n "$tmp4" ]]; then + compquote -p tmp1 + compquote tmp4 + else + compquote tmp4 tmp1 + fi + if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" && + "${PREFIX#\~}$SUFFIX" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then + tmp1=("$linepath$tmp4${(@)^tmp1}") + _list_files tmp1 "$prepath$realpath" + compadd -Qf -W "$prepath$realpath" "$pfxsfx[@]" "$mopts[@]" \ + -M "r:|/=* r:|=*" $listopts -a tmp1 + else + # Not a pattern match + _list_files tmp1 "$prepath$realpath$testpath" + compadd -Qf -p "$linepath$tmp4" -W "$prepath$realpath$testpath" \ + "$pfxsfx[@]" "$mopts[@]" -M "r:|/=* r:|=*" $listopts -a tmp1 + fi + fi + fi +done + +# 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. Do that only if we are currently trying the +# last entry in the matcher-list style, otherwise other match specs might +# make the suffix that didn't match this time match in one of the following +# attempts. + +if [[ _matcher_num -eq ${#_matchers} ]] && + zstyle -t ":completion:${curcontext}:paths" expand prefix && + [[ nm -eq compstate[nmatches] && $#exppaths -ne 0 && + "$linepath$exppaths" != "$eorig" ]]; then + PREFIX="${opre}" + SUFFIX="${osuf}" + compadd -Q "$mopts[@]" -S '' -M "r:|/=* r:|=*" -p "$linepath" -a exppaths +fi + +[[ nm -ne compstate[nmatches] ]]