Background

I’m a Vim user. At the end of the day Vim is what makes sense to me, partly because I’ve been using Vim for years now, and partly because the way Vim forces you to interact with text is more efficient than other methods that I’ve found. So why am I, a Vim user, writing about the difference between Vim and Emacs? Emacs has evil-mode, or the “Extensible VI Layer”.

For the few who are out of the loop, Vim and Emacs are like the Cowboys and Redskins, usually diametrically opposed to each other. Vim and Emacs are (probably) repsonsible for the invention of the term “editor wars”. It’s probably (definitely) a silly argument, but there’s merit to it. Emacs is an everything and the kitchen sink environment designed to allow you to live in your editor and become one with it. Vim is an editor, and that’s about it. Emacs has elisp, a fantastic programming language that leads some people to say that Emacs is a Lisp machine disguised as an editor. (The more popular joke is that Emacs is an OS, or Eight Megabytes of ram And Constantly Swapping.) Vim has Vimscript, a franken-language that has been mangled over the years into a semi-decent scripting language for a misunderstood editor.

For those of us who use a text editor in place of an IDE, one of the more important factors in choosing an editor is extensibility. We want to massage the editor to do our bidding in the way that makes most sense to us. This is why the comparison between elisp and vimscript is relevant. Both editors are extensible. There are a myriad of tutorials talking about making Vim an IDE. This is where my statement that Vim is misunderstood comes from. Vim is not an IDE. It shouldn’t be treated like one. When using Vim, it should be treated like one part of the IDE that is UNIX. This is why I like Vim. It is a tool for editing text and it doesn’t try to do anything else. Additional features like test integration and running shell commands are delegated to other tools like TMUX or, very recently, the builtin shell. That, in my mind is the biggest strength of Vim, the fact that it delegates non-editing tasks to non-editing tools.

Emacs’ strength is exactly opposite that of Vim. Emacs is and editor, yes, but it’s also anything else you want it to be. There are tutorials on making Emacs an IDE, and I’m not salty about it the way I am about “Vim as an IDE” tutorials. Emacs thrives on having everything integrated, hence the joke about it being an operating system. Emacs, being a lisp environment, can run anything you can write in elisp. People have written web browsers, PDF viewers, image viewers, and at least two email clients that run inside Emacs.

(Note: This comparison intentionally ignores NeoVim. NeoVim is fantastic, but I have all of 3 weeks experience with it, and therefore can’t judge it vs “vanilla” Vim or Emacs.)

Configuration

Vim

Vim can be run vanilla, and a lot of people choose to do this. Most of the people that choose to run Vim with little or no configuration tend to be sysadmins or other people who are frequently working on multiple machines. That being said, if you want to extend vim, it’s quite easy to do so. The way to do this is via a vimrc that is found either in ~/.vimrc or ~/.vim/vimrc. Syntax wise it’s not difficult. For a quick example:

" Text after double-quotes is ignored as a comment.
set shell=zsh " Tells vim to use zsh when running shell commands.
set updatetime=2000 " Sets the "timeout" for :checktime

set backspace=indent,eol,start " Make backspace behave in a sane manner. By default, Vim doesn't allow backspace across linebreaks in insert mode, this fixes that.
set showcmd " Shows in-progress commands in the lower right corner.

That’s just basic settings. More advanced customizations are possible via more advanced Vimscript. Like this:

" Rotate through numbering options
" This definition is shows where vimscript is a franken-lanugage.
" The functions# is akin to a namespace, and the # designates the function as an autolaod.
" The bang (!) prevents vim from yelling when this file is reloaded.
" Without it Vim would yell about NumberToggle already being defined.
function! functions#NumberToggle()
    " Check if a variable is set. This reveals the oddity of vimscript.
    " The g: defines NumState as a global variable. s: would scope the
    " variable to the script in which it's defined. a: is function argument level scope
    if !exists('b:NumState')
        " Define a buffer local variable.
        " The b: designtes the variable as buffer local.
        let b:NumState = 1
    endif
    if b:NumState == 1
        " adjust settings based on state
        set number " Enable line numbers
        set norelativenumber " And relative line numbers.

        " The combination of both shows the current line
        " number and the count of lines above and below to
        " allow quick jumps with `10k` for example, to jump
        " up 10 lines.

        let b:NumState = 2
    elseif b:NumState == 2
        set relativenumber
        set number
        let b:NumState = 3
    elseif b:NumState == 3
        set relativenumber
        set nonumber
        let b:NumState = 0
    else
        set nonumber
        set norelativenumber
        let b:NumState = 1
    endif
endfunction

The above is an example. Vimscript is quite capable, if a bit odd. People have written rather complex software in Vimscript. See nerdtree and vimplug for quick examples. There are plenty of others.

Emacs

Emacs actually has a GUI for configuring it, but the most power is found in writing your own elisp code. Most of this, in a lot of cases, involves selecting and installing packages. It seems, in my somewhat limited experience, that the canoical way to do this is via use-package. For example:

;; Semicolons preceed comments. I've frequently seen two, but one is enough.
;; Modes
(use-package markdown-mode
:mode "\\.md\\'"
:hook (markdown-mode . (lambda nil
                        ( visual-line-mode )
                        (linum-relative-mode 0)
                        ( display-line-numbers-mode 0))))

;; Mu4e
;; Mu4e is a mail client in emacs that integrates with the mail indexer Mu.
;; It comes with the Mu package on most distributions, so I've configured Emacs to look
;; for it in 2 likely locations based on if I'm on a Mac or a Linux box.
(if (file-exists-p "/usr/share/emacs/site-lisp/mu4e/")
    (add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e/")
(message "mu4e not found in /usr/share/emacs/site-lisp/mu4e/"))

(if (file-exists-p "/usr/local/Cellar/mu/1.4.5/share/emacs/site-lisp/mu/mu4e" )
    (add-to-list 'load-path "/usr/local/Cellar/mu/1.4.5/share/emacs/site-lisp/mu/mu4e" )
(message "mu4e not found in /usr/local/Cellar/mu/1.4.5/share/emacs/site-lisp/mu/mu4e"))

(add-hook 'after-init-hook
        (lambda nil ""
            (require 'mu4e)
            (defvar mu4e-maildir (expand-file-name "~/.config/mail/"))

            (defvar mu4e-mu-binary (executable-find "mu"))
            (defvar mu4e-drafts-folder "/Drafts")
            (defvar mu4e-sent-folder   "/Sent")
            (defvar mu4e-trash-folder  "/trash")
            (defvar mu4e-sent-messages-behavior 'delete)
            (defvar mu4e-get-mail-command (expand-file-name "~/.local/bin/getallmail") )
            (setq user-mail-address "email@example.com"
                user-full-name "Bob")
            (setq message-send-mail-function 'message-send-mail-with-sendmail
                sendmail-program "/bin/msmtp")))

In contrast to Vim, I’m unaware of anyone who runs Emacs without some sort of extensions. A lot of people run Emacs with dozens of extensions. Thankfully, there’s a lot of infrastructure within Emacs to make sure that loading dozens of plugins doesn’t slow things down. Within use-package there’s the :defer directive, and the :command directive, both of which tell Emacs to load things either after initial startup when there’s a “dull” moment, or when a particular command is run. Magit is one place where the :command directive is useful since there are defined commands that Magit can be told to load in on. Magit is also not the best example as they do a lot of autoloading by default. Both of these hook into core emacs functionality such as the eval-after-load function that defers evaluation until an opportune moment, as outlined above.

Editing text

For me, editing text is similar in both programs thanks to the existence of evil-mode. Without evil mode, Emacs uses key-chords to move around and control the editor. Counter-intuitively, at least to modern users, saving exists as one of these key-chords, C-x C-s, which is Emacs-speak for press control + x immediately followed by control + s. My initial exposure to Emacs resembled a Vim meme, actually. I couldn’t figure out how to get out of the program, so I would close the terminal session. (My intial exposure was on a Mac, which by default only had terminal Emacs by default.) It turns out that the “save and exit” chord is C-x C-c. More general editing can be done with key-chords as well. C-a jumps to the beginning of a line, C-e jumps to the end of a line, C-k kills a whole line forward from the cursor. There are other interesting functions along those lines, but I’ve never really learned the Emacs binding for them, thanks again to evil-mode.

Vim is where I’m more familiar with the bindings. Vim is also a bit odd. When Vim starts up, you can’t type anything without getting lucky. This is because you’re initially dropped into “normal mode” which allows you to control the cursor and edit text directly via short key commands. For example dap deletes around an entire paragraph, which is defined by newlines, di" deletes inside quotation marks. There are multiple variants of each of these commands, each involving a verb such as delete(d), change (c) or substitute (s); a text object such as a word (w), paragraph (p), or delimiter (most punctuation), and a limiter such as around (a), inside (i), until (t) and find (f). Commands are composed quickly by thinking about exactly what is needed. In the following code block, if I want to change all the arguments to the function I can simply say change (c) inside (i) parenthesis [( or )], which comes out as ci(.

def pull_push(self, pull, push):
    subprocess.call(self.fetch)
    stat = self.check_status()
    dir = os.getcwd().replace(self.home, '~')
    if stat != 0:
        if stat == -1:
            print('\n' + dir + '\n' + str(subprocess.call(pull)))
        elif stat == 1:
            print('\n' + dir + '\n' + str(subprocess.call(push)))
        else:
            # since nothings equal we'll diff it.
            subprocess.call(['git', 'diff', 'HEAD', 'master',
                             'origin/HEAD', 'origin/master'])
    else:
        print(self.show_message())

The power of this is that once you understand the mnemonics any action can be composed of quick keystrokes in normal mode. Deleting any sequence of characters is as simple as figuring out what the text object is needed to describe the desired action. (A text object is a word, paragraph or other section of text defined by a know delimiter.) This explains the old joke that Emacs is a great operating system lacking only a decent editor. Evil solves this.

Most programmers enjoy having some sort of help with their code. Emacs and both have build in means of completing keywords and other programming bits. I’m not very familiar with the builtin emacs completion options, mostly because they were instantly replaced with other things. Vim, on the other hand has very solid built in completions, even if they are a bit challenging to get to by default. OmniComplete advantage of any tags files you have configured along with analysis of the content in any open buffers to provide very solid completions. Emacs also has the capability of reading tags files and providing completions for them, but, like I said, I immediately replace the completions in Emacs with other options. (M-x apropos tags will find all the commands related to tags, and M-x apropos completion will find completion related functions and commands.)

This is where the lines start to blur between my specific Emacs config and Vim. My Emacs has more functionality than my Vim does now. That’s not to say that Emacs is better than Vim. Vim, and in particular NeoVim, is absolutely capable of doing what my Emacs is doing now. At the end of the day, I’m choosing Emacs for now. Some day I may switch back to Vim and implement similar capability to what I have in Emacs. For now evil-mode, elisp, solid code completion, and the extreme extensibility of Emacs are going to keep me here.