From 52f25ab3d2d81a7c3172a54e3c0824de40b30270 Mon Sep 17 00:00:00 2001 From: Paul Ackersviller Date: Mon, 29 Oct 2007 20:13:58 +0000 Subject: Merge of 22780: _arguments -n sets NORMARG to index of first non-option argument (via comparguments -n). --- Completion/Base/Utility/_arguments | 470 +++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 Completion/Base/Utility/_arguments (limited to 'Completion/Base') diff --git a/Completion/Base/Utility/_arguments b/Completion/Base/Utility/_arguments new file mode 100644 index 000000000..a87486168 --- /dev/null +++ b/Completion/Base/Utility/_arguments @@ -0,0 +1,470 @@ +#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 rawret optarg singopt alwopt +local setnormarg + +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_program options ${~words[1]} --help 2>&1)//\[--/ +--}:#[ ]#-*}//,/ +}}:#[ ]#--*}#*--}%%[] ]*}:#}//\[=/=}") + + # Remove options also described by user-defined specs. + + tmp=() + for opt in "${(@)${(@)lopts:#--}%%\=*}"; do + + # Using (( ... )) gives a parse error. + + let "$tmpargv[(I)(|\([^\)]#\))(|\*)${opt}(|[-+]|=(|-))(|\[*\])(|:*)]" || + tmp=( "$tmp[@]" "$lopts[(r)$opt(|=*)]" ) + done + lopts=( "$tmp[@]" ) + + # 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. + # The last one matches all options; the `special' description and action + # makes those options be completed without an argument description. + + 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:#*\[\=*}") + + if [[ "$descr" = :\=* ]]; then + for opt in "$tmpo[@]"; do + cache=( "$cache[@]" + "${${opt%%\=*}//[^a-zA-Z0-9-]}=::${(L)${opt%\]}#*\=}: " ) + done + else + tmpo=("${(@)${(@)tmpo%%\=*}//[^a-zA-Z0-9-]}") + if [[ "$descr" = ::* ]]; then + cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" ) + else + cache=( "$cache[@]" "${(@)^tmpo}=${dir}:${descr}" ) + fi + fi + fi + + # Descriptions with `=': mandatory argument. + + tmpo=("${(@M)tmp:#*\=*}") + if (( $#tmpo )); then + tmp=("${(@)tmp:#*\=*}") + + if [[ "$descr" = :\=* ]]; then + for opt in "$tmpo[@]"; do + cache=( "$cache[@]" + "${${opt%%\=*}//[^a-zA-Z0-9-]}=:${(L)${opt%\]}#*\=}: " ) + done + else + tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") + + cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" ) + fi + fi + + # Everything else is just added as an option without arguments or + # as described by $descr. + + if (( $#tmp )); then + tmp=("${(@)tmp//[^a-zA-Z0-9-]}") + if [[ -n "$descr" && "$descr" != ': : ' ]]; then + cache=( "$cache[@]" "${(@)^tmp}${descr}" ) + else + cache=( "$cache[@]" "$tmp[@]" ) + fi + fi + done + set -A "$name" "${(@)cache:# #}" + fi + set -- "$tmpargv[@]" "${(@P)name}" +fi + +subopts=() +singopt=() +while [[ "$1" = -(O*|[CRWnsw]) ]]; do + case "$1" in + -C) usecc=yes; shift ;; + -O) subopts=( "${(@P)2}" ); shift 2 ;; + -O*) subopts=( "${(@P)${1[3,-1]}}" ); shift ;; + -R) rawret=yes; shift;; + -n) setnormarg=yes; NORMARG=-1; shift;; + -w) optarg=yes; shift;; + -s) singopt=(-s); shift;; + -W) alwopt=arg; shift;; + esac +done + +[[ "$PREFIX" = [-+] ]] && alwopt=arg + +zstyle -s ":completion:${curcontext}:options" auto-description autod + +if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then + local action noargs aret expl local tried ret=1 + 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 + if [[ -z "$tried" ]]; then + while [[ anum -le $#descrs ]]; do + + action="$actions[anum]" + descr="$descrs[anum]" + subc="$subcs[anum++]" + + if [[ $subc = argument* && -n $setnormarg ]]; then + comparguments -n NORMARG + fi + + 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 -e "$subc" "$descr" + mesg=yes + tried=yes + alwopt=${alwopt:-yes} + elif [[ "$action" = \(\(*\)\) ]]; then + + # ((...)) contains literal strings with descriptions. + + eval ws\=\( "${action[3,-3]}" \) + + _describe -t "$subc" "$descr" ws -M "$matcher" "$subopts[@]" || + alwopt=${alwopt:-yes} + tried=yes + + elif [[ "$action" = \(*\) ]]; then + + # Anything inside `(...)' is added directly. + + eval ws\=\( "${action[2,-2]}" \) + + _all_labels "$subc" expl "$descr" compadd "$subopts[@]" -a - ws || + alwopt=${alwopt:-yes} + tried=yes + elif [[ "$action" = \{*\} ]]; then + + # A string in braces is evaluated. + + while _next_label "$subc" expl "$descr"; do + eval "$action[2,-2]" && ret=0 + done + (( ret )) && alwopt=${alwopt:-yes} + 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[@]" && ret=0 + done + (( ret )) && alwopt=${alwopt:-yes} + tried=yes + else + + # Otherwise we call it with the description-arguments. + + eval "action=( $action )" + while _next_label "$subc" expl "$descr"; do + "$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}" && ret=0 + done + (( ret )) && alwopt=${alwopt:-yes} + tried=yes + fi + fi + fi + done + fi + if _requested options && + [[ -z "$hasopts" && + -z "$matched" && + ( -z "$aret" || "$PREFIX" = "$origpre" ) ]] && + { ! zstyle -T ":completion:${oldcontext%:*}:options" prefix-needed || + [[ "$origpre" = [-+]* || -z "$aret$mesg$tried" ]] } ; then + local prevpre="$PREFIX" previpre="$IPREFIX" prevcontext="$curcontext" + + curcontext="${oldcontext%:*}:options" + + hasopts=yes + + PREFIX="$origpre" + IPREFIX="$origipre" + + if [[ -z "$alwopt" || -z "$tried" || "$alwopt" = arg ]] && + comparguments -s single; then + + if [[ "$single" = direct ]]; then + _all_labels options expl option \ + compadd -QS '' - "${PREFIX}${SUFFIX}" + elif [[ -z "$optarg" && "$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=( "${(@M)tmp1:#${PREFIX[1]}*}" ) + + [[ "$single" = next ]] && + tmp1=( "${(@)tmp1:#[-+]${PREFIX[-1]}((#e)|:*)}" ) + + [[ "$PREFIX" != --* ]] && tmp1=( "${(@)tmp1:#--*}" ) + tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" ) + tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" ) + tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" ) + + _describe -O option \ + tmp1 tmp2 -Q -S '' -- \ + tmp3 -Q + + [[ -n "$optarg" && "$single" = next && nm -eq $compstate[nmatches] ]] && + _all_labels options expl option \ + compadd -Q - "${PREFIX}${SUFFIX}" + + 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" + curcontext="$prevcontext" + fi + [[ -n "$tried" && "${${alwopt:+$origpre}:-$PREFIX}" != [-+]* ]] && break + done + if [[ -n "$opts" && -z "$aret" && + -z "$matched" && + ( -z "$tried" || -n "$alwopt" ) && + 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" + + if [[ -n "$aret" ]]; then + [[ -n $rawret ]] && return 300 + +### Returning non-zero would allow the calling function to add its own +### completions if we generated only options and have to use a ->state +### action. But if that then doesn't generate matches, the calling +### function's return value would be wrong unless it compares +### $compstate[nmatches] to its previous value. Ugly. +### +### return 1 + else + [[ -n "$noargs" && nm -eq "$compstate[nmatches]" ]] && _message "$noargs" + fi + # Set the return value. + + [[ nm -ne "$compstate[nmatches]" ]] +else + return 1 +fi -- cgit 1.4.1