Evilのカーソルにちょっとした工夫

# Evilのカーソルにちょっとした工夫

## これは何?

[Evil Advent Calendar 2014](http://qiita.com/advent-calendar/2014/evil) の10日目の記事です.

9日目は私 keno による [Dired with Evil](http://qiita.com/kenoss/items/ce2cfee1f86a430e3a07) でした.

簡単な話のつもりがスクリーンショットや動画でわかりやすくしにくい系のことで初心者お断りな感じに
なってしまったので今日は画像を使ってゆるふわな記事を書こうと思います.

3日目の nobkz さんによる [Evilのステートにちょっとした工夫](http://qiita.com/nobkz/items/a995dafb39fb47017f46) と似た感じですが少し違うアプローチです.


## ステート判別

ステートとは何か...とかの基本的な話は nobkz さんが上の記事で扱われているのでそちらを見てもらうこと
にして. nobkz さんの方法はステート(モード)によって color-theme を切り替えるというものでわかりやすい
のですが, 個人的には全体の配色はステートによってころころ変って欲しくありません.
(color-theme 使ってないというのもある.) ではどうするかというと, 視覚情報を部分的に変えるのです:

- マウスカーソルの色と形をステートと同期
- `hlinum.el` を使ってカーソルの色を linum の色に反映
- `powerline.el` を使ってモードラインに反映

Normal state
![normal-state.png](https://qiita-image-store.s3.amazonaws.com/0/33149/ebac41aa-8c55-00dc-ed58-ed11d4986398.png)

Insert state
![insert-state.png](https://qiita-image-store.s3.amazonaws.com/0/33149/85d8ddec-c97f-4815-290b-553014e0bbc5.png)

Visual state
![visual-state.png](https://qiita-image-store.s3.amazonaws.com/0/33149/8f3bf5cf-1e6b-77a1-9fa0-4fabe7a72106.png)


## マウスカーソルの色と形をステートと同期

これは難しくありません. Evil がデフォルトで備えている機能です.

```el
(setq evil-default-cursor 'hbar
      evil-normal-state-cursor '("darkolivegreen")      ; ノーマルステートでは穏かな緑の水平バーに.
      evil-insert-state-cursor '("#800000" (bar . 2)))  ; 挿入ステートでは目立つ赤い垂直バーに.
(blink-cursor-mode -1)
```

ノーマルステートはソースを読んだりするときに使いたいので目立たない色にしていますが, こうすると
カーソルを見失うことがあります. これに対応するために, Escapeキーを押したとき(ノーマルステートに
帰ってきたとき)に現在行を光らせましょう. 単純には

```el
(require 'hl-line)
(add-hook 'evil-normal-state-entry-hook 'hl-line-flash)
```

の様にして数秒間 `hl-line-mode` を on にすることができます. しかし, 時と場合によって早く消えて
欲しかったりコードを読む際の目印に使いたいときがあります. `erfi-emacs.el` には
「次のコマンド実行まで現在行をハイライトする」という函数があるので, こちらを使うのも良いでしょう.

```el
(require 'hl-line)
(require 'erfi-emacs)
(add-hook 'evil-normal-state-entry-hook 'erfi-emacs-hl-turn-on-until-next-command)
```

`hl-line.el` の代わりに `crosshair.el` を使うというのもアリです.
(日本語と併用すると表示が乱れるので僕は使っていません.)


## `hlinum.el` を使ってカーソルの色を linum の色に反映

目立たない色や形を設定していると, カーソルがそもそもどこにあるかわからなかったりしてステートが
判別しにくい場合があります. `linum.el` の拡張である `hlinum.el` を使えば現在行の番号に色を
付けられます. 以下の様にしてステートが変更されたときにこの色も変更しましょう.

```el
(require 'hlinum)
(hlinum-activate)
(defadvice evil-set-cursor-color (after keu-hlinum activate)
  "Reflect cursor color change to `linum-highlight-face' for `hlinum'."
  (set-face-attribute 'linum-highlight-face nil
                      :background (ad-get-arg 0)))
```

ちょっと注意: 実は現在は `evil-set-cursor-color` はステートやカーソルの色が変更されたときに呼ばれる
のではなく, `post-command-hook` でコマンド実行毎に呼ばれます. なのであんまり重いことをやると
hjkl移動に支障が出るかもしれません. Evil にパッチは送っているのでそのうち改善されるでしょう.


## `powerline.el` を使ってモードラインに反映

`powerline` と `evil-mode-line` を組み合わせてモードラインにも色を付けるとよりわかりやすくなります.
設定は長いので Gist に置いておきますね.

https://gist.github.com/kenoss/b2d2daf38a956644fb04

(主に darkolivegreen を基調にしたいが為に`powerline-angle-left` を定義しているせい.
arrow だとひっついちゃってだめなのよね.)

抜粋すると大体以下の様な感じ.

```el
(if (locate-library "mode-line-color")
    (require 'evil-mode-line)
    (progn
      (defmacro define-mode-line-color (arg body)
        nil)
      (let ((orig-require (symbol-function 'require)))
        (cl-letf (((symbol-function 'require) 'ignore))
                 (funcall orig-require 'evil-mode-line)))))

(setq evil-normal-state-msg  " NORMAL "
      evil-insert-state-msg  " INSERT "
      evil-replace-state-msg " REPLAC "
      evil-emacs-state-msg   " EMACS  "
      evil-visual-state-msg-alist '((normal . " VISUAL ") (line . " V LINE ") (block . " V BLCK "))
      )

(dolist (state '(normal insert replace emacs visual))
  (eval `(defface ,(intern (concat "modeline-evil-" (symbol-name state) "-state"))
           nil "Evil modeline face.")))
(custom-set-faces
  '(modeline-evil-normal-state
    ((t (:background "darkolivegreen"
         :foreground "black"))))
  '(modeline-evil-insert-state
    ((t (:background "#500000"
         :foreground "black"))))
  '(modeline-evil-replace-state
    ((t (:background "chocolate1"
         :foreground "black"))))
  '(modeline-evil-visual-state
    ((t (:background "#55295b"
         :foreground "black"))))
  '(modeline-evil-emacs-state
    ((t (:background "gray50"
         :foreground "black"))))
  )
```

で, powerline の設定にこんな風に書く.

```el
(setq-default
 mode-line-format
 (let* ((separator-left  'powerline-angle-left)    ; angle は標準では未定義
        (separator-right 'powerline-angle-right))
   `("%e"
     (:eval
      (let* ((active (powerline-selected-window-active))
             ...
             (evil-face (erfi:case evil-state
                          ((normal) 'modeline-evil-normal-state)
                          ((insert) 'modeline-evil-insert-state)
                          ((visual) 'modeline-evil-visual-state)
                          ((emacs) 'modeline-evil-emacs-state)
                          (t 'modeline-evil-emacs-state)))
             (lhs (list (powerline-alternate-left 'default face0)
                        ...
                        (,separator-left face0 evil-face)
                        (powerline-raw (evil-mode-line-state-msg) evil-face)
                        (,separator-left evil-face mode-line)
                        ...
                        ))
             (rhs (list ...)))
        (concat (powerline-render lhs)
                (powerline-fill face2 (powerline-width rhs))
                (powerline-render rhs)))))))
```

使ったことはないのですが, `powerline-evil.el` みたいなのもあるようです.
https://github.com/raugturi/powerline-evil
http://www.emacswiki.org/emacs/PowerLine


## 終わりに

いかがでしたでしょうか. 工夫次第でまだまだ色々できると思います. 良いアイデアがあれば是非
教えてください.

明日の記事を書く人が決まってないようなので Evil を使っている人は是非何か書きましょう!