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.

TODO: %>> allows the next placeholder to eat the trailing spaces in
resulting string if it exceeds its allocated space. Maybe useful in
combination with %<(100)%s%>(40)%d, where spaces are wasted sometimes.

TODO: maybe support modifier '!' in addition to '|' above to cut
exceeding part in the next placeholder.

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

diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 8304325..7710ea8 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -149,6 +149,10 @@ 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 45fe878..6662f52 100644
--- a/pretty.c
+++ b/pretty.c
@@ -617,12 +617,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;
@@ -631,6 +639,7 @@ struct format_commit_context {
        char *message;
        size_t width, indent1, indent2;
        unsigned use_color;
+       int padding;
 
        /* These offsets are relative to the start of the commit message. */
        struct chunk author;
@@ -916,6 +925,52 @@ static size_t parse_color_placeholder(struct strbuf *sb,
        return 0;
 }
 
+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 '<':
+               if (*ch == '>') {
+                       flush_type = flush_both;
+                       ch++;
+               } else
+                       flush_type = flush_right;
+               break;
+       case '>':
+               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, const char *placeholder,
                                void *context)
 {
@@ -966,6 +1021,10 @@ static size_t format_commit_one(struct strbuf *sb, const 
char *placeholder,
                        return end - placeholder + 1;
                } else
                        return 0;
+
+       case '<':
+       case '>':
+               return parse_padding_placeholder(sb, placeholder, c);
        }
 
        /* these depend on the commit */
@@ -1125,6 +1184,7 @@ static size_t format_commit_item(struct strbuf *sb, const 
char *placeholder,
                                 void *context)
 {
        struct format_commit_context *c = context;
+       struct strbuf local_sb = STRBUF_INIT;
        int consumed;
        size_t orig_len;
        enum {
@@ -1151,7 +1211,51 @@ static size_t format_commit_item(struct strbuf *sb, 
const char *placeholder,
                placeholder++;
 
        orig_len = sb->len;
-       consumed = format_commit_one(sb, placeholder, context);
+       if (c->flush_type != no_flush) {
+               int 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;
+               }
+               consumed = format_commit_one(&local_sb, placeholder, context);
+               len = utf8_strnwidth(local_sb.buf, -1, 1);
+               if (len >= padding)
+                       /* Maybe we can trim the exceeded part */
+                       strbuf_addstr(sb, local_sb.buf);
+               else {
+                       int sb_len = sb->len, offset;
+                       switch (c->flush_type) {
+                       case flush_left:
+                               offset = padding - len;
+                               break;
+                       case flush_right:
+                               offset = 0;
+                               break;
+                       case flush_both:
+                               offset = (padding - len) / 2;
+                               break;
+                       case no_flush: /* to make gcc happy */
+                               break;
+                       }
+                       /*
+                        * 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;
+       } else
+               consumed = format_commit_one(sb, placeholder, context);
        if (c->use_color)
                c->use_color--;
        if (magic == NO_MAGIC)
-- 
1.7.12.1.384.g7b808e7

--
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