diff: add an internal option to dual-color diffs of diffs

When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs. Let's make this easier by adding a flag in diff_options that allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is. Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now. This is a feature that was invented by git-tbdiff, and it will be used by `git range-diff` in the next commit, by offering it via a new option: `--dual-color`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Aug 13, 2018 at 04:33 UTC f7c3b4e2d8c16890c40c7e10e16cabb910f6b3ae
2 files changed +69 -15
diff.c
+68 -15
@@ -609,14 +609,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
609 ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
610 }
611
612 -static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
612 +static void emit_line_0(struct diff_options *o,
613 + const char *set, unsigned reverse, const char *reset,
614 int first, const char *line, int len)
615 {
616 int has_trailing_newline, has_trailing_carriage_return;
617 int nofirst;
618 FILE *file = o->file;
619
619 - fputs(diff_line_prefix(o), file);
620 + if (first)
621 + fputs(diff_line_prefix(o), file);
622 + else if (!len)
623 + return;
624
625 if (len == 0) {
626 has_trailing_newline = (first == '\n');
@@ -634,8 +638,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
638 }
639
640 if (len || !nofirst) {
641 + if (reverse && want_color(o->use_color))
642 + fputs(GIT_COLOR_REVERSE, file);
643 fputs(set, file);
638 - if (!nofirst)
644 + if (first && !nofirst)
645 fputc(first, file);
646 fwrite(line, len, 1, file);
647 fputs(reset, file);
@@ -649,7 +655,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
655 static void emit_line(struct diff_options *o, const char *set, const char *reset,
656 const char *line, int len)
657 {
652 - emit_line_0(o, set, reset, line[0], line+1, len-1);
658 + emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
659 }
660
661 enum diff_symbol {
@@ -1168,7 +1174,8 @@ static void dim_moved_lines(struct diff_options *o)
1174
1175 static void emit_line_ws_markup(struct diff_options *o,
1176 const char *set, const char *reset,
1171 - const char *line, int len, char sign,
1177 + const char *line, int len,
1178 + const char *set_sign, char sign,
1179 unsigned ws_rule, int blank_at_eof)
1180 {
1181 const char *ws = NULL;
@@ -1179,14 +1186,20 @@ static void emit_line_ws_markup(struct diff_options *o,
1186 ws = NULL;
1187 }
1188
1182 - if (!ws)
1183 - emit_line_0(o, set, reset, sign, line, len);
1184 - else if (blank_at_eof)
1189 + if (!ws && !set_sign)
1190 + emit_line_0(o, set, 0, reset, sign, line, len);
1191 + else if (!ws) {
1192 + /* Emit just the prefix, then the rest. */
1193 + emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
1194 + sign, "", 0);
1195 + emit_line_0(o, set, 0, reset, 0, line, len);
1196 + } else if (blank_at_eof)
1197 /* Blank line at EOF - paint '+' as well */
1186 - emit_line_0(o, ws, reset, sign, line, len);
1198 + emit_line_0(o, ws, 0, reset, sign, line, len);
1199 else {
1200 /* Emit just the prefix, then the rest. */
1189 - emit_line_0(o, set, reset, sign, "", 0);
1201 + emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
1202 + sign, "", 0);
1203 ws_check_emit(line, len, ws_rule,
1204 o->file, set, reset, ws);
1205 }
@@ -1196,7 +1209,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1209 struct emitted_diff_symbol *eds)
1210 {
1211 static const char *nneof = " No newline at end of file\n";
1199 - const char *context, *reset, *set, *meta, *fraginfo;
1212 + const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
1213 struct strbuf sb = STRBUF_INIT;
1214
1215 enum diff_symbol s = eds->s;
@@ -1209,7 +1222,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1222 context = diff_get_color_opt(o, DIFF_CONTEXT);
1223 reset = diff_get_color_opt(o, DIFF_RESET);
1224 putc('\n', o->file);
1212 - emit_line_0(o, context, reset, '\\',
1225 + emit_line_0(o, context, 0, reset, '\\',
1226 nneof, strlen(nneof));
1227 break;
1228 case DIFF_SYMBOL_SUBMODULE_HEADER:
@@ -1236,7 +1249,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1249 case DIFF_SYMBOL_CONTEXT:
1250 set = diff_get_color_opt(o, DIFF_CONTEXT);
1251 reset = diff_get_color_opt(o, DIFF_RESET);
1239 - emit_line_ws_markup(o, set, reset, line, len, ' ',
1252 + set_sign = NULL;
1253 + if (o->flags.dual_color_diffed_diffs) {
1254 + char c = !len ? 0 : line[0];
1255 +
1256 + if (c == '+')
1257 + set = diff_get_color_opt(o, DIFF_FILE_NEW);
1258 + else if (c == '@')
1259 + set = diff_get_color_opt(o, DIFF_FRAGINFO);
1260 + else if (c == '-')
1261 + set = diff_get_color_opt(o, DIFF_FILE_OLD);
1262 + }
1263 + emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
1264 flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
1265 break;
1266 case DIFF_SYMBOL_PLUS:
@@ -1263,7 +1287,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1287 set = diff_get_color_opt(o, DIFF_FILE_NEW);
1288 }
1289 reset = diff_get_color_opt(o, DIFF_RESET);
1266 - emit_line_ws_markup(o, set, reset, line, len, '+',
1290 + if (!o->flags.dual_color_diffed_diffs)
1291 + set_sign = NULL;
1292 + else {
1293 + char c = !len ? 0 : line[0];
1294 +
1295 + set_sign = set;
1296 + if (c == '-')
1297 + set = diff_get_color_opt(o, DIFF_FILE_OLD);
1298 + else if (c == '@')
1299 + set = diff_get_color_opt(o, DIFF_FRAGINFO);
1300 + else if (c != '+')
1301 + set = diff_get_color_opt(o, DIFF_CONTEXT);
1302 + }
1303 + emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
1304 flags & DIFF_SYMBOL_CONTENT_WS_MASK,
1305 flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
1306 break;
@@ -1291,7 +1328,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1328 set = diff_get_color_opt(o, DIFF_FILE_OLD);
1329 }
1330 reset = diff_get_color_opt(o, DIFF_RESET);
1294 - emit_line_ws_markup(o, set, reset, line, len, '-',
1331 + if (!o->flags.dual_color_diffed_diffs)
1332 + set_sign = NULL;
1333 + else {
1334 + char c = !len ? 0 : line[0];
1335 +
1336 + set_sign = set;
1337 + if (c == '+')
1338 + set = diff_get_color_opt(o, DIFF_FILE_NEW);
1339 + else if (c == '@')
1340 + set = diff_get_color_opt(o, DIFF_FRAGINFO);
1341 + else if (c != '-')
1342 + set = diff_get_color_opt(o, DIFF_CONTEXT);
1343 + }
1344 + emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
1345 flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
1346 break;
1347 case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1482,6 +1532,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
1532 const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
1533 const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
1534 const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1535 + const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
1536 static const char atat[2] = { '@', '@' };
1537 const char *cp, *ep;
1538 struct strbuf msgbuf = STRBUF_INIT;
@@ -1502,6 +1553,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
1553 ep += 2; /* skip over @@ */
1554
1555 /* The hunk header in fraginfo color */
1556 + if (ecbdata->opt->flags.dual_color_diffed_diffs)
1557 + strbuf_addstr(&msgbuf, reverse);
1558 strbuf_addstr(&msgbuf, frag);
1559 strbuf_add(&msgbuf, line, ep - line);
1560 strbuf_addstr(&msgbuf, reset);
diff.h
+1
@@ -95,6 +95,7 @@ struct diff_flags {
95 unsigned default_follow_renames:1;
96 unsigned stat_with_summary:1;
97 unsigned suppress_diff_headers:1;
98 + unsigned dual_color_diffed_diffs:1;
99 };
100
101 static inline void diff_flags_or(struct diff_flags *a,