a812b27b1e06293fe97b32e620d099926434a502
[p5sagit/Devel-REPL.git] / lib / Devel / REPL / Plugin / MultiLine / PPI.pm
1 package Devel::REPL::Plugin::MultiLine::PPI;
2
3 use Moose::Role;
4 use PPI;
5 use namespace::clean -except => [ 'meta' ];
6
7 has 'continuation_prompt' => (
8   is => 'rw', required => 1, lazy => 1,
9   default => sub { '> ' }
10 );
11
12 has 'line_depth' => (
13   is => 'rw', required => 1, lazy => 1,
14   default => sub { 0 }
15 );
16
17 around 'read' => sub {
18   my $orig = shift;
19   my ($self, @args) = @_;
20   my $line = $self->$orig(@args);
21
22   if (defined $line) {
23     while ($self->line_needs_continuation($line)) {
24       my $orig_prompt = $self->prompt;
25       $self->prompt($self->continuation_prompt);
26
27       $self->line_depth($self->line_depth + 1);
28       my $append = $self->read(@args);
29       $self->line_depth($self->line_depth - 1);
30
31       $line .= $append if defined($append);
32
33       $self->prompt($orig_prompt);
34
35       # ^D means "shut up and eval already"
36       return $line if !defined($append);
37     }
38   }
39   return $line;
40 };
41
42 sub line_needs_continuation
43 {
44   my $repl = shift;
45   my $line = shift;
46   my $document = PPI::Document->new(\$line);
47   return 0 if !defined($document);
48
49   # this could use more logic, such as returning 1 on s/foo/ba<Enter>
50   my $unfinished_structure = sub
51   {
52     my ($document, $element) = @_;
53     return 0 unless $element->isa('PPI::Structure');
54     return 1 unless $element->start && $element->finish;
55     return 0;
56   };
57
58   return $document->find_any($unfinished_structure);
59 }
60
61 1;
62
63 __END__
64
65 =head1 NAME
66
67 Devel::REPL::Plugin::MultiLine::PPI - read lines until all blocks are closed
68
69 =head1 SYNOPSIS
70
71     #!/usr/bin/perl 
72
73     use lib './lib';
74     use Devel::REPL;
75
76     my $repl = Devel::REPL->new;
77     $repl->load_plugin('LexEnv');
78     $repl->load_plugin('History');
79     $repl->load_plugin('MultiLine::PPI');
80     $repl->run;
81
82 =head1 DESCRIPTION
83
84 Plugin that will collect lines until you have no unfinished structures. This
85 lets you write subroutines, C<if> statements, loops, etc. more naturally.
86
87 For example, without a MultiLine plugin,
88
89     $ my $x = 3;
90     3
91     $ if ($x == 3) {
92
93 will throw a compile error, because that C<if> statement is incomplete. With a
94 MultiLine plugin,
95
96     $ my $x = 3;
97     3
98     $ if ($x == 3) {
99
100     > print "OH NOES!"
101
102     > }
103     OH NOES
104     1
105
106 you may write the code across multiple lines, such as in C<irb> and C<python>.
107
108 This module uses L<PPI>. This plugin is named C<MultiLine::PPI> because someone
109 else may conceivably implement similar behavior some other less
110 dependency-heavy way.
111
112 =head1 SEE ALSO
113
114 C<Devel::REPL>
115
116 =head1 AUTHOR
117
118 Shawn M Moore, C<< <sartak at gmail dot com> >>
119
120 =head1 COPYRIGHT AND LICENSE
121
122 Copyright (C) 2007 by Shawn M Moore
123
124 This library is free software; you can redistribute it and/or modify
125 it under the same terms as Perl itself.
126
127 =cut