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