My last post was a tour of Emacs
31, which has since moved into pretest. Sean Whitton announced
31.0.91,
the second pretest, on July 23rd, and that's what I'm writing this on.
One of the sections in that post was a short list of new completion
knobs, three lines of config I mentioned and moved on from. Those three
lines turned into a month-long experiment, so they get their own post.
I know how that title reads, so let me put it in context before you
close the tab. It comes from someone who ran company, moved to Corfu,
and then left Corfu behind for icomplete, which has been my weapon of
choice for years now, for both the minibuffer and in-buffer completion.
And I didn't just use icomplete. I sent patches to it.
bug#75784,
which brought vertical in-buffer behavior and prefix indicators to
icomplete, is mine, and I wrote about the road to it
here and
here. So when I say I turned icomplete
off for a month, understand that I was arguing with my own past self,
and that the title isn't me saying the last few years were wasted. It's
me saying the floor moved.
What made me try was Emacs 31 itself. The *Completions* buffer
improved nicely while I wasn't looking, and I wanted to know whether the
UI I'd been layering on top of it was still earning its place.
While I was drafting this, Protesilaos published a video on the
same territory. I haven't
watched it all the way through yet, and I'm publishing anyway, because
what follows is my month with this setup rather than a summary of his.
His videos have shaped a lot of how I think about Emacs, so go watch it
too. I'll probably end up fixing some of my own setup after I do.
Everything below runs on stock Emacs 31. Save the snippets into a
test.el and launch with:
The full playground file (search for test.el) is at the bottom of
the post if you'd rather skip ahead.
Two completions, one buffer
Emacs completes in two places, and it's worth keeping them apart in
your head:
✔️ Minibuffer completion. M-x, C-x C-f, C-x b, every
completing-read prompt in every package you have. You type in the
minibuffer, candidates come from a completion table.
✔️ In-buffer completion. completion-at-point, the thing bound to
M-TAB, and to TAB if you set tab-always-indent to complete.
You type in a normal buffer and the candidates come from
completion-at-point-functions: elisp symbols, file names, whatever
your LSP server offers through Eglot.
Corfu and company handle the second case. Vertico, ivy and friends
handle the first. You probably run one of each. Emacs covers both with
the same *Completions* buffer, which surprised me, and it means one
block of config does the whole job.
The base config
This is what I run:
Two of those carry the post, and both are new in Emacs 31:
completion-eager-display decides whether *Completions* shows up on
its own. completion-eager-update decides whether it refreshes as you
type. Both default to 'auto in Emacs 31, which means "only if the
completion table asks for it," and almost nothing asks. Set both to t
and you stop summoning the buffer with TAB. It's already open.
Run emacs -Q, hit M-x, type wind, and you get nothing but your own
text in the minibuffer. Load the config above, do the same, and 34
candidates appear the moment you stop typing. Keep typing move-del and
the list narrows to 5 without you touching a key that isn't a letter.
completions-format 'one-column and completions-max-height 10 are
what turn that list into a vertical one. The default is 'horizontal,
which packs candidates across the window like ls output. One column,
capped at ten lines, reads as a dropdown.
completions-sort 'historical sorts alphabetically and then floats
whatever you picked recently to the top. It's the smallest of these
settings and the one I'd miss first.
That exact moment, below: four letters typed into M-x, no TAB
pressed, 34 candidates already sitting there in one column.

completion-auto-select t moves point into the *Completions* window
when TAB pops it up, so you can navigate the list without a second
key to get there. completion-show-help nil drops the two-line
"Click or type RET on a completion to select it" banner from the top of
the buffer, which I've read enough times for one lifetime.
enable-recursive-minibuffers plus minibuffer-depth-indicate-mode
belong together: the first lets you start a minibuffer command from
inside another one, the second puts a [2] in the prompt so you know
how deep you are. minibuffer-electric-default-mode hides the
(default foo) part of a prompt as soon as you type, and brings it
back if you erase.
The minibuffer-prompt-properties line keeps point out of the
read-only prompt text, and cursor-intangible-mode on
minibuffer-setup-hook is what enforces it. truncate-lines in the
minibuffer keeps long candidates on one line instead of ballooning the
minibuffer to N rows.
One of those settings needs a keybinding to go with it. With
minibuffer-visible-completions set to 'up-down, the arrow keys move
point inside *Completions* while you're still typing in the
minibuffer, and RET takes the highlighted candidate instead of your
literal input. Left and right still move in the minibuffer, which is
why I use 'up-down rather than t.
By the way, I tend to avoid arrow keys, M-<some-arrow-direction>
doesn't feel natural in a completion list for me. That's why I set
C-n and C-p like this:
Below, the same list after a couple of C-n. The highlight has walked
down to windmove-delete-default-keybindings while the cursor is still
in the minibuffer, where I'm still typing.

The same buffer, in your code
This setting is the one that made the experiment worth running:
TAB in a code buffer now indents the line if it needs indenting, then
completes if it doesn't. Open *scratch*, type (window-lay, hit
TAB. Emacs fills in the common prefix, window-layout-. Hit TAB
again and *Completions* opens with the five commands that match, with
point already inside it thanks to completion-auto-select. C-n to
the one you want, RET, done.
The same keys and config as the minibuffer half, in a code buffer now.
After the second TAB, below: the buffer holds (window-layout-, the
five commands sit underneath, and the first is already selected.

*Completions* is a real window, so it takes room in your frame instead
of floating over it. After a month I've stopped noticing, and I got
something back for it: the candidate list is an ordinary buffer, so I
can search it, scroll it, and yank out of it.
Where LSP completion goes wrong
One place did annoy me enough to write code.
My completion styles are:
flex is the fuzzy one: it matches an in-order subset of what you
typed, so faL finds faLinkedin and faList without you spelling
out the middle. Exactly what I want from an LSP buffer, where I
half-remember a name and want to see the family.
But flex does two jobs, and only one of them is filtering. Its
try-completion also merges the surviving candidates and inserts the
result. With tab-always-indent set to complete, that merge happens
on TAB, before you ever see the list.
The case that cost me an afternoon comes from this blog's own source.
components/footer.tsx imports a handful of FontAwesome icons. I wanted
faLinkedin, so I typed faL and pressed TAB once:
And no list is shown, faEnvelopeSquare matches faL under flex
because the l sits inside "Envelope", and that's where the merge
landed. TAB typed sixteen characters I didn't ask for, hid the
candidates I did want, and left me deleting.
The frame below shows the whole thing. The echo area tells me Complete, but not unique, which is Emacs admitting it guessed and still showing
me none of the alternatives:

That's the gap I kept hitting between the default UI and what icomplete
had trained me to expect. icomplete shows me candidates on TAB. Here
it types at me.
Eglot ran into this too and works around it with its own
eglot--dumb-flex style, which skips the merge. That fixes the typing
and loses the scoring, so candidates come back in whatever order the
server sent them.
I wanted both: flex's filtering and relevance sorting, and no
insertion until I pick something. So I wrapped it.
flex-noinsert
A completion style in Emacs is four things in a list: a name, a
try-completion function, an all-completions function, and a doc
string. I reuse completion-flex-all-completions unchanged, so
filtering and scoring are stock flex, and I swap in a
try-completion that refuses to merge. The completion--adjust-metadata
property is what keeps *Completions* sorted by flex score rather than
alphabetically.
Then aim it at Eglot only:
Minibuffer completion keeps stock flex, where the merge behavior is
fine because you can see what it did. Eglot's eglot-capf category
gets the no-insert variant.
Now in the same situation as before, trying to complete faL with
TAB. The buffer keeps saying faL, and *Completions* opens
instead.
It opens with 2664 candidates, which sounds absurd until you look at
what's on top: faL itself, then faLeaf, faLess, faLine. Flex
matches loosely and scores tightly, so I get a huge tail under a head
that's already correct. The trade only works if nothing gets typed into
my buffer first.
One more letter sharpens it. faLi cuts the list to 859, and the
visible window holds the family I was after:
Each one appears twice, once for the package and once for the deep
import path, and Eglot shows me which module it would pull from. C-n
and RET, or keep typing. Unique completions still finish on the first
TAB, because that's the one case where the merge can't be wrong.
Here is faL with the fix in place. Emacs wrote nothing into the
buffer, and the long tail sits under a head that starts where I was
aiming:

And faLi, where the whole visible window is the answer:

Living with it
A month in and:
✔️ The minibuffer half is a straight swap. Take the drawing away
and the functionality is exactly what I already had with
icomplete-vertical-mode. M-x, C-x b, C-x C-f: same
filtering, same narrowing as I type, same keys under the same
fingers. My hands and eyes resynced in a day or two, which is the
part nobody can argue you into. I'm not tolerating this until I go
back to icomplete. It's an alternative with a different UI and the
same behavior, and I'm running it. The list being a real buffer is a
bonus on top.
✔️ The in-buffer half surprised me. I assumed I'd miss a floating
popup within a week. It took a couple of days to stop thinking about
it, and splits are what sold me. Complete inside the left window of
a C-x 3 and *Completions* opens in that window, under the code
I'm completing. The right split never moves. The list arrives where
I'm already looking, and the rest of the frame stays where I left
it.
A two-window frame below, avatar.tsx on the left and footer.tsx on
the right. I completed on the left, and that's the only half that
changed:

✔️ flex-noinsert is doing real work. Without it I'd have given up
on the default and gone back inside a week.
✔️ The scoring is where the defaults still trail. A dedicated fuzzy
matcher does better when I type three letters from the middle of a
name. flex gets close without getting there.
IMPORTANT: I haven't deleted icomplete from my config, and I'm not
telling you to delete yours. And if you enjoy Vertico, Corfu, company
or anything else in that family, please keep using it. Those are
excellent projects, with real care put into how they look and feel,
and which one you run is a personal preference, not a correctness
contest. Nothing here is an argument against them.
I wanted to show how good, and how complete, the default completion has
become. That's all. It's really cool now, and it's sitting in the editor
you already have.
What changed for me is that I now know what the default does. When I go
back to icomplete I'll be choosing it rather than inheriting it.
If you run Emacs Solo, you
don't have to take my word for any of it, because the switch is already
wired up:
It's a defcustom, so M-x customize-variable RET emacs-solo-enable-icomplete works too. Flip it, restart, and live with
the other side for a week. Emacs Solo adjusts the related settings for
you: with icomplete off it sets completion-eager-display to t rather
than 'auto, which is the setting that makes *Completions* show up on
its own.
Give each side long enough that you stop noticing it. After one day
you'll only know which one feels unfamiliar, and that's a different
question from which one you want.
The full test.el
Save this, run emacs -Q --load test.el, and poke at it. Emacs 31 or
newer, since completion-eager-update, completion-eager-display and
minibuffer-visible-completions-up-down-map are all new here.
Things to try once it's loaded:
✔️ M-x and type a few letters, then C-n and C-p through the list.
✔️ C-x C-f and walk a directory tree with the same keys.
✔️ In *scratch*, type (window-lay and hit TAB twice.
✔️ C-x C-f into a project with an LSP server, M-x eglot, and type a
partial method name followed by TAB to watch flex-noinsert behave.
And C-x C-f doing the same job on a directory, below:

All of it lives in Emacs Solo
too, my no-external-packages config, if you'd rather read it in
context.
Now, go watch Prot's take after this one.
Happy hacking!