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