builtin/repo: fix table alignment for UTF-8 characters

The output table from "git repo structure" is misaligned when displaying UTF-8 characters (e.g., non-ASCII glyphs). E.g.: | 仓库结构 | 值 | | -------------- | ---- | | * 引用 | | | * 计数 | 67 | The previous implementation used simple width formatting with printf() which didn't properly handle multi-byte UTF-8 characters, causing misaligned table columns when displaying repository structure information. This change modifies the stats_table_print_structure function to use strbuf_utf8_align() instead of basic printf width specifiers. This ensures proper column alignment regardless of the character encoding of the content being displayed. Also add test cases for strbuf_utf8_align(), a function newly introduced in "builtin/repo.c". Signed-off-by: Jiang Xin <worldhello.net@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jiang Xin committed Nov 15, 2025 at 08:36 UTC 7a03a10a3a746dd8565a3a0e6126f60523b41738
2 files changed +54 -4
builtin/repo.c
+17 -4
@@ -292,14 +292,20 @@ static void stats_table_print_structure(const struct stats_table *table)
292 int name_col_width = utf8_strwidth(name_col_title);
293 int value_col_width = utf8_strwidth(value_col_title);
294 struct string_list_item *item;
295 + struct strbuf buf = STRBUF_INIT;
296
297 if (table->name_col_width > name_col_width)
298 name_col_width = table->name_col_width;
299 if (table->value_col_width > value_col_width)
300 value_col_width = table->value_col_width;
301
301 - printf("| %-*s | %-*s |\n", name_col_width, name_col_title,
302 - value_col_width, value_col_title);
302 + strbuf_addstr(&buf, "| ");
303 + strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, name_col_title);
304 + strbuf_addstr(&buf, " | ");
305 + strbuf_utf8_align(&buf, ALIGN_LEFT, value_col_width, value_col_title);
306 + strbuf_addstr(&buf, " |");
307 + printf("%s\n", buf.buf);
308 +
309 printf("| ");
310 for (int i = 0; i < name_col_width; i++)
311 putchar('-');
@@ -317,9 +323,16 @@ static void stats_table_print_structure(const struct stats_table *table)
323 value = entry->value;
324 }
325
320 - printf("| %-*s | %*s |\n", name_col_width, item->string,
321 - value_col_width, value);
326 + strbuf_reset(&buf);
327 + strbuf_addstr(&buf, "| ");
328 + strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);
329 + strbuf_addstr(&buf, " | ");
330 + strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);
331 + strbuf_addstr(&buf, " |");
332 + printf("%s\n", buf.buf);
333 }
334 +
335 + strbuf_release(&buf);
336 }
337
338 static void stats_table_clear(struct stats_table *table)
t/unit-tests/u-utf8-width.c
+37
@@ -95,3 +95,40 @@ void test_utf8_width__strnwidth_cjk_with_ansi(void)
95 /* "Hello"(5) + "世界"(4) + "!"(1) = 10 */
96 cl_assert_equal_i(10, width);
97 }
98 +
99 +/*
100 + * Test the strbuf_utf8_align function with CJK characters
101 + */
102 +void test_utf8_width__strbuf_utf8_align(void)
103 +{
104 + struct strbuf buf = STRBUF_INIT;
105 +
106 + /* Test left alignment with CJK */
107 + strbuf_utf8_align(&buf, ALIGN_LEFT, 10, "你好");
108 + /* Since "你好" is 4 display columns, we need 6 more spaces to reach 10 */
109 + cl_assert_equal_s("你好 ", buf.buf);
110 + strbuf_reset(&buf);
111 +
112 + /* Test right alignment with CJK */
113 + strbuf_utf8_align(&buf, ALIGN_RIGHT, 8, "世界");
114 + /* "世界" is 4 display columns, so we need 4 leading spaces */
115 + cl_assert_equal_s(" 世界", buf.buf);
116 + strbuf_reset(&buf);
117 +
118 + /* Test center alignment with CJK */
119 + strbuf_utf8_align(&buf, ALIGN_MIDDLE, 10, "中");
120 + /* "中" is 2 display columns, so (10-2)/2 = 4 spaces on left, 4 on right */
121 + cl_assert_equal_s(" 中 ", buf.buf);
122 + strbuf_reset(&buf);
123 +
124 + strbuf_utf8_align(&buf, ALIGN_MIDDLE, 5, "中");
125 + /* "中" is 2 display columns, so (5-2)/2 = 1 spaces on left, 2 on right */
126 + cl_assert_equal_s(" 中 ", buf.buf);
127 + strbuf_reset(&buf);
128 +
129 + /* Test alignment that is smaller than string width */
130 + strbuf_utf8_align(&buf, ALIGN_LEFT, 2, "你好");
131 + /* Since "你好" is 4 display columns, it should not be truncated */
132 + cl_assert_equal_s("你好", buf.buf);
133 + strbuf_release(&buf);
134 +}