about summary refs log tree commit diff
path: root/Etc
diff options
context:
space:
mode:
Diffstat (limited to 'Etc')
-rw-r--r--Etc/completion-style-guide416
1 files changed, 342 insertions, 74 deletions
diff --git a/Etc/completion-style-guide b/Etc/completion-style-guide
index 286cb2a71..f5ec8ff14 100644
--- a/Etc/completion-style-guide
+++ b/Etc/completion-style-guide
@@ -1,91 +1,359 @@
-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
+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 matche by giving the tags for them as arguments. But 
+it 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 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' following them supported by `_description':
+
+  _wanted names -V2 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 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 function
+`_style'.
+
+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 `description' is set for 
+the tag `options' in the context you are currently in, you can just do:
+
+  if _style options description; then
+    # yes, it is set...
+  fi
+
+I.e. with two arguments `_style' 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 `_style' with three arguments:
+
+  if _style 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.
+
+But sometimes you want to actually get the value stored for a certain
+style instead of just testing it. For this `_style' supports four
+options: `-b', `-s', `-a', and `-h'. After these options, three
+arguments are expected, namely 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
+
+Note that if you want to test or get styles for a certain context
+name which you have to build yourself, you have to call `_style' after 
+the call to `_tags', `_wanted', or whatever. When you are using
+utility functions like `_alternate' or `_arguments' the context will
+automatically be set up appropriately at the time when you have a
+chance to call `_style'.
+
+Some random comments about style names. Use the ones already in use if 
+possible. Especially, use the `description' style if you can add
+matches in a simple and a verbose way. Use the verbose form only if
+the `description' 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.
+
+But, I think, using any number of new style names is ok -- different
+from tag-names where I would like to keep the number of names used
+small.
+
+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 `_style'. 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 use 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 expl <descr>
+  compadd "$expl@]" - <matches ...>
+
+Note that this function also accepts `-V' und `-J', optionally (in the 
+same word) followed by `1' or `2' to describe the type of group you
+want to use. For example:
+
+  _description expl '...'
+  compadd "$expl[@]" -V1 foo - ...    # THIS IS WRONG!!!
+
+is *not* the right way to use a unsorted group. Instead do the
+simpler:
+
+  _description -V1 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 often 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 <tag> [ -V[1,2] | -J[1,2] ] expl <descr>
+
+and
+
+  _requested <tag> [ -V[1,2] | -J[1,2] ] 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.
-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
+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.
-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
+    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).
-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
+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 aa state-machine might be a good idea. See the `_rpm' 
+    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.
-13) If a completion function generates completely different types of
+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