pretty: support padding placeholders, %< %> and %><
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 <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Apr 19, 2013 at 09:08 UTC
a57523428b4c08795e793356e12afc3de812f84a
3 files changed
+246
-1
Documentation/pretty-formats.txt
+8
@@ -164,6 +164,14 @@ The placeholders are:
164
- '%x00': print a byte from a hex code
165
- '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
166
linkgit:git-shortlog[1].
167
+- '%<(<N>)': make the next placeholder take at least N columns,
168
+ padding spaces on the right if necessary
169
+- '%<|(<N>)': make the next placeholder take at least until Nth
170
+ columns, padding spaces on the right if necessary
171
+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
172
+ respectively, but padding spaces on the left
173
+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
174
+ respectively, but padding both sides (i.e. the text is centered)
175
176
NOTE: Some placeholders may depend on other options given to the
177
revision traversal engine. For example, the `%g*` reflog options will
pretty.c
+116
-1
@@ -769,16 +769,25 @@ struct chunk {
769
size_t len;
770
};
771
772
+enum flush_type {
773
+ no_flush,
774
+ flush_right,
775
+ flush_left,
776
+ flush_both
777
+};
778
+
779
struct format_commit_context {
780
const struct commit *commit;
781
const struct pretty_print_context *pretty_ctx;
782
unsigned commit_header_parsed:1;
783
unsigned commit_message_parsed:1;
784
struct signature_check signature_check;
785
+ enum flush_type flush_type;
786
char *message;
787
char *commit_encoding;
788
size_t width, indent1, indent2;
789
int auto_color;
790
+ int padding;
791
792
/* These offsets are relative to the start of the commit message. */
793
struct chunk author;
@@ -993,6 +1002,52 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
1002
return 0;
1003
}
1004
1005
+static size_t parse_padding_placeholder(struct strbuf *sb,
1006
+ const char *placeholder,
1007
+ struct format_commit_context *c)
1008
+{
1009
+ const char *ch = placeholder;
1010
+ enum flush_type flush_type;
1011
+ int to_column = 0;
1012
+
1013
+ switch (*ch++) {
1014
+ case '<':
1015
+ flush_type = flush_right;
1016
+ break;
1017
+ case '>':
1018
+ if (*ch == '<') {
1019
+ flush_type = flush_both;
1020
+ ch++;
1021
+ } else
1022
+ flush_type = flush_left;
1023
+ break;
1024
+ default:
1025
+ return 0;
1026
+ }
1027
+
1028
+ /* the next value means "wide enough to that column" */
1029
+ if (*ch == '|') {
1030
+ to_column = 1;
1031
+ ch++;
1032
+ }
1033
+
1034
+ if (*ch == '(') {
1035
+ const char *start = ch + 1;
1036
+ const char *end = strchr(start, ')');
1037
+ char *next;
1038
+ int width;
1039
+ if (!end || end == start)
1040
+ return 0;
1041
+ width = strtoul(start, &next, 10);
1042
+ if (next == start || width == 0)
1043
+ return 0;
1044
+ c->padding = to_column ? -width : width;
1045
+ c->flush_type = flush_type;
1046
+ return end - placeholder + 1;
1047
+ }
1048
+ return 0;
1049
+}
1050
+
1051
static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1052
const char *placeholder,
1053
void *context)
@@ -1057,6 +1112,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1112
return end - placeholder + 1;
1113
} else
1114
return 0;
1115
+
1116
+ case '<':
1117
+ case '>':
1118
+ return parse_padding_placeholder(sb, placeholder, c);
1119
}
1120
1121
/* these depend on the commit */
@@ -1221,6 +1280,59 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1280
return 0; /* unknown placeholder */
1281
}
1282
1283
+static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
1284
+ const char *placeholder,
1285
+ struct format_commit_context *c)
1286
+{
1287
+ struct strbuf local_sb = STRBUF_INIT;
1288
+ int total_consumed = 0, len, padding = c->padding;
1289
+ if (padding < 0) {
1290
+ const char *start = strrchr(sb->buf, '\n');
1291
+ int occupied;
1292
+ if (!start)
1293
+ start = sb->buf;
1294
+ occupied = utf8_strnwidth(start, -1, 1);
1295
+ padding = (-padding) - occupied;
1296
+ }
1297
+ while (1) {
1298
+ int modifier = *placeholder == 'C';
1299
+ int consumed = format_commit_one(&local_sb, placeholder, c);
1300
+ total_consumed += consumed;
1301
+
1302
+ if (!modifier)
1303
+ break;
1304
+
1305
+ placeholder += consumed;
1306
+ if (*placeholder != '%')
1307
+ break;
1308
+ placeholder++;
1309
+ total_consumed++;
1310
+ }
1311
+ len = utf8_strnwidth(local_sb.buf, -1, 1);
1312
+ if (len > padding)
1313
+ strbuf_addstr(sb, local_sb.buf);
1314
+ else {
1315
+ int sb_len = sb->len, offset = 0;
1316
+ if (c->flush_type == flush_left)
1317
+ offset = padding - len;
1318
+ else if (c->flush_type == flush_both)
1319
+ offset = (padding - len) / 2;
1320
+ /*
1321
+ * we calculate padding in columns, now
1322
+ * convert it back to chars
1323
+ */
1324
+ padding = padding - len + local_sb.len;
1325
+ strbuf_grow(sb, padding);
1326
+ strbuf_setlen(sb, sb_len + padding);
1327
+ memset(sb->buf + sb_len, ' ', sb->len - sb_len);
1328
+ memcpy(sb->buf + sb_len + offset, local_sb.buf,
1329
+ local_sb.len);
1330
+ }
1331
+ strbuf_release(&local_sb);
1332
+ c->flush_type = no_flush;
1333
+ return total_consumed;
1334
+}
1335
+
1336
static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
1337
const char *placeholder,
1338
void *context)
@@ -1251,7 +1363,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
1363
placeholder++;
1364
1365
orig_len = sb->len;
1254
- consumed = format_commit_one(sb, placeholder, context);
1366
+ if (((struct format_commit_context *)context)->flush_type != no_flush)
1367
+ consumed = format_and_pad_commit(sb, placeholder, context);
1368
+ else
1369
+ consumed = format_commit_one(sb, placeholder, context);
1370
if (magic == NO_MAGIC)
1371
return consumed;
1372
t/t4205-log-pretty-formats.sh
+122
@@ -99,4 +99,126 @@ test_expect_failure 'NUL termination with --stat' '
99
test_i18ncmp expected actual
100
'
101
102
+test_expect_success 'setup more commits' '
103
+ test_commit "message one" one one message-one &&
104
+ test_commit "message two" two two message-two
105
+'
106
+
107
+test_expect_success 'left alignment formatting' '
108
+ git log --pretty="format:%<(40)%s" >actual &&
109
+ # complete the incomplete line at the end
110
+ echo >>actual &&
111
+ qz_to_tab_space <<\EOF >expected &&
112
+message two Z
113
+message one Z
114
+add bar Z
115
+initial Z
116
+EOF
117
+ test_cmp expected actual
118
+'
119
+
120
+test_expect_success 'left alignment formatting at the nth column' '
121
+ git log --pretty="format:%h %<|(40)%s" >actual &&
122
+ # complete the incomplete line at the end
123
+ echo >>actual &&
124
+ qz_to_tab_space <<\EOF >expected &&
125
+fa33ab1 message two Z
126
+7cd6c63 message one Z
127
+1711bf9 add bar Z
128
+af20c06 initial Z
129
+EOF
130
+ test_cmp expected actual
131
+'
132
+
133
+test_expect_success 'left alignment formatting with no padding' '
134
+ git log --pretty="format:%<(1)%s" >actual &&
135
+ # complete the incomplete line at the end
136
+ echo >>actual &&
137
+ cat <<\EOF >expected &&
138
+message two
139
+message one
140
+add bar
141
+initial
142
+EOF
143
+ test_cmp expected actual
144
+'
145
+
146
+test_expect_success 'right alignment formatting' '
147
+ git log --pretty="format:%>(40)%s" >actual &&
148
+ # complete the incomplete line at the end
149
+ echo >>actual &&
150
+ qz_to_tab_space <<\EOF >expected &&
151
+Z message two
152
+Z message one
153
+Z add bar
154
+Z initial
155
+EOF
156
+ test_cmp expected actual
157
+'
158
+
159
+test_expect_success 'right alignment formatting at the nth column' '
160
+ git log --pretty="format:%h %>|(40)%s" >actual &&
161
+ # complete the incomplete line at the end
162
+ echo >>actual &&
163
+ qz_to_tab_space <<\EOF >expected &&
164
+fa33ab1 message two
165
+7cd6c63 message one
166
+1711bf9 add bar
167
+af20c06 initial
168
+EOF
169
+ test_cmp expected actual
170
+'
171
+
172
+test_expect_success 'right alignment formatting with no padding' '
173
+ git log --pretty="format:%>(1)%s" >actual &&
174
+ # complete the incomplete line at the end
175
+ echo >>actual &&
176
+ cat <<\EOF >expected &&
177
+message two
178
+message one
179
+add bar
180
+initial
181
+EOF
182
+ test_cmp expected actual
183
+'
184
+
185
+test_expect_success 'center alignment formatting' '
186
+ git log --pretty="format:%><(40)%s" >actual &&
187
+ # complete the incomplete line at the end
188
+ echo >>actual &&
189
+ qz_to_tab_space <<\EOF >expected &&
190
+Z message two Z
191
+Z message one Z
192
+Z add bar Z
193
+Z initial Z
194
+EOF
195
+ test_cmp expected actual
196
+'
197
+
198
+test_expect_success 'center alignment formatting at the nth column' '
199
+ git log --pretty="format:%h %><|(40)%s" >actual &&
200
+ # complete the incomplete line at the end
201
+ echo >>actual &&
202
+ qz_to_tab_space <<\EOF >expected &&
203
+fa33ab1 message two Z
204
+7cd6c63 message one Z
205
+1711bf9 add bar Z
206
+af20c06 initial Z
207
+EOF
208
+ test_cmp expected actual
209
+'
210
+
211
+test_expect_success 'center alignment formatting with no padding' '
212
+ git log --pretty="format:%><(1)%s" >actual &&
213
+ # complete the incomplete line at the end
214
+ echo >>actual &&
215
+ cat <<\EOF >expected &&
216
+message two
217
+message one
218
+add bar
219
+initial
220
+EOF
221
+ test_cmp expected actual
222
+'
223
+
224
test_done