color: allow multiple attributes
authorJunio C Hamano <gitster@pobox.com>
Sun, 28 Feb 2010 02:56:38 +0000 (18:56 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 7 Mar 2010 20:00:36 +0000 (12:00 -0800)
In configuration files (and "git config --color" command line), we
supported one and only one attribute after foreground and background
color.  Accept combinations of attributes, e.g.

    [diff.color]
            old = red reverse bold

Signed-off-by: Junio C Hamano <gitster@pobox.com>
color.c
color.h
t/t4026-color.sh

diff --git a/color.c b/color.c
index db4dccf..90268fb 100644 (file)
--- a/color.c
+++ b/color.c
@@ -49,7 +49,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
 {
        const char *ptr = value;
        int len = value_len;
 {
        const char *ptr = value;
        int len = value_len;
-       int attr = -1;
+       unsigned int attr = 0;
        int fg = -2;
        int bg = -2;
 
        int fg = -2;
        int bg = -2;
 
@@ -58,7 +58,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
                return;
        }
 
                return;
        }
 
-       /* [fg [bg]] [attr] */
+       /* [fg [bg]] [attr]... */
        while (len > 0) {
                const char *word = ptr;
                int val, wordlen = 0;
        while (len > 0) {
                const char *word = ptr;
                int val, wordlen = 0;
@@ -87,19 +87,27 @@ void color_parse_mem(const char *value, int value_len, const char *var,
                        goto bad;
                }
                val = parse_attr(word, wordlen);
                        goto bad;
                }
                val = parse_attr(word, wordlen);
-               if (val < 0 || attr != -1)
+               if (0 <= val)
+                       attr |= (1 << val);
+               else
                        goto bad;
                        goto bad;
-               attr = val;
        }
 
        }
 
-       if (attr >= 0 || fg >= 0 || bg >= 0) {
+       if (attr || fg >= 0 || bg >= 0) {
                int sep = 0;
                int sep = 0;
+               int i;
 
                *dst++ = '\033';
                *dst++ = '[';
 
                *dst++ = '\033';
                *dst++ = '[';
-               if (attr >= 0) {
-                       *dst++ = '0' + attr;
-                       sep++;
+
+               for (i = 0; attr; i++) {
+                       unsigned bit = (1 << i);
+                       if (!(attr & bit))
+                               continue;
+                       attr &= ~bit;
+                       if (sep++)
+                               *dst++ = ';';
+                       *dst++ = '0' + i;
                }
                if (fg >= 0) {
                        if (sep++)
                }
                if (fg >= 0) {
                        if (sep++)
diff --git a/color.h b/color.h
index 5019df8..e2988f4 100644 (file)
--- a/color.h
+++ b/color.h
@@ -1,8 +1,20 @@
 #ifndef COLOR_H
 #define COLOR_H
 
 #ifndef COLOR_H
 #define COLOR_H
 
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-#define COLOR_MAXLEN 24
+/*  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:
+ * - 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;")
+ * - 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
 
 /*
  * This variable stores the value of color.ui
 
 /*
  * This variable stores the value of color.ui
index b61e516..49eced2 100755 (executable)
@@ -8,14 +8,13 @@ test_description='Test diff/status color escape codes'
 
 color()
 {
 
 color()
 {
-       git config diff.color.new "$1" &&
-       test "`git config --get-color diff.color.new`" = "\e$2"
+       actual=$(git config --get-color no.such.slot "$1") &&
+       test "$actual" = "\e$2"
 }
 
 invalid_color()
 {
 }
 
 invalid_color()
 {
-       git config diff.color.new "$1" &&
-       test -z "`git config --get-color diff.color.new 2>/dev/null`"
+       test_must_fail git config --get-color no.such.slot "$1"
 }
 
 test_expect_success 'reset' '
 }
 
 test_expect_success 'reset' '
@@ -42,6 +41,14 @@ test_expect_success 'fg bg attr' '
        color "blue red ul" "[4;34;41m"
 '
 
        color "blue red ul" "[4;34;41m"
 '
 
+test_expect_success 'fg bg attr...' '
+       color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
+'
+
+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"
+'
+
 test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
 test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '