From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26995 invoked from network); 5 Jan 2000 14:38:26 -0000 Received: from sunsite.auc.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 5 Jan 2000 14:38:26 -0000 Received: (qmail 10639 invoked by alias); 5 Jan 2000 14:37:56 -0000 Mailing-List: contact zsh-users-help@sunsite.auc.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 2831 Received: (qmail 10632 invoked from network); 5 Jan 2000 14:37:56 -0000 Date: Wed, 5 Jan 2000 15:37:54 +0100 (MET) Message-Id: <200001051437.PAA10986@beta.informatik.hu-berlin.de> From: Sven Wischnowsky To: zsh-users@sunsite.auc.dk In-reply-to: Shao Zhang's message of Wed, 5 Jan 2000 13:08:29 +1100 Subject: Re: help with command completion Shao Zhang wrote: > Hi, > I am trying to learn programming command completion using > zstyle. After a long read of /usr/share/zsh/functions, I still > have to idea, not even how to start. > > > I have the following two cases to do, can someone please give me > a hand and tell me how to start?? > > > lpr -P[TAB] complets to a list a printer name, read from > /etc/printcap. It's not `using zstyle'. zstyle is only the mechanism used for configuration it can be used by completion functions but it need not. The first step is always to create a file named like _lp (the underscore is the important bit). Place that in a directory in your $fpath (so that compinit will find it). In the first line in that file you specify the commands this file will provide completion functionality for, e.g.: #compdef lpr Since there is already a function offering completion for lpr (namely _ps, which completes only names of postscript files) you have to make sure that the file you created will be found before that (i.e. it is in a directory earlier in $fpath than the User directory from the distribution). In the rest of the file you use the basic mechanisms described in the compwid manual page and/or the utility functions described in the compsys manual page to generate the completions. Since this is only a simple example I'll use only the basic ones. First we want to see if we have to complete printer names, which is the case if we are after a -P option, directly after it in the same word or in the word after it. For this kind of stuff there are the special parameters like $PREFIX and $words. We could do: if [[ "$PREFIX" = -P* || "$words[CURRENT-1]" = -P ]]; then ... fi Since $PREFIX is the beginning of the string the cursor is on (up to the cursor position), the first test is true if we are on a word beginning with -P and the second one uses the $words array which contains all words from the line and the $CURRENT special parameter which gives the index for the word we are on into $words array. So $words[CURRENT-1] gives the previous word. We would now start generating names of printers as possible completions, the completion code will use the strings in $PREFIX and $SUFFIX to see if the names we generate match the stuff from the line. But since in the first case there is the -P at the beginning this wouldn't match, so we have to `remove' that. More precisely, we have to ensure that the completion code still knows about it but doesn't use it for matching. This is done by removing it from $PREFIX and appending it to another special parameter: $IPREFIX (that's `ignored prefix'). We could do it by hand: IPREFIX="${IPREFIX}-P" PREFIX="${PREFIX#??}" But since such tests followed by such modifications are needed quite often, there is support for it: the compset builtin. This does the test and if it succeeds, immediatly modifies the parameters that need to be modified. So we can do everything together with: if compset -P -P || [[ "$words[CURRENT-1] = -P ]]; then ... fi Then we can just start generating the printer names in this `if'. One possibility is something like: compadd - "${(@)${(@s:|:)${(@)${(@f)$(< /etc/printcap)}:#[ \#]*}%%:*}%%[ ]*}" The compadd builtin gets the possible completions as arguments. It also supports several options but these are indeed needed relatively seldom. In the functions from the distribution most options used are generated more or less automatically by some helper functions. These mainly make the completion function more fun to use or the completion listing more fun to look at (at least I hope that). But lets first continue with the example. Until now the function can complete only printer names after -P which isn't really useful for a command like lpr, so we have to make it complete file names, too, but only for the other arguments: if ...; then ... else _ps fi This uses the utility function _ps from the distribution which completes Postscript files (or directories or all files if there are no Postscript files). _ps itself, btw, mainly uses another helper function: _files. This is used throughout the completion system and completes filenames allowing partial pathname completion (i.e. /u/inc can be completed to /usr/include). So, the whole function (file) could look like: #compdef lpr if compset -P -P || [[ "$words[CURRENT-1] = -P ]]; then compadd - "${(@)${(@s:|:)${(@)${(@f)$(< /etc/printcap)}:#[ \#]*}%%:*}%%[ ]*}" else _ps fi Since in the completion system we generally want to avoid the extra processes needed for $(...), we would use caching for the printer names there (and the function for the lp commands I just sent to zsh-workers does that). And we would also make this nicer by supporting the other mechanisms used throughout the completion system. One of these is that matches are always added with descriptions that can be made visible in completion lists using the format style for the `descriptions' and `messages' tags. Everything a completion function needs to do for that is call the utility function _description. It gets a tag name (users are completely free to chose any tag name they want), the name of an array and a short description as arguments. E.g.: local expl _description printers expl printer (That `printer' is the description.) _description will set up the array $expl to contain options for compadd that add the description if requested by the user and several other things users can request for all matches generated (this `requesting', btw, is what is done using zstyle). Another thing all completion functions (I mean those in the distribution) should support is tags. More precisely, users should be able to configure (using the tag-order style) if they want to see completions of a certain type or not. For this, there are utility functions like _wanted and _requested which get a tag name as argument. In the example above we can use the first one: _wanted printers && compadd ... _wanted and _requested only return zero if the user requested the type of matches represented by the tag (more precisely: didn't say explicitly that he doesn't want them). With that users can say that they don't want printer names to be completed (this sounds weird when talking about contexts where only one type of matches can be generated, but if multiple types are possible, this quickly makes sense). And since every decent completion function should use both descriptions and tags, _wanted and _requested can also be made to call _description by giving them the same arguments that would be given to that, so: #compdef lpr if compset -P -P || [[ "$words[CURRENT-1] = -P ]]; then local expl _wanted printers expl printer && compadd - "${(@)${(@s:|:)${(@)${(@f)$(< /etc/printcap)}:#[ \#]*}%%:*}%%[ ]*}" else _ps fi That's all that's needed to make the function behave like the other functions in the completion system (_ps already does all the description and tag stuff, so we don't have to do anything there). As Peter already said I, too, would suggest that people who want to write completion functions first start by copying and modifying existing functions. Then continue with simple functions using the basic stuff from the compwid manual and then start using the utility functions. Note that there is also the completion-style-guide in the Etc directory of the distribution which should help a bit with the more complicated stuff (mainly the utility functions from the completion system). And if you then arrive at some interesting completion function I would be glad if you could send it to the list and at least I (and I guess others, too) will look at it and see if and how it can be improved or made more standard in its behaviour. > > > rm -rf completes fine, but sudo rm -rf does not work. Hm. The current development version (and some older version, too) have a completion function for sudo and it works nicely for me. Don't you have _sudo or do you have aliased sudo to something? Bye Sven -- Sven Wischnowsky wischnow@informatik.hu-berlin.de