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