From 62b2cf9cbf0e460155d5db5d07ed1a3a1b1d6cfd Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Mon, 30 Oct 2023 14:36:19 -0400 Subject: [PATCH] feat(corfu): more CAPFs and ergonomy changes Add various CAPFs from cape: - `cape-dabbrev`; - `cape-emoji`; - `cape-dict`; Fixed some CAPFs via cape: - Make non-exclusive, purified and silent `pcomplete-completions-at-point`; - Make non-exclusive and non-interruptable `lsp-completion-at-point`; - Make non-exclusive `eglot-completion-at-point`; - Make non-exclusive `comint-completion-at-point`; Fix and improve keybindings: - Smart `DEL`/`backspace` for `+tng`; - Smart `RET`; Add depth to CAPFs, allowing ordering to be adjustable. Enable in minibuffer. --- modules/completion/corfu/README.org | 64 ++++++++++++++++--------- modules/completion/corfu/config.el | 73 +++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 24 deletions(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 534aa04a2..64935940e 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -26,6 +26,14 @@ and highly non-native, but has some extra features and more maturity. - +orderless :: Pull in [[doom-package:orderless]] if necessary and apply multi-component completion (still needed if [[doom-module::completion vertico]] is active). +- +dabbrev :: + Enable and configure [[doom-package:dabbrev]] as a close-to-universal CAPF + fallback. +- +dict :: + Enable and configure dictionary completion for text modes and related regions + in programming modes. +- +emoji :: + Enable and configure emoji completion via the emoji input method. ** Packages - [[doom-package:corfu]] @@ -63,26 +71,25 @@ By default, completion gets triggered after typing 2 non-space consecutive characters, or by means of the [[kbd:][C-SPC]] keybinding at any moment. While the popup is visible, the following relevant keys are available: -| Keybind | Description | -|----------+------------------------------------------------------------------| -| [[kbd:][]] | Go to next candidate | -| [[kbd:][]] | Go to previous candidate | -| [[kbd:][C-n]] | Go to next candidate | -| [[kbd:][C-p]] | Go to previous candidate | -| [[kbd:][C-j]] | (evil) Go to next candidate | -| [[kbd:][C-k]] | (evil) Go to previous candidate | -| [[kbd:][C-]] | Go to next doc line | -| [[kbd:][C-]] | Go to previous doc line | -| [[kbd:][C-S-n]] | Go to next doc line | -| [[kbd:][C-S-p]] | Go to previous doc line | -| [[kbd:][C-S-j]] | (evil) Go to next doc line | -| [[kbd:][C-S-k]] | (evil) Go to previous doc line | -| [[kbd:][C-h]] | Toggle documentation (if available) | +| Keybind | Description | +|----------+---------------------------------------------------------| +| [[kbd:][]] | Go to next candidate | +| [[kbd:][]] | Go to previous candidate | +| [[kbd:][C-n]] | Go to next candidate | +| [[kbd:][C-p]] | Go to previous candidate | +| [[kbd:][C-j]] | (evil) Go to next candidate | +| [[kbd:][C-k]] | (evil) Go to previous candidate | +| [[kbd:][C-]] | Go to next doc line | +| [[kbd:][C-]] | Go to previous doc line | +| [[kbd:][C-S-n]] | Go to next doc line | +| [[kbd:][C-S-p]] | Go to previous doc line | +| [[kbd:][C-S-j]] | (evil) Go to next doc line | +| [[kbd:][C-S-k]] | (evil) Go to previous doc line | +| [[kbd:][C-h]] | Toggle documentation (if available) | | [[kbd:][M-m]] | Export to minibuffer (if [[doom-module::completion vertico]]) | -| [[kbd:][M-S-j]] | (evil) Export to minibuffer (if [[doom-module::completion vertico]]) | -| [[kbd:][RET]] | Insert candidate | -| [[kbd:][SPC]] | (after wildcard) Reset completion | -| [[kbd:][DEL]] | Reset completion | +| [[kbd:][M-j]] | (evil) Export to minibuffer (if [[doom-module::completion vertico]]) | +| [[kbd:][RET]] | Insert candidate | +| [[kbd:][SPC]] | Quit autocompletion after a wildcard or pass-through | | [[kbd:][C-SPC]] | Complete (unless [[doom-module::completion corfu +tng]]) | | [[kbd:][C-SPC]] | (when completing) Insert separator DWIM (see below) | @@ -95,6 +102,7 @@ following additional binds: | [[kbd:][TAB]] | Complete | | [[kbd:][TAB]] | (when completing) Go to next candidate | | [[kbd:][S-TAB]] | (when completing) Go to previous candidate | +| [[kbd:][DEL]] | (when completing) Reset completion DWIM-style | ** Searching with multiple keywords If the [[doom-module::completion corfu +orderless]] flag is enabled, users can @@ -129,15 +137,18 @@ A few variables may be set to change behavior of this module: Configures behavior for exact matches. Its default is nil, and it's recommended to leave it at that. Otherwise, single matches on snippet keys expand immediately. +- [[var:+corfu-buffer-scanning-size-limit]] :: + Sets the maximum buffer size to be scanned by ~cape-dabbrev~. Defaults to 1 + MB. Set this if you are having performance problems using the CAPF. ** Adding CAPFs to a mode To add other CAPFs on a mode-per-mode basis, put either of the following in your ~config.el~: #+begin_src emacs-lisp -(add-hook! some-mode (add-to-list 'completion-at-point-functions #'some-capf)) +(add-hook! some-mode (add-hook 'completion-at-point-functions #'some-capf depth t)) ;; OR, but note the different call signature -(add-hook 'some-mode-hook (lambda () (add-to-list 'completion-at-point-functions #'some-capf))) +(add-hook 'some-mode-hook (lambda () (add-hook 'completion-at-point-functions #'some-capf depth t))) #+end_src ~DEPTH~ above is an integer between -100, 100, and defaults to 0 of omitted. Also @@ -147,6 +158,17 @@ accepts the quoted arguments form above. * Troubleshooting [[doom-report:][Report an issue?]] +If you have performance issues with ~cape-dabbrev~, the first thing I recommend +doing is to look at the list of buffers Dabbrev is scanning: + +#+begin_src emacs-lisp +(dabbrev--select-buffers) ; => (# #> # ...) +(length (dabbrev--select-buffers)) ; => 37 +#+end_src + +... and modify ~dabbrev-ignored-buffer-regexps~ or ~dabbrev-ignored-buffer-modes~ +accordingly. + * Frequently asked questions /This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index f3f91beba..9743b6946 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -1,5 +1,8 @@ ;;; completion/corfu/config.el -*- lexical-binding: t; -*- +(defvar +corfu-buffer-scanning-size-limit (* 1 1024 1024) ; 1 MB + "Size limit for a buffer to be scanned by `cape-dabbrev'.") + ;; ;;; Packages (use-package! corfu @@ -18,7 +21,7 @@ t) corfu-cycle t corfu-separator (when (modulep! +orderless) ?\s) - corfu-preselect 'valid + corfu-preselect (if (modulep! +tng) 'prompt 'valid) corfu-count 16 corfu-max-width 120 corfu-preview-current 'insert @@ -41,6 +44,13 @@ :map corfu-mode-map "C-M-i" #'completion-at-point) + (add-hook! 'minibuffer-setup-hook + (defun +corfu-enable-in-minibuffer () + "Enable Corfu in the minibuffer if `completion-at-point' is bound." + (when (where-is-internal #'completion-at-point (list (current-local-map))) + (setq-local corfu-echo-delay nil) + (corfu-mode +1)))) + (after! evil (add-hook 'evil-insert-state-exit-hook #'corfu-quit)) @@ -78,10 +88,67 @@ :init (add-hook! prog-mode (defun +corfu-add-cape-file-h () - (add-to-list 'completion-at-point-functions #'cape-file))) + (add-hook 'completion-at-point-functions #'cape-file -10 t))) (add-hook! (org-mode markdown-mode) (defun +corfu-add-cape-elisp-block-h () - (add-to-list 'completion-at-point-functions #'cape-elisp-block))) + (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t))) + ;; Enable Dabbrev completion basically everywhere as a fallback. + (when (modulep! +dabbrev) + ;; Set up `cape-dabbrev' options. + (defun +dabbrev-friend-buffer-p (other-buffer) + (< (buffer-size other-buffer) +corfu-buffer-scanning-size-limit)) + (after! dabbrev + (setq cape-dabbrev-check-other-buffers t + 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) + + (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))))) + ;; Complete emojis :). + (when (and (modulep! +emoji) (> emacs-major-version 28)) + (add-hook! (prog-mode conf-mode) + (defun +corfu-add-cape-emoji-h () + (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 + (defun +corfu-add-cape-emoji-text-h () + (add-hook 'completion-at-point-functions + (cape-capf-prefix-length #'cape-emoji 1) 10 t)))) + ;; Enable dictionary-based autocompletion. + (when (modulep! +dict) + (add-hook! (prog-mode conf-mode) + (defun +corfu-add-cape-dict-h () + (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))) + (add-hook! text-mode + (defun +corfu-add-cape-dict-text-h () + (add-hook 'completion-at-point-functions #'cape-dict 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)) (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible)) (use-package! yasnippet-capf