Either %<, %> or %>< standing before a placeholder specifies how many
columns (at least as the placeholder can exceed it) it takes. Each
differs on how spaces are padded:

  %< pads on the right (aka left alignment)
  %> pads on the left (aka right alignment)
  %>< pads both ways equally (aka centered)

The (<N>) follows them, e.g. `%<(100)', to specify the number of
columns the next placeholder takes.

However, if '|' stands before (<N>), e.g. `%>|(100)', then the number
of columns is calculated so that it reaches the Nth column on screen.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 Documentation/pretty-formats.txt |   8 +++
 pretty.c                         | 117 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 8734224..0ab1233 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -162,6 +162,14 @@ The placeholders are:
 - '%x00': print a byte from a hex code
 - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
   linkgit:git-shortlog[1].
+- '%<(<N>)': make the next placeholder take at least N columns,
+  padding spaces on the right if necessary
+- '%<|(<N>)': make the next placeholder take at least until Nth
+  columns, padding spaces on the right if necessary
+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
+  respectively, but padding spaces on the left
+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
+  respectively, but padding both sides (i.e. the text is centered)
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
diff --git a/pretty.c b/pretty.c
index c333fd6..233d69c 100644
--- a/pretty.c
+++ b/pretty.c
@@ -760,12 +760,20 @@ struct chunk {
        size_t len;
 };
 
+enum flush_type {
+       no_flush,
+       flush_right,
+       flush_left,
+       flush_both
+};
+
 struct format_commit_context {
        const struct commit *commit;
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
        unsigned commit_signature_parsed:1;
+       enum flush_type flush_type;
        struct {
                char *gpg_output;
                char good_bad;
@@ -775,6 +783,7 @@ struct format_commit_context {
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
+       int padding;
 
        /* These offsets are relative to the start of the commit message. */
        struct chunk author;
@@ -1004,6 +1013,52 @@ static int format_reflog_person(struct strbuf *sb,
        return format_person_part(sb, part, ident, strlen(ident), dmode);
 }
 
+static size_t parse_padding_placeholder(struct strbuf *sb,
+                                       const char *placeholder,
+                                       struct format_commit_context *c)
+{
+       const char *ch = placeholder;
+       enum flush_type flush_type;
+       int to_column = 0;
+
+       switch (*ch++) {
+       case '<':
+               flush_type = flush_right;
+               break;
+       case '>':
+               if (*ch == '<') {
+                       flush_type = flush_both;
+                       ch++;
+               } else
+                       flush_type = flush_left;
+               break;
+       default:
+               return 0;
+       }
+
+       /* the next value means "wide enough to that column" */
+       if (*ch == '|') {
+               to_column = 1;
+               ch++;
+       }
+
+       if (*ch == '(') {
+               const char *start = ch + 1;
+               const char *end = strchr(start, ')');
+               char *next;
+               int width;
+               if (!end || end == start)
+                       return 0;
+               width = strtoul(start, &next, 10);
+               if (next == start || width == 0)
+                       return 0;
+               c->padding = to_column ? -width : width;
+               c->flush_type = flush_type;
+               return end - placeholder + 1;
+       }
+       return 0;
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                const char *placeholder,
                                void *context)
@@ -1090,6 +1145,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in 
UTF-8 */
                        return end - placeholder + 1;
                } else
                        return 0;
+
+       case '<':
+       case '>':
+               return parse_padding_placeholder(sb, placeholder, c);
        }
 
        /* these depend on the commit */
@@ -1247,6 +1306,59 @@ static size_t format_commit_one(struct strbuf *sb, /* in 
UTF-8 */
        return 0;       /* unknown placeholder */
 }
 
+static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
+                                   const char *placeholder,
+                                   struct format_commit_context *c)
+{
+       struct strbuf local_sb = STRBUF_INIT;
+       int total_consumed = 0, len, padding = c->padding;
+       if (padding < 0) {
+               const char *start = strrchr(sb->buf, '\n');
+               int occupied;
+               if (!start)
+                       start = sb->buf;
+               occupied = utf8_strnwidth(start, -1, 1);
+               padding = (-padding) - occupied;
+       }
+       while (1) {
+               int modifier = *placeholder == 'C';
+               int consumed = format_commit_one(&local_sb, placeholder, c);
+               total_consumed += consumed;
+
+               if (!modifier)
+                       break;
+
+               placeholder += consumed;
+               if (*placeholder != '%')
+                       break;
+               placeholder++;
+               total_consumed++;
+       }
+       len = utf8_strnwidth(local_sb.buf, -1, 1);
+       if (len > padding)
+               strbuf_addstr(sb, local_sb.buf);
+       else {
+               int sb_len = sb->len, offset = 0;
+               if (c->flush_type == flush_left)
+                       offset = padding - len;
+               else if (c->flush_type == flush_both)
+                       offset = (padding - len) / 2;
+               /*
+                * we calculate padding in columns, now
+                * convert it back to chars
+                */
+               padding = padding - len + local_sb.len;
+               strbuf_grow(sb, padding);
+               strbuf_setlen(sb, sb_len + padding);
+               memset(sb->buf + sb_len, ' ', sb->len - sb_len);
+               memcpy(sb->buf + sb_len + offset, local_sb.buf,
+                      local_sb.len);
+       }
+       strbuf_release(&local_sb);
+       c->flush_type = no_flush;
+       return total_consumed;
+}
+
 static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
                                 const char *placeholder,
                                 void *context)
@@ -1277,7 +1389,10 @@ static size_t format_commit_item(struct strbuf *sb, /* 
in UTF-8 */
                placeholder++;
 
        orig_len = sb->len;
-       consumed = format_commit_one(sb, placeholder, context);
+       if (((struct format_commit_context *)context)->flush_type != no_flush)
+               consumed = format_and_pad_commit(sb, placeholder, context);
+       else
+               consumed = format_commit_one(sb, placeholder, context);
        if (magic == NO_MAGIC)
                return consumed;
 
-- 
1.8.2.83.gc99314b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to