File Coverage

blib/lib/Perl/Critic/Policy/Documentation/ProhibitAdjacentLinks.pm
Criterion Covered Total %
statement 83 83 100.0
branch 18 22 81.8
condition 9 12 75.0
subroutine 16 16 100.0
pod 1 1 100.0
total 127 134 94.7


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
2              
3             # This file is part of Perl-Critic-Pulp.
4              
5             # Perl-Critic-Pulp is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Perl-Critic-Pulp is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
17              
18             package Perl::Critic::Policy::Documentation::ProhibitAdjacentLinks;
19 40     40   25745 use 5.006;
  40         127  
20 40     40   185 use strict;
  40         70  
  40         654  
21 40     40   157 use warnings;
  40         72  
  40         847  
22 40     40   171 use base 'Perl::Critic::Policy';
  40         67  
  40         3579  
23 40     40   145779 use Perl::Critic::Utils;
  40         69  
  40         655  
24              
25             # uncomment this to run the ### lines
26             #use Smart::Comments;
27              
28             # perlcritic -s ProhibitAdjacentLinks ProhibitAdjacentLinks.pm
29             # perlcritic -s ProhibitAdjacentLinks /usr/share/perl5/SVG.pm
30              
31             # cf /usr/lib/perl5/Template/Context.pm
32             # L<Template> L<new()|Template#new()>
33             # the "#" separator is wrong though
34             #
35             # cf /usr/share/perl5/DBIx/Class/Storage/DBI.pm
36             # L<DBI|DBI/ATTRIBUTES_COMMON_TO_ALL_HANDLES>
37             # L<connection|DBI/Database_Handle_Attributes>
38             #
39             # /usr/share/perl5/DhMakePerl/PodParser.pm
40             # L<Pod::Parser> L<command|Pod::Parser/command>
41             #
42              
43             our $VERSION = 97;
44              
45 40     40   39138 use constant supported_parameters => ();
  40         97  
  40         2117  
46 40     40   215 use constant default_severity => $Perl::Critic::Utils::SEVERITY_LOWEST;
  40         97  
  40         2025  
47 40     40   210 use constant default_themes => qw(pulp cosmetic);
  40         69  
  40         1889  
48 40     40   203 use constant applies_to => 'PPI::Document';
  40         91  
  40         4175  
49              
50             sub violates {
51 19     19 1 525540 my ($self, $elem, $document) = @_;
52             ### ProhibitAdjacentLinks on: $elem->content
53              
54 19         238 my $parser = Perl::Critic::Policy::Documentation::ProhibitAdjacentLinks::Parser->new
55             (policy => $self);
56 19         70 $parser->parse_from_elem ($elem);
57 19         72 return $parser->violations;
58             }
59              
60             #------------------------------------------------------------------------------
61             package Perl::Critic::Policy::Documentation::ProhibitAdjacentLinks::Parser;
62 40     40   291 use strict;
  40         88  
  40         899  
63 40     40   174 use warnings;
  40         72  
  40         1027  
64 40     40   604 use Pod::ParseLink;
  40         788  
  40         1566  
65 40     40   210 use base 'Perl::Critic::Pulp::PodParser';
  40         83  
  40         21151  
66              
67             my %command_non_text = (for => 1,
68             begin => 1,
69             end => 1,
70             cut => 1);
71             sub command {
72 23     23   2013 my $self = shift;
73 23         54 my ($command, $text, $linenum, $paraobj) = @_;
74 23         95 $self->SUPER::command(@_); # maintain 'in_begin'
75              
76 23 100       60 if ($command_non_text{$command}) {
77             # skip directives
78 4         63 return '';
79             }
80 19         50 $self->textblock ($text, $linenum, $paraobj);
81 19         200 return '';
82             }
83              
84             sub textblock {
85 40     40   1199 my ($self, $text, $linenum, $pod_para) = @_;
86             ### textblock
87             ### $text
88              
89             # process outside =begin, and inside =begin which is ":" markup
90 40 100 66     116 unless ($self->{'in_begin'} eq '' || $self->{'in_begin'} =~ /^:/) {
91 1         7 return '';
92             }
93              
94 39         3191 my $expand = $self->interpolate ($text, $linenum);
95             ### $expand
96 39         2148 my $ptree = $self->parse_text ($text, $linenum);
97 39         149 my @pending = reverse $ptree->children;
98 39         64 my $last_L = 0;
99 39         56 my $last_L_name = '';
100 39         48 my $last_L_display;
101 39         61 my $last_L_linenum = 0;
102              
103 39         88 while (@pending) {
104 74         110 my $obj = pop @pending;
105 74 100       141 if (! ref $obj) {
106             # plain text
107 36 100       130 if ($obj !~ /^\s*$/) {
108             # some text, not just whitespace
109 4         12 $last_L = 0;
110             }
111              
112             } else {
113             # a Pod::InteriorSequence
114 38         106 my $cmd = $obj->cmd_name;
115              
116 38 100 33     85 if ($cmd eq 'L') {
    50          
117 37         130 (undef, $linenum) = $obj->file_line;
118              
119             my $obj_text = join ('',
120 37 50       100 map {ref $_ ? $_->raw_text : $_}
  37         181  
121             $obj->parse_tree);
122 37         105 my ($display, $inferred, $name, $section, $type)
123             = Pod::ParseLink::parselink ($obj_text);
124             ### $obj_text
125             ### $display
126             ### $name
127 37 100       1318 if (! defined $name) { $name = ''; }
  2         3  
128              
129 37 100 100     140 if ($last_L
      100        
130             && ! ($name eq $last_L_name
131             && (defined $display || defined $last_L_display))) {
132 11         44 $self->violation_at_linenum_and_textpos
133             ("Adjacent L<> sequences, perhaps a comma or words should be in between",
134             $last_L_linenum, '', 0);
135             }
136 37         57 $last_L = 1;
137 37         178 $last_L_name = $name;
138 37         56 $last_L_display = $display;
139 37         101 $last_L_linenum = $linenum;
140              
141             } elsif ($cmd eq 'X' || $cmd eq 'Z') {
142             # ignore X<> index entries, maybe Z<> crunched already
143              
144             } else {
145             # descend into other like C<>
146 1 50       6 if (my $subtree = $obj->parse_tree) {
147 1         6 push @pending, reverse $subtree->children;
148             }
149             }
150             }
151             }
152 39 50       102 if ($text !~ /^\s.*$/) {
153 39         78 $self->{'last'} = '';
154             }
155             ### last now: $self->{'last'}
156 39         797 return;
157             }
158              
159             1;
160             __END__
161              
162             =for stopwords Ryde
163              
164             =head1 NAME
165              
166             Perl::Critic::Policy::Documentation::ProhibitAdjacentLinks - consecutive LE<lt>E<gt> links
167              
168             =head1 DESCRIPTION
169              
170             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
171             add-on. It asks you not to have two adjacent LE<lt>E<gt> sequences in a
172             paragraph. For example,
173              
174             =for ProhibitVerbatimMarkup allow next 2
175              
176             =head1 SEE ALSO
177              
178             L<Foo> # bad
179             L<Bar>
180              
181             The idea is adjacent LE<lt>E<gt> like this is probably a missing comma or
182             missing text. It's easy to make this mistake in a "SEE ALSO" list.
183              
184             This is normally only very minor and on that basis this policy is lowest
185             severity and under the "cosmetic" theme (see L<Perl::Critic/POLICY THEMES>).
186              
187             =head2 Exceptions
188              
189             An exception is made for two links to the same page where one (or both) have
190             display text,
191              
192             =for ProhibitVerbatimMarkup allow next
193              
194             See L<My::Package> L<new()|My::Package/Contructors> for more.
195              
196             This hyperlinks both the package name and a function etc within it. Perhaps
197             exactly when to allow or disallow this might be loosened or tightened in the
198             future. Adjacent linking is fairly unusual though, and too much linking is
199             often not a good thing since the meaning ought to be made clear in plain
200             text too.
201              
202             =head2 Disabling
203              
204             If you don't care about this sort of thing at all you can disable
205             C<ProhibitAdjacentLinks> from your F<.perlcriticrc> in the usual way
206             (see L<Perl::Critic/CONFIGURATION>),
207              
208             [-Documentation::ProhibitAdjacentLinks]
209              
210             =head1 BUGS
211              
212             The column position of the offending adjacency is not included in the
213             violation reported. You may need to look carefully at the line to see the
214             problem, and at the following line if the adjacent link is on the next line.
215              
216             =head1 SEE ALSO
217              
218             L<Perl::Critic::Pulp>,
219             L<Perl::Critic::Policy::Documentation::ProhibitDuplicateSeeAlso>,
220             L<Perl::Critic::Policy::Documentation::ProhibitLinkToSelf>,
221             L<Perl::Critic::Policy::Documentation::RequirePodLinksIncludeText>,
222             L<Perl::Critic::Policy::Documentation::RequireLinkedURLs>
223              
224             =head1 HOME PAGE
225              
226             http://user42.tuxfamily.org/perl-critic-pulp/index.html
227              
228             =head1 COPYRIGHT
229              
230             Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
231              
232             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
233             under the terms of the GNU General Public License as published by the Free
234             Software Foundation; either version 3, or (at your option) any later
235             version.
236              
237             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
238             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
239             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
240             more details.
241              
242             You should have received a copy of the GNU General Public License along with
243             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
244              
245             =cut