From 7cc13c717b52d3539e76f087d747f96d0d24a914 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 16 Mar 2016 09:15:53 -0700 Subject: [PATCH] pretty: expand tabs in indented logs to make things line up properly MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A commit log message sometimes tries to line things up using tabs, assuming fixed-width font with the standard 8-place tab settings. Viewing such a commit however does not work well in "git log", as we indent the lines by prefixing 4 spaces in front of them. This should all line up: Column 1 Column 2 -------- -------- A B ABCD EFGH SPACES Instead of Tabs Even with multi-byte UTF8 characters: Column 1 Column 2 -------- -------- Ä B åäö 100 A Møøse once bit my sister.. Tab-expand the lines in "git log --expand-tabs" output before prefixing 4 spaces. This is based on the patch by Linus Torvalds, but at this step, we require an explicit command line option to enable the behaviour. Signed-off-by: Junio C Hamano --- Documentation/pretty-options.txt | 5 +++ commit.h | 1 + log-tree.c | 1 + pretty.c | 71 ++++++++++++++++++++++++++++++++++++++-- revision.c | 2 ++ revision.h | 1 + 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 4b659ac1a..d82065378 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -42,6 +42,11 @@ people using 80-column terminals. verbatim; this means that invalid sequences in the original commit may be copied to the output. +--expand-tabs:: + Perform a tab expansion (replace each tab with enough spaces + to fill to the next display column that is multiple of 8) + in the log message before showing it in the output. + ifndef::git-rev-list[] --notes[=]:: Show the notes (see linkgit:git-notes[1]) that annotate the diff --git a/commit.h b/commit.h index 5d58be001..a7ef682b5 100644 --- a/commit.h +++ b/commit.h @@ -147,6 +147,7 @@ struct pretty_print_context { int preserve_subject; struct date_mode date_mode; unsigned date_mode_explicit:1; + unsigned expand_tabs_in_log:1; int need_8bit_cte; char *notes_message; struct reflog_walk_info *reflog_info; diff --git a/log-tree.c b/log-tree.c index 60f983934..78a5381d0 100644 --- a/log-tree.c +++ b/log-tree.c @@ -683,6 +683,7 @@ void show_log(struct rev_info *opt) ctx.fmt = opt->commit_format; ctx.mailmap = opt->mailmap; ctx.color = opt->diffopt.use_color; + ctx.expand_tabs_in_log = opt->expand_tabs_in_log; ctx.output_encoding = get_log_output_encoding(); if (opt->from_ident.mail_begin && opt->from_ident.name_begin) ctx.from_ident = &opt->from_ident; diff --git a/pretty.c b/pretty.c index 92b2870a7..c8b075d91 100644 --- a/pretty.c +++ b/pretty.c @@ -1629,6 +1629,72 @@ void pp_title_line(struct pretty_print_context *pp, strbuf_release(&title); } +static int pp_utf8_width(const char *start, const char *end) +{ + int width = 0; + size_t remain = end - start; + + while (remain) { + int n = utf8_width(&start, &remain); + if (n < 0 || !start) + return -1; + width += n; + } + return width; +} + +static void strbuf_add_tabexpand(struct strbuf *sb, + const char *line, int linelen) +{ + const char *tab; + + while ((tab = memchr(line, '\t', linelen)) != NULL) { + int width = pp_utf8_width(line, tab); + + /* + * If it wasn't well-formed utf8, or it + * had characters with badly defined + * width (control characters etc), just + * give up on trying to align things. + */ + if (width < 0) + break; + + /* Output the data .. */ + strbuf_add(sb, line, tab - line); + + /* .. and the de-tabified tab */ + strbuf_addchars(sb, ' ', 8 - (width % 8)); + + /* Skip over the printed part .. */ + linelen -= tab + 1 - line; + line = tab + 1; + } + + /* + * Print out everything after the last tab without + * worrying about width - there's nothing more to + * align. + */ + strbuf_add(sb, line, linelen); +} + +/* + * pp_handle_indent() prints out the intendation, and + * the whole line (without the final newline), after + * de-tabifying. + */ +static void pp_handle_indent(struct pretty_print_context *pp, + struct strbuf *sb, int indent, + const char *line, int linelen) +{ + strbuf_addchars(sb, ' ', indent); + if (pp->expand_tabs_in_log) + strbuf_add_tabexpand(sb, line, linelen); + else + strbuf_add(sb, line, linelen); +} + void pp_remainder(struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, @@ -1653,8 +1719,9 @@ void pp_remainder(struct pretty_print_context *pp, strbuf_grow(sb, linelen + indent + 20); if (indent) - strbuf_addchars(sb, ' ', indent); - strbuf_add(sb, line, linelen); + pp_handle_indent(pp, sb, indent, line, linelen); + else + strbuf_add(sb, line, linelen); strbuf_addch(sb, '\n'); } } diff --git a/revision.c b/revision.c index df56fcea0..e662230ff 100644 --- a/revision.c +++ b/revision.c @@ -1915,6 +1915,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(arg+9, revs); + } else if (!strcmp(arg, "--expand-tabs")) { + revs->expand_tabs_in_log = 1; } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) { revs->show_notes = 1; revs->show_notes_given = 1; diff --git a/revision.h b/revision.h index 23857c0ed..40797535f 100644 --- a/revision.h +++ b/revision.h @@ -133,6 +133,7 @@ struct rev_info { show_notes_given:1, show_signature:1, pretty_given:1, + expand_tabs_in_log:1, abbrev_commit:1, abbrev_commit_given:1, zero_commit:1, -- 2.11.0