aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authortypebrook <typebrook@gmail.com>2020-04-23 09:45:46 +0800
committertypebrook <typebrook@gmail.com>2020-04-23 09:45:46 +0800
commitdd57a4e158b0988cd78fbd5cb709e65f1439b97e (patch)
treeaebd7e3bb395bea683edc1612796223f37a1bddb
parent67abd96aff30682fed98b65ec3322fe674adc894 (diff)
parent3921aa071e9c5b78d52dbf8c335ce9ba97716d6a (diff)
Merge branch 'dev' of github.com:typebrook/settings into dev
-rwxr-xr-xtools/diff-highlight213
-rwxr-xr-xtools/install.sh2
2 files changed, 214 insertions, 1 deletions
diff --git a/tools/diff-highlight b/tools/diff-highlight
new file mode 100755
index 0000000..08c88bb
--- /dev/null
+++ b/tools/diff-highlight
@@ -0,0 +1,213 @@
1#!/usr/bin/perl
2
3use warnings FATAL => 'all';
4use strict;
5
6# Highlight by reversing foreground and background. You could do
7# other things like bold or underline if you prefer.
8my @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);
13my @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
19my $RESET = "\x1b[m";
20my $COLOR = qr/\x1b\[[0-9;]*m/;
21my $BORING = qr/$COLOR|\s/;
22
23my @removed;
24my @added;
25my $in_hunk;
26
27# Some scripts may not realize that SIGPIPE is being ignored when launching the
28# pager--for instance scripts written in Python.
29$SIG{PIPE} = 'DEFAULT';
30
31while (<>) {
32 if (!$in_hunk) {
33 print;
34 $in_hunk = /^$COLOR*\@/;
35 }
36 elsif (/^$COLOR*-/) {
37 push @removed, $_;
38 }
39 elsif (/^$COLOR*\+/) {
40 push @added, $_;
41 }
42 else {
43 show_hunk(\@removed, \@added);
44 @removed = ();
45 @added = ();
46
47 print;
48 $in_hunk = /^$COLOR*[\@ ]/;
49 }
50
51 # Most of the time there is enough output to keep things streaming,
52 # but for something like "git log -Sfoo", you can get one early
53 # commit and then many seconds of nothing. We want to show
54 # that one commit as soon as possible.
55 #
56 # Since we can receive arbitrary input, there's no optimal
57 # place to flush. Flushing on a blank line is a heuristic that
58 # happens to match git-log output.
59 if (!length) {
60 local $| = 1;
61 }
62}
63
64# Flush any queued hunk (this can happen when there is no trailing context in
65# the final diff of the input).
66show_hunk(\@removed, \@added);
67
68exit 0;
69
70# Ideally we would feed the default as a human-readable color to
71# git-config as the fallback value. But diff-highlight does
72# not otherwise depend on git at all, and there are reports
73# of it being used in other settings. Let's handle our own
74# fallback, which means we will work even if git can't be run.
75sub color_config {
76 my ($key, $default) = @_;
77 my $s = `git config --get-color $key 2>/dev/null`;
78 return length($s) ? $s : $default;
79}
80
81sub show_hunk {
82 my ($a, $b) = @_;
83
84 # If one side is empty, then there is nothing to compare or highlight.
85 if (!@$a || !@$b) {
86 print @$a, @$b;
87 return;
88 }
89
90 # If we have mismatched numbers of lines on each side, we could try to
91 # be clever and match up similar lines. But for now we are simple and
92 # stupid, and only handle multi-line hunks that remove and add the same
93 # number of lines.
94 if (@$a != @$b) {
95 print @$a, @$b;
96 return;
97 }
98
99 my @queue;
100 for (my $i = 0; $i < @$a; $i++) {
101 my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
102 print $rm;
103 push @queue, $add;
104 }
105 print @queue;
106}
107
108sub highlight_pair {
109 my @a = split_line(shift);
110 my @b = split_line(shift);
111
112 # Find common prefix, taking care to skip any ansi
113 # color codes.
114 my $seen_plusminus;
115 my ($pa, $pb) = (0, 0);
116 while ($pa < @a && $pb < @b) {
117 if ($a[$pa] =~ /$COLOR/) {
118 $pa++;
119 }
120 elsif ($b[$pb] =~ /$COLOR/) {
121 $pb++;
122 }
123 elsif ($a[$pa] eq $b[$pb]) {
124 $pa++;
125 $pb++;
126 }
127 elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
128 $seen_plusminus = 1;
129 $pa++;
130 $pb++;
131 }
132 else {
133 last;
134 }
135 }
136
137 # Find common suffix, ignoring colors.
138 my ($sa, $sb) = ($#a, $#b);
139 while ($sa >= $pa && $sb >= $pb) {
140 if ($a[$sa] =~ /$COLOR/) {
141 $sa--;
142 }
143 elsif ($b[$sb] =~ /$COLOR/) {
144 $sb--;
145 }
146 elsif ($a[$sa] eq $b[$sb]) {
147 $sa--;
148 $sb--;
149 }
150 else {
151 last;
152 }
153 }
154
155 if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
156 return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
157 highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
158 }
159 else {
160 return join('', @a),
161 join('', @b);
162 }
163}
164
165sub split_line {
166 local $_ = shift;
167 return map { /$COLOR/ ? $_ : (split //) }
168 split /($COLOR*)/;
169}
170
171sub highlight_line {
172 my ($line, $prefix, $suffix, $theme) = @_;
173
174 my $start = join('', @{$line}[0..($prefix-1)]);
175 my $mid = join('', @{$line}[$prefix..$suffix]);
176 my $end = join('', @{$line}[($suffix+1)..$#$line]);
177
178 # If we have a "normal" color specified, then take over the whole line.
179 # Otherwise, we try to just manipulate the highlighted bits.
180 if (defined $theme->[0]) {
181 s/$COLOR//g for ($start, $mid, $end);
182 chomp $end;
183 return join('',
184 $theme->[0], $start, $RESET,
185 $theme->[1], $mid, $RESET,
186 $theme->[0], $end, $RESET,
187 "\n"
188 );
189 } else {
190 return join('',
191 $start,
192 $theme->[1], $mid, $theme->[2],
193 $end
194 );
195 }
196}
197
198# Pairs are interesting to highlight only if we are going to end up
199# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting
200# is just useless noise. We can detect this by finding either a matching prefix
201# or suffix (disregarding boring bits like whitespace and colorization).
202sub is_pair_interesting {
203 my ($a, $pa, $sa, $b, $pb, $sb) = @_;
204 my $prefix_a = join('', @$a[0..($pa-1)]);
205 my $prefix_b = join('', @$b[0..($pb-1)]);
206 my $suffix_a = join('', @$a[($sa+1)..$#$a]);
207 my $suffix_b = join('', @$b[($sb+1)..$#$b]);
208
209 return $prefix_a !~ /^$COLOR*-$BORING*$/ ||
210 $prefix_b !~ /^$COLOR*\+$BORING*$/ ||
211 $suffix_a !~ /^$BORING*$/ ||
212 $suffix_b !~ /^$BORING*$/;
213}
diff --git a/tools/install.sh b/tools/install.sh
index f16b2dd..0b59e28 100755
--- a/tools/install.sh
+++ b/tools/install.sh
@@ -16,7 +16,7 @@ if [ ! -d $SETTING_DIR ]; then
16 } 16 }
17fi 17fi
18 18
19sed -i "\^# $REPO^, /^$/ d" $RCFILE 19sed -i "\^# $REPO^, /^$/ d" $RCFILE
20echo " 20echo "
21# $REPO 21# $REPO
22export SETTING_DIR=$SETTING_DIR 22export SETTING_DIR=$SETTING_DIR