diff options
Diffstat (limited to 'Completion/Base/_arguments')
-rw-r--r-- | Completion/Base/_arguments | 519 |
1 files changed, 344 insertions, 175 deletions
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments index bcdbde373..12ff025a1 100644 --- a/Completion/Base/_arguments +++ b/Completion/Base/_arguments @@ -5,7 +5,7 @@ setopt localoptions extendedglob -local long args rest ws cur nth def nm expl descr action opt arg tmp +local args rest ws cur nth def nm expl descr action opt arg tmp local single uns ret=1 soptseq soptseq1 sopts prefix line local beg optbeg argbeg nargbeg inopt fromrest @@ -22,7 +22,7 @@ if [[ "$*" != "$_args_cache_descr" ]]; then unset _args_cache_{opts,dopts,odopts,oneshot} typeset -gA _args_cache_{opts,dopts,odopts,oneshot} - unset _args_cache_{long,single,rest,args,sopts,soptseq,soptseq1} + unset _args_cache_{long,longcmd,single,rest,args,sopts,soptseq,soptseq1} # See if we are using single-letter options. @@ -35,10 +35,149 @@ if [[ "$*" != "$_args_cache_descr" ]]; then nth=$argv[(I)--] if (( nth )); then - _args_cache_long=( "${(@)argv[nth+1,-1]}" ) - _args_cache_long_nth=$(( nth - 1 )) - else - _args_cache_long=() + local tmpargv + + if [[ nth -eq 1 ]]; then + tmpargv=() + else + tmpargv=( "${(@)argv[1,nth-1]}" ) + fi + + if [[ "$words[1]" = /* ]]; then + tmp="$words[1]" + else + tmp="$PWD/$words[1]" + fi + + if [[ "$tmp" != "$_args_cache_longcmd" ]]; then + local iopts pattern tmpo + typeset -U lopts + + _args_cache_longcmd="$tmp" + + # We have to build the long-option cache anew, get the `-i' and + # `-s' options. + + 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)$("$words[1]" --help 2>&1)//\[--/ +--}:#[ ]#-*}//,/ +}}:#[ ]#--*}#*--}%%[, ]*}:#}") + + # Now remove all ignored options ... + + while (( $#iopts )); do + lopts=( ${lopts:#$~iopts[1]} ) + shift iopts + done + + # ... and add "same" options + + while (( $#sopts )); do + lopts=( $lopts ${opts/$sopts[1]/$sopts[2]} ) + shift 2 sopts + done + + # Then we walk through the descriptions plus a few builtin ones. + + set -- "${(@)argv[nth+1,-1]}" '*=FILE*:file:_files' \ + '*=(DIR|PATH)*:directory:_files -/' '*:unknown:' + + 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 + _args_cache_long=( "$_args_cache_long[@]" + "${(@)^tmpo}=${descr}" ) + else + _args_cache_long=( "$_args_cache_long[@]" + "${(@)^tmpo}=:${descr}" ) + fi + fi + + # Descriptions with `=': mandatory argument. + + tmpo=("${(@M)tmp:#*\=*}") + if (( $#tmpo )); then + tmp=("${(@)tmp:#*\=*}") + tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") + + if [[ "$descr" = ::* ]]; then + _args_cache_long=( "$_args_cache_long[@]" + "${(@)^tmpo}=${descr[2,-1]}" ) + else + _args_cache_long=( "$_args_cache_long[@]" + "${(@)^tmpo}=${descr}" ) + fi + fi + + # Everything else is just added as a option without arguments. + + if (( $#tmp )); then + tmp=("${(@)tmp//[^a-zA-Z0-9-]}") + _args_cache_long=( "$_args_cache_long[@]" "$tmp[@]" ) + fi + done + fi + + set -- "$tmpargv[@]" "$_args_cache_long[@]" fi # Now parse the arguments... @@ -47,51 +186,71 @@ if [[ "$*" != "$_args_cache_descr" ]]; then nth=1 while (( $# )); do - # This describes a one-shot option. + # Description for both the `-foo' and `+foo' form? - if [[ "$1" = [-+]* ]]; then - if [[ "$1" = *:* ]]; then + if [[ "$1" = (\*|)(-+|+-)* ]]; then - # If the option name ends in a `-', the first argument comes - # directly after the option, if it ends in a `+', the first - # argument *may* come directly after the option, otherwise it - # is in the next word. + # With a `*' at the beginning, the option may appear more than + # once. - if [[ "$1" = [^:]##-:* ]]; then - tmp="${${1%%:*}[1,-2]}" - _args_cache_dopts[$tmp]="${1#*:}" - elif [[ "$1" = [^:]##+:* ]]; then - tmp="${${1%%:*}[1,-2]}" - _args_cache_odopts[$tmp]="${1#*:}" - else - tmp="${1%%:*}" - _args_cache_opts[$tmp]="${1#*:}" - fi + if [[ "$1" = \** ]]; then + tmp="${1[4,-1]%%:*}" + [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]" + unset "_args_cache_oneshot[-$tmp]" "_args_cache_oneshot[+$tmp]" else - tmp="$1" - _args_cache_opts[$tmp]='' + tmp="${1[3,-1]%%:*}" + [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]" + _args_cache_oneshot[-$tmp]=yes + _args_cache_oneshot[+$tmp]=yes fi - _args_cache_oneshot[$tmp]=yes - elif [[ "$1" = \*[-+]* ]]; then - - # The same for options that may appear more than once. - - if [[ "$1" = *:* ]]; then - if [[ "$1" = [^:]##-:* ]]; then - tmp="${${1[2,-1]%%:*}[1,-2]}" - _args_cache_dopts[$tmp]="${1#*:}" - elif [[ "$1" = [^:]##+:* ]]; then - tmp="${${1[2,-1]%%:*}[1,-2]}" - _args_cache_odopts[$tmp]="${1#*:}" - else - tmp="${1[2,-1]%%:*}" - _args_cache_opts[$tmp]="${1#*:}" - fi + + # If the option name ends in a `-', the first argument comes + # directly after the option, if it ends in a `+', the first + # argument *may* come directly after the option, otherwise it + # is in the next word. + + if [[ "$1" = [^:]##-:* ]]; then + _args_cache_dopts[-$tmp]="${1#*:}" + _args_cache_dopts[+$tmp]="${1#*:}" + elif [[ "$1" = [^:]##[+=]:* ]]; then + _args_cache_odopts[-$tmp]="${1#*:}" + _args_cache_odopts[+$tmp]="${1#*:}" + elif [[ "$1" = *:* ]]; then + _args_cache_opts[-$tmp]="${1#*:}" + _args_cache_opts[+$tmp]="${1#*:}" + else + _args_cache_opts[-$tmp]='' + _args_cache_opts[+$tmp]='' + fi + elif [[ "$1" = (\*|)[-+]* ]]; then + + # With a `*' at the beginning, the option may appear more than + # once. + + if [[ "$1" = \** ]]; then + tmp="${1[2,-1]%%:*}" + [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]" + unset "_args_cache_oneshot[$tmp]" + else + tmp="${1%%:*}" + [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]" + _args_cache_oneshot[$tmp]=yes + fi + + # If the option name ends in a `-', the first argument comes + # directly after the option, if it ends in a `+', the first + # argument *may* come directly after the option, otherwise it + # is in the next word. + + if [[ "$1" = [^:]##-:* ]]; then + _args_cache_dopts[$tmp]="${1#*:}" + elif [[ "$1" = [^:]##[+=]:* ]]; then + _args_cache_odopts[$tmp]="${1#*:}" + elif [[ "$1" = *:* ]]; then + _args_cache_opts[$tmp]="${1#*:}" else - tmp="${1[2,-1]}" _args_cache_opts[$tmp]='' fi - unset "_args_cache_oneshot[$tmp]" elif [[ "$1" = \*::* ]]; then # This is `*:...', describing `all other arguments', with argument @@ -131,7 +290,7 @@ if [[ "$*" != "$_args_cache_descr" ]]; then _args_cache_soptseq='' _args_cache_soptseq1='' fi - _args_cache_sopts="${(@j::)${(@M)${(@k)_args_cache_opts}:#[-+]?}#[-+]}${(@j::)${(@M)${(@k)_args_cache_dopts}:#[-+]?}#[-+]}${(@j::)${(@M)${(@k)_args_cache_odopts}:#[-+]?}#[-+]}" + _args_cache_sopts="${(@j::)${(@)${(@M)${=:-${(k)_args_cache_opts} ${(k)_args_cache_dopts} ${(k)_args_cache_odopts}}:#[-+]?(|=)}#?}%\=}" else _args_cache_soptseq='' _args_cache_soptseq1='' @@ -149,9 +308,6 @@ dopts=( "${(@kv)_args_cache_dopts}" ) odopts=( "${(@kv)_args_cache_odopts}" ) oneshot=( "${(@kv)_args_cache_oneshot}" ) single="$_args_cache_single" -long=( "$_args_cache_long[@]" ) - -argv=( "${(@)argv[1,_args_cache_long_nth]}" ) # Parse the command line... @@ -200,16 +356,10 @@ while [[ cur -gt 0 ]]; do fi elif [[ -z "$def" ]]; then - # If it is empty, and the word starts with `--' and we should - # complete long options, just ignore this word, otherwise make sure - # we test for options below and handle normal arguments. + # Make sure we test for options below and handle normal arguments. - if [[ $#long -eq 0 || "$ws[1]" != --* ]]; then - opt=yes - arg=yes - else - def='' - fi + opt=yes + arg=yes fi if [[ -n "$opt" ]]; then @@ -287,7 +437,7 @@ while [[ cur -gt 0 ]]; do fi fi if [[ -n "$opt" && $#odopts -ne 0 ]]; then - tmp=( "${(@k)odopts}" ) + tmp=( "${(@k)odopts%\=}" ) while (( $#tmp )); do if [[ -n "$sopts" && $tmp[1] = [-+]? ]]; then if [[ "$ws[1]" = ${tmp[1][1]}${~soptseq}${tmp[1][2]}* ]]; then @@ -303,6 +453,7 @@ while [[ cur -gt 0 ]]; do if (( $#tmp )); then opt='' def="$odopts[$tmp[1]]" + [[ -z "$def" ]] && def="$odopts[$tmp[1]=]" optbeg="$beg" argbeg="$beg" inopt=yes @@ -362,132 +513,101 @@ done # Now generate the matches. -if [[ $#long -ne 0 && "$PREFIX" = --* ]]; then +nm="$compstate[nmatches]" - # If the current words starts with `--' and we should use long - # options, just call... +if [[ -z "$def" || "$def" = :* ]]; then + local pre="$PREFIX" - _long_options "$long[@]" + uns='' -else - - nm="$compstate[nmatches]" + # We either don't have a description for an argument of an option + # or we have a description for a optional argument. - if [[ -z "$def" || "$def" = :* ]]; then - local pre="$PREFIX" - - uns='' + if [[ -z "$def" ]]; then - # We either don't have a description for an argument of an option - # or we have a description for a optional argument. + # If we have none at all, use the one for this argument position. + def="$args[nth]" if [[ -z "$def" ]]; then + def="$rest" + optbeg="$nargbeg" + argbeg="$nargbeg" + fromrest=yes + fi + fi - # If we have none at all, use the one for this argument position. + # In any case, we have to complete option names here, but we may + # be in a string that starts with an option name and continues with + # the first argument, test that (again, two loops). - def="$args[nth]" - if [[ -z "$def" ]]; then - def="$rest" - optbeg="$nargbeg" - argbeg="$nargbeg" - fromrest=yes - fi - fi + opt=yes + if (( $#dopts )); then - # In any case, we have to complete option names here, but we may - # be in a string that starts with an option name and continues with - # the first argument, test that (again, two loops). + # Get the option names. - opt=yes - if (( $#dopts )); then + tmp=( "${(@k)dopts}" ) + prefix="$PREFIX" + while (( $#tmp )); do + if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then + def="$dopts[$tmp[1]]" + opt='' + uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}" + break + elif compset -P "$tmp[1]"; then - # Get the option names. + # The current string starts with the option name, so ignore + # that and complete the rest of the string. - tmp=( "${(@k)dopts}" ) - prefix="$PREFIX" - while (( $#tmp )); do - if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then - def="$dopts[$tmp[1]]" - opt='' - uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}" - break - elif compset -P "$tmp[1]"; then + def="$dopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi + if [[ -n "$opt" && $#odopts -ne 0 ]]; then + tmp=( "${(@k)odopts}" ) + prefix="$PREFIX" + while (( $#tmp )); do + if [[ -n "$sopts" && $tmp[1] = [-+]?(|=) ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}${tmp[1][3]}"; then + def="$odopts[$tmp[1]]" + opt='' + uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}" + break + elif compset -P "$tmp[1]"; then + def="$odopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi - # The current string starts with the option name, so ignore - # that and complete the rest of the string. + [[ -n "$sopts" && -n "$opt" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]] && \ + uns="${PREFIX[2,-1]}" + + if [[ -n "$uns" ]]; then + uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}" + tmp=( + "opts[${(@)^opts[(I)${pre[1]}[$uns]]}]" + "dopts[${(@)^dopts[(I)${pre[1]}[$uns]]}]" + "odopts[${(@)^odopts[(I)${pre[1]}[$uns](|=)]}]" + ) + (( $#tmp )) && unset "$tmp[@]" + fi - def="$dopts[$tmp[1]]" - opt='' - break - fi - shift 1 tmp - done - fi - if [[ -n "$opt" && $#odopts -ne 0 ]]; then - tmp=( "${(@k)odopts}" ) - prefix="$PREFIX" - while (( $#tmp )); do - if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then - def="$odopts[$tmp[1]]" - opt='' - uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}" - break - elif compset -P "$tmp[1]"; then - def="$odopts[$tmp[1]]" - opt='' - break - fi - shift 1 tmp - done - fi + # If we aren't in an argument directly after a option name, all option + # names are possible matches. - [[ -n "$sopts" && -n "$opt" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]] && \ - uns="${PREFIX[2,-1]}" - - if [[ -n "$uns" ]]; then - uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}" - tmp=( - "opts[${(@)^opts[(I)${pre[1]}[$uns]]}]" - "dopts[${(@)^dopts[(I)${pre[1]}[$uns]]}]" - "odopts[${(@)^odopts[(I)${pre[1]}[$uns]]}]" - ) - (( $#tmp )) && unset "$tmp[@]" - fi + [[ -z "$opt" || ( "$def" = \** && + ( -z "$fromrest" || CURRENT -ne argbeg+1 ) ) ]] && opt='' +else + opt='' +fi - if [[ -n "$opt" && ( "$def" != \** || - ( -n "$fromrest" && CURRENT -eq argbeg+1 ) ) ]]; then - - # We aren't in an argument directly after a option name, so - # all option names are possible matches. - - if [[ "$compconfig[option_prefix]" != *(short|all)* || - "$PREFIX" = [-+]* ]]; then - _description expl option - if [[ -n "$sopts" && -n "$PREFIX" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]]; then - if [[ "$PREFIX" = [-+]${~soptseq1} ]]; then - compadd "$expl[@]" -Q -M 'r:|_=* r:|=*' \ - -y "( ${(j: :)${(@M)${(@k)opts}:#[-+]?}} ${(j: :)${(@M)${(@k)dopts}:#[-+]?}} ${(j: :)${(@M)${(@k)odopts}:#[-+]?}} )" - \ - "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \ - "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \ - "${PREFIX}${(@k)^odopts[(I)${PREFIX[1]}?]#?}" && ret=0 - else - # The last option takes an argument in the next word. - compadd "$expl[@]" -Q -M 'r:|_=* r:|=*' - "${PREFIX}" && ret=0 - fi - else - compadd "$expl[@]" -Q -M 'r:|_=* r:|=*' - \ - "${(@k)opts}" "${(@k)odopts}" && ret=0 - compadd "$expl[@]" -QS '' -M 'r:|_=* r:|=*' - "${(@k)dopts}" && ret=0 - fi - fi - [[ $#long -ne 0 && - ( "$compconfig[option_prefix]" != *(long|all)* || - "$PREFIX" = --* ) ]] && \ - _long_options "$long[@]" && ret=0 - fi - fi +# Now add the matches from the description, if any. - # Now add the matches from the description, if any. +while true; do if [[ -n "$def" ]]; then @@ -503,20 +623,20 @@ else if [[ "$def" = ::* ]]; then def="$def[3,-1]" beg=$argbeg - else + else def="$def[2,-1]" beg=$optbeg - fi + fi - [[ beg -ge $#words ]] && beg=$(( $#words - 1 )) + [[ beg -ge $#words ]] && beg=$(( $#words - 1 )) - shift beg words - (( CURRENT -= beg )) + shift beg words + (( CURRENT -= beg )) - if [[ -n "$tmp" ]]; then + if [[ -n "$tmp" ]]; then tmp="$words[(ib:CURRENT:)${~tmp}]" [[ tmp -le $#words ]] && words=( "${(@)words[1,tmp-1]}" ) - fi + fi fi fi @@ -536,7 +656,7 @@ else # An empty action means that we should just display a message. _message "$descr" - return ret + break elif [[ "$action" = \(\(*\)\) ]]; then @@ -587,7 +707,56 @@ ${(r:beg:: :)nth%%:*} -- ${nth#*:}" fi fi - # Set the return value. + if [[ nm -eq compstate[nmatches] && $#_args_cache_long -ne 0 && + "$PREFIX" = --*=* ]]; then + local suffix + + tmp=( "${(@Mk)odopts:#--[^:]#\=}" ) + prefix="${PREFIX#*\=}" + suffix="$SUFFIX" + PREFIX="${PREFIX%%\=*}" + SUFFIX='' + compadd -M 'r:|[_-]=* r:|=*' -D tmp - "${(@)tmp%\=}" + + if [[ $#tmp -eq 1 ]]; then + def="$odopts[$tmp[1]]" + PREFIX="$prefix" + SUFFIX="$suffix" + IPREFIX="$tmp[1]" + continue + fi + fi + break +done + +# Probably add the option names. + +if [[ -n "$opt" && + ( nm -eq compstate[nmatches] || + -z "$compconfig[option_prefix]" || "$PREFIX" = [-+]* ) ]]; then + _description expl option + if [[ -n "$sopts" && -n "$PREFIX" && + "$PREFIX" = [-+]${~soptseq}[$sopts] ]]; then + if [[ "$PREFIX" = [-+]${~soptseq1} ]]; then + compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' \ + -y "( ${(j: :)${(@)${(@M)${=:-${(k)opts} ${(k)dopts} ${(k)odopts}}:#[-+]?(|=)}#?}%=} )" - \ + "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \ + "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \ + "${PREFIX}${(@)^${(@k)odopts[(I)${PREFIX[1]}?(|=)]#?}%=}" && ret=0 + else + # The last option takes an argument in the next word. - [[ nm -ne "$compstate[nmatches]" ]] + compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' - "${PREFIX}" && ret=0 + fi + else + compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' - \ + "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0 + compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' -qS= - \ + "${(@k)odopts[(I)*=]%=}" && ret=0 + compadd "$expl[@]" -QS '' -M 'r:|[_-]=* r:|=*' - "${(@k)dopts}" && ret=0 + fi fi + +# Set the return value. + +[[ nm -ne "$compstate[nmatches]" ]] |