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