From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15359 invoked from network); 18 Jul 2001 13:07:58 -0000 Received: from sunsite.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 18 Jul 2001 13:07:58 -0000 Received: (qmail 892 invoked by alias); 18 Jul 2001 13:07:45 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 15407 Received: (qmail 818 invoked from network); 18 Jul 2001 13:07:43 -0000 From: Sven Wischnowsky Date: Wed, 18 Jul 2001 15:07:27 +0200 (MET DST) Message-Id: <200107181307.PAA07497@beta.informatik.hu-berlin.de> To: zsh-workers@sunsite.dk Subject: PATCH: Re: long/short options In-Reply-To: <200107160714.JAA01384@beta.informatik.hu-berlin.de> I just had to play a bit today... This makes _describe and compdescribe try to group matches with the same description. I've used Bart's first suggestion, i.e. it really searches for matches with the same desription, not with some special description syntax, although that would be quite easy to add or change now. But I preferred it this way, because it makes adding multiple options with equal arguments easier using brace expansion. The whole thing can be turned off by using the new list-grouped style. It defaults to `true', because it can make completion lists shorter, which is always a good thing. I've also made sure that in menu selection the entries for all options belonging together are shown together. Unfortunately that meant to make all those strings start with the same (the list of options), which is a bit ugly. If you try it, you'll see that I at least changed the descriptions of the second to n'th string to be `|' which sorts in after most normal characters and hence makes the list look not too stupid. I'm open to every suggestion to make this even more pleasing -- the best we could get is probably to make the second to n'th entry show only the i'th option and something like ` - " - ', but for that we would have to sort the strings and add them as an unsorted group (i.e. mess with the compadd-options _describe gets from its caller). Certainly doable, though. The hunks in compresult and complist make sure that matches with a line-display string (compadd -ld) can be hidden with the `-n' option which didn't work before and which we need to make the strings appear only in menu selection. It also changes complist to not treat duplicate and `hidden' matches specially (in terms of colouring) unless the `du' and `hi' capabilities are set by the user. And one last remark: some completion functions seem to make sure to not add multiple options with the same description (try `ls', for an example). That was probably sensible before, but now it should be changed. I haven't done anything about that yet, maybe we should all try to look out for such things. The patch actually looks larger than it is -- quite a bit of re-indentation and I've changed the internals of compdescribe almost completely, making it much cleaner and easier to extend. I'm now considering if it might be sensible to make it cache the last list it built, but so far I haven't found anything that really got slower. Bye Sven Index: Completion/Base/Utility/_describe =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Base/Utility/_describe,v retrieving revision 1.1 diff -u -r1.1 _describe --- Completion/Base/Utility/_describe 2001/04/02 11:10:44 1.1 +++ Completion/Base/Utility/_describe 2001/07/18 12:56:08 @@ -2,8 +2,8 @@ # This can be used to add options or values with descriptions as matches. -local _opt _expl _tmps _tmpd _tmpmd _tmpms _ret=1 _showd _nm _hide _args -local _type=values _descr +local _opt _expl _tmps _tmpd _tmph _tmpmd _tmpms _tmpmh +local _type=values _descr _ret=1 _showd _nm _hide _args _grp # Get the option. @@ -21,6 +21,11 @@ # Do the tests. `showd' is set if the descriptions should be shown. zstyle -T ":completion:${curcontext}:$_type" verbose && _showd=yes +if zstyle -T ":completion:${curcontext}:$_type" list-grouped; then + _grp=(-g) +else + _grp=() +fi _descr="$1" shift @@ -33,12 +38,12 @@ while _next_label "$_type" _expl "$_descr"; do if [[ -n "$_showd" ]]; then - compdescribe -I ' -- ' "$@" + compdescribe -I ' -- ' "$_grp[@]" "$@" else compdescribe -i "$@" fi - while compdescribe -g _args _tmpd _tmpmd _tmps _tmpms; do + while compdescribe -g _args _tmpd _tmpmd _tmph _tmpmh _tmps _tmpms; do # See if we should remove the option prefix characters. @@ -53,6 +58,7 @@ fi compadd "$_args[@]" "$_expl[@]" -ld _tmpd -a _tmpmd && _ret=0 + compadd -n "$_args[@]" "$_expl[@]" -ld _tmph -a _tmpmh && _ret=0 compadd "$_args[@]" "$_expl[@]" -d _tmps -a _tmpms && _ret=0 done done Index: Completion/Zsh/Command/_zstyle =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Zsh/Command/_zstyle,v retrieving revision 1.2 diff -u -r1.2 _zstyle --- Completion/Zsh/Command/_zstyle 2001/07/17 09:04:29 1.2 +++ Completion/Zsh/Command/_zstyle 2001/07/18 12:56:08 @@ -55,6 +55,7 @@ last-prompt c:bool list c:listwhen list-colors c: + list-grouped c:bool list-packed c:bool list-prompt c: list-rows-first c:bool Index: Doc/Zsh/compsys.yo =================================================================== RCS file: /cvsroot/zsh/zsh/Doc/Zsh/compsys.yo,v retrieving revision 1.129 diff -u -r1.129 compsys.yo --- Doc/Zsh/compsys.yo 2001/07/17 09:04:29 1.129 +++ Doc/Zsh/compsys.yo 2001/07/18 12:56:11 @@ -1508,6 +1508,17 @@ The default colors are the same as for the GNU tt(ls) command and can be obtained by setting the style to an empty string (i.e. tt('')). ) +kindex(list-grouped, completion style) +item(tt(list-grouped))( +If this style is `true' (the default), the completion system will try to +make some completion listings more compact by grouping matches together. +For example, options for commands that have the same description (which +are shown because the tt(verbose) style is set to `true') will have only +one entry in the list, showing all options that can be used to select +the specific behaviour. When menu selection is done on such a list, the +matches will appear as separate entries in the list to be able to select +each of them. +) kindex(list-packed, completion style) item(tt(list-packed))( Like the tt(list-colors) style, this is tested with the tt(default) @@ -3462,6 +3473,9 @@ the tt(prefix-hidden), tt(prefix-needed) and tt(verbose) styles to find out if the strings should be added at all and if the descriptions should be shown. Without the `tt(-o)' option, only the tt(verbose) style is used. + +If selected by the tt(list-grouped) style, strings with the same +description will be added in a way that they appear together in the list. tt(_describe) uses the tt(_all_labels) function to generate the matches, so it does not need to appear inside a loop over tag labels. Index: Src/Zle/complist.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/complist.c,v retrieving revision 1.42 diff -u -r1.42 complist.c --- Src/Zle/complist.c 2001/06/13 14:03:44 1.42 +++ Src/Zle/complist.c 2001/07/18 12:56:12 @@ -77,7 +77,7 @@ static char *defcols[] = { "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL, - "\033[", "m", NULL, "0", "0", "7", "0", "0" + "\033[", "m", NULL, "0", "0", "7", NULL, NULL }; /* This describes a terminal string for a file type. */ @@ -444,6 +444,7 @@ return; } + zlrputs(c, "0"); } /* Turn off colouring. */ @@ -1170,7 +1171,8 @@ p = g->matches; for (; (m = *p); p++) { - if (m->disp && (m->flags & CMF_DISPLINE)) { + if (m->disp && (m->flags & CMF_DISPLINE) && + (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) { if (pnl) { if (dolistnl(ml) && compprintnl(ml)) goto end; @@ -1412,9 +1414,11 @@ mgtabp = mgtab + mm; mmlen = mcols; zcputs(&mcolors, g->name, COL_MA); - } else if (m->flags & CMF_NOLIST) + } else if ((m->flags & CMF_NOLIST) && + mcolors.files[COL_HI] && mcolors.files[COL_HI]->col) zcputs(&mcolors, g->name, COL_HI); - else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT))) + else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)) && + mcolors.files[COL_DU] && mcolors.files[COL_DU]->col) zcputs(&mcolors, g->name, COL_DU); else subcols = putmatchcol(&mcolors, g->name, m->disp); Index: Src/Zle/compresult.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/compresult.c,v retrieving revision 1.37 diff -u -r1.37 compresult.c --- Src/Zle/compresult.c 2001/05/28 11:42:00 1.37 +++ Src/Zle/compresult.c 2001/07/18 12:56:12 @@ -1443,44 +1443,46 @@ } m->flags &= ~CMF_HIDE; - if (m->disp) { - if (m->flags & CMF_DISPLINE) { - nlines += 1 + printfmt(m->disp, 0, 0, 0); - g->flags |= CGF_HASDL; - } else { - l = niceztrlen(m->disp); - ndisp++; - if (l > glong) - glong = l; - if (l < gshort) - gshort = l; - totl += l; - mlens[m->gnum] = l; - } - nlist++; - if (!(m->flags & CMF_PACKED)) - g->flags &= ~CGF_PACKED; - if (!(m->flags & CMF_ROWS)) - g->flags &= ~CGF_ROWS; - } else if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) { + if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) { if ((m->flags & (CMF_NOLIST | CMF_MULT)) && (!m->str || !*m->str)) { m->flags |= CMF_HIDE; continue; } - l = niceztrlen(m->str); - ndisp++; - if (l > glong) - glong = l; - if (l < gshort) - gshort = l; - totl += l; - mlens[m->gnum] = l; - nlist++; - if (!(m->flags & CMF_PACKED)) - g->flags &= ~CGF_PACKED; - if (!(m->flags & CMF_ROWS)) - g->flags &= ~CGF_ROWS; + if (m->disp) { + if (m->flags & CMF_DISPLINE) { + nlines += 1 + printfmt(m->disp, 0, 0, 0); + g->flags |= CGF_HASDL; + } else { + l = niceztrlen(m->disp); + ndisp++; + if (l > glong) + glong = l; + if (l < gshort) + gshort = l; + totl += l; + mlens[m->gnum] = l; + } + nlist++; + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; + } else { + l = niceztrlen(m->str); + ndisp++; + if (l > glong) + glong = l; + if (l < gshort) + gshort = l; + totl += l; + mlens[m->gnum] = l; + nlist++; + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; + } } else hidden = 1; } @@ -1972,7 +1974,8 @@ if (g->flags & CGF_HASDL) { for (p = g->matches; (m = *p); p++) - if (m->disp && (m->flags & CMF_DISPLINE)) { + if (m->disp && (m->flags & CMF_DISPLINE) && + (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) { if (pnl) { putc('\n', shout); pnl = 0; Index: Src/Zle/computil.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/computil.c,v retrieving revision 1.62 diff -u -r1.62 computil.c --- Src/Zle/computil.c 2001/06/26 09:05:10 1.62 +++ Src/Zle/computil.c 2001/07/18 12:56:14 @@ -31,59 +31,42 @@ #include "computil.pro" -/* Help for `_display'. */ +/* Help for `_describe'. */ -/* Calculation state. */ - -typedef struct cdisp *Cdisp; - -struct cdisp { - int pre; /* prefix length */ - int suf; /* suffix length */ - int colon; /* number of strings with descriptions */ -}; - -/* Calculate longest prefix and suffix and count the strings with - * descriptions. */ - -static void -cdisp_calc(Cdisp disp, char **args) -{ - char *cp; - int i, nbc; - - for (; *args; args++) { - for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++) - if (*cp == '\\' && cp[1]) - cp++, nbc++; - if (*cp == ':' && cp[1]) { - disp->colon++; - if ((i = cp - *args - nbc) > disp->pre) - disp->pre = i; - if ((i = strlen(cp + 1)) > disp->suf) - disp->suf = i; - } - } -} - -/* Help fuer `_describe'. */ - typedef struct cdset *Cdset; +typedef struct cdstr *Cdstr; struct cdstate { int showd; /* != 0 if descriptions should be shown */ char *sep; /* the separator string */ + int slen; /* its length */ Cdset sets; /* the sets of matches */ - struct cdisp disp; /* used to calculate the alignment */ + int pre; /* longest prefix (before description) */ + int suf; /* longest suffix (description) */ +}; + +struct cdstr { + Cdstr next; /* the next one in this set */ + char *str; /* the string to display */ + char *desc; /* the description or NULL */ + char *match; /* the match to add */ + Cdstr other; /* next string with the same description */ + int kind; /* 0: not in a group, 1: the first, 2: other */ }; struct cdset { Cdset next; /* guess what */ char **opts; /* the compadd-options */ - char **strs; /* the display-strings */ - char **matches; /* the matches (or NULL) */ + Cdstr strs; /* the strings/matches */ + int count; /* number of matches in this set */ + int desc; /* number of matches with description */ }; +/* Maximum string length when used with descriptions. */ + +#define CD_MAXLEN 20 + + static struct cdstate cd_state; static int cd_parsed = 0; @@ -91,26 +74,116 @@ freecdsets(Cdset p) { Cdset n; + Cdstr s, sn; for (; p; p = n) { n = p->next; if (p->opts) freearray(p->opts); - if (p->strs) - freearray(p->strs); - if (p->matches) - freearray(p->matches); + for (s = p->strs; s; s = sn) { + sn = s->next; + zsfree(s->str); + zsfree(s->desc); + if (s->match != s->str) + zsfree(s->match); + zfree(s, sizeof(*s)); + } zfree(p, sizeof(*p)); } } +/* Find matches with same descriptions and group them. */ + +static void +cd_group() +{ + Cdset set1, set2; + Cdstr str1, str2, *strp; + int yep = 0; + char *buf; + + for (set1 = cd_state.sets; set1; set1 = set1->next) { + for (str1 = set1->strs; str1; str1 = str1->next) { + if (!str1->desc || str1->kind != 0) + continue; + + strp = &(str1->other); + + for (set2 = set1; set2; set2 = set2->next) + for (str2 = (set2 == set1 ? str1->next : set2->strs); + str2; str2 = str2->next) + if (str2->desc && !strcmp(str1->desc, str2->desc)) { + str1->kind = 1; + str2->kind = 2; + zsfree(str2->desc); + str2->desc = ztrdup("|"); + *strp = str2; + strp = &(str2->other); + yep = 1; + } + *strp = NULL; + } + } + if (!yep) + return; + + for (set1 = cd_state.sets; set1; set1 = set1->next) { + for (str1 = set1->strs; str1; str1 = str1->next) { + if (str1->kind != 1) + continue; + + buf = str1->str; + for (str2 = str1->other; str2; str2 = str2->other) + buf = zhtricat(buf, ", ", str2->str); + + for (str2 = str1; str2; str2 = str2->other) { + if (str2->str == str2->match) + str2->match = ztrdup(str2->match); + zsfree(str2->str); + str2->str = ztrdup(buf); + } + } + } +} + +/* Calculate longest prefix and suffix and count the strings with + * descriptions. */ + +static void +cd_calc() +{ + Cdset set; + Cdstr str; + int l; + + cd_state.pre = cd_state.suf = 0; + + for (set = cd_state.sets; set; set = set->next) { + set->count = set->desc = 0; + for (str = set->strs; str; str = str->next) { + set->count++; + if ((l = strlen(str->str)) > cd_state.pre) + cd_state.pre = l; + if (str->desc) { + set->desc++; + if ((l = strlen(str->desc)) > cd_state.suf) + cd_state.suf = l; + } + } + } + if (cd_state.pre > 20) + cd_state.pre = 20; +} + /* Initialisation. Store and calculate the string and matches and so on. */ static int cd_init(char *nam, char *sep, char **args, int disp) { Cdset *setp, set; + Cdstr *strp, str; char **ap, *tmp; + int grp = 0; if (cd_parsed) { zsfree(cd_state.sep); @@ -119,29 +192,57 @@ } setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); + cd_state.slen = ztrlen(sep); cd_state.sets = NULL; - cd_state.disp.pre = cd_state.disp.suf = cd_state.disp.colon = 0; cd_state.showd = disp; + if (*args && !strcmp(*args, "-g")) { + args++; + grp = 1; + } while (*args) { *setp = set = (Cdset) zcalloc(sizeof(*set)); setp = &(set->next); + *setp = NULL; + set->opts = NULL; + set->strs = NULL; if (!(ap = get_user_var(*args))) { zwarnnam(nam, "invalid argument: %s", *args, 0); + zsfree(cd_state.sep); + freecdsets(cd_state.sets); return 1; } - set->strs = zarrdup(ap); - - if (disp) - cdisp_calc(&(cd_state.disp), set->strs); + for (strp = &(set->strs); *ap; ap++) { + *strp = str = (Cdstr) zalloc(sizeof(*str)); + strp = &(str->next); + + str->kind = 0; + str->other = NULL; + + for (tmp = *ap; *tmp && *tmp != ':'; tmp++) + if (*tmp == '\\' && tmp[1]) + tmp++; + + if (*tmp) + str->desc = ztrdup(rembslash(tmp + 1)); + else + str->desc = NULL; + *tmp = '\0'; + str->str = str->match = ztrdup(rembslash(*ap)); + } + str->next = NULL; if (*++args && **args != '-') { if (!(ap = get_user_var(*args))) { zwarnnam(nam, "invalid argument: %s", *args, 0); + zsfree(cd_state.sep); + freecdsets(cd_state.sets); return 1; } - set->matches = zarrdup(ap); + for (str = set->strs; str && *ap; str = str->next, ap++) + str->match = ztrdup(*ap); + args++; } for (ap = args; *args && @@ -154,6 +255,11 @@ if ((*args = tmp)) args++; } + if (disp && grp) + cd_group(); + + cd_calc(); + cd_parsed = 1; return 0; } @@ -166,77 +272,60 @@ Cdset set; if ((set = cd_state.sets)) { - char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; - char **p, **mp, *cp, *copy, *cpp, oldc; - int dl = 1, sl = 1, sepl = strlen(cd_state.sep); - int pre = cd_state.disp.pre, suf = cd_state.disp.suf; - VARARR(char, buf, pre + suf + sepl + 1); - - for (p = set->strs; *p; p++) - if (cd_state.showd) { - for (cp = *p; *cp && *cp != ':'; cp++) - if (*cp == '\\' && cp[1]) - cp++; - if (*cp == ':' && cp[1]) - dl++; - else - sl++; - } else - sl++; - - sd = (char **) zalloc(dl * sizeof(char *)); - ss = (char **) zalloc(sl * sizeof(char *)); - md = (char **) zalloc(dl * sizeof(char *)); - ms = (char **) zalloc(sl * sizeof(char *)); - - if (cd_state.showd) { - memcpy(buf + pre, cd_state.sep, sepl); - suf = pre + sepl; - } - - /* Build the aligned display strings. */ - - for (sdp = sd, ssp = ss, mdp = md, msp = ms, - p = set->strs, mp = set->matches; *p; p++) { - copy = dupstring(*p); - for (cp = cpp = copy; *cp && *cp != ':'; cp++) { - if (*cp == '\\' && cp[1]) - cp++; - *cpp++ = *cp; - } - oldc = *cpp; - *cpp = '\0'; - if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] && - cd_state.showd) { - memset(buf, ' ', pre); - memcpy(buf, copy, (cpp - copy)); - strcpy(buf + suf, cp + 1); - *sdp++ = ztrdup(buf); - if (mp) { - *mdp++ = ztrdup(*mp); - if (*mp) - mp++; - } else - *mdp++ = ztrdup(copy); - } else { - *ssp++ = ztrdup(copy); - if (mp) { - *msp++ = ztrdup(*mp); - if (*mp) - mp++; - } else - *msp++ = ztrdup(copy); - } - } - *sdp = *ssp = *mdp = *msp = NULL; - - p = zarrdup(set->opts); + char **sd, **sdp, **md, **mdp, **sh, **shp, **mh, **mhp; + char **ss, **ssp, **ms, **msp; + Cdstr str; + VARARR(char, buf, cd_state.pre + cd_state.suf + cd_state.slen + 1); + char *sufp = NULL, *disp; + + if (cd_state.showd) { + memcpy(buf + cd_state.pre, cd_state.sep, cd_state.slen); + sufp = buf + cd_state.pre + cd_state.slen; + } + sd = (char **) zalloc((set->desc + 1) * sizeof(char *)); + md = (char **) zalloc((set->desc + 1) * sizeof(char *)); + sh = (char **) zalloc((set->desc + 1) * sizeof(char *)); + mh = (char **) zalloc((set->desc + 1) * sizeof(char *)); + ss = (char **) zalloc((set->count + 1) * sizeof(char *)); + ms = (char **) zalloc((set->count + 1) * sizeof(char *)); + + for (sdp = sd, mdp = md, shp = sh, mhp = mh, + ssp = ss, msp = ms, str = set->strs; + str; + str = str->next) { + if (cd_state.showd && str->desc) { + if (strlen(str->str) > CD_MAXLEN) + disp = tricat(str->str, cd_state.sep, str->desc); + else { + strcpy(sufp, str->desc); + memset(buf, ' ', cd_state.pre); + memcpy(buf, str->str, strlen(str->str)); + disp = ztrdup(buf); + } + if (strlen(disp) >= columns) + disp[columns - 1] = '\0'; + + if (str->kind == 2) { + *shp++ = disp; + *mhp++ = ztrdup(str->match); + } else { + *sdp++ = disp; + *mdp++ = ztrdup(str->match); + } + } else { + *ssp++ = ztrdup(str->str); + *msp++ = ztrdup(str->match); + } + } + *sdp = *mdp = *shp = *mhp = *ssp = *msp = NULL; - setaparam(params[0], p); + setaparam(params[0], zarrdup(set->opts)); setaparam(params[1], sd); setaparam(params[2], md); - setaparam(params[3], ss); - setaparam(params[4], ms); + setaparam(params[3], sh); + setaparam(params[4], mh); + setaparam(params[5], ss); + setaparam(params[6], ms); cd_state.sets = set->next; set->next = NULL; @@ -268,8 +357,8 @@ if (cd_parsed) { int n = arrlen(args); - if (n != 6) { - zwarnnam(nam, (n < 6 ? "not enough arguments" : + if (n != 8) { + zwarnnam(nam, (n < 8 ? "not enough arguments" : "too many arguments"), NULL, 0); return 1; } -- Sven Wischnowsky wischnow@informatik.hu-berlin.de