File Coverage

blib/lib/String/ShowHTMLDiff.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package String::ShowHTMLDiff;
2              
3 1     1   785 use strict;
  1         3  
  1         56  
4              
5             require Exporter;
6              
7 1     1   5 use vars qw/@ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION/;
  1         2  
  1         154  
8              
9             @ISA = qw(Exporter);
10              
11             %EXPORT_TAGS = ( 'all' => [ qw(
12             html_colored_diff
13             ) ] );
14              
15             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
16              
17             @EXPORT = qw(
18            
19             );
20              
21             $VERSION = '0.01';
22              
23 1     1   10966 use Algorithm::Diff qw/sdiff/;
  0            
  0            
24              
25             sub html_colored_diff {
26             my ($string, $changed_string, $options) = @_;
27             $options ||= {};
28             my %colors = (
29             '-' => $options->{'-'} || 'diff_minus',
30             '+' => $options->{'+'} || 'diff_plus',
31             'u' => $options->{'u'} || 'diff_unchanged',
32             );
33             my $context_re = $options->{context} || qr/.*/;
34             my $gap = $options->{gap} || '';
35            
36             my @sdiff = sdiff(map {[split //, $_]} $string, $changed_string);
37             my @html;
38             my $first_while_loop = 1;
39             while (@sdiff and my ($mod, $s1, $s2) = @{shift @sdiff}) {
40             if ($mod =~ /[+-]/) {
41             push @html, _colored($s1 || $s2, $colors{$mod});
42             } else { # Must be either a change or a part of unchanged characters
43             # So take a look, whether there are more chars that should be
44             # handled in a row
45             while (@sdiff && $sdiff[0]->[0] eq $mod) {
46             $s1 .= $sdiff[0]->[1]; # if so, join all chars from the old
47             # string to $s1
48             $s2 .= $sdiff[0]->[2]; # and from the new to $s2
49             shift @sdiff; # The information of this element
50             # is already in $s1, $s2 and $mod
51             # and thus unnecessary now
52             }
53             if ($mod eq 'u') {
54             my $unchanged_part = _construct_glue(
55             $s1, $context_re, $gap, $first_while_loop, @sdiff==0
56             );
57             push @html, _colorize_string($unchanged_part, $colors{'u'});
58             } else {
59             push @html,
60             _colorize_string($s1, $colors{'-'}),
61             _colorize_string($s2, $colors{'+'});
62             }
63             }
64             $first_while_loop = 0;
65             }
66             return join "", @html;
67             }
68              
69             sub _colored {
70             my($text, $style) = @_;
71             return "$text";
72             }
73              
74             # call with _colorize_string($string, $color)
75             sub _colorize_string { join "", map {_colored($_,$_[1])} split //, $_[0] }
76              
77             sub _construct_glue {
78             my ($full_string, $context_re, $gap, $at_beginning, $at_end) = @_;
79             my ($start) = $full_string =~ /^($context_re)/;
80             my ($end) = $full_string =~ /($context_re)$/;
81             $_ ||= "" for ($start, $end);
82              
83             # Return now the shorter string of either a constructed context with a gap
84             # string or the normal string between the two differences
85             my $start_gap_end = $start . $gap . $end;
86             return length($start_gap_end) < length($full_string)
87             ? $start_gap_end
88             : $full_string;
89             }
90              
91             1;
92             __END__