Merge branch 'jk/ansi-color'

The output coloring scheme learned two new attributes, italic and
strike, in addition to existing bold, reverse, etc.

* jk/ansi-color:
  color: support strike-through attribute
  color: support "italic" attribute
  color: allow "no-" for negating attributes
  color: refactor parse_attr
  add skip_prefix_mem helper
  doc: refactor description of color format
  color: fix max-size comment
This commit is contained in:
Junio C Hamano 2016-07-11 10:31:05 -07:00
commit 3c5de5c77b
5 changed files with 84 additions and 37 deletions

View File

@ -150,27 +150,34 @@ integer::
1024", "by 1024x1024", etc. 1024", "by 1024x1024", etc.
color:: color::
The value for a variables that takes a color is a list of The value for a variable that takes a color is a list of
colors (at most two) and attributes (at most one), separated colors (at most two, one for foreground and one for background)
by spaces. The colors accepted are `normal`, `black`, and attributes (as many as you want), separated by spaces.
`red`, `green`, `yellow`, `blue`, `magenta`, `cyan` and
`white`; the attributes are `bold`, `dim`, `ul`, `blink` and
`reverse`. The first color given is the foreground; the
second is the background. The position of the attribute, if
any, doesn't matter. Attributes may be turned off specifically
by prefixing them with `no` (e.g., `noreverse`, `noul`, etc).
+ +
Colors (foreground and background) may also be given as numbers between The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
0 and 255; these use ANSI 256-color mode (but note that not all `blue`, `magenta`, `cyan` and `white`. The first color given is the
terminals may support this). If your terminal supports it, you may also foreground; the second is the background.
specify 24-bit RGB values as hex, like `#ff0ab3`.
+ +
The attributes are meant to be reset at the beginning of each item Colors may also be given as numbers between 0 and 255; these use ANSI
in the colored output, so setting color.decorate.branch to `black` 256-color mode (but note that not all terminals may support this). If
will paint that branch name in a plain `black`, even if the previous your terminal supports it, you may also specify 24-bit RGB values as
thing on the same output line (e.g. opening parenthesis before the hex, like `#ff0ab3`.
list of branch names in `log --decorate` output) is set to be +
painted with `bold` or some other attribute. The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
`italic`, and `strike` (for crossed-out or "strikethrough" letters).
The position of any attributes with respect to the colors
(before, after, or in between), doesn't matter. Specific attributes may
be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
`no-ul`, etc).
+
For git's pre-defined color slots, the attributes are meant to be reset
at the beginning of each item in the colored output. So setting
`color.decorate.branch` to `black` will paint that branch name in a
plain `black`, even if the previous thing on the same output line (e.g.
opening parenthesis before the list of branch names in `log --decorate`
output) is set to be painted with `bold` or some other attribute.
However, custom log formats may do more complicated and layered
coloring, and the negated forms may be useful there.
pathname:: pathname::
A variable that takes a pathname value can be given a A variable that takes a pathname value can be given a

35
color.c
View File

@ -123,19 +123,34 @@ static int parse_color(struct color *out, const char *name, int len)
return -1; return -1;
} }
static int parse_attr(const char *name, int len) static int parse_attr(const char *name, size_t len)
{ {
static const int attr_values[] = { 1, 2, 4, 5, 7, static const struct {
22, 22, 24, 25, 27 }; const char *name;
static const char * const attr_names[] = { size_t len;
"bold", "dim", "ul", "blink", "reverse", int val, neg;
"nobold", "nodim", "noul", "noblink", "noreverse" } attrs[] = {
#define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) }
ATTR("bold", 1, 22),
ATTR("dim", 2, 22),
ATTR("italic", 3, 23),
ATTR("ul", 4, 24),
ATTR("blink", 5, 25),
ATTR("reverse", 7, 27),
ATTR("strike", 9, 29)
#undef ATTR
}; };
int negate = 0;
int i; int i;
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
const char *str = attr_names[i]; if (skip_prefix_mem(name, len, "no", &name, &len)) {
if (!strncasecmp(name, str, len) && !str[len]) skip_prefix_mem(name, len, "-", &name, &len);
return attr_values[i]; negate = 1;
}
for (i = 0; i < ARRAY_SIZE(attrs); i++) {
if (attrs[i].len == len && !memcmp(attrs[i].name, name, len))
return negate ? attrs[i].neg : attrs[i].val;
} }
return -1; return -1;
} }

15
color.h
View File

@ -3,20 +3,23 @@
struct strbuf; struct strbuf;
/* 2 + (2 * num_attrs) + 8 + 1 + 8 + 'm' + NUL */
/* "\033[1;2;4;5;7;38;5;2xx;48;5;2xxm\0" */
/* /*
* 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
* - attr + ';' 3 * 10 (e.g. "1;") * - attr + ';' 2 * num_attr (e.g. "1;")
* - 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;")
* - bg color + ';' 17 (e.g. "48;2;255;255;255;") * - bg color + ';' 17 (e.g. "48;2;255;255;255;")
* - terminating 'm' NUL 2 * - terminating 'm' NUL 2
* *
* The above overcounts attr (we only use 5 not 8) and one semicolon * The above overcounts by one semicolon but it is close enough.
* but it is close enough. *
* The space for attributes is also slightly overallocated, as
* the negation for some attributes is the same (e.g., nobold and nodim).
*
* We allocate space for 7 attributes.
*/ */
#define COLOR_MAXLEN 70 #define COLOR_MAXLEN 75
/* /*
* IMPORTANT: Due to the way these color codes are emulated on Windows, * IMPORTANT: Due to the way these color codes are emulated on Windows,

View File

@ -473,6 +473,23 @@ static inline int skip_prefix(const char *str, const char *prefix,
return 0; return 0;
} }
/*
* Like skip_prefix, but promises never to read past "len" bytes of the input
* buffer, and returns the remaining number of bytes in "out" via "outlen".
*/
static inline int skip_prefix_mem(const char *buf, size_t len,
const char *prefix,
const char **out, size_t *outlen)
{
size_t prefix_len = strlen(prefix);
if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
*out = buf + prefix_len;
*outlen = len - prefix_len;
return 1;
}
return 0;
}
/* /*
* If buf ends with suffix, return 1 and subtract the length of the suffix * If buf ends with suffix, return 1 and subtract the length of the suffix
* from *len. Otherwise, return 0 and leave *len untouched. * from *len. Otherwise, return 0 and leave *len untouched.

View File

@ -50,14 +50,19 @@ 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"
' '
test_expect_success '"no-" variant of negation' '
color "no-bold no-blink" "[22;25m"
'
test_expect_success 'long color specification' ' test_expect_success 'long color specification' '
color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m" color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
' '
test_expect_success 'absurdly long color specification' ' test_expect_success 'absurdly long color specification' '
color \ color \
"#ffffff #ffffff bold nobold dim nodim ul noul blink noblink reverse noreverse" \ "#ffffff #ffffff bold nobold dim nodim italic noitalic
"[1;2;4;5;7;22;24;25;27;38;2;255;255;255;48;2;255;255;255m" ul noul blink noblink reverse noreverse strike nostrike" \
"[1;2;3;4;5;7;9;22;23;24;25;27;29;38;2;255;255;255;48;2;255;255;255m"
' '
test_expect_success '0-7 are aliases for basic ANSI color names' ' test_expect_success '0-7 are aliases for basic ANSI color names' '