Switching entirely to Corfu in my GNU Emacs configuration

Somewhat recently I read this article on a modular completion framework for GNU Emacs ( via ) and expressed a thought on the Fediverse :

If lsp-mode in GNU Emacs supported corfu in addition to (or instead of) company-mode, I would probably switch from company to corfu just to have a unified completion environment. But I don't think as-you-type completion with LSP is supported in anything except company-mode, and I'm not moving to eglot (I looked once and rejected it).

Oh well, maybe someday I can unify things a bit more. (Or I will get annoyed with as-you-type completion.)

( I already use corfu for general completion , with company-mode only used in lsp-mode buffers.)

I was wrong; corfu does support as you type completion . Corfu calls this auto completion and doesn't enable it by default, but you can change that if you want, either locally to specific buffers or generally. Today I gave things a try and after the dust has settled, I've switched entirely to corfu , even in lsp-mode, with some additional changes.

As I discovered when I first explored as you type autocompletion in GNU Emacs , I like seeing the completion information but what I don't want is to have my keystrokes stolen just because some autocomplete information showed up. Corfu's default keybindings steal common keys that I might want to type while programming, such as RETURN, TAB, and cursor up and down; this is perfectly reasonable in corfu's normal environment where you have to manually trigger completion, but isn't what I want with autocomplete on. Corfu makes life slightly more difficult for me by using '<remap>' in its corfu-map local keybindings, so I have to unset them by hand :

  (keymap-unset corfu-map "RET" 'remove)
  (keymap-unset corfu-map "TAB" 'remove)
  (keymap-unset corfu-map "<up>" 'remove)
  (keymap-unset corfu-map "<down>" 'remove)
  (keymap-unset corfu-map "<remap> <next-line>" 'remove)
  (keymap-unset corfu-map "<remap> <previous-line>" 'remove)

Then, as with company-mode, I bind C-RET, C-TAB, C-<up>, and C-<down> to do these actions, which are corfu-insert, corfu-complete, corfu-previous, and corfu-next respectively. I also made my wheel mouse scroll up and down through the selections.

While I was fiddling around in corfu, I made the fortuitous discovery of completion-preview-mode . The visually obvious thing completion-preview mode does for me is that it shows the current completion prefix ahead of what I'm typing (if there is one). The non-obvious thing it does is that I can immediately hit TAB to complete to that prefix (the same way a single tab works in shell filename completion). This completion sort of works even even in lsp-mode, where corfu's normal completion expansion gives up entirely . Initially I thought that completion-preview could successfully complete prefixes in lsp-mode, but I was being fooled by how often the prefix was the first completion.

With completion-preview showing me basic information about what I can immediately complete, I decided to slow down how soon corfu's auto-completion popup appears. If I want to trigger it early, I can always hit M-TAB. My current value for ' corfu-auto-delay ' is 0.5 (seconds).

The remaining fix needed is that lsp-mode is extremely attached to company-mode. If you have company-mode installed lsp-mode will activate it in buffers, and if you don't have it installed, lsp-mode will complain. This behavior can be turned off by setting the somewhat oddly named lsp-completion-provider variable to ' :none ' from its default value of ' :capf '. Despite capf being a standard GNU Emacs jargon, lsp-mode really means 'company' here. No doubt there's some history involved.

(It's not clear to me if corfu makes some use of company-mode if it's available.)

Although I had to shave a certain amount of yaks to get here, I feel glad to have switched to only using Corfu. Company-mode is a perfectly fine autocompletion environment and I was happy with it for years, but I didn't use it everywhere and once I added corfu I was juggling two sets of reflexes, one for corfu M-TAB initiated completion in places like Emacs Lisp and the other for company autocompletion in LSP buffers. Every so often I'd hit M-TAB in an LSP buffer out of reflex, and sometimes that got confusing. Now I only have one set of reflexes.

One tricky bit of using autocompletion in corfu is that you can't change the value of ' corfu-auto ' on the fly. What matters is its value when corfu-mode starts. Fortunately we can use brute force; if we assume that we're only going to change corfu-auto when corfu-mode is on, we can write functions like:

 (defun corfu-enable-auto ()
   "Enable corfu auto-completion in this buffer."
   (interactive)
   (setq-local corfu-auto t)
   (corfu-mode -1)
   (corfu-mode 1))
 (defun corfu-disable-auto ()
   "Disable corfu auto-completion in this buffer."
   (interactive)
   (setq-local corfu-auto nil)
   (corfu-mode -1)
   (corfu-mode 1))

There is probably a better way to do this, and possibly I should turn the mode off before changing the corfu-auto value. Also, don't forget to make these interactive functions, as I did in the first version I wrote.

(Well, it's GNU Emacs, we can always read the source , also , and then duplicate what the source is doing when it goes into or out of corfu-mode. But those are internal details that might change, while having corfu-mode redo its setup should always work.)

PS: Someday I would like to make corfu complete prefixes properly in lsp-mode (which would probably also fix completion-preview, and even company-mode, since they all have the same problem), but that's another and bigger problem. For today I'm happy to have switched.