#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" != /* ]] && tmp="$PWD/$name" name="_args_cache_${name}" name="${name//[^a-zA-Z0-9_]/_}" if (( ! ${(P)+name} )); then local iopts sopts pattern tmpo 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 int # 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 comma 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}}" 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 get an # optional argument. tmpo=("${(@M)tmp:#*\[\=*}") if (( $#tmpo )); then tmp=("${(@)tmp:#*\[\=*}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") if [[ "$descr" = ::* ]]; then cache=( "$cache[@]" "${(@)^tmpo}=${descr}" ) else cache=( "$cache[@]" "${(@)^tmpo}=:${descr}" ) fi fi # Descriptions with `=': mandatory argument. tmpo=("${(@M)tmp:#*\=*}") if (( $#tmpo )); then tmp=("${(@)tmp:#*\=*}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") cache=( "$cache[@]" "${(@)^tmpo}=${descr}" ) fi # Everything else is just added as a option without arguments. if (( $#tmp )); then tmp=("${(@)tmp//[^a-zA-Z0-9-]}") cache=( "$cache[@]" "$tmp[@]" ) fi done eval "${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 ;; *) subopts=( "${(@P)1[3,-1]}" ); shift ;; esac done zstyle -s ":completion:${curcontext}:options" auto-description autod if (( $# )) && comparguments -i "$autod" "$@"; then local nm="$compstate[nmatches]" action noargs aret expl local local next direct odirect equal single match matched ws tmp1 tmp2 tmp3 local opts subc prefix suffix local origpre="$PREFIX" origipre="$IPREFIX" if comparguments -D descr action; then comparguments -C subc curcontext="${oldcontext%:*}:$subc" if comparguments -O next direct odirect equal; then opts=yes _tags arguments options else _tags arguments fi else if comparguments -a; then noargs='no more arguments' else noargs='no arguments' fi comparguments -O next direct odirect equal || return 1 opts=yes _tags options fi while true; do while _tags; do if [[ -n "$matched" ]] || _requested arguments; then _description arguments expl "$descr" if [[ "$action" = -\>* ]]; then comparguments -W line opt_args state="${${action[3,-1]##[ ]#}%%[ ]#}" if [[ -n "$usecc" ]]; then curcontext="${oldcontext%:*}:$subc" else context="$subc" fi compstate[restore]='' aret=yes 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. [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX" mesg="$descr" elif [[ "$action" = \(\(*\)\) ]]; then # ((...)) contains literal strings with descriptions. eval ws\=\( "${action[3,-3]}" \) _describe "$descr" ws -M "$match" "$subopts[@]" elif [[ "$action" = \(*\) ]]; then # Anything inside `(...)' is added directly. _all_labels arguments expl "$descr" \ compadd "$subopts[@]" - ${=action[2,-2]} elif [[ "$action" = \{*\} ]]; then # A string in braces is evaluated. while _next_label arguments expl "$descr"; do eval "$action[2,-2]" done elif [[ "$action" = \ * ]]; then # If the action starts with a space, we just call it. eval "action=( $action )" while _next_label arguments expl "$descr"; do "$action[@]" done else # Otherwise we call it with the description-arguments. eval "action=( $action )" _all_labels arguments expl "$descr" \ "$action[1]" "$subopts[@]" "${(@)action[2,-1]}" fi fi fi if [[ -z "$matched$hasopts" ]] && _requested options && { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ "$origpre" = [-+]* || ( -z "$aret$mesg" && nm -eq compstate[nmatches] ) ]] } ; then local prevpre="$PREFIX" previpre="$IPREFIX" hasopts=yes PREFIX="$origpre" IPREFIX="$origipre" comparguments -M match if comparguments -s single; then _description options expl option if [[ "$single" = direct ]]; then compadd "$expl[@]" -QS '' - "${PREFIX}${SUFFIX}" elif [[ "$single" = next ]]; then compadd "$expl[@]" -Q - "${PREFIX}${SUFFIX}" elif [[ "$single" = equal ]]; then compadd "$expl[@]" -QqS= - "${PREFIX}${SUFFIX}" else tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" ) 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 "$match" -- \ direct -QS '' -M "$match" -- \ equal -QqS= -M "$match" fi PREFIX="$prevpre" IPREFIX="$previpre" fi 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 "$match" -D equal - "${(@)equal%%:*}" if [[ $#equal -eq 1 ]]; then PREFIX="$prefix" SUFFIX="$suffix" IPREFIX="${IPREFIX}${equal[1]%%:*}=" matched=yes comparguments -L "${equal[1]%%:*}" descr action subc curcontext="${oldcontext%:*}:$subc" _tags arguments continue fi fi break done [[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext" [[ -n "$aret" ]] && return 300 [[ -n "$mesg" ]] && _message "$mesg" [[ -n "$noargs" ]] && _message "$noargs" # Set the return value. [[ nm -ne "$compstate[nmatches]" ]] else return 1 fi