#autoload # Complete the arguments of the current command according to the # descriptions given as arguments to this function. local long cmd="$words[1]" descr mesg subopts opt usecc autod local oldcontext="$curcontext" hasopts long=$argv[(I)--] if (( long )); then local name tmp tmpargv if [[ long -eq 1 ]]; then tmpargv=() else tmpargv=( "${(@)argv[1,long-1]}" ) fi name=${~words[1]} [[ "$name" = [^/]*/* ]] && name="$PWD/$name" name="_args_cache_${name}" name="${name//[^a-zA-Z0-9_]/_}" if (( ! ${(P)+name} )); then local iopts sopts pattern tmpo dir cur cache typeset -U lopts cache=() # We have to build a new long-option cache, get the `-i' and # `-s' options. set -- "${(@)argv[long+1,-1]}" iopts=() sopts=() while [[ "$1" = -[is]* ]]; do if [[ "$1" = -??* ]]; then tmp="${1[3,-1]}" cur=1 else tmp="$2" cur=2 fi if [[ "$tmp[1]" = '(' ]]; then tmp=( ${=tmp[2,-2]} ) else tmp=( "${(@P)tmp}" ) fi if [[ "$1" = -i* ]]; then iopts=( "$iopts[@]" "$tmp[@]" ) else sopts=( "$sopts[@]" "$tmp[@]" ) fi shift cur done # Now get the long option names by calling the command with `--help'. # The parameter expansion trickery first gets the lines as separate # array elements. Then we select all lines whose first non-blank # character is a hyphen. Since some commands document more than one # option per line, separated by commas, we convert commas into # newlines and then split the result again at newlines after joining # the old array elements with newlines between them. Then we select # those elements that start with two hyphens, remove anything up to # those hyphens and anything from the space or tab after the # option up to the end. lopts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$(_call options ${~words[1]} --help 2>&1)//\[--/ --}:#[ ]#-*}//,/ }}:#[ ]#--*}#*--}%%[] ]*}:#}") lopts=( "${(@)lopts:#--}" ) # Now remove all ignored options ... while (( $#iopts )); do lopts=( ${lopts:#$~iopts[1]} ) shift iopts done # ... and add "same" options while (( $#sopts )); do lopts=( $lopts ${lopts/$~sopts[1]/$sopts[2]} ) shift 2 sopts done # Then we walk through the descriptions plus a few builtin ones. set -- "$@" '*=FILE*:file:_files' \ '*=(DIR|PATH)*:directory:_files -/' '*: :' while (( $# )); do # First, we get the pattern and the action to use and take them # from the positional parameters. pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}" descr="${1#${pattern}}" if [[ "$pattern" = *\(-\) ]]; then pattern="$pattern[1,-4]" dir=- else dir= fi shift # We get all options matching the pattern and take them from the # list we have built. If no option matches the pattern, we # continue with the next. tmp=("${(@M)lopts:##$~pattern}") lopts=("${(@)lopts:##$~pattern}") (( $#tmp )) || continue opt='' # If there are option strings with a `[=', we take these to get an # optional argument. tmpo=("${(@M)tmp:#*\[\=*}") if (( $#tmpo )); then tmp=("${(@)tmp:#*\[\=*}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") if [[ "$descr" = ::* ]]; then cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" ) else cache=( "$cache[@]" "${(@)^tmpo}=${dir}:${descr}" ) fi fi # Descriptions with `=': mandatory argument. tmpo=("${(@M)tmp:#*\=*}") if (( $#tmpo )); then tmp=("${(@)tmp:#*\=*}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" ) fi # Everything else is just added as an option without arguments. if (( $#tmp )); then tmp=("${(@)tmp//[^a-zA-Z0-9-]}") cache=( "$cache[@]" "$tmp[@]" ) fi done set -A "$name" "${(@)cache:# #}" fi set -- "$tmpargv[@]" "${(@P)name}" fi subopts=() while [[ "$1" = -(O*|C) ]]; do case "$1" in -C) usecc=yes; shift ;; -O) subopts=( "${(@P)2}" ); shift 2 ;; -O*) subopts=( "${(@P)1[3,-1]}" ); shift ;; esac done zstyle -s ":completion:${curcontext}:options" auto-description autod if (( $# )) && comparguments -i "$autod" "$@"; then local action noargs aret expl local tried local next direct odirect equal single matcher matched ws tmp1 tmp2 tmp3 local opts subc tc prefix suffix descrs actions subcs anum local origpre="$PREFIX" origipre="$IPREFIX" nm="$compstate[nmatches]" if comparguments -D descrs actions subcs; then if comparguments -O next direct odirect equal; then opts=yes _tags "$subcs[@]" options else _tags "$subcs[@]" fi else if comparguments -a; then noargs='no more arguments' else noargs='no arguments' fi if comparguments -O next direct odirect equal; then opts=yes _tags options elif [[ $? -eq 2 ]]; then compadd -Q - "${PREFIX}${SUFFIX}" return 0 else _message "$noargs" return 1 fi fi comparguments -M matcher context=() state=() while true; do while _tags; do anum=1 while [[ anum -le $#descrs ]]; do action="$actions[anum]" descr="$descrs[anum]" subc="$subcs[anum++]" if [[ -n "$matched" ]] || _requested "$subc"; then curcontext="${oldcontext%:*}:$subc" _description "$subc" expl "$descr" if [[ "$action" = \=\ * ]]; then action="$action[3,-1]" words=( "$subc" "$words[@]" ) (( CURRENT++ )) fi if [[ "$action" = -\>* ]]; then action="${${action[3,-1]##[ ]#}%%[ ]#}" if (( ! $state[(I)$action] )); then comparguments -W line opt_args state=( "$state[@]" "$action" ) if [[ -n "$usecc" ]]; then curcontext="${oldcontext%:*}:$subc" else context=( "$context[@]" "$subc" ) fi compstate[restore]='' aret=yes fi else if [[ -z "$local" ]]; then local line typeset -A opt_args local=yes fi comparguments -W line opt_args if [[ "$action" = \ # ]]; then # An empty action means that we should just display a message. _message "$descr" mesg=yes tried=yes elif [[ "$action" = \(\(*\)\) ]]; then # ((...)) contains literal strings with descriptions. eval ws\=\( "${action[3,-3]}" \) _describe -t "$subc" "$descr" ws -M "$matcher" "$subopts[@]" tried=yes elif [[ "$action" = \(*\) ]]; then # Anything inside `(...)' is added directly. _all_labels "$subc" expl "$descr" \ compadd "$subopts[@]" - ${=action[2,-2]} tried=yes elif [[ "$action" = \{*\} ]]; then # A string in braces is evaluated. while _next_label "$subc" expl "$descr"; do eval "$action[2,-2]" done tried=yes elif [[ "$action" = \ * ]]; then # If the action starts with a space, we just call it. eval "action=( $action )" while _next_label "$subc" expl "$descr"; do "$action[@]" done tried=yes else # Otherwise we call it with the description-arguments. set -A action ${=~action} while _next_label "$subc" expl "$descr"; do "$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}" done tried=yes fi fi fi done if [[ -z "$matched$hasopts" ]] && _requested options && { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ "$origpre" = [-+]* || -z "$aret$mesg$tried" ]] } ; then local prevpre="$PREFIX" previpre="$IPREFIX" hasopts=yes PREFIX="$origpre" IPREFIX="$origipre" if comparguments -s single; then if [[ "$single" = direct ]]; then _all_labels options expl option \ compadd -QS '' - "${PREFIX}${SUFFIX}" elif [[ "$single" = next ]]; then _all_labels options expl option \ compadd -Q - "${PREFIX}${SUFFIX}" elif [[ "$single" = equal ]]; then _all_labels options expl option \ compadd -QqS= - "${PREFIX}${SUFFIX}" else tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" ) [[ "$PREFIX" != --* ]] && tmp1=( "${(@)tmp1:#--*}" ) tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" ) tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" ) tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" ) _describe -o option \ tmp1 tmp2 -Q -S '' -- \ tmp3 -Q fi single=yes else next=( "$next[@]" "$odirect[@]" ) _describe -o option \ next -Q -M "$matcher" -- \ direct -QS '' -M "$matcher" -- \ equal -QqS= -M "$matcher" fi PREFIX="$prevpre" IPREFIX="$previpre" fi [[ -n "$tried" && "$PREFIX" != [-+]* ]] && break done if [[ -n "$opts" && -z "$aret$matched$mesg" && nm -eq compstate[nmatches] ]]; then PREFIX="$origpre" IPREFIX="$origipre" prefix="${PREFIX#*\=}" suffix="$SUFFIX" PREFIX="${PREFIX%%\=*}" SUFFIX='' compadd -M "$matcher" -D equal - "${(@)equal%%:*}" if [[ $#equal -eq 1 ]]; then PREFIX="$prefix" SUFFIX="$suffix" IPREFIX="${IPREFIX}${equal[1]%%:*}=" matched=yes comparguments -L "${equal[1]%%:*}" descrs actions subcs _tags "$subcs[@]" continue fi fi break done [[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext" [[ -n "$aret" ]] && return 300 [[ -n "$noargs" && nm -eq "$compstate[nmatches]" ]] && _message "$noargs" # Set the return value. [[ nm -ne "$compstate[nmatches]" ]] else return 1 fi