The Complete Computing Environment

Evil Mode -- Vim in Emacs

LifeTechEmacsTopicsArcology
(provide 'cce/evil-mode)

Evil-Mode is Vim keybindings and mode-switching, implemented in Emacs.

Context switching is unproductive. Modal editing is not. Those are at odds. Any interface modality introduced to a system should be understandable and it should be consistent so as to minimize the negative effects of context switching. I use Emacs with Evil-Mode to achieve this right now with a set of custom keybindings which build on top of the idioms of both Emacs and Vim where appropriate.

Vim modal commands are by no means perfect. It's not perfect in any respect, but it works well enough, and it's a set of portable idioms. The Evil project provides an additional set of libraries [Evil Collection], which I use below:

(use-package evil
  :init
  (setq evil-want-keybinding nil)
  <<evil-respect-visual-line>>
  :config
  (evil-mode 1))

(use-package evil-collection
  :after evil
  :config
  (evil-collection-init))
(use-package evil-matchit
  :config (global-evil-matchit-mode 1))

(use-package evil-surround
  :config (global-evil-surround-mode 1))

Set up and configure a <leader> key; The idea here is to have a somewhat standard key to hang user-commands off of. I extend this idea maybe past its limit.

(use-package evil-leader
  :after evil
  :demand
  :config
  (global-evil-leader-mode 1)
  (evil-leader/set-leader "SPC"))

Syndicate is evil bindings and support for org-mode.

(use-package evil-org
  :ensure t
  :after org
  :hook (org-mode . (lambda () (evil-org-mode)))
  :init
  (fset 'evil-redirect-digit-argument 'ignore)
  :config
  (evil-define-key 'normal 'org-mode-map (kbd "gc") #'org-cycle)
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

This construction iterates over a set of major modes, and tells Evil Mode what state buffers should start in when they're opened in that mode. This is useful for starting modes which do not support Evil Mode (or more correctly, evil-collection does not support the mode!) with Emacs controls, or starting git commit with insert mode.

(cl-loop for (mode . state) in '((text-mode . normal)
                                 (exwm-mode . emacs)
                                 (sauron-mode . emacs)
                                 (shell-mode . insert)
                                 (git-commit-mode . insert)
                                 (git-rebase-mode . emacs)
                                 (magit-branch-manager-mode . emacs)
                                 (mingus-playlist-mode . motion)
                                 (mingus-browse-mode . motion)
                                 (org-fc-review-flip-mode . motion)
                                 (org-fc-review-rate-mode . motion)
                                 (yari-mode . motion)
                                 (mastodon-mode . emacs)
                                 (mpc-mode . emacs)
                                 (mpc-status-mode . emacs)
                                 (mpc-tagbrowser-mode . emacs)
                                 (mpc-tagbrowser-dir-mode . emacs)
                                 (mpc-songs-mode . emacs)
                                 (org-capture-mode . insert)
                                 (org-fc-dashboard-mode . motion))
         do (evil-set-initial-state mode state))

In my Text Editing Fundamental Opinions, I set up my system to use visual-line-mode to format my org-mode documents rather than do my own text wrapping. I prefer this a lot. This has to be set to true before evil-mode loads:

(setq evil-respect-visual-line-mode t)

visual line commands

(evil-define-key 'normal text-mode-map [remap evil-next-line] #'evil-next-visual-line)
(evil-define-key 'normal text-mode-map [remap evil-previous-line] #'evil-previous-visual-line)
(evil-define-key 'normal text-mode-map [remap evil-end-of-line] #'evil-end-of-visual-line)