File Coverage

blib/lib/DB/Color.pm
Criterion Covered Total %
statement 63 88 71.5
branch 4 22 18.1
condition 2 15 13.3
subroutine 20 22 90.9
pod 0 3 0.0
total 89 150 59.3


line stmt bran cond sub pod time code
1             package DB::Color;
2              
3 1     1   20554 use 5.008;
  1         4  
4 1     1   5 use strict;
  1         2  
  1         28  
5 1     1   5 use warnings;
  1         5  
  1         35  
6 1     1   509 use DB::Color::Highlight;
  1         3  
  1         31  
7 1     1   675 use DB::Color::Config;
  1         3  
  1         29  
8              
9 1     1   1017 use IO::Handle;
  1         7391  
  1         56  
10 1     1   5 use File::Spec::Functions qw(catfile catdir);
  1         2  
  1         88  
11 1     1   5 use Scalar::Util 'dualvar';
  1         2  
  1         87  
12 1     1   5 use File::Find;
  1         1  
  1         248  
13              
14             =head1 NAME
15              
16             DB::Color - Colorize your debugger output
17              
18             =head1 VERSION
19              
20             Version 0.20
21              
22             =cut
23              
24             our $VERSION = '0.20';
25              
26             =head1 SYNOPSIS
27              
28             Put the following in your F<$HOME/.perldb> file:
29              
30             use DB::Color;
31              
32             Then use your debugger like normal:
33              
34             perl -d some_file.pl
35              
36             If you don't want a F<$HOME/.perldb> file, you can do this:
37              
38             perl -MDB::Color -d some_file.pl
39              
40             =head1 DISABLING COLOR
41              
42             If the NO_DB_COLOR environment variable is set to a true value, syntax
43             highlighting will be disabled.
44              
45             =head1 WINDOWS
46              
47             No, sorry. It's a combination of bad Windows support for ANSI escape sequences
48             and bad debugger design.
49              
50             =head1 PERFORMANCE
51              
52             When using the debugger and when you step into something, or continue to a
53             breakpoint in a new file, the debugger may appear to hang for a moment
54             (perhaps a long moment if the file is big) while the file is syntax
55             highlighted and cached. The next time the debugger enters this file, the
56             highlighting should be instantaneous.
57              
58             You can speed up the debugger by using the L program which is
59             included in this distribution. It will pregenerate syntax files for you.
60              
61             Syntax highlighting the code is very slow. As a result, we cache the output
62             files in F<$HOME/.perldbcolor>. This is done by calculating the md5 sum of the
63             file contents. If the file is changed, we get a new sum. This means that
64             syntax highlighting is very slow at first, but every time you hit the same
65             file, assuming its unchanged, the cached version is served first.
66              
67             Note that the cache files are removed after they become 30 (but see config)
68             days old without being used. If you use the debugger regularly, commonly
69             debugged files will load very quickly (assuming they haven't changed).
70              
71             =head1 CONDITIONAL LOADING
72              
73             If you prefer, you may only want to have I of your projects "colorized".
74             If so, you can do something like this:
75              
76             use DB::Color sentinel => '.colorize';
77              
78             If an if the C<.colorize> sentinel (or whatever you named it) does not exist,
79             C will not be used.
80              
81             =head1 WORKFLOW
82              
83             To use C effectively, I recommend the following:
84              
85             $ cpanm DB::Color
86             $ echo "use DB::Color sentinel => '.colorize'" >> ~/.perldb
87             # cd to project you want to colorize and create the sentinel
88             $ touch .colorize
89             # colorize the project. This will likely take a long time
90             $ PERL5LIB=lib:t/tests perldbsyntax
91              
92             At that point, you're almost good to go. However, as you're rapidly changing
93             files, the debugger will still probably be very slow. Instead, create a
94             watcher to watch your project directories and rehighlight any files which have
95             been created or modified. An example of a watcher program is the
96             F program included with this distribution.
97              
98             =head1 CONFIGURATION
99              
100             You can optionally configure C by creating a
101             F<$HOME/.perldbcolorrc> configuration file. It looks like this:
102              
103             [core]
104            
105             # the class that will highlight the code
106             highlighter = DB::Color::Highlight
107            
108             # Any cache file not accessed after this number of days is purged
109             cache_max_age = 30
110            
111             # where to put the cache dir
112             cache_dir = /users/ovid/.perldbcolor
113            
114             The above values are more or less the defaults for this module. They are all
115             optional.
116              
117             =head1 ALPHA
118              
119             This is only a proof of concept. In fact, it's fair to say that this code
120             sucks. It's not very configurable and has bugs. It's also going to possibly be
121             a memory hog, as if the debugger wasn't bad enough already.
122              
123             =cut
124              
125             my $config = DB::Color::Config->read( default_rcfile() );
126              
127             my %COLORED;
128             my $DB_BASE_DIR = $config->{core}{cache_dir} || default_base_dir();
129              
130             my $DB_LOG = catfile( $DB_BASE_DIR, 'debug.log' );
131             my $CACHE_MAX_AGE = $config->{core}{cache_max_age} || 30;
132             my $DEBUG;
133              
134             # Not documenting this because I don't guarantee stability, but you can play
135             # with it if you want.
136             if ( $ENV{DB_COLOR_DEBUG} ) {
137             open $DEBUG, '>>', $DB_LOG
138             or die "Cannot open $DB_LOG for appending: $!";
139             $DEBUG->autoflush(1);
140             }
141              
142             my $HIGHLIGHTER_CLASS = $config->{core}{highlighter} || 'DB::Color::Highlight';
143 1     1   6 eval "use $HIGHLIGHTER_CLASS";
  1         2  
  1         28  
144             die $@ if $@;
145              
146             my $HIGHLIGHTER = $HIGHLIGHTER_CLASS->new(
147             {
148             cache_dir => $DB_BASE_DIR,
149             debug_fh => $DEBUG,
150             }
151             );
152              
153             sub DB::afterinit {
154 1     1   5 no warnings 'once';
  1         2  
  1         326  
155 0 0   0 0 0 push @DB::typeahead => "{{v"
156             unless $DB::already_curly_curly_v++;
157             }
158              
159 1     1 0 12 sub default_rcfile { catfile( $ENV{HOME}, '.perldbcolorrc' ) }
160 1     1 0 8 sub default_base_dir { catfile( $ENV{HOME}, '.perldbcolor' ) }
161              
162             sub import {
163 1     1   13 my ( $package, %arg_for ) = @_;
164 1         2 my $sentinel = $arg_for{sentinel};
165              
166 1 50 33     5 if ( defined $sentinel && !-e $sentinel ) {
167 0         0 warn "DB::Color not running because '$sentinel' was requested, but not found\n";
168 0         0 return;
169             }
170 1 50       4 return if $ENV{NO_DB_COLOR};
171 1 50       4 if ( 'MSWin32' eq $^O ) {
172 0         0 warn <<"END";
173             DB::Color does not run under Windows because the Windows terminal is too
174             broken to understand terminal color code.
175              
176             DB::Color does not use Win32::Console because the debugger is too broken to be
177             properly extensible.
178             END
179 0         0 return;
180             }
181 1         7 my $old_db = \&DB::DB;
182              
183             my $new_DB = sub {
184 0     0   0 my $lvl = 0;
185 0         0 while ( my ($pkg) = caller( $lvl++ ) ) {
186 0 0 0     0 return if $pkg eq "DB" or $pkg =~ /^DB::/;
187             }
188 0         0 my ( $package, $filename ) = caller;
189 0 0       0 if ($DEBUG) {
190 0         0 print $DEBUG "In package '$package', filename '$filename'\n";
191             }
192              
193             # syntax highlight everything and cache it
194 0   0     0 my $lines = $COLORED{$filename} ||= do {
195 1     1   6 no strict 'refs';
  1         1  
  1         33  
196 1     1   5 no warnings 'uninitialized';
  1         1  
  1         95  
197             [
198             split /(?<=\n)/ =>
199 0         0 $HIGHLIGHTER->highlight_text( join "" => @{"::_<$filename"} )
  0         0  
200             ];
201             };
202              
203             {
204              
205             # lie to the debugger about what the lines of code are
206 1     1   5 no strict 'refs';
  1         2  
  1         83  
  0         0  
207 0         0 my $line_num = 0;
208 0         0 foreach ( @{"::_<$filename"} ) {
  0         0  
209              
210             # uncomment these to blow your f'in mind
211             #if ( not defined ) {
212             # use Devel::Peek;
213             # warn "line number is $line_num";
214             # Dump($_);
215             #}
216             # The debugger special cases the first value in ::_<$filename.
217             # It's "undef" but sometimes contains some data about the
218             # program. I don't know entirely what it is, but this solves
219             # the "off by one" bug.
220 0 0       0 next unless defined; # thanks Liz! (why does this work?)
221 0         0 my $line = $lines->[ $line_num++ ];
222 0 0       0 next unless defined $line; # happens when $_ = "\n"
223              
224             # XXX Cheap hack to fix
225             # Argument "{\n" isn't numeric in addition (+) at DB/Color.pm line 189.
226 1     1   4 no warnings 'numeric';
  1         2  
  1         110  
227 0         0 my $numeric_value = 0 + $_;
228              
229             # Internally, the debugger uses dualvars for each line of
230             # code. If it's numeric value is 0, then the line is not
231             # breakable. If we don't include this, no lines in the
232             # debugger are breakable.
233 0         0 $_ = dualvar $numeric_value, $line;
234             }
235             }
236 0         0 goto $old_db;
237 1         7 };
238              
239             {
240 1     1   4 no warnings 'redefine';
  1         2  
  1         233  
  1         2  
241 1         3 *DB::DB = $new_DB;
242             }
243              
244 1         13 return;
245             }
246              
247             END {
248             find(
249             sub {
250              
251             # delete empty files or files > $CACHE_MAX_AGE days old
252 1 0 0     116 if ( -f $_ && ( -z _ || -M _ > $CACHE_MAX_AGE ) ) {
      33        
253 0 0       0 unlink($_) or die "Could not unlink '$File::Find::name': $!";
254             }
255             },
256 1     1   309 $DB_BASE_DIR,
257             );
258             # we're not testing for failure as this is a cheap hack to delete empty
259             # directories
260 1 50       101 finddepth( sub { rmdir $_ if -d }, $DB_BASE_DIR );
  1         41  
261             }
262              
263             1;
264              
265             =head1 AUTHOR
266              
267             Curtis "Ovid" Poe, C<< >>
268              
269             =head1 BUGS
270              
271             Please report any bugs or feature requests through the web interface at
272             L. I will be notified, and then
273             you'll automatically be notified of progress on your bug as I make changes.
274              
275             =head1 SUPPORT
276              
277             You can find documentation for this module with the perldoc command.
278              
279             perldoc DB::Color
280              
281             You can also look for information at:
282              
283             =over 4
284              
285             =item * Bug tracker (report bugs here)
286              
287             L
288              
289             =item * AnnoCPAN: Annotated CPAN documentation
290              
291             L
292              
293             =item * CPAN Ratings
294              
295             L
296              
297             =item * Search CPAN
298              
299             L
300              
301             =item * Github
302              
303             L
304              
305             =back
306              
307             =head1 ACKNOWLEDGEMENTS
308              
309             Thanks to Nick Perez, Liz, and the 2012 Perl Hackathon for helping to overcome
310             some major hurdles with this module.
311              
312             =head1 LICENSE AND COPYRIGHT
313              
314             Copyright 2011 Curtis "Ovid" Poe.
315              
316             This program is free software; you can redistribute it and/or modify it
317             under the terms of either: the GNU General Public License as published
318             by the Free Software Foundation; or the Artistic License.
319              
320             See http://dev.perl.org/licenses/ for more information.
321              
322              
323             =cut
324              
325             1; # End of DB::Color