diff options
Diffstat (limited to 'Completion/Base/_arguments')
-rw-r--r-- | Completion/Base/_arguments | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments new file mode 100644 index 000000000..5170acb84 --- /dev/null +++ b/Completion/Base/_arguments @@ -0,0 +1,388 @@ +#autoload + +# Complete the arguments of the current command according to the +# descriptions given as arguments to this function. + +local long args rest ws cur nth def nm expl descr action opt arg tmp + +# Associative arrays used to collect information about the options. + +typeset -A opts mopts dopts dmopts odopts odmopts + +# See if we support long options, too. + +nth=$argv[(I)--] +if (( nth )); then + long=( "${(@)argv[nth+1,-1]}" ) + argv=("${(@)argv[1,nth-1]}") +else + long=() +fi + +# Now parse the arguments... + +args=() +nth=1 +while (( $# )); do + + # This describes a one-shot option. + + 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. + + if [[ "$1" = [^:]##-:* ]]; then + dopts[${${1%%:*}[1,-2]}]="${1#*:}" + elif [[ "$1" = [^:]##+:* ]]; then + odopts[${${1%%:*}[1,-2]}]="${1#*:}" + else + opts[${1%%:*}]="${1#*:}" + fi + else + opts[$1]='' + fi + elif [[ "$1" = \*[-+]* ]]; then + + # The same for options that may appear more than once. + + if [[ "$1" = *:* ]]; then + if [[ "$1" = [^:]##-:* ]]; then + dmopts[${${1[2,-1]%%:*}[1,-2]}]="${1#*:}" + elif [[ "$1" = [^:]##+:* ]]; then + odmopts[${${1[2,-1]%%:*}[1,-2]}]="${1#*:}" + else + mopts[${1[2,-1]%%:*}]="${1#*:}" + fi + else + mopts[${1[2,-1]}]='' + fi + elif [[ "$1" = \*:* ]]; then + + # This is `*:...', describing `all other arguments'. + + rest="${1[3,-1]}" + elif [[ "$1" = :* ]]; then + + # This is `:...', describing `the next argument'. + + args[nth++]="${1#*:}" + else + + # And this is `n:...', describing the `n'th argument. + + args[${1%%:*}]="${1#*:}" + nth=$(( ${1%%:*} + 1 )) + fi + shift +done + +if [[ $#long -ne 0 && "$PREFIX" = --* ]]; then + + # If the current words starts with `--' and we should use long + # options, just call... + + _long_options "$long[@]" +else + + # Otherwise parse the command line... + + ws=( "${(@)words[2,-1]}" ) + cur=$(( CURRENT-2 )) + nth=1 + + # ...until the current word is reached. + + while [[ cur -gt 0 ]]; do + + # `def' holds the description for the option we are currently after. + # Check if the next argument for the option is optional. + + if [[ "$def" = :* ]]; then + opt=yes + else + opt='' + fi + arg='' + + # Remove one description/action pair from `def' if that isn't empty. + + if [[ -n "$def" ]]; then + if [[ "$def" = ?*:*:* ]]; then + def="${def#?*:*:}" + else + def='' + fi + else + + # 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. + + if [[ $#long -eq 0 || "$ws[1]" != --* ]]; then + opt=yes + arg=yes + else + def='' + fi + fi + + if [[ -n "$opt" ]]; then + + # `opt' was set above if we have to test if the word is an option. + # We first test for the simple options -- those without arguments or + # those whose arguments have to be given as separate words. + + if (( $+opts[$ws[1]] )); then + + # Options that may only be given once are removed from the + # associative array so that we are not offered them again. + + def="$opts[$ws[1]]" + unset "opts[$ws[1]]" + elif (( $+mopts[$ws[1]] )); then + def="$mopts[$ws[1]]" + else + + # If the word is none of the simple options, test for those + # whose first argument has to or may come directly after the + # option. This is done in four loops looking very much alike. + + if (( $#dopts )); then + + # First we get the option names. + + tmp=( "${(@k)dopts}" ) + + # Then we loop over them and see if the current word begins + # with one of the option names. + + while (( $#tmp )); do + [[ "$ws[1]" = ${tmp[1]}* ]] && break + shift 1 tmp + done + + if (( $#tmp )); then + + # It does. So use the description for it, but only from + # the second argument on, because we are searching the + # description for the next command line argument. + + opt='' + def="$dopts[$tmp[1]]" + unset "dopts[$tmp[1]]" + if [[ "$def" = ?*:*:* ]]; then + def="${def#?*:*:}" + else + def='' + fi + fi + fi + if [[ -n "$opt" && $#dmopts -ne 0 ]]; then + tmp=( "${(@k)dmopts}" ) + while (( $#tmp )); do + [[ "$ws[1]" = ${tmp[1]}* ]] && break + shift 1 tmp + done + + if (( $#tmp )); then + opt='' + def="$dmopts[$tmp[1]]" + if [[ "$def" = ?*:*:* ]]; then + def="${def#?*:*:}" + else + def='' + fi + fi + fi + if [[ -n "$opt" && $#odopts -ne 0 ]]; then + tmp=( "${(@k)odopts}" ) + while (( $#tmp )); do + [[ "$ws[1]" = ${tmp[1]}* ]] && break + shift 1 tmp + done + + if (( $#tmp )); then + opt='' + def="$odopts[$tmp[1]]" + unset "odopts[$tmp[1]]" + + # For options whose first argument *may* come after the + # option, we skip over the first description only if there + # is something after the option name on the line. + + if [[ "$ws[1]" != "$tmp[1]" ]]; then + if [[ "$def" = ?*:*:* ]]; then + def="${def#?*:*:}" + else + def='' + fi + fi + fi + fi + if [[ -n "$opt" && $#odmopts -ne 0 ]]; then + tmp=( "${(@k)odmopts}" ) + while (( $#tmp )); do + [[ "$ws[1]" = ${tmp[1]}* ]] && break + shift 1 tmp + done + + if (( $#tmp )); then + opt='' + def="$odmopts[$tmp[1]]" + if [[ "$ws[1]" != "$tmp[1]" ]]; then + if [[ "$def" = ?*:*:* ]]; then + def="${def#?*:*:}" + else + def='' + fi + fi + fi + fi + + # If we didn't find a matching option description and we were + # told to use normal argument descriptions, just increase + # our counter `nth'. + + if [[ -n "$opt" && -n "$arg" ]]; then + def='' + (( nth++ )) + fi + fi + fi + + shift 1 ws + (( cur-- )) + done + + # Now generate the matches. + + nm="$compstate[nmatches]" + + if [[ -z "$def" || "$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 [[ -z "$def" ]]; then + + # If we have none at all, use the one for this argument position. + + def="$args[nth]" + [[ -z "$def" ]] && def="$rest" + fi + + # In any case, we have to complete option names here, but we may + # be in a string that starts with an option names and continues with + # the first argument, test that (again, four loops). + + opt=yes + if (( $#dopts )); then + + # Get the option names. + + tmp=( "${(@k)dopts}" ) + while (( $#tmp )); do + if compset -P "$tmp[1]"; then + + # The current string starts with the option name, so ignore + # that and complete the rest of the string. + + def="$dopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi + if [[ -n "$opt" && $#dmopts -ne 0 ]]; then + tmp=( "${(@k)dmopts}" ) + while (( $#tmp )); do + if compset -P "$tmp[1]"; then + def="$dmopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi + if [[ -n "$opt" && $#odopts -ne 0 ]]; then + tmp=( "${(@k)odopts}" ) + while (( $#tmp )); do + if compset -P "$tmp[1]"; then + def="$odopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi + if [[ -n "$opt" && $#odmopts -ne 0 ]]; then + tmp=( "${(@k)odmopts}" ) + while (( $#tmp )); do + if compset -P "$tmp[1]"; then + def="$odmopts[$tmp[1]]" + opt='' + break + fi + shift 1 tmp + done + fi + if [[ -n "$opt" ]]; then + + # We aren't in an argument directly after a option name, so + # all option names are possible matches. + + _description expl option + compadd "$expl[@]" - "${(@k)opts}" "${(@k)mopts}" \ + "${(@k)dopts}" "${(@k)dmopts}" \ + "${(@k)odopts}" "${(@k)odmopts}" + fi + fi + + # Now add the matches from the description, if any. + + if [[ -n "$def" ]]; then + + # Ignore the leading colon describing optional arguments. + + [[ "$def" = :* ]] && def="$def[2,-1]" + + # Get the description and the action. + + descr="${def%%:*}" + action="${${def#*:}%%:*}" + + _description expl "$descr" + + if [[ -z "$action" ]]; then + + # An empty action means that we should just display a message. + _message "$descr" + return 1 + elif [[ "$action[1]" = \( ]]; then + + # Anything inside `(...)' is added directly. + + compadd "$expl[@]" - ${=action[2,-2]} + elif [[ "$action" = \ * ]]; then + + # If the action starts with a space, we just call it. + + $=action + else + + # Otherwise we call it with the description-arguments built above. + + action=( $=action ) + "$action[1]" "$expl[@]" "${(@)action[2,-1]}" + fi + fi + + # Set the return value. + + [[ nm -ne "$compstate[nmatches]" ]] +fi |