File Coverage

lib/Perl/Critic/Policy/CodeLayout/ProhibitLongLines.pm
Criterion Covered Total %
statement 94 94 100.0
branch 23 26 88.4
condition 13 18 72.2
subroutine 19 19 100.0
pod 5 5 100.0
total 154 162 95.0


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::CodeLayout::ProhibitLongLines v0.2.4;
2              
3 5     5   1029414 use v5.26.0;
  5         19  
4 5     5   28 use strict;
  5         21  
  5         136  
5 5     5   18 use warnings;
  5         6  
  5         229  
6 5     5   19 use feature "signatures";
  5         7  
  5         569  
7 5     5   768 use experimental "signatures";
  5         1816  
  5         26  
8              
9 5     5   1403 use parent qw( Perl::Critic::Policy );
  5         687  
  5         48  
10              
11 5     5   744472 use File::Basename qw( dirname );
  5         16  
  5         237  
12 5     5   30 use List::Util qw( any );
  5         13  
  5         291  
13 5     5   24 use Perl::Critic::Utils qw( $SEVERITY_MEDIUM );
  5         8  
  5         401  
14 5     5   2284 use Perl::Critic::Utils::SourceLocation ();
  5         12  
  5         4896  
15              
16             my $Desc = "Line exceeds maximum length";
17             my $Expl = "Keep lines under the configured maximum for readability";
18              
19             sub supported_parameters { (
20             {
21 18     18 1 1398419 name => "max_line_length",
22             description => "Maximum allowed line length in characters",
23             default_string => "80",
24             behavior => "integer",
25             integer_minimum => 1,
26             }, {
27             name => "allow_lines_matching",
28             description => "Regex patterns for lines exempt from length check",
29             default_string => "",
30             behavior => "string list",
31             }, {
32             name => "gitattributes_line_length",
33             description => "Git attribute name for per-file line length override",
34             default_string => "custom-line-length",
35             behavior => "string",
36             },
37             ) }
38              
39 28     28 1 294 sub default_severity { $SEVERITY_MEDIUM }
40 1     1 1 9171 sub default_themes { qw( cosmetic formatting ) }
41              
42 37     37 1 108959 sub applies_to { "PPI::Document" }
43              
44 42     42 1 24688 sub violates ($self, $elem, $doc) {
  42         63  
  42         50  
  42         57  
  42         49  
45 42         132 my $override = $self->_get_gitattr_line_length($doc->filename);
46 42 100 100     174 return if defined $override && $override eq "ignore";
47              
48 41   66     168 my $max_length = $override // $self->{_max_line_length};
49 41         103 my @patterns = keys $self->{_allow_lines_matching}->%*;
50 41         213 my $source = $doc->serialize;
51 41         5167 my @lines = split /\n/, $source;
52              
53 41         59 my @violations;
54              
55 41         93 for my $line_num (0 .. $#lines) {
56 96         714 my $length = length $lines[$line_num];
57 96 100       176 if ($length > $max_length) {
58 32 100   9   221 next if any { $lines[$line_num] =~ /$_/ } @patterns;
  9         159  
59 28         112 my $violation_desc
60             = "Line is $length characters long (exceeds $max_length)";
61              
62             # Find a token on this line for accurate line number reporting
63 28         75 my $line_token = $self->_find_token_on_line($doc, $line_num + 1);
64              
65             # If no token found, create synthetic element with correct line number
66 28 100       81 if (!$line_token) {
67 6         14 $line_token = Perl::Critic::Utils::SourceLocation->new(
68             line_number => $line_num + 1,
69             column_number => 1,
70             content => $lines[$line_num],
71             filename => $doc->filename
72             );
73             }
74              
75 28         125 push @violations, $self->violation($violation_desc, $Expl, $line_token);
76             }
77             }
78              
79             @violations
80 41         2979 }
81              
82 47     47   2885 sub _get_gitattr_line_length ($self, $filename) {
  47         47  
  47         58  
  47         49  
83 47 100 100     180 return unless defined $filename && length $filename;
84 8         17 my $attr = $self->{_gitattributes_line_length};
85 8 100 66     40 return unless defined $attr && length $attr;
86              
87 7         12 my $output = eval {
88 7         535 my $dir = dirname($filename);
89 7 50       26634 open my $fh, "-|", "git", "-C", $dir, "check-attr", $attr, "--", $filename
90             or return;
91 7         68 my $result = do { local $/ = undef; <$fh> };
  7         166  
  7         16674  
92 7 50       291 close $fh or return;
93 7         287 $result
94             };
95 7 50 33     304 return unless defined $output && $output =~ /: \Q$attr\E: (.+)$/m;
96              
97 7         64 my $value = $1;
98 7 100       145 return "ignore" if $value eq "ignore";
99 5 100       150 return $value if $value =~ /^\d+$/;
100             return
101 2         91 }
102              
103 28     28   38 sub _find_token_on_line ($self, $doc, $target_line) {
  28         34  
  28         32  
  28         29  
  28         27  
104 28         50 my $found_token;
105              
106 442         422 $doc->find(
107 442     442   3041 sub ($top, $elem) {
  442         367  
  442         368  
108 442 100       801 return 0 unless $elem->isa("PPI::Token");
109              
110 398         681 my $line = $elem->line_number;
111 398 100 66     12353 if (defined $line && $line == $target_line) {
112 164         154 $found_token = $elem;
113 164         228 return 1;
114             }
115 234         265 return 0;
116             }
117 28         173 );
118              
119 28         332 $found_token
120             }
121              
122             "
123             I know you have a little life in you yet
124             I know you have a lot of strength left
125             "
126              
127             __END__
128              
129             =pod
130              
131             =head1 NAME
132              
133             Perl::Critic::Policy::CodeLayout::ProhibitLongLines - Prohibit long lines
134              
135             =head1 VERSION
136              
137             version v0.2.4
138              
139             =head1 SYNOPSIS
140              
141             [CodeLayout::ProhibitLongLines]
142             max_line_length = 72
143              
144             # Bad - line exceeds configured maximum
145             my $very_long_variable_name = "long string that exceeds maximum length";
146              
147             # Good - line within limit
148             my $very_long_variable_name =
149             "long string that exceeds maximum length";
150              
151             =head1 DESCRIPTION
152              
153             This policy flags lines that exceed a configurable maximum length. Long lines
154             can be difficult to read, especially in narrow terminal windows or when
155             viewing code side-by-side with diffs or other files.
156              
157             The default maximum line length is 80 characters, which provides good
158             readability across various display contexts while still allowing reasonable
159             code density.
160              
161             You can configure C<perltidy> to keep lines within the specified limit. Only
162             when it is unable to do that will you need to manually make changes.
163              
164             =head1 CONFIGURATION
165              
166             =head2 max_line_length
167              
168             The maximum allowed line length in characters. Defaults to 80.
169              
170             [CodeLayout::ProhibitLongLines]
171             max_line_length = 72
172              
173             =head2 allow_lines_matching
174              
175             A space-separated list of regex patterns. Lines matching any pattern are
176             exempt from the length check. This is useful for lines that cannot
177             reasonably be shortened, such as long package declarations or URLs.
178              
179             [CodeLayout::ProhibitLongLines]
180             allow_lines_matching = ^\s*package\s+
181              
182             Multiple patterns (space-separated):
183              
184             [CodeLayout::ProhibitLongLines]
185             allow_lines_matching = ^\s*package\s+ https?://
186              
187             =head2 gitattributes_line_length
188              
189             The name of a git attribute to look up for per-file line length overrides.
190             Defaults to C<custom-line-length>. Set to an empty string to disable.
191              
192             The attribute value may be an integer (overriding C<max_line_length> for
193             that file) or the literal string C<ignore> (suppressing all violations for
194             that file).
195              
196             Configure in C<.gitattributes>:
197              
198             t/legacy/messy.t custom-line-length=ignore
199             t/generated/*.t custom-line-length=200
200              
201             Then in C<.perlcriticrc> (the default attribute name is shown; you only
202             need this line if you want a different name):
203              
204             [CodeLayout::ProhibitLongLines]
205             gitattributes_line_length = custom-line-length
206              
207             Requires C<git> on C<$PATH>. Falls back to the configured
208             C<max_line_length> when git is unavailable, the file is outside a
209             repository, or the attribute is unspecified.
210              
211             =head1 EXAMPLES
212              
213             =head2 Long Variable Assignments
214              
215             # Bad - exceeds 72 characters
216             my $configuration_manager = VeryLongModuleName::ConfigurationManager->new;
217              
218             # Good - broken into multiple lines
219             my $configuration_manager =
220             VeryLongModuleName::ConfigurationManager->new;
221              
222             =head2 Long Method Calls
223              
224             # Bad - exceeds 72 characters
225             $object->some_very_very_long_method_name($param1, $param2, $param3, $param4);
226              
227             # Good - parameters on separate lines
228             $object->some_very_very_long_method_name(
229             $param1, $param2, $param3, $param4
230             );
231              
232             =head2 Long String Literals
233              
234             # Bad - exceeds 72 characters
235             my $error_message =
236             "This is a very long error message that exceeds the configured maximum";
237              
238             # Good - use concatenation or heredoc
239             my $error_message = "This is a very long error message that " .
240             "exceeds the configured maximum";
241              
242             =head1 METHODS
243              
244             =head2 supported_parameters
245              
246             This method returns the parameters supported by this policy.
247              
248             =head1 AFFILIATION
249              
250             This Policy is part of the Perl::Critic::PJCJ distribution.
251              
252             =head1 AUTHOR
253              
254             Paul Johnson C<< <paul@pjcj.net> >>
255              
256             =head1 COPYRIGHT
257              
258             Copyright 2025 Paul Johnson.
259              
260             =head1 LICENCE
261              
262             This program is free software; you can redistribute it and/or modify
263             it under the same terms as Perl itself.
264              
265             =cut