ChatGPT解决这个技术问题 Extra ChatGPT

Evil Mode best practice? [closed]

Closed. This question is opinion-based. It is not currently accepting answers. Want to improve this question? Update the question so it can be answered with facts and citations by editing this post. Closed 6 years ago. Improve this question

I've been using Vim as my primary editor for years and tried Emacs several times during that time. Then I discovered Evil and decided that it meets my demand for speedy movement well enough that I can finally move on to Emacs.

So, to all you Evil users, how do you integrate it with normal Emacs functions? Have you encountered any conflicts between this mode and others? What's your sharing-worthy experiences/tips on this topic?


C
Christopher Bottoms

I used a highly customized vim, and now use an even more customized emacs. I think you'll find every instance of keymapping in my keymapping config file https://github.com/mbriggs/.emacs.d-oldv2/blob/master/init/init-keymaps.el

Keep in mind, I am rebinding stuff that real emacs users would consider heresy, so YMMV if you ever want to learn "real" emacs (I really don't).

One thing I would recommend to any ex vimmer is this

;;; esc quits
(defun minibuffer-keyboard-quit ()
  "Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark  t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))
(define-key evil-normal-state-map [escape] 'keyboard-quit)
(define-key evil-visual-state-map [escape] 'keyboard-quit)
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)

so that esc actually quits pretty much anything (like pending prompts in the minibuffer)


The definition for minibuffer-keyboard-quit can be found here: github.com/davvil/.emacs.d/blob/master/init.el
@Adam sorry about that >.> I have a lot of stuff installed, sometimes I use a function that i think is built in, but is actually coming from somewhere else
I find that this doesn't work for me. I use the latest Emacs 24.4, does anyone experience the same?
The link is broken. I believe this is the new one: github.com/mbriggs/.emacs.d/blob/master/config/keybinds.el
@SeanLeather Updated answer to a working link.
R
RussellStewart

As someone who came from emacs, tried vim, and realized there were a huge number of things to gain, I did a lot of experimenting when I first started using evil. While the following are controversial, I wanted to keep the emacs keys that are used more universally in terminal, firefox, cocoa, etc..., but didn't want to lose the vim editing capabilities. I ended up deciding to rebind the following keys in my .emacs:

(define-key evil-normal-state-map "\C-e" 'evil-end-of-line)
(define-key evil-insert-state-map "\C-e" 'end-of-line)
(define-key evil-visual-state-map "\C-e" 'evil-end-of-line)
(define-key evil-motion-state-map "\C-e" 'evil-end-of-line)
(define-key evil-normal-state-map "\C-f" 'evil-forward-char)
(define-key evil-insert-state-map "\C-f" 'evil-forward-char)
(define-key evil-insert-state-map "\C-f" 'evil-forward-char)
(define-key evil-normal-state-map "\C-b" 'evil-backward-char)
(define-key evil-insert-state-map "\C-b" 'evil-backward-char)
(define-key evil-visual-state-map "\C-b" 'evil-backward-char)
(define-key evil-normal-state-map "\C-d" 'evil-delete-char)
(define-key evil-insert-state-map "\C-d" 'evil-delete-char)
(define-key evil-visual-state-map "\C-d" 'evil-delete-char)
(define-key evil-normal-state-map "\C-n" 'evil-next-line)
(define-key evil-insert-state-map "\C-n" 'evil-next-line)
(define-key evil-visual-state-map "\C-n" 'evil-next-line)
(define-key evil-normal-state-map "\C-p" 'evil-previous-line)
(define-key evil-insert-state-map "\C-p" 'evil-previous-line)
(define-key evil-visual-state-map "\C-p" 'evil-previous-line)
(define-key evil-normal-state-map "\C-w" 'evil-delete)
(define-key evil-insert-state-map "\C-w" 'evil-delete)
(define-key evil-visual-state-map "\C-w" 'evil-delete)
(define-key evil-normal-state-map "\C-y" 'yank)
(define-key evil-insert-state-map "\C-y" 'yank)
(define-key evil-visual-state-map "\C-y" 'yank)
(define-key evil-normal-state-map "\C-k" 'kill-line)
(define-key evil-insert-state-map "\C-k" 'kill-line)
(define-key evil-visual-state-map "\C-k" 'kill-line)
(define-key evil-normal-state-map "Q" 'call-last-kbd-macro)
(define-key evil-visual-state-map "Q" 'call-last-kbd-macro)
(define-key evil-normal-state-map (kbd "TAB") 'evil-undefine)

(defun evil-undefine ()
 (interactive)
 (let (evil-mode-map-alist)
   (call-interactively (key-binding (this-command-keys)))))

Unfortunately, these overlap with the vim "move one screen up or down" operations. However, I have become comfortable using the following instead:

(define-key evil-normal-state-map (kbd "DEL") (lambda ()
                    (interactive)
                    (previous-line 10)
                    (evil-scroll-line-up 10)
                    ))

(define-key evil-normal-state-map (kbd "=") (lambda ()
                      (interactive)
                      (next-line 10)
                      (evil-scroll-line-down 10)
                      ))

Also, if you are coming from vim and want a quick path from insert to normal mode using "jk" (or any other 2 stroke combination), the best way is to copy the text from http://www.emacswiki.org/emacs/download/key-chord.el and paste it into your ~/.emacs.d/key-chord.el . Then add the following to your .emacs:

;load a file named key-chord.el from some directory in the load-path (e.g. "~/.emacs.d")
(require 'key-chord)
(key-chord-mode 1)
(key-chord-define-global "jk" 'evil-normal-state)

Also, if you are coming from vim and you think the copy-to-clipboard in emacs is no good, you're probably right. However, you may find the following useful after running sudo apt-get install xsel:

(defun copy-to-clipboard ()
  (interactive)
  (if (display-graphic-p)
      (progn
        (message "Yanked region to x-clipboard!")
        (call-interactively 'clipboard-kill-ring-save)
        )
    (if (region-active-p)
        (progn
          (shell-command-on-region (region-beginning) (region-end) "xsel -i -b")
          (message "Yanked region to clipboard!")
          (deactivate-mark))
      (message "No region active; can't yank to clipboard!")))
  )

(evil-define-command paste-from-clipboard()
  (if (display-graphic-p)
      (progn
        (clipboard-yank)
        (message "graphics active")
        )
    (insert (shell-command-to-string "xsel -o -b"))
    )
  )

(global-set-key [f8] 'copy-to-clipboard)
(global-set-key [f9] 'paste-from-clipboard)

Obviously, you will have to decide for yourself whether any of these controversial changes are worth it, but perhaps these basic changes will inspire you.

For some other really cool function implementations, such as delete and paste, delete without copying to clipboard, efficient 4x / 16x movement, use of counts for paste register specification, tab settings that actually work for c/c++, and more, you can check out the full .emacs, init.el, my-keymaps.el, and my-functions.el versons on my git at https://github.com/Russell91/emacs


all this is great, I added some in wikemacs: wikemacs.org/index.php/Evil#Configuration
I understand your will to reuse your habits from emacs. For working in Firefox, I highly recommend trying Vimperator addon, which brings many vim like functionality to your browsing. However, it is using vim movement commands by default (as is case for less command and others).
Same as you, I don't like the evil binding in insert-state-mode, so I just rebind "i" in normal-state-map to 'evil-emacs-state.
Instead of override the keyboard of evil, I remove it, so it falls back to the keyboard setting of normal emacs, example (dolist (m (list evil-normal-state-map evil-motion-state-map evil-insert-state-map)) (define-key m "\C-e" 'nil))
K
Kenny Meyer

I also used to be a Viper/Vimpulse user, with a huge amount of configuration. Then I found Evil-mode.

What's your sharing-worthy experiences/tips on this topic?

This is my whole evil-mode configuration, and it works great for me:

(require 'evil)
(evil-mode 1)

;; Remap org-mode meta keys for convenience
(mapcar (lambda (state)
    (evil-declare-key state org-mode-map
      (kbd "M-l") 'org-metaright
      (kbd "M-h") 'org-metaleft
      (kbd "M-k") 'org-metaup
      (kbd "M-j") 'org-metadown
      (kbd "M-L") 'org-shiftmetaright
      (kbd "M-H") 'org-shiftmetaleft
      (kbd "M-K") 'org-shiftmetaup
      (kbd "M-J") 'org-shiftmetadown))
  '(normal insert))

Have you encounter any conflicts between this mode and others?

No, in contrast to Viper/Vimpulse which was causing trouble in several modes.


I'm getting this error: progn: Symbol's value as variable is void: state Where is state defined?
You can try evil-emacs-state
Can you clarify where you put that?
This is fantastic, I looked for exactly this. Upvotes galore!
Have a look at this github.com/edwtjo/evil-org-mode
d
dennis

I started to use Evil a month ago; before it, I tried to use viper/vimpulse without much of success. To be honest, vimpulse is quite nice, but using it with various modes was a bit troublesome (e.g. compilation mode where vimpulse went always crazy) leaving emacs in some mode between vi-emacs-something.

When I switched to Evil, I finally started to explore full Emacs power, and believe me, I didn't regret. Evil works nicely in all modes I used (mostly editing, compilation, scratch and eshell) and even reading info/man/help is working without any problems.

Except that, I only found buffer switching odd as I used to do :b<0-9> instead :b-TAB-then-complete-name or :bn. Note however that Evil developers tries (in some cases) to reduce duplicate functionalities, so instead :! (to exec shell command), you should use native M-!.

If you find urge to add/redefine some custom ex commands, just open evil-maps.el and edit it (try that in vim!).

Evil is still young but promising project and I'm waiting the day when will replace viper in official Emacs distribution.


Is this supposed to imply that it's not possible to add/redefine commands in vim?
@JohnTyree User defined commands are required to start with an upper-case letter in Vim, whereas all built-in commands start with a lower-case letter.
E
Ehvince

I like to save the buffer when I exit the insert-mode: (edited: do not ask to save when there is no associated file for this buffer, like when in a scratch or a magit buffer)

(defun my-save ()
  (if (buffer-file-name)
    (evil-save))
)

   (add-hook 'evil-insert-state-exit-hook 'my-save)

for more possibilities: see http://wikemacs.org/index.php/Evil

Comments welcome for improvements !


Thanks. BTW, your link is broken.
Thanks too. Wikemacs will come back. The site is under maintainance, hopefully not for too long again.
C
Community

I use evil-leader and use ",xm" to replace "M-x", so I seldom press Alt key. There is also general.el which supports multiple leader keys. evil-matchit, press "%" to jump between tag pair. evil-nerd-commenter, press "9,ci" to comment/uncomment 9 lines avoid using ESC key, you can press "kj" instead. Have faith in free software! Nothing is impossible with Evil which combining the power of Vim and Emacs. For example, many people assume that Evil keybindings conflicts with existing plugins Emacs without heavy re-binding. That's wrong actually


u
unhammer

Coming from the emacs side, I very much prefer M-. to be go-to-definition, but the function that runs on M-. differs across modes. I could override it in the regular way with (define-key evil-normal-state-map (kbd "M-.") 'foo) where foo checks the current major mode and runs the appropriate function, but that sounds like it'd require lots of hardcoding. A more general solution is this:

(defun evil-emacs-key-binding (key)
  (evil-execute-in-emacs-state)
  (key-binding key))

(defmacro evil-revert-key-binding (state-map key)
  `(define-key ,state-map ,key (lambda ()
                                 (interactive)
                                 (call-interactively
                                  (evil-emacs-key-binding ,key)))))

(eval-after-load "evil-autoloads"
  '(add-hook 'evil-after-load-hook
        (lambda ()
          (evil-revert-key-binding evil-normal-state-map (kbd "M-."))
          ;; and so on
        )))

Other than that, I like the plugins evil-surround (though I feel smartparens is a more complete solution) and evil-leader.

I used to use key-chord to map jk to ESC like I've learnt to do in vim, but it insisted on treating kj as the same as jk, so instead I'm using the following:

(defun evil-escape-if-next-char (trigger)
  "Watches the next letter. If `trigger', then switch to normal mode,
otherwise keep the previously inserted key and forward unpressed
key to `unread-command-events'."
  (self-insert-command 1)
  (let ((next-key (read-event)))
    (if (eq trigger next-key)
        (progn
          (delete-char -1)
          (evil-normal-state))
      (setq unread-command-events (cons next-key unread-command-events)))))

(defun evil-escape-if-next-char-is-k (arg)
  (interactive "p")
  (if (= arg 1)
      (evil-escape-if-next-char ?k)
    (self-insert-command arg)))

(eval-after-load "evil-autoloads"
  '(add-hook 'evil-after-load-hook
             (lambda ()
               ;; … other stuff …
               (define-key evil-insert-state-map (kbd "j") 'evil-escape-if-next-char-is-k))))

I use (setq evil-move-cursor-back nil) which isn't very vimmy (although apparantly you can make your vimrc do that as well), I just never got used to the cursor moving back after exiting insert.

Practical tip: use evil-local-mode-hook for stuff like lazy loading evil-surround-mode, it won't help to put it in plain evil-mode-hook. So if you install evil and evil-surround with package-install, you can have it start when you do M-x evil-mode by doing

(eval-after-load "evil-surround-autoloads"
  '(add-hook 'evil-local-mode-hook #'evil-surround-mode))

(Of course, if you always run evil-mode and always have evil installed, there's no need for that autoload stuff, but I prefer to have my .emacs be general enough that I can use it on machines with old emacsen or without having any elpa packages installed.)


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now