1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-11-19 21:44:11 +01:00
zsh/Completion/Base/_arguments
1999-08-19 11:18:10 +00:00

389 lines
9.0 KiB
Plaintext

#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