Skip to content

Commit 7f81911

Browse files
committed
init
0 parents  commit 7f81911

File tree

4 files changed

+345
-0
lines changed

4 files changed

+345
-0
lines changed

diff-highlight

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

diff-so-fancy

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/bin/bash
2+
3+
###############
4+
# diff-so-fancy builds on the good-lookin' output of diff-highlight to upgrade your diffs' appearances
5+
# * Output will not be in standard patch format, but will be readable
6+
# * No pesky `+` or `-` at line-stars, making for easier copy-paste.
7+
#
8+
# Screenshot: https://github.com/paulirish/dotfiles/commit/6743b907ff58#commitcomment-13349456
9+
#
10+
#
11+
# Usage
12+
#
13+
# git diff | diff-highlight | diff-so-fancy
14+
#
15+
# Add to .gitconfig so all `git diff` uses it.
16+
# git config --global core.pager "diff-highlight | diff-so-fancy | less -r"
17+
#
18+
#
19+
# Requirements / Install
20+
#
21+
# * GNU sed. On Mac, install it with Homebrew:
22+
# brew install gnu-sed --default-names # You'll have to change below to `gsed` otherwise
23+
# * diff-highlight. It's shipped with Git, but probably not in your $PATH
24+
# ln -sf "$(brew --prefix)/share/git-core/contrib/diff-highlight/diff-highlight" ~/bin/diff-highlight
25+
# * Add some coloring to your .gitconfig:
26+
# git config --global color.diff-highlight.oldNormal "red bold"
27+
# git config --global color.diff-highlight.oldHighlight "red bold 52"
28+
# git config --global color.diff-highlight.newNormal "green bold"
29+
# git config --global color.diff-highlight.newHighlight "green bold 22"
30+
#
31+
###############
32+
33+
# TODO:
34+
# Put on NPM.
35+
36+
37+
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
38+
39+
color_code_regex=$'(\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)[m|K])?'
40+
reset_color="\x1B\[m"
41+
dim_magenta="\x1B\[38;05;146m"
42+
43+
format_diff_header () {
44+
# simplify the unified patch diff header
45+
sed -E "s/^($color_code_regex)diff --git .*$//g" | \
46+
sed -E "s/^($color_code_regex)index .*$/\
47+
\1$(print_horizontal_rule)/g" | \
48+
sed -E "s/^($color_code_regex)\+\+\+(.*)$/\1\+\+\+\5\\
49+
\1$(print_horizontal_rule)/g"
50+
}
51+
52+
colorize_context_line () {
53+
# extra color for @@ context line
54+
sed -E "s/@@$reset_color $reset_color(.*$)/@@ $dim_magenta\1/g"
55+
}
56+
57+
strip_leading_symbols () {
58+
# strip the + and -
59+
sed -E "s/^($color_code_regex)[\+\-]/\1 /g"
60+
}
61+
62+
print_horizontal_rule () {
63+
printf "%$(tput cols)s\n"|tr " " ""
64+
}
65+
66+
# run it.
67+
cat $input | format_diff_header | colorize_context_line | strip_leading_symbols

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "diff-so-fancy",
3+
"version": "0.0.0",
4+
"description": "good-lookin' output of diff-highlight to upgrade your diffs' appearances",
5+
"bin": {
6+
"diff-so-fancy": "diff-so-fancy",
7+
"diff-highlight": "diff-highlight"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/stevemao/diff-so-fancy.git"
12+
},
13+
"keywords": [
14+
"git",
15+
"diff",
16+
"fancy",
17+
"good-lookin'",
18+
"diff-highlight"
19+
],
20+
"author": "Paul Irish",
21+
"license": "ISC",
22+
"bugs": {
23+
"url": "https://github.com/stevemao/diff-so-fancy/issues"
24+
},
25+
"homepage": "https://github.com/stevemao/diff-so-fancy#readme"
26+
}

readme.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
###############
2+
# diff-so-fancy builds on the good-lookin' output of diff-highlight to upgrade your diffs' appearances
3+
# * Output will not be in standard patch format, but will be readable
4+
# * No pesky `+` or `-` at line-stars, making for easier copy-paste.
5+
#
6+
# Screenshot: https://github.com/paulirish/dotfiles/commit/6743b907ff58#commitcomment-13349456
7+
#
8+
#
9+
# Usage
10+
#
11+
# git diff | diff-highlight | diff-so-fancy
12+
#
13+
# Add to .gitconfig so all `git diff` uses it.
14+
# git config --global core.pager "diff-highlight | diff-so-fancy | less -r"
15+
#
16+
#
17+
# Requirements / Install
18+
#
19+
# * GNU sed. On Mac, install it with Homebrew:
20+
# brew install gnu-sed --default-names # You'll have to change below to `gsed` otherwise
21+
# * diff-highlight. It's shipped with Git, but probably not in your $PATH
22+
# ln -sf "$(brew --prefix)/share/git-core/contrib/diff-highlight/diff-highlight" ~/bin/diff-highlight
23+
# * Add some coloring to your .gitconfig:
24+
# git config --global color.diff-highlight.oldNormal "red bold"
25+
# git config --global color.diff-highlight.oldHighlight "red bold 52"
26+
# git config --global color.diff-highlight.newNormal "green bold"
27+
# git config --global color.diff-highlight.newHighlight "green bold 22"
28+
#
29+
###############
30+
31+
# npm
32+
# npm install -g diff-so-fancy
33+
34+
Extracted from https://github.com/paulirish/dotfiles/blob/master/bin/diff-so-fancy

0 commit comments

Comments
 (0)