EmacsにSEPIA (Simple Emacs Perl Integration) をインストールしてみた

SEPIA (Simple Emacs Perl Integration) は補完機能、モジュールのソースへのアクセス、ドキュメントとの連携、インタラクティブシェル、デバッガの提供、特定の範囲の式の評価、CPAN との連携など、かなりいろんな機能を提供してくれる Emacs のメジャーモードです。
ただ、結論から言うと今回試したバージョン0.992は微妙でした。

補完やモジュールのソースへのアクセス、ドキュメントとの連携は perl-completion の方が秀逸ですし、デバッガは shell とかで perl -d で実行した方が手軽ですし、式の評価も my とか使ってると評価し終えた時点で破棄されますし。
あとまだまだバグが多そうです。
一番重宝しそうなのが eldoc で、あとはインタラクティブシェルはそこそこ良いかなぁといった印象です。

いろいろ導入が大変だったんで、今後 SEPIA を使ってみようと思う人が出てきた時のためにインストール手順などを記しておきます。

インストール

cpanm を使ってインストールする場合、verbose モードでインストールしないと肝心の elisp がインストールされません。

$ cpanm -v Sepia

verbose モードにすることで Emacs のパスや、elisp のインストール先などが聞かれるので自分の環境に合わせて答えてください。

バグフィックス

(seipa-ensure-process) が実行されるだけでインタラクティブシェルのバッファである sepia-perl が作成されるんですが、(sepia-ensure-process) の後に sleep しないと、後続の処理で Perl の処理結果を受け取るはずが sepia-perl に出力されるなんてことがあるみたいです。
あと、sepia-interactive-arg という関数では blah-choices という存在しない変数にアクセスしているせいでエラーになります。perl-completion を使う場合は縁のない関数ですが。

というわけで、次のようなパッチを当てると幸せになれるかもしれません。

sepia.el.patch

--- sepia.el.orig	2012-03-25 23:22:38.000000000 +0900
+++ sepia.el	2012-03-26 00:18:14.000000000 +0900
@@ -98,7 +98,9 @@
 
 (defun sepia-eval-raw (str)
   "Evaluate perl code STR, returning a pair (RESULT-STRING . OUTPUT)."
-  (sepia-ensure-process)
+  (if (sepia-ensure-process)
+      ;; sometimes output result to '*sepia-repl*' if without wait
+      (sleep-for 0.1))
   (let (ocpof)
     (unwind-protect
          (let ((sepia-output "")
@@ -480,7 +482,7 @@
                      (format "%s [%s]: " text default)
                      (format "%s: " text)))
 	 (ret (if sepia-use-completion
-                  (completing-read prompt 'blah-choices nil nil nil 'sepia-history
+                  (completing-read prompt (funcall choices nil) nil nil nil 'sepia-history
                                    default)
 		  (read-string prompt nil 'sepia-history default))))
     (push ret sepia-history)
$ cd /path/to/sepia.el/parent/directory
$ patch -p0 < sepia.el.patch

perl-completion & auto-complete を考慮した設定

perl-completion & auto-complete を使いたかったんですが、(auto-complete-mode t) を実行しても無効にされてしまうので .emacs には次のように記述しました。
Emacs の仕組みがよくわかってないんで、変なことをしているかもしれませんが・・・

(defun init-perl-mode()
  (setq cperl-indent-parens-as-block t)
  (when (require 'perl-completion nil t)
    (perl-completion-mode t))
  (when (require 'auto-complete nil t)
    ;; sepia seems to turns auto-complete-mode off
    (run-at-time 0.1 nil (lambda() (auto-complete-mode t)))
    (make-variable-buffer-local 'ac-sources)
    (setq ac-sources '(ac-source-perl-completion))))
(defalias 'perl-mode 'cperl-mode)
(add-hook 'cperl-mode-hook 'init-perl-mode)
(when (require 'sepia nil t)
  (defalias 'perl-mode 'sepia-mode)
  (defalias 'perl-shell 'sepia-repl)
  (defun sepia-eval-line()
    "Evaluate line using current Sepia process."
    (interactive)
    (sepia-eval-region (point-at-bol) (point-at-eol)))
  ;; compatibility for ESS
  (define-key sepia-mode-map (kbd "C-c C-j") 'sepia-eval-line)
  (define-key sepia-mode-map (kbd "C-c C-f") 'sepia-eval-defun))

perl-completion を auto-complete と組み合わせる場合、cperl-mode が前提となっているので perl-completion.el も変更する必要があります。
基本的には “cperl-mode” を “sepia-mode” にするだけです。

perl-completion.el.patch

--- perl-completion.el.orig	2011-08-22 00:01:33.000000000 +0900
+++ perl-completion.el	2012-03-25 22:22:13.000000000 +0900
@@ -227,7 +227,7 @@
 ;;;code:
 (require 'cl)
 (require 'anything) ; perl-completion.el uses `anything-aif' macro.
-(require 'cperl-mode)
+(require 'sepia)
 (require 'dabbrev)
 (require 'rx)
 (require 'regexp-opt)
@@ -447,7 +447,7 @@
 
       ;; other
       (define-key map (kbd "C-c c") 'plcmp-cmd-clear-all-caches)
-      (define-key map (kbd "C-c C-f") 'plcmp-cmd-project-files)
+      ;(define-key map (kbd "C-c C-f") 'plcmp-cmd-project-files)
       (define-key map (kbd "C-c C-c s") 'plcmp-cmd-show-environment)
       (define-key map (kbd "C-c C-c u") 'plcmp-cmd-update-check)
       (define-key map (kbd "C-c C-c d") 'plcmp-cmd-set-additional-lib-directory))
@@ -2069,7 +2069,7 @@
 
 (defun plcmp-ac-candidates ()
   (plcmp-ignore-errors
-   (when (and (eq major-mode 'cperl-mode)
+   (when (and (eq major-mode 'sepia-mode)
               (boundp 'perl-completion-mode)
               perl-completion-mode)
      (plcmp-initialize-variables)
@@ -2116,7 +2116,7 @@
 ;;; patial
 (defun plcmp-ac-candidates-patial ()
   (plcmp-ignore-errors
-   (when (and (eq major-mode 'cperl-mode)
+   (when (and (eq major-mode 'sepia-mode)
               (boundp 'perl-completion-mode)
               perl-completion-mode)
      (plcmp-initialize-variables)
@@ -2141,7 +2141,7 @@
 ;;;; Extend find-file
 ;;;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 (add-to-list 'ffap-alist
-             '(cperl-mode . plcmp-ffap-perl))
+             '(sepia-mode . plcmp-ffap-perl))
 
 (defun plcmp-ffap-perl (module)
   ;; dont use argument MODULE
@@ -2267,17 +2267,17 @@
       (desc "plcmp-get-face-words")
       (expect nil
         (let ((b (get-buffer-create "*plcmp-test*"))
-              (cperl-mode-hook nil))
+              (sepia-mode-hook nil))
           (with-current-buffer b
-            (cperl-mode)
+            (sepia-mode)
             (plcmp-get-face-words))))
       (expect "$test"
         (let ((b (get-buffer-create "*plcmp-test*"))
-              (cperl-mode-hook nil))
+              (sepia-mode-hook nil))
           (with-current-buffer b
             (erase-buffer)
             (insert "my $test = 'hoge';\n")
-            (cperl-mode)
+            (sepia-mode)
             (font-lock-mode t)
             (font-lock-fontify-region (point-min) (point-max))
             (prog1 (car (plcmp-get-face-words))
$ cd /path/to/perl-completion.el/parent/directory
$ patch -p0 < perl-completion.el.patch

※コンパイルしている方はコンパイルし直してください

使い方

個人的に使うかもしれない機能や、ドキュメントを見てもよくわからなかった部分についてメモがてら紹介します。

インタラクティブシェル

これと eldoc(関数上でキャレットを止めるとエコー領域に usage が表示される)が SEPIA の目玉だと思います。
“M-x sepia-repl” で起動します。

次のコマンドが実行されるようです。

perl -MSepia -MSepia::Xref -e "Sepia::repl"

一般的な Perl のインタラクティブシェルのように使えます。
特殊なのは先頭にカンマを付けることで特殊なコマンドを実行できるということです。

main @> ,help
REPL commands (prefixed with ','):
break [F:N [E]]          Break at file F, line N (or at current position) if E
                         is true.
cd DIR                   Change directory to DIR
debug [0|1]              Enable or disable debugging.
define NAME ['DOC'] BODY Define NAME as a shortcut executing BODY
delete                   Delete current breakpoint.
eval EXP                 (internal)
exit                     Quit the REPL
format [TYPE]            Set output formatter to TYPE (one of 'dumper', 'dump',
                         'yaml', 'plain'; default: 'dumper'), or show current
                         type.
freload MODULE           Reload MODULE and all its dependencies.
help [CMD]               Display help on all commands, or just
                         CMD.
load [FILE]              Load state from FILE.
lsbreak                  List breakpoints.
lsmod [PATTERN]          List loaded modules matching PATTERN.
methods X [RE]           List methods for reference or package X, matching
                         optional pattern RE
package PKG              Set evaluation package to PKG
pwd                      Show current working directory
quit                     Quit the REPL
reload [MODULE | /RE/]   Reload MODULE, or all modules matching
                         RE.
restart                  Reload Sepia.pm and relaunch the REPL.
save [PATTERN [FILE]]    Save variables matching PATTERN to FILE.
shell CMD ...            Run CMD in the shell
size PKG [RE]            List total sizes of objects in PKG matching optional
                         pattern RE.
strict [0|1]             Turn 'use strict' mode on or off
test FILE...             Run tests interactively.
time [0|1]               Print timing information for each command.
undef NAME               Undefine shortcut NAME
wantarray [0|1]          Set or toggle evaluation context
who PKG [RE]             List variables and subs in PKG matching optional
                         pattern RE.

こんな感じですね。

main @> ,pwd
/Users/arabiki/work/perl
main @> ,shell echo hello
hello

あとは、sepia-mode で開いている Perl のコードに対して “M-x sepia-eval-defun” (C-M-x) や “M-x sepia-eval-region” (C-c C-r) などで評価し、その結果をインタラクティブシェル側で確認できることも特殊です。

リージョンに対する処理

“M-x shell-command-on-region perl -pe 'EXP'" 的なことが "M-x sepia-perl-pe-region EXP" でできます。 同様に "M-x shell-command-on-region perl -ne 'EXP'" 的なことが "M-x sepia-perl-ne-region EXP" でできます。 これらは1行ごとに EXP を実行するわけですが、"M-x sepia-perlize-region EXP" はリージョンの内容を一気に $_ に格納し、一度だけ EXP を実行します。

関数定義の検索

例えば “M-. f ok" と入力すると、関数名に ok を含む関数が定義されている箇所を検索します。最初の検索結果の箇所には C-x b で移動することができます。一見何も起こらないので注意です。 次に "M-, C-x b" を実行することで次の検索結果に移動することができます。

デバッガ

これがよくわからなかったんですが、”M-x sepia-load-file” で現在の編集しているスクリプトを実行した際に die で終了した場合に、デバッガが起動します。sepia-load-file を実行すると、一見何も起きていないように見えますが密かに sepia-repl が作成され、よく見ると die が実行される部分に “=>” という印ができています。

例えば次のようなスクリプトで sepia-load-file を実行した場合、

#!/usr/bin/env perl
use strict;
use warnings;

sub f {
  my $str = shift;
  return g($str);
}

sub g {
  my $str = shift;
  die if $str eq 'die';
  return $str;
}

print f("live"),"\n";
print f("die"),"\n";

関数 g の die の横に “=>” が表示されます。

sub g {
  my $str = shift;
=>die if $str eq 'die';
  return $str;
}

っで、$str の値が何かを確認したいんですが、

main @*1*> $str
'do { no strict; package main; local *str = $Sepia::ENV->{\'$str\'};  $str
 }'

となっており、何か変だと思って関数 f と g を次のように書き換えて

sub f {
  my $str_f = shift;
  return g($str_f);
}

sub g {
  my $str_g = shift;
  die if $str_g eq 'die';
  return $str_g;
}

再度試してみると

main @> $str_g
'die'
main @*1*> $str
'do { no strict; package main; local *str_g = $Sepia::ENV->{\'$str_g\'};  $str
 }'

となっていて愕然としたわけです。
他にも試してみるとわかりますが、このデバッガは Emacs との連携によって特殊なことはできますが、余計なモジュールにラッピングされているので step も期待したように実行できなかったり、普通のことができません。
デバッガは期待大だったのでかなり残念でした・・・

以上、微妙な SEPIA の紹介でした!