1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-10-03 01:11:37 +02:00
zsh/Completion/Base/Utility/_arguments

465 lines
13 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 rawret optarg singopt alwopt
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" = [^/]*/* ]] && name="$PWD/$name"
name="_args_cache_${name}"
name="${name//[^a-zA-Z0-9_]/_}"
if (( ! ${(P)+name} )); then
local iopts sopts pattern tmpo dir 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 into
# 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 tab after the
# option up to the end.
lopts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$(_call_program options ${~words[1]} --help 2>&1)//\[--/
--}:#[ ]#-*}//,/
}}:#[ ]#--*}#*--}%%[] ]*}:#}")
# Remove options also described by user-defined specs.
tmp=()
for opt in "${(@)${(@)lopts:#--}%%\=*}"; do
# Using (( ... )) gives a parse error.
let "$tmpargv[(I)(|\([^\)]#\))(|\*)${opt}(|[-+]|=(|-))(|\[*\])(|:*)]" ||
tmp=( "$tmp[@]" "$lopts[(r)$opt(|=*)]" )
done
lopts=( "$tmp[@]" )
# 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.
# The last one matches all options; the `special' description and action
# makes those options be completed without an argument description.
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}}"
if [[ "$pattern" = *\(-\) ]]; then
pattern="$pattern[1,-4]"
dir=-
else
dir=
fi
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 to get an
# optional argument.
tmpo=("${(@M)tmp:#*\[\=*}")
if (( $#tmpo )); then
tmp=("${(@)tmp:#*\[\=*}")
if [[ "$descr" = :\=* ]]; then
for opt in "$tmpo[@]"; do
cache=( "$cache[@]"
"${${opt%%\=*}//[^a-zA-Z0-9-]}=::${(L)${opt%\]}#*\=}: " )
done
else
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-zA-Z0-9-]}")
if [[ "$descr" = ::* ]]; then
cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" )
else
cache=( "$cache[@]" "${(@)^tmpo}=${dir}:${descr}" )
fi
fi
fi
# Descriptions with `=': mandatory argument.
tmpo=("${(@M)tmp:#*\=*}")
if (( $#tmpo )); then
tmp=("${(@)tmp:#*\=*}")
if [[ "$descr" = :\=* ]]; then
for opt in "$tmpo[@]"; do
cache=( "$cache[@]"
"${${opt%%\=*}//[^a-zA-Z0-9-]}=:${(L)${opt%\]}#*\=}: " )
done
else
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" )
fi
fi
# Everything else is just added as an option without arguments or
# as described by $descr.
if (( $#tmp )); then
tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
if [[ -n "$descr" && "$descr" != ': : ' ]]; then
cache=( "$cache[@]" "${(@)^tmp}${descr}" )
else
cache=( "$cache[@]" "$tmp[@]" )
fi
fi
done
set -A "$name" "${(@)cache:# #}"
fi
set -- "$tmpargv[@]" "${(@P)name}"
fi
subopts=()
singopt=()
while [[ "$1" = -(O*|[CRWsw]) ]]; do
case "$1" in
-C) usecc=yes; shift ;;
-O) subopts=( "${(@P)2}" ); shift 2 ;;
-O*) subopts=( "${(@P)${1[3,-1]}}" ); shift ;;
-R) rawret=yes; shift;;
-w) optarg=yes; shift;;
-s) singopt=(-s); shift;;
-W) alwopt=arg; shift;;
esac
done
[[ "$PREFIX" = [-+] ]] && alwopt=arg
zstyle -s ":completion:${curcontext}:options" auto-description autod
if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then
local action noargs aret expl local tried ret=1
local next direct odirect equal single matcher matched ws tmp1 tmp2 tmp3
local opts subc tc prefix suffix descrs actions subcs anum
local origpre="$PREFIX" origipre="$IPREFIX" nm="$compstate[nmatches]"
if comparguments -D descrs actions subcs; then
if comparguments -O next direct odirect equal; then
opts=yes
_tags "$subcs[@]" options
else
_tags "$subcs[@]"
fi
else
if comparguments -a; then
noargs='no more arguments'
else
noargs='no arguments'
fi
if comparguments -O next direct odirect equal; then
opts=yes
_tags options
elif [[ $? -eq 2 ]]; then
compadd -Q - "${PREFIX}${SUFFIX}"
return 0
else
_message "$noargs"
return 1
fi
fi
comparguments -M matcher
context=()
state=()
while true; do
while _tags; do
anum=1
if [[ -z "$tried" ]]; then
while [[ anum -le $#descrs ]]; do
action="$actions[anum]"
descr="$descrs[anum]"
subc="$subcs[anum++]"
if [[ -n "$matched" ]] || _requested "$subc"; then
curcontext="${oldcontext%:*}:$subc"
_description "$subc" expl "$descr"
if [[ "$action" = \=\ * ]]; then
action="$action[3,-1]"
words=( "$subc" "$words[@]" )
(( CURRENT++ ))
fi
if [[ "$action" = -\>* ]]; then
action="${${action[3,-1]##[ ]#}%%[ ]#}"
if (( ! $state[(I)$action] )); then
comparguments -W line opt_args
state=( "$state[@]" "$action" )
if [[ -n "$usecc" ]]; then
curcontext="${oldcontext%:*}:$subc"
else
context=( "$context[@]" "$subc" )
fi
compstate[restore]=''
aret=yes
fi
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.
_message -e "$subc" "$descr"
mesg=yes
tried=yes
alwopt=${alwopt:-yes}
elif [[ "$action" = \(\(*\)\) ]]; then
# ((...)) contains literal strings with descriptions.
eval ws\=\( "${action[3,-3]}" \)
_describe -t "$subc" "$descr" ws -M "$matcher" "$subopts[@]" ||
alwopt=${alwopt:-yes}
tried=yes
elif [[ "$action" = \(*\) ]]; then
# Anything inside `(...)' is added directly.
eval ws\=\( "${action[2,-2]}" \)
_all_labels "$subc" expl "$descr" compadd "$subopts[@]" -a - ws ||
alwopt=${alwopt:-yes}
tried=yes
elif [[ "$action" = \{*\} ]]; then
# A string in braces is evaluated.
while _next_label "$subc" expl "$descr"; do
eval "$action[2,-2]" && ret=0
done
(( ret )) && alwopt=${alwopt:-yes}
tried=yes
elif [[ "$action" = \ * ]]; then
# If the action starts with a space, we just call it.
eval "action=( $action )"
while _next_label "$subc" expl "$descr"; do
"$action[@]" && ret=0
done
(( ret )) && alwopt=${alwopt:-yes}
tried=yes
else
# Otherwise we call it with the description-arguments.
eval "action=( $action )"
while _next_label "$subc" expl "$descr"; do
"$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}" && ret=0
done
(( ret )) && alwopt=${alwopt:-yes}
tried=yes
fi
fi
fi
done
fi
if _requested options &&
[[ -z "$hasopts" &&
-z "$matched" &&
( -z "$aret" || "$PREFIX" = "$origpre" ) ]] &&
{ ! zstyle -T ":completion:${oldcontext%:*}:options" prefix-needed ||
[[ "$origpre" = [-+]* || -z "$aret$mesg$tried" ]] } ; then
local prevpre="$PREFIX" previpre="$IPREFIX" prevcontext="$curcontext"
curcontext="${oldcontext%:*}:options"
hasopts=yes
PREFIX="$origpre"
IPREFIX="$origipre"
if [[ -z "$alwopt" || -z "$tried" || "$alwopt" = arg ]] &&
comparguments -s single; then
if [[ "$single" = direct ]]; then
_all_labels options expl option \
compadd -QS '' - "${PREFIX}${SUFFIX}"
elif [[ -z "$optarg" && "$single" = next ]]; then
_all_labels options expl option \
compadd -Q - "${PREFIX}${SUFFIX}"
elif [[ "$single" = equal ]]; then
_all_labels options expl option \
compadd -QqS= - "${PREFIX}${SUFFIX}"
else
tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" )
[[ "$PREFIX" = [-+]* ]] && tmp1=( "${(@M)tmp1:#${PREFIX[1]}*}" )
[[ "$single" = next ]] &&
tmp1=( "${(@)tmp1:#[-+]${PREFIX[-1]}((#e)|:*)}" )
[[ "$PREFIX" != --* ]] && tmp1=( "${(@)tmp1:#--*}" )
tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" )
tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" )
tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )
_describe -O option \
tmp1 tmp2 -Q -S '' -- \
tmp3 -Q
[[ -n "$optarg" && "$single" = next && nm -eq $compstate[nmatches] ]] &&
_all_labels options expl option \
compadd -Q - "${PREFIX}${SUFFIX}"
fi
single=yes
else
next=( "$next[@]" "$odirect[@]" )
_describe -O option \
next -Q -M "$matcher" -- \
direct -QS '' -M "$matcher" -- \
equal -QqS= -M "$matcher"
fi
PREFIX="$prevpre"
IPREFIX="$previpre"
curcontext="$prevcontext"
fi
[[ -n "$tried" && "${${alwopt:+$origpre}:-$PREFIX}" != [-+]* ]] && break
done
if [[ -n "$opts" && -z "$aret" &&
-z "$matched" &&
( -z "$tried" || -n "$alwopt" ) &&
nm -eq compstate[nmatches] ]]; then
PREFIX="$origpre"
IPREFIX="$origipre"
prefix="${PREFIX#*\=}"
suffix="$SUFFIX"
PREFIX="${PREFIX%%\=*}"
SUFFIX=''
compadd -M "$matcher" -D equal - "${(@)equal%%:*}"
if [[ $#equal -eq 1 ]]; then
PREFIX="$prefix"
SUFFIX="$suffix"
IPREFIX="${IPREFIX}${equal[1]%%:*}="
matched=yes
comparguments -L "${equal[1]%%:*}" descrs actions subcs
_tags "$subcs[@]"
continue
fi
fi
break
done
[[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext"
if [[ -n "$aret" ]]; then
[[ -n $rawret ]] && return 300
### Returning non-zero would allow the calling function to add its own
### completions if we generated only options and have to use a ->state
### action. But if that then doesn't generate matches, the calling
### function's return value would be wrong unless it compares
### $compstate[nmatches] to its previous value. Ugly.
###
### return 1
else
[[ -n "$noargs" && nm -eq "$compstate[nmatches]" ]] && _message "$noargs"
fi
# Set the return value.
[[ nm -ne "$compstate[nmatches]" ]]
else
return 1
fi