File Coverage

blib/lib/Devel/REPL/Plugin/Carp/REPL.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Devel::REPL::Plugin::Carp::REPL;
2 1     1   7244 use Devel::REPL::Plugin;
  0            
  0            
3             #use namespace::clean -except => [ 'meta' ];
4             use Devel::LexAlias;
5             use Devel::StackTrace::WithLexicals;
6             use Data::Dump::Streamer;
7              
8             sub BEFORE_PLUGIN {
9             my $self = shift;
10             $self->load_plugin('LexEnv');
11             }
12              
13             has stacktrace => (
14             is => 'ro',
15             isa => 'Devel::StackTrace::WithLexicals',
16             handles => [qw/frame_count/],
17             default => sub {
18             my $stacktrace = Devel::StackTrace::WithLexicals->new(
19             ignore_class => ['Carp::REPL', __PACKAGE__],
20             );
21              
22             # skip all the Moose metaclass frames
23             shift @{ $stacktrace->{raw} }
24             until @{ $stacktrace->{raw} } == 0
25             || $stacktrace->{raw}[0]{caller}[3] eq 'Carp::REPL::repl';
26              
27             # get out of Carp::
28             shift @{ $stacktrace->{raw} }
29             until @{ $stacktrace->{raw} } == 0
30             || $stacktrace->{raw}[0]{caller}[0] !~ /^Carp(?:::|$)/;
31              
32             shift @{ $stacktrace->{raw} }
33             until @{ $stacktrace->{raw} } == 0
34             || $Carp::REPL::bottom_frame-- <= 0;
35              
36             return $stacktrace;
37             },
38             );
39              
40             has frame_index => (
41             is => 'rw',
42             isa => 'Int',
43             default => 0,
44             );
45              
46             sub frame {
47             my $self = shift;
48             my $i = @_ ? shift : $self->frame_index;
49              
50             return $self->stacktrace->frame($i);
51             }
52              
53             around 'frame_index' => sub {
54             my $orig = shift;
55             my ($self, $index) = @_;
56              
57             return $orig->(@_) if !defined($index);
58              
59             if ($index < 0) {
60             warn "You're already at the bottom frame.\n";
61             }
62             elsif ($index >= $self->frame_count) {
63             warn "You're already at the top frame.\n";
64             }
65             else {
66             $orig->(@_);
67             my $frame = $self->frame;
68             my ($file, $line) = ($frame->filename, $frame->line);
69             $self->print("Now at $file:$line (frame $index).");
70             }
71             };
72              
73             # this is totally the wrong spot for this. oh well.
74             around 'read' => sub {
75             my $orig = shift;
76             my ($self, @rest) = @_;
77             my $line = $self->$orig(@rest);
78              
79             return if !defined($line) || $line =~ /^\s*:q\s*/;
80              
81             if ($line =~ /^\s*:b?t\b/) {
82             $self->print($self->stacktrace);
83             return '';
84             }
85              
86             if ($line =~ /^\s*:top\b/) {
87             $self->frame_index($self->frame_count - 1);
88             return '';
89             }
90              
91             if ($line =~ /^\s*:b(?:ot(?:tom)?)?\b/) {
92             $self->frame_index(0);
93             return '';
94             }
95              
96             if ($line =~ /^\s*:up?\b/) {
97             $self->frame_index($self->frame_index + 1);
98             return '';
99             }
100              
101             if ($line =~ /^\s*:d(?:own)?\b/) {
102             $self->frame_index($self->frame_index - 1);
103             return '';
104             }
105              
106             if ($line =~ /^\s*:l(?:ist)?\b/) {
107             my $frame = $self->frame;
108             my ($file, $num) = ($frame->filename, $frame->line);
109             open my $handle, '<', $file or do {
110             warn "Unable to open $file for reading: $!\n";
111             return '';
112             };
113             my @code = <$handle>;
114             chomp @code;
115              
116             my $min = $num - 6;
117             my $max = $num + 4;
118             $min = 0 if $min < 0;
119             $max = $#code if $max > $#code;
120              
121             my @lines;
122             $self->print("File $file:\n");
123             for my $cur ($min .. $max) {
124             next if !defined($code[$cur]);
125              
126             push @lines, sprintf "%s%*d: %s",
127             $cur + 1 == $num ? '*' : ' ',
128             length($max),
129             $cur + 1,
130             $code[$cur];
131             }
132              
133             $self->print(join "\n", @lines);
134              
135             return '';
136             }
137              
138             if ($line =~ /^\s*:e(?:nv)?\s*/) {
139             $self->print(Dump($self->frame->lexicals)->Names('Env')->Out);
140             return '';
141             }
142              
143             return $line;
144             };
145              
146             # Provide an alias for each lexical in the current stack frame
147             around 'mangle_line' => sub {
148             my $orig = shift;
149             my ($self, @rest) = @_;
150             my $line = $self->$orig(@rest);
151              
152             my $frame = $self->frame;
153             my $package = $frame->package;
154             my $lexicals = $frame->lexicals;
155              
156             my $declarations = join "\n",
157             map {"my $_;"}
158             keys %$lexicals;
159              
160             my $aliases = << ' ALIASES';
161             while (my ($k, $v) = each %{ $_REPL->frame->lexicals }) {
162             Devel::LexAlias::lexalias 0, $k, $v;
163             }
164             my $_a; Devel::LexAlias::lexalias 0, '$_a', \$_REPL->frame->{args};
165             ALIASES
166              
167             return << " CODE";
168             package $package;
169             no warnings 'misc'; # declaration in same scope masks earlier instance
170             no strict 'vars'; # so we get all the global variables in our package
171             $declarations
172             $aliases
173             $line
174             CODE
175             };
176              
177             1;
178              
179             __END__
180              
181             =head1 NAME
182              
183             Devel::REPL::Plugin::Carp::REPL - Devel::REPL plugin for Carp::REPL
184              
185             =head1 SYNOPSIS
186              
187             This sets up the environment captured by L<Carp::REPL>. This plugin
188             isn't intended for use by anything else. There are plans to move some features
189             from this into a generic L<Devel::REPL> plugin.
190              
191             This plugin also adds a few extra commands like :up and :down to move up and
192             down the stack.
193              
194             =head1 AUTHOR
195              
196             Shawn M Moore, C<< <sartak at gmail.com> >>
197              
198             =head1 BUGS
199              
200             Please report any bugs to a medium given by Carp::REPL.
201              
202             =head1 COPYRIGHT & LICENSE
203              
204             Copyright 2007-2008 Best Practical Solutions, all rights reserved.
205              
206             This program is free software; you can redistribute it and/or modify it
207             under the same terms as Perl itself.
208              
209             =cut
210