git am: ignore dirty submodules
[git/dscho.git] / contrib / diff-highlight / diff-highlight
blobd8938982e413a9bf994bd12386121249c888649d
1 #!/usr/bin/perl
3 # Highlight by reversing foreground and background. You could do
4 # other things like bold or underline if you prefer.
5 my $HIGHLIGHT = "\x1b[7m";
6 my $UNHIGHLIGHT = "\x1b[27m";
7 my $COLOR = qr/\x1b\[[0-9;]*m/;
9 my @window;
11 while (<>) {
12 # We highlight only single-line changes, so we need
13 # a 4-line window to make a decision on whether
14 # to highlight.
15 push @window, $_;
16 next if @window < 4;
17 if ($window[0] =~ /^$COLOR*(\@| )/ &&
18 $window[1] =~ /^$COLOR*-/ &&
19 $window[2] =~ /^$COLOR*\+/ &&
20 $window[3] !~ /^$COLOR*\+/) {
21 print shift @window;
22 show_pair(shift @window, shift @window);
24 else {
25 print shift @window;
28 # Most of the time there is enough output to keep things streaming,
29 # but for something like "git log -Sfoo", you can get one early
30 # commit and then many seconds of nothing. We want to show
31 # that one commit as soon as possible.
33 # Since we can receive arbitrary input, there's no optimal
34 # place to flush. Flushing on a blank line is a heuristic that
35 # happens to match git-log output.
36 if (!length) {
37 local $| = 1;
41 # Special case a single-line hunk at the end of file.
42 if (@window == 3 &&
43 $window[0] =~ /^$COLOR*(\@| )/ &&
44 $window[1] =~ /^$COLOR*-/ &&
45 $window[2] =~ /^$COLOR*\+/) {
46 print shift @window;
47 show_pair(shift @window, shift @window);
50 # And then flush any remaining lines.
51 while (@window) {
52 print shift @window;
55 exit 0;
57 sub show_pair {
58 my @a = split_line(shift);
59 my @b = split_line(shift);
61 # Find common prefix, taking care to skip any ansi
62 # color codes.
63 my $seen_plusminus;
64 my ($pa, $pb) = (0, 0);
65 while ($pa < @a && $pb < @b) {
66 if ($a[$pa] =~ /$COLOR/) {
67 $pa++;
69 elsif ($b[$pb] =~ /$COLOR/) {
70 $pb++;
72 elsif ($a[$pa] eq $b[$pb]) {
73 $pa++;
74 $pb++;
76 elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
77 $seen_plusminus = 1;
78 $pa++;
79 $pb++;
81 else {
82 last;
86 # Find common suffix, ignoring colors.
87 my ($sa, $sb) = ($#a, $#b);
88 while ($sa >= $pa && $sb >= $pb) {
89 if ($a[$sa] =~ /$COLOR/) {
90 $sa--;
92 elsif ($b[$sb] =~ /$COLOR/) {
93 $sb--;
95 elsif ($a[$sa] eq $b[$sb]) {
96 $sa--;
97 $sb--;
99 else {
100 last;
104 print highlight(\@a, $pa, $sa);
105 print highlight(\@b, $pb, $sb);
108 sub split_line {
109 local $_ = shift;
110 return map { /$COLOR/ ? $_ : (split //) }
111 split /($COLOR*)/;
114 sub highlight {
115 my ($line, $prefix, $suffix) = @_;
117 return join('',
118 @{$line}[0..($prefix-1)],
119 $HIGHLIGHT,
120 @{$line}[$prefix..$suffix],
121 $UNHIGHLIGHT,
122 @{$line}[($suffix+1)..$#$line]