2017-05-22 23:57:52 +02:00
|
|
|
;; Evil
|
|
|
|
|
2017-06-19 20:26:20 +02:00
|
|
|
;; TODO: helm-show-yank-ring behaves like Emacs when pasting whole lines, not like Vim.
|
|
|
|
|
|
|
|
;; TODO: helm-mark-ring seems to have issues with Evil:
|
|
|
|
;; - The first entry is not the last position but the current one.
|
|
|
|
;; - Navigating through the marks randomly produces a "Marker points into wrong buffer" error.
|
|
|
|
;; https://github.com/emacs-evil/evil/issues/845#issuecomment-306050231
|
2017-05-27 16:36:01 +02:00
|
|
|
|
|
|
|
;; TODO: M-; comments next line in VISUAL. This is because of a different
|
|
|
|
;; newline definition between Emacs and Vim.
|
|
|
|
;; https://github.com/redguardtoo/evil-nerd-commenter: Not so different, cannot
|
|
|
|
;; comment up without M--.
|
2017-05-22 23:57:52 +02:00
|
|
|
|
|
|
|
;; Several packages handle relative line numbering:
|
|
|
|
;; - nlinum-relative: Seems slow as of May 2017.
|
|
|
|
;; - linum-relative: integrates well but not with fringe string, must be a function.
|
|
|
|
;; - relative-line-number: linum must be disabled before running this.
|
|
|
|
(when (require 'linum-relative nil t)
|
2017-06-18 20:52:00 +02:00
|
|
|
;; REVIEW: Current symbol is displayed on all lines when we run `occur', `set-variables',
|
|
|
|
;; `helm-occur', etc: https://github.com/coldnew/linum-relative/issues/40.
|
2017-05-22 23:57:52 +02:00
|
|
|
(setq linum-relative-current-symbol "")
|
|
|
|
(linum-relative-toggle))
|
|
|
|
|
|
|
|
;; The evil-leader package might seem worthless but it centralizes the leader
|
2017-05-27 16:36:01 +02:00
|
|
|
;; key configuration and automatically makes it available in relevant states.
|
2017-06-17 18:46:02 +02:00
|
|
|
;; Should we map <leader<leader> to the most used command, e.g. `helm-mini'?
|
|
|
|
;; Could be misleading.
|
2017-05-22 23:57:52 +02:00
|
|
|
(when (require 'evil-leader nil t)
|
2017-05-28 16:30:41 +02:00
|
|
|
;; Leader mode and its key must be set before evil-mode.
|
|
|
|
(evil-leader/set-leader "<SPC>")
|
2017-05-22 23:57:52 +02:00
|
|
|
(global-evil-leader-mode))
|
|
|
|
|
|
|
|
(evil-mode 1)
|
|
|
|
|
2017-06-15 18:57:02 +02:00
|
|
|
(defun eshell-or-new-session (&optional arg)
|
|
|
|
"Create an interactive Eshell buffer.
|
|
|
|
If there is already an Eshell session active, switch to it.
|
|
|
|
If current buffer is already an Eshell buffer, create a new one and switch to it.
|
|
|
|
See `eshell' for the numeric prefix arg."
|
|
|
|
(interactive "P")
|
|
|
|
(if (eq major-mode 'eshell-mode)
|
|
|
|
(eshell (or arg t))
|
|
|
|
(eshell arg)))
|
|
|
|
|
2017-06-18 17:56:59 +02:00
|
|
|
(defun org-find-first-agenda ()
|
|
|
|
(interactive)
|
|
|
|
(find-file (car org-agenda-files)))
|
|
|
|
|
2017-05-27 16:36:01 +02:00
|
|
|
(evil-leader/set-key
|
2017-06-15 18:57:02 +02:00
|
|
|
"RET" 'eshell-or-new-session
|
2017-05-27 16:36:01 +02:00
|
|
|
"\\" 'toggle-window-split
|
|
|
|
"b" 'buffer-menu
|
|
|
|
"e" 'find-file
|
2017-06-04 16:31:41 +02:00
|
|
|
"k" 'kill-this-buffer
|
2017-05-27 16:36:01 +02:00
|
|
|
"o" 'delete-other-windows
|
2017-06-18 17:56:59 +02:00
|
|
|
"t" 'org-find-first-agenda
|
2017-05-27 16:36:01 +02:00
|
|
|
"w" 'evil-window-next
|
2017-06-12 22:26:50 +02:00
|
|
|
"|" 'swap-windows)
|
2017-05-27 16:36:01 +02:00
|
|
|
(when (require 'magit nil t)
|
|
|
|
;; Use S-SPC instead of SPC to browse commit details.
|
|
|
|
(evil-leader/set-key "v" 'magit-status))
|
|
|
|
|
2017-06-20 16:25:46 +02:00
|
|
|
;; Motion map: useful for `Info-mode', `help-mode', etc.
|
2017-05-22 23:57:52 +02:00
|
|
|
;; See `evil-motion-state-modes'.
|
|
|
|
(evil-global-set-key 'motion (kbd "TAB") 'forward-button)
|
|
|
|
(evil-global-set-key 'motion (kbd "<backtab>") 'backward-button)
|
2017-06-20 16:25:46 +02:00
|
|
|
(evil-define-key 'motion Info-mode-map
|
|
|
|
(kbd "S-SPC") 'Info-scroll-up
|
|
|
|
"\C-f" 'Info-scroll-up
|
|
|
|
"\C-b" 'Info-scroll-down
|
|
|
|
"\M-sf" 'Info-goto-node
|
|
|
|
"gg" 'evil-goto-first-line)
|
|
|
|
(evil-define-key 'motion help-mode-map
|
|
|
|
(kbd "S-SPC") 'scroll-up-command
|
|
|
|
"\C-f" 'scroll-up-command
|
|
|
|
"\C-b" 'scroll-down-command
|
|
|
|
"\C-o" 'help-go-back)
|
2017-05-27 16:36:01 +02:00
|
|
|
|
2017-06-16 20:07:31 +02:00
|
|
|
;;; Term mode should be in emacs state. It confuses 'vi' otherwise.
|
|
|
|
;;; Upstream will not change this:
|
|
|
|
;;; https://github.com/emacs-evil/evil/issues/854#issuecomment-309085267
|
|
|
|
(evil-set-initial-state 'term-mode 'emacs)
|
|
|
|
|
2017-05-27 16:36:01 +02:00
|
|
|
;; This depends on the local configuration of Helm which might not be loaded
|
|
|
|
;; yet.
|
2017-06-18 18:50:31 +02:00
|
|
|
(with-eval-after-load 'init-helm
|
2017-05-27 16:36:01 +02:00
|
|
|
(evil-leader/set-key
|
|
|
|
"b" 'helm-mini
|
|
|
|
"e" 'helm-find-files
|
|
|
|
"E" 'helm-find
|
|
|
|
"g" 'helm-grep-git-or-ag
|
|
|
|
"G" 'helm-grep-git-all-or-ag
|
|
|
|
"r" 'helm-resume)
|
|
|
|
|
2017-06-19 23:11:21 +02:00
|
|
|
;; To navigate helm entries with hjkl, using the C- modifier would conflict
|
|
|
|
;; with C-h (help prefix) and C-k (`evil-insert-digraph'). We use M- instead.
|
2017-06-16 21:33:53 +02:00
|
|
|
(define-keys helm-map
|
2017-06-19 23:11:21 +02:00
|
|
|
"C-\\" 'helm-toggle-resplit-and-swap-windows
|
2017-06-16 21:33:53 +02:00
|
|
|
"C-f" 'helm-next-page
|
|
|
|
"C-b" 'helm-previous-page
|
2017-06-18 15:08:56 +02:00
|
|
|
"M-h" 'helm-next-source
|
2017-06-16 21:33:53 +02:00
|
|
|
"M-j" 'helm-next-line
|
|
|
|
"M-k" 'helm-previous-line
|
2017-06-19 23:11:21 +02:00
|
|
|
"M-l" 'helm-execute-persistent-action
|
2017-06-20 16:25:46 +02:00
|
|
|
"M-." 'helm-end-of-buffer
|
|
|
|
"M-," 'helm-beginning-of-buffer
|
2017-06-16 21:33:53 +02:00
|
|
|
"<escape>" 'helm-keyboard-quit)
|
2017-06-20 16:25:46 +02:00
|
|
|
(evil-define-key 'normal helm-map
|
|
|
|
"\C-f" 'helm-next-page
|
|
|
|
"\C-b" 'helm-previous-page)
|
|
|
|
(define-key helm-buffer-map (kbd "M-o") 'helm-buffer-switch-other-window)
|
2017-06-21 18:34:04 +02:00
|
|
|
(define-key helm-moccur-map (kbd "M-o") 'helm-moccur-run-goto-line-ow)
|
|
|
|
(define-key helm-grep-map (kbd "M-o") 'helm-grep-run-other-window-action)
|
2017-05-22 23:57:52 +02:00
|
|
|
(dolist (keymap (list helm-find-files-map helm-read-file-map))
|
2017-06-16 21:33:53 +02:00
|
|
|
(define-keys keymap
|
|
|
|
"M-o" 'helm-ff-run-switch-other-window
|
2017-06-19 23:11:21 +02:00
|
|
|
"C-/" 'helm-ff-run-find-sh-command
|
2017-06-16 21:33:53 +02:00
|
|
|
"M-h" 'helm-find-files-up-one-level
|
2017-06-20 16:25:46 +02:00
|
|
|
"M-l" 'helm-execute-persistent-action
|
|
|
|
"C-l" nil))) ; So that the header displays the above binding.
|
2017-05-22 23:57:52 +02:00
|
|
|
|
2017-05-27 16:36:01 +02:00
|
|
|
;; Add support for magit.
|
|
|
|
(require 'evil-magit nil t)
|
2017-05-22 23:57:52 +02:00
|
|
|
(when (and (require 'magit-mode nil t) (require 'evil-magit nil t))
|
2017-06-16 20:23:56 +02:00
|
|
|
(evil-magit-define-key evil-magit-state 'magit-mode-map "<" 'magit-section-up)
|
2017-06-19 23:11:21 +02:00
|
|
|
;; C-j/k is the default, M-j/k is more intuitive if we use it for helm.
|
2017-05-22 23:57:52 +02:00
|
|
|
(evil-magit-define-key evil-magit-state 'magit-mode-map "M-j" 'magit-section-forward)
|
|
|
|
(evil-magit-define-key evil-magit-state 'magit-mode-map "M-k" 'magit-section-backward))
|
|
|
|
|
2017-06-04 17:46:46 +02:00
|
|
|
;; Add support for ediff.
|
|
|
|
(require 'evil-ediff nil t)
|
|
|
|
|
2017-05-22 23:57:52 +02:00
|
|
|
;; For git commit, web edits and others.
|
|
|
|
;; Since `with-editor-mode' is not a major mode, `evil-set-initial-state' cannot
|
|
|
|
;; be used.
|
|
|
|
(when (require 'with-editor nil t)
|
|
|
|
(add-hook 'with-editor-mode-hook 'evil-insert-state))
|
|
|
|
|
|
|
|
;; Allow for evil states in minibuffer. Double <ESC> exits.
|
|
|
|
(dolist
|
|
|
|
(keymap
|
|
|
|
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/
|
|
|
|
;; Text-from-Minibuffer.html#Definition of minibuffer-local-map
|
|
|
|
'(minibuffer-local-map
|
|
|
|
minibuffer-local-ns-map
|
|
|
|
minibuffer-local-completion-map
|
|
|
|
minibuffer-local-must-match-map
|
|
|
|
minibuffer-local-isearch-map))
|
|
|
|
(evil-define-key 'normal (eval keymap) [escape] 'abort-recursive-edit)
|
|
|
|
(evil-define-key 'normal (eval keymap) [return] 'exit-minibuffer))
|
|
|
|
|
2017-06-18 15:08:56 +02:00
|
|
|
(defun evil-minibuffer-setup ()
|
|
|
|
(set (make-local-variable 'evil-echo-state) nil)
|
|
|
|
;; (evil-set-initial-state 'mode 'insert) is the evil-proper
|
|
|
|
;; way to do this, but the minibuffer doesn't have a mode.
|
|
|
|
;; The alternative is to create a minibuffer mode (here), but
|
|
|
|
;; then it may conflict with other packages' if they do the same.
|
|
|
|
(evil-insert 1))
|
|
|
|
(add-hook 'minibuffer-setup-hook 'evil-minibuffer-setup)
|
2017-06-03 13:37:44 +02:00
|
|
|
;; Because of the above minibuffer-setup-hook, some bindings need be reset.
|
|
|
|
(evil-define-key 'normal evil-ex-completion-map [escape] 'abort-recursive-edit)
|
|
|
|
(evil-define-key 'insert evil-ex-completion-map "\M-p" 'previous-complete-history-element)
|
|
|
|
(evil-define-key 'insert evil-ex-completion-map "\M-n" 'next-complete-history-element)
|
|
|
|
;; TODO: evil-ex history binding in normal mode do not work.
|
|
|
|
(evil-define-key 'normal evil-ex-completion-map "\M-p" 'previous-history-element)
|
|
|
|
(evil-define-key 'normal evil-ex-completion-map "\M-n" 'next-history-element)
|
2017-06-16 21:33:53 +02:00
|
|
|
(define-keys evil-ex-completion-map
|
|
|
|
"M-p" 'previous-history-element
|
|
|
|
"M-n" 'next-history-element)
|
2017-05-22 23:57:52 +02:00
|
|
|
|
|
|
|
;; Remap org-mode meta keys for convenience
|
|
|
|
;; - org-evil: Not as polished as of May 2017.
|
|
|
|
;; - evil-org: Depends on MELPA's org-mode, too big a dependency for me.
|
|
|
|
;; See https://github.com/Somelauw/evil-org-mode/blob/master/doc/keythemes.org for inspiration.
|
2017-05-27 16:36:01 +02:00
|
|
|
(when (require 'evil-leader nil t)
|
|
|
|
(evil-leader/set-key-for-mode 'org-mode "a" 'org-agenda))
|
|
|
|
(evil-define-key 'normal org-mode-map
|
2017-06-03 17:40:25 +02:00
|
|
|
(kbd "M-<return>") (lambda () (interactive) (evil-insert 1) (org-meta-return))
|
2017-06-17 18:46:02 +02:00
|
|
|
"L" 'org-shiftright
|
|
|
|
"H" 'org-shiftleft
|
|
|
|
"K" 'org-shiftup
|
|
|
|
"J" 'org-shiftdown
|
|
|
|
"\M-l" 'org-metaright
|
|
|
|
"\M-h" 'org-metaleft
|
|
|
|
"\M-k" 'org-metaup
|
|
|
|
"\M-j" 'org-metadown
|
2017-05-27 16:36:01 +02:00
|
|
|
"\M-L" 'org-shiftmetaright
|
|
|
|
"\M-H" 'org-shiftmetaleft
|
|
|
|
"\M-K" 'org-shiftmetaup
|
|
|
|
"\M-J" 'org-shiftmetadown
|
|
|
|
"<" 'org-up-element)
|
2017-05-22 23:57:52 +02:00
|
|
|
|
|
|
|
;; Package-menu mode
|
|
|
|
(delete 'package-menu-mode evil-emacs-state-modes)
|
2017-06-12 22:29:47 +02:00
|
|
|
(evil-define-key 'normal package-menu-mode-map "q" 'quit-window)
|
2017-05-22 23:57:52 +02:00
|
|
|
(evil-define-key 'normal package-menu-mode-map "i" 'package-menu-mark-install)
|
|
|
|
(evil-define-key 'normal package-menu-mode-map "U" 'package-menu-mark-upgrades)
|
|
|
|
(evil-define-key 'normal package-menu-mode-map "u" 'package-menu-mark-unmark)
|
|
|
|
(evil-define-key 'normal package-menu-mode-map "d" 'package-menu-mark-delete)
|
|
|
|
(evil-define-key 'normal package-menu-mode-map "x" 'package-menu-execute)
|
|
|
|
|
2017-06-12 22:26:50 +02:00
|
|
|
;; Eshell
|
2017-06-15 18:58:39 +02:00
|
|
|
(defun evil/eshell-next-prompt ()
|
2017-06-12 22:26:50 +02:00
|
|
|
(when (get-text-property (point) 'read-only)
|
2017-06-15 18:58:39 +02:00
|
|
|
;; If at end of prompt, `eshell-next-prompt' will not move, so go backward.
|
|
|
|
(beginning-of-line)
|
|
|
|
(eshell-next-prompt 1)))
|
|
|
|
(defun evil/eshell-setup ()
|
|
|
|
(dolist (hook '(evil-replace-state-entry-hook evil-insert-state-entry-hook))
|
|
|
|
(add-hook hook 'evil/eshell-next-prompt nil t)))
|
|
|
|
(add-hook 'eshell-mode-hook 'evil/eshell-setup)
|
|
|
|
|
2017-06-12 22:26:50 +02:00
|
|
|
(defun evil/eshell-interrupt-process ()
|
|
|
|
(interactive)
|
|
|
|
(eshell-interrupt-process)
|
|
|
|
(evil-insert 1))
|
2017-06-15 18:56:22 +02:00
|
|
|
|
|
|
|
;;; `eshell-mode-map' is reset when Eshell is initialized in `eshell-mode'. We
|
|
|
|
;;; need to add bindings to `eshell-first-time-mode-hook'.
|
|
|
|
(defun evil/eshell-set-keys ()
|
2017-06-18 18:50:31 +02:00
|
|
|
(with-eval-after-load 'init-helm
|
2017-06-12 22:26:50 +02:00
|
|
|
(evil-define-key 'insert eshell-mode-map "\C-e" 'helm-find-files))
|
|
|
|
(evil-define-key 'normal eshell-mode-map "\M-k" 'eshell-previous-prompt)
|
|
|
|
(evil-define-key 'normal eshell-mode-map "\M-j" 'eshell-next-prompt)
|
|
|
|
(evil-define-key 'normal eshell-mode-map "0" 'eshell-bol)
|
|
|
|
(evil-define-key 'normal eshell-mode-map (kbd "RET") 'eshell-send-input)
|
|
|
|
(evil-define-key 'normal eshell-mode-map (kbd "C-c C-c") 'evil/eshell-interrupt-process)
|
|
|
|
(evil-define-key '(normal insert) eshell-mode-map "\M-h" 'eshell-backward-argument)
|
|
|
|
(evil-define-key '(normal insert) eshell-mode-map "\M-l" 'eshell-forward-argument))
|
2017-06-15 18:56:22 +02:00
|
|
|
(add-hook 'eshell-first-time-mode-hook 'evil/eshell-set-keys)
|
2017-06-12 22:26:50 +02:00
|
|
|
|
|
|
|
;; TODO: Make Evil commands react more dynamically with read-only text.
|
|
|
|
;; Add support for I, C, D, S, s, c*, d*, R, r.
|
2017-06-18 22:25:56 +02:00
|
|
|
;; See https://github.com/emacs-evil/evil/issues/852.
|
2017-06-12 22:26:50 +02:00
|
|
|
|
2017-05-22 23:57:52 +02:00
|
|
|
;; Go-to-definition.
|
2017-06-18 22:25:56 +02:00
|
|
|
;; From https://emacs.stackexchange.com/questions/608/evil-map-keybindings-the-vim-way.
|
2017-05-22 23:57:52 +02:00
|
|
|
(evil-global-set-key
|
|
|
|
'normal "gd"
|
|
|
|
(lambda () (interactive)
|
|
|
|
(evil-execute-in-emacs-state)
|
|
|
|
(call-interactively (key-binding (kbd "M-.")))))
|
|
|
|
|
|
|
|
;; Multiple cursors.
|
2017-05-28 18:37:54 +02:00
|
|
|
;; This shadows evil-magit's "gr", but we can use "?g" for that instead.
|
|
|
|
;; It shadows C-n/p (`evil-paste-pop'), but we use `helm-show-kill-ring' on
|
|
|
|
;; another binding.
|
2017-05-22 23:57:52 +02:00
|
|
|
(when (require 'evil-mc nil t)
|
|
|
|
(global-evil-mc-mode 1)
|
2017-05-28 18:37:54 +02:00
|
|
|
(define-key evil-mc-key-map (kbd "C-<mouse-1>") 'evil-mc-toggle-cursor-on-click)
|
|
|
|
(set-face-attribute 'evil-mc-cursor-default-face nil :inherit nil :inverse-video nil :box "white")
|
|
|
|
(when (require 'evil-mc-extras nil t)
|
|
|
|
(global-evil-mc-extras-mode 1)))
|
2017-05-22 23:57:52 +02:00
|
|
|
|
2017-06-21 18:33:17 +02:00
|
|
|
;; nXML
|
|
|
|
(evil-define-key 'normal nxml-mode-map "<" 'nxml-backward-up-element)
|
|
|
|
|
2017-06-18 18:50:31 +02:00
|
|
|
(provide 'init-evil)
|