My understanding of various sorts of completion in GNU Emacs

October 9, 2023

One of the things that happens when you (I) only touch your Emacs configuration every few years is that you forget exactly how things work, especially if you didn't fully understand them when you were copying directions from elsewhere when you set things up. Due to recent events I've been doing a lot of GNU Emacs stuff, which has involved both recovering old understanding and learning new things about completions. Before I forget it all again, I'm writing it down for my future use.

GNU Emacs broadly has two built-in forms of completion. The one people use routinely is minibuffer completion. Because it's so common, there are a variety of built-in and third party things to change and improve it, such as fido-vertical-mode and vertico to always show some of the completion targets and marginalia to add more information about them. Orderless technically affects more than minibuffer completion, but in practice it can be hard to use it outside of the minibuffer.

The second form is on-demand completion in buffers. The dominant form of this is completion at point ('point' is the Emacs term for where the cursor is), normally invoked through M-TAB, but standard Emacs also has, for example, dabbrev-expand (bound to M-/), which tries to complete things through another mechanism. The default completion at point behavior has some aspects that are like minibuffer completion, but it's more minimal. The corfu package augments M-TAB completion at point to always show (some of) the completion targets.

(Because dabbrev-expand is not doing its completion as 'completion at point' completion, corfu's UI doesn't appear for it. The whole thing is part of Abbrevs, also, and also completions in general. Defined abbrevs can be expanded as you type, instead of on demand.)

Emacs has an entire ecosystem for generating the completions for on-demand buffer completion at point. The data for this can come from all sorts of sources, depending on what's in the buffer. In particular, if you're editing something with a LSP server active (through eg lsp-mode or eglot (also), which is part of Emacs 29.1), then information from the language server will be used to provide completion at point data, so you can use M-TAB to complete things on demand, possibly with corfu providing a popup list and so on.

As far as I know, nothing in stock GNU Emacs provides general IDE like, 'as you type' autocompletion (and this is not normally provided by LSP modes). To get this, you need to use a package like company(-mode) (also). Company can draw from multiple completion sources, but in modern use it normally primarily draws from the same 'completion at point' information than M-TAB uses, which means that it draws completion information from a LSP mode if you have that active. You can use company autocompletion independently from a LSP mode; for example, you can enable it in Emacs Lisp buffers (where there's no LSP for elisp). Since company is doing its actual completion outside of the completion at point system, it has its own popup UI of completion targets and information about them, which is independent of standard completion at point enhancements like corfu. Company has a command to explicitly start a company completion, which is potentially useful to bind to eg M-/ so that you can restart a completion that you exited without having to delete and retype some characters. Or you can type M-TAB to use Emacs' regular completion at point UI for this (including, eg, corfu's UI), unless you rebound M-TAB to company's completion (which you might, to avoid confusion).

(You can use company without as you type autocompletion and instead bind company-complete to M-TAB and M-/, if you globally set 'company-begin-commands' to 'nil' (it also works as a buffer local variable). I believe that this will still use the company UI, not the standard completion at point or corfu UI. To use company completion, the buffer must be in company-mode, but you can disable as you type autocomplete with a buffer local value for 'company-begin-commands'.)

This gives us (me) three different completion environments with three different sets of completion customizations. There's minibuffer completions (vertico, marginalia, and orderless), Emacs native completion at point (corfu), and finally company as you type (auto)completion (well, that's the normal setup for company). Depending on your usage, you may normally use only one of the last two; for example, until recently I made basically no use of M-TAB completion at point.

To summarize the common situation in LSP modes for me, company-mode is providing as you type autocompletion stacked on top of Emacs' general completion at point infrastructure, with the LSP mode (and LSP server) providing the primary completion data. In non LSP modes, like Emacs Lisp or C, I could enable company-mode and it would most likely be drawing completion data from the language mode. Even if I don't enable company-mode, I can still access the same completions through standard M-TAB completion at point (either with or without a LSP).

My personal experience has been that programming languages with large, flat namespaces of identifiers with short names don't necessarily go very well with as you type autocompletion. There are generally so many options so you get prompted all of the time (which is at best distracting and at worst routinely obscures code that you want to see), and many of them aren't very useful (or are actively wrong). Possibly this would be improved by increasing the number of characters before company starts offering autocomplete options, but so far it's been simpler to only use company in modes where I already use LSP.

(For instance, with shell scripts and LSP-mode, company will sometimes offer you autocompletion that includes every program in your $PATH. This is technically correct but often not particularly useful. Unfortunately disabling company within specific language lsp-modes seems rather difficult and I haven't been successful so far.)

PS: My understanding is that the Emacs situation used to be much less unified, especially for company, which in old days required its own specialized connections to programming language modes (eg company-go) and LSP modes (eg company-lsp). Modern Emacs has unified all of this around the completion at point infrastructure (often using the acronym 'capf', which is short for 'completion at point functions', which are backend functions that provide completion information; see eg). I'm not sure what Emacs version is 'modern' here, but probably you want to be using a recent Emacs anyway.

PPS: This can make it perfectly sensible to have a whole collection of third party packages (especially small, focused ones) that affect the different sorts of completion. If you count company, I'm currently up to five: vertico, marginalia, orderless, corfu, and company itself. There are undoubtedly more that I could add; suggestions are welcome.

Written on 09 October 2023.
« The tools I use to read email affect how I read it (more than I thought)
Keyboard keys and characters »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Oct 9 21:53:16 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.