Corfu, Kind-icon, and Corfu-doc
Table of Contents
As is noted in the
corfu-docrepository,corfu-dochas been deprecated by the built-incorfu-popupinfocorfuextension.corfu-popupinfo’s functionality is roughly identical tocorfu-doc’s, though its interface and code is naturally more idiomatic tocorfu.
Synopsis §
I will be going over my personal Emacs’ text completion (e.g. company-mode and its accessories) configuration, which includes the following packages:
- Corfu by Minad — simpler alternative to
company-mode - Kind-icon by jdtsmith (u/JDRiverRun)— add icons to
corfupopup (analog tocompany-box-icons) - Corfu-doc by Galeo — add documentation popup for
corfucandidates (analog tocompany-box-doc)
Note: I use straight.el for package management and general.el to set my keybindings. Both of these packages have integration with use-package which come in the form of the :straight and :general keywords, respectively.
Corfu §
What is corfu? How does it differ from company?
§

corfu popup window. The GIF’s framerate is low, which makes corfu appear less performant here than in actuality.Corfu is a text completion (e.g. completion-at-point, company-mode) package. In my opinion, since its release, corfu has not gotten the attention that it deserves. I prefer it to company for the following reasons:
- It is easier to configure since
corfu’s internals rely on the built-incompletion-at-point. This also means that, unlikecompany,[1]…- any built-in invocation of
completion-at-pointorcompletion-in-regionleveragescorfu, - and any
completion-style(e.g.orderless) can be used for filtering candidates.
- any built-in invocation of
Corfuhas been more performant (i.e. fewer stutters, smoother cycling of candidates) in my experience.Corfucan support anycompanybackend viacape-company-to-capf, provided by the complementarycapepackage. Thus, packages likecompany-yasnippetcan be used withcorfueasily (see the next post in my Text completion and minibuffer UI series for more details and examples.)
Basic §
The following is a basic corfu configuration with my preferred keybinds:
(use-package corfu :general (:keymaps 'corfu-map :states 'insert "C-n" #'corfu-next "C-p" #'corfu-previous "<escape>" #'corfu-quit "<return>" #'corfu-insert "M-d" #'corfu-show-documentation "M-l" #'corfu-show-location) :config (corfu-global-mode))These keybinds have C-n and C-p move through the candidates popup, <return> choose the current candidate, and <escape> close the corfu popup. Moreover, I have corfu’s documentation command (corfu-show-documentation; shows the available documentation for the current candidate, if any) bound to M-d, and corfu’s location command (corfu-show-location) to go to the location of the current candidate to M-l.
Corfu offers a few variables to configure. You can take a look at each docstring to see its function. Here are my preferences:
:custom(corfu-auto nil) ; Only use `corfu' when calling `completion-at-point' or ; `indent-for-tab-command'(corfu-auto-prefix 2)(corfu-auto-delay 0.25)
(corfu-min-width 80)(corfu-max-width corfu-min-width) ; Always have the same width(corfu-count 14)(corfu-scroll-margin 4)(corfu-cycle nil)
;; `nil' means to ignore `corfu-separator' behavior, that is, use the older;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using;; `corfu-auto' = `t' workflow (in that case, make sure you also set up;; `corfu-separator' and a keybind for `corfu-insert-separator', which my;; configuration already has pre-prepared). Necessary for manual corfu usage with;; orderless, otherwise first component is ignored, unless `corfu-separator';; is inserted.(corfu-quit-at-boundary nil)(corfu-preselect-first t) ; Preselect first candidate?
;; Other;; NOTE 2022-02-05: In my actual configuration, I have this variable set to nil;; since I use `corfu-doc', whose configuration comes later. But if you don't;; use `corfu-doc', this might be helpful to you.(corfu-echo-documentation t) ; Show documentation in echo area?Additionally, the following two variables not under corfu but related to completion-at-point will be useful to set:
;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you;; want to perform completion(tab-always-indent 'complete)(completion-cycle-threshold nil) ; Always show all candidates in popup menuWorking with the recent changes to corfu
§
On February 7, 2022, corfu introduced an important change[2], particularly the interaction between corfu and orderless. You can read more on their README, but, essentially, orderless now introduces the corfu-insert-separator command that inserts the corfu-separator character into the buffer. This character is what delimits orderless components (see this Reddit comment for a more lengthy description of this behavior.) A corfu workflow in which corfu-auto is set to t leverages this change, for without it users could not realistically use corfu with a multi-component completion-style like orderless.
I do not use this workflow[3], but if this behavior is desirable, you can set corfu-separator to your orderless separator character to properly delimit orderless components. I personally use the regular space character. You can make the following modifications to your configuration:
:general;; NOTE 2022-02-28: `general-override-mode-map' is necessary to override local;; binds to SPC in evil-mode's insert mode. May not be necessary if you don't use `evil'(:keymaps 'corfu-map :states 'insert "H-SPC" #'corfu-insert-separator ; I have a hyper key so this is an alternative keybind I use sometimes "SPC" #'corfu-insert-separator):custom(corfu-quit-at-boundary 'separator) ; a non-nil value is necessary(corfu-separator ?\s) ; Use space(corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted(corfu-preview-current 'insert) ; Preview current candidate?:config;; NOTE 2022-03-01: This allows for a more evil-esque way to have;; `corfu-insert-separator' work with space in insert mode without resorting to;; overriding keybindings with `general-override-mode-map'. See;; https://github.com/minad/corfu/issues/12#issuecomment-869037519;; Alternatively, add advice without `general.el':;; (advice-add 'corfu--setup :after 'evil-normalize-keymaps);; (advice-add 'corfu--teardown :after 'evil-normalize-keymaps)(general-add-advice '(corfu--setup corfu--teardown) :after 'evil-normalize-keymaps)(evil-make-overriding-map corfu-map)Further configuration in minibuffers and with lsp
§
Corfu’s README provides a way to be able to use corfu completion in the minibuffer:
;; Enable Corfu more generally for every minibuffer, as long as no other;; completion UI is active. If you use Mct or Vertico as your main minibuffer;; completion UI. From;; https://github.com/minad/corfu#completing-with-corfu-in-the-minibuffer(defun corfu-enable-always-in-minibuffer () "Enable Corfu in the minibuffer if Vertico/Mct are not active." (unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT (bound-and-true-p vertico--input)) (setq-local corfu-auto nil) ; Ensure auto completion is disabled (corfu-mode 1)))(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)This means that in commands like eval-expression, corfu is able to be used (via <tab>) and provide completion.

corfu in the minibuffer prompt for eval-expression.Additionally, for lsp-mode buffers, I have the following lines (this is entirely optional and preferential):
:hook (lsp-completion-mode . kb/corfu-setup-lsp) ; Use corfu for lsp completion:custom(lsp-completion-provider :none) ; Use corfu instead the default for lsp completions:config;; Setup lsp to use corfu for lsp completion(defun kb/corfu-setup-lsp () "Use orderless completion style with lsp-capf instead of the default lsp-passthrough." (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) '(orderless)))End product §
Putting it together, we end with my actual configuration:
(use-package corfu :hook (lsp-completion-mode . kb/corfu-setup-lsp) ; Use corfu for lsp completion :general (:keymaps 'corfu-map :states 'insert "C-n" #'corfu-next "C-p" #'corfu-previous "<escape>" #'corfu-quit "<return>" #'corfu-insert "H-SPC" #'corfu-insert-separator ;; "SPC" #'corfu-insert-separator ; Use when `corfu-quit-at-boundary' is non-nil "M-d" #'corfu-show-documentation "C-g" #'corfu-quit "M-l" #'corfu-show-location) :custom ;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you ;; want to perform completion (tab-always-indent 'complete) (completion-cycle-threshold nil) ; Always show candidates in menu
(corfu-auto nil) (corfu-auto-prefix 2) (corfu-auto-delay 0.25)
(corfu-min-width 80) (corfu-max-width corfu-min-width) ; Always have the same width (corfu-count 14) (corfu-scroll-margin 4) (corfu-cycle nil)
;; `nil' means to ignore `corfu-separator' behavior, that is, use the older ;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using ;; `corfu-auto' = `t' workflow (in that case, make sure you also set up ;; `corfu-separator' and a keybind for `corfu-insert-separator', which my ;; configuration already has pre-prepared). Necessary for manual corfu usage with ;; orderless, otherwise first component is ignored, unless `corfu-separator' ;; is inserted. (corfu-quit-at-boundary nil) (corfu-separator ?\s) ; Use space (corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted (corfu-preview-current 'insert) ; Preview first candidate. Insert on input if only one (corfu-preselect-first t) ; Preselect first candidate?
;; Other (corfu-echo-documentation nil) ; Already use corfu-doc (lsp-completion-provider :none) ; Use corfu instead for lsp completions :init (corfu-global-mode) :config ;; NOTE 2022-03-01: This allows for a more evil-esque way to have ;; `corfu-insert-separator' work with space in insert mode without resorting to ;; overriding keybindings with `general-override-mode-map'. See ;; https://github.com/minad/corfu/issues/12#issuecomment-869037519 ;; Alternatively, add advice without `general.el': ;; (advice-add 'corfu--setup :after 'evil-normalize-keymaps) ;; (advice-add 'corfu--teardown :after 'evil-normalize-keymaps) (general-add-advice '(corfu--setup corfu--teardown) :after 'evil-normalize-keymaps) (evil-make-overriding-map corfu-map)
;; Enable Corfu more generally for every minibuffer, as long as no other ;; completion UI is active. If you use Mct or Vertico as your main minibuffer ;; completion UI. From ;; https://github.com/minad/corfu#completing-with-corfu-in-the-minibuffer (defun corfu-enable-always-in-minibuffer () "Enable Corfu in the minibuffer if Vertico/Mct are not active." (unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT (bound-and-true-p vertico--input)) (setq-local corfu-auto nil) ; Ensure auto completion is disabled (corfu-mode 1))) (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)
;; Setup lsp to use corfu for lsp completion (defun kb/corfu-setup-lsp () "Use orderless completion style with lsp-capf instead of thedefault lsp-passthrough." (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) '(orderless))))Kind-icon §
Kind-icon is essentially company-box-icons for corfu. It adds icons to the left margin of the corfu popup that represent the ‘function’ (e.g. variable, method, file) of that candidate.

corfu-doc in java-mode with completion candidates provided by lsp-mode.The following is my configuration:
(use-package kind-icon :after corfu :custom (kind-icon-use-icons t) (kind-icon-default-face 'corfu-default) ; Have background color be the same as `corfu' face background (kind-icon-blend-background nil) ; Use midpoint color between foreground and background colors ("blended")? (kind-icon-blend-frac 0.08)
;; NOTE 2022-02-05: `kind-icon' depends `svg-lib' which creates a cache ;; directory that defaults to the `user-emacs-directory'. Here, I change that ;; directory to a location appropriate to `no-littering' conventions, a ;; package which moves directories of other packages to sane locations. (svg-lib-icons-dir (no-littering-expand-var-file-name "svg-lib/cache/")) ; Change cache dir :config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter) ; Enable `kind-icon'
;; Add hook to reset cache so the icon colors match my theme ;; NOTE 2022-02-05: This is a hook which resets the cache whenever I switch ;; the theme using my custom defined command for switching themes. If I don't ;; do this, then the backgound color will remain the same, meaning it will not ;; match the background color corresponding to the current theme. Important ;; since I have a light theme and dark theme I switch between. This has no ;; function unless you use something similar (add-hook 'kb/themes-hooks #'(lambda () (interactive) (kind-icon-reset-cache))))Corfu-doc §
Corfu-doc is basically company-quickhelp for corfu. It shows the documentation of the selected candidate in an adjacent popup window.

corfu-doc in a corfu popup. Called from a java file with completion candidates provided by lsp-mode.Here is a sample configuration[4]:
(use-package corfu-doc ;; NOTE 2022-02-05: At the time of writing, `corfu-doc' is not yet on melpa :straight (corfu-doc :type git :host github :repo "galeo/corfu-doc") :after corfu :hook (corfu-mode . corfu-doc-mode) :general (:keymaps 'corfu-map ;; This is a manual toggle for the documentation popup. [remap corfu-show-documentation] #'corfu-doc-toggle ; Remap the default doc command ;; Scroll in the documentation window "M-n" #'corfu-doc-scroll-up "M-p" #'corfu-doc-scroll-down) :custom (corfu-doc-delay 0.5) (corfu-doc-max-width 70) (corfu-doc-max-height 20)
;; NOTE 2022-02-05: I've also set this in the `corfu' use-package to be ;; extra-safe that this is set when corfu-doc is loaded. I do not want ;; documentation shown in both the echo area and in the `corfu-doc' popup. (corfu-echo-documentation nil))From my experience, corfu-doc is perfect for most. However, it should be noted that for those who have a high repeat rate[5], rapidly scrolling through candidates causes stuttering and/or lag. This is why I find setting a keybind for corfu-doc-toggle to be useful.
Changelog §
-
- Added link to Reddit comment in Working with the recent changes to
corfusection. Also update description of newcorfubehavior. - Added configuration for using
corfuin the minibuffer. - Listed more benefits to
corfu, provided by u/JDRiverRun.
- Added link to Reddit comment in Working with the recent changes to
-
- Added link to relevant
corfuGitHub Issue. - Changed
corfuconfiguration to avoid setting keybinds ingeneral-override-mode-map, suggested by a comment to this point. - Added a note and GIF to
corfu-docsection.
- Added link to relevant
-
- Update to include new compatibility with
corfu-insert-separatorandcorfu-quit-at-boundaryfunctionality.
- Update to include new compatibility with