| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
87164
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
26
|
|
|
2
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
21
|
|
|
3
|
1
|
|
|
1
|
|
5
|
use utf8; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Pod::Weaver::Plugin::EnsureUniqueSections; |
|
6
|
|
|
|
|
|
|
$Pod::Weaver::Plugin::EnsureUniqueSections::VERSION = '0.163250'; |
|
7
|
1
|
|
|
1
|
|
43
|
use Moose; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
4
|
|
|
8
|
1
|
|
|
1
|
|
5704
|
use MooseX::Has::Sugar; |
|
|
1
|
|
|
|
|
592
|
|
|
|
1
|
|
|
|
|
4
|
|
|
9
|
1
|
|
|
1
|
|
93
|
use Moose::Autobox 0.10; |
|
|
1
|
|
|
|
|
20
|
|
|
|
1
|
|
|
|
|
6
|
|
|
10
|
1
|
|
|
1
|
|
820
|
use Text::Trim; |
|
|
1
|
|
|
|
|
435
|
|
|
|
1
|
|
|
|
|
52
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
237
|
use Lingua::EN::Inflect::Number qw(to_S); |
|
|
1
|
|
|
|
|
22827
|
|
|
|
1
|
|
|
|
|
5
|
|
|
13
|
1
|
|
|
1
|
|
162
|
use Carp; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
545
|
|
|
14
|
|
|
|
|
|
|
with 'Pod::Weaver::Role::Finalizer'; |
|
15
|
|
|
|
|
|
|
with 'Pod::Weaver::Role::Preparer'; |
|
16
|
|
|
|
|
|
|
# ABSTRACT: Ensure that POD has no duplicate section headers. |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
has strict => ( |
|
20
|
|
|
|
|
|
|
ro, lazy, |
|
21
|
|
|
|
|
|
|
isa => 'Bool', |
|
22
|
|
|
|
|
|
|
default => sub { 0 }, |
|
23
|
|
|
|
|
|
|
); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub _header_key { |
|
26
|
40
|
|
|
40
|
|
77
|
my ($self, $text) = @_; |
|
27
|
40
|
100
|
|
|
|
1375
|
if (!$self->strict) { |
|
28
|
|
|
|
|
|
|
# Replace all non-words with a single space |
|
29
|
36
|
|
|
|
|
101
|
$text =~ s{\W+}{ }xsmg; |
|
30
|
|
|
|
|
|
|
# Trim leading and trailing whitespace |
|
31
|
36
|
|
|
|
|
101
|
$text = trim $text; |
|
32
|
|
|
|
|
|
|
# All to uppercase |
|
33
|
36
|
|
|
|
|
614
|
$text = uc $text; |
|
34
|
|
|
|
|
|
|
# Reorder "AND" lists and singularize nouns |
|
35
|
|
|
|
|
|
|
$text = $text |
|
36
|
|
|
|
|
|
|
->split(qr{ AND }i) |
|
37
|
43
|
100
|
|
43
|
|
4739
|
->map(sub { m{\W} ? $_ : to_S $_; }) |
|
38
|
36
|
|
|
|
|
181
|
->sort->join(' AND '); |
|
39
|
|
|
|
|
|
|
} |
|
40
|
40
|
|
|
|
|
22462
|
return $text; |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub prepare_input { |
|
45
|
6
|
|
|
6
|
1
|
921337
|
my $self = shift; |
|
46
|
|
|
|
|
|
|
# Put EnsureUniqueSections plugins at the end |
|
47
|
6
|
|
|
|
|
160
|
my $plugins = $self->weaver->plugins; |
|
48
|
6
|
|
|
|
|
178
|
@$plugins = ((grep { $_ != $self } @$plugins), $self); |
|
|
90
|
|
|
|
|
167
|
|
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub finalize_document { |
|
53
|
6
|
|
|
6
|
1
|
105532
|
my ($self, $document) = @_; |
|
54
|
|
|
|
|
|
|
my $headers = $document->children |
|
55
|
40
|
50
|
|
40
|
|
1353
|
->grep(sub{ $_->can( 'command' ) and $_->command eq 'head1' }) |
|
56
|
6
|
|
|
40
|
|
136
|
->map(sub{ $_->content }); |
|
|
40
|
|
|
|
|
1102
|
|
|
57
|
6
|
|
|
|
|
58
|
my %header_group; |
|
58
|
6
|
|
|
|
|
17
|
for my $h (@$headers) { |
|
59
|
40
|
|
|
|
|
88
|
push @{$header_group{$self->_header_key($h)}}, $h; |
|
|
40
|
|
|
|
|
100
|
|
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $duplicate_headers = [ keys %header_group ] |
|
63
|
36
|
100
|
|
36
|
|
121
|
->map(sub{ @{$header_group{$_}} > 1 ? $header_group{$_}->head : () }) |
|
|
36
|
|
|
|
|
107
|
|
|
64
|
6
|
|
|
|
|
55
|
->sort; |
|
65
|
6
|
100
|
|
|
|
80
|
if (@$duplicate_headers > 0) { |
|
66
|
4
|
|
|
|
|
9
|
my $pod_string = ""; |
|
67
|
4
|
|
|
|
|
10
|
for my $h (@$duplicate_headers) { |
|
68
|
4
|
|
|
|
|
7
|
for my $node (@{ $document->children->grep( |
|
69
|
|
|
|
|
|
|
sub { |
|
70
|
30
|
50
|
33
|
30
|
|
1640
|
$_->can('command') && $_->command eq 'head1' && |
|
71
|
|
|
|
|
|
|
$_->content eq $h |
|
72
|
4
|
|
|
|
|
115
|
}) }) { |
|
73
|
7
|
|
|
|
|
1126
|
$pod_string .= $node->as_pod_string; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
} |
|
76
|
4
|
|
|
|
|
1099
|
$self->log_debug(["POD of duplicated headers:\n\n%s", $pod_string]); |
|
77
|
4
|
|
|
|
|
361
|
my $message = "Error: The following headers appear multiple times: '" . $duplicate_headers->join(q{', '}) . q{'}; |
|
78
|
4
|
|
|
|
|
45
|
$self->log_fatal($message); |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
__END__ |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=pod |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=head1 NAME |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Pod::Weaver::Plugin::EnsureUniqueSections - Ensure that POD has no duplicate section headers. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head1 VERSION |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
version 0.163250 |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
In F<weaver.ini> |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
[-EnsureUniqueSections] |
|
101
|
|
|
|
|
|
|
strict = 0 ; The default |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
This plugin simply ensures that the POD after weaving has no duplicate |
|
106
|
|
|
|
|
|
|
top-level section headers. This can help you if you are converting |
|
107
|
|
|
|
|
|
|
from writing all your own POD to generating it with L<Pod::Weaver>. If |
|
108
|
|
|
|
|
|
|
you begin generating a section with L<Pod::Weaver> but you forget to |
|
109
|
|
|
|
|
|
|
delete the manually written section of the same name, this plugin will |
|
110
|
|
|
|
|
|
|
warn you. |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
By default, this module does some tricks to detect similar headers, |
|
113
|
|
|
|
|
|
|
such as C<AUTHOR> and C<AUTHORS>. You can turn this off by setting |
|
114
|
|
|
|
|
|
|
C<strict = 1> in F<weaver.ini>, in which case only I<exactly identical> |
|
115
|
|
|
|
|
|
|
headers will be considered duplicates of each other. |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 DIAGNOSTIC MESSAGES |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
If any similar (or identical if C<strict> is 1) section headers are |
|
120
|
|
|
|
|
|
|
found, all of their names will be listed on STDERR. Generally, you |
|
121
|
|
|
|
|
|
|
should take this list of modules and remove each from your POD. Then |
|
122
|
|
|
|
|
|
|
you should ensure that the sections generated by L<Pod::Weaver> are |
|
123
|
|
|
|
|
|
|
suitable substitutes for those sections. In the case of similar names, |
|
124
|
|
|
|
|
|
|
only the first instance in each set of similar names will be listed. |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head2 strict |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
If set to true (1), section headers will only be considered duplicates |
|
131
|
|
|
|
|
|
|
if they match exactly. If false (the default), certain similar section |
|
132
|
|
|
|
|
|
|
headers will be considered equivalent. The following similarities are |
|
133
|
|
|
|
|
|
|
considered (more may be added later): |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=over 4 |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item All whitespace and punctuation are equivalant |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
For example, the following would all be considered duplicates of each |
|
140
|
|
|
|
|
|
|
other: C< SEE ALSO>, C<SEE ALSO>, C<SEE,ALSO:>. |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item Case-insensitive |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
For example, C<Name> and C<NAME> would be considered duplicates. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=item Sets of words separated by "AND". |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
For example, "COPYRIGHT AND LICENSE" would be considered a duplicate |
|
149
|
|
|
|
|
|
|
of "LICENSE AND COPYRIGHT". |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item Plurals |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
A plural noun is considered equivalent to its singular. For example, |
|
154
|
|
|
|
|
|
|
"AUTHOR" and "AUTHORS" are the same section. A section header |
|
155
|
|
|
|
|
|
|
consisting of multiple words, such as "DISCLAIMER OF WARRANTY", is not |
|
156
|
|
|
|
|
|
|
affected by this rule. |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
This rule uses L<Lingua::EN::Inflect::Number> to interconvert between |
|
159
|
|
|
|
|
|
|
singular and plural forms. Hopefully you don't need to make a section |
|
160
|
|
|
|
|
|
|
called C<OCTOPI>. |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=back |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Note that these rules apply recursively, so C<Authors; and |
|
165
|
|
|
|
|
|
|
Contributors> would be a duplicate of C< CONTRIBUTORS AND AUTHOR>. |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 METHODS |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head2 prepare_input |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
This method modifies the weaver object by moving EnsureUniqueSections |
|
172
|
|
|
|
|
|
|
to the end of the weaver's plugin list to ensure that it gets to look |
|
173
|
|
|
|
|
|
|
at the final woven POD. |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
THIS IS PURE EVIL. This is a hack to ensure that this plugin gets "the |
|
176
|
|
|
|
|
|
|
last word". Obviously if all plugins used this it would be total |
|
177
|
|
|
|
|
|
|
chaos. I welcome alternative suggestions. The main issue is that when |
|
178
|
|
|
|
|
|
|
other Finalizers, such as Section::Leftovers (which happens to be the |
|
179
|
|
|
|
|
|
|
most likely plugin to create duplicate sections), produce sections, |
|
180
|
|
|
|
|
|
|
this plugin will only see those sections if it runs after those |
|
181
|
|
|
|
|
|
|
Finalizers. Hence the need to be the last plugin on the list. |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head2 finalize_document |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
This method checks the document for duplicate headers, and throws an |
|
186
|
|
|
|
|
|
|
error if any are found. If no duplicates are found, it simply does |
|
187
|
|
|
|
|
|
|
nothing. It does not modify the POD in any way. |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 Should also be available as a L<Dist::Zilla> testing plugin |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
I would like to convert this to a L<Dist::Zilla> testing plugin, so that |
|
194
|
|
|
|
|
|
|
you can use it without enabling L<Pod::Weaver> if you don't want to, |
|
195
|
|
|
|
|
|
|
but I haven't yet figured out how to find all files in a dist with POD |
|
196
|
|
|
|
|
|
|
and extract all their headers. If anyone knows, please tell me. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head2 No recursive duplicate checks |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
This module only checks for duplicates in top-level headers (i.e. |
|
201
|
|
|
|
|
|
|
C<head1>). It could be extended to check the C<head2> elements within |
|
202
|
|
|
|
|
|
|
each C<head1> section and so on, but generally L<Pod::Weaver> is not |
|
203
|
|
|
|
|
|
|
called upon to generate subsections, so you are unlikely to end up |
|
204
|
|
|
|
|
|
|
with duplicates at any level other than the first. However, if there |
|
205
|
|
|
|
|
|
|
is demand for recursive duplicate detection, I will add it. |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
|
208
|
|
|
|
|
|
|
C<rct+perlbug@thompsonclan.org>. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=over 4 |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
L<Pod::Weaver> - The module that this is a plugin for. |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=item * |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
L<Lingua::EN::Inflect::Number> - Used to determine the singular forms of plural nouns. |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=back |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 INSTALLATION |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
See perlmodinstall for information and options on installing Perl modules. |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head1 AUTHOR |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Ryan C. Thompson <rct@thompsonclan.org> |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
This software is copyright (c) 2010 by Ryan C. Thompson. |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
237
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTY |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
|
242
|
|
|
|
|
|
|
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT |
|
243
|
|
|
|
|
|
|
WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER |
|
244
|
|
|
|
|
|
|
PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, |
|
245
|
|
|
|
|
|
|
EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE |
|
246
|
|
|
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
247
|
|
|
|
|
|
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE |
|
248
|
|
|
|
|
|
|
SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME |
|
249
|
|
|
|
|
|
|
THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|
252
|
|
|
|
|
|
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
|
253
|
|
|
|
|
|
|
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE |
|
254
|
|
|
|
|
|
|
TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR |
|
255
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE |
|
256
|
|
|
|
|
|
|
SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
|
257
|
|
|
|
|
|
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
|
258
|
|
|
|
|
|
|
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
|
259
|
|
|
|
|
|
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
|
260
|
|
|
|
|
|
|
DAMAGES. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=cut |