Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Please add pinfo completion



Vincent Lefevre <vincent@xxxxxxxxxx> wrote:
> > compdef _gnu_generic pinfo
>
> One problem is that it doesn't give help text. So, is there a way
> to generate a completion file (to be completed), assuming that the
> command has the --help feature?

OK, here is _arguments updated to provide option descriptions from --help
text automatically.

It'll be a little slower since a horrible nested parameter substitution has
turned into a loop.  (I didn't even try to understand it in too much depth,
I just started again from scratch.)  However, there is in any case a cache,
and there was an external program to run at that point anyway, so I don't
think it'll make much difference.

In my case when using _gnu_generic for pinfo completion (with some
non-standard configuration settings; you're unlikely to get exactly the
same), "pinfo --^D" gives me

Completing option
--apropos                        # call apropos if nothing found
--clear-at-exit                  # clear screen at exit
--cut-man-headers                # cut out repeated man headers
--dont-handle-without-tag-table  # don't display texinfo pages without tag
--file                           # synonym for -r
--force-manual-tag-table         # force manual detection of tag table
--help                           # help
--long-manual-links              # use long link names in manuals
--manual                         # use man page
--node                           # jump directly to the node nodename
--plain-apropos                  # call only apropos
--raw-filename                   # use raw filename
--rcfile                         # use alternate rcfile
--squeeze-manlines               # cut empty lines from manual pages
--version                        # version

which certainly impressed me, I can tell you.

After fiddling with _arguments for an hour, I'd like to say "if there's a
problem, DEAL WITH IT", but since I'm now a professional software engineer,
I'll have a go at fixing any problems I've created with this change; there
are bound to be some.

Note in particular I found some problems with optional arguments like
"--option[=STUFF]" and tried to address them, but this isn't properly
tested.  It looked like somebody had fixed previous problems just by
treating them as mandatory arguments, but the code should have handled them
better than that.

I have to admit this was at least more tractable than the other problem
I've been tackling today.

Index: Completion/Base/Utility/_arguments
===================================================================
RCS file: /cvsroot/zsh/zsh/Completion/Base/Utility/_arguments,v
retrieving revision 1.17
diff -u -r1.17 _arguments
--- Completion/Base/Utility/_arguments	27 Sep 2006 16:53:59 -0000	1.17
+++ Completion/Base/Utility/_arguments	10 Oct 2006 17:52:22 -0000
@@ -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 @@
     # 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 @@
       # 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 @@
       # 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 [[ "$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 @@
       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 [[ "$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 @@
       # 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

-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


To access the latest news from CSR copy this link into a web browser:  http://www.csr.com/email_sig.php



Messages sorted by: Reverse Date, Date, Thread, Author