From d6629f38e2f71b0d5a265af32843a64998af9715 Mon Sep 17 00:00:00 2001 From: Sven Wischnowsky Date: Wed, 13 Jun 2001 14:05:30 +0000 Subject: *** empty log message *** --- Src/Zle/complist.c | 1336 ++++++++++++++++++++++++++++++++++++++++++++++---- Src/Zle/zle_tricky.c | 234 ++++++--- 2 files changed, 1403 insertions(+), 167 deletions(-) (limited to 'Src/Zle') diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index a7cab9807..f76e54116 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -37,7 +37,7 @@ static Widget w_menuselect; -static Keymap mskeymap; +static Keymap mskeymap, lskeymap; /* Indixes into the terminal string arrays. */ @@ -76,7 +76,7 @@ static char *colnames[] = { /* Default values. */ static char *defcols[] = { - "0", "0", "1;34", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL, + "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL, "\033[", "m", NULL, "0", "0", "7", "0", "0" }; @@ -122,13 +122,17 @@ struct listcols { Extcol exts; /* strings for extensions */ }; +/* Combined length of LC and RC, maximum length of capability strings. */ + +static int lr_caplen, max_caplen; + /* This parses the value of a definition (the part after the `='). * The return value is a pointer to the character after it. */ static char * getcolval(char *s, int multi) { - char *p; + char *p, *o = s; for (p = s; *s && *s != ':' && (!multi || *s != '='); p++, s++) { if (*s == '\\' && s[1]) { @@ -172,6 +176,8 @@ getcolval(char *s, int multi) } if (p != s) *p = '\0'; + if ((s - o) > max_caplen) + max_caplen = s - o; return s; } @@ -325,10 +331,6 @@ filecol(char *col) return fc; } -/* Combined length of LC and RC, maximum length of capability strings. */ - -static int lr_caplen, max_caplen; - /* This initializes the given terminal color structure. */ static void @@ -337,6 +339,8 @@ getcols(Listcols c) char *s; int i, l; + max_caplen = lr_caplen = 0; + queue_signals(); if (!(s = getsparam("ZLS_COLORS")) && !(s = getsparam("ZLS_COLOURS"))) { for (i = 0; i < NUM_COLS; i++) @@ -348,21 +352,25 @@ getcols(Listcols c) c->files[COL_MA] = filecol(s); c->files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]); } else - c->files[COL_MA] = filecol(""); + c->files[COL_MA] = filecol(defcols[COL_MA]); lr_caplen = 0; if ((max_caplen = strlen(c->files[COL_MA]->col)) < (l = strlen(c->files[COL_EC]->col))) max_caplen = l; + unqueue_signals(); return; } /* We have one of the parameters, use it. */ memset(c, 0, sizeof(*c)); s = dupstring(s); while (*s) - s = getcoldef(c, s); + if (*s == ':') + s++; + else + s = getcoldef(c, s); + unqueue_signals(); /* Use default values for those that aren't set explicitly. */ - max_caplen = lr_caplen = 0; for (i = 0; i < NUM_COLS; i++) { if (!c->files[i] || !c->files[i]->col) c->files[i] = filecol(defcols[i]); @@ -382,11 +390,19 @@ getcols(Listcols c) /* Information about the list shown. */ static int noselect, mselect, inselect, mcol, mline, mcols, mlines, mmlen; -static int selected; +static int selected, mlbeg = -1, mlend = 9999999, mscroll, mrestlines; +static int mnew, mlastcols, mlastlines, mhasstat, mfirstl, mlastm; +static int mlprinted; +static char *mstatus, *mlistp; static Cmatch **mtab, **mmtabp; static Cmgroup *mgtab, *mgtabp; static struct listcols mcolors; +/* Used in mtab/mgtab, for explanations. */ + +#define mtexpl ((Cmatch *) 1) +#define mgexpl ((Cmgroup) 1) + /* Information for in-string colours. */ static int nrefs; @@ -481,7 +497,7 @@ doiscol(Listcols c, int pos) /* insert e in sendpos */ for (i = curissend; sendpos[i] <= e; ++i) ; - for (j = i + 1; j < MAX_POS; ++j) + for (j = MAX_POS - 1; j > i; --j) sendpos[j] = sendpos[j-1]; sendpos[i] = e; @@ -496,10 +512,10 @@ doiscol(Listcols c, int pos) /* Stripped-down version of printfmt(). But can do in-string colouring. */ -static void -clprintfmt(Listcols c, char *p) +static int +clprintfmt(Listcols c, char *p, int ml) { - int cc = 0, i = 0; + int cc = 0, i = 0, ask, beg; initiscol(c); @@ -507,34 +523,32 @@ clprintfmt(Listcols c, char *p) doiscol(c, i++); cc++; if (*p == '\n') { - if (tccan(TCCLEAREOL)) + if (mlbeg >= 0 && tccan(TCCLEAREOL)) tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } cc = 0; } + if (ml == mlend - 1 && (cc % columns) == columns - 1) + return 0; + putc(*p, shout); + if ((beg = !(cc % columns))) + ml++; + if (mscroll && !(cc % columns) && + !--mrestlines && (ask = asklistscroll(ml))) + return ask; } - if (tccan(TCCLEAREOL)) + if (mlbeg >= 0 && tccan(TCCLEAREOL)) tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } + return 0; } /* Local version of nicezputs() with in-string colouring. */ -static void -clnicezputs(Listcols c, char *s) +static int +clnicezputs(Listcols c, char *s, int ml) { - int cc, i = 0; + int cc, i = 0, col = 0, ask, oml = ml; + char *t; initiscol(c); @@ -548,8 +562,26 @@ clnicezputs(Listcols c, char *s) } if (cc == Meta) cc = *s++ ^ 32; - fputs(nicechar(cc), shout); + + for (t = nicechar(cc); *t; t++) { + if (ml == mlend - 1 && col == columns - 1) { + mlprinted = ml - oml; + return 0; + } + putc(*t, shout); + if (++col == columns) { + ml++; + if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) { + mlprinted = ml - oml; + return ask; + } + col = 0; + fputs(" \010", shout); + } + } } + mlprinted = ml - oml; + return 0; } /* Get the terminal color string for the given match. */ @@ -636,12 +668,706 @@ putfilecol(Listcols c, char *group, char *n, mode_t m) static Cmgroup last_group; -static void +/**/ +static int +asklistscroll(int ml) +{ + Thingy cmd; + int i, ret = 0; + + compprintfmt(NULL, 1, 1, 1, ml, NULL); + + fflush(shout); + zsetterm(); + selectlocalmap(lskeymap); + if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) + ret = 1; + else if (cmd == Th(z_acceptline) || + cmd == Th(z_downhistory) || + cmd == Th(z_downlineorhistory) || + cmd == Th(z_downlineorsearch) || + cmd == Th(z_vidownlineorhistory)) + mrestlines = 1; + else if (cmd == Th(z_completeword) || + cmd == Th(z_expandorcomplete) || + cmd == Th(z_expandorcompleteprefix) || + cmd == Th(z_menucomplete) || + cmd == Th(z_menuexpandorcomplete) || + !strcmp(cmd->nam, "menu-select") || + !strcmp(cmd->nam, "complete-word") || + !strcmp(cmd->nam, "expand-or-complete") || + !strcmp(cmd->nam, "expand-or-complete-prefix") || + !strcmp(cmd->nam, "menu-complete") || + !strcmp(cmd->nam, "menu-expand-or-complete")) + mrestlines = lines - 1; + else { + ungetkeycmd(); + ret = 1; + } + selectlocalmap(NULL); + settyinfo(&shttyinfo); + putc('\r', shout); + for (i = columns - 1; i--; ) + putc(' ', shout); + putc('\r', shout); + + return ret; +} + +#define dolist(X) ((X) >= mlbeg && (X) < mlend) +#define dolistcl(X) ((X) >= mlbeg && (X) < mlend + 1) +#define dolistnl(X) ((X) >= mlbeg && (X) < mlend - 1) + +/**/ +static int +compprintnl(int ml) +{ + int ask; + + if (mlbeg >= 0 && tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + putc('\n', shout); + + if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) + return ask; + + return 0; +} + +/* This is used to print the strings (e.g. explanations). * + * It returns the number of lines printed. */ + +/**/ +static int +compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) +{ + char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12]; + int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat; + + if ((stat = !fmt)) { + if (mlbeg >= 0) { + if (!(fmt = mstatus)) { + mlprinted = 0; + return 0; + } + cc = -1; + } else + fmt = mlistp; + } + for (p = fmt; *p; p++) { + if (doesc && *p == '%') { + if (*++p) { + m = 0; + switch (*p) { + case '%': + if (dopr == 1) + putc('%', shout); + cc++; + break; + case 'n': + if (!stat) { + sprintf(nc, "%d", n); + if (dopr == 1) + fputs(nc, shout); + cc += strlen(nc); + } + break; + case 'B': + b = 1; + if (dopr) + tcout(TCBOLDFACEBEG); + break; + case 'b': + b = 0; m = 1; + if (dopr) + tcout(TCALLATTRSOFF); + break; + case 'S': + s = 1; + if (dopr) + tcout(TCSTANDOUTBEG); + break; + case 's': + s = 0; m = 1; + if (dopr) + tcout(TCSTANDOUTEND); + break; + case 'U': + u = 1; + if (dopr) + tcout(TCUNDERLINEBEG); + break; + case 'u': + u = 0; m = 1; + if (dopr) + tcout(TCUNDERLINEEND); + break; + case '{': + for (p++; *p && (*p != '%' || p[1] != '}'); p++) + if (dopr) + putc(*p, shout); + if (*p) + p++; + else + p--; + break; + case 'm': + if (stat) { + sprintf(nc, "%d/%d", (n ? mlastm : mselect), + listdat.nlist); + m = 2; + } + break; + case 'M': + if (stat) { + sprintf(nbuf, "%d/%d", (n ? mlastm : mselect), + listdat.nlist); + sprintf(nc, "%-9s", nbuf); + m = 2; + } + break; + case 'l': + if (stat) { + sprintf(nc, "%d/%d", ml + 1, listdat.nlines); + m = 2; + } + break; + case 'L': + if (stat) { + sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines); + sprintf(nc, "%-9s", nbuf); + m = 2; + } + break; + case 'p': + if (stat) { + if (ml == listdat.nlines - 1) + strcpy(nc, "Bottom"); + else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl)) + sprintf(nc, "%d%%", + ((ml + 1) * 100) / listdat.nlines); + else + strcpy(nc, "Top"); + m = 2; + } + break; + case 'P': + if (stat) { + if (ml == listdat.nlines - 1) + strcpy(nc, "Bottom"); + else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl)) + sprintf(nc, "%2d%% ", + ((ml + 1) * 100) / listdat.nlines); + else + strcpy(nc, "Top "); + m = 2; + } + break; + } + if (m == 2 && dopr == 1) { + int l = strlen(nc); + + if (l + cc > columns - 2) + nc[l -= l + cc - (columns - 2)] = '\0'; + fputs(nc, shout); + cc += l; + } else if (dopr && m == 1) { + if (b) + tcout(TCBOLDFACEBEG); + if (s) + tcout(TCSTANDOUTBEG); + if (u) + tcout(TCUNDERLINEBEG); + } + } else + break; + } else { + if ((++cc == columns - 2 || *p == '\n') && stat) + dopr = 2; + if (*p == '\n') { + if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + l += 1 + ((cc - 1) / columns); + cc = 0; + } + if (dopr == 1) { + if (ml == mlend - 1 && (cc % columns) == columns - 1) { + dopr = 0; + continue; + } + putc(*p, shout); + if ((beg = !(cc % columns)) && !stat) { + ml++; + fputs(" \010", shout); + } + if (mscroll && beg && !--mrestlines && (ask = asklistscroll(ml))) { + *stop = 1; + if (stat && n) + mfirstl = -1; + return (mlprinted = l + (cc / columns)); + } + } + } + } + if (dopr) { + if (!(cc % columns)) + fputs(" \010", shout); + if (mlbeg >= 0 && tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + } + if (stat && n) + mfirstl = -1; + + return (mlprinted = l + (cc / columns)); +} + +/* This is like zputs(), but allows scrolling. */ + +/**/ +static int +compzputs(char const *s, int ml) +{ + int c, col = 0, ask; + + while (*s) { + if (*s == Meta) + c = *++s ^ 32; + else if(itok(*s)) { + s++; + continue; + } else + c = *s; + s++; + putc(c, shout); + if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + if (mscroll && (++col == columns || c == '\n')) { + ml++; + if (!--mrestlines && (ask = asklistscroll(ml))) + return ask; + + col = 0; + } + } + return 0; +} + +/* This is like nicezputs(), but allows scrolling. */ + +/**/ +static int +compnicezputs(char *s, int ml) +{ + int c, col = 0, ask, oml = ml; + char *t; + + while ((c = *s++)) { + if (itok(c)) { + if (c <= Comma) + c = ztokens[c - Pound]; + else + continue; + } + if (c == Meta) + c = *s++ ^ 32; + + for (t = nicechar(c); *t; t++) { + if (ml == mlend - 1 && col == columns - 1) { + mlprinted = ml - oml; + return 0; + } + putc(*t, shout); + if (++col == columns) { + ml++; + if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) { + mlprinted = ml - oml; + return ask; + } + col = 0; + } + } + } + mlprinted = ml - oml; + return 0; +} + +/**/ +static int +compprintlist(int showall) +{ + static int lasttype = 0, lastbeg = 0, lastml = 0, lastinvcount = -1; + static int lastn = 0, lastnl = 0, lastnlnct = -1; + static Cmgroup lastg = NULL; + static Cmatch *lastp = NULL; + static Cexpl *lastexpl = NULL; + + Cmgroup g; + Cmatch *p, m; + Cexpl *e; + int pnl = 0, cl, mc = 0, ml = 0, printed = 0, stop = 0, asked = 1; + int lastused = 0; + + mfirstl = -1; + if (mnew || lastinvcount != invcount || lastbeg != mlbeg || mlbeg < 0) { + lasttype = 0; + lastg = NULL; + lastexpl = NULL; + lastml = 0; + lastnlnct = -1; + } + cl = (listdat.nlines > lines - nlnct - mhasstat ? + lines - nlnct - mhasstat : listdat.nlines) - (lastnlnct > nlnct); + lastnlnct = nlnct; + mrestlines = lines - 1; + lastinvcount = invcount; + + if (cl < 2) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } else if (mlbeg >= 0 && !tccan(TCCLEAREOL) && tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + + g = ((lasttype && lastg) ? lastg : amatches); + while (g) { + char **pp = g->ylist; + + if ((e = g->expls)) { + int l; + + if (!lastused && lasttype == 1) { + e = lastexpl; + ml = lastml; + lastused = 1; + } + while (*e) { + if ((*e)->count && + (!listdat.onlyexpl || + (listdat.onlyexpl & ((*e)->count > 0 ? 1 : 2)))) { + if (pnl) { + if (dolistnl(ml) && compprintnl(ml)) + goto end; + pnl = 0; + ml++; + if (dolistcl(ml) && cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + if (mlbeg < 0 && mfirstl < 0) + mfirstl = ml; + l = compprintfmt((*e)->str, (*e)->count, dolist(ml), 1, + ml, &stop); + if (mselect >= 0) { + int mm = (mcols * ml), i; + + for (i = mcols; i--; ) { + mtab[mm + i] = mtexpl; + mgtab[mm + i] = mgexpl; + } + } + if (stop) + goto end; + if (!lasttype && ml >= mlbeg) { + lasttype = 1; + lastg = g; + lastbeg = mlbeg; + lastml = ml; + lastexpl = e; + lastp = NULL; + lastused = 1; + } + ml += mlprinted; + if (dolistcl(ml) && cl >= 0 && (cl -= mlprinted) <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + pnl = 1; + } + e++; + if (!mnew && ml > mlend) + goto end; + } + } + if (!listdat.onlyexpl && mlbeg < 0 && pp && *pp) { + if (pnl) { + if (dolistnl(ml) && compprintnl(ml)) + goto end; + pnl = 0; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + if (mlbeg < 0 && mfirstl < 0) + mfirstl = ml; + if (g->flags & CGF_LINES) { + while (*pp) { + if (compzputs(*pp, ml)) + goto end; + if (*++pp && compprintnl(ml)) + goto end; + } + } else { + int n = g->lcount, nl, nc, i, a; + char **pq; + + nl = nc = g->lins; + + while (n && nl--) { + i = g->cols; + mc = 0; + pq = pp; + while (n && i--) { + if (pq - g->ylist >= g->lcount) + break; + if (compzputs(*pq, mscroll)) + goto end; + if (i) { + a = (g->widths ? g->widths[mc] : g->width) - + strlen(*pq); + while (a--) + putc(' ', shout); + } + pq += ((g->flags & CGF_ROWS) ? 1 : nc); + mc++; + n--; + } + if (n) { + if (compprintnl(ml)) + goto end; + ml++; + if (cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + pp += ((g->flags & CGF_ROWS) ? g->cols : 1); + } + } + } else if (!listdat.onlyexpl && + (g->lcount || (showall && g->mcount))) { + int n = g->dcount, nl, nc, i, j, wid; + Cmatch *q; + + nl = nc = g->lins; + + if ((g->flags & CGF_HASDL) && + (lastused || !lasttype || lasttype == 2)) { + if (!lastused && lasttype == 2) { + p = lastp; + ml = lastml; + n = lastn; + nl = lastnl; + lastused = 1; + pnl = 0; + } else + p = g->matches; + + for (; (m = *p); p++) { + if (m->disp && (m->flags & CMF_DISPLINE)) { + if (pnl) { + if (dolistnl(ml) && compprintnl(ml)) + goto end; + pnl = 0; + ml++; + if (dolistcl(ml) && cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + if (!lasttype && ml >= mlbeg) { + lasttype = 2; + lastg = g; + lastbeg = mlbeg; + lastml = ml; + lastp = p; + lastn = n; + lastnl = nl; + lastused = 1; + } + if (mfirstl < 0) + mfirstl = ml; + if (dolist(ml)) + printed++; + if (clprintm(g, p, 0, ml, 1, 0, NULL, NULL)) + goto end; + ml += mlprinted; + if (dolistcl(ml) && (cl -= mlprinted) <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + pnl = 1; + } + if (!mnew && ml > mlend) + goto end; + } + } + if (n && pnl) { + if (dolistnl(ml) && compprintnl(ml)) + goto end; + pnl = 0; + ml++; + if (dolistcl(ml) && cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + } + if (!lastused && lasttype == 3) { + p = lastp; + n = lastn; + nl = lastnl; + ml = lastml; + lastused = 1; + } else + p = skipnolist(g->matches, showall); + + while (n && nl--) { + if (!lasttype && ml >= mlbeg) { + lasttype = 3; + lastg = g; + lastbeg = mlbeg; + lastml = ml; + lastp = p; + lastn = n; + lastnl = nl + 1; + lastused = 1; + } + i = g->cols; + mc = 0; + q = p; + while (n && i--) { + wid = (g->widths ? g->widths[mc] : g->width); + if (!(m = *q)) { + if (clprintm(g, NULL, mc, ml, (!i), wid, NULL, NULL)) + goto end; + break; + } + if (!m->disp && (m->flags & CMF_FILE) && + m->str[0] && m->str[strlen(m->str) - 1] != '/') { + 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); + + if (ztat(pb, &buf, 1) ? + clprintm(g, q, mc, ml, (!i), wid, NULL, NULL) : + clprintm(g, q, mc, ml, (!i), wid, pb, &buf)) + goto end; + } else if (clprintm(g, q, mc, ml, (!i), wid, NULL, NULL)) + goto end; + + if (dolist(ml)) + printed++; + ml += mlprinted; + if (dolistcl(ml) && (cl -= mlprinted) < 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + if (mfirstl < 0) + mfirstl = ml; + + if (--n) + for (j = ((g->flags & CGF_ROWS) ? 1 : nc); + j && *q; j--) + q = skipnolist(q + 1, showall); + mc++; + } + while (i-- > 0) { + if (clprintm(g, NULL, mc, ml, (!i), + (g->widths ? g->widths[mc] : g->width), + NULL, NULL)) + goto end; + mc++; + } + if (n) { + if (dolistnl(ml) && compprintnl(ml)) + goto end; + ml++; + if (dolistcl(ml) && cl >= 0 && --cl <= 1) { + cl = -1; + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + } + if (nl) + for (j = ((g->flags & CGF_ROWS) ? g->cols : 1); + j && *p; j--) + p = skipnolist(p + 1, showall); + } + if (!mnew && ml > mlend) + goto end; + } + } + if (g->lcount || (showall && g->mcount)) + pnl = 1; + g = g->next; + } + asked = 0; + end: + lastlistlen = 0; + if (nlnct <= 1) + mscroll = 0; + if (clearflag) { + int nl; + + /* Move the cursor up to the prompt, if always_last_prompt * + * is set and all that... */ + if (mlbeg >= 0) { + if ((nl = listdat.nlines + nlnct) >= lines) { + if (mhasstat) { + putc('\n', shout); + compprintfmt(NULL, 0, 1, 1, mline, NULL); + } + nl = lines - 1; + } else + nl--; + tcmultout(TCUP, TCMULTUP, nl); + showinglist = -1; + + lastlistlen = listdat.nlines; + } else if ((nl = listdat.nlines + nlnct - 1) < lines) { + if (mlbeg >= 0 && tccan(TCCLEAREOL)) + tcout(TCCLEAREOL); + tcmultout(TCUP, TCMULTUP, nl); + showinglist = -1; + + lastlistlen = listdat.nlines; + } else { + clearflag = 0; + if (!asked) { + mrestlines = (ml + nlnct > lines); + compprintnl(ml); + } + } + } else if (!asked) { + mrestlines = (ml + nlnct > lines); + compprintnl(ml); + } + listshown = (clearflag ? 1 : -1); + mnew = 0; + + return printed; +} + +/**/ +static int clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, char *path, struct stat *buf) { Cmatch m; - int len, subcols = 0; + int len, subcols = 0, stop = 0, ret = 0; if (g != last_group) *last_cap = '\0'; @@ -649,14 +1375,22 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, last_group = g; if (!mp) { - zcputs(&mcolors, g->name, COL_SP); - len = width - 2; - while (len-- > 0) - putc(' ', shout); - zcoff(); - return; + if (dolist(ml)) { + zcputs(&mcolors, g->name, COL_SP); + len = width - 2; + while (len-- > 0) + putc(' ', shout); + zcoff(); + } + mlprinted = 0; + return 0; } m = *mp; + + if ((m->flags & CMF_ALL) && (!m->disp || !m->disp[0])) + bld_all_str(m); + + mlastm = m->gnum; if (m->disp && (m->flags & CMF_DISPLINE)) { if (mselect >= 0) { int mm = (mcols * ml), i; @@ -666,6 +1400,10 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, mgtab[mm + i] = g; } } + if (!dolist(ml)) { + mlprinted = printfmt(m->disp, 0, 0, 0) / columns; + return 0; + } if (m->gnum == mselect) { int mm = (mcols * ml); mline = ml; @@ -681,9 +1419,12 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, else subcols = putmatchcol(&mcolors, g->name, m->disp); if (subcols) - clprintfmt(&mcolors, m->disp); - else - printfmt(m->disp, 0, 1, 0); + ret = clprintfmt(&mcolors, m->disp, ml); + else { + compprintfmt(m->disp, 0, 1, 0, ml, &stop); + if (stop) + ret = 1; + } zcoff(); } else { int mx; @@ -704,6 +1445,10 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, mgtab[mx + mm + i] = g; } } + if (!dolist(ml)) { + mlprinted = niceztrlen(m->disp ? m->disp : m->str) / columns; + return 0; + } if (m->gnum == mselect) { int mm = mcols * ml; @@ -723,12 +1468,17 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, subcols = putmatchcol(&mcolors, g->name, (m->disp ? m->disp : m->str)); if (subcols) - clnicezputs(&mcolors, (m->disp ? m->disp : m->str)); + ret = clnicezputs(&mcolors, (m->disp ? m->disp : m->str), ml); else - nicezputs((m->disp ? m->disp : m->str), shout); + ret = compnicezputs((m->disp ? m->disp : m->str), ml); + if (ret) { + zcoff(); + return 1; + } len = niceztrlen(m->disp ? m->disp : m->str); + mlprinted = len / columns; - if (isset(LISTTYPES) && buf) { + if (isset(LISTTYPES) && buf) { if (m->gnum != mselect) { zcoff(); zcputs(&mcolors, g->name, COL_TC); @@ -751,6 +1501,7 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, zcoff(); } } + return ret; } static int @@ -760,32 +1511,66 @@ complistmatches(Hookdef dummy, Chdata dat) amatches = dat->matches; - if (minfo.asked == 2) { + noselect = 0; + + if ((minfo.asked == 2 && mselect < 0) || nlnct >= lines) { showinglist = 0; amatches = oamatches; return (noselect = 1); } getcols(&mcolors); - calclist(mselect >= 0); + mnew = ((calclist(mselect >= 0) || mlastcols != columns || + mlastlines != listdat.nlines) && mselect >= 0); if (!listdat.nlines || (mselect >= 0 && - (!(isset(USEZLE) && !termflags && - complastprompt && *complastprompt) || - (listdat.nlines + nlnct - 1) >= lines))) { + !(isset(USEZLE) && !termflags && + complastprompt && *complastprompt))) { showinglist = listshown = 0; noselect = 1; amatches = oamatches; return 1; } - if (inselect) + if (inselect || mlbeg >= 0) clearflag = 0; - if (asklist()) { - amatches = oamatches; - return (noselect = 1); + mscroll = 0; + mlistp = NULL; + + queue_signals(); + if (mselect >= 0 || mlbeg >= 0 || + (mlistp = dupstring(getsparam("LISTPROMPT")))) { + unqueue_signals(); + if (mlistp && !*mlistp) + mlistp = "%SAt %p: Hit TAB for more, or the character to insert%s"; + trashzle(); + showinglist = listshown = 0; + + lastlistlen = 0; + + if (mlistp) { + clearflag = (isset(USEZLE) && !termflags && dolastprompt); + mscroll = 1; + } else { + clearflag = 1; + minfo.asked = (listdat.nlines + nlnct <= lines); + } + } else { + unqueue_signals(); + mlistp = NULL; + if (asklist()) { + amatches = oamatches; + return (noselect = 1); + } } - if (mselect >= 0) { + if (mlbeg >= 0) { + mlend = mlbeg + lines - nlnct - mhasstat; + while (mline >= mlend) + mlbeg++, mlend++; + } else + mlend = 9999999; + + if (mnew) { int i; i = columns * listdat.nlines; @@ -795,14 +1580,13 @@ complistmatches(Hookdef dummy, Chdata dat) free(mgtab); mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup)); memset(mgtab, 0, i * sizeof(Cmgroup)); - mcols = columns; - mlines = listdat.nlines; + mlastcols = mcols = columns; + mlastlines = mlines = listdat.nlines; } last_cap = (char *) zhalloc(max_caplen + 1); *last_cap = '\0'; - if (!printlist(1, clprintm, (mselect >= 0)) || listdat.nlines >= lines || - !clearflag) + if (!compprintlist(mselect >= 0) || !clearflag) noselect = 1; amatches = oamatches; @@ -818,8 +1602,8 @@ adjust_mcol(int wish, Cmatch ***tabp, Cmgroup **grp) tab -= mcol; - for (p = wish; p >= 0 && !tab[p]; p--); - for (n = wish; n < mcols && !tab[n]; n++); + for (p = wish; p >= 0 && (!tab[p] || tab[p] == mtexpl); p--); + for (n = wish; n < mcols && (!tab[n] || tab[n] == mtexpl); n++); if (n == mcols) n = -1; @@ -849,9 +1633,11 @@ struct menustack { Brinfo brbeg; Brinfo brend; int nbrbeg, nbrend; - int cs, acc, nmatches; + int cs, acc, nmatches, mline, mlbeg; struct menuinfo info; Cmgroup amatches, pmatches, lastmatches, lastlmatches; + char *origline; + int origcs, origll; }; static int @@ -863,27 +1649,108 @@ domenuselect(Hookdef dummy, Chdata dat) Thingy cmd; Menustack u = NULL; int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0; + int space, lbeg = 0, step = 1, wrap, pl = nlnct, broken = 0, first = 1; + int nolist = 0; char *s; - if (fdat || (dummy && (!(s = getsparam("SELECTMIN")) || + queue_signals(); + if (fdat || (dummy && (!(s = getsparam("MENUSELECT")) || (dat && dat->num < atoi(s))))) { if (fdat) { fdat->matches = dat->matches; fdat->num = dat->num; + fdat->nmesg = dat->nmesg; } + unqueue_signals(); return 0; } + if ((s = getsparam("MENUSCROLL"))) { + if (!(step = mathevali(s))) + step = (lines - nlnct) >> 1; + else if (step < 0) + if ((step += lines - nlnct) < 0) + step = 1; + } + if ((mstatus = dupstring(getsparam("MENUPROMPT"))) && !*mstatus) + mstatus = "%SScrolling active: current selection at %p%s"; + unqueue_signals(); + mhasstat = (mstatus && *mstatus); fdat = dat; selectlocalmap(mskeymap); - noselect = 0; + noselect = 1; + while ((menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && + (!(*minfo.cur)->str || !*(*minfo.cur)->str))) + do_menucmp(0); + mselect = (*(minfo.cur))->gnum; + mline = 0; + mlines = 999999; + mlbeg = 0; for (;;) { + if (mline < 0) { + int x, y; + Cmatch **p = mtab; + + for (y = 0; y < mlines; y++) { + for (x = mcols; x; x--, p++) + if (*p && *p != mtexpl && **p && mselect == (**p)->gnum) + break; + if (x) + break; + } + if (y < mlines) + mline = y; + } + while (mline < mlbeg) + if ((mlbeg -= step) < 0) + mlbeg = 0; + + if (mlbeg && lbeg != mlbeg) { + Cmatch **p = mtab + ((mlbeg - 1) * columns), **q; + int c; + + while (mlbeg) { + for (q = p, c = columns; c; q++, c--) + if (*q && *q != mtexpl) + break; + if (c) + break; + p -= columns; + mlbeg--; + } + } + if ((space = lines - pl - mhasstat)) + while (mline >= mlbeg + space) + if ((mlbeg += step) + space > mlines) + mlbeg = mlines - space; + if (lbeg != mlbeg) { + Cmatch **p = mtab + (mlbeg * columns), **q; + int c; + + while (mlbeg < mlines) { + for (q = p, c = columns; c; q++, c--) + if (*q) + break; + if (c) + break; + p += columns; + mlbeg++; + } + } + lbeg = mlbeg; onlyexpl = 0; showinglist = -2; + if (first && !listshown && isset(LISTBEEP)) + zbeep(); + first = 0; zrefresh(); inselect = 1; - if (noselect) + if (noselect) { + broken = 1; break; + } selected = 1; if (!i) { i = mcols * mlines; @@ -911,9 +1778,13 @@ domenuselect(Hookdef dummy, Chdata dat) getk: - if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) + if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { + zbeep(); break; - else if (cmd == Th(z_acceptline)) { + } else if (nolist && cmd != Th(z_undo)) { + ungetkeycmd(); + break; + } else if (cmd == Th(z_acceptline)) { acc = 1; break; } else if (cmd == Th(z_acceptandinfernexthistory)) { @@ -923,6 +1794,8 @@ domenuselect(Hookdef dummy, Chdata dat) u = s; s->line = dupstring((char *) line); s->cs = cs; + s->mline = mline; + s->mlbeg = mlbeg; memcpy(&(s->info), &minfo, sizeof(struct menuinfo)); s->amatches = amatches; s->pmatches = pmatches; @@ -934,30 +1807,57 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; + s->origline = origline; + s->origcs = origcs; + s->origll = origll; menucmp = menuacc = hasoldlist = 0; + minfo.cur = NULL; fixsuffix(); + handleundo(); validlist = 0; amatches = pmatches = lastmatches = NULL; invalidate_list(); + iforcemenu = 1; + comprecursive = 1; menucomplete(zlenoargs); - if (dat->num < 2 || !minfo.cur || !*(minfo.cur)) { - noselect = clearlist = listshown = 1; - onlyexpl = 0; - zrefresh(); - break; + iforcemenu = 0; + + if (dat->num < 1 || !minfo.cur || !*(minfo.cur)) { + nolist = 1; + if (dat->nmesg || nmessages) { + showinglist = -2; + zrefresh(); + } else { + trashzle(); + zsetterm(); + if (tccan(TCCLEAREOD)) + tcout(TCCLEAREOD); + fputs("no matches\r", shout); + fflush(shout); + tcmultout(TCUP, TCMULTUP, nlnct); + showinglist = clearlist = 0; + clearflag = 1; + zrefresh(); + showinglist = clearlist = 0; + } + goto getk; } clearlist = listshown = 1; mselect = (*(minfo.cur))->gnum; setwish = wasnext = 1; + mline = 0; continue; } else if (cmd == Th(z_acceptandhold) || cmd == Th(z_acceptandmenucomplete)) { Menustack s = (Menustack) zhalloc(sizeof(*s)); + int ol; s->prev = u; u = s; s->line = dupstring((char *) line); s->cs = cs; + s->mline = mline; + s->mlbeg = mlbeg; memcpy(&(s->info), &minfo, sizeof(struct menuinfo)); s->amatches = s->pmatches = s->lastmatches = s->lastlmatches = NULL; @@ -967,18 +1867,45 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; + s->origline = origline; + s->origcs = origcs; + s->origll = origll; accept_last(); + handleundo(); + comprecursive = 1; do_menucmp(0); mselect = (*(minfo.cur))->gnum; + + p -= mcol; + mcol = 0; + ol = mline; + do { + for (mcol = 0; mcol < mcols; mcol++, p++) + if (*p == minfo.cur) + break; + if (mcol != mcols) + break; + if (++mline == mlines) { + mline = 0; + p -= mlines * mcols; + } + } while (mline != ol); + if (*p != minfo.cur) { + noselect = clearlist = listshown = 1; + onlyexpl = 0; + zrefresh(); + break; + } setwish = 1; continue; } else if (cmd == Th(z_undo)) { int l; if (!u) - goto getk; + break; - cs = 0; + handleundo(); + cs = nolist = 0; foredel(ll); spaceinline(l = strlen(u->line)); strncpy((char *) line, u->line, l); @@ -986,15 +1913,17 @@ domenuselect(Hookdef dummy, Chdata dat) menuacc = u->acc; memcpy(&minfo, &(u->info), sizeof(struct menuinfo)); p = &(minfo.cur); + mline = u->mline; + mlbeg = u->mlbeg; if (u->lastmatches && lastmatches != u->lastmatches) { if (lastmatches) - freematches(lastmatches); + freematches(lastmatches, 0); amatches = u->amatches; pmatches = u->pmatches; lastmatches = u->lastmatches; lastlmatches = u->lastlmatches; nmatches = u->nmatches; - hasoldlist = 1; + hasoldlist = validlist = 1; } freebrinfo(brbeg); freebrinfo(brend); @@ -1002,6 +1931,9 @@ domenuselect(Hookdef dummy, Chdata dat) brend = dupbrinfo(u->brend, &lastbrend, 0); nbrbeg = u->nbrbeg; nbrend = u->nbrend; + origline = u->origline; + origcs = u->origcs; + origll = u->origll; u = u->prev; clearlist = 1; @@ -1017,67 +1949,240 @@ domenuselect(Hookdef dummy, Chdata dat) cmd == Th(z_downlineorhistory) || cmd == Th(z_downlineorsearch) || cmd == Th(z_vidownlineorhistory)) { + int omline; + Cmatch **op; + + wrap = 0; + + down: + + omline = mline; + op = p; + do { if (mline == mlines - 1) { + if (wrap & 2) { + mline = omline; + p = op; + break; + } p -= mline * mcols; mline = 0; + wrap |= 1; } else { mline++; p += mcols; } if (adjust_mcol(wishcol, &p, NULL)) continue; - } while (!*p); + } while (!*p || *p == mtexpl); + + if (wrap == 1) + goto right; } else if (cmd == Th(z_uphistory) || cmd == Th(z_uplineorhistory) || cmd == Th(z_uplineorsearch) || cmd == Th(z_viuplineorhistory)) { + int omline; + Cmatch **op; + + wrap = 0; + + up: + + omline = mline; + op = p; + do { if (!mline) { + if (wrap & 2) { + mline = omline; + p = op; + break; + } mline = mlines - 1; p += mline * mcols; + wrap |= 1; + } else { + mline--; + p -= mcols; + } + if (adjust_mcol(wishcol, &p, NULL)) + continue; + } while (!*p || *p == mtexpl); + + if (wrap == 1) { + if (mcol == wishcol) + goto left; + + wishcol = mcol; + } + } else if (cmd == Th(z_emacsforwardword) || + cmd == Th(z_viforwardword) || + cmd == Th(z_viforwardwordend) || + cmd == Th(z_forwardword)) { + int i = lines - pl - 1, oi = i, ll = 0; + Cmatch **lp = NULL; + + if (mline == mlines - 1) + goto top; + while (i > 0) { + if (mline == mlines - 1) { + if (i != oi && lp) + break; + goto top; + } else { + mline++; + p += mcols; + } + if (adjust_mcol(wishcol, &p, NULL)) + continue; + if (*p && *p != mtexpl) { + i--; + lp = p; + ll = mline; + } + } + p = lp; + mline = ll; + } else if (cmd == Th(z_emacsbackwardword) || + cmd == Th(z_vibackwardword) || + cmd == Th(z_backwardword)) { + int i = lines - pl - 1, oi = i, ll = 0; + Cmatch **lp = NULL; + + if (!mline) + goto bottom; + while (i > 0) { + if (!mline) { + if (i != oi && lp) + break; + goto bottom; } else { mline--; p -= mcols; } if (adjust_mcol(wishcol, &p, NULL)) continue; - } while (!*p); + if (*p || *p != mtexpl) { + i--; + lp = p; + ll = mline; + } + } + p = lp; + mline = ll; + } else if (cmd == Th(z_beginningofhistory)) { + int ll; + Cmatch **lp; + + top: + + ll = mline; + lp = p; + while (mline) { + mline--; + p -= mcols; + if (adjust_mcol(wishcol, &p, NULL)) + continue; + if (*p && *p != mtexpl) { + lp = p; + ll = mline; + } + } + mline = ll; + p = lp; + } else if (cmd == Th(z_endofhistory)) { + int ll; + Cmatch **lp; + + bottom: + + ll = mline; + lp = p; + while (mline < mlines - 1) { + mline++; + p += mcols; + if (adjust_mcol(wishcol, &p, NULL)) + continue; + if (*p && *p != mtexpl) { + lp = p; + ll = mline; + } + } + mline = ll; + p = lp; } else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) { - int omcol = mcol; - Cmatch *op = *p; + int omcol; + Cmatch **op; + + wrap = 0; + + right: + + omcol = mcol; + op = p; do { if (mcol == mcols - 1) { + if (wrap & 1) { + p = op; + mcol = omcol; + break; + } p -= mcol; mcol = 0; + wrap |= 2; } else { mcol++; p++; } - } while (!*p || (mcol != omcol && *p == op)); + } while (!*p || *p == mtexpl || (mcol != omcol && *p == *op)); wishcol = mcol; + + if (wrap == 2) + goto down; } else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) { - int omcol = mcol; - Cmatch *op = *p; + int omcol; + Cmatch **op; + + wrap = 0; + + left: + + omcol = mcol; + op = p; do { if (!mcol) { + if (wrap & 1) { + p = op; + mcol = omcol; + break; + } mcol = mcols - 1; p += mcol; + wrap |= 2; } else { mcol--; p--; } - } while (!*p || (mcol != omcol && *p == op)); + } while (!*p || *p == mtexpl || (mcol != omcol && *p == *op)); wishcol = mcol; + + if (wrap == 2) { + p += mcols - 1 - mcol; + wishcol = mcol = mcols - 1; + adjust_mcol(wishcol, &p, NULL); + goto up; + } } else if (cmd == Th(z_beginningofbufferorhistory) || cmd == Th(z_beginningofline) || cmd == Th(z_beginningoflinehist) || cmd == Th(z_vibeginningofline)) { p -= mcol; mcol = 0; - while (!*p) { + while (!*p || *p == mtexpl) { mcol++; p++; } @@ -1088,15 +2193,13 @@ domenuselect(Hookdef dummy, Chdata dat) cmd == Th(z_viendofline)) { p += mcols - mcol - 1; mcol = mcols - 1; - while (!*p) { + while (!*p || *p == mtexpl) { mcol--; p--; } wishcol = mcols - 1; - } else if (cmd == Th(z_forwardword) || - cmd == Th(z_emacsforwardword) || - cmd == Th(z_viforwardword) || - cmd == Th(z_viforwardwordend)) { + } else if (cmd == Th(z_viforwardblankword) || + cmd == Th(z_viforwardblankwordend)) { Cmgroup g = *pg; int ol = mline; @@ -1112,10 +2215,8 @@ domenuselect(Hookdef dummy, Chdata dat) } if (adjust_mcol(wishcol, &p, &pg)) continue; - } while (ol != mline && (*pg == g || !*pg)); - } else if (cmd == Th(z_backwardword) || - cmd == Th(z_emacsbackwardword) || - cmd == Th(z_vibackwardword)) { + } while (ol != mline && (*pg == g || !*pg || *pg == mgexpl)); + } else if (cmd == Th(z_vibackwardblankword)) { Cmgroup g = *pg; int ol = mline; @@ -1131,7 +2232,7 @@ domenuselect(Hookdef dummy, Chdata dat) } if (adjust_mcol(wishcol, &p, &pg)) continue; - } while (ol != mline && (*pg == g || !*pg)); + } while (ol != mline && (*pg == g || !*pg || *pg == mgexpl)); } else if (cmd == Th(z_completeword) || cmd == Th(z_expandorcomplete) || cmd == Th(z_expandorcompleteprefix) || @@ -1143,18 +2244,29 @@ domenuselect(Hookdef dummy, Chdata dat) !strcmp(cmd->nam, "expand-or-complete-prefix") || !strcmp(cmd->nam, "menu-complete") || !strcmp(cmd->nam, "menu-expand-or-complete")) { + comprecursive = 1; do_menucmp(0); mselect = (*(minfo.cur))->gnum; setwish = 1; + mline = -1; continue; } else if (cmd == Th(z_reversemenucomplete) || !strcmp(cmd->nam, "reverse-menu-complete")) { + comprecursive = 1; reversemenucomplete(zlenoargs); mselect = (*(minfo.cur))->gnum; setwish = 1; + mline = -1; + continue; + } else if (cmd == Th(z_undefinedkey)) { continue; } else { ungetkeycmd(); + if (cmd->widget && (cmd->widget->flags & WIDGET_NCOMP)) { + acc = 0; + broken = 2; + } else + acc = 1; break; } do_single(**p); @@ -1163,30 +2275,39 @@ domenuselect(Hookdef dummy, Chdata dat) if (u) for (; u; u = u->prev) if (u->lastmatches != lastmatches) - freematches(u->lastmatches); + freematches(u->lastmatches, 0); selectlocalmap(NULL); - mselect = -1; - inselect = 0; + mselect = mlastcols = mlastlines = -1; + mstatus = NULL; + inselect = mhasstat = 0; if (acc) { menucmp = lastambig = hasoldlist = 0; do_single(*(minfo.cur)); } - if (wasnext) { + if (wasnext || broken) { menucmp = 2; showinglist = -2; minfo.asked = 0; + if (!noselect) { + int nos = noselect; + + zrefresh(); + noselect = nos; + } } - if (!noselect) { + if (!noselect && (!dat || acc)) { showinglist = -2; onlyexpl = oe; if (!smatches) clearlist = 1; zrefresh(); } + mlbeg = -1; fdat = NULL; - return (!noselect ^ acc); + return (broken == 2 ? 3 : + ((dat && !broken) ? (acc ? 1 : 2) : (!noselect ^ acc))); } /* The widget function. */ @@ -1247,6 +2368,14 @@ boot_(Module m) bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL); bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL); + lskeymap = newkeymap(NULL, "listscroll"); + linkkeymap(lskeymap, "listscroll", 1); + bindkey(lskeymap, "\t", refthingy(t_completeword), NULL); + bindkey(lskeymap, " ", refthingy(t_completeword), NULL); + bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL); + bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL); + bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL); + bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); return 0; } @@ -1261,6 +2390,7 @@ cleanup_(Module m) deletehookfunc("comp_list_matches", (Hookfn) complistmatches); deletehookfunc("menu_start", (Hookfn) domenuselect); unlinkkeymap("menuselect", 1); + unlinkkeymap("listscroll", 1); return 0; } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 00bc0382a..5886b4114 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -58,11 +58,6 @@ mod_export int clwsize, clwnum, clwpos; /**/ mod_export char **clwords; -/* wb and we hold the beginning/end position of the word we are completing. */ - -/**/ -mod_export int wb, we; - /* offs is the cursor position within the tokenized * * current word after removing nulargs. */ @@ -77,6 +72,11 @@ mod_export int offs; /**/ mod_export int usemenu, useglob; +/* != 0 if we would insert a TAB if we weren't calling a completion widget. */ + +/**/ +mod_export int wouldinstab; + /* != 0 if we are in the middle of a menu completion. May be == 2 to force * * menu completion even if using different widgets. */ @@ -136,7 +136,7 @@ mod_export char *compfunc = NULL; * lastambig == 2. */ /**/ -mod_export int lastambig; +mod_export int lastambig, bashlistfirst; /* Arguments for and return value of completion widget. */ @@ -145,6 +145,11 @@ mod_export char **cfargs; /**/ mod_export int cfret; +/* != 0 if recursive calls to completion are (temporarily) allowed */ + +/**/ +mod_export int comprecursive; + /* Find out if we have to insert a tab (instead of trying to complete). */ /**/ @@ -153,9 +158,16 @@ usetab(void) { unsigned char *s = line + cs - 1; + if (keybuf[0] != '\t' || keybuf[1]) + return 0; for (; s >= line && *s != '\n'; s--) if (*s != '\t' && *s != ' ') return 0; + if (compfunc) { + wouldinstab = 1; + + return 0; + } return 1; } @@ -179,12 +191,15 @@ completeword(char **args) { usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; if (c == '\t' && usetab()) return selfinsert(args); else { int ret; if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) { + bashlistfirst = 1; ret = docomplete(COMP_LIST_COMPLETE); + bashlistfirst = 0; lastambig = 2; } else ret = docomplete(COMP_COMPLETE); @@ -198,6 +213,7 @@ menucomplete(char **args) { usemenu = 1; useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; if (c == '\t' && usetab()) return selfinsert(args); else @@ -210,6 +226,7 @@ listchoices(char **args) { usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; return docomplete(COMP_LIST_COMPLETE); } @@ -218,6 +235,7 @@ int spellword(char **args) { usemenu = useglob = 0; + wouldinstab = 0; return docomplete(COMP_SPELL); } @@ -227,6 +245,7 @@ deletecharorlist(char **args) { usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; if (cs != ll) { fixsuffix(); @@ -241,6 +260,7 @@ int expandword(char **args) { usemenu = useglob = 0; + wouldinstab = 0; if (c == '\t' && usetab()) return selfinsert(args); else @@ -253,12 +273,15 @@ expandorcomplete(char **args) { usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; if (c == '\t' && usetab()) return selfinsert(args); else { int ret; if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) { + bashlistfirst = 1; ret = docomplete(COMP_LIST_COMPLETE); + bashlistfirst = 0; lastambig = 2; } else ret = docomplete(COMP_EXPAND_COMPLETE); @@ -272,6 +295,7 @@ menuexpandorcomplete(char **args) { usemenu = 1; useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; if (c == '\t' && usetab()) return selfinsert(args); else @@ -284,6 +308,7 @@ listexpand(char **args) { usemenu = !!isset(MENUCOMPLETE); useglob = isset(GLOBCOMPLETE); + wouldinstab = 0; return docomplete(COMP_LIST_EXPAND); } @@ -291,6 +316,7 @@ listexpand(char **args) mod_export int reversemenucomplete(char **args) { + wouldinstab = 0; if (!menucmp) return menucomplete(args); @@ -302,6 +328,7 @@ reversemenucomplete(char **args) int acceptandmenucomplete(char **args) { + wouldinstab = 0; if (!menucmp) return 1; runhookdef(ACCEPTCOMPHOOK, NULL); @@ -513,21 +540,34 @@ parambeg(char *s) static int docomplete(int lst) { + static int active = 0; + char *s, *ol; - int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0; + int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, dat[2]; + if (active && !comprecursive) { + zwarn("completion cannot be used recursively (yet)", NULL, 0); + return 1; + } + active = 1; + comprecursive = 0; if (undoing) setlastline(); - if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst)) - return 0; + if (!module_loaded("zsh/complete")) + load_module("zsh/compctl"); + if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst)) { + active = 0; + return 0; + } /* Expand history references before starting completion. If anything * * changed, do no more. */ - if (doexpandhist()) + if (doexpandhist()) { + active = 0; return 0; - + } metafy_line(); ocs = cs; @@ -556,7 +596,8 @@ docomplete(int lst) qisuf = ztrdup(""); zsfree(autoq); autoq = NULL; - /* Get the word to complete. */ + /* Get the word to complete. + * NOTE: get_comp_string() calls pushheap(), but not popheap(). */ noerrs = 1; s = get_comp_string(); DPUTS(wb < 0 || cs < wb || cs > we, @@ -578,7 +619,11 @@ docomplete(int lst) strcpy((char *) line, ol); ll = strlen((char *) line); cs = ocs; + popheap(); unmetafy_line(); + zsfree(s); + zsfree(qword); + active = 0; return 1; } ocs = cs; @@ -738,8 +783,20 @@ docomplete(int lst) } } ret = docompletion(s, lst, lincmd); - } else if (ret) - clearlist = 1; + } else { + if (ret) + clearlist = 1; + if (!strcmp(ol, (char *)line)) { + /* We may have removed some quotes. For completion, other + * parts of the code re-install them, but for expansion + * we have to do it here. */ + cs = 0; + foredel(ll); + spaceinline(origll); + memcpy(line, origline, origll); + cs = origcs; + } + } } else /* Just do completion. */ ret = docompletion(s, lst, lincmd); @@ -752,9 +809,12 @@ docomplete(int lst) zsfree(qword); unmetafy_line(); - runhookdef(AFTERCOMPLETEHOOK, (void *) &lst); + dat[0] = lst; + dat[1] = ret; + runhookdef(AFTERCOMPLETEHOOK, (void *) dat); - return ret; + active = 0; + return dat[1]; } /* 1 if we are completing the prefix */ @@ -786,7 +846,8 @@ addx(char **ptmp) if (!line[cs] || line[cs] == '\n' || (iblank(line[cs]) && (!cs || line[cs-1] != '\\')) || - line[cs] == ')' || line[cs] == '`' || + line[cs] == ')' || line[cs] == '`' || line[cs] == '}' || + line[cs] == ';' || line[cs] == '|' || line[cs] == '&' || (instring && (line[cs] == '"' || line[cs] == '\'')) || (addspace = (comppref && !iblank(line[cs])))) { *ptmp = (char *)line; @@ -915,6 +976,7 @@ static char * get_comp_string(void) { int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0; + int ona = noaliases; char *s = NULL, *linptr, *tmp, *p, *tt = NULL; freebrinfo(brbeg); @@ -970,13 +1032,10 @@ get_comp_string(void) * the previously massaged command line using the lexer. It stores * * each token in each command (commands being regarded, roughly, as * * being separated by tokens | & &! |& || &&). The loop stops when * - * the end of the command containing the cursor is reached. It's a * - * simple way to do things, but suffers from an inability to * - * distinguish actual command arguments from, for example, * - * filenames in redirections. (But note that code elsewhere checks * - * if we are completing *in* a redirection.) The only way to fix * - * this would be to pass the command line through the parser too, * - * and get the arguments that way. Maybe in 3.1... */ + * the end of the command containing the cursor is reached. What * + * makes this messy is checking for things like redirections, loops * + * and whatnot. */ + do { lincmd = ((incmdpos && !ins && !incond) || (oins == 2 && i == 2) || (ins == 3 && i == 1)); @@ -1126,6 +1185,7 @@ get_comp_string(void) line[ll + addedx] = '\0'; } lexrestore(); + tt = NULL; goto start; } } @@ -1186,12 +1246,12 @@ get_comp_string(void) addedx = 0; goto start; } - noaliases = 0; + noaliases = ona; lexrestore(); return NULL; } - noaliases = 0; + noaliases = ona; /* Check if we are in an array subscript. We simply assume that * * we are in a subscript if we are in brackets. Correct solution * @@ -1281,6 +1341,7 @@ get_comp_string(void) } else insubscr = 1; } + parse_subst_string(s); } /* This variable will hold the current word in quoted form. */ qword = ztrdup(s); @@ -1291,6 +1352,19 @@ get_comp_string(void) *p = '"'; else if (*p == Snull) *p = '\''; + } else { + int level = 0; + + for (p = s; *p; p++) { + if (level && *p == Snull) + *p = '\''; + else if (level && *p == Dnull) + *p = '"'; + else if ((*p == String || *p == Qstring) && p[1] == Inbrace) + level++; + else if (*p == Outbrace) + level--; + } } if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) { char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, ""); @@ -1307,10 +1381,12 @@ get_comp_string(void) autoq = ztrdup(q); } /* While building the quoted form, we also clean up the command line. */ - for (p = s, tt = qword, i = wb; *p; p++, tt++, i++) + for (p = s, tt = qword, i = wb, j = 0; *p; p++, tt++, i++) if (INULL(*p)) { if (i < cs) offs--; + if (*p == Snull && isset(RCQUOTES)) + j = 1-j; if (p[1] || *p != Bnull) { if (*p == Bnull) { *tt = '\\'; @@ -1337,7 +1413,8 @@ get_comp_string(void) we--; } chuck(p--); - } + } else if (j && *p == '\'' && i < cs) + offs--; zsfree(origword); origword = ztrdup(s); @@ -1408,6 +1485,14 @@ get_comp_string(void) break; } if (*p == Inbrace) { + char *tp = p; + + if (!skipparens(Inbrace, Outbrace, &tp)) { + i += tp - p - 1; + dp += tp - p - 1; + p = tp - 1; + continue; + } if (bbeg) { Brinfo new; int len = bend - bbeg; @@ -1423,11 +1508,12 @@ get_comp_string(void) lastbrbeg = new; new->next = NULL; - new->str = ztrduppfx(bbeg, len); + new->str = dupstrpfx(bbeg, len); + new->str = ztrdup(bslashquote(new->str, NULL, instring)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(bslashquote(predup, NULL, instring)); *dbeg = '{'; i -= len; boffs -= len; @@ -1444,6 +1530,14 @@ get_comp_string(void) } } else { if (*p == Inbrace) { + char *tp = p; + + if (!skipparens(Inbrace, Outbrace, &tp)) { + i += tp - p - 1; + dp += tp - p - 1; + p = tp - 1; + continue; + } cant = 1; break; } @@ -1462,11 +1556,12 @@ get_comp_string(void) brbeg = new; lastbrbeg = new; - new->str = ztrduppfx(bbeg, len); + new->str = dupstrpfx(bbeg, len); + new->str = ztrdup(bslashquote(new->str, NULL, instring)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(bslashquote(predup, NULL, instring)); *dbeg = '{'; i -= len; boffs -= len; @@ -1478,7 +1573,7 @@ get_comp_string(void) if (*p == Comma) { if (!bbeg) bbeg = p; - hascom = 1; + hascom = 2; } else if (*p == Outbrace) { Brinfo new; int len; @@ -1498,7 +1593,8 @@ get_comp_string(void) new->next = brend; brend = new; - new->str = ztrduppfx(bbeg, len); + new->str = dupstrpfx(bbeg, len); + new->str = ztrdup(bslashquote(new->str, NULL, instring)); untokenize(new->str); new->pos = dp - predup - len + 1; new->qpos = len; @@ -1526,11 +1622,12 @@ get_comp_string(void) brbeg = new; lastbrbeg = new; - new->str = ztrduppfx(bbeg, len); + new->str = dupstrpfx(bbeg, len); + new->str = ztrdup(bslashquote(new->str, NULL, instring)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(bslashquote(predup, NULL, instring)); *dbeg = '{'; boffs -= len; strcpy(dbeg, dbeg + len); @@ -1545,7 +1642,7 @@ get_comp_string(void) p = bp->pos; l = bp->qpos; bp->pos = strlen(predup + p + l); - bp->qpos = strlen(quotename(predup + p + l, NULL)); + bp->qpos = strlen(bslashquote(predup + p + l, NULL, instring)); strcpy(predup + p, predup + p + l); } } @@ -1596,13 +1693,20 @@ inststrlen(char *str, int move, int len) static int doexpansion(char *s, int lst, int olst, int explincmd) { - int ret = 1; + int ret = 1, first = 1; LinkList vl; - char *ss; + char *ss, *ts; pushheap(); vl = newlinklist(); ss = dupstring(s); + /* get_comp_string() leaves these quotes unchanged when they are + * inside parameter expansions. */ + for (ts = ss; *ts; ts++) + if (*ts == '"') + *ts = Dnull; + else if (*ts == '\'') + *ts = Snull; addlinknode(vl, ss); prefork(vl, 0); if (errflag) @@ -1630,9 +1734,15 @@ doexpansion(char *s, int lst, int olst, int explincmd) goto end; } if (lst == COMP_LIST_EXPAND) { - /* Only the list of expansions was requested. */ - ret = listlist(vl); - showinglist = 0; + /* Only the list of expansions was requested. Restore the + * command line. */ + cs = 0; + foredel(ll); + spaceinline(origll); + memcpy(line, origline, origll); + cs = origcs; + ret = listlist(vl); + showinglist = 0; goto end; } /* Remove the current word and put the expansions there. */ @@ -1647,10 +1757,11 @@ doexpansion(char *s, int lst, int olst, int explincmd) if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) || (cs && line[cs-1] != '/')) { #endif - if (nonempty(vl)) { + if (nonempty(vl) || !first) { spaceinline(1); line[cs++] = ' '; } + first = 0; } end: popheap(); @@ -1658,19 +1769,6 @@ doexpansion(char *s, int lst, int olst, int explincmd) return ret; } -/* This is called from the lexer to give us word positions. */ - -/**/ -void -gotword(void) -{ - we = ll + 1 - inbufct + (addedx == 2 ? 1 : 0); - if (cs <= we) { - wb = ll - wordbeg + addedx; - zleparse = 0; - } -} - /**/ static int docompletion(char *s, int lst, int incmd) @@ -1851,14 +1949,19 @@ printfmt(char *fmt, int n, int dopr, int doesc) putc(' ', shout); } } - l += 1 + (cc / columns); + l += 1 + ((cc - 1) / columns); cc = 0; } - if (dopr) + if (dopr) { putc(*p, shout); + if (!(cc % columns)) + fputs(" \010", shout); + } } } if (dopr) { + if (!(cc % columns)) + fputs(" \010", shout); if (tccan(TCCLEAREOL)) tcout(TCCLEAREOL); else { @@ -1983,13 +2086,16 @@ listlist(LinkList l) max = getiparam("LISTMAX"); if ((max && num > max) || (!max && nlines > lines)) { - int qup; + int qup, l; zsetterm(); - qup = printfmt("zsh: do you wish to see all %n possibilities? ", - num, 1, 1); + l = (num > 0 ? + fprintf(shout, "zsh: do you wish to see all %d possibilities (%d lines)? ", + num, nlines) : + fprintf(shout, "zsh: do you wish to see all %d lines? ", nlines)); + qup = ((l + columns - 1) / columns) - 1; fflush(shout); - if (getzlequery() != 'y') { + if (getzlequery(1) != 'y') { if (clearflag) { putc('\r', shout); tcmultout(TCUP, TCMULTUP, qup); @@ -2073,7 +2179,7 @@ int doexpandhist(void) { unsigned char *ol; - int oll, ocs, ne = noerrs, err; + int oll, ocs, ne = noerrs, err, ona = noaliases; pushheap(); metafy_line(); @@ -2100,7 +2206,7 @@ doexpandhist(void) * means that the expanded string is unusable. */ err = errflag; noerrs = ne; - noaliases = 0; + noaliases = ona; strinend(); inpop(); zleparse = 0; -- cgit 1.4.1