File Coverage

blib/lib/PPIx/EditorTools/Outline.pm
Criterion Covered Total %
statement 109 117 93.1
branch 52 64 81.2
condition 7 12 58.3
subroutine 15 15 100.0
pod 1 1 100.0
total 184 209 88.0


line stmt bran cond sub pod time code
1             package PPIx::EditorTools::Outline;
2              
3             # ABSTRACT: Collect use pragmata, modules, subroutiones, methods, attributes
4              
5 2     2   222273 use 5.008;
  2         7  
  2         77  
6 2     2   12 use strict;
  2         3  
  2         71  
7 2     2   11 use warnings;
  2         3  
  2         60  
8 2     2   12 use Carp;
  2         3  
  2         130  
9 2     2   1835 use Try::Tiny;
  2         3362  
  2         109  
10 2     2   12 use base 'PPIx::EditorTools';
  2         4  
  2         645  
11 2     2   15 use Class::XSAccessor accessors => {};
  2         4  
  2         17  
12              
13 2     2   261 use PPI;
  2         4  
  2         1041  
14              
15             our $VERSION = '0.18';
16              
17             sub find {
18 9     9 1 34184 my ( $self, %args ) = @_;
19 9         74 $self->process_doc(%args);
20              
21 9         33 my $ppi = $self->ppi;
22              
23 9 50       29 return [] unless defined $ppi;
24 9         84 $ppi->index_locations;
25              
26             # Search for interesting things
27 9         30964 require PPI::Find;
28              
29             # TODO things not very discriptive
30             my @things = PPI::Find->new(
31             sub {
32              
33             # This is a fairly ugly search
34 1133 100   1133   13788 return 1 if ref $_[0] eq 'PPI::Statement::Package';
35 1131 100       2082 return 1 if ref $_[0] eq 'PPI::Statement::Include';
36 1114 100       1993 return 1 if ref $_[0] eq 'PPI::Statement::Sub';
37 1108 100       3266 return 1 if ref $_[0] eq 'PPI::Statement';
38             }
39 9         1717 )->in($ppi);
40              
41             # Define a flag indicating that further Method::Signature/Moose check should run
42 9         223 my $check_alternate_sub_decls = 0;
43              
44             # Build the outline structure from the search results
45 9         19 my @outline = ();
46 9         17 my $cur_pkg = {};
47 9         11 my $not_first_one = 0;
48 9         188 foreach my $thing (@things) {
49 63 100       1141 if ( ref $thing eq 'PPI::Statement::Package' ) {
    100          
    100          
    50          
50 2 50       7 if ($not_first_one) {
51 0 0       0 if ( not $cur_pkg->{name} ) {
52 0         0 $cur_pkg->{name} = 'main';
53             }
54 0         0 push @outline, $cur_pkg;
55 0         0 $cur_pkg = {};
56             }
57 2         5 $not_first_one = 1;
58 2         16 $cur_pkg->{name} = $thing->namespace;
59 2         91 $cur_pkg->{line} = $thing->location->[0];
60             } elsif ( ref $thing eq 'PPI::Statement::Include' ) {
61 17 50       73 next if $thing->type eq 'no';
62 17 100       434 if ( $thing->pragma ) {
    100          
63 9         253 push @{ $cur_pkg->{pragmata} }, { name => $thing->pragma, line => $thing->location->[0] };
  9         37  
64             } elsif ( $thing->module ) {
65 7         364 push @{ $cur_pkg->{modules} }, { name => $thing->module, line => $thing->location->[0] };
  7         33  
66 7 50       290 unless ($check_alternate_sub_decls) {
67 35         603 $check_alternate_sub_decls = 1
68 7 100       24 if grep { $thing->module eq $_ } (
69             'Method::Signatures',
70             'MooseX::Declare',
71             'MooseX::Method::Signatures',
72             'Moose::Role',
73             'Moose',
74             );
75             }
76             }
77             } elsif ( ref $thing eq 'PPI::Statement::Sub' ) {
78 6         9 push @{ $cur_pkg->{methods} }, { name => $thing->name, line => $thing->location->[0] };
  6         29  
79             } elsif ( ref $thing eq 'PPI::Statement' ) {
80              
81             # last resort, let's analyse further down...
82 38         103 my $node1 = $thing->first_element;
83 38         197 my $node2 = $thing->child(2);
84              
85 38 100       171 next unless defined $node2;
86              
87             # Tests for has followed by new line
88             try {
89 2     2   13 no warnings 'exiting'; # suppress warning Exiting eval via next
  2         4  
  2         1733  
90 35 100   35   602 if ( defined $node2->{content} ) {
91 33 50       123 if ( $node2->{content} =~ /\n/ ) {
92 0         0 next;
93             }
94             }
95 35         160 };
96              
97             # Moose attribute declaration
98 35 100 66     483 if ( $node1->isa('PPI::Token::Word') && $node1->content eq 'has' ) {
99              
100             # p $_[1]->next_sibling->isa('PPI::Token::Whitespace');
101 17         129 $self->_Moo_Attributes( $node2, $cur_pkg, $thing );
102 17         36 next;
103             }
104              
105             # MooseX::POE event declaration
106 18 50 33     176 if ( $node1->isa('PPI::Token::Word') && $node1->content eq 'event' ) {
107 0         0 push @{ $cur_pkg->{events} }, { name => $node2->content, line => $thing->location->[0] };
  0         0  
108 0         0 next;
109             }
110             }
111             }
112              
113 9 100       140 if ($check_alternate_sub_decls) {
114             $ppi->find(
115             sub {
116 886 100   886   12504 $_[1]->isa('PPI::Token::Word') or return 0;
117 129 100       304 $_[1]->content =~ /^(?:func|method|before|after|around|override|augment|class|role)\z/ or return 0;
118 19 50       155 $_[1]->next_sibling->isa('PPI::Token::Whitespace') or return 0;
119 19 50       513 my $sib_content = $_[1]->next_sibling->next_sibling->content or return 0;
120              
121 19         1366 my $name = eval $sib_content;
122              
123             # if eval() failed for whatever reason, default to original trimmed original token
124 19   66     144 $name ||= ( $sib_content =~ m/^\b(\w+)\b/ )[0];
125              
126 19 50       43 return 0 unless defined $name;
127              
128             # test for MooseX::Declare class, role
129 19 100       64 if ( $_[1]->content =~ m/(class|role)/ ) {
130 4         49 $self->_Moo_PkgName( $cur_pkg, $sib_content, $_[1] );
131 4         9 return 1; # break out so we don't write Package name as method
132             }
133              
134 15         87 push @{ $cur_pkg->{methods} }, { name => $name, line => $_[1]->line_number };
  15         63  
135              
136 15         235 return 1;
137             }
138 5         56 );
139             }
140              
141 9 100       111 if ( not $cur_pkg->{name} ) {
142 4         13 $cur_pkg->{name} = 'main';
143             }
144              
145 9         18 push @outline, $cur_pkg;
146              
147 9         68 return \@outline;
148             }
149              
150             ########
151             # Composed Method, internal, Moose Attributes
152             # cleans moose attributes up, and single lines them.
153             # only runs if PPI finds has
154             # prefix all vars with ma_ otherwise same name
155             ########
156             sub _Moo_Attributes {
157 17     17   29 my ( $self, $ma_node2, $ma_cur_pkg, $ma_thing ) = @_;
158              
159 17         44 my $line_num = $ma_thing->location->[0];
160              
161 17 100       261 if ( $ma_node2->content =~ /[\n|;]/ ) {
162 1         5 return;
163             }
164              
165 16         196 my $attrs = eval $ma_node2->content;
166              
167             # if eval() failed for whatever reason, default to original token
168 16   66     237 $attrs ||= $ma_node2->content;
169              
170 16 100       55 if ( ref $attrs eq 'ARRAY' ) {
171 10         13 map { push @{ $ma_cur_pkg->{attributes} }, { name => $_, line => $line_num, } }
  10         35  
  10         17  
172 2         6 grep {defined} @{$attrs};
  2         4  
173              
174             } else {
175              
176 14         17 push @{ $ma_cur_pkg->{attributes} },
  14         66  
177             {
178             name => $attrs,
179             line => $line_num,
180             };
181             }
182 16         36 return;
183             }
184              
185             ########
186             # Composed Method, internal, Moose Pakage Name
187             # write first Class or Role as Package Name if none
188             # prefix all vars with mpn_ otherwise same name
189             ########
190             sub _Moo_PkgName {
191 4     4   8 my ( $self, $mpn_cur_pkg, $mpn_sib_content, $mpn_ppi_tuple ) = @_;
192 4 100       15 if ( $mpn_cur_pkg->{name} ) { return 1; } # break if we have a pkg name
  1         3  
193             # add to outline
194 3         7 $mpn_cur_pkg->{name} = $mpn_sib_content; # class or role name
195 3         12 $mpn_cur_pkg->{line} = $mpn_ppi_tuple->line_number; # class or role location
196 3         42 return 1;
197             }
198              
199             1;
200              
201             __END__