init-evil.el 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. ;;; Evil
  2. ;;; TODO: helm-show-kill-ring behaves like Emacs when pasting whole lines, not like Vim.
  3. ;;; TODO: Make Evil commands react more dynamically with read-only text, like eshell, wdired.
  4. ;;; Add support for I, C, D, S, s, c*, d*, R, r.
  5. ;;; See https://github.com/emacs-evil/evil/issues/852.
  6. ;;; REVIEW: 'cw' fails on the last character of the line when \n does not terminate it.
  7. ;;; See https://github.com/emacs-evil/evil/issues/863.
  8. ;;; Several packages handle relative line numbering:
  9. ;;; - nlinum-relative: Seems slow as of May 2017.
  10. ;;; - linum-relative: integrates well but not with fringe string, must be a function.
  11. ;;; - relative-line-number: linum must be disabled before running this.
  12. (when (require 'linum-relative nil t)
  13. ;; REVIEW: Current symbol is displayed on all lines when we run `occur', `set-variables',
  14. ;; `helm-occur', etc: https://github.com/coldnew/linum-relative/issues/40.
  15. (setq linum-relative-current-symbol "")
  16. (linum-relative-toggle))
  17. (evil-mode 1)
  18. (remove-hook 'evil-insert-state-exit-hook 'expand-abbrev)
  19. ;; (setq evil-want-abbrev-expand-on-insert-exit nil)
  20. (setq undo-tree-mode-lighter "")
  21. (setq evil-cross-lines t
  22. evil-move-beyond-eol t ; Especially useful for Edebug.
  23. evil-move-cursor-back nil
  24. evil-want-fine-undo t)
  25. (setq-default evil-symbol-word-search t)
  26. ;;; Commenting.
  27. ;;; M-; comments next line in VISUAL. This is because of a different newline
  28. ;;; definition between Emacs and Vim.
  29. ;;; https://github.com/redguardtoo/evil-nerd-commenter: does not work well with
  30. ;;; motions and text objects, e.g. it cannot comment up without M--.
  31. ;;; `evil-commentary' is the way to go. We don't need an additional minor-mode though.
  32. (when (require 'evil-commentary nil t)
  33. (evil-global-set-key 'normal "gc" 'evil-commentary)
  34. (evil-global-set-key 'normal "gy" 'evil-commentary-yank))
  35. ;;; Term mode should be in emacs state. It confuses 'vi' otherwise.
  36. ;;; Upstream will not change this:
  37. ;;; https://github.com/emacs-evil/evil/issues/854#issuecomment-309085267
  38. (evil-set-initial-state 'term-mode 'emacs)
  39. ;;; For git commit, web edits and others.
  40. ;;; Since `with-editor-mode' is not a major mode, `evil-set-initial-state' cannot
  41. ;;; be used.
  42. ;;; This requires Eshell, shells and more.
  43. (when (require 'with-editor nil t)
  44. (add-hook 'with-editor-mode-hook 'evil-insert-state))
  45. ;;; Go-to-definition.
  46. ;;; From https://emacs.stackexchange.com/questions/608/evil-map-keybindings-the-vim-way.
  47. (evil-global-set-key
  48. 'normal "gd"
  49. (lambda () (interactive)
  50. (evil-execute-in-emacs-state)
  51. (call-interactively (key-binding (kbd "M-.")))))
  52. ;;; Multiedit
  53. (when (require 'evil-multiedit nil t)
  54. ;; iedit exits as soon as an Evil operator is pressed.
  55. ;; Replace iedit's default binding with multiedit to avoid confusions.
  56. (global-set-key (kbd "C-;") 'evil-multiedit-match-all)
  57. ;; REVIEW: Some bindings are missing:
  58. ;; See https://github.com/hlissner/evil-multiedit/issues/20.
  59. (evil-multiedit-default-keybinds))
  60. ;;; Change mode-line color by Evil state.
  61. (setq evil-default-modeline-color (cons (face-background 'mode-line) (or (face-foreground 'mode-line) "black")))
  62. (defun ambrevar/evil-color-modeline ()
  63. (let ((color (cond ((minibufferp) evil-default-modeline-color)
  64. ((evil-insert-state-p) '("#006fa0" . "#ffffff")) ; 00bb00
  65. ((evil-emacs-state-p) '("#444488" . "#ffffff"))
  66. (t evil-default-modeline-color))))
  67. (set-face-background 'mode-line (car color))
  68. (set-face-foreground 'mode-line (cdr color))))
  69. (add-hook 'post-command-hook 'ambrevar/evil-color-modeline)
  70. (setq evil-mode-line-format nil)
  71. ;;; Add defun text-object. TODO: Does not work for "around".
  72. ;;; https://github.com/emacs-evil/evil/issues/874
  73. ;;; See Evil-cleverparens?
  74. (evil-define-text-object evil-a-defun (count &optional beg end type)
  75. "Select a defun."
  76. (evil-select-an-object 'evil-defun beg end type count))
  77. (evil-define-text-object evil-inner-defun (count &optional beg end type)
  78. "Select inner defun."
  79. (evil-select-inner-object 'evil-defun beg end type count))
  80. (define-key evil-outer-text-objects-map "d" 'evil-a-defun)
  81. (define-key evil-inner-text-objects-map "d" 'evil-inner-defun)
  82. ;;; Without the hook, the Edebug keys (f, n, i, etc.) would get mixed up on initialization.
  83. (add-hook 'edebug-mode-hook 'evil-normalize-keymaps)
  84. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  85. ;;; Mode specific bindings.
  86. ;; Note: Config must be done before `require'-ing evil-collection.
  87. (setq evil-collection-setup-minibuffer t
  88. evil-collection-term-sync-state-and-mode-p t)
  89. (when (require 'evil-collection nil t)
  90. (evil-collection-init))
  91. (with-eval-after-load 'elfeed
  92. ;; Custom
  93. (evil-define-key 'normal elfeed-search-mode-map
  94. (kbd "S-<return>") 'ambrevar/elfeed-visit-maybe-external)
  95. (evil-define-key 'normal elfeed-show-mode-map
  96. (kbd "S-<return>") 'ambrevar/elfeed-visit-maybe-external)
  97. (evil-define-key 'normal elfeed-show-mode-map
  98. (kbd "q") 'ambrevar/elfeed-kill-entry))
  99. ;; Custom Helm
  100. (with-eval-after-load 'helm
  101. (global-set-key (kbd "M-y") 'helm-show-kill-ring)
  102. (dolist (map (list helm-find-files-map helm-read-file-map))
  103. (ambrevar/define-keys map
  104. "M-." 'helm-end-of-buffer
  105. "M-," 'helm-beginning-of-buffer))
  106. ;; `helm-mark-or-exchange-rect' is not needed with Evil.
  107. (global-set-key (kbd "C-x C-x") 'helm-all-mark-rings))
  108. ;;; nXML
  109. ;;; TODO: Add to evil-collection?
  110. (evil-define-key 'normal nxml-mode-map
  111. (kbd "C-j") 'nxml-forward-element
  112. (kbd "C-k") 'nxml-backward-element
  113. (kbd "M-j") 'nxml-forward-element ; Custom
  114. (kbd "M-k") 'nxml-backward-element ; Custom
  115. ">" 'nxml-down-element
  116. "<" 'nxml-backward-up-element)
  117. (evil-define-key 'visual nxml-mode-map
  118. (kbd "C-j") 'nxml-forward-element
  119. (kbd "C-k") 'nxml-backward-element
  120. (kbd "M-j") 'nxml-forward-element ; Custom
  121. (kbd "M-k") 'nxml-backward-element ; Custom
  122. ">" 'nxml-down-element
  123. "<" 'nxml-backward-up-element)
  124. (with-eval-after-load 'mu4e
  125. (when (require 'evil-mu4e nil t)
  126. ;; TODO: evil-mu4e needs a big overhaul, e.g. 'visual commands are not supported. Report upstream.
  127. (evil-define-key 'motion mu4e-headers-mode-map
  128. "i" 'mu4e-headers-mark-for-flag
  129. "I" 'mu4e-headers-mark-for-unflag
  130. "]" 'mu4e-headers-next-unread
  131. "[" 'mu4e-headers-prev-unread
  132. ;; "R" 'mu4e-headers-mark-for-refile
  133. "p" 'mu4e-headers-toggle-include-related
  134. "r" 'mu4e-compose-reply
  135. ;; Custom
  136. "d" 'ambrevar/mu4e-headers-move-to-trash)
  137. (evil-define-key 'visual mu4e-headers-mode-map
  138. "m" 'mu4e-headers-mark-for-move
  139. "D" 'mu4e-headers-mark-for-delete
  140. "u" 'mu4e-headers-mark-for-unmark
  141. ;; Custom
  142. "d" 'ambrevar/mu4e-headers-move-to-trash)
  143. (evil-define-key 'motion mu4e-view-mode-map
  144. (kbd "SPC") 'mu4e-view-scroll-up-or-next
  145. (kbd "<tab>") 'shr-next-link
  146. (kbd "<backtab>") 'shr-previous-link
  147. "i" 'mu4e-view-mark-for-flag
  148. "I" 'mu4e-view-mark-for-unflag
  149. "]" 'mu4e-view-headers-next-unread
  150. "[" 'mu4e-view-headers-prev-unread
  151. ;; "R" 'mu4e-view-mark-for-refile
  152. "r" 'mu4e-compose-reply
  153. "za" 'mu4e-view-save-attachment-multi
  154. (kbd "C-j") 'mu4e-view-headers-next
  155. (kbd "C-k") 'mu4e-view-headers-prev
  156. (kbd "M-j") 'mu4e-view-headers-next ; Custom
  157. (kbd "M-k") 'mu4e-view-headers-prev ; Custom
  158. "h" 'evil-backward-char
  159. "zh" 'mu4e-view-toggle-html
  160. "gx" 'mu4e-view-go-to-url
  161. ;; Custom
  162. "d" 'ambrevar/mu4e-view-move-to-trash)
  163. (evil-set-initial-state 'mu4e-compose-mode 'insert)
  164. (evil-define-key 'normal mu4e-compose-mode-map
  165. "gg" 'mu4e-compose-goto-top)))
  166. (with-eval-after-load 'magit
  167. (when (require 'evil-magit nil t)
  168. (evil-magit-define-key evil-magit-state 'magit-mode-map "<" 'magit-section-up)
  169. ;; C-j/k is the default, M-j/k is more consistent with our customization for Helm.
  170. (evil-magit-define-key evil-magit-state 'magit-mode-map "M-j" 'magit-section-forward)
  171. (evil-magit-define-key evil-magit-state 'magit-mode-map "M-k" 'magit-section-backward)))
  172. (require 'evil-ediff nil t)
  173. (with-eval-after-load 'org
  174. ;; Don't require evil-org before org is loaded.
  175. ;; Elfeed-link is loaded after org.
  176. (when (require 'evil-org nil t)
  177. ;; org-evil is not as polished as of May 2017.
  178. ;; See https://github.com/Somelauw/evil-org-mode/blob/master/doc/keythemes.org for inspiration.
  179. (add-hook 'org-mode-hook 'evil-org-mode)
  180. ;; No need for 'insert, 'todo 'heading.
  181. (evil-org-set-key-theme '(navigation textobjects additional shift))
  182. (defun ambrevar/evil-org-meta-return ()
  183. "Like `org-meta-return' but switch to insert mode."
  184. (interactive)
  185. (evil-insert 1)
  186. (org-meta-return))
  187. (evil-define-key 'normal evil-org-mode-map
  188. "^" 'org-up-element ; Evil-Magit-inspired. TODO: Suggest upstream.
  189. "<" 'org-up-element ; Custom
  190. ">" 'org-down-element ; Custom
  191. (kbd "M-<return>") 'ambrevar/evil-org-meta-return)
  192. (with-eval-after-load 'org-agenda
  193. (require 'evil-org-agenda)
  194. (evil-org-agenda-set-keys))))
  195. (with-eval-after-load 'gnus (require 'init-evil-gnus))
  196. ;; EWW
  197. (defun ambrevar/evil-eww (mode _mode-keymaps &rest _rest)
  198. (when (eq mode 'eww)
  199. (evil-define-key 'operator eww-mode-map
  200. "t" '(menu-item
  201. ""
  202. nil
  203. :filter (lambda (&optional _)
  204. (when (memq evil-this-operator
  205. evil-collection-yank-operators)
  206. (setq evil-inhibit-operator t)
  207. #'ambrevar/eww-copy-page-title))))
  208. (evil-define-key 'normal eww-mode-map "O" 'ambrevar/eww-open-in-new-buffer)
  209. (evil-define-key 'normal eww-mode-map "[" 'ambrevar/eww-previous-url)
  210. (evil-define-key 'normal eww-mode-map "]" 'ambrevar/eww-next-url)))
  211. (add-hook 'evil-collection-setup-hook 'ambrevar/evil-eww)
  212. (with-eval-after-load 'helm-eww
  213. (evil-define-key '(insert normal) helm-eww-buffers-map (kbd "S-<return>") 'helm-buffer-switch-other-window)
  214. (evil-define-key '(insert normal) helm-eww-bookmarks-map (kbd "S-<return>") 'helm-buffer-switch-other-window))
  215. (provide 'init-evil)