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

PATCH: colouring in prompts done properly



...after all these years.  Not yet well tested but I'm sure you'll let
me know.

The best letters are taken, as usual, but I don't think %K for
bacKground is so bad.  It has the right hard, guttural, background-type
feel.  It's the last letter of Jackson PollocK, so you expect lots of
splatter all over the screen.  It makes everything Kolourful.  Enough
justification.

I'm happy to add prompt codes for specifying attributes in one
go, but the syntax becomes a little less obvious:  with the current set,
it's clear that individual attributes are being turned on or off.
I think with a %A or something it would have to be clear that this was
setting all display attributes in one go, overriding what was there
before.

Index: Doc/Zsh/prompt.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/prompt.yo,v
retrieving revision 1.11
diff -u -r1.11 prompt.yo
--- Doc/Zsh/prompt.yo	17 Feb 2008 18:15:10 -0000	1.11
+++ Doc/Zsh/prompt.yo	9 May 2008 17:25:14 -0000
@@ -183,6 +183,20 @@
 item(tt(%S) LPAR()tt(%s)RPAR())(
 Start (stop) standout mode.
 )
+item(tt(%F) LPAR()tt(%f)RPAR())(
+Start (stop) using a different foreground colour, if supported
+by the terminal.  The colour may be specified two ways: either
+as a numeric argument, as normal, or by a sequence in braces
+following the tt(%F), for example tt(%F{red}).  In the latter case
+the values allowed are as described for the tt(fg) tt(zle_highlight)
+attribute;
+ifzman(see em(Character Highlighting) in zmanref(zshzle))\
+ifnzman(noderef(Character Highlighting)).
+)
+item(tt(%K) LPAR()tt(%k)RPAR())(
+Start (stop) using a different bacKground colour.  The syntax is
+identical to that for tt(%F) and tt(%f).
+)
 item(tt(%{)...tt(%}))(
 Include a string as a literal escape sequence.
 The string within the braces should not change the cursor
Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.71
diff -u -r1.71 zle.yo
--- Doc/Zsh/zle.yo	6 May 2008 21:33:10 -0000	1.71
+++ Doc/Zsh/zle.yo	9 May 2008 17:25:17 -0000
@@ -2182,9 +2182,10 @@
 facilities to test the support, hence the user should decide based on the
 terminal type.  Most terminals support the colours tt(black), tt(red),
 tt(green), tt(yellow), tt(blue), tt(magenta), tt(cyan) and tt(white),
-which can be set by name.  Abbreviations are allowed; tt(b) or tt(bl)
-selects black.  Some terminals may generate additional colours if the
-tt(bold) attribute is also present.
+which can be set by name.  In addition. tt(default) may be used to
+set the terminal's default foreground colour.  Abbreviations are allowed;
+tt(b) or tt(bl) selects black.  Some terminals may generate additional
+colours if the tt(bold) attribute is also present.
 
 On recent terminals and on systems with an up-to-date terminal database the
 number of colours supported may be tested by with the command `tt(echotc
Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.83
diff -u -r1.83 init.c
--- Src/init.c	29 Apr 2008 17:19:26 -0000	1.83
+++ Src/init.c	9 May 2008 17:25:18 -0000
@@ -909,6 +909,9 @@
     for (i = 0; i < 10; i++)
 	if (close_fds[i])
 	    close(i);
+
+    /* Colour sequences for outputting colours in prompts and zle */
+    set_default_colour_sequences();
 }
 
 /* Initialize signal handling */
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.47
diff -u -r1.47 prompt.c
--- Src/prompt.c	22 Apr 2008 15:08:12 -0000	1.47
+++ Src/prompt.c	9 May 2008 17:25:19 -0000
@@ -423,38 +423,78 @@
 	    case 'S':
 		txtchangeset(TXTSTANDOUT, TXTNOSTANDOUT);
 		txtset(TXTSTANDOUT);
-		tsetcap(TCSTANDOUTBEG, 1);
+		tsetcap(TCSTANDOUTBEG, TSC_PROMPT);
 		break;
 	    case 's':
 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
-		txtset(TXTDIRTY);
 		txtunset(TXTSTANDOUT);
-		tsetcap(TCSTANDOUTEND, 1);
+		tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY);
 		break;
 	    case 'B':
 		txtchangeset(TXTBOLDFACE, TXTNOBOLDFACE);
-		txtset(TXTDIRTY);
 		txtset(TXTBOLDFACE);
-		tsetcap(TCBOLDFACEBEG, 1);
+		tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY);
 		break;
 	    case 'b':
 		txtchangeset(TXTNOBOLDFACE, TXTBOLDFACE);
 		txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT);
 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
-		txtset(TXTDIRTY);
 		txtunset(TXTBOLDFACE);
-		tsetcap(TCALLATTRSOFF, 1);
+		tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
 		break;
 	    case 'U':
 		txtchangeset(TXTUNDERLINE, TXTNOUNDERLINE);
 		txtset(TXTUNDERLINE);
-		tsetcap(TCUNDERLINEBEG, 1);
+		tsetcap(TCUNDERLINEBEG, TSC_PROMPT);
 		break;
 	    case 'u':
 		txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE);
-		txtset(TXTDIRTY);
 		txtunset(TXTUNDERLINE);
-		tsetcap(TCUNDERLINEEND, 1);
+		tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY);
+		break;
+	    case 'F':
+		if (fm[1] == '{') {
+		    fm += 2;
+		    arg = match_colour((const char **)&fm, 1, 0);
+		    if (*fm != '}')
+			fm--;
+		} else
+		    arg = match_colour(NULL, 1, arg);
+		if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) {
+		    txtchangeset(arg & TXT_ATTR_FG_ON_MASK,
+				 TXTNOFGCOLOUR);
+		    txtset(arg & TXT_ATTR_FG_ON_MASK);
+		    set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT);
+		    break;
+		}
+		/* else FALLTHROUGH */
+		break;
+	    case 'f':
+		txtchangeset(TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK);
+		txtunset(TXT_ATTR_FG_ON_MASK);
+		set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT);
+		break;
+	    case 'K':
+		if (fm[1] == '{') {
+		    fm += 2;
+		    arg = match_colour((const char **)&fm, 0, 0);
+		    if (*fm != '}')
+			fm--;
+		} else
+		    arg = match_colour(NULL, 0, arg);
+		if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) {
+		    txtchangeset(arg & TXT_ATTR_BG_ON_MASK,
+				 TXTNOBGCOLOUR);
+		    txtset(arg & TXT_ATTR_BG_ON_MASK);
+		    set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT);
+		    break;
+		}
+		/* else FALLTHROUGH */
+		break;
+	    case 'k':
+		txtchangeset(TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK);
+		txtunset(TXT_ATTR_BG_ON_MASK);
+		set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT);
 		break;
 	    case '[':
 		if (idigit(*++fm))
@@ -610,7 +650,7 @@
 		    stradd(psvar[arg - 1]);
 		break;
 	    case 'E':
-                tsetcap(TCCLEAREOL, 1);
+                tsetcap(TCCLEAREOL, TSC_PROMPT);
 		break;
 	    case '^':
 		if (cmdsp) {
@@ -816,18 +856,19 @@
 
 /**/
 mod_export void
-tsetcap(int cap, int flag)
+tsetcap(int cap, int flags)
 {
     if (tccan(cap) && !isset(SINGLELINEZLE) &&
         !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
-	switch(flag) {
-	case -1:
+	switch (flags & TSC_OUTPUT_MASK) {
+	case TSC_RAW:
 	    tputs(tcstr[cap], 1, putraw);
 	    break;
 	case 0:
+	default:
 	    tputs(tcstr[cap], 1, putshout);
 	    break;
-	case 1:
+	case TSC_PROMPT:
 	    if (!dontcount) {
 		addbufspc(1);
 		*bp++ = Inpar;
@@ -850,14 +891,14 @@
 	    break;
 	}
 
-	if (txtisset(TXTDIRTY)) {
-	    txtunset(TXTDIRTY);
+	if (flags & TSC_DIRTY) {
+	    flags &= ~TSC_DIRTY;
 	    if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
-		tsetcap(TCBOLDFACEBEG, flag);
+		tsetcap(TCBOLDFACEBEG, flags);
 	    if (txtisset(TXTSTANDOUT))
-		tsetcap(TCSTANDOUTBEG, flag);
+		tsetcap(TCSTANDOUTBEG, flags);
 	    if (txtisset(TXTUNDERLINE))
-		tsetcap(TCUNDERLINEBEG, flag);
+		tsetcap(TCUNDERLINEBEG, flags);
 	}
     }
 }
@@ -1361,3 +1402,481 @@
     } else
 	cmdsp--;
 }
+
+
+/*****************************************************************************
+ * Utilities dealing with colour and other forms of highlighting.
+ *
+ * These are shared by prompts and by zle, so it's easiest to have them
+ * in the main shell.
+ *****************************************************************************/
+
+/* Defines standard ANSI colour names in index order */
+static const char *ansi_colours[] = {
+    "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
+    "default", NULL
+};
+
+/* Defines the available types of highlighting */
+struct highlight {
+    const char *name;
+    int mask_on;
+    int mask_off;
+};
+
+static const struct highlight highlights[] = {
+    { "none", 0, TXT_ATTR_ON_MASK },
+    { "bold", TXTBOLDFACE, 0 },
+    { "standout", TXTSTANDOUT, 0 },
+    { "underline", TXTUNDERLINE, 0 },
+    { NULL, 0, 0 }
+};
+
+/*
+ * Return index of ANSI colour for which *teststrp is an abbreviation.
+ * Any non-alphabetic character ends the abbreviation.
+ * 8 is the special value for default (note this is *not* the
+ * right sequence for default which is typically 9).
+ * -1 is failure.
+ */
+
+static int
+match_named_colour(const char **teststrp)
+{
+    const char *teststr = *teststrp, *end, **cptr;
+    int len;
+
+    for (end = teststr; ialpha(*end); end++)
+	;
+    len = end - teststr;
+    *teststrp = end;
+
+    for (cptr = ansi_colours; *cptr; cptr++) {
+	if (!strncmp(teststr, *cptr, len))
+	    return cptr - ansi_colours;
+    }
+
+    return -1;
+}
+
+/*
+ * Match just the colour part of a highlight specification.
+ * If teststrp is NULL, use the already parsed numeric colour.
+ * Return the attributes to set in the attribute variable.
+ * Return -1 for out of range.  Does not check the character
+ * following the colour specification.
+ */
+
+/**/
+static int
+match_colour(const char **teststrp, int is_fg, int colour)
+{
+    int shft, on, named, tc;
+
+    if (teststrp) {
+	if ((named = ialpha(**teststrp))) {
+	    colour = match_named_colour(teststrp);
+	    if (colour == 8) {
+		/* default */
+		return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR;
+	    }
+	}
+	else
+	    colour = (int)zstrtol(*teststrp, (char **)teststrp, 10);
+    }
+    if (colour < 0 || colour >= 256)
+	return -1;
+    if (is_fg) {
+	shft = TXT_ATTR_FG_COL_SHIFT;
+	on = TXTFGCOLOUR;
+	tc = TCFGCOLOUR;
+    } else {
+	shft = TXT_ATTR_BG_COL_SHIFT;
+	on = TXTBGCOLOUR;
+	tc = TCBGCOLOUR;
+    }
+    /*
+     * Try termcap for numbered characters if posible.
+     * Don't for named characters, since our best bet
+     * of getting the names right is with ANSI sequences.
+     */
+    if (!named && tccan(tc)) {
+	if (tccolours >= 0 && colour >= tccolours) {
+	    /*
+	     * Out of range of termcap colours.
+	     * Can we assume ANSI colours work?
+	     */
+	    if (colour > 7)
+		return -1; /* No. */
+	} else {
+	    /*
+	     * We can handle termcap colours and the number
+	     * is in range, so use termcap.
+	     */
+	    on |= is_fg ? TXT_ATTR_FG_TERMCAP :
+		TXT_ATTR_BG_TERMCAP;
+	}
+    }
+    return on | (colour << shft);
+}
+
+/*
+ * Match a set of highlights in the given teststr.
+ * Set *on_var to reflect the values found.
+ */
+
+/**/
+mod_export void
+match_highlight(const char *teststr, int *on_var)
+{
+    int found = 1;
+
+    *on_var = 0;
+    while (found && *teststr) {
+	const struct highlight *hl;
+
+	found = 0;
+	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
+	    int is_fg = (teststr[0] == 'f'), atr;
+
+	    teststr += 3;
+	    atr = match_colour(&teststr, is_fg, 0);
+	    if (*teststr == ',')
+		teststr++;
+	    else if (*teststr)
+		break;
+	    found = 1;
+	    /* skip out of range colours but keep scanning attributes */
+	    if (atr >= 0)
+		*on_var |= atr;
+	} else {
+	    for (hl = highlights; hl->name; hl++) {
+		if (strpfx(hl->name, teststr)) {
+		    const char *val = teststr + strlen(hl->name);
+
+		    if (*val == ',')
+			val++;
+		    else if (*val)
+			break;
+
+		    *on_var |= hl->mask_on;
+		    *on_var &= ~hl->mask_off;
+		    teststr = val;
+		    found = 1;
+		}
+	    }
+	}
+    }
+}
+
+/*
+ * Count or output a string for colour information: used
+ * by output_highlight().
+ */
+
+static int
+output_colour(int colour, int fg_bg, int use_tc, char *buf)
+{
+    int atrlen = 3, len;
+    char *ptr = buf;
+    if (buf) {
+	strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg=");
+	ptr += 3;
+    }
+    /* colour should only be > 7 if using termcap but let's be safe */
+    if (use_tc || colour > 7) {
+	char digbuf[DIGBUFSIZE];
+	sprintf(digbuf, "%d", colour);
+	len = strlen(digbuf);
+	atrlen += len;
+	if (buf)
+	    strcpy(ptr, digbuf);
+    } else {
+	len = strlen(ansi_colours[colour]);
+	atrlen += len;
+	if (buf)
+	    strcpy(ptr, ansi_colours[colour]);
+    }
+
+    return atrlen;
+}
+
+/*
+ * Count the length needed for outputting highlighting information
+ * as a string based on the bits for the attributes.
+ *
+ * If buf is not NULL, output the strings into the buffer, too.
+ * As conventional with strings, the allocated length should be
+ * at least the returned value plus 1 for the NUL byte.
+ */
+
+/**/
+mod_export int
+output_highlight(int atr, char *buf)
+{
+    const struct highlight *hp;
+    int atrlen = 0, len;
+    char *ptr = buf;
+
+    if (atr & TXTFGCOLOUR) {
+	len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL),
+			    COL_SEQ_FG,
+			    (atr & TXT_ATTR_FG_TERMCAP),
+			    ptr);
+	atrlen += len;
+	if (buf)
+	    ptr += len;
+    }
+    if (atr & TXTBGCOLOUR) {
+	if (atrlen) {
+	    atrlen++;
+	    if (buf) {
+		strcpy(ptr, ",");
+		ptr++;
+	    }
+	}
+	len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL),
+			    COL_SEQ_BG,
+			    (atr & TXT_ATTR_BG_TERMCAP),
+			    ptr);
+	atrlen += len;
+	if (buf)
+	    ptr += len;
+    }
+    for (hp = highlights; hp->name; hp++) {
+	if (hp->mask_on & atr) {
+	    if (atrlen) {
+		atrlen++;
+		if (buf) {
+		    strcpy(ptr, ",");
+		    ptr++;
+		}
+	    }
+	    len = strlen(hp->name);
+	    atrlen += len;
+	    if (buf) {
+		strcpy(ptr, hp->name);
+		ptr += len;
+	    }
+	}
+    }
+
+    if (atrlen == 0) {
+	if (buf)
+	    strcpy(ptr, "none");
+	return 4;
+    }
+    return atrlen;
+}
+
+/* Structure and array for holding special colour terminal sequences */
+
+/* Start of escape sequence for foreground colour */
+#define TC_COL_FG_START	"\033[3"
+/* End of escape sequence for foreground colour */
+#define TC_COL_FG_END	"m"
+/* Code to reset foreground colour */
+#define TC_COL_FG_DEFAULT	"9"
+
+/* Start of escape sequence for background colour */
+#define TC_COL_BG_START	"\033[4"
+/* End of escape sequence for background colour */
+#define TC_COL_BG_END	"m"
+/* Code to reset background colour */
+#define TC_COL_BG_DEFAULT	"9"
+
+struct colour_sequences {
+    char *start;		/* Escape sequence start */
+    char *end;			/* Escape sequence terminator */
+    char *def;			/* Code to reset default colour */
+};
+struct colour_sequences fg_bg_sequences[2];
+
+/*
+ * We need a buffer for colour sequence compostion.  It may
+ * vary depending on the sequences set.  However, it's inefficient
+ * allocating it separately every time we send a colour sequence,
+ * so do it once per refresh.
+ */
+static char *colseq_buf;
+
+/**/
+void
+set_default_colour_sequences(void)
+{
+    fg_bg_sequences[COL_SEQ_FG].start = ztrdup(TC_COL_FG_START);
+    fg_bg_sequences[COL_SEQ_FG].end = ztrdup(TC_COL_FG_END);
+    fg_bg_sequences[COL_SEQ_FG].def = ztrdup(TC_COL_FG_DEFAULT);
+
+    fg_bg_sequences[COL_SEQ_BG].start = ztrdup(TC_COL_BG_START);
+    fg_bg_sequences[COL_SEQ_BG].end = ztrdup(TC_COL_BG_END);
+    fg_bg_sequences[COL_SEQ_BG].def = ztrdup(TC_COL_BG_DEFAULT);
+}
+
+static void
+set_colour_code(char *str, char **var)
+{
+    char *keyseq;
+    int len;
+
+    zsfree(*var);
+    keyseq = getkeystring(str, &len, GETKEYS_BINDKEY, NULL);
+    *var = metafy(keyseq, len, META_DUP);
+}
+
+/* Allocate buffer for colour code composition */
+
+/**/
+mod_export void
+allocate_colour_buffer(void)
+{
+    char **atrs = getaparam("zle_highlight");
+    int lenfg, lenbg, len;
+
+    if (atrs) {
+	for (; *atrs; atrs++) {
+	    if (strpfx("fg_start_code:", *atrs)) {
+		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_FG].start);
+	    } else if (strpfx("fg_default_code:", *atrs)) {
+		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_FG].def);
+	    } else if (strpfx("fg_end_code:", *atrs)) {
+		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_FG].end);
+	    } else if (strpfx("bg_start_code:", *atrs)) {
+		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_BG].start);
+	    } else if (strpfx("bg_default_code:", *atrs)) {
+		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_BG].def);
+	    } else if (strpfx("bg_end_code:", *atrs)) {
+		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_BG].end);
+	    }
+	}
+    }
+
+    lenfg = strlen(fg_bg_sequences[COL_SEQ_FG].def);
+    /* always need 1 character for non-default code */
+    if (lenfg < 1)
+	lenfg = 1;
+    lenfg += strlen(fg_bg_sequences[COL_SEQ_FG].start) +
+	strlen(fg_bg_sequences[COL_SEQ_FG].end);
+
+    lenbg = strlen(fg_bg_sequences[COL_SEQ_BG].def);
+    /* always need 1 character for non-default code */
+    if (lenbg < 1)
+	lenbg = 1;
+    lenbg += strlen(fg_bg_sequences[COL_SEQ_BG].start) +
+	strlen(fg_bg_sequences[COL_SEQ_BG].end);
+
+    len = lenfg > lenbg ? lenfg : lenbg;
+    colseq_buf = (char *)zalloc(len+1);
+}
+
+/* Free the colour buffer previously allocated. */
+
+/**/
+mod_export void
+free_colour_buffer(void)
+{
+    DPUTS(!colseq_buf, "Freeing colour sequence buffer without alloc");
+    /* Free buffer for colour code composition */
+    free(colseq_buf);
+    colseq_buf = NULL;
+}
+
+/*
+ * Handle outputting of a colour for prompts or zle.
+ * colour is the numeric colour, 0 to 255 (or less if termcap
+ * says fewer are supported).
+ * fg_bg indicates if we're changing the foreground or background.
+ * tc indicates the termcap code to use, if appropriate.
+ * def indicates if we're resetting the default colour.
+ * use_termcap indicates if we should use termcap to output colours.
+ * flags is either 0 or TSC_PROMPT.
+ */
+
+/**/
+mod_export void
+set_colour_attribute(int atr, int fg_bg, int flags)
+{
+    char *ptr;
+    int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0;
+    int colour, tc, def, use_termcap;
+
+    if (fg_bg == COL_SEQ_FG) {
+	colour = txtchangeget(atr, TXT_ATTR_FG_COL);
+	tc = TCFGCOLOUR;
+	def = txtchangeisset(atr, TXTNOFGCOLOUR);
+	use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP);
+    } else {
+	colour = txtchangeget(atr, TXT_ATTR_BG_COL);
+	tc = TCBGCOLOUR;
+	def = txtchangeisset(atr, TXTNOBGCOLOUR);
+	use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP);
+    }
+
+    /*
+     * If we're not restoring the default, and either have a
+     * colour value that is too large for ANSI, or have been told
+     * to use the termcap sequence, try to use the termcap sequence.
+     *
+     * We have already sanitised the values we allow from the
+     * highlighting variables, so much of this shouldn't be
+     * necessary at this point, but we might as well be safe.
+     */
+    if (!def && (colour > 7 || use_termcap)) {
+	/*
+	 * We can if it's available, and either we couldn't get
+	 * the maximum number of colours, or the colour is in range.
+	 */
+	if (tccan(tc) && (tccolours < 0 || colour < tccolours))
+	{
+	    if (is_prompt)
+	    {
+		if (!dontcount) {
+		    addbufspc(1);
+		    *bp++ = Inpar;
+		}
+		tputs(tgoto(tcstr[tc], colour, colour), 1, putstr);
+		if (!dontcount) {
+		    addbufspc(1);
+		    *bp++ = Outpar;
+		}
+	    } else {
+		tputs(tgoto(tcstr[tc], colour, colour), 1, putshout);
+	    }
+	}
+	/* for 0 to 7 assume standard ANSI works, otherwise it won't. */
+	if (colour > 7)
+	    return;
+    }
+
+    if ((do_free = (colseq_buf == NULL))) {
+	/* This can happen when moving the cursor in trashzle() */
+	allocate_colour_buffer();
+    }
+
+    strcpy(colseq_buf, fg_bg_sequences[fg_bg].start);
+
+    ptr = colseq_buf + strlen(colseq_buf);
+    if (def) {
+	strcpy(ptr, fg_bg_sequences[fg_bg].def);
+	while (*ptr)
+	    ptr++;
+    } else
+	*ptr++ = colour + '0';
+    strcpy(ptr, fg_bg_sequences[fg_bg].end);
+
+    if (is_prompt) {
+	if (!dontcount) {
+	    addbufspc(1);
+	    *bp++ = Inpar;
+	}
+	tputs(colseq_buf, 1, putstr);
+	if (!dontcount) {
+	    addbufspc(1);
+	    *bp++ = Outpar;
+	}
+    } else
+	tputs(colseq_buf, 1, putshout);
+
+    if (do_free)
+	free_colour_buffer();
+}
Index: Src/watch.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/watch.c,v
retrieving revision 1.5
diff -u -r1.5 watch.c
--- Src/watch.c	4 Apr 2005 09:59:05 -0000	1.5
+++ Src/watch.c	9 May 2008 17:25:19 -0000
@@ -338,31 +338,27 @@
 		    break;
 		case 'S':
 		    txtset(TXTSTANDOUT);
-		    tsetcap(TCSTANDOUTBEG, -1);
+		    tsetcap(TCSTANDOUTBEG, TSC_RAW);
 		    break;
 		case 's':
-		    txtset(TXTDIRTY);
 		    txtunset(TXTSTANDOUT);
-		    tsetcap(TCSTANDOUTEND, -1);
+		    tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY);
 		    break;
 		case 'B':
-		    txtset(TXTDIRTY);
 		    txtset(TXTBOLDFACE);
-		    tsetcap(TCBOLDFACEBEG, -1);
+		    tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY);
 		    break;
 		case 'b':
-		    txtset(TXTDIRTY);
 		    txtunset(TXTBOLDFACE);
-		    tsetcap(TCALLATTRSOFF, -1);
+		    tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY);
 		    break;
 		case 'U':
 		    txtset(TXTUNDERLINE);
-		    tsetcap(TCUNDERLINEBEG, -1);
+		    tsetcap(TCUNDERLINEBEG, TSC_RAW);
 		    break;
 		case 'u':
-		    txtset(TXTDIRTY);
 		    txtunset(TXTUNDERLINE);
-		    tsetcap(TCUNDERLINEEND, -1);
+		    tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY);
 		    break;
 		default:
 		    putchar('%');
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.131
diff -u -r1.131 zsh.h
--- Src/zsh.h	8 May 2008 12:07:06 -0000	1.131
+++ Src/zsh.h	9 May 2008 17:25:20 -0000
@@ -1975,7 +1975,6 @@
 #define TXTUNDERLINE  0x0004
 #define TXTFGCOLOUR   0x0008
 #define TXTBGCOLOUR   0x0010
-#define TXTDIRTY      0x0020
 
 #define TXT_ATTR_ON_MASK   0x001F
 
@@ -1983,15 +1982,15 @@
 #define txtset(X)    (txtattrmask |= (X))
 #define txtunset(X)  (txtattrmask &= ~(X))
 
-#define TXTNOBOLDFACE	0x0040
-#define TXTNOSTANDOUT	0x0080
-#define TXTNOUNDERLINE	0x0100
-#define TXTNOFGCOLOUR	0x0200
-#define TXTNOBGCOLOUR	0x0400
+#define TXTNOBOLDFACE	0x0020
+#define TXTNOSTANDOUT	0x0040
+#define TXTNOUNDERLINE	0x0080
+#define TXTNOFGCOLOUR	0x0100
+#define TXTNOBGCOLOUR	0x0200
 
-#define TXT_ATTR_OFF_MASK  0x07C0
+#define TXT_ATTR_OFF_MASK  0x03E0
 /* Bits to shift off right to get on */
-#define TXT_ATTR_OFF_ON_SHIFT 6
+#define TXT_ATTR_OFF_ON_SHIFT 5
 #define TXT_ATTR_OFF_FROM_ON(attr)	\
     (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT)
 #define TXT_ATTR_ON_FROM_OFF(attr)	\
@@ -2000,7 +1999,7 @@
  * Indicates to zle_refresh.c that the character entry is an
  * index into the list of multiword symbols.
  */
-#define TXT_MULTIWORD_MASK  0x0800
+#define TXT_MULTIWORD_MASK  0x0400
 
 /* Mask for colour to use in foreground */
 #define TXT_ATTR_FG_COL_MASK     0x000FF000
@@ -2021,16 +2020,45 @@
     (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\
      TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP)
 
+/* Mask out everything to do with setting a foreground colour */
+#define TXT_ATTR_FG_ON_MASK \
+    (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP)
+
+/* Mask out everything to do with setting a background colour */
+#define TXT_ATTR_BG_ON_MASK \
+    (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP)
+
 /* Mask out everything to do with activating colours */
 #define TXT_ATTR_COLOUR_ON_MASK			\
-    (TXTFGCOLOUR|TXTBGCOLOUR|			\
-     TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK| \
-     TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP)
+    (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK)
 
 #define txtchangeisset(T,X)	((T) & (X))
 #define txtchangeget(T,A)	(((T) & A ## _MASK) >> A ## _SHIFT)
 #define txtchangeset(X, Y)	(txtchange |= (X), txtchange &= ~(Y))
 
+/*
+ * For outputting sequences to change colour: specify foreground
+ * or background.
+ */
+#define COL_SEQ_FG	(0)
+#define COL_SEQ_BG	(1)
+#define COL_SEQ_COUNT	(2)
+
+/*
+ * Flags to testcap() and set_colour_attribute (which currently only
+ * handles TSC_PROMPT).
+ */
+enum {
+    /* Raw output: use stdout rather than shout */
+    TSC_RAW = 0x0001,
+    /* Output to current prompt buffer: only used when assembling prompt */
+    TSC_PROMPT = 0x0002,
+    /* Mask to get the output mode */
+    TSC_OUTPUT_MASK = 0x0003,
+    /* Change needs reset of other attributes */
+    TSC_DIRTY = 0x0004
+};
+
 /****************************************/
 /* Definitions for the %_ prompt escape */
 /****************************************/
Index: Src/Zle/zle_refresh.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_refresh.c,v
retrieving revision 1.68
diff -u -r1.68 zle_refresh.c
--- Src/Zle/zle_refresh.c	6 May 2008 21:33:10 -0000	1.68
+++ Src/Zle/zle_refresh.c	9 May 2008 17:25:21 -0000
@@ -327,232 +327,6 @@
 #define ZR_START_ELLIPSIS_SIZE	\
     ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])))
 
-/* Defines standard ANSI colour names in index order */
-static const char *ansi_colours[] = {
-    "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
-    NULL
-};
-
-/* Defines the available types of highlighting */
-struct highlight {
-    const char *name;
-    int mask_on;
-    int mask_off;
-};
-
-static const struct highlight highlights[] = {
-    { "none", 0, TXT_ATTR_ON_MASK },
-    { "bold", TXTBOLDFACE, 0 },
-    { "standout", TXTSTANDOUT, 0 },
-    { "underline", TXTUNDERLINE, 0 },
-    { NULL, 0, 0 }
-};
-
-/* Structure and array for holding special colour terminal sequences */
-
-/* Start of escape sequence for foreground colour */
-#define TC_COL_FG_START	"\033[3"
-/* End of escape sequence for foreground colour */
-#define TC_COL_FG_END	"m"
-/* Code to reset foreground colour */
-#define TC_COL_FG_DEFAULT	"9"
-
-/* Start of escape sequence for background colour */
-#define TC_COL_BG_START	"\033[4"
-/* End of escape sequence for background colour */
-#define TC_COL_BG_END	"m"
-/* Code to reset background colour */
-#define TC_COL_BG_DEFAULT	"9"
-
-struct colour_sequences {
-    char *start;		/* Escape sequence start */
-    char *end;			/* Escape sequence terminator */
-    char *def;			/* Code to reset default colour */
-};
-struct colour_sequences fg_bg_sequences[2];
-
-#define COL_SEQ_FG	(0)
-#define COL_SEQ_BG	(1)
-#define COL_SEQ_COUNT	(2)
-
-/*
- * We need a buffer for colour sequence compostion.  It may
- * vary depending on the sequences set.  However, it's inefficient
- * allocating it separately every time we send a colour sequence,
- * so do it once per refresh.
- */
-static char *colseq_buf;
-
-static void
-set_default_colour_sequences(void)
-{
-    fg_bg_sequences[COL_SEQ_FG].start = ztrdup(TC_COL_FG_START);
-    fg_bg_sequences[COL_SEQ_FG].end = ztrdup(TC_COL_FG_END);
-    fg_bg_sequences[COL_SEQ_FG].def = ztrdup(TC_COL_FG_DEFAULT);
-
-    fg_bg_sequences[COL_SEQ_BG].start = ztrdup(TC_COL_BG_START);
-    fg_bg_sequences[COL_SEQ_BG].end = ztrdup(TC_COL_BG_END);
-    fg_bg_sequences[COL_SEQ_BG].def = ztrdup(TC_COL_BG_DEFAULT);
-}
-
-static void
-free_colour_sequences(void)
-{
-    int i;
-
-    for (i = 0; i < COL_SEQ_COUNT; i++) {
-	zsfree(fg_bg_sequences[i].start);
-	zsfree(fg_bg_sequences[i].end);
-	zsfree(fg_bg_sequences[i].def);
-    }
-}
-
-/*
- * Return index of ANSI colour for which *teststrp is an abbreviation.
- * Any non-alphabetic character ends the abbreviation.
- */
-
-static int
-match_colour(const char **teststrp)
-{
-    const char *teststr = *teststrp, *end, **cptr;
-    int len;
-
-    for (end = teststr; ialpha(*end); end++)
-	;
-    len = end - teststr;
-    *teststrp = end;
-
-    for (cptr = ansi_colours; *cptr; cptr++) {
-	if (!strncmp(teststr, *cptr, len))
-	    return cptr - ansi_colours;
-    }
-
-    return -1;
-}
-
-static void
-set_colour_code(char *str, char **var)
-{
-    char *keyseq;
-    int len;
-
-    zsfree(*var);
-    keyseq = getkeystring(str, &len, GETKEYS_BINDKEY, NULL);
-    *var = metafy(keyseq, len, META_DUP);
-}
-
-
-/*
- * Match a set of highlights in the given teststr.
- * Set *on_var to reflect the values found.
- */
-
-static void
-match_highlight(const char *teststr, int *on_var)
-{
-    int found = 1;
-
-    *on_var = 0;
-    while (found && *teststr) {
-	const struct highlight *hl;
-
-	found = 0;
-	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
-	    int is_fg = (teststr[0] == 'f');
-	    int colour, shft, on, named, tc;
-
-	    teststr += 3;
-	    if ((named = ialpha(*teststr)))
-		colour = match_colour(&teststr);
-	    else
-		colour = (int)zstrtol(teststr, (char **)&teststr, 10);
-	    if (*teststr == ',')
-		teststr++;
-	    else if (*teststr)
-		break;
-	    found = 1;
-	    /* skip out of range colours but keep scanning attributes */
-	    if (colour < 0 || colour >= 256)
-		continue;
-	    if (is_fg) {
-		shft = TXT_ATTR_FG_COL_SHIFT;
-		on = TXTFGCOLOUR;
-		tc = TCFGCOLOUR;
-	    } else {
-		shft = TXT_ATTR_BG_COL_SHIFT;
-		on = TXTBGCOLOUR;
-		tc = TCBGCOLOUR;
-	    }
-	    /*
-	     * Try termcap for numbered characters if posible.
-	     * Don't for named characters, since our best bet
-	     * of getting the names right is with ANSI sequences.
-	     */
-	    if (!named && tccan(tc)) {
-		if (tccolours >= 0 && colour >= tccolours) {
-		    /*
-		     * Out of range of termcap colours.
-		     * Can we assume ANSI colours work?
-		     */
-		    if (colour > 7)
-			continue; /* No. */
-		} else {
-		    /*
-		     * We can handle termcap colours and the number
-		     * is in range, so use termcap.
-		     */
-		    *on_var |= is_fg ? TXT_ATTR_FG_TERMCAP :
-			TXT_ATTR_BG_TERMCAP;
-		}
-	    }
-	    *on_var |= on | (colour << shft);
-	} else {
-	    for (hl = highlights; hl->name; hl++) {
-		if (strpfx(hl->name, teststr)) {
-		    const char *val = teststr + strlen(hl->name);
-
-		    if (*val == ',')
-			val++;
-		    else if (*val)
-			break;
-
-		    *on_var |= hl->mask_on;
-		    *on_var &= ~hl->mask_off;
-		    teststr = val;
-		    found = 1;
-		}
-	    }
-	}
-    }
-}
-
-
-/* Allocate buffer for colour code composition */
-
-static void
-set_colseq_buf(void)
-{
-    int lenfg, lenbg, len;
-
-    lenfg = strlen(fg_bg_sequences[COL_SEQ_FG].def);
-    /* always need 1 character for non-default code */
-    if (lenfg < 1)
-	lenfg = 1;
-    lenfg += strlen(fg_bg_sequences[COL_SEQ_FG].start) +
-	strlen(fg_bg_sequences[COL_SEQ_FG].end);
-
-    lenbg = strlen(fg_bg_sequences[COL_SEQ_BG].def);
-    /* always need 1 character for non-default code */
-    if (lenbg < 1)
-	lenbg = 1;
-    lenbg += strlen(fg_bg_sequences[COL_SEQ_BG].start) +
-	strlen(fg_bg_sequences[COL_SEQ_BG].end);
-
-    len = lenfg > lenbg ? lenfg : lenbg;
-    colseq_buf = (char *)zalloc(len+1);
-}
-
 /*
  * Parse the variable zle_highlight to decide how to highlight characters
  * and regions.  Set defaults for anything not explicitly covered.
@@ -599,18 +373,6 @@
 	    } else if (strpfx("isearch:", *atrs)) {
 		match_highlight(*atrs + 8, &(region_highlights[1].atr));
 		isearch_atr_on_set = 1;
-	    } else if (strpfx("fg_start_code:", *atrs)) {
-		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_FG].start);
-	    } else if (strpfx("fg_default_code:", *atrs)) {
-		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_FG].def);
-	    } else if (strpfx("fg_end_code:", *atrs)) {
-		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_FG].end);
-	    } else if (strpfx("bg_start_code:", *atrs)) {
-		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_BG].start);
-	    } else if (strpfx("bg_default_code:", *atrs)) {
-		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_BG].def);
-	    } else if (strpfx("bg_end_code:", *atrs)) {
-		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_BG].end);
 	    }
 	}
     }
@@ -623,7 +385,7 @@
     if (!isearch_atr_on_set)
 	region_highlights[1].atr = TXTUNDERLINE;
 
-    set_colseq_buf();
+    allocate_colour_buffer();
 }
 
 
@@ -631,10 +393,7 @@
 static void
 zle_free_highlight(void)
 {
-    DPUTS(!colseq_buf, "Freeing colour sequence buffer without alloc");
-    /* Free buffer for colour code composition */
-    free(colseq_buf);
-    colseq_buf = NULL;
+    free_colour_buffer();
 }
 
 /*
@@ -663,21 +422,12 @@
 	 arrsize--;
 	 rhp++, arrp++) {
 	char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
-	int atrlen = 0, alloclen, done1;
-	const struct highlight *hp;
+	int atrlen = 0, alloclen;
 
 	sprintf(digbuf1, "%d", rhp->start);
 	sprintf(digbuf2, "%d", rhp->end);
 
-	for (hp = highlights; hp->name; hp++) {
-	    if (hp->mask_on & rhp->atr) {
-		if (atrlen)
-		    atrlen++; /* comma */
-		atrlen += strlen(hp->name);
-	    }
-	}
-	if (atrlen == 0)
-	    atrlen = 4; /* none */
+	atrlen = output_highlight(rhp->atr, NULL);
 	alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
 	    3; /* 2 spaces, 1 0 */
 	if (rhp->flags & ZRH_PREDISPLAY)
@@ -693,17 +443,7 @@
 	sprintf(*arrp, "%s%s %s ",
 		(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
 		digbuf1, digbuf2);
-	if (atrlen) {
-	    for (hp = highlights, done1 = 0; hp->name; hp++) {
-		if (hp->mask_on & rhp->atr) {
-		    if (done1)
-			strcat(*arrp, ",");
-		    strcat(*arrp, hp->name);
-		    done1 = 1;
-		}
-	    }
-	} else
-	    strcat(*arrp, "none");
+	(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
     }
     *arrp = '\0';
     return retarr;
@@ -1147,56 +887,6 @@
 }
 
 
-static void
-setcolourattribute(int colour, int fg_bg, int tc, int def,
-		   int use_termcap)
-{
-    char *ptr;
-    int do_free;
-
-    if ((do_free = (colseq_buf == NULL))) {
-	/* This can happen when moving the cursor in trashzle() */
-	set_colseq_buf();
-    }
-    /*
-     * If we're not restoring the default, and either have a
-     * colour value that is too large for ANSI, or have been told
-     * to use the termcap sequence, try to use the termcap sequence.
-     *
-     * We have already sanitised the values we allow from the
-     * highlighting variables, so much of this shouldn't be
-     * necessary at this point, but we might as well be safe.
-     */
-    if (!def && (colour > 7 || use_termcap)) {
-	/*
-	 * We can if it's available, and either we couldn't get
-	 * the maximum number of colours, or the colour is in range.
-	 */
-	if (tccan(tc) && (tccolours < 0 || colour < tccolours))
-	    tcoutarg(tc, colour);
-	/* for 0 to 7 assume standard ANSI works, otherwise it won't. */
-	if (colour > 7)
-	    return;
-    }
-
-    strcpy(colseq_buf, fg_bg_sequences[fg_bg].start);
-
-    ptr = colseq_buf + strlen(colseq_buf);
-    if (def) {
-	strcpy(ptr, fg_bg_sequences[fg_bg].def);
-	while (*ptr)
-	    ptr++;
-    } else
-	*ptr++ = colour + '0';
-    strcpy(ptr, fg_bg_sequences[fg_bg].end);
-    tputs(colseq_buf, 1, putshout);
-
-    if (do_free) {
-	free(colseq_buf);
-	colseq_buf = NULL;
-    }
-}
-
 /**/
 static void
 settextattributes(int atr)
@@ -1213,18 +903,10 @@
 	tsetcap(TCSTANDOUTBEG, 0);
     if (txtchangeisset(atr, TXTUNDERLINE))
 	tsetcap(TCUNDERLINEBEG, 0);
-    if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) {
-	setcolourattribute(txtchangeget(atr, TXT_ATTR_FG_COL),
-			   COL_SEQ_FG, TCFGCOLOUR,
-			   txtchangeisset(atr, TXTNOFGCOLOUR),
-			   txtchangeisset(atr, TXT_ATTR_FG_TERMCAP));
-    }
-    if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR)) {
-	setcolourattribute(txtchangeget(atr, TXT_ATTR_BG_COL),
-			   COL_SEQ_BG, TCBGCOLOUR,
-			   txtchangeisset(atr, TXTNOBGCOLOUR),
-			   txtchangeisset(atr, TXT_ATTR_BG_TERMCAP));
-    }
+    if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR))
+	set_colour_attribute(atr, COL_SEQ_FG, 0);
+    if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR))
+	set_colour_attribute(atr, COL_SEQ_BG, 0);
 }
 
 #ifdef MULTIBYTE_SUPPORT
@@ -1433,7 +1115,7 @@
 	tsetcap(TCSTANDOUTEND, 0);
 	tsetcap(TCUNDERLINEEND, 0);
 	/* cheat on attribute unset */
-	txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE|TXTDIRTY);
+	txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
 
 	if (trashedzle)
 	    reexpandprompt(); 
@@ -2942,7 +2624,6 @@
 void
 zle_refresh_boot(void)
 {
-    set_default_colour_sequences();
 }
 
 /* Provided for unloading the module in a modular fashion */
@@ -2956,6 +2637,4 @@
     if (region_highlights)
 	zfree(region_highlights,
 	      sizeof(struct region_highlight) * n_region_highlights);
-
-    free_colour_sequences();
 }


-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070



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