1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-11-20 05:53:52 +01:00
zsh/Completion/Base/_arguments

355 lines
9.1 KiB
Plaintext

#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
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" != /* ]] && tmp="$PWD/$name"
name="_args_cache_${name}"
name="${name//[^a-zA-Z0-9_]/_}"
if (( ! ${(P)+name} )); then
local iopts sopts pattern tmpo 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 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)$(_call options ${~words[1]} --help 2>&1)//\[--/
--}:#[ ]#-*}//,/
}}:#[ ]#--*}#*--}%%[], ]*}:#}")
lopts=( "${(@)lopts:#--}" )
# 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.
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}}"
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
cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
else
cache=( "$cache[@]" "${(@)^tmpo}=:${descr}" )
fi
fi
# Descriptions with `=': mandatory argument.
tmpo=("${(@M)tmp:#*\=*}")
if (( $#tmpo )); then
tmp=("${(@)tmp:#*\=*}")
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
fi
# Everything else is just added as a option without arguments.
if (( $#tmp )); then
tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
cache=( "$cache[@]" "$tmp[@]" )
fi
done
eval "${name}=( \"\${(@)cache:# #}\" )"
fi
set -- "$tmpargv[@]" "${(@P)name}"
fi
subopts=()
while [[ "$1" = -(O*|C) ]]; do
case "$1" in
-C) usecc=yes; shift ;;
-O) subopts=( "${(@P)2}" ); shift 2 ;;
*) subopts=( "${(@P)1[3,-1]}" ); shift ;;
esac
done
zstyle -s ":completion:${curcontext}:options" auto-description autod
if (( $# )) && comparguments -i "$autod" "$@"; then
local nm="$compstate[nmatches]" action noargs aret expl local
local next direct odirect equal single match matched ws tmp1 tmp2 tmp3
local opts subc prefix suffix
local origpre="$PREFIX" origipre="$IPREFIX"
if comparguments -D descr action; then
comparguments -C subc
curcontext="${oldcontext%:*}:$subc"
if comparguments -O next direct odirect equal; then
opts=yes
_tags arguments options
else
_tags arguments
fi
else
if comparguments -a; then
noargs='no more arguments'
else
noargs='no arguments'
fi
comparguments -O next direct odirect equal || return 1
opts=yes
_tags options
fi
while true; do
while _tags; do
if [[ -n "$matched" ]] || _requested arguments; then
_description arguments expl "$descr"
if [[ "$action" = \=\ * ]]; then
action="$action[3,-1]"
words=( "$subc" "$words[@]" )
(( CURRENT++ ))
fi
if [[ "$action" = -\>* ]]; then
comparguments -W line opt_args
state="${${action[3,-1]##[ ]#}%%[ ]#}"
if [[ -n "$usecc" ]]; then
curcontext="${oldcontext%:*}:$subc"
else
context="$subc"
fi
compstate[restore]=''
aret=yes
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.
[[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
mesg="$descr"
elif [[ "$action" = \(\(*\)\) ]]; then
# ((...)) contains literal strings with descriptions.
eval ws\=\( "${action[3,-3]}" \)
_describe "$descr" ws -M "$match" "$subopts[@]"
elif [[ "$action" = \(*\) ]]; then
# Anything inside `(...)' is added directly.
_all_labels arguments expl "$descr" \
compadd "$subopts[@]" - ${=action[2,-2]}
elif [[ "$action" = \{*\} ]]; then
# A string in braces is evaluated.
while _next_label arguments expl "$descr"; do
eval "$action[2,-2]"
done
elif [[ "$action" = \ * ]]; then
# If the action starts with a space, we just call it.
eval "action=( $action )"
while _next_label arguments expl "$descr"; do
"$action[@]"
done
else
# Otherwise we call it with the description-arguments.
eval "action=( $action )"
while _next_label arguments expl "$descr"; do
"$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}"
done
fi
fi
fi
if [[ -z "$matched$hasopts" ]] && _requested options &&
{ ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
[[ "$origpre" = [-+]* ||
( -z "$aret$mesg" && nm -eq compstate[nmatches] ) ]] } ; then
local prevpre="$PREFIX" previpre="$IPREFIX"
hasopts=yes
PREFIX="$origpre"
IPREFIX="$origipre"
comparguments -M match
if comparguments -s single; then
_description options expl option
if [[ "$single" = direct ]]; then
compadd "$expl[@]" -QS '' - "${PREFIX}${SUFFIX}"
elif [[ "$single" = next ]]; then
compadd "$expl[@]" -Q - "${PREFIX}${SUFFIX}"
elif [[ "$single" = equal ]]; then
compadd "$expl[@]" -QqS= - "${PREFIX}${SUFFIX}"
else
tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" )
tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" )
tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" )
tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )
_describe -o option \
tmp1 tmp2 -Q -S '' -- \
tmp3 -Q
fi
single=yes
else
next=( "$next[@]" "$odirect[@]" )
_describe -o option \
next -Q -M "$match" -- \
direct -QS '' -M "$match" -- \
equal -QqS= -M "$match"
fi
PREFIX="$prevpre"
IPREFIX="$previpre"
fi
done
if [[ -n "$opts" && -z "$aret$matched$mesg" &&
nm -eq compstate[nmatches] ]]; then
PREFIX="$origpre"
IPREFIX="$origipre"
prefix="${PREFIX#*\=}"
suffix="$SUFFIX"
PREFIX="${PREFIX%%\=*}"
SUFFIX=''
compadd -M "$match" -D equal - "${(@)equal%%:*}"
if [[ $#equal -eq 1 ]]; then
PREFIX="$prefix"
SUFFIX="$suffix"
IPREFIX="${IPREFIX}${equal[1]%%:*}="
matched=yes
comparguments -L "${equal[1]%%:*}" descr action subc
curcontext="${oldcontext%:*}:$subc"
_tags arguments
continue
fi
fi
break
done
[[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext"
[[ -n "$aret" ]] && return 300
[[ -n "$mesg" ]] && _message "$mesg"
[[ -n "$noargs" ]] && _message "$noargs"
# Set the return value.
[[ nm -ne "$compstate[nmatches]" ]]
else
return 1
fi