mirror of
https://github.com/doomemacs/doomemacs
synced 2025-09-16 15:56:52 -05:00
Adds a new value of `+corfu-want-ret-to-confirm` to cover the case where the user wants to select the candidate as well as perform the normal behavior of RET. Also, the logic for RET behavior has been refactored for clarity, and the variable's documentation has been clarified a little.
186 lines
7.8 KiB
EmacsLisp
186 lines
7.8 KiB
EmacsLisp
;;; completion/corfu/config.el -*- lexical-binding: t; -*-
|
|
|
|
(defvar +corfu-want-ret-to-confirm t
|
|
"Configure how the user expects RET to behave.
|
|
Possible values are:
|
|
- t (default): Insert candidate if one is selected, pass-through otherwise;
|
|
- nil: Pass-through without inserting;
|
|
- `both': Insert candidate if one is selected, then pass-through;
|
|
- `minibuffer': Behaves like `both` in the minibuffer and `t` otherwise.")
|
|
|
|
(defvar +corfu-buffer-scanning-size-limit (* 1 1024 1024) ; 1 MB
|
|
"Size limit for a buffer to be scanned by `cape-dabbrev'.")
|
|
|
|
(defvar +corfu-want-minibuffer-completion t
|
|
"Whether to enable Corfu in the minibuffer.
|
|
Setting this to `aggressive' will enable Corfu in more commands which
|
|
use the minibuffer such as `query-replace'.")
|
|
|
|
;;
|
|
;;; Packages
|
|
(use-package! corfu
|
|
:hook (doom-first-input . global-corfu-mode)
|
|
:init
|
|
(add-hook! 'minibuffer-setup-hook
|
|
(defun +corfu-enable-in-minibuffer ()
|
|
"Enable Corfu in the minibuffer."
|
|
(when (pcase +corfu-want-minibuffer-completion
|
|
('aggressive
|
|
(not (or (bound-and-true-p mct--active)
|
|
(bound-and-true-p vertico--input)
|
|
(eq (current-local-map) read-passwd-map)
|
|
(and (featurep 'helm-core) (helm--alive-p))
|
|
(and (featurep 'ido) (ido-active))
|
|
(where-is-internal 'minibuffer-complete
|
|
(list (current-local-map)))
|
|
(memq #'ivy--queue-exhibit post-command-hook))))
|
|
('nil nil)
|
|
(_ (where-is-internal #'completion-at-point
|
|
(list (current-local-map)))))
|
|
(setq-local corfu-echo-delay nil)
|
|
(corfu-mode +1))))
|
|
:config
|
|
(setq corfu-auto t
|
|
corfu-auto-delay 0.1
|
|
corfu-auto-prefix 2
|
|
global-corfu-modes '((not
|
|
erc-mode
|
|
circe-mode
|
|
help-mode
|
|
gud-mode
|
|
vterm-mode)
|
|
t)
|
|
corfu-cycle t
|
|
corfu-preselect 'prompt
|
|
corfu-count 16
|
|
corfu-max-width 120
|
|
corfu-on-exact-match nil
|
|
corfu-quit-at-boundary (if (or (modulep! :completion vertico)
|
|
(modulep! +orderless))
|
|
'separator t)
|
|
corfu-quit-no-match corfu-quit-at-boundary
|
|
tab-always-indent 'complete)
|
|
(add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles)))
|
|
(add-to-list 'corfu-auto-commands #'lispy-colon)
|
|
(add-to-list 'corfu-continue-commands #'+corfu-move-to-minibuffer)
|
|
(add-to-list 'corfu-continue-commands #'+corfu-smart-sep-toggle-escape)
|
|
(add-hook 'evil-insert-state-exit-hook #'corfu-quit)
|
|
|
|
;; If you want to update the visual hints after completing minibuffer commands
|
|
;; with Corfu and exiting, you have to do it manually.
|
|
(defadvice! +corfu--insert-before-exit-minibuffer-a ()
|
|
:before #'exit-minibuffer
|
|
(when (or (and (frame-live-p corfu--frame)
|
|
(frame-visible-p corfu--frame))
|
|
(and (featurep 'corfu-terminal)
|
|
(popon-live-p corfu-terminal--popon)))
|
|
(when (member isearch-lazy-highlight-timer timer-idle-list)
|
|
(apply (timer--function isearch-lazy-highlight-timer)
|
|
(timer--args isearch-lazy-highlight-timer)))
|
|
(when (member (bound-and-true-p anzu--update-timer) timer-idle-list)
|
|
(apply (timer--function anzu--update-timer)
|
|
(timer--args anzu--update-timer)))
|
|
(when (member (bound-and-true-p evil--ex-search-update-timer)
|
|
timer-idle-list)
|
|
(apply (timer--function evil--ex-search-update-timer)
|
|
(timer--args evil--ex-search-update-timer)))))
|
|
|
|
;; HACK: If your dictionaries aren't set up in text-mode buffers, ispell will
|
|
;; continuously pester you about errors. This ensures it only happens once
|
|
;; per session.
|
|
(defadvice! +corfu--auto-disable-ispell-capf-a (fn &rest args)
|
|
"If ispell isn't properly set up, only complain once per session."
|
|
:around #'ispell-completion-at-point
|
|
(condition-case-unless-debug e
|
|
(apply fn args)
|
|
('error
|
|
(message "Error: %s" (error-message-string e))
|
|
(message "Auto-disabling `text-mode-ispell-word-completion'")
|
|
(setq text-mode-ispell-word-completion nil)
|
|
(remove-hook 'completion-at-point-functions #'ispell-completion-at-point t)))))
|
|
|
|
(use-package! cape
|
|
:defer t
|
|
:init
|
|
(add-hook! prog-mode
|
|
(defun +corfu-add-cape-file-h ()
|
|
(add-hook 'completion-at-point-functions #'cape-file -10 t)))
|
|
(add-hook! (org-mode markdown-mode)
|
|
(defun +corfu-add-cape-elisp-block-h ()
|
|
(add-hook 'completion-at-point-functions #'cape-elisp-block 0 t)))
|
|
;; Enable Dabbrev completion basically everywhere as a fallback.
|
|
(when (modulep! +dabbrev)
|
|
(setq cape-dabbrev-check-other-buffers t)
|
|
;; Set up `cape-dabbrev' options.
|
|
(defun +dabbrev-friend-buffer-p (other-buffer)
|
|
(< (buffer-size other-buffer) +corfu-buffer-scanning-size-limit))
|
|
(add-hook! (prog-mode text-mode conf-mode comint-mode minibuffer-setup
|
|
eshell-mode)
|
|
(defun +corfu-add-cape-dabbrev-h ()
|
|
(add-hook 'completion-at-point-functions #'cape-dabbrev 20 t)))
|
|
(after! dabbrev
|
|
(setq dabbrev-friend-buffer-function #'+dabbrev-friend-buffer-p
|
|
dabbrev-ignored-buffer-regexps
|
|
'("^ "
|
|
"\\(TAGS\\|tags\\|ETAGS\\|etags\\|GTAGS\\|GRTAGS\\|GPATH\\)\\(<[0-9]+>\\)?")
|
|
dabbrev-upcase-means-case-search t)
|
|
(add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)))
|
|
|
|
;; Make these capfs composable.
|
|
(advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible)
|
|
(advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive)
|
|
(advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive)
|
|
(advice-add #'eglot-completion-at-point :around #'cape-wrap-nonexclusive)
|
|
(advice-add #'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive)
|
|
;; From the `cape' readme. Without this, Eshell autocompletion is broken on
|
|
;; Emacs28.
|
|
(when (< emacs-major-version 29)
|
|
(advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)
|
|
(advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)))
|
|
|
|
(use-package! yasnippet-capf
|
|
:when (modulep! :editor snippets)
|
|
:defer t
|
|
:init
|
|
(add-hook! 'yas-minor-mode-hook
|
|
(defun +corfu-add-yasnippet-capf-h ()
|
|
(add-hook 'completion-at-point-functions #'yasnippet-capf 30 t))))
|
|
|
|
(use-package! corfu-terminal
|
|
:when (modulep! :os tty)
|
|
:when (not (display-graphic-p))
|
|
:hook ((corfu-mode . corfu-terminal-mode)))
|
|
|
|
|
|
;;
|
|
;;; Extensions
|
|
|
|
(use-package! corfu-history
|
|
:hook ((corfu-mode . corfu-history-mode))
|
|
:config
|
|
(after! savehist (add-to-list 'savehist-additional-variables 'corfu-history)))
|
|
|
|
(use-package! corfu-popupinfo
|
|
:hook ((corfu-mode . corfu-popupinfo-mode))
|
|
:config
|
|
(setq corfu-popupinfo-delay '(0.5 . 1.0)))
|
|
|
|
(use-package! nerd-icons-corfu
|
|
:when (modulep! +icons)
|
|
:defer t
|
|
:init
|
|
(after! corfu
|
|
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)))
|
|
|
|
;; If vertico is not enabled, orderless will be installed but not configured.
|
|
;; That may break smart separator behavior, so we conditionally configure it.
|
|
(use-package! orderless
|
|
:when (and (not (modulep! :completion vertico))
|
|
(modulep! +orderless))
|
|
:config
|
|
(setq completion-styles '(orderless basic)
|
|
completion-category-defaults nil
|
|
completion-category-overrides '((file (styles orderless partial-completion)))
|
|
orderless-component-separator #'orderless-escapable-split-on-space)
|
|
(set-face-attribute 'completions-first-difference nil :inherit nil))
|