Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Scrolling (was: Re: Question)



Bart Schaefer wrote:

> ...
> 
> There's a difference between pausing after N lines and implementing a
> pager.  A pager implies being able to scroll one or a few lines at a
> time, not by whole screenfuls, and being able to move backwards as well
> as forwards (or at least that's what it implies nowadays).

Ah. That's it. I was only thinking about such a `pausing after N
lines' thing (I can still remember when I met my first pager that
could go up). So...

> Is the following a pager?

...in my terminology: yes.

> ...
> 
> } A final thought: complist is intended as the module to allow fancy
> } completion list handling... would it be acceptable to add this
> } scrolling stuff only to it?
> 
> If it's getting added, that's the place for it.

So be it.

The patch below does all this scrolling and whatnot. I have *not*
committed it.

To get scrolling completion lists just set LISTMAX to `scroll'. It
will then stop after every screenful of lines, showing a prompt. At
that prompt TAB and Space scroll to the next screen. Return scrolls
one line, `q' stops listing, ignoring the `q' and eevry other key
stops listing and executes that key's function (this is ugly, I know,
see below).

For a good time, however, use menu-selection with a list that doesn't
fit on the screen: it will scroll up and down as needed. There are
also some more special functions to move the mark one screenful of
lines up and down, to go to the first/last line, that kind of stuff.
See the manual, which is also made much more itemised.

Some comments (scrolling):

- The prompt shown when waiting for the key to do scrolling could be
  better. I don't we would need to make it configurable. Maybe it
  should contain the number of lines to follow, etc. Suggestions
  welcome (if the patch makes it in at all).
- Moving up (i.e. turning it into a real pager) in a scrolled
  completion list isn't entirely trivial. I have some ideas for that,
  but it didn't seem worth it for this preview version. In the end,
  every kind of movement (in vertical directions) would of course be
  possible to implement. Somehow.
- We could use a keymap when waiting for the key at the scroll-prompt
  and give special meaning to some of the zle functions as we do for
  menu-selection. That would make it configurable. At least `q'
  shouldn't be handled differently from other alphanumeric keys.

Some comments (selection):

- It's ugly that there is currently no visual feedback telling you if
  the list can be scrolled up/down. Maybe we should use the statusline 
  to display that information together with the current position etc.
  This would then be getting very pager-like, right? But menu-selection
  always was a bit like a very specialised editor and as long as zle
  doesn't support a more elaborate buffer concept with colouring and
  so on...
- Scrolling in menu-selection currently uses a smooth scroll (always
  one line up or down). It is *very* simple to change that (I once had 
  it scrolling one-half screenful up and down). Configurable scroll-step?
- It is currently rather slow when used for very long lists (it gets
  slower further down). When all this makes it in, I'll try to make it 
  faster.

Bye
 Sven

diff -ru ../z.old/Doc/Zsh/mod_complist.yo Doc/Zsh/mod_complist.yo
--- ../z.old/Doc/Zsh/mod_complist.yo	Thu Apr 13 11:12:16 2000
+++ Doc/Zsh/mod_complist.yo	Thu Apr 13 11:46:15 2000
@@ -3,17 +3,18 @@
 !MOD!)
 cindex(completion, listing)
 cindex(completion, coloured listings)
-The tt(zsh/complist) module offers two extensions to completion listings:
-the ability to highlight matches in such a list and a different
-style of menu-completion.
+cindex(completion, scroll listings)
+The tt(zsh/complist) module offers three extensions to completion listings:
+the ability to highlight matches in such a list, the ability to
+scroll through long lists and a different style of menu-completion.
 
+subsect(Colored completion listings)
 Whenever one of the parameters tt(ZLS_COLORS) or tt(ZLS_COLOURS) is set 
 and the tt(zsh/complist) module is loaded or linked into the shell,
 completion lists will be colored.  Note, however, that tt(complist) will
 not automatically be loaded if it is not linked in:  on systems with
 dynamic loading, `tt(zmodload zsh/complist)' is required.
 
-subsect(Parameters)
 vindex(ZLS_COLORS)
 vindex(ZLS_COLOURS)
 The parameters tt(ZLS_COLORS) and tt(ZLS_COLOURS) describe how matches
@@ -126,6 +127,31 @@
 ifnzman(noderef(Completion System Configuration))\
 ).
 
+subsect(Scrolling in completion listings)
+vindex(LISTMAX)
+To be able to scroll through a completion list, the tt(LISTMAX)
+parameter has to be set to the string `tt(scroll)'. If it has this
+value, the completion code will not ask if the list should be
+shown. Instead it immediately starts displaying the list, stopping
+after the first screenful, showing a simple prompt at the bottom,
+waiting for a keypress. The following keys have a special meaning:
+
+startitem()
+item(tt(Space), tt(Tab))(
+scroll forward one screenful
+)
+item(tt(Return), tt(Newline))(
+scroll forward one line
+)
+item(tt(q))(
+stops listing and redisplays the command line without inserting the
+`tt(q)'
+)
+enditem()
+
+Every other character stops listing and immediately processes the key
+as usual.
+
 subsect(Menu selection)
 cindex(completion, selecting by cursor)
 vindex(SELECTMIN)
@@ -151,7 +177,9 @@
 and tt(ZLS_COLOURS) parameters described above). Instead, the tt(menu) 
 style should be used.
 
-After menu-selection is started, the matches will be listed. The
+After menu-selection is started, the matches will be listed. If there
+are more matches than fit on the screen, only the first screenful is
+shown. The
 matches to insert into the command line can be selected from this
 list. In the list one match is highlighted using the value for tt(ma)
 from the tt(ZLS_COLORS) or tt(ZLS_COLOURS) parameter. The default
@@ -173,25 +201,86 @@
 hidden matches of the first and second kind, respectively.
 
 Selecting matches is done by moving the mark around using the zle movement
-functions. The zle functions tt(send-break) and tt(accept-line) can be used
-to leave menu-selection, leaving the match currently inserted into the line
-in place.  In the case of tt(accept-line), the match currently inserted
-will be accepted and a new completion may be attempted.
-Using tt(send-break) leaves menu-selection and continues with normal
-menu-completion.  The functions tt(accept-and-hold) and
-tt(accept-and-menu-complete) can be used to accept the match currently
-inserted and continue inserting matches from the same list. The
-function tt(accept-and-infer-next-history) accepts the current match and
-then tries completion with menu-selection again.  In the case of
-files this allows one to select a directory and immediately attempt to
-complete files in it.  Matches inserted in one of these ways can be removed
-by invoking the tt(undo) function.  Keys bound to one of
-the completion functions will cycle to the next (or, in case of
-tt(reverse-menu-complete), the previous) match, and the tt(redisplay) and
-tt(clear-screen) functions work as usual without leaving
-menu-selection.
+functions. When not all matches can be shown on the screen at the same 
+time, the list will scroll up and down when crossing the top or
+bottom line. The following zle functions have special meaning during
+menu selection:
+
+startitem()
+item(tt(accept-line))(
+accepts the current match and leaves menu selection
+)
+item(tt(send-break))(
+leaves menu selection and continues with normal menu completion
+)
+item(tt(redisplay), tt(clear-screen))(
+execute their normal function without leaving menu selection
+)
+item(tt(accept-and-hold), tt(accept-and-menu-complete))(
+accept the currently inserted match and continue selection allowing to 
+select the next match to insert into the line
+)
+item(tt(accept-and-infer-next-history))(
+accepts the current match and then tries completion with
+menu-selection again;  in the case of files this allows one to select
+a directory and immediately attempt to complete files in it
+)
+item(tt(undo))(
+removes matches inserted during the menu selection by one of the three 
+functions before
+)
+xitem(tt(down-history), tt(down-line-or-history))
+item(tt(vi-down-line-or-history),  tt(down-line-or-search))(
+moves the mark one line down
+)
+xitem(tt(up-history), tt(up-line-or-history))
+item(tt(vi-up-line-or-history), tt(up-line-or-search))(
+moves the mark one line up
+)
+item(tt(forward-char), tt(vi-forward-char))(
+moves the mark one column right
+)
+item(tt(backward-char), tt(vi-backward-char))(
+moves the mark one column left
+)
+xitem(tt(forward-word), tt(vi-forward-word))
+item(tt(vi-forward-word-end), tt(emacs-forward-word))(
+moves the mark one screenful down
+)
+item(tt(backward-word), tt(vi-backward-word), tt(emacs-backward-word))(
+moves the mark one screenful up
+)
+item(tt(vi-forward-blank-word), tt(vi-forward-blank-word-end))(
+moves the mark to the first line of the next group of matches
+)
+item(tt(vi-backward-blank-word))(
+moves the mark to the last line of the previous group of matches
+)
+item(tt(beginning-of-history))(
+moves the mark to the first line
+)
+item(tt(end-of-history))(
+moves the mark to the last line
+)
+xitem(tt(beginning-of-buffer-or-history), tt(beginning-of-line))
+item(tt(beginning-of-line-hist), tt(vi-beginning-of-line))(
+moves the mark to the leftmost column
+)
+xitem(tt(end-of-buffer-or-history), tt(end-of-line))
+item(tt(end-of-line-hist), tt(vi-end-of-line))(
+moves the mark to the rightmost column
+)
+xitem(tt(complete-word), tt(menu-complete), tt(expand-or-complete))
+item(tt(expand-or-complete-prefix), tt(menu-expand-or-complete))(
+moves the mark to the next match
+)
+item(tt(reverse-menu-omplete))(
+moves the mark to the previous match
+)
+enditem()
 
-Any other zle function leaves menu-selection and executes that function.
+All movement function do wrap-around at the edges and
+any other zle function leaves menu-selection and executes that function.
 It is possible to make widgets in the above list do the same by using the
 form of the widget with a `tt(.)' in front.  For example, the widget
 `tt(.accept-line)' has the effect of leaving menu selection and accepting
diff -ru ../z.old/Src/Zle/compcore.c Src/Zle/compcore.c
--- ../z.old/Src/Zle/compcore.c	Thu Apr 13 11:12:06 2000
+++ Src/Zle/compcore.c	Thu Apr 13 11:13:32 2000
@@ -296,7 +296,8 @@
     comppatinsert = ztrdup("menu");
     forcelist = 0;
     haspattern = 0;
-    complistmax = getiparam("LISTMAX");
+    zsfree(complistmax);
+    complistmax = ztrdup(getsparam("LISTMAX"));
     zsfree(complastprompt);
     complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
 			     (unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
diff -ru ../z.old/Src/Zle/complete.c Src/Zle/complete.c
--- ../z.old/Src/Zle/complete.c	Thu Apr 13 11:12:07 2000
+++ Src/Zle/complete.c	Thu Apr 13 11:13:32 2000
@@ -35,8 +35,7 @@
 /**/
 mod_export zlong compcurrent;
 /**/
-zlong complistmax,
-      complistlines,
+zlong complistlines,
       compignored;
 
 /**/
@@ -50,7 +49,8 @@
      *compquote,
      *compqstack,
      *comppatmatch,
-     *complastprompt;
+     *complastprompt,
+     *complistmax;
 /**/
 char *compiprefix,
      *compcontext,
@@ -904,7 +904,7 @@
     { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
     { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
       VAL(get_unambig_curs) },
-    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+    { "list_max", PM_SCALAR, VAL(complistmax), NULL, NULL },
     { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
     { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
     { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
@@ -1292,7 +1292,7 @@
     comprpms = compkpms = NULL;
     compwords = NULL;
     compprefix = compsuffix = compiprefix = compisuffix = 
-	compqiprefix = compqisuffix =
+	compqiprefix = compqisuffix = complistmax = 
 	compcontext = compparameter = compredirect = compquote =
 	compquoting = comprestore = complist = compinsert =
 	compexact = compexactstr = comppatmatch = comppatinsert =
@@ -1347,6 +1347,7 @@
 {
     if (compwords)
 	freearray(compwords);
+    zsfree(complistmax);
     zsfree(compprefix);
     zsfree(compsuffix);
     zsfree(compiprefix);
diff -ru ../z.old/Src/Zle/complist.c Src/Zle/complist.c
--- ../z.old/Src/Zle/complist.c	Thu Apr 13 11:12:07 2000
+++ Src/Zle/complist.c	Thu Apr 13 11:13:33 2000
@@ -382,7 +382,8 @@
 /* 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;
 static Cmatch **mtab, **mmtabp;
 static Cmgroup *mgtab, *mgtabp;
 static struct listcols mcolors;
@@ -496,10 +497,10 @@
 
 /* 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 +508,32 @@
 	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()))
+	    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;
+    char *t;
 
     initiscol(c);
 
@@ -548,8 +547,21 @@
 	}
 	if (cc == Meta)
 	    cc = *s++ ^ 32;
-	fputs(nicechar(cc), shout);
+
+	for (t = nicechar(cc); *t; t++) {
+	    if (ml == mlend - 1 && col == columns - 1)
+		return 0;
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll()))
+		    return ask;
+
+		col = 0;
+	    }
+	}
     }
+    return 0;
 }
 
 /* Get the terminal color string for the given match. */
@@ -636,12 +648,490 @@
 
 static Cmgroup last_group;
 
-static void
+/**/
+static int
+asklistscroll(void)
+{
+    int v;
+
+    fputs("continue... ", shout);
+    fflush(shout);
+    zsetterm();
+    v = getzlequery(0);
+    settyinfo(&shttyinfo);
+    putc('\r', shout);
+    fputs("               ", shout);
+    putc('\r', shout);
+
+    if (v == '\n' || v == '\r') {
+	mrestlines = 1;
+	return 0;
+    }
+    mrestlines = lines - 1;
+
+    if (v == ' ' || v == '\t')
+	return 0;
+    if (v != 'q')
+	ungetkey(v);
+
+    return 1;
+}
+
+#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(void)
+{
+    int ask;
+
+    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+    putc('\n', shout);
+
+    if (mscroll && !--mrestlines && (ask = asklistscroll()))
+	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 = fmt, nc[DIGBUFSIZE];
+    int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg;
+
+    for (; *p; p++) {
+	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
+	if (doesc && *p == '%') {
+	    if (*++p) {
+		m = 0;
+		switch (*p) {
+		case '%':
+		    if (dopr)
+			putc('%', shout);
+		    cc++;
+		    break;
+		case 'n':
+		    sprintf(nc, "%d", n);
+		    if (dopr)
+			fprintf(shout, nc);
+		    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;
+		}
+		if (dopr && m) {
+		    if (b)
+			tcout(TCBOLDFACEBEG);
+		    if (s)
+			tcout(TCSTANDOUTBEG);
+		    if (u)
+			tcout(TCUNDERLINEBEG);
+		}
+	    } else
+		break;
+	} else {
+	    cc++;
+	    if (*p == '\n') {
+		if (dopr && mlbeg >= 0 && tccan(TCCLEAREOL))
+		    tcout(TCCLEAREOL);
+		l += 1 + (cc / columns);
+		cc = 0;
+	    }
+	    if (dopr) {
+		if (ml == mlend - 1 && (cc % columns) == columns - 1) {
+		    dopr = 0;
+		    continue;
+		}
+		putc(*p, shout);
+		if ((beg = !(cc % columns)))
+		    ml++;
+		if (mscroll && beg && !--mrestlines && (ask = asklistscroll())) {
+		    *stop = 1;
+		    return l + (cc / columns);
+		}
+	    }
+	}
+    }
+    if (dopr && mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+
+    return 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')) {
+	    if (!--mrestlines && (ask = asklistscroll()))
+		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;
+    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)
+		return 0;
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll()))
+		    return ask;
+
+		col = 0;
+	    }
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+compprintlist(int showall)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int pnl = 0, cl;
+    int mc = 0, ml = 0, printed = 0, stop = 0, asked = 1;
+
+    cl = (listdat.nlines > lines - nlnct ? lines - nlnct : listdat.nlines);
+    mrestlines = lines - 1;
+
+    if (cl < 2) {
+	cl = -1;
+	if (tccan(TCCLEAREOD))
+	    tcout(TCCLEAREOD);
+    } else if (mlbeg >= 0 && !tccan(TCCLEAREOL) && 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) {
+			if (dolistnl(ml) && compprintnl())
+			    goto end;
+			pnl = 0;
+			ml++;
+			if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    l = compprintfmt((*e)->str, (*e)->count, dolist(ml), 1,
+				     ml, &stop);
+		    if (stop)
+			goto end;
+		    ml += l;
+		    if (dolistcl(ml) && cl >= 0 && (cl -= l) <= 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())
+		    goto end;
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    if (compzputs(*pp, ml))
+			goto end;
+		    if (*++pp && compprintnl())
+			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())
+			    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) {
+		for (p = g->matches; (m = *p); p++) {
+		    if (m->disp && (m->flags & CMF_DISPLINE)) {
+			if (pnl) {
+			    if (dolistnl(ml) && compprintnl())
+				goto end;
+			    pnl = 0;
+			    ml++;
+			    if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+				cl = -1;
+				if (tccan(TCCLEAREOD))
+				    tcout(TCCLEAREOD);
+			    }
+			}
+			if (dolist(ml))
+			    printed++;
+			if (clprintm(g, p, 0, ml, 1, 0, NULL, NULL))
+			    goto end;
+			pnl = 1;
+		    }
+		    if (!mnew && ml > mlend)
+			goto end;
+		}
+	    }
+	    if (n && pnl) {
+		if (dolistnl(ml) && compprintnl())
+		    goto end;
+		pnl = 0;
+		ml++;
+		if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    for (p = skipnolist(g->matches, showall); n && nl--;) {
+		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)) {
+			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++;
+
+		    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())
+			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) {
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if (mlbeg >= 0) {
+	    if ((ml = listdat.nlines + nlnct - 1) > lines)
+		ml = lines - 1;
+	    tcmultout(TCUP, TCMULTUP, ml);
+	    showinglist = -1;
+
+	    lastlistlen = listdat.nlines;
+	} else if ((ml = listdat.nlines + nlnct - 1) < lines) {
+	    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+		tcout(TCCLEAREOL);
+	    tcmultout(TCUP, TCMULTUP, ml);
+	    showinglist = -1;
+
+	    lastlistlen = listdat.nlines;
+	} else {
+	    clearflag = 0;
+	    if (!asked)
+		compprintnl();
+	}
+    } else if (!asked)
+	compprintnl();
+
+    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,12 +1139,14 @@
     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();
+	}
+	return 0;
     }
     m = *mp;
     if (m->disp && (m->flags & CMF_DISPLINE)) {
@@ -666,6 +1158,8 @@
 		mgtab[mm + i] = g;
 	    }
 	}
+	if (!dolist(ml))
+	    return 0;
 	if (m->gnum == mselect) {
 	    int mm = (mcols * ml);
 	    mline = ml;
@@ -681,9 +1175,12 @@
 	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 +1201,8 @@
 		mgtab[mx + mm + i] = g;
 	    }
 	}
+	if (!dolist(ml))
+	    return 0;
 	if (m->gnum == mselect) {
 	    int mm = mcols * ml;
 
@@ -723,9 +1222,13 @@
 	    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);
 
 	 if (isset(LISTTYPES) && buf) {
@@ -751,28 +1254,30 @@
 	    zcoff();
 	}
     }
+    return ret;
 }
 
 static int
 complistmatches(Hookdef dummy, Chdata dat)
 {
     Cmgroup oamatches = amatches;
+    char *p = NULL;
 
     amatches = dat->matches;
 
-    if (minfo.asked == 2) {
+    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;
@@ -781,11 +1286,34 @@
     if (inselect)
 	clearflag = 0;
 
-    if (asklist()) {
+    mscroll = 0;
+
+    if (mselect >= 0 || mlbeg >= 0 ||
+	((p = getsparam("LISTMAX")) && !strcmp(p, "scroll"))) {
+	trashzle();
+	showinglist = listshown = 0;
+
+	lastlistlen = 0;
+
+	if (p) {
+	    clearflag = (isset(USEZLE) && !termflags && dolastprompt);
+	    mscroll = 1;
+	} else {
+	    clearflag = 1;
+	    minfo.asked = (listdat.nlines + nlnct <= lines);
+	}
+    } else if (asklist()) {
 	amatches = oamatches;
 	return (noselect = 1);
     }
-    if (mselect >= 0) {
+    if (mlbeg >= 0) {
+	mlend = mlbeg + lines - nlnct;
+	while (mline >= mlend)
+	    mlbeg++, mlend++;
+    } else
+	mlend = 9999999;
+
+    if (mnew) {
 	int i;
 
 	i = columns * listdat.nlines;
@@ -795,14 +1323,13 @@
 	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;
@@ -849,7 +1376,7 @@
     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;
 };
@@ -863,6 +1390,7 @@
     Thingy cmd;
     Menustack u = NULL;
     int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0;
+    int space, lbeg = 0;
     char *s;
 
     if (fdat || (dummy && (!(s = getsparam("SELECTMIN")) ||
@@ -877,7 +1405,34 @@
     selectlocalmap(mskeymap);
     noselect = 0;
     mselect = (*(minfo.cur))->gnum;
+    mline = 0;
+    mlines = 999999;
+    mlbeg = 0;
     for (;;) {
+	space = lines - nlnct;
+	while (mline < mlbeg)
+	    if (--mlbeg < 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)
+			break;
+		if (c)
+		    break;
+		p -= columns;
+		mlbeg--;
+	    }
+	}
+	while (mline >= mlbeg + space)
+	    if (++mlbeg + space > mlines)
+		mlbeg = mlines - space;
+
+	lbeg = mlbeg;
 	onlyexpl = 0;
 	showinglist = -2;
 	zrefresh();
@@ -923,6 +1478,8 @@
 	    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;
@@ -949,15 +1506,19 @@
 	    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;
@@ -970,6 +1531,24 @@
 	    accept_last();
 	    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;
+		mline++;
+	    } while (mline != ol);
+	    if (*p != minfo.cur) {
+		noselect = clearlist = listshown = 1;
+		onlyexpl = 0;
+		zrefresh();
+		break;
+	    }
 	    setwish = 1;
 	    continue;
 	} else if (cmd == Th(z_undo)) {
@@ -986,6 +1565,8 @@
 	    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);
@@ -1043,6 +1624,97 @@
 		if (adjust_mcol(wishcol, &p, NULL))
 		    continue;
 	    } while (!*p);
+	} else if (cmd == Th(z_emacsforwardword) ||
+		   cmd == Th(z_viforwardword) ||
+		   cmd == Th(z_viforwardwordend) ||
+		   cmd == Th(z_forwardword)) {
+	    int i = lines - nlnct, oi = i, ll;
+	    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) {
+		    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 - nlnct, oi = i, ll;
+	    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;
+		if (*p) {
+		    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) {
+		    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) {
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    mline = ll;
+	    p = lp;
 	} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
 	    int omcol = mcol;
 	    Cmatch *op = *p;
@@ -1093,10 +1765,8 @@
 		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;
 
@@ -1113,9 +1783,7 @@
 		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)) {
+	} else if (cmd == Th(z_vibackwardblankword)) {
 	    Cmgroup g = *pg;
 	    int ol = mline;
 
@@ -1166,7 +1834,7 @@
 		freematches(u->lastmatches);
 
     selectlocalmap(NULL);
-    mselect = -1;
+    mselect = mlastcols = mlastlines = -1;
     inselect = 0;
     if (acc) {
 	menucmp = lastambig = hasoldlist = 0;
@@ -1184,6 +1852,7 @@
 	    clearlist = 1;
 	zrefresh();
     }
+    mlbeg = -1;
     fdat = NULL;
 
     return (!noselect ^ acc);
diff -ru ../z.old/Src/Zle/compresult.c Src/Zle/compresult.c
--- ../z.old/Src/Zle/compresult.c	Thu Apr 13 11:12:07 2000
+++ Src/Zle/compresult.c	Thu Apr 13 11:14:05 2000
@@ -1157,7 +1157,7 @@
 }
 
 /**/
-mod_export void
+mod_export int
 calclist(int showall)
 {
     Cmgroup g;
@@ -1170,7 +1170,7 @@
     if (listdat.valid && onlyexpl == listdat.onlyexpl &&
 	menuacc == listdat.menuacc && showall == listdat.showall &&
 	lines == listdat.lines && columns == listdat.columns)
-	return;
+	return 0;
 
     for (g = amatches; g; g = g->next) {
 	char **pp = g->ylist;
@@ -1572,11 +1572,16 @@
     listdat.columns = columns;
     listdat.lines = lines;
     listdat.showall = showall;
+
+    return 1;
 }
 
 /**/
-mod_export int asklist(void)
+mod_export int
+asklist(void)
 {
+    int lmax = (complistmax ? (int) mathevali(complistmax) : 0);
+
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = listshown = 0;
@@ -1586,9 +1591,9 @@
 
     /* Maybe we have to ask if the user wants to see the list. */
     if ((!minfo.cur || !minfo.asked) &&
-	((complistmax > 0 && listdat.nlist >= complistmax) ||
-	 (complistmax < 0 && listdat.nlines <= -complistmax) ||
-	 (!complistmax && listdat.nlines >= lines))) {
+	((lmax > 0 && listdat.nlist >= lmax) ||
+	 (lmax < 0 && listdat.nlines <= -lmax) ||
+	 (!lmax && listdat.nlines >= lines))) {
 	int qup, l;
 
 	zsetterm();
@@ -1599,7 +1604,7 @@
 		     listdat.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);
diff -ru ../z.old/Src/Zle/zle_tricky.c Src/Zle/zle_tricky.c
--- ../z.old/Src/Zle/zle_tricky.c	Thu Apr 13 11:12:09 2000
+++ Src/Zle/zle_tricky.c	Thu Apr 13 11:13:33 2000
@@ -1997,7 +1997,7 @@
 	     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);
diff -ru ../z.old/Src/Zle/zle_utils.c Src/Zle/zle_utils.c
--- ../z.old/Src/Zle/zle_utils.c	Thu Apr 13 11:12:09 2000
+++ Src/Zle/zle_utils.c	Thu Apr 13 11:13:34 2000
@@ -277,6 +277,7 @@
  * question is assumed to have been printed already, and the    *
  * cursor is left immediately after the response echoed.        *
  * (Might cause a problem if this takes it onto the next line.) *
+ * If yesno is non-zero:                                        *
  * <Tab> is interpreted as 'y'; any other control character is  *
  * interpreted as 'n'.  If there are any characters in the      *
  * buffer, this is taken as a negative response, and no         *
@@ -284,29 +285,32 @@
 
 /**/
 mod_export int
-getzlequery(void)
+getzlequery(int yesno)
 {
     int c;
 #ifdef FIONREAD
     int val;
 
-    /* check for typeahead, which is treated as a negative response */
-    ioctl(SHTTY, FIONREAD, (char *)&val);
-    if (val) {
-	putc('n', shout);
-	return 'n';
+    if (yesno) {
+	/* check for typeahead, which is treated as a negative response */
+	ioctl(SHTTY, FIONREAD, (char *)&val);
+	if (val) {
+	    putc('n', shout);
+	    return 'n';
+	}
     }
 #endif
 
     /* get a character from the tty and interpret it */
     c = getkey(0);
-    if (c == '\t')
-	c = 'y';
-    else if (icntrl(c) || c == EOF)
-	c = 'n';
-    else
-	c = tulower(c);
-
+    if (yesno) {
+	if (c == '\t')
+	    c = 'y';
+	else if (icntrl(c) || c == EOF)
+	    c = 'n';
+	else
+	    c = tulower(c);
+    }
     /* echo response and return */
     putc(c, shout);
     return c;
diff -ru ../z.old/Src/params.c Src/params.c
--- ../z.old/Src/params.c	Thu Apr 13 11:12:04 2000
+++ Src/params.c	Thu Apr 13 11:13:34 2000
@@ -482,10 +482,10 @@
     setiparam("MAILCHECK", 60);
     setiparam("LOGCHECK", 60);
     setiparam("KEYTIMEOUT", 40);
-    setiparam("LISTMAX", 100);
 #ifdef HAVE_SELECT
     setiparam("BAUD", getbaudrate(&shttyinfo));  /* get the output baudrate */
 #endif
+    setsparam("LISTMAX", ztrdup("100"));
     setsparam("FCEDIT", ztrdup(DEFAULT_FCEDIT));
     setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX));
     setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT));

--
Sven Wischnowsky                         wischnow@xxxxxxxxxxxxxxxxxxxxxxx



Messages sorted by: Reverse Date, Date, Thread, Author