Completion Preview in Emacs

Some details about a new Emacs text completion feature

Created on [2023-11-17], last updated [2023-11-17]

Earlier this week, a new Elisp library of mine, completion-preview.el, made its way to upstream Emacs master.

This library provides a minor mode called Completion Preview mode. Can you guess what it does? Well, let me spell it out anyway. Completion Preview mode automatically displays a preview of a completion suggestion as you type. Here’s how this preview looks like when you start typing something (a Prolog predicate, in this case):

completion-preview-1.png

The preview appears right after the cursor, showing you how the text will look if you follow this completion suggestion. When there’s only one matching completion candidate, Completion Preview mode indicates that with an underline:

completion-preview-2.png

The idea is to suggest something that you might want to insert, and give you a shortcut for inserting it quickly, while staying minimally intrusive.

The Emacs manual and the commentary section of completion-preview.el contain helpful documentation for using this library, nevertheless I want to go into more detail and share a bit about my own set up here.

Usage

To enable Completion Preview mode, say M-x completion-preview-mode. Whenever Completion Preview mode shows the preview, it temporarily enables a helper minor mode called completion-preview-active-mode. The most important job of this “active” minor mode is to enable its associated keymap, completion-preview-active-mode-map. Other than that, it doesn’t do much by default, but this gives us a convenient way to bind some keys only when showing the completion preview. Crucially, whenever the preview is shown, TAB inserts its contents. Otherwise, Completion Preview mode doesn’t bind any keys. Again, the aim is to be maximally helpful while remaining minimally intrusive.

So far I’ve mostly described what the completion preview looks like and how you use it, but there are two important questions that you probably want me to explain to really understand how this mode works.

The first question is “where do the completion suggestions come from”? The answer is that Completion Preview mode uses completion-at-point-functions as its “backend”, and only that. This means that you control which completion suggestions you get with the same extensible mechanism that you use for customizing your regular in-buffer completions, no extra configuration needed. You can think of Completion Preview as another, second, “frontend” for completion-at-point-functions. I explicitly say it’s a second frontend, because Completion Preview does not replace your regular completion selection UI, be it corfu and its kind, icomplete-in-buffer, or the good old *Completions* buffer. The preview and the selection UI are two complementary features, so you can use Completion Preview with any completion selection UI.

The second important question is “when exactly is the preview shown?” The answer is that it may be shown after each command. My initial implementation included a user-extensible hook that Completion Preview mode consulted with after each command to determine whether or not to show the preview, but that was dimmed “over-engineered” during the patch review, so currently we have two simple conditions that must hold for Completion Preview to show the preview:

  1. the last command is one of completion-preview-commands, and
  2. the symbol at point is at least completion-preview-minimum-symbol-length characters long.

By default, this concretely means that the preview is shown whenever you’re typing or deleting characters and there’s a partial symbol/word of at least three characters at point. Of course, you can customize completion-preview-commands and completion-preview-minimum-symbol-length to modify these conditions. If you need more flexibility in specifying restrictions for when to show the preview, please speak up, and I’ll try to reintroduce that hook I mentioned earlier.

Here’s my current personal configuration for Completion Preview mode:

;; Enable Completion Preview mode in code buffers
(add-hook 'prog-mode-hook #'completion-preview-mode)
;; also in text buffers
(add-hook 'text-mode-hook #'completion-preview-mode)
;; and in \\[shell] and friends
(with-eval-after-load 'comint
  (add-hook 'comint-mode-hook #'completion-preview-mode))

(with-eval-after-load 'completion-preview
  ;; Show the preview already after two symbol characters
  (setq completion-preview-minimum-symbol-length 2)

  ;; Non-standard commands to that should show the preview:

  ;; Org mode has a custom `self-insert-command'
  (push 'org-self-insert-command completion-preview-commands)
  ;; Paredit has a custom `delete-backward-char' command
  (push 'paredit-backward-delete completion-preview-commands)

  ;; Bindings that take effect when the preview is shown:

  ;; Cycle the completion candidate that the preview shows
  (keymap-set completion-preview-active-mode-map "M-n" #'completion-preview-next-candidate)
  (keymap-set completion-preview-active-mode-map "M-p" #'completion-preview-prev-candidate)
  ;; Convenient alternative to C-i after typing one of the above
  (keymap-set completion-preview-active-mode-map "M-i" #'completion-preview-insert))

Recall that Completion Preview mode relies on completion-at-point-functions to produce the actual candidates, so make sure you set that up properly as well!

Alternatives

Now that we know what this new Completion Preview mode is all about, you might be wondering how it compares to other habitants of Emacs-land. There are several third party packages whose functionality is similar to, or overlaps with, that of Completion Preview mode. Sometimes, the same concept goes by different names, such as “suggestion preview” or “ghost text”.

Other than shipping with core Emacs, here are some key differences of Completion Preview compared to other embodiments of similar concepts:

corfu-candidate-overlay
builds on top of corfu while Completion Preview is completely agnostic to which completion UI you’re using
mono-complete
departs from the strong coupling with completion-at-point-functions that Completion Preview enjoys by supporting multiple backends, and even including a bespoke word prediction Python program
fancy-dabbrev
targets only dabbrev as its backend
capf-autosuggest
only works in comint-mode buffers
company
brings along its own completion framework with multiple backends and frontends, including a preview completion frontend. Unlike other packages on this list, it makes sense to use Company and Completion Preview together, when you’re using Company for its other frontends.

Conclusion

I’ve been using this mode myself since I started working on it several weeks ago, and so far I find it quite nice. I usually steer clear of various autopopups that distract me from what I’m actually trying to write down, but I find that this preview interface strikes a great balance between being suggestive and being pushy. So I’m pretty happy with this addition to Emacs. There’s still some time before Emacs 30 will be cut, so if you’re reading this and you find something that warrants some improvement, let me know! (Or just do M-x report-emacs-bug.)