Evil: EmacsをVimのごとく使う - 導入編

You underestimate the power of the dark side.

Star Wars: Episode VI - Return of the Jedi

EmacsLispで自由自在に拡張でき, エディタの枠におさまらず, コンピュータ上でのあらゆる創造的活動のための環境として発達してきました. しかし, 少なくともファイルを閲覧し編集するという操作に関しては, vi/Vimが非常に優れたインタフェースであることもまた事実です. 両者はそれぞれが根強いファンを抱え, 長らく宗教戦争を繰り返してきました.

この対立が止揚された結果として生まれたのがEvilです. Emacsのなんでもありな環境の上でVimをエミュレートすることで, EmacsでありながらVimの操作性を実現したのです.

本稿では, Evilとは何かということに始まり, 具体的な導入方法について解説します.

歴史

Evilについての理解を深めるためには, その前にずっと続いてきた開発の歴史について触れずにはいられません.

Emacsにおけるvi/Vimエミュレーションの歴史は, GNU Emacsの最初の公開リリースの翌年, 1986年*1まで遡ります. 現在は日本語入力システムSKKの作者として知られる佐藤雅彦先生*2によって, viの操作性をエミュレートするVIPが開発されました. 佐藤先生がEmacsを愛用していらっしゃるのは, EmacsLispインタプリタを備えているから(そしておそらくインタラクティブUIをLispで実装する環境として優れているから)*3ですが, UIの操作性, とくにCtrlキーを多用して指が疲れるという点はあまり好ましく思っていなかったようで*4, これはEmacsよりもvi/Vimを好む人たちにとっては常套句かと思います. けれど, Emacsが指の痛くなる馬鹿げたやり方に固執していたと考えるのは早計です. Richard Stallmanのもとに佐藤先生よりvip.elが送られ, 興味深いことに, 1988年にはVIP 3.5がEmacsの標準パッケージとして取り込まれたので, 彼は少なくともこのような代替的な操作方法が提供されることを疎ましく思っていたわけではないのでしょう.

佐藤先生のvip.elはその後も標準パッケージとしてメンテナンスされ続けますが, それとは別に1991年にAamod SaneによってVIP 4系統に改良され, 1993年頃まで開発が続きます. さらに, これらの実装を元に1994年にMichael KiferによってVIP-19*5が作られ, 1995年にViperという名前でふたたびEmacsの標準パッケージに取り入れられます. この後しばらく, Emacsをviキーバインドで操作するにはViperを使うという時代が続きますが, Viperはあくまでviのエミュレーションであるため, Vimの機能性に差をつけらていくこととなります. 2005年ごろからViperにオブジェクト単位の選択やビジュアルモードの機能を追加するパッチを当てるなどの試みもなされましたが, Viperの機能が本質的にVimに追いつくことはありませんでした.

2006年になって, Brad Beveridgeが, Viperを拡張してVimに近づけるプロジェクトextended-viperを立ち上げ, Alessandro Pirasのvimper*6に引き継がれた後, 2007年にJason SpiroによってVimpulseというプロジェクトになります. その後, メンテナが2009年にAlessandro, 2010年にVegard Øyeに交代しつつ, VimpulseはVimの主要な機能を実装し, 完成度を高めていきます. これらはすべて, 標準添付のViperには直接手を加えず, 追加機能の差分だけ実装していました. しかし, Viperのコードはレガシーで, 新しい機能と衝突する部分も多く, adviceや関数の再定義でViperのコードを修正すること(いわゆるモンキーパッチ)も多くなり, メンテナのVegardは, Viperをコアとして継ぎ足していくやり方に限界を感じはじめます.

これらの流れとは全く別に, 2009年からFrank Fischerによってvim-modeが開発されます. vim-modeの目的は, Viperに頼らないことで拡張性の高いVimエミュレーションを提供することでしたが, Frankは個人的に日常のワークフローで必要な機能やユーザから要望のあった機能を優先的に実装していたため, 決して十分な完成度が達成できているとは思っていなかったようです.

2011年2月に, Vegardが新たなVimpulseのコアを実装しはじめ, Viperへの依存をなくすことにしたのを受けて, FrankがVegardにVimpulseとvim-modeの2つのプロジェクトの合流を提案します. そして, 同じ目的のパッケージをそれぞれ独立に実装してきた知見を活かし, より拡張性が高く, Emacsに無理なく統合できるようなやり方でコア部分を再設計し, 残りの部分はVimpulseとvim-modeの実装を取り入れることでできるだけ多くのVimの機能をカバーしていく, ということで合意します. こうして, 2011年3月にスタートしたのがEvilプロジェクトです. Evilは新しいコアを使ってVimの操作性に関する主要な機能を実装し終え, 2013年2月にバージョン1.0.0がリリースされました.


設計思想

Evilの目的は, できる限り多くのVimの機能をエミュレートすることですが, ただやみくもにVimをエミュレートするのではなく, 過去の失敗に学んだ設計思想に沿った実装となっています. この設計思想と相容れない使い方をしようとすればただ不満が募るばかりで, ユーザにとっても開発者にとっても不幸なことです.

高い拡張性の確保

Evilがextensible vi layerの略であることからもわかるように, Vimpulseやvim-modeを捨てた理由, あるいはViperをコアとすることをやめて一から実装することになった理由は, 高い拡張性を確保するためです. この拡張性というのは, 将来あらたな機能を増やしたり, プラグインの作成を容易にするという面もありますが, どちらかといえばVimの豊富な機能を実装していくにあたり, 開発の後期に破綻しないためです.

Viperはviのエミュレーションのために作られたこともあって, Vimpulseの開発末期にはViperがコアであることがかえって不都合なことがしばしばあり, 欠けているVimの機能を実装するコストが大幅に増していきました. ViperはVimの機能を想定して開発されたわけではないので当然と言えば当然です. それでも高い完成度を達成できていたことは賞賛に値すると思います.

このような経緯から, EvilではVimの様々な機能を視野に入れて, それぞれの機能がなるべく独立に実装できるように, コア部分のコンセプトを明確にし, またできる限りコアを小さくするという目標が最初に掲げられました. コアを小さく保つことの一つの帰結として, Emacsにも備わっている機能はできるだけそのまま使い, EvilはあくまでEmacsとのインタフェースの差異を吸収することに注力する, ということが挙げられます.

Emacsの機能とうまく共存

Emacsに備わっている機能をそのまま使えることはそれ自体に意味があり, またEvilが作られた理由でもあります.

たとえば, Vimpulseの初期のバージョンでは, ビジュアルモードがEmacsのリージョンとは別に実装されていたために, Vimpulseの選択範囲にEmacsのリージョンコマンドを適用したり, EmacsのリージョンにVimpulseのオペレータを適用するということができませんでした. これでは何のためにわざわざEmacsを使っているのかわかりません.

VimpulseとEvilのユーザであるTim Harperによりメーリングリストに投稿された次の文章は, Evilの目指すところをよく言い表していると思います*7.

if it's a choice between interoperability with Emacs at the cost of 97% compatibility with vim, and 100% compatibility with them at the cost of reduced interoperability with Emacs commands, I would prefer sacrificing the vim compatibility.

If 100% vim compatibility were important to me, I would use vim.

(拙訳)
Emacs(の機能)と相互運用できてVimとの互換性が97%になってしまうのと, 100%互換な代わりにEmacsのコマンドが使えないのと, もしどちらかを選ぶなら, 互換性を犠牲にする方がいい. もし100%の互換性が大事なら, Vimを使えばいい.
implementations-list - Vimpulse and vim-mode from Tim Harper

Emacs本来の機能を利用できるようにするには, 大きくわけて2つのアプローチが考えられます.

  1. Vimのあらゆる機能を独自に実装し, Emacsのすべての機能に対してVim風のインタフェースを用意する
  2. 設計をEmacsと親和性の高いものにしておく(Emacsの機能はEmacsのやり方で使えるままにしておく)

もし, Vimとの互換性にこだわるなら, 前者を選ぶことになるでしょう. 前述の例で言えば, Emacsのすべてのリージョンコマンドに対して対応するオペレータを用意しておくということです. しかし, Emacsの標準パッケージは膨大であり, それらはすべてEmacsの哲学で実装されています. ひとくちに「Vim風のインタフェースを用意する」と言っても, 単にキーバインドを入れ替えればいい場合もあれば, リージョンコマンドとオペレータのように近い概念に置き換える必要があるもの, そもそも使い方のレベルで再設計が必要なものもあるでしょう. また, 非標準のパッケージにも何らかの形で対応しなければなりません. これらのすべてのコストをEvilの開発者が負うのは事実上不可能です. このような理由から, Evilでは後者のアプローチが採用されました.

とくに元々Vimを使っていたユーザからの不満の声として多いと感じるのは, Evilを使っていると何かの拍子にEmacsくささが姿を見せるという点です. 彼らはこれをEvilの欠陥であると見ているのかもしれませんが, これは意図されたことだと言えます.

それでも開発者のFrankは, この設計思想の下にユーザからの不満をすべて退けるつもりはなく, Timの投稿に対しても次のように述べています.

I completely agree that we should not focus on 100% vim compatibility (even sometimes Vim does things wrong IMO). We should just be prepared that sometimes incompatibilities or inconveniences may arise that are no problem for one user but are important for the other (and this with a good reason as the example above illustrates). So if the effort is not too big we should try to be as close as possible to vim without overemphasizing this goal.

(拙訳)
100%の互換性に注力すべきでない(し, 個人的にはときにVimのやり方は間違っている*8と思うことさえある)ことには完全に同意する. それでも, なにか非互換だったり不便だったりする部分が, 誰かにとって問題ないことでも, 他の誰かにとってはとても重大な問題かもしれないということは覚悟しておかないといけない. だから, 労力の許す限り, できるだけVimに近づけて, あまりこのこと(訳注:互換性にこだわらないこと)を強調しすぎない方がいい.
implementations-list - Vimpulse and vim-mode from Frank Fischer

実際, ユーザからの要望があれば, Vim風の使い方を維持できるように調整する場合があります. まずは要望を出してみて, 開発者にとって荷が重いようであれば, ユーザ自ら調整部分をプラグインとして実装することで貢献するというのが望ましいと思います.

インストール

それではEvilをインストールしてみましょう. Evilは(いまのところ)標準パッケージではないので, 自分でインストールする必要があります. ここではインストール方法と, Evilを有効化する設定について説明します. それ以外の細かい設定については設定編を見て下さい.

とにかくお試し

ここでは, 真っ新なEmacs環境にEvilをインストールして有効化する方法を解説します. 次のいずれかに当てはまる人は, この手順に従うとよいでしょう.

  • Emacsをはじめて使う人
  • 自分のEmacsの設定を汚さずに試してみたい人
  • 後述のインストール方法がよくわからなかった人

まず, 準備としてEmacs本体(バージョン24以降)がインストールされていることを確認して下さい.

次に, 以下のように書いたファイルを作り, init.elというファイル名でてきとうに新しく作ったディレクトリに保存しましょう.

;; Emacs directory
(when load-file-name
  (setq user-emacs-directory (file-name-directory load-file-name)))

;; Package management
(require 'package)
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)

(defun package-install-with-refresh (package)
  (unless (assq package package-alist)
    (package-refresh-contents))
  (unless (package-installed-p package)
    (package-install package)))

;; Install evil
(package-install-with-refresh 'evil)

;; Enable evil
(require 'evil)
(evil-mode 1)

続いて, init.elを保存したディレクトリで以下のコマンドを実行すると, Emacsの既存の設定を汚すことなく, Evilをインストールして試すことができます. はじめて実行したときは必要なファイルをダウンロードするため時間がかかります.

emacs -q -l init.el

Emacsを使うときに常にEvilを使うようにするには, ~/.emacs.d/init.elに上記の設定を書きます. (ただし, 既にEmacsを使っていて~/.emacsに設定を書いている場合は, ~/.emacs.d/init.elに移動する必要があります.) これで,

emacs

とするだけで, Evilが有効な状態のEmacsが起動するようになります.

以上の手順に従った場合は, これ以降のインストール方法を読む必要はありません.

リリース版のインストール

Emacs 24以降もしくはpackage.elを入れたEmacs 23が必要です. まず設定ファイルに以下のように書きます.

(require 'package)
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)

Emacsを起動したら, M-x package-install RET evil RET (M-xAltxの同時押し)と入力します. これでEvilがインストールされます.

Evilを現在のEmacsセッションで有効にするには, M-x evil-mode RETとします.

起動時に有効化

設定ファイルに以下のように書くと, 次回起動時にはEvilが自動的に有効になります.

(require 'evil)
(evil-mode 1)
開発版のインストール

リリース版ではなく開発版をインストールするには, 3つの方法があります.

MELPAは, Emacs標準のパッケージ管理の仕組みを使うもので, インストール方法やビルド方法はパッケージ提供者側*9が決めたものに従い, パッケージ提供者によって登録されたバージョンがインストールされます. El-Getは, ユーザが自らパッケージのダウンロード元, ビルド方法などをレシピという形で指定する*10パッケージ管理方法で, Emacs標準のやり方ではありませんが自由度が高く玄人向きです.

MELPAを使う場合は, リリース版と同様で, package-archivesに指定するURLが異なるだけです. すなわち,

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.milkbox.net/packages/"))
(package-initialize)

とすれば, あとはM-x package-install RET evil RETでインストールされます.

El-Getを使う場合は, まず, El-Getのドキュメントの手順に従ってEl-Getをインストールする設定を書きます. Evilのインストールそのものは, 設定ファイルでel-get関数を呼ぶようにするか, あるいはEmacs起動後にM-x el-get-install RET evil RETとします.

;; El-Get
(add-to-list 'load-path (locate-user-emacs-file "el-get/el-get"))
(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "http://raw.github.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

;; Install evil
(el-get-bundle evil)

El-Getでのインストールには, gitコマンドとmakeコマンドが必要です. デフォルトでinfoファイルも生成するようになっているので, Texinfo(のmakeinfoコマンドとtexi2pdfコマンド)も必要です. もしinfoファイルの生成が不要なら,

;; El-Get
(add-to-list 'load-path (locate-user-emacs-file "el-get/el-get"))
(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "http://raw.github.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

;; Install evil
(el-get-bundle evil :info nil)

とすることで, Texinfoがなくてもインストールできます.

手動でインストールするには, EvilとUndo Treeリポジトリからgit cloneして, それぞれのディレクトリをload-pathに追加します. リポジトリのURLは以下の通りです. バイトコンパイルしたい場合は, Undo Treeは手動(emacs --batch -f batch-byte-compile undo-tree.el)で, Evilはmakeします.

Evil
git://gitorious.org/evil/evil.git
Undo Tree
http://www.dr-qubit.org/git/undo-tree.git

使い方

Evilを実際に使うには, EmacsVimについてどれくらい知っているかによって, 知るべきことが変わってきます.

Emacsからの移民もしくはvi/Vim初心者

Evilにはステートと呼ばれる状態*11があります. 起動時にはノーマルステートになっていて, モードラインに<N>と表示されます. ノーマルステートは, 主に移動やコマンドの実行のための状態で, 文字を打ってもそのまま入力されません. 文字を入力するにはi, a, o等のキーを入力して挿入ステート(<I>と表示されます)に移る必要があります. 挿入ステートでESCキーを押すとノーマルステートに戻ります.

Emacsに慣れ親しんでいて, 万が一操作方法がよくわからなくなってしまった場合は, C-zEmacsステートに移ることができます. EmacsステートではEvilの機能は無効化されて, 従来通りのEmacsとして操作できます. ふたたびEvilに戻るにはもう一度C-zを入力します. しかしながら, Emacsステートを使わなくとも, C-x, M-x, C-g等のキーは, 従来のEmacsと同じように動作し, 挿入ステートでのキーバインドも従来のEmacsキーバインドに近い(異なる部分をEmacs本来のものに戻す方法は設定編で解説します)ので, ノーマルステートと挿入ステートの違いをきちんとおさえておけば, よほどのことがない限りEmacsステートのお世話になることはないでしょう. また, 一時的にEmacsキーバインドを使いたいだけなら, \キー(evil-execute-in-emacs-stateコマンド)で, その次のコマンドをEmacsステートで実行し, 直後に元のステートに戻すことができます.

他にどんなステートがあるか, 効率よい編集操作をするにはどうしたらよいかを学ぶには, Vimチュートリアルを利用するとよいでしょう. tutor(英語)もしくはtutor.ja.utf-8(日本語)をダウンロードして, Evilを有効にしたEmacsで開きましょう. ただし, これは本来Vimのためのチュートリアルなので, いくつか注意する点があります.

  • 2.7 Evilでは未実装: U*12
  • 3.2 日本語版ではうまくいかないかも
  • 4.1 EvilではC-gはキャンセルコマンド
    • ファイル情報はモードラインに表示される
  • 6.5 Evilでは未実装: :set, \c
    • 検索オプションは別の方法で設定可能(設定編を参照)
  • 7.1 EvilではEmacsのヘルプコマンドが呼び出される: :help
  • 7.2 Evilでは~/.emacs~/.emacs.d/init.elを使う
  • 7.3 Evilではバージョン1.0-dev以降
Vimからの移民

Vimを使っていたユーザにとってEvilを使うことは容易いでしょう. ただし, Emacs固有の部分に関しては多少気にする必要があります.

  • 機能の名称の違い
    • Vimの「モード」はEvilでは「ステート」
    • yank/paste(Vim)とkill/yank(Emacs)等
  • 設定は~/.emacs~/.emacs.d/init.elEmacs Lispで書く
    • Vimスクリプトでは書けず, 今後も書けるようになる可能性は極めて低い*13
    • :setでのオプション設定もいまのところできない*14
  • おかしなことになったらとにかくC-g(キャンセル)を連打
  • C-cESCとは違う意味(同じ意味にする方法は設定編を参照)
  • ヘルプの代わりにM-x describe-* (*key, bindings, functionなど)

Evilの最初の目標は, Vimの操作性に関する主要な機能を実装することで, 先日これが達成されバージョン1.0.0がリリースされました. しかし, マイナーな機能やプラグインで提供されるべき機能などは実装されていない場合もあります. マイナーな機能のうち優先的に実装してほしいものがある場合は, 課題トラッカーに要望を出しましょう.

Evilで実装されていなくても, Vimの一部の機能や, Vimプラグインの機能の多くには, Emacsの標準/非標準のパッケージで代替できるものがあります. 以下に主なものを挙げておきます. Evil用に開発されたプラグインについては, 設定編で利用方法を解説します.

機能/プラグイン Emacsのパッケージ
C-a, C-x evil-numbers
g;, g, goto-chg.el
<Leader> evil-leader
:grep grep.el (標準) (M-x grep), color-moccor.el
:make compile.el (標準) (M-x compile)
タグ検索 etags.el (標準)
スペルチェック ispell.el (標準), flyspell.el (標準)
差分モード Ediff (標準)
タブページ ElScreen
vundle.vim, neobundle.vim package.el (標準), El-Get
vimfiler.vim dired.el (標準), dired-x.el (標準)
project.vim eproject
vcs.vim, fugitive.vim vc.el (標準), Magit
rails.vim evil-rails
surround.vim evil-surround
align.vim align.el (標準)
NERD_commenter.vim evil-nerd-commenter
commentop.vim evil-operator-comment
camelcasemotion.vim evil-little-word
textobj/between.vim evil-textobj-between
neosnippet.vim YASnippet
neocomplcache.vim Auto Complete Mode
unite.vim, fuf.vim Anything, Helm
vimshell.vim Eshell (標準)
quickrun.vim quickrun.el
syntastic.vim, watchdogs.vim Flymake (標準)
xxd, vinarise.vim hexl.el (標準)

EvilにあってVimにない機能もあります. たとえば, Evilでは置換の際にもパターンにマッチした部分をハイライトでき, さらに置換結果をバッファ内にプレビューできます.

サポート等

英語のみですが, PDFの公式ドキュメントも提供されています. 他のユーザから寄せられた使い方のヒントなどはEmacsWikiのページにまとめられています. バグの報告や機能の要望は課題トラッカーとメーリングリストの両方で受け付けています(どちらも登録なしに投稿できます). バグを報告する際には開発Wikiに書かれた注意点に目を通すとよいでしょう.

公式ドキュメント
https://gitorious.org/evil/evil/blobs/raw/doc/doc/evil.pdf
Wiki
http://emacswiki.org/emacs/Evil
開発Wiki
https://bitbucket.org/lyro/evil/wiki/Home
課題トラッカー
https://bitbucket.org/lyro/evil/issues?status=new&status=open
メーリングリスト
implementations-list@lists.ourproject.org
メーリングリスト(購読)
http://lists.ourproject.org/cgi-bin/mailman/listinfo/implementations-list
メーリングリスト(ログ)
https://lists.ourproject.org/pipermail/implementations-list/

おわりに

EvilはEmacs上でVimをエミュレートするためのプロジェクトですが, その源流はVimが生まれる前まで遡り, GNU Emacsの歴史と同じくらい古くから受け継がれてきたものです. 長い歴史と現代のモダンな設計によりその完成度は今までにないものとなっており, 多くの人がこのプロジェクトに期待し, また貢献しています.

本稿は, Evilへの貢献の一環として, 導入方法を日本語で提供することを目的として書かれました. これにより, 日本人によるEvilへの貢献が増すことを願っています.

謝辞

本稿にレビューコメントを寄せて下さったid:supermomongaさんとid:hakobe932さんに感謝いたします.

  1. 導入編
  2. 設定編
  3. 拡張編
  4. 付録

*1:これはvip.elのコメントとして書かれた年であって, 初期の開発はもっと前に始まっていた可能性もあります

*2:筆者の元指導教員のため「先生」の敬称を使わせていただきます

*3:現在もご自身の計算と論理に関する理論の実装をEmacs上でやっていらっしゃいます

*4:これは御本人から酒の席で伺ったことなので, 正確に意図を汲み取れたかどうかは自信がありません

*5:Emacs 19に対応したVIPという意味のようです

*6:EmacsWiki: Vimpulseの情報で, オンライン上にはないようです

*7:この投稿に対して開発者のVegardとFrankも同意の旨を返信しています

*8:たとえば, このメールの少し前のやりとりで, Vimの繰り返し処理のやり方では本質的に扱えないものがあることが指摘されています

*9:必ずしもパッケージの開発者ではなく, パッケージを登録し, バージョンアップに追従する作業をやっている人のことです

*10:EvilのレシピはEl-Getそのものに含まれています

*11:Vimではこれをモードと呼びますが, Emacsには既に(メジャー/マイナー)モードの概念があるので, 混同しないためにステートと名付けられています

*12:課題として登録されています: https://bitbucket.org/lyro/evil/issue/144/u-does-not-do-anything

*13:Frankがメーリングリストで理由を述べています: https://lists.ourproject.org/pipermail/implementations-list/2011-September/001213.html

*14:有志で実装されるかもしれません: https://lists.ourproject.org/pipermail/implementations-list/2013-February/001776.html