diff --git a/cache.h b/cache.h index 96055c222929e4ba307f3162b58740eccf165f2c..18aabacba53ec934e64cfe6d309cd5d0c161f91d 100644 --- a/cache.h +++ b/cache.h @@ -1996,6 +1996,8 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob #define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF) #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8) #define WS_TAB_WIDTH_MASK 077 +/* All WS_* -- when extended, adapt diff.c emit_symbol */ +#define WS_RULE_MASK 07777 extern unsigned whitespace_rule_cfg; extern unsigned whitespace_rule(const char *); extern unsigned parse_whitespace_rule(const char *); diff --git a/diff.c b/diff.c index 488096b757377b4f86109253e3b275ea54254087..e5430d56da79f56eacb55226c69778563dbc5355 100644 --- a/diff.c +++ b/diff.c @@ -561,17 +561,54 @@ static void emit_line(struct diff_options *o, const char *set, const char *reset } enum diff_symbol { + DIFF_SYMBOL_CONTEXT, + DIFF_SYMBOL_PLUS, + DIFF_SYMBOL_MINUS, DIFF_SYMBOL_NO_LF_EOF, DIFF_SYMBOL_CONTEXT_FRAGINFO, DIFF_SYMBOL_CONTEXT_MARKER, DIFF_SYMBOL_SEPARATOR }; +/* + * Flags for content lines: + * 0..12 are whitespace rules + * 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT + * 16 is marking if the line is blank at EOF + */ +#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16) +#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK) + +static void emit_line_ws_markup(struct diff_options *o, + const char *set, const char *reset, + const char *line, int len, char sign, + unsigned ws_rule, int blank_at_eof) +{ + const char *ws = NULL; + + if (o->ws_error_highlight & ws_rule) { + ws = diff_get_color_opt(o, DIFF_WHITESPACE); + if (!*ws) + ws = NULL; + } + + if (!ws) + emit_line_0(o, set, reset, sign, line, len); + else if (blank_at_eof) + /* Blank line at EOF - paint '+' as well */ + emit_line_0(o, ws, reset, sign, line, len); + else { + /* Emit just the prefix, then the rest. */ + emit_line_0(o, set, reset, sign, "", 0); + ws_check_emit(line, len, ws_rule, + o->file, set, reset, ws); + } +} static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s, - const char *line, int len) + const char *line, int len, unsigned flags) { static const char *nneof = " No newline at end of file\n"; - const char *context, *reset; + const char *context, *reset, *set; switch (s) { case DIFF_SYMBOL_NO_LF_EOF: context = diff_get_color_opt(o, DIFF_CONTEXT); @@ -593,6 +630,25 @@ static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s, diff_line_prefix(o), o->line_termination); break; + case DIFF_SYMBOL_CONTEXT: + set = diff_get_color_opt(o, DIFF_CONTEXT); + reset = diff_get_color_opt(o, DIFF_RESET); + emit_line_ws_markup(o, set, reset, line, len, ' ', + flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0); + break; + case DIFF_SYMBOL_PLUS: + set = diff_get_color_opt(o, DIFF_FILE_NEW); + reset = diff_get_color_opt(o, DIFF_RESET); + emit_line_ws_markup(o, set, reset, line, len, '+', + flags & DIFF_SYMBOL_CONTENT_WS_MASK, + flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF); + break; + case DIFF_SYMBOL_MINUS: + set = diff_get_color_opt(o, DIFF_FILE_OLD); + reset = diff_get_color_opt(o, DIFF_RESET); + emit_line_ws_markup(o, set, reset, line, len, '-', + flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0); + break; default: die("BUG: unknown diff symbol"); } @@ -609,57 +665,31 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line return ws_blank_line(line, len, ecbdata->ws_rule); } -static void emit_line_checked(const char *reset, - struct emit_callback *ecbdata, - const char *line, int len, - enum color_diff color, - unsigned ws_error_highlight, - char sign) -{ - const char *set = diff_get_color(ecbdata->color_diff, color); - const char *ws = NULL; - - if (ecbdata->opt->ws_error_highlight & ws_error_highlight) { - ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE); - if (!*ws) - ws = NULL; - } - - if (!ws) - emit_line_0(ecbdata->opt, set, reset, sign, line, len); - else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len)) - /* Blank line at EOF - paint '+' as well */ - emit_line_0(ecbdata->opt, ws, reset, sign, line, len); - else { - /* Emit just the prefix, then the rest. */ - emit_line_0(ecbdata->opt, set, reset, sign, "", 0); - ws_check_emit(line, len, ecbdata->ws_rule, - ecbdata->opt->file, set, reset, ws); - } -} - static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) { - emit_line_checked(reset, ecbdata, line, len, - DIFF_FILE_NEW, WSEH_NEW, '+'); + unsigned flags = WSEH_NEW | ecbdata->ws_rule; + if (new_blank_line_at_eof(ecbdata, line, len)) + flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF; + + emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags); } static void emit_del_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) { - emit_line_checked(reset, ecbdata, line, len, - DIFF_FILE_OLD, WSEH_OLD, '-'); + unsigned flags = WSEH_OLD | ecbdata->ws_rule; + emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags); } static void emit_context_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) { - emit_line_checked(reset, ecbdata, line, len, - DIFF_CONTEXT, WSEH_CONTEXT, ' '); + unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule; + emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags); } static void emit_hunk_header(struct emit_callback *ecbdata, @@ -683,7 +713,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata, memcmp(line, atat, 2) || !(ep = memmem(line + 2, len - 2, atat, 2))) { emit_diff_symbol(ecbdata->opt, - DIFF_SYMBOL_CONTEXT_MARKER, line, len); + DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0); return; } ep += 2; /* skip over @@ */ @@ -719,7 +749,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata, strbuf_add(&msgbuf, line + len, org_len - len); strbuf_complete_line(&msgbuf); emit_diff_symbol(ecbdata->opt, - DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len); + DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0); strbuf_release(&msgbuf); } @@ -778,7 +808,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb, data += len; } if (!endp) - emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0); + emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0); } static void emit_rewrite_diff(const char *name_a, @@ -4771,6 +4801,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) { int i; struct diff_queue_struct *q = &diff_queued_diff; + + if (WSEH_NEW & WS_RULE_MASK) + die("BUG: WS rules bit mask overlaps with diff symbol flags"); + for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (check_pair_status(p)) @@ -4861,7 +4895,7 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_PATCH) { if (separator) { - emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0); + emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); if (options->stat_sep) { /* attach patch instead of inline */ fputs(options->stat_sep, options->file); diff --git a/diff.h b/diff.h index 2d442e296f9821ea4085188602b9ea4a4ba159a6..ea66168454060199c61568e4ec88c549c54c1d84 100644 --- a/diff.h +++ b/diff.h @@ -148,9 +148,9 @@ struct diff_options { int abbrev; int ita_invisible_in_index; /* white-space error highlighting */ -#define WSEH_NEW 1 -#define WSEH_CONTEXT 2 -#define WSEH_OLD 4 +#define WSEH_NEW (1<<12) +#define WSEH_CONTEXT (1<<13) +#define WSEH_OLD (1<<14) unsigned ws_error_highlight; const char *prefix; int prefix_length;