File Coverage

lib/OODoc/Format/Html.pm
Criterion Covered Total %
statement 42 499 8.4
branch 0 276 0.0
condition 0 86 0.0
subroutine 14 71 19.7
pod 38 44 86.3
total 94 976 9.6


]; \n]; \n];
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             #oorestyle: not found P for method expandTemplate($name)
12              
13             package OODoc::Format::Html;{
14             our $VERSION = '3.05';
15             }
16              
17 1     1   1831 use parent 'OODoc::Format';
  1         2  
  1         11  
18              
19 1     1   94 use strict;
  1         2  
  1         27  
20 1     1   4 use warnings;
  1         2  
  1         76  
21              
22 1     1   5 use Log::Report 'oodoc';
  1         2  
  1         10  
23 1     1   1316 use OODoc::Template ();
  1         6593  
  1         52  
24              
25 1     1   11 use Encode qw/decode/;
  1         3  
  1         94  
26 1     1   7 use File::Spec::Functions qw/catfile catdir/;
  1         1  
  1         51  
27 1     1   5 use File::Find qw/find/;
  1         2  
  1         63  
28 1     1   7 use File::Basename qw/basename dirname/;
  1         2  
  1         66  
29 1     1   686 use File::Copy qw/copy/;
  1         4390  
  1         125  
30 1     1   11 use POSIX qw/strftime/;
  1         3  
  1         12  
31 1     1   124 use List::Util qw/first/;
  1         3  
  1         149  
32 1     1   7 use HTML::Entities qw/encode_entities/;
  1         1  
  1         10082  
33              
34             #--------------------
35              
36             sub init($)
37 0     0 0   { my ($self, $args) = @_;
38 0   0       $args->{format} //= 'html';
39              
40 0 0         $self->SUPER::init($args) or return;
41              
42 0   0       my $html = delete $args->{html_root} || '/';
43 0           $html =~ s! /$ !!x;
44              
45 0           $self->{OFH_html} = $html;
46 0   0       $self->{OFH_jump} = delete $args->{jump_script} || "$html/jump.cgi";
47              
48 0   0       my $meta = delete $args->{html_meta_data} || '';
49 0 0         if(my $ss = delete $args->{html_stylesheet})
50 0           { $meta .= qq[\n];
51             }
52 0           $meta .= qq[\n];
53              
54 0           $self->{OFH_meta} = $meta;
55 0           $self;
56             }
57              
58             #--------------------
59              
60 0     0 1   sub jumpScript() { $_[0]->{OFH_jump} }
61 0     0 1   sub htmlRoot() { $_[0]->{OFH_html} }
62 0     0 1   sub meta() { $_[0]->{OFH_meta} }
63              
64 0 0   0 1   sub manual(;$) { @_==2 ? $_[0]->{OFH_manual} = $_[1] : $_[0]->{OFH_manual} }
65 0 0   0 1   sub markers(;$) { @_==2 ? $_[0]->{OFH_mark} = $_[1] : $_[0]->{OFH_mark} }
66 0 0   0 1   sub filename(;$) { @_==2 ? $_[0]->{OFH_fn} = $_[1] : $_[0]->{OFH_fn} }
67              
68             #--------------------
69              
70             sub cleanup($$%)
71 0     0 1   { my ($self, $manual, $string, %args) = @_;
72             $manual->parser->cleanupHtml($manual, $string, %args,
73 0     0     create_link => sub { $self->link(@_) },
74 0           );
75             }
76              
77              
78             sub cleanupString($$@)
79 0     0 1   { my $self = shift;
80 0           $self->SUPER::cleanupString(@_)
81             =~ s!

\s*

!
!grs # keep line-breaks

82             =~ s!!!gr # remove paragraphing
83             =~ s!\!!gr;
84             }
85              
86              
87             sub link($$;$)
88 0     0 1   { my ($self, $manual, $object, $html, $settings) = @_;
89 0   0       $html //= encode_entities $object->name;
90              
91 0           my $jump;
92 0 0         if($object->isa('OODoc::Manual'))
93 0           { my $manname = $object->name =~ s!\:\:!_!gr;
94 0           $jump = $self->htmlRoot . "/$manname/index.html";
95             }
96             else
97 0           { my $manname = $manual->name =~ s!\:\:!_!gr;
98 0           $jump = $self->jumpScript . "?$manname&". $object->unique;
99             }
100              
101 0           qq[$html];
102             }
103              
104              
105             sub mark($$)
106 0     0 1   { my ($self, $manual, $id) = @_;
107 0           my @fields = ($id, $manual =~ s/\:\:/_/gr, $self->filename);
108 0           $self->markers->print(join(' ', @fields), "\n");
109             }
110              
111              
112             sub createManual($@)
113 0     0 1   { my ($self, %args) = @_;
114 0   0       my $verbose = $args{verbose} || 0;
115 0 0         my $manual = $args{manual} or panic;
116              
117             # Location for the manual page files.
118              
119 0   0       my $template = $args{template} || (catdir 'html', 'manual');
120 0           my %template = $self->expandTemplate($template, [ %args ]);
121              
122 0           my $manfile = "$manual" =~ s!\:\:!_!gr;
123 0           my $dest = catdir $self->workdir, $manfile;
124 0           $self->mkdirhier($dest);
125              
126             # File to trace markers must be open.
127              
128 0 0         unless(defined $self->markers)
129 0           { my $markers = catfile $self->workdir, 'markers';
130 0 0         open my $mark, ">:encoding(UTF-8)", $markers
131             or fault __x"cannot write markers to {file}", file => $markers;
132 0           $self->markers($mark);
133 0           $mark->print($self->htmlRoot, "\n");
134             }
135              
136             #
137             # Process template
138             #
139              
140 0           my $manifest = $self->manifest;
141 0           while(my($raw, $options) = each %template)
142 0           { my $cooked = catfile $dest, basename $raw;
143              
144 0 0         print "$manual: $cooked\n" if $verbose > 2;
145 0           $manifest->add($cooked);
146              
147 0 0         open my $output, ">:encoding(UTF-8)", $cooked
148             or fault __x"cannot write html manual to {file}", file => $cooked;
149              
150 0           $self->filename(basename $raw);
151              
152 0           $self->manual($manual);
153 0           $self->interpolate(output => $output, template_fn => $raw, @$options);
154 0           $self->manual(undef);
155 0           $output->close;
156             }
157              
158 0           $self->filename(undef);
159 0           $self;
160             }
161              
162              
163             sub createOtherPages(@)
164 0     0 1   { my ($self, %args) = @_;
165              
166 0   0       my $verbose = $args{verbose} || 0;
167              
168             #
169             # Collect files to be processed
170             #
171              
172 0           my $source = $args{source};
173 0 0         if(defined $source)
174 0 0         { -d $source
175             or fault __x"html source directory {dir}", dir => $source;
176             }
177             else
178 0           { $source = catdir "html", "other";
179 0 0         -d $source or return $self;
180             }
181              
182 0   0       my $process = $args{process} || qr/\.(?:s?html|cgi)$/;
183              
184 0           my $dest = $self->workdir;
185 0           $self->mkdirhier($dest);
186              
187 0           my @sources;
188             find( +{
189             no_chdir => 1,
190             wanted => sub {
191 0     0     my $fn = $File::Find::name;
192 0 0         push @sources, $fn if -f $fn;
193 0           } }, $source);
194              
195             #
196             # Process files, one after the other
197             #
198              
199 0           my $manifest = $self->manifest;
200 0           foreach my $raw (@sources)
201 0           { (my $cooked = $raw) =~ s/\Q$source\E/$dest/;
202              
203 0 0         print "create $cooked\n" if $verbose > 2;
204 0           $manifest->add($cooked);
205              
206 0 0         if($raw =~ $process)
207 0           { $self->mkdirhier(dirname $cooked);
208 0 0         open my $output, '>:encoding(UTF-8)', $cooked
209             or fault __x"cannot write html to {fn}", fn => $cooked;
210              
211 0           my $options = [];
212 0           $self->interpolate(manual => undef, output => $output, template_fn => $raw, @$options);
213 0           $output->close;
214             }
215             else
216 0 0         { copy $raw, $cooked
217             or fault __x"copy from {from} to {to} failed", from => $raw, to => $cooked;
218             }
219              
220 0           my $rawmode = (stat $raw)[2] & 07777;
221 0 0         chmod $rawmode, $cooked
222             or fault __x"chmod of {fn} to {mode%o} failed", fn => $cooked, mode => $rawmode;
223             }
224              
225 0           $self;
226             }
227              
228              
229             sub expandTemplate($$)
230 0     0 1   { my $self = shift;
231 0   0       my $loc = shift || panic;
232 0   0       my $defaults = shift || [];
233              
234 0           my @result;
235 0 0         if(ref $loc eq 'HASH')
    0          
    0          
236 0           { foreach my $n (keys %$loc)
237 0           { my %options = (@$defaults, @{$loc->{$n}});
  0            
238 0           push @result, $self->expandTemplate($n, [ %options ])
239             }
240             }
241             elsif(-d $loc)
242             { find( +{
243             no_chdir => 1,
244             wanted => sub {
245 0     0     my $fn = $File::Find::name;
246 0 0         push @result, $fn, $defaults if -f $fn;
247 0           } }, $loc
248             );
249             }
250 0           elsif(-f $loc) { push @result, $loc => $defaults }
251 0           else { error __x"cannot find template source '{name}'", name => $loc }
252              
253 0           @result;
254             }
255              
256             sub showStructureExpanded(@)
257 0     0 1   { my ($self, %args) = @_;
258              
259 0   0       my $examples = $args{show_examples} || 'EXPAND';
260 0 0         my $text = $args{structure} or panic;
261              
262 0           my $name = $text->name;
263 0           my $level = $text->level +1; # header level, chapter = H2
264 0 0         my $output = $args{output} or panic;
265 0 0         my $manual = $args{manual} or panic;
266              
267 0           my $descr = $self->cleanup($manual, $text->description, tag => 'block_intro');
268 0           my $unique = $text->unique;
269 0           my $id = $name =~ s/\W+/_/gr;
270 0           my $n = $self->cleanupString($manual, $name);
271 0           $output->print( qq[\n$n\n$descr] );
272              
273 0           $self->mark($manual, $unique);
274              
275             # Link to inherited documentation.
276              
277 0           my $super = $text;
278 0           while($super = $super->extends)
279 0 0         { last if $super->description !~ m/^\s*$/;
280             }
281              
282 0 0         if(defined $super)
283 0           { my $superman = $super->manual; # :-)
284 0           $output->print( "

See ", $self->link($superman, $super), " in " , $self->link(undef, $superman), "

\n");
285             }
286              
287             # Show the subroutines and examples.
288              
289 0 0         $self->showExamples(%args, examples => [ $text->examples] )
290             if $examples eq 'EXPAND';
291              
292 0           $self->showSubroutines(%args, subroutines => [ $text->subroutines ]);
293 0           $self;
294             }
295              
296             sub showStructureRefer(@)
297 0     0 1   { my ($self, %args) = @_;
298              
299 0 0         my $text = $args{structure} or panic;
300 0           my $name = $text->name;
301 0           my $level = $text->level;
302              
303 0 0         my $output = $args{output} or panic;
304 0 0         my $manual = $args{manual} or panic;
305              
306 0           my $link = $self->link($manual, $text);
307 0           my $n = $self->cleanup($manual, $name);
308 0           $output->print( qq[\n$n\n] );
309 0           $self;
310             }
311              
312             sub chapterDiagnostics(@)
313 0     0 0   { my ($self, %args) = @_;
314              
315 0 0         my $manual = $args{manual} or panic;
316 0           my $diags = $manual->chapter('DIAGNOSTICS');
317              
318 0           my @diags = map $_->diagnostics, $manual->subroutines;
319 0 0 0       $diags = OODoc::Text::Chapter->new(name => 'DIAGNOSTICS')
320             if !$diags && @diags;
321              
322 0 0         $diags or return $self;
323              
324 0 0         $self->showChapter(chapter => $diags, %args)
325             if defined $diags;
326              
327 0           $self->showDiagnostics(%args, diagnostics => \@diags);
328 0           $self;
329             }
330              
331             sub showExamples(@)
332 0     0 1   { my ($self, %args) = @_;
333 0 0         my $examples = $args{examples} or panic;
334 0 0         @$examples or return $self;
335              
336 0 0         my $manual = $args{manual} or panic;
337 0 0         my $output = $args{output} or panic;
338              
339 0           $output->print( qq[
\n] );
340              
341 0           foreach my $example (@$examples)
342 0           { my $name = $example->name;
343 0           my $descr = $self->cleanup($manual, $example->description);
344 0           my $unique = $example->unique;
345 0           $output->print( <
346            
» example: $name
347            
$descr
348             EXAMPLE
349              
350 0           $self->mark($manual, $unique);
351             }
352 0           $output->print( qq[\n] );
353              
354 0           $self;
355             }
356              
357             sub showDiagnostics(@)
358 0     0 0   { my ($self, %args) = @_;
359 0 0         my $diagnostics = $args{diagnostics} or panic;
360 0 0         @$diagnostics or return $self;
361              
362 0 0         my $manual = $args{manual} or panic;
363 0 0         my $output = $args{output} or panic;
364              
365 0           $output->print( qq[
\n] );
366              
367 0           foreach my $diag (sort @$diagnostics)
368 0           { my $name = $diag->name;
369 0           my $type = $diag->type;
370 0   0       my $text = $self->cleanup($manual, $diag->description) || ' ';
371 0           my $unique = $diag->unique;
372              
373 0           $output->print( <
374            
» $type: $name
375            
$text
376             DIAG
377              
378 0           $self->mark($manual, $unique);
379             }
380              
381 0           $output->print( qq[\n] );
382 0           $self;
383             }
384              
385             sub showSubroutine(@)
386 0     0 1   { my ($self, %args) = @_;
387 0 0         my $output = $args{output} or panic;
388 0 0         my $sub = $args{subroutine} or panic;
389 0           my $type = $sub->type;
390 0           my $name = $sub->name;
391              
392 0           $self->SUPER::showSubroutine(%args);
393              
394 0           $output->print( qq[\n\n\n] );
395 0           $self;
396             }
397              
398             sub showSubroutineUse(@)
399 0     0 1   { my ($self, %args) = @_;
400 0 0         my $subroutine = $args{subroutine} or panic;
401 0 0         my $manual = $args{manual} or panic;
402 0 0         my $output = $args{output} or panic;
403 0           my $type = $subroutine->type;
404              
405 0           my $unique = $subroutine->unique;
406 0           $self->mark($manual, $unique);
407              
408 0           my $name = $self->cleanupString($manual, $subroutine->name);
409 0           my $paramlist = $self->cleanupString($manual, $subroutine->parameters);
410 0           my $call = qq[$name];
411 0 0         my $param = length $paramlist ? "( $paramlist )" : '';
412              
413 0 0         my $use
    0          
    0          
    0          
    0          
    0          
414             = $type eq 'i_method' ? qq[\$obj->$call$param]
415             : $type eq 'c_method' ? qq[\$class->$call$param]
416             : $type eq 'ci_method'? qq[\$any->$call$param]
417             : $type eq 'overload' ? qq[overload: $call $paramlist]
418             : $type eq 'function' ? qq[$call$param]
419             : $type eq 'tie' ? qq[tie $call, $paramlist]
420             : panic "Type $type? for $call";
421              
422 0 0         my $is_inherited = $manual->inherited($subroutine) ? 'inherited' : '';
423 0           $output->print( <
424            
425            
426            
$use
427            
428             SUBROUTINE
429              
430 0 0         if($is_inherited)
431 0           { my $defd = $subroutine->manual;
432 0           my $sublink = $self->link($defd, $subroutine, $name);
433 0           my $manlink = $self->link($manual, $defd);
434 0           $output->print( qq[Inherited from $sublink in $manlink.
\n] );
435             }
436              
437 0           $self;
438             }
439              
440             sub showSubsIndex(@)
441 0     0 0   { my ($self, %args) = @_;
442 0 0         my $output = $args{output} or panic;
443             #XXX
444             }
445              
446             sub showSubroutineName(@)
447 0     0 1   { my ($self, %args) = @_;
448 0 0         my $subroutine = $args{subroutine} or panic;
449 0 0         my $manual = $args{manual} or panic;
450 0 0         my $output = $args{output} or panic;
451 0           my $name = $subroutine->name;
452              
453 0 0         my $url = $manual->inherited($subroutine) ? "M<".$subroutine->manual."::$name>" : "M<$name>";
454 0 0         $output->print($self->cleanupString($manual, $url), ($args{last} ? ".\n" : ",\n"));
455             }
456              
457             sub showOptions(%)
458 0     0 1   { my ($self, %args) = @_;
459 0 0         my $output = $args{output} or panic;
460 0           $output->print( qq[
\n] );
461              
462 0           $self->SUPER::showOptions(%args);
463 0           $output->print( qq[\n] );
464 0           $self;
465             }
466              
467             sub showOptionUse(@)
468 0     0 1   { my ($self, %args) = @_;
469 0 0         my $output = $args{output} or panic;
470 0 0         my $option = $args{option} or panic;
471 0 0         my $manual = $args{manual} or panic;
472              
473 0           my $params = $self->cleanupString($manual, $option->parameters) =~ s/\s+$//r =~ s/^\s+//r;
474 0 0         $params = qq[ => $params]
475             if length $params;
476              
477 0           my $id = $option->unique;
478 0           $self->mark($manual, $id);
479              
480 0           my $use = qq[$option];
481 0           $output->print( qq[
$use$params
\n] );
482 0           $self;
483             }
484              
485             sub showOptionExpand(@)
486 0     0 1   { my ($self, %args) = @_;
487 0 0         my $output = $args{output} or panic;
488 0 0         my $option = $args{option} or panic;
489 0 0         my $manual = $args{manual} or panic;
490              
491 0           $self->showOptionUse(%args);
492              
493 0 0         my $where = $option->findDescriptionObject or return $self;
494 0           my $descr = $self->cleanupString($manual, $where->description);
495              
496 0 0         $output->print( qq[
$descr
\n] )
497             if length $descr;
498              
499 0           $self;
500             }
501              
502              
503             sub writeTable($@)
504 0     0 1   { my ($self, %args) = @_;
505              
506 0 0         my $rows = $args{rows} or panic;
507 0 0         @$rows or return $self;
508              
509 0 0         my $head = $args{header} or panic;
510 0 0         my $output = $args{output} or panic;
511              
512 0           $output->print( qq[\n] ); \n] ); \n] )
513              
514 0           local $" = qq[ ];
515 0           $output->print( qq[
@$head
516              
517 0           local $" = qq[ ];
518             $output->print( qq[
@$_
519 0           for @$rows;
520              
521 0           $output->print( qq[
\n] );
522 0           $self;
523             }
524              
525             sub showSubroutineDescription(@)
526 0     0 1   { my ($self, %args) = @_;
527 0 0         my $manual = $args{manual} or panic;
528 0 0         my $subroutine = $args{subroutine} or panic;
529 0 0         my $output = $args{output} or panic;
530              
531 0           my $text = $self->cleanup($manual, $subroutine->description);
532 0           my $extends = $subroutine->extends;
533 0 0         if(my $refer = $extends ? $extends->findDescriptionObject : undef)
    0          
534 0           { my $super = $refer->manual;
535 0           my $link = 'Improves base, see ' . $self->link($super, $refer) . ' in ' . $self->link(undef, $super) . "\n";
536 0 0         $text = length $text ? $text =~ s#

$#
\n$link

#r : "

$link

";
537             }
538             else
539 0   0       { $text ||= ' ';
540             }
541              
542 0           $output->print($text);
543             }
544              
545             sub showSubroutineDescriptionRefer(@)
546 0     0 0   { my ($self, %args) = @_;
547 0 0         my $manual = $args{manual} or panic;
548 0 0         my $subroutine = $args{subroutine} or panic;
549 0 0         my $output = $args{output} or panic;
550              
551 0           my $defd = $subroutine->manual;
552 0           my $sublink = $self->link($defd, $subroutine);
553 0           my $manlink = $self->link($manual, $defd);
554 0           $output->print("\n

See $sublink in $manlink

\n");
555             }
556              
557             #--------------------
558              
559             our %producers = (
560             a => 'templateHref',
561             chapter => 'templateChapter',
562             date => 'templateDate',
563             index => 'templateIndex',
564             inheritance => 'templateInheritance',
565             list => 'templateList',
566             manual => 'templateManual',
567             meta => 'templateMeta',
568             distribution=> 'templateDistribution',
569             name => 'templateName',
570             project => 'templateProject',
571             title => 'templateTitle',
572             version => 'templateVersion',
573             );
574              
575             sub interpolate(@)
576 0     0 1   { my ($self, %args) = @_;
577 0           my $output = delete $args{output};
578              
579 0           my %permitted = %args;
580 0           my $template = OODoc::Template->new;
581 0           while(my ($tag, $method) = each %producers)
582             { $permitted{$tag} = sub
583             { # my ($istag, $attrs, $ifblock, $elseblock) = @_;
584 0     0     shift;
585 0           $self->$method($template, @_)
586 0           };
587             }
588              
589 0           $output->print(scalar $template->processFile($args{template_fn}, \%permitted));
590             }
591              
592              
593             sub templateProject($$)
594 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
595 0           $self->project;
596             }
597              
598              
599             sub templateTitle($$)
600 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
601              
602 0 0         my $manual = $self->manual
603             or error __x"not a manual, so no automatic template title in {file}", file => scalar $templ->valueFor('template_fn');
604              
605 0           my $name = $self->cleanupString($manual, $manual->name);
606 0           $name =~ s/\<[^>]*\>//g;
607 0           $name;
608             }
609              
610              
611             sub templateManual($$)
612 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
613              
614 0 0         my $manual = $self->manual
615             or error __x"not a manual, so no manual name for template {file}", file => scalar $templ->valueFor('template_fn');
616              
617 0           $self->cleanupString($manual, $manual->name, tag => 'manual_name');
618             }
619              
620              
621             sub templateDistribution($$)
622 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
623 0           my $manual = $self->manual;
624 0 0         defined $manual ? $manual->distribution : '';
625             }
626              
627              
628             sub templateVersion($$)
629 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
630 0           my $manual = $self->manual;
631 0 0         defined $manual ? $manual->version : $self->version;
632             }
633              
634              
635             sub templateDate($$)
636 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
637 0           strftime "%Y/%m/%d", localtime;
638             }
639              
640              
641             sub templateName($$)
642 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
643              
644 0 0         my $manual = $self->manual
645             or error __x"not a manual, so no name for template {file}", file => scalar $templ->valueFor('template_fn');
646              
647 0 0         my $chapter = $manual->chapter('NAME')
648             or error __x"cannot find chapter NAME in manual {file}", file => $manual->source;
649              
650 0           my $descr = $chapter->description;
651              
652 0 0         return $1 if $descr =~ m/^ \s*\S+\s*\-\s*(.*?)\s* $ /x;
653              
654 0           error __x"chapter NAME in manual {manual} has illegal shape",
655             manual => $manual;
656             }
657              
658              
659             our %path_lookup = (
660             front => "/index.html",
661             manuals => "/manuals/index.html",
662             methods => "/methods/index.html",
663             diagnostics => "/diagnostics/index.html",
664             details => "/details/index.html",
665             );
666              
667             sub templateHref($$)
668 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
669 0   0       my $window = delete $attrs->{window} || '_top';
670 0 0         keys %$attrs==1 or error __x"expect one name with 'a'";
671 0           (my $to) = keys %$attrs;
672              
673 0 0         my $path = $path_lookup{$to}
674             or error __x"missing path for {dest}", dest => $to;
675              
676 0           my $root = $self->htmlRoot;
677 0           qq[];
678             }
679              
680              
681             sub templateMeta($$)
682 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
683 0           $self->meta;
684             }
685              
686              
687             sub templateInheritance(@)
688 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
689              
690 0           my $manual = $self->manual;
691 0 0         my $chapter = $manual->chapter('INHERITANCE')
692             or return '';
693              
694 0           open my $out, '>:encoding(UTF-8)', \(my $buffer);
695 0           $self->showChapter(%$attrs, manual => $self->manual, chapter => $chapter, output => $out);
696 0           close $out;
697              
698 0           $buffer = decode 'UTF-8', $buffer; # open to buffer produces bytes :-(
699              
700 0           for($buffer)
701 0           { s#\\s*(.*?)\\n*#\n$1#gs; # over-eager cleanup
702 0           s#^( +)#' ' x length($1)#gme;
  0            
703 0           s# $ #
#gmx;
704 0           s#(\)(\\n?)+#$1\n#;
705             }
706              
707 0           $buffer;
708             }
709              
710              
711             sub templateChapter($$)
712 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
713 0 0 0       warning __x"no meaning for container {tags} in chapter block", tags => $if
714             if defined $if && length $if;
715              
716 0     0     my $name = first { !/[a-z]/ } keys %$attrs;
  0            
717 0 0         defined $name
718             or error __x"chapter without name in template {file}", file => scalar $templ->valueFor('template_fn');
719              
720 0           my $manual = $self->manual;
721 0 0         defined $manual or panic;
722 0 0         my $chapter = $manual->chapter($name) or return '';
723              
724 0           open my $out, '>:encoding(UTF-8)', \(my $buffer);
725 0           $self->showChapter(%$attrs, manual => $self->manual, chapter => $chapter, output => $out);
726 0           close $out;
727              
728 0           decode 'UTF-8', $buffer;
729             }
730              
731              
732             sub templateIndex($$)
733 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
734              
735 0 0 0       ! defined $if || ! length $if
736             or warning __x"no meaning for container {tags} in list block", tags => $if;
737              
738 0 0   0     my $group = first { !/[a-z]/ } keys %$attrs
  0            
739             or error __x"no group named as attribute for list";
740              
741 0   0       my $start = $attrs->{starting_with} || 'ALL';
742 0   0       my $types = $attrs->{type} || 'ALL';
743              
744 0     0     my $select = sub { @_ };
  0            
745 0 0         if($start ne 'ALL')
746 0           { $start =~ s/_/[\\W_]/g;
747 0           my $regexp = qr/^$start/i;
748 0     0     $select = sub { grep $_->name =~ $regexp, @_ };
  0            
749             }
750              
751 0 0         if($types ne 'ALL')
752 0 0         { my @take = map { $_ eq 'method' ? '.*method' : $_ } split /[_|]/, $types;
  0            
753 0           local $" = ')|(';
754 0           my $regexp = qr/^(@take)$/i;
755 0           my $before = $select;
756 0     0     $select = sub { grep $_->type =~ $regexp, $before->(@_) };
  0            
757             }
758              
759 0   0       my $columns = $attrs->{table_columns} || 2;
760 0           my @rows;
761 0           my @manuals = $self->index->manuals;
762              
763 0 0         if($group eq 'SUBROUTINES')
    0          
    0          
    0          
764 0           { my @subs;
765              
766 0           foreach my $manual (@manuals)
767 0           { foreach my $sub ($select->($manual->ownSubroutines))
768 0           { my $linksub = $self->link($manual, $sub, $sub->name);
769 0           my $linkman = $self->link(undef, $manual, $manual->name);
770 0           my $link = "$linksub -- $linkman";
771 0           push @subs, [ lc("$sub-$manual"), $link ];
772             }
773             }
774              
775 0           @rows = map $_->[1], sort { $a->[0] cmp $b->[0] } @subs;
  0            
776             }
777             elsif($group eq 'DIAGNOSTICS')
778 0           { foreach my $manual (@manuals)
779 0           { foreach my $sub ($manual->ownSubroutines)
780 0 0         { my @diags = $select->($sub->diagnostics) or next;
781              
782 0           my $linksub = $self->link($manual, $sub, $sub->name);
783 0           $linksub =~ s#\#()#; # add call ()
784 0           my $linkman = $self->link(undef, $manual, $manual->name);
785              
786 0           foreach my $diag (@diags)
787 0           { my $type = lc($diag->type);
788 0           push @rows, <<"DIAG";
789             $type: $diag
790             · $linksub in $linkman
791             DIAG
792             }
793             }
794             }
795              
796 0           @rows = sort @rows;
797             }
798             elsif($group eq 'DETAILS')
799 0           { foreach my $manual (sort $select->(@manuals))
800 0 0         { my $details = $manual->chapter("DETAILS") or next;
801 0           my @sections;
802 0           foreach my $section ($details->sections)
803 0   0       { my @subsect = grep !$manual->inherited($_) && $_->description, $section->subsections;
804 0 0 0       push @sections, $section
805             if @subsect || $section->description;
806             }
807              
808 0 0 0       @sections || length $details->description
809             or next;
810              
811 0           my $sections = join "\n", map "
  • ".$self->link($manual, $_)."
  • ", @sections;
    812              
    813 0           push @rows, $self->link($manual, $details, "Details in $manual") . qq[\n
      \n$sections
    \n]
    814             }
    815             }
    816             elsif($group eq 'MANUALS')
    817 0           { @rows = map $self->link(undef, $_, $_->name), sort $select->(@manuals);
    818             }
    819             else
    820 0           { error __x"unknown group {name} as list attribute", name => $group;
    821             }
    822              
    823 0           push @rows, ('') x ($columns-1);
    824 0           my $rows = int(@rows/$columns);
    825 0           my $width = int(100/$columns) . '%';
    826              
    827 0           my $output = qq[
    828 0           while(@rows >= $columns)
    829 0           { $output .= qq[] . join( "
    \n", splice(@rows, 0, $rows)) . qq[
    830             }
    831 0           $output .= qq[
    832 0           $output;
    833             }
    834              
    835              
    836             sub templateList($$)
    837 0     0 1   { my ($self, $templ, $attrs, $if, $else) = @_;
    838 0 0 0       warning __x"no meaning for container {tags} in index block", tags => $if
    839             if defined $if && length $if;
    840              
    841 0     0     my $group = first { !/[a-z]/ } keys %$attrs;
      0            
    842 0 0         defined $group
    843             or error __x"no group named as attribute for list";
    844              
    845 0   0       my $show_sub = $attrs->{show_subroutines} || 'LIST';
    846 0   0       my $types = $attrs->{subroutine_types} || 'ALL';
    847 0 0         my $manual = $self->manual or panic;
    848 0           my $output = '';
    849              
    850 0     0     my $selected = sub { @_ };
      0            
    851 0 0         unless($types eq 'ALL')
    852 0 0         { my @take = map { $_ eq 'method' ? '.*method' : $_ } split /[_|]/, $types;
      0            
    853 0           local $" = ')|(?:';
    854 0           my $regexp = qr/^(?:@take)$/;
    855 0     0     $selected = sub { grep $_->type =~ $regexp, @_ };
      0            
    856             }
    857              
    858 0     0     my $sorted = sub { sort {$a->name cmp $b->name} @_ };
      0            
      0            
    859              
    860 0 0         if($group eq 'ALL')
    861 0           { my @subs = $sorted->($selected->($manual->subroutines));
    862 0 0 0       if(!@subs || $show_sub eq 'NO') { ; }
        0          
    863 0           elsif($show_sub eq 'COUNT') { $output .= @subs }
    864             else
    865 0           { $output .= $self->indexListSubroutines($manual,@subs);
    866             }
    867             }
    868             else # any chapter
    869 0 0         { my $chapter = $manual->chapter($group) or return '';
    870 0   0       my $show_sec = $attrs->{show_sections} || 'LINK';
    871 0 0         my @sections = $show_sec eq 'NO' ? () : $chapter->sections;
    872              
    873 0 0         my @subs = $sorted->( $selected->(
    874             @sections ? $chapter->subroutines : $chapter->all('subroutines')
    875             ));
    876              
    877 0           $output .= $self->link($manual, $chapter, $chapter->niceName);
    878 0 0 0       my $count = @subs && $show_sub eq 'COUNT' ? ' ('.@subs.')' : '';
    879              
    880 0 0 0       if($show_sec eq 'NO') { $output .= qq[$count
    \n] }
      0 0          
    881             elsif($show_sec eq 'LINK' || $show_sec eq 'NAME')
    882 0           { $output .= qq[
    \n
      \n];
    883 0 0         if(!@subs) {;}
        0          
        0          
    884             elsif($show_sec eq 'LINK')
    885 0           { my $link = $self->link($manual, $chapter, 'unsorted');
    886 0           $output .= qq[
  • $link$count\n];
  • 887             }
    888             elsif($show_sec eq 'NAME')
    889 0           { $output .= qq[
  • ];
  • 890             }
    891              
    892 0 0 0       $output .= $self->indexListSubroutines($manual,@subs)
    893             if @subs && $show_sub eq 'LIST';
    894             }
    895             else
    896 0           { error __x"illegal value to show_sections: {value}", value => $show_sec;
    897             }
    898              
    899             # All sections within the chapter (if show_sec is enabled)
    900              
    901 0           foreach my $section (@sections)
    902 0           { my @subs = $sorted->($selected->($section->all('subroutines')));
    903              
    904 0 0         my $count =
        0          
    905             ! @subs ? ''
    906             : $show_sub eq 'COUNT' ? ' ('.@subs.')'
    907             : ': ';
    908              
    909 0 0         if($show_sec eq 'LINK')
    910 0           { my $link = $self->link($manual, $section, $section->niceName);
    911 0           $output .= qq[
  • $link$count\n];
  • 912             }
    913             else
    914 0           { $output .= qq[
  • $section$count\n];
  • 915             }
    916              
    917 0 0 0       $output .= $self->indexListSubroutines($manual,@subs)
    918             if $show_sub eq 'LIST' && @subs;
    919              
    920 0           $output .= qq[\n];
    921             }
    922              
    923 0 0 0       $output .= qq[\n]
    924             if $show_sec eq 'LINK' || $show_sec eq 'NAME';
    925             }
    926              
    927 0           $output;
    928             }
    929              
    930             sub indexListSubroutines(@)
    931 0     0 0   { my $self = shift;
    932 0           my $manual = shift;
    933              
    934 0           join ",\n", map $self->link($manual, $_, $_), @_;
    935             }
    936              
    937              
    938 1     1   24 no warnings 'once';
      1         4  
      1         154  
    939             *mkdirhier = \&OODoc::mkdirhier;
    940              
    941             1;