File Coverage

blib/lib/Perl/Tags.pm
Criterion Covered Total %
statement 93 95 97.8
branch 16 22 72.7
condition 11 15 73.3
subroutine 18 19 94.7
pod 11 11 100.0
total 149 162 91.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4            
5             Perl::Tags - Generate (possibly exuberant) Ctags style tags for Perl sourcecode
6            
7             =head1 SYNOPSIS
8            
9             =head2 Using Perl::Tags to assist your development
10            
11             C<Perl::Tags> is designed to be integrated into your development
12             environment. Here are a few ways to use it:
13            
14             =head3 With Vim
15            
16             C<Perl::Tags> was originally designed to be used with vim. See
17             L<https://github.com/osfameron/perl-tags-vim> for an easily installable Plugin.
18            
19             NB: You will need to have a vim with perl compiled in it. Debuntu packages
20             this as C<vim-perl>. Alternatively you can compile from source (you'll need
21             Perl + the development headers C<libperl-dev>).
22            
23             (Note that C<perl-tags-vim> includes its own copy of C<Perl::Tags> through
24             the magic of git submodules and L<App::FatPacker>, so you don't need to install
25             this module from CPAN if you are only intending to use it with Vim as above!)
26            
27             =head3 From the Command Line
28            
29             See the L<bin/perl-tags> script provided.
30            
31             =head3 From other editors
32            
33             Any editor that supports ctags should be able to use this output. Documentation
34             and code patches on how to do this are welcome.
35            
36             =head2 Using the Perl::Tags module within your code
37            
38             use Perl::Tags;
39             my $naive_tagger = Perl::Tags::Naive->new( max_level=>2 );
40             $naive_tagger->process(
41             files => ['Foo.pm', 'bar.pl'],
42             refresh=>1
43             );
44            
45             print $naive_tagger; # stringifies to ctags file
46            
47             Recursively follows C<use> and C<require> statements, up to a maximum
48             of C<max_level>.
49            
50             =head1 DETAILS
51            
52             There are several taggers distributed with this distribution, including:
53            
54             =over 4
55            
56             =item L<Perl::Tags::Naive>
57            
58             This is a more-or-less straight ripoff, slightly updated, of the original
59             pltags code. This is a "naive" tagger, in that it makes pragmatic assumptions
60             about what Perl code usually looks like (e.g. it doesn't actually parse the
61             code.) This is fast, lightweight, and often Good Enough.
62            
63             This has additional subclasses such as L<Perl::Tags::Naive::Moose> to parse
64             Moose declarations, and L<Perl::Tags::Naive::Lib> to parse C<use lib>.
65            
66             =item L<Perl::Tags::PPI>
67            
68             Uses the L<PPI> module to do a deeper analysis and parsing of your Perl code.
69             This is more accurate, but slower.
70            
71             =item L<Perl::Tags::Hybrid>
72            
73             Can run multiple taggers, such as ::Naive and ::PPI, combining the results.
74            
75             =back
76            
77             =head1 EXTENDING
78            
79             Documentation patches are welcome: in the meantime, have a look at
80             L<Perl::Tags::Naive> and its subclasses for a simple line-by-line method of
81             tagging files. Alternatively L<Perl::Tags::PPI> uses L<PPI>'s built in
82             method of parsing Perl documents.
83            
84             In general, you will want to override the C<get_tags_for_file> method,
85             returning a list of C<Perl::Tags::Tag> objects to be registered.
86            
87             For recursively checking other modules, return a C<Perl::Tags::Tag::Recurse>
88             object, which does I<not> create a new tag in the resulting perltags file,
89             but instead processes the next file recursively.
90            
91             =head1 FEATURES
92            
93             * Recursive, incremental tagging.
94             * parses `use_ok`/`require_ok` line from Test::More
95            
96             =head1 METHODS
97            
98             =cut
99              
100             package Perl::Tags;
101              
102 9     9   127345 use strict; use warnings;
  9     9   22  
  9         304  
  9         48  
  9         19  
  9         242  
103              
104 9     9   4928 use Perl::Tags::Tag;
  9         60  
  9         1099  
105 9     9   2575 use Data::Dumper;
  9         14901  
  9         504  
106 9     9   60 use File::Spec;
  9         16  
  9         474  
107              
108             our $VERSION = '0.32';
109             $VERSION = eval $VERSION;
110              
111 9     9   43 use overload q("") => \&to_string;
  9         16  
  9         89  
112              
113             =head2 C<new>
114            
115             L<Perl::Tags> is an abstract baseclass. Use a class such as
116             L<Perl::Tags::Naive> and instantiate it with C<new>.
117            
118             $naive_tagger = Perl::Tags::Naive->new( max_level=>2 );
119            
120             Accepts the following parameters
121            
122             max_level: levels of "use" statements to descend into, default 2
123             do_variables: tag variables? default 1 (true)
124             exts: use the Exuberant extensions
125            
126             =cut
127              
128             sub new {
129 9     9 1 1069     my $class = shift;
130 9         195     my %options = (
131                     max_level => 2, # go into next file, but not down the whole tree
132                     do_variables => 1,
133                     @_);
134              
135 9         22     my $self = \%options;
136              
137 9         59     return bless $self, $class;
138             }
139              
140             =head2 C<to_string>
141            
142             A L<Perl::Tags> object will stringify to a textual representation of a ctags
143             file.
144            
145             print $tagger;
146            
147             =cut
148              
149             sub to_string {
150 31     31 1 332     my $self = shift;
151 31 50       95     my $tags = $self->{tags} or return '';
152 31         150     my %tags = %$tags;
153              
154 31         50     my $s; # to test
155              
156                 my @lines;
157              
158             # the structure is an HoHoA of
159             #
160             # {tag_name}
161             # {file_name}
162             # [ tags ]
163             #
164             # where the file_name level is to allow us to prioritize tags from
165             # first-included files (on the basis that they may well be the files we
166             # want to see first.
167              
168 31         53     my $ord = $self->{order};
169 31         171     my @names = sort keys %$tags;
170 31         71     for (@names) {
171 149         190         my $files = $tags{$_};
172 149         144         push @lines, map { @{$files->{$_}} }
  149         445  
  0         0  
173 149         264             sort { $ord->{$a} <=> $ord->{$b} } keys %$files;
174                 }
175 31         333     return join "\n", @lines;
176             }
177              
178             =head2 C<clean_file>
179            
180             Delete all tags, but without touching the "order" seen, that way, if the tags
181             are recreated, they will remain near the top of the "interestingness" tree
182            
183             =cut
184              
185             sub clean_file {
186 1     1 1 2     my ($self, $file) = @_;
187                 
188 1 50       5     my $tags = $self->{tags} or die "Trying to clean '$file', but there's no tags";
189                 
190 1         4     for my $name (keys %$tags) {
191 4         40         delete $tags->{$name}{$file};
192                 }
193 1         4     delete $self->{seen}{$file};
194             # we don't delete the {order} though
195             }
196              
197             =head2 C<output>
198            
199             Save the file to disk if it has changed. (The private C<{is_dirty}> attribute
200             is used, as the tags object may be made up incrementally and recursively within
201             your IDE.
202            
203             =cut
204              
205             sub output {
206 5     5 1 1009308     my $self = shift;
207 5         33     my %options = @_;
208 5 100       42     my $outfile = $options{outfile} or die "No file to write to";
209              
210 4 100 100     114     return unless $self->{is_dirty} || ! -e $outfile;
211              
212 3 50       470     open (my $OUT, '>', $outfile) or die "Couldn't open $outfile for write: $!";
213 3     1   71 binmode STDOUT, ":encoding(UTF-8)";
  1         10  
  1         2  
  1         8  
214 3         12886     print $OUT $self;
215 3 50       229     close $OUT or die "Couldn't close $outfile for write: $!";
216              
217 3         75     $self->{is_dirty} = 0;
218             }
219              
220             =head2 C<process>
221            
222             Scan one or more Perl file for tags
223            
224             $tagger->process(
225             files => [ 'Module.pm', 'script.pl' ]
226             );
227             $tagger->process(
228             files => 'script.pl',
229             refresh => 1,
230             );
231            
232             =cut
233              
234             sub process {
235 9     9 1 15427     my $self = shift;
236 9         47     my %options = @_;
237 9   50     47     my $files = $options{files} || die "No file passed to process";
238 9 50       47     my @files = ref $files ? @$files : ($files);
239              
240 9         107     $self->queue( map {
241 9         24                           { file=>$_, level=>1, refresh=>$options{refresh} }
242                                   } @files);
243              
244 9         66     while (my $file = $self->popqueue) {
245 13         92         $self->process_item( %options, %$file );
246                 }
247 9         85     return 1;
248             }
249              
250             =head2 C<queue>, C<popqueue>
251            
252             Internal methods managing the processing
253            
254             =cut
255              
256             sub queue {
257 37     37 1 63     my $self = shift;
258 37         249     for (@_) {
259 37 100       634         push @{$self->{queue}}, $_ unless $_->{level} > $self->{max_level};
  13         58  
260                 }
261             }
262              
263             sub popqueue {
264 22     22 1 40     my $self = shift;
265 22         35     return pop @{$self->{queue}};
  22         110  
266             }
267              
268             =head2 C<process_item>, C<process_file>, C<get_tags_for_file>
269            
270             Do the heavy lifting for C<process> above.
271            
272             Taggers I<must> override the abstract method C<get_tags_for_file>.
273            
274             =cut
275              
276             sub process_item {
277 13     13 1 26     my $self = shift;
278 13         43     my %options = @_;
279 13   50     48     my $file = $options{file} || die "No file passed to proces";
280              
281             # make filename absolute, (this could become an option if appropriately
282             # refactored) but because of my usage (tags_$PID file in /tmp) I need the
283             # absolute path anyway, and it prevents the file being included twice under
284             # slightly different names (unless you have 2 hardlinked copies, as I do
285             # for my .vim/ directory... bah)
286              
287 13         387     $file = File::Spec->rel2abs( $file ) ;
288              
289 13 100       92     if ($self->{seen}{$file}++) {
290 1 50       5         return unless $options{refresh};
291 1         9         $self->clean_file( $file );
292                 }
293              
294 13         27     $self->{is_dirty}++; # we haven't yet been written out
295              
296 13   100     95     $self->{order}{$file} = $self->{curr_order}++ || 0;
297              
298 13         93     $self->{current} = {
299                     file => $file,
300                     package_name => '',
301                     has_subs => 0,
302                     var_continues => 0,
303                     level => $options{level},
304                 };
305              
306 13         81     $self->process_file( $file );
307              
308 13         100     return $self->{tags};
309             }
310              
311             sub process_file {
312 13     13 1 26     my ($self, $file) = @_;
313              
314 13         71     my @tags = $self->get_tags_for_file( $file );
315              
316 13         129     $self->register( $file, @tags );
317             }
318              
319             sub get_tags_for_file {
320 9     9   7894     use Carp 'confess';
  9         62  
  9         1729  
321 0     0 1 0     confess "Abstract method get_tags_for_file called";
322             }
323              
324             =head2 C<register>
325            
326             The parsing is done by a number of lightweight objects (parsers) which look for
327             subroutine references, variables, module inclusion etc. When they are
328             successful, they call the C<register> method in the main tags object.
329            
330             Note that if your tagger wants to register not a new I<declaration> but rather
331             a I<usage> of another module, then your tagger should return a
332             C<Perl::Tags::Tag::Recurse> object. This is a pseudo-tag which causes the linked
333             module to be scanned in turn. See L<Perl::Tags::Naive>'s handling of C<use>
334             statements as an example!
335            
336             =cut
337              
338             sub register {
339 13     13 1 39     my ($self, $file, @tags) = @_;
340 13         31     for my $tag (@tags) {
341 81 100       1006         $tag->on_register( $self ) or next;
342 41   66     817         $tag->{pkg} ||= $self->{current}{package_name};
343 41   66     157         $tag->{exts} ||= $self->{exts};
344              
345             # and copy absolute file if requested
346             # $tag->{file} = $file if $self->{absolute};
347              
348 41         1848         my $name = $tag->{name};
349 41         54         push @{ $self->{tags}{$name}{$file} }, $tag;
  41         321  
350                 }
351             }
352              
353             ##
354             1;
355              
356             =head1 SEE ALSO
357            
358             L<bin/perl-tags>
359            
360             =head1 CONTRIBUTIONS
361            
362             Contributions are always welcome. The repo is in git:
363            
364             http://github.com/osfameron/perl-tags
365            
366             Please fork and make pull request. Maint bits available on request.
367            
368             =over 4
369            
370             =item DMITRI
371            
372             many patches for features and bugfixes
373            
374             =item wolverian
375            
376             ::PPI subclass
377            
378             =item Ian Tegebo
379            
380             patch to use File::Temp
381            
382             =item drbean
383            
384             ::Naive::Moose, ::Naive::Spiffy and ::Naive::Lib subclasses
385            
386             =item Alias
387            
388             prodding me to make repo public
389            
390             =item tsee
391            
392             Command line interface, applying patches
393            
394             =item nothingmuch
395            
396             =item Andreas Koenig
397            
398             =item ether
399            
400             =back
401            
402             =head1 AUTHOR and LICENSE
403            
404             osfameron (2006-2014) - osfameron@cpan.org
405             and contributors, as above
406            
407             For support, try emailing me or grabbing me on irc #london.pm on irc.perl.org
408            
409             This was originally ripped off pltags.pl, as distributed with vim
410             and available from L<http://www.mscha.com/mscha.html?pltags#tools>
411             Version 2.3, 28 February 2002
412             Written by Michael Schaap <pltags@mscha.com>.
413            
414             This is licensed under the same terms as Perl itself. (Or as Vim if you prefer).
415            
416             =cut
417