diff-highlight: split code into module

The diff-so-fancy project is also written in perl, and most of its users pipe diffs through both diff-highlight and diff-so-fancy. It would be nice if this could be done in a single script. So let's pull most of diff-highlight's code into its own module which can be used by diff-so-fancy. In addition, we'll abstract a few basic items like reading from stdio so that a script using the module can do more processing before or after diff-highlight handles the lines. See the README update for more details. One small downside is that the diff-highlight script must now be built using the Makefile. There are ways around this, but it quickly gets into perl arcana. Let's go with the simple solution. As a bonus, our Makefile now respects the PERL_PATH variable if it is set. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Jun 15, 2017 at 12:30 UTC 0c977dbc8180892af42d7ab9235fd3e51d6c4078
5 files changed +82 -19
contrib/diff-highlight/.gitignore new
+2
@@ -0,0 +1,2 @@
1 +shebang.perl
2 +diff-highlight
contrib/diff-highlight/DiffHighlight.pm renamed
+24 -16
@@ -1,4 +1,4 @@
1 -#!/usr/bin/perl
1 +package DiffHighlight;
2
3 use 5.008;
4 use warnings FATAL => 'all';
@@ -29,13 +29,14 @@ my @removed;
29 my @added;
30 my $in_hunk;
31
32 -# Some scripts may not realize that SIGPIPE is being ignored when launching the
33 -# pager--for instance scripts written in Python.
34 -$SIG{PIPE} = 'DEFAULT';
32 +our $line_cb = sub { print @_ };
33 +our $flush_cb = sub { local $| = 1 };
34 +
35 +sub handle_line {
36 + local $_ = shift;
37
36 -while (<>) {
38 if (!$in_hunk) {
38 - print;
39 + $line_cb->($_);
40 $in_hunk = /^$GRAPH*$COLOR*\@\@ /;
41 }
42 elsif (/^$GRAPH*$COLOR*-/) {
@@ -49,7 +50,7 @@ while (<>) {
50 @removed = ();
51 @added = ();
52
52 - print;
53 + $line_cb->($_);
54 $in_hunk = /^$GRAPH*$COLOR*[\@ ]/;
55 }
56
@@ -62,15 +63,22 @@ while (<>) {
63 # place to flush. Flushing on a blank line is a heuristic that
64 # happens to match git-log output.
65 if (!length) {
65 - local $| = 1;
66 + $flush_cb->();
67 }
68 }
69
69 -# Flush any queued hunk (this can happen when there is no trailing context in
70 -# the final diff of the input).
71 -show_hunk(\@removed, \@added);
70 +sub flush {
71 + # Flush any queued hunk (this can happen when there is no trailing
72 + # context in the final diff of the input).
73 + show_hunk(\@removed, \@added);
74 +}
75
73 -exit 0;
76 +sub highlight_stdin {
77 + while (<STDIN>) {
78 + handle_line($_);
79 + }
80 + flush();
81 +}
82
83 # Ideally we would feed the default as a human-readable color to
84 # git-config as the fallback value. But diff-highlight does
@@ -88,7 +96,7 @@ sub show_hunk {
96
97 # If one side is empty, then there is nothing to compare or highlight.
98 if (!@$a || !@$b) {
91 - print @$a, @$b;
99 + $line_cb->(@$a, @$b);
100 return;
101 }
102
@@ -97,17 +105,17 @@ sub show_hunk {
105 # stupid, and only handle multi-line hunks that remove and add the same
106 # number of lines.
107 if (@$a != @$b) {
100 - print @$a, @$b;
108 + $line_cb->(@$a, @$b);
109 return;
110 }
111
112 my @queue;
113 for (my $i = 0; $i < @$a; $i++) {
114 my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
107 - print $rm;
115 + $line_cb->($rm);
116 push @queue, $add;
117 }
110 - print @queue;
118 + $line_cb->(@queue);
119 }
120
121 sub highlight_pair {
contrib/diff-highlight/Makefile
+18 -3
@@ -1,5 +1,20 @@
1 -# nothing to build
2 -all:
1 +all: diff-highlight
2
4 -test:
3 +PERL_PATH = /usr/bin/perl
4 +-include ../../config.mak
5 +
6 +PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
7 +
8 +diff-highlight: shebang.perl DiffHighlight.pm diff-highlight.perl
9 + cat $^ >$@+
10 + chmod +x $@+
11 + mv $@+ $@
12 +
13 +shebang.perl: FORCE
14 + @echo '#!$(PERL_PATH_SQ)' >$@+
15 + @cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@
16 +
17 +test: all
18 $(MAKE) -C t
19 +
20 +.PHONY: FORCE
contrib/diff-highlight/README
+30
@@ -99,6 +99,36 @@ newHighlight = "black #aaffaa"
99 ---------------------------------------------
100
101
102 +Using diff-highlight as a module
103 +--------------------------------
104 +
105 +If you want to pre- or post- process the highlighted lines as part of
106 +another perl script, you can use the DiffHighlight module. You can
107 +either "require" it or just cat the module together with your script (to
108 +avoid run-time dependencies).
109 +
110 +Your script may set up one or more of the following variables:
111 +
112 + - $DiffHighlight::line_cb - this should point to a function which is
113 + called whenever DiffHighlight has lines (which may contain
114 + highlights) to output. The default function prints each line to
115 + stdout. Note that the function may be called with multiple lines.
116 +
117 + - $DiffHighlight::flush_cb - this should point to a function which
118 + flushes the output (because DiffHighlight believes it has completed
119 + processing a logical chunk of input). The default function flushes
120 + stdout.
121 +
122 +The script may then feed lines, one at a time, to DiffHighlight::handle_line().
123 +When lines are done processing, they will be fed to $line_cb. Note that
124 +DiffHighlight may queue up many input lines (to analyze a whole hunk)
125 +before calling $line_cb. After providing all lines, call
126 +DiffHighlight::flush() to flush any unprocessed lines.
127 +
128 +If you just want to process stdin, DiffHighlight::highlight_stdin()
129 +is a convenience helper which will loop and flush for you.
130 +
131 +
132 Bugs
133 ----
134
contrib/diff-highlight/diff-highlight.perl new
+8
@@ -0,0 +1,8 @@
1 +package main;
2 +
3 +# Some scripts may not realize that SIGPIPE is being ignored when launching the
4 +# pager--for instance scripts written in Python.
5 +$SIG{PIPE} = 'DEFAULT';
6 +
7 +DiffHighlight::highlight_stdin();
8 +exit 0;