mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 21:44:11 +01:00
389 lines
9.0 KiB
Plaintext
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
|