diff options
-rw-r--r-- | Doc/Zsh/options.yo | 13 | ||||
-rw-r--r-- | Src/Zle/comp.h | 24 | ||||
-rw-r--r-- | Src/Zle/complist.c | 560 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 492 | ||||
-rw-r--r-- | Src/options.c | 2 | ||||
-rw-r--r-- | Src/zsh.h | 2 |
6 files changed, 628 insertions, 465 deletions
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 3359630d8..d4d101b71 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -603,6 +603,19 @@ completion widgets to return status 1 on an ambiguous completion, which causes the shell to beep if the option tt(BEEP) is also set; this may be modified if completion is called from a user-defined widget. ) +pindex(LIST_PACKED) +cindex(completion, listing) +item(tt(LIST_PACKED))( +Try to make the completion list smaller (occupying less lines) by +printing the matches in columns with different widths. +) +pindex(LIST_ROWS_FIRST) +cindex(completion, listing order) +item(tt(LIST_ROWS_FIRST))( +Lay out the matches in completion lists sorted horizontally, that is, +the second match is to the right of the first one, not under it as +usual. +) pindex(LIST_TYPES) cindex(marking file types) cindex(files, marking type of) diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index f27326a02..d7178a73a 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -195,6 +195,14 @@ struct cmgroup { LinkList lfmatches; /* list of matches without fignore */ LinkList lallccs; /* list of used compctls */ int num; /* number of this group */ + /* The following is collected/used during listing. */ + int dcount; /* number of matches to list in columns */ + int cols; /* number of columns */ + int lins; /* number of lines */ + int width; /* column width */ + int *widths; /* column widths for listpacked */ + int totl; /* total length */ + int shortest; /* length of shortest match */ }; @@ -321,6 +329,22 @@ struct cadata { char *disp; /* array with display lists (-d) */ }; +/* List data. */ + +typedef struct cldata *Cldata; + +struct cldata { + int columns; /* screen width */ + int lines; /* screen height */ + int valid; /* no need to calculate anew */ + int nlist; /* number of matches to list */ + int nlines; /* number of lines needed */ + int hidden; /* != 0 if there are hidden matches */ +}; + +typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int, + char *, struct stat *); + /* Data given to hooks. */ typedef struct chdata *Chdata; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 55e78c0cb..25fea68e1 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -317,434 +317,198 @@ putcolstr(Listcols c, char *n, mode_t m) static int noselect, mselect, inselect, mcol, mline, mcols, mlines; static Cmatch *mmatch, **mtab; static Cmgroup mgroup, *mgtab; +static struct listcols mcolors; -/* List the matches. Most of this is just taken from ilistmatches(), - * of course. */ + +static void +clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, + char *path, struct stat *buf) +{ + Cmatch m; + int len, cc; + + if (!mp) { + zcputs(&mcolors, COL_MI); + len = width - 2; + while (len-- > 0) + putc(' ', shout); + if (mcolors.cols[COL_EC]) + tputs(mcolors.cols[COL_EC], 1, putshout); + else + zcputs(&mcolors, COL_NO); + + return; + } + m = *mp; + if (m->disp && (m->flags & CMF_DISPLINE)) { + if (mselect >= 0) { + int mm = (mcols * ml) + (mcols >> 1); + + mtab[mm] = mp; + mgtab[mm] = g; + } + if (m->gnum == mselect) { + mline = ml; + mmatch = mp; + mgroup = g; + cc = COL_MA; + } else + cc = COL_NO; + zcputs(&mcolors, cc); + printfmt(m->disp, 0, 1, 0); + if (mcolors.cols[COL_EC]) + tputs(mcolors.cols[COL_EC], 1, putshout); + else + zcputs(&mcolors, COL_NO); + } else { + int mx; + + if (g->widths) { + int i; + + for (i = mx = 0; i < mc; i++) + mx += g->widths[i]; + } else + mx = mc * g->width; + mx += (width >> 1); + + if (mselect >= 0) { + int mm = mcols * ml; + + mtab[mx + mm] = mp; + mgtab[mx + mm] = g; + } + if (m->gnum == mselect) { + mcol = mx; + mline = ml; + mmatch = mp; + mgroup = g; + zcputs(&mcolors, COL_MA); + } else if (buf) + putcolstr(&mcolors, path, buf->st_mode); + else + zcputs(&mcolors, COL_NO); + + nicezputs((m->disp ? m->disp : m->str), shout); + len = niceztrlen(m->disp ? m->disp : m->str); + + if (isset(LISTTYPES)) { + if (buf) + putc(file_type(buf->st_mode), shout); + else + putc(' ', shout); + len++; + } + len = width - len - 2; + + while (len-- > 0) + putc(' ', shout); + + if (mcolors.cols[COL_EC]) + tputs(mcolors.cols[COL_EC], 1, putshout); + else + zcputs(&mcolors, COL_NO); + if (!lastc) { + zcputs(&mcolors, COL_NO); + fputs(" ", shout); + if (mcolors.cols[COL_EC]) + tputs(mcolors.cols[COL_EC], 1, putshout); + else + zcputs(&mcolors, COL_NO); + } + } +} static int complistmatches(Hookdef dummy, Chdata dat) { - Cmgroup amatches = dat->matches, g; - Cmatch *p, m; - Cexpl *e; - int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0; - int of = isset(LISTTYPES), cf; - int mc, ml = 0, cc, hasm = 0, cl = -1; - struct listcols col; + Cmgroup oamatches = amatches; + + amatches = dat->matches; if (minfo.asked == 2) { showinglist = 0; + amatches = oamatches; return (noselect = 1); } - getcols(&col); - - for (g = amatches; g; g = g->next) { - char **pp = g->ylist; - int nl = 0, l; - - if (pp) { - /* We have an ylist, lets see, if it contains newlines. */ - while (!nl && *pp) - nl = !!strchr(*pp++, '\n'); - - pp = g->ylist; - if (nl || !pp[1]) { - /* Yup, there are newlines, count lines. */ - char *nlptr, *sptr; - - g->flags |= CGF_LINES; - noselect = 1; - while ((sptr = *pp)) { - while (sptr && *sptr) { - nlines += (nlptr = strchr(sptr, '\n')) - ? 1 + (nlptr-sptr)/columns - : strlen(sptr)/columns; - sptr = nlptr ? nlptr+1 : NULL; - } - nlines++; - pp++; - } - nlines--; - } else { - while (*pp) { - if ((l = strlen(*pp)) > longest) - longest = l; - nlist++; - pp++; - } - } - } else { - for (p = g->matches; (m = *p); p++) { - if (m->disp) { - if (m->flags & CMF_DISPLINE) { - nlines += 1 + printfmt(m->disp, 0, 0, 0); - g->flags |= CGF_HASDL; - } else if ((l = niceztrlen(m->disp)) > longest) - longest = l; - nlist++; - } else if (!(m->flags & CMF_NOLIST)) { - if ((l = niceztrlen(m->str)) > longest) - longest = l; - nlist++; - } else - noselect = 1; - } - } - if ((e = g->expls)) { - while (*e) { - if ((*e)->count) - nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1); - e++; - } - } - } - longest += 2 + of; - if ((ncols = columns / longest)) { - for (g = amatches; g; g = g->next) - nlines += (g->lcount - g->llcount + ncols - 1) / ncols; - } else { - ncols = 1; - opl = 1; - for (g = amatches; g; g = g->next) { - char **pp = g->ylist; - - if (pp) { - if (!(g->flags & CGF_LINES)) { - while (*pp) { - nlines += 1 + (strlen(*pp) / columns); - pp++; - } - } - } else - for (p = g->matches; (m = *p); p++) - if (m->disp) { - if (m->flags & CMF_DISPLINE) - nlines += 1 + printfmt(m->disp, 0, 0, 0); - else - nlines += 1 + (niceztrlen(m->disp) / columns); - } else if (!(m->flags & CMF_NOLIST)) - nlines += 1 + ((1 + niceztrlen(m->str)) / columns); - } - } - cf = (isset(USEZLE) && !termflags && complastprompt && *complastprompt); - if (!nlines || (mselect >= 0 && (!cf || (nlines + nlnct - 1) >= lines))) { + getcols(&mcolors); + + calclist(); + + if (!listdat.nlines || (mselect >= 0 && + (!(isset(USEZLE) && !termflags && + complastprompt && *complastprompt) || + (listdat.nlines + nlnct - 1) >= lines))) { showinglist = listshown = 0; noselect = 1; + amatches = oamatches; return 1; } - /* Set the cursor below the prompt. */ + if (listdat.hidden) { + noselect = 1; + mselect = -1; + } if (inselect) clearflag = 0; - trashzle(); - showinglist = listshown = 0; - - clearflag = cf; - - /* Maybe we have to ask if the user wants to see the list. */ - if ((!minfo.cur || !minfo.asked) && - ((complistmax && nlist > complistmax) || - (!complistmax && nlines >= lines))) { - int qup; - zsetterm(); - qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1, 1); - fflush(shout); - if (getzlequery() != 'y') { - if (clearflag) { - putc('\r', shout); - tcmultout(TCUP, TCMULTUP, qup); - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - tcmultout(TCUP, TCMULTUP, nlnct); - } else - putc('\n', shout); - noselect = 1; - if (minfo.cur) - minfo.asked = 2; - return 1; - } - if (clearflag) { - putc('\r', shout); - tcmultout(TCUP, TCMULTUP, qup); - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } else - putc('\n', shout); - settyinfo(&shttyinfo); - if (minfo.cur) - minfo.asked = 1; + + if (asklist()) { + amatches = oamatches; + return 1; } if (mselect >= 0) { int i; - i = ncols * nlines; + i = columns * listdat.nlines; free(mtab); mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **)); memset(mtab, 0, i * sizeof(Cmatch **)); free(mgtab); mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup)); memset(mgtab, 0, i * sizeof(Cmgroup)); - mcols = ncols; - mlines = cl = nlines; - if (cl < 2) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } + mcols = columns; + mlines = listdat.nlines; } - /* Now print the matches. */ last_col = COL_NO - 1; - g = amatches; - while (g) { - char **pp = g->ylist; - if ((e = g->expls)) { - int l; - - while (*e) { - if ((*e)->count) { - if (pnl) { - putc('\n', shout); - pnl = 0; - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - } - l = printfmt((*e)->str, (*e)->count, 1, 1); - ml += l; - if (cl >= 0 && (cl -= l) <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - pnl = 1; - } - e++; - } - } - if (pp && *pp) { - if (pnl) { - putc('\n', shout); - pnl = 0; - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - } - if (g->flags & CGF_LINES) { - while (*pp) { - zputs(*pp, shout); - if (*++pp) - putc('\n', shout); - } - } else { - int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a; - char **pq; - - while (n && nl--) { - i = ncols; - mc = 0; - pq = pp; - while (n && i--) { - if (pq - g->ylist >= g->lcount) - break; - zputs(*pq, shout); - if (i) { - a = longest - strlen(*pq); - while (a--) - putc(' ', shout); - } - pq += nc; - n--; - } - if (n) { - putc('\n', shout); - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - } - pp++; - } - } - } else if (g->lcount) { - int n = g->lcount - g->llcount, nl = (n + ncols - 1) / ncols; - int nc = nl, i, j, a = 0; - int zt; - Cmatch *q; - - if (g->flags & CGF_HASDL) { - for (p = g->matches; (m = *p); p++) - if (m->disp && (m->flags & CMF_DISPLINE)) { - if (pnl) { - putc('\n', shout); - pnl = 0; - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - } - hasm = 1; - if (mselect >= 0) { - for (i = 0; i < ncols; i++) { - mtab[i + (ncols * ml)] = p; - mgtab[i + (ncols * ml)] = g; - } - } - if (m->gnum == mselect) { - mline = ml; - mmatch = p; - mgroup = g; - cc = COL_MA; - } else - cc = COL_NO; - zcputs(&col, cc); - printfmt(m->disp, 0, 1, 0); - if (col.cols[COL_EC]) - tputs(col.cols[COL_EC], 1, putshout); - else - zcputs(&col, COL_NO); - pnl = 1; - } - } - if (n && pnl) { - putc('\n', shout); - pnl = 0; - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - } - for (p = skipnolist(g->matches); n && nl--;) { - i = ncols; - mc = 0; - q = p; - while (n && i--) { - if (!(m = *q)) { - zcputs(&col, COL_MI); - a = longest - 2; - while (a--) - putc(' ', shout); - if (col.cols[COL_EC]) - tputs(col.cols[COL_EC], 1, putshout); - else - zcputs(&col, COL_NO); - break; - } - hasm = 1; - if (mselect >= 0) { - mtab[mc + (ncols * ml)] = q; - mgtab[mc + (ncols * ml)] = g; - } - if (m->gnum == mselect) { - mcol = mc; - mline = ml; - mmatch = q; - mgroup = g; - cc = COL_MA; - } else - cc = -1; - if (!m->disp && m->flags & CMF_FILE) { - struct stat buf; - char *pb; - - pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) + - 3 + strlen(m->str)); - sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"), - m->str); - - zt = ztat(pb, &buf, 1); - if (cc >= 0) - zcputs(&col, cc); - else if (zt) - zcputs(&col, COL_NO); - else - putcolstr(&col, pb, buf.st_mode); - nicezputs(m->str, shout); - if (zt) - putc(' ', shout); - else - putc(file_type(buf.st_mode), shout); - } else { - zcputs(&col, cc >= 0 ? cc : COL_NO); - nicezputs((m->disp ? m->disp : m->str), shout); - if (of) - putc(' ', shout); - } - a = longest - niceztrlen(m->disp ? m->disp : m->str) - 2 - of; - while (a-- > 0) - putc(' ', shout); - if (col.cols[COL_EC]) - tputs(col.cols[COL_EC], 1, putshout); - else - zcputs(&col, COL_NO); - if (i) { - zcputs(&col, COL_NO); - fputs(" ", shout); - if (col.cols[COL_EC]) - tputs(col.cols[COL_EC], 1, putshout); - else - zcputs(&col, COL_NO); - } - if (--n) - for (j = nc; j && *q; j--) - q = skipnolist(q + 1); - mc++; - } - if (i > 0) { - zcputs(&col, COL_MI); - a = longest - 2; - while (a--) - putc(' ', shout); - if (col.cols[COL_EC]) - tputs(col.cols[COL_EC], 1, putshout); - else - zcputs(&col, COL_NO); - } - if (n) { - putc('\n', shout); - ml++; - if (cl >= 0 && --cl <= 1) { - cl = -1; - if (tccan(TCCLEAREOD)) - tcout(TCCLEAREOD); - } - if (n && nl) - p = skipnolist(p + 1); - } - } - } - if (g->lcount) - pnl = 1; - g = g->next; - } - if (clearflag) { - /* Move the cursor up to the prompt, if always_last_prompt * - * is set and all that... */ - if ((nlines += nlnct - 1) < lines) { - tcmultout(TCUP, TCMULTUP, nlines); - showinglist = -1; - } else - clearflag = 0, putc('\n', shout); - } else - putc('\n', shout); - listshown = (clearflag ? 1 : -1); - if (!hasm || nlines >= lines) + if (!printlist(1, clprintm) || listdat.nlines >= lines) noselect = 1; + amatches = oamatches; + return noselect; } +static int +adjust_mcol(Cmatch ***tabp, Cmgroup **grp) +{ + Cmatch **tab = *tabp; + int p, n, c; + + tab -= mcol; + + for (p = mcol; p >= 0 && !tab[p]; p--); + for (n = mcol; n < mcols && !tab[n]; n++); + if (n == mcols) + n = -1; + + if (p < 0) { + if (n < 0) + return 1; + c = n; + } else if (n < 0) + c = p; + else + c = ((mcol - p) < (n - mcol) ? p : n); + + *tabp = tab + c; + if (grp) + *grp = *grp + c - mcol; + + mcol = c; + + return 0; +} + typedef struct menustack *Menustack; struct menustack { @@ -887,6 +651,8 @@ domenuselect(Hookdef dummy, Chdata dat) mline++; p += mcols; } + if (adjust_mcol(&p, NULL)) + continue; } while (!*p); } else if (cmd == Th(z_uphistory) || cmd == Th(z_uplineorhistory) || @@ -900,6 +666,8 @@ domenuselect(Hookdef dummy, Chdata dat) mline--; p -= mcols; } + if (adjust_mcol(&p, NULL)) + continue; } while (!*p); } else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) { do { @@ -958,6 +726,8 @@ domenuselect(Hookdef dummy, Chdata dat) p += mcols; pg += mcols; } + if (adjust_mcol(&p, &pg)) + continue; } while (ol != mline && (*pg == g || !*pg)); } else if (cmd == Th(z_backwardword) || cmd == Th(z_emacsbackwardword) || @@ -975,6 +745,8 @@ domenuselect(Hookdef dummy, Chdata dat) p -= mcols; pg -= mcols; } + if (adjust_mcol(&p, &pg)) + continue; } while (ol != mline && (*pg == g || !*pg)); } else if (cmd == Th(z_completeword) || cmd == Th(z_expandorcomplete) || diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 222e042f5..0183bea3b 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -155,6 +155,11 @@ static int nmatches, smatches; /**/ int validlist; +/* Information about the matches for listing. */ + +/**/ +struct cldata listdat; + /* This flag is non-zero if we are completing a pattern (with globcomplete) */ static int ispattern, haspattern; @@ -7087,6 +7092,7 @@ invalidatelist(void) if (validlist) freematches(); lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0; + listdat.valid = 0; if (listshown < 0) listshown = 0; minfo.cur = NULL; @@ -7496,6 +7502,8 @@ permmatches(void) *cp = NULL; } else n->ccs = NULL; + n->widths = NULL; + g = g->next; } for (g = amatches; g; g = g->next) { @@ -7575,6 +7583,7 @@ freematches(void) g = n; } hasperm = 0; + listdat.valid = 0; } /* Insert the given string into the command line. If move is non-zero, * @@ -8531,21 +8540,26 @@ listmatches(void) } /**/ -int -ilistmatches(Hookdef dummy, Chdata dat) +void +calclist(void) { Cmgroup g; Cmatch *p, m; Cexpl *e; - int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0; - int of = isset(LISTTYPES), opl = 0; + int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES); + int max = 0, i; + VARARR(int, mlens, nmatches + 1); + + if (listdat.valid && lines == listdat.lines && columns == listdat.columns) + return; for (g = amatches; g; g = g->next) { char **pp = g->ylist; - int nl = 0, l; + int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0; if (pp) { /* We have an ylist, lets see, if it contains newlines. */ + hidden = 1; while (!nl && *pp) nl = !!strchr(*pp++, '\n'); @@ -8555,7 +8569,7 @@ ilistmatches(Hookdef dummy, Chdata dat) char *nlptr, *sptr; g->flags |= CGF_LINES; - + hidden = 1; while ((sptr = *pp)) { while (sptr && *sptr) { nlines += (nlptr = strchr(sptr, '\n')) @@ -8569,8 +8583,13 @@ ilistmatches(Hookdef dummy, Chdata dat) nlines--; } else { while (*pp) { - if ((l = strlen(*pp)) > longest) - longest = l; + l = strlen(*pp); + ndisp++; + if (l > glong) + glong = l; + if (l < gshort) + gshort = l; + totl += l; nlist++; pp++; } @@ -8581,14 +8600,29 @@ ilistmatches(Hookdef dummy, Chdata dat) if (m->flags & CMF_DISPLINE) { nlines += 1 + printfmt(m->disp, 0, 0, 0); g->flags |= CGF_HASDL; - } else if ((l = niceztrlen(m->disp)) > longest) - longest = l; + } else { + l = niceztrlen(m->disp); + ndisp++; + if (l > glong) + glong = l; + if (l < gshort) + gshort = l; + totl += l; + mlens[m->gnum] = l; + } nlist++; } else if (!(m->flags & CMF_NOLIST)) { - if ((l = niceztrlen(m->str)) > longest) - longest = l; + l = niceztrlen(m->str); + ndisp++; + if (l > glong) + glong = l; + if (l < gshort) + gshort = l; + totl += l; + mlens[m->gnum] = l; nlist++; - } + } else + hidden = 1; } } if ((e = g->expls)) { @@ -8598,39 +8632,228 @@ ilistmatches(Hookdef dummy, Chdata dat) e++; } } + g->totl = totl + (ndisp * add); + g->dcount = ndisp; + g->width = glong + add; + g->shortest = gshort + add; + if ((g->cols = columns / g->width) > g->dcount) + g->cols = g->dcount; + if (g->cols) { + i = g->cols * g->width - add; + if (i > max) + max = i; + } } - longest += 2 + of; - if ((ncols = columns / longest)) { - for (g = amatches; g; g = g->next) - nlines += (g->lcount - g->llcount + ncols - 1) / ncols; - } else { - ncols = 1; - opl = 1; - for (g = amatches; g; g = g->next) { - char **pp = g->ylist; + for (g = amatches; g; g = g->next) { + char **pp; + int glines = 0; + + zfree(g->widths, 0); + g->widths = NULL; + + if ((pp = g->ylist)) { + if (!(g->flags & CGF_LINES)) { + if (g->cols) { + glines += (arrlen(pp) + g->cols - 1) / g->cols; + if (g->cols > 1) + g->width += (max - (g->width * g->cols - add)) / g->cols; + } else { + g->cols = 1; + g->width = 1; - if (pp) { - if (!(g->flags & CGF_LINES)) { - while (*pp) { - nlines += 1 + (strlen(*pp) / columns); - pp++; - } + while (*pp) + glines += 1 + (strlen(*pp++) / columns); } - } else + } + } else { + if (g->cols) { + glines += (g->dcount + g->cols - 1) / g->cols; + if (g->cols > 1) + g->width += (max - (g->width * g->cols - add)) / g->cols; + } else if (!(g->flags & CGF_LINES)) { + g->cols = 1; + g->width = 0; + for (p = g->matches; (m = *p); p++) if (m->disp) { - if (m->flags & CMF_DISPLINE) - nlines += 1 + printfmt(m->disp, 0, 0, 0); - else - nlines += 1 + (niceztrlen(m->disp) / columns); + if (!(m->flags & CMF_DISPLINE)) + glines += 1 + (mlens[m->gnum] / columns); } else if (!(m->flags & CMF_NOLIST)) - nlines += 1 + ((1 + niceztrlen(m->str)) / columns); + glines += 1 + ((1 + mlens[m->gnum]) / columns); + } } + g->lins = glines; + nlines += glines; } - if (!nlines) { - showinglist = listshown = 0; - return 1; + if (isset(LISTPACKED)) { + char **pp; + int *ws, j, k, cl, ml, mm; + + for (g = amatches; g; g = g->next) { + ws = g->widths = (int *) zalloc(columns * sizeof(int)); + memset(ws, 0, columns * sizeof(int)); + i = g->lins; + mm = g->cols; + cl = 0; + + if ((pp = g->ylist)) { + if (!(g->flags & CGF_LINES)) { + int yl = arrlen(pp), i; + VARARR(int, ylens, yl); + + for (i = 0; *pp; i++, pp++) + ylens[i] = strlen(*pp) + add; + + if (isset(LISTROWSFIRST)) { + int x, l = 0, v; + + for (mm = columns / g->shortest; mm > g->cols; mm--) { + for (j = i = ml = cl = l = v = x = 0, k = g->dcount; + k > 0; k--) { + if (ylens[j] > ml) + ml = ylens[j]; + j += mm; + v++; + if (j >= g->dcount) { + if ((cl += ml) >= columns) + break; + ws[x++] = ml; + ml = 0; + j = ++i; + if (v > l) + l = v; + v = 0; + } + } + if (j < g->dcount) { + ws[x++] = ml; + cl += ml; + } + if (!k && cl < columns) + break; + } + if (mm > g->cols) + i = l; + } else { + for (i = ((g->totl + columns) / columns); + i < g->lins; i++) { + for (pp = g->ylist, j = k = cl = ml = mm; + *pp; j++, pp++) { + if (ylens[j] > ml) + ml = ylens[j]; + if (++k == i) { + if ((cl += ml) >= columns) + break; + ws[mm++] = ml; + ml = k = 0; + } + } + if (k) { + ws[mm++] = ml; + cl += ml; + } + if (j == yl && cl < columns) + break; + } + } + } + } else if (g->width) { + if (isset(LISTROWSFIRST)) { + int x, l = 0, v, al; + + for (mm = columns / g->shortest; mm > g->cols; mm--) { + for (j = i = ml = cl = l = v = x = 0, k = g->dcount; + k > 0; k--) { + m = g->matches[j]; + if (!(m->flags & (m->disp ? CMF_DISPLINE : + CMF_NOLIST))) { + al = mlens[m->gnum] + add; + if (al > ml) + ml = al; + j += mm; + v++; + if (j >= g->dcount) { + if ((cl += ml) >= columns) + break; + ws[x++] = ml; + ml = 0; + j = ++i; + if (v > l) + l = v; + v = 0; + } + } + } + if (j < g->dcount) { + ws[x++] = ml; + cl += ml; + } + if (!k && cl < columns) + break; + } + if (mm > g->cols) + i = l; + } else { + int al; + + for (i = ((g->totl + columns) / columns); + i < g->lins; i++) { + for (p = g->matches, j = k = cl = ml = mm = 0; + (m = *p); p++, j++) { + if (!(m->flags & (m->disp ? CMF_DISPLINE : + CMF_NOLIST))) { + al = mlens[m->gnum] + add; + if (al > ml) + ml = al; + if (++k == i) { + if ((cl += ml) >= columns) + break; + ws[mm++] = ml; + ml = k = 0; + } + } + } + if (k) { + ws[mm++] = ml; + cl += ml; + } + if (j == g->dcount && cl < columns) + break; + } + } + } + if (i == g->lins) { + zfree(ws, columns * sizeof(int)); + g->widths = NULL; + } else { + nlines += i - g->lins; + g->lins = i; + g->cols = mm; + } + g->totl = cl; + cl -= add; + if (cl > max) + max = cl; + } + for (g = amatches; g; g = g->next) { + if (g->widths) { + int *p, a = (max - g->totl - add) / g->cols; + + for (i = g->cols, p = g->widths; i; i--, p++) + *p += a; + } else if (g->width && g->cols > 1) + g->width += (max - (g->width * g->cols - add)) / g->cols; + } } + listdat.valid = 1; + listdat.hidden = hidden; + listdat.nlist = nlist; + listdat.nlines = nlines; +} + +/**/ +int asklist(void) +{ /* Set the cursor below the prompt. */ trashzle(); showinglist = listshown = 0; @@ -8640,12 +8863,12 @@ ilistmatches(Hookdef dummy, Chdata dat) /* Maybe we have to ask if the user wants to see the list. */ if ((!minfo.cur || !minfo.asked) && - ((complistmax && nlist > complistmax) || - (!complistmax && nlines >= lines))) { + ((complistmax && listdat.nlist > complistmax) || + (!complistmax && listdat.nlines >= lines))) { int qup; zsetterm(); qup = printfmt("zsh: do you wish to see all %n possibilities? ", - nlist, 1, 1); + listdat.nlist, 1, 1); fflush(shout); if (getzlequery() != 'y') { if (clearflag) { @@ -8658,7 +8881,7 @@ ilistmatches(Hookdef dummy, Chdata dat) putc('\n', shout); if (minfo.cur) minfo.asked = 2; - return 0; + return 1; } if (clearflag) { putc('\r', shout); @@ -8671,20 +8894,50 @@ ilistmatches(Hookdef dummy, Chdata dat) if (minfo.cur) minfo.asked = 1; } + return 0; +} + +/**/ +int +printlist(int over, CLPrintFunc printm) +{ + Cmgroup g; + Cmatch *p, m; + Cexpl *e; + int pnl = 0, cl = (over ? listdat.nlines : -1); + int mc, ml = 0, printed = 0; - /* Now print the matches. */ + if (cl < 2) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } g = amatches; while (g) { char **pp = g->ylist; if ((e = g->expls)) { + int l; + while (*e) { if ((*e)->count) { if (pnl) { putc('\n', shout); pnl = 0; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + l = printfmt((*e)->str, (*e)->count, 1, 1); + ml += l; + if (cl >= 0 && (cl -= l) <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); } - printfmt((*e)->str, (*e)->count, 1, 1); pnl = 1; } e++; @@ -8694,6 +8947,12 @@ ilistmatches(Hookdef dummy, Chdata dat) if (pnl) { putc('\n', shout); pnl = 0; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } } if (g->flags & CGF_LINES) { while (*pp) { @@ -8702,60 +8961,86 @@ ilistmatches(Hookdef dummy, Chdata dat) putc('\n', shout); } } else { - int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a; + int n = g->lcount, nl, nc, i, a; char **pq; + nl = nc = g->lins; + while (n && nl--) { - i = ncols; + i = g->cols; + mc = 0; pq = pp; while (n && i--) { if (pq - g->ylist >= g->lcount) break; zputs(*pq, shout); if (i) { - a = longest - strlen(*pq); + a = (g->widths ? g->widths[mc] : g->width) - + strlen(*pq); while (a--) putc(' ', shout); } - pq += nc; + pq += (isset(LISTROWSFIRST) ? 1 : nc); + mc++; n--; } - if (n) + if (n) { putc('\n', shout); - pp++; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + pp += (isset(LISTROWSFIRST) ? g->cols : 1); } } } else if (g->lcount) { - int n = g->lcount - g->llcount, nl = (n + ncols - 1) / ncols; - int nc = nl, i, j, a = 0; + int n = g->dcount, nl, nc, i, j, wid; Cmatch *q; + nl = nc = g->lins; + if (g->flags & CGF_HASDL) { for (p = g->matches; (m = *p); p++) if (m->disp && (m->flags & CMF_DISPLINE)) { if (pnl) { putc('\n', shout); pnl = 0; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } } - printfmt(m->disp, 0, 1, 0); + printed++; + printm(g, p, mc, ml, 1, 0, NULL, NULL); pnl = 1; } } if (n && pnl) { putc('\n', shout); pnl = 0; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } } for (p = skipnolist(g->matches); n && nl--;) { - i = ncols; + i = g->cols; + mc = 0; q = p; while (n && i--) { - if (!(m = *q)) + wid = (g->widths ? g->widths[mc] : g->width); + if (!(m = *q)) { + printm(g, NULL, mc, ml, (!i), wid, NULL, NULL); break; - nicezputs((m->disp ? m->disp : m->str), shout); - if (i) - a = longest - niceztrlen(m->disp ? m->disp : m->str); - - if (of && !m->disp && m->flags & CMF_FILE) { + } + if (!m->disp && (m->flags & CMF_FILE)) { struct stat buf; char *pb; @@ -8765,23 +9050,34 @@ ilistmatches(Hookdef dummy, Chdata dat) m->str); if (ztat(pb, &buf, 1)) - putc(' ', shout); + printm(g, q, mc, ml, (!i), wid, NULL, NULL); else - putc(file_type(buf.st_mode), shout); + printm(g, q, mc, ml, (!i), wid, pb, &buf); + } else + printm(g, q, mc, ml, (!i), wid, NULL, NULL); + + printed++; - a--; - } - if (i && !opl && a > 0) - while (a--) - putc(' ', shout); if (--n) - for (j = nc; j && *q; j--) + for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--) q = skipnolist(q + 1); + mc++; } + while (i-- > 0) + printm(g, NULL, mc, ml, (!i), + (g->widths ? g->widths[mc] : g->width), NULL, NULL); + if (n) { putc('\n', shout); - if (n && nl) - p = skipnolist(p + 1); + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + if (nl) + for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--) + p = skipnolist(p + 1); } } } @@ -8792,8 +9088,8 @@ ilistmatches(Hookdef dummy, Chdata dat) if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ - if ((nlines += nlnct - 1) < lines) { - tcmultout(TCUP, TCMULTUP, nlines); + if ((ml = listdat.nlines + nlnct - 1) < lines) { + tcmultout(TCUP, TCMULTUP, ml); showinglist = -1; } else clearflag = 0, putc('\n', shout); @@ -8801,6 +9097,60 @@ ilistmatches(Hookdef dummy, Chdata dat) putc('\n', shout); listshown = (clearflag ? 1 : -1); + return printed; +} + +static void +iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, + char *path, struct stat *buf) +{ + Cmatch m; + int len = 0; + + if (!mp) + return; + + m = *mp; + if (m->disp) { + if (m->flags & CMF_DISPLINE) { + printfmt(m->disp, 0, 1, 0); + return; + } + nicezputs(m->disp, shout); + len = niceztrlen(m->disp); + } else { + nicezputs(m->str, shout); + len = niceztrlen(m->str); + + if (isset(LISTTYPES)) { + if (buf) + putc(file_type(buf->st_mode), shout); + len++; + } + } + if (!lastc) { + len = width - len; + + while (len-- > 0) + putc(' ', shout); + } +} + +/**/ +int +ilistmatches(Hookdef dummy, Chdata dat) +{ + calclist(); + + if (!listdat.nlines) { + showinglist = listshown = 0; + return 1; + } + if (asklist()) + return 0; + + printlist(0, iprintm); + return 0; } diff --git a/Src/options.c b/Src/options.c index 03fa89f5a..25084a771 100644 --- a/Src/options.c +++ b/Src/options.c @@ -142,6 +142,8 @@ static struct optname optns[] = { {NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH, KSHOPTIONPRINT}, {NULL, "listambiguous", OPT_ALL, LISTAMBIGUOUS}, {NULL, "listbeep", OPT_ALL, LISTBEEP}, +{NULL, "listpacked", 0, LISTPACKED}, +{NULL, "listrowsfirst", 0, LISTROWSFIRST}, {NULL, "listtypes", OPT_ALL, LISTTYPES}, {NULL, "localoptions", OPT_EMULATE|OPT_KSH, LOCALOPTIONS}, {NULL, "localtraps", OPT_EMULATE|OPT_KSH, LOCALTRAPS}, diff --git a/Src/zsh.h b/Src/zsh.h index 7add4faa1..0d494cd84 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1315,6 +1315,8 @@ enum { KSHOPTIONPRINT, LISTAMBIGUOUS, LISTBEEP, + LISTPACKED, + LISTROWSFIRST, LISTTYPES, LOCALOPTIONS, LOCALTRAPS, |