Skip to content

Commit b8767f7

Browse files
committed
diff.c: --ws-error-highlight=<kind> option
Traditionally, we only cared about whitespace breakages introduced in new lines. Some people want to paint whitespace breakages on old lines, too. When they see a whitespace breakage on a new line, they can spot the same kind of whitespace breakage on the corresponding old line and want to say "Ah, those breakages are there but they were inherited from the original, so let's not touch them for now." Introduce `--ws-error-highlight=<kind>` option, that lets them pass a comma separated list of `old`, `new`, and `context` to specify what lines to highlight whitespace errors on. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0e383e1 commit b8767f7

File tree

4 files changed

+179
-16
lines changed

4 files changed

+179
-16
lines changed

Documentation/diff-options.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,16 @@ ifndef::git-format-patch[]
278278
initial indent of the line are considered whitespace errors.
279279
Exits with non-zero status if problems are found. Not compatible
280280
with --exit-code.
281+
282+
--ws-error-highlight=<kind>::
283+
Highlight whitespace errors on lines specified by <kind>
284+
in the color specified by `color.diff.whitespace`. <kind>
285+
is a comma separated list of `old`, `new`, `context`. When
286+
this option is not given, only whitespace errors in `new`
287+
lines are highlighted. E.g. `--ws-error-highlight=new,old`
288+
highlights whitespace errors on both deleted and added lines.
289+
`all` can be used as a short-hand for `old,new,context`.
290+
281291
endif::git-format-patch[]
282292

283293
--full-index::

diff.c

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -478,42 +478,57 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line
478478
return ws_blank_line(line, len, ecbdata->ws_rule);
479479
}
480480

481-
static void emit_add_line(const char *reset,
482-
struct emit_callback *ecbdata,
483-
const char *line, int len)
481+
static void emit_line_checked(const char *reset,
482+
struct emit_callback *ecbdata,
483+
const char *line, int len,
484+
enum color_diff color,
485+
unsigned ws_error_highlight,
486+
char sign)
484487
{
485-
const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
486-
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
488+
const char *set = diff_get_color(ecbdata->color_diff, color);
489+
const char *ws = NULL;
487490

488-
if (!*ws)
489-
emit_line_0(ecbdata->opt, set, reset, '+', line, len);
490-
else if (new_blank_line_at_eof(ecbdata, line, len))
491+
if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
492+
ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
493+
if (!*ws)
494+
ws = NULL;
495+
}
496+
497+
if (!ws)
498+
emit_line_0(ecbdata->opt, set, reset, sign, line, len);
499+
else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
491500
/* Blank line at EOF - paint '+' as well */
492-
emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
501+
emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
493502
else {
494503
/* Emit just the prefix, then the rest. */
495-
emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
504+
emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
496505
ws_check_emit(line, len, ecbdata->ws_rule,
497506
ecbdata->opt->file, set, reset, ws);
498507
}
499508
}
500509

501-
static void emit_del_line(const char *reset,
510+
static void emit_add_line(const char *reset,
502511
struct emit_callback *ecbdata,
503512
const char *line, int len)
504513
{
505-
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_OLD);
514+
emit_line_checked(reset, ecbdata, line, len,
515+
DIFF_FILE_NEW, WSEH_NEW, '+');
516+
}
506517

507-
emit_line_0(ecbdata->opt, set, reset, '-', line, len);
518+
static void emit_del_line(const char *reset,
519+
struct emit_callback *ecbdata,
520+
const char *line, int len)
521+
{
522+
emit_line_checked(reset, ecbdata, line, len,
523+
DIFF_FILE_OLD, WSEH_OLD, '-');
508524
}
509525

510526
static void emit_context_line(const char *reset,
511527
struct emit_callback *ecbdata,
512528
const char *line, int len)
513529
{
514-
const char *set = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
515-
516-
emit_line_0(ecbdata->opt, set, reset, ' ', line, len);
530+
emit_line_checked(reset, ecbdata, line, len,
531+
DIFF_PLAIN, WSEH_CONTEXT, ' ');
517532
}
518533

519534
static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -3250,6 +3265,7 @@ void diff_setup(struct diff_options *options)
32503265
options->rename_limit = -1;
32513266
options->dirstat_permille = diff_dirstat_permille_default;
32523267
options->context = diff_context_default;
3268+
options->ws_error_highlight = WSEH_NEW;
32533269
DIFF_OPT_SET(options, RENAME_EMPTY);
32543270

32553271
/* pathchange left =NULL by default */
@@ -3636,6 +3652,40 @@ static void enable_patch_output(int *fmt) {
36363652
*fmt |= DIFF_FORMAT_PATCH;
36373653
}
36383654

3655+
static int parse_one_token(const char **arg, const char *token)
3656+
{
3657+
return skip_prefix(*arg, token, arg) && (!**arg || **arg == ',');
3658+
}
3659+
3660+
static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
3661+
{
3662+
const char *orig_arg = arg;
3663+
unsigned val = 0;
3664+
while (*arg) {
3665+
if (parse_one_token(&arg, "none"))
3666+
val = 0;
3667+
else if (parse_one_token(&arg, "default"))
3668+
val = WSEH_NEW;
3669+
else if (parse_one_token(&arg, "all"))
3670+
val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
3671+
else if (parse_one_token(&arg, "new"))
3672+
val |= WSEH_NEW;
3673+
else if (parse_one_token(&arg, "old"))
3674+
val |= WSEH_OLD;
3675+
else if (parse_one_token(&arg, "context"))
3676+
val |= WSEH_CONTEXT;
3677+
else {
3678+
error("unknown value after ws-error-highlight=%.*s",
3679+
(int)(arg - orig_arg), orig_arg);
3680+
return 0;
3681+
}
3682+
if (*arg)
3683+
arg++;
3684+
}
3685+
opt->ws_error_highlight = val;
3686+
return 1;
3687+
}
3688+
36393689
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
36403690
{
36413691
const char *arg = av[0];
@@ -3833,6 +3883,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
38333883
DIFF_OPT_SET(options, SUBMODULE_LOG);
38343884
else if (skip_prefix(arg, "--submodule=", &arg))
38353885
return parse_submodule_opt(options, arg);
3886+
else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
3887+
return parse_ws_error_highlight(options, arg);
38363888

38373889
/* misc options */
38383890
else if (!strcmp(arg, "-z"))

diff.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ struct diff_options {
137137
int dirstat_permille;
138138
int setup;
139139
int abbrev;
140+
/* white-space error highlighting */
141+
#define WSEH_NEW 1
142+
#define WSEH_CONTEXT 2
143+
#define WSEH_OLD 4
144+
unsigned ws_error_highlight;
140145
const char *prefix;
141146
int prefix_length;
142147
const char *stat_sep;

t/t4015-diff-whitespace.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,4 +838,100 @@ test_expect_success 'diff that introduces a line with only tabs' '
838838
test_cmp expected current
839839
'
840840

841+
test_expect_success 'diff that introduces and removes ws breakages' '
842+
git reset --hard &&
843+
{
844+
echo "0. blank-at-eol " &&
845+
echo "1. blank-at-eol "
846+
} >x &&
847+
git commit -a --allow-empty -m preimage &&
848+
{
849+
echo "0. blank-at-eol " &&
850+
echo "1. still-blank-at-eol " &&
851+
echo "2. and a new line "
852+
} >x &&
853+
854+
git -c color.diff=always diff |
855+
test_decode_color >current &&
856+
857+
cat >expected <<-\EOF &&
858+
<BOLD>diff --git a/x b/x<RESET>
859+
<BOLD>index d0233a2..700886e 100644<RESET>
860+
<BOLD>--- a/x<RESET>
861+
<BOLD>+++ b/x<RESET>
862+
<CYAN>@@ -1,2 +1,3 @@<RESET>
863+
0. blank-at-eol <RESET>
864+
<RED>-1. blank-at-eol <RESET>
865+
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
866+
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
867+
EOF
868+
869+
test_cmp expected current
870+
'
871+
872+
test_expect_success 'the same with --ws-error-highlight' '
873+
git reset --hard &&
874+
{
875+
echo "0. blank-at-eol " &&
876+
echo "1. blank-at-eol "
877+
} >x &&
878+
git commit -a --allow-empty -m preimage &&
879+
{
880+
echo "0. blank-at-eol " &&
881+
echo "1. still-blank-at-eol " &&
882+
echo "2. and a new line "
883+
} >x &&
884+
885+
git -c color.diff=always diff --ws-error-highlight=default,old |
886+
test_decode_color >current &&
887+
888+
cat >expected <<-\EOF &&
889+
<BOLD>diff --git a/x b/x<RESET>
890+
<BOLD>index d0233a2..700886e 100644<RESET>
891+
<BOLD>--- a/x<RESET>
892+
<BOLD>+++ b/x<RESET>
893+
<CYAN>@@ -1,2 +1,3 @@<RESET>
894+
0. blank-at-eol <RESET>
895+
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
896+
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
897+
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
898+
EOF
899+
900+
test_cmp expected current &&
901+
902+
git -c color.diff=always diff --ws-error-highlight=all |
903+
test_decode_color >current &&
904+
905+
cat >expected <<-\EOF &&
906+
<BOLD>diff --git a/x b/x<RESET>
907+
<BOLD>index d0233a2..700886e 100644<RESET>
908+
<BOLD>--- a/x<RESET>
909+
<BOLD>+++ b/x<RESET>
910+
<CYAN>@@ -1,2 +1,3 @@<RESET>
911+
<RESET>0. blank-at-eol<RESET><BLUE> <RESET>
912+
<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
913+
<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
914+
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
915+
EOF
916+
917+
test_cmp expected current &&
918+
919+
git -c color.diff=always diff --ws-error-highlight=none |
920+
test_decode_color >current &&
921+
922+
cat >expected <<-\EOF &&
923+
<BOLD>diff --git a/x b/x<RESET>
924+
<BOLD>index d0233a2..700886e 100644<RESET>
925+
<BOLD>--- a/x<RESET>
926+
<BOLD>+++ b/x<RESET>
927+
<CYAN>@@ -1,2 +1,3 @@<RESET>
928+
0. blank-at-eol <RESET>
929+
<RED>-1. blank-at-eol <RESET>
930+
<GREEN>+1. still-blank-at-eol <RESET>
931+
<GREEN>+2. and a new line <RESET>
932+
EOF
933+
934+
test_cmp expected current
935+
'
936+
841937
test_done

0 commit comments

Comments
 (0)