From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4558 invoked from network); 2 Dec 1999 10:39:14 -0000 Received: from sunsite.auc.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 2 Dec 1999 10:39:14 -0000 Received: (qmail 3163 invoked by alias); 2 Dec 1999 10:39:09 -0000 Mailing-List: contact zsh-workers-help@sunsite.auc.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 8851 Received: (qmail 3156 invoked from network); 2 Dec 1999 10:39:08 -0000 Date: Thu, 2 Dec 1999 11:39:07 +0100 (MET) Message-Id: <199912021039.LAA20175@beta.informatik.hu-berlin.de> From: Sven Wischnowsky To: zsh-workers@sunsite.auc.dk Subject: PATCH: groups in ZLS_COLOURS (and questions) Step by step... This allows to make specs in ZLS_COLO(|U)RS work only in certain groups. For now I've taken the syntax `(group-pat)', i.e. a pattern for the group name(s) has to be given at the beginning in parentheses. Another enhancement is that one can also give pattern-specs that will be used for all matches (not only files, but also files). The syntax for that is now `=='. Of course, the syntax for both of them can easily be changed if you don't like it. What this patch doesn't do is make this configurable with styles. That's because I'm not sure how to do that best. What I would like to achieve is that one could do, e.g.: compstyle '*:jobs' list-colors which would make the completion code use these patterns only for groups named `jobs'. Building appropriate specs for ZLS_* is simple. But there are three problems I see: 1) Where to do it. I think I would put this in a helper function that can would be called from _description (and from _files because that has to mess around with the group names). 2) How to merge different definitions. Most of the things we could do before affected only file names but there were already some specs that have to take affect everywhere (the lc, rc, ec capabilities, for example). With the per-group specs above the user would either have to include the definitions for these things (and other specs he wants to have used everywhere) in every value for list-colors or we make the completion code look up such default specs at some central place (e.g. _main_complete) and the special specs are then appended to that when then groups are added. The problem is that I don't know if that nice enough, maybe if we use a tag `default' (or `global'?) for that: compstyle '*:default' list-colors lc=... rc=... compstyle '*:jobs' list-colors ... Would that be ok? Btw, when we do this with styles we could also use more descriptive names for the capabilities and a nicer syntax for the extension (`*.foo=') and pattern (`==') specs. I would then probably try to get some help from computil for this. 3) When and how to set ZLS_*. The completion system would have to set/change the value of one of these. But what if a user has set it already? Is it enough to document that one shouldn't set it directly when using the new completion system? And document that one can use: compstyle '*(files|directories)' list-colors ${(s.:.)LS_COLORS} (or put it into the `default' list-colors style) to include the stuff one uses for GNU ls? The same question is important when we try to support things like menu-selection via styles, btw, so I'd really like to hear your opinions and suggestions here. One last comment about this colouring: I guess with this per-group and pattern matching stuff going on somebody will ask if it wouldn't be possible to highlight different line-parts of, say, a process listing differently (e.g. pids in red cmd-line in blue or whatever). This is something even I may like so I'm thinking about it. One step would be to enhance the pattern matching code to directly report backreferences in integer arrays (so that we don't have to set/use the match arrays). Doesn't look to hairy. Being able to print different parts of a string in different colours is more difficult. complist would have to implement its own versions of printfmt() and nicezputs(). Hm. But still, looks like something I could like to play with... Bye Sven diff -u oldsrc/Zle/compcore.c Src/Zle/compcore.c --- oldsrc/Zle/compcore.c Thu Dec 2 10:16:19 1999 +++ Src/Zle/compcore.c Thu Dec 2 09:30:23 1999 @@ -445,9 +445,8 @@ /* We may have to reset the cursor to its position after the * * string inserted by the last completion. */ - if (fromcomp & FC_INWORD) - if ((cs = lastend) > ll) - cs = ll; + if ((fromcomp & FC_INWORD) && (cs = lastend) > ll) + cs = ll; /* Check if we have to start a menu-completion (via automenu). */ @@ -2512,6 +2511,7 @@ n->mcount = g->mcount; n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) * sizeof(Cmatch)); + n->name = ztrdup(g->name); for (q = g->matches; *q; q++, p++) *p = dupmatch(*q, nbrbeg, nbrend); *p = NULL; @@ -2609,6 +2609,7 @@ } free(g->expls); } + zsfree(g->name); free(g); g = n; diff -u oldsrc/Zle/complist.c Src/Zle/complist.c --- oldsrc/Zle/complist.c Thu Dec 2 10:16:19 1999 +++ Src/Zle/complist.c Thu Dec 2 11:05:27 1999 @@ -74,11 +74,33 @@ "\033[", "m", NULL, "0", "0", "7" }; +/* This describes a terminal string for a file type. */ + +typedef struct filecol *Filecol; + +struct filecol { + Patprog prog; /* group pattern */ + char *col; /* color string */ + Filecol next; /* next one */ +}; + +/* This describes a terminal string for a pattern. */ + +typedef struct patcol *Patcol; + +struct patcol { + Patprog prog; + Patprog pat; /* pattern for match */ + char *col; + Patcol next; +}; + /* This describes a terminal string for a filename extension. */ typedef struct extcol *Extcol; struct extcol { + Patprog prog; /* group pattern or NULL */ char *ext; /* the extension */ char *col; /* the terminal color string */ Extcol next; /* the next one in the list */ @@ -89,7 +111,8 @@ typedef struct listcols *Listcols; struct listcols { - char *cols[NUM_COLS]; /* strings for file types */ + Filecol files[NUM_COLS]; /* strings for file types */ + Patcol pats; /* strings for patterns */ Extcol exts; /* strings for extensions */ }; @@ -152,8 +175,33 @@ static char * getcoldef(Listcols c, char *s) { + Patprog gprog = NULL; + + if (*s == '(') { + char *p; + int l = 0; + + for (p = s + 1, l = 0; *p && (*p != ')' || l); p++) + if (*p == '\\' && p[1]) + p++; + else if (*p == '(') + l++; + else if (*p == ')') + l--; + + if (*p == ')') { + char sav = p[1]; + + p[1] = '\0'; + tokenize(s); + gprog = patcompile(s, 0, NULL); + p[1] =sav; + + s = p + 1; + } + } if (*s == '*') { - Extcol ec; + Extcol ec, eo; char *n, *p; /* This is for an extension. */ @@ -166,13 +214,50 @@ *s++ = '\0'; p = getcolval(s); ec = (Extcol) zhalloc(sizeof(*ec)); + ec->prog = gprog; ec->ext = n; ec->col = s; - ec->next = c->exts; - c->exts = ec; + ec->next = NULL; + if ((eo = c->exts)) { + while (eo->next) + eo = eo->next; + eo->next = ec; + } else + c->exts = ec; if (*p) *p++ = '\0'; return p; + } else if (*s == '=') { + char *p = ++s, *t; + Patprog prog; + + /* This is for a pattern. */ + + while (*s && *s != '=') + s++; + if (!*s) + return s; + *s++ = '\0'; + t = getcolval(s); + tokenize(p); + if ((prog = patcompile(p, 0, NULL))) { + Patcol pc, po; + + pc = (Patcol) zhalloc(sizeof(*pc)); + pc->prog = gprog; + pc->pat = prog; + pc->col = s; + pc->next = NULL; + if ((po = c->pats)) { + while (po->next) + po = po->next; + po->next = pc; + } else + c->pats = pc; + } + if (*t) + *t++ = '\0'; + return t; } else { char *n = s, *p, **nn; int i; @@ -185,17 +270,43 @@ return s; *s++ = '\0'; for (i = 0, nn = colnames; *nn; i++, nn++) - if (!strcmp(n ,*nn)) + if (!strcmp(n, *nn)) break; p = getcolval(s); - if (*nn) - c->cols[i] = s; + if (*nn) { + Filecol fc, fo; + + fc = (Filecol) zhalloc(sizeof(*fc)); + fc->prog = (i == COL_EC || i == COL_LC || i == COL_RC ? + NULL : gprog); + fc->col = s; + fc->next = NULL; + if ((fo = c->files[i])) { + while (fo->next) + fo = fo->next; + fo->next = fc; + } else + c->files[i] = fc; + } if (*p) *p++ = '\0'; return p; } } +static Filecol +filecol(char *col) +{ + Filecol fc; + + fc = (Filecol) zhalloc(sizeof(*fc)); + fc->prog = NULL; + fc->col = col; + fc->next = NULL; + + return fc; +} + /* Combined length of LC and RC, maximum length of capability strings. */ static int lr_caplen, max_caplen; @@ -211,17 +322,18 @@ if (!(s = getsparam("ZLS_COLORS")) && !(s = getsparam("ZLS_COLOURS"))) { for (i = 0; i < NUM_COLS; i++) - c->cols[i] = ""; + c->files[i] = filecol(""); + c->pats = NULL; c->exts = NULL; - if (!(c->cols[COL_MA] = tcstr[TCSTANDOUTBEG]) || - !c->cols[COL_MA][0]) - c->cols[COL_MA] = ""; - else - c->cols[COL_EC] = tcstr[TCSTANDOUTEND]; + if ((s = tcstr[TCSTANDOUTBEG]) && s[0]) { + c->files[COL_MA] = filecol(s); + c->files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]); + } else + c->files[COL_MA] = filecol(""); lr_caplen = 0; - if ((max_caplen = strlen(c->cols[COL_MA])) < - (l = strlen(c->cols[COL_EC]))) + if ((max_caplen = strlen(c->files[COL_MA]->col)) < + (l = strlen(c->files[COL_EC]->col))) max_caplen = l; return; } @@ -234,16 +346,17 @@ /* Use default values for those that aren't set explicitly. */ max_caplen = lr_caplen = 0; for (i = 0; i < NUM_COLS; i++) { - if (!c->cols[i]) - c->cols[i] = defcols[i]; - if (c->cols[i] && (l = strlen(c->cols[i])) > max_caplen) + if (!c->files[i] || !c->files[i]->col) + c->files[i] = filecol(defcols[i]); + if (c->files[i] && c->files[i]->col && + (l = strlen(c->files[i]->col)) > max_caplen) max_caplen = l; } - lr_caplen = strlen(c->cols[COL_LC]) + strlen(c->cols[COL_RC]); + lr_caplen = strlen(c->files[COL_LC]->col) + strlen(c->files[COL_RC]->col); /* Default for missing files. */ - if (!c->cols[COL_MI]) - c->cols[COL_MI] = c->cols[COL_FI]; + if (!c->files[COL_MI] || !c->files[COL_MI]->col) + c->files[COL_MI] = c->files[COL_FI]; return; } @@ -258,30 +371,36 @@ /* The last color used. */ -static int last_col = COL_NO; +static char *last_cap; static void zlrputs(Listcols c, char *cap) { - VARARR(char, buf, lr_caplen + max_caplen + 1); + if (strcmp(last_cap, cap)) { + VARARR(char, buf, lr_caplen + max_caplen + 1); - strcpy(buf, c->cols[COL_LC]); - strcat(buf, cap); - strcat(buf, c->cols[COL_RC]); + strcpy(buf, c->files[COL_LC]->col); + strcat(buf, cap); + strcat(buf, c->files[COL_RC]->col); - tputs(buf, 1, putshout); + tputs(buf, 1, putshout); + + strcpy(last_cap, cap); + } } static void -zcputs(Listcols c, int colour) +zcputs(Listcols c, char *group, int colour) { - if (colour != last_col - && (last_col < COL_NO - || strcmp(c->cols[last_col], c->cols[colour]))) { - zlrputs(c, c->cols[colour]); - last_col = colour; - } - return; + Filecol fc; + + for (fc = c->files[colour]; fc; fc = fc->next) + if (fc->col && + (!fc->prog || !group || pattry(fc->prog, group))) { + zlrputs(c, fc->col); + + return; + } } /* Turn off colouring. */ @@ -289,28 +408,52 @@ static void zcoff(void) { - if (mcolors.cols[COL_EC]) - tputs(mcolors.cols[COL_EC], 1, putshout); + if (mcolors.files[COL_EC] && mcolors.files[COL_EC]->col) + tputs(mcolors.files[COL_EC]->col, 1, putshout); else - zcputs(&mcolors, COL_NO); + zcputs(&mcolors, NULL, COL_NO); +} + +/* Get the terminal color string for the given match. */ + +static void +putmatchcol(Listcols c, char *group, char *n) +{ + Patcol pc; + + for (pc = c->pats; pc; pc = pc->next) + if ((!pc->prog || !group || pattry(pc->prog, group)) && + pattry(pc->pat, n)) { + zlrputs(c, pc->col); + + return; + } + + zcputs(c, group, COL_NO); } /* Get the terminal color string for the file with the given name and * file modes. */ static void -putcolstr(Listcols c, char *n, mode_t m) +putfilecol(Listcols c, char *group, char *n, mode_t m) { int colour; - Extcol e; + Extcol ec; + Patcol pc; + + for (ec = c->exts; ec; ec = ec->next) + if (strsfx(ec->ext, n) && + (!ec->prog || !group || pattry(ec->prog, group))) { + zlrputs(c, ec->col); - for (e = c->exts; e; e = e->next) - if (strsfx(e->ext, n)) { /* XXX: unoptimised if used */ - if (last_col < COL_NO - || strcmp(c->cols[last_col], e->col)) - zlrputs(c, e->col); + return; + } + for (pc = c->pats; pc; pc = pc->next) + if ((!pc->prog || !group || pattry(pc->prog, group)) && + pattry(pc->pat, n)) { + zlrputs(c, pc->col); - last_col = COL_NO - 1; return; } @@ -331,8 +474,7 @@ else colour = COL_FI; - zcputs(c, colour); - return; + zcputs(c, group, colour); } static void @@ -340,10 +482,10 @@ char *path, struct stat *buf) { Cmatch m; - int len, cc; + int len; if (!mp) { - zcputs(&mcolors, COL_MI); + zcputs(&mcolors, g->name, COL_MI); len = width - 2; while (len-- > 0) putc(' ', shout); @@ -367,10 +509,9 @@ mmtabp = mtab + mm; mgtabp = mgtab + mm; mmlen = mcols; - cc = COL_MA; + zcputs(&mcolors, g->name, COL_MA); } else - cc = COL_NO; - zcputs(&mcolors, cc); + putmatchcol(&mcolors, g->name, m->disp); printfmt(m->disp, 0, 1, 0); zcoff(); } else { @@ -400,11 +541,11 @@ mmtabp = mtab + mx + mm; mgtabp = mgtab + mx + mm; mmlen = width; - zcputs(&mcolors, COL_MA); + zcputs(&mcolors, g->name, COL_MA); } else if (buf) - putcolstr(&mcolors, path, buf->st_mode); + putfilecol(&mcolors, g->name, path, buf->st_mode); else - zcputs(&mcolors, COL_NO); + putmatchcol(&mcolors, g->name, (m->disp ? m->disp : m->str)); nicezputs((m->disp ? m->disp : m->str), shout); len = niceztrlen(m->disp ? m->disp : m->str); @@ -412,7 +553,7 @@ if (isset(LISTTYPES) && buf) { if (m->gnum != mselect) { zcoff(); - zcputs(&mcolors, COL_TC); + zcputs(&mcolors, g->name, COL_TC); } putc(file_type(buf->st_mode), shout); len++; @@ -420,14 +561,14 @@ if ((len = width - len - 2) > 0) { if (m->gnum != mselect) { zcoff(); - zcputs(&mcolors, COL_SP); + zcputs(&mcolors, g->name, COL_SP); } while (len-- > 0) putc(' ', shout); } zcoff(); if (!lastc) { - zcputs(&mcolors, COL_NO); + zcputs(&mcolors, g->name, COL_NO); fputs(" ", shout); zcoff(); } @@ -483,7 +624,8 @@ mcols = columns; mlines = listdat.nlines; } - last_col = COL_NO - 1; + last_cap = (char *) zhalloc(max_caplen + 1); + *last_cap = '\0'; if (!printlist(1, clprintm) || listdat.nlines >= lines) noselect = 1; diff -u olddoc/Zsh/mod_complist.yo Doc/Zsh/mod_complist.yo --- olddoc/Zsh/mod_complist.yo Wed Dec 1 16:02:57 1999 +++ Doc/Zsh/mod_complist.yo Thu Dec 2 09:48:43 1999 @@ -76,6 +76,24 @@ Apart from these strings, the var(name) may also be an asterisk (`tt(*)') followed by any string. The var(value) given for such a string will be used for all files whose name ends with the string. +The var(name) may also be a equal sign (`tt(=)') followed by a +pattern. The var(value) given for this pattern will be used for all +matches (not only filenames) that whose display string are matched by +the pattern. Definitions for both of these take precedence over the +values defined for file types and the form with the leading asterisk +takes precedence over the form with the leading equal sign. + +All three forms of var(name) may be preceded by a pattern in +parentheses. If such a pattern is given, the var(value) will be used +only for matches in groups whose names are matched by the pattern +given in the parentheses. E.g. `tt((g*)~m*=43)' says to highlight all +matches beginning with `tt(m)' in groups whose names begin with +`tt(g)' using the color code `tt(43)'. In case of the `tt(lc)', +`tt(rc)', and `tt(ec)' codes, the group pattern is ignored. + +Note also that all patterns are tried in the order in which they +appear in the parameter value until the first one matches which is +then used. When printing a match, the code prints the value of tt(lc), the value for the file-type or the last matching specification with a `tt(*)', -- Sven Wischnowsky wischnow@informatik.hu-berlin.de