diff-highlight: allow configurable colors

Until now, the highlighting colors were hard-coded in the script (as "reverse" and "noreverse"), and you had to edit the script to change them. This patch teaches diff-highlight to read from color.diff-highlight.* to set them. In addition, it expands the possiblities considerably by adding two features: 1. Old/new lines can be colored independently (so you can use a color scheme that complements existing line coloring). 2. Normal, unhighlighted parts of the lines can be colored, too. Technically this can be done by separately configuring color.diff.old/new and matching it to your diff-highlight colors. But you may want a different look for your highlighted diffs versus your regular diffs. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Nov 20, 2014 at 10:29 UTC bca45fbc1f8fe0daa76e840fa2ad4a9c663500a0
2 files changed +90 -13
contrib/diff-highlight/README
+41
@@ -58,6 +58,47 @@ following in your git configuration:
58 diff = diff-highlight | less
59 ---------------------------------------------
60
61 +
62 +Color Config
63 +------------
64 +
65 +You can configure the highlight colors and attributes using git's
66 +config. The colors for "old" and "new" lines can be specified
67 +independently. There are two "modes" of configuration:
68 +
69 + 1. You can specify a "highlight" color and a matching "reset" color.
70 + This will retain any existing colors in the diff, and apply the
71 + "highlight" and "reset" colors before and after the highlighted
72 + portion.
73 +
74 + 2. You can specify a "normal" color and a "highlight" color. In this
75 + case, existing colors are dropped from that line. The non-highlighted
76 + bits of the line get the "normal" color, and the highlights get the
77 + "highlight" color.
78 +
79 +If no "new" colors are specified, they default to the "old" colors. If
80 +no "old" colors are specified, the default is to reverse the foreground
81 +and background for highlighted portions.
82 +
83 +Examples:
84 +
85 +---------------------------------------------
86 +# Underline highlighted portions
87 +[color "diff-highlight"]
88 +oldHighlight = ul
89 +oldReset = noul
90 +---------------------------------------------
91 +
92 +---------------------------------------------
93 +# Varying background intensities
94 +[color "diff-highlight"]
95 +oldNormal = "black #f8cbcb"
96 +oldHighlight = "black #ffaaaa"
97 +newNormal = "black #cbeecb"
98 +newHighlight = "black #aaffaa"
99 +---------------------------------------------
100 +
101 +
102 Bugs
103 ----
104
contrib/diff-highlight/diff-highlight
+49 -13
@@ -5,8 +5,18 @@ use strict;
5
6 # Highlight by reversing foreground and background. You could do
7 # other things like bold or underline if you prefer.
8 -my $HIGHLIGHT = "\x1b[7m";
9 -my $UNHIGHLIGHT = "\x1b[27m";
8 +my @OLD_HIGHLIGHT = (
9 + color_config('color.diff-highlight.oldnormal'),
10 + color_config('color.diff-highlight.oldhighlight', "\x1b[7m"),
11 + color_config('color.diff-highlight.oldreset', "\x1b[27m")
12 +);
13 +my @NEW_HIGHLIGHT = (
14 + color_config('color.diff-highlight.newnormal', $OLD_HIGHLIGHT[0]),
15 + color_config('color.diff-highlight.newhighlight', $OLD_HIGHLIGHT[1]),
16 + color_config('color.diff-highlight.newreset', $OLD_HIGHLIGHT[2])
17 +);
18 +
19 +my $RESET = "\x1b[m";
20 my $COLOR = qr/\x1b\[[0-9;]*m/;
21 my $BORING = qr/$COLOR|\s/;
22
@@ -53,6 +63,17 @@ show_hunk(\@removed, \@added);
63
64 exit 0;
65
66 +# Ideally we would feed the default as a human-readable color to
67 +# git-config as the fallback value. But diff-highlight does
68 +# not otherwise depend on git at all, and there are reports
69 +# of it being used in other settings. Let's handle our own
70 +# fallback, which means we will work even if git can't be run.
71 +sub color_config {
72 + my ($key, $default) = @_;
73 + my $s = `git config --get-color $key 2>/dev/null`;
74 + return length($s) ? $s : $default;
75 +}
76 +
77 sub show_hunk {
78 my ($a, $b) = @_;
79
@@ -128,8 +149,8 @@ sub highlight_pair {
149 }
150
151 if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
131 - return highlight_line(\@a, $pa, $sa),
132 - highlight_line(\@b, $pb, $sb);
152 + return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
153 + highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
154 }
155 else {
156 return join('', @a),
@@ -144,15 +165,30 @@ sub split_line {
165 }
166
167 sub highlight_line {
147 - my ($line, $prefix, $suffix) = @_;
148 -
149 - return join('',
150 - @{$line}[0..($prefix-1)],
151 - $HIGHLIGHT,
152 - @{$line}[$prefix..$suffix],
153 - $UNHIGHLIGHT,
154 - @{$line}[($suffix+1)..$#$line]
155 - );
168 + my ($line, $prefix, $suffix, $theme) = @_;
169 +
170 + my $start = join('', @{$line}[0..($prefix-1)]);
171 + my $mid = join('', @{$line}[$prefix..$suffix]);
172 + my $end = join('', @{$line}[($suffix+1)..$#$line]);
173 +
174 + # If we have a "normal" color specified, then take over the whole line.
175 + # Otherwise, we try to just manipulate the highlighted bits.
176 + if (defined $theme->[0]) {
177 + s/$COLOR//g for ($start, $mid, $end);
178 + chomp $end;
179 + return join('',
180 + $theme->[0], $start, $RESET,
181 + $theme->[1], $mid, $RESET,
182 + $theme->[0], $end, $RESET,
183 + "\n"
184 + );
185 + } else {
186 + return join('',
187 + $start,
188 + $theme->[1], $mid, $theme->[2],
189 + $end
190 + );
191 + }
192 }
193
194 # Pairs are interesting to highlight only if we are going to end up