a908c8f8c4a51e8e94bf24d78733728031d5ae56
[p5sagit/Devel-REPL.git] / lib / Devel / REPL / Plugin / Turtles.pm
1 use strict;
2 use warnings;
3 package Devel::REPL::Plugin::Turtles;
4
5 use Devel::REPL::Plugin;
6 use Scalar::Util qw(reftype);
7 use namespace::autoclean;
8
9 has default_command_prefix => (
10   isa => "RegexpRef",
11   is  => "rw",
12   default => sub { qr/\#/ },
13 );
14
15 has turtles_matchers => (
16   traits => ['Array'],
17   isa => "ArrayRef[RegexpRef|CodeRef]",
18   is  => "rw",
19   lazy => 1,
20   default => sub { my $prefix = shift->default_command_prefix; [qr/^ $prefix (\w+) \s* (.*) /x] },
21   handles => {
22     add_turtles_matcher => 'unshift',
23   },
24 );
25
26 around 'formatted_eval' => sub {
27   my $next = shift;
28   my ($self, $line, @args) = @_;
29
30   if ( my ( $command, @rest ) = $self->match_turtles($line) ) {
31     my $method = "command_$command";
32     my $expr_method = "expr_$method";
33
34     if ( my $expr_code = $self->can($expr_method) ) {
35       if ( my $read_more = $self->can("continue_reading_if_necessary") ) {
36         push @rest, $self->$read_more(pop @rest);
37       }
38       $self->$expr_code($next, @rest);
39     } elsif ( my $cmd_code = $self->can($method) ) {
40       return $self->$cmd_code($next, @rest);
41     } else {
42       unless ( $line =~ /^\s*#/ ) { # special case for comments
43         return $self->format($self->error_return("REPL Error", "Command '$command' does not exist"));
44       }
45     }
46   } else {
47     return $self->$next($line, @args);
48   }
49 };
50
51 sub match_turtles {
52   my ( $self, $line ) = @_;
53
54   foreach my $thingy ( @{ $self->turtles_matchers } ) {
55     if ( reftype $thingy eq 'CODE' ) {
56       if ( my @res = $self->$thingy($line) ) {
57         return @res;
58       }
59     } else {
60       if ( my @res = ( $line =~ $thingy ) ) {
61         return @res;
62       }
63     }
64   }
65
66   return;
67 }
68
69 1;
70
71 __END__
72
73 =head1 NAME
74
75 Devel::REPL::Plugin::Turtles - Generic command creation using a read hook
76
77 =head1 DESCRIPTION
78
79 By default, this plugin allows calling commands using a read hook
80 to detect a default_command_prefix followed by the command name,
81 say MYCMD as an example.  The actual routine to call for the
82 command is constructed by looking for subs named 'command_MYCMD'
83 or 'expr_MYCMD' and executing them.
84
85 =head2 NOTE
86
87 The C<default_command_prefix> is C<qr/\#/> so care must be taken
88 if other uses for that character are needed (e.g., '#' for the
89 shell escape character in the PDL shell.
90
91 =cut
92