Skip to content

Commit 77de3bd

Browse files
Update DiffHighlight.pm to (hopefully) address #199
1 parent 4786a8e commit 77de3bd

File tree

1 file changed

+76
-36
lines changed

1 file changed

+76
-36
lines changed

lib/DiffHighlight.pm

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package DiffHighlight;
33
use 5.008;
44
use warnings FATAL => 'all';
55
use strict;
6-
use Encode;
7-
use File::Spec; # For devnull
86

9-
my $NULL = File::Spec->devnull(); # get correct value for unix/win
7+
# Use the correct value for both UNIX and Windows (/dev/null vs nul)
8+
use File::Spec;
9+
10+
my $NULL = File::Spec->devnull();
1011

1112
# Highlight by reversing foreground and background. You could do
1213
# other things like bold or underline if you prefer.
@@ -21,41 +22,88 @@ our @NEW_HIGHLIGHT = (
2122
$OLD_HIGHLIGHT[2],
2223
);
2324

25+
26+
2427
my $RESET = "\x1b[m";
2528
my $COLOR = qr/\x1b\[[0-9;]*m/;
2629
my $BORING = qr/$COLOR|\s/;
2730

28-
# The patch portion of git log -p --graph should only ever have preceding | and
29-
# not / or \ as merge history only shows up on the commit line.
30-
my $GRAPH = qr/$COLOR?\|$COLOR?\s+/;
31-
3231
my @removed;
3332
my @added;
3433
my $in_hunk;
34+
my $graph_indent = 0;
3535

3636
our $line_cb = sub { print @_ };
3737
our $flush_cb = sub { local $| = 1 };
3838

39-
sub handle_line {
39+
# Count the visible width of a string, excluding any terminal color sequences.
40+
sub visible_width {
4041
local $_ = shift;
42+
my $ret = 0;
43+
while (length) {
44+
if (s/^$COLOR//) {
45+
# skip colors
46+
} elsif (s/^.//) {
47+
$ret++;
48+
}
49+
}
50+
return $ret;
51+
}
52+
53+
# Return a substring of $str, omitting $len visible characters from the
54+
# beginning, where terminal color sequences do not count as visible.
55+
sub visible_substr {
56+
my ($str, $len) = @_;
57+
while ($len > 0) {
58+
if ($str =~ s/^$COLOR//) {
59+
next
60+
}
61+
$str =~ s/^.//;
62+
$len--;
63+
}
64+
return $str;
65+
}
66+
67+
sub handle_line {
68+
my $orig = shift;
69+
local $_ = $orig;
70+
71+
# match a graph line that begins a commit
72+
if (/^(?:$COLOR?\|$COLOR?[ ])* # zero or more leading "|" with space
73+
$COLOR?\*$COLOR?[ ] # a "*" with its trailing space
74+
(?:$COLOR?\|$COLOR?[ ])* # zero or more trailing "|"
75+
[ ]* # trailing whitespace for merges
76+
/x) {
77+
my $graph_prefix = $&;
78+
79+
# We must flush before setting graph indent, since the
80+
# new commit may be indented differently from what we
81+
# queued.
82+
flush();
83+
$graph_indent = visible_width($graph_prefix);
84+
85+
} elsif ($graph_indent) {
86+
if (length($_) < $graph_indent) {
87+
$graph_indent = 0;
88+
} else {
89+
$_ = visible_substr($_, $graph_indent);
90+
}
91+
}
4192

4293
if (!$in_hunk) {
43-
$line_cb->($_);
44-
$in_hunk = /^$GRAPH*$COLOR*\@\@ /;
94+
$line_cb->($orig);
95+
$in_hunk = /^$COLOR*\@\@ /;
4596
}
46-
elsif (/^$GRAPH*$COLOR*-/) {
47-
push @removed, $_;
97+
elsif (/^$COLOR*-/) {
98+
push @removed, $orig;
4899
}
49-
elsif (/^$GRAPH*$COLOR*\+/) {
50-
push @added, $_;
100+
elsif (/^$COLOR*\+/) {
101+
push @added, $orig;
51102
}
52103
else {
53-
show_hunk(\@removed, \@added);
54-
@removed = ();
55-
@added = ();
56-
57-
$line_cb->($_);
58-
$in_hunk = /^$GRAPH*$COLOR*[\@ ]/;
104+
flush();
105+
$line_cb->($orig);
106+
$in_hunk = /^$COLOR*[\@ ]/;
59107
}
60108

61109
# Most of the time there is enough output to keep things streaming,
@@ -75,6 +123,8 @@ sub flush {
75123
# Flush any queued hunk (this can happen when there is no trailing
76124
# context in the final diff of the input).
77125
show_hunk(\@removed, \@added);
126+
@removed = ();
127+
@added = ();
78128
}
79129

80130
sub highlight_stdin {
@@ -125,7 +175,6 @@ sub show_hunk {
125175
sub highlight_pair {
126176
my @a = split_line(shift);
127177
my @b = split_line(shift);
128-
my $opts = shift();
129178

130179
# Find common prefix, taking care to skip any ansi
131180
# color codes.
@@ -170,18 +219,9 @@ sub highlight_pair {
170219
}
171220
}
172221

173-
my @OLD_COLOR_SPEC = @OLD_HIGHLIGHT;
174-
my @NEW_COLOR_SPEC = @NEW_HIGHLIGHT;
175-
176-
# If we're only highlight the differences temp disable the old/new normal colors
177-
if ($opts->{'only_diff'}) {
178-
$OLD_COLOR_SPEC[0] = '';
179-
$NEW_COLOR_SPEC[0] = '';
180-
}
181-
182222
if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
183-
return highlight_line(\@a, $pa, $sa, \@OLD_COLOR_SPEC),
184-
highlight_line(\@b, $pb, $sb, \@NEW_COLOR_SPEC);
223+
return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
224+
highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
185225
}
186226
else {
187227
return join('', @a),
@@ -194,8 +234,8 @@ sub highlight_pair {
194234
# or "+"
195235
sub split_line {
196236
local $_ = shift;
197-
return eval { $_ = Encode::decode('UTF-8', $_, 1); 1 } ?
198-
map { Encode::encode('UTF-8', $_) }
237+
return utf8::decode($_) ?
238+
map { utf8::encode($_); $_ }
199239
map { /$COLOR/ ? $_ : (split //) }
200240
split /($COLOR+)/ :
201241
map { /$COLOR/ ? $_ : (split //) }
@@ -240,8 +280,8 @@ sub is_pair_interesting {
240280
my $suffix_a = join('', @$a[($sa+1)..$#$a]);
241281
my $suffix_b = join('', @$b[($sb+1)..$#$b]);
242282

243-
return $prefix_a !~ /^$GRAPH*$COLOR*-$BORING*$/ ||
244-
$prefix_b !~ /^$GRAPH*$COLOR*\+$BORING*$/ ||
283+
return visible_substr($prefix_a, $graph_indent) !~ /^$COLOR*-$BORING*$/ ||
284+
visible_substr($prefix_b, $graph_indent) !~ /^$COLOR*\+$BORING*$/ ||
245285
$suffix_a !~ /^$BORING*$/ ||
246286
$suffix_b !~ /^$BORING*$/;
247287
}

0 commit comments

Comments
 (0)