From 17a4be26060b00a867cbe54ee906fe03813470ec Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 20 Nov 2014 10:25:39 -0500 Subject: [PATCH] parse_color: support 24-bit RGB values Some terminals (like XTerm) allow full 24-bit RGB color specifications using an extension to the regular ANSI color scheme. Let's allow users to specify hex RGB colors, enabling the all-important feature of hot pink ref decorations: git log --format="%h%C(#ff69b4)%d%C(reset) %s" Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 ++- color.c | 29 ++++++++++++++++++++++++++++- color.h | 6 +++--- t/t4026-color.sh | 4 ++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 9678ab6aa8..35b9731d49 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -828,7 +828,8 @@ doesn't matter. + Colors (foreground and background) may also be given as numbers between 0 and 255; these use ANSI 256-color mode (but note that not all -terminals may support this). +terminals may support this). If your terminal supports it, you may also +specify 24-bit RGB values as hex, like `#ff0ab3`. color.diff:: Whether to use ANSI escape sequences to add color to patches. diff --git a/color.c b/color.c index 3afda9d9dc..ae68792923 100644 --- a/color.c +++ b/color.c @@ -32,10 +32,13 @@ struct color { COLOR_UNSPECIFIED = 0, COLOR_NORMAL, COLOR_ANSI, /* basic 0-7 ANSI colors */ - COLOR_256 + COLOR_256, + COLOR_RGB } type; /* The numeric value for ANSI and 256-color modes */ unsigned char value; + /* 24-bit RGB color values */ + unsigned char red, green, blue; }; /* @@ -47,6 +50,16 @@ static int match_word(const char *word, int len, const char *match) return !strncasecmp(word, match, len) && !match[len]; } +static int get_hex_color(const char *in, unsigned char *out) +{ + unsigned int val; + val = (hexval(in[0]) << 4) | hexval(in[1]); + if (val & ~0xff) + return -1; + *out = val; + return 0; +} + static int parse_color(struct color *out, const char *name, int len) { /* Positions in array must match ANSI color codes */ @@ -64,6 +77,16 @@ static int parse_color(struct color *out, const char *name, int len) return 0; } + /* Try a 24-bit RGB value */ + if (len == 7 && name[0] == '#') { + if (!get_hex_color(name + 1, &out->red) && + !get_hex_color(name + 3, &out->green) && + !get_hex_color(name + 5, &out->blue)) { + out->type = COLOR_RGB; + return 0; + } + } + /* Then pick from our human-readable color names... */ for (i = 0; i < ARRAY_SIZE(color_names); i++) { if (match_word(name, len, color_names[i])) { @@ -140,6 +163,10 @@ static char *color_output(char *out, const struct color *c, char type) case COLOR_256: out += sprintf(out, "%c8;5;%d", type, c->value); break; + case COLOR_RGB: + out += sprintf(out, "%c8;2;%d;%d;%d", type, + c->red, c->green, c->blue); + break; } return out; } diff --git a/color.h b/color.h index f5beab1ed7..4ec34b448f 100644 --- a/color.h +++ b/color.h @@ -9,14 +9,14 @@ struct strbuf; * The maximum length of ANSI color sequence we would generate: * - leading ESC '[' 2 * - attr + ';' 2 * 8 (e.g. "1;") - * - fg color + ';' 9 (e.g. "38;5;2xx;") - * - fg color + ';' 9 (e.g. "48;5;2xx;") + * - fg color + ';' 17 (e.g. "38;2;255;255;255;") + * - bg color + ';' 17 (e.g. "48;2;255;255;255;") * - terminating 'm' NUL 2 * * The above overcounts attr (we only use 5 not 8) and one semicolon * but it is close enough. */ -#define COLOR_MAXLEN 40 +#define COLOR_MAXLEN 56 /* * IMPORTANT: Due to the way these color codes are emulated on Windows, diff --git a/t/t4026-color.sh b/t/t4026-color.sh index 63e423838f..65386db916 100755 --- a/t/t4026-color.sh +++ b/t/t4026-color.sh @@ -53,6 +53,10 @@ test_expect_success '256 colors' ' color "254 bold 255" "[1;38;5;254;48;5;255m" ' +test_expect_success '24-bit colors' ' + color "#ff00ff black" "[38;2;255;0;255;40m" +' + test_expect_success '"normal" yields no color at all"' ' color "normal black" "[40m" ' -- 2.11.4.GIT