Early on in the history of the editor wars, adherents of Emacs banded together to form a religion. As a result of this ecclesiastical schism, vi came to be known as "the editor of the beast". It comes as no surprise that something denounced by a particular religious sect turns out, in reality, to be very awesome, useful, and generally good for society.
I am talking, of course, about the power of modal editing.
The ubiquitous, second-nature process of reading and writing belies the fact that working with text is a subtle, complex, and multifaceted task. Moreover, in our interactions with text we typically spend the majority of the time reading, searching, and editing. This is doubly true for structured text, such as Wikipedia markup or computer programs. Key-chord-based text editors make this process needlessly slow and inconvenient.
But this post isn't just another vi/Vim paean. This post is about Evil, the Extensible Vi Layer for Emacs written by Frank Fischer and Vegard Øye. It allows to combine the incredible power of Emacs with the ergonomics, terseness, and efficiency of Vim, and if you are an Emacs user, you should check it out. If you are a Vim user, here's your chance to switch to a better editor without having to retrain the muscle memory.
Currently, most Evil users seem to come from the Vim camp. As a result, Evil's defaults skew towards Vim behavior. This makes it somewhat difficult to adopt for us Emacs users: we have to face the double task of learning Vim, as well as figuring out how to make the modal editing paradigm work smoothly with the existing Emacs usage patterns. As someone that has done this journey, I would like to share some tips.
So let's assume you successfully installed Evil. Great! Now, what exactly did you get yourself into? Well, effectively, all Evil does is redefine the key bindings depending on the context, or "state". As a user, you will primarily deal with three states: normal (for navigating/editing), insert (for typing characters), and motion (used for read-only buffers such as help). There is also the Emacs state, which simply gives you familiar Emacs behavior, the same as what you'd get if you turned off Evil.
One option, therefore, is to use Evil's Emacs state as a default, using C-z
to toggle between it and the normal state. However, I don't recommend this approach, as one would be tempted to revert to old Emacs habits, thereby missing 90% of the awesomeness Evil provides. Let's just plunge in:
(setq evil-default-state 'normal)
Now do a Google search for a good Vim tutorial, and experiment lots!
Issues you might encounter as an Emacs user
Insert state clobbers some useful Emacs keybindings
The solution to this is to clear the insert state keymap, leaving you with unadulterated Emacs behavior. You might still want to poke around the keymap (defined in evil-maps.el
) and see if you want to salvage some useful insert state command by rebinding them to keys of your liking. Also, you need to bind ESC
to putting you back in normal mode. So, try using this code. With it, I have no practical need to ever switch to Emacs state.
(setcdr evil-insert-state-map nil) (define-key evil-insert-state-map (read-kbd-macro evil-toggle-key) 'evil-emacs-state)
I want the keybinding X to work in Evil!
You can always override or add bindings to any Evil state. Just use something like this:
(define-key evil-normal-state-map "\C-r" 'isearch-backward) (define-key evil-normal-state-map "\C-e" 'evil-end-of-line) (define-key evil-motion-state-map "\C-e" 'evil-end-of-line)
Evil bindings for key X shadow the default bindings in mode Y
A common culprit here is the return
key, which is ordinarily bound to evil-ret
(a command that, as of this writing, doesn't know about what return is supposed to do in a current mode).
A crude but effective solution is to change Evil bindings on a per-state, per-mode basis, like so:
(evil-declare-key 'motion completion-list-mode-map (kbd "<return>") 'choose-completion) (evil-declare-key 'motion completion-list-mode-map (kbd "RET") 'choose-completion) (evil-declare-key 'motion browse-kill-ring-mode-map (kbd "<return>") 'browse-kill-ring-insert-and-quit) (evil-declare-key 'motion browse-kill-ring-mode-map (kbd "RET") 'browse-kill-ring-insert-and-quit) (evil-declare-key 'motion occur-mode-map (kbd "<return>") 'occur-mode-goto-occurrence) (evil-declare-key 'motion occur-mode-map (kbd "RET") 'occur-mode-goto-occurrence)
Note that I am using both the RET
and <return>
forms to make sure the key works both in terminal and under X.
This issue becomes more tricky in "read-only" modes that use letter keys for navigation (e.g. info, dired, ibuffer). It's not obvious to me what the best practices are for such modes. Should the Emacs bindings shadow Evil normal state? Does insert or normal state make more sense as the default? Currently, I don't have clear-cut answers.
I don't want Evil to ever touch keybinding X
This can too be arranged! Define the following function:
(defun evil-undefine () (interactive) (let (evil-mode-map-alist) (call-interactively (key-binding (this-command-keys)))))
Now, to make sure that Evil's normal state never touches TAB
, just wire this fall-through binding like so:
(define-key evil-normal-state-map (kbd "TAB") 'evil-undefine)
Mode X should start in normal state, but mode Y should start in insert state
Use evil-set-initial-state
:
(evil-set-initial-state mode-x 'normal) (evil-set-initial-state mode-y 'insert)
Doing a copy via M-w loses a character
If you are running Emacs 24, see if you are being affected by this bug.
My selection is off by a character
This can be somewhat subtle, as Emacs and Vim have different defaults regarding the visual/transient mark. In particular, in Emacs, the last selected character is always the one before the point. This makes operating on text from left to right and from right to left asymmetrical: if you wanted to select the string 123
, and your cursor is on the 1, you press C-SPC,C-3,C-f
. However, if your cursor is on the 3, you first have to move it past the 3, and only then do C-SPC,C=3,C-b
. In Vim, on the other hand, the last selected character is always under point, so you'd just do v2l
if the cursor is no the 1, or v2h
if the cursor is on the 3. The fact that in Emacs, you always deal with this asymmetry, whether you are aware of it or not, can lead to selections (or, in general, cursor positioning) being off by a character when you use commands that are, conceptually, the same in Emacs and Vim, but differ in their treatment of character under point vs character before point. The good news is that Evil provides the following setting, which might help with correct character selection at beginnings and ends of lines:
(setq evil-want-visual-char-semi-exclusive t)
You might also look into changing Vim's default behavior whereby the cursor moves back one space (although this behavior makes a fair amount of sense). For this, you could look into
(setq evil-move-cursor-back nil) ;;and maybe also: (setq evil-highlight-closing-paren-at-point-states nil)
You could also try playing with (setq evil-visual-char 'exclusive)
, but I personally see no good reason to use that setting. In my own configuration, I just turned on evil-want-visual-char-semi-exclusive
and it's been working well for me thus far.
Favorite Vim / Evil usage patterns
I will keep this section brief, since I am relatively new to the Vim way of editing text. Here are a few things that I have found handy or elegant in my 1.5 months with Evil. Some of these are pretty trivial, and some have Emacs equivalents (either built-in or via add-on Elisp). However, everything just feels cleaner without having to press C
or M
all the time.
Searching via /
We all know that isearch is the way to navigate text in Emacs buffers, and /
is very similar to isearch. Because /
is ergonomically superior to C-s
, I find myself using the search-to-navigate paradigm much more than with my old Emacs setup.
Block selection
In Vim/Evil, C-v
allows you to select rectangles and do things like insert or paste text before every line in a rectangle. In Emacs, CUA rectangle mode does something similar, but you have to enable it. In Evil, it comes built-in.
Combining search, motion, deletion, and selection commands
Such combinations are very powerful. Here are a few examples to give you a flavor of what I am talking about:
d/foo[RET]
: deletes from point to string "foo"dfa
: deletes from point to character "a", inclusivecta
: deletes from point to character "a", exclusive, and puts you in insert modeviw
: selects inside wordvfa;
: selects from point until the second occurrence of char "a", inclusiveyi)
: copy text inside parensdi"
: delete text inside double quotes
Operating on surrounding delimiters (quotes, parentheses, etc.)
These are enabled by the port of Vim's surround plugin, available here. You can do things like change double quotes to single quotes via cs"'
, surround words with HTML tags, and if you have selected some text, surround it with delimiters via e.g. s)
or s'
.
Defining your own normal mode commands
For instance, I have gj
mapped to org-goto
in org-mode, and gb
mapped to ido-switch-buffer
. I like those way more than the original bindings.
One can take this idea further and create keymaps starting with a dedicated leader key (,
has been suggested as a good leader key choice). See this discussion for more insight into how one might do this via a plugin or Emacs built-in keymap functions.
Where to learn more
Evil is a relatively new project, and resources online are somewhat sparse at the moment. Some of the useful resources out there are:
- Evil on Emacswiki
- Official mailing list
- Michael Markert's Evil config file is an excellent example of how one might want to customize Evil bindings and behavior, and do so in a clean manner. There are lots of goodies there, my favorite being a method to dynamically change the look of the cursor based on the state (insert vs normal). (Note that the code in that file sometimes relies on external utility functions.)
- For a quick run-down of Evil's internals, consult the PDF or Info documentation that comes with the code.
I would have very much liked to include some Vim tutorials here, for the benefit of Emacs users, however, most of the materials that I have stumbled upon seem to be either too basic or too advanced. If you have something that you like, please leave a comment!
Conclusion
Most of the choices we make result in imperfect compromises, which makes it easy to lapse into the "grass is greener" mentality. I am a lindy hopper that envies salsa dancers, and a Linux/Android user that evangelizes all things Apple. Up until recently, I've been an Emacs devotee that coveted modal editing. Today, however, my CapsLock key is mapped to ESC instead of Ctrl, and the world is a slightly more perfect place – all thanks to the powers of Evil.
12 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
Excellent post, very useful!
As a Vim user moving to Emacs/evil, I already have CapsLock mapped to ESC as you do, but I also swapped rctrl and ralt so I can hit M- with my left thumb and C- with my right. Might want to give it a try, for those chords you can't avoid :) I did this systemwide with xmodmap, actually - it comes in handy in other programs too.
I use control-lock-mode to achieve the same, without sacrifizing all the knowledge of useful key-combinations.
I am a long time (very long time) user of Emacs who learned vi on Unix V7 in ancient times and had always pined for a modal editor. Evil has been a fantastic development, managing to combine modal editing with Emacs in a way that other attempts hadn't managed. Your blog is a good description of the issues that do arise and I enjoyed reading it.
One minor possibly pedantic point: I would argue that there is no asymmetry in how Emacs handles selections. The key difference between Emacs and vi is that in emacs the position is always "between" characters where in vi it is "on" a character. For this reason, I have always set the cursor in Emacs to be a vertical bar to highlight this aspect. When using evil, I keep the cursor as a block. The difference positioning between the modes is then made visually clear.
Thanks again!
@eric: Actually, the point that you make is pretty important, and better captures the Emacs/Vim selection behavior difference than my description. Also, IIUC, "between characters" is also the correct way to think of the cursor positioning in vi insert mode, while "on character" is the visual/normal state behavior. So perhaps the asymmetry between Emacs and vi is really a consequence of that "internal asymmetry" in vi itself.
The reason it's important to think about this is that using some Emacs motion commands while in Evil normal/visual mode results in unexpected or incorrect (usually, in the off-by-one sense) behavior due to the different underlying model of the cursor positioning. Sexp-motion commands are the key culprits here, but there might be others. Ideally, I'd like to see Evil compensating for this difference in behavior (see this discussion), but it's somewhat subtle and I personally don't have a clear conception of how one would do this in such a way as to preserve behavior that's sensible for Emacs without breaking Vim commands.
(Alternatively, someone could tell me how to navigate through code in Evil effectively without resorting to sexp-motions. I have found them to be very handy, and I know seasoned Emacs users that swear by them for any sort of code navigation.)
Thanks for your insightful post, great that there are some people who come from Emacs to modal editing via Evil :)
Some points to add:
@Combining Commands
vfa; can also be written as v2fa just to demonstrate that f(ind) and t(o) also take numeric arguments (think of M-2 for the rest of Emacs). Also most other (motion) commands take numeric arguments.
Evil supports text objects so you can also define your di$KEY to delete inside such a text object. (For more see evil-commands.el)
@Defining normal mode commands
From the Merge Request emerged the evil-leader package, to be found here: https://github.com/cofi/evil-leader (I also added it now to the plugins list on emacswiki/Evil)
@My dotfiles
cofi-evil.el depends on cofi-util.el and the cl package, if you copied code and something fails look there ;) (well and the other `require'd packages)
evil-{define,declare}-key support also more than one definition at a time to keep your key bindings clean.
I mapped my CapsLock to Ctrl and use `jk' to exit insert mode (look for cofi/evil-maybe-exit) because this way using other Emacs packages (that sadly never heard about Evil) is way easier. You can also exit insert state via C-[ but that's quite cumbersome.
Last but not least: Evil is discussed at the vim-emulation mailing list also accessible via gmane: http://news.gmane.org/gmane.emacs.vim-emulation that surely should also go into the 'Where you can learn more' list ;)
You may try the following motions for sexp-navigation. The main difference to the Emacs ones is that (point) is that the closing ")" is considered as the end of an sexp and point is placed there instead of the following character. Note that this is only a quick hack and widely untested (there may very well be edge cases)!
(evil-define-motion evil-fwd-sexp (count)
:type inclusive
(setq count (or count 1))
(unless (zerop count)
(goto-char
(save-excursion
(condition-case err
(let ((opoint (point)))
(cond
((> count 0)
(condition-case nil
(forward-sexp)
(error (forward-char)))
(when (> (point) (1+ opoint))
(setq count (1- count)))
(forward-sexp count)
(backward-char)
(point))
(t
(setq count (- count))
(forward-char)
(condition-case nil
(progn
(backward-sexp)
(setq count (1- count)))
(error (backward-char)))
(backward-sexp count)
(point)))))))))
(evil-define-motion evil-bwd-sexp (count)
:type inclusive
(evil-fwd-sexp (- (or count 1))))
This is the best post on evil-mode. Besides, I love your post on org2blog.
Great.
I just discovered evil mode. One more thing I can recommend is Pentadactyl addon for firefox.
I tried Evil, recently. I found it buggy. I'm sticking to Vimpulse for the time being. Yes, Vimpulse is discontinued, but it gives you enough Vim power to be dangerous. Thank you for sharing, anyway. The future is Evil, anyway. Cheers.
I too tried evil, recently. Found it buggy. Definitely better
compared to Viper/Vimpulse, but still buggy. simple eg.
like copying to registers doesn't work. "n2yy. Still needs
those final touches.
Continuing the Discussion