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. 15push@window,$_; 16next if@window<4; 17if($window[0] =~/^$COLOR*(\@| )/&& 18$window[1] =~/^$COLOR*-/&& 19$window[2] =~/^$COLOR*\+/&& 20$window[3] !~/^$COLOR*\+/) { 21print shift@window; 22 show_pair(shift@window,shift@window); 23} 24else{ 25print 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. 36if(!length) { 37local$| =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*\+/) { 46print shift@window; 47 show_pair(shift@window,shift@window); 48} 49 50# And then flush any remaining lines. 51while(@window) { 52print shift@window; 53} 54 55exit0; 56 57sub show_pair { 58my@a= split_line(shift); 59my@b= split_line(shift); 60 61# Find common prefix, taking care to skip any ansi 62# color codes. 63my$seen_plusminus; 64my($pa,$pb) = (0,0); 65while($pa<@a&&$pb<@b) { 66if($a[$pa] =~/$COLOR/) { 67$pa++; 68} 69elsif($b[$pb] =~/$COLOR/) { 70$pb++; 71} 72elsif($a[$pa]eq$b[$pb]) { 73$pa++; 74$pb++; 75} 76elsif(!$seen_plusminus&&$a[$pa]eq'-'&&$b[$pb]eq'+') { 77$seen_plusminus=1; 78$pa++; 79$pb++; 80} 81else{ 82last; 83} 84} 85 86# Find common suffix, ignoring colors. 87my($sa,$sb) = ($#a,$#b); 88while($sa>=$pa&&$sb>=$pb) { 89if($a[$sa] =~/$COLOR/) { 90$sa--; 91} 92elsif($b[$sb] =~/$COLOR/) { 93$sb--; 94} 95elsif($a[$sa]eq$b[$sb]) { 96$sa--; 97$sb--; 98} 99else{ 100last; 101} 102} 103 104print highlight(\@a,$pa,$sa); 105print highlight(\@b,$pb,$sb); 106} 107 108sub split_line { 109local$_=shift; 110returnmap{/$COLOR/?$_: (split//) } 111split/($COLOR*)/; 112} 113 114sub highlight { 115my($line,$prefix,$suffix) =@_; 116 117returnjoin('', 118@{$line}[0..($prefix-1)], 119$HIGHLIGHT, 120@{$line}[$prefix..$suffix], 121$UNHIGHLIGHT, 122@{$line}[($suffix+1)..$#$line] 123); 124}