color: allow colors to be prefixed with "reset"

"reset" was previously treated as a standalone special color name
representing `\e[m`. Now, it can apply to other color properties,
allowing exact specifications without implicit attribute inheritance.

For example, "reset green" now renders `\e[;32m`, which is interpreted
as "reset everything; then set foreground to green". This means the
background and other attributes are also reset to their defaults.

Previously, this was impossible to represent in a single color:
"reset" could be specified alone, or a color with attributes, but some
thing like clearing a background color were impossible.

There is a separate change that introduces the "default" color name to
assist with that, but even then, the above could only to be represented
by explicitly disabling each of the attributes:
  green default no-bold no-dim no-italic no-ul no-blink no-reverse no-strike

Signed-off-by: Robert Estelle <robertestelle@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Robert Estelle 2021-10-26 01:03:47 +00:00 committed by Junio C Hamano
parent 05f1f41c9b
commit de658515ae
4 changed files with 21 additions and 7 deletions

View File

@ -288,6 +288,11 @@ The position of any attributes with respect to the colors
be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`, be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
`no-ul`, etc). `no-ul`, etc).
+ +
The pseudo-attribute `reset` resets all colors and attributes before
applying the specified coloring. For example, `reset green` will result
in a green foreground and default background without any active
attributes.
+
An empty color string produces no color effect at all. This can be used An empty color string produces no color effect at all. This can be used
to avoid coloring specific elements without disabling color entirely. to avoid coloring specific elements without disabling color entirely.
+ +

18
color.c
View File

@ -255,6 +255,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
const char *ptr = value; const char *ptr = value;
int len = value_len; int len = value_len;
char *end = dst + COLOR_MAXLEN; char *end = dst + COLOR_MAXLEN;
unsigned int has_reset = 0;
unsigned int attr = 0; unsigned int attr = 0;
struct color fg = { COLOR_UNSPECIFIED }; struct color fg = { COLOR_UNSPECIFIED };
struct color bg = { COLOR_UNSPECIFIED }; struct color bg = { COLOR_UNSPECIFIED };
@ -269,12 +270,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
return 0; return 0;
} }
if (!strncasecmp(ptr, "reset", len)) { /* [reset] [fg [bg]] [attr]... */
xsnprintf(dst, end - dst, GIT_COLOR_RESET);
return 0;
}
/* [fg [bg]] [attr]... */
while (len > 0) { while (len > 0) {
const char *word = ptr; const char *word = ptr;
struct color c = { COLOR_UNSPECIFIED }; struct color c = { COLOR_UNSPECIFIED };
@ -291,6 +287,11 @@ int color_parse_mem(const char *value, int value_len, char *dst)
len--; len--;
} }
if (match_word(word, wordlen, "reset")) {
has_reset = 1;
continue;
}
if (!parse_color(&c, word, wordlen)) { if (!parse_color(&c, word, wordlen)) {
if (fg.type == COLOR_UNSPECIFIED) { if (fg.type == COLOR_UNSPECIFIED) {
fg = c; fg = c;
@ -316,13 +317,16 @@ int color_parse_mem(const char *value, int value_len, char *dst)
*dst++ = (x); \ *dst++ = (x); \
} while(0) } while(0)
if (attr || !color_empty(&fg) || !color_empty(&bg)) { if (has_reset || attr || !color_empty(&fg) || !color_empty(&bg)) {
int sep = 0; int sep = 0;
int i; int i;
OUT('\033'); OUT('\033');
OUT('['); OUT('[');
if (has_reset)
sep++;
for (i = 0; attr; i++) { for (i = 0; attr; i++) {
unsigned bit = (1 << i); unsigned bit = (1 << i);
if (!(attr & bit)) if (!(attr & bit))

View File

@ -6,6 +6,7 @@ struct strbuf;
/* /*
* The maximum length of ANSI color sequence we would generate: * The maximum length of ANSI color sequence we would generate:
* - leading ESC '[' 2 * - leading ESC '[' 2
* - reset ';' .................1
* - attr + ';' 2 * num_attr (e.g. "1;") * - attr + ';' 2 * num_attr (e.g. "1;")
* - no-attr + ';' 3 * num_attr (e.g. "22;") * - no-attr + ';' 3 * num_attr (e.g. "22;")
* - fg color + ';' 17 (e.g. "38;2;255;255;255;") * - fg color + ';' 17 (e.g. "38;2;255;255;255;")

View File

@ -60,6 +60,10 @@ test_expect_success 'fg bg attr...' '
color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m" color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
' '
test_expect_success 'reset fg bg attr...' '
color "reset blue bold dim ul blink reverse" "[;1;2;4;5;7;34m"
'
# note that nobold and nodim are the same code (22) # note that nobold and nodim are the same code (22)
test_expect_success 'attr negation' ' test_expect_success 'attr negation' '
color "nobold nodim noul noblink noreverse" "[22;24;25;27m" color "nobold nodim noul noblink noreverse" "[22;24;25;27m"