defined and bound for them to work. So to use all of these,
autoload -U incremental-complete-word predict-on zle -N incremental-complete-word zle -N predict-on zle -N predict-off bindkey '^Xi' incremental-complete-word bindkey '^Xp' predict-on bindkey '^X^P' predict-off
`Prediction' is a sort of dynamic history completion. With predict-on
in effect, the line editor will try to retrieve a line back in the history
which matches what you type. If it does, it will show the line, extending
past the current cursor position. You can then edit the line; characters
which do not insert anything mostly behave as normal. If you continue to
type, and what you type does not match the line which was found, the line
editor will look further back for another line; if no line matches, editing
is essentially as normal. Often this is flexible enough that you can leave
predict-on
in effect, but you can return to basic editing with
predict-off
.
Note that, with prediction turned on, deleting characters reverses the direction of the history search, so that you go back to previous lines, like an ordinary incremental search; unfortunately the previous line found could be one you've already half-edited, because they don't disappear from the list until you finally hit `return' on an edited line to accept it. There's another problem with moving around the line and inserting characters somewhere else: history searching will resume as soon as you try to insert the new characters, which means everything on the right of the cursor is liable to disappear again. So in that case you need to turn prediction off explicitly. A final problem: prediction is bad with multi-line buffers.
If prediction fails with predict-on
active, completion is automatically
tried. The context for this looks like `:completion:predict::::
'.
Various styles are useful at this point: `list
' could be set to
always
, which will show a possible completion even if there is only
one, for example. The style `cursor
' may have the values
`complete
' to move to the end of the word completed, `key
' to move
past the rightmost occurrence of the character just typed, allowing you
just to keep typing, or anything else not to move the cursor which is the
default behaviour.
The incremental-complete-word
function allows you to see a list of
possible completions as you type them character by character after the
first. The function is quite basic; it is really just an example of using
various line editor facilities, and needs some work to make a useful
system. It will understand DEL
to delete the previous character,
return to accept, ^G
to abort, TAB
to complete the word as normal
and ^D
to list possibilities; otherwise, keys which do not insert are
unlikely to have a useful effect. The completion is done behind the scenes
by the standard function complete-word
.
The final matter before I delve into the system for writing new completion
functions is matching control; the name refers in this case to how the
matching between characters already typed on the command line and
characters in a trial completion is performed. This can be done in two
ways: by setting the matcher-list
style, which applies to all
completions, or by using an argument (-M
) to the low-level completion
functions. Mostly we will be concerned with the first. All this is best
illustrated by examples, which are taken from the section `Matching
Control' in the zshcompwid
manual page; in the printed manual and
the `info' pages this occurs within the section `Completion Widgets
'.
The matcher-list
style takes an array value. The values will be tried
in order from left to right. For example,
zstyle ':completion:*' matcher-list 'm:{a-z-}={A-Z_}' \ 'r:|[-_./]=* r:|=*'tries the first specification, which is for case-insensitive completion, and if no matches are generated tries the second, which does partial word completion; I'll explain both these specifications in detail as we go along. You can make it do both forms the second time round simply by combining the values with a space, i.e. the last word on the command line becomes
'm:{a-z-}={A-Z_} r:|[-_./]=* r:|=*'
.
It is also perfectly valid to have a first matcher empty, i.e. '
'
;
this means that completion is tried with no matching rule the first time,
and will only go on to subsequent matchers in the list if that fails. This
is quite a good practice as it avoids surprises.
To perform case-insensitive matching for all completions, you can set:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'The `
m:
' specifies standard matching, with the `{a-z}
' describing
what's on the command line, and the `{A-Z}
' what's in the trial
completion. The braces indicate `correspondence classes', which are not
lessons taken by email (that's a joke), but a relative of the more usual
character classes like `[a-z]
', which, as you no doubt know, would
match any of the letters between a
and z
. In this context, with
the braces, the letters are forced to match on the left and right hand side
of the `=
', so an `a
' on the command line must match an `A
' in
the trial completion, a `b
' must match a `B
', and so on. Since an
a
in the command line will always match an `a
' in the trial
completion, matcher or no matcher, this means that if you type an `a
'
it will match either `a
' or `A
' --- in other words,
case-insensitively. The same goes for any other lowercase letter you
type. The difference from `m:[a-z]=[A-Z]
' is that, because ordinary
character classes are unordered, any lowercase letter would have
matched any uppercase letter, which isn't what you want. The rest of
the shell doesn't know about correspondence classes at all.
Finally, the use of a lowercase `m
' at the start means that the
characters actually inserted onto the line are those from the trial
completion --- if you type `make<TAB>
', the completion process
generates file names, and matcher-list
allows what you type to match
the file `Makefile
', then you need the latter to be inserted on the
command line. Use of `M:
' at the start of the matcher would keep
whatever was on the line to begin with there.
If you want completely case-insensitive matching, so that typing
`MAKE<TAB>
' would also potentially complete to `Makefile
' or
`makefile
' (and so on), the extension is fairly obvious:
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'because now as well as `
a
' matching `A
', `A
' will match `a
'
--- and, of course, `a
' and `A
' each still match themselves.
More detail on the patterns: they do not, in fact, allow all the possible
patterns you can use elsewhere in the shell, since that would be too
complicated to implement with little extra use. Apart from character
classes and correspondence classes, you can use `?
' which has its usual
meaning of matching one character, or literal characters, which match
themselves; or the pattern for the trial completion only can be a single
`*
'. which matches anything. That's it, however; you can't do other
things with the `*
' since it's too difficult for the system to guess
what characters should be covered by it.
For the same reason, the `*
' must be in an anchored pattern, the
idea behind which is shown in the next example.
I explained back in chapter 1 that zsh didn't care too much
how you specified options: `noglob
' and `NOGLOB
' and
`No_Glob
' and `__NO_GLOB_
' are all treated the same way. Also,
this is the negation of the option `glob
'. Having learnt how to
match case-insensitively, we have two further challenges: how to ignore
a `_
' anywhere in the word, and how to ignore the NO
at the
beginning so that we can complete an unnegated option name after it.
Well, here's how. Since you don't want this for all completions, just for
option names, I shall show it as an argument for the `compadd
' command,
which gives the system the list of possible completions. The option names
should then appear as the remaining arguments to the command, and the
easiest way of doing that is to have the zsh/parameter
module loaded,
which it always is for new completion, and use the keys of the special
associative array $options
:
compadd -M 'B:|[nN][oO]= M:_= M:{A-Z}={a-z}' - ${(k)options}Here, we're interested in the thing in quotes --- it means exactly the same here as it would as an element of the matcher list, except that it only applies to the trial completions given after the `
-
'. It's in three
bits, separated by spaces; as they're in the same word, all are applied
one after the other regardless of any previous ones having matched.
Starting from the right, you can see that the last part matches letters
case-insensitively; the capital `M
' means that, this time, the letters
on the command line, not those in the trial completion are kept; this is
safe because of the way options are parsed, and reduces unexpected changes.
Moving left, you can now guess `M:_=
': it means that the `_
'
matches nothing at all in the trial completion --- in other words, it is
simply ignored. The rule for matching across the `=
' is that you move from
left to right, pairing off characters or elements of character classes as I
already described, and when you run out, you treat any missing characters
as, well, missing.
The first part has an `anchor', indicated by what lies between the `:
'
and the `|
'. The B
specifies that the case insensitive match of
`no
' must occur at the start of the word on the command line (with
`b
' it would be the word in the list of matches), but here it is
lax enough to allow this to happen after the `M:_=
' has stripped any
initial underscores away. Hence
it matches no
, NO
, No
or nO
at the start of the
string, and, just like the `M:_=
' part, it ignores it, since there's
nothing on the right. Again, the capital `B
' at the start means
keep what's on the command line: that's important in this case, since
if you lost the `no
', the meaning would change completely.
So consider the combined effect when trying to complete NO_GL
. The
first specification allows it to match against _GL
; the second allows
it to match against GL
; the third, against gl
; and finally the
usual effect of completion means that any option beginning gl
may be
completed. Try `setopt NO_GL^D
' and you should see something like:
NO_GLob NO_GLobassign NO_GLobdots NO_GLobalrcs NO_GLobcomplete NO_GLobsubst--- after the bit you've typed, the form of the words reverts to whatever's in the trial completion, i.e. lowercase letters with no `
_
's.
This example shows the other sort of anchoring, on the right, and also how
to use a `*
' in the right hand part of a pattern. Consider:
zstyle ':completion:*' matcher-list 'r:|.=* r:|=*'The `
r:
' specifies a right-anchored match, using the characters from the
trial completion rather than what's already on the command line. As the
anchor is on the right this time, the pattern (between `:
' and `|
')
is empty, and its anchor (between `|
' and `=
') is `.
'. So this
specifies that nothing --- a zero length string, or a gap between
characters if you want to think of it like that --- when followed by a
`.
', matches anything at all in the trial completion.
Consequently, the second part says that nothing anchored on the right by nothing --- in other words, the right hand end of the command line string --- matches anything. This is what completion normally does, add anything at all at the end of the string; we've added this part to the matcher in case the cursor is in the middle of the word. It means that the right hand end will always be completed, too.
Let's see that in action. Here are the actual contents of my actual
tmp
directory, never mind why:
regframe.rpm t.c testpage.dvi testpage.log testpage.psNow I set the
matcher-list
style as above and type:
echo t.p<TAB>and get
echo testpage.psSo, apart from the normal completion at the end (
p
to ps
), the
empty string followed by a .
was allowed to match anything, too, and I
got the effect of completing both bits of the word.
You might wonder what happens when there's a file testpage.old.ps
around, i.e. the anchor appears twice in that. With the matcher set as
given above, that won't be completed; the anchor needs to be matched
explicitly, not by a wildcard. If you don't like that, you can change the
`*
' after the `=
' in the specification to `**
'; this form
allows the anchor to occur in the string being matched. You can think of
`*
' and `**
' as taking the shortest and the longest possible
matches respectively. If you use a lot of `**
' specifications in your
matches, things can get very confusing, however.
Other shells have a facility for completing inside words like this, where
it goes by such names as `enhanced' completion, although it is usually not
so flexible. In the case of tcsh, not just `.
' but also `-
' and
`_
' have this effect. You can force this with
zstyle ':completion:*' matcher-list 'r:|[._-]=* r:|=*'
I've mentioned `r
' and `B
', but corresponding to `r
' there is
`l
', which anchors on the left instead of the right, and corresponding
to `B
' there is `E
' which matches at the end instead of the
beginning; and, of course, all exist in both upper- and lowercase forms,
meaning `keep what the user typed' and `keep what is in the list of
possible matches', respectively.
Here is an example of using `l:|=*
' to match anything at the start of
the word: this is the effect of having an empty anchor, as you saw with
`r
' above, but note with `l
', the anchor appears, logically enough,
on the left of the `|
', in the order they would appear on the command
line. By combining this with the `r
' form, you can make the completion
system work when what is on the command line matches only a substring of a
trial completion --- i.e., has anything else on the left and on the right.
Since this can potentially generate a lot of matches, it might by an idea
to try it after any other matcher specifications you have. So the following
tries case-insensitive completion, then partial-word completion
(case-sensitively), then substring completion:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' \ 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
This section illustrates another feature: if you use `||
' when
specifying anchors for `L
' or `R
' or their lowercase variants,
the pattern part for what appears on the command line, which would usually
be translated into some other pattern, is treated instead as another anchor
on the other side of the pattern --- which isn't matched against the
pattern in the word, it just has to appear. In other words, this part
matches without being `swallowed up' in the process. An example (again
adapted from the manual) will make this clearer.
compadd -M 'r:[^A-Z0-9]||[A-Z0-9]=** r:|=*' \ LikeTHIS LooHoo foo123 bar234The four possible completions are on the second line. The second of the two matcher specifications just allows anything to match on the right, so if we are inside the word, the remainder may be completed. The first word is where the action is; it says `A part of the completion which has on the left something other than an upper case letter or a digit, and on the right an upper case letter or a digit, may match anything, including the anchor'. So in particular, this would allow `
LH
' to complete to `LooHoo
' ---
and only that, since `LikeTHIS
' has an uppercase letter to the left of
the `H
', which is not allowed. In other words, the chunks of word
beginning with uppercase letters and digits act like the start of
substrings. (If you like, remember that last sentence and the
specification, and forget the rest.)
To put everything together, the possible specifications are
`m:...=...
', `l:...|...=...
', `r:...|...=...
',
`b:...|...=...
' and `e:...|...=...
', which cause
the command line to be altered to the match found, and their counterparts
with an uppercase letter, which cause what's already on the command line to
be left alone and the remaining characters to be inserted directly from the
completion found. The `...
' are patterns, which all use the same
format. They can include literal characters, a `?
', and character or
correspondence classes, while the rightmost pattern in each type may also
consist of a `*
' on its own. Characters are matched from left to
right; a missing character matches an empty string, `*
' matches any
number of characters. Specifications may be joined in a single string, in
which case all parts will be applied together.
When using the matcher-list
style, a list of different specifications
can be given; in this case, they will be tried in turn until one of them
generates matches, and the rest will not be used.
There's another style apart from matcher-list
, called matcher
.
This can be set for a particular context, possibly with specific tags, and
will add the given matcher specifications using exactly the same syntax as
matcher-list
for that context, except that here all specifications are
used at once, even if they are given as different elements of an array.
This is possibly useful because matcher-list
is only aware of the
completer, not of any more specific part of the context.
Although I won't talk about matching control after this section, there may
be cases where you want to include `compadd -M ...
' in a completion
function of your own to help the user. Many of the existing completion
functions provide partial word completion where it seems useful; for
example, completion of zle functions allows i-c-w
to be completed to
incremental-complete-word
in this way.
Actually, you can configure this to a considerable extent without altering a function, using styles and labelled tags. From the manual:
zstyle ':completion:*:*:foo:*' tag-order '*' '*:-case' zstyle ':completion:*-case' matcher 'm:{a-z}={A-Z}'In command
foo
, whatever the tags are, they are to be tried normally
first (the `*
' argument to tag-order
), then under the same name
with `-case
' appended. The second style defines a matcher for any tag
ending in the suffix `-case
', which allows lowercase characters to
match uppercase ones. The upshot is that completion of anything at all for
the command foo
will be tried first case-sensitively, then
case-insensitively.
Before bamboozling you with everything there is to know about writing
your own completion function, I'll give you an example of something I
wrote myself recently. If you were doing this yourself, you would then
just stick this function somewhere in your function search path, and
next time you started the shell it would start doing its work. However,
the file already exists: it's called _perforce
and you should find
it in the function search for versions 4.1.1 and above of zsh. I
apologize if it's not the ideal function to start with, but it is fresh
in my mind, so what I'm saying has some chance of being correct.
This section is subtitled, `How I struggled to write a set of completions for Perforce'. Perforce is a commercial configuration management tool (as they now call revision control systems); consult http://www.perforce.com/ for details. It's concepts aren't a million miles from CVS, the archetypal system of this kind, but it was sufficiently different that the completion functions needed rewriting from the ground up. You won't need to know anything about CVS or Perforce, because at each stage I'll explain what I'm trying to complete and why. This should give you plenty of meat for writing completions of your own. After the tutorial, the chapter goes into the individual details, which will expand on some of the things that appeared briefly in the tutorial.
What I tend to find the most complicated part of this is making sure the completion system knows the correct types of completions and their tags to be completed at once. This probably won't be your first priority when trying to write completions of your own, but if you do it right, all the stuff about selecting types and arranging them in groups that I showed above will just work. In this tutorial we arrange to use enough of the higher level functions that it will work without too much (apparent) effort. Of course, working out from scratch which those functions are isn't always that easy; hence the tutorial.
Needless to say, I will simplify grossly at a lot of points. You can see the finished product in the zsh 4.1 distribution. It even has a few comments in.
Basic structure
Like the cvs
command and a few other of the more complicated
commands you might use, Perforce is run by a single command, p4
,
followed by an argument giving the particular Perforce command, followed
by an options and arguments to that command.
This dictates the basic tasks the completion functions must do:
In addition to the subcommands, there are lots of other types of object Perforce knows about: files, obviously, plus revisions of files, set of changes (`changelists') applied at once, numbers of fixes applied to files (essentially a way of tying changlists to a particular change request for bugtracking purposes), types of file --- text, binary, etc., and several others. We will break down each of these completions into its own function. That means that any time we need to complete a particular type of object, wherever it appears (and many of these objects can appear in lots of different places), we just call the same function.
Hence there are a large number of different functions:
_perforce
for
clarity --- the main command it handles is `p4
', but the
name Perforce is more familiar.
_perforce
, we don't need to tell the
shell to autoload all the other functions; it can just hook them in from
the main file. The file _perforce
therefore has the structure:
#compdef p4 # Main dispatcher _perforce() { # ... } # Helper functions for the various types of object _perforce_files() { # ... } # ... # Dispatchers for the individual subcommands. _perforce_cmd_help() { # ... } # Code to make sure _perforce is run when we load it _perforce "$@"That last line is probably the least obvious. It's because of the fact that zsh (unlike other shells) usually treats the file of an autoloaded function as being the body of the function. Since everything else here just defines a function, without the last line nothing would happen the first time it was run; it would define
_perforce
and all the other
functions, but that was it. The last line makes sure _perforce
gets
run with all the arguments passed down. The shell is smart enough to
know that the _perforce
function we defined in the file is the one to keep
for future use, not the entire file, so from then on things are easy; we
just have a complete set of ready-defined files.
In fact the various helper functions didn't even need to use the `_
'
convention for completion functions, since the completion system didn't
see them directly. However, I've kept it for consistency.
There's one extra trick: apart from _perforce
itself, the function
definitions look like this:
(( $+functions[_perforce_cmd_diff] )) || _perforce_cmd_diff() { # body of function }This is to allow the user to override each function separately. The test uses the
$functions
special associative array from the
zsh/parameter
module, which the completion system loads. If the
function is already defined, because the corresponding element in the
$functions
parameter is set, then we skip the definition of the
function here, because the user has already defined it. So if you were
to write your own _perforce_cmd_diff
and put it into the function path, it
would be used, as you no doubt intended.
This top level is only necessary for complex commands with multiple subcommands. There are interesting titbits here, but if you just want to know how to complete a command with ordinary UNIX-style argument parsing, skip to the next section.
The main _perforce
function has the two purposes described at the top of
the previous subsection. We need to decide whether we are in the first
word after the p4
command itself. A simple way of doing that is:
if (( CURRENT > 2 )); then # Remember the subcommand name local cmd=${words[2]} # Set the context for the subcommand. curcontext="${curcontext%:*:*}:p4-$cmd" # Narrow the range of words we are looking at to exclude `p4' (( CURRENT-- )) shift words # Run the completion for the subcommand _perforce_cmd_$cmd else local hline local -a cmdlist _call_program help-commands p4 help commands | while read -A hline; do (( ${#hline} < 2 )) && continue [[ $hline[1] = (#i)perforce ]] && continue cmdlist=($cmdlist "${hline[1]}:${hline[2,-1]}") done _describe -t p4-commands 'Perforce command' cmdlist fi
This already looks a bit horrific, but it breaks down quite easily.
We test the $CURRENT
parameter, which is a special parameter in the
completion system giving the word on the command line we are on. This
is the syntactic word --- the completion system has already done the
hard job (and that's not an overstatement, I can tell you) of deciding
what makes up a word on the command line, taking into account quoting
and special characters. The array of words is stored, unsurprisingly,
in the array $words
. So word 1 will be `p4
' and word 2 the
subcommand.
Hence if we are past word 2, we look at ${words[2]}
to get the
subcommand, and use that to decide what to do next. The change to
$curcontext
is a bit of cleverness to make it easy for the user to
defined styles for particular subcommands; refresh your mind by looking
at the discussion of styles and contexts above if you need to. For
example, if you are completing after `p4 diff
', the context will
look something like
`:completion::complete:p4-diff:argument-1:opened-files
'
where the remainder says you are on the first argument and are complete
the tag `opened-files
', We'll see down below how we tell the system
to use that tag; the `argument-1
' is handled by the _arguments
utility function, which takes away a lot of the load of handling options
and arguments in a standard UNIX format.
Next, we pretend that the `p4
' at the start wasn't there by removing
the front of $words
and decrementing $CURRENT
so as to reflect
its new position in $words
. The reason for doing this is that we
are going to use _arguments
for handling the subcommand. As is only
sensible, this function looks at the first element of $words
to find
the command word, and treats the rest as options or arguments to the
command.
We then dispatch the right function for the command simply by
constructing the name of the function on the fly. Of course it's a
little neater to check the function exists first;
$+functions[_perforce_cmd_$cmd]
would come to our aid again.
However, if we're still on the second (original) word, we have to
generate a list of functions to complete. We will do this by asking
Perforce's help system to list them, and store the results in the array
$cmdlist
. The loop has a couple of checks to remove blank lines and
the title line at the start. The remaining lines have a command and a
description. We take the command, but also tack the description on
after a colon --- we can then show the user the description, too, as a
bit of extra help.
Actually, the Perforce command that generates the list of subcommands is
simply `p4 help command
'. (That's really all you need to know; skip
the rest of the paragraph if you just want the basics.) The
`_call_program help-commands
' was stuck in front for the name of
configurability. Before executing the command, the system checks in the
current context with the given tag help-commands
for the style
command
. If it finds a value for that style, it will use that as
the command to execute in the place of the remaining arguments. If the
style it read began with -
, then the command it was going to execute
--- i.e. `p4 help commands
' is appended to the end of the command
read from the style, so that the user's command can process the original
command if it needs to. This is really extreme sophistication; you will
rarely actually need the command
style, but if you are writing a
completion for others to use it's polite to give them a chance to
intercept calls in this way.
The _describe
command then does the work for us. The `-t
p4-commands
' gives the tag we are going to use; the convention is that
tag names are plural, though there's nothing to enforce this. Then we
give an overall description --- this is what appears after
`Completing
' in the examples of the format
style above; if you
don't have that set, you won't see it. Finally, we give the array name
--- note it is the name, not the substituted value. This is more
efficient because the shell doesn't need to extract the values until the
last minute; until then it can pass around just the single word. The
_description
function knows about the `completion:description
'
syntax; reread what I said about the verbose
style for what the
system does with the descriptions for the completion.
The _describe
function is one level above the completion system's
basic builtin command, compadd
; it just knows about a single tag,
with a little icing sugar to display verbose descriptions. Later, we'll
see ways of building up alternatives where different types of completion
can be completed at the same point. There are lots of ways of doing
this; some of the more complicated are relegated to the detailed
descriptions that follow the tutorial.
_arguments
Suppose we are now completing after `p4 diff
'. We have altered the
command line so that the function now sees the `diff
' as the first
word, as if this were the command. This makes the next step easier; the
_arguments
function won't see irrelevant words on the command line,
since it is designed to handle the arguments to a simple command
in the standard form `command [ options ] arguments ...
'.
Here's the simple version.
_perforce_cmd_diff() { _arguments -s : \ '-f[diff every file]' \ '-t[include non-text files]' \ '(-sd -se -sr)-sa[opened files, different or missing]' \ '(-sa -se -sr)-sd[unopened files, missing]' \ '(-sa -sd -sr)-se[unopened files, different]' \ '(-sa -sd -se)-sr[opened files, same as depot]' \ '-d-[select diff option]:diff option:'\ '((b\:ignore\ blanks c\:context n\:RCS s\:summary'\ 'u\:unified w\:ignore\ all\ whitespace))' \ "*::file:_perforce_files" }I've split the argument beginning
-d
into three lines to fit, but
it's just a single argument. Also, for clarity I've missed out the line
with the `$+functions
' test to see if _perforce_cmd_diff
was already
defined; I'll forget about that for now.
The function _arguments
has been described as having `the syntax
from hell', but with the arguments already laid out in front of you it
doesn't look so bad. The are three types of argument: options to
_arguments
itself, arguments saying how to handle options to the
command (i.e. `p4 diff
'), and arguments saying how to handle normal
arguments to the command.
The first two are for _arguments
itself; `-s
' tells it that
single-letter options are allowed, i.e. they can be combined as in
`-ft
'. Luckily for our purposes, that doesn't stop us having
multiple word options, too. The colon on its own then says everything
else is an argument relating to the command line being handled.
We then start off with some simple options; as you can probably guess
straight away, the first two say that `p4 diff -f
' passes a flag to
say any file can be diff'ed (not just ones open for editing), and that
`p4 diff -t
' passes a flag to say that binary files can be diff'ed
(not just text files). Note the use of square brackets for giving a
description; this is handled by the verbose
style as I mentioned for
_describe
. In fact, the list of possible options and arguments,
suitably rearranged, will end up passing through _describe
.
The descriptions in square brackets are optional, as the use of square
brackets might suggest; you could just have `-f
' and `-t
'
(making it fairly obvious why the `:
' to separate off _arguments's
own options is a good idea).
The next step in complexity is that set of functions with the list in
parentheses in front. These give mutually exclusive options. In other
words, if there's already a -sa
on the command line, don't complete
any of -sd
, -se
or -sr
, and so on. (Remember that by
default you need to type the first `-
' of an option, or the system
will go straight to normal arguments, which we'll come to in a moment.)
Next comes the specification for the option -d
. All those colons
indicate that this option has an argument, and the -
following
straight after the -d
indicates that it has to be in the same word,
i.e. follow the -d
without a space. After the first colon comes
a description for the argument. This is what you see when you try to
complete the after -d
; compare this with the expression in
square brackets before, which is what you see when you try to complete
the -d
itself. Then after the second colon is an expression saying
how to complete that argument.
This final part of the specification for an option with an argument can
take various forms. The simplest is just a single space; this means
there's nothing to complete, but the system is aware the user needs to
type something for that word and can prompt with the description. The
next simplest is a set of words in parentheses: here, we could have had
`(b c n s u w)
'. Instead, we've had a variant on that which gives
yet another set of descriptions, namely those for the individual
completions that appear after -d
. Note various things: the
parentheses are doubled and the colons and spaces within the completion
options are backslashed. All of these are simply there to make it easy
for _arguments
to parse the string. The upshot of this is that in
the following context:
p4 diff -da verbose completion using the
format
style as described above looks like:
Completing diff option b # ignore blanks c # context n # RCS s # summary u # unified w # ignore all whitespaceor similar --- I have the
list-separator
style set to `#
', because it looks like a comment normal shell
syntax, but in your case you may get `-
-
' as the separator.
(In case you were wondering why the colons needed to be quoted when it
seemed you'd already got to the last argument: it's possible for
options to have multiple arguments, and you can continue having
sets of :
description:
action pairs. This means the
system needs some way of distinguishing these colons from ones inside
arguments. While I'm digressing, you may also have noticed that I could
have written the -s
X as an option with arguments, in which case
you can have a bonus point.)
The final argument starts with a `*
', which means it applies to all
remaining arguments to `p4 diff
' after the options have been
processed. Most of the rest is similar to the form for options, except
for the doubled colon, which indicates that $CURRENT
and $words
should be altered to reflect only the arguments being handled by this
argument specifier --- exactly what we did before calling
_perforce_cmd_diff
in the first place, in fact. As we mentioned before,
this makes the next step of processing easier if happens to call
_arguments
again. (Actually it doesn't in this case.) The
`file
' then describes the arguments and the final part,
_perforce_files
, tells the system to call that function to complete a
file name.
There are numerous (it sometimes seems, endless) subtleties to
_arguments
. I won't try to go into them in the tutorial; see the
description of _arguments
below for something more detailed to refer
to, and if you are feeling really brave look at the description in
the zshcompsys
manual page. Even better, dig into one of the
existing completion functions --- something handling completion for a
UNIX command is probably good, since these make heavy use of
_arguments
--- and see how those work. Despite the complexities, I
would definitely suggest using _arguments
wherever possible to take
away any need on your part to do processing of command line arguments.
Now we'll look inside the _perforce_files
function as an example of the
nitty gritty of completing one particular type of argument, which might
have some quite complicated internal structure. This is true in
Perforce as the filename can have extra information tacked on the end:
`file#
revision' indicates the revision of a file,
`file@
change' indicates a change status, and in some cases
you can get `file@
change1,
change2' to indicate a range
of changes (likewise revisions). Furthermore, file
can be specified
in different ways, and the file to be completed may be limited by some
kind of context information. We'll start from simple filenames and
gradually add these possibilities in.
Different types of file, part 1
There are so many possibilities for files that I'm going to split up
_perforce_files
into individual functions handling different aspects.
For example, even if we are just handling ordinary files in the way the
completion system normally does, Perforce commands understand a special
file name `...
' which means `every subdirectory to any depth'.
(Interestingly, zsh used to have this to mean the same thing, instead of
`**
'; it was changed in zsh because as the `.
's are regular
characters there's no easy way of quoting them. You didn't need to know
this.)
I'm going to say we can complete both like this:
_alternative \ "files:file:_path_files" \ "subdirs:subdirectory search:_perforce_subdir_search"The function
_alternative
is a little bit like _arguments
, but
thankfully much simpler. It's name gives away its purpose; every
argument specifies one of a set of possible alternatives, all of which
are valid at that point --- so the user is offered anything which
matches out of the choices, unlike _arguments
, which has to decide
between the various possibilities. It's a sort of glorified loop around
`_describe
', with _arguments
's conventions on the action for
generating completions (up to a point --- _alternative
doesn't have
all the whackier ones, though it does have the ones I've been talking
about so far).
Each set of possibilities consists of the name of a tag, a description,
and an argument. The tag isn't present in _arguments
. If you use
^xh
to tell you about valid tags, you'll see _arguments
has its
own generic tag, argument-rest
; this isn't usually all that useful,
so we are going to supply more specific ones.
In the first possibility, it's the standard one for files, `files
.
The function is the basic low-level one for completing files, too; it's
described below, but you already know a lot about the effect since it's
the completion system's workhorse which you use it all the time without
realising. Actually, it will supply its own tags, but that doesn't
matter since they will silently override what we say.
The second possibility is the new one we're adding. I've therefore
invented a suitable tag `subdirs
', a description, `subdirectory
search
', and the name of the function I'm going to supply to do the
completion. This is quite simple:
_perforce_subdir_search() { compset -P '*/' compadd "$@" '...' }The first line tells the completion system to ignore anything up to the last `
/
'. That's so we can append a `...
' to any directory
which already exists on the command line. The builtin compset
does
various low-level transformations of this time. Note that the -P
is
`greedy' --- it looks for the longest possible pattern match, which is the
usual default in zsh and other UNIX pattern matchers.
The second line actually adds the `...
' as a completion; compadd
is the key builtin for the whole completion system. I've actually
passed some on the arguments which we got to `_perforce_subdir_search
' via
`"$@"
'. In fact, looking back it seems as if there weren't any!
However, _alternative
actually passed some behind my back --- and
it's a good thing, too, since it's exactly those arguments that give the
tag `subdirs
' and the description `subdirectory search
'. So
that extra `"$@"
' is actually quite important. The buck stops here;
there's nothing below compadd
. A function of this simplest only
works well when the handling of tags and contexts has already been done;
but we just saw that _alternative
did that, so as long as we always
call _perforce_subdir_search
suitably, we're in the clear.
Different types of file, part 2
Furthermore, a Perforce file specification can look like a normal UNIX file path, or it can look like:
//depot/dirs/moredirs/file(don't get confused with paths to network resources, which also use the doubled slash or backslash on some systems, notably Cygwin). We could use
_alternative
to handle this, too, and if I was writing _perforce
again I probably would for simplicity. However, I decided to do it just
by testing for the `//
' in _perforce_files
. This means that the
structure of p4_files
so far looks like:
if [[ $PREFIX = //* ]]; then # ask Perforce for files that match local -a altfiles altfiles=( 'depot-files:file in depot:_perforce_depot_files' depot-dirs:directory in depot:_perforce_depot_dirs' ) # add other alternatives, such as the `...' thing altfiles=($altfiles "subdirs:subdirectory search:_perforce_subdir_search" ) _alternative $altfiles else _alternative \ "files:file:_path_files" \ "subdirs:subdirectory search:_perforce_subdir_search" fiwhere we are still to write the functions for the first two alternatives in the first branch; the `
...
' is still valid for that branch, so
I've added that as the third alternative. I've used the array
$altfiles
because, actually, the structure is more complicated than
I've shown; doing it this way makes it easier to add different sets of
alternatives.
The choice of which branch is made by examining the $PREFIX
special
variable, which contains everything (well, everything interesting) that
comes before the cursor position in the word being completed. There is
a counterpart $SUFFIX
which we will see in a moment. The `almost
everything' comes because sometimes we definitely don't want to see the
whole $PREFIX
. Completing the three dots was such as case --- we
didn't want to see anything up to the last /
. What that `compset
-P '*/'
' actually did was move the matched pattern from the front of
$PREFIX
to the end of $IPREFIX
, another special parameter which
contains parts of the completion we aren't currently interested in, but
which are still there. This allows us to concentrate on a particular
part of the completion. However you do that --- whether by compset
or directly manipulating $PREFIX
and friends --- the completion
system usually restores the parameters when you exit the function where
you altered them. This fits in nicely with what we're doing here with
_alternative
--- if we handle adding `...
' by ignoring
everything up to the last slash, for example, we don't want the next
completion we try to continue to ignore that; other file completions
will want to look at the directory path.
`Depot' is Perforce's name for what CVS calls a repository --- the
central location where all versions of all files are stored, and from
where they are retrieved when you ask to look at one. I've separated
out `depot-dirs
' and `depot-files
' for various reasons. First,
the commands to examine files and directories are different, so the
completion function is different. Second, we can offer different tags
for files and directories --- this is what _path_files
does for
normal UNIX files. Third, it will later allow us more control --- some
commands only operate on directories. Here's _perforce_depot_files
;
_perforce_depot_dirs
is extremely similar:
_perforce_depot_files() { # Normal completion of files in depots local pfx=${(Q)PREFIX} expl local -a files compset -P '*/' files=(${${${(f)"$(\ _call_program files p4 files \ \"\$pfx\*\$\{\(Q\)SUFFIX\}\" 2>/dev/null)"}%\#*}##*/}) [[ $#files -eq 1 && $files[1] = '' ]] && files=() compadd "$@" -a files }A little messy (and still not quite the full horror). I've split the key line in the middle which fetches the list from Perforce to make it fit. If you ploughed through chapter 5, you'll recognised what's going on here --- we're reading a list of files, one per line, from the command `
p4 files
', and we're stripping off the directory at the
front, and everything from a `#
' on at the end. The latter is
a revision number; we're not handling those at this point, though we
will later.
Notice the way I remembered $PREFIX
before I told the system to
ignore it for the word we're now completing. I remembered it as
`${(Q)PREFIX}
' in order to remove any quotes from the name. For
example, if the name on the line so far had a space, $PREFIX
(which
comes from what is on the command line without any quotes being
stripped) would have the space quoted somehow, e.g. `name\ with\
space
'. We arrange for $pfx
to contain `name with space
',
which is how Perforce knows the file, using the (Q)
parameter flag.
We then pass the argument "$pfx*${(Q)SUFFIX}"
to `p4 files
';
this generates matching files internally. The extra layer of
backslash-quoting is for the benefit of _call_program
, which
re-evaluates its arguments; this ensures the argument is expanded at the
point it gets passed to p4 files
. All this goes to show just how
difficult getting the quoting right can be.
Once we've got the list of bare filenames, we check to see if the list
is just one element with no length. That's an artefact of the the
"$(cmd)"
syntax; if the output is empty, because its quoted you
still get one zero-length string output, which we don't want.
Finally, we pass the result to compadd
as before. Again, tags and
the description have already been handled and we just need to make sure
the appropriate options get passed in with "$@"
. This time we use
the `-a
' option which tells compadd
that any arguments are array
name, not a list of completions. This is more efficient; compadd only
needs to expand the array internally instead of the shell passing a
potentially huge list to the builtin.
Handling extra bits on a completion
`Extra bits' on a completion could be anything; common examples include
an extra value for a comma-separated list (the _values
functions is
for this), or some kind of modifier applied to the completion you have
already. We've already seen an example, in fact, since the principle of
handling the directory and basename parts of a file is very similar.
The phrase `extra bits' may already alert you to the fact that we are
heading towards the deeper recesses of completion.
Anyway, here's how we tack a revision or change number onto the end of a file.
I'll stick with revisions: `filename#
revision', where
revision is a number. For the full sophistication, there are three
steps to this. First, make it easy for the user to add `#
' to an
existing filename; second, recognise that a `#
' is already there so
that revisions need to be completed; third, find out the actual
revisions which can be completed. As a revision is just a number, you
might think completing it was a bit pointless. However, given the
sophistication of zsh's completion system there's actually one very good
reason --- we can supply a description with the revisions, so that the
user is given information about the revisions and can pick the right one
without running some external command to find out. There was the same
sort of rationale behind the `-d
' option to p4 diff
; there was
just one letter to type, but zsh was able to generate extra information
to describe the possibilities, so it wasn't just laziness.
First part: make it easy for the user to add the `#
'. This
actually depends on a new feature in version 4.1 of zsh; in 4.0 you
couldn't play the trick we need or grabbing the keyboard input after a
completion was finished unless you specified a particular suffix to add
to the completion (such as the `/
' after a directory --- this is
historically where this feature came from).
The method is to add an extra argument everywhere we complete a file
name. For example, change the compadd
in _perforce_depot_files
to:
compadd "$@" -R _perforce_file_suffix -a fileswhere the option argument specifies a function:
_perforce_file_suffix() { [[ $1 = 1 ]] || return if [[ $LBUFFER[-1] = ' ' ]]; then if [[ $KEYS = '#' ]]; then # Suffix removal with an added backslash LBUFFER="$LBUFFER[1,-2]\\" elif [[ $KEYS = (*[^[:print:]]*|[[:blank:]\;\&\|@]) ]]; then # Normal suffix removal LBUFFER="$LBUFFER[1,-2]" fi fi }This has been simplified, too; I've ignored revision ranges in the form file
#
rev1,
rev2. However, I've handled changes
(`@
' following a filename) as well as revisions. You'll see this
function looks much more like a zle widget rather than a completion
widget --- which is exactly what it is; it's not called as part of the
completion system at all. After the specified completion, zle reads in
the next keystroke, which is stored in $KEYS
, and calls this
function as a zle widget. This means it can manipulate the line buffer;
we only need to look at what is at the left of the cursor, stored in
$LBUFFER
.
The function is called with the length of the suffix added to the function. In this case, it's just a space --- we've finished a normal completion, so the system has automatically added a space to what's on the command line. We therefore check we've just got one single character in the suffix, to avoid getting confused.
Next, we look at what's immediately left of the cursor, which is the
last character in $LBUFFER
, i.e. $LBUFFER[-1]
, to make sure this
is a space.
If everything looks OK, we consider the keys typed and decide whether to
modify the line. You may already have noticed that in some cases zsh
automatically removes that space by itself; for example, if you hit
return --- or any other non-printing character --- or if it's a
character that terminates a command such as `&
' or `;
'. We
emulate that behaviour --- most of the second test is simply to do
that. The only differences from normal are if the key typed was `@
'
or `#
'.
The `@
' is simple --- we just remove the last character, the same as
we do for the other characters. For `#
', however, we also add a
backslash to the command line before the `#
'. That's because
`#
' is a special character with extended globbing, and the
completion system generally runs with extended globbing switched on.
Adding the backslash means the user doesn't have to; it's never
harmful.
To show the next effect, suppose we complete a file name:
p4 diff fil<TAB>to get:
p4 diff filename _where `
_
' shows the cursor position, and then typed `#
'; we
would get:
p4 diff filename\#with the cursor right at the end.
So far so good. For the second step, we need to modify _perforce_files
to
spot that there is a `#
' on the line before the cursor, and to
call the revision code. To do this we add an extra branch at the start
of the `if
' in _perforce_files
--- at the start, because any `#
'
before the cursor forces us to look at revisions, so this takes
precedence over the other choices. When this is added, the code will
look like:
if [[ -prefix *\# ]]; then _perforce_revisions elif [[ $PREFIX = //* ]]; then # as before.In fact, that
-prefix
test is just a fancy way of saying the same
thing as the `[[ $PREFIX = *\# ]]
' and if I wasn't so hopelessly
inconsistent I would have written both tests the same.
So now the third step: write _perforce_revisions
to complete revisions
numbers with the all-important descriptions.
_perforce_revisions() { local rline match mbegin mend pfx local -a rl pfx=${${(Q)PREFIX}%%\#*} compset -P '*\#' # Numerical revision numbers, possibly with text. if [[ -z $PREFIX || $PREFIX = <-> ]]; then # always allowed (same as none) rl=($rl 0) _call_program filelog p4 filelog \$pfx 2>/dev/null | while read rline; do if [[ $rline = (#b)'... #'(<->)*\'(*)\' ]]; then rl=($l "${match[1]}:${match[2]}") fi done fi # Non-numerical (special) revision names. if [[ -z $PREFIX || $PREFIX != <-> ]]; then rl=($rl 'head:head revision' 'none:empty revision' 'have:current synced revision') fi _describe -t revisions 'revision' rl }Thankfully, a lot of the structure of this is already familiar. We extract the existing prefix before the `
#
', being careful about
quoting --- this is the filename for which we want a list of revisions.
We ignore everything in the command argument before the `#
'. After
generating the completions, we use the _describe
function to add
them with the tag `revisions
' and the description `revision
'.
The main new part is the loop over output from `p4 filelog
', which is
the Perforce command that tells us about the revisions of a file.
We extract the revision number and the comment from the line using
backreferences (see previous chapter) and weld them together with a
colon so that _describe
will be able to separate the completion from
its description. Then we add a few special non-numerical revisions
which Perforce allows, and pass this list down to _describe
. The
extra if
's are a very minor optimization to check if we are
completing a numerical or non-numerical revision.
It's obvious that this tutorial could expand in any number of directions, but as it's really just to point out some possibilities and directions, that would would miss the point. So the rest of this chapter takes the completion system apart and looks at the individual components. It should at least now be a bit more obvious where each component fits.
Now down to the nitty gritty. When I first talked about new completion,
I explained that the functions beginning `_
' were the core of the
system. For the remainder of the chapter, I'll explain what goes in
them in more detail than I did in the tutorial. However, I'll try to do
it in such a way that you don't need to know every single detail. The
trade off is that if you just use the simplest way of writing functions,
many of the mechanisms I told you about above, particularly those
involving styles and tags, won't work. For example, much of the code
that helps with smart formatting of completion listings is buried in the
function `_description
'; if you don't know how to call that ---
which is often done indirectly --- then your own completions won't
appear in the same format as the pre-defined ones.
The easiest way of getting round that is to take a dual approach: read the
following as far as you need, but also try to find the existing completion
that comes nearest to meeting your needs, then copy that and change it.
For example, here's a function that completes files ending in .gz
(the
supplied function which does this has now changed), which are files
compressed by the gzip
program, for use by the corresponding program
that does decompression, gunzip
--- hence the file and function are
called _gunzip
:
#compdef gunzip zcat local expl _description files expl 'compressed file' _files "$expl[@]" -g '*.[gG][zZ]'You can probably see straight away that if you want to design your own completion function for a command which takes, say, files ending in
.exe
, you need to change three things: the line at the top, which
gives the names of programmes whose arguments are to be completed here,
the description `compressed file
' to some appropriate string,
and the argument following the -g
to something like '*.exe'
--- any
globbing pattern should work, just remember to quote it, since it shouldn't
be expanded until the inside of the function _files
. Once you've
installed that somewhere in your $fpath
and restarted the shell,
everything should work, probably following a longer pause than usual as
the completion system has to rescan every completion function when it finds
there is a new one.
What you might miss is that the first argument to _description
,
`files
', is the all-important mystical tag for the type of completion.
In this case, you would probably want to keep it. Indeed, the _files
function is used for all file completions of any type, and knows all about
the other tags --- globbed-files
, directories
, all-files
--- so
virtually all your work's done for you here.
If you're adding your own functions, you will need your own functions
directory. This was described earlier in this guide, but just to remind
you: all you need to do is create a directory and add it to $fpath
in
either .zshenv
(which a lot of people use) or .zshrc
(which some
sticklers insist on, since it doesn't affect non-interactive shells):
fpath=(~/funcs $fpath)It's best to put it before the standard completion directories, since then you can override a standard completion function simply by copying it into your own directory; that copy will then be found first and used. This is a perfectly reasonable thing to do with any completion function --- although if you find you need to tweak one of the larger standard functions, that's probably better done with styles, and you should suggest this to us.
compdef
The first thing to understand is that top line of _gunzip
. The
`#compdef
' tag is what tells the system when it checks through all
files beginning with `_
' that this is a function implementing a
completion. Files which don't directly implement completions, but are
needed by the system, instead have the single word `#autoload
' at that
point. All files are only loaded when needed, using the usual autoloading
system, to keep memory usage down.
You can supply various options to the `#compdef
' tag; these are listed
in the `Initialization
' section of the zshcompsys(1)
manual page or
`Completion System' info node. The most useful are -k
and -K
,
which allow you to define a completion command and binding rather than a
function used in a particular context. There are also -p
and -P
which tell the system that what follows is a pattern rather than a literal
command name; any command matching the pattern will use that completion
function, unless you used -P
and a normal (non-pattern) completion
function for the name was found first.
For normal #compdef
entries, however, what comes next is a list of
command names --- or rather a list of contexts, since the form
`-context-
' can be used here. For example, the function _default
has the line `#compdef -default-
'. You can give as many words as you
like and that completion will be used for each. Note that contexts in the
colon-separated form can't appear here, just command names or the special
contexts named with hyphens.
The system does its work by using a function compdef
; it gets as
arguments more or less what you see, except that the function name is
passed as the first argument. Thus the _gunzip
completion is loaded by
`compdef _gunzip gunzip zcat
', _default
by `compdef _default
-default-
', and so on. This simply records the name of the function
handling the context in the $_comps
associative array which you've
already met. You can make extra commands/contexts be handled by an
existing completion function in this way, too; this is generally more
convenient than copying and modifying the function. Just add `compdef
<_function> <command-to-handle>
' to .zshrc
after the call to
compinit
.
It's also high time I mentioned an easy way of using the completion already
defined for an existing function: `compdef newcmd=oldcmd
' tells the
completion system that the completion arguments for `newcmd
' are to be
the same as the ones already defined for `oldcmd
'; it will complain if
nothing is known about completing for oldcmd
. This works recursively;
you can now define completions in terms of that for newcmd
. If you
happen to know the name of the completion function called, you can use
that; the following three lines are broadly equivalent:
compdef $_comps[typeset] foo compdef _vars_eq foo compdef foo=typesetsince the completion for
typeset
is stored in $_comps
along with
all the others, and this happens to resolve to _vars_eq
; but the last
example is easier and safer and the intention more obvious. The manual
refers to typeset
here as a `service' for foo
(guess what the shell
stores in the associative array element $_services[foo]
).
There's actually more to services: when a function is called, the parameter
$service
is set. Usually this will just be the name of the command
being completed for, or one of the special contexts like `-math-
'.
However, in a case like the last compdef
in the list above, the service
will be typeset
even though the command name may be `foo
'.
This is also used in `#compdef
' lines. The top of `_gzip
'
contains:
#compdef gzip gunzip gzcat=gunzipwhich says that the file provides two services, for
gzip
and
gunzip
, and also handles completion for gzcat
, but with the service
name gunzip
. Only a few of the completion functions actually care what
service they provide (you can check, obviously, by looking to see if they
refer to $service
); but you may have uses for this. Note that if you
define services with a compdef
command, all the arguments must be
in the foo=
bar form; the mixed form is only useful after a
#compdef
inside completion functions.
compadd
Once you know how to make a new completion function, there is only one
other basic command you need to know before you can create your own
completions yourself. This is the builtin compadd
. It is at the heart
of the completions system; all its arguments, after the options, are taken
as possible completions. This is the list from which the system selects
the possibilities that match what you have already typed. Here's a very
basic example which you can type or paste at the command line:
_foo() { compadd Yan Tan Tethera; } compdef _foo fooNow type `
foo
' and experiment with completions after it. If only it
were all that simple.
There are a whole list of options to compadd
, and you will have to look
in the zshcompwid(1)
manual page or the `Completion Widgets' info
node for all of them. I've already mentioned -M
and (long ago) -f
.
Here are other interesting ones. -X <description>
provides a
description --- this is used by the format
style to pass descriptions,
and if you use the normal tags system you shouldn't pass it directly; I'll
explain this later.
-P <prefix>
and -S <suffix>
allow you to specify bits which are not
treated as part of the completion, but appear on the line none the less.
In fact, they do two different things: if the prefix or suffix is already
there, it is ignored, and if it isn't, it is inserted. There are also
corresponding hidden and ignored prefixes, necessary for the full power of
the completion system, but you will need to read the manual for the full
story. The -q
option is useful with -S
; it enables auto-remove
behaviour for the suffix you gave, just like /
with the
AUTO_REMOVE_SLASH
option when completing filenames.
-J <group>
is the way group names are specified, used by the
group-name
tag; there is also -V <group>
, but the group here is not
sorted (and is distinct from any group of the same name passed to -J
).
-Q
tells the completion code not to quote the words --- this is
useful where you need to have unquoted metacharacters in the final
completion. It is also useful when you are completion something where
the result isn't going to be expanded by the shell.
-U
tells compadd
to use the list of completions even if they don't
match what's on the command line; you will need this if your completion
function modifies the prefix or suffix so that they no longer fit what's
already there. If you use this, you might consider turning on menu
completion (using compstate[insert]=menu
), since it might otherwise be
difficult to select the appropriate completion.
Finally, note the -F
and -W
options which I describe below for
_files
actually are options to compadd
too.
However, for most types of completion the possibilities will not be a simple
list of things you already know, so that you need to have some way of
generating the required values. In this section, I will describe some of
the existing functions you can call to do the hard work. In the next
section I will show how to retrieve information from some special
parameters made available by the zsh/parameter
module.
Files etc.: the function _files
You have already seen _files
in action. Calling this with no arguments
simply adds all possible files as completions, taking account of the word
on the command line to establish directories and so on.
For more specific use, you can give it various options: `-/
' means
complete directories, and, as you saw, `-g "<pattern>"
' gives a
filename generation pattern to produce matching files.
A couple of other options, which can be combined with the ones above, are
worthy of mention. If you use `-W <dir>
', then completion takes place
under directory <dir
> rather than in the current directory --- it has
no effect if you are using an absolute path. Here, `<dir>
' can also be
a set of directories separated by spaces or, most usefully since it avoids
any problems with quoting, the name of an array variable which contains the
list of possible directories. This is essentially how completion for
cd
with the $cdpath
array works. So if you have a program that
looks for files with the suffix `.mph
', first in the current directory,
then in a standard directory, say, /usr/local/oomph
', you can do
this:
local oomph_dirs oomph_dirs=(. /usr/local/oomph) _files -W oomph_dirs -g '*.mph'--- note there is no `
$
' before the variable $oomph_dirs
here,
since it should only be expanded deep inside _files
.
The system that implements $fignore
and the ignored-patterns
style
can be intercepted, if you need to, with the option `-F "<pat>"
';
`<pat>
' is an array of patterns to ignore, in the usual completion
format, in other words the name of a real shell array, or a list of values
inside parentheses. If you make sure all the tags stuff is handled
properly, ignored-patterns
will work automatically, however, and in
addition extended globbing allows you to specify patterns with exclusion
directly, so you probably won't use this feature directly unless you're in
one of your superhero moods.
In addition, _files
also takes many of the standard completion options
which apply to compadd
, for convenience.
Actually, the function _path_files
is the real engine room of the
system. The advantage of using _files
is that it prepares all the tags
for you, deciding whether you want directories to be completed as well as
the globbed files, and so on. If you have particularly specific needs you
can use _path_files
directly, but you won't get the automatic
fallback one directories
and all-files
. Because it doesn't handle
the tags, _path_files
is too lowly to do the usual tricks with label
loops, i.e. pretending `dog:-setter
' is a tag `dog-setter
' with the
usual completions for `dog
'; likewise, it doesn't implement the
file-patterns
style. So you need to know what you're doing when you
use it directly.
Parameters and options
These can be completed by calls to the _parameters
and _options
functions, respectively. Both set up their own tags, and _options
uses
the matching control mechanism described above to allow options to be given
in all the available forms. As with _files
, they will also pass
standard compadd
options down to that function. Furthermore, they are
all at a high enough level to handle tags with labels: to translate that
into English, you can use them directly without any of the preprocessing
described later on which are necessary to make sure the styles dealing
with tags are respected.
For more detailed control with options, the functions _set_options
and
_unset_options
behave like _options
, but the possible completions
are limited to options which are set or unset, respectively. However, it's
not that simple: the completion system itself alters the options, and you
need to enable some code near the top of _main_complete
(it's clearly
marked) to remember the options which were set or unset when completion
started. A straw poll based on a sample of two zsh developers revealed
that in any case many people don't like the completion system to second
guess the options they want to set or unset in this way, so it's probably
better just to stick to _options
.
Miscellaneous
There are also many other completion functions adding matches of a certain
type. These can be used in the same way as _parameters
and
_options
; in other words they do all the work needed for tags
themselves and can be given options for compadd
as arguments.
Normally, these functions are named directly after the type of matches they
generate, like _users
, _groups
, _hosts
, _pids
, _jobs
,
etc.
zsh/parameter
module
The new completion system automatically makes the zsh/parameter
module available for use. This provides an easy way of generating
arguments for compadd
. To get the maximum use out of this, you
should be familiar with zsh's rather self-willed syntax for extracting
bits out of associative arrays. Note in particular ${(k)assoc}
,
which expands to a list of the keys of the associative array $assoc
,
${(v)assoc}
, which expands to just its values (actually, so does
$assoc
on its own), and ${(kv)assoc}
which produces key/value
pairs. For all intents and purposes, the keys and values, or the pairs
of them, are in a random order, but as the completion system does it's
own sorting that shouldn't be a problem. Mostly, the important parts
for completion are in the keys, i.e. to add all aliases as possible
completions, you need `compadd ${(k)aliases}
'.
Here's a list of associative and ordinary arrays provided; for more
information on the values of the associative arrays, which could be useful
in some cases, consult the section The zsh/parameter Module in the
zshmodules(1)
manual page or the corresponding info node. First, the
associative arrays.
$aliases
, $dis_aliases
, $galiases
disable -a <alias>
to turn
them off temporarily, and global aliases as defined with alias -g
.
$builtins
, $dis_builtins
$commands
$path
directories has changed since the shell last updated
its tables; the rehash
command fixes it.
$functions
, $dis_functions
$history
historywords
ordinary array below.
$jobtexts
, $jobdirs
, $jobstates
jobs
command, and the values give you the other
information from jobs: $jobtexts
tells you what the job is
executing, $jobdirs
its working directory, and $jobstates
its state, where the bit before the colon is the most useful as it
refers to the whole job. The remainder describes the state of
individual processes in the job.
$modules
$nameddirs
foo=/mydir
' and using `~foo
') or via the AUTO_NAME_DIRS
option, the keys of this associative array give the names and the
values the expanded directories.
$options
, $parameters
_options
and _parameters
for completion, so you
will mostly not need to refer to them directly.
$userdirs
${userdirs[juser]}
' is equivalent
to having ~juser
expanded and is thus not all that interesting,
except that by doing it this way you can test whether the expansion
exists without causing an error.
Now here are the ordinary arrays, which you would therefore refer to simply
as ${reswords}
etc.
$dirstack
dirs -v
'.
Note, however that the current directory, which appears as number 0
with that command, doesn't appear in dirstack
. Of course it's
easy to add it to a completion if you want.
$funcstack
^Xh
uses this
to display which functions have been called for completion.
$historywords
$history
, this contains just the individual words of the
shell's command line history, and is therefore likely to be more useful
for completion purposes.
$reswords
, $dis_reswords
Other ways of getting at information
Since the arguments to compadd
undergo all the usual shell expansions,
it's easy to get words from other sources for completion, and you can look
in the existing completion functions for many examples. A good
understanding of zsh's parameter and command expansion mechanisms and a
strong stomach will be useful here.
For example, here is the expansion used by the _limits
function to
retrieve the names of resource limits from the limit
command itself:
print ${${(f)"$(limit)"}%% *}which you can test does the right thing. Here's a translation:
"$(limit)"
calls the command in a quoted context, which means you get
the output as if it were a single file (just type `limit
' to see what
that is). ${(f)...}
splits this into an array (it is now outside
quotes, so splitting will generate an array) with one element per line.
Finally, ${...%% *}
removes the trailing end of each array element from
the first piece of whitespace on, so that `cputime unlimited
'
is reduced to `cputime
', and so on. Type `limit ^D
', and you will
see the practical upshot of this.
That's by no means the most complicated example. The nested expansion facility is used throughout the completion functions, which adds to brevity but subtracts considerably from readability. It will repay further study, however.
compset
Up to now, I've assumed that at the start of your completion function you
already know what to complete. In more complicated cases that won't be the
case: different things may need completing in different arguments of a
command, or even some part of a word may need to be handled differently
from another part, or you need to look for a word following a particular
option. I will first describe some of the lower level facilities which
allow you to manipulate this; see the manual page zshcompwid(1)
or the
info node Completion Widgets for the details of these. Later, I will
show how you can actually skip a lot of this for ordinary commands with
options and arguments by using such functions as _arguments
, where you
simply specify what arguments and options the function takes and what sort
of completion they need.
The heart of this is the special parameters made available in completion for testing what has already been typed. It doesn't matter if there are parameters of that name outside the completion system; they will be safely hidden, the special values used, and the original values restored when completion is over.
$words
is an array corresponding to the words on the command
line --- where by a `word' I mean as always a single argument to the
command, which may include quoted whitespace. $CURRENT
is the index
into that array of the current word. Note that to avoid confusion the
ksh-like array behaviour is explicitly turned off in _main_complete
, so
the command itself is $words[1]
, and so on.
The word being completed is treated specially. The reason is that you may
only want to complete some of it. An obvious example is a file with a
path: if you are completing at `foo/bar
', you don't want to have to
check the entire file system; you want the directory foo
to be fixed,
and completion just for files in that. There are actually two parts to
this. First, when completion is entered, $PREFIX
and $SUFFIX
give
you the part of the current word before the cursor, and the remainder,
respectively. It's done like this to make it possible to write functions
for completing inside a word, not just at the end. The simplest possible
way of completing a file is then to find everything that matches
$PREFIX*$SUFFIX
.
But there's more to it than that: you need to separate off the directory,
hence the second part. The parameters $IPREFIX
and $ISUFFIX
contain a part of the string which will be ignored for completion. It's up
to you to decide what that is, then to move the bit you want to be ignored
from $PREFIX
to $IPREFIX
(that's the usual case) or from
$SUFFIX
to $ISUFFIX
, making sure that the word so far typed is
still given by $IPREFIX$PREFIX$SUFFIX$ISUFFIX
. Thus in completing
foo/bar
, you would strip foo/
from the start of $PREFIX
and
tack it onto the end of $IPREFIX
--- after recording the fact that you
need to move to directory foo
, of course. Then you generate files in
foo
, and the completion system will happily accept barrack
or
barbarous
as completions because it doesn't care about the foo
any
more.
Actually, this is already done by the the _files
and _path_files
functions for filename completion. Also, you can get some help using the
compset
builtin command. In this case, the incantation is
if compset -P "*/"; then # do whatever you need to with the leading # string up to / stripped off else # no prefix stripped, do whatever's necessary in this case fiIn other words, any initial match of the pattern `
*/
' in $PREFIX
is
removed and transferred to the end of $IPREFIX
; the command status
tells you whether this was done. Note that it is the longest possible such
match, so if there were multiple slashes, all will be moved into
$IPREFIX
. You can control this by putting a number <N>
between the
-P
and the pattern, which says to move only up to the <N>
th such
match; here, that would be a pattern with exactly <N>
slashes. Note
that -P
stands for prefix, not pattern; there is a corresponding -S
option for the suffix. See the manual for other uses of compset
; these
are probably the most frequent.
If you want to make the test made by compset
, but without the side
effect of changing the prefixes and suffixes, there are tests like this:
if [[ -prefix */ ]]; then # same as with `compset -P "*/"', except prefixes were left alone. fiThese have the advantage of looking like all the standard tests understood by the shell.
There are three other parameters special to completion. The $QIPREFIX
and $QISUFFIX
are a special prefix and suffix used when you are
dividing up a quoted word --- for example, in `zsh -c "echo hi"
', the
word "echo hi"
is going to be used as a command line in its own right,
so if you want to do completion there, you need to have it split up. You
can use `compset -q
' to split a word in this fashion.
There is also an associative array $compstate
, which allows you to
inspect and change the state of many internal aspects of completion, such as
use of menus, context, number of matches, and so on. Again, consult the
manual for more detail. Many of the standard styles work by altering
elements of $compstate
.
Finally, in addition to the parameters special to completion, you can
examine (but not alter) any of the parameters which appear in all editing
widgets: $BUFFER
, the contents of the current editing line;
$LBUFFER
, the part of that before the cursor; $RBUFFER
, the rest;
$CURSOR
, the index of the cursor into $BUFFER
(with the first
character at zero, in this case --- or you can think of the zero as being
the point before the first character, which is where insertion would take
place with the cursor on the first character); $WIDGET
and
$LASTWIDGET
, the names of the current and last editing or completion
widget; $KEYS
, the keys typed to invoke the current widget;
$NUMERIC
, any numeric prefix given, unset if there is none, and a few
other probably less useful values. These are described in the
zshzle(1)
manual page and the Zsh Line Editor info node. In
particular, I already mentioned $NUMERIC
as of possible use in various
styles, and it is used by the completers which understand a `numeric
'
value in their relevant styles; the $WIDGET
and $KEYS
parameters
are useful for deciding between different behaviours based on what the
widget is called (as in _history_complete_word
), or which keys are used
to invoke it (as in _bash_completions
).
Here are a few examples of using special parameters and compset
.
One of the shortest standard completions is this, _precommand
:
#compdef - nohup nice eval time rusage noglob nocorrect exec shift words (( CURRENT-- )) _normalIt applies for all the standard commands which do nothing but evaluate their remaining arguments as a command, with some change of state, e.g. ignoring a certain signal (
nohup
) or altering the priority (nice
).
All the completion system does here is shift the first word off the end of
the $words
array, decrement the index of the current word into
$words
, and call _normal
. This is the function called when
completion occurs not in one of the special -context-
s, in other words
when an argument to an ordinary command is being completed. It will look
at the new command word $words[1]
, which was previously the first
argument to nohup
or whatever, and start completion again based on that,
or even complete that word itself as a command if necessary. The net
effect is that the first word is ignored completely, as required.
Here's just an edited chunk of the file _user_at_host
; as its name
suggests, it completes words of the form <user>@<host>
, and it's used
anywhere the user-hosts
style, described above, is appropriate:
if [[ -prefix 1 *@ ]]; then local user=${PREFIX%%@*} compset -P 1 '*@' # complete the host for which we want the user else # no @, so complete the user fiWe test to see if there is already a `
<user>@
' part. If there is, we
extract the user with an ordinary parameter substitution (so ordinary even
other shells could do it). Then we strip off that from the bit to be
completed with compset
; we already know it matches the prefix, so we
don't need to test the return value. Then we just do normal hostname
completion on what remains --- except that the user-hosts
style might be
able to give us a clue as to which hosts have such a user. If the original
test failed, then we simply complete what's there as a user.
Finally, here is essentially what the function _most_recent_file
uses
to extract the $NUMERIC
th (default first) most recently modified file.
local file file=($~PREFIX*$~SUFFIX(om[${NUMERIC:-1}]N)) (( $#file )) && compadd -U -i "$IPREFIX" -I "$ISUFFIX" -f -Q - $fileInstead of doing it with mirrors, this uses globbing qualifiers to extract the required file;
om
specifies ordering by modification time, and the
expression in square brackets selects the single match we're after. The
N
turns on NULL_GLOB
, so $file
is empty if there are no
matches, and the parameter expansions with `$~
' force patterns in
$PREFIX
and $SUFFIX
to be available for expansion (a little extra
feature I use, although ordinary completion would work without).
Most of the compadd
command is bookkeeping to make sure the parts of
the prefix and suffix we've already removed, if there are any, get passed
on, but the reason for that deserves a mention, since normally this is
handled automatically. The difference here is that -U
usually replaces
absolutely everything that was in the word before, so if you need to keep
it you have to pass it back to compadd
. For example, suppose you were
in a context where you were completing after `file=...
and you had told
the completion system that everything up to `file=
' was not to count
and not to be shown as part of the completion. You would want to keep that
when the word was put back on the command line. However, `-U
' would
delete that too. Hence the `-i "$IPREFIX"
' to make sure it's retained.
The same argument goes for the ignored suffix. However, there's currently
no way of getting _most_recent_file
to work on only a part of a string,
so this explanation really only applies when you call it from another
completion function, not directly from the command line.
At this point, you should be in a position to construct, although maybe not
in the best possible way, pretty much any completion list you want. Now I
need to explain how you make sure it all fits in with the usual tags and
styles system. You will need to pick appropriate tags for your
completions. Although there is no real restriction, it's probably best to
pick one of the standard tags, some of which are suitably general to cover
just about anything: files
, options
, values
, etc. There is a
list in the completion system manual entry. Remember that the main use for
tags is to choose what happens when more than one tag can be completed in
the same place. Finding such things that can't be separated using the
standard tag names is a good reason for inventing some new ones; you don't
have to do anything special if the tag names are new, just make sure
they're documented for anyone using the completion function.
How to call functions so that `It Just Works'
The simplest way of making your own completion function recognize tags
is to use the _description
function, which is usually called with
three arguments: the name of the tag you're completing for, the name of a
variable which will become an array containing arguments to pass to
compadd
, and the full description. Then you have to make sure
that array gets passed down to compadd
, or to any of the higher-level
completion functions which will pass the arguments on to compadd
.
For example,
local expl _description files expl 'my special files' _files "$expl[@]"This sets the files tag;
_description
sets $expl
to pass on the
description, and maybe other things such as a group name for the tag, in
the appropriate format; we pass this down to _files
which will use it
for calling compadd
. Generally, you will call _description
for
each time you call compadd
or something that in turn calls compadd
.
The _description
function calls another function _setup
to do much
of the setting up of styles for the particular tag. Mostly, _setup
is
buried deeply enough that you don't need to worry about it yourself.
Sometimes you can't do completion, and just want to print a message
unconditionally to say so, irrespective of tags etc.; the function
_message
does this, taking the message as its sole argument.
There are two levels above that; these implement the tags mechanism in
full. In _description
, all that happens is that the user is informed
what tag is coming up; there's no check what preferences the user has for
tags (the first level), nor whether he wants tags to be split up using the
labelling mechanism, e.g. picking out certain sorts of files using the
labelled tag `file:-myfiles
' to get the final tag `file-myfiles
'
(the second level).
To get this for simple cases you use the function _wanted
. Unlike
_description
, it's an interface to the function that generates
completion as well as a handler for tags --- that's so it can loop over the
generated tags, checking the labels. The call above would now look like
this:
_wanted files expl 'my special files' _filesNote that you now don't pass the
"$expl[@]"
, which hasn't even been set
yet; _wanted
will generate the string using the parameter name you say
(here `expl
', as usual), and assume that the function generating the
completions can use the result passed down to it. This is true of pretty
much anything you are likely to want to use.
Note also the fact you need to pass `_files
', i.e. the function
generating the completion. You can put pretty much any command line which
generates completions here, down to a simple `compadd
' expression. The
reason it has to be here is the tag labelling business: _wanted
could
check whether the tag you specify, `files
', is wanted by the user and
then return control to you, but it wouldn't be able to split up and loop
over labelled tags set in this case for the file-patterns
style and in
other case by the tag-order
style.
Unless you're really going into the bowels, _wanted
is probably
the lowest level you will want to use. I'd suggest you remember that one,
and only go back and look at the other stuff if you need to do something
more complicated.
If your function handles multiple tags, you need to loop over the
different tags to find out which sort the tag order wants next. For this,
you first need to tell the system which tags are coming up, using the
_tags
function with a list. Then you need to to test whether each tag
in turn actually needs to be completed, and go on doing this until you run
out of tags which need completions performing; the _tags
function
without arguments does this. Finally, you need to use _requested
,
which works a bit like _wanted
but is made to fit inside the loop we
are using. The end result looks like
this:
local expl ret=1 _tags foo bar rod while _tags; do _requested foo expl "This is the description for tag foo" \ compadd all foos completions && ret=0 _requested bar expl "This is the description for tag bar" \ compadd all bars completions && ret=0 _requested rod expl "This is the description for tag rod" \ compadd all rods completions && ret=0 (( ret )) || return 0 # leave if matches were generated doneIf you do include the completion function line as arguments, the loop over labels for the tag you specify is automatically handled as with
_wanted
. It may be a
little confusing that both _requested
and _wanted
exist: the
specific difference is that with _requested
you call the _tags
function yourself, whereas _wanted
assumes the only valid tag is its
argument and acts accordingly, and can be used only for simple, `one-shot'
completions.
With _requested
, unlike _wanted
, you can separate out the arguments
to the completion generator itself --- here compadd
--- into a
different statement, remembering the "$expl[@]"
argument in that case.
You can miss out the second and third arguments for _requested
in this
way. This time the loop which generates labels for tags is not performed,
and you have to arrange it yourself, with the usual trade off of greater
complexity for greater flexibility. To do this, there are two other
functions: _all_labels
and _next_label
. The simpler case is with
_all_labels
, which just implements the loop over the labels using the
same arguments as _wanted
:
_requested values && _all_labels values expl 'values for my special things' \ compadd alpha bravo charlie delta echo foxtrot.In case you haven't understood (and it's quite complicated, I'm afraid): the
_requested
looks at whether the tag you use has been asked for by the
user. Having found out that it is, the _all_labels
function calls the
command compadd
which actually adds the completions, but it does it in
such a way as to take account of labelled tags --- you might have both a
plain `values
' tag and `values:-special
' labelled tag, and
_all_labels
is needed to decide which is being used here. This
last example is actually exactly what _requested
does when given
the compadd
as argument, so it's only really useful when there is
some code between the _requested
and the _all_labels
, for
example to compute the strings to complete.
The most complicated case you are likely to come across is when inside the
part of the tags loop which handles a particular tag (i.e. the
_requested
lines in the example above), you actually want to add more
than one possible sort of completion. Then _all_labels
is no longer
enough, because completion needs to sort out the different things which are
being added. This can also happen when there is only one valid tag, but
that has multiple completions so that _wanted
isn't any use. In this
case you need to use _next_label
inside a loop, which, as its names
suggests, fixes up labels for the current tag and stops when it's found the
right one. Here's a stripped down example which handles completion of
messages from the MH
mail handling system; you'll find it complete
inside the function _mh
.
_tags sequences while _tags; do while _next_label sequences expl sequence; do compadd "$expl[@]" $(mark $foldnam 2>/dev/null | awk -F: '{ print $1 }') && ret=0 compadd "$expl[@]" reply next cur prev \ first last all unseen && ret=0 _files "$expl[@]" -W folddir -g '<->' && ret=0 done (( ret )) || return 0 doneHere's what's going on. The
_tags
call works just as it did in the
first example I showed for that, deciding whether the tag in question,
sequences
, has been asked for; the tag name comes because MH allows you
to define sets of messages called exactly `sequences'. The first `while
'
selects all values from tag-order
where the `sequences
' tag
appears, with or without a label. The second `while
' loop then sorts
out any occurrences of labelled sequences to be presented to the user at
the same time, i.e. given in the same element of the tag-order
value
array. The first compadd
extracts from the folder (MH's name for a
directory) identified by the function the names of any sequences you have
defined; the second adds a lot of standard sequences --- although strictly
speaking unseen
isn't a standard sequence since you can name it
yourself in ~/.mh_profile
. Finally, the third adds files in the folder
itself whose names are just digits, which is how MH stores messages. The
handling of return
makes sure it stops as soon as you have matches for
one particular element of tag-order
; if you put it in the inner loop,
you would just have the first of those sets that happened to be generated,
while here, if you specify that all types of sequence should appear in the
same completion list, they are all correctly collected.
Why, in that last example, is there no call to _requested
, now I've
gone to the trouble of explaining what that does? The answer is that there
is only one tag; _tags
can decide if we want it at all, and after that
the tag is known, so we don't need _requested
to find that information
out for us. It's only needed if there is more than one type of match ---
indeed, that's why we introduced it, so this is not actually a new
complication, although you can be forgiven for thinking otherwise.
Here's an example of using that code for sequences. You might decide that you only want to see named sequences unless there aren't any, otherwise ordinary messages. You could do this by setting your styles as follows:
zstyle ':completion:*' tag-order sequences:-name sequences:-num zstyle ':completion:*:sequences-name' ignored-patterns '(|,)<->' zstyle ':completion:*:sequences-num' ignored-patterns '^<->'which tries
sequences
under the labels sequences-name
and
sequences-num
; which ignore completions which are all digits, and those
which are not all digits, respectively. The slight twiddle in the pattern
for sequences-name
ignores messages marked for deletion as well, which
have a comma stuck in front of the number (this is configurable, so your
version of MH may be different).
All of _description
, _wanted
, _requested
, _all_labels
and
_next_label
take the options -J
and -V
to specify sorted or
unsorted listings and menus, and the options -1
and -2
for removing
consecutive duplicates or all duplicates. These are also options to
compadd
; the reason for handling them here is that they can be
different for each tag, and the function called will set expl
appropriately.
If your requirements are simple enough, you can replace that _tags
loop
above with a single function, _alternative
. This takes a series of
arguments each in the form `<tag>:<description>:<action>', with the first
two in the form you now know, and the third an action. These are
essentially the same as actions for the _arguments
function, described
below, except that the form `->state
', which says that the calling
function will handle the action itself by using the value of the parameter
$state
, is not available. The most common forms of action here will be
a call to another completion function, maybe with arguments
(e.g. `_files -/
'), or a simple list in parentheses (e.g. `(see saw
margery daw)
'). Here, for example, is how the _cd
function handles
the two cases of local directories (under the current directory) and
directories reached via the $cdpath
parameter:
local tmpcdpath tmpcdpath=(${(@)cdpath:#.}) _alternative \ 'local-directories:local directories:_path_files -/' \ 'path-directories:directories in cdpath: _path_files -W tmpcdpath -/'The only tricky bit is that
$tmpcdpath
: it removes the `.
' from
$cdpath
, if it's present, so that the current directory is always
searched for with the tag `local-directories
', never with
`path-directories
'. Actually, you could argue that it should be
treated as being in `path-directories
' when it's present; but that
confuses the issue over what `local-directories
' really means, and it
is useful to have the distinction.
It's now an easy exercise to replace the example function I gave for
_requested
by a call to _alternative
with the arguments to
compadd
turned into a list in parentheses as the <action>
part of
the arguments to _alternative
.
How to look up styles
If your completion function gets really sophisticated, you may want it to
look up styles to decide what its behaviour should be. The same advice
goes as for tags: only invent a new style if the old ones don't seem to
cover the use you want to make, since by using contexts you can always
restrict the scope of the style. However, by the same token don't try to
squeeze too much meaning into one style, which will force the user to
narrow the context --- it's always much easier to set a style for the
general context `:completion:*
' than to have to worry about all the
circumstances where you need a particular value.
Retrieving values of styles is no harder than defining them, but you will
need to know about the parameter $curcontext
, which is what stores the
middle part of the context, sans `:completion:
' and sans tag. When you
need to look something up, you pass this context to zstyle
with
`:completion:
' stuck in front:
zstyle -b ":completion:${curcontext}:tag" style-name parameterIf the tag is irrelevant, you can leave it empty, but you still need the final colon since there should always be six in total. In some cases where multiple tags apply it's useful to have a
:default
tag context as a
fall back if none of the actual tags yield styles for that context; hence
you should test the style first for the specific tag, then with the
default
.
Style lookups all have the form just shown; the result for looking up
style-name
in the given context will be saved in the parameter
(which you should make local, obviously). In addition, zstyle
returns
a zero status if the lookup succeeded and non-zero if it failed. The
-t
lookup is different from the rest as it only returns a status for a
boolean, i.e. returns status 0 if the value is true
, yes
, 1
or
on
, and doesn't require a parameter name. There is also a -T
,
which is identical except that it returns status 0 if the style doesn't
exist, i.e. the style is taken to default to true.
The other lookup options return the style as a particular type in the
parameter with exit status zero if the lookup succeeded, i.e. a value was
found, and non-zero otherwise; -b
, -s
, and -a
specify
boolean (parameter
is either yes
or no
), scalar (parameter
is a scalar), and array (parameter
is an array, which may still be a single
word, of course), You can retrieve an associative array with -a
as
long as the parameter has already been declared as one.
There's also a convenience option for matching, -m
; instead of a
parameter
this takes a pattern
as the final argument, and returns
status zero if and only if the pattern
matches one of the values stored
in the style for the given context.
Typical usages are thus:
if zstyle -t ":completion:${curcontext}:" foo; then # do things in a fooish way else # do things in an unfooish way fior to use the value:
local val if zstyle -s ":completion:${curcontext}:" foo val; then # use $val to establish how fooish to be else # be defaultly fooish fi
The last piece of unfinished completion business is to explain the higher
level functions which can save you time writing completions for commands
which behave in a standard way, with arguments and options. The good news
is that all the higher functions here handle tags and labels internally, so
you don't need to worry about _tags
, _wanted
, _requested
, etc.
There's one exception: the `state' mechanism to be described, where a
function signals you that you're in a given state using the parameter
$state
, expects you to handle tag labels yourself --- pretty
reasonable, as you have requested that the function return control to you
to generate the completions. I've mentioned that here so that I don't have
to gum up the description of the functions in this section by mentioning it
again.
Handling ordinary arguments
The most useful function is _arguments
. There are many examples of
this in the completion functions for external commands, since so many
external commands take the standard format of a command with options, some
taking their own arguments, plus command arguments.
The basic usage is to call it with a series of arguments (which I'll call `specifications') like:
<where I am>:<description>:<what action to take>although there are a whole series of more complicated possibilities.
The initial `<where I am>
' part tells the function whether the
specification applies to an argument in a particular position, or to an
option and possibly any arguments for that option. Let's start with
ordinary arguments, since these are simpler. In this case `<where I
am>
' will be either a number, giving the number of the argument, or a
`*
', saying that this applies to all remaining arguments (or all
arguments, if you haven't used any of the other form). You can simplify
the first form, by just missing out the number; then the function will
assume it applies to the first argument not yet specified. Hence the
standard way of handling arguments is with a series of specifications just
beginning `:
' for arguments that need to be handled their own way, if
any, then one beginning `*:
for all remaining arguments, if any.
The message that follows is a description to be passed on down to
_description
. You don't specify the tags at this point; that comes
with the action.
The action can have various forms, chosen to be easily distinguishable from one another.
(red blue
green)
'. These are the possible completions, passed straight down
to compadd
.
((red\:The\
colour\ red blue\:The\ colour\ blue))
' and so on. You can escape
other colons inside the specifications in this way, too.
_files -/
' to complete directories. Usually this does the
business with $expl
which should be familiar from the section
on basic tag handling, however you can put an extra space in
front of the action to have it called exactly as is, after
word splitting.
->
' for example `->state
'. This
specifies that _arguments
should return and allow the calling
function to process the argument. To signal back to the calling
function, the parameter $state
will be set to what follows the
`->
'. It's up to the calling function to make $state
a local
parameter --- _arguments
can't do that, since then it couldn't
return a value.
You should also make the parameters $context
and $line
local; the former is set to the new part to be
added to $curcontext
, which, as you can find out from ^Xh
,
is option-<option>-<arg>
, for example option-file-1
for the
first argument of the option-file
option, or argument-N
, for
example argument-2
for the second argument of the command.
In simple cases, you will just test the parameter $state
after _arguments
has returned to see what to do: the return
value is 300 to distinguish it from other returns where _arguments
itself performed the completion.
Here's a concocted example for that `->state
' action specifier, in case
it's confusing you. It's for a command that takes arguments `alpha
',
`beta
' and `gamma
', and takes a single option `-type
' which
takes one argument, either `normal
' or `unusual
'.
local context state line typeset -A opt_args _arguments '-type[specify type]:type:->type' \ '*:greek letter:->gklet' && return 0 case $state in (type) compadd normal unusual && return 0 ;; (gklet) compadd alpha beta gamma && return 0 ;; esac return 1In fact the possibilities here are so simple that you don't need to use
$state
; you can just use the form with the values in parentheses as the
action passed to `_arguments
'. Anyway, if you put this into a function
`_foo
', type `compdef _foo foo
', and attempt completion for the
fictitious command `foo
', you will see _arguments
in action.
I haven't shown the gory tag handling; as it's written, you'll see that
no tag is ever defined for the compadd
arguments shown. In this
case you could just use _wanted
. What you get for free with
arguments, however, is the context: in the first case, you would have
`:option-type-1
' in the argument field (the second last, just before
the tag), and in the second case `:argument-rest:
'. Go back to
where I originally described contexts if you've forgotten about these; I
didn't tell you at the time, but it's the _argument
function that is
responsible for them. (However, you can supply a `-C
' argument to
_wanted
to tell that a context.)
A note about the form: that `&& return 0
' makes the completion function
return if _arguments
was satisfied that it found a completion on its
own. It's useful in more complex cases. Remember that most completion
functions return status zero if and only if matches were added; this
function is written to follow that convention. I already showed this in
the section on tags, but you might have skipped that.
Note all the things you had to make local: $context
, $state
,
$line
and the associative array $opt_args
. The last named allows
you to retrieve the values for a particular option; for example
`$opt_args[-o]
' contains any value already on the command line for the
option -o
. For options that take multiple arguments, these appear
separated by colons, so if the line contains `-P prefix 3
',
$opt_args[-P]
will contain `prefix:3
'.
Handling options
Option handling is broadly similar, with the `<where I am>
' part just
giving the option name --- I already showed one example with `-type
'
above. In this case, the option will just be completed to itself, the
first part of the specification, and the rest says how to complete its
arguments. Since options can take any number of arguments, including zero,
the :description:action
pair can be repeated, or omitted entirely.
Otherwise, it behaves similarly to the way described for ordinary command
arguments, with all the same possible actions. So a simple option
specification could be
_arguments '-turnmeon'for an option with no arguments,
_arguments '-file:input file:_files'for an option with one argument, or
_arguments '-iofiles:input file:_files:output file:_files'for an option with two arguments, both files but with different descriptions.
The first part of the specification for an option can be more complicated, to reflect the fact that options can be used in all sorts of different ways. You can specify a description for the option itself --- as I tried to explain, the descriptions in the rest of the specification are instead for the arguments to the option. To specify an option description, just put that after the option, before any colons, in square brackets:
_arguments '-on[turn me on, why not]'
Next, some options to a command are mutually exclusive. As _arguments
has to read its way along the command line to parse it, it can record what
options have already appeared, and can ensure that an option incompatible
with one there already will not be completed. To do this, you need to
include the excluded option in parentheses before the option itself:
_arguments '(-off)-on[turn me on, why not]' \ '(-on)-off[turn me off, please]'This completes either of the options `
-on
' or `-off
', but if you've
already given one, it won't complete the other on the same command line.
If you need to give multiple excluded options, just list them separated by
spaces, like `(-off -noton)
'.
 The *
appears after any list of excluded options.
There are also ways of allowing different methods of option handling. If
the option is followed by -
, that means the value must be in the same
word as the option, instead of in the next word; if that is allowed, but
the argument could be in the next word instead, the option should be
followed by a `+
'. The latter behaviour is very common for commands
which take single letter options. Some commands, particularly many recent
GNU commands, allow you to have the argument in the next word or in the
current word after an `=
' sign; you get this by putting an `=
'
after the option name. For example,
_arguments '-file=:input file:_files'allows you to complete `
-file
<filename>' or
`-file=
<filename>'. With
_arguments '-file=-:input file:_files'only the second is possible, i.e. the argument must be after the `
=
',
not in its own word.
You can handle optional and repeated arguments to options, too. This illustrates some possibilities:
_arguments '-option:first arg:->first::optional arg:->second'The doubled colon indicates that the second argument is optional. In other words, at that point on the command line
_arguments
will either try to
complete via the state second
, or will try to start another
specification entirely.
_arguments '-option:first arg:->first:*:other args:->other'Here, all arguments after the first --- everything else on the command line --- is taken as an argument to the option, to be completed using the state
other
.
_arguments '-option:first arg:->first:*-:other args till -:->other'This is similar, but less drastic: there is a pattern after the `
*
',
here a `-
', and when that is encountered, processing of arguments to
`-option
' stops. A command using this might be called as follows:
cmdname -option <first> <other1> <other2> .... - <remainder>where of course completion for
<remainder>
might be handled by other
specifications.
There are yet more possible ways of handling options. I've assumed that
option names can have multiple letters and hence must occur in separate
words. You can specify single-letter options as well, of course, but many
commands allow you to combine these into one word. To tell _arguments
that's OK you should give it the option -s
; it needs to come before any
specifications, to avoid getting mixed up with them. After you specify
this, a command argument beginning with a single `-
' will be treated by
_arguments
as a list of single options, so `-lt
' is treated the
same as `-l -t
'. However, options beginning with `-
-
' are still
treated as single options, so a `-
-prefix
' on the command line is
still handled as a single long option by _arguments
.
One nice feature which can save a lot of trouble when using certain
commands, notably those written by the GNU project and hence installed on
most Linux-based systems, which take an option `-
-help
' that prints
out a list of all options. This is in a human-readable form, but
_arguments
is usually able to extract a list of available options which
use the `-
-...
' form, and even in many cases whether they take an
argument, and if so what type that is. It knows because `<command>
-
-help
' often prints out a message like `-
-file=FILE
' which
would tell _arguments
(1) that `-
-file
' is a possible option
(2) that it takes an argument because of the `=
' (3) that that argument
should be a file because of the message `FILE
' at the end.
You specify that the command in question works in this way by using the
(fairly memorable) option `-
-
' to `_arguments
'. You can then help
it out with completion of option arguments by including a pattern to be
matched in the help test after the `-
-
'; the format is otherwise
similar to a normal specification. For example `*=FILE*:file:_files
'
says that any option with `=FILE
' in it has the description `file
'
and uses the standard _files
function for completion, while
`*=DIR*:directory:_files -/
' does the same for directories. These two
examples are so common that they are assumed by `_arguments -
-
'.
So for example, here is the completion for gdb
, the GNU debugger, which
not surprisingly understands the GNU option format:
_arguments -- '*=(CORE|SYM)FILE:core file:_files' \ '*=EXECFILE:executable:_files -g \*\(\*\)' \ '*=TTY:terminal device:compadd /dev/tty\*' && return 0If you run `
gdb --help
', you'll see where these come from:
`--core=COREFILE
', `--exec=EXECFILE
' and `--tty=TTY
' are all
listed as possible option/argument pairs. Doing it this way neatly
allows the argument completions to work whatever the names of the options
--- though of course it's possible for the rest of the pattern to change,
too, and the commands, being written by lots of different people, are not
necessarily completely consistent in the way their help text is presented.
This is now just a ragbag of other functions which might prove useful in
your own completion functions, and which haven't been mentioned before,
with some examples; once again, consult the manual for more detail.
Note that many of these functions can take the most useful arguments to
compadd
and pass them on, even where I haven't explicitly said so.
_call_function
This is a simple front end to calling a function which may not be defined and hanging onto the return status of the function. One good use for this is to call a possibly non-existent function which might have been defined by the user, before doing some default stuff the user might want to skip. That would look like this:
local ret # returned status from called function, if it was called _call_function ret _hook_function arg1 arg2 && return ret # if we get here, _hook_function wasn't called, # so do the default stuff.As you can work out,
_call_function
itself returns status zero if the
function in the second argument got called, and in that case the first
argument is the name of a parameter with the return status from the
function itself. The whole point is that this is safe if
_hook_function
doesn't exist.
This function is too low level to know about the tags mechanism; use
_wanted
or similar to handle tags properly.
_contexts
This is another shorthand: the arguments it takes are a set of short
contexts, in other words either names of commands or special contexts like
`-math-
'. The completion for each of these contexts is tried in turn;
_contexts
simply handles all the boring looking up of functions and
testing the return values. The definition, if you want to look, is
reassuringly simple. It only has one use at the moment: _subscript
,
which handles the -subscript-
context we met early in the chapter,
calls `_contexts -math-
' to try mathematical completion, since ordinary
array subscripts can contain mathematical expressions.
This is also too low level to handle tags. In zsh 4.1, it is made obsolete by a cleverer mechanism for handling different contexts which can be used, for example, for handling of arguments to redirections for particular commands, or keys in a particular associative array. I expect I'll describe that when 4.1 is finally released.
_describe
Don't confuse this with _description
which was explained above and is
the basic function for adding a description to a set of completions of a
certain type. I mentioned in the description of the verbose
style that
this function was responsible for showing, or not showing, the descriptions
for a whole lot of options at once. It allows you to do that with several
different sets of completions that may require different options to
compadd
. The general form looks something like this:
_describe "description of set 1" descs1 compls1 \ <compadd-opts-1> -- \ "description of set 2" ...where you can have any number of sets separated by the `
-
-
'. The
descs1
and compls1
are arrays of the same length, giving a list of
descriptions and a list of completions, respectively. Alternatively, you
need only give one array name and each element of that will contain a
completion and a description separated by the now-traditional colon. The
`<compadd-opts-1>
' are a set of any old options recognised by
compadd
, such as -q
, or -S=/
, or what have you. I won't give
an example for this, since to find something requiring it would almost need
me to rewrite the completion system from scratch.
_combination
This is the function at the heart of the completions such as
users-hosts
described above, where combinations of elements need to be
completed at the same time. It's easiest to describe with an example;
let's pick the users-hosts
example, and I'll assume you remember how
that works from the user's point of view, including the format of the
users-hosts
style itself. The completion for the username part is
performed as:
_combination my-accounts users-hosts userswhere
my-accounts
is the tag to be used for the completion, then comes
the style, and then the part of the style to be extracted.
Now suppose we come back into the completion function again to complete the
host later on the command line, so that the username is already there. We
can find that by searching the command line; suppose we store what we find
in $userarg
. Then we can complete the hostname as follows:
_combination my-accounts users-hosts users=$userarg hostsand the magic part, the fact that we can limit the hostnames to be completed to only those with a user
$userarg
, is handled by
_combination
. This extends to hosts-ports-users
and any larger
combined set in the obvious way: the first field not to contain an `=
'
is the one being completed. You don't need to supply other fields if they
are not known; in other words, the field to be completed doesn't need to be
the first one in sequence not known, it can be any, just as long as it
matches part of the style given in the second argument, so you could have
omitted the `users=$userarg
' in the last example if you couldn't
extract the right username.
There are various bells and whistles: after the field to be completed you
can add any options to be passed down to compadd
; you can give
_combination
itself the option `-s <sep>
' to specify a character
other than colon to separate the parts of the style values; if the style
lookup fails, but there is a corresponding function, which would be called
`_users
' or `_hosts
' in this example, it is called to generate the matches,
and gets the options at the end which are otherwise destined for
compadd
.
As you can see, this function is at a high enough level to handle the tags mechanism itself.
_multi_parts
This takes two arguments, a separator and a list of matches. The list of
matches is normal, except that each element is likely to contain the
separator. In the most obvious usage, the separator is `/
' and the
list of matches is a lot of files with path components. Here's another
reasonable usage:
local groups expl groups=($(awk -F: '{ print $1 }' ~/.newsrc)) _wanted groups expl 'newsgroup' _multi_parts "$expl[@]" . groupsThe generated array contains names of Usenet newsgroups, i.e. names with components separated by a `
.
', and _multi_parts
allows you to complete these piece by piece instead of in one go. This is
a good deal better for use with menu completion, and the list which appears
is smaller too. The _wanted
part handles the tags mechanism, which
_multi_parts
doesn't.
_sep_parts
This also completes a word piece by piece, but unlike _multi_parts
the
trial completions are also only supplied for each piece. The arguments are
alternating arrays and separators; arrays are in the usual form, in other
words either the name of an array parameter, or a literal array in
parentheses, quoted to protect it from immediate shell expansion. The
separators are simply strings. For example
local expl array1=(apple banana cucumber) _wanted breakfast expl 'breakfast' \ _sep_parts array1 + '(bread toast croissant)' @ '(bowl plate saucer)';completes strings like `
apple+toast@plate
', piece by piece. This is
currently not used by the distributed completion code.
_values
This works a little like _arguments
, but is designed for completing the
values of a single argument in a form like `key=val,flag,key=other
',
in which you can specify the list separator, here `,
' by using the
option -s
, e.g. `-s ,
'. The first argument to _values
is the
overall description of the set of arguments. The other arguments are very
much like those to _arguments
except that, as you would expect from the
form given, no pluses or minus signs are involved and each value can only
have one argument, which must follow an `=
'. Virtually everything else
is identical, with the exception that the associative array where the
arguments are stored for each value is called $val_args
.
I won't bother giving the instructions for _arguments
again; instead,
here is an example based on the values used by the -o
option to the
mount
command:
local context state line typeset -A val_args _values -s , 'file system options' \ '(rw)ro[mount file system read-only]' \ '(ro)rw[mount file system read-write]' \ 'uid[set owner of root]:user ID:' \ 'gid[set group of root]:group ID:' \ 'bs[specify block size]:block size:(512 1024 2048 4192)'
I've just picked out a few of the umpteen possibilities for illustration;
see the function _mount
if you want more. Remember that the
`(rw)
' before the `ro
' means that the options are mutually
exclusive, and the one in parentheses won't be offered if the other appears
on the command line; the strings in square brackets are descriptions of the
particular options; and if there is a colon after the name of the value,
the value takes an argument whose own description comes next. The second
colon is followed by possible completions for that argument, using
the usual convention for actions in _arguments
; as you'll see from
the local
statement, the $state
mechanism can be used here. Only
the `bs
' argument here is given possible completions; for uid
and
gid
you'll have to type in the number without completion; ro
and
rw
don't take arguments.
Hence a typical(?) list to be completed by this would be
`rw,uid=123,bs=2048
'.
Remember also that you can use a `*
' before the option name to say that
it can appear more than once in the value list. The _values
function
handles the context and tags in a similar way to _arguments
.
_regex_arguments
This function is for use when the behaviour of a set of command arguments
is so complicated that even _arguments
can't help. It allows you to
describe the arguments as a regular expression (i.e. a pattern). I won't
explain it because I haven't yet figured out how it works. If you think
you need to use it, look at the manual entry and then at the _apt
function which is currently its main application.
Completion is big and complex: this means that there are probably lots of
bugs around, and things that I haven't described simply enough or which may
be implemented in too complicated a way. Please send the zsh-workers
mailing list any reports or constructive criticism on the subject.
Last of all, remember that the new completion system is ideally just supposed to work without you needing to worry exactly how. That's a bold hope, but at least much of the time you should be able to get away with using just the tab key and ordinary characters.