File Coverage

blib/lib/Pod/Index/Search.pm
Criterion Covered Total %
statement 72 75 96.0
branch 22 30 73.3
condition 6 8 75.0
subroutine 13 13 100.0
pod 3 4 75.0
total 116 130 89.2


line stmt bran cond sub pod time code
1             package Pod::Index::Search;
2              
3 3     3   4902 use 5.008;
  3         11  
  3         150  
4             $VERSION = '0.14';
5              
6 3     3   16 use strict;
  3         7  
  3         103  
7 3     3   18 use warnings;
  3         6  
  3         104  
8 3     3   2930 use Search::Dict ();
  3         3327  
  3         60  
9 3     3   1619 use Pod::Index::Entry;
  3         9  
  3         96  
10 3     3   20 use Carp qw(croak);
  3         5  
  3         178  
11 3     3   17 use File::Spec::Functions;
  3         8  
  3         2815  
12              
13             sub new {
14 4     4 1 2877 my ($class, %args) = @_;
15              
16 4         22 my $self = bless {
17             %args,
18             }, $class;
19              
20 4 50       29 if ($self->{filename}) {
21 4 50       233 open my $fh, "<", $self->{filename}
22             or die "couldn't open $self->{filename}: $!\n";
23 4         14 $self->{fh} = $fh;
24             }
25              
26 4 50       23 unless ($self->{fh}) {
27 0         0 require perlindex;
28 0         0 $self->{fh} = *perlindex::DATA;
29             }
30              
31 4         12 $self->{start} = tell $self->{fh};
32             $self->{filemap} ||= sub {
33 12     12   17 my ($podname) = @_;
34 12         37 my @path_elems = split /::/, $podname;
35 12         24 for my $inc (@INC) {
36 144         613 my $file = catfile($inc, @path_elems);
37 144 100       3164 return "$file.pod" if -e "$file.pod";
38 132 50       2532 return "$file.pm" if -e "$file.pm";
39             }
40 0         0 return undef;
41 4   50     93 };
42              
43 4         17 return $self;
44             }
45              
46             sub subtopics {
47 6     6 1 3659 my ($self, $keyword, %args) = @_;
48              
49 6 50       22 croak "need a keyword " unless defined $keyword;
50 6         529 my $fh = $self->{fh};
51              
52 6         14 $self->look($keyword);
53              
54 6 100       113 my $i = $self->{nocase} ? 'i' : '';
55              
56 6         110 my $re_filter = qr/^\Q$keyword\E/i;
57 6 100       157 my $re_select = $args{deep}
58             ? qr/^((?$i)\Q$keyword\E,.*)/
59             : qr/^((?$i)\Q$keyword\E,[^,]*)/;
60              
61 6         13 local $_;
62 6         8 my @ret;
63             my %seen;
64 6         82 while (<$fh>) {
65 30         68 my ($topic) = split /\t/;
66 30 100       210 last unless $topic =~ $re_filter;
67            
68 24 100 66     181 if ($topic =~ $re_select and not $seen{$1}++) {
69 12         47 push @ret, $1;
70             }
71             }
72 6         55 return @ret;
73             }
74              
75             # hack to make 'look' skip everything before __DATA__:
76             # everything before start always compares negatively
77             sub look {
78 15     15 0 22 my ($self, $keyword) = @_;
79              
80 15         21 my $fh = $self->{fh};
81 15         23 my $start = $self->{start};
82              
83             # the search is case-insensitive (fold => 1), but the results are filtered
84             # later if the user wanted it case-sensitive
85             Search::Dict::look($fh, $keyword, {
86             comp => sub {
87 156 50   156   2654 tell($fh) <= $start ? -1 : $_[0] cmp $_[1];
88             },
89 15         114 fold => 1,
90             });
91             }
92              
93             sub search {
94 9     9 1 2464 my ($self, $keyword) = @_;
95              
96 9 50       24 croak "need a keyword " unless defined $keyword;
97 9         566 my $fh = $self->{fh};
98              
99 9         564 $self->look($keyword);
100              
101 9         141 local $_;
102 9         10 my @ret;
103 9         18 my $keyword_lc = lc $keyword;
104 9         10 my %seen;
105 9         73 while (<$fh>) {
106 22         28 chomp;
107 22         76 my ($entry, $podname, $line, $context) = split /\t/;
108 22 100       60 last unless lc $entry eq $keyword_lc;
109 14 100 100     66 next if !$self->{nocase} and $entry ne $keyword;
110 12 50       45 next if $seen{"$podname\t$line"}++;
111 12         31 push @ret, Pod::Index::Entry->new(
112             keyword => $entry,
113             podname => $podname,
114             line => $line,
115             filename => $self->{filemap}($podname),
116             context => $context,
117             );
118             }
119 9         60 return @ret;
120             }
121              
122              
123             1;
124              
125             __END__