Emacs in fifty keystrokes (and why some of you should just use Vim)

enzu.ru
11 min readNov 21, 2021
My gccmacs running as a window manager on GNU Guix

This article is geared towards:

  • Beginners trying to figure out where to start with Emacs
  • Intermediates trying to figure out where to take their keystrokes next
  • Advanced users looking for new ideas and interested in comparing notes

This article also reflects on the editor holy war and how to choose your side.

This article assumes you know how to evaluate Lisp in Emacs (hint: write code in the *scratch* buffer and execute M-x eval-buffer), where to put init code, and understand basic Emacs nomenclature (buffers, windows, frames, etc).

Emacs vs Vim, or Bob Dylan vs Elton John

Jakob Dylan, lead singer of the Wallflowers, said in an interview with Mark Maron regarding his father, Nobel Laureate Bob Dylan:

A lot of those other guys you talk about, The Rolling Stones and Elton John, those guys made like perfect records. And people want to hear those records and see them look hopefully similar to those records. But [Bob Dylan’s] records, you know, they don’t have the same kind of production value. […] They’re not locked in time. It’s the songs that have really traveled, not so much the recordings. So he can do them anyway he wants.

I believe Emacs is an imperfect record. In my opinion, out-of-the-box Emacs is confusing, ugly, garish, shocking, and maybe even campy.

Emacs has probably survived in spite of its default settings, not because of them. And this is both a strength and weakness, because now Emacs does not need to conform to any set form, much like Bob Dylan’s imperfect records have freed his songs to be permanently rearranged until the end of time.

In my completely subjective and anecdotally-based opinion, Vim has excellent keystrokes out-of-the-box, and I’ll controversially say that I think that the default Vim’s modal keybindings are far faster than default Emacs keybindings.

Part of using a programmable text editor is that you should constantly be reprogramming it in new ways to make it more efficient for you. Emacs users are like musicians that are building their own instrument. If you are not interested in changing, extending, or modifying Emacs, as a lifelong Emacs user I urge you to switch to another editor that better rewards your priorities.

Emacs users should not value muscle memory over new approaches

Sometimes people say they don’t want to rework their muscle memory for new keys… if that is the case, please switch to that other primordial text editor, Vim.

Emacs is about programmability. If you are not constantly programming Emacs to the best of your ability (and it’s okay if your ability is modest!), Emacs will be a poor investment for you. Vim would be a better investment
if all you’re looking for is a (mostly) static approach to keystrokes along with some plugins.

If you’re looking for ease-of-use and convenience, use a modern IDE or text editor. Use whatever you need; this is not a competition.

Part of reprogramming Emacs is constantly improving the keystrokes available to you whether you have been using Emacs for 2 years or 20 years. Experimenting between Vim-inspired modes (like evil) and vanilla Emacs modes. A whole Emacs community around is constantly writing amazing Lisp. 20 years ago your C-s wouldn’t be Swiper, but it possibly should be now.

StackOverflow Developer Survey 2019: https://insights.stackoverflow.com/survey/2019

Vim and Emacs are investments, and not everyone is an investor

Whenever I mentor a new programmer, I advise them that they have four major options for text editing:

  • Use an IDE as these are usually geared towards specific stacks and this focus brings unique quality-of-life perks
  • Use a modern text editor (VS Code, Sublime, Atom, etc) as they have easy learning curves and innovative features that apply across languages
  • Use Vim if you’re going for speed and muscle memory
  • Use Emacs if you want to program your text editor to do anything

The first two are an always shifting category. Cutting edge IDEs and editors come and go. Since they are both easy-to-learn but always going out of style, they are immediately efficient but hardly investing.

Yet, the latter two have out-survived almost all software projects that have ever existed. Through the decades, they are updated to this day.

This is worthy of reflection. In an industry that is constantly amorphous, they have transcended time. These editors are hard-to-learn, and have almost always existed, perhaps before you were born; Emacs dates back in its original incarnation to 1976. They are among the greatest and most enigmatic of programs to have ever been written.

I chose Emacs because there is that nothing more powerful than a programmer being able to program their own text editor in a language as powerful as Lisp.

Many programmers are not trying to invest heavily in their tools. Often programming for them is just a means to take care of their families; and what in life is more admirable than that? Perhaps the devotion to ancient editors strikes them somehow as mystical or cultish (depending on their politeness). And that’s perfect; everyone’s life journey is special and their own. This should never be about elitism; Emacs or Vim users are not better human beings.

But for those of us that will be machine whispering well past retirement, investments likes Emacs or Vim are incredibly powerful. The learning curve is incredibly steep, and the skill ceiling is incredibly high. Think of compound interest.

How this article works

The problem with learning Emacs is that it is overwhelming. There are far too many packages, far too many keystrokes, and far too many approaches.

In order to circumvent this learning problem, I am going to attempt to teach Emacs to you a few keystrokes and packages at a time. I am going to present to you fairly popular Emacs packages and keystrokes in small bite-sized groups.

Perhaps if you spent a day memorizing each group, in a week you could be reasonably proficient in a modern Emacs workflow.

I try to group the keystrokes by behavior, and make those keystrokes memorable. There are limits to such an exercise, so it remains imperfect.

I also unnecessarily and explicitly set some of the default keystrokes to their default values. This is just to make this article and your init.el easier to study.

You will want to follow this guide in order, as I only discuss how to setup each package once.

Let’s get started

First install MELPA, the dominant package service for Emacs. Some of my keystrokes involve third-party packages.

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)

Now, whenever I ask you to install a package, you can run M-x list-packages and use C-s to find the package to install it.

The five helper keystrokes

These are the five most important keystrokes of all. If you know these five keys keystrokes, you can get helpful information whenever you are stuck! These keystrokes are far more helpful than Google.

To make these keystrokes even more helpful, I want you to install swiper and counsel first which offers helpful searching and autocompletion for them. I have code like this for them in my init.el file:

(ivy-mode)
(counsel-mode)

(setq ivy-display-style 'fancy)
(setq ivy-use-virtual-buffers t)
(setq enable-recursive-minibuffers t)
(setq ivy-wrap t)
(defun counsel-ag-thing-at-point ()
(interactive)
(ivy-with-thing-at-point 'counsel-ag))

C-h(elp) b(indings) shows the keybindings available in the current buffer.

C-h(elp) f(unction) displays the current list of available functions.

C-h(elp) i(nfo) shows the info docs installed on your computer (no need to have a browser open just to read documentation).

C-h(elp) k(ey) let’s you type a keystroke and find out what it does.

C-h(elp) v(ariable) displays the list of available variables.

(global-set-key (kbd "C-h b") 'describe-bindings)
(global-set-key (kbd "C-h f") 'counsel-describe-function)
(global-set-key (kbd "C-h i") 'info)
(global-set-key (kbd "C-h k") 'describe-key)
(global-set-key (kbd "C-h v") 'counsel-describe-variable)

The ten navigation keystrokes

These keystrokes are for navigating Emacs. They are not intuitive at all, but are unfortunately necessary to memorize.

Like with the helper keys, you should have swiper and counsel installed for advanced search features.

C-tab let’s me switch to another visible buffer.

C-a and C-e get me between the front and end of a line respectively.

C-f and C-v let me scroll up and down respectively.

C-s let’s me search a buffer using Swiper.

C-w is my cut, M-w is my copy, C-y is my paste.

M-g let’s me quickly jump to any line number.

(global-set-key (kbd "<C-tab>") 'other-window)
(global-set-key (kbd "C-a") 'move-beginning-of-line)
(global-set-key (kbd "C-e") 'move-end-of-line)
(global-set-key (kbd "C-f") 'scroll-down)
(global-set-key (kbd "C-s") 'swiper-isearch)
(global-set-key (kbd "C-v") 'scroll-up)
(global-set-key (kbd "C-w") 'kill-region)
(global-set-key (kbd "M-g") 'goto-line)
(global-set-key (kbd "M-w") 'kill-ring-save)

The ten buffer management keystrokes

You should install windmove for this section, which allows us to select buffers using the shift button and arrow keys.

Some Vim and Emacs users share a distaste for the arrow keys for speed reasons, but I personally don’t. I don’t deny that they are a little slower.

C-up and C-down let me skip up and down paragraphs respectively.

C-x up and C-x down let me get to the top and bottom of a buffer respectively.

C-x left and C-x right let me run forward and backward through the list of buffers.

Shift-up, shift-left, shift-right, and shift-down let me move to a buffer in that direction.

(global-set-key (kbd "C-<up>") 'backward-paragraph)
(global-set-key (kbd "C-<down>") 'forward-paragraph)
(global-set-key (kbd "C-x <up>") 'beginning-of-buffer)
(global-set-key (kbd "C-x <down>") 'end-of-buffer)
(global-set-key (kbd "C-x <left>") 'previous-buffer)
(global-set-key (kbd "C-x <right>") 'next-buffer)
(when (fboundp 'windmove-default-keybindings)
(windmove-default-keybindings))

The four window management keystrokes

C-x 0 deletes an Emacs window, whereas C-x 1 deletes all other Emacs windows.

C-x 2 does a horizontal split, and C-x 3 does a vertical split.

(global-set-key (kbd "C-x 0") 'delete-window)
(global-set-key (kbd "C-x 1") 'delete-other-windows)
(global-set-key (kbd "C-x 2") 'split-window-below)
(global-set-key (kbd "C-x 3") 'split-window-right)

The four elscreen keystrokes

Install elscreen from MELPA to use these keys. This adds GNU Screen like functionality to emacs, where you can have multiple workspaces each with their own window layout.

C-z(one) c(reates) a new workspace.

C-z(one) k(ills) a current workspace.

C-z(one) n(ext) workspace.

C-z(one) p(revious) workspace.

(global-set-key (kbd "C-z c") 'elscreen-create)
(global-set-key (kbd "C-z k") 'elscreen-kill)
(global-set-key (kbd "C-z n") 'elscreen-next)
(global-set-key (kbd "C-z p") 'elscreen-previous)

The five console keystrokes

You will need to install docker, async-term, and magit for these keystrokes.

These keystrokes encompass things one commonly does in a terminal

C-c(onsole) d(ocker) brings up tools for managing and shelling into Docker containers.

C-c(onsole) e(shell) let’s me boot up an Emacs shell which is an awesome hybrid between a Lisp interpreter and a traditional POSIX shell.

C-c(onsole) x(term) let’s me boot up a real terminal.

C-c(onsole) s(tatus) let’s me bring up a dashboard with the current git status for the git repo that I am working in.

C-c(onsole) C-p(ushes) my current git branch to upstream.

(global-set-key (kbd "C-c d") 'docker)
(global-set-key (kbd "C-c e") 'eshell)
(global-set-key (kbd "C-c x") 'async-term)
(global-set-key (kbd "C-c s") 'magit-status)
(global-set-key (kbd "C-c C-p") 'magit-push-current-to-upstream)

The six primary executable keystrokes

You will need counsel for these keystrokes.

C-x c(ompile) let’s me run a compile command on any buffer I’m working on. This can be used to run an interpreter for your language too.

C-x f(ile) uses counsel to navigate to a file.

C-x k(ill) aggressively kills a buffer.

C-x l(ocate) uses counsel to locate a file on your disk.

C-x o(ther) let’s me select another window.

C-x s(tring replace) let’s me replace all strings in a buffer or selection.

(global-set-key (kbd "C-x c") 'compile)
(global-set-key (kbd "C-x f") 'counsel-find-file)
(global-set-key (kbd "C-x k") 'volatile-kill-buffer)
(global-set-key (kbd "C-x l") 'counsel-locate)
(global-set-key (kbd "C-x o") 'other-window)
(global-set-key (kbd "C-x s") 'replace-string)

The six secondary executable keys

You will first want to install ag for your operating system, which is a popular search tool for CLI users.

Then for Emacs, you are going to want to install ivy-xref for better autocompletion, and dumb-jump for function-finding, and devdocs for developer documentation search. Once those are installed, I have Lisp like this in my init.el:

(setq dumb-jump-force-searcher 'ag)

(require 'ivy-xref)
(when (>= emacs-major-version 27)
(setq xref-show-definitions-function #'ivy-xref-show-defs))
(setq xref-show-xrefs-function #'ivy-xref-show-xrefs)

(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)

C-x C-b(uffer) uses ivy to help me find a buffer.

C-x C-c(ounsel) let’s me search for a word at point in a project.

C-x C-d(ocumentation) let’s me search online documentation.

C-x C-m(eta) let’s me run an Emacs function.

C-x C-s(aves) a buffer for me.

C-x C-w(here) let’s me find where a function is defined in a language agnostic basis.

(global-set-key (kbd "C-x C-b") 'ivy-switch-buffer)
(global-set-key (kbd "C-x C-c") 'counsel-ag-thing-at-point)
(global-set-key (kbd "C-x C-d") 'devdocs-search)
(global-set-key (kbd "C-x C-m") 'execute-extended-command)
(global-set-key (kbd "C-x C-s") 'save-buffer)
(global-set-key (kbd "C-x C-w") 'dumb-jump-go)

I redefine C-x C-c because advanced Emacs users almost never exit Emacs and therefore don’t need a hotkey when save-buffers-exit-emacs will do.

Final notes

I redefine many keys on a per mode and per project basis. Remember how C-x c compiles, C-x C-d gets documentation, and C-x C-w locates a function?

Well, I will sometimes customize that functionality per language. Like for Emacs Lisp, I override those to better functions:

(add-hook 'emacs-lisp-mode-hook
(lambda ()
(local-set-key (kbd "C-x C-d") 'elisp-documentation-search)
(local-set-key (kbd "C-x C-w") 'find-function-at-point)
(local-set-key (kbd "C-x c") 'eval-buffer)))

This allows me to keep my keystrokes relevant in every language mode.

Happy hacking!

My .emacs.d/ may be of further help and can be viewed here: https://github.com/enzuru/.emacs.d

--

--

enzu.ru

Learning content for the GNU operating system and Lisp user space, developed through my eponymous charity (https://enzu.ru)