Skip to content

Commit ce98e0e

Browse files
committed
Capturing STDOUT&ERR, improved debug output
1 parent f193acc commit ce98e0e

File tree

1 file changed

+52
-48
lines changed

1 file changed

+52
-48
lines changed

web/regex_tester.html

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@
6565
float: right;
6666
cursor: pointer;
6767
}
68-
.re_warns {
68+
.re_output {
6969
clear: both;
70+
background-color: rgba(234,234,234,255);
71+
}
72+
.re_warns {
7073
background-color: rgba(255,255,200,255);
7174
}
7275
.re_errors {
@@ -100,7 +103,6 @@
100103
use warnings;
101104
use 5.028;
102105
use WebPerl qw/js/;
103-
use File::Temp qw/tempfile/;
104106
use Data::Dumper ();
105107
my $jq = js('jQuery');
106108

@@ -111,14 +113,33 @@
111113

112114
my $run_code_body = <<'END_CODE';
113115
my (@warns,@output);
114-
#TODO Later: Capture STDOUT too? (and STDERR, instead of $SIG{__WARN__})?
116+
117+
require File::Temp;
118+
my ($fh1,$fn1) = File::Temp::tempfile();
119+
open my $oldout, '>&', \*STDOUT or die "dup STDOUT: $!";
120+
open STDOUT, '>&', $fh1 or die "open STDOUT: $!";
121+
my ($fh2,$fn2) = File::Temp::tempfile();
122+
open my $olderr, '>&', \*STDERR or die "dup STDERR: $!";
123+
open STDERR, '>&', $fh2 or die "open STDERR: $!";
124+
115125
my $ok = do {
116126
local $SIG{__WARN__} = sub { push @warns, shift };
117127
eval "package RunCode {$code\n};1" };
118128
my $err = $ok ? undef : $@||"Unknown error";
129+
130+
open STDERR, '>&', $olderr or die "dup \$olderr: $!";
131+
close $fh2;
132+
open STDOUT, '>&', $oldout or die "dup \$oldout: $!";
133+
close $fh1;
134+
my $stdout = do { open my $fh, '<', $fn1 or die $!; local $/; <$fh> };
135+
my $stderr = do { open my $fh, '<', $fn2 or die $!; local $/; <$fh> };
136+
unlink($fn1,$fn2)==2 or warn "unlink('$fn1','$fn2'): $!";
137+
119138
defined && s/\bat .+? line \d+(?:\.$|,\h)//mg for (@warns,$err);
120139
chomp(@warns);
121-
my $rv = { ctx=>$context, warns=>\@warns, $ok ? (out=>\@output) : (err=>$err) };
140+
my $rv = { ctx=>$context, warns=>\@warns,
141+
$ok ? (out=>\@output) : (err=>$err),
142+
stdout => $stdout, stderr => $stderr };
122143
END_CODE
123144

124145
my $runcode_iframe;
@@ -200,6 +221,7 @@
200221
my $closebtn = $jq->('<div/>', {html=>"&#x274E;",class=>"closebtn",
201222
title=>"Delete Sample"});
202223
$closebtn->appendTo($samp);
224+
$jq->('<pre/>', {class=>'re_output'})->appendTo($samp);
203225
$jq->('<pre/>', {class=>'re_warns'})->appendTo($samp);
204226
$jq->('<pre/>', {class=>'re_errors'})->appendTo($samp);
205227
$samp->click(sub {
@@ -239,34 +261,7 @@
239261
# $re_debug is actually a parameter to update()/actual_update(), but since
240262
# we register &update as a event handler, it'll get passed varying parameters
241263
our $re_debug=0;
242-
my $ta_debugout = $jq->('#debugout');
243-
my $re_debug_hide = $jq->('#re_debug_hide');
244-
$jq->('#re_debug')->click(sub {
245-
#TODO: re_debug no longer works in IFrame! (ugly "workaround" below)
246-
# Not only this capturing, but also $re_debug is unset by the time the callback gets called
247-
open my $olderr, '>&', \*STDERR or die "dup STDERR: $!";
248-
my ($fh,$fn) = tempfile();
249-
open STDERR, '>&', $fh or die "open STDERR: $!";
250-
251-
{ local $re_debug=1; update(); }
252-
253-
open STDERR, '>&', $olderr or die "dup \$olderr: $!";
254-
close $fh;
255-
my $err = do { open my $fh, '<', $fn or die $!; local $/; <$fh> };
256-
unlink($fn)==1 or warn "unlink $fn: $!";
257-
258-
$err .= "\n### Please see the JavaScript console! ###\n"
259-
if $RUN_CODE_IN_IFRAME;
260-
$ta_debugout->val($err);
261-
$ta_debugout->show;
262-
$re_debug_hide->show;
263-
});
264-
$re_debug_hide->click(sub{
265-
$re_debug_hide->hide;
266-
$ta_debugout->hide;
267-
});
268-
$re_debug_hide->hide;
269-
$ta_debugout->hide;
264+
$jq->('#re_debug')->click(sub { local $re_debug=1; update() });
270265

271266
my $sampcodebtn = $jq->('#sampcodebtn');
272267
my $codecopy = $jq->('#codecopy');
@@ -358,13 +353,14 @@
358353
my $precode = $precode_ta->is(':visible') ? $precode_ta->val : '';
359354
$precode .= "\n" if length $precode && substr($precode,-1) ne "\n";
360355
my $regex_str = 'm{'.$regex.'}'.$flags;
361-
$re_debug and !length($regex) and say STDERR
362-
"NOTE: The empty regex $regex_str requires a workaround with /(?:)/," # https://www.perlmonks.org/?node_id=1221517
363-
."\n this will be reflected in the debug output";
364356

365357
my $warn = '';
366-
$warn .= "Notice: The empty pattern has special behavior, see perlop!\n"
367-
." Here, a workaround is used so it acts as a true empty pattern.\n" unless length $regex;
358+
if (not length $regex) {
359+
$warn .= "Notice: The empty pattern has special behavior, see perlop!\n"
360+
." Here, a workaround is used so it acts as a true empty pattern.\n";
361+
if ($re_debug) # https://www.perlmonks.org/?node_id=1221517
362+
{ $warn .= " The workaround uses /(?:)/, which you will see in the debug output.\n" }
363+
}
368364
$warn .= "\\n is recommended over literal newlines\n" if $regex=~/\n/ && $flags!~/x/;
369365
$warn .= "\\t is recommended over literal tabs\n" if $regex=~/\t/ && $flags!~/x/;
370366
state $warnmsgs = $jq->("#warnmsgs");
@@ -375,10 +371,15 @@
375371
my $samptxt = $sample->children('.samptxt');
376372
my $text = $samptxt->text;
377373
push @samps, $text; # for use below
378-
my $code = $precode . ($re_debug?'use re "debug";'
379-
. 'say STDERR '.pp('##### ##### ##### '.pp($text).' =~ '.pp($regex_str).' ##### ##### #####').';':'')
380-
. (length($regex)?'':"''=~/(?:)/$flags;")
381-
. 'push @output,[[@-],[@+]] ' . ($flags=~/g/?'while':'if') . ' $input=~'.$regex_str;
374+
my $code = $precode . ( $re_debug ? "use re \"debug\";\n" : '' )
375+
. ( length($regex) ? '' : "''=~/(?:)/$flags; # // workaround\n" )
376+
. 'push @output,[[@-],[@+]] ' . ($flags=~/g/?'while':'if') . ' $input=~'.$regex_str.";\n";
377+
$re_debug and
378+
$code = 'BEGIN{require Data::Dumper;'
379+
.'print(STDERR Data::Dumper->new([$input],["input"])->Indent(0)->Dump,'
380+
.'"\n-- Code --\n",' . pp($code) . ',"----\n")}' . "\n"
381+
. $code . "\n"
382+
. q{print STDERR "----\n",Data::Dumper->new([\@output],["*output"])->Indent(0)->Dump;};
382383
run_code($sample->attr('id'), $code, $text, \&run_code_callback);
383384
}
384385

@@ -449,7 +450,6 @@
449450
if ( $rv->{out} && $rv->{out}->@* ) {
450451
$samptxt->removeClass('nomatch');
451452
my %hi;
452-
$re_debug and say STDERR '@-/@+ are ',pp($rv->{out});
453453
for my $i (0..$#{$rv->{out}}) {
454454
my ($s,$e) = $rv->{out}[$i]->@*;
455455
for my $j (0..$#$e) { # Use @+ to count all capture groups instead of @-!
@@ -465,7 +465,6 @@
465465
}
466466
}
467467
}
468-
$re_debug and say STDERR 'highlights are ',pp(\%hi);
469468
my $html='';
470469
my $p=0;
471470
my (%active_match,%active_caps);
@@ -509,7 +508,16 @@
509508
{ $errs = $rv->{err} }
510509
$samptxt->text($text);
511510
}
511+
my $stdoe = '';
512+
for my $s (qw/stdout stderr/) {
513+
next unless length $rv->{$s} && $rv->{$s}=~/\S/;
514+
$rv->{$s} =~ s/\A\n+|\n+\z//g;
515+
$stdoe .= "### ".uc($s)." ###\n".$rv->{$s}."\n";
516+
}
517+
$sample->children('.re_output')->text($stdoe);
518+
unshift @{ $rv->{warns} }, "### Warnings ###" if $rv->{warns}->@*;
512519
$sample->children('.re_warns')->text( join "\n", $rv->{warns}->@* );
520+
$errs = "### Errors ###\n$errs" if $errs=~/\S/;
513521
$sample->children('.re_errors')->text($errs);
514522
}
515523

@@ -586,11 +594,7 @@
586594
</div>
587595

588596
<div style="margin-top:0.5em">
589-
<div>
590-
<button id="re_debug"><tt>use re "debug";</tt></button>
591-
<button id="re_debug_hide">Hide</button>
592-
</div>
593-
<textarea id="debugout" rows="25" cols="80" readonly="readonly" style="display:none"></textarea>
597+
<button id="re_debug"><tt>use re "debug";</tt></button>
594598
</div>
595599

596600
<div style="margin-top:0.5em">

0 commit comments

Comments
 (0)