File Coverage

blib/lib/Perl/Tidy/FileWriter.pm
Criterion Covered Total %
statement 149 215 69.3
branch 33 64 51.5
condition 12 21 57.1
subroutine 24 30 80.0
pod 0 20 0.0
total 218 350 62.2


line stmt bran cond sub pod time code
1             #####################################################################
2             #
3             # the Perl::Tidy::FileWriter class writes the output file
4             #
5             #####################################################################
6              
7             package Perl::Tidy::FileWriter;
8 39     39   269 use strict;
  39         79  
  39         1165  
9 39     39   192 use warnings;
  39         69  
  39         3335  
10             our $VERSION = '20230909';
11              
12 39     39   920 use constant DEVEL_MODE => 0;
  39         122  
  39         2430  
13 39     39   285 use constant EMPTY_STRING => q{};
  39         69  
  39         8321  
14              
15             sub AUTOLOAD {
16              
17             # Catch any undefined sub calls so that we are sure to get
18             # some diagnostic information. This sub should never be called
19             # except for a programming error.
20 0     0   0 our $AUTOLOAD;
21 0 0       0 return if ( $AUTOLOAD =~ /\bDESTROY$/ );
22 0         0 my ( $pkg, $fname, $lno ) = caller();
23 0         0 my $my_package = __PACKAGE__;
24 0         0 print {*STDERR} <<EOM;
  0         0  
25             ======================================================================
26             Error detected in package '$my_package', version $VERSION
27             Received unexpected AUTOLOAD call for sub '$AUTOLOAD'
28             Called from package: '$pkg'
29             Called from File '$fname' at line '$lno'
30             This error is probably due to a recent programming change
31             ======================================================================
32             EOM
33 0         0 exit 1;
34             } ## end sub AUTOLOAD
35              
36       0     sub DESTROY {
37              
38             # required to avoid call to AUTOLOAD in some versions of perl
39             }
40              
41             my $input_stream_name = EMPTY_STRING;
42              
43             # Maximum number of little messages; probably need not be changed.
44 39     39   370 use constant MAX_NAG_MESSAGES => 6;
  39         97  
  39         5728  
45              
46 0         0 BEGIN {
47              
48             # Array index names for variables.
49             # Do not combine with other BEGIN blocks (c101).
50 39     39   52212 my $i = 0;
51             use constant {
52 39         8714 _logger_object_ => $i++,
53             _rOpts_ => $i++,
54             _output_line_number_ => $i++,
55             _consecutive_blank_lines_ => $i++,
56             _consecutive_nonblank_lines_ => $i++,
57             _consecutive_new_blank_lines_ => $i++,
58             _first_line_length_error_ => $i++,
59             _max_line_length_error_ => $i++,
60             _last_line_length_error_ => $i++,
61             _first_line_length_error_at_ => $i++,
62             _max_line_length_error_at_ => $i++,
63             _last_line_length_error_at_ => $i++,
64             _line_length_error_count_ => $i++,
65             _max_output_line_length_ => $i++,
66             _max_output_line_length_at_ => $i++,
67             _rK_checklist_ => $i++,
68             _K_arrival_order_matches_ => $i++,
69             _K_sequence_error_msg_ => $i++,
70             _K_last_arrival_ => $i++,
71             _save_logfile_ => $i++,
72             _routput_string_ => $i++,
73 39     39   301 };
  39         135  
74             } ## end BEGIN
75              
76             sub Die {
77 0     0 0 0 my ($msg) = @_;
78 0         0 Perl::Tidy::Die($msg);
79 0         0 return;
80             }
81              
82             sub Fault {
83 0     0 0 0 my ($msg) = @_;
84              
85             # This routine is called for errors that really should not occur
86             # except if there has been a bug introduced by a recent program change.
87             # Please add comments at calls to Fault to explain why the call
88             # should not occur, and where to look to fix it.
89 0         0 my ( $package0, $filename0, $line0, $subroutine0 ) = caller(0);
90 0         0 my ( $package1, $filename1, $line1, $subroutine1 ) = caller(1);
91 0         0 my ( $package2, $filename2, $line2, $subroutine2 ) = caller(2);
92 0         0 my $pkg = __PACKAGE__;
93              
94 0         0 Die(<<EOM);
95             ==============================================================================
96             While operating on input stream with name: '$input_stream_name'
97             A fault was detected at line $line0 of sub '$subroutine1'
98             in file '$filename1'
99             which was called from line $line1 of sub '$subroutine2'
100             Message: '$msg'
101             This is probably an error introduced by a recent programming change.
102             $pkg reports VERSION='$VERSION'.
103             ==============================================================================
104             EOM
105              
106             # This return is to keep Perl-Critic from complaining.
107 0         0 return;
108             } ## end sub Fault
109              
110             sub warning {
111 0     0 0 0 my ( $self, $msg ) = @_;
112 0         0 my $logger_object = $self->[_logger_object_];
113 0 0       0 if ($logger_object) { $logger_object->warning($msg); }
  0         0  
114 0         0 return;
115             } ## end sub warning
116              
117             sub write_logfile_entry {
118 1120     1120 0 2726 my ( $self, $msg ) = @_;
119 1120         2155 my $logger_object = $self->[_logger_object_];
120 1120 100       2787 if ($logger_object) {
121 1116         2854 $logger_object->write_logfile_entry($msg);
122             }
123 1120         2733 return;
124             } ## end sub write_logfile_entry
125              
126             sub new {
127 560     560 0 2208 my ( $class, $line_sink_object, $rOpts, $logger_object ) = @_;
128              
129 560         1583 my $self = [];
130 560         1898 $self->[_logger_object_] = $logger_object;
131 560         1438 $self->[_rOpts_] = $rOpts;
132 560         1455 $self->[_output_line_number_] = 1;
133 560         1451 $self->[_consecutive_blank_lines_] = 0;
134 560         1501 $self->[_consecutive_nonblank_lines_] = 0;
135 560         1622 $self->[_consecutive_new_blank_lines_] = 0;
136 560         1494 $self->[_first_line_length_error_] = 0;
137 560         1431 $self->[_max_line_length_error_] = 0;
138 560         1410 $self->[_last_line_length_error_] = 0;
139 560         1359 $self->[_first_line_length_error_at_] = 0;
140 560         1390 $self->[_max_line_length_error_at_] = 0;
141 560         1360 $self->[_last_line_length_error_at_] = 0;
142 560         1429 $self->[_line_length_error_count_] = 0;
143 560         1290 $self->[_max_output_line_length_] = 0;
144 560         1310 $self->[_max_output_line_length_at_] = 0;
145 560         1568 $self->[_rK_checklist_] = [];
146 560         1426 $self->[_K_arrival_order_matches_] = 0;
147 560         1644 $self->[_K_sequence_error_msg_] = EMPTY_STRING;
148 560         1565 $self->[_K_last_arrival_] = -1;
149 560         1628 $self->[_save_logfile_] = defined($logger_object);
150 560         1440 $self->[_routput_string_] = undef;
151              
152             # '$line_sink_object' is a SCALAR ref which receives the lines.
153 560         1686 my $ref = ref($line_sink_object);
154 560 50       2859 if ( !$ref ) {
    50          
155 0         0 Fault("FileWriter expects line_sink_object to be a ref\n");
156             }
157             elsif ( $ref eq 'SCALAR' ) {
158 560         1476 $self->[_routput_string_] = $line_sink_object;
159             }
160             else {
161 0         0 my $str = $ref;
162 0 0       0 if ( length($str) > 63 ) { $str = substr( $str, 0, 60 ) . '...' }
  0         0  
163 0         0 Fault(<<EOM);
164             FileWriter expects 'line_sink_object' to be ref to SCALAR but it is ref to:
165             $str
166             EOM
167             }
168              
169             # save input stream name for local error messages
170 560         1474 $input_stream_name = EMPTY_STRING;
171 560 100       1995 if ($logger_object) {
172 558         2939 $input_stream_name = $logger_object->get_input_stream_name();
173             }
174              
175 560         1698 bless $self, $class;
176 560         1816 return $self;
177             } ## end sub new
178              
179             sub setup_convergence_test {
180 557     557 0 2172 my ( $self, $rlist ) = @_;
181 557 100       1111 if ( @{$rlist} ) {
  557         2254  
182              
183             # We are going to destroy the list, so make a copy
184             # and put in reverse order so we can pop values
185 546         1231 my @list = @{$rlist};
  546         3945  
186 546 100       2148 if ( $list[0] < $list[-1] ) {
187 470         1294 @list = reverse @list;
188             }
189 546         1764 $self->[_rK_checklist_] = \@list;
190             }
191 557         1395 $self->[_K_arrival_order_matches_] = 1;
192 557         1419 $self->[_K_sequence_error_msg_] = EMPTY_STRING;
193 557         1182 $self->[_K_last_arrival_] = -1;
194 557         1612 return;
195             } ## end sub setup_convergence_test
196              
197             sub get_convergence_check {
198 560     560 0 1626 my ($self) = @_;
199 560         1543 my $rlist = $self->[_rK_checklist_];
200              
201             # converged if all K arrived and in correct order
202 560   66     3684 return $self->[_K_arrival_order_matches_] && !@{$rlist};
203             } ## end sub get_convergence_check
204              
205             sub get_output_line_number {
206 493     493 0 1390 return $_[0]->[_output_line_number_];
207             }
208              
209             sub decrement_output_line_number {
210 560     560 0 1458 $_[0]->[_output_line_number_]--;
211 560         1220 return;
212             }
213              
214             sub get_consecutive_nonblank_lines {
215 1     1 0 10 return $_[0]->[_consecutive_nonblank_lines_];
216             }
217              
218             sub get_consecutive_blank_lines {
219 0     0 0 0 return $_[0]->[_consecutive_blank_lines_];
220             }
221              
222             sub reset_consecutive_blank_lines {
223 120     120 0 215 $_[0]->[_consecutive_blank_lines_] = 0;
224 120         223 return;
225             }
226              
227             # This sub call allows termination of logfile writing for efficiency when we
228             # know that the logfile will not be saved.
229             sub set_save_logfile {
230 558     558 0 1858 my ( $self, $save_logfile ) = @_;
231 558         1617 $self->[_save_logfile_] = $save_logfile;
232 558         1484 return;
233             }
234              
235             sub want_blank_line {
236 21     21 0 47 my $self = shift;
237 21 100       70 if ( !$self->[_consecutive_blank_lines_] ) {
238 13         53 $self->write_blank_code_line();
239             }
240 21         41 return;
241             } ## end sub want_blank_line
242              
243             sub require_blank_code_lines {
244              
245             # write out the requested number of blanks regardless of the value of -mbl
246             # unless -mbl=0. This allows extra blank lines to be written for subs and
247             # packages even with the default -mbl=1
248 45     45 0 123 my ( $self, $count ) = @_;
249 45         104 my $need = $count - $self->[_consecutive_blank_lines_];
250 45         96 my $rOpts = $self->[_rOpts_];
251 45         121 my $forced = $rOpts->{'maximum-consecutive-blank-lines'} > 0;
252 45         143 foreach ( 0 .. $need - 1 ) {
253 31         94 $self->write_blank_code_line($forced);
254             }
255 45         124 return;
256             } ## end sub require_blank_code_lines
257              
258             sub write_blank_code_line {
259 873     873 0 2090 my ( $self, $forced ) = @_;
260              
261             # Write a blank line of code, given:
262             # $forced = optional flag which, if set, forces the blank line
263             # to be written. This allows the -mbl flag to be temporarily
264             # exceeded.
265              
266 873         1791 my $rOpts = $self->[_rOpts_];
267             return
268             if (!$forced
269             && $self->[_consecutive_blank_lines_] >=
270 873 100 100     4357 $rOpts->{'maximum-consecutive-blank-lines'} );
271              
272 771         1613 $self->[_consecutive_nonblank_lines_] = 0;
273              
274             # Balance old blanks against new (forced) blanks instead of writing them.
275             # This fixes case b1073.
276 771 50 66     3239 if ( !$forced && $self->[_consecutive_new_blank_lines_] > 0 ) {
277 0         0 $self->[_consecutive_new_blank_lines_]--;
278 0         0 return;
279             }
280              
281 771         1304 ${ $self->[_routput_string_] } .= "\n";
  771         1988  
282              
283 771         1425 $self->[_output_line_number_]++;
284 771         1300 $self->[_consecutive_blank_lines_]++;
285 771 100       1776 $self->[_consecutive_new_blank_lines_]++ if ($forced);
286              
287 771         1608 return;
288             } ## end sub write_blank_code_line
289              
290 39     39   338 use constant MAX_PRINTED_CHARS => 80;
  39         80  
  39         38875  
291              
292             sub write_code_line {
293 7376     7376 0 15625 my ( $self, $str, $K ) = @_;
294              
295             # Write a line of code, given
296             # $str = the line of code
297             # $K = an optional check integer which, if if given, must
298             # increase monotonically. This was added to catch cache
299             # sequence errors in the vertical aligner.
300              
301 7376         12611 $self->[_consecutive_blank_lines_] = 0;
302 7376         11034 $self->[_consecutive_new_blank_lines_] = 0;
303 7376         10894 $self->[_consecutive_nonblank_lines_]++;
304 7376         10623 $self->[_output_line_number_]++;
305              
306 7376         9906 ${ $self->[_routput_string_] } .= $str;
  7376         21383  
307              
308 7376 100       17759 if ( $self->[_save_logfile_] ) { $self->check_line_lengths($str) }
  5         21  
309              
310             #----------------------------
311             # Convergence and error check
312             #----------------------------
313 7376 100       15260 if ( defined($K) ) {
314              
315             # Convergence check: we are checking if all defined K values arrive in
316             # the order which was defined by the caller. Quit checking if any
317             # unexpected K value arrives.
318 6573 100       14981 if ( $self->[_K_arrival_order_matches_] ) {
319 3204         4834 my $Kt = pop @{ $self->[_rK_checklist_] };
  3204         6637  
320 3204 100 66     12841 if ( !defined($Kt) || $Kt != $K ) {
321 265         977 $self->[_K_arrival_order_matches_] = 0;
322             }
323             }
324              
325             # Check for out-of-order arrivals of index K. The K values are the
326             # token indexes of the last token of code lines, and they should come
327             # out in increasing order. Otherwise something is seriously wrong.
328             # Most likely a recent programming change to VerticalAligner.pm has
329             # caused lines to go out in the wrong order. This could happen if
330             # either the cache or buffer that it uses are emptied in the wrong
331             # order.
332 6573 50 33     16854 if ( $K < $self->[_K_last_arrival_]
333             && !$self->[_K_sequence_error_msg_] )
334             {
335 0         0 my $K_prev = $self->[_K_last_arrival_];
336              
337 0         0 chomp $str;
338 0 0       0 if ( length($str) > MAX_PRINTED_CHARS ) {
339 0         0 $str = substr( $str, 0, MAX_PRINTED_CHARS ) . "...";
340             }
341              
342 0         0 my $msg = <<EOM;
343             While operating on input stream with name: '$input_stream_name'
344             Lines have arrived out of order in sub 'write_code_line'
345             as detected by token index K=$K arriving after index K=$K_prev in the following line:
346             $str
347             This is probably due to a recent programming change and needs to be fixed.
348             EOM
349              
350             # Always die during development, this needs to be fixed
351 0         0 if (DEVEL_MODE) { Fault($msg) }
352              
353             # Otherwise warn if string is not empty (added for b1378)
354 0 0       0 $self->warning($msg) if ( length($str) );
355              
356             # Only issue this warning once
357 0         0 $self->[_K_sequence_error_msg_] = $msg;
358              
359             }
360 6573         10239 $self->[_K_last_arrival_] = $K;
361             }
362 7376         16555 return;
363             } ## end sub write_code_line
364              
365             sub write_line {
366 259     259 0 541 my ( $self, $str ) = @_;
367              
368             # Write a line directly to the output, without any counting of blank or
369             # non-blank lines.
370              
371 259         361 ${ $self->[_routput_string_] } .= $str;
  259         705  
372              
373 259 50       696 if ( chomp $str ) { $self->[_output_line_number_]++; }
  259         421  
374 259 50       585 if ( $self->[_save_logfile_] ) { $self->check_line_lengths($str) }
  0         0  
375              
376 259         507 return;
377             } ## end sub write_line
378              
379             sub check_line_lengths {
380 5     5 0 26 my ( $self, $str ) = @_;
381              
382             # collect info on line lengths for logfile
383              
384             # This calculation of excess line length ignores any internal tabs
385 5         11 my $rOpts = $self->[_rOpts_];
386 5         8 chomp $str;
387 5         10 my $len_str = length($str);
388 5         12 my $exceed = $len_str - $rOpts->{'maximum-line-length'};
389 5 50 33     157 if ( $str && substr( $str, 0, 1 ) eq "\t" && $str =~ /^\t+/g ) {
      33        
390 0         0 $exceed += pos($str) * $rOpts->{'indent-columns'};
391             }
392              
393             # Note that we just incremented output line number to future value
394             # so we must subtract 1 for current line number
395 5 100       15 if ( $len_str > $self->[_max_output_line_length_] ) {
396 3         5 $self->[_max_output_line_length_] = $len_str;
397 3         8 $self->[_max_output_line_length_at_] =
398             $self->[_output_line_number_] - 1;
399             }
400              
401 5 50       14 if ( $exceed > 0 ) {
402 0         0 my $output_line_number = $self->[_output_line_number_];
403 0         0 $self->[_last_line_length_error_] = $exceed;
404 0         0 $self->[_last_line_length_error_at_] = $output_line_number - 1;
405 0 0       0 if ( $self->[_line_length_error_count_] == 0 ) {
406 0         0 $self->[_first_line_length_error_] = $exceed;
407 0         0 $self->[_first_line_length_error_at_] = $output_line_number - 1;
408             }
409              
410 0 0       0 if ( $self->[_last_line_length_error_] >
411             $self->[_max_line_length_error_] )
412             {
413 0         0 $self->[_max_line_length_error_] = $exceed;
414 0         0 $self->[_max_line_length_error_at_] = $output_line_number - 1;
415             }
416              
417 0 0       0 if ( $self->[_line_length_error_count_] < MAX_NAG_MESSAGES ) {
418 0         0 $self->write_logfile_entry(
419             "Line length exceeded by $exceed characters\n");
420             }
421 0         0 $self->[_line_length_error_count_]++;
422             }
423 5         12 return;
424             } ## end sub check_line_lengths
425              
426             sub report_line_length_errors {
427 560     560 0 1677 my $self = shift;
428              
429             # Write summary info about line lengths to the log file
430              
431 560         1357 my $rOpts = $self->[_rOpts_];
432 560         1445 my $line_length_error_count = $self->[_line_length_error_count_];
433 560 50       1929 if ( $line_length_error_count == 0 ) {
434 560         4234 $self->write_logfile_entry(
435             "No lines exceeded $rOpts->{'maximum-line-length'} characters\n");
436 560         2116 my $max_output_line_length = $self->[_max_output_line_length_];
437 560         2059 my $max_output_line_length_at = $self->[_max_output_line_length_at_];
438 560         3421 $self->write_logfile_entry(
439             " Maximum output line length was $max_output_line_length at line $max_output_line_length_at\n"
440             );
441              
442             }
443             else {
444              
445 0 0       0 my $word = ( $line_length_error_count > 1 ) ? "s" : EMPTY_STRING;
446 0         0 $self->write_logfile_entry(
447             "$line_length_error_count output line$word exceeded $rOpts->{'maximum-line-length'} characters:\n"
448             );
449              
450 0 0       0 $word = ( $line_length_error_count > 1 ) ? "First" : EMPTY_STRING;
451 0         0 my $first_line_length_error = $self->[_first_line_length_error_];
452 0         0 my $first_line_length_error_at = $self->[_first_line_length_error_at_];
453 0         0 $self->write_logfile_entry(
454             " $word at line $first_line_length_error_at by $first_line_length_error characters\n"
455             );
456              
457 0 0       0 if ( $line_length_error_count > 1 ) {
458 0         0 my $max_line_length_error = $self->[_max_line_length_error_];
459 0         0 my $max_line_length_error_at = $self->[_max_line_length_error_at_];
460 0         0 my $last_line_length_error = $self->[_last_line_length_error_];
461 0         0 my $last_line_length_error_at =
462             $self->[_last_line_length_error_at_];
463 0         0 $self->write_logfile_entry(
464             " Maximum at line $max_line_length_error_at by $max_line_length_error characters\n"
465             );
466 0         0 $self->write_logfile_entry(
467             " Last at line $last_line_length_error_at by $last_line_length_error characters\n"
468             );
469             }
470             }
471 560         2301 return;
472             } ## end sub report_line_length_errors
473             1;