What's New in Emacs: Last Decade Edition

What's New in Emacs: Last Decade Edition

14 Dec 2024

Emacs has come a long way in the past decade. This is meant as a guide to anyone who’s been using stock or near-stock Emacs for some years and wants a quick update on the new shiny stuff that comes bundled with Emacs.

This guide assumes you are running Emacs 29, which was released in 2023.

Completion #

Completion is pervasive in Emacs: hit TAB whenever you’re selecting a file (C-x C-f) or running a command by name (M-x) and the like, and completion kicks in. Emacs’ built-in completion framework and interface have gotten a huge upgrade in recent years.

If you hit TAB a bunch of times when writing e.g. a file name, you’ll open up the *Completions* buffer. Emacs 29 has lots of ways to configure this buffer to be much more useful than the default:

(setopt enable-recursive-minibuffers t)
(setopt completion-auto-help 'always)
(setopt completions-max-height 20)
(setopt completions-format 'one-column)
(setopt completion-auto-select 'second-tab)

Here’s what each one does:

enable-recursive-minibuffers
You can interrupt doing something in the minibuffer with another minibuffer-using operation.
completion-auto-help
The value 'always means always show the *Completions* buffer when trying to complete; other options include nil and 'lazy.
completions-max-height
Controls how many lines high the *Completions* buffer should be.
completions-format
Put all completions in one column. I like this formatting better.
completion-auto-select
Controls when to jump to the *Completions* buffer automatically. The 'second-tab option is nice: the first TAB opens the *Completions* buffer, and if you want to select something from the list, you just hit TAB again.

That’s for minibuffer completion. Emacs also supports completion for whatever the cursor is on; Emacs calls this “completion-at-point”. Here’s how to get nice tab-complete behavior in Emacs:

(setopt tab-always-indent 'complete)
(setopt completion-styles '(basic initials substring))

Setting tab-always-indent to 'complete means that when you hit TAB, Emacs will first try to indent the current line. If the line is already indented, then Emacs will call the completion-at-point facilities of Emacs. Assuming you have the minibuffer completions set up as explained above, you can use the same interface to complete in the minibuffer as well as tab-completion that you see in other editors.

These are just the built-in completion mechanisms. Emacs’ framework has gotten a great upgrade that has allowed packages like Vertico (minibuffer completion) and Corfu (completion-at-point in a popup window) to flourish. Vertico and Corfu are two of my favorite packages.

The completion-styles is neat: it takes a list of different “styles” that can be used when filtering candidates. The initials is particularly fun: type M-x dtw TAB and you should see the function delete-trailing-whitespace in the *Completions* buffer.

Editing code #

Smart completion, jump-to-definition, etc. #

Emacs 29 added the Eglot (“Emacs polyGLOT”) package to the core distribution: Eglot is a lightweight Language Server Protocol (LSP) client. This lets Emacs talk to language servers like clangd or rust-analyzer (etc.) to get things like good code completion, jump-to-definition, documentation, etc.

Eglot is conservative: it doesn’t get in your way, and all the completion smarts come up only when you ask for them. Of course, it’s possible to make it more eager and behave a little more like VS Code, but that’s your choice. I personally use jump-to-definition more than any other feature.

If you’re working on—say—a Rust project, install rust-analyzer on your system, then open a file in a Rust project and run M-x eglot. Now all of the completion-at-point mechanisms should be smart about the language you’re working with. You should also be able to jump to e.g. a function definition by putting your cursor on a function and invoking xref-find-definitions (bound to M-. by default.)

Projects #

Emacs now has project.el package that adds some nice stuff for navigating projects. For example, the project-find-file command is like find-file but is restricted to files in the current project. This pairs nicely with fancier completing-read interfaces to let you find and jump to files without having to navigate the entire file hierarchy.

Tree-sitter #

Tree-sitter is a parser generator tool that is fast and—most importantly—works on incomplete inputs (e.g. a file you’re in the middle of editing). Emacs 29 added support for tree-sitter enabled modes. To be honest, it’s a little clunky still, but support is improving. (The Emacs 30 pre-release is already better than what 29 does.) Tree-sitter is too big for me to cover here; see Mickey Petersen’s excellent article on the subject.

Editing prose #

There’s a built-in dictionary feature in Emacs 28. Use this to look up words from https://dict.org:

(setopt dictionary-server "dict.org")

Use M-x dictionary-lookup-definition to look up the word at point. You can of course bind this to a key for more convenience. (Looks like there’s also dictionary-tooltip-mode which shows the definition of a word when you hover over it with your mouse. Fancy, and a bit too much for me. But very cool!)

Going really far back is flyspell-mode: spellchecking while-you-type. Nothing special about that; what is special is how easy it is to correct words: hit C-; to cycle through corrections of the closest misspelled word. For code, there’s flyspell-prog-mode, which does spellchecking in just comments and strings. Even though this has existed for a while, I don’t see it turned on very often.

I now use Jinx for my spellchecking needs. It’s faster and more flexible, but it draws a lot of inspiration from flyspell-mode.

This mode is also a bit older, but I love visual-line-mode: turning this on will soft-wrap lines at word boundaries and make all your motion commands behave according to visual rather than logical lines: e.g. if I have a really long line that is wider than my window, it will be wrapped—just like in any word processor—and pressing the arrow keys will move me to the character that is visually above the current one, even though it might be on the same line.

General improvements #

New themes #

Emacs comes with two new themes: modus-operandi (light) and modus-vivendi (dark). These themes have excellent contrast and are ment to conform to the highest levels of visual accessibility. Try them with M-x load-theme RET modus-vivendi.

Package manager and configuration #

Emacs has a built-in package manager called package.el. This makes it easy to install 3rd-party packages. Run M-x list-packages to activate.

I have a list of my favorite Emacs packages, which include (but are not limited to) Magit, Vertico, and Avy.

There are several places you can get packages from; when package.el came out it only supported packages from GNU ELPA. Recently Non-GNU ELPA got added to the stock list which opens up a huge set of packages such as the venerable Magit package. And of course, there’s MELPA, which you have to add to your config yourself if you want packages from there:

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

Emacs 28 added the awesome use-package macro, which makes configuring packages really nice. Previously you might have written something like this to configure a package:

(require 'citar)
(define-key global-map (kbd "C-c C-i") 'citar-insert-citation)
(define-key global-map (kbd "C-c C-d") 'citar-dwim)
(add-hook 'org-mode-hook 'citar-capf-setup)
(add-hook 'LaTeX-mode 'citar-capf-setup)
(setq citar-bibliography  '("~/Research/refs.bib"))

Now you can do this with a macro:

(use-package citar
  :bind (("C-c C-i" . citar-insert-citation)
         ("C-c C-d" . citar-dwim))
  :hook ((org-mode LaTeX-mode) . citar-capf-setup)
  :custom (citar-bibliography '("~/Research/refs.bib")))

What you don’t see is all the extra work that use-package does behind the scenes to speed up startup times by lazily loading your package. There are lots of options for setting up hooks, different kinds of configuration, keymaps, etc.

Performance and utilities #

Emacs 27 added a native JSON parsing library. This meant that things like Eglot (and other LSP clients) got much snappier. Emacs 28 can compile Emacs Lisp code to native byte code. This means that all Emacs packages just run faster.

There’s a lot more, but those are some of the biggest ones. Nice to see Emacs getting a lot of love.

Ecosystem #

Many of the improvements to the core have also lead to a flourishing of excellent third-party packages. Here are some of my favorite:

Magit
The only Git porcelain worth using. I was once a Git command-line purist. Then I found Magit and became a convert: unlike other Git porcelains, Magit hides none of what Git can do. Moreover, it makes complicated operations (e.g. staging or reverting individual lines of a file) easy and exposes you to more of what Git can do.
Vertico
“Vertical completion”: wraps the standard completing-read interface with a nice interactive version. Thanks to the improvements to the completing-read API, all Emacs features that use completing-read just work.
Consult
Enhancements for a bunch of Emacs’ UI: for example, consult-buffer replaces the standard switch-to-buffer with a version that shows you a live preview. Pairs nicely with Vertico.
Corfu
Enhanced completion-at-point interface: get a fancy popup window to show completion candidates when completing a word in-buffer. Corfu uses a child frame by default; for non-GUI users there’s corfu-terminal to make it work in TUI mode as well.
Citar
Citations made easy: Citar reads .bib databases and provides a slick completing-read interface to insert citation keys.

There are so many more packages that have come out in the past few years that I use all day every day. I wrote about some of them here.

A starter kit for this #

I made Emacs Bedrock as a starter kit to help explore all these nice new built-in features of Emacs. It’s a true starter kit: you download it once, then tweak it to your liking. By default it installs only one package (which-key; Emacs 30 will have this package built-in) and the rest is tweaking some settings to be what I think the defaults should be. It’s meant to encourage exploration. If you liked something from this post, take a look at Bedrock and maybe you’ll find something else there that you like!

Mastodon