File Coverage

blib/lib/Perl/Tidy/FileWriter.pm
Criterion Covered Total %
statement 148 216 68.5
branch 31 62 50.0
condition 15 24 62.5
subroutine 24 30 80.0
pod 0 19 0.0
total 218 351 62.1


line stmt bran cond sub pod time code
1             #####################################################################
2             #
3             # The Perl::Tidy::FileWriter class writes the output file created
4             # by the formatter. It receives each output line and performs some
5             # important monitoring services. These include:
6             #
7             # - Verifying that lines do not go out with tokens in the wrong order
8             # - Checking for obvious iteration convergence when all output tokens
9             # match all input tokens
10             # - Keeping track of consecutive blank and non-blank lines
11             # - Looking for line lengths which exceed the maximum requested length
12             # - Reporting results to the log file
13             #
14             #####################################################################
15              
16             package Perl::Tidy::FileWriter;
17 44     44   266 use strict;
  44         75  
  44         1395  
18 44     44   160 use warnings;
  44         63  
  44         2304  
19             our $VERSION = '20260204';
20 44     44   212 use Carp;
  44         88  
  44         2672  
21              
22 44     44   194 use constant DEVEL_MODE => 0;
  44         62  
  44         2428  
23 44     44   236 use constant EMPTY_STRING => q{};
  44         69  
  44         1907  
24              
25             # Maximum number of little messages; probably need not be changed.
26 44     44   185 use constant MAX_NAG_MESSAGES => 6;
  44         69  
  44         11890  
27              
28             # List of hash keys to prevent -duk from listing them.
29             my @unique_hash_keys_uu = qw( indent-columns );
30              
31             sub AUTOLOAD {
32              
33             # Catch any undefined sub calls so that we are sure to get
34             # some diagnostic information. This sub should never be called
35             # except for a programming error.
36 0     0   0 our $AUTOLOAD;
37 0 0       0 return if ( $AUTOLOAD =~ /\bDESTROY$/ );
38 0         0 my ( $pkg, $fname, $lno ) = caller();
39 0         0 my $my_package = __PACKAGE__;
40 0         0 print {*STDERR} <<EOM;
  0         0  
41             ======================================================================
42             Error detected in package '$my_package', version $VERSION
43             Received unexpected AUTOLOAD call for sub '$AUTOLOAD'
44             Called from package: '$pkg'
45             Called from File '$fname' at line '$lno'
46             This error is probably due to a recent programming change
47             ======================================================================
48             EOM
49 0         0 exit 1;
50             } ## end sub AUTOLOAD
51              
52       0     sub DESTROY {
53              
54             # required to avoid call to AUTOLOAD in some versions of perl
55             }
56              
57 0         0 BEGIN {
58              
59             # Array index names for variables.
60             # Do not combine with other BEGIN blocks (c101).
61 44     44   58430 my $i = 0;
62             use constant {
63 44         14644 _logger_object_ => $i++,
64             _rOpts_ => $i++,
65             _output_line_number_ => $i++,
66             _consecutive_blank_lines_ => $i++,
67             _consecutive_nonblank_lines_ => $i++,
68             _consecutive_new_blank_lines_ => $i++,
69             _first_line_length_error_ => $i++,
70             _max_line_length_error_ => $i++,
71             _last_line_length_error_ => $i++,
72             _first_line_length_error_at_ => $i++,
73             _max_line_length_error_at_ => $i++,
74             _last_line_length_error_at_ => $i++,
75             _line_length_error_count_ => $i++,
76             _max_output_line_length_ => $i++,
77             _max_output_line_length_at_ => $i++,
78             _rK_checklist_ => $i++,
79             _K_arrival_order_matches_ => $i++,
80             _K_sequence_error_msg_ => $i++,
81             _K_last_arrival_ => $i++,
82             _save_logfile_ => $i++,
83             _routput_string_ => $i++,
84 44     44   283 };
  44         69  
85             } ## end BEGIN
86              
87             sub Die {
88 0     0 0 0 my ($msg) = @_;
89 0         0 Perl::Tidy::Die($msg);
90 0         0 croak "unexpected return from Perl::Tidy::Die";
91             }
92              
93             sub Fault {
94 0     0 0 0 my ($msg) = @_;
95              
96             # This routine is called for errors that really should not occur
97             # except if there has been a bug introduced by a recent program change.
98             # Please add comments at calls to Fault to explain why the call
99             # should not occur, and where to look to fix it.
100 0         0 my ( $package0_uu, $filename0_uu, $line0, $subroutine0_uu ) = caller(0);
101 0         0 my ( $package1_uu, $filename1, $line1, $subroutine1 ) = caller(1);
102 0         0 my ( $package2_uu, $filename2_uu, $line2_uu, $subroutine2 ) = caller(2);
103 0         0 my $pkg = __PACKAGE__;
104 0         0 my $input_stream_name = Perl::Tidy::get_input_stream_name();
105              
106 0         0 Die(<<EOM);
107             ==============================================================================
108             While operating on input stream with name: '$input_stream_name'
109             A fault was detected at line $line0 of sub '$subroutine1'
110             in file '$filename1'
111             which was called from line $line1 of sub '$subroutine2'
112             Message: '$msg'
113             This is probably an error introduced by a recent programming change.
114             $pkg reports VERSION='$VERSION'.
115             ==============================================================================
116             EOM
117              
118 0         0 croak "unexpected return from sub Die";
119             } ## end sub Fault
120              
121             sub warning {
122 0     0 0 0 my ( $self, $msg ) = @_;
123              
124             # log a warning message from any caller
125 0         0 my $logger_object = $self->[_logger_object_];
126 0 0       0 if ($logger_object) { $logger_object->warning($msg); }
  0         0  
127 0         0 return;
128             } ## end sub warning
129              
130             sub write_logfile_entry {
131 1296     1296 0 2206 my ( $self, $msg ) = @_;
132 1296         1974 my $logger_object = $self->[_logger_object_];
133 1296 100       2430 if ($logger_object) {
134 1292         2542 $logger_object->write_logfile_entry($msg);
135             }
136 1296         1962 return;
137             } ## end sub write_logfile_entry
138              
139             sub new {
140 648     648 0 1800 my ( $class, $line_sink_object, $rOpts, $logger_object ) = @_;
141              
142 648         1183 my $self = [];
143 648         1320 bless $self, $class;
144 648         1662 $self->[_logger_object_] = $logger_object;
145 648         1245 $self->[_rOpts_] = $rOpts;
146 648         1151 $self->[_output_line_number_] = 1;
147 648         1175 $self->[_consecutive_blank_lines_] = 0;
148 648         1430 $self->[_consecutive_nonblank_lines_] = 0;
149 648         1381 $self->[_consecutive_new_blank_lines_] = 0;
150 648         1260 $self->[_first_line_length_error_] = 0;
151 648         1174 $self->[_max_line_length_error_] = 0;
152 648         1228 $self->[_last_line_length_error_] = 0;
153 648         1101 $self->[_first_line_length_error_at_] = 0;
154 648         1251 $self->[_max_line_length_error_at_] = 0;
155 648         1021 $self->[_last_line_length_error_at_] = 0;
156 648         1082 $self->[_line_length_error_count_] = 0;
157 648         1035 $self->[_max_output_line_length_] = 0;
158 648         1159 $self->[_max_output_line_length_at_] = 0;
159 648         1386 $self->[_rK_checklist_] = [];
160 648         1192 $self->[_K_arrival_order_matches_] = 0;
161 648         1558 $self->[_K_sequence_error_msg_] = EMPTY_STRING;
162 648         1469 $self->[_K_last_arrival_] = -1;
163 648   100     3781 $self->[_save_logfile_] =
164             defined($logger_object) && $logger_object->get_save_logfile();
165              
166             # '$line_sink_object' is a SCALAR ref which receives the lines.
167 648         1556 my $ref = ref($line_sink_object);
168 648 50       2207 if ( !$ref ) {
    50          
169 0         0 Fault("FileWriter expects line_sink_object to be a ref\n");
170             }
171             elsif ( $ref eq 'SCALAR' ) {
172 648         1171 $self->[_routput_string_] = $line_sink_object;
173             }
174             else {
175 0         0 my $str = $ref;
176 0 0       0 if ( length($str) > 63 ) { $str = substr( $str, 0, 60 ) . '...' }
  0         0  
177 0         0 Fault(<<EOM);
178             FileWriter expects 'line_sink_object' to be ref to SCALAR but it is ref to:
179             $str
180             EOM
181             }
182              
183 648         1526 return $self;
184             } ## end sub new
185              
186             sub setup_convergence_test {
187 645     645 0 1560 my ( $self, $rlist ) = @_;
188              
189             # Setup the convergence test,
190              
191             # Given:
192             # $rlist = a reference to a list of line-ending token indexes 'K' of
193             # the input stream. We will compare these with the line-ending token
194             # indexes of the output stream. If they are identical, then we have
195             # convergence.
196 645 100       975 if ( @{$rlist} ) {
  645         1752  
197              
198             # We are going to destroy the list, so make a copy and put in
199             # reverse order so we can pop values as they arrive
200 634         1059 my @list = @{$rlist};
  634         2038  
201 634 100       1687 if ( $list[0] < $list[-1] ) {
202 557         1157 @list = reverse(@list);
203             }
204 634         1568 $self->[_rK_checklist_] = \@list;
205             }
206              
207             # We will zero this flag on any error in arrival order:
208 645         1402 $self->[_K_arrival_order_matches_] = 1;
209 645         1875 $self->[_K_sequence_error_msg_] = EMPTY_STRING;
210 645         1156 $self->[_K_last_arrival_] = -1;
211 645         1431 return;
212             } ## end sub setup_convergence_test
213              
214             sub get_convergence_check {
215 648     648 0 1301 my ($self) = @_;
216              
217             # converged if:
218             # - all expected indexes arrived
219             # - and in correct order
220 648   66     921 return !@{ $self->[_rK_checklist_] }
221             && $self->[_K_arrival_order_matches_];
222              
223             } ## end sub get_convergence_check
224              
225             sub get_output_line_number {
226 562     562 0 796 my $self = shift;
227 562         1227 return $self->[_output_line_number_];
228             }
229              
230             sub decrement_output_line_number {
231 648     648 0 1067 my $self = shift;
232 648         3707 $self->[_output_line_number_]--;
233 648         1177 return;
234             }
235              
236             sub get_consecutive_nonblank_lines {
237 1     1 0 1 my $self = shift;
238 1         7 return $self->[_consecutive_nonblank_lines_];
239             }
240              
241             sub get_consecutive_blank_lines {
242 0     0 0 0 my $self = shift;
243 0         0 return $self->[_consecutive_blank_lines_];
244             }
245              
246             sub reset_consecutive_blank_lines {
247 175     175 0 243 my $self = shift;
248 175         254 $self->[_consecutive_blank_lines_] = 0;
249 175         250 return;
250             }
251              
252             sub want_blank_line {
253 22     22 0 44 my $self = shift;
254 22 100       72 if ( !$self->[_consecutive_blank_lines_] ) {
255 13         2422 $self->write_blank_code_line();
256             }
257 22         39 return;
258             } ## end sub want_blank_line
259              
260             sub require_blank_code_lines {
261 51     51 0 119 my ( $self, $count ) = @_;
262              
263             # Given:
264             # $count = number of blank lines to write
265             # Write out $count blank lines regardless of the value of -mbl
266             # unless -mbl=0. This allows extra blank lines to be written for subs and
267             # packages even with the default -mbl=1
268 51         102 my $need = $count - $self->[_consecutive_blank_lines_];
269 51         82 my $rOpts = $self->[_rOpts_];
270 51         100 my $forced = $rOpts->{'maximum-consecutive-blank-lines'} > 0;
271 51         134 foreach ( 0 .. $need - 1 ) {
272 32         94 $self->write_blank_code_line($forced);
273             }
274 51         112 return;
275             } ## end sub require_blank_code_lines
276              
277             sub write_blank_code_line {
278 1100     1100 0 2342 my ( $self, ($forced) ) = @_;
279              
280             # Write a blank line of code, given:
281             # $forced = optional flag which, if set, forces the blank line
282             # to be written. This allows the -mbl flag to be temporarily
283             # exceeded.
284              
285 1100         3449 my $rOpts = $self->[_rOpts_];
286             return
287             if (!$forced
288             && $self->[_consecutive_blank_lines_] >=
289 1100 100 100     5107 $rOpts->{'maximum-consecutive-blank-lines'} );
290              
291 986         1658 $self->[_consecutive_nonblank_lines_] = 0;
292              
293             # Balance old blanks against new (forced) blanks instead of writing them.
294             # This fixes case b1073.
295 986 50 66     3536 if ( !$forced && $self->[_consecutive_new_blank_lines_] > 0 ) {
296 0         0 $self->[_consecutive_new_blank_lines_]--;
297 0         0 return;
298             }
299              
300 986         1270 ${ $self->[_routput_string_] } .= "\n";
  986         2162  
301              
302 986         1486 $self->[_output_line_number_]++;
303 986         1424 $self->[_consecutive_blank_lines_]++;
304 986 100       1888 $self->[_consecutive_new_blank_lines_]++ if ($forced);
305              
306 986         1818 return;
307             } ## end sub write_blank_code_line
308              
309 44     44   5156 use constant MAX_PRINTED_CHARS => 80;
  44         1840  
  44         52594  
310              
311             sub write_code_line {
312 8401     8401 0 14206 my ( $self, $str, $K ) = @_;
313              
314             # Write a line of code, given
315             # $str = the line of code
316             # $K = an optional check integer which, if given, must
317             # increase monotonically. This was added to catch cache
318             # sequence errors in the vertical aligner.
319              
320 8401         11618 $self->[_consecutive_blank_lines_] = 0;
321 8401         10888 $self->[_consecutive_new_blank_lines_] = 0;
322 8401         10332 $self->[_consecutive_nonblank_lines_]++;
323 8401         9961 $self->[_output_line_number_]++;
324              
325 8401         9173 ${ $self->[_routput_string_] } .= $str;
  8401         20682  
326              
327 8401 100       15673 if ( $self->[_save_logfile_] ) { $self->check_line_lengths($str) }
  2         6  
328              
329             #----------------------------
330             # Convergence and error check
331             #----------------------------
332 8401 100       13794 if ( defined($K) ) {
333              
334             # Convergence check: we are checking if all defined K values arrive in
335             # the order which was defined by the caller. Quit checking if any
336             # unexpected K value arrives.
337 7513 100       12336 if ( $self->[_K_arrival_order_matches_] ) {
338 3956         4424 my $Kt = pop @{ $self->[_rK_checklist_] };
  3956         6834  
339 3956 100 66     11960 if ( !defined($Kt) || $Kt != $K ) {
340 292         648 $self->[_K_arrival_order_matches_] = 0;
341             }
342             }
343              
344             # Check for out-of-order arrivals of index K. The K values are the
345             # token indexes of the last token of code lines, and they should come
346             # out in increasing order. Otherwise something is seriously wrong.
347             # Most likely a recent programming change to VerticalAligner.pm has
348             # caused lines to go out in the wrong order. This could happen if
349             # either the cache or buffer that it uses are emptied in the wrong
350             # order.
351 7513 50 33     15121 if ( $K < $self->[_K_last_arrival_]
352             && !$self->[_K_sequence_error_msg_] )
353             {
354 0         0 my $K_prev = $self->[_K_last_arrival_];
355              
356 0         0 chomp $str;
357 0 0       0 if ( length($str) > MAX_PRINTED_CHARS ) {
358 0         0 $str = substr( $str, 0, MAX_PRINTED_CHARS ) . "...";
359             }
360              
361 0         0 my $msg = <<EOM;
362             Lines have arrived out of order in sub 'write_code_line'
363             as detected by token index K=$K arriving after index K=$K_prev in the following line:
364             $str
365             This is probably due to a recent programming change and needs to be fixed.
366             EOM
367              
368             # Always die during development, this needs to be fixed
369 0         0 if (DEVEL_MODE) { Fault($msg) }
370              
371             # Otherwise warn if string is not empty (added for b1378)
372 0 0       0 $self->warning($msg) if ( length($str) );
373              
374             # Only issue this warning once
375 0         0 $self->[_K_sequence_error_msg_] = $msg;
376              
377             }
378 7513         9692 $self->[_K_last_arrival_] = $K;
379             }
380 8401         15048 return;
381             } ## end sub write_code_line
382              
383             sub write_line {
384 335     335 0 524 my ( $self, $str ) = @_;
385              
386             # Write a line directly to the output, without any counting of blank or
387             # non-blank lines.
388              
389             # Given:
390             # $str = line of text to write
391              
392 335         381 ${ $self->[_routput_string_] } .= $str;
  335         812  
393              
394 335 50       717 if ( chomp $str ) { $self->[_output_line_number_]++; }
  335         442  
395 335 50       667 if ( $self->[_save_logfile_] ) { $self->check_line_lengths($str) }
  0         0  
396              
397 335         475 return;
398             } ## end sub write_line
399              
400             sub check_line_lengths {
401 2     2 0 4 my ( $self, $str ) = @_;
402              
403             # Collect info on line lengths for logfile
404             # Given:
405             # $str = line of text being written
406              
407             # This calculation of excess line length ignores any internal tabs
408 2         6 my $rOpts = $self->[_rOpts_];
409 2         3 chomp $str;
410 2         3 my $len_str = length($str);
411 2         3 my $exceed = $len_str - $rOpts->{'maximum-line-length'};
412 2 50 33     8 if ( $str && substr( $str, 0, 1 ) eq "\t" && $str =~ /^\t+/g ) {
      33        
413 0         0 $exceed += pos($str) * $rOpts->{'indent-columns'};
414             }
415              
416             # Note that we just incremented output line number to future value
417             # so we must subtract 1 for current line number
418 2 100       4 if ( $len_str > $self->[_max_output_line_length_] ) {
419 1         2 $self->[_max_output_line_length_] = $len_str;
420 1         2 $self->[_max_output_line_length_at_] =
421             $self->[_output_line_number_] - 1;
422             }
423              
424 2 50       3 if ( $exceed > 0 ) {
425 0         0 my $output_line_number = $self->[_output_line_number_];
426 0         0 $self->[_last_line_length_error_] = $exceed;
427 0         0 $self->[_last_line_length_error_at_] = $output_line_number - 1;
428 0 0       0 if ( $self->[_line_length_error_count_] == 0 ) {
429 0         0 $self->[_first_line_length_error_] = $exceed;
430 0         0 $self->[_first_line_length_error_at_] = $output_line_number - 1;
431             }
432              
433 0 0       0 if ( $self->[_last_line_length_error_] >
434             $self->[_max_line_length_error_] )
435             {
436 0         0 $self->[_max_line_length_error_] = $exceed;
437 0         0 $self->[_max_line_length_error_at_] = $output_line_number - 1;
438             }
439              
440 0 0       0 if ( $self->[_line_length_error_count_] < MAX_NAG_MESSAGES ) {
441 0         0 $self->write_logfile_entry(
442             "Line length exceeded by $exceed characters\n");
443             }
444 0         0 $self->[_line_length_error_count_]++;
445             }
446 2         4 return;
447             } ## end sub check_line_lengths
448              
449             sub report_line_length_errors {
450 648     648 0 1035 my $self = shift;
451              
452             # Write summary info about line lengths to the log file
453              
454 648         1228 my $rOpts = $self->[_rOpts_];
455 648         1340 my $line_length_error_count = $self->[_line_length_error_count_];
456 648 50       1667 if ( $line_length_error_count == 0 ) {
457 648         3573 $self->write_logfile_entry(
458             "No lines exceeded $rOpts->{'maximum-line-length'} characters\n");
459 648         1134 my $max_output_line_length = $self->[_max_output_line_length_];
460 648         1135 my $max_output_line_length_at = $self->[_max_output_line_length_at_];
461 648         2131 $self->write_logfile_entry(
462             " Maximum output line length was $max_output_line_length at line $max_output_line_length_at\n"
463             );
464              
465             }
466             else {
467              
468 0 0       0 my $word = ( $line_length_error_count > 1 ) ? "s" : EMPTY_STRING;
469 0         0 $self->write_logfile_entry(
470             "$line_length_error_count output line$word exceeded $rOpts->{'maximum-line-length'} characters:\n"
471             );
472              
473 0 0       0 $word = ( $line_length_error_count > 1 ) ? "First" : EMPTY_STRING;
474 0         0 my $first_line_length_error = $self->[_first_line_length_error_];
475 0         0 my $first_line_length_error_at = $self->[_first_line_length_error_at_];
476 0         0 $self->write_logfile_entry(
477             " $word at line $first_line_length_error_at by $first_line_length_error characters\n"
478             );
479              
480 0 0       0 if ( $line_length_error_count > 1 ) {
481 0         0 my $max_line_length_error = $self->[_max_line_length_error_];
482 0         0 my $max_line_length_error_at = $self->[_max_line_length_error_at_];
483 0         0 my $last_line_length_error = $self->[_last_line_length_error_];
484 0         0 my $last_line_length_error_at =
485             $self->[_last_line_length_error_at_];
486 0         0 $self->write_logfile_entry(
487             " Maximum at line $max_line_length_error_at by $max_line_length_error characters\n"
488             );
489 0         0 $self->write_logfile_entry(
490             " Last at line $last_line_length_error_at by $last_line_length_error characters\n"
491             );
492             }
493             }
494 648         1123 return;
495             } ## end sub report_line_length_errors
496             1;