File Coverage

blib/lib/String/ShowDiff.pm
Criterion Covered Total %
statement 43 43 100.0
branch 6 6 100.0
condition 21 24 87.5
subroutine 7 7 100.0
pod 1 1 100.0
total 78 81 96.3


line stmt bran cond sub pod time code
1             package String::ShowDiff;
2              
3 3     3   7985 use strict;
  3         6  
  3         128  
4              
5             require Exporter;
6              
7 3     3   14 use vars qw/@ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION/;
  3         6  
  3         486  
8              
9             @ISA = qw(Exporter);
10              
11             %EXPORT_TAGS = ( 'all' => [ qw(
12             ansi_colored_diff
13             ) ] );
14              
15             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
16              
17             @EXPORT = qw(
18            
19             );
20              
21             $VERSION = '0.03';
22              
23 3     3   3539 use Algorithm::Diff qw/sdiff/;
  3         25583  
  3         273  
24 3     3   6369 use Term::ANSIColor qw/colored/;
  3         50609  
  3         12091  
25              
26             sub ansi_colored_diff {
27 16     16 1 23430 my ($string, $changed_string, $options) = @_;
28 16   100     58 $options ||= {};
29 16   100     185 my %colors = (
      100        
      100        
30             '-' => $options->{'-'} || 'on_red',
31             '+' => $options->{'+'} || 'on_green',
32             'u' => $options->{'u'} || 'reset',
33             );
34 16   66     98 my $context_re = $options->{context} || qr/.*/;
35 16   100     67 my $gap = $options->{gap} || '';
36            
37 16         29 my @sdiff = sdiff(map {[split //, $_]} $string, $changed_string);
  32         432  
38 16         27221 my @ansi;
39 16         27 my $first_while_loop = 1;
40 16   66     51 while (@sdiff and my ($mod, $s1, $s2) = @{shift @sdiff}) {
  202         839  
41 202 100       550 if ($mod =~ /[+-]/) {
42 139   66     591 push @ansi, colored($s1 || $s2, $colors{$mod});
43             } else { # Must be either a change or a part of unchanged characters
44             # So take a look, whether there are more chars that should be
45             # handled in a row
46 63   100     286 while (@sdiff && $sdiff[0]->[0] eq $mod) {
47 230         331 $s1 .= $sdiff[0]->[1]; # if so, join all chars from the old
48             # string to $s1
49 230         246 $s2 .= $sdiff[0]->[2]; # and from the new to $s2
50 230         954 shift @sdiff; # The information of this element
51             # is already in $s1, $s2 and $mod
52             # and thus unnecessary now
53             }
54 63 100       126 if ($mod eq 'u') {
55 47         103 my $unchanged_part = _construct_glue(
56             $s1, $context_re, $gap, $first_while_loop, @sdiff==0
57             );
58 47         177 push @ansi, _colorize_string($unchanged_part, $colors{'u'});
59             } else {
60 16         42 push @ansi,
61             _colorize_string($s1, $colors{'-'}),
62             _colorize_string($s2, $colors{'+'});
63             }
64             }
65 202         6361 $first_while_loop = 0;
66             }
67 16         132 return join "", @ansi;
68             }
69              
70             # call with _colorize_string($string, $color)
71 79     79   679 sub _colorize_string { join "", map {colored($_,$_[1])} split //, $_[0] }
  242         4637  
72              
73             sub _construct_glue {
74 47     47   112 my ($full_string, $context_re, $gap, $at_beginning, $at_end) = @_;
75 47         338 my ($start) = $full_string =~ /^($context_re)/;
76 47         282 my ($end) = $full_string =~ /($context_re)$/;
77 47   100     321 $_ ||= "" for ($start, $end);
78              
79             # Return now the shorter string of either a constructed context with a gap
80             # string or the normal string between the two differences
81 47         98 my $start_gap_end = $start . $gap . $end;
82 47 100       169 return length($start_gap_end) < length($full_string)
83             ? $start_gap_end
84             : $full_string;
85             }
86              
87             1;
88             __END__