File Coverage

blib/lib/App/sdview/Output/Pod.pm
Criterion Covered Total %
statement 151 163 92.6
branch 38 44 86.3
condition n/a
subroutine 19 22 86.3
pod 0 13 0.0
total 208 242 85.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2021-2024 -- leonerd@leonerd.org.uk
5              
6 2     2   1440 use v5.26;
  2         10  
7 2     2   16 use warnings;
  2         4  
  2         151  
8              
9 2     2   14 use Object::Pad 0.807;
  2         19  
  2         123  
10              
11             package App::sdview::Output::Pod 0.20;
12             class App::sdview::Output::Pod :strict(params);
13              
14 2     2   2168 apply App::sdview::Output;
  2         7  
  2         175  
15              
16 2     2   17 use constant format => "Pod";
  2         6  
  2         11943  
17              
18             =head1 NAME
19              
20             C - generate Pod output from L
21              
22             =head1 SYNOPSIS
23              
24             $ sdview README.md -o Pod > README.pod
25              
26             =head1 DESCRIPTION
27              
28             This output module adds to L the ability to output text in Pod
29             formatting. Given a Pod file as input, the output should be relatively
30             similar, up to minor details like whitespacing. Given input in some other
31             format, it will do a reasonable job attempting to represent most of the
32             structure and formatting.
33              
34             As an extension it will output underline formatting using the C...E>
35             code, which is recognised by L.
36              
37             =cut
38              
39             field $_printed_pod;
40              
41 3     3 0 7 method output_head1 ( $para ) { $self->_output_head( "=head1", $para ); }
  3         11  
  3         6  
  3         5  
  3         14  
42 1     1 0 3 method output_head2 ( $para ) { $self->_output_head( "=head2", $para ); }
  1         3  
  1         2  
  1         2  
  1         4  
43 0     0 0 0 method output_head3 ( $para ) { $self->_output_head( "=head3", $para ); }
  0         0  
  0         0  
  0         0  
  0         0  
44 0     0 0 0 method output_head4 ( $para ) { $self->_output_head( "=head4", $para ); }
  0         0  
  0         0  
  0         0  
  0         0  
45              
46 4     4   9 method _output_head ( $leader, $para )
  4         12  
  4         8  
  4         9  
  4         7  
47             {
48 4         22 $self->maybe_blank;
49              
50 4         20 $self->say( $leader, " ", $self->_convert_str( $para->text ) );
51 4         26 $_printed_pod = 1;
52             }
53              
54 8     8 0 15 method output_plain ( $para )
  8         21  
  8         15  
  8         13  
55             {
56 8 100       31 $self->say( "=pod" ), $_printed_pod = 1 unless $_printed_pod;
57 8         35 $self->maybe_blank;
58              
59 8         31 $self->say( $self->_convert_str( $para->text ) );
60             }
61              
62 1     1 0 3 method output_verbatim ( $para )
  1         4  
  1         3  
  1         2  
63             {
64 1 50       6 $self->say( "=pod" ), $_printed_pod = 1 unless $_printed_pod;
65 1         6 $self->maybe_blank;
66              
67 1         5 $self->say( " ", $_ ) for split m/\n/, $para->text;
68             }
69              
70 1     1 0 3 method output_list_bullet ( $para ) { $self->_output_list( $para ); }
  1         3  
  1         3  
  1         2  
  1         6  
71 1     1 0 2 method output_list_number ( $para ) { $self->_output_list( $para ); }
  1         3  
  1         3  
  1         2  
  1         5  
72 1     1 0 4 method output_list_text ( $para ) { $self->_output_list( $para ); }
  1         4  
  1         3  
  1         2  
  1         6  
73              
74 3     3   6 method _output_list ( $para )
  3         7  
  3         6  
  3         6  
75             {
76 3         13 $self->maybe_blank;
77              
78 3         13 $self->say( "=over ", $para->indent );
79 3         9 $self->say;
80              
81 3         12 my @items = $para->items;
82 3         13 foreach my $idx ( 0 .. $#items ) {
83 10         25 my $item = $items[$idx];
84              
85 10 100       101 if( $item->type ne "item" ) {
    100          
    100          
    50          
86             # Non-item has no leader
87             }
88             elsif( $para->listtype eq "bullet" ) {
89 3         14 $self->say( "=item *" );
90 3         8 $self->say;
91             }
92             elsif( $para->listtype eq "number" ) {
93 3         17 $self->say( sprintf "=item %d.", $idx + 1 );
94 3         8 $self->say;
95             }
96             elsif( $para->listtype eq "text" ) {
97 3         11 $self->say( sprintf "=item %s", $self->_convert_str( $item->term ) );
98 3         9 $self->say;
99             }
100              
101 10         79 $self->say( $self->_convert_str( $item->text ) );
102 10         29 $self->say;
103             }
104              
105 3         10 $self->say( "=back" );
106             }
107              
108 0     0 0 0 field $table_style :param :reader;
  0         0  
109              
110 2     2 0 3 method output_table ( $para )
  2         5  
  2         4  
  2         3  
111             {
112 2         10 $self->maybe_blank;
113              
114 2 50       13 my $method = $self->can( "output_table_$table_style" ) or return;
115 2         7 $self->$method( $para );
116             }
117              
118 1     1 0 128 method output_table_md ( $para )
  1         9  
  1         2  
  1         3  
119             {
120 1         7 $self->say( "=begin table md" );
121 1         4 $self->say;
122              
123 1         2 my $first = 1;
124 1         7 foreach my $row ( $para->rows ) {
125 2         8 my @cells = @$row;
126 2         5 $self->say( join " | ", map { $self->_convert_str( $_->text ) } @cells );
  6         29  
127              
128 2 100       10 next unless $first;
129              
130             my @aligns = map {
131 1         3 my $n = length $_->text;
  3         9  
132 3 50       19 $n = 3 if $n < 3;
133 3 50       10 $_->align eq "centre" ? ":".("-"x($n-2)).":" :
    50          
134             $_->align eq "right" ? ("-"x($n-1)).":" :
135             ("-"x $n );
136             } @cells;
137 1         7 $self->say( join " | ", @aligns );
138 1         4 undef $first;
139             }
140              
141 1         5 $self->say;
142 1         3 $self->say( "=end table" );
143             }
144              
145 1     1 0 2 method output_table_mediawiki ( $para )
  1         3  
  1         1  
  1         2  
146             {
147 1         3 $self->say( "=begin table mediawiki" );
148 1         3 $self->say;
149              
150 1         1 my $first = 1;
151 1         3 foreach my $row ( $para->rows ) {
152 2 100       11 $self->say( "|-" ) unless $first;
153              
154 2         5 my @cells = @$row;
155             $self->say(
156             $_->heading ? "!" : "|",
157             " ",
158             $self->_convert_str( $_->text )
159 2 100       7 ) for @cells;
160              
161 2         37 undef $first;
162             }
163              
164 1         3 $self->say;
165 1         3 $self->say( "=end table" );
166             }
167              
168 37     37   67 method _convert_str ( $s )
  37         77  
  37         62  
  37         100  
169             {
170 37         70 my $ret = "";
171              
172             # Paint extra "nobreak" tags on extents separated by NBSP, but leave the
173             # NBSP itself in the string
174 37         148 while( $s =~ m/\S+(?:\xA0+\S+)+/g ) {
175 1         29 $s->apply_tag( $-[0], $+[0]-$-[0], nobreak => 1 );
176             }
177              
178             # TODO: This sucks for nested tags
179             $s->iter_substr_nooverlap(
180 47     47   3614 sub ( $substr, %tags ) {
  47         107  
  47         84  
  47         79  
181 47         112 $substr =~ s/\xA0/ /g;
182              
183             # Escape any literal '<'s that would otherwise break
184 47         104 my $pod = $substr =~ s/[A-Z]\K/gr;
185              
186 47         84 my $count = 1;
187 47         197 $count++ while index( $pod, ">"x$count ) > -1;
188              
189 47 100       186 my ( $open, $close ) =
190             ( $count == 1 ) ? ( "<", ">" ) : ( "<"x$count . " ", " " . ">"x$count );
191              
192 47 100       128 if( my $link = $tags{link} ) {
193             # TODO: This is even suckier than the bit in the parser
194 2 100       11 if( $link->{uri} eq "https://metacpan.org/pod/$substr" ) {
195 1         3 $pod = "L$open$substr$close";
196             }
197             else {
198 1         28 $pod = "L$open$pod|$link->{uri}$close";
199             }
200             }
201              
202 47 100       118 $pod = "C$open$pod$close" if $tags{monospace};
203 47 100       108 $pod = "B$open$pod$close" if $tags{bold};
204 47 100       138 $pod = "I$open$pod$close" if $tags{italic};
205 47 100       118 $pod = "F$open$pod$close" if $tags{file};
206 47 100       123 $pod = "U$open$pod$close" if $tags{underline};
207              
208 47 100       142 $pod = "S$open$pod$close" if $tags{nobreak};
209              
210 47         218 $ret .= $pod;
211             }
212 37         664 );
213              
214 37         942 return $ret;
215             }
216              
217             =head1 AUTHOR
218              
219             Paul Evans
220              
221             =cut
222              
223             0x55AA;