From 4c3c7456c2bb1f008d1e96fc97be320dbd2d8875 Mon Sep 17 00:00:00 2001 From: Paul Ackersviller Date: Sun, 4 Nov 2007 20:21:53 +0000 Subject: Merge of 22858, 22859, and 23182 (up to trunk revision 1.20). --- Completion/Base/Utility/_arguments | 179 ++++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 43 deletions(-) (limited to 'Completion/Base') diff --git a/Completion/Base/Utility/_arguments b/Completion/Base/Utility/_arguments index a87486168..d1aafd79a 100644 --- a/Completion/Base/Utility/_arguments +++ b/Completion/Base/Utility/_arguments @@ -3,9 +3,10 @@ # 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 long cmd="$words[1]" descr odescr mesg subopts opt opt2 usecc autod local oldcontext="$curcontext" hasopts rawret optarg singopt alwopt local setnormarg +local -a match mbegin mend long=$argv[(I)--] if (( long )); then @@ -50,9 +51,9 @@ if (( long )); then tmp=( "${(@P)tmp}" ) fi if [[ "$1" = -i* ]]; then - iopts=( "$iopts[@]" "$tmp[@]" ) + iopts+=( "$tmp[@]" ) else - sopts=( "$sopts[@]" "$tmp[@]" ) + sopts+=( "$tmp[@]" ) fi shift cur done @@ -68,33 +69,89 @@ if (( long )); then # 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)//\[--/ ---}:#[ ]#-*}//,/ -}}:#[ ]#--*}#*--}%%[] ]*}:#}//\[=/=}") + tmp=() + _call_program options ${~words[1]} --help 2>&1 | while IFS= read -r opt; do + if (( ${#tmp} )); then + # Previous line had no comment. Is the current one suitable? + # It's hard to be sure, but if it there was nothing on the + # previous line and the current one is indented more than + # a couple of spaces (and isn't completely whitespace or punctuation) + # there's a pretty good chance. + if [[ $opt = [[:space:]][[:space:]][[:space:]]*[[:alpha:]]* ]]; then + # Assume so. + opt=${opt##[[:space:]]##} + # Same substitution as below. + lopts+=("${^tmp[@]}":${${${opt//:/-}//\[/(}//\]/)}) + tmp=() + # Finished with this line. + continue + else + # Still no comment, add the previous options anyway. + lopts+=("${tmp[@]}") + tmp=() + fi + fi + while [[ $opt = [,[:space:]]#(#b)(-[^,[:space:]]#)(*) ]]; do + # We used to remove the brackets from "[=STUFF]", + # but later the code appears to handle it with the brackets + # present. Maybe the problem was that the intervening code + # didn't. If it's buggy without removing them, the problem + # probably is later, not here. + if [[ -z ${tmp[(r)${match[1]%%[^a-zA-Z0-9-]#}]} ]]; then + tmp+=($match[1]) + fi + opt=$match[2] + done + # If there's left over text, assume it's a description; it + # may be truncated but if it's too long it's no use anyway. + # There's one hiccup: we sometimes get descriptions like + # --foo fooarg Do some foo stuff with foo arg + # and we need to remove fooarg. Use whitespace for hints. + opt=${opt## [^[:space:]]## } + opt=${opt##[[:space:]]##} + if [[ -n $opt ]]; then + # Add description after a ":", converting any : in the description + # to a -. Use RCQUOTES to append this to all versions of the option. + lopts+=("${^tmp[@]}":${${${opt//:/-}//\[/(}//\]/)}) + tmp=() + # If there's no comment, we'll see if there's one on the + # next line. + fi + done + # Tidy up any remaining uncommented options. + if (( ${#tmp} )); then + lopts+=("${tmp[@]}") + fi # Remove options also described by user-defined specs. tmp=() - for opt in "${(@)${(@)lopts:#--}%%\=*}"; do + # Ignore any argument and description information when searching + # the long options array here and below. + for opt in "${(@)${(@)lopts:#--}%%[\[:=]*}"; do # Using (( ... )) gives a parse error. let "$tmpargv[(I)(|\([^\)]#\))(|\*)${opt}(|[-+]|=(|-))(|\[*\])(|:*)]" || - tmp=( "$tmp[@]" "$lopts[(r)$opt(|=*)]" ) + tmp+=( "$lopts[(r)$opt(|[\[:=]*)]" ) done lopts=( "$tmp[@]" ) # Now remove all ignored options ... while (( $#iopts )); do - lopts=( ${lopts:#$~iopts[1]} ) + lopts=( ${lopts:#$~iopts[1](|[\[:=]*)} ) shift iopts done # ... and add "same" options while (( $#sopts )); do - lopts=( $lopts ${lopts/$~sopts[1]/$sopts[2]} ) + # This implements adding things like --disable-* based + # on the existence of --enable-*. + # TODO: there's no anchoring here, is that correct? + # If it's not, careful with the [\[:=]* stuff. + lopts+=( ${lopts/$~sopts[1]/$sopts[2]} ) shift 2 sopts done @@ -102,17 +159,27 @@ if (( long )); then # 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 -/' '*=*:=: ' '*: : ' + argv+=( + '*=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. + # This is the first bit of the arguments in the special form + # for converting --help texts, taking account of any quoting + # of colons. pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}" + # Any action specifications that go with it. descr="${1#${pattern}}" if [[ "$pattern" = *\(-\) ]]; then + # This is the special form to disallow arguments + # in the next word. pattern="$pattern[1,-4]" dir=- else @@ -124,8 +191,10 @@ if (( long )); then # list we have built. If no option matches the pattern, we # continue with the next. - tmp=("${(@M)lopts:##$~pattern}") - lopts=("${(@)lopts:##$~pattern}") + # Ignore :descriptions at the ends of lopts for matching this; + # they aren't in the patterns. + tmp=("${(@M)lopts:##$~pattern(|:*)}") + lopts=("${(@)lopts:##$~pattern(|:*)}") (( $#tmp )) || continue @@ -138,48 +207,72 @@ if (( long )); then 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 + for opt in "$tmpo[@]"; do + # Look for --option:description and turn it into + # --option[description]. We didn't do that above + # since it could get confused with the [=ARG] stuff. + if [[ $opt = (#b)(*):([^:]#) ]]; then + opt=$match[1] + odescr="[${match[2]}]" + else + odescr= + fi + if [[ $opt = (#b)(*)\[\=* ]]; then + opt2=${${match[1]}//[^a-zA-Z0-9-]}=-${dir}${odescr} + else + opt2=${${opt}//[^a-zA-Z0-9-]}=${dir}${odescr} + fi + if [[ "$descr" = :\=* ]]; then + cache+=( "${opt2}::${(L)${opt%\]}#*\=}: " ) + elif [[ "$descr" = ::* ]]; then + cache+=( "${opt2}${descr}" ) + else + cache+=( "${opt2}:${descr}" ) + fi + done fi # Descriptions with `=': mandatory argument. + # Basically the same as the foregoing. + # TODO: could they be combined? 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 + for opt in "$tmpo[@]"; do + if [[ $opt = (#b)(*):([^:]#) ]]; then + opt=$match[1] + odescr="[${match[2]}]" + else + odescr= + fi + opt2="${${opt%%\=*}//[^a-zA-Z0-9-]}=${dir}${odescr}" + if [[ "$descr" = :\=* ]]; then + cache+=( "${opt2}:${(L)${opt%\]}#*\=}: " ) + else + cache+=( "${opt2}${descr}" ) + fi + done 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-]}") + tmp=( + # commands with a description of the option (as opposed + # to the argument, which is what descr contains): needs to be + # "option[description]". + # Careful: \[ on RHS of substitution keeps the backslash, + # I discovered after about half an hour, so don't do that. + "${(@)^${(@)tmp:#^*:*}//:/[}]" + # commands with no description + "${(@)${(@)tmp:#*:*}//[^a-zA-Z0-9-]}") if [[ -n "$descr" && "$descr" != ': : ' ]]; then - cache=( "$cache[@]" "${(@)^tmp}${descr}" ) + cache+=( "${(@)^tmp}${descr}" ) else - cache=( "$cache[@]" "$tmp[@]" ) + cache+=( "$tmp[@]" ) fi fi done @@ -273,11 +366,11 @@ if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then action="${${action[3,-1]##[ ]#}%%[ ]#}" if (( ! $state[(I)$action] )); then comparguments -W line opt_args - state=( "$state[@]" "$action" ) + state+=( "$action" ) if [[ -n "$usecc" ]]; then curcontext="${oldcontext%:*}:$subc" else - context=( "$context[@]" "$subc" ) + context+=( "$subc" ) fi compstate[restore]='' aret=yes @@ -404,7 +497,7 @@ if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then fi single=yes else - next=( "$next[@]" "$odirect[@]" ) + next+=( "$odirect[@]" ) _describe -O option \ next -Q -M "$matcher" -- \ direct -QS '' -M "$matcher" -- \ -- cgit 1.4.1