mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-10-04 08:30:54 +02:00
367 lines
15 KiB
Text
367 lines
15 KiB
Text
Contexts, tags and all that
|
|
---------------------------
|
|
|
|
The completion system keeps track of the current context in the
|
|
parameter `curcontext'. It's content is the hierarchical name for the
|
|
current context sans 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.
|
|
|
|
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. The functions `_arguments' and
|
|
`_values' do that too, but there is a small difference. These
|
|
functions effectively add a new component to the hierarchical context
|
|
name and if you are using the `->state' form for actions, this new
|
|
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'
|
|
...
|
|
if [[ "$state" = foo ]]; then
|
|
_tags -C "$context" ...
|
|
...
|
|
fi
|
|
|
|
This will append the context name given to the `curcontext' parameter
|
|
(preceding it with a colon) 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 ... 'foo:foo:->foo'
|
|
...
|
|
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).
|
|
|
|
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 `_tags'
|
|
or the function `_wanted' for this. `_tags' is normally used to offer
|
|
multiple types of matches by giving the tags for them as arguments. But
|
|
in any case its return value is zero only if at least one of these
|
|
types is requested by the user, so you can just do:
|
|
|
|
_tags names || return 1
|
|
|
|
_description names expl 'name'
|
|
compadd "$expl[@]" - alice bob
|
|
|
|
Since this sequence of command is used so often, the `_wanted'
|
|
function was added which just calls `_tags' with its first argument
|
|
(i.e. the first argument os a tag) and then calls `_description' with
|
|
all other arguments. The return value is as for `_tags' -- zero if the
|
|
matches should be added. So the example becomes:
|
|
|
|
_wanted names expl 'name' && compadd "$expl[@]" alice bob
|
|
|
|
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 ...
|
|
|
|
The more 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
|
|
if _requested friends; then
|
|
_description friends expl friend
|
|
compad "$expl[@]" alice bob && ret=0
|
|
fi
|
|
_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 calls
|
|
`_sort_tags' so that the user can say which 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.
|
|
|
|
But `_requested' can do more: since it is very common that you add
|
|
different types of matches in different groups, with each group having
|
|
its own description the sequence of `_requested' followed by
|
|
`_description' would be used very often. Hence, `_requested' can
|
|
accept extra arguments which will be given to a call to `_description'
|
|
if the tag given as the first argument is to be used. I.e. we could
|
|
change the example above to:
|
|
|
|
local expl ret=1
|
|
|
|
_tags friends users hosts
|
|
|
|
while _tags; do
|
|
_requested friends expl friend && compad "$expl[@]" alice bob && ret=0
|
|
_requested users && _users && ret=0
|
|
_requested hosts && _hosts && ret=0
|
|
|
|
(( ret )) || break # leave the loop if matches were added
|
|
done
|
|
|
|
This looks better already. But in many cases such as this one you can
|
|
also use the function `_laternative' which simply implements a loop
|
|
like this one. 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 examples. (Note the empty
|
|
descriptions in the last two arguments -- the actions start with a
|
|
space so that they are executed without giving the 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, the use those names. This will
|
|
allow users to define styles for certain types of matches indepent of
|
|
the place where they are added.
|
|
|
|
One final comment about when to use your own sub-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 `compstyle' function. The completion code
|
|
should then use these styles to decide how matches should be added and
|
|
to get user-configured values. This 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 tag 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 style 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 tag, 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' und `-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 the
|
|
simpler:
|
|
|
|
_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 allow 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>
|
|
|
|
and
|
|
|
|
_requested [ -[1,2]V | -[1,2]J ] <tag> expl <descr>
|
|
|
|
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 where 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' or `compgen' (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 comamnd 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.
|