{"thread":{"id":"31624","subject":"[PATCH 3/6] pretty: support %C(auto[,N]) to turn on coloring on next placeholder(s)","startedAt":"2012-09-22T04:22:13Z","lastAt":"2012-10-24T08:25:12Z","messageCount":21,"participants":["Nguyễn Thái Ngọc Duy","Robin Rosenberg","Nguyen Thai Ngoc Duy","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"199720","messageId":"1348287739-12128-1-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":null,"subject":"[PATCH 0/6] Advanced --oneline layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:13Z","receivedAt":"2012-09-22T04:22:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Still a toy series (previous discussion [1]) but it's getting close to\nsomething useful.\n\nThis introduces three placeholders %<, %> and %<> for\nleft/right/center align the next placeholder. Any of them can be\nfollowed by '|' to make the next placeholder use up to the Nth column.\n\nIt's pretty useful already. You should be able to do multi column\nlayout on your wiiide terminal. It might even be useful in multi-line\nprintouts (e.g. we can relayout the commit header)\n\nThere are a few things we can improve:\n\n - utf8-only support does not sound nice (even though I'm\n   perfectly happy with it). At some point I think it'll be easier for\n   us to declare all internal strings be in utf8 (or ascii in certain\n   places), input/output routines are supposed to do the conversion.\n   GNOME has gone this way since 2.0.\n - ellipsis to prevent too long text breaking layout\n - specifing \"30% of my screen width\" would be nice, but it could get\n   complicated if users start to demand \"30% width, but no less than 30\n   columns and no more than 100 columns, and if blah blah\"\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/205922/focus=206049\n\nNguyễn Thái Ngọc Duy (6):\n  pretty: share code between format_decoration and show_decorations\n  pretty: split parsing %C into a separate function\n  pretty: support %C(auto[,N]) to turn on coloring on next\n    placeholder(s)\n  utf8.c: move display_mode_esc_sequence_len() for use by other\n    functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  pretty: support padding placeholders, %< %> and %<>\n\n Documentation/pretty-formats.txt |   5 +\n log-tree.c                       |  59 ++++++-----\n log-tree.h                       |   3 +\n pretty.c                         | 211 +++++++++++++++++++++++++++++++--------\n t/t4207-log-decoration-colors.sh |   8 +-\n utf8.c                           |  48 +++++----\n utf8.h                           |   1 +\n 7 files changed, 244 insertions(+), 91 deletions(-)\n\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199721","messageId":"1348287739-12128-2-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/6] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:14Z","receivedAt":"2012-09-22T04:22:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decoration()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 59 ++++++++++++++++++++++++----------------\n log-tree.h                       |  3 ++\n pretty.c                         | 19 +------------\n t/t4207-log-decoration-colors.sh |  8 +++---\n 4 files changed, 44 insertions(+), 45 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex c894930..19c54dc 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -174,36 +174,50 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color)\n {\n-\tconst char *prefix;\n-\tstruct name_decoration *decoration;\n+\tconst char *prefix = \" (\";\n+\tstruct name_decoration *d;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n+\n+\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\td = lookup_decoration(&name_decoration, &commit->object);\n+\tif (!d)\n+\t\treturn;\n+\tstrbuf_addstr(sb, color_commit);\n+\twhile (d) {\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, d->type));\n+\t\tif (d->type == DECORATION_REF_TAG)\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, d->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tprefix = \", \";\n+\t\td = d->next;\n+\t}\n+\tif (prefix[0] == ',') {\n+\t\tstrbuf_addch(sb, ')');\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t}\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n \t\tprintf(\"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n-\tdecoration = lookup_decoration(&name_decoration, &commit->object);\n-\tif (!decoration)\n-\t\treturn;\n-\tprefix = \" (\";\n-\twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n-\t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n-\t\tprefix = \", \";\n-\t\tdecoration = decoration->next;\n-\t}\n-\tputchar(')');\n+\tformat_decoration(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n /*\n@@ -617,7 +631,6 @@ void show_log(struct rev_info *opt)\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex f5ac238..10c2682 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex 8b1ea9f..e910679 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -764,23 +764,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1005,7 +988,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tformat_decoration(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..067f5aa 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199717","messageId":"1348287739-12128-3-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/6] pretty: split parsing %C into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:15Z","receivedAt":"2012-09-22T04:22:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 60 +++++++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 35 insertions(+), 25 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex e910679..93c96c2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -863,6 +863,40 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n+static size_t parse_color_placeholder(struct strbuf *sb,\n+\t\t\t\t      const char *placeholder,\n+\t\t\t\t      struct format_commit_context *c)\n+{\n+\tif (placeholder[1] == '(') {\n+\t\tconst char *end = strchr(placeholder + 2, ')');\n+\t\tchar color[COLOR_MAXLEN];\n+\t\tif (!end)\n+\t\t\treturn 0;\n+\t\tcolor_parse_mem(placeholder + 2,\n+\t\t\t\tend - (placeholder + 2),\n+\t\t\t\t\"--pretty format\", color);\n+\t\tstrbuf_addstr(sb, color);\n+\t\treturn end - placeholder + 1;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"red\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n+\t\treturn 4;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"green\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n+\t\treturn 6;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"blue\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n+\t\treturn 5;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"reset\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n+\t\treturn 6;\n+\t}\n+\treturn 0;\n+}\n+\n static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\tvoid *context)\n {\n@@ -875,31 +909,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\tif (placeholder[1] == '(') {\n-\t\t\tconst char *end = strchr(placeholder + 2, ')');\n-\t\t\tchar color[COLOR_MAXLEN];\n-\t\t\tif (!end)\n-\t\t\t\treturn 0;\n-\t\t\tcolor_parse_mem(placeholder + 2,\n-\t\t\t\t\tend - (placeholder + 2),\n-\t\t\t\t\t\"--pretty format\", color);\n-\t\t\tstrbuf_addstr(sb, color);\n-\t\t\treturn end - placeholder + 1;\n-\t\t}\n-\t\tif (!prefixcmp(placeholder + 1, \"red\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n-\t\t\treturn 4;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n-\t\t\treturn 6;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n-\t\t\treturn 5;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n-\t\t\treturn 6;\n-\t\t} else\n-\t\t\treturn 0;\n+\t\treturn parse_color_placeholder(sb, placeholder, c);\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199715","messageId":"1348287739-12128-4-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/6] pretty: support %C(auto[,N]) to turn on coloring on next placeholder(s)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:16Z","receivedAt":"2012-09-22T04:22:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  1 +\n pretty.c                         | 28 +++++++++++++++++++++++++++-\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex e3d8a83..8304325 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -142,6 +142,7 @@ The placeholders are:\n - '%Cblue': switch color to blue\n - '%Creset': reset color\n - '%C(...)': color specification, as described in color.branch.* config option\n+- '%C(auto[,<N>])': turn on coloring on the next placeholder (or the next N placeholders) if supported\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex 93c96c2..45fe878 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -630,6 +630,7 @@ struct format_commit_context {\n \t} signature;\n \tchar *message;\n \tsize_t width, indent1, indent2;\n+\tunsigned use_color;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -872,6 +873,24 @@ static size_t parse_color_placeholder(struct strbuf *sb,\n \t\tchar color[COLOR_MAXLEN];\n \t\tif (!end)\n \t\t\treturn 0;\n+\t\tif (!prefixcmp(placeholder + 1, \"(auto\")) {\n+\t\t\t/*\n+\t\t\t * use_color decreased after every\n+\t\t\t * format_commit_one() so we lose one right\n+\t\t\t * after parsing \"%C\". Which is why we add an\n+\t\t\t * extra one to use_color here.\n+\t\t\t */\n+\t\t\tif (placeholder[6] == ',') {\n+\t\t\t\tchar *next;\n+\t\t\t\tint v = strtoul(placeholder + 7, &next, 10);\n+\t\t\t\tif (next == end)\n+\t\t\t\t\tc->use_color = v + 1;\n+\t\t\t\telse\n+\t\t\t\t\treturn 0;\n+\t\t\t} else\n+\t\t\t\tc->use_color = 2;\n+\t\t\treturn end - placeholder + 1;\n+\t\t}\n \t\tcolor_parse_mem(placeholder + 2,\n \t\t\t\tend - (placeholder + 2),\n \t\t\t\t\"--pretty format\", color);\n@@ -955,13 +974,17 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_COMMIT));\n \t\tif (add_again(sb, &c->abbrev_commit_hash))\n \t\t\treturn 1;\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -998,7 +1021,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit, 0);\n+\t\tformat_decoration(sb, commit, c->use_color);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n@@ -1101,6 +1124,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \t\t\t\t void *context)\n {\n+\tstruct format_commit_context *c = context;\n \tint consumed;\n \tsize_t orig_len;\n \tenum {\n@@ -1128,6 +1152,8 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \n \torig_len = sb->len;\n \tconsumed = format_commit_one(sb, placeholder, context);\n+\tif (c->use_color)\n+\t\tc->use_color--;\n \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199716","messageId":"1348287739-12128-5-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/6] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:17Z","receivedAt":"2012-09-22T04:22:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex a544f15..18a4d9c 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199719","messageId":"1348287739-12128-6-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/6] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:18Z","receivedAt":"2012-09-22T04:22:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 18a4d9c..791499e 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)))\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 3c0ae76..58d5b6b 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199718","messageId":"1348287739-12128-7-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/6] pretty: support padding placeholders, %< %> and %<>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:22:19Z","receivedAt":"2012-09-22T04:22:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %<> standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %<> pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nTODO: %>> allows the next placeholder to eat the trailing spaces in\nresulting string if it exceeds its allocated space. Maybe useful in\ncombination with %<(100)%s%>(40)%d, where spaces are wasted sometimes.\n\nTODO: maybe support modifier '!' in addition to '|' above to cut\nexceeding part in the next placeholder.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   4 ++\n pretty.c                         | 106 ++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 109 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 8304325..7710ea8 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -149,6 +149,10 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns, padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding spaces on the left\n+- '%<>(<N>)', '%<>|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex 45fe878..6662f52 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -617,12 +617,20 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n \tunsigned commit_header_parsed:1;\n \tunsigned commit_message_parsed:1;\n \tunsigned commit_signature_parsed:1;\n+\tenum flush_type flush_type;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -631,6 +639,7 @@ struct format_commit_context {\n \tchar *message;\n \tsize_t width, indent1, indent2;\n \tunsigned use_color;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -916,6 +925,52 @@ static size_t parse_color_placeholder(struct strbuf *sb,\n \treturn 0;\n }\n \n+static size_t parse_padding_placeholder(struct strbuf *sb,\n+\t\t\t\t\tconst char *placeholder,\n+\t\t\t\t\tstruct format_commit_context *c)\n+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tif (*ch == '>') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\n+\n static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\tvoid *context)\n {\n@@ -966,6 +1021,10 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1125,6 +1184,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \t\t\t\t void *context)\n {\n \tstruct format_commit_context *c = context;\n+\tstruct strbuf local_sb = STRBUF_INIT;\n \tint consumed;\n \tsize_t orig_len;\n \tenum {\n@@ -1151,7 +1211,51 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\n+\tif (c->flush_type != no_flush) {\n+\t\tint len, padding = c->padding;\n+\t\tif (padding < 0) {\n+\t\t\tconst char *start = strrchr(sb->buf, '\\n');\n+\t\t\tint occupied;\n+\t\t\tif (!start)\n+\t\t\t\tstart = sb->buf;\n+\t\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\t\tpadding = (-padding) - occupied;\n+\t\t}\n+\t\tconsumed = format_commit_one(&local_sb, placeholder, context);\n+\t\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\t\tif (len >= padding)\n+\t\t\t/* Maybe we can trim the exceeded part */\n+\t\t\tstrbuf_addstr(sb, local_sb.buf);\n+\t\telse {\n+\t\t\tint sb_len = sb->len, offset;\n+\t\t\tswitch (c->flush_type) {\n+\t\t\tcase flush_left:\n+\t\t\t\toffset = padding - len;\n+\t\t\t\tbreak;\n+\t\t\tcase flush_right:\n+\t\t\t\toffset = 0;\n+\t\t\t\tbreak;\n+\t\t\tcase flush_both:\n+\t\t\t\toffset = (padding - len) / 2;\n+\t\t\t\tbreak;\n+\t\t\tcase no_flush: /* to make gcc happy */\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * we calculate padding in columns, now\n+\t\t\t * convert it back to chars\n+\t\t\t */\n+\t\t\tpadding = padding - len + local_sb.len;\n+\t\t\tstrbuf_grow(sb, padding);\n+\t\t\tstrbuf_setlen(sb, sb_len + padding);\n+\t\t\tmemset(sb->buf + sb_len, ' ', sb->len - sb_len);\n+\t\t\tmemcpy(sb->buf + sb_len + offset, local_sb.buf,\n+\t\t\t       local_sb.len);\n+\t\t}\n+\t\tstrbuf_release(&local_sb);\n+\t\tc->flush_type = no_flush;\n+\t} else\n+\t\tconsumed = format_commit_one(sb, placeholder, context);\n \tif (c->use_color)\n \t\tc->use_color--;\n \tif (magic == NO_MAGIC)\n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199723","messageId":"1348288000-12485-1-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/6] pretty: trim trailing spaces due to padding","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-22T04:26:40Z","receivedAt":"2012-09-22T04:26:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"TODO: should only be enabled explicitly to avoid unexpected trimming\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n And I did not remember I was in interactive rebase mode when I made\n the series. This patch is for discussion only. My screen seems to\n flash (I think) printing trailing spaces.\n\n pretty.c          |  1 +\n strbuf.c          | 29 +++++++++++++++++++++++++++++\n strbuf.h          |  1 +\n t/t9010-svn-fe.sh |  2 +-\n 4 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 6662f52..70f776b 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1327,6 +1327,7 @@ void format_commit_message(const struct commit *commit,\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n+\tdelete_trailing_whitespace(sb);\n \n \tif (context.message != commit->buffer)\n \t\tfree(context.message);\ndiff --git a/strbuf.c b/strbuf.c\nindex 0510f76..e001175 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -488,3 +488,32 @@ int fprintf_ln(FILE *fp, const char *fmt, ...)\n \t\treturn -1;\n \treturn ret + 1;\n }\n+\n+void delete_trailing_whitespace(struct strbuf *sb)\n+{\n+\tchar *src = sb->buf, *dst = sb->buf;\n+\tchar *end = src + sb->len, *anchor = NULL;\n+\twhile (src < end) {\n+\t\tif (*src == ' ') {\n+\t\t\tif (anchor)\n+\t\t\t\tsrc++;\n+\t\t\telse\n+\t\t\t\tanchor = src++;\n+\t\t} else if (*src == '\\n') {\n+\t\t\tif (anchor) {\n+\t\t\t\t*dst++ = *src++;\n+\t\t\t\tanchor = NULL;\n+\t\t\t} else\n+\t\t\t\t*dst++ = *src++;\n+\t\t} else {\n+\t\t\tif (anchor) {\n+\t\t\t\tmemcpy(dst, anchor, src - anchor);\n+\t\t\t\tdst += src - anchor;\n+\t\t\t\tanchor = NULL;\n+\t\t\t}\n+\t\t\t*dst++ = *src++;\n+\t\t}\n+\t}\n+\tsb->len = dst - sb->buf;\n+\tsb->buf[sb->len] = '\\0';\n+}\ndiff --git a/strbuf.h b/strbuf.h\nindex be941ee..75a8908 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -42,6 +42,7 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) {\n extern void strbuf_trim(struct strbuf *);\n extern void strbuf_rtrim(struct strbuf *);\n extern void strbuf_ltrim(struct strbuf *);\n+extern void delete_trailing_whitespace(struct strbuf *);\n extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);\n \n extern struct strbuf **strbuf_split_buf(const char *, size_t,\ndiff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh\nindex b7eed24..b603e70 100755\n--- a/t/t9010-svn-fe.sh\n+++ b/t/t9010-svn-fe.sh\n@@ -96,7 +96,7 @@ test_expect_failure PIPE 'empty revision' '\n \n test_expect_success PIPE 'empty properties' '\n \treinit_git &&\n-\tprintf \"rev <nobody, nobody@local>: %s\\n\" \"\" \"\" >expect &&\n+\tprintf \"rev <nobody, nobody@local>: %s\\n\" \"\" \"\" | sed \"s/ *$//\" >expect &&\n \tcat >emptyprop.dump <<-\\EOF &&\n \tSVN-fs-dump-format-version: 3\n \n-- \n1.7.12.1.384.g7b808e7\n"},{"id":"199758","messageId":"1348391433-11300-1-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348287739-12128-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 0/9] Advanced --oneline layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:24Z","receivedAt":"2012-09-23T09:10:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The series is now feature complete from my usage pov. The syntax may\nchange though. No tests yet until we agree on the syntax and semantics.\nSummary:\n\n - %C(auto) turns on coloring for the next placeholder\n - %C(auto,N) the same on the next N placeholders\n - %<(W) makes sure the next placeholder takes W columns, left aligned\n - %>(W) like %< but right aligned\n - %><(W) like %< but centered\n - %>|(W), %<|(W), %><|(W) are similar, except that W is calculated\n   to reach the Nth column on screen. (We might want %>|< instead of\n   %><|)\n - %>>(W) like %>(W) but it will try to steal trailing spaces if the\n   placeholder uses more than \"W\" columns. Very useful in \"%<%>\"\n   combination where the last placeholder may leave a lot of spaces\n   unused\n - All the above may take trunc, ltrunc, mtrunc optios in the bracket\n   to truncate the result string if exceeds the given width. It\n   truncates on the right, left and middle respectively.\n - No support for $COLUMNS. If you want to fit your screen, bash's\n   $(()) syntax is your friend to calculate columns.\n - We might want to support %<direction>(W,N), e.g. %<(40,3), where\n   the next N placeholders are grouped and aligned together as a\n   single placeholder.\n\nShow off time:\n\n6ab07c4 pretty: support %>> that steal trailing spaces     (HEAD, lanh/prettier) Nguyễn..gọc Duy\nee1c10e pretty: support truncating in %>, %< and %><                             Nguyễn..gọc Duy\n77aefae pretty: support padding placeholders, %< %> and %><                      Nguyễn..gọc Duy\n8f81f6b pretty: two phase conversion for non utf-8 commits                       Nguyễn..gọc Duy\nc3f1243 utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences     Nguyễn..gọc Duy\n1a4e9bb utf8.c: move display_mode_esc_sequence_len() for use by other functions  Nguyễn..gọc Duy\n99aa0c2 pretty: support %C(auto[,N]) to turn on coloring on next placeholder(s)  Nguyễn..gọc Duy\n1feaab6 pretty: split parsing %C into a separate function                        Nguyễn..gọc Duy\nb2676ac pretty: share code between format_decoration and show_decorations        Nguyễn..gọc Duy\n6bdccbe graph: avoid infinite loop in graph_show_commit()                        Nguyễn..gọc Duy\n16eed7c Merge branch 'mh/fetch-filter-refs'                      (origin/master)  Junio C Hamano\n\nProduced with \"%C(auto,99)%h %<|(80,trunc)%s%>>|(80)%d%>(15,mtrunc)% an\"\n\nNguyễn Thái Ngọc Duy (9):\n  pretty: share code between format_decoration and show_decorations\n  pretty: split parsing %C into a separate function\n  pretty: support %C(auto[,N]) to turn on coloring on next placeholder(s)\n  utf8.c: move display_mode_esc_sequence_len() for use by other functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  pretty: two phase conversion for non utf-8 commits\n  pretty: support padding placeholders, %< %> and %><\n  pretty: support truncating in %>, %< and %><\n  pretty: support %>> that steal trailing spaces\n\n Documentation/pretty-formats.txt |   7 +\n log-tree.c                       |  60 ++++---\n log-tree.h                       |   3 +\n pretty.c                         | 327 ++++++++++++++++++++++++++++++++-------\n t/t4207-log-decoration-colors.sh |   8 +-\n t/t6006-rev-list-format.sh       |  16 +-\n t/t6006/commit-msg.iso8859-1     |   5 +\n utf8.c                           |  94 ++++++++---\n utf8.h                           |   4 +\n 9 files changed, 412 insertions(+), 112 deletions(-)\n create mode 100644 t/t6006/commit-msg.iso8859-1\n\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199759","messageId":"1348391433-11300-2-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/9] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:25Z","receivedAt":"2012-09-23T09:10:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decoration()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 60 +++++++++++++++++++++++++---------------\n log-tree.h                       |  3 ++\n pretty.c                         | 19 +------------\n t/t4207-log-decoration-colors.sh |  8 +++---\n 4 files changed, 45 insertions(+), 45 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex c894930..528cab0 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -174,36 +174,50 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color)\n {\n-\tconst char *prefix;\n-\tstruct name_decoration *decoration;\n+\tconst char *prefix = \" (\";\n+\tstruct name_decoration *d;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n+\n+\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\td = lookup_decoration(&name_decoration, &commit->object);\n+\tif (!d)\n+\t\treturn;\n+\twhile (d) {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, d->type));\n+\t\tif (d->type == DECORATION_REF_TAG)\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, d->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t\tprefix = \", \";\n+\t\td = d->next;\n+\t}\n+\tif (prefix[0] == ',') {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addch(sb, ')');\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t}\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n \t\tprintf(\"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n-\tdecoration = lookup_decoration(&name_decoration, &commit->object);\n-\tif (!decoration)\n-\t\treturn;\n-\tprefix = \" (\";\n-\twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n-\t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n-\t\tprefix = \", \";\n-\t\tdecoration = decoration->next;\n-\t}\n-\tputchar(')');\n+\tformat_decoration(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n /*\n@@ -616,8 +630,8 @@ void show_log(struct rev_info *opt)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex f5ac238..10c2682 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex 8b1ea9f..e910679 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -764,23 +764,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1005,7 +988,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tformat_decoration(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..925f577 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199760","messageId":"1348391433-11300-3-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/9] pretty: split parsing %C into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:26Z","receivedAt":"2012-09-23T09:10:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 60 +++++++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 35 insertions(+), 25 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex e910679..93c96c2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -863,6 +863,40 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n+static size_t parse_color_placeholder(struct strbuf *sb,\n+\t\t\t\t      const char *placeholder,\n+\t\t\t\t      struct format_commit_context *c)\n+{\n+\tif (placeholder[1] == '(') {\n+\t\tconst char *end = strchr(placeholder + 2, ')');\n+\t\tchar color[COLOR_MAXLEN];\n+\t\tif (!end)\n+\t\t\treturn 0;\n+\t\tcolor_parse_mem(placeholder + 2,\n+\t\t\t\tend - (placeholder + 2),\n+\t\t\t\t\"--pretty format\", color);\n+\t\tstrbuf_addstr(sb, color);\n+\t\treturn end - placeholder + 1;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"red\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n+\t\treturn 4;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"green\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n+\t\treturn 6;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"blue\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n+\t\treturn 5;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"reset\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n+\t\treturn 6;\n+\t}\n+\treturn 0;\n+}\n+\n static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\tvoid *context)\n {\n@@ -875,31 +909,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\tif (placeholder[1] == '(') {\n-\t\t\tconst char *end = strchr(placeholder + 2, ')');\n-\t\t\tchar color[COLOR_MAXLEN];\n-\t\t\tif (!end)\n-\t\t\t\treturn 0;\n-\t\t\tcolor_parse_mem(placeholder + 2,\n-\t\t\t\t\tend - (placeholder + 2),\n-\t\t\t\t\t\"--pretty format\", color);\n-\t\t\tstrbuf_addstr(sb, color);\n-\t\t\treturn end - placeholder + 1;\n-\t\t}\n-\t\tif (!prefixcmp(placeholder + 1, \"red\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n-\t\t\treturn 4;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n-\t\t\treturn 6;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n-\t\t\treturn 5;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n-\t\t\treturn 6;\n-\t\t} else\n-\t\t\treturn 0;\n+\t\treturn parse_color_placeholder(sb, placeholder, c);\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199761","messageId":"1348391433-11300-4-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/9] pretty: support %C(auto[,N]) to turn on coloring on next placeholder(s)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:27Z","receivedAt":"2012-09-23T09:10:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  1 +\n pretty.c                         | 28 +++++++++++++++++++++++++++-\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex d9edded..c706556 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -145,6 +145,7 @@ The placeholders are:\n - '%Cblue': switch color to blue\n - '%Creset': reset color\n - '%C(...)': color specification, as described in color.branch.* config option\n+- '%C(auto[,<N>])': turn on coloring on the next placeholder (or the next N placeholders) if supported\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex 93c96c2..45fe878 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -630,6 +630,7 @@ struct format_commit_context {\n \t} signature;\n \tchar *message;\n \tsize_t width, indent1, indent2;\n+\tunsigned use_color;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -872,6 +873,24 @@ static size_t parse_color_placeholder(struct strbuf *sb,\n \t\tchar color[COLOR_MAXLEN];\n \t\tif (!end)\n \t\t\treturn 0;\n+\t\tif (!prefixcmp(placeholder + 1, \"(auto\")) {\n+\t\t\t/*\n+\t\t\t * use_color decreased after every\n+\t\t\t * format_commit_one() so we lose one right\n+\t\t\t * after parsing \"%C\". Which is why we add an\n+\t\t\t * extra one to use_color here.\n+\t\t\t */\n+\t\t\tif (placeholder[6] == ',') {\n+\t\t\t\tchar *next;\n+\t\t\t\tint v = strtoul(placeholder + 7, &next, 10);\n+\t\t\t\tif (next == end)\n+\t\t\t\t\tc->use_color = v + 1;\n+\t\t\t\telse\n+\t\t\t\t\treturn 0;\n+\t\t\t} else\n+\t\t\t\tc->use_color = 2;\n+\t\t\treturn end - placeholder + 1;\n+\t\t}\n \t\tcolor_parse_mem(placeholder + 2,\n \t\t\t\tend - (placeholder + 2),\n \t\t\t\t\"--pretty format\", color);\n@@ -955,13 +974,17 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_COMMIT));\n \t\tif (add_again(sb, &c->abbrev_commit_hash))\n \t\t\treturn 1;\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->use_color, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -998,7 +1021,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit, 0);\n+\t\tformat_decoration(sb, commit, c->use_color);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n@@ -1101,6 +1124,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \t\t\t\t void *context)\n {\n+\tstruct format_commit_context *c = context;\n \tint consumed;\n \tsize_t orig_len;\n \tenum {\n@@ -1128,6 +1152,8 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n \n \torig_len = sb->len;\n \tconsumed = format_commit_one(sb, placeholder, context);\n+\tif (c->use_color)\n+\t\tc->use_color--;\n \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199762","messageId":"1348391433-11300-5-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/9] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:28Z","receivedAt":"2012-09-23T09:10:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex a544f15..18a4d9c 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199763","messageId":"1348391433-11300-6-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/9] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:29Z","receivedAt":"2012-09-23T09:10:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 18a4d9c..791499e 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)))\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 3c0ae76..58d5b6b 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199764","messageId":"1348391433-11300-7-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/9] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:30Z","receivedAt":"2012-09-23T09:10:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Always assume format_commit_item() takes an utf-8 string for\nsimplicity. If commit message is in non-utf8, or output encoding is\nnot, then the commit is first converted to utf-8, processed, then\noutput converted to output encoding.\n\nThis of course only works with encodings that are compatible with\nUnicode.\n\nThis also fixes the iso8859-1 test. It's supposed to create an\niso8859-1 commit, but the commit content in t6006-rev-list-format.sh\nis in UTF-8. Split the content out in a separate file (so its encoding\nwon't accidentally be converted) and convert it back to iso8859-1.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c                     | 29 +++++++++++++++++++----------\n t/t6006-rev-list-format.sh   | 16 +++++-----------\n t/t6006/commit-msg.iso8859-1 |  5 +++++\n 3 files changed, 29 insertions(+), 21 deletions(-)\n create mode 100644 t/t6006/commit-msg.iso8859-1\n\ndiff --git a/pretty.c b/pretty.c\nindex 45fe878..f3275a7 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -916,7 +916,8 @@ static size_t parse_color_placeholder(struct strbuf *sb,\n \treturn 0;\n }\n \n-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\tconst char *placeholder,\n \t\t\t\tvoid *context)\n {\n \tstruct format_commit_context *c = context;\n@@ -1121,7 +1122,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n {\n \tstruct format_commit_context *c = context;\n@@ -1205,25 +1207,32 @@ void format_commit_message(const struct commit *commit,\n \tstruct format_commit_context context;\n \tstatic const char utf8[] = \"UTF-8\";\n \tconst char *output_enc = pretty_ctx->output_encoding;\n+\tchar *enc;\n \n \tmemset(&context, 0, sizeof(context));\n \tcontext.commit = commit;\n \tcontext.pretty_ctx = pretty_ctx;\n \tcontext.wrap_start = sb->len;\n \tcontext.message = commit->buffer;\n-\tif (output_enc) {\n-\t\tchar *enc = get_header(commit, \"encoding\");\n-\t\tif (strcmp(enc ? enc : utf8, output_enc)) {\n-\t\t\tcontext.message = logmsg_reencode(commit, output_enc);\n-\t\t\tif (!context.message)\n-\t\t\t\tcontext.message = commit->buffer;\n-\t\t}\n-\t\tfree(enc);\n+\tenc = get_header(commit, \"encoding\");\n+\tif (enc && strcmp(utf8, enc)) {\n+\t\tcontext.message = reencode_string(context.message, utf8, enc);\n+\t\tif (!context.message)\n+\t\t\tcontext.message = commit->buffer;\n \t}\n+\tfree(enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n \n+\tif (output_enc && strcmp(utf8, output_enc)) {\n+\t\tchar *out = reencode_string(sb->buf, output_enc, utf8);\n+\t\tif (out) {\n+\t\t\tint len = strlen(out);\n+\t\t\tstrbuf_attach(sb, out, len, len + 1);\n+\t\t}\n+\t}\n+\n \tif (context.message != commit->buffer)\n \t\tfree(context.message);\n \tfree(context.signature.gpg_output);\ndiff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh\nindex f94f0c4..cd24839 100755\n--- a/t/t6006-rev-list-format.sh\n+++ b/t/t6006-rev-list-format.sh\n@@ -124,27 +124,21 @@ commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n \u001b[1;31;43mfoo\u001b[m\n EOF\n \n-cat >commit-msg <<'EOF'\n-Test printing of complex bodies\n-\n-This commit message is much longer than the others,\n-and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n-EOF\n+cat \"$TEST_DIRECTORY/t6006/commit-msg.iso8859-1\" >commit-msg\n test_expect_success 'setup complex body' '\n git config i18n.commitencoding iso8859-1 &&\n   echo change2 >foo && git commit -a -F commit-msg\n '\n \n test_format complex-encoding %e <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n iso8859-1\n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_format complex-subject %s <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n Test printing of complex bodies\n commit 131a310eb913d107dd3c09a65d1651175898735d\n changed foo\n@@ -153,7 +147,7 @@ added foo\n EOF\n \n test_format complex-body %b <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n include an iso8859 character: ¡bueno!\n@@ -163,7 +157,7 @@ commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_expect_success '%x00 shows NUL' '\n-\techo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&\n+\techo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&\n \techo >>expect fooQbar &&\n \tgit rev-list -1 --format=foo%x00bar HEAD >actual.nul &&\n \tnul_to_q <actual.nul >actual &&\ndiff --git a/t/t6006/commit-msg.iso8859-1 b/t/t6006/commit-msg.iso8859-1\nnew file mode 100644\nindex 0000000..f8fe808\n--- /dev/null\n+++ b/t/t6006/commit-msg.iso8859-1\n@@ -0,0 +1,5 @@\n+Test printing of complex bodies\n+\n+This commit message is much longer than the others,\n+and it will be encoded in iso8859-1. We should therefore\n+include an iso8859 character: �bueno!\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199765","messageId":"1348391433-11300-8-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/9] pretty: support padding placeholders, %< %> and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:31Z","receivedAt":"2012-09-23T09:10:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %<> standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %>< pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   4 ++\n pretty.c                         | 113 ++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 116 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex c706556..49d29ab 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -152,6 +152,10 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns, padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding spaces on the left\n+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex f3275a7..d169673 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -617,12 +617,20 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n \tunsigned commit_header_parsed:1;\n \tunsigned commit_message_parsed:1;\n \tunsigned commit_signature_parsed:1;\n+\tenum flush_type flush_type;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -631,6 +639,7 @@ struct format_commit_context {\n \tchar *message;\n \tsize_t width, indent1, indent2;\n \tunsigned use_color;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -916,6 +925,52 @@ static size_t parse_color_placeholder(struct strbuf *sb,\n \treturn 0;\n }\n \n+static size_t parse_padding_placeholder(struct strbuf *sb,\n+\t\t\t\t\tconst char *placeholder,\n+\t\t\t\t\tstruct format_commit_context *c)\n+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tif (*ch == '<') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\n+\n static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\tconst char *placeholder,\n \t\t\t\tvoid *context)\n@@ -967,6 +1022,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1122,6 +1181,55 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \treturn 0;\t/* unknown placeholder */\n }\n \n+static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t    const char *placeholder,\n+\t\t\t\t    struct format_commit_context *c)\n+{\n+\tstruct strbuf local_sb = STRBUF_INIT;\n+\tint consumed, len, padding = c->padding;\n+\tif (padding < 0) {\n+\t\tconst char *start = strrchr(sb->buf, '\\n');\n+\t\tint occupied;\n+\t\tif (!start)\n+\t\t\tstart = sb->buf;\n+\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\tpadding = (-padding) - occupied;\n+\t}\n+\tconsumed = format_commit_one(&local_sb, placeholder, c);\n+\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\tif (len > padding)\n+\t\tstrbuf_addstr(sb, local_sb.buf);\n+\telse {\n+\t\tint sb_len = sb->len, offset;\n+\t\tswitch (c->flush_type) {\n+\t\tcase flush_left:\n+\t\t\toffset = padding - len;\n+\t\t\tbreak;\n+\t\tcase flush_right:\n+\t\t\toffset = 0;\n+\t\t\tbreak;\n+\t\tcase flush_both:\n+\t\t\toffset = (padding - len) / 2;\n+\t\t\tbreak;\n+\t\tcase no_flush: /* to make gcc happy */\n+\t\t\tbreak;\n+\t\t}\n+\t\t/*\n+\t\t * we calculate padding in columns, now\n+\t\t * convert it back to chars\n+\t\t */\n+\t\tpadding = padding - len + local_sb.len;\n+\t\tstrbuf_grow(sb, padding);\n+\t\tstrbuf_setlen(sb, sb_len + padding);\n+\t\tmemset(sb->buf + sb_len, ' ', sb->len - sb_len);\n+\t\tmemcpy(sb->buf + sb_len + offset, local_sb.buf,\n+\t\t       local_sb.len);\n+\t}\n+\tstrbuf_release(&local_sb);\n+\tc->flush_type = no_flush;\n+\treturn consumed;\n+}\n+\n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n@@ -1153,7 +1261,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\n+\tif (c->flush_type != no_flush)\n+\t\tconsumed = format_and_pad_commit(sb, placeholder, context);\n+\telse\n+\t\tconsumed = format_commit_one(sb, placeholder, context);\n \tif (c->use_color)\n \t\tc->use_color--;\n \tif (magic == NO_MAGIC)\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199766","messageId":"1348391433-11300-9-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/9] pretty: support truncating in %>, %< and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:32Z","receivedAt":"2012-09-23T09:10:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"%>(N,trunc) truncates the righ part after N columns and replace the\nlast two letters with \"..\". ltrunc does the same on the left. mtrunc\ncuts the middle out.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  2 +-\n pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n utf8.h                           |  2 ++\n 4 files changed, 97 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 49d29ab..3f056dc 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -152,7 +152,7 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n-- '%<(<N>)': make the next placeholder take at least N columns, padding spaces on the right if necessary\n+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at least N columns, padding spaces on the right if necessary\n - '%<|(<N>)': make the next placeholder take at least until Nth columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding spaces on the left\n - '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding both sides (i.e. the text is centered)\ndiff --git a/pretty.c b/pretty.c\nindex d169673..673193e 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -624,6 +624,13 @@ enum flush_type {\n \tflush_both\n };\n \n+enum trunc_type {\n+\ttrunc_none,\n+\ttrunc_left,\n+\ttrunc_middle,\n+\ttrunc_right\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n@@ -631,6 +638,7 @@ struct format_commit_context {\n \tunsigned commit_message_parsed:1;\n \tunsigned commit_signature_parsed:1;\n \tenum flush_type flush_type;\n+\tenum trunc_type truncate;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -956,7 +964,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n \tif (*ch == '(') {\n \t\tconst char *start = ch + 1;\n-\t\tconst char *end = strchr(start, ')');\n+\t\tconst char *end = start + strcspn(start, \",)\");\n \t\tchar *next;\n \t\tint width;\n \t\tif (!end || end == start)\n@@ -966,6 +974,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\treturn 0;\n \t\tc->padding = to_column ? -width : width;\n \t\tc->flush_type = flush_type;\n+\n+\t\tif (*end == ',') {\n+\t\t\tstart = end + 1;\n+\t\t\tend = strchr(start, ')');\n+\t\t\tif (!end || end == start)\n+\t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(start, \"trunc)\"))\n+\t\t\t\tc->truncate = trunc_right;\n+\t\t\telse if (!prefixcmp(start, \"ltrunc)\"))\n+\t\t\t\tc->truncate = trunc_left;\n+\t\t\telse if (!prefixcmp(start, \"mtrunc)\"))\n+\t\t\t\tc->truncate = trunc_middle;\n+\t\t\telse\n+\t\t\t\treturn 0;\n+\t\t} else\n+\t\t\tc->truncate = trunc_none;\n+\n \t\treturn end - placeholder + 1;\n \t}\n \treturn 0;\n@@ -1197,9 +1222,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t}\n \tconsumed = format_commit_one(&local_sb, placeholder, c);\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n-\tif (len > padding)\n+\tif (len > padding) {\n+\t\tswitch (c->truncate) {\n+\t\tcase trunc_left:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\t\t\t    0, len - (padding - 2),\n+\t\t\t\t\t    \"..\");\n+\t\t\tbreak;\n+\t\tcase trunc_middle:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\t\t\t    padding / 2 - 1,\n+\t\t\t\t\t    len - (padding - 2),\n+\t\t\t\t\t    \"..\");\n+\t\t\tbreak;\n+\t\tcase trunc_right:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\t\t\t    padding - 2, len - (padding - 2),\n+\t\t\t\t\t    \"..\");\n+\t\t\tbreak;\n+\t\tcase trunc_none:\n+\t\t\tbreak;\n+\t\t}\n \t\tstrbuf_addstr(sb, local_sb.buf);\n-\telse {\n+\t} else {\n \t\tint sb_len = sb->len, offset;\n \t\tswitch (c->flush_type) {\n \t\tcase flush_left:\ndiff --git a/utf8.c b/utf8.c\nindex 791499e..095c5ff 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -422,6 +422,52 @@ int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \treturn r;\n }\n \n+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n+\t\t\t const char *subst)\n+{\n+\tstruct strbuf sb_dst = STRBUF_INIT;\n+\tchar *src = sb_src->buf;\n+\tchar *end = src + sb_src->len;\n+\tchar *dst;\n+\tint w = 0, subst_len = 0;\n+\n+\tif (subst)\n+\t\tsubst_len = strlen(subst);\n+\tstrbuf_grow(&sb_dst, sb_src->len + subst_len);\n+\tdst = sb_dst.buf;\n+\n+\twhile (src < end) {\n+\t\tchar *old;\n+\t\tsize_t n;\n+\n+\t\twhile ((n = display_mode_esc_sequence_len(src))) {\n+\t\t\tmemcpy(dst, src, n);\n+\t\t\tsrc += n;\n+\t\t\tdst += n;\n+\t\t}\n+\n+\t\told = src;\n+\t\tn = utf8_width((const char**)&src, NULL);\n+\t\tif (!src) \t/* broken utf-8, do nothing */\n+\t\t\treturn;\n+\t\tif (n && w >= pos && w < pos + width) {\n+\t\t\tif (subst) {\n+\t\t\t\tmemcpy(dst, subst, subst_len);\n+\t\t\t\tdst += subst_len;\n+\t\t\t\tsubst = NULL;\n+\t\t\t}\n+\t\t\tw += n;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tmemcpy(dst, old, src - old);\n+\t\tdst += src - old;\n+\t\tw += n;\n+\t}\n+\tstrbuf_setlen(&sb_dst, dst - sb_dst.buf);\n+\tstrbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n+\t\t      sb_dst.len, sb_dst.alloc);\n+}\n+\n int is_encoding_utf8(const char *name)\n {\n \tif (!name)\ndiff --git a/utf8.h b/utf8.h\nindex 58d5b6b..0451a70 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -13,6 +13,8 @@ int strbuf_add_wrapped_text(struct strbuf *buf,\n \t\tconst char *text, int indent, int indent2, int width);\n int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n+\t\t\t const char *subst);\n \n #ifndef NO_ICONV\n char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199767","messageId":"1348391433-11300-10-git-send-email-pclouds@gmail.com","threadId":"31624","inReplyTo":"1348391433-11300-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 9/9] pretty: support %>> that steal trailing spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-23T09:10:33Z","receivedAt":"2012-09-23T09:10:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\nuse up all 100 columns and %an needs more than 20 columns. By\nreplacing %>(20) with %>>(20), %an can steal spaces from %s.\n\n%>> understands escape sequences, so %Cred does not stop it from\nstealing spaces in %<(100).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  4 +++-\n pretty.c                         | 35 +++++++++++++++++++++++++++++++++++\n utf8.c                           |  2 +-\n utf8.h                           |  1 +\n 4 files changed, 40 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 3f056dc..e752da0 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -155,7 +155,9 @@ The placeholders are:\n - '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at least N columns, padding spaces on the right if necessary\n - '%<|(<N>)': make the next placeholder take at least until Nth columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding spaces on the left\n-- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)' respectively, but padding both sides (i.e. the text is centered)\n+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N<)', '%>|(<N<)' respectively, except that if the next placeholder takes more spaces than given and there are spaces on its left, use those spaces\n+- '%><(<N>)', '%><|(<N>)': similar to '%\n+<(<N<)', '%<|(<N<)' respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex 673193e..deeacf2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -621,6 +621,7 @@ enum flush_type {\n \tno_flush,\n \tflush_right,\n \tflush_left,\n+\tflush_left_and_steal,\n \tflush_both\n };\n \n@@ -949,6 +950,9 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\tif (*ch == '<') {\n \t\t\tflush_type = flush_both;\n \t\t\tch++;\n+\t\t} else if (*ch == '>') {\n+\t\t\tflush_type = flush_left_and_steal;\n+\t\t\tch++;\n \t\t} else\n \t\t\tflush_type = flush_left;\n \t\tbreak;\n@@ -1222,6 +1226,36 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t}\n \tconsumed = format_commit_one(&local_sb, placeholder, c);\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\n+\tif (c->flush_type == flush_left_and_steal) {\n+\t\tconst char *ch = sb->buf + sb->len - 1;\n+\t\twhile (len > padding && ch > sb->buf) {\n+\t\t\tconst char *p;\n+\t\t\tif (*ch == ' ') {\n+\t\t\t\tch--;\n+\t\t\t\tpadding++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* check for trailing ansi sequences */\n+\t\t\tif (*ch != 'm')\n+\t\t\t\tbreak;\n+\t\t\tp = ch - 1;\n+\t\t\twhile (ch - p < 10 && *p != '\\033')\n+\t\t\t\tp--;\n+\t\t\tif (*p != '\\033' ||\n+\t\t\t    ch + 1 - p != display_mode_esc_sequence_len(p))\n+\t\t\t\tbreak;\n+\t\t\t/*\n+\t\t\t * got a good ansi sequence, put it back to\n+\t\t\t * local_sb as we're cutting sb\n+\t\t\t */\n+\t\t\tstrbuf_insert(&local_sb, 0, p, ch + 1 - p);\n+\t\t\tch = p - 1;\n+\t\t}\n+\t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n+\t\tc->flush_type = flush_left;\n+\t}\n+\n \tif (len > padding) {\n \t\tswitch (c->truncate) {\n \t\tcase trunc_left:\n@@ -1256,6 +1290,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\tcase flush_both:\n \t\t\toffset = (padding - len) / 2;\n \t\t\tbreak;\n+\t\tcase flush_left_and_steal:\n \t\tcase no_flush: /* to make gcc happy */\n \t\t\tbreak;\n \t\t}\ndiff --git a/utf8.c b/utf8.c\nindex 095c5ff..6af288d 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,7 +9,7 @@ struct interval {\n   int last;\n };\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n+size_t display_mode_esc_sequence_len(const char *s)\n {\n \tconst char *p = s;\n \tif (*p++ != '\\033')\ndiff --git a/utf8.h b/utf8.h\nindex 0451a70..758a1ed 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -3,6 +3,7 @@\n \n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n+size_t display_mode_esc_sequence_len(const char *s);\n int utf8_width(const char **start, size_t *remainder_p);\n int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199772","messageId":"189332269.134567.1348408475044.JavaMail.root@dewire.com","threadId":"31624","inReplyTo":"1348391433-11300-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 6/9] pretty: two phase conversion for non utf-8 commits","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2012-09-23T13:54:35Z","receivedAt":"2012-09-23T13:54:35Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nA few nitpicks\n\n----- Ursprungligt meddelande -----\n> Always assume format_commit_item() takes an utf-8 string for\n> simplicity. If commit message is in non-utf8, or output encoding is\n> not, then the commit is first converted to utf-8, processed, then\n> output converted to output encoding.\n> \n> This of course only works with encodings that are compatible with\n> Unicode.\nSuch as? Unicode was defined to encompass all knows encodings.\n\n> -static size_t format_commit_one(struct strbuf *sb, const char\n> *placeholder,\n> +static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n> +\t\t\t\tconst char *placeholder,\n>  \t\t\t\tvoid *context)\n>  {\n>  \tstruct format_commit_context *c = context;\n\nWhich parameter does the comment apply to? I believe most conventions\nnowadays include parameter documentation in the comment preceding\nthe function header.\n\n[...]\n\n> b/t/t6006/commit-msg.iso8859-1\n> new file mode 100644\n> index 0000000..f8fe808\n> --- /dev/null\n> +++ b/t/t6006/commit-msg.iso8859-1\n> @@ -0,0 +1,5 @@\n> +Test printing of complex bodies\n> +\n> +This commit message is much longer than the others,\n> +and it will be encoded in iso8859-1. We should therefore\n> +include an iso8859 character: �bueno!\n\n\"8859-1\" to be exact. Only three 8859 encoding has the\ncharacter.\n\n-- robin\n"},{"id":"199784","messageId":"CACsJy8DoBwLccaGGFqDfW2F3isZ74S2FLGXW3bDOcr2X-hQc3w@mail.gmail.com","threadId":"31624","inReplyTo":"189332269.134567.1348408475044.JavaMail.root@dewire.com","subject":"Re: [PATCH 6/9] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-24T01:21:01Z","receivedAt":"2012-09-24T01:21:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 23, 2012 at 8:54 PM, Robin Rosenberg\n<robin.rosenberg@dewire.com> wrote:\n>> This of course only works with encodings that are compatible with\n>> Unicode.\n> Such as? Unicode was defined to encompass all knows encodings.\n\nJust a precaution because I have never read Unicode standard (and it\nkeeps getting updated, hence \"incomplete\")\n\n>\n>> -static size_t format_commit_one(struct strbuf *sb, const char\n>> *placeholder,\n>> +static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n>> +                             const char *placeholder,\n>>                               void *context)\n>>  {\n>>       struct format_commit_context *c = context;\n>\n> Which parameter does the comment apply to? I believe most conventions\n> nowadays include parameter documentation in the comment preceding\n> the function header.\n\nYeah. I should have followed that.\n\n>> b/t/t6006/commit-msg.iso8859-1\n>> new file mode 100644\n>> index 0000000..f8fe808\n>> --- /dev/null\n>> +++ b/t/t6006/commit-msg.iso8859-1\n>> @@ -0,0 +1,5 @@\n>> +Test printing of complex bodies\n>> +\n>> +This commit message is much longer than the others,\n>> +and it will be encoded in iso8859-1. We should therefore\n>> +include an iso8859 character: �bueno!\n>\n> \"8859-1\" to be exact. Only three 8859 encoding has the\n> character.\n\nYep. But i'll probably need a closer look at t6006. It seems there's a\nfew \"upside down exclamation\" (not sure what it's called) in UTF-8 in\nthat test. I'll fix the text too.\n-- \nDuy\n"},{"id":"201801","messageId":"20121024082512.GA22127@sigill.intra.peff.net","threadId":"31624","inReplyTo":"1348391433-11300-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 7/9] pretty: support padding placeholders, %< %> and %><","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-10-24T08:25:12Z","receivedAt":"2012-10-24T08:25:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Sep 23, 2012 at 04:10:31PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> +\telse {\n> +\t\tint sb_len = sb->len, offset;\n> +\t\tswitch (c->flush_type) {\n> +\t\tcase flush_left:\n> +\t\t\toffset = padding - len;\n> +\t\t\tbreak;\n> +\t\tcase flush_right:\n> +\t\t\toffset = 0;\n> +\t\t\tbreak;\n> +\t\tcase flush_both:\n> +\t\t\toffset = (padding - len) / 2;\n> +\t\t\tbreak;\n> +\t\tcase no_flush: /* to make gcc happy */\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\t/*\n> +\t\t * we calculate padding in columns, now\n> +\t\t * convert it back to chars\n> +\t\t */\n> +\t\tpadding = padding - len + local_sb.len;\n> +\t\tstrbuf_grow(sb, padding);\n> +\t\tstrbuf_setlen(sb, sb_len + padding);\n> +\t\tmemset(sb->buf + sb_len, ' ', sb->len - sb_len);\n> +\t\tmemcpy(sb->buf + sb_len + offset, local_sb.buf,\n> +\t\t       local_sb.len);\n> +\t}\n\ngcc complains (rightly, I think) that offset can be used uninitialized\nin the final line (looks like it is from the no_flush case). If it is a\n\"can never happen\" case that is there to appease gcc in the switch\nstatement, should we drop a die(\"BUG: XXX\") there? If so, what would the\nXXX be?\n\n-Peff\n"}]}