summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2006-10-10 18:06:28 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2006-10-10 18:06:28 +0000
commit3162c03cc3219a8b7d54abb43514245b006a5319 (patch)
tree9757e0e4f6dd3e276a2f6ed8a081ddda3dacb47d
parentc303529348a7c9171347acb5278370f78e67f7e4 (diff)
downloadzsh-3162c03cc3219a8b7d54abb43514245b006a5319.tar.gz
zsh-3162c03cc3219a8b7d54abb43514245b006a5319.tar.xz
zsh-3162c03cc3219a8b7d54abb43514245b006a5319.zip
22858: _arguments can generate documentation from --help text
-rw-r--r--ChangeLog3
-rw-r--r--Completion/Base/Utility/_arguments99
2 files changed, 88 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 93ed9adc5..617618a5e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2006-10-10  Peter Stephenson  <pws@csr.com>
 
+	* 22858: Completion/Base/Utility/_arguments: options generated
+	from --help text can now be documented.
+
 	* 22851: arno: Completion/Unix/Command/_init_d: "-" can occur
 	in script names.
 
diff --git a/Completion/Base/Utility/_arguments b/Completion/Base/Utility/_arguments
index a87486168..ef76d8eba 100644
--- a/Completion/Base/Utility/_arguments
+++ b/Completion/Base/Utility/_arguments
@@ -6,6 +6,7 @@
 local long cmd="$words[1]" descr mesg subopts opt usecc autod
 local oldcontext="$curcontext" hasopts rawret optarg singopt alwopt
 local setnormarg
+local -a match mbegin mend
 
 long=$argv[(I)--]
 if (( long )); then
@@ -68,32 +69,59 @@ 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)//\[--/
---}:#[ 	]#-*}//,/
-}}:#[ 	]#--*}#*--}%%[]	 ]*}:#}//\[=/=}")
+   _call_program options ${~words[1]} --help 2>&1 | while read opt; do
+     tmp=()
+     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:]]##}
+     # 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:+:${opt//:/-}})
+   done
 
     # 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=( "$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
+      # 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 ${lopts/$~sopts[1]/$sopts[2]} )
       shift 2 sopts
     done
@@ -110,9 +138,15 @@ if (( long )); then
       # 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 +158,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
 
@@ -140,11 +176,28 @@ if (( long )); then
 
         if [[ "$descr" = :\=* ]]; then
           for opt in "$tmpo[@]"; do
-            cache=( "$cache[@]"
-                    "${${opt%%\=*}//[^a-zA-Z0-9-]}=::${(L)${opt%\]}#*\=}: " )
+	    # 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]
+	      descr="[${match[2]}]"
+	    else
+	      descr=
+	    fi
+            cache=(
+	      "$cache[@]"
+	      "${${opt%%\=*}//[^a-zA-Z0-9-]}=${descr}::${(L)${opt%\]}#*\=}: "
+	    )
           done
         else
-          tmpo=("${(@)${(@)tmpo%%\=*}//[^a-zA-Z0-9-]}")
+	  # We don't handle the [description] form here.
+	  # TODO: we could with a bit of rewriting.
+	  #
+	  # The "[" didn't get removed here until I added it.
+	  # This may be why we used to try to remove the square brackets
+	  # higher up.
+          tmpo=("${(@)${(@)tmpo%%\[\=*}//[^a-zA-Z0-9-]}")
           if [[ "$descr" = ::* ]]; then
 	    cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" )
           else
@@ -154,6 +207,8 @@ if (( long )); then
       fi
 
       # Descriptions with `=': mandatory argument.
+      # Basically the same as the foregoing.
+      # TODO: could they be combined?
 
       tmpo=("${(@M)tmp:#*\=*}")
       if (( $#tmpo )); then
@@ -161,8 +216,16 @@ if (( long )); then
 
         if [[ "$descr" = :\=* ]]; then
           for opt in "$tmpo[@]"; do
-            cache=( "$cache[@]"
-                    "${${opt%%\=*}//[^a-zA-Z0-9-]}=:${(L)${opt%\]}#*\=}: " )
+	    if [[ $opt = (#b)(*):([^:]#) ]]; then
+	      opt=$match[1]
+	      descr="[${match[2]}]"
+	    else
+	      descr=
+	    fi
+            cache=(
+	      "$cache[@]"
+	      "${${opt%%\=*}//[^a-zA-Z0-9-]}=${descr}:${(L)${opt%\]}#*\=}: "
+	    )
           done
         else
           tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
@@ -175,7 +238,15 @@ if (( long )); then
       # 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}" )
         else