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..936016ab9 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -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" 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..972012afe 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -464,41 +464,68 @@ 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)))))) + (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