Statement Toggler for Vim
Statement Toggler for Vim
Update: mysz pointed out splitjoin, a vim plugin very much in the same spirit as this hack, but generalized to other languages as well.
Update: Also changed the vim mapping to use leader
, as proposed by Joe
Frazer.
Raise your hand if that happened to you before: you have written
say "*hiccup*" if $eggnog_glass > 3;
and then realized that you also need to run titubate()
as well if you drank
that many eggnogs?
Or you wrote
for my $gift ( @heap ) {
wrap($gift);
write_card($gift);
give($gift);
}
and after a few rounds of refactoring get down to simply
for my $gift ( @heap ) {
give($gift);
}
which would be much better written with a postfix for
?
Wouldn’t it be nice if there was a way to flip block and postfix statements with the ease of a single command? Well, I thought so, so I came up with a dirty little script:
#!/usr/bin/env perl
use 5.16.0;
use strict;
local $/;
my $snippet = <>;
$snippet =~ s/^s*\n//gm;
my ($indent) = $snippet =~ /^(s*)/;
$snippet =~ s/^$indent//gm;
my $operators = join '|', qw/ if unless while for until /;
my $block_re = qr/
^
(?<operator>$operators)
s* (?:my s+ (?<variable>$w+) s* )?
( s* (?<array>[^)]+) ) s* {
(?<inner>.*)
}
s* $
/xs;
my $postfix_re = qr/
^
(?<inner>[^;]+?)
s+ (?<operator>$operators)
s+ (?<array>[^;]+?)
s* ;
$
/xs;
if ( $snippet =~ $block_re ) {
$snippet = block_to_postifx( $snippet, %+ );
}
elsif( $snippet =~ $postfix_re ) {
$snippet = postfix_to_block( $snippet, %+ );
}
$snippet =~ s/^/$indent/gm;
say $snippet;
sub postfix_to_block {
my( $snippet, %capture ) = @_;
$snippet = $capture{inner};
chomp $capture{array};
$snippet = "$capture{operator} ( $capture{array} ) {\n $snippet\n}";
}
sub block_to_postifx {
my( $snippet, %capture ) = @_;
# more than one statement? Don't touch it
return $snippet if $capture{inner} =~ /(;)/ > 1;
$snippet = $capture{inner};
$snippet =~ s/;s*$//;
$snippet =~ s/Q$capture{variable}/$_/g;
$snippet =~ s/$_s*=~s*//g;
$capture{array} =~ s/s*$//;
$snippet .= " $capture{operator} $capture{array};";
return $snippet;
}
(And, yeah, it’s not the most robust thing ever, and would be better if it was using PPI, but for a first pass, it’ll do.)
And then hooking the little macro
vmap <leader>f :! ~/bin/postfix_toggle.pl<CR>
to my .vimrc
. Suddenly, I can turn
say "*hiccup*" if $eggnog_glass > 3;
into
if ( $eggnog_glass > 3 ) {
say "*hiccup*"
}
and
for my $gift ( @heap ) {
give($gift);
}
into
give($_) for @heap;
Cute, isn’t?
Oh, and if you want to see how this experiment will develop, the code will soon appear on my GitHub environment project repo.