Statement Toggler for Vim

Posted in: Technical Track

Raise your hand if the following has happened to you before.

1- You wrote

[perl] say "*hiccup*" if $eggnog_glass > 3;
[/perl]

and then realized that you also need to run titubate() as well if you drank that many eggnogs.

2- Or, you wrote

[perl]for my $gift ( @heap ) {
wrap($gift);
write_card($gift);
give($gift);
}
[/perl]

and after a few rounds of refactoring get down to simply

[perl]for my $gift ( @heap ) {
give($gift);
}
[/perl]

which would be much better written with a postfix for.

Wouldn’t it be nice if there were a way to flip block and postfix statements with the ease of a single command? Well, I thought so. I came up with a dirty little script:

[perl]#!/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;
}
</pre>[/perl]

(And yeah, it’s not the most robust thing ever, and it would be better if it was using PPI, but for a first pass, it’ll do.)

And then hooking the little macro:

[perl] <pre><code>vmap &lt;leader&gt;f :! ~/bin/postfix_toggle.pl&lt;CR&gt;
</code></pre>
[/perl]

to my .vimrc. Suddenly, I can turn

[perl]say "*hiccup*" if $eggnog_glass &gt; 3;
</pre>[/perl]

into

[perl]if ( $eggnog_glass &gt; 3 ) {
say "*hiccup*"
}
</pre>[/perl]

and

[perl]for my $gift ( @heap ) {
give($gift);
}
[/perl]

into

[perl] give($_) for @heap;
[/perl]

Cute, isn’t it?

Oh, and if you want to see how this experiment will develop, the code will soon appear on my GitHub environment project repo.

email
Want to talk with an expert? Schedule a call with our team to get the conversation started.

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *