From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 16813 invoked from network); 19 Nov 1997 17:29:29 -0000 Received: from ns2.primenet.com.au (7795@203.24.36.3) by ns1.primenet.com.au with SMTP; 19 Nov 1997 17:29:29 -0000 Received: (qmail 1661 invoked from network); 19 Nov 1997 17:29:21 -0000 Received: from math.gatech.edu (list@130.207.146.50) by ns2.primenet.com.au with SMTP; 19 Nov 1997 17:29:21 -0000 Received: (from list@localhost) by math.gatech.edu (8.8.5/8.8.5) id MAA26217; Wed, 19 Nov 1997 12:00:49 -0500 (EST) Resent-Date: Wed, 19 Nov 1997 12:00:49 -0500 (EST) Message-Id: <199711191700.SAA08643@hydra.ifh.de> To: zsh-workers@math.gatech.edu (Zsh hackers list), zsh@peak.org Subject: PATCH: User-defined completion listing Date: Wed, 19 Nov 1997 18:00:55 +0100 From: Peter Stephenson Resent-Message-ID: <"20bKe.0.aP6.1jnSq"@math> Resent-From: zsh-workers@math.gatech.edu X-Mailing-List: archive/latest/3621 X-Loop: zsh-workers@math.gatech.edu Precedence: list Resent-Sender: zsh-workers-request@math.gatech.edu This implements something like the outcome of the recent discussion of a new option for showing a user-defined list instead of the actual completions. First, the bad news: this patch is simply against my own current adaption of 3.1.2, which probably doesn't correspond to anyone else's. In particular, it has my compctl -/ and -W patches in, but it doesn't have Sven's big patch, which may make a difference. I could probably do it against vanilla 3.1.2 if that becomes sensible. Now the good news: the syntax (see manual patch for more) is either compctl ... -Y '$array' or compctl ... -Y 'func' In the first case, $array is, err, an array, in the second, func is called and must set $reply, just like a -K function. (They're evaluated at different times, so there's no clash.) func is also passed the complete list of matches, with full prefix expansion, so it can process the matches to produce a display. For example, llfn() { reply=("$(ls -fld $*)"); } compctl -f -Y llfn foo foo has ordinary file name completion, but the list of matches will be made line by line in ls -l format. read -c and read -l are available if you want them. The output is literal in this case, since you have control over what comes out, though only newline is specially handled; other control sequences could mess up the formatting and are best avoided (at least with always_last_prompt set). Note, however, that all the other formatting capabilities are retained, so a -Y function like func() { reply=($*); } produces full columnated output just as without the -Y func (assuming there are no funny characters in the $* which would be massaged in that case). So the suggestion for job completion is along the lines of: joblist() { local jf=/tmp/zjobs$$ line jobs >& $jf reply=() joblist=() while read line; do joblist=($joblist $line) reply=($reply ${${line#\[}%%\]*}) done < $jf # The above allows the output to appear in columns. If you want the # exact output of 'jobs', you can instead use: # joblist=("$(<$jf)") # It must still be an array, though. rm -f $jf } compctl -P% -K joblist -Y '$joblist' \ -x 's[-]' -k signals -- kill # ... and friends Note that in the current implementation you need a completion list: if there are no completions, nothing is displayed, so you can't just display a memo and let the user complete by hand. That could probably be remedied. Obviously, '$joblist' could be a static variable as well. One thing to check for with the patch is that I haven't somehow gummed up the logic for displaying other types of completion in my zeal to get -Y displays to appear OK; I obviously couldn't check everything directly myself. (I think I caught the only memory leak, though.) Also, a -Y func is blindly passed the fully expanded completion possibilities (necessary for the llfn example above, which needs the full path to the candidate file): maybe there are occasions where that's wrong. Another thing under the `could perhaps be better, but it's time I did some work' heading: literal arrays are allowed as with -k, i.e. -Y '(option1 option2 ...)', but there's no way of getting a literal string there. It might be nice to enhance get_user_var() to understand a double quote at the beginning and return the text as an array with a single element. (The name of a real array with just one element is fine, of course.) While I'm getting picky: the fact that the completion function gets the full path could be something of a memory hog if you are completing in a full directory with a long path name, but until the Commodore 64 version of zsh appears... *** Doc/Zsh/compctl.yo.YoY Thu Sep 18 09:58:39 1997 --- Doc/Zsh/compctl.yo Wed Nov 19 17:34:51 1997 *************** *** 103,111 **** [ tt(-s) var(subststring) ]) list([ tt(-K) var(function) ] [ tt(-H) var(num pattern) ]) list([ tt(-Q) ] [ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) ! liat([ tt(-W) var(file-prefix) ] list([ tt(-q) ] [ tt(-X) var(explanation) ]) ! list([ tt(-l) var(cmd) ] [ tt(-U) ]) endlist() The remaining var(options) specify the type of command arguments --- 103,111 ---- [ tt(-s) var(subststring) ]) list([ tt(-K) var(function) ] [ tt(-H) var(num pattern) ]) list([ tt(-Q) ] [ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) ! list([ tt(-W) var(file-prefix) ]) list([ tt(-q) ] [ tt(-X) var(explanation) ]) ! list([ tt(-Y) var(func-or-var) ] [ tt(-l) var(cmd) ] [ tt(-U) ]) endlist() The remaining var(options) specify the type of command arguments *************** *** 360,365 **** --- 360,385 ---- options. A `tt(%n)' in this string is replaced by the number of matches. The explanation only appears if completion was tried and there was no unique match. + ) + item(tt(-Y) var(func-or-var))( + The list provided by var(func-or-var) is displayed instead of the list + of completions whenever a listing is required; the actual completions + to be inserted are not affected. It can be provided in two + ways. Firstly, if var(func-or-var) begins with a tt($) it defines an + array variable, or if it begins with a left parenthesis a literal + array, which contains the list. A variable may have been set by a + call to a function using the tt(-K) option. Otherwise it contains the + name of a function which will be executed to create the list. The + function will be passed as an argument list all matching completions, + including prefixes and suffixes expanded in full, and should set the + array var(reply) to the result. In both cases, the display list will + only be retrieved after a complete list of matches has been created. + + Note that the returned list does not have to correspond, even in + length, to the original set of matches; the use of an array is purely + a convenience for formatting. No special formatting of characters is + performed on the output in this case; in particular, newlines are + printed literally and if they appear output in columns is suppressed. ) enditem() texinode(Alternative Completion)(Extended Completion)(Option Flags)(Programmable Completion) *** Src/Zle/comp.h.YoY Thu Sep 18 09:58:39 1997 --- Src/Zle/comp.h Wed Nov 19 11:45:01 1997 *************** *** 102,107 **** --- 102,108 ---- char *str; /* for -s (expansion) */ char *func; /* for -K (function) */ char *explain; /* for -X (explanation) */ + char *ylist; /* for -Y (user-defined desc. for listing) */ char *prefix, *suffix; /* for -P and -S (prefix, suffix) */ char *subcmd; /* for -l (command name to use) */ char *withd; /* for -w (with directory */ *** Src/Zle/comp1.c.YoY Thu Sep 18 09:58:40 1997 --- Src/Zle/comp1.c Wed Nov 19 12:18:44 1997 *************** *** 80,85 **** --- 80,86 ---- zsfree(cc->str); zsfree(cc->func); zsfree(cc->explain); + zsfree(cc->ylist); zsfree(cc->prefix); zsfree(cc->suffix); zsfree(cc->hpat); *** Src/Zle/compctl.c.YoY Thu Sep 18 09:59:17 1997 --- Src/Zle/compctl.c Wed Nov 19 12:18:41 1997 *************** *** 217,222 **** --- 217,234 ---- *argv = "" - 1; } break; + case 'Y': + if ((*argv)[1]) { + cct.ylist = (*argv) + 1; + *argv = "" - 1; + } else if (!argv[1]) { + zwarnnam(name, "function/variable expect after -%c", + NULL, **argv); + } else { + cct.ylist = *++argv; + *argv = "" - 1; + } + break; case 'P': if ((*argv)[1]) { cct.prefix = (*argv) + 1; *************** *** 687,693 **** Compctl cc; if (cct->subcmd && (cct->keyvar || cct->glob || cct->str || ! cct->func || cct->explain || cct->prefix)) { zwarnnam(name, "illegal combination of options", NULL, 0); return 1; } --- 699,706 ---- Compctl cc; if (cct->subcmd && (cct->keyvar || cct->glob || cct->str || ! cct->func || cct->explain || cct->ylist || ! cct->prefix)) { zwarnnam(name, "illegal combination of options", NULL, 0); return 1; } *************** *** 725,730 **** --- 738,744 ---- zsfree(cc->str); zsfree(cc->func); zsfree(cc->explain); + zsfree(cc->ylist); zsfree(cc->prefix); zsfree(cc->suffix); zsfree(cc->subcmd); *************** *** 740,745 **** --- 754,760 ---- cc->str = ztrdup(cct->str); cc->func = ztrdup(cct->func); cc->explain = ztrdup(cct->explain); + cc->ylist = ztrdup(cct->ylist); cc->prefix = ztrdup(cct->prefix); cc->suffix = ztrdup(cct->suffix); cc->subcmd = ztrdup(cct->subcmd); *************** *** 857,862 **** --- 872,878 ---- printif(cc->keyvar, 'k'); printif(cc->func, 'K'); printif(cc->explain, 'X'); + printif(cc->ylist, 'Y'); printif(cc->prefix, 'P'); printif(cc->suffix, 'S'); printif(cc->glob, 'g'); *** Src/Zle/zle_tricky.c.YoY Thu Sep 18 10:01:00 1997 --- Src/Zle/zle_tricky.c Wed Nov 19 17:46:32 1997 *************** *** 123,128 **** --- 123,133 ---- static int nmatches; + /* A list of user-defined explanations for the completions to be shown * + * instead of amatches when listing completions. */ + + static char **aylist; + /* !=0 if we have a valid completion list. */ static int validlist; *************** *** 2996,3003 **** ccsuffix = cc->suffix; validlist = 1; ! if ((nmatches || expl) && !errflag) return 0; if ((isf || cc->xor) && !parampre) { /* We found no matches, but there is a xor'ed completion: * --- 3001,3045 ---- ccsuffix = cc->suffix; validlist = 1; ! if ((nmatches || expl) && !errflag) { ! /* generating the user-defined explanations must happen last: * ! * if anything fails, we silently allow the normal completion * ! * list to be used. */ ! if (cc->ylist) { ! char **yaptr, *uv = NULL; ! List list; ! ! if (cc->ylist[0] == '$' || cc->ylist[0] == '(') { ! /* from variable: must be an array */ ! uv = cc->ylist + (cc->ylist[0] == '$'); ! } else if ((list = getshfunc(cc->ylist)) != &dummy_list) { ! /* from function: pass completions as arg list */ ! LinkList args = newlinklist(); ! int addlen = strlen(rpre) + strlen(rsuf) + 1; ! ! addlinknode(args, cc->ylist); ! for (yaptr = amatches; *yaptr; yaptr++) { ! /* can't use tricat(). rats. */ ! char *ptr = (char *)halloc(addlen + strlen(*yaptr)); ! sprintf(ptr, "%s%s%s", rpre, *yaptr, rsuf); ! addlinknode(args, ptr); ! } ! ! /* No harm in allowing read -l and -c here, too */ ! incompctlfunc = 1; ! doshfunc(list, args, 0, 1); ! incompctlfunc = 0; ! uv = "reply"; ! } else ! return 0; ! if (uv && (yaptr = get_user_var(uv))) { ! PERMALLOC { ! aylist = arrdup(yaptr); ! } LASTALLOC; ! } ! } return 0; + } if ((isf || cc->xor) && !parampre) { /* We found no matches, but there is a xor'ed completion: * *************** *** 3045,3050 **** --- 3087,3095 ---- listmatches(); if(validlist) { freearray(amatches); + if (aylist) + freearray(aylist); + aylist = 0; zsfree(rpre); zsfree(rsuf); zsfree(lpre); *************** *** 3516,3526 **** listmatches(void) { int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0; ! int off, boff, nboff; ! int of = (isset(LISTTYPES) && !(haswhat & HAS_MISC)); char **arr, **ap, sav; int nfpl, nfsl, nlpl, nlsl; ! int listmax = getiparam("LISTMAX"); #ifdef DEBUG /* Sanity check */ --- 3561,3572 ---- listmatches(void) { int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0; ! int off = 0, boff = 0, nboff = 0; ! int of = (!aylist && isset(LISTTYPES) && !(haswhat & HAS_MISC)); char **arr, **ap, sav; int nfpl, nfsl, nlpl, nlsl; ! int listmax = getiparam("LISTMAX"), litnl = 0; ! size_t (*strlenfn) _((char const *)); #ifdef DEBUG /* Sanity check */ *************** *** 3538,3549 **** /* Calculate the lengths of the prefixes/suffixes we have to ignore during printing. */ ! off = ispattern && ppre && *ppre && ! !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? strlen(ppre) : 0; ! boff = ispattern && psuf && *psuf && ! !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? strlen(psuf) : 0; ! nboff = ispattern && psuf && *psuf && ! !(haswhat & (HAS_MISC | HAS_PATHPAT)) ? niceztrlen(psuf) : 0; /* When called from expandorcompleteprefix, we probably have to remove a space now. */ --- 3584,3597 ---- /* Calculate the lengths of the prefixes/suffixes we have to ignore during printing. */ ! if (ispattern && !aylist && !(haswhat & (HAS_MISC | HAS_PATHPAT))) { ! if (ppre && *ppre) ! off = strlen(ppre); ! if (psuf && *psuf) { ! boff = strlen(psuf); ! nboff = niceztrlen(psuf); ! } ! } /* When called from expandorcompleteprefix, we probably have to remove a space now. */ *************** *** 3558,3595 **** /* Set the cursor below the prompt. */ trashzle(); - ct = nmatches; showinglist = 0; clearflag = (isset(USEZLE) && !termflags && (isset(ALWAYSLASTPROMPT) && zmult == 1)) || (unset(ALWAYSLASTPROMPT) && zmult != 1); ! arr = amatches; ! /* Calculate the column width, the number of columns and the number ! of lines. */ ! for (ap = arr; *ap; ap++) ! if ((cl = niceztrlen(*ap + off) - nboff + ! (ispattern ? 0 : ! (!(haswhat & HAS_MISC) ? nfpl + nfsl : nlpl + nlsl))) > longest) ! longest = cl; ! if (of) ! longest++; ! ! fw = longest + 2; ! fct = (columns + 1) / fw; ! if (fct == 0) { ! fct = 1; ! colsz = ct; ! up = colsz + nlnct - clearflag; for (ap = arr; *ap; ap++) ! up += (niceztrlen(*ap + off) - nboff + of + ! (ispattern ? 0 : ! (!(haswhat & HAS_MISC) ? nfpl + nfsl : nlpl + nlsl))) / columns; ! } else { ! colsz = (ct + fct - 1) / fct; ! up = colsz + nlnct - clearflag + (ct == 0); } /* Print the explanation string, if any. */ --- 3606,3683 ---- /* Set the cursor below the prompt. */ trashzle(); showinglist = 0; clearflag = (isset(USEZLE) && !termflags && (isset(ALWAYSLASTPROMPT) && zmult == 1)) || (unset(ALWAYSLASTPROMPT) && zmult != 1); ! /* just to keep gcc happy */ ! fw = colsz = up = 0; ! if (aylist) { ! arr = aylist; ! /* If no literal newlines, the remaining code should use strlen() */ ! strlenfn = (size_t (*) _((char const *)))strlen; ! ! /* The hard bit here is that we are handling newlines literally. * ! * In fact, we are in principle handling all characters literally, * ! * but it's quite enough work with just newlines. * ! * If there are such, we give up trying to print the list as * ! * columns and print as rows, counting the extra newlines. */ ! ct = 0; ! for (ap = arr; *ap; ap++) { ! ct++; ! if (strchr(*ap, '\n')) ! litnl++; ! } ! if (litnl) { ! colsz = ct; ! up = colsz + nlnct - clearflag; ! /* Count real newlines, as well as overflowing lines. */ ! for (ap = arr; *ap; ap++) { ! char *nlptr, *sptr = *ap; ! while (sptr && *sptr) { ! up += (nlptr = strchr(sptr, '\n')) ! ? 1 + (nlptr-sptr)/columns ! : strlen(sptr)/columns; ! sptr = nlptr ? nlptr+1 : NULL; ! } ! } ! } ! } else { ! arr = amatches; ! ct = nmatches; ! strlenfn = niceztrlen; ! } ! ! if (!litnl) { ! /* Calculate the column width, the number of columns and the ! number of lines. */ for (ap = arr; *ap; ap++) ! if ((cl = strlenfn(*ap + off) - nboff + ! ((ispattern || aylist) ? 0 : ! (!(haswhat & HAS_MISC) ? ! nfpl + nfsl : nlpl + nlsl))) > longest) ! longest = cl; ! if (of) ! longest++; ! ! fw = longest + 2; ! fct = (columns + 1) / fw; ! if (fct == 0) { ! fct = 1; ! colsz = ct; ! up = colsz + nlnct - clearflag; ! for (ap = arr; *ap; ap++) ! up += (strlenfn(*ap + off) - nboff + of + ! ((ispattern || aylist) ? 0 : ! (!(haswhat & HAS_MISC) ? ! nfpl + nfsl : nlpl + nlsl))) / columns; ! } else { ! colsz = (ct + fct - 1) / fct; ! up = colsz + nlnct - clearflag + (ct == 0); ! } } /* Print the explanation string, if any. */ *************** *** 3671,3677 **** while (*ap) { int t2; ! if (ispattern) { int cut = strlen(*ap) - boff; sav = ap[0][cut]; --- 3759,3768 ---- while (*ap) { int t2; ! if (aylist) { ! zputs(*ap, shout); ! t2 = strlen(*ap); ! } else if (ispattern) { int cut = strlen(*ap) - boff; sav = ap[0][cut]; -- Peter Stephenson Tel: +49 33762 77366 WWW: http://www.ifh.de/~pws/ Fax: +49 33762 77413 Deutsches Elektronen-Synchrotron --- Institut fuer Hochenergiephysik Zeuthen DESY-IfH, Platanenallee 6, 15738 Zeuthen, Germany.