My next installment was going to cover a different topic, but Marty made a wish and unless Rajaton intercedes before this blog entry is over, the curriculum is going to careen off the track.

The relevant question is something like “Why do I have to type all that junk in when there's perfectly good --help output to parse?” and the relevant answer is something like “Yes. No. ROFLcows. Lamar.” This is somewhat easy when a program has well-behaved, sane-ish, GNU-style --help output.

Let's say you want completion for GNU ptx. Now you could go through and write a function using _arguments like you learned all about here, or you could just type

compdef _gnu_generic ptx

Suddenly you'll be able to complete options after ptx. You'll notice a few things as you perform your QA due diligence. It won't understand the subtleties of -G. It won't know what STRING and REGEXP and NUMBER mean, but then, neither do I.

It's possible to use _arguments to explain what WORDS IN ALL CAPITALS mean, but let's find a less BORING example.

Usage: nurmepuu [OPTION]... [FILE]
Drive Miss Nurmepuu

  -c, --compress=TYPE            apply this compression TYPE to the aircon
  -f, --force                    use force
  -s, --side=SIDE                drive on this SIDE of the road
  -u, --unbearability=LEVEL      tolerate this LEVEL of unbearability

So start out the way you have now learned instinctually.

#compdef nurmepuu

_arguments -- \

WAIT. WHAT'S THAT‽‽ The double hyphen-minus, contrary to the expectations of GNU and git users alike, means (are you ready for this?) parse the --help output and try to sanely complete long options. Now lets give it some hints so it isn't too braindead.

  '*=TYPE*:compression type:(gzip bzip2 lossy gainy pancakebatfish)' \
  '*=SIDE*:side of the road:(left right east west up)' \
  '*=LEVEL*:tolerance level:((japan\:haha radiohead\:hoho))'

There you go. The patterns on the left will be matched against the help output, and the completion actions on the right. The list in double parens are matches paired with descriptions (note the backslashed colons). Once more, the whole thing:

#compdef nurmepuu

_arguments -- \
  '*=TYPE*:compression type:(gzip bzip2 lossy gainy pancakebatfish)' \
  '*=SIDE*:side of the road:(left right east west up)' \
  '*=LEVEL*:tolerance level:((japan\:haha radiohead\:hoho))'
Posted Sat 29 Sep 2007 04:33:35 PM EDT Tags:

Once upon a time I proposed a DebConf talk about how to write zsh completion functions, but it was rejected. Accordingly, I didn't waste any time preparing materials for it, so I never have anything to throw at people when they ask for some kind of introduction.

Here we have a fictitious program called arismom. Usage information from the fictitious manpage and the fictitious --help output is as follows:

Usage: arismom [OPTION]... [FILE]...
Do it Jersey style.

  -a, --all                       do all those things
  -b                              bubble
      --bounce                    bonuce
      --CoC=STYLE                 adhere to STYLE code of conduct
  -d, --debian=PACKAGE            dedicate actions to Debian PACKAGE
  -e, --ensqualm=USER             ensqualm USER first
  -t, --tempdir=DIRECTORY         spew temporary files into DIRECTORY

So let's cut to the quick. Create a file called _arismom somewhere in your function search path. This is described by the array $fpath. You can view its contents by typing print -l $fpath, and you can add a a directory to the beginning with fpath=(\~/.zsh/scratch $fpath) or to the end with fpath+=(\~/.zsh/scritch). For the purposes of this blog entry, we'll pretend you have a \~/.michaelbolton/squatch directory in your $fpath and that you are now editing \~/.michaelbolton/squatch/_arismom. The first line of the file, at the very tippy top, should read

#compdef arismom

This ensures that when the completion system boots up and finds your file, it will associate your function with the command arismom and complete options and arguments for it accordingly. Speaking of arguments, skip a line for aesthetic equilibrium, and invoke the _arguments utility function.

_arguments \

_arguments is sort of the cdbs of the zsh completion fleet. By the end of this blog entry, you'll have no idea how it works, and if you want to do anything particularly complex with it, you might encounter some resistance. For those of you unfamiliar with Z-Shell syntax or shell syntax in general, the trailing backslash means “I'm a-gonna feed you a ton of information about the command-line interface to arismom.” So tell it about the first option already.

  '(-a --all)'{-a,--all}'[do all those things]' \

To oversimplify, this declares that -a and --all are options which produce the identical behavior of “do all those things”. Specifically, the part in parentheses says to not complete either -a or --all when either -a or --all is already on the command-line. The part in braces is merely brace expansion; for that reason it is outside of the single quotes. If you're unfamiliar with brace expansion, try print '(alice)'{bob,carnie}'[wilson]' to see how it expands. Finally, the phrase in brackets is an explanation of the option, which may or may not be displayed depending on your configuration. Next, do a short option that has no long option equivalent.

  '-b[bubble]' \

A long option with no short option equivalent looks similar. Don't feel limited by upstream's inadequate descriptions, misspellings, or poor grammar.

  '--bounce[amplify bounce level according to X-la algorithm]' \

Some options take arguments. Now we use colons.

  '--CoC=[adhere to CoC]:CoC style:(mjg59 buxy ubuntu)' \

The first “column” is the same optspec by which you've been so excited thus far, and the part between the colons is the message or description of that which will be matched. The part after the last colon is the action; in this case you are specifying a list of possibilities within single parentheses. In most cases, you'll want to be more dynamic than a pre-defined list, and there are many helper functions all ready to serve you.

  '(-d --debian)'{-d,--debian=}'[dedicate actions to Debian package]:package:_deb_packages avail' \

_deb_packages is a function that completes Debian packages; it can take avail, installed, or uninstalled to restrict which set of packages it offers. In this case we want it to complete any package available from your sources.

  '(-e --ensqualm)'{-e,--ensqualm=}'[ensqualm user first]:user to ensqualm:_users' \

Here the _users function will complete usernames.

  '(-t --tempdir)'{-t,--tempdir=}'[spew]:temp dir:_files -/' \

Normally the _files function will complete files, but you can tell it that you only want directories with the -/ option. Finally, we want to cover all the remaining arguments (which according to the fictitious usage information is a list of files). In this case, you happen to believe that files with the .nj extension are to be completed.

  '*:NJ files:_files -g "*.nj"'

The -g option specifies a glob pattern to match files. Now the entire file should look like this.

#compdef arismom

_arguments \
  '(-a --all)'{-a,--all}'[do all those things]' \
  '-b[bubble]' \
  '--bounce[amplify bounce level according to X-la algorithm]' \
  '--CoC=[adhere to CoC]:CoC style:(mjg59 buxy ubuntu)' \
  '(-d --debian)'{-d,--debian=}'[dedicate actions to Debian package]:package:_deb_packages avail' \
  '(-e --ensqualm)'{-e,--ensqualm=}'[ensqualm user first]:user to ensqualm:_users' \
  '(-t --tempdir)'{-t,--tempdir=}'[spew]:temp dir:_files -/' \
  '*:NJ files:_files -g "*.nj"'

There you have it. Restart zsh and try tab-completing various things after arismom.

P.S. I expect bug reports containing functions for dpatch-edit-patch, pkill, and pgrep by tomorrow morning.

P.P.S. Why is Scott James Remnant still on Planet?

Posted Fri 28 Sep 2007 05:01:17 PM EDT Tags: