e9a2c2d354fc806054b536974d5aeeb1d6f46494
[p5sagit/Devel-REPL.git] / lib / Devel / REPL / Plugin / MultiLine / PPI.pm
1 package Devel::REPL::Plugin::MultiLine::PPI;
2
3 use Devel::REPL::Plugin;
4 use PPI;
5 use namespace::autoclean;
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     return $self->continue_reading_if_necessary($line, @args);
24   } else {
25     return $line;
26   }
27 };
28
29 sub continue_reading_if_necessary {
30   my ( $self, $line, @args ) = @_;
31
32   while ($self->line_needs_continuation($line)) {
33     my $orig_prompt = $self->prompt;
34     $self->prompt($self->continuation_prompt);
35
36     $self->line_depth($self->line_depth + 1);
37     my $append = $self->read(@args);
38     $self->line_depth($self->line_depth - 1);
39
40     $line .= "\n$append" if defined($append);
41
42     $self->prompt($orig_prompt);
43
44     # ^D means "shut up and eval already"
45     return $line if !defined($append);
46   }
47
48   return $line;
49 }
50
51 sub line_needs_continuation
52 {
53   my $repl = shift;
54   my $line = shift;
55
56   # add this so we can test whether the document ends in PPI::Statement::Null
57   $line .= "\n;;";
58
59   my $document = PPI::Document->new(\$line);
60   return 0 if !defined($document);
61
62   # adding ";" to a complete document adds a PPI::Statement::Null. we added a ;;
63   # so if it doesn't end in null then there's probably something that's
64   # incomplete
65   return 0 if $document->child(-1)->isa('PPI::Statement::Null');
66
67   # this could use more logic, such as returning 1 on s/foo/ba<Enter>
68   my $unfinished_structure = sub
69   {
70     my ($document, $element) = @_;
71     return 0 unless $element->isa('PPI::Structure');
72     return 1 unless $element->finish;
73     return 0;
74   };
75
76   return $document->find_any($unfinished_structure);
77 }
78
79 1;
80
81 __END__
82
83 =head1 NAME
84
85 Devel::REPL::Plugin::MultiLine::PPI - read lines until all blocks are closed
86
87 =head1 SYNOPSIS
88
89     #!/usr/bin/perl 
90
91     use lib './lib';
92     use Devel::REPL;
93
94     my $repl = Devel::REPL->new;
95     $repl->load_plugin('LexEnv');
96     $repl->load_plugin('History');
97     $repl->load_plugin('MultiLine::PPI');
98     $repl->run;
99
100 =head1 DESCRIPTION
101
102 Plugin that will collect lines until you have no unfinished structures. This
103 lets you write subroutines, C<if> statements, loops, etc. more naturally.
104
105 For example, without a MultiLine plugin,
106
107     $ my $x = 3;
108     3
109     $ if ($x == 3) {
110
111 will throw a compile error, because that C<if> statement is incomplete. With a
112 MultiLine plugin,
113
114     $ my $x = 3;
115     3
116     $ if ($x == 3) {
117
118     > print "OH NOES!"
119
120     > }
121     OH NOES
122     1
123
124 you may write the code across multiple lines, such as in C<irb> and C<python>.
125
126 This module uses L<PPI>. This plugin is named C<MultiLine::PPI> because someone
127 else may conceivably implement similar behavior some other less
128 dependency-heavy way.
129
130 =head1 SEE ALSO
131
132 C<Devel::REPL>
133
134 =head1 AUTHOR
135
136 Shawn M Moore, C<< <sartak at gmail dot com> >>
137
138 =head1 COPYRIGHT AND LICENSE
139
140 Copyright (C) 2007 by Shawn M Moore
141
142 This library is free software; you can redistribute it and/or modify
143 it under the same terms as Perl itself.
144
145 =cut