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 4     4   55917 use 5.006;
  4         11  
2 4     4   15 use strict;
  4         4  
  4         81  
3 4     4   23 use warnings;
  4         5  
  4         250  
4              
5             package Comment::Spell;
6              
7             our $VERSION = '0.001002';
8              
9             # ABSTRACT: Spell Checking for your comments
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 4     4   47 use Carp qw( croak );
  4         6  
  4         240  
14 4     4   1881 use Moo qw( has );
  4         44234  
  4         18  
15 4     4   6921 use Pod::Wordlist 1.07;
  4         976665  
  4         197  
16 4     4   2098 use PPI;
  4         368735  
  4         148  
17 4     4   31 use Path::Tiny qw( path );
  4         4  
  4         179  
18 4     4   1900 use IO::Handle;
  4         14731  
  4         163  
19 4     4   1630 use IO::Scalar;
  4         11218  
  4         150  
20 4     4   1580 use Text::Wrap qw( wrap );
  4         7602  
  4         327  
21              
22             # this comment is for self testing
23             ## this comment is hidden for self testing
24              
25             has stopwords => (
26             is => 'rw',
27             lazy => 1,
28             builder => '_build_stopwords',
29             handles => {
30             '_learn_stopwords' => 'learn_stopwords',
31             },
32             );
33              
34             has output_filehandle => (
35             is => 'ro' =>,
36             writer => 'set_output_filehandle',
37             builder => '_build_output_filehandle',
38             handles => {
39             '_print_output' => 'print',
40             '_printf_output' => 'printf',
41             '_flush_output' => 'flush',
42             },
43             );
44              
45 4     4   20 no Moo;
  4         5  
  4         28  
46              
47             # Default loader for the stopword list
48             sub _build_stopwords {
49 3     3   82 return Pod::Wordlist->new();
50             }
51              
52             # Default output is STDOUT
53             sub _build_output_filehandle {
54 3     3   3656 return \*STDOUT;
55             }
56              
57             # ->set_output_file( "path/to/file" )
58             sub set_output_file {
59 1     1 1 8192 my ( $self, $filename ) = @_;
60 1         4 $self->set_output_filehandle( path($filename)->openw_raw );
61 1         118 return;
62             }
63              
64             # ->set_output_string( my $str );
65             sub set_output_string { ## no critic (Subroutines::RequireArgUnpacking)
66 1     1 1 14 my $fh = IO::Scalar->new( \$_[1] );
67 1         86 $_[0]->set_output_filehandle($fh);
68 1         2 return;
69             }
70              
71             # Returns a PPI Document for a filehandle
72             # ->_ppi_fh( $filehandle )
73             sub _ppi_fh {
74 3     3   7 my ( undef, $fh ) = @_;
75 3         3 my $content = do {
76 3         11 local $/ = undef;
77 3         70 scalar <$fh>;
78             };
79 3         35 return PPI::Document->new( \$content, readonly => 1 );
80             }
81              
82             # Returns a PPI Document for a file name
83             # ->_ppi_file( $filename )
84             sub _ppi_file {
85 3     3   7 my ( undef, $file ) = @_;
86 3         19 return PPI::Document->new( $file, readonly => 1 );
87             }
88              
89             # Returns a PPI Document for a scalar
90             # ->_ppi_string( $source_code )
91             sub _ppi_string { ## no critic (Subroutines::RequireArgUnpacking)
92 1     1   11 return PPI::Document->new( \$_[1], readonly => 1 );
93             }
94              
95             # Determines if a PPI::Token::Comment should be skipped.
96             # Presently this skips directive comments, which by default have two # marks leading them
97             # if ( ->_skip_comment( PPI::Token::Comment ) )
98             sub _skip_comment {
99 147     147   158 my ( undef, $comment ) = @_;
100 147         342 return scalar $comment->content =~ /\A[#]{2}/msx;
101             }
102              
103             # Extract comment text from a PPI::Token::Comment
104             # Returns comments with leading # removed and trailing \n or \r\n removed.
105             # my $txt = ->_comment_text( PPI::Token::Comment )
106             sub _comment_text {
107 128     128   121 my ( undef, $comment ) = @_;
108 128         167 my $content = $comment->content;
109 128         443 $content =~ s/\A[#]//msx;
110 128         359 $content =~ s/\r?\n\z//msx;
111 128         218 return $content;
112             }
113              
114             # Primary target for "this is the text of a comment we want"
115             # strips stopwords from the comments, and then prints them to the output target
116             # ->_handle_comment_text( $text_string );
117             sub _handle_comment_text {
118 128     128   98 my ( $self, $comment_text ) = @_;
119 128         1747 return $self->_print_words( $self->stopwords->strip_stopwords($comment_text) );
120             }
121              
122             # Primary target for "This is a PPI::Token::Comment we want"
123             # Extracts the content and ferrys it to the output target via _handle_comment_text
124             # ->_handle_comment( PPI::Token::Comment )
125             sub _handle_comment {
126 128     128   117 my ( $self, $comment ) = @_;
127 128         192 return $self->_handle_comment_text( $self->_comment_text($comment) );
128             }
129              
130             # Print a text to the output target wrapped
131             # Overflows instead of snapping words.
132             # ->_print_words( $text )
133             sub _print_words {
134 128     128   63073 my ( $self, $text ) = @_;
135 128 100       275 return unless length $text;
136              
137 98         94 local $Text::Wrap::huge = 'overflow'; ## no critic (Variables::ProhibitPackageVars)
138 98         204 return $self->_print_output( wrap( q[], q[], $text ) . "\n\n" );
139             }
140              
141             # Scan a PPI::Document for Comments, feeding
142             # only the comments to the output target.
143             # ->parse_from_document( PPI::Document )
144             sub parse_from_document {
145 7     7 1 288552 my ( $self, $document ) = @_;
146 7 50       17 my (@comments) = @{ $document->find('PPI::Token::Comment') || [] };
  7         53  
147 7         81288 for my $comment (@comments) {
148 147 100       12727 next if $self->_skip_comment($comment);
149 128         571 $self->_handle_comment($comment);
150             }
151 7         293 $self->_flush_output;
152 7         268 return;
153             }
154              
155             # Load a PPI::Document from a filehandle and process it for comments
156             # ->parse_from_filehandle( $fh );
157             sub parse_from_filehandle {
158 3     3 1 113 my ( $self, $infh ) = @_;
159 3         11 return $self->parse_from_document( $self->_ppi_fh($infh) );
160             }
161              
162             # Load a PPI::Document from a file and process it for comments
163             # ->parse_from_file( $filename )
164             sub parse_from_file {
165 3     3 1 2407 my ( $self, $infile ) = @_;
166 3         8 return $self->parse_from_document( $self->_ppi_file($infile) );
167             }
168              
169             # Load a PPI::Document from a string, and process it for comments
170             # ->parse_from_string( "A String" )
171             sub parse_from_string { ## no critic (Subroutines::RequireArgUnpacking)
172 1     1 1 7119 return $_[0]->parse_from_document( $_[0]->_ppi_string( $_[1] ) );
173             }
174              
175             1;
176              
177             __END__