File Coverage

blib/lib/Pod/POM/View/Restructured.pm
Criterion Covered Total %
statement 162 247 65.5
branch 37 88 42.0
condition 7 39 17.9
subroutine 27 33 81.8
pod 18 21 85.7
total 251 428 58.6


line stmt bran cond sub pod time code
1             # AUTHOR: Don Owens
2             # AUTHOR: Jeff Fearn
3             # AUTHOR: Alex Muntada
4             # OWNER: 2010 Don Owens
5             # OWNER: 2016 Jeff Fearn
6             # OWNER: 2016-2018 Alex Muntada
7             # LICENSE: Perl_5
8              
9              
10 3     3   199359 use strict;
  3         25  
  3         90  
11 3     3   17 use warnings;
  3         4  
  3         77  
12 3     3   1849 use Data::Dumper ();
  3         20201  
  3         91  
13              
14 3     3   1823 use Pod::POM;
  3         69641  
  3         231  
15              
16             package Pod::POM::View::Restructured;
17              
18             our $VERSION = '1.000002'; # VERSION
19              
20 3     3   29 use base 'Pod::POM::View::Text';
  3         6  
  3         1519  
21              
22              
23             sub new {
24 4     4 1 184 my ($class, $params) = @_;
25 4 50 33     34 $params = { } unless $params and UNIVERSAL::isa($params, 'HASH');
26              
27 4   33     30 my $self = bless { seen_something => 0, title_set => 0, params => { } }, ref($class) || $class;
28              
29 4         11 my $callbacks = $params->{callbacks};
30 4 100       13 $callbacks = { } unless $callbacks;
31 4         25 $self->{callbacks} = $callbacks;
32 4         10 $self->{namespace} = delete($params->{namespace});
33              
34 4         14 return $self;
35             }
36              
37             sub convert_file {
38 2     2 1 1195 my ($self, $source_file, $title, $dest_file, $callbacks) = @_;
39              
40 2         5 my $cb;
41 2 50       8 if ($callbacks) {
42 0         0 $cb = { %{ $self->{callbacks} }, %$callbacks };
  0         0  
43             }
44             else {
45 2         6 $cb = $self->{callbacks};
46             }
47              
48 2         12 my $view = Pod::POM::View::Restructured->new({ callbacks => $cb, namespace => $self->{namespace} });
49 2         20 my $parser = Pod::POM->new;
50              
51 2 50       87 unless (-r $source_file) {
52 0         0 warn "can't read source file $source_file";
53 0         0 return;
54             }
55              
56 2         13 my $pom = $parser->parse_file($source_file);
57              
58 2 50       20958 $view->{title_set} = 1 if defined($title);
59 2         53 my $out = $pom->present($view);
60              
61 2 50       60 if (defined($title)) {
62 0         0 $out = $self->_build_header($title, '#', 1) . "\n" . $out;
63             }
64             else {
65 2         7 $title = $view->{title};
66             }
67              
68 2 50 33     12 if (defined($dest_file) and $dest_file ne '') {
69 0         0 my $out_fh;
70 0 0       0 if (UNIVERSAL::isa($dest_file, 'GLOB')) {
71 0         0 $out_fh = $dest_file;
72             }
73             else {
74 0 0       0 unless (open($out_fh, '>', $dest_file)) {
75 0         0 warn "couldn't open output file $dest_file";
76 0         0 return;
77             }
78             }
79              
80 0         0 print $out_fh $out;
81 0         0 close $out_fh;
82             }
83              
84 2         9 my $rv = { content => $out, title => $title };
85              
86 2         13 return $rv;
87             }
88              
89             sub convert_files {
90 0     0 1 0 my ($self, $file_spec, $index_file, $index_title, $out_dir) = @_;
91              
92 0         0 my $index_fh = $self->_get_file_handle($index_file, '>');
93              
94 0 0 0     0 if ($index_fh and defined($index_title) and $index_title ne '') {
      0        
95 0         0 my $header = $self->_build_header($index_title, '#', 1);
96             # my $line = '#' x length($index_title);
97             # my $header = $line . "\n" . $index_title . "\n" . $line . "\n\n";
98              
99 0         0 print $index_fh $header;
100              
101 0         0 print $index_fh "\nContents:\n\n";
102 0         0 print $index_fh ".. toctree::\n";
103 0         0 print $index_fh " :maxdepth: 1\n\n";
104             }
105              
106 0         0 my $count = 0;
107 0         0 my $toc = '';
108 0         0 foreach my $spec (@$file_spec) {
109 0         0 $count++;
110             my $data = $self->convert_file($spec->{source_file}, $spec->{title},
111 0         0 $spec->{dest_file}, $spec->{callbacks});
112              
113 0         0 my $this_title = $data->{title};
114             # print STDERR Data::Dumper->Dump([ $this_title ], [ 'this_title' ]) . "\n\n";
115              
116 0 0 0     0 unless (defined($this_title) and $this_title !~ /\A\s*\Z/) {
117 0         0 $this_title = 'section_' . $count;
118             }
119              
120 0         0 my $name = $spec->{dest_file};
121 0 0       0 if (defined($name)) {
122 0         0 $name =~ s/\.rst\Z//;
123             }
124             else {
125 0         0 ($name = $this_title) =~ s/\W/_/g;
126 0         0 my $dest_file = $out_dir . '/' . $name . '.rst';
127 0         0 my $out_fh;
128              
129 0 0       0 unless (open($out_fh, '>', $dest_file)) {
130 0         0 warn "couldn't open output file $dest_file";
131 0         0 return;
132             }
133              
134 0         0 print $out_fh $data->{content};
135 0         0 close $out_fh;
136             }
137              
138 0 0       0 unless ($spec->{no_toc}) {
139 0         0 $toc .= ' ' . $name . "\n";
140             }
141              
142 0 0 0     0 if ($index_fh and not $spec->{no_toc}) {
143 0         0 print $index_fh " " . $name . "\n";
144             }
145             }
146              
147 0 0       0 if ($index_fh) {
148 0         0 print $index_fh "\n";
149             }
150              
151 0         0 return { toc => $toc };
152             }
153              
154             sub _get_file_handle {
155 0     0   0 my ($self, $file, $mode) = @_;
156              
157 0 0       0 return unless defined $file;
158              
159 0 0 0     0 if (ref($file) and UNIVERSAL::isa($file, 'GLOB')) {
160 0         0 return $file;
161             }
162              
163 0 0       0 $mode = '<' unless $mode;
164              
165 0         0 my $fh;
166 0 0       0 if ($file ne '') {
167 0 0       0 unless (open($fh, $mode, $file)) {
168 0         0 warn "couldn't open input file $file: $!";
169 0         0 return;
170             }
171             }
172              
173 0         0 return $fh;
174             }
175              
176             sub view_pod {
177 2     2 1 52 my ($self, $node) = @_;
178              
179 2         26 return $node->content()->present($self);
180             }
181              
182             sub _generic_head {
183 4     4   10 my ($self, $node, $marker, $do_overline) = @_;
184              
185 4         21 return scalar($self->_generic_head_multi($node, $marker, $do_overline));
186             }
187              
188             sub _generic_head_multi {
189 16     16   35 my ($self, $node, $marker, $do_overline) = @_;
190              
191 16         67 my $title = $node->title()->present($self);
192 16         129 my $content = $node->content()->present($self);
193              
194 16 50       84 $title = ' ' if $title eq '';
195             # my $section_line = $marker x length($title);
196              
197 16         36 my $section = $self->_build_header($title, $marker, $do_overline) . "\n" . $content;
198              
199             # my $section = $title . "\n" . $section_line . "\n\n" . $content;
200             # if ($do_overline) {
201             # $section = $section_line . "\n" . $section;
202             # }
203              
204 16         34 $section .= "\n";
205              
206 16 100       74 return wantarray ? ($section, $content, $title) : $section;
207             }
208              
209             sub _build_header {
210 17     17   37 my ($self, $text, $marker, $do_overline) = @_;
211              
212 17         37 my $line = $marker x length($text);
213 17         34 my $header = $text . "\n" . $line . "\n";
214              
215 17 100       33 if ($do_overline) {
216 13         35 $header = $line . "\n" . $header;
217             }
218              
219 17         34 my $namespace = $self->{namespace};
220 17 50       30 if($namespace) {
221 17         25 my $a = $text;
222             # prepend the namesspace to gaurantee document wide unique names
223 17 100       80 $a = "$namespace\:\:$a" unless($text =~ /^$namespace/);
224 17         59 $a =~ s/(?:\s)/-/g;
225              
226 17         46 $header = qq{.. _$a:\n\n} . $header;
227             }
228              
229 17         76 return "\n" . $header;
230             }
231              
232             sub _do_indent {
233 19     19   41 my ($self, $text, $indent_amount, $dbg) = @_;
234              
235 19         37 my $indent = ' ' x $indent_amount;
236              
237             # $indent = "'$dbg" . $indent . "'";
238              
239 19         66 my @lines = split /\n/, $text, -1;
240 19         42 foreach my $line (@lines) {
241 68         133 $line = $indent . $line;
242             }
243              
244 19         70 return join("\n", @lines);
245             }
246              
247             sub view_head1 {
248 12     12 1 286 my ($self, $node) = @_;
249              
250 12         28 my ($section, $content, $title) = $self->_generic_head_multi($node, '*', 1);
251              
252 12 50 66     38 unless ($self->{seen_something} or $self->{title_set}) {
253 1 50       5 if ($title eq 'NAME') {
254 1         3 $self->{seen_something} = 1;
255              
256 1 50       15 if ($content =~ /\A\s*(\w+(?:::\w+)+)\s+-\s+/s) {
257 1         5 my $mod_name = $1;
258 1         3 $self->{module_name} = $mod_name;
259 1         3 $self->{title} = $mod_name;
260 1         2 $self->{title_set} = 1;
261              
262 1         4 $section = $self->_build_header($mod_name, '#', 1) . $section;
263              
264             # my $line = '#' x length($mod_name);
265             # $section = $line . "\n" . $mod_name . "\n" . $line . "\n\n" . $section;
266             }
267              
268 1         4 return $section;
269             }
270             }
271              
272 11         18 $self->{seen_something} = 1;
273 11         27 return $section;
274             }
275              
276             sub view_head2 {
277 4     4 1 93 my ($self, $node) = @_;
278              
279 4         7 $self->{seen_something} = 1;
280 4         10 return $self->_generic_head($node, '=');
281             }
282              
283             sub view_head3 {
284 0     0 1 0 my ($self, $node) = @_;
285              
286 0         0 $self->{seen_something} = 1;
287 0         0 return $self->_generic_head($node, '-');
288             }
289              
290             sub view_head4 {
291 0     0 1 0 my ($self, $node) = @_;
292              
293 0         0 $self->{seen_something} = 1;
294 0         0 return $self->_generic_head($node, '^');
295             }
296              
297             sub view_item {
298 16     16 1 400 my ($self, $node) = @_;
299              
300 16         29 $self->{seen_something} = 1;
301              
302 16         61 my $title = $node->title()->present($self);
303 16         124 my $content = $node->content()->present($self);
304              
305 16         145 $title =~ s/\A\s+//;
306 16         32 $title =~ s/\n/ /;
307 16 100       61 $title = "- $title"
308             unless $title =~ /
309             # the line starts with
310             \A
311             # single unordered bullet,
312             (?:(?:[-+] | \\[*])
313             # or ordered bullet followed by dot,
314             | [1AaIi] \.
315             # or ordered bullet within parens (first optional),
316             | \(? [1AaIi] \)
317             # then finally followed by whitespace.
318             )\s
319             /xms;
320             # Make asterisk an actual bullet
321 16         34 $title =~ s/ \A \\ [*]/*/xms;
322             # $content =~ s/\n/\n /g;
323             # $content = ' ' . $content;
324              
325 16         30 $self->{view_item_count}++;
326 16         52 $content = $self->_do_indent($content, 1, "[[view_item_$self->{view_item_count}]]");
327              
328 16         52 return "\n" . $title . "\n" . $content . "\n\n";
329             }
330              
331             sub view_over {
332 6     6 1 162 my ($self, $node) = @_;
333              
334 6         29 my $content = $node->content()->present($self);
335             # my $indent = $node->indent();
336              
337 6         72 return "\n" . $content;
338             }
339              
340             sub view_text {
341 0     0 0 0 my ($self, $node) = @_;
342              
343 0         0 my @lines = split /\n/, $node;
344 0         0 foreach my $line (@lines) {
345 0         0 $line =~ s/\A\s+//;
346             }
347              
348 0         0 return join("\n", @lines);
349             }
350              
351             sub view_textblock {
352 38     38 1 276 my ($self, $text) = @_;
353              
354 38         162 return "\n" . $text . "\n";
355             }
356              
357              
358             sub view_verbatim {
359 3     3 1 54 my ($self, $node) = @_;
360              
361             # (my $node_part = ' ' . $node) =~ s/\n/\n /g;
362 3         12 my $node_part = $self->_do_indent($node . '', 1, '[[view_verbatim]]');
363              
364 3         8 my $block_part = ".. code-block:: perl\n\n";
365 3 100       8 if (defined($self->{next_code_block})) {
366 1         3 my $lang = $self->{next_code_block};
367 1         3 delete $self->{next_code_block};
368              
369 1 50       4 if ($lang eq 'none') {
370             # FIXME: need to output a preformatted paragraph here, but no highlighting
371 0         0 $block_part = '';
372             }
373             else {
374 1         3 $block_part = ".. code-block:: $lang\n\n";
375             }
376             }
377              
378 3         8 my $content = $block_part . $node_part;
379              
380 3         24 return "\n\n" . $content . "\n\n";
381             }
382              
383             sub view_for {
384 1     1 1 32 my ($self, $node) = @_;
385              
386 1         9 my $fmt = $node->format();
387              
388             # print STDERR "got for: fmt='$fmt', text='" . $node->text() . "'\n";
389              
390 1 50       18 if ($fmt eq 'pod2rst') {
391 1         6 my $text = $node->text();
392 1 50       36 if ($text =~ /\A\s*next-code-block\s*:\s*(\S+)/) {
393 1         3 my $lang = $1;
394 1         3 $self->{next_code_block} = $lang;
395 1         3 return '';
396             }
397              
398 0         0 return "\n". $node->text() . "\n\n";
399             }
400              
401 0         0 return $self->SUPER::view_for($node);
402             }
403              
404             sub view_seq_code {
405 29     29 1 249 my ($self, $text) = @_;
406              
407 29         84 return '\ ``' . $text . '``\ ';
408             }
409              
410             sub view_seq_bold {
411 2     2 1 19 my ($self, $text) = @_;
412              
413 2         6 $text =~ s/\*/\\*/g;
414 2         3 $text =~ s/\`/\\`/g;
415              
416 2         9 return '\ **' . $text . '**\ ';
417             }
418              
419             sub view_seq_italic {
420 3     3 1 29 my ($self, $text) = @_;
421              
422 3         6 $text =~ s/\*/\\*/g;
423 3         6 $text =~ s/\`/\\`/g;
424              
425 3         11 return '\ *' . $text . '*\ ';
426             }
427              
428             sub view_seq_file {
429 0     0 1 0 my ($self, $text) = @_;
430              
431 0         0 $text =~ s/\*/\\*/g;
432 0         0 $text =~ s/\`/\\`/g;
433              
434 0         0 return '\ *' . $text . '*\ ';
435             }
436              
437             sub view_seq_text {
438 137     137 0 3040 my ($self, $node) = @_;
439              
440 137         279 my $text = $node . '';
441              
442 137         236 $text =~ s/\*/\\*/g;
443 137         213 $text =~ s/\`/\\`/g;
444              
445 137         381 return $text;
446             }
447              
448             sub view_seq_zero {
449 1     1 0 21 return '';
450             }
451              
452             sub view_seq_link {
453 11     11 1 98 my ($self, $text) = @_;
454              
455             # FIXME: determine if has label, if manpage, etc., and pass that info along to the callback,
456             # instead of just the text, e.g.,
457             # $link_cb->($label, $name, $sec, $url);
458 11         22 my $link_cb = $self->{callbacks}{link};
459 11 50       24 if ($link_cb) {
460 0         0 my ($url, $label) = $link_cb->($text);
461              
462 0 0       0 if (defined($url)) {
463 0 0 0     0 if ($url eq '' and defined($label) and $label ne '') {
    0 0        
      0        
464 0         0 $text = $label;
465             }
466             elsif (defined($label) and $label ne '') {
467 0         0 $text = qq{`$label <$url>`_};
468             }
469             else {
470 0         0 $text = qq{`$url <$url>`_};
471             }
472              
473 0         0 return $text;
474             }
475             }
476              
477 11         19 my $url = '';
478 11         16 my $label = '';
479 11         18 my $module = $text;
480 11         17 my $namespace = $self->{namespace};
481              
482 11 100       46 if ($text =~ m{\A/(.+)}) {
    100          
    100          
483 2         12 (my $section = $1) =~ s/\A"(.+)"/$1/;
484 2         7 $text = qq{`$section`_};
485             }
486             elsif ($text =~ m{\Ahttps?://}) {
487 4         10 $text = qq{`$text <$text>`_};
488             }
489             elsif ($text =~ /::/) {
490 4         7 $label = $text;
491 4 50       19 if ($text =~ /\A(.+?)\|(.+::.+)/) {
492 0         0 $label = $1;
493 0         0 $module = $2;
494             }
495              
496             # Links in this namespace are cross refereneces
497 4 100 66     33 if (($namespace) and ($text =~ /^$namespace/)) {
498 1 50       23 $module = qq{$namespace\:\:$module} unless($module =~ /^$namespace/);
499 1         41 $module =~ s/(?:\s)/-/g;
500 1         7 $text = qq{:ref:`$label <$module>`};
501             }
502             else {
503 3         9 $module = $self->_url_encode($module);
504 3         8 my $url = "http://search.cpan.org/search?query=$module&mode=module";
505 3         9 $text = qq{`$label <$url>`_};
506             }
507             }
508              
509 11         39 return $text;
510             }
511              
512             sub _url_encode {
513 3     3   8 my ($self, $str) = @_;
514              
515 3     3   23085 use bytes;
  3         6  
  3         24  
516 3         13 $str =~ s{([^A-Za-z0-9_])}{sprintf("%%%02x", ord($1))}eg;
  14         52  
517 3         11 return $str;
518             }
519              
520              
521              
522             1;
523              
524             __END__