File Coverage

lib/PPI/Transform/Doxygen.pm
Criterion Covered Total %
statement 280 282 99.2
branch 87 104 83.6
condition 43 53 81.1
subroutine 30 30 100.0
pod 3 3 100.0
total 443 472 93.8


line stmt bran cond sub pod time code
1             package PPI::Transform::Doxygen;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Transform::Doxygen - PPI::Transform class for generating Doxygen input
8              
9             =head1 SYNOPSIS
10              
11             use PPI;
12             use PPI::Transform::Doxygen;
13              
14             my $transform = PPI::Transform::Doxygen->new();
15              
16             # appends Doxygen Docs after __END__ (default when no output is given)
17             $transform->file('Module.pm');
18              
19             # prints Doxygen docs for use as a doxygen filter
20             $transform->file('Module.pm' => \*STDOUT);
21              
22             =head1 DESCRIPTION
23              
24             This module is normally used by the script L that is
25             part of this distribution and acts as a doxygen input filter (look for
26             B in the Doxygen docs).
27              
28             There is already L doing roughly the same task, but it
29             uses special doxygen comments.
30              
31             The goal of PPI::Transform::Doxygen is to use only POD documentation with a
32             minimal amount of special syntax, while still producing decent results with
33             doxygen.
34              
35             As doxygen is not able to parse perl directly, the input filter will
36             convert the source so that it will look like C++.
37              
38             =head1 CONVENTIONS
39              
40             The only thing really needed, is documenting your methods and functions with
41             a POD tag B<=head2> that contains a function string with parentheses ( it has
42             to match the regular expression /[\w:]+\(.*\)/) like so:
43              
44             =head2 do_things()
45              
46             This function does things
47              
48             =cut
49              
50             sub do_things {
51             print "Hi!\n";
52             }
53              
54             or so:
55              
56             =head2 class_method $obj THINGY::new(%args)
57              
58             Creates a new THINGY object
59              
60             =cut
61              
62             sub new {
63             my($class, %args) = @_;
64             return bless(\%args, $class);
65             }
66              
67              
68             All other POD documentation (including other =head2 tags) is added as HTML
69             (provided by Pod::POM::View::HTML) into the Doxygen section named
70             B. IMHO it looks better when this is at the top of the
71             doxygen docs. Look under L on how to do that.
72              
73             =head1 FUNCTION HEADERS
74              
75             The complete syntax of a =head2 function description is:
76              
77             C<< =head2 [] [] () >>
78              
79             =over
80              
81             =item category (optional)
82              
83             The category defines the type of the function definition. The values
84             C and C result in the function being tagged
85             as B for Doxygen. Other values will be ignored, which will result
86             interpreting the function as method.
87              
88             =item return_value (optional)
89              
90             Since Doxygen expects C++ input, a return value is mandatory and will
91             default to B. A given string will be passed to Doxygen as is, so be
92             careful with non word characters.
93              
94             =item name
95              
96             The function name with optional package name e.g. C. The
97             module will try to map the function name to the current package when none is
98             given. If your code is correctly parsable with PPI, then this should work.
99              
100             If the corresponding subroutine is not found it will be tagged as B
101             to Doxygen. This is useful for dynamically generated functions (e.g via
102             AUTOLOAD). Yes this has nothing to do with the C++ virtual keyword, but so
103             what? If you want to have the virtual subroutine mapped to the correct
104             namespace you will have to add it to the subs name
105             (e.g. C< MyClass::mysub() >)
106              
107             Subroutine names with leading underscore will be tagged as B
108             for Doxygen.
109              
110             If there is no package declaration, the subroutine is created in the main
111             namespace, named C<< _main >>.
112              
113             =item parameters
114              
115             The subroutine's comma separated parameter list. References are given in
116             dereference syntax so C<%$varname> specifies a hash reference. This will
117             be given as C to Doxygen e.g. C.
118              
119             =back
120              
121             =head1 SIGNATURES
122              
123             If you are using subroutine signatures, they will be parsed for information
124             and you can put the pod after the sub declaration like so:
125              
126             sub my_sig_sub ($self, $first = 'default', $second=[], %args) {
127             =for method $self
128              
129             Sub documentation.
130              
131             =cut
132              
133             print join(' ', $first, @$second), "\n";
134             return $self;
135             }
136              
137             In that case there is no redundant information you'll have to synchronize on
138             each change.
139             In that case the first parameter behind the B<=for> has to be C,
140             C or C. The second parameter specifies the return
141             value. Both parameters must be present because the =for tag requires them.
142             There are no defaults.
143              
144             A conflicting B<=head2> declaration for the same subroutine will take
145             precedence.
146              
147             =head1 DETAILS ON TOP
148              
149             For having the non subroutine POD documentation at the top of the Doxygen
150             page do the following:
151              
152             =over
153              
154             =item 1.
155              
156             Create a doxygen layout XML file with C
157              
158             =item 2.
159              
160             Edit the XML file. Move C<< >> up to the
161             line directly behind C<< >>
162              
163             =item 3.
164              
165             Specify the file under C in your Doxyfile.
166              
167             =back
168              
169             =head1 METHODS
170              
171             =cut
172              
173 2     2   318039 use strict;
  2         10  
  2         50  
174 2     2   9 use warnings;
  2         4  
  2         56  
175              
176 2     2   773 use parent 'PPI::Transform';
  2         474  
  2         9  
177              
178 2     2   2522 use 5.010001;
  2         6  
179 2     2   9 use PPI;
  2         4  
  2         34  
180 2     2   9 use File::Basename qw(fileparse);
  2         3  
  2         117  
181 2     2   1058 use Pod::POM;
  2         34757  
  2         98  
182 2     2   849 use Pod::POM::View::Text;
  2         7912  
  2         66  
183 2     2   733 use PPI::Transform::Doxygen::POD;
  2         5  
  2         61  
184 2     2   12 use Params::Util qw{_INSTANCE};
  2         4  
  2         3431  
185              
186             our $VERSION = '0.33';
187              
188             my %vtype = qw(% hash @ array $ scalar & func * glob);
189              
190             my %defaults = (
191             rx_version => qr/our\s*\$VERSION\s*=\s*["']([\d\.]+)/,
192             rx_revision => qr/\$(?:Id|Rev|Revision|LastChangedRevision)\:\s*(\d+)\s*\$/,
193             rx_parent => qr/use\s+(?:base|parent|Mojo::Base)\s+["']?([\w:]+)["']?/,
194             );
195              
196             #=================================================
197              
198             =head2 $obj new(%args)
199              
200             B
201              
202             There are 3 optional arguments for extracting a version number, a revision
203             number and the parent class. Their values have to consist of a regex with one
204             capture group. The key C<> defines the behaviour when there is no
205             output device on calling C<>. Default behaviour is to append the
206             doxygen docs after an __END__ Token. Setting overwrite to a true value will
207             overwrite the input file.
208              
209             The defaults are:
210              
211             rx_version => qr/our\s*\$VERSION\s*=\s*["']([\d\.]+)/,
212             rx_revision => qr/\$(?:Id|Rev|Revision|LastChangedRevision)\:\s*(\d+)\s*\$/,
213             rx_parent => qr/use\s+(?:base|parent|Mojo::Base)\s+["']?([\w:]+)["']?/,
214             overwrite => 0,
215              
216             =cut
217              
218             sub new {
219 2     2 1 271 my ( $class, %args ) = @_;
220              
221 2         22 my $self = shift->SUPER::new(%defaults);
222              
223 2         22 @$self{ keys %args } = values %args;
224              
225 2         6 return $self;
226             }
227              
228             #=================================================
229              
230             =head2 file($in, $out)
231              
232             Start the transformation reading from C<$in> and saving to C<$out>. C<$in>
233             has to be a filename and C<$out> can be a filename or a filehandle.
234             If C<$out> is not given, behaviour is defined by the parameter overwrite
235             (see C).
236              
237             =cut
238              
239             sub file {
240 3     3 1 3116 my ($self, $in, $out) = @_;
241              
242 3 50       12 return unless $in;
243              
244 3   33     11 my $preserve = !$out && !$self->{overwrite};
245              
246 3 50       23 my $Document = PPI::Document->new($in) or return undef;
247 3         148366 $Document->{_in_fn} = $in;
248 3 50       18 $self->document($Document, $preserve) or return undef;
249              
250 3   33     175 $out //= $in;
251              
252 3 50       30 if ( ref($out) eq 'GLOB' ) {
253 3         19 print $out $Document->serialize();
254             } else {
255 0         0 $Document->save($out);
256             }
257             }
258              
259             #=================================================
260              
261             =head2 document($ppi_doc, $preserve)
262              
263             This is normally called by C (see the docs for
264             L). It will convert a PPI::Document object
265             in place.
266              
267             =cut
268              
269             sub document {
270 3     3 1 9 my ( $self, $doc, $preserve ) = @_;
271              
272 3 50       25 _INSTANCE( $doc, 'PPI::Document' ) or return undef;
273              
274 3         16 my $pkg_subs = $self->_parse_packages_subs($doc);
275              
276 3         201 my($fname, $fdir, $fext) = fileparse( $doc->{_in_fn}, qr/\..*/ );
277              
278 3         25 my($pod_txt, $sub_info) = $self->_parse_pod($doc, $fname);
279              
280 3         17 _integrate_sub_info($pkg_subs, $sub_info);
281              
282 3         12 my @packages = sort keys %$pkg_subs;
283 3 100 66     22 my $file_pod = $pod_txt if @packages == 1 and $packages[0] eq '!main';
284              
285 3         15 my $dxout = _out_head($fname . $fext, $file_pod);
286              
287 3         38 for my $pname ( @packages ) {
288              
289 3         14 my @parts = split( /::/, $pname );
290 3         7 my $short = pop @parts;
291 3   100     18 my $namespace = join( '::', @parts ) || '';
292              
293             $dxout .= _out_class_begin(
294             $pname, $short, $namespace, $fname,
295             $pkg_subs->{$pname}{inherit},
296             $pkg_subs->{$pname}{used},
297             $pkg_subs->{$pname}{version},
298             $pkg_subs->{$pname}{revision},
299 3 100       26 $short eq $fname ? $pod_txt : '',
300             );
301              
302 3         14 $dxout .= _out_process_subs( $pname, $pkg_subs, $sub_info );
303              
304 3         19 $dxout .= _out_class_end($namespace);
305             }
306              
307 3 50       11 unless ($preserve) {
308 3         17 $_->delete for $doc->children();
309             }
310              
311 3   33     11317 my $end_tok = $doc->find_first('PPI::Token::End') || PPI::Token::End->new();
312 3         1154 $end_tok->add_content($dxout);
313 3         44 $doc->add_element($end_tok);
314             }
315              
316              
317 49     49   59 sub _strip { my $str = shift; $str =~ s/^ +//mg; $str }
  49         306  
  49         84  
318              
319              
320             sub _out_head {
321 3     3   9 my($fn, $txt) = @_;
322              
323 3   100     12 $txt //= '';
324 3         17 my $out = _strip(qq(
325             /** \@file $fn
326             $txt
327             */
328             ));
329              
330 3         8 return $out;
331             }
332              
333              
334             sub _get_used_modules {
335 3     3   8 my($root) = @_;
336              
337 3         6 my %used;
338 3         12 for my $chld ( $root->schildren() ) {
339 52 100       489 next unless $chld->isa('PPI::Statement::Include');
340 10 100       27 next if $chld->pragma();
341 6         158 $used{$chld->module()} = 1
342             }
343 3         14 return \%used;
344             }
345              
346              
347             my %modifier = (
348             has => 'Accessor Method',
349             before => 'Method Modifier: before',
350             after => 'Method Modifier: after',
351             around => 'Method Modifier: around',
352             fresh => 'Method Modifier: fresh',
353             );
354              
355              
356             sub _parse_packages_subs {
357 3     3   8 my($self, $doc) = @_;
358              
359 3         6 my %pkg_subs;
360              
361             my @main_pkgs = grep {
362 3         22 $_->isa('PPI::Statement::Package')
  158         406  
363             } $doc->children();
364              
365 3 100       17 unless (@main_pkgs) {
366 2         9 $pkg_subs{'!main'}{used} = _get_used_modules($doc);
367 2         10 my($v, $r) = $self->_get_pkg_version($doc);
368 2         5 $pkg_subs{'!main'}{version} = $v;
369 2         6 $pkg_subs{'!main'}{revision} = $r;
370             }
371              
372 3   50     21 my $stmt_nodes = $doc->find('PPI::Statement') || [];
373 3         31675 for my $stmt_node ( @$stmt_nodes ) {
374              
375 148         934 my $pkg = '!main';
376 148         305 my $mod = $stmt_node->child(0);
377             next unless $stmt_node->class() eq 'PPI::Statement::Sub'
378 148 100 100     808 or $modifier{$mod};
379              
380 19         112 my $node = $stmt_node;
381 19         43 while ($node) {
382 856 100       31209 if ( $node->class() eq 'PPI::Statement::Package' ) {
383 13         61 $pkg = $node->namespace();
384 13 50       294 unless ( $pkg_subs{$pkg}{version} ) {
385 13         26 my($v, $r) = $self->_get_pkg_version($node->parent());
386 13         23 $pkg_subs{$pkg}{version} = $v;
387 13         18 $pkg_subs{$pkg}{revision} = $r;
388             }
389 13 100       25 unless ( defined $pkg_subs{$pkg}{inherit} ) {
390             my ($inherit) = _find_first_regex(
391             $node->parent(),
392             'PPI::Statement::Include',
393             $self->{rx_parent},
394 1         4 );
395 1         3 $pkg_subs{$pkg}{inherit} = $inherit;
396             }
397 13 100       27 unless ( defined $pkg_subs{$pkg}{used} ) {
398 1         4 my $parent = $node->parent();
399 1 50       10 $pkg_subs{$pkg}{used} = _get_used_modules($parent)
400             if $parent;
401             }
402             }
403 856   100     2824 $node = $node->previous_sibling() || $node->parent();
404             }
405              
406 19 100       159 my $sub_name = $stmt_node->class() eq 'PPI::Statement::Sub'
407             ? $stmt_node->name
408             : $stmt_node->child(2)->content;
409              
410             # split has sub_name => [qw(one two three)]
411 19 100       473 for my $sn ( grep { /\w/ && $_ ne 'qw' } split(/\W+/, $sub_name) ) {
  25         131  
412 22         80 $pkg_subs{$pkg}{subs}{$sn} = $stmt_node;
413 22 100       43 $pkg_subs{$pkg}{mtype}{$sn} = $modifier{$mod} if $modifier{$mod};
414             }
415             }
416              
417 3         35 return \%pkg_subs;
418             }
419              
420              
421             sub _out_process_subs {
422 3     3   10 my($class, $pkg_subs, $sub_info) = @_;
423              
424 3         7 my $sub_nodes = $pkg_subs->{$class}{subs};
425 3         8 my $mod_types = $pkg_subs->{$class}{mtype};
426              
427 3         6 my $out = '';
428              
429 3         6 my %types;
430 3         14 for my $sname ( sort keys %$sub_nodes ) {
431 23   100     96 my $si = $sub_info->{$sname} || {
432             type => $sname =~ /^_/ ? 'private' : 'public',
433             rv => 'void',
434             params => [],
435             name => $sname,
436             static => 0,
437             virtual => 0,
438             class => $class,
439             text => '

Undocumented Function

',
440             };
441 23         51 $types{ $si->{type} }{$sname} = $si;
442             }
443              
444 3         8 for my $type (qw/public private/) {
445 6         21 $out .= "$type:\n";
446 6         10 for my $sname ( sort keys %{ $types{$type} } ) {
  6         44  
447 23         34 my $si = $types{$type}{$sname};
448 23 100       52 my @static = $si->{static} ? 'static' : ();
449 23 100       37 my @virtual = $si->{virtual} ? 'virtual' : ();
450              
451 23         45 my $fstr = join( ' ', @static, @virtual, $si->{rv}, "$sname(" );
452 23         30 $fstr .= join( ', ', @{ $si->{params} } );
  23         38  
453 23         28 $fstr .= ')';
454              
455 23         37 $out .= "/** \@fn $fstr\n";
456 23 100       52 $out .= "$mod_types->{$sname}\n" if $mod_types->{$sname};
457 23         32 $out .= $si->{text} . "\n";
458 23         42 $out .= _out_html_code( $sname, $sub_nodes->{$sname} );
459 23         38 $out .= "*/\n";
460 23         45 $out .= $fstr . ";\n\n";
461             }
462             }
463              
464 3         52 return $out;
465             }
466              
467              
468             sub _out_class_begin {
469 3     3   17 my($pname, $pkg_short, $namespace, $fname, $inherit, $used, $ver, $rev, $pod_txt) = @_;
470              
471 3 100       15 if ( $pname eq '!main' ) {
472 2         8 $pkg_short = $pname = "${fname}_main";
473             }
474              
475 3         19 my $out = '';
476              
477 3 100       11 $out .= "namespace $namespace {\n" if $namespace;
478              
479 3         10 $out .= "\n/** \@class $pname\n\n";
480 3 50       9 $out .= "\@version $ver" if $ver;
481 3 50       11 $out .= " rev:$rev" if $rev;
482 3         6 $out .= "\n\n";
483              
484 3 50       9 if ($used) {
485 3         8 $out .= "\@section ${pkg_short}_USED_MODULES USED_MODULES\n";
486 3         5 $out .= "
    \n";
487 3         23 for my $uname ( sort keys %$used ) {
488 6         26 $out .= "
  • $uname
  • \n";
    489             }
    490 3         7 $out .= "\n";
    491             }
    492              
    493 3         8 $out .= "$pod_txt\n*/\n\n";
    494              
    495 3         8 $out .= "class $pkg_short: public";
    496 3 50       14 $out .= " ::$inherit" if $inherit;
    497 3         7 $out .= " {\n\n";
    498              
    499 3         8 return $out;
    500             }
    501              
    502              
    503             sub _out_class_end {
    504 3     3   9 my($namespace) = @_;
    505              
    506 3         8 my $out = "};\n";
    507 3 100       10 $out .= "};\n" if $namespace;
    508              
    509 3         9 return $out;
    510             }
    511              
    512              
    513             sub _parse_pod {
    514 3     3   11 my($self, $doc, $fname) = @_;
    515              
    516 3         43 my $parser = Pod::POM->new();
    517              
    518 3         60 my $txt = '';
    519 3         8 my %subs;
    520              
    521 3         14 my $pod_tokens = $doc->find('PPI::Token::Pod');
    522              
    523 3 100       31405 return '', \%subs unless $pod_tokens;
    524              
    525 2     2   27 no warnings qw(once);
      2         4  
      2         3165  
    526 2         9 $PPI::Transform::Doxygen::POD::PREFIX = $fname;
    527 2         7 for my $tok ( @$pod_tokens ) {
    528 16         3894 ( my $quoted = $tok->content() ) =~ s/(\@|\\|\%|#)/\\$1/g;
    529 16         134 my $pom = $parser->parse_text($quoted);
    530 16         6670 _filter_head2( $pom, \%subs );
    531 16         142 $txt .= PPI::Transform::Doxygen::POD->print($pom);
    532             }
    533              
    534 2         575 return $txt, \%subs;
    535             }
    536              
    537              
    538             sub _filter_head2 {
    539 35     35   118 my($pom, $sub_ref) = @_;
    540 35         41 for my $sn ( @{ $pom->content() } ) {
      35         132  
    541 27 100 100     407 if ( $sn->type() eq 'head2' and $sn->title() =~ /[\w:]+\s*\(.*\)/ ) {
    542 8         580 my $sinfo = _sub_extract( $sn->title() );
    543 8 50       34 if ( $sinfo ) {
    544 8   50     36 $sinfo->{text} = PPI::Transform::Doxygen::POD->print($sn) // '';
    545 8         9700 $sub_ref->{$sinfo->{name}} = $sinfo;
    546 8         16 $sn = '';
    547             }
    548             }
    549 27 100       314 _filter_head2($sn, $sub_ref) if $sn;
    550             }
    551             }
    552              
    553              
    554             my $rx_name_parms = qr/\s*([\w:]+)\s*\(\s*([^\)]*)\s*\)$/;
    555             sub _sub_extract {
    556 8     8   117 my($str) = @_;
    557              
    558              
    559 8         35 my($long, $params) = $str =~ /$rx_name_parms/;
    560 8 50       311 return unless $long;
    561              
    562 8         26 $str =~ s/$rx_name_parms//;
    563              
    564 8         560 my @parts = split(/\s+/, $str);
    565              
    566 8   100     24 my $rv = pop(@parts) || 'void';
    567 8         18 $rv =~ s/(\%|\@|\&)/\\$1/g;
    568              
    569 8   100     27 my $cat = pop(@parts) || '';
    570              
    571 8         18 my @params = _add_type($params);
    572              
    573 8         20 my @nparts = split( /::/, $long );
    574 8         22 my $name = pop @nparts;
    575 8   50     30 my $class = join( '::', @nparts ) || '!main';
    576              
    577 8   100     28 my $static = $cat eq 'function' || $cat eq 'class_method';
    578 8 100       23 my $type = $name =~ /^_/ ? 'private' : 'public';
    579              
    580             return {
    581 8         50 type => $type,
    582             rv => $rv,
    583             params => \@params,
    584             name => $name,
    585             static => $static,
    586             class => $class,
    587             text => '',
    588             };
    589             }
    590              
    591              
    592             sub _add_type {
    593 15 100   15   40 return unless my $params = shift;
    594              
    595 14 100       29 unless ( ref($params) ) {
    596 7         14 $params =~ s/\s//g;
    597 7         22 $params = [ split(/,/, $params) ];
    598             }
    599              
    600             return map {
    601 14         27 my @sig = $_ =~ /^(.)(.)(.?)/;
      25         88  
    602 25 100       57 if ( $sig[0] eq '\\' ) { shift @sig }
      4         6  
    603 25         29 my $ref;
    604 25 100       50 if ( $sig[1] eq '$' ) { $ref = 1; splice(@sig, 1, 1) }
      2         3  
      2         4  
    605 25         45 my $typ = $vtype{ $sig[0] };
    606 25 100       50 $typ .= '_ref' if $ref;
    607 25         57 s/^\W*//;
    608 25         104 $_ = "$typ $_";
    609             } @$params;
    610             }
    611              
    612              
    613             sub _find_first_regex {
    614 31     31   57 my($root, $name, $regex) = @_;
    615 31         62 for my $chld ( $root->schildren() ) {
    616 479 100       4111 next unless $chld->isa($name);
    617 15 50       43 if ( my @capture = $chld->content() =~ /$regex/ ) {
    618 0         0 return @capture;
    619             }
    620             }
    621 31         59 return '';
    622             }
    623              
    624              
    625             sub _get_pkg_version {
    626 15     15   62 my($self, $root) = @_;
    627             my($version) = _find_first_regex(
    628             $root,
    629             'PPI::Statement::Variable',
    630             $self->{rx_version},
    631 15         37 );
    632              
    633             my($revision) = _find_first_regex(
    634             $root,
    635             'PPI::Statement::Variable',
    636             $self->{rx_revision},
    637 15         26 );
    638 15         33 return $version, $revision;
    639             }
    640              
    641              
    642             sub _out_html_code {
    643 23     23   37 my($sname, $sub) = @_;
    644              
    645 23         66 my $html = _strip(qq(
    646             \@htmlonly
    647            
    648             Code:
    649            
    650            
    click to view
    651            
    652             \@endhtmlonly
    653             \@code
    654             ));
    655              
    656 23         60 $html .= $sub;
    657 23         1262 $html .= "\n";
    658              
    659 23         40 $html .= _strip(q(
    660             @endcode
    661             @htmlonly
    662            
    663             @endhtmlonly
    664             ));
    665              
    666 23         107 return $html;
    667             }
    668              
    669              
    670             sub _sub_info_from_node {
    671 15     15   26 my($sname, $class, $node) = @_;
    672              
    673 15 100 100     41 return undef unless $node->class eq 'PPI::Statement::Sub'
          100        
    674             or ($node->children > 6 and $node->child(6)->content eq 'sub');
    675              
    676 9         140 my $parser = Pod::POM->new();
    677 9         89 my %si;
    678 9         15 my $txt = my $def = my $fmt = '';
    679 9         10 my @params;
    680 9         16 my($rv, $static);
    681 9 50       30 my $type = $sname =~ /^_/ ? 'private' : 'public';
    682              
    683 9   100     29 my $pod = $node->find('PPI::Token::Pod') || [];
    684 9         5530 for my $tok ( @$pod ) {
    685 4         13 ( my $quoted = $tok ) =~ s/(\@|\\|\%|#)/\\$1/g;
    686 4         27 my $pom = $parser->parse_text($quoted);
    687 4 50       1314 next unless my $for = $pom->for->[0];
    688 4         78 $rv = $for->text;
    689 4         53 $fmt = $for->format;
    690 4   100     55 $static = $fmt eq 'function' || $fmt eq 'class_method';
    691 4         11 $txt .= PPI::Transform::Doxygen::POD->print($pom);
    692             }
    693 9   100     1180 my $proto = $node->find('PPI::Token::Prototype') || [];
    694 9         5354 for my $tok ( @$proto ) {
    695 7         21 for my $pmt ( split(/,/, $tok->prototype) ) {
    696 17         144 my($attr, $default) = split(/=/, $pmt);
    697 17         29 push @params, $attr;
    698 17 100       30 next unless $default;
    699 3         10 $def .= "

    Default value for $attr is $default.

    \n";
    700             }
    701 7         17 @params = _add_type(\@params);
    702             }
    703              
    704 9 100       35 return undef unless $txt;
    705              
    706 4 100       10 $txt .= "\n$def" if $def;
    707              
    708             return {
    709 4         120 type => $type,
    710             rv => $rv,
    711             params => \@params,
    712             name => $sname,
    713             static => $static,
    714             class => $class,
    715             text => $txt,
    716             regex => qr/\r?\n=for\s+$fmt\s+\Q$rv\E.+?\r?\n=cut\n\n?/s,
    717             }
    718             }
    719              
    720              
    721             sub _integrate_sub_info {
    722 3     3   8 my($pkg_subs, $sub_info) = @_;
    723              
    724 3         5 my %look;
    725 3         12 for my $class ( keys %$pkg_subs ) {
    726 3         6 for my $subname ( keys %{ $pkg_subs->{$class}{subs} } ) {
      3         19  
    727 22 100       43 if ( $sub_info->{$subname} ) {
    728             # pod info exists
    729 7         15 $sub_info->{$subname}{class} = $class;
    730 7         10 $look{$subname} = 1;
    731 7         10 next;
    732             };
    733             my $si = _sub_info_from_node(
    734             $subname,
    735             $class,
    736 15         32 $pkg_subs->{$class}{subs}{$subname},
    737             );
    738 15 100       89 if ( $si ) {
    739 4         9 $sub_info->{$subname} = $si;
    740 4         20 $pkg_subs->{$class}{subs}{$subname} =~ s/$si->{regex}//;
    741 4         952 $look{$subname} = 1;
    742             }
    743             }
    744             }
    745              
    746 3         11 for my $si ( values %$sub_info ) {
    747 12 100       29 next if $look{ $si->{name} };
    748 1         4 $si->{virtual} = 1;
    749             $pkg_subs->{$si->{class}}{subs}{$si->{name}}
    750 1         4 = '

    virtual function or method

    ';
    751             }
    752             }
    753              
    754             1;
    755              
    756             =pod
    757              
    758             =head1 AUTHOR
    759              
    760             Thomas Kratz Etomk@cpan.orgE
    761              
    762             =head1 REPOSITORY
    763              
    764             L
    765              
    766             =head1 COPYRIGHT
    767              
    768             Copyright 2016-2018 Thomas Kratz.
    769              
    770             This program is free software; you can redistribute
    771             it and/or modify it under the same terms as Perl itself.
    772              
    773             =cut