dot-doom/modules/completion/corfu/config.el

295 lines
12 KiB
EmacsLisp
Raw Normal View History

2023-03-05 12:03:36 -05:00
;;; completion/corfu/config.el -*- lexical-binding: t; -*-
2023-10-31 11:50:28 -04:00
(defvar +cape-buffer-scanning-size-limit (* 1 1024 1024) ; 1 MB
"Size limit for a buffer to be scanned by `cape-line' or `cape-dabbrev'.
As an exception, `cape-line' will also scan buffers with the same
major mode regardless of size.")
(defvar +orderless-wildcard-character ?,
"A character used as a wildcard in Corfu for fuzzy autocompletion. If you
want to match the wildcard literally in completion, you can
escape it with forward slash. Do NOT set this to SPC.
This variable needs to be set at the top-level before any `after!' blocks.")
2023-10-19 11:23:45 -04:00
;;
;;; Packages
2023-03-05 12:03:36 -05:00
(use-package! corfu
:hook (doom-first-buffer . global-corfu-mode)
2023-10-19 11:23:45 -04:00
:hook (org-mode . corfu-mode)
2023-03-05 12:03:36 -05:00
:init
;; Auto-completion settings, must be set before calling `global-corfu-mode'.
2023-10-31 11:50:28 -04:00
;; Due to lazy-loading, overriding these in config.el works too.
(setq corfu-auto t
2023-09-06 10:06:14 -04:00
corfu-auto-delay 0.1
corfu-auto-prefix 2
corfu-excluded-modes '(erc-mode
circe-mode
help-mode
gud-mode
vterm-mode))
2023-10-31 11:50:28 -04:00
(map! (:unless (modulep! +tng)
:i "C-SPC" #'completion-at-point))
2023-03-05 12:03:36 -05:00
:config
(setq corfu-cycle t
2023-10-31 11:50:28 -04:00
corfu-preselect (if (modulep! :completion corfu +tng) 'prompt t)
corfu-count 16
corfu-max-width 120
corfu-preview-current 'insert
2023-09-06 10:06:14 -04:00
corfu-on-exact-match nil
corfu-quit-at-boundary (if (modulep! +orderless) 'separator t)
corfu-quit-no-match (if (modulep! +orderless) 'separator t)
;; In the case of +tng, TAB should be smart regarding completion;
;; However, it should otherwise behave like normal, whatever normal was.
tab-always-indent (if (modulep! +tng) 'complete tab-always-indent))
2023-10-19 11:23:45 -04:00
2023-10-31 11:50:28 -04:00
(defun corfu-disable-in-minibuffer-p ()
(or (bound-and-true-p mct--active)
(bound-and-true-p vertico--input)
(and (featurep 'helm-core) (helm--alive-p))
(eq (current-local-map) read-passwd-map)))
(defun corfu-enable-in-minibuffer ()
"Enable Corfu in the minibuffer if `completion-at-point' is bound."
(unless (corfu-disable-in-minibuffer-p)
(setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
corfu-popupinfo-delay nil)
(corfu-mode 1)))
(add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer)
(defun corfu-visible-p ()
(or (and (frame-live-p corfu--frame)
(frame-visible-p corfu--frame))
(and (featurep 'corfu-terminal)
(popon-live-p corfu-terminal--popon))))
;; 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 (corfu-visible-p)
(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)
;; Pending a PR I am making to expose `anzu--update-timer'.
(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)))))
;; Do not make us type RET twice with Corfu.
(defun corfu--maybe-return-filter (cmd)
(if (eq corfu--index -1) (corfu-quit) cmd))
(keymap-set corfu-map "RET" `(menu-item "corfu-maybe-return" corfu-insert
:filter corfu--maybe-return-filter))
(keymap-set
corfu-map "<return>" `(menu-item "corfu-maybe-return" corfu-insert
:filter corfu--maybe-return-filter))
;; Allow completion after `:' in Lispy.
(add-to-list 'corfu-auto-commands #'lispy-colon)
(when (and (modulep! +orderless)
+orderless-wildcard-character)
(defmacro +orderless-escapable-split-fn (char)
(let ((char-string (string (if (symbolp char) (symbol-value char) char))))
`(defun +orderless-escapable-split-on-space-or-char (s)
(mapcar
(lambda (piece)
(replace-regexp-in-string
(string 1) ,char-string
(replace-regexp-in-string
(concat (string 0) "\\|" (string 1))
(lambda (x)
(pcase x
("\0" " ")
("\1" ,char-string)
(_ x)))
piece
;; These are arguments to `replace-regexp-in-string'.
'fixedcase 'literal)
'fixedcase 'literal))
(split-string (replace-regexp-in-string
(concat "\\\\\\\\\\|\\\\ \\|\\\\"
,char-string)
(lambda (x)
(pcase x
("\\ " "\0")
(,(concat "\\" char-string)
"\1")
(_ x)))
s 'fixedcase 'literal)
,(concat "[ " char-string "]+")
t)))))
(after! orderless
;; Orderless splits the string into components and then determines the
;; matching style for each component. This is all regexp stuff.
(setq orderless-component-separator
(+orderless-escapable-split-fn +orderless-wildcard-character))
(setq corfu-separator +orderless-wildcard-character)
(keymap-set corfu-map (char-to-string +orderless-wildcard-character)
#'+corfu-insert-wildcard-separator)
;; Quit completion after typing the wildcard followed by a space.
(keymap-set corfu-map "SPC"
`(menu-item "corfu-maybe-quit" nil
:filter
,(lambda (_)
(when (and (> (point) (point-min))
(eq (char-before)
+orderless-wildcard-character))
(corfu-quit)
nil))))))
(add-hook! 'evil-insert-state-exit-hook
(defun +corfu-quit-on-evil-insert-state-exit-h ()
;; This predicate a workaround for unexpected calls to `corfu-quit' in
;; :company-doc-buffer buffers. This was specifically happening when using
;; `yasnippet-capf' and `company-yasnippet'.
(when (eq (current-buffer) (window-buffer (selected-window)))
(corfu-quit))))
2023-10-19 11:23:45 -04:00
(when (modulep! +icons)
2023-10-31 11:50:28 -04:00
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
2023-10-19 11:23:45 -04:00
2023-10-31 11:50:28 -04:00
;; Reset completion DWIM-style with backspace.
(when (modulep! +tng)
(defun corfu--maybe-reset-backspace-filter (cmd)
(when (and (> corfu--index -1)
(eq corfu-preview-current 'insert))
cmd))
(keymap-set corfu-map "DEL" `(menu-item "corfu-maybe-reset" corfu-reset
:filter corfu--maybe-reset-backspace-filter))
(keymap-set
corfu-map "<backspace>" `(menu-item "corfu-maybe-reset" corfu-reset
:filter corfu--maybe-reset-backspace-filter)))
(map! (:map 'corfu-map
(:when (modulep! +orderless)
"C-SPC" #'corfu-insert-separator)
(:when (modulep! +tng)
[tab] #'corfu-next
[backtab] #'corfu-previous
"TAB" #'corfu-next
"S-TAB" #'corfu-previous)))
2023-10-19 11:23:45 -04:00
(after! evil-collection-corfu
2023-09-06 10:06:14 -04:00
(evil-collection-define-key 'insert 'corfu-map
(kbd "RET") #'corfu-insert
[return] #'corfu-insert))
(after! vertico
2023-10-31 11:50:28 -04:00
(map! :map 'corfu-map "M-m" #'corfu-move-to-minibuffer)
(after! evil-collection-corfu
(evil-collection-define-key 'insert 'corfu-map
(kbd "M-j") #'corfu-move-to-minibuffer))))
2023-03-05 12:03:36 -05:00
(use-package! cape
2023-10-31 11:50:28 -04:00
:defer t
:init
;; Set up `cape-dabbrev' and `cape-line' options.
(defun +cape-line-buffers ()
(cl-loop for buf in (buffer-list)
if (or (eq major-mode (buffer-local-value 'major-mode buf))
(< (buffer-size buf) +cape-buffer-scanning-size-limit))
collect buf))
(defun +dabbrev-friend-buffer-p (other-buffer)
(< (buffer-size other-buffer) +cape-buffer-scanning-size-limit))
(setq cape-dabbrev-check-other-buffers t
cape-line-buffer-function #'+cape-line-buffers
dabbrev-friend-buffer-function #'+dabbrev-friend-buffer-p
dabbrev-ignored-buffer-regexps
'("\\.\\(?:pdf\\|jpe?g\\|png\\|svg\\|eps\\)\\'"
"^ "
"\\(TAGS\\|tags\\|ETAGS\\|etags\\|GTAGS\\|GRTAGS\\|GPATH\\)\\(<[0-9]+>\\)?")
dabbrev-upcase-means-case-search t)
(add-hook! prog-mode
(add-hook 'completion-at-point-functions #'cape-file -10 t))
(add-hook! (org-mode markdown-mode)
(add-hook 'completion-at-point-functions #'cape-elisp-block 0 t))
;; Enable Dabbrev completion basically everywhere as a fallback.
(add-hook! (prog-mode text-mode conf-mode comint-mode minibuffer-setup
eshell-mode)
(add-hook 'completion-at-point-functions #'cape-dabbrev 20 t))
;; Complete emojis :).
(when (> emacs-major-version 28)
(add-hook! (prog-mode conf-mode)
(add-hook 'completion-at-point-functions
(cape-capf-inside-faces
(cape-capf-prefix-length #'cape-emoji 1)
;; Only call inside comments and docstrings.
'tree-sitter-hl-face:doc 'font-lock-doc-face
'font-lock-comment-face 'tree-sitter-hl-face:comment)
10 t))
(add-hook! text-mode
(add-hook 'completion-at-point-functions
(cape-capf-prefix-length #'cape-emoji 1) 10 t)))
;; Enable dictionary-based autocompletion.
(add-hook! text-mode
(add-hook 'completion-at-point-functions #'cape-dict 40 t))
(add-hook! (prog-mode conf-mode)
(add-hook 'completion-at-point-functions
(cape-capf-inside-faces
;; Only call inside comments and docstrings.
#'cape-dict 'tree-sitter-hl-face:doc 'font-lock-doc-face
'font-lock-comment-face 'tree-sitter-hl-face:comment)
40 t))
;; Make these capfs composable.
(advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive)
(advice-add #'eglot-completion-at-point :around #'cape-wrap-nonexclusive)
(advice-add #'lsp-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))
2023-10-19 11:23:45 -04:00
(advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible))
2023-09-06 10:06:14 -04:00
(use-package! yasnippet-capf
2023-10-31 11:50:28 -04:00
:when (modulep! :editor snippets)
:defer t
:init
(add-hook! yas-minor-mode
(add-hook 'completion-at-point-functions #'yasnippet-capf 30 t)))
2023-03-05 12:03:36 -05:00
(use-package! corfu-terminal
2023-09-06 10:06:14 -04:00
:when (not (display-graphic-p))
:hook (corfu-mode . corfu-terminal-mode))
2023-03-05 12:03:36 -05:00
;;
;;; Extensions
2023-10-31 11:50:28 -04:00
(use-package! corfu-history
:hook (corfu-mode . corfu-history-mode)
:config
2023-10-31 11:50:28 -04:00
(after! savehist (add-to-list 'savehist-additional-variables 'corfu-history)))
2023-03-05 12:03:36 -05:00
(use-package! corfu-popupinfo
:hook (corfu-mode . corfu-popupinfo-mode)
:config
(setq corfu-popupinfo-delay '(0.5 . 1.0))
(map! (:map 'corfu-map
2023-09-06 10:06:14 -04:00
"C-<up>" #'corfu-popupinfo-scroll-down
"C-<down>" #'corfu-popupinfo-scroll-up
"C-S-p" #'corfu-popupinfo-scroll-down
"C-S-n" #'corfu-popupinfo-scroll-up
"C-h" #'corfu-popupinfo-toggle)
(:map 'corfu-popupinfo-map
:when (modulep! :editor evil)
;; Reversed because popupinfo assumes opposite of what feels intuitive
;; with evil.
2023-09-06 10:06:14 -04:00
"C-S-k" #'corfu-popupinfo-scroll-down
"C-S-j" #'corfu-popupinfo-scroll-up)))