This post shows a couple differences between Emacs and Vim.
Now, before I begin, I’d like to say that the only reason why I recently started using Emacs along with Vim is because of Emacs’ org-mode. It is a very powerful outlining platform that really takes advantage of Emacs’ Lisp architecture (well, all Emacs extensions, by design, enjoy the powerful Lisp backend). I’ve started using org-mode recently to act as my digital planner.
Also, I’m a die-hard Vim user, so I am obviously biased.
Cool things about Emacs:
- Lisp backend (it makes Emacs verrry powerful, perhaps too powerful (see “cons” below)): Emacs is not a text editor. It is a Lisp interpreter, that happens to be used primarily as a text editor. Org-mode, as I mentioned above, is an Emacs extension that really makes Emacs shine.
- Command-based keybindings: In Vim, keybindings resemble macros, like this:
nmap <F1> :up<CR>
where “:up” is the actual keys that need to be pressed by the user to achieve the affect you want (here, :up and pressing ENTER results in saving a document). In Emacs, instead of specifying the literal key presses, you describe which commands are called on by the keys. So for me, I have
(global-set-key "\C-cl" 'org-store-link)
which maps “CTRL-c, l” to the org-store-link function. You can of course do the same thing in Vim by just calling a Vimscript function for some particular keybinding, but check this out: in Emacs you can easily compose multiple-function hotkeys by defining a big blank function (aka “lambda expression” or “closure”) that is made up of smaller regular functions, like so:
(global-set-key "\C-cl" (lambda () (function1) (function2) (function3) (function4) ... (functionN) ))
How cool is that? This is yet another example where the Lisp backend makes this possible, and so easy. I actually prefer Emacs’ way of calling the functions/commands directly like this, as it makes keybindings much easier to understand and just sticks to a simple, uniform layout: make keys call either 1 or more functions.
- Better speed: By speed, I’m judging 3 common areas: startup time, parenthesis matching, and syntax highlighting. Emacs wins 2 out of 3. Here is the breakdown:
- Startup time: Vim is faster (GVim is faster than Emacs, and console vim starts *instantly* whereas console emacs takes a couple seconds). CORRECTION (Feb 17, 2011): Emacs’ startup time depends on two factors (how you write your ~/.emacs file, and whether you run it as a client of a long-running emacs daemon).
- Parenthesis matching: Emacs is lightyears ahead. Sadly, Vim slows down to a deathly crawl whenever I edit non-trivial LaTeX files, because whenever I move my cursor around (e.g., by holding down the h/l keys), it re-calculates the matching parenthesis/bracket/brace for every cursor position. Emacs chews up matching parenthesis highlighting in realtime, even for my slowest (on Vim) LaTeX documents. Again, Emacs is lightyears ahead here.
- Syntax highlighting: Emacs’ syntax highlighting is much faster, and does not slow down *at all* even with my heaviest LaTeX files. Vim slows down to a crawl for some of my LaTeX documents when I hold down the h/l/j/k keys for incremental navigation.
- Of course, Vim’s Normal mode makes editing files a breeze compared to Emacs’ clunky CTRL CTRL CTRL META META hotkey insanity. But Emacs is not a modal editor, so that’s why I didn’t include it in the breakdown above.
Some Emacs cons:
- It’s not modal. This is a huuuuuuuuuge issue for me. The *best* complaint against modal editing, from what I gather from the endless Emacs vs. Vim debate, is “Vim users have to keep reaching up to the ESC key with their left pinky every 5 seconds, and the ESC key is too far away from the home row — the same home row that Vimmers proudly call their home!”. And to this complaint, I have two practical answers:
- Use the built-in, default CTRL-[ shortcut instead of ESC. (For some reason, this is a big mystery to both Emacs and Vim users alike.)
- Map “jk” to ESC. These two keys are: on the home row (and if you learned how to type, your fingers are already on these keys), are faster to type (faster than “jj” or “kk” because in those cases you only use a single finger whereas with “jk” you use two), and almost never come up in English/programming. This is what I do; I never use the ESC key.
- Emacs’s Lisp backend makes it too powerful for its own good. Because “extending Emacs” really means “writing Lisp programs”, Emacs has become something of an OS. There are many Emacs users out there who use it as their text editor, as well as: an email/news reader, a PDF viewer, a web browser, version control management system, encryption/decryption mechanism, etc. This bizarre agglomeration of various programs all into Emacs makes it very bulky, and I suspect it is this bulkiness that slows down its startup time (although, as stated above, its parenthesis matching and syntax highlighting speed is lightning fast). CORRECTION (Feb 17, 2011): Actually, the startup time is very fast if my ~/.emacs config file is blank, but apparently my modest, relatively new ~/.emacs file loads up various plugins (Vimpulse and a color theme, among others) which slow the startup time down by a couple seconds.
That’s it! If Emacs were modal, then I would probably immediately switch to it. Currently, I use Emacs + Vimpulse whenever I need to use Org-mode (the fact that Vimpulse and Org-mode, two independent Emacs projects, work seamlessly side-by-side, emphasizes Emacs’ rich versatility). For all real text editing needs, I use Vim. Vim’s Normal mode lets me fly around a document like one of those crazy Harry Potter kids in a quidditch game.
What I don’t like about Vim:
- Vimscript: Vimscript is just painful to use for me. It’s a scripting language but it doesn’t even have case/switch statements. All functions must start with a capital letter. Yuck! Customizing/extending Emacs with Emacs Lisp is a dream compared to working with Vimscript.
That’s about it, apart from the parenthesis/syntax speed issues noted above. I am a die-hard Vim user, by choice.
UPDATE Feb 17, 2011: Wow, apparently this post got over 3k views today, because of referrals from (among others) HackerNews and Reddit. I’ll look over the discussions at those sites and post my thoughts on here, to “fill in the holes” in my original post, so to speak. Stay tuned.
- slysf says “The biggest con about emacs that’s not mentioned is that it’s just not _everywhere_.” Yeah, I’m just a hobbyist programmer so I have not had to deal with sysadmin/remote client issues. But it’s obviously a big bonus if you can use your editor of choice everywhere you go. Emacs’ saving grace is that it IS very portable, just that it’s not installed on every UNIX-y box out there by default…
- d0mine mentions that Python doesn’t have case/switch statements as well. Holy cow, I did not know that! And apparently, you can just use Python’s dictionary type to achieve what case/switch statements do. Ruby has case/when statements (same thing as switch/case), and I used them before many times, so I just found it strange when I tried to use Vimscript and there was no such construct. However, I’ve learned that Vimscript also has a dictionary type, so, like Python, I’m sure it can be used to emulate switch/case statements. Still, in the big picture of things, Vimscript’s power will always be dwarfed by larger, more mature languages that get beaten, abused, and tested by armies of professional developers; and this is why Vimscript will always be inferior: there aren’t that many users who pound on it daily and work to develop it. Vimscript is sort of like an island on its own, while Emacs Lisp has excellent lineage (it’s a LISP dialect).
- jaybee mentions that startup times really has to do with what’s in your init file. This statement is very true, and has already been incorporated in my original post as a correction. The fact that you can also run emacs as a server on boot and just launch clients instantaneously is very valuable advice. I will heed to it!
- sping says that he just uses one emacs window (frame) and works within it. As an XMonad user on a dual head, I like using multiple windows so much that I just use multiple windows, shells, etc. all the time. If you’re on Gnome or KDE, with no tiling window manager, then sping’s method would be superior in terms of speed (no mouse dependancy). I think there are legions of Emacs users out there who started using it before the advent of popular tiling window managers, and got used to the notion of “do everything within Emacs” so much that they never bothered to try out tiling window managers (and the benefits they bring — lightning fast window navigation/management, which makes using multiple shells/programs a breeze).
- sping also says “I have never yet read someone talking about vim who has actually made a good case for any superiority over Emacs in any significant area.” And my answer is: modal editing. Modal editing is a killer feature that Emacs is sorely missing. sping also says “I’m not actually against its modal nature, but I’m still waiting to find out exactly how it’s so f’ing awesome in ways which Emacs isn’t.” Hmm, what part of “modal editing makes navigation verrry fast” did I make unclear? I even made a Harry Potter reference, for goodness’ sake! Well, if you don’t care about fast cursor movement around, inside a file, then Vim has very little, if *any*, advantages over vanilla Emacs (the only “neat” thing I can think of is Visual Block mode, but that’s it). To be fair, sping is a former vi user, so he finds the whole modal editing thing not very impressive. To each his own.
- Just to expand more on sping’s comment about how he has yet to find anything in Vim that is so great over Emacs, I think he’s asking fundamentally the wrong question. Vim will never really have more “awesome ways” that make it superior over Emacs, because Vim is just a text editor. And Emacs is waaaay more than a text editor. So feature-wise, Emacs will always win. However, if you’re only comparing text editing vs. text editing, then I will re-iterate that Vim’s (only real) killer feature over Emacs is modal editing (and super-fast intra-file navigation).
- zck asks “Wouldn’t [pressing "jk"] just move the cursor up and then down?” Well, no. First, we are talking about going from Insert mode to Normal mode. If you’re already in Normal mode, then duh: your cursor will move up and then down. But since we are in Insert mode, and have “jk” insert-mode-mapped to ESC (imap jk <ESC>), every time you press j, Vim starts a little timer and sees if you press k next, and if you do indeed press k next, you escape into Normal mode. This timer is customizable, and I have it at 1 second.
On navigation speed: There are still people asking: “Is Vim really faster to navigate around a file than Emacs?” And my answer is: the hotkeys speak for themselves. (Vim is lightyears ahead.) Oh, and I also mapped the Space and Backspace keys in Normal mode like this:
nnoremap <space> 10jzz nnoremap <backspace> 10kzz
to make navigation even faster. (You should all customize Space/Backspace keys because by default they just do what l/h do.) UPDATE March 2, 2011: Oh, and if you are using vimpulse.el (version 0.5), you can do:
; simulate vim's "nnoremap <space> 10jzz" (vimpulse-map " " (lambda () (interactive) (next-line 10) (viper-scroll-up-one 10) )) ; simulate vim's "nnoremap <backspace> 10kzz" (define-key viper-vi-global-user-map [backspace] (lambda () (interactive) (previous-line 10) (viper-scroll-down-one 10) ))
to get the same behavior.
UPDATE Feb 17, 2011: Oh, and another thing: for years some people have wanted a “breakindent” (aka “soft wrap”) feature in Vim (i.e., make it so that a very long line gets a virtual indent for all successive “lines”, such that you end up with a neat, clean rectangle for a long line that has been indented at the very beginning). See this page and this page. Among other things, a breakindent feature would make outlines look very clean, with neat rectangular blocks for lines that start out at a deep indent. Sadly, I don’t imagine breakindent becoming a feature in Vim in the foreseeable future. In Emacs’ org-mode, breakindent is availble! Just put “#+STARTUP: indent” at the top of the .org file you are working on, and voila: sane line wrapping for outlines.
UPDATE August 5, 2011: I’ve recently switched to using the emacs –daemon and emacsclient command. Basically, I have a emacs –daemon & in my ~/.xinitrc, and then I use emacsclient -nw and emacsclient -c instead of emacs -nw and emacs, respectively. The startup times of emacsclient is near-instant — very impressive! The only drawbacks are that the emacs daemon process takes up 37MB on my system (but then again, with machines using gigabytes of RAM these days, it’s a nonissue), and you have to fiddle a bit with your ~/.emacs file to load up the correct font and color scheme (since emacs –daemon does not read the ~/.emacs file like regular standalone emacs with regard to the X11 options; see http://www.tychoish.com/rhizome/getting-emacs-daemon-to-work-right/ and http://old.nabble.com/–daemon-vs.-server-start-td21594587.html).
I’ve also dropped Vimpulse in favor of Evil, since the core developer for it (Vegard Øye) decided to join forces with another Vim-minded emacs extension author (Frank Fischer, of vim-mode) to create a newer, better Vim emulation layer earlier in February this year (just 10 days after the date of my original post!). See http://news.gmane.org/find-root.php?group=gmane.emacs.vim-emulation&article=692. Anyway, the official Evil repo is at http://gitorious.org/evil/ (more info at http://gitorious.org/evil/pages/Home). The only drawback right now is that Evil does not currently emulate Vi’s Ex mode (in Vim, when you type the colon ‘:’ character from Normal mode). If you really want this feature right now, too, then I suggest cloning from Frank Fischer’s personal branch, at https://bitbucket.org/lyro/evil/overview, which includes Ex mode. I suspect that Fischer’s Ex-mode code will be merged into Evil sometime in the future since Fischer keeps his branch in sync with the official Evil repo.
The best part about using Evil is that it does not depend on Viper, the old Vi extension for emacs.
Anyway, here’s how I’ve set up Evil in my ~/.emacs (many of these keymaps only make sense if you’re in org-mode, but whatever):
; evil, the Extensible VI Layer! seee http://gitorious.org/evil/pages/Home (add-to-list 'load-path "~/.emacs.d/script/evil") (require 'evil) (evil-mode 1) ; some keymaps from ~/.vimrc (define-key evil-insert-state-map [f1] 'save-buffer) ; save (define-key evil-normal-state-map [f1] 'save-buffer) ; save (define-key evil-normal-state-map ",w" 'save-buffer) ; save (define-key evil-normal-state-map ",q" 'kill-buffer) ; quit (current buffer; have to press RETURN) (define-key evil-normal-state-map ",x" 'save-buffers-kill-emacs) ; save and quit ; make "kj" behave as ESC key ,adapted from http://permalink.gmane.org/gmane.emacs.vim-emulation/684 ; you can easily change it to map "jj" or "kk" or "jk" to ESC) (defun escape-if-next-char (c) "Watches the next letter. If c, then switch to Evil's normal mode; otherwise insert a k and forward unpressed key to unread-command events" (self-insert-command 1) (let ((next-key (read-event))) (if (= c next-key) (progn (delete-backward-char 1) (evil-esc)) (setq unread-command-events (list next-key))))) (defun escape-if-next-char-is-j (arg) (interactive "p") (if (= arg 1) (escape-if-next-char ?j) (self-insert-command arg))) (define-key evil-insert-state-map (kbd "k") 'escape-if-next-char-is-j) ; simulate vim's "nnoremap <space> 10jzz" (define-key evil-normal-state-map " " (lambda () (interactive) (next-line 10) (evil-scroll-line-down 10) )) ; simulate vim's "nnoremap <backspace> 10kzz" (define-key evil-normal-state-map [backspace] (lambda () (interactive) (previous-line 10) (evil-scroll-line-up 10) )) ; make evil work for org-mode! (define-key evil-normal-state-map "O" (lambda () (interactive) (end-of-line) (org-insert-heading) (evil-append nil) )) (defun always-insert-item () (interactive) (if (not (org-in-item-p)) (insert "\n- ") (org-insert-item))) (define-key evil-normal-state-map "O" (lambda () (interactive) (end-of-line) (org-insert-heading) (evil-append nil) )) (define-key evil-normal-state-map "o" (lambda () (interactive) (end-of-line) (always-insert-item) (evil-append nil) )) (define-key evil-normal-state-map "t" (lambda () (interactive) (end-of-line) (org-insert-todo-heading nil) (evil-append nil) )) (define-key evil-normal-state-map (kbd "M-o") (lambda () (interactive) (end-of-line) (org-insert-heading) (org-metaright) (evil-append nil) )) (define-key evil-normal-state-map (kbd "M-t") (lambda () (interactive) (end-of-line) (org-insert-todo-heading nil) (org-metaright) (evil-append nil) )) (define-key evil-normal-state-map "T" 'org-todo) ; mark a TODO item as DONE (define-key evil-normal-state-map ";a" 'org-agenda) ; access agenda buffer (define-key evil-normal-state-map "-" 'org-cycle-list-bullet) ; change bullet style ; allow us to access org-mode keys directly from Evil's Normal mode (define-key evil-normal-state-map "L" 'org-shiftright) (define-key evil-normal-state-map "H" 'org-shiftleft) (define-key evil-normal-state-map "K" 'org-shiftup) (define-key evil-normal-state-map "J" 'org-shiftdown) (define-key evil-normal-state-map (kbd "M-l") 'org-metaright) (define-key evil-normal-state-map (kbd "M-h") 'org-metaleft) (define-key evil-normal-state-map (kbd "M-k") 'org-metaup) (define-key evil-normal-state-map (kbd "M-j") 'org-metadown) (define-key evil-normal-state-map (kbd "M-L") 'org-shiftmetaright) (define-key evil-normal-state-map (kbd "M-H") 'org-shiftmetaleft) (define-key evil-normal-state-map (kbd "M-K") 'org-shiftmetaup) (define-key evil-normal-state-map (kbd "M-J") 'org-shiftmetadown) (define-key evil-normal-state-map (kbd "<f12>") 'org-export-as-html)
UPDATE August 12, 2011: Great news! Frank Fischer’s Ex mode branch was merged into upstream just two days ago (commit 46447065ed5a374452504bd311b91902b8ce56d4), so now Evil has Ex mode!
Also, I noticed that Evil uses (kbd “DEL”) instead of [backspace] for binding the Backspace key (commit 8fc3c4065c7ed50a6033a7f1c68deba5a9270043), so I recommend changing the Backspace binding I posted previously to:
; simulate vim's "nnoremap <backspace> 10kzz" (define-key evil-normal-state-map (kbd "DEL") (lambda () (interactive) (previous-line 10) (evil-scroll-line-up 10) ))
I tested the change, and indeed, the previous binding with [backspace] was broken for console emacs, but it works with (kbd “DEL”).
UPDATE November 17, 2011: Tom (comment #19) pointed out that you can define hotkeys in a mode-specific way from within evil, as follows:
(evil-declare-key 'normal org-mode-map "T" 'org-todo) (evil-declare-key 'normal org-mode-map "-" 'org-cycle-list-bullet)
This way, your hotkeys will not collide if you change modes. (Not a problem for me because I only use emacs for org-mode, but there you have it.)
Oh, and the old “kj” escape key function no longer works; here is the new version:
(define-key evil-insert-state-map "k" #'cofi/maybe-exit) (evil-define-command cofi/maybe-exit () :repeat change (interactive) (let ((modified (buffer-modified-p))) (insert "k") (let ((evt (read-event (format "Insert %c to exit insert state" ?j) nil 0.5))) (cond ((null evt) (message "")) ((and (integerp evt) (char-equal evt ?j)) (delete-char -1) (set-buffer-modified-p modified) (push 'escape unread-command-events)) (t (setq unread-command-events (append unread-command-events (list evt))))))))
The original source is at http://article.gmane.org/gmane.emacs.vim-emulation/980. What’s great about this version is that it behaves almost exactly like the original vim mapping; once you type “k”, you have a 0.5-second window to type in “j” to escape — otherwise, you just get “k” on the screen.
UPDATE March 7, 2012: For a long time, I did not realize that the “10<c-e>10j” keybinding for <space> was not what I wanted, as it moved the cursor more than 10 lines down (<c-e> is an “impure” way to scroll). The proper way is to use “zz”, which resets the screen to have the cursor in the middle of the screen (vertically), without changing the current line position.