vertico-multiform.el (8983B)
1 ;;; vertico-multiform.el --- Configure Vertico in different forms per command -*- lexical-binding: t -*- 2 3 ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. 4 5 ;; Author: Daniel Mendler <mail@daniel-mendler.de> 6 ;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> 7 ;; Created: 2021 8 ;; Version: 1.8 9 ;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (vertico "1.8")) 10 ;; Homepage: https://github.com/minad/vertico 11 12 ;; This file is part of GNU Emacs. 13 14 ;; This program is free software: you can redistribute it and/or modify 15 ;; it under the terms of the GNU General Public License as published by 16 ;; the Free Software Foundation, either version 3 of the License, or 17 ;; (at your option) any later version. 18 19 ;; This program is distributed in the hope that it will be useful, 20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 ;; GNU General Public License for more details. 23 24 ;; You should have received a copy of the GNU General Public License 25 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 26 27 ;;; Commentary: 28 29 ;; This package is a Vertico extension for fine tuning the Vertico 30 ;; display and other minibuffer modes per command or completion 31 ;; category. For some commands you may want to use the 32 ;; `vertico-buffer' display and for completion categories like file 33 ;; you prefer the `vertico-grid-mode'. 34 ;; 35 ;; Example: 36 ;; 37 ;; (setq vertico-multiform-commands 38 ;; '((consult-line buffer) 39 ;; (consult-imenu reverse buffer) 40 ;; (execute-extended-command flat))) 41 ;; 42 ;; (setq vertico-multiform-categories 43 ;; '((file buffer grid) 44 ;; (imenu (:not indexed mouse)) 45 ;; (symbol (vertico-sort-function . vertico-sort-alpha)))) 46 ;; 47 ;; (vertico-multiform-mode) 48 ;; 49 ;; Temporary toggling between the different display modes is possible. 50 ;; The following keys are bound in the `vertico-multiform-map'. 51 ;; 52 ;; M-B -> `vertico-multiform-buffer' 53 ;; M-F -> `vertico-multiform-flat' 54 ;; M-G -> `vertico-multiform-grid' 55 ;; M-R -> `vertico-multiform-reverse' 56 ;; M-U -> `vertico-multiform-unobtrusive' 57 ;; M-V -> `vertico-multiform-vertical' 58 ;; 59 ;;; Code: 60 61 (require 'vertico) 62 (eval-when-compile (require 'cl-lib)) 63 64 (defcustom vertico-multiform-commands nil 65 "Alist of commands/regexps and list of settings to turn on per command. 66 Takes precedence over `vertico-multiform-categories'. A setting 67 can either be a mode symbol, a function, an inverted mode symbol 68 or function, or a cons cell of variable name and value. The key 69 t can be used to specify catch all/default settings. The value 70 of `this-command' is used as key for the lookup." 71 :group 'vertico 72 :type '(alist :key-type (choice symbol regexp (const t)) :value-type (repeat sexp))) 73 74 (defcustom vertico-multiform-categories nil 75 "Alist of categories/regexps and list of settings to turn on per category. 76 See `vertico-multiform-commands' on details about the settings. The 77 category settings have lower precedence than 78 `vertico-multiform-commands'." 79 :group 'vertico 80 :type '(alist :key-type (choice symbol regexp (const t)) :value-type (repeat sexp))) 81 82 (defvar vertico-multiform--stack nil) 83 84 (defun vertico-multiform--toggle (arg) 85 "Toggle modes from stack depending on ARG." 86 (when-let ((win (active-minibuffer-window)) 87 (modes (car vertico-multiform--stack))) 88 (when (> arg 0) (setq modes (reverse modes))) 89 (with-selected-window win 90 (dolist (m modes) 91 (if (eq (car-safe m) :not) 92 (funcall (cdr m) (- arg)) 93 (funcall m arg)))))) 94 95 (defun vertico-multiform--lookup (key list) 96 "Lookup symbolic KEY in LIST. 97 The keys in LIST can be symbols or regexps." 98 (and (symbolp key) 99 (let (case-fold-search) 100 (seq-find (pcase-lambda (`(,x . ,_)) 101 (cond 102 ((eq x t)) 103 ((symbolp x) (eq key x)) 104 ((string-match-p x (symbol-name key))))) 105 list)))) 106 107 (defun vertico-multiform--setup () 108 "Enable modes at minibuffer setup." 109 (let ((cat (completion-metadata-get 110 (completion-metadata (buffer-substring-no-properties 111 (minibuffer-prompt-end) 112 (max (minibuffer-prompt-end) (point))) 113 minibuffer-completion-table 114 minibuffer-completion-predicate) 115 'category)) 116 (exit (make-symbol "vertico-multiform--exit")) 117 (depth (recursion-depth)) 118 (modes nil)) 119 (fset exit (lambda () 120 (when (= depth (recursion-depth)) 121 (remove-hook 'minibuffer-exit-hook exit) 122 (vertico-multiform--toggle -1) 123 (pop vertico-multiform--stack)))) 124 (add-hook 'minibuffer-exit-hook exit) 125 (dolist (x (cdr (or (vertico-multiform--lookup this-command vertico-multiform-commands) 126 (vertico-multiform--lookup cat vertico-multiform-categories)))) 127 (pcase x 128 (`(:not . ,fs) 129 (dolist (f fs) 130 (let ((sym (and (symbolp f) (intern-soft (format "vertico-%s-mode" f))))) 131 (push (cons :not (if (and sym (fboundp sym)) sym f)) modes)))) 132 ((or (pred functionp) (pred symbolp)) 133 (let ((sym (and (symbolp x) (intern-soft (format "vertico-%s-mode" x))))) 134 (push (if (and sym (fboundp sym)) sym x) modes))) 135 (`(,k . ,v) (set (make-local-variable k) v)) 136 (_ (error "Invalid multiform setting %S" x)))) 137 (push modes vertico-multiform--stack) 138 (vertico-multiform--toggle 1) 139 (vertico--setup))) 140 141 (defvar-keymap vertico-multiform-map 142 :doc "Additional keymap activated in multiform mode.") 143 144 ;;;###autoload 145 (define-minor-mode vertico-multiform-mode 146 "Configure Vertico in various forms per command." 147 :global t :group 'vertico 148 (when (/= (recursion-depth) 0) 149 (warn "vertico-multiform must not be toggled from recursive minibuffers")) 150 (when vertico-multiform--stack 151 (warn "vertico-multiform state is inconsistent") 152 (setq vertico-multiform--stack nil)) 153 (cl-callf2 rassq-delete-all vertico-multiform-map minor-mode-map-alist) 154 (when vertico-multiform-mode 155 (push `(vertico--input . ,vertico-multiform-map) minor-mode-map-alist))) 156 157 (cl-defmethod vertico--advice (&context (vertico-multiform-mode (eql t)) &rest app) 158 (unwind-protect 159 (progn 160 (vertico-multiform--toggle -1) 161 (minibuffer-with-setup-hook #'vertico-multiform--setup 162 (apply app))) 163 (vertico-multiform--toggle 1))) 164 165 (defun vertico-multiform--temporary-mode (mode arg) 166 "Enable or disable MODE temporarily in minibuffer given ARG. 167 ARG can be nil, t, -1, 1 or toggle." 168 (unless (minibufferp) 169 (user-error "`%s' must be called inside the minibuffer" this-command)) 170 (unless vertico-multiform-mode 171 (user-error "`vertico-multiform-mode' is not enabled")) 172 (setq arg (pcase arg 173 ('toggle (not (and (boundp mode) (symbol-value mode)))) 174 ((or 'nil 't) arg) 175 (_ (> arg 0)))) 176 (unless (eq arg (and (boundp mode) (symbol-value mode))) 177 (funcall mode (if arg 1 -1)) 178 (let ((modes (car vertico-multiform--stack)) 179 (not-mode (cons :not mode))) 180 (when arg 181 (cl-rotatef not-mode mode)) 182 (if (member mode modes) 183 (setcar vertico-multiform--stack (remove mode modes)) 184 (push not-mode (car vertico-multiform--stack)))))) 185 186 (defvar vertico-multiform--display-modes nil) 187 (defvar-local vertico-multiform--display-last nil) 188 189 (defun vertico-multiform-vertical (&optional mode) 190 "Toggle to display MODE temporarily in minibuffer. 191 MODE defaults to the vertical display." 192 (interactive) 193 (let (last) 194 (dolist (m vertico-multiform--display-modes) 195 (when (and (boundp m) (symbol-value m)) 196 (setq last m) 197 (vertico-multiform--temporary-mode m -1))) 198 (when (eq last mode) 199 (setq mode vertico-multiform--display-last)) 200 (when mode 201 (vertico-multiform--temporary-mode mode 1)) 202 (setq vertico-multiform--display-last last))) 203 204 (pcase-dolist (`(,key ,name) '(("M-B" buffer) 205 ("M-F" flat) 206 ("M-G" grid) 207 ("M-R" reverse) 208 ("M-U" unobtrusive) ;; must come after flat 209 ("M-V" vertical))) 210 (let ((toggle (intern (format "vertico-multiform-%s" name)))) 211 (unless (eq name 'vertical) 212 (let ((mode (intern (format "vertico-%s-mode" name)))) 213 (defalias toggle 214 (lambda () (interactive) (vertico-multiform-vertical mode)) 215 (format "Toggle the %s display." name)) 216 (push mode vertico-multiform--display-modes))) 217 (put toggle 'completion-predicate #'vertico--command-p) 218 (keymap-set vertico-multiform-map key toggle))) 219 220 (provide 'vertico-multiform) 221 ;;; vertico-multiform.el ends here