κeenのHappy Hacκing Blog | Lispエイリアンの狂想曲

.emacsの整理をした話 + EmacsとViとShellとLispを悪魔合体させたら超絶便利だった

やや長いタイトルですが・・・年末になって大掃除がやってきましたね。みなさんもそろそろ.emacsの大掃除をしましょう。

私の.emacsは元々1300行ちょいあってEmacsの起動に7~8秒(体感)かかってましたが大掃除&高速化をした結果800行弱、起動に1秒(体感)ほどになったので整理の仕方を共有しますね。

前提ですが、私はinitローダーとかは使ってません。全部init.elに書いてます。で、機能毎にページを作って(C-q C-l)ます。ただ、それだけだと視認性が悪いので見出しとしてC-u C-u C-u ;;を64個挿入して次の行にコメントで#付きのタイトルを付けてます。

具体的には

1
2
3
^L
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; #Lisp

こんな感じのものが機能毎に書かれてます。これでC-sM-x occurでハッシュタグのように検索することもC-vでスクロールしていって目grepすることもC-x ]で機能毎にジャンプすることもできます。

1. Emacsの最新版を使う

結構重要です。「標準のやつだと欲いこの機能がないから拡張パッケージ入れた」なんてのも最新版では改善されていたりします。例えば私はemacs-w3mを使っていましたが、EmacsのmasterブランチにはewwなるEmacs Lisp製のブラウザが入っているのでそれを使うようにしました。

ただ、これが絶対的正義かというとそうでもなく、パッケージで入れてない分 Ubuntuのインプットメソッドとの連携部分がなかったのでuim.elを入れる 必要が出てきたりと、面倒な部分もありました。Emacs標準のインプットメソッドはどうにも使いものにならず、 ddskkもuim-skkとコンフリクトする(というかC-j上書きとかありえない)ので使いません。インプットメソッドの切り替え部分は

1
2
(global-set-key (kbd "<hiragana-katakana>") #'uim-mode)
(global-set-key (kbd "<zenkaku-hankaku>") #'uim-mode) (autoload #'uim-mode "uim" nil t)

になりました。

2013-12-16追記
これは私が~/.XresourcesEmacs*useXIM: falseを書いていたのが原因でした。Emacs*useXIM: trueに書き換え、xrdb ~/.Xresourcesすると直りました。

2. 普段使わない設定は全部消す

基本ですね。私はsummary-edit.elだとかmultiverse.elだとかるびきちさんの本を読んで便利そうだから入れたものの、結局使わなかったものの設定&elispをごっそり削除。あとかなりの言語に対してデフォルトでauto-mode-alistが設定されていたのでauto-mode-alistの設定も全部消して、必要になったら書き足すようにしました。

3. 普段使っていても代替の効くものは削除

これは高速化の意味と自分の環境に依存しない意味があります。最近、自分のラップトップ以外でもEmacsを触ることが多くあって、デフォルトのキーを上書きして使ってる部分で何度も誤操作したのでそれを減らす目的です。bm.elC-x r SPCregister系やC-x C-SPCで対応(registerは覚えれば使い出がありそうなのでいつか解説書くかもです)、open-junk-file.el~/tmpを作って対応、recentf-ext.elhelm-file-buffersだとか。

あと全てhelm.elに置き換えてhelm.elanything.elが混在してる状態をどうにかしたかったのですが、php-completion.elかなにかが依存しててトドメを刺せませんでした。

あと、viewerの代替を探していたらタイトルにあるように悪魔合体が起きたので後で書きますね。

4. できる限り標準のものを使う

標準で提供されているパッケージはautoloademacsバイナリに組込まれてる(と思う)ので起動時のオーバーヘッドはありません。flymake.elruby-mode.elが標準で提供されてるのに気付いたのでそれを使ったりなど。一度(emacsroot)/lisp以下を眺めてみることをお勧めします。結構発見があるものです。

5.autoloadを使う

autoloadとはファイルの読み込みを必要になるまで遅らせる仕組みです。「必要になる」ってのはそのファイルで定義されている関数が呼ばれたときです。賢いrequireと思えば良いでしょう。

(autoload #'関数名 "関数が呼ばれたときに読むファイル名" nil interactivep)

みたいに使います。interactivepの部分はM-xで呼ぶものならt、そうでなければnilです。requireautoloadで書き換えていけば理論上起動時の読み込み0にできるのでかなり高速化できます。

が、実際は一々autoload書くのはしんどいので次です。

6.できる限りpackage.elを使う

package.elは必要な関数のautoloadを自動生成して読み込んでおいてくれるのでかなりの手間が省けます。そしてautoloadがあるのにrequireしてると折角のpackage.elの配慮が無駄になります。

自動生成されたautoloadelpa/パッケージのディレクトリ/パッケージ-autoloads.elにあるので確認しながらinit.elの邪魔なものを消していきます。これでかなりinit.elの行数が減ります。今まで無駄な設定していたんだなと気付きます。

7. eval-after-loadを使う

8割程の設定はautoloadで対応できるのですが、踏み込んだ設定をしているとパッケージの内部の関数を使ってしまってどうしてもその式が評価される前にパッケージが読み込まれている必要があることがあります。

そんなときはeval-after-loadを使います。名前のまんま、ロードした後でevalしてくれます。

(eval-after-load 'ファイル名
    '式)

の形で使います。複数の式を使いたい場合はprognを使って

(eval-after-load 'ファイル名
    '(progn
         式1
         式2...))

のように使います。あるパッケージの拡張パッケージなんかもここで読むと良いかもしれません。

8.その他

メールクライアントを標準のものにしようとしましたが、gnus.elはちょっと受け付けなくてその他はimapを喋らないので断念。でも色々調べてたらmewよりwanderlustの方が良いようなので使い初めました。表示が綺麗で良いですね。HTMLのレンダリングもemacs-w3mに頼らず標準のshr.elを使っているのも◎。

同じような経緯でJDEEをやめてmalabar.elを使うようにしました。ただ、私はmaven使いではないので微妙ではあります。まあ、そもそもプロジェクト単位でJavaを書くことがないってのもあるんですが。Androidのスケルトンがantなのでantでできたら嬉しいなーって。

EmacsとViとShellとLispを悪魔合体させた話

私はEmacsの狂信者ですが読み専のときはちょいちょいviを使うこともあります。片手で操作できるのは便利です。Emacsで読み専といえばview-modeです。そこでもhjklを使うべくview-mode-mapに手を加えてましたが、大掃除ということで全部削除。

その後でemacsroot/lisp/emulate/以下を読んでいるとなんかviのエミュレーターが3つも見付かりました。vi.elvip.elviper.elです。後者になるほどviとの互換性が高くなります。とりあえずはhjklが使えれば良いのでvi.elを使ってみたところ、ん〜…といったところ。vip.elと試して結局viper.elに落ち着きました。

(global-set-key (kbd "C-x C-q") #'(lambda ()
                    (interactive)
                    (toggle-viper-mode)
                    (force-mode-line-update)))

設定はこんな感じです。toggle-viper-modeしてもモードラインの表示が変わらないことがあったのでforce-mode-line-updateを加えました。

viper.elは単なるviのエミュレートだけではなく、Levelに応じて良い感じにemacsと悪魔合体してくれます。私は最高レベルの5にしました。”C-x C-s”など基本的なコマンドはそのまま使えるようになってます。:で始まるvi(ex)のコマンドも使えます。C-zでemacs<–>viを切り替えたり。非常に便利です。

尚、私はvi使いであってvim使いではないのでevilは使いません。

もう一つ、shellの話。今まではmultiterm × zshな感じでしたが、「できる限り標準のものを使う」方針でeshellに切り替えました。eshellはEmacs Lispで書かれたshellです。これが思った以上に便利です。るびきちさんの本では標準出力とエラー出力の切り分けができてないと書かれてましたがそれは修正されてるようです。

あとは/dev/killだとか>>>だとかバッファへのリダイレクトだとかgrepの上書きだとか色々楽しい拡張もあるのですが、一番はeshellがLispであること。eshell上で任意のEmacs Lisp式を実行できます。最近Emacs LispやLispに精通してきたので非常に有り難いです。
それにファイルを開くときもその後で同じディレクトリのファイルを開くことが多いので一旦 cdしてからfind-fileをするとアクセスし易くて捗ります。もう起動時にeshellが立ち上がるようにして、基本そこから操作するようにしてます。guakeもそんなに使わなくなりました。他の環境でも使えるので安心して依存できます。

で、ファイルを開くときはどうしてるかというと実はemacsのfind-fileではなくviの:e file-nameです。Emacs上でLispで出来たShellを使いつつviを動かしてます。かなり人を選びますが「EmacsのヘビーユーザーでLispに精通しててviを便利だと思ってる人」は試してみてはいかがでしょうか。

Written by κeen