I'm hacking on code with some methods which are fairly long (inlined code for performance), but sometimes I have to extract some code out into its own method. Padre uses Devel::Refactor for this, but I didn't want to go down that road as it doesn't use PPI. Thus, I hacked my own using PPIx::EditorTools. It's not great, but long-term, I think it's a more robust solution.

In vim, I have the following binding for Perl:

vnoremap <leader>sub :!~/bin/extract_sub <cr>

Thus, when I select code in vim with shift-v, I can type ",sub" and that passes the select code via STDIN to this filter:

#!/usr/bin/env perl use strict; use warnings; use PPI; use PPIx::EditorTools 0.11; my $code = do { local $/; <STDIN> }; chomp($code); my $ppi = PPI::Document->new( \$code ); my %is_found = map { $_ => 1 } map { keys %$_ } values %{ PPIx::EditorTools::get_all_variable_declarations($ppi) }; my $vars = $ppi->find( sub { my ( $self, $thingy ) = @_; no warnings 'uninitialized'; return $thingy->isa('PPI::Token::Symbol') && not $is_found{ $thingy->content }++; } ); my $subname = $ENV{SUBNAME} || 'XXX'; my $unroll_at_underscore = ''; if ($vars) { $unroll_at_underscore = " my ( " . ( join ', ' => map { $_->content } @$vars ) . " ) = \@_;

"; } print <<"END"; sub $subname { $unroll_at_underscore $code } END

And the "print" at the end writes the code back to the selected area, expanding it as needed. So let's say we have the following method:

sub process_doc { my ( $self, %args ) = @_; $self->ppi( $args{ppi} ) if defined $args{ppi}; return 1 if $self->ppi && $self->ppi->isa('PPI::Document'); # TODO: inefficient to pass around full code/ppi $self->code( $args{code} ) if $args{code}; my $code = $self->code; $self->ppi( PPI::Document->new( \$code ) ); return 1 if $self->ppi && $self->ppi->isa('PPI::Document'); croak "arguments ppi or code required"; return; }

If you want the "# TODO" line and the following four lines extracted into a method, you select them and run the filter. That results in the following:

sub process_doc { my ( $self, %args ) = @_; $self->ppi( $args{ppi} ) if defined $args{ppi}; return 1 if $self->ppi && $self->ppi->isa('PPI::Document'); sub XXX { my ( $self, $args ) = @_; # TODO: inefficient to pass around full code/ppi $self->code( $args{code} ) if $args{code}; my $code = $self->code; $self->ppi( PPI::Document->new( \$code ) ); return 1 if $self->ppi && $self->ppi->isa('PPI::Document'); } croak "arguments ppi or code required"; return; }

That's not legal Perl and it seems trivial, but when you've selected a hundred lines of code, it's very handy. It clearly needs a lot more work, but it's already saving me a lot of time and frustration.