13. Extended completion — Zsh 5.9.999.3-test documentation

# 13. Extended completion

If you play with completion, you will soon notice that you would like to specify what to complete, depending on what flags you give to the command and where you are on the command line. For example, a command could take any filename argument after a `-f` flag, a username after a `-u` flag and an executable after a `-x` flag. This section will introduce you to the ways to specify these things. To many people it seems rather difficult at first, but taking the trouble to understand it can save you lots of typing in the end. Even I keep being surprised when `zsh` manages to complete a small or even empty prefix to the right file in a large directory.

To tell `zsh` about these kinds of completion, you use “extended completion” by specifying the `-x` flag to compctl. The `-x` flag takes a list of patterns/flags pairs. The patterns specify when to complete and the flags specify what. The flags are simply those mentioned above, like `-f` or `-g `:emphasis:`glob pattern`.

As an example, the `r[`string1`,`string2`]` pattern matches if the cursor is after something that starts with string1 and before something that starts with string2. The string2 is often something that you do not want to match anything at all.

Example 13.1: r[] pattern for compress -d

    % ls
    foo1   bar1   foo.Z  bar.Z
    % compctl -g '^*.Z' -x 'r[-d,---]' -g '*.Z' -- compress
    % compress f«Tab»
    % compress foo1 _
    % compress -d f«Tab»
    % compress -d foo.Z _

In the above example, if the cursor is after the `-d` the pattern will match and therefore `zsh` uses the `-g *.Z` flag that will only complete files ending in `.Z`. Otherwise, if no pattern matches, it will use the flags before the `-x` and in this case complete every file that does not end in `.Z`.

The `s[`string`]` pattern matches if the current word starts with string. The string itself is not considered to be part of the completion.

Example 13.2: s[] pattern for kill signals

    % compctl -x 's[-]' -k signals -- kill
    % kill -H«Tab»
    % kill -HUP _

The `tar` command takes a tar file as an argument after the `-f` option. The `c[`offset`,`string`]` pattern matches if the word in position offset relative to the current word is string. More in particular, if offset is -1, it matches if the previous word is string. This suggests

Example 13.3: c[] pattern for tar -f

    % compctl -f -x 'c[-1,-f]' -g '*.tar' -- tar

But this is not enough. The `-f` option could be the last of a longer string of options. `C[`…`,`…`]` is just like `c[`…`,`…`]`, except that it uses glob-like pattern matching for string. So

Example 13.4: C[] pattern for tar -f cluster

    % compctl -f -x 'C[-1,-*f]' -g '*.tar' -- tar

will complete tar files after any option string ending in an `f`. But we’d like even more. Old versions of tar used all options as the first argument, but without the minus sign. This might be inconsistent with option usage in all other commands, but it is still supported by newer versions of `tar`. So we would also like to complete tar files if the first argument ends in an `f` and we’re right behind it.

We can and’ patterns by putting them next to each other with a space between them. We can `or’ these sets by putting comma’s between them. We will also need some new patterns. :literal:`p[num`]` will match if the current argument (the one to be completed) is the numth argument. `W[`index`,`pattern`]` will match if the argument in place index matches the pattern. This gives us

Example 13.5: tar -f and old-style options

    % compctl -f -x 'C[-1,-*f] , W[1,*f] p[2]' -g '*.tar' -- tar

In words: If the previous argument is an option string that ends in an `f`, or the first argument ended in an `f` and it is now the second argument, then complete only filenames ending in `.tar`.

All the above examples used only one set of patterns with one completion flag. You can use several of these pattern/flag pairs separated by a `-`. The first matching pattern will be used. Suppose you have a version of `tar` that supports compressed files by using a `-Z` option. Leaving the old tar syntax aside for a moment, we would like to complete files ending in `.tar.Z` if a `-Z` option has been used and files ending in `.tar` otherwise, all this only after a `-f` flag. Again, the `-Z` can be alone or it can be part of a longer option string, perhaps the same as that of the `-f` flag. Here’s how to do it; note the backslash and the secondary prompt which are not part of the `compctl` command.

Example 13.6: tar compressed vs plain files

    % compctl -f -x 'C[-1,-*Z*f] , R[-*Z*,---] C[-1,-*f]' -g '*.tar.Z' - \
    > 'C[-1,-*f]' -g '*.tar' -- tar

The first pattern set tells us to match if either the previous argument was an option string including a `Z` and ending in an `f` or there was an option string with a `Z` somewhere and the previous word was any option string ending in an `f`. If this is the case, we need a compressed tar file. Only if this is not the case the second pattern set will be considered. By the way, `R[`pattern1`,`pattern2`]` is just like `r[`…`,`…`]` except that it uses pattern matching with shell metacharacters instead of just strings.

You will have noticed the `--` before the command name. This ends the list of pattern/flag pairs of `-x`. It is usually used just before the command name, but you can also use an extended completion as one part of a list of xored completions, in which case the `--` appears just before one of the `+` signs.

Note the difference between using extended completion as part of a list of xored completions as in

Example 13.7: Xor after extended completion

    % ls
    foo  bar
    % compctl -x 'r[-d,---]' -g '*.Z' -- + -g '^*.Z' compress
    % compress -d f«Tab»
    % compress -d foo _

and specifying something before the `-x` as in

Example 13.8: Flags before -x fallback

    % compctl -g '^*.Z' -x 'r[-d,---]' -g '*.Z' -- compress
    % compress -d f«Tab»
    % compress -d f_

In the first case, the alternative glob pattern (`^*.Z`) will be used if the first part does not generate any possible completions, while in the second case the alternative glob pattern will only be used if the `r[`…`]` pattern doesn’t match.