mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-25 17:41:19 +02:00
345 lines
11 KiB
Text
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
|