1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
For now this is just a list of things one should or shouldn't do.
1) Use the functions `_files' and `_path_files' instead of `compgen'
with the `-f', `-/', or `-g' options.
2) *Never* use `compgen' with the `-s' option. This can always be done
by a call to `compadd' which is faster.
3) Using `compgen' with the `-k' option should only be done if a) the
array is already existent or b) it is very large (several hundred
or thousend elements). In other cases using `compadd' is faster.
4) Supply match specifications to `compadd' and `compgen' if there are
sensible ones.
5) Use `_description' when adding matches with `compadd' or
`compgen'. Use `_message' in places where no matches can be
generated. If you want to add different types of matches, add them
with multiple calls to `compadd' or `compgen', supplying different
descriptions.
6) Use helper functions that do option completion for you (like
`_arguments' and `_values') -- it will make your life much
easier.
7) Use helper functions like `_users' and `_groups' instead of direct
calls to `compgen -u' or 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.
8) 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.
9) In places where different behaviors may be useful, add a
configuration key to allow users to select the behavior they
prefer. Names for configuration keys should look like `prefix_name',
where `prefix' is the (probably abbreviated) name of your function
(without any leading underscore) and `name' describes what can be
configured.
If you want to have this completion function to be included in the
distribution, it would help if you describe the configuration key
at the end of the `compsys.yo' manual.
When testing the values of configuration keys, the empty string
should result in the same behavior as if the key were unset. This
can be achieved by the test `[[ -n "$compconfig[prefix_name]" ]]'.
10) 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).
11) When option names are generated as possible matches, add them *with*
the `-', `+', or `--' at the beginning. This is the style used
throughout the completion system from the distribution and makes it
easier to distinguish options from other matches in completion lists.
Also, when adding options as matches, put them in a sorted group
named `option'. The best way to do this is by using the `_description'
helper function as in:
local expl
_description expl option
compadd "$expl[@]" - <option-names ...>
Also, before adding options as possible matches, test the
`option_prefix' configuration key. If it set and it doesn't contain
the sub-string `!cmd' (where `cmd' is the name of the command that
is completed for) options should only be added if the prefix character
(`-' or `+') is on the line or if no other useful matches could be
generated. This can be achieved by first generating these other matches
(if any) and then using a test like:
if [[ nm -eq "$compstate[nmatches]" ||
-z "$compconfig[option_prefix]" ||
"$compconfig[option_prefix]" = *\!${words[1]}* ||
"$PREFIX" = [-+]* ]]; then
# Add options...
fi
Finally, it is good style to display descriptions for options that
aren't self-explanatory. See the `_display' and `_describe' functions
and their uses in `_arguments'.
All this should make it look like a really good idea to just use the
supplied `_arguments' function to complete options.
12) 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 aa 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.
13) 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.
|