mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-04 22:51:42 +02:00
298 lines
7.5 KiB
Text
298 lines
7.5 KiB
Text
#autoload
|
|
|
|
# Complete the arguments of the current command according to the
|
|
# descriptions given as arguments to this function.
|
|
|
|
setopt localoptions extendedglob
|
|
|
|
local long cmd="$words[1]" descr
|
|
|
|
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]}
|
|
if [[ "$name" != /* ]]; then
|
|
tmp="$PWD/$name"
|
|
fi
|
|
name="_args_cache_${name}"
|
|
name="${name//[^a-zA-Z0-9_]/_}"
|
|
|
|
if (( ! ${(P)+name} )); then
|
|
local iopts sopts pattern tmpo cur opt 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)$(${~words[1]} --help 2>&1)//\[--/
|
|
--}:#[ ]#-*}//,/
|
|
}}:#[ ]#--*}#*--}%%[, ]*}:#}")
|
|
|
|
# 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-]}")
|
|
|
|
if [[ "$descr" = ::* ]]; then
|
|
cache=( "$cache[@]" "${(@)^tmpo}=${descr[2,-1]}" )
|
|
else
|
|
cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
|
|
fi
|
|
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
|
|
|
|
if comparguments -i "$compconfig[autodescribe_options]" "$@"; then
|
|
local nm="$compstate[nmatches]" action noargs aret expl local
|
|
local next direct odirect equal single match matched ws tmp1 tmp2
|
|
|
|
if ! comparguments -D descr action; then
|
|
if comparguments -a; then
|
|
noargs='no more arguments'
|
|
else
|
|
noargs='no arguments'
|
|
fi
|
|
fi
|
|
|
|
while true; do
|
|
|
|
if [[ -z "$noargs" || -n "$matched" ]]; then
|
|
_description expl "$descr"
|
|
|
|
if [[ "$action" = -\>* ]]; then
|
|
comparguments -W line opt_args
|
|
state="${${action[3,-1]##[ ]#}%%[ ]#}"
|
|
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"
|
|
_message "$descr"
|
|
break
|
|
|
|
elif [[ "$action" = \(\(*\)\) ]]; then
|
|
|
|
# ((...)) contains literal strings with descriptions.
|
|
|
|
eval ws\=\( "${action[3,-3]}" \)
|
|
|
|
_describe -c "$cmd" "$descr" ws -M "$match"
|
|
|
|
elif [[ "$action" = \(*\) ]]; then
|
|
|
|
# Anything inside `(...)' is added directly.
|
|
|
|
compadd "$expl[@]" - ${=action[2,-2]}
|
|
elif [[ "$action" = \{*\} ]]; then
|
|
|
|
# A string in braces is evaluated.
|
|
|
|
eval "$action[2,-2]"
|
|
|
|
elif [[ "$action" = \ * ]]; then
|
|
|
|
# If the action starts with a space, we just call it.
|
|
|
|
${(e)=~action}
|
|
else
|
|
|
|
# Otherwise we call it with the description-arguments built above.
|
|
|
|
action=( $=action )
|
|
${(e)action[1]} "$expl[@]" ${(e)~action[2,-1]}
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$matched" ]] &&
|
|
comparguments -O next direct odirect equal &&
|
|
[[ ( ( nm -eq compstate[nmatches] || -n "$noargs" ) && -z "$aret" ) ||
|
|
-z "$compconfig[option_prefix]" ||
|
|
"$compconfig[option_prefix]" = *\!${cmd}* ||
|
|
"$PREFIX" = [-+]* ]]; then
|
|
|
|
comparguments -M match
|
|
|
|
if comparguments -s single; then
|
|
|
|
_description 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[@]" )
|
|
tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )
|
|
|
|
_describe -o -c "$cmd" option tmp1 tmp2 -Q -S ''
|
|
fi
|
|
single=yes
|
|
else
|
|
next=( "$next[@]" "$odirect[@]" )
|
|
_describe -o -c "$cmd" option \
|
|
next -Q -M "$match" -- \
|
|
direct -QS '' -M "$match" -- \
|
|
equal -QqS= -M "$match"
|
|
fi
|
|
|
|
if [[ nm -eq compstate[nmatches] && -z "$aret" &&
|
|
( ( -z "$single" && "$PREFIX" = [-+]*\=* ) ||
|
|
"$PREFIX" = --* ) ]]; then
|
|
|
|
local prefix suffix
|
|
|
|
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
|
|
continue
|
|
fi
|
|
fi
|
|
fi
|
|
break
|
|
done
|
|
|
|
[[ -n "$aret" ]] && return 300
|
|
|
|
[[ -n "$noargs" ]] && _message "$noargs"
|
|
|
|
# Set the return value.
|
|
|
|
[[ nm -ne "$compstate[nmatches]" ]]
|
|
|
|
else
|
|
return 1
|
|
fi
|