Cape: Corfu's Best Friend
I’ve been using corfu for in-buffer completion for a while now and I’m quite happy with it. There was one thing that kept bugging me, though - the completion popup would only ever show whatever the current major mode’s completion function had to offer. No plain word completion, no file name completion - if the major mode (or your LSP server) didn’t know about something, neither did the popup.
Turns out I was simply missing one piece of the puzzle, namely cape.
The Problem
A bit of background first. Modern Emacs completion is built around
completion-at-point-functions (capfs for short) - each major mode registers a
function that knows how to complete things in its buffers, and UIs like
corfu (or the built-in completion-at-point) simply display the results.
The catch is that most major modes register only their own capf. That’s why
the trusty old dabbrev-style completion (complete any word that appears in
your buffers) is nowhere to be found in the popup, even though most of us have
been relying on it for decades via M-/.
Enter Cape
Cape (Completion At Point Extensions) is another package by the prolific Daniel Mendler and it does exactly what its name suggests - it provides a bunch of extra capfs that you can mix into any buffer:
cape-dabbrev- complete words from the current and other buffers (the star of the show, in my opinion)cape-file- complete file pathscape-keyword- complete programming language keywordscape-elisp-symbol- complete Elisp symbols anywhere (e.g. in comments or docstrings)cape-dict- complete words from a dictionary filecape-line- complete entire lines from the buffercape-emoji- complete emoji, if that’s your thing (Emacs 29+)
My setup is deliberately minimal - buffer words and file names everywhere, on top of whatever the major mode provides:
(use-package cape
:ensure t
:init
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file))
That’s it. Now when I type a few characters, corfu shows the major mode’s
candidates and falls back to buffer words when there’s nothing smarter to
offer. Typing a path like ~/proj offers to complete it as a file name. It’s
one of those small quality of life improvements you stop noticing after a day,
because it feels like it has always been there.
Note: Every cape capf is also an interactive command, so you can invoke
them on demand - e.g. M-x cape-file to complete a file name regardless of
your setup. Cape’s README suggests putting them on the C-c p prefix, but for
me (and my fellow Projectile users)
that’s a no-go, as C-c p is Projectile territory. Pick your own prefix if
you want quick access to them.
Tips and Tricks
A few practical things worth knowing:
- Type
file:anywhere andcape-filekicks in for the text right after it, even in places where a path wouldn’t normally be recognized (say, in the middle of a comment). The prefix is customizable viacape-file-prefix. cape-dabbrevgathers its candidates from buffers with the same major mode by default, so your Elisp buffers won’t pollute the completions of your Clojure buffers. If you want different behavior, that’scape-dabbrev-buffer-function.- If you set
tab-always-indenttocomplete(as I do),TABbecomes the perfect trigger for the whole completion stack - it indents the line if needed and otherwise summonscorfuwith all your capfs, cape ones included:
;; smart tab behavior - indent or complete
(setq tab-always-indent 'complete)
cape-dictreads from/usr/share/dict/wordsby default, which is fine on macOS and most Linux distros, but you can pointcape-dict-fileto any word list - e.g. one for your native language.
A Couple of Power Tricks
Cape also ships a few combinators that are worth knowing about:
cape-capf-supermerges several capfs into one, so their candidates show up in a single unified popup. (e.g. you can blendeglot’s completion withcape-dabbrev)cape-company-to-capfconvertscompany-modebackends into capfs, which is super handy if there’s a company backend for your favorite tool but no capf in sight.
I haven’t needed either of them yet, but it’s good to know they are there.
Closing Thoughts
If you’re already on the vertico/consult/corfu bandwagon, cape is
pretty much a mandatory addition - corfu deliberately stays small and
focused, and cape is the intended way to extend what it can complete.1
Funny enough, I went years without it simply because I never stopped to ask
why file names wouldn’t complete in the popup. Sometimes you don’t know
something is missing until you try it.
What’s in your completion-at-point-functions? Any cape goodies I’ve
overlooked? Let me know in the comments!
That’s all I have for you today. Keep completing (at point)!
-
Also, let’s be honest - corfu wearing a cape is a pretty great mental image. ↩