merge: pull request #7739 from LemonBreezes/corfu-update-smart-tab

feat(corfu): update smart tab completion
This commit is contained in:
Henrik Lissner 2024-06-30 15:24:52 -04:00 committed by GitHub
commit 3cb9f17132
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 176 additions and 62 deletions

View file

@ -202,6 +202,15 @@ A few variables may be set to change behavior of this module:
- [[var:+corfu-want-minibuffer-completion]] :: - [[var:+corfu-want-minibuffer-completion]] ::
Whether to enable Corfu in the minibuffer. See its documentation for Whether to enable Corfu in the minibuffer. See its documentation for
additional tweaks. additional tweaks.
- [[var:+corfu-want-tab-prefer-expand-snippets]] ::
Whether to prefer expanding snippets over cycling candidates when pressing
[[kbd:][TAB]].
- [[var:+corfu-want-tab-prefer-navigating-snippets]] ::
Whether to prefer navigating snippets over cycling candidates when pressing
[[kbd:][TAB]] and [[kbd:][S-TAB]].
- [[var:+corfu-want-tab-prefer-navigating-org-tables]] ::
Whether to prefer navigating org tables over cycling candidates when pressing
[[kbd:][TAB]] and [[kbd:][S-TAB]].
** Turning off auto-completion ** Turning off auto-completion
To disable idle (as-you-type) completion, unset ~corfu-auto~: To disable idle (as-you-type) completion, unset ~corfu-auto~:
@ -241,6 +250,8 @@ all CAPFs are interactive to be called this way, in which case you can use
* Troubleshooting * Troubleshooting
[[doom-report:][Report an issue?]] [[doom-report:][Report an issue?]]
** Troubleshooting ~cape-dabbrev~
If you have performance issues with ~cape-dabbrev~, the first thing I recommend 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: doing is to look at the list of buffers Dabbrev is scanning:
@ -268,6 +279,34 @@ debug the issue:
(search-in-dabbrev-buffers "\342\200\231") (search-in-dabbrev-buffers "\342\200\231")
#+end_src #+end_src
** Fixing TAB Keybindings
If you encounter an issue where your ~TAB~ keybindings are not responding in Doom
Emacs while the ~:editor evil~ module is active, it's likely caused by a conflict
where ~<tab>~ keybindings and insert state bindings are overriding your ~TAB~ key
assignments.
In Evil mode, keybinding priorities are set such that:
1. ~<tab>~ keybindings supersede ~TAB~ keybindings and only work in GUI Emacs.
2. Bindings in insert state take precedence whenever the insert state is active.
To resolve this conflict and to assign your desired command to the ~TAB~ key, you
must redefine the keybindings with insert state set explicitly. You can do this
by configuring your ~evil~ keybindings for the insert state as follows:
#+begin_src emacs-lisp
(map! :gi "TAB" #'your-command
:gi "<tab>" #'your-command)
#+end_src
Place this code in your Doom Emacs configuration file to set the function ~your-command~ as the response to pressing ~TAB~ during insert mode.
Remember to replace ~#'your-command~ with the actual command you wish to invoke
with the ~TAB~ key.
If ever in a situation like this, use ~describe-key~ with ~C-h k~ and look at what
command is being called as well as what keymaps the command is defined in.
* Frequently asked questions * Frequently asked questions
/This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] /This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]]

View file

@ -16,6 +16,18 @@ Possible values are:
Setting this to `aggressive' will enable Corfu in more commands which Setting this to `aggressive' will enable Corfu in more commands which
use the minibuffer such as `query-replace'.") use the minibuffer such as `query-replace'.")
(defvar +corfu-want-tab-prefer-expand-snippets nil
"If non-nil, prefer expanding snippets over cycling candidates with
TAB.")
(defvar +corfu-want-tab-prefer-navigating-snippets nil
"If non-nil, prefer navigating snippets over cycling candidates with
TAB/S-TAB.")
(defvar +corfu-want-tab-prefer-navigating-org-tables nil
"If non-nil, prefer navigating org tables over cycling candidates with
TAB/S-TAB.")
;; ;;
;;; Packages ;;; Packages
(use-package! corfu (use-package! corfu

View file

@ -38,38 +38,66 @@
;;; Global keybindings ;;; Global keybindings
;; Smart tab, these will only work in GUI Emacs ;; Smart tab, these will only work in GUI Emacs
(map! :i [tab] (cmds! (and (modulep! :editor snippets) (map! :i [tab]
(yas-maybe-expand-abbrev-key-filter 'yas-expand)) `(menu-item "Evil insert smart tab" nil :filter
#'yas-expand (lambda (cmd)
(and (bound-and-true-p company-mode) (cond
(modulep! :completion company +tng)) ((or (doom-lookup-key [tab] overriding-terminal-local-map)
#'company-indent-or-complete-common (doom-lookup-key (kbd "TAB") overriding-terminal-local-map))
(and (bound-and-true-p corfu-mode) cmd)
(modulep! :completion corfu)) ,@(when (modulep! :editor snippets)
#'completion-at-point) '(((+yas-active-p)
:m [tab] (cmds! (and (modulep! :editor snippets) #'yas-next-field-or-maybe-expand)
(evil-visual-state-p) ((yas-maybe-expand-abbrev-key-filter 'yas-expand)
(or (eq evil-visual-selection 'line) #'yas-expand)))
(not (memq (char-after) (list ?\( ?\[ ?\{ ?\} ?\] ?\)))))) ,@(when (modulep! :completion company +tng)
#'yas-insert-snippet '(((bound-and-true-p company-mode)
(and (modulep! :editor fold) #'company-indent-or-complete-common)))
(save-excursion (end-of-line) (invisible-p (point)))) ,@(when (modulep! :completion corfu)
#'+fold/toggle '(((bound-and-true-p corfu-mode)
;; Fixes #4548: without this, this tab keybind overrides (if (derived-mode-p 'eshell-mode 'comint-mode)
;; mode-local ones for modes that don't have an evil #'completion-at-point
;; keybinding scheme or users who don't have :editor (evil #'indent-for-tab-command)))))))
;; +everywhere) enabled. :m [tab]
(or (doom-lookup-key `(menu-item "Evil motion smart tab" nil :filter
[tab] (lambda (cmd)
(list (evil-get-auxiliary-keymap (current-local-map) evil-state) (cond
(current-local-map))) ((or (doom-lookup-key [tab] overriding-terminal-local-map)
(doom-lookup-key (doom-lookup-key (kbd "TAB") overriding-terminal-local-map))
(kbd "TAB") cmd)
(list (evil-get-auxiliary-keymap (current-local-map) evil-state))) ,@(when (modulep! :editor snippets)
(doom-lookup-key (kbd "TAB") (list (current-local-map)))) '(((and (evil-visual-state-p)
it (or (eq evil-visual-selection 'line)
(fboundp 'evil-jump-item) (not (memq (char-after)
#'evil-jump-item) (list ?\( ?\[ ?\{ ?\} ?\] ?\))))))
#'yas-insert-snippet)))
,@(when (modulep! :editor fold)
'(((save-excursion (end-of-line) (invisible-p (point)))
#'+fold/toggle)))
;; Fixes #4548: without this, this tab keybind overrides
;; mode-local ones for modes that don't have an evil
;; keybinding scheme or users who don't have :editor (evil
;; +everywhere) enabled.
((or (doom-lookup-key
[tab]
(list (evil-get-auxiliary-keymap (current-local-map)
evil-state)
(current-local-map)))
(doom-lookup-key
(kbd "TAB")
(list (evil-get-auxiliary-keymap (current-local-map)
evil-state)))
(doom-lookup-key (kbd "TAB") (list (current-local-map))))
cmd)
((fboundp 'evil-jump-item)
#'evil-jump-item))))
;; Extend smart tab for specific modes. This way, we process the entire
;; smart tab logic and only fall back to these commands at the end.
(:when (modulep! :lang org)
(:after org :map org-mode-map
[remap indent-for-tab-command]
`(menu-item "Go to the next field" org-table-next-field
:filter ,(lambda (cmd) (when (org-at-table-p) cmd)))))
(:after help :map help-mode-map (:after help :map help-mode-map
:n "o" #'link-hint-open-link) :n "o" #'link-hint-open-link)
@ -90,9 +118,9 @@
:n "o" #'link-hint-open-link) :n "o" #'link-hint-open-link)
(:unless (modulep! :input layout +bepo) (:unless (modulep! :input layout +bepo)
(:after (evil-org evil-easymotion) (:after (evil-org evil-easymotion)
:map evil-org-mode-map :map evil-org-mode-map
:m "gsh" #'+org/goto-visible)) :m "gsh" #'+org/goto-visible))
(:when (modulep! :editor multiple-cursors) (:when (modulep! :editor multiple-cursors)
:prefix "gz" :prefix "gz"

View file

@ -464,41 +464,76 @@ Continues comments if executed from a commented line. Consults
[remap corfu-insert-separator] #'+corfu-smart-sep-toggle-escape [remap corfu-insert-separator] #'+corfu-smart-sep-toggle-escape
"C-S-s" #'+corfu-move-to-minibuffer "C-S-s" #'+corfu-move-to-minibuffer
"C-p" #'corfu-previous "C-p" #'corfu-previous
"C-n" #'corfu-next "C-n" #'corfu-next))
"S-TAB" #'corfu-previous
[backtab] #'corfu-previous
"TAB" #'corfu-next
[tab] #'corfu-next))
(let ((cmds-del (let ((cmds-del
`(menu-item "Reset completion" corfu-reset `(menu-item "Reset completion" corfu-reset
:filter ,(lambda (cmd) :filter ,(lambda (cmd)
(when (and (>= corfu--index 0) (cond
(eq corfu-preview-current 'insert)) ((and (>= corfu--index 0)
cmd)))) (eq corfu-preview-current 'insert))
(cmds-ret cmd)))))
`(menu-item "Insert completion DWIM" corfu-insert (cmds-ret
:filter ,(lambda (cmd) `(menu-item "Insert completion DWIM" corfu-insert
(interactive) :filter ,(lambda (cmd)
(cond ((null +corfu-want-ret-to-confirm) (cond
(corfu-quit) ((null +corfu-want-ret-to-confirm)
nil) (corfu-quit)
((eq +corfu-want-ret-to-confirm 'minibuffer) nil)
(funcall-interactively cmd) ((eq +corfu-want-ret-to-confirm 'minibuffer)
nil) (funcall-interactively cmd)
((and (or (not (minibufferp nil t)) nil)
(eq +corfu-want-ret-to-confirm t)) ((and (or (not (minibufferp nil t))
(>= corfu--index 0)) (eq +corfu-want-ret-to-confirm t))
cmd) (>= corfu--index 0))
((or (not (minibufferp nil t)) cmd)
(eq +corfu-want-ret-to-confirm t)) ((or (not (minibufferp nil t))
nil) (eq +corfu-want-ret-to-confirm t))
(t cmd)))))) nil)
(t cmd)))))
(cmds-tab
`(menu-item "Select next candidate or expand/traverse snippet" corfu-next
:filter (lambda (cmd)
(cond
,@(when (modulep! :editor snippets)
'(((and +corfu-want-tab-prefer-navigating-snippets
(memq (bound-and-true-p yas--active-field-overlay)
(overlays-in (1- (point)) (1+ (point)))))
#'yas-next-field-or-maybe-expand)
((and +corfu-want-tab-prefer-expand-snippets
(yas-maybe-expand-abbrev-key-filter 'yas-expand))
#'yas-expand)))
,@(when (modulep! :lang org)
'(((and +corfu-want-tab-prefer-navigating-org-tables
(featurep 'org)
(org-at-table-p))
#'org-table-next-field)))
(t cmd)))) )
(cmds-s-tab
`(menu-item "Select previous candidate or expand/traverse snippet"
corfu-previous
:filter (lambda (cmd)
(cond
,@(when (modulep! :editor snippets)
'(((and +corfu-want-tab-prefer-navigating-snippets
(memq (bound-and-true-p yas--active-field-overlay)
(overlays-in (1- (point)) (1+ (point)))))
#'yas-prev-field)))
,@(when (modulep! :lang org)
'(((and +corfu-want-tab-prefer-navigating-org-tables
(featurep 'org)
(org-at-table-p))
#'org-table-previous-field)))
(t cmd))))))
(map! :when (modulep! :completion corfu) (map! :when (modulep! :completion corfu)
:map corfu-map :map corfu-map
[backspace] cmds-del [backspace] cmds-del
"DEL" cmds-del "DEL" cmds-del
:gi [return] cmds-ret :gi [return] cmds-ret
:gi "RET" cmds-ret)) :gi "RET" cmds-ret
"S-TAB" cmds-s-tab
[backtab] cmds-s-tab
:gi "TAB" cmds-tab
:gi [tab] cmds-tab))
;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation. ;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation.
;; Pressing it again will send you to the true bol. Same goes for C-e, except ;; Pressing it again will send you to the true bol. Same goes for C-e, except