erefactor.el

最近、長年書き溜めた色んな elisp を公開してます。

Emacs Lisp の refactoring 用ツールをまとめた erefactor.el を公開しました。

http://www.emacswiki.org/emacs/erefactor.el
https://github.com/mhayashi1120/Emacs-erefactor

erefactor.el は大きく三つの機能に分けられます。

  1. symbol 名の変更
  2. ローカル変数 highlight
  3. 外部 elint

似た機能を持つ elisp として https://github.com/mitsuo-saito/auto-highlight-symbol-mode がありますが、あちらは色々な言語用の汎用版で、こちらは Elisp 限定版と捉えればよいと思います。highlight 機能が被ってしまったので、その場しのぎではありますが、一部の機能の重複を advice をつけることで回避しています。
昨日、この elisp の存在を知ったのですが、やりたいことが似ていたり上回っていたりしてちょっと悔しい。こっちはすぐ不要になりそう。。

インストール

  • erefactor.el をダウンロードして load-path に置いてください。
  • .emacs に以下のように記述してください。*1
(require 'erefactor)
(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (define-key emacs-lisp-mode-map "\C-c\C-v" erefactor-map)))
  • ローカル変数の highlight をする場合は以下も追加してください。
(add-hook 'emacs-lisp-mode-hook 'erefactor-lazy-highlight-turn-on)
(add-hook 'lisp-interaction-mode-hook 'erefactor-lazy-highlight-turn-on)

symbol 名の変更

今まで面倒だったこと

  • region 選択して query-replace する際のカーソルを移動。
  • defun や defvar など大域で宣言されている symbol を query-replace などで変更すると find-function などでジャンプできなくなる。
  • 定義を変更した後で再度 C-M-x や C-x C-e などで定義してまわる。
  • 使用箇所が複数ファイルに跨るときに grep 検索してから query-replace など。

割とシンプルなメカニズムなので以下のものには対応していません。

  • shadow されてるローカル変数
(let ((v1 "S1")) 
  (print v1) ;; <- カーソルがこの位置だと下の層の v1 も同一変数と判定してしまう。
  (let ((v1 "S"))
    (print v1))) ;; <- この位置なら ok
  • let と let* の違い

使い方

単純な symbol 名の変更 (C-c C-v r)
カーソル位置にあるシンボルのローカルスコープも考慮し、y-or-n-p で確認しながら変更します。ローカルスコープで定義が発見できなかった場合は buffer 全体で変更します。
require している package を含めてすべての symbol の変更 (C-c C-v R)
この機能は近いうちに C-c C-v r に統合する予定です。*2別ファイルに分かれている定義も変更するのが先述の C-c C-v r との違いです。provide/require を正しく使ってあるライブラリのみ処理可能です。

ローカル変数 highlight

今までめんどうだったこと

  • 気になる変数を isearch highlight するために key stroke しないといけない
  • loop マクロの変数が分かりづらい。


ほんとは 1 stroke たりともしたくないので変数への視線を感じて highlight してほしいんですけどね。誰か革新的なインタフェースを考えてほしいです。多分キーボード、マウスだけでは無理ですね。

外部 elint

Emacs にはある程度、静的にソースを解析して警告を出してくれる elint というライブラリがついています。*3

何がうれしいか

ヘビーな Elisp の開発者にとっては複数バージョンの Emacsen で動くかをチェックするのは結構めんどうな作業だと思います。私が elint を使ってて特に便利だと思うのが、

  • 宣言していない変数へのアクセスを検出。
  • 定義していない関数呼び出しの検出。

しかし、起動中の Emacs はたくさんの Elisp を load しているため正確な結果が期待できません。また、当たらし目の elint は高機能な分処理が重く、lint している間ブロックしてしまい他の作業ができません。

そこで新しいプロセス内で elint を非同期で実行するようにしてみました。

使い方

elint を別プロセスで実行する (C-c C-v l)
設定がちょっと分かりづらく煩雑なのですが、それぞれのリストの先頭が lint をしたい elisp ファイル。残りが新しいプロセスの Emacs で load-path に追加されるディレクトリのリストです。require モジュールが初期 load-path 以外にある場合は設定してください。
(setq erefactor-lint-path-alist
   '(("/home/bob/.emacs.d/linting-file.el"
       "/home/bob/.emacs.d/misc"))

この機能はいずれ flymake にできるといいなぁと思っています。

elint を複数の Emacsen で実行する (C-c C-v L)
ここまで必要な人はあんまりいないとは思うのですが、いろいろなバージョンの Emacs それぞれで順番に elint できます。
(setq erefactor-lint-emacsen
    '("emacs-21" "emacs-22.1" "emacs-23.2" "emacs-current"))
はてな記法は複数段にしたいときは書きづらい気がするなぁ。

*1:C-c C-v を別のコマンドに設定している場合は以下の設定を変更して使ってください。

*2:多分。。

*3:Refactoring の範疇から離れてしまっています。