January 2011 Archives

Show Perl subname in vim statusline

I asked on the vim mailing list how to see the name of Perl's current sub/method in the status line and Alan Young, the author of PPIx::IndexLines has a great suggestion which unfortunately relied on PPI. I'm working with very large modules and PPI ground to a halt for me. As a result, I took his suggestion and worked out the following.

First, make sure that your .vimrc has set laststatus=2 in it. That will ensure that you always get a status line, even if you only have one window (i.e., don't have split windows). Then drop the following into your .vim/ftplugin/perl.vim:

if ! exists("g:did_perl_statusline")
    setlocal statusline+=%(\ %{StatusLineIndexLine()}%)
    setlocal statusline+=%=
    setlocal statusline+=%f\ 
    setlocal statusline+=%P
    let g:did_perl_statusline = 1
endif

if has( 'perl' )
perl << EOP
    use strict;
    sub current_sub {
        my $curwin = $main::curwin;
        my $curbuf = $main::curbuf;

        my @document = map { $curbuf->Get($_) } 0 .. $curbuf->Count;
        my ( $line_number, $column  ) = $curwin->Cursor;

        my $sub_name = '(not in sub)';
        for my $i ( reverse ( 1 .. $line_number  -1 ) ) {
            my $line = $document[$i];
            if ( $line =~ /^\s*sub\s+(\w+)\b/ ) {
                $sub_name = $1;
                last;
            }
        }
        VIM::DoCommand "let subName='$line_number: $sub_name'";
    }
EOP

function! StatusLineIndexLine()
  perl current_sub()
  return subName
endfunction
endif

All this does is naïvely read backwards from the current line to get "sub $name" and report $name. It will fail on many common cases. However, it's fast. Very fast. Unlike the PPI solution, I can use this and manually correct any files which don't fit this convention.

It's a quick and nasty hack, but already I'm finding it very useful. Suggestions welcome :)

Note that this requires Perl support. Just ":echo has('perl')" and if it displays '1', you're good to go. Then type "help perl-using" to see what's going on.

Update: I've updated that statusline. There's a space after the '\' and it now shows the column, filename and percent. See ":help statusline" or this blog post for more ideas.

Update2: Changed "set" to "setlocal" so we don't screw with non-Perl buffers.

Update3: If I do an ":e $anotherfile", I lose the new status line. Eliminating the exists("g:did_perl_statusline") seems to fix this.

Convert FLAC to MP3 on OS X for iTunes

Sorry. No Perl in this post, but this appears to be such a common problem that I've decided to write up a simple description of how to convert FLAC to run in iTunes.

FLAC is the "Free Lossless Audio Codec". It's a fantastic file format, but there are a couple of issues. First, songs typically run 25 to 40 megs per song. This means that they take up roughly 10 times the hard disk space as a corresponding MP3. For extremely large music collections this could be a problem.

The other problem is that Apple has chosen not to support FLAC for iTunes. I tried many solutions listed online, including Xiph and Fluke and got nowhere. Finally I decided it was time to go "old-school" and fall back to the command line.

I downloaded the FLAC tools and installed them. That gave me access to the flac program. Then I installed the LAME MP3 encoder. I used MacPorts for the latter, but you can get it from the Sourceforge link I provided.

Then I wrote this and saved it in my path as flac_to_mp3:

#!/bin/bash

WAV='.wav'
MP3='.mp3'

flac -d *.flac
for oldfilename in *$WAV
do
    newfilename="${oldfilename/%${WAV}/${MP3}}"
    lame "$oldfilename" "$newfilename" && rm "$oldfilename"
done

I just cd into the directory containing my flac files and run the above code (don't forget to chmod +x flac_to_mp3 for the above).

Takes your flac files, converts them to .wav, and then converts each of those to mp3 before deleting the .wav (if the conversion to mp3 was successful).

On a typical album, it's taking between 3 to 4 minutes to run on my MacBook Pro.

Please note that I'm not an audiophile, so I've no idea if the above is the best strategy. I only know that it works. Suggestions for improving it welcome.

warnings::unused versus PPI

I had the following in my .bash_aliases file:

alias unused='perlcritic --single-policy ProhibitUnusedVariables'

However, I found a few cases where it didn't work. That's when mithaldu told me about warnings::unused. I installed it locally and ran it on some code which had a case that ProhibitUnusedVariables didn't find:

$ unused $some_module
$some_module source OK
$ perl -Mwarnings::unused -c $some_module 2>&1 | wc -l
34

Whoa! The one area were Perl::Critic modules repeatedly break for me is when I'm dealing with scope issues. It appears to miss where a variable is legitimately declared and used in one sub, but declared and unused in another. It also missed something like the following:

while ( my ($k,$v) = each %hash ) {
    my $k = munge($v);
}

And lucky me! The output of warnings::unused fits the format I'm expecting with the VI::Quickfix code I posted, so I just type :cf and :cn to jump to the next unused variable. Bonus: go ahead and delete them: vim's quickfix mode will note the deleted lines and still jump to the correct line.

Try dropping this in your .vimrc:

au! FileType perl :noremap <leader>c 
    \ :!time perl -Mwarnings::unused -MVi::QuickFix -c %<cr>

With that, every time you hit your leader and 'c', you'll get a quick compile check and a dump of all unused variables. Just hit ':cn" to keep jumping to the next one (or the next error, if you have any).

(Now if I can find some nifty code which finds variables declared outside of the scope they're needed ...)

Role::Basic - what does DOES do?

Over the years it has become abundantly clear to me that people who object to OO fall into two categories:

  1. A handful of people who really understand object-oriented programming.
  2. Wankers with blogs.

Though I don't object to OO, it's still unclear which of the above categories I fall into. I suspect it's not the first.

This brings me to a lovely quote from H. L Mencken, "for every complex problem, there is a solution that is simple, neat, and wrong" (annoyingly, there are tons of subtle variations on that quote. I need to find the original). A "solution" I had in Role::Basic was was simple, neat, and wrong. I'm kicking myself, but my problem was my failure to apply what I thought I had "learned" from the original traits papers.

Role::Basic - what is a conflict?

It's frustrating to me that I have limited bandwidth right now. A new job, a new country, a (soon to be) new baby, and a new blogging venture have all conspired to ensure that I don't have as much time for writing software outside of work. However, I've been working hard on Role::Basic and following a suggestion from Stevan Little, I've started a branch with Moose::Role tests and have started working through them. Some are clearly inapplicable (for example, Role::Basic has no notion of attributes), but a few have given me pause. I wasn't sure if they were supporting original traits behavior or if they were extensions for Moose. Since I've already ready the original traits paper (pdf) several times, I decided to move on to Traits: The Formal Model (pdf) to make sure I hadn't missed anything. There are some interesting bits there.

Role::Basic tweaks and the future

I wanted to know if people wanted Role::Basic as a stepping stone for Moose or just for roles. The results of my question are in and every person who has commented on this blog or in my Twitter feed has said the same thing: just roles. They know where Moose is if they want it.

That said, I still want to minimize pain if people upgrade to Moose. That leaves me with three issues.

  • Allow people to easily add getter/setter methods (gets around a limitation in Perl's OO)
  • Probably add -alias and -excludes methods to requires
  • Find out why Role::Basic is sometimes failing on 5.6.2

Happy New Year/Roles!

Happy New Year everyone!

Here it is, only a few hours into the new year, and I'm already breaking a resolution of sorts. It wasn't a proper resolution, but given that I have plenty of things on my plate — not the least of which is a baby on the way — I had decided that I wasn't going to hack on Role::Basic for a bit until I saw the dust settling.

First, a simple question: if you're interested in Role::Basic, is it as a stepping stone to Moose, or because you just want roles and nothing else? The answer to this question could have a huge bearing on a fundamental problem that I face. This post is going to be rather long, so if all you do is answer that question, that's fine. I need to know. The reason this post is going to be long is because making things hard is easy. Making things easy is hard. Thus, in trying to solve a problem in an "easy" way, I have to think about a hard problem.

About Ovid

user-pic Have Perl; Will Travel. Freelance Perl/Testing/Agile consultant. Photo by http://www.circle23.com/. Warning: that site is not safe for work. The photographer is a good friend of mine, though, and it's appropriate to credit his work.