File Coverage

blib/lib/File/CodeSearch/RegexBuilder.pm
Criterion Covered Total %
statement 96 96 100.0
branch 46 46 100.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 6 6 100.0
total 164 164 100.0


line stmt bran cond sub pod time code
1             package File::CodeSearch::RegexBuilder;
2              
3             # Created on: 2009-08-07 18:41:21
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 5     5   34362 use Moose;
  5         406195  
  5         30  
10 5     5   24233 use warnings;
  5         27  
  5         149  
11 5     5   578 use version;
  5         1441  
  5         36  
12 5     5   306 use Carp;
  5         5  
  5         317  
13 5     5   24 use List::MoreUtils qw/any/;
  5         25  
  5         67  
14 5     5   2551 use English qw/ -no_match_vars /;
  5         5136  
  5         35  
15              
16             our $VERSION = version->new('0.7.4');
17              
18             has regex => (
19             is => 'rw',
20             );
21             has re => (
22             is => 'ro',
23             isa => 'ArrayRef',
24             );
25             has whole => (
26             is => 'ro',
27             isa => 'Bool',
28             );
29             has all => (
30             is => 'ro',
31             isa => 'Bool',
32             );
33             has words => (
34             is => 'ro',
35             isa => 'Bool',
36             );
37             has ignore_case => (
38             is => 'ro',
39             isa => 'Bool',
40             );
41             has files => (
42             is => 'rw',
43             isa => 'HashRef',
44             default => sub{{}},
45             );
46             has current_file => (
47             is => 'rw',
48             );
49             has current_count => (
50             is => 'rw',
51             isa => 'Int',
52             default => 0,
53             );
54             has sub_matches => (
55             is => 'rw',
56             isa => 'ArrayRef[Str]',
57             default => sub{[]},
58             );
59             has sub_match => (
60             is => 'rw',
61             isa => 'Bool',
62             );
63             has sub_not_matches => (
64             is => 'rw',
65             isa => 'ArrayRef[Str]',
66             default => sub{[]},
67             );
68             has sub_not_match => (
69             is => 'rw',
70             isa => 'Bool',
71             );
72             has last => (
73             is => 'rw',
74             isa => 'ArrayRef[Str]',
75             );
76             has lasts => (
77             is => 'rw',
78             isa => 'HashRef[Str]',
79             default => sub{{}},
80             );
81             has smart => (
82             is => 'rw',
83             isa => 'Bool',
84             );
85              
86             sub make_regex {
87 4550     4550 1 24665 my ($self) = @_;
88 4550 100       135480 return $self->regex if ref $self->regex eq 'Regexp';
89              
90 33         59 my $re;
91 33         1240 my $words = $self->re;
92              
93 33         51 my $start = shift @{ $words };
  33         88  
94 33 100       150 return $self->regex(qr//) if !defined $start;
95              
96 32 100   93   269 if (!any {$start eq $_} qw/n b ss/) {
  93         144  
97 29         36 unshift @{ $words }, $start;
  29         73  
98 29         58 undef $start;
99             }
100              
101 32 100       1368 if ($self->whole) {
102 5         6 @{$words} = map { "\\b$_\\b" } @{$words};
  5         15  
  8         26  
  5         14  
103             }
104              
105 32 100       1197 if ($self->all) {
    100          
106 4 100       5 if (@{ $words } == 2 ) {
  4         16  
107 3         21 $re = "$words->[0].*$words->[1]|$words->[1].*$words->[0]";
108             }
109             else {
110 1         4 $re = join ' ', @$words;
111             }
112             }
113             elsif ( $self->words ) {
114 2         4 $re = join '.*', @{ $words };
  2         11  
115             }
116             else {
117 26         40 $re = join ' ', @{ $words };
  26         81  
118             }
119              
120 32 100       1293 if ($self->ignore_case) {
121 1         5 $re = "(?i:$re)";
122             }
123              
124             $re =
125 32 100       97 !defined $start ? $re
    100          
    100          
126             : $start eq 'n' ? "function(?:&?\\s+|\\s+&?\\s*)$re|$re\\s+=\\s+function"
127             : $start eq 'b' ? "sub\\s+$re"
128             : "class\\s+$re";
129              
130 32         1786 return $self->regex(qr/$re/);
131             }
132              
133             sub match {
134 4518     4518 1 10812 my ($self, $line) = @_;
135 4518         7461 my $re = $self->make_regex;
136              
137 4518         7912 $self->check_sub_matches($line);
138 4518         7021 $self->check_lasts($line);
139              
140 4518         15341 my ($match) = $line =~ /($re)/;
141              
142 4518 100       7738 if (defined $match) {
143 3311         102820 $self->current_count( $self->current_count + 1 );
144             }
145              
146 4518         17304 return $match;
147             }
148              
149             sub check_sub_matches {
150 4523     4523 1 5746 my ($self, $line) = @_;
151 4523         136856 my $matches = $self->sub_matches;
152 4523         4486 my $match = 0;
153 4523         137724 my $not_matches = $self->sub_not_matches;
154 4523         3940 my $not_match = 0;
155              
156 4523 100       133593 return if $self->sub_match;
157 4522 100       138296 return if $self->sub_not_match;
158              
159 4521         8120 for my $match_re (@$matches) {
160 3         21 $match = $line =~ /$match_re/;
161 3 100       10 last if $match;
162             }
163              
164 4521         133712 $self->sub_match($match);
165              
166 4521         6270 for my $not_match_re (@$not_matches) {
167 3         10 $not_match = $line =~ /$not_match_re/;
168 3 100       6 last if $not_match;
169             }
170              
171 4521         141635 $self->sub_not_match($not_match);
172              
173 4521         6325 return;
174             }
175              
176             sub check_lasts {
177 4518     4518 1 4895 my ($self, $line) = @_;
178              
179 4518 100       133753 if ($self->last) {
180 12         14 for my $last (@{ $self->last }) {
  12         502  
181 18 100       180 my ($match) =
    100          
    100          
182             $last eq 'function' ? $line =~ /function \s+ (?: & \s*)? ([\w-]+)/xms
183             : $last eq 'class' ? $line =~ /class \s+ ([\w-]+)/xms
184             : $last eq 'sub' ? $line =~ /sub \s+ ([\w-]+)/xms
185             : $line =~ /$last \s+ ([\w-]+)/xms;
186 18 100       295 $self->lasts->{$last} = $match if $match;
187             }
188             }
189              
190 4518         5192 return;
191             }
192              
193             sub get_last_found {
194 5     5 1 19 my ($self) = @_;
195 5         12 my $out = '';
196              
197 5 100       6 return '' if ! %{$self->lasts};
  5         234  
198              
199 4         9 for my $last (sort keys %{$self->lasts} ) {
  4         170  
200 6         261 $out .= "$last " . $self->lasts->{$last} . "\n";
201             }
202              
203 4         41 return $out;
204             }
205              
206             sub reset_file {
207 28     28 1 1723 my ($self, $file) = @_;
208 28 100 100     1007 if ( $self->current_count() && $self->current_file ) {
209 22         658 $self->files->{$self->current_file} = $self->current_count;
210             }
211              
212 28         1048 $self->sub_match(0);
213 28         965 $self->sub_not_match(0);
214 28         987 $self->current_count(0);
215 28         942 $self->current_file($file);
216 28         947 $self->lasts({});
217              
218 28         65 return;
219             }
220              
221              
222             1;
223              
224             __END__
225              
226             =head1 NAME
227              
228             File::CodeSearch::RegexBuilder - Takes in various options and builds a regular expression to check lines of a file
229              
230             =head1 VERSION
231              
232             This documentation refers to File::CodeSearch::RegexBuilder version 0.7.4.
233              
234             =head1 SYNOPSIS
235              
236             use File::CodeSearch::RegexBuilder;
237              
238             # Brief but working code example(s) here showing the most common usage(s)
239             # This section will be as far as many users bother reading, so make it as
240             # educational and exemplary as possible.
241              
242             =head1 DESCRIPTION
243              
244             =head1 ATTRIBUTES
245              
246             =over 4
247              
248             =item C<regex>
249              
250             The compiled regex
251              
252             =item C<re (ArrayRef)>
253              
254             The strings to compile the regular expression from
255              
256             =item C<whole (Bool)>
257              
258             Makes sure each element of C<re> is matched as a whole word
259              
260             =item C<all (Bool)>
261              
262             Makes sure that the elements of C<re> are matched in any order (currently only two elements supported)
263              
264             =item C<words (Bool)>
265              
266             Match each word separated by arbitrary number of characters (default separation is one space)
267              
268             =item C<ignore_case (Bool)>
269              
270             Ignore case in the final regex
271              
272             =item C<files (HashRef)>
273              
274             Stores a count of matches in each file
275              
276             =item C<current_file>
277              
278             Reference to the current file being searched
279              
280             =item C<current_count (Int)>
281              
282             The number of matches found in the currently searched file
283              
284             =item C<sub_matches (ArrayRef[Str])>
285              
286             Terms to search on that the file should also contain to be considered to have matched
287              
288             =item C<sub_match (Bool)>
289              
290             Stores if a sub match has been found
291              
292             =item C<sub_not_matches (ArrayRef[Str])>
293              
294             Terms to search on that the file should not contain to be considered to have matched
295              
296             =item C<sub_not_match (Bool)>
297              
298             Stores if a not sub match has been found
299              
300             =item C<last (ArrayRef[Str])>
301              
302             A list of types to keep track of for context of a match (eg the last function, class or sub)
303              
304             =item C<lasts (HashRef[Str])>
305              
306             The current state of requested "last" types
307              
308             =item C<smart (Bool)>
309              
310             Create smart regular expression
311              
312             =back
313              
314             =head1 SUBROUTINES/METHODS
315              
316             =head2 C<make_regex ()>
317              
318             =head2 C<match ($line)>
319              
320             =head2 C<sub_matches ($line)>
321              
322             =head2 C<reset_file ( $file )>
323              
324             Resets file based counters and adds $file as the new file being processed
325              
326             =head2 C<check_sub_matches ( $line )>
327              
328             Checks that $line matches any specified sub matches
329              
330             =head2 C<check_lasts ( $line )>
331              
332             Checks if the line matches a block start signature eg checks if we are starting
333             a sub, function or class so that any matches in that block can be identified as
334             coming from there.
335              
336             =head2 C<get_last_found ()>
337              
338             Returns the last match block
339              
340             =head1 DIAGNOSTICS
341              
342             =head1 CONFIGURATION AND ENVIRONMENT
343              
344             =head1 DEPENDENCIES
345              
346             =head1 INCOMPATIBILITIES
347              
348             =head1 BUGS AND LIMITATIONS
349              
350             There are no known bugs in this module.
351              
352             Please report problems to Ivan Wills (ivan.wills@gmail.com).
353              
354             Patches are welcome.
355              
356             =head1 AUTHOR
357              
358             Ivan Wills - (ivan.wills@gmail.com)
359              
360             =head1 LICENSE AND COPYRIGHT
361              
362             Copyright (c) 2009 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
363             All rights reserved.
364              
365             This module is free software; you can redistribute it and/or modify it under
366             the same terms as Perl itself. See L<perlartistic>. This program is
367             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
368             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
369             PARTICULAR PURPOSE.
370              
371             =cut