Add Devel::REPL::Plugin::MutliLine::PPI
[p5sagit/Devel-REPL.git] / lib / Devel / REPL / Plugin / MultiLine / PPI.pm
diff --git a/lib/Devel/REPL/Plugin/MultiLine/PPI.pm b/lib/Devel/REPL/Plugin/MultiLine/PPI.pm
new file mode 100644 (file)
index 0000000..177484f
--- /dev/null
@@ -0,0 +1,52 @@
+package Devel::REPL::Plugin::MultiLine::PPI;
+
+use Moose::Role;
+use PPI;
+use namespace::clean -except => [ 'meta' ];
+
+has 'continuation_prompt' => (
+  is => 'rw', required => 1, lazy => 1,
+  default => sub { '> ' }
+);
+
+around 'read' => sub {
+  my $orig = shift;
+  my ($self, @args) = @_;
+  my $line = $self->$orig(@args);
+
+  if (defined $line) {
+    while (needs_continuation($line)) {
+      my $orig_prompt = $self->prompt;
+      $self->prompt($self->continuation_prompt);
+
+      my $append = $self->read(@args);
+      $line .= $append if defined($append);
+
+      $self->prompt($orig_prompt);
+
+      # ^D means "shut up and eval already"
+      return $line if !defined($append);
+    }
+  }
+  return $line;
+};
+
+sub needs_continuation
+{
+  my $line = shift;
+  my $document = PPI::Document->new(\$line);
+  return 0 if !defined($document);
+
+  # this could use more logic, such as returning 1 on s/foo/ba<Enter>
+  my $unfinished_structure = sub
+  {
+    my ($document, $element) = @_;
+    return 0 unless $element->isa('PPI::Structure');
+    return 1 unless $element->start && $element->finish;
+    return 0;
+  };
+
+  return $document->find_any($unfinished_structure);
+}
+
+1;