From cfdae2365c7783382532096d4489609e16bc2a51 Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Wed, 20 Mar 2024 18:02:27 -0500 Subject: [PATCH] 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. --- modules/completion/corfu/README.org | 9 ++ modules/completion/corfu/config.el | 12 +++ modules/config/default/+evil-bindings.el | 98 ++++++++++++++-------- modules/config/default/autoload/filters.el | 7 ++ modules/config/default/config.el | 85 +++++++++++++------ 5 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 modules/config/default/autoload/filters.el diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index e10d1b17d..cf0e35a72 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -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 diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index 475894fa5..f50ac40be 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -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 diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index b07109ebe..874d054e8 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -38,38 +38,66 @@ ;;; 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) + (if (derived-mode-p 'eshell-mode 'comint-mode) + #'completion-at-point + #'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 +118,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" diff --git a/modules/config/default/autoload/filters.el b/modules/config/default/autoload/filters.el new file mode 100644 index 000000000..09e6d2323 --- /dev/null +++ b/modules/config/default/autoload/filters.el @@ -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))))) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index 21aa7268d..b92ee6405 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -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