File Coverage

blib/lib/Comment/Spell.pm
Criterion Covered Total %
statement 79 79 100.0
branch 5 6 83.3
condition n/a
subroutine 28 28 100.0
pod 6 6 100.0
total 118 119 99.1


line stmt bran cond sub pod time code
1             package Comment::Spell;
2              
3 3     3   192972 use 5.006;
  3         24  
4 3     3   15 use strict;
  3         4  
  3         56  
5 3     3   11 use warnings;
  3         5  
  3         119  
6 3     3   18 use Carp qw( croak );
  3         3  
  3         127  
7 3     3   1581 use Moo qw( has );
  3         32416  
  3         14  
8 3     3   5524 use Pod::Wordlist 1.07;
  3         1042261  
  3         229  
9 3     3   1983 use PPI;
  3         305096  
  3         150  
10 3     3   32 use Path::Tiny qw( path );
  3         6  
  3         200  
11 3     3   1796 use IO::Handle;
  3         14463  
  3         172  
12 3     3   1668 use IO::Scalar;
  3         11221  
  3         153  
13 3     3   1553 use Text::Wrap qw( wrap );
  3         7720  
  3         392  
14              
15             =head1 NAME
16              
17             Comment::Spell - Spell Checking for your comments
18              
19             =head1 VERSION
20              
21             0.001004
22              
23             =cut
24              
25             our $VERSION = '0.001004';
26              
27             =head1 SYNOPSIS
28              
29             C is a work-a-like for Perl Comments similar to C.
30              
31             It offers no I spell checking services, merely streamlines extracting tokens
32             to pass to a spell checker of your choice, while removing some basic useful items (stop-words).
33              
34             It also, by default, ignores comments with two or more leading hashes so to avoid directive comments
35             like those found in C
36              
37             # Shorthand for CLI
38             perl -MComment::Spell -e 'Comment::Spell->new->parse_from_file(q[Foo.pm])' | spell -a
39              
40             # Advanced Usage:
41              
42             my $speller = Comment::Spell->new();
43              
44             $speller->parse_from_file(q[Foo.pm]); # streams words to spell to STDOUT by default
45              
46             $speller->parse_from_filehandle( $myfh ); # again to STDOUT
47              
48             $speller->set_output_file('out.txt');
49              
50             $speller->parse_from_file(q[Foo.pm]); # Now writes to out.txt
51              
52             my $str;
53              
54             $speller->set_output_string($str);
55              
56             $speller->parse_from_file(q[Foo.pm]); # Now writes to $str
57              
58             =head2 C
59              
60             ->new(
61             stopwords => A Pod::Wordlist instance
62             output_filehandle => A IO Handle ( default is STDOUT )
63             )
64              
65             =head2 C
66              
67             The file handle to write to.
68              
69             See L, L and L
70              
71             =head2 C
72              
73             ->set_output_filehandle( $fh );
74             ->set_output_filehandle( \*STDOUT );
75              
76             =head2 C
77              
78             my $str;
79             ->set_output_string( $str ); # will write to $str
80              
81             =head2 C
82              
83             ->set_output_file('./out.txt');
84              
85             =head2 C
86              
87             ->parse_from_file('./in.pm'); # Read in.pm and stream tokens to current FH
88              
89             =head2 C
90              
91             ->parse_from_filehandle( $fh ); # Slurps FH and streams its tokens to current FH
92              
93             =head2 C
94              
95             ->parse_from_string( $string ); # decode $string as a PPI document and stream its comments tokens to FH
96              
97             =head2 C
98              
99             Lower level interface if you want to make C Objects yourself.
100              
101             ->parse_from_document( $ppi_document );
102              
103             =head1 SUBROUTINES/METHODS
104              
105             =cut
106              
107             # this comment is for self testing
108             ## this comment is hidden for self testing
109              
110             has stopwords => (
111             is => 'rw',
112             lazy => 1,
113             builder => '_build_stopwords',
114             handles => {
115             '_learn_stopwords' => 'learn_stopwords',
116             },
117             );
118              
119             has output_filehandle => (
120             is => 'ro' =>,
121             writer => 'set_output_filehandle',
122             builder => '_build_output_filehandle',
123             handles => {
124             '_print_output' => 'print',
125             '_printf_output' => 'printf',
126             '_flush_output' => 'flush',
127             },
128             );
129              
130 3     3   27 no Moo;
  3         9  
  3         39  
131              
132             # Default loader for the stopword list
133             sub _build_stopwords {
134 3     3   97 return Pod::Wordlist->new();
135             }
136              
137             # Default output is STDOUT
138             sub _build_output_filehandle {
139 3     3   4785 return \*STDOUT;
140             }
141              
142             # ->set_output_file( "path/to/file" )
143             sub set_output_file {
144 1     1 1 9375 my ( $self, $filename ) = @_;
145 1         4 $self->set_output_filehandle( path($filename)->openw_raw );
146 1         130 return;
147             }
148              
149             # ->set_output_string( my $str );
150             sub set_output_string { ## no critic (Subroutines::RequireArgUnpacking)
151 1     1 1 21 my $fh = IO::Scalar->new( \$_[1] );
152 1         120 $_[0]->set_output_filehandle($fh);
153 1         2 return;
154             }
155              
156             # Returns a PPI Document for a filehandle
157             # ->_ppi_fh( $filehandle )
158             sub _ppi_fh {
159 3     3   23 my ( undef, $fh ) = @_;
160 3         7 my $content = do {
161 3         14 local $/ = undef;
162 3         139 scalar <$fh>;
163             };
164 3         43 return PPI::Document->new( \$content, readonly => 1 );
165             }
166              
167             # Returns a PPI Document for a file name
168             # ->_ppi_file( $filename )
169             sub _ppi_file {
170 3     3   9 my ( undef, $file ) = @_;
171 3         18 return PPI::Document->new( $file, readonly => 1 );
172             }
173              
174             # Returns a PPI Document for a scalar
175             # ->_ppi_string( $source_code )
176             sub _ppi_string { ## no critic (Subroutines::RequireArgUnpacking)
177 1     1   11 return PPI::Document->new( \$_[1], readonly => 1 );
178             }
179              
180             # Determines if a PPI::Token::Comment should be skipped.
181             # Presently this skips directive comments, which by default have two # marks leading them
182             # if ( ->_skip_comment( PPI::Token::Comment ) )
183             sub _skip_comment {
184 138     138   240 my ( undef, $comment ) = @_;
185 138         354 return scalar $comment->content =~ /\A[#]{2}/msx;
186             }
187              
188             # Extract comment text from a PPI::Token::Comment
189             # Returns comments with leading # removed and trailing \n or \r\n removed.
190             # my $txt = ->_comment_text( PPI::Token::Comment )
191             sub _comment_text {
192 119     119   176 my ( undef, $comment ) = @_;
193 119         191 my $content = $comment->content;
194 119         600 $content =~ s/\A[#]//msx;
195 119         485 $content =~ s/\r?\n\z//msx;
196 119         285 return $content;
197             }
198              
199             # Primary target for "this is the text of a comment we want"
200             # strips stopwords from the comments, and then prints them to the output target
201             # ->_handle_comment_text( $text_string );
202             sub _handle_comment_text {
203 119     119   193 my ( $self, $comment_text ) = @_;
204 119         1849 return $self->_print_words( $self->stopwords->strip_stopwords($comment_text) );
205             }
206              
207             # Primary target for "This is a PPI::Token::Comment we want"
208             # Extracts the content and ferrys it to the output target via _handle_comment_text
209             # ->_handle_comment( PPI::Token::Comment )
210             sub _handle_comment {
211 119     119   184 my ( $self, $comment ) = @_;
212 119         230 return $self->_handle_comment_text( $self->_comment_text($comment) );
213             }
214              
215             # Print a text to the output target wrapped
216             # Overflows instead of snapping words.
217             # ->_print_words( $text )
218             sub _print_words {
219 119     119   78467 my ( $self, $text ) = @_;
220 119 100       288 return unless length $text;
221              
222 89         148 local $Text::Wrap::huge = 'overflow'; ## no critic (Variables::ProhibitPackageVars)
223 89         235 return $self->_print_output( wrap( q[], q[], $text ) . "\n\n" );
224             }
225              
226             # Scan a PPI::Document for Comments, feeding
227             # only the comments to the output target.
228             # ->parse_from_document( PPI::Document )
229             sub parse_from_document {
230 7     7 1 490969 my ( $self, $document ) = @_;
231 7 50       20 my (@comments) = @{ $document->find('PPI::Token::Comment') || [] };
  7         55  
232 7         103123 for my $comment (@comments) {
233 138 100       17428 next if $self->_skip_comment($comment);
234 119         643 $self->_handle_comment($comment);
235             }
236 7         310 $self->_flush_output;
237 7         282 return;
238             }
239              
240             # Load a PPI::Document from a filehandle and process it for comments
241             # ->parse_from_filehandle( $fh );
242             sub parse_from_filehandle {
243 3     3 1 134 my ( $self, $infh ) = @_;
244 3         12 return $self->parse_from_document( $self->_ppi_fh($infh) );
245             }
246              
247             =head2 parse_from_file
248              
249             Load a PPI::Document from a file and process it for comments
250              
251             =cut
252              
253             # ->parse_from_file( $filename )
254             sub parse_from_file {
255 3     3 1 3459 my ( $self, $infile ) = @_;
256 3         14 return $self->parse_from_document( $self->_ppi_file($infile) );
257             }
258              
259             # Load a PPI::Document from a string, and process it for comments
260             # ->parse_from_string( "A String" )
261             sub parse_from_string { ## no critic (Subroutines::RequireArgUnpacking)
262 1     1 1 8654 return $_[0]->parse_from_document( $_[0]->_ppi_string( $_[1] ) );
263             }
264              
265             =head1 SUPPORT
266              
267             You can find documentation for this module with the perldoc command.
268              
269             perldoc Comment::Spell
270              
271             You can also look for information at:
272              
273             =over 4
274              
275             =item * MetaCPAN
276              
277             L
278              
279             =item * RT: CPAN's request tracker
280              
281             L
282              
283             =item * CPANTS
284              
285             L
286              
287             =item * CPAN Testers' Matrix
288              
289             L
290              
291             =item * CPAN Ratings
292              
293             L
294              
295             =item * CPAN Testers Dependencies
296              
297             L
298              
299             =back
300              
301             =head1 AUTHOR
302              
303             Kent Fredric C<< >>
304              
305             Maintained by Nigel Horne, C<< >>
306              
307             =head1 LICENSE AND COPYRIGHT
308              
309             This software is copyright (c) 2017-2021 by Kent Fredric .
310              
311             This is free software; you can redistribute it and/or modify it under
312             the same terms as the Perl 5 programming language system itself.
313              
314             =cut
315              
316             1;