Z-Shell Frequently-Asked Questions

Chapter 4: The mysteries of completion

Programmable completion using the `compctl' command is one of the most powerful, and also potentially confusing, features of zsh; here I give a short introduction. There is a set of example completions supplied with the source in Misc/compctl-examples; completion definitions for many of the most obvious commands can be found there.

4.1: What is completion?

`Completion' is where you hit a particular command key (TAB is the standard one) and the shell tries to guess the word you are typing and finish it for you --- a godsend for long file names, in particular, but in zsh there are many, many more possibilities than that.

There is also a related process, `expansion', where the shell sees you have typed something which would be turned by the shell into something else, such as a variable turning into its value ($PWD becomes /home/users/mydir) or a history reference (!! becomes everything on the last command line). In zsh, when you hit TAB it will look to see if there is an expansion to be done; if there is, it does that, otherwise it tries to perform completion. (You can see if the word would be expanded --- not completed --- by TAB by typing \C-x g, which lists expansions.) Expansion is generally fairly intuitive and not under user control; for the rest of the chapter I will discuss completion only.

4.2: What sorts of things can be completed?

The simplest sort is filename completion, mentioned above. Unless you have made special arrangements, as described below, then after you type a command name, anything else you type is assumed by the completion system to be a filename. If you type part of a word and hit TAB, zsh will see if it matches the first part a file name and if it does it will automatically insert the rest.

The other simple type is command completion, which applies (naturally) to the first word on the line. In this case, zsh assumes the word is some command to be executed lying in your $PATH (or something else you can execute, like a builtin comman, a function or an alias) and tries to complete that.

Other forms of completion have to be set up by special arrangement. See the manual entry for compctl for a list of all the flags: you can make commands complete variable names, user names, job names, etc., etc.

For example, one common use is that you have an array variable, $hosts, which contains names of other machines you use frequently on the network:

hosts=(fred.ph.ku.ac.uk snuggles.floppy-bunnies.com here.there.edu) then you can tell zsh that when you use telnet (or ftp, or ...), the argument will be one of those names: compctl -k hosts telnet ftp ... so that if you type telnet fr and hit TAB, the rest of the name will appear by itself.

An even more powerful option to compctl (-g) is to tell zsh that only certain sorts of filename are allowed. The argument to -g is exactly like a glob pattern, with the usual wildcards *, ?, etc. In the compctl statement it needs to be quoted to avoid it being turned into filenames straight away. For example,

compctl -g '*.(ps|eps)' ghostview tells zsh that if you type TAB on an argument after a ghostview command, only files ending in .ps or .eps should be considered for completion.

Note that flags may be combined; if you have more than one, all the possible completions for all of them are put into the same list, all of them being possible completions. So

compctl -k hosts -f rcp tells zsh that rcp can have a hostname or a filename after it. (You really need to be able to handle host:file, which is where programmable completion comes in, see
4.4.)

4.3: How does zsh deal with ambiguous completions?

Often there will be more than one possible completion: two files start with the same characters, for example. Zsh has a lot of flexibility for what it does here via its options. The default is for it to beep and completion to stop until you type another character. You can type \C-D to see all the possible completions. (That's assuming your at the end of the line, otherwise \C-D will delete the next character and you have to use ESC-\C-D.) This can be changed by the following options, among others:

Combinations of these are possible; for example, autolist and automenu together give an intuitive combination.

4.4: How do I get started with programmable completion?

Finally, the hairiest part of completion. It is possible to get zsh to consider different completions not only for different commands, but for different words of the same command, or even to look at other words on the command line (for example, if the last word was a particular flag) and decide then.

There are really two sorts of things to worry about. The simpler is alternative completion: that just means zsh will try one alternative, and only if there are no possible completions try the next. For example

compctl -g '*.ps' + -f lpr says that after lpr you'd prefer to find only .ps files, so if there are any, only those are used, but if there aren't any, any old file is a possibility. You can also have a + with no flags after it, which tells zsh that it's to treat the command like any other if nothing was found. That's only really useful if your default completion is fancy, i.e. you have done something with compctl -D to tell zsh how commands which aren't specially handled are to have their arguments completed.

The second sort is the hard one. Following a -x, zsh expects that the next thing will be some completion code, which is a single letter followed by an argument in square brackets. For example p[1]: p is for position, and the argument tells it to look at position 1; that says that this completion only applies to the word immediately after the command. You can also say p[1,3] which says the completion only applies to the word if it's between the first and third words, inclusive, after the command, and so on. See the list in the `compctl' manual entry for a list of these conditions: some conditions take one argument in the square brackets, some two. Usually, negative numeric arguments count backwards from the end (for example, p[-1] applies to the last word on the line).

The condition is then followed by the flags as usual (as in 4.2), and possibly other condition/flag sets following a single -; the whole lot ends with a double -- before the command name. In other words, each extended completion section looks like this:

-x <pattern> <flags>... [ - <pattern> <flags>... ...] --

Let's look at rcp again: this assumes you've set up $hosts as above. This uses the n[<n>,<string>] flag, which tells zsh to look for the <n>'th occurrence of <string> in the word, ignoring anything up to and including that. We'll use it for completing the bits of rcp's user@host:file combination. (Of course, the file name is on the local machine, not host, but let's ignore that; it may still be useful.)

compctl -k hosts -S ':' + -f -x 'n[1,:]' -f - \ 'n[1,@]' -k hosts -S ':' -- rcp This means: (1) try and complete a hostname (the bit before the +), if successful add a : (-S for suffix); (2) if that fails move on to try the code after the +: look and see if there is a : in a word (the n[1,:]); if there is, complete filenames (-f) after the first of them; (3) otherwise look for an @ and complete hostnames after the first of them (the n[1,@]), adding a : if successful; (4) if all else fails use the -f before the -x and try to complete files.

So the rules for order are (1) try anything before a + before anything after it (2) try the conditions after a -x in order until one succeeds (3) use the default flags before the -x if none of the conditions was true.

Different conditions can also be combined. There are three levels of this (in decreasing order of precedence):

  1. multiple square brackets after a single condition give alternatives: for example, s[foo][bar] says apply the completion if the word begins with foo or bar,
  2. spaces between conditions mean both must match: for example, p[1] s[-] says this completion only applies for the first word after the command and only if it begins with a -,
  3. commas between conditions mean either can match: for example, c[-1,-f], s[-f] means either the previous word (-1 relative to the current one) is -f, or the current word begins with -f --- useful to use the same completion whether or not the -f has a space after it.

Here's a useless example just to show a general -x completion.

compctl -f -x 'c[-1,-u][-1,-U] p[2], s[-u]' -u - \ 'c[-1,-j]' -P % -j -- foobar The way to read this is: for command foobar, look and see if (((the word before the current one is -u) or (the word before the current one is -U)) and (the current word is 2)) or (the current word begins with -u); if so, try to complete user names. If the word before the current one is -j, insert the prefix % before the current word if it's not there already and complete job names. Otherwise, just complete file names.

4.5: And if programmable completion isn't good enough?

...then your last resort is to write a shell function to do it for you. By combining the -U and -K func flags you can get almost unlimited power. The -U tells zsh that whatever the completion produces is to be used, even if it doesn't fit what's there already (so that gets deleted when the completion is inserted). The -K func tells zsh a function name. The function is passed what's on the line already, but it can return anything it likes via the reply array, and this becomes the set of possible completions. The best way to understand this is to look at multicomp and other functions supplied with the zsh distribution.