In my last post I mentioned my attempt to provide syntax highlighting for the Perl debugger. It works and I think it's useful, but I did that by hacking directly on the debugger. Every version of Perl gets a slightly newer version of perl5db.pl (the debugger) shipped with it and hacking on a particular version doesn't help¹. Thus, I need to write a separate module which encapsulates my hack and (relatively) cleanly alters how the debugger operates.

I could do something stupid like try to override print or something like that, but it's fragile. Instead, I only want to highlight Perl code and I can do that by only highlighting the code in @DB::dbline . That's because if you know the debugger, you know this little tidbit:

The array @{$main::{'_<'.$filename}} (aliased locally to @dbline via glob assignment) contains the text from $filename , with each element corresponding to a single line of $filename .

At this point, you may be wondering "what the hell is $main::{'_<'.$filename} ?" Basically, the *::main{"_<$filename"} typeglob contains a lot of special information useful for the debugger. The array, scalar and hash slots are where the debugger has a lot of magic. Regrettably, this magic typeglob comes straight from the Perl internals and because of this, I have a problem.

My approach to solving this was to set up all of the highlighting code and simply tieing the array in question. This attempt at 'cleanly' highlighting debugger output looked very similar to this:

#!/usr/bin/env perl use strict; use warnings; use Term::ANSIColor ':constants'; use Syntax::Highlight::Engine::Kate::Perl; no warnings 'redefine'; *Syntax::Highlight::Engine::Kate::Template::logwarning = sub { }; my $highlighter = Syntax::Highlight::Engine::Kate::Perl->new( format_table => { 'Keyword' => [ GREEN, RESET ], 'Comment' => [ BLUE, RESET ], 'Decimal' => [ YELLOW, RESET ], 'Float' => [ YELLOW, RESET ], 'Function' => [ CYAN, RESET ], 'Identifier' => [ RED, RESET ], 'Normal' => [ MAGENTA, RESET ], 'Operator' => [ CYAN, RESET ], 'Preprocessor' => [ RED, RESET ], 'String' => [ RED, RESET ], 'String Char' => [ RED, RESET ], 'Symbol' => [ CYAN, RESET ], } ); { package Devel::DB::Syntax; use Tie::Array; @Devel::DB::Syntax::ISA = 'Tie::StdArray'; sub FETCH { my ( $self, $index ) = @_; return $highlighter->highlightText( $self->[$index] ); } } tie @::target => 'Devel::DB::Syntax'; @::code = split "

" => <<'END'; for my $var ( 1 .. 10 ) { print $var, $/; } END { local *::target = \@::code; print $::target[$_], $/ for 0 .. $#::target; }

In other words, I create the syntax highlighter and I tie an array so that every time you fetch something from that array, we try to highlight it (naturally, the debugger makes this harder than this simple example, but this is the core of it). However, the above code doesn't highlight anything. Note the local *::target line. That's conceptually similar to what the debugger is doing:

sub DB { # skip a bunch of lines local ( $package, $filename, $line ) = caller; # skip a bunch more lines local (*dbline) = $main::{ '_<' . $filename }; # skip a couple of thousand more lines }

So we're localizing what I need to tie. Ah, but what if I could tie the right hand side? As it turns out, you can do this for my sample highlighting code above:

# don't tie the target #tie @::target => 'Devel::DB::Syntax'; # tie the source tie @::code => 'Devel::DB::Syntax';

If you do that, the tie survives across the aliasing, as one would expect. However, because the source variable in this case is a magic wünder variable from the Perl internals, tieing it breaks things horribly (no code is emitted at all in 5.8.9).

So there's my dilemma. How can I successfully tie a package variable which is going to get localized? There are further problems after that, but this is the big one.