feat(corfu): update smart tab completion

This commit updates the smart tab functionality so that:
1. The only functionality checked is for the modules that are enabled.
2. The priority of the TAB behavior is tunable by the user.

This also updates the TAB behavior for the Corfu module to be
`indent-for-tab-command` instead of `completion-at-point` so that users
can use the TAB key to indent their code and navigating Org tables.

We also address #7372 by checking overriding-terminal-local-map, as that
is used by Embark.
This commit is contained in:
StrawberryTea 2024-03-20 18:02:27 -05:00
parent 73f19acb66
commit 390a9c1ea4
5 changed files with 147 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]] ::
Whether to enable Corfu in the minibuffer. See its documentation for
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]].
** Adding CAPFs to a mode
To add other CAPFs on a mode-per-mode basis, put either of the following in your

View file

@ -16,6 +16,18 @@ Possible values are:
Setting this to `aggressive' will enable Corfu in more commands which
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
(use-package! corfu

View file

@ -38,38 +38,64 @@
;;; Global keybindings
;; Smart tab, these will only work in GUI Emacs
(map! :i [tab] (cmds! (and (modulep! :editor snippets)
(yas-maybe-expand-abbrev-key-filter 'yas-expand))
#'yas-expand
(and (bound-and-true-p company-mode)
(modulep! :completion company +tng))
#'company-indent-or-complete-common
(and (bound-and-true-p corfu-mode)
(modulep! :completion corfu))
#'completion-at-point)
:m [tab] (cmds! (and (modulep! :editor snippets)
(evil-visual-state-p)
(or (eq evil-visual-selection 'line)
(not (memq (char-after) (list ?\( ?\[ ?\{ ?\} ?\] ?\))))))
#'yas-insert-snippet
(and (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))))
it
(fboundp 'evil-jump-item)
#'evil-jump-item)
(map! :i [tab]
`(menu-item "Evil insert smart tab" nil :filter
(lambda (cmd)
(cond
((or (doom-lookup-key [tab] overriding-terminal-local-map)
(doom-lookup-key (kbd "TAB") overriding-terminal-local-map))
cmd)
,@(when (modulep! :editor snippets)
'(((+yas-active-p)
#'yas-next-field-or-maybe-expand)
((yas-maybe-expand-abbrev-key-filter 'yas-expand)
#'yas-expand)))
,@(when (modulep! :completion company +tng)
'(((bound-and-true-p company-mode)
#'company-indent-or-complete-common)))
,@(when (modulep! :completion corfu)
'(((bound-and-true-p corfu-mode)
#'indent-for-tab-command))))))
:m [tab]
`(menu-item "Evil motion smart tab" nil :filter
(lambda (cmd)
(cond
((or (doom-lookup-key [tab] overriding-terminal-local-map)
(doom-lookup-key (kbd "TAB") overriding-terminal-local-map))
cmd)
,@(when (modulep! :editor snippets)
'(((and (evil-visual-state-p)
(or (eq evil-visual-selection 'line)
(not (memq (char-after)
(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
:n "o" #'link-hint-open-link)
@ -90,9 +116,9 @@
:n "o" #'link-hint-open-link)
(:unless (modulep! :input layout +bepo)
(:after (evil-org evil-easymotion)
:map evil-org-mode-map
:m "gsh" #'+org/goto-visible))
(:after (evil-org evil-easymotion)
:map evil-org-mode-map
:m "gsh" #'+org/goto-visible))
(:when (modulep! :editor multiple-cursors)
:prefix "gz"

View file

@ -0,0 +1,7 @@
;;; config/default/autoload/filters.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +yas-active-p ()
"Return t if we are in a YASnippet field."
(memq (bound-and-true-p yas--active-field-overlay)
(overlays-in (1- (point)) (1+ (point)))))

View file

@ -464,41 +464,72 @@ Continues comments if executed from a commented line. Consults
[remap corfu-insert-separator] #'+corfu-smart-sep-toggle-escape
"C-S-s" #'+corfu-move-to-minibuffer
"C-p" #'corfu-previous
"C-n" #'corfu-next
"S-TAB" #'corfu-previous
[backtab] #'corfu-previous
"TAB" #'corfu-next
[tab] #'corfu-next))
"C-n" #'corfu-next))
(let ((cmds-del
`(menu-item "Reset completion" corfu-reset
:filter ,(lambda (cmd)
(when (and (>= corfu--index 0)
(eq corfu-preview-current 'insert))
cmd))))
(cmds-ret
`(menu-item "Insert completion DWIM" corfu-insert
:filter ,(lambda (cmd)
(interactive)
(cond ((null +corfu-want-ret-to-confirm)
(corfu-quit)
nil)
((eq +corfu-want-ret-to-confirm 'minibuffer)
(funcall-interactively cmd)
nil)
((and (or (not (minibufferp nil t))
(eq +corfu-want-ret-to-confirm t))
(>= corfu--index 0))
cmd)
((or (not (minibufferp nil t))
(eq +corfu-want-ret-to-confirm t))
nil)
(t cmd))))))
(cond
((and (>= corfu--index 0)
(eq corfu-preview-current 'insert))
cmd)))))
(cmds-ret
`(menu-item "Insert completion DWIM" corfu-insert
:filter ,(lambda (cmd)
(cond
((null +corfu-want-ret-to-confirm)
(corfu-quit)
nil)
((eq +corfu-want-ret-to-confirm 'minibuffer)
(funcall-interactively cmd)
nil)
((and (or (not (minibufferp nil t))
(eq +corfu-want-ret-to-confirm t))
(>= corfu--index 0))
cmd)
((or (not (minibufferp nil t))
(eq +corfu-want-ret-to-confirm t))
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
(+yas-active-p))
#'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
(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
(+yas-active-p))
#'yas-prev-field)))
,@(when (modulep! :lang org)
'(((and +corfu-want-tab-prefer-navigating-org-tables
(org-at-table-p))
#'org-table-previous-field)))
(t cmd))))))
(map! :when (modulep! :completion corfu)
:map corfu-map
[backspace] cmds-del
"DEL" cmds-del
: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.
;; Pressing it again will send you to the true bol. Same goes for C-e, except