File Coverage

blib/lib/Term/HiliteDiff/_impl.pm
Criterion Covered Total %
statement 60 60 100.0
branch 25 26 96.1
condition 2 3 66.6
subroutine 10 10 100.0
pod 3 3 100.0
total 100 102 98.0


line stmt bran cond sub pod time code
1             package Term::HiliteDiff::_impl;
2             BEGIN {
3 5     5   95 $Term::HiliteDiff::_impl::VERSION = '0.10';
4             } ## no critic (NamingConventions::Capitalization)
5             # ABSTRACT: Highlights differences in text with ANSI escape codes
6 5     5   24 use strict;
  5         7  
  5         132  
7              
8             ## no critic (RequireUseWarnings)
9             ## no critic (ProhibitCascadingIfElse)
10             ## no critic (ProhibitMagicNumbers)
11             ## no critic (RequireDotMatchAnything)
12              
13 5     5   25 use constant _PREVIOUS_INPUT => 0;
  5         7  
  5         4775  
14              
15             sub new {
16 16     16 1 27 my @self;
17 16         32 $self[_PREVIOUS_INPUT] = undef;
18              
19 16         79 return bless \ @self, $_[0];
20             }
21              
22             sub hilite_diff {
23             # Arguments:
24             # 0: self
25             # 1: input string or arrayref
26              
27 22     22 1 112 return $_[0]->_do_diff( $_[1], 0 );
28             }
29              
30             sub watch {
31             # Arguments:
32             # 0: self
33             # 1: input string or arrayref
34              
35 25     25 1 100 return $_[0]->_do_diff( $_[1], 1 );
36             }
37              
38             # This library uses the following ANSI escape codes
39             # \e[s Save a cursor position
40             # \e[u Go back to the saved cursor position
41             # \e[K Erase everything from the cursor to the end of the line
42             # \e[31m Red
43             # \e[32m Green
44             # \e[33m Yellow
45             # \e[0m Turns off all formatting
46              
47             sub _parse_input {
48             # Arguments:
49             # 0: self
50             # 1: array mode
51             # 2: input string
52             #
53             # Returns:
54             # input arrayref, separator
55              
56 47 100   47   256 if ( $_[1] ) {
    100          
    100          
    100          
57 8         18 return $_[2], "\t";
58             }
59             elsif ( -1 != index( $_[2], "\t" ) ) {
60 8         38 return [ split /\t/, $_[2], -1 ], "\t";
61             }
62             elsif ( -1 != index( $_[2], '|' ) ) {
63 8         38 return [ split /\|/, $_[2], -1 ], '|';
64             }
65             elsif ( $_[2] =~ /\n(?!\z)/ ) {
66 11         55 return [ split /\n/, $_[2], -1 ], "\n";
67             }
68             else {
69 12         77 return [ split ' ', $_[2], -1 ], ' ';
70             }
71             }
72              
73             sub _color {
74             # Arguments:
75             # 0: input hunk
76              
77             # Color the region
78 54     54   179 local $^W = 0;
79 54         110 my $val = "\e[7m$_[0]\e[0m";
80              
81             # Turn off coloring over newlines
82 54         84 $val =~ s/\n/\e[0m\n\e[7m/g;
83              
84             # Remove empty colored regions
85 54         71 $val =~ s/\e\[7m\e\[0m//g;
86              
87 54         186 return $val;
88             }
89              
90             # sdiff does the primary markup work for this module. Everything else
91             # just riffs on this.
92              
93             sub _sdiff {
94 47     47   137 local $^W = 0;
95              
96             # Arguments:
97             # 0: previous input arrayref
98             # 1: input arrayref
99             # 2: redraw?:
100              
101 47         56 my $prev_max_index = $#{$_[0]};
  47         85  
102 47         55 my $new_max_index = $#{$_[1]};
  47         68  
103 47 50       101 my $max_index =
104             $prev_max_index >= $new_max_index
105             ? $prev_max_index
106             : $new_max_index;
107              
108 47         114 my @sdiff = (undef) x (1 + $max_index);
109 47         99 for my $idx ( 0 .. $max_index ) {
110              
111 144 100       366 if ( $_[0][$idx] eq $_[1][$idx] ) {
    100          
112 87 100       243 $sdiff[$idx] =
113             defined $_[1][$idx]
114             ? $_[1][$idx]
115             : '';
116             }
117             elsif ( length $_[1][$idx] ) {
118 54         129 $sdiff[$idx] = _color( $_[1][$idx] );
119             }
120             else {
121             # This was changed but it's empty so there's nothing to color.
122 3         4 $sdiff[$idx] = '';
123             }
124             }
125              
126 47         140 return \ @sdiff;
127             }
128              
129             sub _do_diff {
130             # Arguments:
131             # 0: self
132             # 1: input string or arrayref
133             # 2: redraw mode?
134              
135             # We accept either an array and then we don't parse it or we
136             # accept a string and we try to parse it.
137 47     47   92 my $array_mode = 'ARRAY' eq ref $_[1];
138              
139             # Interpret input
140 47         127 my ( $input, $separator ) =
141             $_[0]->_parse_input(
142             $array_mode,
143             $_[1]
144             );
145              
146             # Fetch previous input
147 47         126 my $prev_input = $_[0][_PREVIOUS_INPUT];
148 47   66     157 $prev_input ||= $input;
149              
150             # Diff it
151 47         109 my $sdiff = _sdiff(
152             $prev_input,
153             $input,
154             $_[2],
155             );
156              
157             # Manage redrawing over the same place on the screen
158 47 100       126 if ( $_[2] ) {
159 25 100       64 if ( ! $_[0][_PREVIOUS_INPUT] ) {
160              
161             # Save the cursor position
162 7         16 $sdiff->[0] = "\e[s$sdiff->[0]";
163             }
164             else {
165              
166             # Restore the cursor position
167 18         38 $sdiff->[0] = "\e[u$sdiff->[0]";
168             }
169             }
170              
171             # Save our "previous" state into the object
172 47         83 $_[0][_PREVIOUS_INPUT] = $input;
173              
174 47 100       216 if ( $array_mode ) {
175              
176 8 100       18 if ( $_[2] ) {
177 4         6 for ( @$sdiff ) {
178 12         15 s/(?
179 12         13 s/(?<=\e\[K)(?:\e\[K)+//g;
180 12         42 s/(?
181             }
182             }
183              
184 8         51 return $sdiff;
185             }
186             else {
187 39         94 my $output =
188             join $separator,
189             @$sdiff;
190              
191 39 100       106 if ( $_[2] ) {
192 21         61 for ( $output ) {
193 21         81 s/(?
194 21         36 s/(?<=\e\[K)(?:\e\[K)+//g;
195 21         105 s/(?
196             }
197             }
198              
199 39         251 return $output;
200             }
201             }
202              
203             # Blatantly copied this from errantstory.com
204             q[Wow, you're pretty uptight for a guy who worships a multi-armed, hermaphrodite embodiment of destruction who has a fetish for vaguely phallic shaped headgear.];
205              
206              
207              
208             =pod
209              
210             =head1 NAME
211              
212             Term::HiliteDiff::_impl - Highlights differences in text with ANSI escape codes
213              
214             =head1 VERSION
215              
216             version 0.10
217              
218             =head1 METHODS
219              
220             =head2 new
221             =method hilite_diff
222             =method watch
223              
224             =head1 AUTHOR
225              
226             Josh Jore
227              
228             =head1 COPYRIGHT AND LICENSE
229              
230             This software is copyright (c) 2011 by Josh Jore.
231              
232             This is free software; you can redistribute it and/or modify it under
233             the same terms as the Perl 5 programming language system itself.
234              
235             =cut
236              
237              
238             __END__