Vim Tips Wiki
(Change <tt> to <code>, perhaps also minor tweak.)
 
(15 intermediate revisions by 7 users not shown)
Line 1: Line 1:
{{review}}
 
 
{{TipImported
 
{{TipImported
 
|id=140
 
|id=140
 
|previous=139
 
|previous=139
 
|next=141
 
|next=141
|created=October 17, 2001
+
|created=2001
 
|complexity=advanced
 
|complexity=advanced
 
|author=Benoit Cerrina
 
|author=Benoit Cerrina
|version=6.0
+
|version=7.2
 
|rating=35/14
 
|rating=35/14
|category1=
+
|category1=Perl
 
|category2=
 
|category2=
 
}}
 
}}
When writing scripts using the embedded interpreter available if Vim has the +perl ore +perl/dyn on gives you access to this powerfull and FAST scripting language (especially fast compared to vim scripts) there are some gotchas.
+
Writing scripts using the embedded Perl interpreter provides a powerful and fast scripting language (much faster than Vim scripts). Embedded Perl is available if the Vim <code>:version</code> command shows the <code>+perl</code> or <code>+perl/dyn</code> configuration options. This tip shows how to access embedded Perl scripts from Vim.
 
First:
 
 
never embed complex perl command in the body of a Vim function this will be recompiled and evaled each time for a tremendous loss of time.instead to it like this
 
   
  +
==Defining a Perl function efficiently==
 
Never embed complex Perl command in the body of a Vim function as this will be recompiled and evaled each time for a tremendous loss of time. Instead do it like this:
 
<pre>
 
<pre>
 
perl << EOF
 
perl << EOF
 
sub mySub
 
sub mySub
 
{
 
{
#some usefull perl stuff
+
# some useful Perl stuff
 
}
 
}
 
EOF
 
EOF
Line 31: Line 28:
 
</pre>
 
</pre>
   
  +
==Passing values between Vim and Perl==
to pass computed argument to your perl sub use the Vim exec command
+
To pass a computed argument to your Perl sub use the Vim <code>:execute</code> command:
 
 
<pre>
 
<pre>
 
function! MyFunction
 
function! MyFunction
exec "perl mySub " . aLocalVar . ", " b:aBufferLocalVar
+
execute "perl mySub " . aLocalVar . ", " b:aBufferLocalVar
 
endfunction
 
endfunction
 
</pre>
 
</pre>
   
 
Another way to do this is to get the arguments from within the Perl function:
It may be very hard to debug your perl sub since the output of the perl compiler is somehow lost in the middle of nowhere and the debugger is not available.
 
 
<pre>
 
perl << EOF
 
sub mySub
 
{
 
my $anArg=VIM::Eval("a:anArg");
 
# some useful Perl stuff
 
}
 
EOF
   
 
function! MyFunction(anArg)
When a compilation error occurs in your sub definition you'll get an error message when you try to call it saying that the sub does not exists.
 
 
perl mySub
 
endfunction
 
</pre>
   
 
To be able to return something from Perl:
One thing which I have found very usefull is to write a fake VIM module with stub methods which will allow you to use the command line perl interpretor to at least compile your program. You could make your stub smart enough to fake a Vim and use the debugger. Here is a sample for such a fake module defining just those method which I was using.
 
 
<pre>
 
perl << EOF
 
sub mySub
 
{
 
my $anArg=VIM::Eval("a:anArg");
 
# some useful Perl stuff
 
VIM::DoCommand "let retVal=". aMeaningfullThingToReturn;
 
}
 
EOF
   
 
function! MyFunction(anArg)
 
perl mySub
 
if exists('retVal')
 
return retVal
 
endif
 
endfunction
 
</pre>
  +
  +
==Debugging embedded Perl==
 
It may be hard to debug your Perl sub since the output of the Perl compiler is lost in the middle of nowhere, and the debugger is not available.
  +
  +
To make sure you see all the warnings that Perl generates, make most warnings fatal by using something like this at the start of your Perl section:
 
<pre>
 
use strict;
 
use warnings FATAL => 'all';
  +
use warnings NONFATAL => 'redefine';
 
</pre>
  +
  +
This way both compile-time and run-time warnings are propagated up to the Vim UI level. Redefinitions are allowed so that you can continually re-execute your <code>.vimrc</code> or other scripts from a single running Vim session.
  +
 
It's possible to make sure the Perl code embedded in a Vim file is syntacticly correct when it is saved using {{script|id=3411}}.
  +
  +
You can get accurate run-time file and line number errors using {{script|id=3406}}.
  +
 
It may be useful to write a fake Vim module with stub methods which will allow you to use the command line Perl interpreter to at least compile your program. You could make your stub smart enough to fake a Vim and use the debugger, for example:
 
<pre>
 
<pre>
 
package VIM;
 
package VIM;
Line 73: Line 115:
 
sub VIM::DoCommand
 
sub VIM::DoCommand
 
{
 
{
my $package;
+
my ($package, $filename, $line) = caller;
my $filename;
 
my $line;
 
($package, $filename, $line) = caller;
 
 
my $command = shift;
 
my $command = shift;
 
print "at $filename $line\n";
 
print "at $filename $line\n";
Line 85: Line 124:
 
</pre>
 
</pre>
   
Then you can copy other your perl code in a separate file and add a use VIM; at the top and your set to debug.
+
Then you can copy your other Perl code in a separate file and add a <code>use VIM;</code> at the top, and you are set to debug.
   
 
==Comments==
 
==Comments==
 
For an example of the techniques described see {{script|id=100}}.
 
For an example of the techniques described see {{script|id=100}}.
 
----
 
little changes to the tip
 
 
<pre>
 
perl << EOF
 
sub mySub
 
{
 
#some usefull perl stuff
 
}
 
EOF
 
 
function! MyFunction()
 
perl mySub "an argument", "another"
 
endfunction
 
</pre>
 
 
to pass computed argument to your perl sub use the Vim exec command
 
 
<pre>
 
function! MyFunction()
 
exec "perl mySub " . aLocalVar . ", " b:aBufferLocalVar
 
endfunction
 
</pre>
 
 
another way to do this is to get the arguments from within the perl function
 
 
<pre>
 
perl << EOF
 
sub mySub
 
{
 
my $anArg=VIM::Eval("a:anArg");
 
#some usefull perl stuff
 
}
 
EOF
 
 
function! MyFunction(anArg)
 
perl mySub
 
endfunction
 
</pre>
 
 
finally to be able to return something from perl
 
 
<pre>
 
perl << EOF
 
sub mySub
 
{
 
my $anArg=VIM::Eval("a:anArg");
 
#some usefull perl stuff
 
VIM::DoCommand "let retVal=". aMeaningfullThingToReturn;
 
}
 
EOF
 
 
function! MyFunction(anArg)
 
perl mySub
 
if exists('retVal')
 
return retVal
 
endif
 
endfunction
 
</pre>
 
 
Its possible to make sure the perl code embedded in a vim file is syntacticly correct
 
without using a VIM module stub (though that is still great for using the debugger).
 
I use the following script:
 
 
<pre>
 
#!/usr/bin/perl -w
 
 
# Try to compile any perl code we can find embedded in the .vim file given
 
# as an argument.
 
 
use strict;
 
use warnings;
 
 
# Get the VIM code from the specified file.
 
@ARGV == 1 or die "wrong number of arguments";
 
my $vim_file = $ARGV[0];
 
$vim_file =~ m/\.vim$/
 
or die "file '$vim_file' doesn't end in '.vim' (heresy)\n";
 
open(VIM_FILE, "<$vim_file") or die "couldn't open '$vim_file' for reading";
 
my @vim_code_lines = <VIM_FILE>;
 
close(VIM_FILE) or die "couldn't close '$vim_file'";
 
 
# Get the perl chunks out of the vim file.
 
my @perl_chunks;
 
my @chunk_start_lines;
 
my $in_perl_chunk = 0;
 
my $chunk_delimiter;
 
my $current_chunk = "";
 
my $current_line_number = 0;
 
foreach ( @vim_code_lines ) {
 
$current_line_number++;
 
if ( ! $in_perl_chunk ) {
 
if ( m/^\s*\:?\s*perl <<\s*(\w+)\s$/ ) {
 
$in_perl_chunk = 1;
 
$chunk_delimiter = $1;
 
push(@chunk_start_lines, $current_line_number)
 
}
 
}
 
else {
 
if ( m/^$chunk_delimiter\s*$/ ) {
 
$in_perl_chunk = 0;
 
push(@perl_chunks, $current_chunk);
 
$current_chunk = "";
 
}
 
else {
 
$current_chunk .= $_
 
}
 
}
 
}
 
 
# Try to compile all the perl chunks.
 
my $got_errors = 0;
 
my $ii = 0;
 
foreach ( @perl_chunks ) {
 
# Add some goop to turn warnings fatal so we can postprocess them,
 
# and to make sure we don't accidently execute the eval'ed code
 
# (except for its BEGIN and UNITCHECK blocks, if any).
 
my $compile_only_chunk
 
= "use warnings FATAL => 'all'; return 1; "
 
."# This line was added by the check_embedded_perl utility.\n".$_;
 
if ( not defined(eval($compile_only_chunk)) ) {
 
my $errmsg = $@;
 
my @errmsg_lines = split('\n', $errmsg);
 
foreach ( @errmsg_lines ) {
 
m/at \(eval \d+\) line (\d+)/
 
or (print STDERR "$_\n" and next);
 
# We subtract one here because we added a line at the top...
 
my $file_line = $1 - 1 + $chunk_start_lines[$ii];
 
s/at \(eval \d+\) line \d+/at $vim_file line $file_line/;
 
print STDERR "$_\n";
 
$got_errors = 1;
 
}
 
}
 
$ii++;
 
}
 
 
if ( $got_errors ) {
 
print STDERR "$vim_file has embedded perl compilation errors.\n";
 
exit 1;
 
}
 
else {
 
print "$vim_file embedded perl syntax OK\n";
 
exit 0;
 
}
 
</pre>
 
 
Its only argument is the name of the .vim file to process. It expects
 
to find chunks of perl code in HERE-document style delimiters:
 
 
<pre>
 
perl <<EOF
 
sub foo {...
 
EOF
 
</pre>
 
 
The actual document delimiter doesn't have to be exactly 'EOF'.
 
   
 
----
 
----

Latest revision as of 05:16, 13 July 2012

Tip 140 Printable Monobook Previous Next

created 2001 · complexity advanced · author Benoit Cerrina · version 7.2


Writing scripts using the embedded Perl interpreter provides a powerful and fast scripting language (much faster than Vim scripts). Embedded Perl is available if the Vim :version command shows the +perl or +perl/dyn configuration options. This tip shows how to access embedded Perl scripts from Vim.

Defining a Perl function efficiently[]

Never embed complex Perl command in the body of a Vim function as this will be recompiled and evaled each time for a tremendous loss of time. Instead do it like this:

perl << EOF
sub mySub
{
  # some useful Perl stuff
}
EOF

function! MyFunction
  perl mySub "an argument", "another"
endfunction

Passing values between Vim and Perl[]

To pass a computed argument to your Perl sub use the Vim :execute command:

function! MyFunction
  execute "perl mySub " . aLocalVar . ", " b:aBufferLocalVar
endfunction

Another way to do this is to get the arguments from within the Perl function:

perl << EOF
sub mySub
{
  my $anArg=VIM::Eval("a:anArg");
  # some useful Perl stuff
}
EOF

function! MyFunction(anArg)
perl mySub
endfunction

To be able to return something from Perl:

perl << EOF
sub mySub
{
  my $anArg=VIM::Eval("a:anArg");
  # some useful Perl stuff
  VIM::DoCommand "let retVal=". aMeaningfullThingToReturn;
}
EOF

function! MyFunction(anArg)
perl mySub
if exists('retVal')
  return retVal
endif
endfunction

Debugging embedded Perl[]

It may be hard to debug your Perl sub since the output of the Perl compiler is lost in the middle of nowhere, and the debugger is not available.

To make sure you see all the warnings that Perl generates, make most warnings fatal by using something like this at the start of your Perl section:

use strict;
use warnings FATAL => 'all';
use warnings NONFATAL => 'redefine';

This way both compile-time and run-time warnings are propagated up to the Vim UI level. Redefinitions are allowed so that you can continually re-execute your .vimrc or other scripts from a single running Vim session.

It's possible to make sure the Perl code embedded in a Vim file is syntacticly correct when it is saved using script#3411.

You can get accurate run-time file and line number errors using script#3406.

It may be useful to write a fake Vim module with stub methods which will allow you to use the command line Perl interpreter to at least compile your program. You could make your stub smart enough to fake a Vim and use the debugger, for example:

package VIM;
use diagnostics;
use strict;
sub VIM::Eval
{
 $_ = shift;
 print "Eval $_\n";
 {
 return '^(?!!)([^\t]*)\t[^\t]*\t(.*);"\t([^\t]*)\tline:(\d*).*$' if (/g:TagsBase_pattern/);
 return $ARGV[0] if (/b:fileName/);
 return '$3' if (/g:TagsBase_typePar/);
 return '$1' if (/g:TagsBase_namePar/);
 return '$4' if (/g:TagsBase_linePar/);
 return 'Ta&gs' if (/s:menu_name/);
 return $ARGV[1] if (/g:TagsBase_groupByType/);
 die "unknown eval $_";
 }
}

sub VIM::Msg
{
 my $msg = shift;
 print "MSG $msg\n";
}

sub VIM::DoCommand
{
 my ($package, $filename, $line) = caller;
 my $command = shift;
 print "at $filename $line\n";
 print "DoCommand $command\n";
}

1;

Then you can copy your other Perl code in a separate file and add a use VIM; at the top, and you are set to debug.

Comments[]

For an example of the techniques described see script#100.