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