読者です 読者をやめる 読者になる 読者になる

Evil: EmacsをVimのごとく使う - 設定編

article emacs evil

Then you will see, it is not the spoon that bends, it is only yourself.

The Matrix

EmacsあるいはVimに慣れ親しんでいれば, Evilを使うのにある程度は勝手がわかるものの, 逆にしっくりこない点も多いでしょう. EvilはEmacsの機能との相互運用性を重視していることから, Vimユーザにとって不慣れな点が生じることは避けられず, EvilがVimをエミュレートする以上, Emacsユーザにとって不慣れな点が生じることも避けられません. 本稿では, どちらに慣れ親しんだユーザにとっても快適に使えるようにEvilをカスタマイズするためのヒントを紹介します. ただし, いくらカスタマイズしても完全なVimや完全なEmacsになることはありません. 使い勝手をよくして自分の好みに合わせながら, できるだけEvilのやり方に慣れていくように努めることが大切でしょう.

キーマップ

Evilの操作性を好みに合うようにするために一番多用するのは, キー設定でしょう. キー設定をするためには, それぞれのステートでどのようなキーマップが使用されるかに加えて, Emacsのキーマップの優先順位について理解する必要があります.

Emacsのキーマップは階層化されていて, あるキーを押したときには, 上の層から順に探索し, 条件に従って使うキーマップを決めます*1.

  1. overriding-terminal-local-mapが定義されていればこれを使う
  2. overriding-local-mapが定義されていればこれを使う
  3. 以下のいずれかのキーマップにキーが存在していれば, そのキーマップを使う
    1. カーソル位置の文字のkeymapプロパティ
    2. emulation-mode-map-alists中のアクティブなモードのマップ
    3. minor-mode-overriding-map-alist中のアクティブなモードのマップ
    4. minor-mode-map-alist中のアクティブなモードのマップ
    5. カーソル位置の文字のlocal-mapプロパティ
    6. (current-local-map)

これらの条件によりキーマップが選択され, 結果としてそのキーマップ中に該当するキーがなければ, (current-global-map)を使います. (current-global-map)にも該当するキーがなければ, 「key is undefined」のようなメッセージが表示されます.

ふつうのメジャーモードのキーマップは(current-local-map), マイナーモードのキーマップはminor-mode-map-alistに設定されています. そしてminor-mode-overriding-map-alistは, メジャーモードがマイナーモードのキーマップよりも優先するキーを設定するためにあります.

Evilは内部的にはマイナーモードで実装されていますが, その役割はEmacs全体のキー操作を根本から変えるものであるため, ふつうのメジャーモードやマイナーモードよりも優先してキーを受け取れなければなりません. このため, Evilに関するキーはすべてemulation-mode-map-alistsに設定され*2, ふつうのメジャーモードやマイナーモードよりもEvilのキーの方が基本的には優先されます. 逆に, Evilのキーが他のキーバインドに奪われてしまう場合は, emulation-mode-map-alistsよりも上位の層にそのキーがないか確認するとよいでしょう. たとえば, カーソル位置の文字のkeymapプロパティが設定されている場合はこれが優先されることを知らないと, このキーマップの存在に思い至ることすら困難です.

特定のステートでのキー

あるステートで使うキーを設定するには, 設定ファイル(~/.emacs~/.emacs.d/init.el等)の(require 'evil)の後に, (define-key evil-state-state-map key definition)の行を追加します. たとえば, モーションステートで;:(evil-ex)に割り当てるには,

(define-key evil-motion-state-map (kbd ";") #'evil-ex)

とします.

statenormal, insert, visual, operator, replace, motionのいずれかです. normalはノーマルステート, insertは挿入ステート, visualはビジュアルステート(範囲選択中のステート), operatorはオペレータ待機ステート(オペレータを入力してから後続のキーを入力するまでのステート), replaceは置換ステート(Rしたときのステート), motionは編集を伴わない状態を表すためのステートです. motionステートで定義されたキーはnormalvisualでも使えます. normalで定義されたキーはvisualでも使えます.

definitionには上の例のようなコマンドのシンボルの他に, 文字列(キーボードマクロとして動作), キーマップ(プレフィックスキーの定義になる)なども渡すことができます. 詳しくはM-x describe-function RET define-key RETを参照して下さい.

既に定義されているキーを削除するには, define-keydefinitionとしてnilを渡します. たとえば, 挿入ステートでC-yをふつうのEmacsのコマンド(yank)にするには, evil-insert-state-mapからC-yを削除します.

(define-key evil-insert-state-map (kbd "C-y") nil)

初期状態でどのステートにどんなキーが定義されているかはevil-maps.elを見るとわかります. また, evil-state-state-mapの代わりにevil-state-state-local-mapを使うと, バッファローカルなキーバインドを定義できます.

初期状態では, 挿入ステートでもいくつかのキーにはVimの挿入モードと同様のコマンドが割り当てられています. もし挿入ステートでのキーバインドを完全にEmacs互換にしたい場合は, 次のようにします.

(setq evil-insert-state-map nil)
特定のモードの特定のステートでのキー

特定のモードが有効な場合には通常, mode-name-mapのようなキーマップで定義されたキーが有効になりますが, これらはemulation-mode-map-alistsで定義されているEvilのキーバインドより優先順位が低く, またEvilのステートごとに違ったキーバインドにすることができません.

evil-define-keyコマンドを使うと, 特定のモードが有効な場合のキーを, 優先順位を気にすることなく特定のステートでだけ定義できます. (evil-define-key state mode-map key definition)の形で用いて, stateにEvilのステート, mode-mapに特定のモードで有効なキーマップを指定する以外は, define-keyと同様です. たとえば, view-modeのモーションステートでのvに, view-modeを終了するコマンドを割り当てるには, 次のようにします.

(evil-define-key 'motion view-mode-map (kbd "v")
  #'(lambda () (interactive) (view-mode 0)))

statenilを指定すると, すべてのステートで有効なキーを定義できます.

その他のEvilのキーマップ

Evilにはステートごとのキーマップ以外に以下のようなキーマップがあります.

evil-window-map
ウィンドウ移動キー(C-w)を押した後のキーを定義するキーマップ
evil-outer-text-objects-map
OP aに続く, テキストオブジェクトを選択するキー(例: daww)を定義するキーマップ
evil-inner-text-objects-map
OP iに続く, テキストオブジェクトを選択するキー(例: vibb)を定義するキーマップ
evil-ex-search-keymap
検索(/?等)ミニバッファでのキーマップ(検索モジュールがevil-searchの場合のみ有効)
evil-ex-completion-map
コマンドライン(:)でのキーマップ
evil-read-key-map
後続の文字を利用するオペレータ("f)で, 文字を読み取るときのキーマップ

これらのキーマップを変更する場合はdefine-keyを使います.

ふつうのEmacsのキーマップを優先

前述の通り, Evilのキーは他のメジャーモード・マイナーモードのキーよりも優先されます. もし特定のモードで, Evilのキーよりも優先してそのモードのすべてのキーを使いたい場合は, evil-make-overriding-mapを使って, そのモードのキーマップの優先順位を上げる必要があります. (evil-make-overriding-map map state)の形で用いて, mapには優先したいキーマップ, stateにはどのステートのときに優先するかを指定します. stateを省略すると全ステートで優先されます. たとえば, ノーマルステートでhowm-menu-mode-mapの全てのキーを優先するには次のようにします.

(evil-make-overriding-map howm-menu-mode-map 'normal)

むやみに優先順位を上げると, Evilの移動キー(hjkl)などが覆い隠されて, 優先されたモードのコマンドが呼び出されるようになってしまうことがあります. これを避け, 移動キーはEvilのコマンドのままにするには, evil-add-hjkl-bindingsを使います. (evil-add-hjkl-bindings map state)の形で用い, 引数の意味はevil-make-overriding-mapのものと同じです. たとえば, ノーマルステートでmew-message-mode-maphjkl以外の全てのキーを優先するには以下のようにします.

(evil-make-overriding-map mew-message-mode-map 'normal)
(evil-add-hjkl-bindings mew-message-mode-map 'normal)

移動キーを設定するついでに, その他のキーを設定することもできます. 次の例では, ノーマルステートでmew-summary-mode-maphjkl以外の全てのキーを優先した上で, hlmew-summary-mode-mapのキー, GJK;evil-motion-satate-mapのキーを使うようにしています.

(evil-make-overriding-map mew-summary-mode-map 'normal)
(evil-add-hjkl-bindings mew-summary-mode-map 'normal
  "h" (lookup-key mew-summary-mode-map "h")
  "l" (lookup-key mew-summary-mode-map "l")
  "G" (lookup-key evil-motion-state-map "G")
  "J" (lookup-key evil-motion-state-map "J")
  "K" (lookup-key evil-motion-state-map "K")
  ";" (lookup-key evil-motion-state-map ";"))

キーに関すること以外のEmacsの機能との相互運用については後の節で触れます.

カスタマイズ

続いて, Emacs標準のカスタマイズ機構を用いて設定できるオプションについて解説します.

Evilのカスタマイズオプションは, Evilの挙動をVimに近づけるか, あるいはEmacsに近づけるかを制御するためのオプションが中心ですが, Vimのオプションに相当するものもあります. Vimに慣れ親しんだユーザのために, 相当するVimのオプションも明記しました. Vimの挙動にできる限り近づけたい場合は, 後の節も参照して下さい.

オプションを実際に設定する場合, 2通りの方法があります. Emacs標準のカスタマイズ機構を専用のUIで操作する方法と, 設定ファイルにLisp式を書く方法です. 前者の場合, M-x customize-group RET evil RET (M-xAltxの同時押し)とすると, 設定項目一覧が表示され, マウスクリックで設定を変更できます(設定を変更した場合は保存するのを忘れないようにしましょう). 後者の場合は, ~/.emacs~/.emacs.d/init.elで,

(setq evil-cross-lines t
      evil-search-module 'evil-search
      evil-ex-search-vim-style-regexp t)
(require 'evil)
(evil-mode 1)

のように, Evilをrequireするよりも前に, setqで設定します. もしもEl-Getを使っている場合は,

(setq evil-cross-lines t
      evil-search-module 'evil-search
      evil-ex-search-vim-style-regexp t)
(el-get-bundle evil)
(require 'evil)
(evil-mode 1)

のように, (el-get-bundle evil)よりも前に設定しなければならないことに注意しましょう.

setqにはオプションの名前と設定値を書いていきます. 以下の説明では, 設定値としてのシンボルはそのままシンボル名を書いていますが, Lispの式として表現する際には'をつける必要があります(ただし'(...)の中ではつけません).

すべてのカスタマイズオプションの一覧は付録に記載してあります. ここでは重要なもののみを紹介します.

evil-cross-lines
boolean
初期値
nil
バージョン
1.0.0
Vim
whichwrap

カーソル移動時に行をまたぐかどうか. tVimwhichwrap=b,s,h,l,<,>,[,]に相当し, nilVimwhichwrap=[,]に相当します. Vimの初期値とは異なります.

evil-move-cursor-back
boolean
初期値
t
バージョン
1.0.0
Vim
-

挿入ステートを抜けるときにカーソルを後退するかどうか. また, nilの場合, Vimとは違ってカーソルを改行文字の上に移動することができるようになります.

evil-want-C-i-jump
boolean
初期値
t
バージョン
1.0.0
Vim
-

C-iをジャンプコマンド(evil-jump-forward)に割り当てるかどうか. デフォルトでVimと同様にC-iはジャンプコマンドに割り当てられます. デフォルト設定のままだと, TABC-iの区別がないため, ノーマルステートやビジュアルステートではTABで自動インデントなどの操作ができません.

evil-search-module
isearch | evil-search
初期値
isearch
バージョン
1.0.0
Vim
-

検索に使うモジュール. isearchEmacsに組み込みの検索モジュール, evil-searchはEvil独自の検索モジュールです. evil-searchを選択すると, Vimの検索により近い挙動になります.

evil-ex-search-vim-style-regexp
boolean
初期値
nil
バージョン
1.0.0
Vim
-

Vim風の正規表現を使うかどうか. nilの場合は従来のEmacs正規表現を用います. tの場合はVimと互換な正規表現を内部でEmacs正規表現に変換します. Vimと違い, デフォルトではEmacs正規表現を用います. この設定はevil-search-moduleevil-searchの場合のみ有効です. evil-search-moduleisearchの場合は常にEmacs正規表現を用います.

evil-esc-delay
number
初期値
0.01
バージョン
1.0.0
Vim
-

ESCが押された際に後続のキーを待つ秒数. 端末では単体のESCM-を区別することができないため, ごく短い間に(事実上同時に)他のキーが押された場合のみM-として認識する必要があります. 端末そのもの(screentmux)でも同様に短い時間待つ設定がある場合は, そちらの調整も必要です.

Emacsの機能との共存

Emacsの機能のキーマップとEvilのキーマップの衝突を防ぐ方法は前の節で説明した通りです. この節ではそれ以外にEmacsの機能をEvilから扱いやすくする方法を解説します. EvilでEmacs本来の機能をうまく扱うための設定の一部は, Evil本体にevil-integration.elとして組み込まれていて, ここで解説する内容の具体例になっています.

ここで解説する設定は(require 'evil)の行よりも後に書きます.

モードごとのデフォルトのステート

特定のモードでの初期ステートを指定するには, (evil-set-initial-state mode state)を使います. バッファ内容の編集を伴わないようなモードをモーションステートにしたいといったような場合に使います. たとえば, view-modeがモーションステートになるようにするには,

(evil-set-initial-state 'view-mode 'motion)

とします(実際にはこの設定は不要で, view-modeは元からモーションステートになるように設定されています). モードには本来メジャーモードを指定すべきですが, 上記の例のようにマイナーモードを指定することもできます.

EmacsのコマンドをEvil化

ほとんどのEmacsのコマンドは, Evilからもふつうに使えますが, Evil独自の機能(繰り返しやビジュアルステート)との親和性を高めるための調整方法も用意されています. Evilに慣れてきて, Evilの機能とEmacsのコマンドがうまく組み合わさっていないことに気づいたら, 以下の宣言が必要かどうか検討してみましょう.

(evil-declare-not-repeat command)
commandを繰り返し操作の対象外にします.
(evil-declare-abort-repeat command)
commandを実行したら繰り返す操作の記録を中止します.
(evil-declare-change-repeat command)
繰り返し操作のためにcommandによるバッファの変更点を記録します. 通常はキー入力しか記録せず, 多くの場合はそれで問題ありません. 自動補完メニューが表示されるのを待ってから選択する場合など, キー入力の再現がバッファの変更内容を再現しないようなコマンドに対してはこれを設定するとよいでしょう.
(evil-declare-motion command)
commandを移動コマンドとして宣言します. 移動コマンドはビジュアルステートを終了しません.

EmacsのリージョンコマンドをEvilのオペレータにするといったような, 高度なEvil化の方法については拡張編を参照して下さい.

事例紹介: SKKとの共存

Emacsのパッケージの機能とEvilの機能はしばしば衝突します. キーマップの定義やコマンドのEvil化で済む場合は簡単ですが, それでは済まない場合も多々あります. 開発者にメーリングリストで対策を仰ぐのも一つの方法ですが, ここでは自力でうまく動くようにする一例を紹介します.

日本語入力にSKKを使っていると, いくつかの機能がEvilと衝突します.

SKKには入力モードに応じてカーソルの色を自動的に変更する機能がありますが, Evilもステートに応じてカーソル形状を変更するため, 調整が必要です. 挿入ステートの場合だけ, カーソルの制御をSKKに任せるのがよいでしょう. defadviceで関数そのものを置き換えて, ad-do-itで元の関数を呼び出すというイディオムを使うと, 以下のように書けます.

(defadvice update-buffer-local-cursor-color
  (around evil-update-buffer-local-cursor-color-in-insert-state activate)
  ;; SKKによるカーソル色変更を, 挿入ステートかつ日本語モードの場合に限定
  "Allow ccc to update cursor color only when we are in insert
state and in `skk-j-mode'."
  (when (and (eq evil-state 'insert) (bound-and-true-p skk-j-mode))
    ad-do-it))
(defadvice evil-refresh-cursor
  (around evil-refresh-cursor-unless-skk-mode activate)
  ;; Evilによるカーソルの変更を, 挿入ステートかつ日本語モードではない場合に限定
  "Allow ccc to update cursor color only when we are in insert
state and in `skk-j-mode'."
  (unless (and (eq evil-state 'insert) (bound-and-true-p skk-j-mode))
    ad-do-it))

Evilの検索(evil-searchモジュール)は, 入力するごとに表示をアップデートしていくので, 日本語の変換途中にもアップデートが起きてしまい入力が困難です. SKKの未確定状態ではアップデートを抑制するとよいでしょう. 上の例と同じように, defadviceを使って以下のように書けます.

(defadvice evil-ex-search-update-pattern
  (around evil-inhibit-ex-search-update-pattern-in-skk-henkan activate)
  ;; SKKの未確定状態(skk-henkan-mode)ではない場合だけ, 検索パターンをアップデート
  "Inhibit search pattern update during `skk-henkan-mode'.
This is reasonable since inserted text during `skk-henkan-mode'
is a kind of temporary one which is not confirmed yet."
  (unless (bound-and-true-p skk-henkan-mode)
    ad-do-it))

Vimの再現性を高める

Emacs本来の機能を損なわないために, デフォルト設定ではEmacs寄りの設定とVim寄りの設定の中間になっています. このため, Vimと全く同じ挙動を期待した場合にはしばしばうまくいかないでしょう. この節では, Vimに慣れ親しんだユーザのために, できる限りVimに近い挙動にするためにどんな設定をすればいいか解説します.

多くはVimの機能や設定に対応するEmacsのやり方の紹介ですが, 中にはEmacsの機能を犠牲にしてVimの挙動を愚直に再現しようとするものもあります. そのようにVimのやり方に固執すると, Emacsの機能も最大限に利用するという目標から外れるおそれがあることには留意しておきましょう.

カスタマイズ

カスタマイズオプションで設定できる範囲で, できる限りVimに近づけるには以下のようにします(これは(require 'evil)よりも前に書きます).

(setq evil-want-C-u-scroll t
      evil-search-module 'evil-search
      evil-ex-search-vim-style-regexp t)

ただし, これはVimのデフォルト設定と対応しているわけではなく, Vimで以下のように設定した状態に近いものとなります.

set autoindent
set smartcase
set hlsearch
set incsearch
set shiftwidth=4
set shiftround
set whichwrap=[,]
括弧の対応をハイライト

デフォルトでは対応する括弧のハイライトは有効になっていないので, 設定ファイルで次のようにします.

(show-paren-mode t)
バッファの終端を明示

Vimのように~でバッファの終わりを明示する方法はありませんが, 他のやり方で同等のことが実現できます.


GUI版のEmacsを使っている場合は, 以下のようにするとバッファの終わり以降が左側の縁のところに明示されます.

(setq indicate-empty-lines t)

GUI版ではもう少し凝った方法でバッファの範囲を明示することもできます. バッファがまだ上下に続く場合は矢印が, 上限または下限の場合は鉤印が表示されます. 以下のように設定します.

(setq indicate-buffer-boundaries 'left)


また, 拙作のend-mark.elを使うと, GUI版・コンソール版(emacs -nw)によらず, バッファの終端に[EOF]と表示できます. インストールするには, 上記ファイルを~/.emacs.d/等に保存し, 以下の設定を書きます.

(add-to-list 'load-path user-emacs-directory)
(require 'end-mark)
(global-end-mark-mode)
ファイル末尾で必ず改行

設定ファイルに以下のように書きます.

(setq require-final-newline t)
単語境界をVim互換に

単語の境界をどう判別するかは, 基本的にはEmacsの仕組みをそのまま用いているため, Vimとはわずかに異なります. 具体的には, Vimと違い「_」を単語の境界とみなします. Vimと同じように「_」を単語の一部とみなすようにするには, 設定ファイルに以下のように書きます.

(modify-syntax-entry ?_ "w" (standard-syntax-table))
C-cESCに, C-c/ESCでキャンセル

VimのようにC-cESCのような役割にするには, 以下のようにします.

(defun evil-escape-or-quit (&optional prompt)
  (interactive)
  (cond
   ((or (evil-normal-state-p) (evil-insert-state-p) (evil-visual-state-p)
        (evil-replace-state-p) (evil-visual-state-p)) [escape])
   (t (kbd "C-g"))))
(define-key key-translation-map (kbd "C-c") #'evil-escape-or-quit)
(define-key evil-operator-state-map (kbd "C-c") #'evil-escape-or-quit)
(define-key evil-normal-state-map [escape] #'keyboard-quit)

この設定では, Vimの挙動により近づけるために, C-cESCともにキャンセルの意味も持たせています.

C-a/C-xでインクリメント/デクリメント

EvilそのものにはC-a/C-xによるインクリメント/デクリメントは含まれていませんが, evil-numbersで提供されています.

MELPAを使っている場合はM-x package-install RET evil-numbers RETでインストールされます. それ以外の場合は, evil-numbers.elをダウンロードして, load-path内のディレクトリに保存しましょう.

初期設定ではどのキーにも割り当てられないので, 以下のように設定します.

(define-key evil-normal-state-map (kbd "C-a") #'evil-numbers/inc-at-pt)
(define-key evil-normal-state-map (kbd "C-x") #'evil-numbers/dec-at-pt)

ただし, この設定はEmacsC-xを無効にするため, 多くのEmacsの機能が利用できなくなります. 提供元の設定例では, 以下のようにC-c +/C-c -に割り当てる設定が推奨されています.

(define-key evil-normal-state-map (kbd "C-c +") #'evil-numbers/inc-at-pt)
(define-key evil-normal-state-map (kbd "C-c -") #'evil-numbers/dec-at-pt)
mapleader

EvilそのものにはVimmapleaderに相当する機能は実装されていませんが, evil-leaderで提供されています.

MELPAを使っている場合はM-x package-install RET evil-leader RETでインストールされます. それ以外の場合は, evil-leader.elをダウンロードして, load-path内のディレクトリに保存しましょう.

たとえば, Vimlet leader=",", map e :edit , map b :buffer に相当する設定をするには,

(require 'evil-leader)
(evil-leader/set-leader ",")
(evil-leader/set-key
 "e" #'find-file
 "b" #'switch-to-buffer)

とします.

コマンドラインレジスタから貼り付け

いまのところ, Evil本体のコマンドラインではC-rレジスタからの貼り付けに割り当てられていない(バージョン1.0.0)か, 割り当てられてはいるもののVimコマンドラインで使えるカーソル下のオブジェクトの貼り付け(C-r C-w等)は実装されていません(バージョン1.0-dev). evil-ex-registers.elを用いると, これらが使えるようになります.

インストールするには, evil-ex-registers.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'evil-ex-registers)
(define-key evil-ex-search-keymap (kbd "C-r") #'evil-ex-paste-from-register)
(define-key evil-ex-completion-map (kbd "C-r") #'evil-ex-paste-from-register)
タブページ

Vimのタブページに相当するものはEmacsの標準パッケージには含まれていませんが, ElScreenを使うとほぼ同等のことができます.

ElScreenのインストールはMELPAを使うのが簡単でしょう. まずは設定ファイルに以下のように書きます(既に同じことが書いてある場合は必要ありません).

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

一度Emacsを起動してM-x package-install RET elscreen RETとするとインストールされます.

そのままのElScreenはEmacs用のインタフェースしかないので, :tabnewなどで操作したい場合は設定が必要です. 以下の設定を設定ファイルに書くと, Vimのタブページに関する操作が概ね有効になります. ただし, タブ番号が0で始まる等, 些細な差異はあります.

set listchars

Vimでは空白文字を表示するのにlistcharsオプションを使いますが, Emacsではディスプレイテーブルを使います. この仕組みは, 原理上あらゆる文字の表示方法を制御できます.

空白文字に限れば, 簡単な設定をするだけで, 裏側でディスプレイテーブル(やフォントロック)を使った制御をしてくれるパッケージwhitespace.elEmacs 23から標準搭載されているので, これを使うのがよいでしょう.

以下が, Vimの設定に対応するディスプレイテーブルもしくはwhitespace.elの設定です.

(require 'whitespace)
(setq whitespace-style '(face tabs tab-mark space space-mark newline))

;; set lcs=eol:$
(setcar (nthcdr 2 (assq 'newline-mark whitespace-display-mappings)) [?$ ?\n])

;; set lcs=tab:^\ ,
(setcar (nthcdr 2 (assq 'tab-mark whitespace-display-mappings)) [?^ ?\t])

;; set lcs=tab:^-,
(setcar (nthcdr 2 (assq 'tab-mark whitespace-display-mappings)) [?^ ?- ?- ?- ?- ?- ?- ?-])

;; set lcs=extends:<,precedes:<
(set-display-table-slot standard-display-table 'truncation ?<)

;; set nbsp:%
(setcar (nthcdr 2 (assq 'space-mark whitespace-display-mappings)) [?%])

(global-whitespace-mode)

whitespace.elが表示を制御するのはwhitespace-styleに入っている要素のみです. 要素の意味と, 他にどんなものの表示を制御できるかは, M-x describe-variable RET whitespace-style RETを参照して下さい.

set-display-table-slotを使う場合, 文字のフェイス(フォントや色など)も同時に変更することができます. この場合は(set-display-table-slot standard-display-table slot (make-glyph-code c face))のようにします. faceにはフェイスを表すシンボルを指定します. 既に定義されているフェイスはM-x list-face-display RETで一覧できます. 自分で新しいフェイスを作るにはdeffaceマクロを使います.

このような設定を用いても, 以下の点はVimと異なります.

  • set lcs=tab:xyyを設定する場合はタブ幅が固定
  • set lcs=trail:cは設定できない
    • 色だけ変えることは可能(ただし, whitespace-styleからspace-markを外しておく)
      (setq show-trailing-whitespace t)
      (set-face-background 'trailing-whitespace "#cc9900")
  • set lcs=extends:cset lcs=precedes:cを個別には設定できない
  • set lcs=conceal:cはconcealに完全に対応する概念がないため設定できない
  • Emacsでは折り返し記号も変更可能
    (set-display-table-slot standard-display-table 'wrap c)
  • Emacsでは非表示の行を表す記号も変更可能
    (set-display-table-slot standard-display-table 'selective-display c)
  • Emacsではstandard-display-tableを直接書き換えればあらゆる文字の表示のしかたを変更可能
set tabstop

:set tabstop=num相当のことをするには:(setq tab-width num) RETとします. デフォルトのタブ幅を指定するには, 設定ファイルに以下のように書きます.

(setq tab-width 4)
set expandtab

:set expandtab RET相当のことをするには:(setq indent-tabs-mode nil) RETとします. デフォルト設定を変えるには, 設定ファイルに以下のように書きます.

(setq indent-tabs-mode nil)

既に入力されているタブをtab-widthの幅のスペースに展開するには, 展開する範囲を選択してM-x untabify RETとします.

set statusline

VimのステータスラインはEmacsではモードラインと呼びます. モードラインはmode-line-formatという変数に決まったデータ構造の値を設定することで変更できます. このデータ構造はシンボルや文字列のリストが入れ子になったもので, 文字列にはプロパティも設定できるため, 見せ方を自由に変更できます. 文字列中の%cは特別な意味を持ち表示する際に展開されますが, Vimとは意味が異なるため, Emacsマニュアルを参照して下さい.

set number

:set number RET相当のことをするにはM-x linum-mode RETとします. もう一度同じことをすると:set nonumber RETになります. 行番号を常に表示にするには設定ファイルに以下のように書きます.

(global-linum-mode)
色テーマ

Vimではcolorschemeで色設定をしますが, Emacsでは2つのやり方があります. 一つはcolor-themeを使う方法で, もう一つはEmacs 24から標準搭載されたカスタムテーマを使う方法です. color-themeは近頃はメンテナンスされていないので, 古いEmacsを使う予定がなければ使わない方がよいでしょう.

color-themeは, MELPAを使っている場合はM-x package-install RET color-theme RETでインストールできます. テーマそのものは初期状態でもいくつか定義されていて, 追加でインストールすることもできます. 特定のテーマを適用にするには設定ファイルに以下のように書きます.

(color-theme-initialize)
(color-theme-dark-laptop) ; テーマdark-laptopを適用

カスタムテーマを使うには, load-themeコマンドを使います. デフォルトで提供されているテーマか, 自分で~/.emacs.d/*3に保存したname-theme.elファイルを読み込むことができます. 設定ファイルに書く場合は以下のようにします.

(load-theme 'zenburn t) ; テーマzenburnを適用

Vimで人気の配色はEmacsにも移植されています. 以下にcolor-theme用, カスタムテーマ用の対応するEmacsパッケージを挙げておきます.

colorscheme color-theme カスタムテーマ
desert color-theme-desert.el desert-theme.el
molokai color-theme-molokai.el
solarized color-theme-solarized.el solarized-dark-theme.el, solarized-light-theme.el
zenburn color-theme-zenburn.el zenburn-theme.el*4
モードライン

EmacsにもVimのモードライン相当のものがありますが, 記法が異なります. Vimのモードラインの記法(の一部)をEmacsでも利用できるようにするにはemacs-vim-modelineを使います.

その他のべんり設定

物理行移動と論理行移動を入れ替え

Vimと同じく, デフォルトではj, kは論理行移動(改行文字単位の行移動)で, gj, gkが物理行移動(見たままの行移動)になっています. 通常は物理行で移動した方がわかりやすいので, Vimユーザでこれを入れ替えている人も多いでしょう. Evilでは以下のようにすると入れ替えることができます(この設定は(require 'evil)の行よりも後に書きます).

(defun evil-swap-key (map key1 key2)
  ;; MAP中のKEY1とKEY2を入れ替え
  "Swap KEY1 and KEY2 in MAP."
  (let ((def1 (lookup-key map key1))
        (def2 (lookup-key map key2)))
    (define-key map key1 def2)
    (define-key map key2 def1)))
(evil-swap-key evil-motion-state-map "j" "gj")
(evil-swap-key evil-motion-state-map "k" "gk")
C-n/C-pをハイブリッドに

デフォルトでは, EvilのノーマルステートのC-nC-pYankRing.vimのようにヤンク履歴の操作に割り当てられています*5. pなどで貼り付けた直後に, C-pで一つ前にヤンク(コピー)した内容を貼り付け直し, C-nで一つ後のものを貼り付け直すという機能です.

これはこれで大変べんりな機能ですが, 本来の行移動コマンドをつぶす割に, 直前のコマンドが貼り付けではない場合にはエラーメッセージが出るだけで, 勿体ない感じがします. 直前が貼り付けコマンドでなければふつうに上下移動してくれてもよいでしょう.

そういうわけで, C-n, C-pの直前が貼り付けコマンドでないときに上下移動になるようにするには以下のようにします.

(defadvice evil-paste-pop (around evil-paste-or-move-line activate)
  ;; evil-paste-popできなかったらprevious-lineする
  "If there is no just-yanked stretch of killed text, just move
to previous line."
  (condition-case err
      ad-do-it
    (error (if (eq this-command 'evil-paste-pop)
               (call-interactively 'previous-line)
             (signal (car err) (cdr err))))))
(defadvice evil-paste-pop-next (around evil-paste-or-move-line activate)
  ;; evil-paste-pop-nextできなかったらnext-lineする
  "If there is no just-yanked stretch of killed text, just move
to next line."
  (condition-case err
      ad-do-it
    (error (if (eq this-command 'evil-paste-pop-next)
               (call-interactively 'next-line)
             (signal (car err) (cdr err))))))

プラグイン

Evilのために書かれたプラグインを紹介します. ここで紹介されている以外にも, Vimプラグインと同等の機能を持ったEmacsパッケージは多数存在します(対応表は導入編を参照)が, それらの導入方法は個別のパッケージのドキュメントを参照して下さい. ここではEvilに特化したプラグインのみを挙げます. これらはEvil公式のものではなく, ユーザたちが作成して公開しているものです.

evil-mode-line


EmacsのモードラインにEvilのステートを表示し, ステートに応じてモードラインの背景色を切り替える機能を提供します.

インストールするには, evil-mode-line.elmode-line-color.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'evil-mode-line)

カスタマイズ方法などはtarao/evil-pluginsのページを参照して下さい.

evil-surround

surround.vimの移植版です. 選択範囲に対してStypeとすると, typeで指定された種類の要素を追加します. たとえば, S<tag>とすると, 選択範囲が<tag>タグで囲まれます.

インストールするには, evil-surround.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'evil-surround)
(global-evil-surround-mode 1)

カスタマイズ方法などはevil-surroundのページを参照して下さい.

evil-nerd-commenter

NERD_commenter.vimの移植版です. 行ごとのコメントアウト/アンコメントのためのプラグインです.

インストールするには, evil-nerd-commenter.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと, M-;コメントアウトできるようになります.

(require 'evil-nerd-commenter)
(evilnc-default-hotkeys)

カスタマイズ方法などはevil-nerd-commenterのページを参照して下さい.

これをインストールしなくても, Emacsでは元々M-;comment-dwimに割り当てられていて, 選択範囲のコメントアウト/アンコメントができます. evil-nerd-commenterは, 範囲を選択しなかったときに現在の行をコメントアウトする, 行数を指定できる, という点が異なります.

evil-operator-comment

commentop.vimの移植版です. コメントアウト/アンコメントのためのオペレータを定義します. オペレータなので, 同じキー2回で行に対して適用, 行数指定での適用, オブジェクト単位での適用, 文字・行・矩形選択範囲に対しての適用などが可能です.

インストールするにはevil-operator-comment.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと, Cコメントアウトできるようになります.

(require 'evil-operator-comment)
(global-evil-operator-comment-mode 1)

カスタマイズ方法などはtarao/evil-pluginsのページを参照して下さい.

これをインストールしなくても, Emacsでは元々M-;comment-dwimに割り当てられていて, 選択範囲のコメントアウト/アンコメントができ, 選択範囲に対してはEvilのオペレータとして用いることができます. ただし, 矩形選択時にも正しく動作させるためにはevil-operator-commentが必要です.

evil-operator-moccur

選択範囲などに対して複数ファイル/ディレクトリにまたがる検索(moccur-grep-find)をするためのオペレータです.

インストールするには, color-moccur.elevil-operator-moccur.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと, Mで検索できるようになります.

(require 'evil-operator-moccur)
(global-evil-operator-moccur-mode 1)

カスタマイズ方法などはtarao/evil-pluginsのページを参照して下さい.

evil-relative-linum

オペレータを入力した直後から適用範囲が確定するまでの間, 相対行番号を表示します.

インストールするには, linum+.elevil-relative-linum.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'evil-relative-linum)

Vimrelativenumberオプションと違い, オペレータが入力されたときだけ相対行番号を表示します.

evil-little-word

camelcasemotion.vimの移植版です. 通常の単語単位よりも小さい部分を単語として扱う移動コマンド, オブジェクトを提供します. たとえば, "CamelCase"を"Camel"と"Case"の2語として, "snake_case"を"snake"と"case"の2語として扱うような単語単位です.

インストールするには, evil-little-word.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと, glw/glbによる移動, ilw/alwのオブジェクトが定義されます.

(require 'evil-little-word)

オリジナルのcamelcasemotion.vimと違い, 英語のアルファベット以外の文字の大文字・小文字も正しく扱われます. たとえば, "TietokoneenÄäni"という文字列は, camelcasemotion.vimでは1語になりますが, evil-little-wordでは"Tietokoneen"と"Ääni"の2語になります.

evil-textobj-between

textobj/between.vimの移植版です. ifcafcで, 文字cに囲まれた部分を選択できるようになります.

インストールするには, evil-textobj-between.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'evil-textobj-between)
evil-paredit

ParEditをEvilから使えるようにするためのプラグインです. 括弧の対応を保った編集が強制されるようになります.

インストールするにはMELPAを使ってM-x package-install RET evil-paredit RETとします. 有効にするにはM-x evil-paredit-mode RETとします. たとえば, Emacs Lispファイルを編集する際に有効にするには設定ファイルに以下のように書きます.

(require 'evil-paredit)
(add-hook 'emacs-lisp-mode-hook 'evil-paredit-mode)
evil-rails

rails.vimの移植版です. Railsプロジェクトを管理するためのインタフェースを提供します.

インストールするには, M-x package-install RET rinari RETRinariをインストールし, evil-rails.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'rinari)
(require 'evil-rails)
hexl-evil-patch

Emacsバイナリエディタモード(hexl-mode)をEvilから問題なく使えるようにするためのプラグインです.

インストールするにはhexl-evil-patch.elをダウンロードして, load-path内のディレクトリに保存します. 設定ファイルに以下のように書くと有効になります.

(require 'hexl-evil-patch)

参考になりそうな個人設定

本来は, Evilを使う上でのおすすめ設定をすぐに使える形で紹介したいところですが, それにはVimEmacsの使い方にまで踏み込む必要があり, Evilの解説という範疇から外れてしまうため, また, VimEmacsでのベストプラクティスそのものが多様であり意見の別れるところなので, 本稿ではそのような設定の紹介はあえてしないことにしました. むしろ, 本稿の読者の皆様が設定自慢の記事を書いて盛り上げてくれることを願っています.

ここでは代わりに, 既にEvilを使っているユーザの個人設定のうち, Web上で公開されているものをピックアップしました. これらを参考に是非とも自分だけの究極の設定を見出して下さい.

以下, ユーザIDの敬称は省略します.

tarao

本稿の筆者の個人設定です. 同じディレクトリに他のEmacs設定のファイルがあります. この設定のリポジトリそのものをgit cloneして, dotfiles/.emacs.d/init.elに対して以下のようにEmacsを起動すると, 必要なパッケージが(リポジトリディレクトリ内に)自動的にダウンロードされて, 既存のEmacsの設定を汚すことなく設定を試すことができます(ただし, ダウンロードには少し不安になるくらい時間がかかります).

emacs -q -l init.el
cofi

いくつかのプラグインの作者でもある方の個人設定です. 挿入ステートでjkと素早く入力すると挿入ステートを抜けられるようになっているのが特徴的です.

fukamachi

ふだんLispを書いている方の設定なので, 同じディレクトリEmacs設定も含めて参考になります.

mikio

いろいろなパッケージのコマンドが細かにEvil化されていて参考になります.

patbl

Colemak配列のための設定です.

yukihr

おそらくDvorak配列でEvilを使っている方だと思います. 同じディレクトリの他のEmacs設定も参考になります.

おわりに

本稿では, Evilのカスタマイズ方法, とくに, よりEmacs的な設定にする方法, あるいは, よりVim的な設定にする方法を紹介しました. 元々EmacsVimを使っていたユーザがEvilに乗り換えるには, どんなにカスタマイズをほどこしたところで, なお不慣れな点が残ると思います. Evilを使うということは, Emacsの良さとVimの良さを両方とも取り入れる代償に, 今まで慣れ親しんだ環境は諦めて, Evilのやり方を受け入れていく覚悟が必要なのかもしれません. 新たな環境への移行が少しでもスムーズにいくように, 本稿を役立ててもらえれば幸いです.

謝辞

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

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

*1:http://www.gnu.org/software/emacs/manual/html_node/elisp/Searching-Keymaps.html

*2:emulation-mode-map-alistsはViperの頃から使われているキーマップ層で, まさにこのような用途のためにあります

*3:正確にはcustom-theme-directoryの値が表すディレクトリ

*4:あちこちに様々な実装がありますが, これが完成度が高くよくメンテナンスされています

*5:ヤンク履歴そのものはEmacsでは標準機能(kill ring)です

広告を非表示にする