-
-
Notifications
You must be signed in to change notification settings - Fork 43
Home
See this video for a nice overview of corfu and a bit about cape, as well as a comparison with other tools.
Eglot is an Emacs packages which provide completions for a wide array of languages and types. It does this by consulting external LSP server processes. LSP servers look at the text in the region and send a list of suggested completions.
The Lsp server assumes that the candidates are retrieved on every change to the
buffer. Therefore Eglot expects that the CAPF is called on every change to the
string to be completed, like is done by Company. As a result, when typing a word
slowly with auto-completion, the list of completion candidates is not be updated
and only post-filtered by the completion style. This behavior can be changed
with the cape-capf-buster
wrapper from Cape, which ensures that the candidates
are always obtained again from the server.
(advice-add 'eglot-completion-at-point :around #'cape-capf-buster)
On the other hand, retrieving the candidates on every key press can also be seen as a disadvantage, since it decreases performance. If the candidates are instead kept for longer we can use post-filtering with a completion style like Orderless. This matters in particular if your Lsp-server returns many candidates (or even a complete set of candidates) right away on the first invocation of completion. See the next section.
Eglot adjusts the completion styles. It sets the style for the eglot
category to
flex
in completion-category-defaults
. This is a reasonable way to get all of the
Lsp server’s completions. If you want to use Corfu in combination with Orderless
or another completion style you can override this setting.
;; Option 1: Specify explicitly to use Orderless for Eglot
(setq completion-category-overrides '((eglot (styles orderless))))
;; Option 2: Undo the Eglot modification of completion-category-defaults
(with-eval-after-load 'eglot
(setq completion-category-defaults nil))
It only works if Eglot returns sufficiently many candidates in the first place. Then the completion style like Orderless is actually useful to post-filter the candidates. Note that it may be necessary to increase the number of returned candidates as described in the next section.
This important point is worth repeating: Configuring Orderless or any other “flexible” completion style within Emacs will not affect what completions your LSP server provides. These styles are nevertheless useful for quickly filtering the list of returned candidates, as long as you keep this limitation in mind.
If you use the orderless completion style and your lsp server doesn’t give all
completions from Data.Vector.
or java.util.list.
that exist, you may wish to
request more completions from the server to filter them with orderless.
WARNING: Your lsp server likely limits completions due to performance concerns and raising the limit could cause performance issues. Keep this in mind for at least a few weeks after changing this setting.
You’ll have to consult your lsp server for it’s configuration details, but elisp
alists mapping to json can be added to eglot-workspace-configuration
,. Here is
an example for haskell-language-server requesting 200 completions:
(setq-default eglot-workspace-configuration
'((haskell
(maxCompletions . 200))))
Lsp-mode is an Emacs packages which provide completions for a wide array of languages and types. It does this by consulting external LSP server processes. LSP servers look at the text in the region and send a list of suggested completions.
The main thing you need to tune is the completion style. LSP servers do not know
about or respect the “completion style” in emacs (they have their own “styles”).
For example, with text foo
they may provide completions foobar, frodo, tofoo,
atafondillo
, etc. So here the local emacs style you configure matters. If you
have setup a “prefix-only” emacs style like basic
, all those completions the LSP
server provided will be filtered; only foobar
in the list above will be shown.
If you want to use a builtin completion style it is recommended to either use
substring
or flex
. The substring
style is slightly limited in that it matches
substrings only, so you will get the candidates foobar
and tofoo
. The flex
style
matches all of the candidates.
(use-package lsp-mode
:custom
(lsp-completion-provider :none) ;; we use Corfu!
:init
(defun my/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(flex))) ;; Configure flex
:hook
(lsp-completion-mode . my/lsp-mode-setup-completion))
An example of a much more flexible and powerful Emacs completion style is Orderless, which can find text anywhere in the string. You can even use spaces in the buffer to add additional regex or normal searches. And the beauty is with Corfu and Orderless, this filtering and selection happens instantly in Emacs, without having to re-contact the LSP server! This can be much faster for slow servers.
(use-package orderless
:init
;; Tune the global completion style settings to your liking!
;; This affects the minibuffer and non-lsp completion at point.
(setq completion-styles '(orderless partial-completion basic)
completion-category-defaults nil
completion-category-overrides nil))
(use-package lsp-mode
:custom
(lsp-completion-provider :none) ;; we use Corfu!
:init
(defun my/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless))) ;; Configure orderless
:hook
(lsp-completion-mode . my/lsp-mode-setup-completion))
To make sure every single completion candidate comes through, we use Orderless’
“dispatcher” capability to setup the orderless-flex
style for its 1st search
term (the string you are completing). You can skip this if you prefer to filter
a bit what the LSP servers has provided when Corfu first pops up (I mean, did I
really type foo
if I wanted atafondillo
?).
;; Optional cape package.
;; See the Cape README for more tweaks!
(use-package cape)
(use-package orderless
:init
;; Tune the global completion style settings to your liking!
;; This affects the minibuffer and non-lsp completion at point.
(setq completion-styles '(orderless partial-completion basic)
completion-category-defaults nil
completion-category-overrides nil))
(use-package lsp-mode
:custom
(lsp-completion-provider :none) ;; we use Corfu!
:init
(defun my/orderless-dispatch-flex-first (_pattern index _total)
(and (eq index 0) 'orderless-flex))
(defun my/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless)))
;; Optionally configure the first word as flex filtered.
(add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local)
;; Optionally configure the cape-capf-buster.
(setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point)))
:hook
(lsp-completion-mode . my/lsp-mode-setup-completion))
The one additional feature we have used here is Cape’s cape-capf-buster
. This
also isn’t entirely necessary, but it gives some nicer behavior when you alter
the original text during completion (e.g. while completing get
, you delete back
to ge
).
See below for examples of grouping results from other completion sources using Cape.
If you use Corfu in the minibuffer below an X11 window, the Corfu childframe is displayed beneath the X11 window, rendering candidates invisible. In order to keep all candidates visible in this scenario use the following code:
(advice-add #'corfu--make-frame :around
(defun +corfu--make-frame-a (oldfun &rest args)
(cl-letf (((symbol-function #'frame-parent)
(lambda (frame)
(or (frame-parameter frame 'parent-frame)
exwm-workspace--current))))
(apply oldfun args))
(when exwm--connection
(set-frame-parameter corfu--frame 'parent-frame nil))))
(advice-add #'corfu--popup-redirect-focus :override
(defun +corfu--popup-redirect-focus-a ()
(redirect-frame-focus corfu--frame
(or (frame-parent corfu--frame)
exwm-workspace--current))))
If you additionally use corfu-doc
, then the same situation above also applies to
child frames created by corfu-doc
. To fix analogously, use the following code.
Note that corfu-doc
has been deprecated in favor of the corfu-popupinfo
extension, which is part of Corfu itself.
(advice-add #'corfu-doc--make-frame :around
(defun +corfu-doc--make-frame-a (oldfun &rest args)
(cl-letf (((symbol-function #'frame-parent)
(lambda (frame)
(or (frame-parameter frame 'parent-frame)
exwm-workspace--current))))
(apply oldfun args))
(when exwm--connection
(set-frame-parameter corfu-doc--frame 'parent-frame nil))))
(advice-add #'corfu-doc--redirect-focus :override
(defun +corfu-doc--redirect-focus ()
(redirect-frame-focus corfu-doc--frame
(or (frame-parent corfu-doc--frame)
exwm-workspace--current))))
If you want to transfer the Corfu popup completion to the minibuffer during the
process you can use the function corfu-move-to-minibuffer
which is defined in
terms of consult-completion-in-region
. Maybe this is sometimes useful?
(defun corfu-move-to-minibuffer ()
(interactive)
(let ((completion-extra-properties corfu--extra)
completion-cycle-threshold completion-cycling)
(apply #'consult-completion-in-region completion-in-region--data)))
(define-key corfu-map "\M-m" #'corfu-move-to-minibuffer)
Corfu works with traditional Emacs completion backends called completion at
point functions (CAPF’s). Cape provides some additional simple backends as
CAPF’s, which can be used directly. It also allows you to tweak them, and even
composite them. This examples shows how to set up emacs-lisp mode with a
“super-capf” composed of two different sources: the normal
elisp-completion-at-point
completions and dabbrev candidates from cape-dabbrev
.
This is great while hacking on emacs-lisp files, when the symbols aren’t yet
known to Emacs, for example. Corfu will pop-up with both symbols known to emacs,
and (as a backup) additional matching symbols it finds in the file. I put a
minimum length of 5 characters to avoid small word pollution.
It also adds cape-file
to the list of completion at point functions, to try as a
“back-up”, e.g. in strings and comments. It also shows how to use another cool
cape feature to add a custom predicate. The predicate is a function which gets
the candidate, and return t if it should be kept. This one drops keywords from
the emacs-lisp candidate list, unless the completion text starts with a `:’.
There are lots of ways to composite together and tweak CAPFs, to supplement your
mode’s completions.
(defun my/ignore-elisp-keywords (cand)
(or (not (keywordp cand))
(eq (char-after (car completion-in-region--data)) ?:)))
(defun my/setup-elisp ()
(setq-local completion-at-point-functions
`(,(cape-super-capf
(cape-capf-predicate
#'elisp-completion-at-point
#'my/ignore-elisp-keywords)
#'cape-dabbrev)
cape-file)
cape-dabbrev-min-length 5))
(add-hook 'emacs-lisp-mode-hook #'my/setup-elisp)
Here’s an example of cape working to combine emacs-lisp and dabbrev completion candidates, with corfu, kind-icons, and orderless completion style in-buffer:
When we’re in eglot-mode
we want more than the eglot-completion-at-point
function provides. In the following example, we use Cape’s cape-super-capf
function to compose our own completion at point using eglot’s
eglot-completion-at-point
, tempel’s tempel-expand
and Cape’s cape-file
.
(defun my/eglot-capf ()
(setq-local completion-at-point-functions
(list (cape-super-capf
#'eglot-completion-at-point
#'tempel-expand
#'cape-file))))
(add-hook 'eglot-managed-mode-hook #'my/eglot-capf)
If Corfu is not triggering completion with corfu-auto
it can be the case that
the command was not called with self-insert-command
or one of the other commands
registered in the list corfu-auto-commands
. You can fix this by adding the
commands you are missing to the list.
(add-to-list 'corfu-auto-commands 'some-special-insert-command)
Using together with prescient.el
prescient.el
is a sorting and filtering package. An integration package is
available for Corfu: corfu-prescient
(available on MELPA). The mode provided by
the package configures the filtering settings that Corfu uses (completion-styles
and family) buffer locally and configures Corfu’s sorting globally.
;; After installing the package (corfu-prescient-mode 1)
In TAB-and-Go style, if a candidate is selected, then further input commits that candidate unless it is the first one. In order that the first candidate is also committed on further input, add this to your configuration:
(dolist (c (list (cons "SPC" " ")
(cons "." ".")
(cons "," ",")
(cons ":" ":")
(cons ")" ")")
(cons "}" "}")
(cons "]" "]")))
(define-key corfu-map (kbd (car c)) `(lambda ()
(interactive)
(corfu-insert)
(insert ,(cdr c)))))
This list can be modified to accommodate anyone’s needs. Note that the inclusion of space assumes that the completion style is “basic”. Also note that the inclusion of “.” disables regex search within the candidate list. A simpler version only including space is this:
(define-key corfu-map (kbd "SPC") (lambda ()
(interactive)
(corfu-insert)
(insert " ")))
See also the Consult Wiki, the Embark Wiki and the Vertico Wiki!