%>(N,trunc) truncates the right part after N columns and replace the
last two letters with "..". ltrunc does the same on the left. mtrunc
cuts the middle out.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 Documentation/pretty-formats.txt |  7 ++++--
 pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---
 t/t4205-log-pretty-formats.sh    | 39 ++++++++++++++++++++++++++++++
 utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++
 utf8.h                           |  2 ++
 5 files changed, 140 insertions(+), 5 deletions(-)

diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index e2345d2..d3450bf 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -164,8 +164,11 @@ 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>[,trunc|ltrunc|mtrunc])': make the next placeholder take at
+  least N columns, padding spaces on the right if necessary.
+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)
+  or the end (trunc) if the output is longer than N columns.
+  Note that truncating only works correctly with N >= 2.
 - '%<|(<N>)': make the next placeholder take at least until Nth
   columns, padding spaces on the right if necessary
 - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
diff --git a/pretty.c b/pretty.c
index 7debfb2..a6c029c 100644
--- a/pretty.c
+++ b/pretty.c
@@ -776,6 +776,13 @@ enum flush_type {
        flush_both
 };
 
+enum trunc_type {
+       trunc_none,
+       trunc_left,
+       trunc_middle,
+       trunc_right
+};
+
 struct format_commit_context {
        const struct commit *commit;
        const struct pretty_print_context *pretty_ctx;
@@ -783,6 +790,7 @@ struct format_commit_context {
        unsigned commit_message_parsed:1;
        struct signature_check signature_check;
        enum flush_type flush_type;
+       enum trunc_type truncate;
        char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
@@ -1033,7 +1041,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
 
        if (*ch == '(') {
                const char *start = ch + 1;
-               const char *end = strchr(start, ')');
+               const char *end = start + strcspn(start, ",)");
                char *next;
                int width;
                if (!end || end == start)
@@ -1043,6 +1051,23 @@ static size_t parse_padding_placeholder(struct strbuf 
*sb,
                        return 0;
                c->padding = to_column ? -width : width;
                c->flush_type = flush_type;
+
+               if (*end == ',') {
+                       start = end + 1;
+                       end = strchr(start, ')');
+                       if (!end || end == start)
+                               return 0;
+                       if (!prefixcmp(start, "trunc)"))
+                               c->truncate = trunc_right;
+                       else if (!prefixcmp(start, "ltrunc)"))
+                               c->truncate = trunc_left;
+                       else if (!prefixcmp(start, "mtrunc)"))
+                               c->truncate = trunc_middle;
+                       else
+                               return 0;
+               } else
+                       c->truncate = trunc_none;
+
                return end - placeholder + 1;
        }
        return 0;
@@ -1302,9 +1327,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, 
/* in UTF-8 */
                total_consumed++;
        }
        len = utf8_strnwidth(local_sb.buf, -1, 1);
-       if (len > padding)
+       if (len > padding) {
+               switch (c->truncate) {
+               case trunc_left:
+                       strbuf_utf8_replace(&local_sb,
+                                           0, len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_middle:
+                       strbuf_utf8_replace(&local_sb,
+                                           padding / 2 - 1,
+                                           len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_right:
+                       strbuf_utf8_replace(&local_sb,
+                                           padding - 2, len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_none:
+                       break;
+               }
                strbuf_addstr(sb, local_sb.buf);
-       else {
+       } else {
                int sb_len = sb->len, offset = 0;
                if (c->flush_type == flush_left)
                        offset = padding - len;
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index e9d7c2c..d1e9677 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -147,6 +147,45 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting with trunc' '
+       git log --pretty="format:%<(10,trunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<\EOF | delete_trailing_dollar >expected &&
+message ..$
+message ..$
+add bar   $
+initial   $
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with ltrunc' '
+       git log --pretty="format:%<(10,ltrunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<\EOF | delete_trailing_dollar >expected &&
+..sage two$
+..sage one$
+add bar   $
+initial   $
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with mtrunc' '
+       git log --pretty="format:%<(10,mtrunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<\EOF | delete_trailing_dollar >expected &&
+mess.. two$
+mess.. one$
+add bar   $
+initial   $
+EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'right alignment formatting' '
        git log --pretty="format:%>(40)%s" >actual &&
        # complete the incomplete line at the end
diff --git a/utf8.c b/utf8.c
index ac630bc..0428e76 100644
--- a/utf8.c
+++ b/utf8.c
@@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const 
char *data, int len,
        free(tmp);
 }
 
+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
+                        const char *subst)
+{
+       struct strbuf sb_dst = STRBUF_INIT;
+       char *src = sb_src->buf;
+       char *end = src + sb_src->len;
+       char *dst;
+       int w = 0, subst_len = 0;
+
+       if (subst)
+               subst_len = strlen(subst);
+       strbuf_grow(&sb_dst, sb_src->len + subst_len);
+       dst = sb_dst.buf;
+
+       while (src < end) {
+               char *old;
+               size_t n;
+
+               while ((n = display_mode_esc_sequence_len(src))) {
+                       memcpy(dst, src, n);
+                       src += n;
+                       dst += n;
+               }
+
+               old = src;
+               n = utf8_width((const char**)&src, NULL);
+               if (!src)       /* broken utf-8, do nothing */
+                       return;
+               if (n && w >= pos && w < pos + width) {
+                       if (subst) {
+                               memcpy(dst, subst, subst_len);
+                               dst += subst_len;
+                               subst = NULL;
+                       }
+                       w += n;
+                       continue;
+               }
+               memcpy(dst, old, src - old);
+               dst += src - old;
+               w += n;
+       }
+       strbuf_setlen(&sb_dst, dst - sb_dst.buf);
+       strbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),
+                     sb_dst.len, sb_dst.alloc);
+}
+
 int is_encoding_utf8(const char *name)
 {
        if (!name)
diff --git a/utf8.h b/utf8.h
index a43ef9a..edde8ee 100644
--- a/utf8.h
+++ b/utf8.h
@@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
 void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
                             int indent, int indent2, int width);
+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,
+                        const char *subst);
 
 #ifndef NO_ICONV
 char *reencode_string_iconv(const char *in, size_t insz,
-- 
1.8.2.82.gc24b958

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