File Coverage

lib/OODoc/Format/Pod.pm
Criterion Covered Total %
statement 51 286 17.8
branch 19 194 9.7
condition 0 19 0.0
subroutine 10 37 27.0
pod 18 26 69.2
total 98 562 17.4


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution OODoc version 3.05.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2003-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12              
13             package OODoc::Format::Pod;{
14             our $VERSION = '3.05';
15             }
16              
17 2     2   94812 use parent 'OODoc::Format';
  2         255  
  2         18  
18              
19 2     2   188 use strict;
  2         4  
  2         48  
20 2     2   9 use warnings;
  2         3  
  2         107  
21 2     2   9 use utf8;
  2         4  
  2         15  
22              
23 2     2   65 use Log::Report 'oodoc';
  2         3  
  2         16  
24              
25 2     2   1116 use File::Spec::Functions qw/catfile/;
  2         642  
  2         147  
26 2     2   89 use List::Util qw/max/;
  2         5  
  2         141  
27 2     2   1215 use Pod::Escapes qw/e2char/;
  2         8896  
  2         12316  
28              
29             #--------------------
30              
31             sub init($)
32 0     0 0 0 { my ($self, $args) = @_;
33 0   0     0 $args->{format} //= 'pod';
34 0         0 $self->SUPER::init($args);
35             }
36              
37             #--------------------
38              
39             sub cleanup($$%)
40 0     0 1 0 { my ($self, $manual, $string, %args) = @_;
41             $manual->parser->cleanupPod($manual, $string, %args,
42 0     0   0 create_link => sub { $self->link(@_) },
43 0         0 );
44             }
45              
46              
47             sub link($$;$$)
48 0     0 1 0 { my ($any, $manual, $object, $text) = @_;
49              
50 0 0       0 $object = $object->subroutine if $object->isa('OODoc::Text::Option');
51 0 0       0 $object = $object->subroutine if $object->isa('OODoc::Text::Default');
52 0 0       0 $object = $object->container if $object->isa('OODoc::Text::Example');
53 0 0       0 $object = $object->container if $object->isa('OODoc::Text::Subroutine');
54 0 0       0 $text = defined $text ? "$text|" : '';
55              
56 0 0       0 return "L<$text$object>"
57             if $object->isa('OODoc::Manual');
58              
59 0 0       0 $object->isa('OODoc::Text::Structure')
60             or error __x"cannot link to a {package}", package => ref $object;
61              
62 0 0       0 my $manlink = defined $manual ? $object->manual.'/' : '';
63 0         0 qq(L<$text$manlink"$object">);
64             }
65              
66              
67             sub createManual($@)
68 0     0 1 0 { my ($self, %args) = @_;
69 0 0       0 my $manual = $args{manual} or panic;
70 0         0 my $podname = $manual->source =~ s/\.pm$/.pod/r;
71 0         0 my $tmpname = $podname . 't';
72              
73 0         0 my $tmpfile = catfile $self->workdir, $tmpname;
74 0         0 my $podfile = catfile $self->workdir, $podname;
75              
76 0 0       0 open my $output, '>:encoding(UTF-8)', $tmpfile
77             or fault __x"cannot write prelimary pod manual to {file}", file => $tmpfile;
78              
79             $self->_formatManual(
80             manual => $manual,
81             output => $output,
82             append => $args{append},
83 0         0 %args
84             );
85              
86 0         0 $output->close;
87              
88 0         0 $self->simplifyPod($tmpfile, $podfile);
89 0         0 unlink $tmpfile;
90              
91 0         0 $self->manifest->add($podfile);
92              
93 0         0 $self;
94             }
95              
96             sub _formatManual(@)
97 0     0   0 { my $self = shift;
98 0         0 $self->chapterName(@_);
99 0         0 $self->chapterInheritance(@_);
100 0         0 $self->chapterSynopsis(@_);
101 0         0 $self->chapterDescription(@_);
102 0         0 $self->chapterOverloaded(@_);
103 0         0 $self->chapterMethods(@_);
104 0         0 $self->chapterExports(@_);
105 0         0 $self->chapterDetails(@_);
106 0         0 $self->chapterDiagnostics(@_);
107 0         0 $self->chapterReferences(@_);
108 0         0 $self->chapterCopyrights(@_);
109 0         0 $self->showAppend(@_);
110 0         0 $self;
111             }
112              
113             sub showAppend(@)
114 0     0 0 0 { my ($self, %args) = @_;
115 0         0 my $append = $args{append};
116              
117 0 0       0 if(!defined $append) { ; }
    0          
118 0         0 elsif(ref $append eq 'CODE') { $append->(formatter => $self, %args) }
119             else
120 0 0       0 { my $output = $args{output} or panic;
121 0         0 $output->print($append);
122             }
123              
124 0         0 $self;
125             }
126              
127             sub showStructureExpanded(@)
128 0     0 1 0 { my ($self, %args) = @_;
129              
130 0   0     0 my $examples = $args{show_examples} || 'EXPAND';
131 0 0       0 my $text = $args{structure} or panic;
132              
133 0         0 my $name = $text->name;
134 0         0 my $level = $text->level;
135 0 0       0 my $output = $args{output} or panic;
136 0 0       0 my $manual = $args{manual} or panic;
137              
138 0         0 my $descr = $self->cleanup($manual, $text->description);
139 0         0 $output->print("\n=head$level $name\n\n$descr");
140              
141 0         0 $self->showSubroutines(%args, subroutines => [ $text->subroutines ]);
142 0 0       0 $self->showExamples(%args, examples => [ $text->examples ])
143             if $examples eq 'EXPAND';
144              
145 0         0 $self;
146             }
147              
148             sub showStructureRefer(@)
149 0     0 1 0 { my ($self, %args) = @_;
150 0 0       0 my $text = $args{structure} or panic;
151              
152 0         0 my $name = $text->name;
153 0         0 my $level = $text->level;
154 0 0       0 my $output = $args{output} or panic;
155 0 0       0 my $manual = $args{manual} or panic;
156              
157 0         0 my $link = $self->link($manual, $text);
158 0         0 $output->print("\n=head$level $name\n\nSee $link.\n");
159 0         0 $self;
160             }
161              
162             sub chapterDescription(@)
163 0     0 0 0 { my ($self, %args) = @_;
164              
165 0         0 $self->showRequiredChapter(DESCRIPTION => %args);
166              
167 0 0       0 my $manual = $args{manual} or panic;
168 0   0     0 my $details = $manual->chapter('DETAILS') // return $self;
169              
170 0 0       0 my $output = $args{output} or panic;
171 0         0 $output->print("\nSee L chapter below\n");
172 0         0 $self->showChapterIndex($output, $details, " ");
173 0         0 $self;
174             }
175              
176             sub chapterDiagnostics(@)
177 0     0 0 0 { my ($self, %args) = @_;
178 0 0       0 my $manual = $args{manual} or panic;
179              
180 0         0 my $diags = $manual->chapter('DIAGNOSTICS');
181 0 0       0 $self->showChapter(chapter => $diags, %args)
182             if defined $diags;
183              
184 0         0 my @diags = map $_->diagnostics, $manual->subroutines;
185 0 0       0 return unless @diags;
186              
187 0 0       0 my $output = $args{output} or panic;
188 0 0       0 $diags
189             or $output->print("\n=head1 DIAGNOSTICS\n");
190              
191 0         0 $output->print("\n=over 4\n\n");
192 0         0 $self->showDiagnostics(%args, diagnostics => \@diags);
193 0         0 $output->print("\n=back\n\n");
194 0         0 $self;
195             }
196              
197              
198             sub showChapterIndex($$;$)
199 0     0 1 0 { my ($self, $output, $chapter, $indent) = @_;
200 0   0     0 $indent //= '';
201              
202 0         0 foreach my $section ($chapter->sections)
203 0         0 { $output->print($indent, $section->name, "\n");
204 0         0 foreach my $subsection ($section->subsections)
205 0         0 { $output->print($indent, $indent, $subsection->name, "\n");
206             }
207             }
208 0         0 $self;
209             }
210              
211             sub showExamples(@)
212 0     0 1 0 { my ($self, %args) = @_;
213 0 0       0 my $examples = $args{examples} or panic;
214 0 0       0 @$examples or return;
215              
216 0 0       0 my $manual = $args{manual} or panic;
217 0 0       0 my $output = $args{output} or panic;
218              
219 0         0 foreach my $example (@$examples)
220 0         0 { my $name = $self->cleanup($manual, $example->name);
221 0         0 $output->print("\n» example: $name\n\n");
222 0         0 $output->print($self->cleanup($manual, $example->description));
223 0         0 $output->print("\n");
224             }
225 0         0 $self;
226             }
227              
228             sub showDiagnostics(@)
229 0     0 0 0 { my ($self, %args) = @_;
230 0 0       0 my $diagnostics = $args{diagnostics} or panic;
231 0 0       0 @$diagnostics or return;
232              
233 0 0       0 my $manual = $args{manual} or panic;
234 0 0       0 my $output = $args{output} or panic;
235              
236 0         0 foreach my $diag (sort @$diagnostics)
237 0         0 { my $name = $self->cleanup($manual, $diag->name);
238 0         0 my $type = $diag->type;
239 0         0 $output->print("\n=item $type: $name\n\n");
240 0         0 $output->print($self->cleanup($manual, $diag->description));
241 0         0 $output->print("Cast by C<", $diag->subroutine->name, "()>\n");
242 0         0 $output->print("\n");
243             }
244 0         0 $self;
245             }
246              
247             sub showSubroutines(@)
248 0     0 1 0 { my ($self, %args) = @_;
249 0   0     0 my $subs = $args{subroutines} || [];
250 0 0       0 @$subs or return;
251              
252 0 0       0 my $output = $args{output} or panic;
253              
254 0         0 $output->print("\n=over 4\n\n");
255 0         0 $self->SUPER::showSubroutines(%args);
256 0         0 $output->print("\n=back\n\n");
257             }
258              
259             sub showSubroutine(@)
260 0     0 1 0 { my $self = shift;
261 0         0 $self->SUPER::showSubroutine(@_);
262              
263 0         0 my %args = @_;
264 0 0       0 my $output = $args{output} or panic;
265 0         0 $output->print("\n");
266 0         0 $self;
267             }
268              
269             sub showSubroutineUse(@)
270 0     0 1 0 { my ($self, %args) = @_;
271 0 0       0 my $subroutine = $args{subroutine} or panic;
272 0 0       0 my $manual = $args{manual} or panic;
273 0 0       0 my $output = $args{output} or panic;
274              
275 0         0 my $use = $self->subroutineUse($manual, $subroutine);
276 0         0 $use =~ s/(.+)/=item $1\n\n/gm;
277              
278 0         0 $output->print($use);
279 0 0       0 $output->print("Inherited, see ". $self->link($manual, $subroutine)."\n\n")
280             if $manual->inherited($subroutine);
281              
282 0         0 $self;
283             }
284              
285             sub subroutineUse($$)
286 0     0 0 0 { my ($self, $manual, $subroutine) = @_;
287 0         0 my $type = $subroutine->type;
288 0         0 my $name = $self->cleanup($manual, $subroutine->name);
289 0         0 my $paramlist = $self->cleanup($manual, $subroutine->parameters);
290              
291 0 0       0 $type eq 'tie'
292             and return qq[tie B<$name>, $paramlist];
293              
294 0 0       0 my $params = !length $paramlist ? '()' :
    0          
295             $paramlist =~ m/^[\[<]|[\]>]$/ ? "( $paramlist )" : "($paramlist)";
296              
297 0         0 my $class = $manual->package;
298 0 0       0 my $use
    0          
    0          
    0          
    0          
299             = $type eq 'i_method' ? qq[\$obj-EB<$name>$params]
300             : $type eq 'c_method' ? qq[\$class-EB<$name>$params]
301             : $type eq 'ci_method'? qq[\$any-EB<$name>$params]
302             : $type eq 'function' ? qq[B<$name>$params]
303             : $type eq 'overload' ? qq[overload: B<$name> $paramlist]
304             : panic $type;
305              
306 0         0 $use;
307             }
308              
309             sub showSubroutineName(@)
310 0     0 1 0 { my ($self, %args) = @_;
311 0 0       0 my $subroutine = $args{subroutine} or panic;
312 0 0       0 my $manual = $args{manual} or panic;
313 0 0       0 my $output = $args{output} or panic;
314 0         0 my $name = $subroutine->name;
315              
316 0 0       0 my $url = $manual->inherited($subroutine) ? "M<".$subroutine->manual."::$name>" : "M<$name>";
317 0 0       0 $output->print( $self->cleanup($manual, $url), ($args{last} ? ".\n" : ",\n") );
318             }
319              
320             sub showOptions(@)
321 0     0 1 0 { my ($self, %args) = @_;
322 0 0       0 my $output = $args{output} or panic;
323 0         0 $output->print("\n=over 2\n\n");
324 0         0 $self->SUPER::showOptions(%args);
325 0         0 $output->print("\n=back\n\n");
326             }
327              
328             sub showOptionUse(@)
329 0     0 1 0 { my ($self, %args) = @_;
330 0 0       0 my $output = $args{output} or panic;
331 0 0       0 my $option = $args{option} or panic;
332 0 0       0 my $manual = $args{manual} or panic;
333              
334 0         0 my $params = $option->parameters =~ s/\s+$//r =~ s/^\s+//r;
335 0 0       0 $params = " => ".$self->cleanup($manual, $params) if length $params;
336              
337 0         0 $output->print("=item $option$params\n\n");
338 0         0 $self;
339             }
340              
341             sub showOptionExpand(@)
342 0     0 1 0 { my ($self, %args) = @_;
343 0 0       0 my $output = $args{output} or panic;
344 0 0       0 my $option = $args{option} or panic;
345 0 0       0 my $manual = $args{manual} or panic;
346              
347 0         0 $self->showOptionUse(%args);
348              
349 0 0       0 my $where = $option->findDescriptionObject or return $self;
350 0         0 my $descr = $self->cleanup($manual, $where->description);
351 0 0       0 $output->print("\n$descr\n\n") if length $descr;
352 0         0 $self;
353             }
354              
355              
356             sub writeTable($@)
357 0     0 1 0 { my ($self, %args) = @_;
358              
359 0 0       0 my $head = $args{header} or panic;
360 0 0       0 my $output = $args{output} or panic;
361 0 0       0 my $rows = $args{rows} or panic;
362 0 0       0 @$rows or return;
363              
364             # Convert all elements to plain text, because markup is not
365             # allowed in verbatim pod blocks.
366 0         0 my @rows;
367 0         0 foreach my $row (@$rows)
368 0         0 { push @rows, [ map {$self->removeMarkup($_)} @$row ];
  0         0  
369             }
370              
371             # Compute column widths
372 0         0 my @w = (0) x @$head;
373              
374 0         0 foreach my $row ($head, @rows)
375             { $w[$_] = max $w[$_], length($row->[$_])
376 0         0 foreach 0..$#$row;
377             }
378              
379 0 0       0 if(my $widths = $args{widths})
380             { defined $widths->[$_] && $widths->[$_] > $w[$_] && ($w[$_] = $widths->[$_])
381 0   0     0 for 0..$#$rows;
      0        
382             }
383              
384 0         0 pop @w; # ignore width of last column
385              
386             # Table head
387 0         0 my $headf = " -".join("--", map "\%-${_}s", @w)."--%s\n";
388 0         0 $output->printf($headf, @$head);
389              
390             # Table body
391 0         0 my $format = " ".join(" ", map "\%-${_}s", @w)." %s\n";
392 0         0 $output->printf($format, @$_) for @rows;
393             }
394              
395              
396             sub removeMarkup($)
397 20     20 1 277490 { my ($self, $string) = @_;
398 20         52 my $out = $self->_removeMarkup($string);
399 20         66 for($out)
400 20         66 { s/^\s+//gm;
401 20         60 s/\s+$//gm;
402 20         44 s/\s{2,}/ /g;
403 20         78 s/\[NB\]/ /g;
404             }
405 20         135 $out;
406             }
407              
408             sub _removeMarkup($)
409 37     37   79 { my ($self, $string) = @_;
410              
411 37         65 my $out = '';
412 37         246 while($string =~ s/
413             (.*?) # before
414             ([BCEFILSXZ]) # known formatting codes
415             ([<]+) # capture ALL starters
416             //x)
417 27         124 { $out .= $1;
418 27         130 my ($tag, $bracks, $brack_count) = ($2, $3, length($3));
419              
420 27 50       667 if($string !~ s/^(|.*?[^>]) # contained
421             [>]{$brack_count}
422             (?![>])
423             //xs)
424 0         0 { $out .= "$tag$bracks";
425 0         0 next;
426             }
427              
428 27         77 my $container = $1;
429 27 100       149 if($tag =~ m/[XZ]/) { ; } # ignore container content
    100          
    100          
    50          
    50          
    0          
430             elsif($tag =~ m/[BCI]/) # cannot display, but can be nested
431 13         44 { $out .= $self->_removeMarkup($container);
432             }
433 3         10 elsif($tag eq 'E') { $out .= e2char($container) }
434 0         0 elsif($tag eq 'F') { $out .= $container }
435             elsif($tag eq 'L')
436 8 100       31 { if($container =~ m!^\s*([^/|]*)\|!)
437 4         13 { $out .= $self->_removeMarkup($1);
438 4         14 next;
439             }
440              
441 4         11 my ($man, $chapter) = ($container, '');
442 4 100       26 if($container =~ m!^\s*([^/]*)/\"([^"]*)\"\s*$!)
    100          
443 2         8 { ($man, $chapter) = ($1, $2);
444             }
445             elsif($container =~ m!^\s*([^/]*)/(.*?)\s*$!)
446 1         5 { ($man, $chapter) = ($1, $2);
447             }
448              
449             $out .=
450 4 100       24 ( !length $man ? "section $chapter"
    100          
451             : !length $chapter ? $man
452             : "$man section $chapter"
453             );
454             }
455             elsif($tag eq 'S')
456 0         0 { my $clean = $self->_removeMarkup($container);
457 0         0 $clean =~ s/ /[NB]/g;
458 0         0 $out .= $clean;
459             }
460             }
461              
462 37         143 $out . $string;
463             }
464              
465             sub showSubroutineDescription(@)
466 0     0 1   { my ($self, %args) = @_;
467 0 0         my $manual = $args{manual} or panic;
468 0 0         my $output = $args{output} or panic;
469 0 0         my $subroutine = $args{subroutine} or panic;
470              
471             # Z<> will cause an empty body when the description is missing, so there
472             # will be a blank line to the next sub description.
473 0           my $text = $self->cleanup($manual, $subroutine->description);
474 0           my $extends = $subroutine->extends;
475 0 0         my $refer = $extends ? $extends->findDescriptionObject : undef;
476 0 0 0       $text ||= $self->cleanup($manual, "Z<>\n") unless $refer;
477              
478 0 0         $output->print("\n", $text) if length $text;
479 0 0         $output->print("Improves base, see ",$self->link($manual, $refer),"\n") if $refer;
480             }
481              
482             sub showSubroutineDescriptionRefer(@)
483 0     0 0   { my ($self, %args) = @_;
484 0 0         my $manual = $args{manual} or panic;
485 0 0         my $output = $args{output} or panic;
486 0 0         my $subroutine = $args{subroutine} or panic;
487 0           $output->print("\nInherited, see ",$self->link($manual, $subroutine),"\n");
488             }
489              
490       0 0   sub showSubsIndex() {;}
491              
492              
493             sub simplifyPod($$)
494 0     0 1   { my ($self, $infn, $outfn) = @_;
495              
496 0 0         open my $in, '<:encoding(UTF-8)', $infn
497             or fault __x"cannot read prelimary pod from {file}", file => $infn;
498              
499 0 0         open my $out, '>:encoding(UTF-8)', $outfn
500             or fault __x"cannot write final pod to {file}", file => $outfn;
501              
502 0           my $last_is_blank = 1;
503             LINE:
504 0           while(my $l = $in->getline)
505 0 0         { if($l =~ m/^\s*$/s)
506 0 0         { next LINE if $last_is_blank;
507 0           $last_is_blank = 1;
508             }
509             else
510 0           { $last_is_blank = 0;
511             }
512              
513 0           $out->print($l);
514             }
515              
516 0           $in->close;
517 0 0         $out->close
518             or fault __x"write to {file} failed", file => $outfn;
519              
520 0           $self;
521             }
522              
523             #--------------------
524              
525             1;