File Coverage

blib/lib/Term/Emit/Format/HTML.pm
Criterion Covered Total %
statement 95 96 98.9
branch 41 52 78.8
condition 1 2 50.0
subroutine 12 12 100.0
pod 1 1 100.0
total 150 163 92.0


line stmt bran cond sub pod time code
1             # Term::Emit::Format::HTML - Formats Term::Emit output into HTML
2             #
3             # $Id: HTML.pm 23 2009-02-13 17:41:11Z steve $
4              
5             package Term::Emit::Format::HTML;
6 3     3   26348 use warnings;
  3         6  
  3         86  
7 3     3   14 use strict;
  3         4  
  3         78  
8 3     3   69 use 5.008;
  3         12  
  3         152  
9              
10             our $VERSION = '0.0.2';
11 3     3   13 use Exporter;
  3         5  
  3         115  
12 3     3   18 use base qw/Exporter/;
  3         5  
  3         3852  
13             our @EXPORT_OK = qw/format_html/;
14             our %EXPORT_TAGS = (all => [@EXPORT_OK]);
15              
16             sub format_html {
17 20 50   20 1 13404 my $opts = ref $_[0] eq 'HASH' ? shift : {};
18 20         39 my $text = shift;
19 20         82 my @lines = split(/\n/, $text);
20 20         38 my %indent_index = ();
21 20         27 my $prior_ix = 0;
22 20         21 my $prior_he = 0;
23 20         27 my $blob = q{};
24 20         28 my @blobs = ();
25 20         35 foreach my $line (@lines) {
26 60         109 $line = _clean_line($line);
27 60 100       166 next if length $line == 0;
28 49         163 my $blip = _extract_blip($line);
29 49         347 my $he = _has_ellipsis($line);
30 49         90 my $st = _has_status($line);
31 49         80 my $ix = _amount_of_indentation($line);
32 49         101 $indent_index{$ix}++;
33              
34             # Level changed (with or without status): a blob transition
35 49 100       129 if ($ix != $prior_ix) {
    100          
    100          
36             # Close off prior blob
37 25 50       77 push @blobs, {-indent => $prior_ix,
    100          
38             -status => undef,
39             -style => $prior_he? 'h' : 'p',
40             -text => $blob,
41             } if length $blob;
42              
43             # Start this blob
44 25         30 $blob = $blip;
45 25         27 $prior_ix = $ix;
46              
47             # Finish it now?
48 25 100       61 if ($st) {
    100          
49             # Look back to close the balancing open
50 10         22 my $info = _find_rollback($blob, $ix, \@blobs);
51 10 100       23 if ($info) {
52 6         584 $info->{-status} = $st;
53             }
54             else {
55 4 50       26 push @blobs, {-indent => $ix,
56             -status => $st,
57             -style => 'h',
58             -text => $blob,
59             } if length $blob;
60             }
61 10         19 $blob = q{};
62             }
63             elsif ($he) {
64 10 50       58 push @blobs, {-indent => $ix,
65             -status => undef,
66             -style => 'h',
67             -text => $blob,
68             } if length $blob;
69 10         18 $blob = q{};
70             }
71             }
72              
73             # Same level, has status - add to & finish a multiline wrap
74             elsif ($st) {
75 12 50       27 $blob .= q{ } if length $blob;
76 12         20 $blob .= $blip;
77 12 50       27 if (length $blob) {
78 12         66 push @blobs, {-indent => $ix,
79             -status => $st,
80             -style => 'h',
81             -text => $blob,
82             };
83 12         21 $blob = q{};
84             }
85             }
86              
87             # Same level, no status, just ellipsis - add to & finish the prior blob
88             elsif ($he) {
89 9 50       20 $blob .= q{ } if length $blob;
90 9         13 $blob .= $blip;
91 9 50       19 if (length $blob) {
92 9         52 push @blobs, {-indent => $ix,
93             -status => undef,
94             -style => 'h',
95             -text => $blob,
96             };
97 9         17 $blob = q{};
98             }
99             }
100              
101             # Same level, no status, no ellipsis - we are continuing the prior blob
102             else {
103 3 100       10 $blob .= q{ } if length $blob;
104 3         24 $blob .= $blip;
105             }
106              
107 49         97 $prior_he = $he;
108             }
109              
110             # Anything left over?
111 20 50       67 push @blobs, {-indent => $prior_ix,
    100          
112             -status => undef,
113             -style => $prior_he? 'h' : 'p',
114             -text => $blob,
115             } if length $blob;
116              
117             # Determine levels from indentation
118 20         25 my $lev = 0;
119 20         75 foreach my $ix (sort {$a <=> $b} keys %indent_index) {
  23         40  
120 29         57 $indent_index{$ix} = ++$lev;
121             }
122              
123             # Make the HTML
124 20         36 my $html = q{};
125 20         30 foreach my $b (@blobs) {
126 42   50     125 my $level = $indent_index{$b->{-indent}} || 0;
127 42         74 $html .= q{ } x $level;
128 42 100       97 if ($b->{-style} eq 'h') {
129             ### TODO: handle levels > 6
130 35         53 $html .= qq{
131 35 100       82 if ($b->{-status}) {
132 22         180 my $cls = lc $b->{-status};
133 22         49 $html .= qq{ class="$cls"};
134             }
135 35         69 $html .= qq{>$b->{-text}};
136 35         81 $html .= qq{\n};
137             }
138             else {
139 7         26 $html .= qq{

$b->{-text}

\n};
140             }
141             }
142              
143 20         141 return $html;
144             }
145              
146             sub _amount_of_indentation {
147 56     56   3556 my $line = shift;
148 56 100       649 return length $1 if $line =~ m{^(\s+)\S}sxm;
149 21         37 return 0;
150             }
151              
152             sub _clean_line {
153 87     87   20083 my $line = shift;
154              
155             # Remove bullets
156 87         468 $line =~ s{^\s?[\#\@\*\+\-\.]}{}sxm;
157              
158             # Remove backspaced-over content
159 87         394 while ($line =~ s{[^\010]\010}{}sxm) {};
160              
161             # Trim trailing only
162 87         278 $line =~ s{\s+$}{}sxm;
163 87         361 return $line;
164             }
165              
166             sub _extract_blip {
167 49     49   60 my $line = shift; # presumes already cleaned line
168 49 50       598 return q{}
169             unless $line =~ m{\s* # Skip leading space
170             (.+?) # The blob we want
171             \s* # Possible trailing space
172             (\.\.\. # Maybe ellipsis
173             .*? # Maybe anything else, like prog/over
174             (\s\[\S+\])? # with [STAT]
175             )?$ # to end of line
176             }sxm;
177 49         361 return $1;
178             }
179              
180             sub _find_rollback {
181 10     10   18 my ($blob, $ix, $blobs) = @_;
182 10         11 foreach my $b (reverse @{$blobs}) {
  10         23  
183 47 100       108 if ($b->{-indent} == $ix) {
184 6 50       27 return $b
185             if $b->{-text} eq $blob;
186 0         0 last;
187             }
188             }
189 4         9 return 0;
190             }
191              
192             sub _has_ellipsis {
193 60     60   4545 my $line = shift;
194 60         483 return $line =~ m{[^\.] # Any non-dot
195             \.\.\. # Then three dots in a row
196             (.+? # Maybe anything else
197             (\s\[\S+\])? # with [STAT]
198             )?$ # to end of line
199             }sxm;
200             }
201              
202             sub _has_status {
203 57     57   3362 my $line = shift;
204 57 100       238 return $line =~ m{\s\[(\S+)\]$}sxm? $1 : 0;
205             }
206              
207             1;
208             __END__