An Introduction to Vim Emulation and Package Management in Emacs

Evil is the package which, without a doubt, has the most significant impact on my emacs experience. Evil, as explained in its README, is “an extensible vi layer for Emacs” or, in other words, “It emulates the main features of Vim”. Most editors have a package for Vim emulation, and for good reason: once you get used to it, it changes the way you think about editing text. Put simply, Vim has two modes: an ‘insert’ mode that allows you to type text in whatever file you have open (this works much like any other editor), and a ‘normal’ mode that binds individual keys on your keyboard to different commands. For example, in normal mode, you would press j (on its own, without any modifiers like control!) to move your cursor to the line below. In other editors you could use the down-arrow key for this, but Vim has all its movement keys (h j k l) on the home row, the most convenient position for touch typists.

Most importantly, though, Vim provides a language for editing text. To quote a comment on reddit, “Vi(m)’s modal language is successful because it is a language, a rather intuitive one where different parts of speech can be combined into sentences. As your vocabulary grows, your overall text-editing repertoire grows quadratically. Each new word you learn can be used in most sentences you already know”. For example, the c key means ‘change’, the d key means ‘delete’, and the w key means ‘word’. In normal mode, you can press the w key on its own to move your cursor to the next word in the file, but you can also press ‘d’ then ‘w’ to ‘delete word’, which will delete the word underneath your cursor at that moment. ‘cw’ is ‘change word’ – it deletes the word under your cursor and puts you in insert mode so that you can type some text to replace the word you just deleted. You can also use numbers to modify your commands, like ‘d2w’ to ‘delete two words’. I recommend this medium article if you want to read more about Vim’s text editing language.

use-package (and straight)

Since use-package and straight.el both relate to package management, it’s probably worth beginning with an explanation of the package management that emacs offers out of the box. Since 2012, emacs has included a package system called ELPA (Emacs Lisp Package Archive). To install a package, you would run the list-packages command to view an interactive package list where you can view descriptions of packages and mark them for installation. ELPA (at the time of writing) only lists around 250 packages through, so many users opt to configure MELPA (Milkypostman’s Emacs Lisp Package Archive) which lists 4,395. To enable MELPA, you have to add some code to your init file. The init file is a file written in Emacs Lisp, a dialect of the Lisp family of programming languages, and by convention is created at ~/.emacs.d/init.el. To offer a practical example, the GitHub repository for the Evil package I introduced at the start of this blog suggests the following as a quick-start to use their package:

;; Set up package.el to work with MELPA
(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages"))
(package-initialize)
(package-refresh-contents)

;; Download Evil
(unless (package-installed-p 'evil)
  (package-install 'evil))

;; Enable Evil
(require 'evil)
(evil-mode 1)

use-package can’t help you with the configuration of MELPA, but it can simplify the configuration of Evil. use-package, according to its README, is a macro which “allows you to isolate package configuration in your [init] file in a way that is both performance-oriented and, well, tidy”. Lisp macros probably deserve a post of their own (and I hope one day to give them one), but for now I’ll draw on gte525u’s excellent Stack Overflow answer explaining them. In their words, “macros are used for defining language syntax extensions”, and they give the example of Python’s list comprehensions syntax. In Python 1.5 you had to write something like this:

divisibleByTwo = []
for x in range( 10 ):
   if x % 2 == 0:
      divisibleByTwo.append( x )

Modern Python, however, allows for this:

divisibleByTwo = [x for x in range(10) if x % 2 == 0]

Much neater. It’s only made possible in Python because the developers built the feature into the language, though. Lisp, on the other hand, lets you define macros to extend the syntax to do whatever neat things you desire. Pretty neat if you ask me.

Long story short, the use-package code to install and configure Evil looks like this:

(use-package evil
  :ensure t
  :config (evil-mode 1))

It offers a bunch of handy keywords, like :ensure t (which installs the package if it isn’t installed already), and enables you to group together all the configuration for a specific package into one neat bundle of code.

use-package is still cloning from ELPA (or MELPA) in the background, though, so if you want to install a package that only exists as a repo on GitHub you’re a bit stuck. The simplest way to solve that problem is to download the relevant Emacs Lisp file from the repo, save it (by convention) as ~/.emacs.d/lisp/some-package.el, and reference it in your init file with something like the following:

(add-to-list 'load-path "~/.emacs.d/lisp/")
(load "some-package")

This gets pretty frustrating when you’re used to use-package installing things for you, though, especially if you want to load your config on a different machine. This frustration is what brought me to embrace straight.el, the “next-generation, purely functional package manager for the Emacs hacker”. It offers use-package integration out of the box, allowing you to install a package like Evil with the following code:

(use-package evil
  :straight t
  :config (evil-mode 1))

But if you wanted to install a package like move-border which isn’t available on MELPA, you could do so like this:

(use-package move-border
    :straight (move-border :host github :repo "ramnes/move-border"))

This combination of use-package and straight.el gives me a gloriously neat config, and enables me to install packages from GitHub automatically, which is a godsend. But that’s not all! Because straight.el can install packages from GitHub, you can also really easily install forks of packages like so:

(use-package browser-refresh
  :straight (browser-refresh :host github :repo "syohex/emacs-browser-refresh"
                             :fork (:host github
                                          :repo "idmyn/emacs-browser-refresh"))

But that’s still not all! When straight.el installs a package from GitHub behind the scenes, it clones the repo to your machine. This means that you can open up the code for your fork of a package, make some changes, reload Emacs to check that your changes achieved what you intended, and then push those changes to GitHub there and then. Glorious.