From 5ef8d77a752884f3c5fad9f143ce5cc72ff1340c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 17 Jan 2009 10:32:30 -0500 Subject: [PATCH 1/3] color: make it easier for non-config to parse color specs We have very featureful color-parsing routines which are used for color.diff.* and other options. Let's make it easier to use those routines from other parts of the code. This patch adds a color_parse_mem() helper function which takes a length-bounded string instead of a NUL-terminated one. While the helper is only a few lines long, it is nice to abstract this out so that: - callers don't forget to free() the temporary buffer - right now, it is implemented in terms of color_parse(). But it would be more efficient to reverse this and implement color_parse in terms of color_parse_mem. This also changes the error string for an invalid color not to mention the word "config", since it is not always appropriate (and when it is, the context is obvious since the offending config variable is given). Finally, while we are in the area, we clean up the parameter names in the declaration of color_parse; the var and value parameters were reversed from the actual implementation. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- color.c | 9 ++++++++- color.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/color.c b/color.c index fc0b72ad59..54a3da1bad 100644 --- a/color.c +++ b/color.c @@ -115,7 +115,7 @@ void color_parse(const char *value, const char *var, char *dst) *dst = 0; return; bad: - die("bad config value '%s' for variable '%s'", value, var); + die("bad color value '%s' for variable '%s'", value, var); } int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) @@ -191,3 +191,10 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) va_end(args); return r; } + +void color_parse_mem(const char *value, int len, const char *var, char *dst) +{ + char *tmp = xmemdupz(value, len); + color_parse(tmp, var, dst); + free(tmp); +} diff --git a/color.h b/color.h index 6cf5c88aaf..70660999df 100644 --- a/color.h +++ b/color.h @@ -16,7 +16,8 @@ extern int git_use_color_default; int git_color_default_config(const char *var, const char *value, void *cb); int git_config_colorbool(const char *var, const char *value, int stdout_is_tty); -void color_parse(const char *var, const char *value, char *dst); +void color_parse(const char *value, const char *var, char *dst); +void color_parse_mem(const char *value, int len, const char *var, char *dst); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); From c002922adce90b69ac56a86eeb77b249afa88eb2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 17 Jan 2009 10:38:46 -0500 Subject: [PATCH 2/3] expand --pretty=format color options Currently, the only colors available to --pretty=format users are red, green, and blue. Rather than expand it with a few new colors, this patch makes the usual config color syntax available, including more colors, backgrounds, and attributes. Because colors are no longer bounded to a single word (e.g., %Cred), this uses a more advanced syntax that features a beginning and end delimiter (but the old syntax still works). So you can now do: git log --pretty=tformat:'%C(yellow)%h%C(reset) %s' to emulate --pretty=oneline, or even git log --pretty=tformat:'%C(cyan magenta bold)%s%C(reset)' if you want to relive the awesomeness of 4-color CGA. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/pretty-formats.txt | 1 + pretty.c | 12 ++++++++++++ t/t6006-rev-list-format.sh | 9 ++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 0a8a948e6f..3d87d3edd5 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -124,6 +124,7 @@ The placeholders are: - '%Cgreen': switch color to green - '%Cblue': switch color to blue - '%Creset': reset color +- '%C(...)': color specification, as described in color.branch.* config option - '%m': left, right or boundary mark - '%n': newline - '%x00': print a byte from a hex code diff --git a/pretty.c b/pretty.c index 343dca556c..b1b86202e1 100644 --- a/pretty.c +++ b/pretty.c @@ -6,6 +6,7 @@ #include "string-list.h" #include "mailmap.h" #include "log-tree.h" +#include "color.h" static char *user_format; @@ -554,6 +555,17 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, /* these are independent of the commit */ switch (placeholder[0]) { case 'C': + if (placeholder[1] == '(') { + const char *end = strchr(placeholder + 2, ')'); + char color[COLOR_MAXLEN]; + if (!end) + return 0; + color_parse_mem(placeholder + 2, + end - (placeholder + 2), + "--pretty format", color); + strbuf_addstr(sb, color); + return end - placeholder + 1; + } if (!prefixcmp(placeholder + 1, "red")) { strbuf_addstr(sb, "\033[31m"); return 4; diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 86bf7e14ba..59d1f6283b 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -14,7 +14,7 @@ touch foo && git add foo && git commit -m "added foo" && test_format() { cat >expect.$1 test_expect_success "format $1" " -git rev-list --pretty=format:$2 master >output.$1 && +git rev-list --pretty=format:'$2' master >output.$1 && test_cmp expect.$1 output.$1 " } @@ -101,6 +101,13 @@ commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 foobarbazxyzzy EOF +test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF' +commit 131a310eb913d107dd3c09a65d1651175898735d +foo +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +foo +EOF + cat >commit-msg <<'EOF' Test printing of complex bodies From 2c2dc7c82cea66c5030583233c73593e0f5a8fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 19 Jan 2009 23:30:30 -0500 Subject: [PATCH 3/3] Optimize color_parse_mem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5ef8d77a implemented color_parse_mem, a function for parsing colors from a non-NUL-terminated string, by simply allocating a new NUL-terminated string and calling color_parse. This had a small but measurable speed impact on a user format that used the advanced color parsing. E.g., # uses quick parsing $ time ./git log --pretty=tformat:'%Credfoo%Creset' >/dev/null real 0m0.673s user 0m0.652s sys 0m0.016s # uses color_parse_mem $ time ./git log --pretty=tformat:'%C(red)foo%C(reset)' >/dev/null real 0m0.692s user 0m0.660s sys 0m0.032s This patch implements color_parse_mem as the primary function, with color_parse as a wrapper for strings. This gives comparable timings to the first case above. Original patch by René. Commit message and debugging by Jeff King. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- color.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/color.c b/color.c index 54a3da1bad..915d7a97f6 100644 --- a/color.c +++ b/color.c @@ -40,30 +40,41 @@ static int parse_attr(const char *name, int len) } void color_parse(const char *value, const char *var, char *dst) +{ + color_parse_mem(value, strlen(value), var, dst); +} + +void color_parse_mem(const char *value, int value_len, const char *var, + char *dst) { const char *ptr = value; + int len = value_len; int attr = -1; int fg = -2; int bg = -2; - if (!strcasecmp(value, "reset")) { + if (!strncasecmp(value, "reset", len)) { strcpy(dst, "\033[m"); return; } /* [fg [bg]] [attr] */ - while (*ptr) { + while (len > 0) { const char *word = ptr; - int val, len = 0; + int val, wordlen = 0; - while (word[len] && !isspace(word[len])) - len++; + while (len > 0 && !isspace(word[wordlen])) { + wordlen++; + len--; + } - ptr = word + len; - while (*ptr && isspace(*ptr)) + ptr = word + wordlen; + while (len > 0 && isspace(*ptr)) { ptr++; + len--; + } - val = parse_color(word, len); + val = parse_color(word, wordlen); if (val >= -1) { if (fg == -2) { fg = val; @@ -75,7 +86,7 @@ void color_parse(const char *value, const char *var, char *dst) } goto bad; } - val = parse_attr(word, len); + val = parse_attr(word, wordlen); if (val < 0 || attr != -1) goto bad; attr = val; @@ -115,7 +126,7 @@ void color_parse(const char *value, const char *var, char *dst) *dst = 0; return; bad: - die("bad color value '%s' for variable '%s'", value, var); + die("bad color value '%.*s' for variable '%s'", value_len, value, var); } int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) @@ -191,10 +202,3 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) va_end(args); return r; } - -void color_parse_mem(const char *value, int len, const char *var, char *dst) -{ - char *tmp = xmemdupz(value, len); - color_parse(tmp, var, dst); - free(tmp); -}