Textmate and Perlbrew

Textmate is a marvelous text editor for programming and other text based tasks on OSX. It even has a great extension for assisting when programming Perl. One of the really helpful features is a syntax validation happening when a file is saved.

Take for example this incomplete Perl source file below

hell-2012-10-14-20-29.png
If you specify the contents as perl (see the bottom of the window) or let Textmate resolve this Textmate will evaluate the contents when I save and get an error due to the very obvious syntax error in line 3.

warning-2012-10-14-20-29.png
Correcting the contents of the file, will give me status indicating an evaluation success.

no-errors-2012-10-14-20-29.png

This is really nifty most of the time, but I ran into a problem.

I am using App::perlbrew (perlbrew) to handle my local Perl installations. Perlbrew is a magnificent tool and it lets me install several different Perl interpreters in parallel. So I can evaluate my code using different versions of Perl.

The check executed by Textmate is handled by a script named: perlcheckmate.pl (available on Github) and it relies solely on the system perl installed in /usr/bin.

So when I work on a project requiring a newer (or older) version and I then use Perlbrew to satisfy this, I have to install external dependencies with two Perls – not exactly optimal also because I am not interested in maintaining and feeding all sort of crazy modules to my system Perl, just because I am working on some project.

Luckily adjusting perlcheckmate.pl to recognize and use perlbrew is quite easy.

perlbrew sets some environment variables when active:

  • PERLBREW_PERL
  • PERLBREW_ROOT

When perlbrew is active these variables will have content, if not or not installed, existing behavior should be preserved – and not everyone uses perlbrew.

So if PERLBREW_PERL is defined we have a Perl interpreter controlled by perlbrew and we can adjust accordingly. See lines 34 to 42. So We define our Perl interpreter to point to this instead of the Perl interpreter located in /usr/bin.

[codesyntax lang=“perl”]
#!/usr/bin/perl

use strict;
use Env qw($PERLBREW_PERL $PERLBREW_ROOT);

# cwd should be $™_DIRECTORY
# filename to check is $ARGV[0]

my $file = $ARGV[0];

my %file_source;
sub read_source {
require File::Spec;
require File::Basename;
my $file = shift;
my $file_source;
{ local $/ = undef; open F, “<$ENV{™_FILEPATH}”; $file_source = <F>; close F }
my @file_source = split /\r?\n/, $file_source;
my $path = $file;
if (!-f $path || !File::Spec->file_name_is_absolute($path)) {
$path = File::Spec->rel2abs($path, $ENV{™_DIRECTORY});
if (!-e $path) {
$path = undef;
my $base = File::Basename::basename($path);
foreach (@INC) {
my $file = File::Spec->catfile($_, $base);
$path = $file, last if -e $file;
}
}
}
$file_source{$file} = { source => \@file_source, path => $path };
}

my $perl;

if ($PERLBREW_PERL) {
$perl = “$PERLBREW_ROOT/perls/$PERLBREW_PERL/bin/perl”;
} else {
$perl = ‘perl’;
}

my @lines = `”$perl” -Tcw “$file” 2>&1`;
my $lines = join ‘’, @lines;

if ((scalar(@lines) == 1) && ($lines =~ m/ syntax OK$/s)) {
exit 0;
}

$lines =~ s/&/&/g;
$lines =~ s/</</g;
$lines =~ s/>/>/g;

# link line numbers to source
$lines =~ s%^((?:.+)[ ]+at[ ]+(.+)[ ]+line[ ]+)(\d+)[.,]%
my $pre = $1;
my $file = $2;
my $lnum = $3;
my $col;
if ($pre =~ m/“([^”]+)”/) {
read_source($file)
unless exists $file_source{$file};
my $source_line = $file_source{$file}{source}[$lnum-1];
$file = $file_source{$file}{path};
$col = index($source_line, $1);
$col = $col != -1 ? $col + 1 : 0;
} else {
if ($file !~ m!^/!) {
read_source($file)
unless exists $file_source{$file};
$file = $file_source{$file}{path};
}
}
my $url = qq{txmt://open?url=file://$file&line=$lnum};
$url .= “&column=$col” if $col;
qq{$pre<a href=“$url”>$lnum</a>.};
%gmex;

my $output = ‘<pre style=“word-wrap: break-word;”>’;
$output .= $lines;
$output .= ‘</pre>’;

print $output;
[/codesyntax]

To make a point on my opinion on the use of system perl. You can have a look at the perlcheckmate.pl script above. This script is an external application, bundled with Textmate and therefor it relies on the presence of perl in /usr/bin in order to work, so this line is perfectly okay and it should work even though the operating system is updated.

If you read the script carefully you can see that the script uses two external components:

  • File::Spec
  • File::Basename::basename

These are Perl core modules and they should not give any problems. But if you by some weird coincidence and accidentally change or break these as part of development, you are breaking your toolchain at the same time. Your system perl should be considered a key component in your operating system on most modern operating systems.

Advertisements
Textmate and Perlbrew

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s