1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-25 17:41:19 +02:00
zsh/Completion/Base/_long_options
1999-06-08 09:25:39 +00:00

345 lines
11 KiB
Text

#autoload
# This function tries to automatically complete long option names. For
# this it invokes the command from the line with the `--help' option
# and then parses the output to find possible option names, so you
# should be careful to make sure that this function is not called for
# a command that does not support this option.
#
# For options that get an argument after a `=', the function also tries
# to automatically find out what should be completed as the argument.
# The possible completions for option-arguments can be described with
# the arguments to this function. This is done by giving pairs of
# patterns and actions as consecutive arguments. The actions specify
# what should be done to complete arguments of those options that match
# the pattern. The action may be a list of words in brackets or in
# parentheses, separated by spaces. A list in brackets denotes
# possible values for an optional argument, a list in parentheses
# gives words to complete for mandatory arguments. If the action does
# not start with a bracket or parentheses, it should be the name of a
# command (probably with arguments) that should be invoked to complete
# after the equal sign. E.g.:
#
# _long_options '*\*' '(yes no)' \
# '*=FILE*' '_files' \
# '*=DIR*' '_files -/'
#
# This makes `yes' and `no' be completed as the argument of options
# whose description ends in a star, file names for options that
# contain the substring `=FILE' in the description, and paths for
# options whose description contains `=DIR'. Note that the last two
# patterns are not needed since this function always completes files
# for option descriptions containing `=FILE' and paths for option
# descriptions that contain `=DIR' or `=PATH'. These builtin patterns
# can be overridden by patterns given as arguments, though.
#
# This function accepts the following options:
#
# -t do completion only on words starting with two hyphens
#
# -i list of patterns. Options, matching these patterns, are ignored.
# The list may be given as a array name or as a literal list in braces.
# E.g. _long_options -i '(--(enable|disable)-FEATURE*)' will ignore
# --enable-FEATURE, that is listed in configure help output
#
# -s list of pattern/replacement pairs. The argument is the same as above.
# E.g. configure often lists only --enable but accepts both
# --enable and --disable options.
# _long_options -s '(#--enable- --disable)' will accept both forms.
#
# This function also accepts the `-X', `-J', and `-V' options which
# are given to `compadd'.
local opt expl group test i name action ret=1 tmp suffix iopts sopts
setopt extendedglob
# Get the options.
group=()
expl=()
if [[ $1 = -*~--* ]]; then
while getopts "J:V:X:ti:s:" opt; do
case "$opt" in
[JV]) group=("-$opt" "$OPTARG");;
X) expl=(-X "$OPTARG");;
t) test=yes;;
i) if [[ "$OPTARG[1]" = '(' ]]; then
iopts=( ${=OPTARG[2,-2]} )
else
iopts=( ${(P)${OPTARG}} )
fi
;;
s) if [[ "$OPTARG[1]" = '(' ]]; then
sopts=( ${=OPTARG[2,-2]} )
else
sopts=( ${(P)${OPTARG}} )
fi
;;
esac
done
shift OPTIND-1
fi
# Test if we are completing after `--' if we were asked to do so.
[[ -n "$test" && "$PREFIX" != --* ]] && return 1
# We cache the information about options and the command name, see if
# we can use the cache.
if [[ "$words[1]" = (.|..)/* ]]; then
tmp="$PWD/$words[1]"
else
tmp="$words[1]"
fi
if [[ "$tmp" != $_lo_cache_cmd ]]; then
# No, store the new command name and clear the old parameters.
_lo_cache_cmd="$tmp"
(( $+_lo_cache_actions )) && unset "$_lo_cache_names[@]" _lo_cache_actions _lo_cache_names
local opts pattern anum=1 tmpo str
typeset -U opts
# 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.
opts=("--${(@)^${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$("$words[1]" --help)}:#[ ]#-*}//,/
}}:#[ ]#--*}#*--}%%[, ]*}")
# Now remove all ignored options ...
while (($#iopts)) ; do
opts=( ${opts:#$~iopts[1]} )
shift iopts
done
# ... and add "same" options
while (($#sopts)) ; do
opts=( $opts ${opts/$sopts[1]/$sopts[2]} )
shift 2 sopts
done
# The interpretation of the options is completely table driven. We
# use the positional parameters we were given and a few standard
# ones. Then we loop through this table.
set -- "$@" '*=FILE*' '_files' '*=(DIR|PATH)*' '_files -/' '*' ''
while [[ $# -gt 1 ]]; do
# First, we get the pattern and the action to use and take them
# from the positional parameters.
pattern="$1"
action="$2"
shift 2
# 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)opts:##$~pattern}")
opts=("${(@)opts:##$~pattern}")
(( $#tmp )) || continue
# Now we collect the options for the pattern in an array. We also
# check if the options take an argument after a `=', and if this
# argument is optional. The name of the array built contains
# `_arg_' for mandatory arguments, `_optarg_' for optional
# arguments, and `_simple_' for options that don't get an
# argument. In `_lo_cache_names' we save the names of these
# arrays and in `_lo_cache_actions' the associated actions.
# If the action is a list of words in brackets, this denotes
# options that get an optional argument. If the action is a list
# of words in parentheses, the option has to get an argument.
# In both cases we just build the array name to use.
if [[ "$action[1]" = '[' ]]; then
name="_lo_cache_optarg_$anum"
elif [[ "$action[1]" = '(' ]]; then
name="_lo_cache_arg_$anum"
else
# If there are option strings with a `[=', we take make these
# get an optional argument...
tmpo=("${(@M)tmp:#*\[\=*}")
if (( $#tmpo )); then
# ...by removing them from the option list and storing them in
# an array.
tmp=("${(@)tmp:#*\[\=*}")
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
_lo_cache_names[anum]="_lo_cache_optarg_$anum"
_lo_cache_actions[anum]="$action"
eval "_lo_cache_optarg_${anum}=(\"\$tmpo[@]\")"
(( anum++ ))
fi
# Now we do the same for option strings containing `=', these
# are options getting an argument.
tmpo=("${(@M)tmp:#*\=*}")
if (( $#tmpo )); then
tmp=("${(@)tmp:#*\=*}")
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
_lo_cache_names[anum]="_lo_cache_arg_$anum"
_lo_cache_actions[anum]="$action"
eval "_lo_cache_arg_${anum}=(\"\$tmpo[@]\")"
(( anum++ ))
fi
# The name for the options without arguments, if any.
name="_lo_cache_simple_$anum"
fi
# Now filter out any option strings we don't like and stuff them
# in an array, if there are still some.
tmp=("${(@)${(@)tmp%%\=*}//[^a-zA-Z0-9-]}")
if (( $#tmp )); then
_lo_cache_names[anum]="$name"
_lo_cache_actions[anum]="$action"
eval "${name}=(\"\$tmp[@]\")"
(( anum++ ))
fi
done
fi
# We get the string from the line and and see if it already contains a
# equal sign.
str="$PREFIX$SUFFIX"
if [[ "$str" = *\=* ]]; then
# It contains a `=', now we ignore anything up to it, but first save
# the old contents of the special parameters we change.
local oipre opre osuf pre parto parta pat patflags anum=1
oipre="$IPREFIX"
opre="$PREFIX"
osuf="$SUFFIX"
pre="${str%%\=*}"
# Then we walk through the array names. For each array we test if it
# contains the option string. If so, we `invoke' the action stored
# with the name. If the action is a list of words, we just add them,
# otherwise we invoke the command or function named.
for name in "$_lo_cache_names[@]"; do
action="$_lo_cache_actions[anum]"
if (( ${(@)${(@P)name}[(I)$pre]} )); then
IPREFIX="${oipre}${pre}="
PREFIX="${str#*\=}"
SUFFIX=""
if [[ "$action[1]" = (\[|\() ]]; then
compadd - ${=action[2,-2]}
elif (( $#action )); then
$=action
fi
# We found the option string, return.
return
fi
# The array did not contain the full option string, see if it
# contains a string matching the string from the line.
# If there is one, we store the option string in `parto' and the
# element from `_lo_actions' in `parta'. If we find more than one
# such option or if we already had one, we set `parto' to `-'.
PREFIX="${str%%\=*}"
SUFFIX=""
compadd -O tmp -M 'r:|-=* r:|=*' - "${(@P)name}"
if [[ $#tmp -eq 1 ]]; then
if [[ -z "$parto" ]]; then
parto="$tmp[1]"
parta="$action"
else
parto=-
fi
elif (( $#tmp )); then
parto=-
fi
(( anum++ ))
done
# If we found only one matching option, we accept it and immediatly
# try to complete the string after the `='.
if [[ -n "$parto" && "$parto" != - ]]; then
IPREFIX="${oipre}${parto}="
PREFIX="${str#*\=}"
SUFFIX=""
if (( $#parta )); then
if [[ "$parta[1]" = (\[|\() ]]; then
compadd - ${=parta[2,-2]}
else
$=parta
fi
else
compadd -S '' - "$PREFIX"
fi
return
fi
# The option string was not found, restore the special parameters.
IPREFIX="$oipre"
PREFIX="$opre"
SUFFIX="$osuf"
fi
# The string on the line did not contain a `=', or we couldn't
# complete the option string since there were more than one matching
# what's on the line. So we just add the option strings as possible
# matches, giving the string from the `=' on as a suffix.
if [[ "$str" = *\=* ]]; then
str="=${str#*\=}"
PREFIX="${PREFIX%%\=*}"
suffix=()
else
str=""
suffix=('-S=')
fi
anum=1
for name in "$_lo_cache_names[@]"; do
action="$_lo_cache_actions[anum]"
if [[ "$name" = *_optarg_* ]]; then
compadd -M 'r:|-=* r:|=*' -Qq "$suffix[@]" -s "$str" - \
"${(@P)name}" && ret=0
elif [[ "$name" = *_arg_* ]]; then
compadd -M 'r:|-=* r:|=*' -Q "$suffix[@]" -s "$str" - \
"${(@P)name}" && ret=0
elif [[ -z "$str" ]]; then
compadd -M 'r:|-=* r:|=*' -Q - \
"${(@P)name}" && ret=0
fi
(( anum++ ))
done
return ret