Blogs

Emacs for LaTeX

Emacs for LaTeX

If you're here to copy and paste the full config file, use this link. Otherwise enjoy reading!

Introduction

Welcome back to another iteration of the Emacs from Scratch series. This time I'm going to be setting up Emacs for writing documents in LaTeX, which I think is one of the best uses for Emacs, especially for beginners. Today I'll be setting this up on a fresh Debian 11 instance on Vultr, but hopefully the same steps will work on other mainstream distributions like Ubuntu and Arch.

As in the last installment, the first command I'll run is

sudo apt update && apt upgrade

to make sure my installed packages are up to date and to refresh the package repositories for installing Emacs. After that, I will actually install Emacs with the command

sudo apt install emacs

That should finish pretty quickly, and in my case it looks like it installed Emacs 27.1, which is a pretty recent version. Now I'll start the long installation of LaTeX. There are several packages available that provide LaTeX with varying levels of accompanying packages. I think texlive-base is the bare essentials, texlive offers "a decent selection" of packages, and texlive-full comes with basically everything you can imagine and requires about 5-6 Gb to fully install. Since I use LaTeX for science and math, I'll give texlive-science a try. I think on Arch I install texlive-most, which, as the name indicates, comes with most of the packages, but excludes most of the language packages that make up most of the size of texlive-full.

apt install texlive-science

Finally, I'll install my favorite PDF viewer, zathura.

apt install zathura

Of course, all of these could be combined into a single command:

apt install emacs zathura texlive-science

At this point, my YouTube video begins, so you can also watch that to follow along.

As a quick aside, Emacs users typically abbreviate the Control key as C- and the Meta or Alt key as M- in a key chord. So when I say type "M-x" that means hold down your Alt key and press the x key at the same time. Space in key chords mean they are separate commands. For example C-x C-f, the chord for find-file, is produced by holding Control, pressing x, releasing x, and pressing f. Of course you can release Control at the same time you release x, but since you're about to press C-f you probably won't.

Basic Emacs Configuration

Just like last time, the first bit to add to the fresh Emacs config initializes the package repositories so we can actually install stuff.

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

After evaluating this code (probably by placing your cursor after each closing parenthesis and typing C-x C-e or by running M-x eval-buffer), you should run M-x package-refresh-contents to ensure your package repositories are up to date. This should head off any issues with installing packages. One other thing I remembered in making the video is that some of the later package operations will write out Custom variables to your config file. If you want to hide that, just add the following snippet:

(setq custom-file "~/.custom.el")
(load custom-file)

For the sake of this post, I just use the .emacs file in my home directory, but if you are using a more normal path inside ~/.emacs.d, just update the path to be in there, or really anywhere you prefer.

With that out of the way, we'll use use-package to install the rest of our packages since that's the easiest technique I've found. This snippet will set that up:

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)

Assuming you're a Vim user, the next thing to set up is evil-mode, the major Vim emulation package for Emacs. Now that we have use-package, we can do that with

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

One "issue" with evil-mode now is that it doesn't come with an undo/redo facility by default. Really this is an issue with Emacs, which is supposed to be fixed, I think in Emacs 28. I call it an "issue" with evil-mode because evil used to come with undo-tree, but several months ago it dropped that as a dependency, instead opting to let (make) you choose your own. We'll just install undo-tree, which is actually pretty nice on its own as well. We'll also enable it globally.

(use-package undo-tree
  :ensure t
  :config
  (global-undo-tree-mode 1))

Now that we have undo-tree, we need to make some tweaks to our evil config. The main thing here is telling evil to use undo-tree as the undo system, but I also added evil-search-module since the new default is to use the built-in Emacs search functionality instead of Vim-style searches. If you're not a Vim user, you may want to try the default as well.

(use-package evil
  :ensure t
  :config
  (evil-set-undo-system 'undo-tree)
  (setq evil-search-module 'evil-search)
  (evil-mode))

One other piece of basic setup I like to do is installing ivy, which offers nicer completion menus than the Emacs defaults. This part is totally optional but may smooth your experience with switching buffers and completing LaTeX commands later on.

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

AUCTeX

With that basic functionality established, all we really have left is to install AUCTeX, which helps to smoothly integrate Emacs and LaTeX. As usual, the basic configuration is just to ensure the package is installed with use-package. However, we also need to make sure to defer the package to avoid an error about auctex not providing the auctex feature, which I ran into in the video. I think this is because auctex doesn't provide its own package, it just adds features to the built-in TeX package or something like that. Regardless, :defer t will prevent that.

(use-package auctex
  :ensure t
  :defer t)

Assuming you want to view the PDFs produced by LaTeX in an external program, you'll also need to set the TeX-view-program-selection variable to your external PDF viewer. Since we installed zathura and since it automatically refreshes when you recompile your document, update your configuration as shown below.

(use-package auctex
  :ensure t
  :defer t
  :hook (LaTeX-mode . (lambda ()
			(push (list 'output-pdf "Zathura")
			      TeX-view-program-selection))))

I don't really think this should need to be a hook, but this is the best way I've found to ensure you don't run into a weird error message on startup. If you try setting the variable directly, you will likely receive an error about it being an unbound variable before you load AUCTeX the first time.

Now that we have auctex installed and set up, let me show you around a bit. To do that, you'll need a minimal example of a LaTeX file such as the one shown below:

\documentclass{article}

\begin{document}

Hello world

\end{document}

The first thing to do is press C-c C-c, which will let you select a LaTeX command to run. The default should fittingly be LaTeX. Pressing RET will compile the document. After that, pressing C-c C-c again should default to View since the document is already up to date. Since we have set up Zathura as our PDF viewer, pressing RET on View should prompt you for a Zathura command. Pressing RET again should open Zathura.

One of the most useful features of AUCTeX, beyond the ease of compilation and viewing, is its insertion of various LaTeX environments through the C-c C-e binding. This part is probably easier to see in the video or just by trying it yourself, but inserting a table, for example, will walk you through the parts of a table environment, including the caption, the label, and the alignment of the columns. This is very helpful especially if you are new to LaTeX. You can similarly insert environments like figures, itemized lists, enumerated lists, equations, and so on. That basically exhausts what I typically use inside of AUCTeX, but if you press C-h b to list the bindings available and search for latex-mode, there are actually many more useful bindings. I already see some that I will try to integrate into my workflow in the future.

One last feature that is pretty nice, and will become apparent as soon as you start typing a real document, is the in-line preview. Assuming you're running a graphical Emacs instance, AUCTeX will preview font features like sub- and superscripts, italics, and boldface. I don't find this that helpful personally, but some people who have seen me editing LaTeX files have been very impressed, so I hope people reading this will be too.

Snippets

If the existing AUCTeX commands don't do enough for you, another nice thing to include when editing LaTeX is a way to insert snippets. These allow you to type a small prefix, which expands into a full piece of code when you press Tab. The package I use for snippets in Emacs is called yasnippet. Install it using the code below.

(use-package yasnippet
  :ensure t
  :config
  (yas-global-mode 1))

Once you have this installed, you can add a new LaTeX snippet from a TeX buffer with the command M-x yas-new-snippet. This will open a new buffer that looks like

# -*- mode: snippet -*-
# name:
# key:
# --

You can put whatever you want in the name spot, but the key is what you will type to trigger the snippet. I usually make them both the same thing. Since I write about fundamental vibrational frequencies often and these are typically referred to with the Greek letter "nu", I have a snippet like the one below set up:

# -*- mode: snippet -*-
# name: nu
# key: nu
# --
\$\nu_{$1}\$$0

After I type nu in a LaTeX file, I can now hit Tab and this text is inserted, then my cursor is placed in the $1 location. There I can type the appropriate subscript. Then when I hit Tab again, my cursor moves to the $0 location and the snippet is finished. One problem I ran into on the video, though, is that saving the snippet did not work as usual. Normally I can just press C-c C-c to finish editing the snippet. If you run into the same problem, this keybinding is supposed to be bound to yas-load-snippet-buffer-and-close, so you can at least call this directly. I got out of it by saving the snippet buffer, but that's not as easy as it should be.

If it's not clear, yasnippet is useful in other modes as well. For example, when working on a very repetitive programming project, I wrote a Golang snippet for inserting the template of a for loop. I've also written some for org-mode that I can't think of right now. But the point is that snippets are very handy anywhere there is a lot of repetitive text to be inserted, potentially with components that need to be modified each time.

Viewing PDFs in Emacs

If you aren't satisfied with opening your PDFs in an external viewer, you can also use Emacs to view the PDFs directly. As I showed (with some difficulty) in the video, you can use the built-in doc-view-mode to view PDFs by finding them with C-x C-f, but this is pretty plain. Another issue with doc-view-mode is that it doesn't update automatically upon recompiling LaTeX, but you can add a hook to fix that:

(add-hook 'doc-view-mode-hook 'auto-revert-mode)

The issue I ran into in the video was actually that I didn't have ghostscript installed, so make sure to run

sudo apt install ghostscript

if you do want to use doc-view-mode. With ghostscript installed, I think it should be triggered automatically for files with a .pdf extension.

Another option is to use the pdf-tools package. I've had some trouble with this package in the past, but that was several years ago. Just in the past week I have heard that one of my friends is using it to great effect, so I expect it is much improved. It looks like they have good documentation for installing it, so I will defer to them for now, but I may make another post or video about setting it up if I give it a try myself or if enough people request it.

Full Config

Here is the full configuration file I put together in the video and modified slightly while writing the post itself. As always, let me know if you have any suggestions or run into problems!

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

(setq custom-file "~/.custom.el")
(load custom-file)

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)

(use-package evil
  :ensure t
  :config
  (evil-mode)
  (evil-set-undo-system 'undo-tree))

(use-package undo-tree
  :ensure t
  :config
  (global-undo-tree-mode 1))

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

(use-package auctex
  :ensure t
  :defer t
  :hook (LaTeX-mode .
		    (lambda ()
		      (push (list 'output-pdf "Zathura")
			    TeX-view-program-selection))))

(use-package yasnippet
  :ensure t
  :config
  (yas-global-mode 1))

(add-hook 'doc-view-mode-hook 'auto-revert-mode)