スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Emacs の中から prove

少しは真面目に *.t を書こうかと思っているのだが、 Emacs で *.pm を編集して、ターミナルに戻って prove -vl t とかやるのは非常にウザい。

Emacs 使ってるのだから、シンタックスエラーが出たら当然キーストローク2つ位で その行へジャンプしたくなるのだが、シェルから実行してるとそれもできないし、 ターミナルのスクロールバーをチクチクいじくりながらエラーメッセージを見つけて M-x goto-line なんてヤだ。

mode-compile をインストールして cperl-mode を使ってると、編集してる Perl スクリプトを単純に実行した場合は ちゃんとエラーの行へ飛べるんだけどなぁ。 proveと組み合わせると快適にいかない。

とかなんとか、Emacs が Emacs らしく使えない、というのを言い訳にしてテストを書くのサボってるわけだ。やだねぇ Emacs 使いって。

とは言うものの、このご時世、テスト書かないとか言ってると 人間扱いしてもらえない危惧もあるので、何とかしたいものである。 10年以上前 C のコードを大量に書いてた頃に、 Emacs の中から C-x-C-emake -k を実行して、 C-x-` でコンパイルエラーの箇所へ飛んで、直して、また make というの をやっていて、それと同じことを Perl でもやりたいなぁと。

えらく前置きが長くなったが、以下そのための設定などなど。 ちなみに使ってるのは Mac (いまだに Panther)。 CarbonEmacs も古め。 carbon-emacs-package-version の値は January 2005 だそうだ。

さて、やっと本題。M-x compileprove を実行すれば望みに近いことになるのだが、そのままだと実行ディレクトリがそのとき編集していたファイルのディレクトリになってしまって上手く行かない。 prove コマンドはモジュールのベースディレクトリ(Makefile.PL とかがある場所)で実行したいわけだ。で、以下のスクリプトを書いた。 このスクリプトは、カレントディレクトリからディレクトリを親方向へたどっていってベースディレクトリらしきところを探し、 そこへ cd して prove を実行、ということをやる。 cd した時に Entering directory ... と出力して Emacs に cd した先を教えるのがミソ。 こいつを emacs-prove という名前で保存する。 置き場所はコマンドサーチパスに入っているところならどこでもよい。 chmod a+x emacs-prove もお忘れなく。

#!/usr/local/bin/perl
# -*- cperl -*-
#
use strict;
use warnings;
use File::Basename qw(dirname);
use File::Spec::Functions qw(catfile);
use File::HomeDir;
use Cwd;

my @test_files = @ARGV;
@test_files = qw(t) unless @test_files;

my $basedir = find_module_base();
die "Can't find module base directory\n" unless $basedir;

chdir($basedir) or die "chdir $basedir:$!";

# tell emacs to change dir.
print "Entering directory `", homedir_to_tilde($basedir), "'\n";

run_prove();
exit 0; # not reached

BEGIN {
    my $myhome = File::HomeDir->my_home;
    sub homedir_to_tilde {
        my $path = shift;
        $path =~ s/^$myhome/\~/o;
        return $path;
    }
}

sub run_prove {
    my @prove = ('prove', '-vl', @test_files);
    print STDERR "@prove\n";
    exec(@prove);
    die "exec failed: $!";
    # not reached.
}

sub find_module_base {
    my $curdir = shift || cwd;
    chomp($curdir);

    while ($curdir && $curdir ne '/') {
        return $curdir if looks_like_base($curdir);
        $curdir = dirname $curdir; # try ../
    }
    return; # not found.
}

sub looks_like_base {
    my $dir = shift;

    return ( -d catfile($dir, 't')
            && -e catfile($dir, 'MANIFEST')
            && -e catfile($dir, 'README')
            && (-e catfile($dir, 'Makefile.PL')
                || -e catfile($dir, 'Build.PL')));
}

あとは Emacs の設定。まず、テストを走らせるための関数を定義する。

(defun perl-run-test ()
  (interactive)
  (let ((compile-command "emacs-prove t/"))
    (call-interactively 'compile)))
さらに cperl-mode-hook の中でこれをキーにバインドしておく。 ここではC-x-C-eにしてるけど、まあお好みで。
(local-set-key "\C-x\C-e" 'perl-run-test)
さて、ここまでで C-x-C-e でテストを実行、 C-x-` でエラー箇所へジャンプ、 ということができるようになったのだが、さらにもう一息。

cperl-mode のデフォルトのエラーメッセージ解析用正規表現だと

# Compilation failed in require at (eval 3) line 2.
という形のエラー出力行にもマッチして、eval がファイル名として解釈されてしまう。これを無視するために .emacs にこんなこと書いてやる。
(setq cperl-compilation-error-regexp-alist
  '(("^[^\n]* \\(file\\|at\\) \\([^ (\t\n]+\\) [^\n]*line \\([0-9]+\\)[\\., \n]"
     2 3)))
赤い太字がオリジナルに対する追加部分。 要するにカッコがファイル名の一部としてマッチしないようにしただけ。

これでおしまい。ちなみに、オレの .emacs の cperl-mode 関係の設定はこんな感じ。

(autoload 'cperl-mode "cperl-mode" "" t)
(defalias 'perl-mode 'cperl-mode)
(setq auto-mode-alist (append '(("\\.\\(pl\\|pm\\|cgi\\|t\\)$" . cperl-mode))
			      auto-mode-alist))

(defun perl-run-test ()
  (interactive)
  (let ((compile-command "emacs-prove t/"))
    (call-interactively 'compile)))
  
(add-hook 'cperl-mode-hook
          '(lambda ()
	     (load-library "mode-compile")
	     (setq cperl-close-paren-offset -4)
	     (setq cperl-continued-statement-offset  4)
	     (setq cperl-indent-level  4)
	     (setq cperl-indent-parens-as-block t)
	     (local-set-key "\C-cc" 'mode-compile)
	     (local-set-key "\C-x\C-e" 'perl-run-test)
	     (local-set-key "\C-c\C-hd" 'cperl-perldoc) ))

(setq cperl-compilation-error-regexp-alist
  '(("^[^\n]* \\(file\\|at\\) \\([^ (\t\n]+\\) [^\n]*line \\([0-9]+\\)[\\., \n]"
     2 3)))

Update

最初このエントリには

M-x compile したら emacs-prove が実行されるように、cperl-mode-hook で以下の設定をする。
 (set (make-local-variable 'compile-command) "emacs-prove t/")
さらにcompileをキーにバインドしておく。 ここではC-x-C-eにしてるけど、まあお好みで。
(local-set-key "\C-x\C-e" 'compile)
と書いていたけど、これだと元々の mode-compile で編集中の Perl スクリプトを実行する機能が働かなくなることが判明したので、上記の perl-run-test を定義する方法に変更しました。
スポンサーサイト

この記事のトラックバックURL

http://saltyduck.blog12.fc2.com/tb.php/23-1df7683a

[Perl][Vim]Vimの中からprove
unwind-protect: Emacs の中から prove Emacsの中からproveを実行するという設定らしい。 自分の環境のVimでは以下のように設定している。 autocmd BufNewFile,BufRead *.t set filetype=perl autocmd BufNewFile,BufRead *.t nnoremap <F5> :!prove %&#60
[perl][emacs] *.tのときはmode-compileでperlじゃなくてproveを実行したい
インスパイア: unwind-protect: Emacs の中から prove emacs-proveはそのまんまいただいて、~/.emacsにこんな感じで。(lisp 10級) これでいつでもC-cC-cで、ファイル名を見て適宜perl -wかprove -vlを実行してくれます。 (autoload ’mode-compile "mode-compile"

コメント

コメントする

管理者にだけ表示を許可する

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。