Completion Preview in Emacs
Some details about a new Emacs text completion feature
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):
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:
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:
- the last command is one of
completion-preview-commands
, and - 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
.)