File Coverage

lib/Devel/Comments.pm
Criterion Covered Total %
statement 268 299 89.6
branch 63 106 59.4
condition 26 44 59.0
subroutine 30 34 88.2
pod 0 7 0.0
total 387 490 78.9


line stmt bran cond sub pod time code
1             package Devel::Comments;
2              
3             ######## use section ########
4 25     25   133911 use 5.008;
  25         90  
  25         1497  
5 25     25   133 use strict;
  25         45  
  25         1132  
6 25     25   128 use warnings;
  25         144  
  25         943  
7              
8             # Please see the file VERSIONS.
9 25     25   32766 use version 0.77; our $VERSION = qv('1.1.4');
  25         117115  
  25         196  
10              
11             # original S::C (originally used here)
12 25     25   2813 use Carp;
  25         49  
  25         3722  
13 25     25   149 use List::Util qw(sum);
  25         47  
  25         4343  
14 25     25   38171 use Filter::Simple;
  25         1157472  
  25         205  
15              
16             # collected S::C (originally distributed in code)
17             use Text::Balanced # Extract delimited text sequences from strings
18 25     25   1661 qw( extract_variable extract_multiple );
  25         73  
  25         2072  
19            
20 25     25   31593 use Data::Dumper 'Dumper';
  25         283862  
  25         56013  
21              
22             # debug only
23              
24             #~ $DB::single=1; #~
25             #~ use feature 'say'; # disable in production #~
26             #~ use Smart::Comments '###'; # playing with fire; debug only #~
27             #~ use Smart::Comments '####'; # playing with fire; debug only #~
28             #~ use Smart::Comments '#####'; # playing with fire; debug only #~
29              
30             ######## / use ########
31              
32             #~ say '---| Devel::Comments at line ', __LINE__; #~
33              
34             ######## pseudo-constants section ########
35              
36             # time and space constants
37             my $maxwidth = 69; # Maximum width of display
38             my $showwidth = 35; # How wide to make the indicator
39             my $showstarttime = 6; # How long before showing time-remaining estimate
40             my $showmaxtime = 10; # Don't start estimate if less than this to go
41             my $whilerate = 30; # Controls the rate at which while indicator grows
42             my $minfillwidth = 5; # Fill area must be at least this wide
43             my $average_over = 5; # Number of time-remaining estimates to average
44             my $minfillreps = 2; # Minimum size of a fill and fill cap indicator
45             my $forupdatequantum = 0.01; # Only update every 1% of elapsed distance
46              
47             # Synonyms for asserts and requirements...
48             my $require = qr/require|ensure|assert|insist/;
49             my $check = qr/check|verify|confirm/;
50              
51             # Horizontal whitespace...
52             my $hws = qr/[^\S\n]/;
53              
54             # Optional colon...
55             my $optcolon = qr/$hws*;?/;
56              
57             # Automagic debugging as well... (perl -d debugger)
58             # Someone has to tell me why *two* assignments here (??)
59             my $DBX = '$DB::single = $DB::single = 1;';
60              
61             # Recognize progress bars...
62             my @progress_pats = (
63             # left extending end marker of bar right
64             # anchor bar ("fill") | gap after bar anchor
65             # ====== ======================= === ================= ====
66             qr{^(\s*.*?) (\[\]\[\]) () \s* (\S?.*)}x,
67             qr{^(\s*.*?) (\(\)\(\)) () \s* (\S?.*)}x,
68             qr{^(\s*.*?) (\{\}\{\}) () \s* (\S?.*)}x,
69             qr{^(\s*.*?) (\<\>\<\>) () \s* (\S?.*)}x,
70             qr{^(\s*.*?) (?>(\S)\2{$minfillreps,}) (\S+) \s{$minfillreps,} (\S.*)}x,
71             qr{^(\s*.*?) (?>(\S)\2{$minfillreps,}) () \s{$minfillreps,} (\S.*)}x,
72             qr{^(\s*.*?) (?>(\S)\2{$minfillreps,}) (\S*) (?=\s*$)}x,
73             qr{^(\s*.*?) () () () \s*$ }x,
74             );
75              
76             # new with DC
77             my $join_up = qq{ }; # used to join replacement code strings
78              
79             ######## / pseudo-constants ########
80              
81             ######## pseudo-global variables section ########
82              
83             ## original S::C stuff
84              
85             # Unique ID assigned to each loop; incremented when assigned
86             # See: for_progress, while_progress
87             my $ID = 0;
88              
89             # See: for_progress
90             my %started ;
91              
92             # See: _moving_average
93             my %moving ;
94              
95             # State information for various progress bars...
96             # See: for_progress, while_progress
97             my (%count, %max, %prev_elapsed, %prev_fraction, %showing);
98              
99             # See: while_progress
100             my $prev_length = -1;
101              
102              
103             ## new with DC
104              
105             # Unique ID assigned to each use of DC
106             # (strictly, per-import)
107             #
108             # Note that since source filtering is applied from use line down to EOF
109             # or (perhaps) 'no Devel::Comments;', a given filtering action is neither
110             # strictly per-package nor per-file.
111             #
112             # See _get_new_caller_id()
113             #
114             my $new_caller_id = 1; # Will be assigned to "this" use
115              
116             # Store per-use (per-fileish) state info
117             # for access by external routines called by replacement code
118             my %state_of ;
119             # SomeCaller => { # $caller_id is primary key
120             # -outfh # desired output filehandle
121             # -tell => { # stored tell() of...
122             # -outfh # ... $outfh
123             # -stdout # ... *STDOUT
124             # },
125             # -caller => { # stored caller()...
126             # -name # ...[0] (= 'SomeCaller')
127             # -file # ...[1]
128             # -line # ...[2]
129             # },
130             # },
131             # AnotherCaller...
132              
133             ######## / pseudo-global variables ########
134              
135             #----------------------------------------------------------------------------#
136              
137              
138             ######## INTERNAL ROUTINE ########
139             #
140             # my $caller_id = _get_new_caller_id(); # unique per-use
141             #
142             # Purpose : Assign a unique ID to each filtering operation
143             # Parms : none
144             # Reads : $new_caller_id
145             # Returns : $caller_id scalar integer
146             # Writes : $new_caller_id
147             # Throws : never
148             # See also : %state_of
149             #
150             # Called once per use line by _prefilter(). Thereafter, $caller_id is either
151             # passed along or interpolated and inserted into client code.
152             # Strictly, $caller_id is unique neither to calling package nor file;
153             # it is assigned whenever Filter::Simple::FILTER calls _prefilter(),
154             # which should happen once per use. So, its scope within client code is:
155             # from: use Devel::Comments
156             # to: no Devel::Comments
157             # ...possibly crossing package boundaries.
158             #
159             sub _get_new_caller_id {
160 25     25   95 return $new_caller_id++;
161             };
162             ######## /_get_new_caller_id ########
163              
164             ######## INTERNAL ROUTINE ########
165             #
166             # my $outfh = _get_outfh($caller_id); # retrieve from %state_of
167             #
168             # Purpose : Retrieve output filehandle associated with some caller
169             # Parms : $caller_id
170             # Reads : %state_of
171             # Returns : stored filehandle for all smart output
172             # Writes : none
173             # Throws : dies if no arg passed
174             # See also : _put_outfh(), _get_new_caller_id()
175             #
176             sub _get_outfh {
177 284 50   284   725 my $caller_id = shift
178             or die q{Devel::Comments: } # called with no arg
179             , q{Internal error: }
180             , q{_get_outfh called with no or false arg. }
181             , $!
182             ;
183 284 50       4768 defined $state_of{$caller_id}
184             or die q{Devel::Comments: } # called with bad id
185             , q{Internal error: }
186             , qq{$caller_id not defined in }
187             , q{%state_of. }
188             , $!
189             ;
190            
191 284 50       1037 defined $state_of{$caller_id}{-outfh}
192             or die q{Devel::Comments: } # no $outfh found
193             , q{Internal error: }
194             , q{No output filehandle found in %state_of }
195             , qq{for $caller_id. }
196             , $!
197             ;
198            
199 284         987 return $state_of{$caller_id}{-outfh};
200            
201             };
202             ######## /_do_ ########
203              
204             ######## INTERNAL ROUTINE ########
205             #
206             # _init_state({ # initialize $state_of this caller
207             # -outfh => $outfh,
208             # -caller_id => $caller_id,
209             # });
210             #
211             # Purpose : Initialize state; store $outfh and avoid warnings later
212             # Parms : hashref
213             # : -caller_id $caller_id
214             # : -outfh $outfh
215             # Reads : none
216             # Returns : 1
217             # Writes : %state_of
218             # Throws : never
219             # See also : _prefilter(), _put_state()
220             #
221             # Call once per use from _prefilter() only.
222             # This is important, lest we get confused about which stack frame is wanted.
223             #
224             sub _init_state {
225 25     25   55 my $href = shift;
226            
227 25 50       141 my $caller_id = $href->{-caller_id}
228             or die q{Devel::Comments: } # called with no -caller_id
229             , q{Internal error: }
230             , q{-caller_id not passed in call to _init_state(). }
231             , $!
232             ;
233            
234 25 50       167 my $outfh = $href->{-outfh}
235             or die q{Devel::Comments: } # called with no -outfh
236             , q{Internal error: }
237             , q{-outfh not passed in call to _init_state(). }
238             , $!
239             ;
240            
241             # frame
242             # 0 _prefilter
243             # 1 FILTER
244             # 2 Filter::Simple
245             # 3 actual use-line caller
246 25         49 my $frame = 3;
247 25         180 my @caller = caller($frame);
248            
249             # my $caller_name = $caller[0];
250 25         72 my $caller_file = $caller[1];
251 25         46 my $caller_line = $caller[2];
252 25         52 my $caller_sub = $caller[3]; # TODO?: Test if we have the right caller...
253            
254             # Stash $outfh as $caller_id-dependent state info
255 25         121 $state_of{$caller_id}{-outfh} = $outfh;
256            
257             # It may not matter *what* you initialize these to...
258 25         175 $state_of{$caller_id}{-tell}{-outfh} = tell $outfh;
259 25         131 $state_of{$caller_id}{-tell}{-stdout} = tell (*STDOUT);
260 25         210 $state_of{$caller_id}{-caller}{-file} = $caller_file;
261 25         93 $state_of{$caller_id}{-caller}{-line} = $caller_line;
262            
263             #~ ### ...Leaving _init_state()... #~
264             #~ ### %state_of #~
265            
266 25         115 return 1;
267             };
268             ######## /_init_state ########
269              
270             ######## INTERNAL ROUTINE ########
271             #
272             # $prefilter = _prefilter(@_); # Handle arguments to FILTER
273             #
274             # Purpose : Handle arguments and do pseudo-global and per-use setup
275             # Parms : @_
276             # Reads : %ENV
277             # Returns : hashref (or 0 to abort filtering entirely)
278             # : -intro $intro
279             # : -caller_id $caller_id
280             # Writes : %state_of
281             # Throws : carp() if passed a bad arg in @_
282             # See also : ____
283             #
284             # Don't want to be fussy about the order of args passed on the use line,
285             # so each bit roots through all of them looking for what it wants.
286             #
287             sub _prefilter {
288            
289             #~ say '---| Devel::Comments at line ', __LINE__; #~
290            
291 25     25   56 shift; # Don't need our own package name
292 25         113 s/\r\n/\n/g; # Handle win32 line endings
293            
294 25         107 my $caller_id = _get_new_caller_id(); # unique per-use
295            
296             # Default introducer pattern...
297 25         109 my $intro = qr/#{3,}/;
298 25         56 my @intros ;
299            
300             # Get filehandle
301            
302 25         54 my $fh_seen = 0; # no filehandle seen yet
303 25         56 my $outfh = undef; # don't assign it first; see open()
304 25         152 my $out_filename = "$0.log"; # default
305 25         48 my $arg ; # trial from @_
306             my %packed_args ; # possible args packed into a hashref
307            
308             # Dig through the args to see if one is a hashref
309             GETHREF:
310 25         134 for my $i ( 0..$#_ ) { # will need the index in a bit
311 8         23 $arg = $_[$i]; # look but don't take
312            
313 8 100       37 if ( ref $arg ) { # some kind of reference
314 1         6 my $stringy = sprintf $arg;
315 1 50       8 if ( $stringy =~ /HASH/ ) { # looks like a hash ref
316 1         5 %packed_args = %$arg;
317 1 50       7 if ( defined $packed_args{-file} ) {
318 1         4 $out_filename = $packed_args{-file};
319             }; # else if undef, use default
320 1         3 splice @_, $i; # remove the parsed arg
321             #~ say '$out_filename: ', $out_filename; ~#
322 1 50       147 open $outfh, '>', $out_filename
323             or die "Devel::Comments: "
324             , "Can't open $out_filename to write."
325             , $!
326             ;
327             # Autoflush $outfh
328 1         6 my $prev_fh = select $outfh;
329 1         6 local $| = 1; # autoflush
330 1         8 select $prev_fh;
331              
332            
333            
334             #~ say $outfh '... Just after opening $outfh ...'; #~
335             #~ say $outfh '$outfh: ', $outfh; #~
336             };
337             };
338            
339             #~ return 0; #~
340             }; # /GETHREF
341            
342             # Dig through the args to see if one is a filehandle
343             SETFH:
344 25         85 for my $i ( 0..$#_ ) { # will need the index in a bit
345 7         13 $arg = $_[$i]; # look but don't take
346            
347             # Is $arg defined by vanilla Smart::Comments?
348 7 50 66     46 if ( $arg eq '-ENV' || (substr $arg, 0, 1) eq '#' ) {
349 7         23 next SETFH; # no, keep looking
350             };
351             #~ print 'Mine: >', $arg, "<\n"; #~
352            
353             # Vanilla doesn't want to see it, so remove from @_
354 0         0 splice @_, $i;
355            
356             # Is it a writable filehandle?
357 0 0       0 if ( not -w $arg ) {
358 0         0 carp q{Not a writable filehandle: }
359             . qq{$arg}
360             . q{ in call to 'use Devel::Comments'.}
361             ;
362             } # and keep looking
363             else {
364 0         0 $outfh = $arg;
365 0         0 last SETFH; # found, so we're done looking
366             };
367             }; # /SETFH
368            
369 25 100       125 if (!$outfh) {
370 24         101 $outfh = *STDERR; # default
371             };
372            
373             #~ say STDERR '... About to _init_state() ...'; #~
374             #~ say STDERR '$outfh: ', $outfh; #~
375 25         203 _init_state({ # initialize $state_of this caller
376             -outfh => $outfh,
377             -caller_id => $caller_id,
378             });
379            
380             #### ...In prefilter()...
381             #### %state_of
382            
383             ## done with the new-for-DC setup
384            
385            
386             # Handle intros and env args...
387 25         136 while (@_) {
388 9         77 my $arg = shift @_;
389              
390 9 100       31 if ($arg eq '-ENV') {
391 4         12 my $env_filters = _handle_env();
392 4 100       17 return 0 if !$env_filters; # i.e. if no filtering ABORT
393 3         6 unshift @_, @{$env_filters};
  3         14  
394             }
395             else {
396 5         14 push @intros, $arg;
397             }
398             }
399              
400 24 50       121 if (my @unknowns = grep {!/$intro/} @intros) {
  5         51  
401 0         0 croak "Incomprehensible arguments: @unknowns\n",
402             "in call to 'use Devel::Comments'";
403             }
404              
405             # Make non-default introducer pattern...
406 24 100       269 if (@intros) {
407 2         11 $intro = '(?-x:'.join('|',@intros).')(?!\#)';
408             }
409              
410             #~ say $outfh '... Leaving _prefilter() ...'; #~
411             return {
412 24         159 -intro => $intro,
413             -caller_id => $caller_id,
414             };
415             };
416             ######## /_prefilter ########
417              
418             ######## INTERNAL ROUTINE ########
419             # _handle_env
420             #
421             # Purpose : Deal with environment variables
422             # Params : *none*
423             # Reads : %ENV
424             # Returns : nothing => no environment variable set
425             # : array ref => a list of things to put onto
426             # the "intros" array.
427             sub _handle_env {
428             # First look to see if the Devel_Comments variable is set, if so
429             # process it and return.
430 4     4   12 my $dc_env = $ENV{Devel_Comments};
431 4 100       14 if ($dc_env) {
432 1         3 return _handle_dc_env($dc_env);
433             }
434             # Now check the multitude of smart comments environment variables.
435 3   33     28 my $sc_env =
436             $ENV{Smart_Comments}
437             || $ENV{SMART_COMMENTS}
438             || $ENV{SmartComments}
439             || $ENV{SMARTCOMMENTS};
440 3 100       13 if ($sc_env) {
441 2         9 return _handle_sc_env($sc_env);
442             }
443              
444 1         2 return;
445             }
446             ######## /_handle_env ########
447              
448             ######## INTERNAL ROUTINE ########
449             # _handle_dc_env
450             #
451             # Purpose : To process the devel comments environment variable.
452             # Params : A scalar containing the value of the environment variable
453             # Returns : An array ref containing 0 or more ???s
454             # - if the env var just contains a 1 a ref to an empty
455             # array is returned.
456             # - otherwise the variable is split on space or (space
457             # surrounded) colons.
458             sub _handle_dc_env {
459 1     1   2 my $env = shift;
460             # For now we can just do the same thing as for a smart comments
461             # env variable. In future it would be possible to handle devel
462             # comments environment variables differently.
463 1         2 return _handle_sc_env($env);
464             }
465              
466             ######## /_handle_dc_env ########
467              
468             ######## INTERNAL ROUTINE ########
469             # _handle_sc_env
470             #
471             # Purpose : To process the devel comments environment variable.
472             # Params : A scalar containing the value of the environment variable
473             # Returns : An array ref containing 0 or more ???s
474             # - if the env var just contains a 1 a ref to an empty
475             # array is returned.
476             # - otherwise the variable is split on space or (space
477             # surrounded) colons.
478             sub _handle_sc_env {
479 3     3   5 my $env = shift;
480 3 100       21 if ( $env !~ m{\A \s* 1 \s* \Z}xms ) {
481 1         12 return [ split m{\s+|\s*:\s*}xms, $env ];
482             }
483 2         9 return [];
484             }
485              
486             ######## /_handle_sc_env ########
487              
488             sub import; # FORWARD
489              
490             ######## EXTERNAL SUB CALL ########
491             #
492             # Purpose : Rewrite caller's smart comments into code
493             # Parms : @_ : The split use line, with $_[0] being *this* package
494             # : $_ : Caller's entire source code to be filtered
495             # Reads : %ENV, %state_of
496             # Returns : $_ : Filtered code
497             # Writes : %state_of
498             # Throws : never
499             # See also : Filter::Simple, _prefilter()
500             #
501             # Implement comments-to-code source filter.
502             #
503             # This is not a subroutine but a call to Filter::Simple::FILTER
504             # with its single argument being its following block.
505             #
506             # The block may be thought of as an import routine
507             # which is passed @_ and $_ and must return the filtered code in $_
508             #
509             # Note (if our module is invoked properly via use):
510             # From caller's viewpoint, use operates as a BEGIN block,
511             # including all our-module inline code and this call to FILTER;
512             # while filtered-in calls to our-module subs take place at run time.
513             # From our viewpoint, our inline code, including FILTER,
514             # is run after any BEGIN or use in our module;
515             # and filtered-in subs may be viewed
516             # as if they were externally called subs in a normal module.
517             # Because FILTER is called as part of a constructed import routine,
518             # it executes every time our module is use()-ed,
519             # although other inline code in our module only executes one time only,
520             # when first use()-ed.
521             #
522             # See "How it works" in Filter::Simple's POD.
523             #
524 0     0 0 0 sub FILTERx {}; # dummy sub only to appear in editor's symbol table
525             #
526             FILTER {
527             ##### |--- Start of filter ---|
528             ##### @_
529             ##### $_
530             #~ say "---| Source to be filtered:\n", $_, '|--- END SOURCE CODE'; #~
531              
532             my $prefilter = _prefilter(@_); # Handle arguments to FILTER
533             return 0 if !$prefilter; # i.e. if no filtering ABORT
534            
535             my $intro = $prefilter->{-intro}; # introducer pattern
536             my $caller_id = $prefilter->{-caller_id}; # unique per-use
537              
538             # Preserve DATA handle if any...
539             if (s{ ^ __DATA__ \s* $ (.*) \z }{}xms) {
540 25     25   667 no strict qw< refs >;
  25         51  
  25         125620  
541             my $DATA = $1;
542             open *{caller(1).'::DATA'}, '<', \$DATA or die "Internal error: DATA. $!";
543             }
544            
545             #~ say '---| Devel::Comments at line ', __LINE__; #~
546            
547             # Progress bar on a for loop...
548             # Calls _decode_for()
549             s{ ^ $hws* ( (?: [^\W\d]\w*: \s*)? for(?:each)? \s* (?:my)? \s* (?:\$ [^\W\d]\w*)? \s* ) \( ([^;\n]*?) \) \s* \{
550             [ \t]* $intro \s (.*) \s* $
551             }
552             { _decode_for($caller_id, $1, $2, $3) }egmx;
553              
554             # Progress bar on a while loop...
555             # Calls _decode_while()
556             s{ ^ $hws* ( (?: [^\W\d]\w*: \s*)? (?:while|until) \s* \( .*? \) \s* ) \{
557             [ \t]* $intro \s (.*) \s* $
558             }
559             { _decode_while($caller_id, $1, $2) }egmx;
560              
561             # Progress bar on a C-style for loop...
562             # Calls _decode_while()
563             s{ ^ $hws* ( (?: [^\W\d]\w*: \s*)? for \s* \( .*? ; .*? ; .*? \) \s* ) \{
564             $hws* $intro $hws (.*) $hws* $
565             }
566             { _decode_while($caller_id, $1, $2) }egmx;
567              
568             # Requirements...
569             # Calls _decode_assert()
570             s{ ^ $hws* $intro [ \t] $require : \s* (.*?) $optcolon $hws* $ }
571             { _decode_assert($caller_id, $1,"fatal") }egmx;
572              
573             # Assertions...
574             # Calls _decode_assert()
575             s{ ^ $hws* $intro [ \t] $check : \s* (.*?) $optcolon $hws* $ }
576             { _decode_assert($caller_id, $1) }egmx;
577              
578             # Any other smart comment is a simple dump.
579             # The replacement code in each case consists mainly
580             # of a call to Dump_for().
581             # But WATCH OUT for subtle differences!
582            
583             # Dump a raw scalar (the varname is used as the label)...
584             s{ ^ $hws* $intro [ \t]+ (\$ [\w:]* \w) $optcolon $hws* $ }
585             { join $join_up,
586             qq* Devel::Comments::Dump_for( *,
587             qq* -caller_id => $caller_id, *,
588             qq* -prefix => q{$1:}, *,
589             qq* -varref => [$1], *,
590             qq* );$DBX *,
591             }egmx;
592              
593             # Dump a labelled scalar...
594             s{ ^ $hws* $intro [ \t] (.+ :) [ \t]* (\$ [\w:]* \w) $optcolon $hws* $ }
595             { join $join_up,
596             qq* Devel::Comments::Dump_for( *,
597             qq* -caller_id => $caller_id, *,
598             qq* -prefix => q{$1}, *,
599             qq* -varref => [$2], *,
600             qq* );$DBX *,
601             }egmx;
602              
603             # Dump a raw hash or array (the varname is used as the label)...
604             s{ ^ $hws* $intro [ \t]+ ([\@%] [\w:]* \w) $optcolon $hws* $ }
605             { join $join_up,
606             qq* Devel::Comments::Dump_for( *,
607             qq* -caller_id => $caller_id, *,
608             qq* -prefix => q{$1:}, *,
609             qq* -varref => [\\$1], *,
610             qq* );$DBX *,
611             }egmx;
612              
613             # Dump a labelled hash or array...
614             s{ ^ $hws* $intro [ \t]+ (.+ :) [ \t]* ([\@%] [\w:]* \w) $optcolon $hws* $ }
615             { join $join_up,
616             qq* Devel::Comments::Dump_for( *,
617             qq* -caller_id => $caller_id, *,
618             qq* -prefix => q{$1}, *,
619             qq* -varref => [\\$2], *,
620             qq* );$DBX *,
621             }egmx;
622              
623             # Dump a labelled expression...
624             s{ ^ $hws* $intro [ \t]+ (.+ :) (.+) }
625             { join $join_up,
626             qq* Devel::Comments::Dump_for( *,
627             qq* -caller_id => $caller_id, *,
628             qq* -prefix => q{$1}, *,
629             qq* -varref => [$2], *,
630             qq* );$DBX *,
631             }egmx;
632              
633             # Dump an 'in progress' message
634             s{ ^ $hws* $intro $hws* (.+ [.]{3}) $hws* $ }
635             { join $join_up,
636             qq* Devel::Comments::Dump_for( *,
637             qq* -caller_id => $caller_id, *,
638             qq* -prefix => qq{$1}, *,
639             qq* );$DBX *,
640             }egmx;
641              
642             # Dump an unlabelled expression (the expression is used as the label)...
643             # Note inserted call to quiet_eval()
644             s{ ^ $hws* $intro $hws* (.*) $optcolon $hws* $ }
645             { join $join_up,
646             qq* Devel::Comments::Dump_for( *,
647             qq* -caller_id => $caller_id, *,
648             qq* -prefix => q{$1:}, *,
649             qq* -varref => Devel::Comments::quiet_eval( q{[$1]} ), *,
650             qq* );$DBX *,
651             }egmx;
652              
653             # This doesn't work as expected, don't know why
654             # It can't help to warn instead of print
655             # # An empty comment dumps an empty line...
656             # # Inserts call to warn()
657             # s{ ^ $hws* $intro [ \t]+ $ }
658             # {warn qq{\n};}gmx;
659              
660             # This is never needed; for some reason it's caught by "unlabeled expression"
661             # Strictly speaking, it's an undocumented feature
662             # # Anything else is a literal string to be printed...
663             # # Inserts call to Dump_for()
664             # s{ ^ $hws* $intro $hws* (.*) }
665             # {Devel::Comments::Dump_for(-prefix=>q{$1});$DBX}gmx;
666              
667             ##### |--- End of filter ---|
668             ##### @_
669             ##### $_
670             #~ say "---| Source after filtering:\n", $_, '|--- END SOURCE CODE'; #~
671              
672             };
673             ######## /FILTER ########
674              
675             ######## IMPORT ROUTINE ########
676             #
677             # Purpose : dummy for now
678             # Parms : ____
679             # Reads : ____
680             # Returns : ____
681             # Writes : ____
682             # Throws : ____
683             # See also : ____
684             #
685             # The "normal" import routine must be declared
686             # *before* the call to FILTER.
687             # However, Filter::Simple will call import()
688             # *after* applying FILTER to caller's source code.
689             #
690             sub import {
691            
692             #~ say '---| Devel::Comments at line ', __LINE__; #~
693              
694             };
695             ######## /import ########
696              
697             #============================================================================#
698              
699             ######## EXTERNAL ROUTINE ########
700             #
701             # $return = quiet_eval($codestring); # string eval, no errors
702             #
703             # Purpose : String eval some code and suppress any errors
704             # Parms : $codestring : Arbitrary client code
705             # Reads, Returns, Writes : Whatever client code does
706             # Throws : never, ever
707             # See also : FILTER # Dump an unlabelled expression
708             #
709             sub quiet_eval {
710 0     0 0 0 local $SIG{__WARN__} = sub{};
  0     0   0  
711 0         0 return scalar eval shift;
712             };
713             ######## /quiet_eval ########
714              
715             ######## INTERNAL ROUTINE ########
716             #
717             # $quantity = _uniq(@list); # short
718             #
719             # Purpose : ____
720             # Parms : any @list
721             # Reads : none
722             # Returns : scalar quantity of unique elements
723             # Writes : none
724             # Throws : never
725             # See also : _decode_assert()
726             #
727             #
728             sub _uniq {
729 21     21   11885 my %seen;
730 21         45 grep { !$seen{$_}++ } @_
  28         148  
731             };
732             ######## /_uniq ########
733              
734             ######## REPLACEMENT CODE GENERATOR ########
735             #
736             # $codestring = _decode_assert( $caller_id, $assertion, $signal_flag);
737             #
738             # Purpose : Converts an assertion to the equivalent Perl code.
739             # Parms : $caller_id
740             # : $assertion : text of assertion
741             # : $signal_flag : TRUE to die
742             # Reads : %state_of
743             # Returns : Replacement code string
744             # Writes : none
745             # Throws : never itself but generated code may die
746             # See also : FILTER # Requirements, # Assertions
747             #
748             # Generates three snippets of code (in reverse order):
749             # $signal_code # real die or sim warn
750             # @vardump_code_lines # Dumped variable(s)
751             # $report_code # entire replacement codestring,
752             # including previous two and $assertion
753             #
754             sub _decode_assert {
755 21     21   40 my $caller_id = shift;
756 21         69 my $assertion = shift;
757 21         32 my $signal_flag = shift;
758            
759 21         43 my $frame = 0; # replacement code calls Warn_for() directly
760            
761 21         34 my $Dump_for = 'Devel::Comments::Dump_for';
762 21         32 my $Print_for = 'Devel::Comments::Print_for';
763 21         29 my $Warn_for = 'Devel::Comments::Warn_for';
764              
765             # Choose the right signalling mechanism
766             # after Warn_for()...
767 21 100       73 my $signal_code
768             = $signal_flag
769             ? q* die "\n" * # ...then real die
770             : qq* $Print_for( $caller_id, "\n" ) * # ...then newline
771             ;
772              
773             # Extract variables from assertion and enreference any arrays or hashes...
774             my @vardump_code_lines
775 28 50       1603 = map {
776 21         137 /^$hws*[%\@]/ # sigil found
777             ? join $join_up,
778             qq* $Dump_for( *,
779             qq* -caller_id => $caller_id, *,
780             qq* -prefix => q{ $_ was:}, *,
781             qq* -varref => [\\$_], *, # enreference
782             qq* -no_newline => 1 *,
783             qq* ); *,
784             : join $join_up,
785             qq* $Dump_for( *,
786             qq* -caller_id => $caller_id, *,
787             qq* -prefix => q{ $_ was:}, *,
788             qq* -varref => [$_], *, # don't enref
789             qq* -no_newline => 1 *,
790             qq* ); *,
791             ;
792             }
793             _uniq extract_multiple($assertion, [\&extract_variable], undef, 1)
794             ## end of map expression
795             ;
796             ## end of assignment
797            
798             # Generate the test-and-report code...
799 21         237 my $report_code = join $join_up,
800             qq* unless($assertion) { *,
801             qq* $Warn_for( *,
802             qq* $caller_id, *, # $caller_id
803             qq* $frame, *, # $frame
804             qq* "\\n", *, # @text to print
805             qq* q{### $assertion was not true} *, # more @text
806             qq* ); *,
807             qq* @vardump_code_lines; *, # call Dump_for
808             qq* $signal_code *, # maybe die
809             qq* } *,
810             ;
811             ## end of assignment
812             #~ $DB::single=1; #~
813 21         498 return $report_code;
814             };
815             ######## /_decode_assert ########
816              
817             ######## REPLACEMENT CODE GENERATOR ########
818             #
819             # $codestring = _decode_for($for, $range, $mesg);
820             #
821             # Purpose : Generate progress-bar code for a Perlish for loop.
822             # Parms : $for :
823             # : $range :
824             # : $mesg :
825             # Reads : ____
826             # Returns : Replacement code string
827             # Writes : $ID
828             # Throws : never
829             # See also : for_progress()
830             #
831             sub _decode_for {
832 5     5   15 my $caller_id = shift;
833 5         20 my $for = shift;
834 5         16 my $range = shift;
835 5         17 my $mesg = shift;
836              
837             # Give the loop a unique ID...
838 5         10 $ID++;
839              
840             # Rewrite the loop with a progress bar as its first statement...
841 5         67 my $report_code = join qq{\n},
842             qq* my \$not_first__$ID; *,
843             qq* $for (my \@SmartComments__range__$ID = $range) { *,
844             qq* Devel::Comments::for_progress( $caller_id, *,
845             qq* qq{$mesg}, *,
846             qq* \$not_first__$ID, *,
847             qq* \\\@SmartComments__range__$ID *,
848             qq* ); *,
849             # closing brace found somewhere in client code
850             ;
851             ## end of assignment
852              
853             ### _decode_for code : $report_code
854 5         155 return $report_code;
855             };
856             ######## /_decode_for ########
857              
858             ######## REPLACEMENT CODE GENERATOR ########
859             #
860             # _decode_while($while, $mesg); # short
861             #
862             # Purpose : Generate progress-bar code for a Perlish while loop.
863             # Parms : $while :
864             # : $mesg :
865             # Reads : ____
866             # Returns : Replacement code string
867             # Writes : $ID
868             # Throws : ____
869             # See also : while_progress()
870             #
871             sub _decode_while {
872 3     3   7 my $caller_id = shift;
873 3         14 my $while = shift;
874 3         10 my $mesg = shift;
875              
876             # Give the loop a unique ID...
877 3         7 $ID++;
878              
879             # Rewrite the loop with a progress bar as its first statement...
880 3         37 my $report_code = join qq{\n},
881             qq* my \$not_first__$ID; *,
882             qq* $while { *,
883             qq* Devel::Comments::while_progress( $caller_id, *,
884             qq* qq{$mesg}, *,
885             qq* \\\$not_first__$ID *,
886             qq* ); *,
887             # closing brace found somewhere in client code
888             ;
889             ## end of assignment
890            
891             ### _decode_while code : $report_code
892 3         33 return $report_code;
893             };
894             ######## /_decode_while ########
895              
896             ######## INTERNAL ROUTINE ########
897             #
898             # _desc_time(); # short
899             #
900             # Purpose : ____
901             # Parms : ____
902             # Reads : ____
903             # Returns : ____
904             # Writes : ____
905             # Throws : ____
906             # See also : ____
907             #
908             # Generate approximate time descriptions...
909             #
910             sub _desc_time {
911 0     0   0 my ($seconds) = @_;
912 0         0 my $hours = int($seconds/3600); $seconds -= 3600*$hours;
  0         0  
913 0         0 my $minutes = int($seconds/60); $seconds -= 60*$minutes;
  0         0  
914 0         0 my $remaining;
915              
916             # Describe hours to the nearest half-hour (and say how close to it)...
917 0 0       0 if ($hours) {
    0          
    0          
918 0 0       0 $remaining =
    0          
    0          
    0          
    0          
919             $minutes < 5 ? "about $hours hour".($hours==1?"":"s")
920             : $minutes < 25 ? "less than $hours.5 hours"
921             : $minutes < 35 ? "about $hours.5 hours"
922             : $minutes < 55 ? "less than ".($hours+1)." hours"
923             : "about ".($hours+1)." hours";
924             }
925             # Describe minutes to the nearest minute
926             elsif ($minutes) {
927 0         0 $remaining = "about $minutes minutes";
928 0 0       0 chop $remaining if $minutes == 1;
929             }
930             # Describe tens of seconds to the nearest ten seconds...
931             elsif ($seconds > 10) {
932 0         0 $seconds = int(($seconds+5)/10);
933 0         0 $remaining = "about ${seconds}0 seconds";
934             }
935             # Never be more accurate than ten seconds...
936             else {
937 0         0 $remaining = "less than 10 seconds";
938             }
939 0         0 return $remaining;
940             };
941             ######## /_desc_time ########
942              
943             ######## INTERNAL ROUTINE ########
944             #
945             # _moving_average(); # short
946             #
947             # Purpose : ____
948             # Parms : ____
949             # Reads : ____
950             # Returns : ____
951             # Writes : ____
952             # Throws : ____
953             # See also : ____
954             #
955             # Update the moving average of a series given the newest measurement...
956             #
957             sub _moving_average {
958 29     29   40 my ($context, $next) = @_;
959 29   100     98 my $moving = $moving{$context} ||= [];
960 29         41 push @$moving, $next;
961 29 100       76 if (@$moving >= $average_over) {
962 4         9 splice @$moving, 0, $#$moving-$average_over;
963             }
964 29         179 return sum(@$moving)/@$moving;
965             };
966             ######## /_moving_average ########
967              
968             ######## INTERNAL ROUTINE ########
969             #
970             # _prog_pat(); # short
971             #
972             # Purpose : ____
973             # Parms : ____
974             # Reads : ____
975             # Returns : ____
976             # Writes : ____
977             # Throws : ____
978             # See also : ____
979             #
980             # Clean up components of progress bar (inserting defaults)...
981             #
982             sub _prog_pat {
983 334     334   484 for my $pat (@progress_pats) {
984 1590 100       13568 $_[0] =~ $pat or next;
985 334   50     3195 return ($1, $2||"", $3||"", $4||"");
      100        
      50        
986             }
987 0         0 return;
988             };
989             ######## /_prog_pat ########
990              
991             ######## EXTERNAL ROUTINE ########
992             #
993             # for_progress(); # short
994             #
995             # Purpose : ____
996             # Parms : ____
997             # Reads : ____
998             # Returns : ____
999             # Writes : $_[2] ($not_first__$ID in caller's code
1000             # Throws : ____
1001             # See also : _decode_for
1002             #
1003             # Animate the progress bar of a for loop...
1004             #
1005             sub for_progress {
1006             ### ...In for_progress...
1007            
1008 34     34 0 23671 my $caller_id = $_[0]; # per-use id of this caller
1009 34         56 my $mesg = $_[1]; #
1010 34         66 my $not_first = $_[2]; # will be altered so don't shift it off
1011 34         41 my $data = $_[3]; #
1012            
1013 34         40 my $at ; #
1014             my $max ; #
1015 0         0 my $elapsed ; #
1016 0         0 my $remaining ; #
1017 0         0 my $fraction ; #
1018            
1019             # Update progress bar...
1020 34 100       71 if ($not_first) {
1021             ### for_progress- if not first
1022             # One more iteration towards the maximum...
1023 29         64 $at = ++$count{$data};
1024 29         56 $max = $max{$data};
1025              
1026             # How long now (both absolute and relative)...
1027 29         56 $elapsed = time - $started{$data};
1028 29 50       75 $fraction = $max>0 ? $at/$max : 1;
1029              
1030             # How much change occurred...
1031 29         49 my $motion = $fraction - $prev_fraction{$data};
1032              
1033             # Don't update if count wrapped (unlikely) or if finished
1034             # or if no visible change...
1035 29 50 66     209 return unless $not_first < 0
      66        
1036             || $at == $max
1037             || $motion > $forupdatequantum;
1038              
1039             # Guestimate how long still to go...
1040 29 50       104 $remaining = _moving_average $data,
1041             $fraction ? $elapsed/$fraction-$elapsed
1042             : 0;
1043             }
1044            
1045             # If first iteration...
1046             else {
1047             ### for_progress- else first
1048             # Start at the beginning...
1049 5         28 $at = $count{$data} = 0;
1050              
1051             # Work out where the end will be...
1052 5         19 $max = $max{$data} = $#$data;
1053              
1054             # Start the clock...
1055 5         48 $started{$data} = time;
1056 5         11 $elapsed = 0;
1057 5         5 $fraction = 0;
1058              
1059             # After which, it will no longer be the first iteration.
1060 5         13 $_[2] = 1; # $not_first
1061             }
1062              
1063             # Remember the previous increment fraction...
1064 34         72 $prev_fraction{$data} = $fraction;
1065              
1066             # Now draw the progress bar (if it's a valid one)...
1067 34 50       72 if (my ($left, $fill, $leader, $right) = _prog_pat($mesg)) {
1068             # Insert the percentage progress in place of a '%'...
1069 34         132 s/%/int(100*$fraction).'%'/ge for ($left, $leader, $right);
  10         39  
1070              
1071             # Work out how much space is available for the bar itself...
1072 34         74 my $fillwidth = $showwidth - length($left) - length($right);
1073              
1074             # But no less than the prespecified minimum please...
1075 34 50       74 $fillwidth = $minfillwidth if $fillwidth < $minfillwidth;
1076              
1077             # Make enough filler...
1078 34         123 my $totalfill = $fill x $fillwidth;
1079              
1080             # How big is the end of the bar...
1081 34         42 my $leaderwidth = length($leader);
1082              
1083             # Truncate where?
1084 34 100       87 my $fillend = $at==$max ? $fillwidth
1085             : $fillwidth*$fraction-$leaderwidth;
1086 34 100       73 $fillend = 0 if $fillend < 0;
1087              
1088             # Now draw the bar, using carriage returns to overwrite it...
1089 34         210 Print_for( $caller_id,
1090             qq{\r},
1091             q{ } x $maxwidth,
1092             qq{\r},
1093             $left,
1094             sprintf("%-${fillwidth}s",
1095             substr($totalfill, 0, $fillend)
1096             . $leader),
1097             $right,
1098             );
1099              
1100             # Work out whether to show an ETA estimate...
1101 34 0 33     131 if (
      0        
      33        
1102             $elapsed >= $showstarttime
1103             && $at < $max
1104             && ($showing{$data} || $remaining && $remaining >= $showmaxtime)
1105             ) {
1106 0         0 Print_for( $caller_id,
1107             q{ (},
1108             _desc_time($remaining),
1109             q{ remaining)},
1110             );
1111 0         0 $showing{$data} = 1;
1112             }
1113              
1114             # Close off the line, if we're finished...
1115 34 100       140 Print_for( $caller_id,
1116             qq{\r},
1117             q{ } x $maxwidth,
1118             qq{\n},
1119             ) if $at >= $max;
1120             }
1121             };
1122             ######## /for_progress ########
1123              
1124             ######## EXTERNAL ROUTINE ########
1125             #
1126             # while_progress(); # short
1127             #
1128             # Purpose : ____
1129             # Parms : ____
1130             # Reads : ____
1131             # Returns : ____
1132             # Writes : ____
1133             # Throws : ____
1134             # See also : ____
1135             #
1136             # Animate the progress bar of a while loop...
1137             #
1138             sub while_progress {
1139 300     300 0 5832 my $caller_id = shift; # per-use id of this caller
1140 300         362 my $mesg = shift; #
1141 300         334 my $not_first_ref = shift; #
1142            
1143 300         284 my $at ; #
1144              
1145             # If we've looped this one before, recover the current iteration count...
1146 300 100       488 if ($$not_first_ref) {
1147 297         627 $at = ++$count{$not_first_ref};
1148             }
1149             # Otherwise set the iteration count to zero...
1150             else {
1151 3         10 $at = $count{$not_first_ref} = 0;
1152 3         8 $$not_first_ref = 1;
1153             }
1154              
1155             # Extract the components of the progress bar...
1156 300 50       556 if (my ($left, $fill, $leader, $right) = _prog_pat($mesg)) {
1157             # Replace any '%' with the current iteration count...
1158 300         1044 s/%/$at/ge for ($left, $leader, $right);
  200         673  
1159              
1160             # How much space is there for the progress bar?
1161 300         508 my $fillwidth = $showwidth - length($left) - length($right);
1162              
1163             # Make it at least the prespecified minimum amount...
1164 300 50       686 $fillwidth = $minfillwidth if $fillwidth < $minfillwidth;
1165              
1166             # How big is the end of the bar?
1167 300         321 my $leaderwidth = length($leader);
1168              
1169             # How big does that make the bar itself (use reciprocal growth)...
1170 300         624 my $length = int(($fillwidth-$leaderwidth)
1171             *(1-$whilerate/($whilerate+$at))+0.000000000001);
1172              
1173             # Don't update if the picture would look the same...
1174             return
1175 300 100 66     1652 if length $fill && $prev_length == $length;
1176              
1177             # Otherwise, remember where we got to...
1178 34         39 $prev_length = $length;
1179              
1180             # And print the bar...
1181 34         221 Print_for( $caller_id,
1182             qq{\r},
1183             q{ } x $maxwidth,
1184             qq{\r},
1185             $left,
1186             sprintf("%-${fillwidth}s",
1187             substr($fill x $fillwidth, 0, $length)
1188             . $leader),
1189             $right,
1190             );
1191             }
1192             };
1193             ######## /while_progress ########
1194              
1195             ######## EXTERNAL ROUTINE ########
1196             #
1197             # Print_for( $caller_id, @args ); # short
1198             #
1199             # Purpose : Print @args to caller's chosen $outfh
1200             # Parms : $caller_id : identify which caller
1201             # : $frame : we may be called directly or by proxy
1202             # : @args : any printable list
1203             # Reads : %state_of
1204             # Returns : 1
1205             # Writes : to $outfh
1206             # Throws : dies if print fails
1207             # See also : _get_new_caller_id(), Warn_for(), _decode_assert(), Dump()
1208             #
1209             # Call this only from within replacement code.
1210             # If called by another our-module routine, it will get the wrong stack frame.
1211             #
1212             sub Print_for {
1213 158     158 0 234 my $caller_id = shift;
1214 158         356 my $outfh = _get_outfh($caller_id); # get from %state_of
1215            
1216 158 50       272 print {$outfh} @_
  158         1032  
1217             or die q{Devel::Comments: } # print failure
1218             , q{Filesystem IO error: }
1219             , qq{Failed to print to output filehandle for $caller_id }
1220             , $!
1221             ;
1222            
1223 158         421 return 1;
1224             };
1225             ######## /Print_for ########
1226              
1227             ######## EXTERNAL ROUTINE ########
1228             #
1229             # Warn_for( $caller_id, $frame, @args ); # short
1230             #
1231             # Purpose : Print @args *and* $file, $line to caller's chosen $outfh
1232             # : as if it were warn().
1233             # Parms : $caller_id : identify which caller
1234             # : $frame : we may be called directly or by proxy
1235             # : @args : any printable list
1236             # Reads : %state_of
1237             # Returns : 1
1238             # Writes : to $outfh
1239             # Throws : dies if print fails
1240             # See also : _get_new_caller_id(), Print_for(), _decode_assert()
1241             #
1242             # This can be called from within replacement code or from S::C;
1243             # but either way, $frame must be passed in.
1244             sub Warn_for {
1245 14     14 0 29585 my $caller_id = shift;
1246 14         25 my $frame = shift;
1247            
1248             ### In Warn_for():
1249             ### $caller_id
1250             ### $frame
1251            
1252 14         90 my @caller = caller($frame);
1253             ### @caller
1254            
1255             # my $caller_name = $caller[0];
1256 14         31 my $caller_file = $caller[1];
1257 14         31 my $caller_line = $caller[2];
1258            
1259 14         78 Print_for( $caller_id, @_, " at $caller_file line $caller_line.\n" );
1260 14         48 return 1;
1261             };
1262             ######## /Warn_for ########
1263              
1264             ######## INTERNAL ROUTINE ########
1265             #
1266             # _put_state( $caller_id, @caller ); # short
1267             #
1268             # Purpose : Store current state info
1269             # Parms : $caller_id : to put %state_of previous state
1270             # : @caller : current state (maybe)
1271             # Reads : %state_of
1272             # Returns : 1
1273             # Writes : %state_of
1274             # Throws : dies if called with unknown caller
1275             # See also : _spacer_required(), Dump_for()
1276             #
1277             # This stores not $outfh itself
1278             # but the initial state of output to it, sort of.
1279             #
1280             sub _put_state {
1281 49     49   83 my $caller_id = shift;
1282 49         127 my @caller = @_;
1283 49         121 my $caller_name = $caller[0];
1284 49         73 my $caller_file = $caller[1];
1285 49         66 my $caller_line = $caller[2];
1286            
1287 49 50       150 die "Devel::Comments: Fatal Error (_put_state): ",
1288             "No state_of $caller_id.",
1289             $! if ( !defined $state_of{$caller_id} );
1290            
1291 49         141 my $outfh = _get_outfh($caller_id);
1292            
1293 49         186 $state_of{$caller_id}{-tell}{-outfh} = tell $outfh;
1294 49         148 $state_of{$caller_id}{-tell}{-stdout} = tell (*STDOUT);
1295 49         173 $state_of{$caller_id}{-caller}{-file} = $caller_file;
1296 49         118 $state_of{$caller_id}{-caller}{-line} = $caller_line;
1297            
1298 49         147 return 1;
1299            
1300             };
1301             ######## /_put_state ########
1302              
1303             ######## INTERNAL ROUTINE ########
1304             #
1305             # $flag = _spacer_required( $caller_id, @caller ); # newline before?
1306             #
1307             # Purpose : Ensure the smart output starts flush left.
1308             # Parms : $caller_id : key %state_of for previous state
1309             # : @caller : current state (maybe)
1310             # Reads : %state_of
1311             # Returns : Boolean: TRUE to prepend a newline to output
1312             # Writes : ____
1313             # Throws : ____
1314             # See also : Dump_for(), %state_of
1315             #
1316             # Vanilla S::C compared both previous tell()-s of STDOUT and STDERR
1317             # before deciding to print a prophylactic newline, even though Vanilla
1318             # only ever printed to STDERR. One might assume Conway does this
1319             # on *his* assumption that both are connected to the same output device,
1320             # namely a terminal window or console.
1321             # This may or may not be wise but we preserve the exact Vanilla behavior;
1322             # while output to disk files contains fewer newlines.
1323             # Since we make no explicit check of which or what kind of filehandle,
1324             # I cannot explain why this is so.
1325             # The missing newlines are not going to STDOUT, STDERR, or the screen anyway.
1326             #
1327             # TODO: Vanilla outputs a gratuitous newline
1328             # if $caller_line has changed by more than one line.
1329             # This may result in rather "loose" output.
1330             # TODO: Accept a "tighten" arg in use line.
1331             #
1332             sub _spacer_required {
1333 28     28   45 my $caller_id = shift;
1334 28         66 my @caller = @_;
1335 28         52 my $caller_name = $caller[0];
1336 28         46 my $caller_file = $caller[1];
1337 28         35 my $caller_line = $caller[2];
1338            
1339 28         59 my $outfh = _get_outfh($caller_id); # retrieve from %state_of
1340            
1341             #say '$outfh: ', $outfh;
1342 28         104 my $prev_tell_outfh = $state_of{$caller_id}{-tell}{-outfh};
1343 28         77 my $prev_tell_stdout = $state_of{$caller_id}{-tell}{-stdout};
1344 28         90 my $prev_caller_file = $state_of{$caller_id}{-caller}{-file};
1345 28         486 my $prev_caller_line = $state_of{$caller_id}{-caller}{-line};
1346            
1347 28         36 my $flag ;
1348            
1349             # This test is *not* needed, oddly enough!
1350             # Intent was to preserve Vanilla behavior by requiring newline
1351             # if tell STDOUT had changed when printing to STDERR.
1352             # But with this paragraph disabled, Vanilla is preserved
1353             # and also 'use Devel::Comments *STDOUT' yields the same output.
1354             # Yet when given a hard disk $fh, fewer gratuitous newlines are output,
1355             # which is desired.
1356             # I cannot figure out why. Let us consider this a blessing.
1357             #
1358             # # You might not think you can compare filehandles, but you can...
1359             # # ... but only if they're identical, not if they're equivalent...
1360             # # ... *STDERR ne \*STDERR # although most io routines will accept either
1361             # if ( $outfh eq *STDERR ) { # STDERR chosen, vanilla behavior
1362             # # newline if STDOUT has been printed to since last smart output
1363             # $flag ||= $prev_tell_stdout != tell(*STDOUT);
1364             #say 'I Vanillaed.';
1365             # };
1366            
1367             # newline if $outfh has been printed to
1368 28   66     178 $flag ||= $prev_tell_outfh != tell $outfh;
1369            
1370             # newline if $caller_file has changed (???)
1371 28   66     124 $flag ||= $prev_caller_file ne $caller_file;
1372            
1373             # TODO: if $tighten do not...
1374             # newline if $caller_line has changed by more or less than 1
1375 28   100     130 $flag ||= $prev_caller_line != $caller_line -1;
1376            
1377             #~ say 'Doing the newline.' if $flag; #~
1378             #~ return 0; # never do the newline #~
1379 28         129 return $flag;
1380             };
1381             ######## /_spacer_required ########
1382              
1383             ######## EXTERNAL ROUTINE ########
1384             #
1385             # Dump_for(); # short
1386             #
1387             # Purpose : Dump a variable (any variable?)
1388             # Parms : flat list (assigned to hash)
1389             # Reads : ____
1390             # Returns : ____
1391             # Writes : ____
1392             # Throws : ____
1393             # See also : Data::Dumper, FILTER # Any other smart comment is a simple dump
1394             #
1395             # Dump a variable and then reformat the resulting string more prettily...
1396             #
1397             sub Dump_for {
1398            
1399 49     49 0 212218 my %hash = @_;
1400 49 50       251 my $caller_id = $hash{-caller_id}
1401             or die q{Devel::Comments: } # called with no -caller_id
1402             , q{Replacement code error: }
1403             , q{-caller_id not passed in call to Dump(). }
1404             , $!
1405             ;
1406            
1407 49         116 my $prefix = $hash{-prefix};
1408             #~ my $exists_varref = exists $hash{-varref}; # save test #~
1409 49         121 my $defined_varref = defined $hash{-varref}; # save test
1410 49         99 my $varref = $hash{-varref};
1411 49         102 my $no_newline = $hash{-no_newline};
1412            
1413 49         202 my @caller = caller; # called by replacement code
1414             # my $caller_name = $caller[0];
1415 49         97 my $caller_file = $caller[1];
1416 49         69 my $caller_line = $caller[2];
1417 49         134 my $outfh = _get_outfh($caller_id); # retrieve from %state_of
1418              
1419 49         84 my $spacer_required ; # TRUE to prepend a newline to output
1420            
1421             #~ say $outfh '... Entering Dump_for() ...'; #~
1422             #~ ### ... Entering Dump_for() #~
1423             #~ ### %state_of #~
1424            
1425             # Handle timestamps...
1426 49         111 $prefix =~ s/<(?:now|time|when)>/scalar localtime()/ge;
  0         0  
1427 49         103 $prefix =~ s/<(?:here|place|where)>/"$caller_file", line $caller_line/g;
1428              
1429             # Add a newline?
1430 49 100       124 if ($no_newline) {
1431 21         35 $spacer_required = 0;
1432             }
1433             else {
1434 28         238 $spacer_required = _spacer_required( $caller_id, @caller );
1435             };
1436             #~ ### $spacer_required #~
1437              
1438             #~ print $outfh 'defined_varref: ', $defined_varref, ' '; #~
1439             # Handle a prefix with no actual variable...
1440 49 100 66     335 if ($prefix && !$defined_varref) {
1441 14         31 $prefix =~ s/:$//;
1442 14 100       58 Print_for( $caller_id, "\n" ) if $spacer_required;
1443 14         53 Print_for( $caller_id, "### $prefix\n" );
1444 14         41 _put_state( $caller_id, @caller );
1445 14         68 return 1; # ...abort if not defined $varref
1446             }
1447            
1448             # or continue...
1449            
1450             # Set Data::Dumper up for a tidy dump and do the dump...
1451 35         66 local $Data::Dumper::Quotekeys = 0;
1452 35         64 local $Data::Dumper::Sortkeys = 1;
1453 35         50 local $Data::Dumper::Indent = 2;
1454 35         162 my $dumped = Dumper $varref;
1455              
1456             # Clean up the results...
1457            
1458             #~ say $outfh q{}; #~
1459             #~ say $outfh q{-----}; #~
1460             #~ my $gotstuff = join '', @{$varref}; #~
1461             #~ say $outfh '@{varref}: >' . $gotstuff . '<'; #~
1462             #~ say $outfh 'dumped before: >' . $dumped . '<'; #~
1463             #~ say $outfh 'exists: (', $exists_varref, ')'; #~
1464            
1465             # report 'null' for "return;" -- see RT#69712
1466 35         2952 $dumped =~ s/\$VAR1 = \[];\n/\$VAR1 = \[\n null\n];\n/;
1467            
1468 35         135 $dumped =~ s/\$VAR1 = \[\n//;
1469 35         391 $dumped =~ s/\s*\];\s*$//;
1470             #~ my $len_1; #~
1471             #~ $len_1 = length $1; #~
1472             #~ say $outfh '$1: ', $1, 'length: ', $len_1; #~
1473 35         145 $dumped =~ s/\A(\s*)//;
1474             #~ $len_1 = length $1; #~
1475             #~ say $outfh '$1: ', $1, 'length: ', $len_1; #~
1476             #~
1477             #~ say $outfh 'dumped after: >' . $dumped . '<'; #~
1478              
1479             # How much to shave off and put back on each line...
1480 35         99 my $indent = length $1;
1481 35         106 my $outdent = q{ } x (length($prefix) + 1);
1482             #~ say $outfh 'indent: ', $indent, ' outdent: >', $outdent, '<'; #~
1483             # Report "inside-out" and "flyweight" objects more cleanly...
1484 35         65 $dumped =~ s{bless[(] do[{]\\[(]my \$o = undef[)][}], '([^']+)' [)]}
1485             {}g;
1486              
1487             # Adjust the indents...
1488 35         485 $dumped =~ s/^[ ]{$indent}([ ]*)/### $outdent$1/gm;
1489             #~ say $outfh 'dumped later: >' . $dumped . '<'; #~
1490              
1491             # Print the message...
1492 35 100       154 Print_for( $caller_id, "\n" ) if $spacer_required;
1493 35         134 Print_for( $caller_id, "### $prefix $dumped\n" );
1494 35         107 _put_state( $caller_id, @caller );
1495              
1496 35         197 return 1;
1497             };
1498             ######## /Dump_for ########
1499              
1500             #~ say '---| Devel::Comments at line ', __LINE__; #~
1501              
1502             #############################
1503             ######## END MODULE #########
1504             1;
1505             __END__