contrib / diff-highlight / diff-highlighton commit Merge branch 'jc/maint-diff-patch-header' into maint (fce8b5d)
   1#!/usr/bin/perl
   2
   3# Highlight by reversing foreground and background. You could do
   4# other things like bold or underline if you prefer.
   5my $HIGHLIGHT   = "\x1b[7m";
   6my $UNHIGHLIGHT = "\x1b[27m";
   7my $COLOR = qr/\x1b\[[0-9;]*m/;
   8
   9my @window;
  10
  11while (<>) {
  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);
  23        }
  24        else {
  25                print shift @window;
  26        }
  27
  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.
  32        #
  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;
  38        }
  39}
  40
  41# Special case a single-line hunk at the end of file.
  42if (@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);
  48}
  49
  50# And then flush any remaining lines.
  51while (@window) {
  52        print shift @window;
  53}
  54
  55exit 0;
  56
  57sub show_pair {
  58        my @a = split_line(shift);
  59        my @b = split_line(shift);
  60
  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++;
  68                }
  69                elsif ($b[$pb] =~ /$COLOR/) {
  70                        $pb++;
  71                }
  72                elsif ($a[$pa] eq $b[$pb]) {
  73                        $pa++;
  74                        $pb++;
  75                }
  76                elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
  77                        $seen_plusminus = 1;
  78                        $pa++;
  79                        $pb++;
  80                }
  81                else {
  82                        last;
  83                }
  84        }
  85
  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--;
  91                }
  92                elsif ($b[$sb] =~ /$COLOR/) {
  93                        $sb--;
  94                }
  95                elsif ($a[$sa] eq $b[$sb]) {
  96                        $sa--;
  97                        $sb--;
  98                }
  99                else {
 100                        last;
 101                }
 102        }
 103
 104        print highlight(\@a, $pa, $sa);
 105        print highlight(\@b, $pb, $sb);
 106}
 107
 108sub split_line {
 109        local $_ = shift;
 110        return map { /$COLOR/ ? $_ : (split //) }
 111               split /($COLOR*)/;
 112}
 113
 114sub highlight {
 115        my ($line, $prefix, $suffix) = @_;
 116
 117        return join('',
 118                @{$line}[0..($prefix-1)],
 119                $HIGHLIGHT,
 120                @{$line}[$prefix..$suffix],
 121                $UNHIGHLIGHT,
 122                @{$line}[($suffix+1)..$#$line]
 123        );
 124}