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

Re: PATCH: isearch match highlighting



On Tue, 29 Apr 2008 18:10:22 +0100
Peter Stephenson <pws@xxxxxxx> wrote:
> I would like at the least to make the use of the ANSI escapes
> configurable.  They are in completion listings, but unfortunately that's
> tied to the variable interface for GNU ls colouring which doesn't really
> fit the case here.  I suppose special values in zle_highlight would be
> suitable (with bindkey escapes).

I've done this.  Looks suspiciously like overkill, but it's probably best
to be general.

I've also added handling of names for ANSI colours.

Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.65
diff -u -r1.65 zle.yo
--- Doc/Zsh/zle.yo	29 Apr 2008 17:19:26 -0000	1.65
+++ Doc/Zsh/zle.yo	1 May 2008 10:35:05 -0000
@@ -2088,6 +2088,39 @@
 )
 enditem()
 
+tt(zle_highlight) may contain additional fields for controlling how
+terminal sequences to change colours are output.  Each of the following is
+followed by a colon and a string in the same form as for key bindings.
+This will not be necessary for the vast majority of terminals as the
+defaults shown in parentheses are widely used.
+
+startitem()
+cindex(escape sequences, terminal, for highlighting)
+cindex(terminal escape sequences for highlighting)
+item(tt(fg_start_code) (tt(\e[3)))(
+The start of the escape sequence for the foreground colour.
+This is followed by an ASCII digit representing the colour.
+)
+item(tt(fg_default_code) (tt(9)))(
+The number to use instead of the colour to reset the default foreground
+colour.
+)
+item(tt(fg_end_code) (tt(m)))(
+The end of the escape sequence for the foreground colour.
+)
+item(tt(bg_start_code) (tt(\e[4)))(
+The start of the escape sequence for the background colour.
+This is followed by an ASCII digit representing the colour.
+)
+item(tt(bg_default_code) (tt(9)))(
+The number to use instead of the colour to reset the default
+background colour.
+)
+item(tt(bg_end_code) (tt(m)))(
+The end of the escape sequence for the background colour.
+)
+enditem()
+
 The available types of highlighting are the following.  Note that
 not all types of highlighting are available on all terminals:
 
@@ -2098,11 +2131,19 @@
 a default.
 )
 item(tt(fg=)var(colour))(
-The foreground colour should be set to var(colour), a decimal integer.  Not
-all terminals support this, and of those that do not all provide facilities
-to test the support, hence the user should decide based on the terminal
-type.  Most terminals with colour support accept the numbers 0 to 7, and
-may generate additional colours if the tt(bold) attributes is also present.
+The foreground colour should be set to var(colour), a decimal integer
+or the name of one of the eight most widely-supported colours.
+
+Not all terminals support this and, of those that do, not all provide
+facilities to test the support, hence the user should decide based on the
+terminal type.  Most terminals with colour support accept the numbers 0 to
+7, and may generate additional colours if the tt(bold) attributes is also
+present.  Most terminals also have a standard range of colours for those
+numbers (though the interpretation of the colour can vary); these colours
+can be set by one of the names tt(black), tt(red), tt(green), tt(yellow),
+tt(blue), tt(magenta), tt(cyan) and tt(white).  Abbreviations are
+allowed; tt(b) or tt(bl) selects black.
+
 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
 Co)'; if this succeeds, it indicates a limit on the number of colours which
@@ -2112,7 +2153,7 @@
 Colour is also known as color.
 )
 item(tt(bg=)var(colour))(
-The background colour should be set to var(colour), a decimal integer.
+The background colour should be set to var(colour).
 This works similarly to the foreground colour, except the background is
 not usually affected by the bold attribute.
 )
@@ -2144,12 +2185,18 @@
 `tt(^)' followed by the base character.
 )
 item(Unprintable multibyte characters)(
-If the tt(MULTIBYTE) option is in effect,
-multibyte characters not in the ASCII character set that are reported as
-having zero width are shown as a hexadecimal number between
-angle brackets.  The number is the code point of the character in
-the wide character set; this may or may not be Unicode, depending
-on the operating system.
+This item applies to control characters not in the ASCII range,
+plus other characters as follows.  If the tt(MULTIBYTE) option is in
+effect, multibyte characters not in the ASCII character set that are
+reported as having zero width are treated as combining characters when the
+option tt(COMBINING_CHARS) is on.  If the option is off, or if a character
+appears where a combining character is not valid, the character
+is treated as unprintable.
+
+Unprintable multibyte characters are shown as a hexadecimal number between
+angle brackets.  The number is the code point of the character in the wide
+character set; this may or may not be Unicode, depending on the operating
+system.
 )
 enditem()
 
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.110
diff -u -r1.110 zle_main.c
--- Src/Zle/zle_main.c	20 Apr 2008 21:17:29 -0000	1.110
+++ Src/Zle/zle_main.c	1 May 2008 10:35:06 -0000
@@ -1903,6 +1903,7 @@
     addhookfunc("before_trap", (Hookfn) zlebeforetrap);
     addhookfunc("after_trap", (Hookfn) zleaftertrap);
     (void)addhookdefs(m, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
+    zle_refresh_boot();
     return 0;
 }
 
Index: Src/Zle/zle_refresh.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_refresh.c,v
retrieving revision 1.64
diff -u -r1.64 zle_refresh.c
--- Src/Zle/zle_refresh.c	29 Apr 2008 17:19:27 -0000	1.64
+++ Src/Zle/zle_refresh.c	1 May 2008 10:35:06 -0000
@@ -326,6 +326,11 @@
 #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 {
@@ -342,6 +347,101 @@
     { 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.
@@ -359,15 +459,20 @@
 	found = 0;
 	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
 	    int is_fg = (teststr[0] == 'f');
-	    int colour = (int)zstrtol(teststr+3, (char **)&teststr, 10);
-	    int shft, on;
+	    int colour, shft, on;
+
+	    teststr += 3;
+	    if (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 >= 256)
+	    if (colour < 0 || colour >= 256)
 		continue;
 	    if (is_fg) {
 		shft = TXT_ATTR_FG_COL_SHIFT;
@@ -404,12 +509,14 @@
  */
 
 /**/
-void zle_set_highlight(void)
+static void
+zle_set_highlight(void)
 {
     char **atrs = getaparam("zle_highlight");
     int special_atr_on_set = 0;
     int region_atr_on_set = 0;
     int isearch_atr_on_set = 0;
+    int lenfg, lenbg, len;
     struct region_highlight *rhp;
 
     special_atr_on = 0;
@@ -442,6 +549,18 @@
 	    } 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);
 	    }
 	}
     }
@@ -453,9 +572,35 @@
 	region_highlights->atr = TXTSTANDOUT;
     if (!isearch_atr_on_set)
 	region_highlights[1].atr = TXTUNDERLINE;
+
+    /* Allocate buffer for colour code composition */
+    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);
 }
 
 
+/**/
+static void
+zle_free_highlight(void)
+{
+    /* Free buffer for colour code composition */
+    free(colseq_buf);
+}
+
 /*
  * Interface to the region_highlight ZLE parameter.
  * Converts betwen a format like "P32 42 underline,bold" to
@@ -942,26 +1087,12 @@
     rpms->sen = rpms->s + winw;
 }
 
-/*
- * HERE: these need to be made configurable, somehow.
- * Ideally we need to make the complist stuff use the
- * same system, but that may be too much tied to the GNU ls
- * interface to make that possible.
- */
-/* Start of escape sequence for foreground colour */
-#define TC_COL_FG_START	"\033[3"
-/* Start of escape sequence for background colour */
-#define TC_COL_BG_START	"\033[4"
-/* End of either escape sequence */
-#define TC_COL_END	"m"
-/* Numeric code (to be turned into ASCII) to reset default colour */
-#define TC_COL_DEFAULT	9
 
 static void
-setcolourattribute(int colour, char *start, int tc, int def,
+setcolourattribute(int colour, int fg_bg, int tc, int def,
 		   int use_termcap)
 {
-    char out[16], *ptr;
+    char *ptr;
     /*
      * If we're not restoring the default, and either have a
      * colour value that is too large for ANSI, or have been told
@@ -980,14 +1111,17 @@
 	    return;
     }
 
-    strcpy(out, start);
-    if (def)
-	colour = TC_COL_DEFAULT;
-
-    ptr = out + strlen(start);
-    *ptr++ = colour + '0';
-    strcpy(ptr, TC_COL_END);
-    tputs(out, 1, putshout);
+    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);
 }
 
 /**/
@@ -1008,13 +1142,13 @@
 	tsetcap(TCUNDERLINEBEG, 0);
     if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) {
 	setcolourattribute(txtchangeget(atr, TXT_ATTR_FG_COL),
-			   TC_COL_FG_START, TCFGCOLOUR,
+			   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),
-			   TC_COL_BG_START, TCBGCOLOUR,
+			   COL_SEQ_BG, TCBGCOLOUR,
 			   txtchangeisset(atr, TXTNOBGCOLOUR),
 			   txtchangeisset(atr, TXT_ATTR_BG_TERMCAP));
     }
@@ -1796,6 +1930,8 @@
     if (tmpalloced)
 	zfree(tmpline, tmpll * sizeof(*tmpline));
 
+    zle_free_highlight();
+
     /* if we have a new list showing, note it; if part of the list has been
     overwritten, redisplay it. We have to metafy line back before calling
     completion code */
@@ -2711,6 +2847,15 @@
     vcs = pos;
 }
 
+/* Provided for loading the module in a modular fashion */
+
+/**/
+void
+zle_refresh_boot(void)
+{
+    set_default_colour_sequences();
+}
+
 /* Provided for unloading the module in a modular fashion */
 
 /**/
@@ -2722,4 +2867,6 @@
     if (region_highlights)
 	zfree(region_highlights,
 	      sizeof(struct region_highlight) * n_region_highlights);
+
+    free_colour_sequences();
 }



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