From cfdae2365c7783382532096d4489609e16bc2a51 Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Wed, 20 Mar 2024 18:02:27 -0500 Subject: [PATCH 1/3] 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 From cfb860f71a8a9d2c15bd023bd74c8c75268553ac Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Fri, 12 Apr 2024 13:20:17 -0500 Subject: [PATCH 2/3] doc(corfu): update troubleshooting section --- modules/completion/corfu/README.org | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index cf0e35a72..fa8502d99 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -242,6 +242,8 @@ all CAPFs are interactive to be called this way, in which case you can use * Troubleshooting [[doom-report:][Report an issue?]] +** Performance issues with ~cape-dabbrev~ + 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: @@ -253,6 +255,34 @@ doing is to look at the list of buffers Dabbrev is scanning: ... and modify ~dabbrev-ignored-buffer-regexps~ or ~dabbrev-ignored-buffer-modes~ accordingly. +** 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 ~~ keybindings and insert state bindings are overriding your ~TAB~ key +assignments. + +In Evil mode, keybinding priorities are set such that: +1. ~~ 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 "" #'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 /This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] From 0a4d22e5b78c90d07bfbb0b587d93be91f2c489e Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Fri, 10 May 2024 22:08:32 -0500 Subject: [PATCH 3/3] fix(config): fix corfu smart tab behavior --- modules/config/default/autoload/filters.el | 7 ------- modules/config/default/config.el | 8 ++++++-- 2 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 modules/config/default/autoload/filters.el diff --git a/modules/config/default/autoload/filters.el b/modules/config/default/autoload/filters.el deleted file mode 100644 index 09e6d2323..000000000 --- a/modules/config/default/autoload/filters.el +++ /dev/null @@ -1,7 +0,0 @@ -;;; 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 b92ee6405..667b6abb0 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -496,13 +496,15 @@ Continues comments if executed from a commented line. Consults (cond ,@(when (modulep! :editor snippets) '(((and +corfu-want-tab-prefer-navigating-snippets - (+yas-active-p)) + (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)))) ) @@ -513,10 +515,12 @@ Continues comments if executed from a commented line. Consults (cond ,@(when (modulep! :editor snippets) '(((and +corfu-want-tab-prefer-navigating-snippets - (+yas-active-p)) + (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))))))