EmacsメインのEvil設定

Evil導入の目的

EmacsでLispを閲覧しているときにうっかりミスでソースを書き換えてしまって動かなくなったりするので、Vimで修正したりということがよくある。 新しいパッケージや設定を試したりするときにも同様なことが起きるので、そんなときにもVimの出番となる。

そこで、Emacsを使いながらVimの操作を覚えるためにEvil導入は良策だと考えるに至った。Evil導入を試すにあたり、数少ないTipsを調べてみた。

Evilバイブルである @taraoさん のTipsからは多くのことを教えられた。けれど私が作りたいのは、「EmacsをVimのごとく使う」という環境ではない。

徹底してEmacsのVim化を図ることにそれほど価値があるとは思えないからで、究極を追求するくらいならあっさりVimを使えば事足りるからである。 私の場合は、あくまでEmacsがメインであって「Vimのいいとこどりができたらいいな」ということである。

この点をしっかり自覚してからEvil導入を進めないと迷路から出られなくなると思う。

Evil設定

(leaf evil
  :ensure t
  :hook ((after-init-hook . evil-mode)
		 (find-file-hook . my:evil-insert-state))
  :bind (("<zenkaku-hankaku>" . toggle-evil-mode)
		 (:evil-normal-state-map
		  ("?" . chromium-vim-chert)
		  ("C-e" . seq-end)
		  ("SPC" . evil-insert-state)
		  ("M-." . nil)	;; Use with other settings
		  ("<hiragana-katakana>" . my:evil-normal-state)
		  ([home] . open-dashboard)))
  :init
  ;; options for Evil, must be written before (require 'evil)
  (setq evil-insert-state-cursor '(bar . 4))
  (setq evil-want-C-u-scroll t)	;; Enable scrolling with C-u
  (setq evil-cross-lines t)
  (setq evil-ex-search-vim-style-regexp nil)
  (setq evil-search-module 'evil-search)
  (setq evil-undo-system 'undo-fu)
  :config
  ;; Use emacs key bindings in insert state
  (setcdr evil-insert-state-map nil)
  ;; Go back to normal state with ESC
  (define-key evil-insert-state-map [escape] 'my:evil-normal-state)
  ;; Hydra-select in visual-state region
  (define-key evil-visual-state-map (kbd ".") 'hydra-selected/body)

  ;; Use muhenkan key as ESC
  (define-key key-translation-map [muhenkan] 'evil-escape-or-quit)
  (define-key evil-operator-state-map [muhenkan] 'evil-escape-or-quit)

  ;; Set the initial state for major mode
  (evil-set-initial-state 'lisp-interaction-mode 'insert)
  (evil-set-initial-state 'fundamental-mode 'insert)
  (evil-set-initial-state 'easy-hugo-mode 'insert)

  (add-to-list 'evil-emacs-state-modes 'neotree-mode)
  (add-to-list 'evil-emacs-state-modes 'dired-mode)
  (add-to-list 'evil-emacs-state-modes 'dashboard-mode)
  (add-to-list 'evil-emacs-state-modes 'org-mode)

  ;; Customized functions
  (defun evil-swap-key (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")

  (defun toggle-evil-mode ()
	"Toggle on and off evil mode."
	(interactive)
	(if evil-mode (evil-mode 0)
	  (evil-mode 1)))

  (defun my:evil-normal-state ()
	"Turn off input-method then return to normal-state."
	(interactive)
	(if current-input-method (deactivate-input-method))
	(evil-normal-state))

  (defun evil-escape-or-quit (&optional prompt)
	"If in evil state to ESC, else muhenkan key."
	(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 [muhenkan])))

  (defun my:evil-insert-state ()
	"New files are opened with insert-state."
	(interactive)
	(unless (file-exists-p buffer-file-name)
	  (evil-insert-state)))

  (defvar my:auto-insert-state-buffers '("COMMIT_EDITMSG"))
  (defun ad:switch-to-buffer (&rest _arg)
	"Set buffer for automatic inser-state"
	(when (member (buffer-name) my:auto-insert-state-buffers))
	(evil-insert-state))
  (advice-add 'switch-to-buffer :after #'ad:switch-to-buffer))


(leaf evil-plugins
  :doc "Plugin for Evil modeline"
  :el-get tarao/evil-plugins
  :after evil
  :require evil-mode-line)
  1. evil-local-mode を使って必要なメジャーモードだけEvil化する
  2. evil-mode をグローバル設定した上で、不要なEvil機能を除外する。

最初は、設定の簡単なevil-local-mode で試してみた。 ところが evil-modeline-color が動作しなかったり、C-[ による [escape] 機能が動作しなかったりというバグがあって不満だったので 最終的に evil-mode で設定を工夫することにした。