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.