Tuesday, October 21, 2014

Conventions can do more with less: Clive options continued.

In a previous post I discussed a new package in Clive, opt, which deals with command arguments.
The package turned to be very easy to use and has unified how clive commands are used beyond understanding a common option syntax.

When defining new options, the user gives a pointer to a Go value, a name, and a help string:

func (f *Flags) NewFlag(name, help string, vp interface{})

The help string follows the convention that

  • for options with no arguments, it simply describes the option.
  • for options with arguments, it starts with  "argument name:" followed by the description of the option.
This allows the usage method to exploit this convention to build a usage string more descriptive than the ones in other Go programs. For example, compare the old

usage: flds -options [file...]

with the new

usage: flds [-1D] [-F sep] [-o sep] {-r range} [file...]

The method writing the usage description scans the defined flags for those that have a single rune as a name, and no argument, and combines them like in "-1D" above. Then it goes over flags that have longer names, or have arguments, and adds the usage for each one in the same line. The name of the argument is taken from the start of the help string (if found, or a generic name for the type of argument [in Go] is used). When arguments may be used more than once, curly brackets are printed instead of square ones. The method knows this because the value pointer given to NewFlag is a pointer to a slice of values.

Thus, there is no need to keep the usage summary synchronised with the source, it is always in sync.

But this convention has done even more work for Clive. Manual pages in clive (for all sections but the go packages section) are written in wr(1). Recently we have
  • added a feature to the opt package so that "-?" can never be a valid flag, which makes all programs inform of their usage when called with this flag.
  • replaced all the "synopsis" subsections in all command man pages with a new "usage" one.
The new manual pages start with something like:

_ LNS(1): print lines

* USAGE

[sh
lns -? >[2=1] | lns -r 1,-2
]
...

Here, a shell escape runs the command to make it inform about its usage, and all the output lines but for the last one (which is an exit status in clive) are inserted into the manual.

After doing so, we could remove most option description lists from manual pages, and we no longer have to document the usage of a command anymore. Plus, we are sure that the usage matches the binary installed in the system. For example, the manual for lns looks like:

LNS(1): PRINT LINES

USAGE

    usage: lns [-Dn] {-r range} [file...]
    -D: debug
    -n: print line numbers
    -r range: print this range
    range is addr,addr or addr
    addr is linenb, or +linenb, or -linenb
    

DESCRIPTION
...

Once again, we could do more by actually doing less.