mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 13:33:52 +01:00
414 lines
17 KiB
Plaintext
414 lines
17 KiB
Plaintext
Contexts, tags and all that
|
|
---------------------------
|
|
|
|
The completion system keeps track of the current context in the
|
|
parameter `curcontext'. Its content is the hierarchical name for the
|
|
current context sans the `:completion:' and the last colon and the tag
|
|
currently tried. The tags represent different types of matches. So,
|
|
whenever you are about to add matches, you should use a tag for them
|
|
and test if the user wants this type of matches to be generated.
|
|
However, this only really needs to be done if no other function in the
|
|
call chain has tested that already or if you can offer different types
|
|
of matches or if you can handle tag aliases in some sophisticated way.
|
|
|
|
Most of the utility functions do the testing themselves, so you don't
|
|
have to worry about that at all. For example if you are adding matches
|
|
with `_files', `_hosts' or functions like these, you can just call
|
|
them and they do the tests needed and the loops over the tag aliases.
|
|
The functions `_arguments' and `_values' do that too, but there is a
|
|
small difference. These functions effectively change the context
|
|
name and if you are using the `->state' form for actions, this changed
|
|
name component has to be reported back to the function calling
|
|
`_arguments' or `_values'. This is done with the parameter `context',
|
|
so you have to make that local in the calling function in the same way
|
|
as you have to make local `line', `state', and `{opt,val}_args'. This
|
|
parameter `context' should then be used when you start adding matches
|
|
by giving it to functions like `_tags' via the `-C' options, as in:
|
|
|
|
local context ...
|
|
...
|
|
_arguments ... '-foo:foo:->foo' && return 0
|
|
...
|
|
if [[ "$state" = foo ]]; then
|
|
_tags -C "$context" ...
|
|
...
|
|
fi
|
|
|
|
This will put the context name given in the argument field of the
|
|
`curcontext' parameter and this context will then be used to look
|
|
up styles for the tags.
|
|
|
|
But since this is often used, `_arguments' and `_values' have support
|
|
to make your life easier in such cases. With the `-C' option, these
|
|
functions set the parameter `curcontext', thus modifying the globally
|
|
used hierarchical context name. This means, that you have to make that
|
|
local, but then you don't have to worry about giving the context name
|
|
reported back to functions you call. E.g.:
|
|
|
|
local curcontext="$curcontext" ...
|
|
...
|
|
_arguments -C ... 'foo:foo:->foo' && return 0
|
|
...
|
|
if [[ "$state" = foo ]]; then
|
|
_tags ...
|
|
...
|
|
fi
|
|
|
|
In this case the parameter `context' is not set, so you don't have to
|
|
make that local. But make sure that `curcontext' is local so that the
|
|
value changed by `_arguments' and `_values' is only used in your
|
|
function (and make sure to initialise it to its old value as in the
|
|
example).
|
|
|
|
All this only works if the specifications given to `_arguments' define
|
|
options and arguments that are completely separate. If there is more
|
|
than one `->state' action and more than one of them might be needed
|
|
for the same word, you'll have to use a loop:
|
|
|
|
local state context line i expl ret=1
|
|
...
|
|
_arguments \
|
|
'::arg1:->arg1' \
|
|
'*:args:->rest' && return 0
|
|
|
|
while (( $#state )); do
|
|
case "$state[1]" in
|
|
arg1) _wanted -C "$context[1]" foo expl 'foo' compadd - foo1 foo2 && ret=0;;
|
|
rest) _wanted -C "$context[1]" bar expl 'bar' compadd - bar1 bar2 && ret=0;;
|
|
esac
|
|
shift 1 state
|
|
shift 1 context
|
|
done
|
|
|
|
return ret
|
|
|
|
As you can see, `state' and `context' are really arrays. In this
|
|
example, completion for the first argument has to complete both `foo's
|
|
and `bar's.
|
|
|
|
Then, before adding the matches, see if matches of that type are
|
|
requested by the user in the current context. If you will add only one
|
|
type of matches, this is very simple. You can use the function
|
|
`_wanted' for this. Well, you can often use it, that is. Use it as in:
|
|
|
|
_wanted names expl 'name' compadd - alice bob
|
|
|
|
This is like testing if the tag `names' is requested by the user and
|
|
then calling `_all_labels' with the same arguments.
|
|
|
|
The `_all_labels' function implements the loop over the tag aliases and
|
|
handles the user-defined description, using (in the example) the
|
|
parameter `expl' to store options to give to the command. These options
|
|
are inserted into the command line either directly before a single
|
|
hyphen if there is such an argument or after the first word if there
|
|
is no single hyphen. Since using `_all_labels' is so much more convenient
|
|
than writing the loop with the `_next_label' function (see below), but
|
|
some functions called to generate matches don't accept a single hyphen
|
|
as an argument anywhere but want the options built as their last arguments,
|
|
`_all_labels' will *replace* the hyphen with the options if the hyphen is
|
|
the last argument. A good example for such a function is
|
|
`_combination' which can be called like:
|
|
|
|
_all_labels foo expl 'descr...' _combination ... -
|
|
|
|
And the `-' will be replaced by the options that are to be given to
|
|
`compadd'.
|
|
|
|
Note that you can also give the `-J' and `-V' options with the
|
|
optional `1' or `2' preceding them supported by `_description':
|
|
|
|
_wanted -2V names expl 'name' compadd ...
|
|
|
|
In some cases one needs to call multiple functions or call `compadd'
|
|
more than once to generate the matches. In such a case one needs to
|
|
implement the loop over the tag aliases directly. This is done with the
|
|
`_next_label' function. Like this:
|
|
|
|
while _next_label names expl 'name'; do
|
|
compadd "$expl[@]" - alice bob && ret=0
|
|
_other_names "$expl[@]" && ret=0
|
|
done
|
|
return ret
|
|
|
|
Simple enough, I hope. But `_next_label' can do some more: utility
|
|
functions normally accept options which are then given to `compadd'.
|
|
Since these may contain options for the description and `_next_label' may
|
|
generate such options, too, it isn't entirely trivial to decide which
|
|
of these options should take precedence. But `_next_label' can do the work
|
|
for you here. All you have to do is to give the options your utility
|
|
function gets to `_next_label', as in:
|
|
|
|
while _next_label names expl 'name' "$@"; do
|
|
compadd "$expl[@]" - alice bob
|
|
...
|
|
done
|
|
|
|
That's all. Note that the positional argument "$@" are *not* given to
|
|
`compadd'. They will be stuffed into the `expl' array by `_next_label'.
|
|
|
|
The most complicated case is where you can offer multiple types of
|
|
matches. In this case the user should be able to say which types he
|
|
wants to see at all and of those which he wants to see he should be
|
|
able to say which types should be tried first. The generic solution
|
|
for this uses `_tags' and `_requested':
|
|
|
|
local expl ret=1
|
|
|
|
_tags friends users hosts
|
|
|
|
while _tags; do
|
|
_requested friends expl friend compadd alice bob && ret=0
|
|
_requested users && _users && ret=0
|
|
_requested hosts && _hosts && ret=0
|
|
|
|
(( ret )) || break # leave the loop if matches were added
|
|
done
|
|
|
|
`_tags' with tags as arguments registers those tags and checks which
|
|
of them the user wants to see and in which order the tags are to be
|
|
tried. This means that internally these tags are stored in multiple
|
|
sets. The types of matches represented by the tags from the first set
|
|
should be tried first. If that generates no matches, the second set is
|
|
tried and so on. `_tags' without arguments just makes the next set be
|
|
tried (on the first call it makes the first set be used). The function
|
|
`_requested' then tests if the tag given as its first argument is in
|
|
the set currently used and returns zero if it is, i.e. if matches of
|
|
that type should be added now. The arguments accepted by `_requested'
|
|
are the same as for `_wanted'. I.e. you can call it with only the tag
|
|
to test, with the `tag array description' or with that plus the
|
|
command to execute.
|
|
|
|
In some cases (like the `users' and `hosts' tags in the example) you
|
|
don't need do the loop over the tag aliases yourself, because the
|
|
utility functions like `_users' and `_hosts' do it automatically.
|
|
|
|
This looks good already. But in many cases such as this one you can
|
|
also use the function `_alternative' which simply implements a loop
|
|
like the one above. It gets arguments of the form `tag:descr:action'.
|
|
E.g.:
|
|
|
|
_alternative \
|
|
'friends:friend:(alice bob)' \
|
|
'users:: _users' \
|
|
'hosts:: _hosts'
|
|
|
|
Which does the same as the previous example. (Note the empty
|
|
descriptions in the last two arguments -- the actions start with a
|
|
space so that they are executed without giving the description
|
|
build by `_alternative', i.e. we just use the description added by
|
|
`_users' and `_hosts').
|
|
|
|
In cases where you have to keep track of the context yourself, you can
|
|
give the sub-context you want to use to `_tags', `_wanted' and
|
|
`_alternative' with the `-C' option as described above. You don't need
|
|
to give it to `_requested' -- that function will work on the context
|
|
used by the corresponding call to `_tags' automatically.
|
|
|
|
For the names of the tags: choose simple (short, if at all possible)
|
|
names in plural. Also, first have a look at the tag names already used
|
|
by other functions and if any of these names seem sensible for the
|
|
type of matches you are about to add, then use those names. This will
|
|
allow users to define styles for certain types of matches independent
|
|
of the place where they are added.
|
|
|
|
One final comment about when to use your own argument-contexts: do
|
|
this when the command you are writing a completion function for has
|
|
different `modes'. E.g. if it accepts host names after a `-h' option
|
|
and users or hosts after `-u' and for some reason you can't use
|
|
`_arguments' to do the work for you, then use context names as in:
|
|
|
|
case "$1" in
|
|
-h)
|
|
_tags -C -h hosts && _hosts && ret=0
|
|
;;
|
|
-u)
|
|
_alternative -C -u 'users:: _users' 'hosts:: _hosts' && ret=0
|
|
;;
|
|
esac
|
|
|
|
|
|
Styles
|
|
------
|
|
|
|
Users can associate patterns for hierarchical context names with
|
|
certain styles using the `zstyle' builtin. The completion code
|
|
should then use these styles to decide how matches should be added and
|
|
to get user-configured values. This, too, is done using the builtin
|
|
`zstyle'.
|
|
|
|
Basically styles map names to a bunch of strings (the `value'). In
|
|
many cases you want to treat the value as a boolean, so let's start
|
|
with that. To test if, for example, the style `verbose' is set for
|
|
the tag `options' in the context you are currently in, you can just do:
|
|
|
|
if zstyle -t ":completion:${curcontext}:options" verbose; then
|
|
# yes, it is set...
|
|
fi
|
|
|
|
I.e. with the -t option and two arguments `zstyle' takes the first one
|
|
as a context and the second one as a style name and returns zero if that
|
|
style has the boolean value `true'. Internally it checks if the style
|
|
is set to one of `yes', `true', `on', or `1' and interprets that as
|
|
`true' and every other value as `false'.
|
|
|
|
For more complicated styles for which you want to test if the value
|
|
matches a certain pattern, you can use `zstyle' with the -m option and
|
|
three arguments:
|
|
|
|
if zstyle -m ":completion:${curcontext}:foo" bar '*baz*'; then
|
|
...
|
|
fi
|
|
|
|
This tests if the value of the style `bar' for the tag `foo' matches
|
|
the pattern `*baz*' and returns zero if it does.
|
|
|
|
If you just want to see if one of the strings in the value is exactly
|
|
equal to any of a number of a strings, you can use the -t option and
|
|
give the strings after the style name:
|
|
|
|
if zstyle -t ":completion:${curcontext}:foo" bar str1 str2; then
|
|
...
|
|
fi
|
|
|
|
But sometimes you want to actually get the value stored for a certain
|
|
style instead of just testing it. For this `zstyle' supports four
|
|
options: `-b', `-s', `-a', and `-h'. After these options, three
|
|
arguments are expected, the context, the style, and a parameter name.
|
|
The parameter will then be set to the value of the style and the option
|
|
says how the strings stored as a value will be stored in the
|
|
parameter:
|
|
|
|
- `-b': the parameter will be set to a either `yes' or `no'
|
|
- `-s': the parameter will be set to all strings in the value
|
|
concatenated (separated by spaces) to one string
|
|
- `-a': the parameter will be set to an array containing the strings
|
|
from the value as elements
|
|
- `-h': the parameter will be set to an association with the strings
|
|
from the value being interpreted alternatingly as keys and
|
|
values
|
|
|
|
Some random comments about style names. Use the ones already in use if
|
|
possible. Especially, use the `verbose' style if you can add
|
|
matches in a simple and a verbose way. Use the verbose form only if
|
|
the `verbose' style is `true' for the current context. Also, if
|
|
the matches you want to add have a common prefix which is somehow
|
|
special, use the `prefix-needed' and `prefix-hidden' styles. The first
|
|
one says if the user has to give the prefix on the line to make these
|
|
matches be added and the second one says if the prefix should be
|
|
visible in the list.
|
|
|
|
And finally, if you need a style whose value can sensibly be
|
|
interpreted as a list of words, use array or association styles with
|
|
the `-a' or `-h' options to `zstyle'. Otherwise you should only make
|
|
sure that an empty value for a style is treated in the same way as if
|
|
the style wasn't set at all (this is used elsewhere and we want to
|
|
keep things consistent).
|
|
|
|
|
|
Descriptions
|
|
------------
|
|
|
|
Always use description. This is important. Really. *Always* use
|
|
descriptions. If you have just written down a `compadd' without a
|
|
"$expl[@]" (or equivalent), you have just made an error. Even in
|
|
helper functions where you use a "$@": if you can't be sure that there
|
|
is a description in the arguments, add one. You can (and, in most
|
|
cases, should) then give the description you generated after the
|
|
"$@". This makes sure that the, probably more specific, description
|
|
given by the calling function takes precedence over the generic one
|
|
you have just generated.
|
|
|
|
And it really isn't that complicated, is it? Think about a string
|
|
people might want to see above the matches (in singular -- that's used
|
|
throughout the completion system) and do:
|
|
|
|
local expl
|
|
|
|
_description tag expl <descr>
|
|
compadd "$expl@]" - <matches ...>
|
|
|
|
Note that this function also accepts `-V' and `-J', optionally (in the
|
|
same word) preceded by `1' or `2' to describe the type of group you
|
|
want to use. For example:
|
|
|
|
_description tag expl '...'
|
|
compadd "$expl[@]" -1V foo - ... # THIS IS WRONG!!!
|
|
|
|
is *not* the right way to use a unsorted group. Instead do:
|
|
|
|
_description -1V tag expl '...'
|
|
compadd "$expl[@]" - ...
|
|
|
|
and everything will work fine.
|
|
|
|
Also, if you are about to add multiple different types of matches, use
|
|
multiple calls to `_description' and add them with multiple calls to
|
|
`compadd'. But in almost all cases you should then add them using
|
|
different tags anyway, so, see above.
|
|
|
|
And since a tag directly corresponds to a group of matches, you'll
|
|
often be using the tags function that allows you to give the
|
|
explanation to the same function that is used to test if the tags are
|
|
requested (again: see above). Just as a reminder:
|
|
|
|
_wanted [ -[1,2]V | -[1,2]J ] <tag> expl <descr> <cmd> ...
|
|
|
|
and
|
|
|
|
_requested [ -[1,2]V | -[1,2]J ] <tag> expl <descr> [ <cmd> ... ]
|
|
|
|
is all you need to make your function work correctly with both tags
|
|
and description at the same time.
|
|
|
|
|
|
Misc. remarks
|
|
-------------
|
|
|
|
1) Supply match specifications to `compadd' if there are sensible ones.
|
|
2) Use helper functions that do option completion for you (like
|
|
`_arguments' and `_values') -- it will make your life much
|
|
easier.
|
|
3) Use helper functions like `_users' and `_groups' instead of some ad hoc
|
|
mechanisms to generate such information. This ensures that users can
|
|
change the way these things will be completed everywhere by just using
|
|
their own implementations for these functions.
|
|
4) Make sure that the return value of your functions is correct: zero
|
|
if matches were added and non-zero if no matches were found.
|
|
In some cases you'll need to test the value of `$compstate[nmatches]'
|
|
for this. This should always be done by first saving the old value
|
|
(`local nm="$compstate[nmatches]"') and later comparing this with
|
|
the current value after all matches have been added (e.g. by
|
|
writing `[[ nm -ne compstate[nmatches] ]]' at the end of your
|
|
function).
|
|
This guarantees that your functions will be re-usable because calling
|
|
functions may rely on the correct return value.
|
|
5) When writing helper functions that generate matches, the arguments
|
|
of these should be given unchanged to `compadd' (if they are not
|
|
used by the helper function itself).
|
|
6) When matches with a common prefix such as option names are generated,
|
|
add them *with* the prefix (like `-', `+', or `--' for options).
|
|
Then check the `prefix-needed' style to see if the matches are to be
|
|
added when the prefix is on the line and use the `prefix-hidden'
|
|
style to see if the prefix should be listed or not.
|
|
7) If at all possible, completion code for a command or a suite of
|
|
commands should go into only one file. If a command has sub-commands,
|
|
implementing a state-machine might be a good idea. See the `_rpm'
|
|
and `_pbm' files for examples of different styles. Also see the
|
|
documentation for `_arguments' and `_values' for two functions
|
|
that may help you with this.
|
|
8) If a completion function generates completely different types of
|
|
completions (for example, because the command has several
|
|
completely different modes), it should allow users to define
|
|
functions that separately override the behavior for these
|
|
different types. This can easily be achieved by using the
|
|
`_funcall' utility function, as in:
|
|
|
|
_funcall ret _command_$subcommand && return ret
|
|
|
|
This will try to call the function `_command_$subcommand' and if
|
|
it exists, it will be called and the completion function exits
|
|
with its exit status. After this call to `funcall' the completion
|
|
function would contain the code for the default way to generate
|
|
the matches.
|
|
See the `_rpm' and `_nslookup' files for examples.
|