about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Kiddle <opk@zsh.org>2024-01-28 00:47:10 +0100
committerOliver Kiddle <opk@zsh.org>2024-01-28 00:47:36 +0100
commit85545af42b8f9278136125324c37c1f88b461421 (patch)
treed07e1f565c6bcb8fdac42cd0a595a9e481f2e57f
parent3c5dacd503a2ac81059346b37d16ab5d1b6a1e04 (diff)
downloadzsh-85545af42b8f9278136125324c37c1f88b461421.tar.gz
zsh-85545af42b8f9278136125324c37c1f88b461421.tar.xz
zsh-85545af42b8f9278136125324c37c1f88b461421.zip
52500: add layer token to zle attributes
This provide control over the precedence of highlighting where
different regions overlap.
-rw-r--r--ChangeLog4
-rw-r--r--Src/Zle/zle.h2
-rw-r--r--Src/Zle/zle_refresh.c115
-rw-r--r--Src/prompt.c13
4 files changed, 88 insertions, 46 deletions
diff --git a/ChangeLog b/ChangeLog
index 2e0b085e5..7335590c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2024-01-28  Oliver Kiddle  <opk@zsh.org>
 
+	* 52500: Src/Zle/zle.h, Src/Zle/zle_refresh.c, Src/prompt.c:
+	add layer token to zle attributes to provide control over
+	the precedence of highlighting
+
 	* 52499: Src/prompt.c: support highlight groups defined in a
 	.zle.hlgroups associative array and referenced using %H in
 	prompt strings or hl= in zle_highlight/region_highlight
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 1a3e4c241..010ead3d2 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -435,6 +435,8 @@ enum {
 struct region_highlight {
     /* Attributes turned on in the region */
     zattr atr;
+    /* Priority for this region relative to others that overlap */
+    int layer;
     /* Start of the region */
     int start;
     /* Start of the region in metafied ZLE line */
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index a587f696a..f076bdd61 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -211,6 +211,12 @@ int predisplaylen, postdisplaylen;
 static zattr default_attr, special_attr, ellipsis_attr;
 
 /*
+ * Layer applied to highlighting for special characters
+ */
+
+static int special_layer;
+
+/*
  * Array of region highlights, no special termination.
  * The first N_SPECIAL_HIGHLIGHTS elements describe special uses of
  * highlighting, documented under N_SPECIAL_HIGHLIGHTS.
@@ -337,6 +343,13 @@ zle_set_highlight(void)
 	}
     }
 
+    /* Default layers */
+    region_highlights[0].layer = 20; /* region */
+    region_highlights[1].layer = 20; /* isearch */
+    region_highlights[2].layer = 10; /* suffix */
+    region_highlights[3].layer = 15; /* paste */
+    special_layer = 30;
+
     if (atrs) {
 	for (; *atrs; atrs++) {
 	    if (!strcmp(*atrs, "none")) {
@@ -346,30 +359,34 @@ zle_set_highlight(void)
 		paste_attr_set = region_attr_set =
 		    isearch_attr_set = suffix_attr_set = 1;
 	    } else if (strpfx("default:", *atrs)) {
-		match_highlight(*atrs + 8, &default_attr);
+		match_highlight(*atrs + 8, &default_attr, NULL);
 	    } else if (strpfx("special:", *atrs)) {
-		match_highlight(*atrs + 8, &special_attr);
+		match_highlight(*atrs + 8, &special_attr, &special_layer);
 		special_attr_set = 1;
 	    } else if (strpfx("region:", *atrs)) {
-		match_highlight(*atrs + 7, &region_highlights[0].atr);
+		match_highlight(*atrs + 7, &(region_highlights[0].atr),
+			&(region_highlights[0].layer));
 		region_attr_set = 1;
 	    } else if (strpfx("isearch:", *atrs)) {
-		match_highlight(*atrs + 8, &(region_highlights[1].atr));
+		match_highlight(*atrs + 8, &(region_highlights[1].atr),
+			&(region_highlights[1].layer));
 		isearch_attr_set = 1;
 	    } else if (strpfx("suffix:", *atrs)) {
-		match_highlight(*atrs + 7, &(region_highlights[2].atr));
+		match_highlight(*atrs + 7, &(region_highlights[2].atr),
+			&(region_highlights[2].layer));
 		suffix_attr_set = 1;
 	    } else if (strpfx("paste:", *atrs)) {
-		match_highlight(*atrs + 6, &(region_highlights[3].atr));
+		match_highlight(*atrs + 6, &(region_highlights[3].atr),
+			&(region_highlights[3].layer));
 		paste_attr_set = 1;
 	    } else if (strpfx("ellipsis:", *atrs)) {
-		match_highlight(*atrs + 9, &ellipsis_attr);
+		match_highlight(*atrs + 9, &ellipsis_attr, NULL);
 		ellipsis_attr_set = 1;
 	    }
 	}
     }
 
-    /* Defaults */
+    /* Default attributes */
     if (!special_attr_set)
 	special_attr = TXTSTANDOUT;
     if (!region_attr_set)
@@ -407,14 +424,13 @@ zle_free_highlight(void)
 char **
 get_region_highlight(UNUSED(Param pm))
 {
-    int arrsize = n_region_highlights;
+    int arrsize = n_region_highlights - N_SPECIAL_HIGHLIGHTS;
     char **retarr, **arrp;
     struct region_highlight *rhp;
 
     /* region_highlights may not have been set yet */
-    if (!arrsize)
+    if (!n_region_highlights)
 	return hmkarray(NULL);
-    arrsize -= N_SPECIAL_HIGHLIGHTS;
     DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights");
     arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
 
@@ -422,20 +438,18 @@ get_region_highlight(UNUSED(Param pm))
     for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 	 arrsize--;
 	 rhp++, arrp++) {
-	char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
-	int atrlen, alloclen;
-	const char memo_equals[] = "memo=";
-
-	sprintf(digbuf1, "%d", rhp->start);
-	sprintf(digbuf2, "%d", rhp->end);
-
-	atrlen = output_highlight(rhp->atr, NULL);
-	alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
-	    3; /* 2 spaces, 1 terminating NUL */
+	char digbuf[2 * DIGBUFSIZE], layerbuf[7 + DIGBUFSIZE];
+	int offset;
+	const char memo_equals[] = " memo=";
+	int alloclen = sprintf(digbuf, "%d %d", rhp->start, rhp->end) +
+	    output_highlight(rhp->atr, NULL) +
+	    2; /* space and terminating NUL */
 	if (rhp->flags & ZRH_PREDISPLAY)
-	    alloclen += 2; /* "P " */
+	    alloclen++; /* "P" */
+	if (rhp->layer != 10)
+	    alloclen += sprintf(layerbuf, ",layer=%d", rhp->layer);
 	if (rhp->memo)
-	    alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo);
+	    alloclen += sizeof(memo_equals) - 1 + strlen(rhp->memo);
 	*arrp = (char *)zhalloc(alloclen * sizeof(char));
 	/*
 	 * On input we allow a space after the flags.
@@ -444,13 +458,14 @@ get_region_highlight(UNUSED(Param pm))
 	 * into three words, and then check the first to
 	 * see if there are flags.  However, it's arguable.
 	 */
-	sprintf(*arrp, "%s%s %s ",
+	offset = sprintf(*arrp, "%s%s ",
 		(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
-		digbuf1, digbuf2);
-	(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
+		digbuf);
+	(void)output_highlight(rhp->atr, *arrp + offset);
 
+	if (rhp->layer != 10)
+	    strcat(*arrp, layerbuf);
 	if (rhp->memo) {
-	    strcat(*arrp, " ");
 	    strcat(*arrp, memo_equals);
 	    strcat(*arrp, rhp->memo);
 	}
@@ -459,12 +474,10 @@ get_region_highlight(UNUSED(Param pm))
     return retarr;
 }
 
-
 /*
  * The parameter system requires the pm argument, but this
  * may be NULL if called directly.
  */
-
 /**/
 void
 set_region_highlight(UNUSED(Param pm), char **aval)
@@ -523,7 +536,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
 	while (inblank(*strp))
 	    strp++;
 
-	strp = (char*) match_highlight(strp, &rhp->atr);
+	rhp->layer = 10; /* default */
+	strp = (char*) match_highlight(strp, &rhp->atr, &rhp->layer);
 
 	while (inblank(*strp))
 	    strp++;
@@ -1180,27 +1194,40 @@ zrefresh(void)
     rpms.s = nbuf[rpms.ln = 0] + lpromptw;
     rpms.sen = *nbuf + winw;
     for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
-	unsigned ireg;
 	zattr base_attr = mixattrs(default_attr, prompt_attr);
 	zattr all_attr;
 	struct region_highlight *rhp;
+	int layer, nextlayer = 0;
 	/*
 	 * Calculate attribute based on region.
 	 */
-	for (ireg = 0, rhp = region_highlights;
-	     ireg < n_region_highlights;
-	     ireg++, rhp++) {
-	    int offset;
-	    if (rhp->flags & ZRH_PREDISPLAY)
-		offset = 0;	/* include predisplay in start end */
-	    else
-		offset = predisplaylen; /* increment over it */
-	    if (rhp->start + offset <= tmppos &&
-		tmppos < rhp->end + offset) {
-		base_attr = mixattrs(rhp->atr, base_attr);
+	do {
+	    unsigned ireg;
+	    layer = nextlayer;
+	    nextlayer = special_layer;
+	    for (ireg = 0, rhp = region_highlights;
+		ireg < n_region_highlights;
+		ireg++, rhp++) {
+		if (rhp->layer == layer) {
+		    int offset;
+		    if (rhp->flags & ZRH_PREDISPLAY)
+			offset = 0;	/* include predisplay in start end */
+		    else
+			offset = predisplaylen; /* increment over it */
+		    if (rhp->start + offset <= tmppos &&
+			tmppos < rhp->end + offset) {
+			base_attr = mixattrs(rhp->atr, base_attr);
+			if (layer > special_layer)
+			    all_attr = mixattrs(rhp->atr, all_attr);
+		    }
+		} else if (rhp->layer > layer && rhp->layer < nextlayer) {
+		    nextlayer = rhp->layer;
+		}
 	    }
-	}
-	all_attr = mixattrs(special_attr, base_attr);
+	    if (special_layer == layer) {
+		all_attr = mixattrs(special_attr, base_attr);
+	    }
+	} while (nextlayer > layer);
 
 	if (t == scs)			/* if cursor is here, remember it */
 	    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
diff --git a/Src/prompt.c b/Src/prompt.c
index e4c213a13..ec79067cd 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -260,7 +260,7 @@ parsehighlight(char *arg, char endchar, zattr *atr)
 	if ((node = (Param) ht->getnode(ht, arg))) {
 	    attrs = node->gsu.s->getfn(node);
 	    entered = 1;
-	    if (match_highlight(attrs, atr) == attrs)
+	    if (match_highlight(attrs, atr, 0) == attrs)
 		*atr = TXT_ERROR;
 	} else
 	    *atr = TXT_ERROR;
@@ -1884,12 +1884,13 @@ match_colour(const char **teststrp, int is_fg, int colour)
 /*
  * Match a set of highlights in the given teststr.
  * Set *on_var to reflect the values found.
+ * Set *layer to the layer
  * Return a pointer to the first character not consumed.
  */
 
 /**/
 mod_export const char *
-match_highlight(const char *teststr, zattr *on_var)
+match_highlight(const char *teststr, zattr *on_var, int *layer)
 {
     int found = 1;
 
@@ -1918,6 +1919,14 @@ match_highlight(const char *teststr, zattr *on_var)
 	    /* skip out of range colours but keep scanning attributes */
 	    if (atr != TXT_ERROR)
 		*on_var |= atr;
+	} else if (layer && strpfx("layer=", teststr)) {
+	    teststr += 6;
+	    *layer = (int) zstrtol(teststr, (char **) &teststr, 10);
+	    if (*teststr == ',')
+		teststr++;
+	    else if (*teststr && *teststr != ' ')
+		break;
+	    found = 1;
 	} else {
 	    for (hl = highlights; hl->name; hl++) {
 		if (strpfx(hl->name, teststr)) {