transient.el (183243B)
1 ;;; transient.el --- Transient commands -*- lexical-binding:t -*- 2 3 ;; Copyright (C) 2018-2024 Free Software Foundation, Inc. 4 5 ;; Author: Jonas Bernoulli <emacs.transient@jonas.bernoulli.dev> 6 ;; Homepage: https://github.com/magit/transient 7 ;; Keywords: extensions 8 9 ;; Package-Version: 0.7.1 10 ;; Package-Requires: ((emacs "26.1") (compat "29.1.4.5") (seq "2.24")) 11 12 ;; SPDX-License-Identifier: GPL-3.0-or-later 13 14 ;; This file is part of GNU Emacs. 15 16 ;; GNU Emacs is free software: you can redistribute it and/or modify 17 ;; it under the terms of the GNU General Public License as published 18 ;; by the Free Software Foundation, either version 3 of the License, 19 ;; or (at your option) any later version. 20 ;; 21 ;; GNU Emacs is distributed in the hope that it will be useful, 22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 ;; GNU General Public License for more details. 25 ;; 26 ;; You should have received a copy of the GNU General Public License 27 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 28 29 ;;; Commentary: 30 31 ;; Transient is the library used to implement the keyboard-driven menus 32 ;; in Magit. It is distributed as a separate package, so that it can be 33 ;; used to implement similar menus in other packages. 34 35 ;;; Code: 36 37 (require 'cl-lib) 38 (require 'compat) 39 (require 'eieio) 40 (require 'edmacro) 41 (require 'format-spec) 42 43 (eval-and-compile 44 (when (and (featurep 'seq) 45 (not (fboundp 'seq-keep))) 46 (unload-feature 'seq 'force))) 47 (require 'seq) 48 (unless (fboundp 'seq-keep) 49 (display-warning 'transient (substitute-command-keys "\ 50 Transient requires `seq' >= 2.24, 51 but due to bad defaults, Emacs' package manager, refuses to 52 upgrade this and other built-in packages to higher releases 53 from GNU Elpa, when a package specifies that this is needed. 54 55 To fix this, you have to add this to your init file: 56 57 (setq package-install-upgrade-built-in t) 58 59 Then evaluate that expression by placing the cursor after it 60 and typing \\[eval-last-sexp]. 61 62 Once you have done that, you have to explicitly upgrade `seq': 63 64 \\[package-upgrade] seq \\`RET' 65 66 Then you also must make sure the updated version is loaded, 67 by evaluating this form: 68 69 (progn (unload-feature 'seq t) (require 'seq)) 70 71 Until you do this, you will get random errors about `seq-keep' 72 being undefined while using Transient. 73 74 If you don't use the `package' package manager but still get 75 this warning, then your chosen package manager likely has a 76 similar defect.") :emergency)) 77 78 (eval-when-compile (require 'subr-x)) 79 80 (declare-function info "info" (&optional file-or-node buffer)) 81 (declare-function Man-find-section "man" (section)) 82 (declare-function Man-next-section "man" (n)) 83 (declare-function Man-getpage-in-background "man" (topic)) 84 85 (defvar Man-notify-method) 86 (defvar pp-default-function) ; since Emacs 29.1 87 88 (defmacro static-if (condition then-form &rest else-forms) 89 "A conditional compilation macro. 90 Evaluate CONDITION at macro-expansion time. If it is non-nil, 91 expand the macro to THEN-FORM. Otherwise expand it to ELSE-FORMS 92 enclosed in a `progn' form. ELSE-FORMS may be empty." 93 (declare (indent 2) 94 (debug (sexp sexp &rest sexp))) 95 (if (eval condition lexical-binding) 96 then-form 97 (cons 'progn else-forms))) 98 99 (defmacro transient--with-emergency-exit (id &rest body) 100 (declare (indent defun)) 101 (unless (keywordp id) 102 (setq body (cons id body)) 103 (setq id nil)) 104 `(condition-case err 105 (let ((debugger #'transient--exit-and-debug)) 106 ,(macroexp-progn body)) 107 ((debug error) 108 (transient--emergency-exit ,id) 109 (signal (car err) (cdr err))))) 110 111 (defun transient--exit-and-debug (&rest args) 112 (transient--emergency-exit :debugger) 113 (apply #'debug args)) 114 115 ;;; Options 116 117 (defgroup transient nil 118 "Transient commands." 119 :group 'extensions) 120 121 (defcustom transient-show-popup t 122 "Whether to show the current transient in a popup buffer. 123 \\<transient-map> 124 - If t, then show the popup as soon as a transient prefix command 125 is invoked. 126 127 - If nil, then do not show the popup unless the user explicitly 128 requests it, by pressing \\[transient-show] or a prefix key. 129 130 - If a number, then delay displaying the popup and instead show 131 a brief one-line summary. If zero or negative, then suppress 132 even showing that summary and display the pressed key only. 133 134 Show the popup when the user explicitly requests it by pressing 135 \\[transient-show] or a prefix key. Unless zero, then also show the popup 136 after that many seconds of inactivity (using the absolute value)." 137 :package-version '(transient . "0.1.0") 138 :group 'transient 139 :type '(choice (const :tag "instantly" t) 140 (const :tag "on demand" nil) 141 (const :tag "on demand (no summary)" 0) 142 (number :tag "after delay" 1))) 143 144 (defcustom transient-enable-popup-navigation t 145 "Whether navigation commands are enabled in the transient popup. 146 147 While a transient is active the transient popup buffer is not the 148 current buffer, making it necessary to use dedicated commands to 149 act on that buffer itself. If this is non-nil, then the following 150 bindings are available: 151 152 \\<transient-popup-navigation-map>\ 153 - \\[transient-backward-button] moves the cursor to the previous suffix. 154 - \\[transient-forward-button] moves the cursor to the next suffix. 155 - \\[transient-push-button] invokes the suffix the cursor is on. 156 \\<transient-button-map>\ 157 - \\`<mouse-1>' and \\`<mouse-2>' invoke the clicked on suffix. 158 \\<transient-popup-navigation-map>\ 159 - \\[transient-isearch-backward]\ 160 and \\[transient-isearch-forward] start isearch in the popup buffer. 161 162 \\`<mouse-1>' and \\`<mouse-2>' are bound in `transient-push-button'. 163 All other bindings are in `transient-popup-navigation-map'. 164 165 By default \\`M-RET' is bound to `transient-push-button', instead of 166 \\`RET', because if a transient allows the invocation of non-suffixes 167 then it is likely that you would want \\`RET' to do what it would do 168 if no transient were active." 169 :package-version '(transient . "0.4.0") 170 :group 'transient 171 :type 'boolean) 172 173 (defcustom transient-display-buffer-action 174 '(display-buffer-in-side-window 175 (side . bottom) 176 (dedicated . t) 177 (inhibit-same-window . t) 178 (window-parameters (no-other-window . t))) 179 "The action used to display the transient popup buffer. 180 181 The transient popup buffer is displayed in a window using 182 183 (display-buffer BUFFER transient-display-buffer-action) 184 185 The value of this option has the form (FUNCTION . ALIST), 186 where FUNCTION is a function or a list of functions. Each such 187 function should accept two arguments: a buffer to display and an 188 alist of the same form as ALIST. See info node `(elisp)Choosing 189 Window' for details. 190 191 The default is: 192 193 (display-buffer-in-side-window 194 (side . bottom) 195 (dedicated . t) 196 (inhibit-same-window . t) 197 (window-parameters (no-other-window . t))) 198 199 This displays the window at the bottom of the selected frame. 200 Another useful FUNCTION is `display-buffer-below-selected', which 201 is what `magit-popup' used by default. For more alternatives see 202 info node `(elisp)Display Action Functions' and info node 203 `(elisp)Buffer Display Action Alists'. 204 205 Note that the buffer that was current before the transient buffer 206 is shown should remain the current buffer. Many suffix commands 207 act on the thing at point, if appropriate, and if the transient 208 buffer became the current buffer, then that would change what is 209 at point. To that effect `inhibit-same-window' ensures that the 210 selected window is not used to show the transient buffer. 211 212 It may be possible to display the window in another frame, but 213 whether that works in practice depends on the window-manager. 214 If the window manager selects the new window (Emacs frame), 215 then that unfortunately changes which buffer is current. 216 217 If you change the value of this option, then you might also 218 want to change the value of `transient-mode-line-format'." 219 :package-version '(transient . "0.3.0") 220 :group 'transient 221 :type '(cons (choice function (repeat :tag "Functions" function)) 222 alist)) 223 224 (defcustom transient-mode-line-format 'line 225 "The mode-line format for the transient popup buffer. 226 227 If nil, then the buffer has no mode-line. If the buffer is not 228 displayed right above the echo area, then this probably is not 229 a good value. 230 231 If `line' (the default) or a natural number, then the buffer 232 has no mode-line, but a line is drawn is drawn in its place. 233 If a number is used, that specifies the thickness of the line. 234 On termcap frames we cannot draw lines, so there `line' and 235 numbers are synonyms for nil. 236 237 The color of the line is used to indicate if non-suffixes are 238 allowed and whether they exit the transient. The foreground 239 color of `transient-key-noop' (if non-suffix are disallowed), 240 `transient-key-stay' (if allowed and transient stays active), or 241 `transient-key-exit' (if allowed and they exit the transient) is 242 used to draw the line. 243 244 Otherwise this can be any mode-line format. 245 See `mode-line-format' for details." 246 :package-version '(transient . "0.2.0") 247 :group 'transient 248 :type '(choice (const :tag "hide mode-line" nil) 249 (const :tag "substitute thin line" line) 250 (number :tag "substitute line with thickness") 251 (const :tag "name of prefix command" 252 ("%e" mode-line-front-space 253 mode-line-buffer-identification)) 254 (sexp :tag "custom mode-line format"))) 255 256 (defcustom transient-show-common-commands nil 257 "Whether to show common transient suffixes in the popup buffer. 258 259 These commands are always shown after typing the prefix key 260 \"C-x\" when a transient command is active. To toggle the value 261 of this variable use \"C-x t\" when a transient is active." 262 :package-version '(transient . "0.1.0") 263 :group 'transient 264 :type 'boolean) 265 266 (defcustom transient-read-with-initial-input nil 267 "Whether to use the last history element as initial minibuffer input." 268 :package-version '(transient . "0.2.0") 269 :group 'transient 270 :type 'boolean) 271 272 (defcustom transient-highlight-mismatched-keys nil 273 "Whether to highlight keys that do not match their argument. 274 275 This only affects infix arguments that represent command-line 276 arguments. When this option is non-nil, then the key binding 277 for infix argument are highlighted when only a long argument 278 \(e.g., \"--verbose\") is specified but no shorthand (e.g., \"-v\"). 279 In the rare case that a short-hand is specified but does not 280 match the key binding, then it is highlighted differently. 281 282 The highlighting is done using `transient-mismatched-key' 283 and `transient-nonstandard-key'." 284 :package-version '(transient . "0.1.0") 285 :group 'transient 286 :type 'boolean) 287 288 (defcustom transient-highlight-higher-levels nil 289 "Whether to highlight suffixes on higher levels. 290 291 This is primarily intended for package authors. 292 293 When non-nil then highlight the description of suffixes whose 294 level is above 4, the default of `transient-default-level'. 295 Assuming you have set that variable to 7, this highlights all 296 suffixes that won't be available to users without them making 297 the same customization." 298 :package-version '(transient . "0.3.6") 299 :group 'transient 300 :type 'boolean) 301 302 (defcustom transient-substitute-key-function nil 303 "Function used to modify key bindings. 304 305 This function is called with one argument, the prefix object, 306 and must return a key binding description, either the existing 307 key description it finds in the `key' slot, or a substitution. 308 309 This is intended to let users replace certain prefix keys. It 310 could also be used to make other substitutions, but that is 311 discouraged. 312 313 For example, \"=\" is hard to reach using my custom keyboard 314 layout, so I substitute \"(\" for that, which is easy to reach 315 using a layout optimized for Lisp. 316 317 (setq transient-substitute-key-function 318 (lambda (obj) 319 (let ((key (oref obj key))) 320 (if (string-match \"\\\\`\\\\(=\\\\)[a-zA-Z]\" key) 321 (replace-match \"(\" t t key 1) 322 key)))))" 323 :package-version '(transient . "0.1.0") 324 :group 'transient 325 :type '(choice (const :tag "Transform no keys (nil)" nil) function)) 326 327 (defcustom transient-semantic-coloring t 328 "Whether to use colors to indicate transient behavior. 329 330 If non-nil, then the key binding of each suffix is colorized to 331 indicate whether it exits the transient state or not, and the 332 line that is drawn below the transient popup buffer is used to 333 indicate the behavior of non-suffix commands." 334 :package-version '(transient . "0.5.0") 335 :group 'transient 336 :type 'boolean) 337 338 (defcustom transient-detect-key-conflicts nil 339 "Whether to detect key binding conflicts. 340 341 Conflicts are detected when a transient prefix command is invoked 342 and results in an error, which prevents the transient from being 343 used." 344 :package-version '(transient . "0.1.0") 345 :group 'transient 346 :type 'boolean) 347 348 (defcustom transient-align-variable-pitch nil 349 "Whether to align columns pixel-wise in the popup buffer. 350 351 If this is non-nil, then columns are aligned pixel-wise to 352 support variable-pitch fonts. Keys are not aligned, so you 353 should use a fixed-pitch font for the `transient-key' face. 354 Other key faces inherit from that face unless a theme is 355 used that breaks that relationship. 356 357 This option is intended for users who use a variable-pitch 358 font for the `default' face. 359 360 Also see `transient-force-fixed-pitch'." 361 :package-version '(transient . "0.4.0") 362 :group 'transient 363 :type 'boolean) 364 365 (defcustom transient-force-fixed-pitch nil 366 "Whether to force use of monospaced font in the popup buffer. 367 368 Even if you use a proportional font for the `default' face, 369 you might still want to use a monospaced font in transient's 370 popup buffer. Setting this option to t causes `default' to 371 be remapped to `fixed-pitch' in that buffer. 372 373 Also see `transient-align-variable-pitch'." 374 :package-version '(transient . "0.2.0") 375 :group 'transient 376 :type 'boolean) 377 378 (defcustom transient-force-single-column nil 379 "Whether to force use of a single column to display suffixes. 380 381 This might be useful for users with low vision who use large 382 text and might otherwise have to scroll in two dimensions." 383 :package-version '(transient . "0.3.6") 384 :group 'transient 385 :type 'boolean) 386 387 (defcustom transient-hide-during-minibuffer-read nil 388 "Whether to hide the transient buffer while reading in the minibuffer." 389 :package-version '(transient . "0.4.0") 390 :group 'transient 391 :type 'boolean) 392 393 (defconst transient--max-level 7) 394 (defconst transient--default-child-level 1) 395 (defconst transient--default-prefix-level 4) 396 397 (defcustom transient-default-level transient--default-prefix-level 398 "Control what suffix levels are made available by default. 399 400 Each suffix command is placed on a level and each prefix command 401 has a level, which controls which suffix commands are available. 402 Integers between 1 and 7 (inclusive) are valid levels. 403 404 The levels of individual transients and/or their individual 405 suffixes can be changed individually, by invoking the prefix and 406 then pressing \"C-x l\". 407 408 The default level for both transients and their suffixes is 4. 409 This option only controls the default for transients. The default 410 suffix level is always 4. The author of a transient should place 411 certain suffixes on a higher level if they expect that it won't be 412 of use to most users, and they should place very important suffixes 413 on a lower level so that they remain available even if the user 414 lowers the transient level. 415 416 \(Magit currently places nearly all suffixes on level 4 and lower 417 levels are not used at all yet. So for the time being you should 418 not set a lower level here and using a higher level might not 419 give you as many additional suffixes as you hoped.)" 420 :package-version '(transient . "0.1.0") 421 :group 'transient 422 :type '(choice (const :tag "1 - fewest suffixes" 1) 423 (const 2) 424 (const 3) 425 (const :tag "4 - default" 4) 426 (const 5) 427 (const 6) 428 (const :tag "7 - most suffixes" 7))) 429 430 (defcustom transient-levels-file 431 (locate-user-emacs-file "transient/levels.el") 432 "File used to save levels of transients and their suffixes." 433 :package-version '(transient . "0.1.0") 434 :group 'transient 435 :type 'file) 436 437 (defcustom transient-values-file 438 (locate-user-emacs-file "transient/values.el") 439 "File used to save values of transients." 440 :package-version '(transient . "0.1.0") 441 :group 'transient 442 :type 'file) 443 444 (defcustom transient-history-file 445 (locate-user-emacs-file "transient/history.el") 446 "File used to save history of transients and their infixes." 447 :package-version '(transient . "0.1.0") 448 :group 'transient 449 :type 'file) 450 451 (defcustom transient-history-limit 10 452 "Number of history elements to keep when saving to file." 453 :package-version '(transient . "0.1.0") 454 :group 'transient 455 :type 'integer) 456 457 (defcustom transient-save-history t 458 "Whether to save history of transient commands when exiting Emacs." 459 :package-version '(transient . "0.1.0") 460 :group 'transient 461 :type 'boolean) 462 463 ;;; Faces 464 465 (defgroup transient-faces nil 466 "Faces used by Transient." 467 :group 'transient) 468 469 (defface transient-heading '((t :inherit font-lock-keyword-face)) 470 "Face used for headings." 471 :group 'transient-faces) 472 473 (defface transient-argument '((t :inherit font-lock-string-face :weight bold)) 474 "Face used for enabled arguments." 475 :group 'transient-faces) 476 477 (defface transient-inactive-argument '((t :inherit shadow)) 478 "Face used for inactive arguments." 479 :group 'transient-faces) 480 481 (defface transient-value '((t :inherit font-lock-string-face :weight bold)) 482 "Face used for values." 483 :group 'transient-faces) 484 485 (defface transient-inactive-value '((t :inherit shadow)) 486 "Face used for inactive values." 487 :group 'transient-faces) 488 489 (defface transient-unreachable '((t :inherit shadow)) 490 "Face used for suffixes unreachable from the current prefix sequence." 491 :group 'transient-faces) 492 493 (defface transient-inapt-suffix '((t :inherit shadow :italic t)) 494 "Face used for suffixes that are inapt at this time." 495 :group 'transient-faces) 496 497 (defface transient-active-infix '((t :inherit highlight)) 498 "Face used for the infix for which the value is being read." 499 :group 'transient-faces) 500 501 (defface transient-enabled-suffix 502 '((t :background "green" :foreground "black" :weight bold)) 503 "Face used for enabled levels while editing suffix levels. 504 See info node `(transient)Enabling and Disabling Suffixes'." 505 :group 'transient-faces) 506 507 (defface transient-disabled-suffix 508 '((t :background "red" :foreground "black" :weight bold)) 509 "Face used for disabled levels while editing suffix levels. 510 See info node `(transient)Enabling and Disabling Suffixes'." 511 :group 'transient-faces) 512 513 (defface transient-higher-level 514 `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) 515 :color ,(let ((color (face-attribute 'shadow :foreground nil t))) 516 (or (and (not (eq color 'unspecified)) color) 517 "grey60"))))) 518 "Face optionally used to highlight suffixes on higher levels. 519 Also see option `transient-highlight-higher-levels'." 520 :group 'transient-faces) 521 522 (defface transient-delimiter '((t :inherit shadow)) 523 "Face used for delimiters and separators. 524 This includes the parentheses around values and the pipe 525 character used to separate possible values from each other." 526 :group 'transient-faces) 527 528 (defface transient-key '((t :inherit font-lock-builtin-face)) 529 "Face used for keys." 530 :group 'transient-faces) 531 532 (defface transient-key-stay 533 `((((class color) (background light)) 534 :inherit transient-key 535 :foreground "#22aa22") 536 (((class color) (background dark)) 537 :inherit transient-key 538 :foreground "#ddffdd")) 539 "Face used for keys of suffixes that don't exit transient state." 540 :group 'transient-faces) 541 542 (defface transient-key-noop 543 `((((class color) (background light)) 544 :inherit transient-key 545 :foreground "grey80") 546 (((class color) (background dark)) 547 :inherit transient-key 548 :foreground "grey30")) 549 "Face used for keys of suffixes that currently cannot be invoked." 550 :group 'transient-faces) 551 552 (defface transient-key-return 553 `((((class color) (background light)) 554 :inherit transient-key 555 :foreground "#aaaa11") 556 (((class color) (background dark)) 557 :inherit transient-key 558 :foreground "#ffffcc")) 559 "Face used for keys of suffixes that return to the parent transient." 560 :group 'transient-faces) 561 562 (defface transient-key-exit 563 `((((class color) (background light)) 564 :inherit transient-key 565 :foreground "#aa2222") 566 (((class color) (background dark)) 567 :inherit transient-key 568 :foreground "#ffdddd")) 569 "Face used for keys of suffixes that exit transient state." 570 :group 'transient-faces) 571 572 (defface transient-unreachable-key 573 '((t :inherit (shadow transient-key) :weight normal)) 574 "Face used for keys unreachable from the current prefix sequence." 575 :group 'transient-faces) 576 577 (defface transient-nonstandard-key 578 `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) 579 :color "cyan"))) 580 "Face optionally used to highlight keys conflicting with short-argument. 581 Also see option `transient-highlight-mismatched-keys'." 582 :group 'transient-faces) 583 584 (defface transient-mismatched-key 585 `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) 586 :color "magenta"))) 587 "Face optionally used to highlight keys without a short-argument. 588 Also see option `transient-highlight-mismatched-keys'." 589 :group 'transient-faces) 590 591 ;;; Persistence 592 593 (defun transient--read-file-contents (file) 594 (with-demoted-errors "Transient error: %S" 595 (and (file-exists-p file) 596 (with-temp-buffer 597 (insert-file-contents file) 598 (read (current-buffer)))))) 599 600 (defun transient--pp-to-file (list file) 601 (make-directory (file-name-directory file) t) 602 (setq list (cl-sort (copy-sequence list) #'string< :key #'car)) 603 (with-temp-file file 604 (let ((print-level nil) 605 (print-length nil) 606 (pp-default-function 'pp-28) 607 (fill-column 999)) 608 (pp list (current-buffer))))) 609 610 (defvar transient-values 611 (transient--read-file-contents transient-values-file) 612 "Values of transient commands. 613 The value of this variable persists between Emacs sessions 614 and you usually should not change it manually.") 615 616 (defun transient-save-values () 617 (transient--pp-to-file transient-values transient-values-file)) 618 619 (defvar transient-levels 620 (transient--read-file-contents transient-levels-file) 621 "Levels of transient commands. 622 The value of this variable persists between Emacs sessions 623 and you usually should not change it manually.") 624 625 (defun transient-save-levels () 626 (transient--pp-to-file transient-levels transient-levels-file)) 627 628 (defvar transient-history 629 (transient--read-file-contents transient-history-file) 630 "History of transient commands and infix arguments. 631 The value of this variable persists between Emacs sessions 632 \(unless `transient-save-history' is nil) and you usually 633 should not change it manually.") 634 635 (defun transient-save-history () 636 (setq transient-history 637 (cl-sort (mapcar (pcase-lambda (`(,key . ,val)) 638 (cons key (seq-take (delete-dups val) 639 transient-history-limit))) 640 transient-history) 641 #'string< :key #'car)) 642 (transient--pp-to-file transient-history transient-history-file)) 643 644 (defun transient-maybe-save-history () 645 "Save the value of `transient-history'. 646 If `transient-save-history' is nil, then do nothing." 647 (when transient-save-history 648 (transient-save-history))) 649 650 (unless noninteractive 651 (add-hook 'kill-emacs-hook #'transient-maybe-save-history)) 652 653 ;;; Classes 654 ;;;; Prefix 655 656 (defclass transient-prefix () 657 ((prototype :initarg :prototype) 658 (command :initarg :command) 659 (level :initarg :level) 660 (variable :initarg :variable :initform nil) 661 (init-value :initarg :init-value) 662 (value) (default-value :initarg :value) 663 (scope :initarg :scope :initform nil) 664 (history :initarg :history :initform nil) 665 (history-pos :initarg :history-pos :initform 0) 666 (history-key :initarg :history-key :initform nil) 667 (show-help :initarg :show-help :initform nil) 668 (info-manual :initarg :info-manual :initform nil) 669 (man-page :initarg :man-page :initform nil) 670 (transient-suffix :initarg :transient-suffix :initform nil) 671 (transient-non-suffix :initarg :transient-non-suffix :initform nil) 672 (transient-switch-frame :initarg :transient-switch-frame) 673 (refresh-suffixes :initarg :refresh-suffixes :initform nil) 674 (incompatible :initarg :incompatible :initform nil) 675 (suffix-description :initarg :suffix-description) 676 (variable-pitch :initarg :variable-pitch :initform nil) 677 (column-widths :initarg :column-widths :initform nil) 678 (unwind-suffix :documentation "Internal use." :initform nil)) 679 "Transient prefix command. 680 681 Each transient prefix command consists of a command, which is 682 stored in a symbol's function slot and an object, which is 683 stored in the `transient--prefix' property of the same symbol. 684 685 When a transient prefix command is invoked, then a clone of that 686 object is stored in the global variable `transient--prefix' and 687 the prototype is stored in the clone's `prototype' slot.") 688 689 ;;;; Suffix 690 691 (defclass transient-child () 692 ((level 693 :initarg :level 694 :initform (symbol-value 'transient--default-child-level) 695 :documentation "Enable if level of prefix is equal or greater.") 696 (if 697 :initarg :if 698 :initform nil 699 :documentation "Enable if predicate returns non-nil.") 700 (if-not 701 :initarg :if-not 702 :initform nil 703 :documentation "Enable if predicate returns nil.") 704 (if-non-nil 705 :initarg :if-non-nil 706 :initform nil 707 :documentation "Enable if variable's value is non-nil.") 708 (if-nil 709 :initarg :if-nil 710 :initform nil 711 :documentation "Enable if variable's value is nil.") 712 (if-mode 713 :initarg :if-mode 714 :initform nil 715 :documentation "Enable if major-mode matches value.") 716 (if-not-mode 717 :initarg :if-not-mode 718 :initform nil 719 :documentation "Enable if major-mode does not match value.") 720 (if-derived 721 :initarg :if-derived 722 :initform nil 723 :documentation "Enable if major-mode derives from value.") 724 (if-not-derived 725 :initarg :if-not-derived 726 :initform nil 727 :documentation "Enable if major-mode does not derive from value.") 728 (inapt 729 :initform nil) 730 (inapt-face 731 :initarg :inapt-face 732 :initform 'transient-inapt-suffix) 733 (inapt-if 734 :initarg :inapt-if 735 :initform nil 736 :documentation "Inapt if predicate returns non-nil.") 737 (inapt-if-not 738 :initarg :inapt-if-not 739 :initform nil 740 :documentation "Inapt if predicate returns nil.") 741 (inapt-if-non-nil 742 :initarg :inapt-if-non-nil 743 :initform nil 744 :documentation "Inapt if variable's value is non-nil.") 745 (inapt-if-nil 746 :initarg :inapt-if-nil 747 :initform nil 748 :documentation "Inapt if variable's value is nil.") 749 (inapt-if-mode 750 :initarg :inapt-if-mode 751 :initform nil 752 :documentation "Inapt if major-mode matches value.") 753 (inapt-if-not-mode 754 :initarg :inapt-if-not-mode 755 :initform nil 756 :documentation "Inapt if major-mode does not match value.") 757 (inapt-if-derived 758 :initarg :inapt-if-derived 759 :initform nil 760 :documentation "Inapt if major-mode derives from value.") 761 (inapt-if-not-derived 762 :initarg :inapt-if-not-derived 763 :initform nil 764 :documentation "Inapt if major-mode does not derive from value.")) 765 "Abstract superclass for group and suffix classes. 766 767 It is undefined what happens if more than one `if*' predicate 768 slot is non-nil." 769 :abstract t) 770 771 (defclass transient-suffix (transient-child) 772 ((definition :allocation :class :initform nil) 773 (key :initarg :key) 774 (command :initarg :command) 775 (transient :initarg :transient) 776 (format :initarg :format :initform " %k %d") 777 (description :initarg :description :initform nil) 778 (face :initarg :face :initform nil) 779 (show-help :initarg :show-help :initform nil)) 780 "Superclass for suffix command.") 781 782 (defclass transient-information (transient-suffix) 783 ((format :initform " %k %d") 784 (key :initform " ")) 785 "Display-only information, aligned with suffix keys. 786 Technically a suffix object with no associated command.") 787 788 (defclass transient-information* (transient-information) 789 ((format :initform " %d")) 790 "Display-only information, aligned with suffix descriptions. 791 Technically a suffix object with no associated command.") 792 793 (defclass transient-infix (transient-suffix) 794 ((transient :initform t) 795 (argument :initarg :argument) 796 (shortarg :initarg :shortarg) 797 (value :initform nil) 798 (init-value :initarg :init-value) 799 (unsavable :initarg :unsavable :initform nil) 800 (multi-value :initarg :multi-value :initform nil) 801 (always-read :initarg :always-read :initform nil) 802 (allow-empty :initarg :allow-empty :initform nil) 803 (history-key :initarg :history-key :initform nil) 804 (reader :initarg :reader :initform nil) 805 (prompt :initarg :prompt :initform nil) 806 (choices :initarg :choices :initform nil) 807 (format :initform " %k %d (%v)")) 808 "Transient infix command." 809 :abstract t) 810 811 (defclass transient-argument (transient-infix) () 812 "Abstract superclass for infix arguments." 813 :abstract t) 814 815 (defclass transient-switch (transient-argument) () 816 "Class used for command-line argument that can be turned on and off.") 817 818 (defclass transient-option (transient-argument) () 819 "Class used for command-line argument that can take a value.") 820 821 (defclass transient-variable (transient-infix) 822 ((variable :initarg :variable) 823 (format :initform " %k %d %v")) 824 "Abstract superclass for infix commands that set a variable." 825 :abstract t) 826 827 (defclass transient-switches (transient-argument) 828 ((argument-format :initarg :argument-format) 829 (argument-regexp :initarg :argument-regexp)) 830 "Class used for sets of mutually exclusive command-line switches.") 831 832 (defclass transient-files (transient-option) () 833 ((key :initform "--") 834 (argument :initform "--") 835 (multi-value :initform rest) 836 (reader :initform transient-read-files)) 837 "Class used for the \"--\" argument or similar. 838 All remaining arguments are treated as files. 839 They become the value of this argument.") 840 841 (defclass transient-value-preset (transient-suffix) 842 ((transient :initform t) 843 (set :initarg := :initform nil)) 844 "Class used by the `transient-preset' suffix command.") 845 846 ;;;; Group 847 848 (defclass transient-group (transient-child) 849 ((suffixes :initarg :suffixes :initform nil) 850 (hide :initarg :hide :initform nil) 851 (description :initarg :description :initform nil) 852 (pad-keys :initarg :pad-keys :initform nil) 853 (info-format :initarg :info-format :initform nil) 854 (setup-children :initarg :setup-children)) 855 "Abstract superclass of all group classes." 856 :abstract t) 857 858 (defclass transient-column (transient-group) () 859 "Group class that displays each element on a separate line.") 860 861 (defclass transient-row (transient-group) () 862 "Group class that displays all elements on a single line.") 863 864 (defclass transient-columns (transient-group) () 865 "Group class that displays elements organized in columns. 866 Direct elements have to be groups whose elements have to be 867 commands or strings. Each subgroup represents a column. 868 This class takes care of inserting the subgroups' elements.") 869 870 (defclass transient-subgroups (transient-group) () 871 "Group class that wraps other groups. 872 873 Direct elements have to be groups whose elements have to be 874 commands or strings. This group inserts an empty line between 875 subgroups. The subgroups are responsible for displaying their 876 elements themselves.") 877 878 ;;; Define 879 880 (defmacro transient-define-prefix (name arglist &rest args) 881 "Define NAME as a transient prefix command. 882 883 ARGLIST are the arguments that command takes. 884 DOCSTRING is the documentation string and is optional. 885 886 These arguments can optionally be followed by key-value pairs. 887 Each key has to be a keyword symbol, either `:class' or a keyword 888 argument supported by the constructor of that class. The 889 `transient-prefix' class is used if the class is not specified 890 explicitly. 891 892 GROUPs add key bindings for infix and suffix commands and specify 893 how these bindings are presented in the popup buffer. At least 894 one GROUP has to be specified. See info node `(transient)Binding 895 Suffix and Infix Commands'. 896 897 The BODY is optional. If it is omitted, then ARGLIST is also 898 ignored and the function definition becomes: 899 900 (lambda () 901 (interactive) 902 (transient-setup \\='NAME)) 903 904 If BODY is specified, then it must begin with an `interactive' 905 form that matches ARGLIST, and it must call `transient-setup'. 906 It may however call that function only when some condition is 907 satisfied; that is one of the reason why you might want to use 908 an explicit BODY. 909 910 All transients have a (possibly nil) value, which is exported 911 when suffix commands are called, so that they can consume that 912 value. For some transients it might be necessary to have a sort 913 of secondary value, called a scope. Such a scope would usually 914 be set in the commands `interactive' form and has to be passed 915 to the setup function: 916 917 (transient-setup \\='NAME nil nil :scope SCOPE) 918 919 \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... GROUP... [BODY...])" 920 (declare (debug ( &define name lambda-list 921 [&optional lambda-doc] 922 [&rest keywordp sexp] 923 [&rest vectorp] 924 [&optional ("interactive" interactive) def-body])) 925 (indent defun) 926 (doc-string 3)) 927 (pcase-let 928 ((`(,class ,slots ,suffixes ,docstr ,body ,interactive-only) 929 (transient--expand-define-args args arglist 'transient-define-prefix))) 930 `(progn 931 (defalias ',name 932 ,(if body 933 `(lambda ,arglist ,@body) 934 `(lambda () 935 (interactive) 936 (transient-setup ',name)))) 937 (put ',name 'interactive-only ,interactive-only) 938 (put ',name 'function-documentation ,docstr) 939 (put ',name 'transient--prefix 940 (,(or class 'transient-prefix) :command ',name ,@slots)) 941 (put ',name 'transient--layout 942 (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s)) 943 suffixes)))))) 944 945 (defmacro transient-define-suffix (name arglist &rest args) 946 "Define NAME as a transient suffix command. 947 948 ARGLIST are the arguments that the command takes. 949 DOCSTRING is the documentation string and is optional. 950 951 These arguments can optionally be followed by key-value pairs. 952 Each key has to be a keyword symbol, either `:class' or a 953 keyword argument supported by the constructor of that class. 954 The `transient-suffix' class is used if the class is not 955 specified explicitly. 956 957 The BODY must begin with an `interactive' form that matches 958 ARGLIST. The infix arguments are usually accessed by using 959 `transient-args' inside `interactive'. 960 961 \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... [BODY...])" 962 (declare (debug ( &define name lambda-list 963 [&optional lambda-doc] 964 [&rest keywordp sexp] 965 [&optional ("interactive" interactive) def-body])) 966 (indent defun) 967 (doc-string 3)) 968 (pcase-let 969 ((`(,class ,slots ,_ ,docstr ,body ,interactive-only) 970 (transient--expand-define-args args arglist 'transient-define-suffix))) 971 `(progn 972 (defalias ',name 973 ,(if (and (not body) class (oref-default class definition)) 974 `(oref-default ',class definition) 975 `(lambda ,arglist ,@body))) 976 (put ',name 'interactive-only ,interactive-only) 977 (put ',name 'function-documentation ,docstr) 978 (put ',name 'transient--suffix 979 (,(or class 'transient-suffix) :command ',name ,@slots))))) 980 981 (defmacro transient-augment-suffix (name &rest args) 982 "Augment existing command NAME with a new transient suffix object. 983 Similar to `transient-define-suffix' but define a suffix object only. 984 \n\(fn NAME [KEYWORD VALUE]...)" 985 (declare (debug (&define name [&rest keywordp sexp])) 986 (indent defun)) 987 (pcase-let 988 ((`(,class ,slots) 989 (transient--expand-define-args args nil 'transient-augment-suffix t))) 990 `(put ',name 'transient--suffix 991 (,(or class 'transient-suffix) :command ',name ,@slots)))) 992 993 (defmacro transient-define-infix (name arglist &rest args) 994 "Define NAME as a transient infix command. 995 996 ARGLIST is always ignored and reserved for future use. 997 DOCSTRING is the documentation string and is optional. 998 999 At least one key-value pair is required. All transient infix 1000 commands are equal to each other (but not eq). It is meaning- 1001 less to define an infix command, without providing at least one 1002 keyword argument (usually `:argument' or `:variable', depending 1003 on the class). The suffix class defaults to `transient-switch' 1004 and can be set using the `:class' keyword. 1005 1006 The function definitions is always: 1007 1008 (lambda () 1009 (interactive) 1010 (let ((obj (transient-suffix-object))) 1011 (transient-infix-set obj (transient-infix-read obj))) 1012 (transient--show)) 1013 1014 `transient-infix-read' and `transient-infix-set' are generic 1015 functions. Different infix commands behave differently because 1016 the concrete methods are different for different infix command 1017 classes. In rare case the above command function might not be 1018 suitable, even if you define your own infix command class. In 1019 that case you have to use `transient-define-suffix' to define 1020 the infix command and use t as the value of the `:transient' 1021 keyword. 1022 1023 \(fn NAME ARGLIST [DOCSTRING] KEYWORD VALUE [KEYWORD VALUE]...)" 1024 (declare (debug ( &define name lambda-list 1025 [&optional lambda-doc] 1026 keywordp sexp 1027 [&rest keywordp sexp])) 1028 (indent defun) 1029 (doc-string 3)) 1030 (pcase-let 1031 ((`(,class ,slots ,_ ,docstr ,_ ,interactive-only) 1032 (transient--expand-define-args args arglist 'transient-define-infix t))) 1033 `(progn 1034 (defalias ',name #'transient--default-infix-command) 1035 (put ',name 'interactive-only ,interactive-only) 1036 (put ',name 'completion-predicate #'transient--suffix-only) 1037 (put ',name 'function-documentation ,docstr) 1038 (put ',name 'transient--suffix 1039 (,(or class 'transient-switch) :command ',name ,@slots))))) 1040 1041 (defalias 'transient-define-argument #'transient-define-infix 1042 "Define NAME as a transient infix command. 1043 1044 Only use this alias to define an infix command that actually 1045 sets an infix argument. To define a infix command that, for 1046 example, sets a variable, use `transient-define-infix' instead. 1047 1048 \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)") 1049 1050 (defun transient--default-infix-command () 1051 ;; Most infix commands are but an alias for this command. 1052 "Cannot show any documentation for this transient infix command. 1053 1054 When you request help for an infix command using `transient-help', that 1055 usually shows the respective man-page and tries to jump to the location 1056 where the respective argument is being described. 1057 1058 If no man-page is specified for the containing transient menu, then the 1059 docstring is displayed instead, if any. 1060 1061 If the infix command doesn't have a docstring, as is the case here, then 1062 this docstring is displayed instead, because technically infix commands 1063 are aliases for `transient--default-infix-command'. 1064 1065 `describe-function' also shows the docstring of the infix command, 1066 falling back to that of the same aliased command." 1067 (interactive) 1068 (let ((obj (transient-suffix-object))) 1069 (transient-infix-set obj (transient-infix-read obj))) 1070 (transient--show)) 1071 (put 'transient--default-infix-command 'interactive-only t) 1072 (put 'transient--default-infix-command 'completion-predicate 1073 #'transient--suffix-only) 1074 1075 (define-advice find-function-advised-original 1076 (:around (fn func) transient-default-infix) 1077 "Return nil instead of `transient--default-infix-command'. 1078 When using `find-function' to jump to the definition of a transient 1079 infix command/argument, then we want to actually jump to that, not to 1080 the definition of `transient--default-infix-command', which all infix 1081 commands are aliases for." 1082 (let ((val (funcall fn func))) 1083 (and val (not (eq val 'transient--default-infix-command)) val))) 1084 1085 (eval-and-compile ;transient--expand-define-args 1086 (defun transient--expand-define-args (args &optional arglist form nobody) 1087 ;; ARGLIST and FORM are only optional for backward compatibility. 1088 ;; This is necessary because "emoji.el" from Emacs 29 calls this 1089 ;; function directly, with just one argument. 1090 (unless (listp arglist) 1091 (error "Mandatory ARGLIST is missing")) 1092 (let (class keys suffixes docstr declare (interactive-only t)) 1093 (when (stringp (car args)) 1094 (setq docstr (pop args))) 1095 (while (keywordp (car args)) 1096 (let ((k (pop args)) 1097 (v (pop args))) 1098 (if (eq k :class) 1099 (setq class v) 1100 (push k keys) 1101 (push v keys)))) 1102 (while (let ((arg (car args))) 1103 (or (vectorp arg) 1104 (and arg (symbolp arg)))) 1105 (push (pop args) suffixes)) 1106 (when (eq (car-safe (car args)) 'declare) 1107 (setq declare (car args)) 1108 (setq args (cdr args)) 1109 (when-let ((int (assq 'interactive-only declare))) 1110 (setq interactive-only (cadr int)) 1111 (delq int declare)) 1112 (unless (cdr declare) 1113 (setq declare nil))) 1114 (cond 1115 ((not args)) 1116 (nobody 1117 (error "%s: No function body allowed" form)) 1118 ((not (eq (car-safe (nth (if declare 1 0) args)) 'interactive)) 1119 (error "%s: Interactive form missing" form))) 1120 (list (if (eq (car-safe class) 'quote) 1121 (cadr class) 1122 class) 1123 (nreverse keys) 1124 (nreverse suffixes) 1125 docstr 1126 (if declare (cons declare args) args) 1127 interactive-only)))) 1128 1129 (defun transient--parse-child (prefix spec) 1130 (cl-typecase spec 1131 (null (error "Invalid transient--parse-child spec: %s" spec)) 1132 (symbol (let ((value (symbol-value spec))) 1133 (if (and (listp value) 1134 (or (listp (car value)) 1135 (vectorp (car value)))) 1136 (cl-mapcan (lambda (s) (transient--parse-child prefix s)) value) 1137 (transient--parse-child prefix value)))) 1138 (vector (and-let* ((c (transient--parse-group prefix spec))) (list c))) 1139 (list (and-let* ((c (transient--parse-suffix prefix spec))) (list c))) 1140 (string (list spec)) 1141 (t (error "Invalid transient--parse-child spec: %s" spec)))) 1142 1143 (defun transient--parse-group (prefix spec) 1144 (setq spec (append spec nil)) 1145 (cl-symbol-macrolet 1146 ((car (car spec)) 1147 (pop (pop spec))) 1148 (let (level class args) 1149 (when (integerp car) 1150 (setq level pop)) 1151 (when (stringp car) 1152 (setq args (plist-put args :description pop))) 1153 (while (keywordp car) 1154 (let ((key pop) 1155 (val pop)) 1156 (cond ((eq key :class) 1157 (setq class (macroexp-quote val))) 1158 ((or (symbolp val) 1159 (and (listp val) (not (eq (car val) 'lambda)))) 1160 (setq args (plist-put args key (macroexp-quote val)))) 1161 ((setq args (plist-put args key val)))))) 1162 (unless (or spec class (not (plist-get args :setup-children))) 1163 (message "WARNING: %s: When %s is used, %s must also be specified" 1164 'transient-define-prefix :setup-children :class)) 1165 (list 'vector 1166 (or level transient--default-child-level) 1167 (cond (class) 1168 ((or (vectorp car) 1169 (and car (symbolp car))) 1170 (quote 'transient-columns)) 1171 ((quote 'transient-column))) 1172 (and args (cons 'list args)) 1173 (cons 'list 1174 (cl-mapcan (lambda (s) (transient--parse-child prefix s)) 1175 spec)))))) 1176 1177 (defun transient--parse-suffix (prefix spec) 1178 (let (level class args) 1179 (cl-symbol-macrolet 1180 ((car (car spec)) 1181 (pop (pop spec))) 1182 (when (integerp car) 1183 (setq level pop)) 1184 (when (or (stringp car) 1185 (vectorp car)) 1186 (setq args (plist-put args :key pop))) 1187 (cond 1188 ((or (stringp car) 1189 (and (eq (car-safe car) 'lambda) 1190 (not (commandp car)))) 1191 (setq args (plist-put args :description pop))) 1192 ((and (symbolp car) 1193 (not (keywordp car)) 1194 (not (commandp car)) 1195 (commandp (cadr spec))) 1196 (setq args (plist-put args :description (macroexp-quote pop))))) 1197 (cond 1198 ((memq car '(:info :info*))) 1199 ((keywordp car) 1200 (error "Need command, `:info' or `:info*', got `%s'" car)) 1201 ((symbolp car) 1202 (setq args (plist-put args :command (macroexp-quote pop)))) 1203 ((and (commandp car) 1204 (not (stringp car))) 1205 (let ((cmd pop) 1206 (sym (intern 1207 (format "transient:%s:%s" 1208 prefix 1209 (let ((desc (plist-get args :description))) 1210 (if (and (stringp desc) 1211 (length< desc 16)) 1212 desc 1213 (plist-get args :key))))))) 1214 (setq args (plist-put 1215 args :command 1216 `(prog1 ',sym 1217 (put ',sym 'interactive-only t) 1218 (put ',sym 'completion-predicate #'transient--suffix-only) 1219 (defalias ',sym 1220 ,(if (eq (car-safe cmd) 'lambda) 1221 cmd 1222 (macroexp-quote cmd)))))))) 1223 ((or (stringp car) 1224 (and car (listp car))) 1225 (let ((arg pop) 1226 (sym nil)) 1227 (cl-typecase arg 1228 (list 1229 (setq args (plist-put args :shortarg (car arg))) 1230 (setq args (plist-put args :argument (cadr arg))) 1231 (setq arg (cadr arg))) 1232 (string 1233 (when-let ((shortarg (transient--derive-shortarg arg))) 1234 (setq args (plist-put args :shortarg shortarg))) 1235 (setq args (plist-put args :argument arg)))) 1236 (setq sym (intern (format "transient:%s:%s" prefix arg))) 1237 (setq args (plist-put 1238 args :command 1239 `(prog1 ',sym 1240 (put ',sym 'interactive-only t) 1241 (put ',sym 'completion-predicate #'transient--suffix-only) 1242 (defalias ',sym #'transient--default-infix-command)))) 1243 (cond ((and car (not (keywordp car))) 1244 (setq class 'transient-option) 1245 (setq args (plist-put args :reader (macroexp-quote pop)))) 1246 ((not (string-suffix-p "=" arg)) 1247 (setq class 'transient-switch)) 1248 (t 1249 (setq class 'transient-option))))) 1250 (t 1251 (error "Needed command or argument, got %S" car))) 1252 (while (keywordp car) 1253 (let ((key pop) 1254 (val pop)) 1255 (cond ((eq key :class) (setq class val)) 1256 ((eq key :level) (setq level val)) 1257 ((eq key :info) 1258 (setq class 'transient-information) 1259 (setq args (plist-put args :description val))) 1260 ((eq key :info*) 1261 (setq class 'transient-information*) 1262 (setq args (plist-put args :description val))) 1263 ((eq (car-safe val) '\,) 1264 (setq args (plist-put args key (cadr val)))) 1265 ((or (symbolp val) 1266 (and (listp val) (not (eq (car val) 'lambda)))) 1267 (setq args (plist-put args key (macroexp-quote val)))) 1268 ((setq args (plist-put args key val))))))) 1269 (unless (plist-get args :key) 1270 (when-let ((shortarg (plist-get args :shortarg))) 1271 (setq args (plist-put args :key shortarg)))) 1272 (list 'list 1273 (or level transient--default-child-level) 1274 (macroexp-quote (or class 'transient-suffix)) 1275 (cons 'list args)))) 1276 1277 (defun transient--derive-shortarg (arg) 1278 (save-match-data 1279 (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) 1280 (match-string 1 arg)))) 1281 1282 (defun transient-command-completion-not-suffix-only-p (symbol _buffer) 1283 "Say whether SYMBOL should be offered as a completion. 1284 If the value of SYMBOL's `completion-predicate' property is 1285 `transient--suffix-only', then return nil, otherwise return t. 1286 This is the case when a command should only ever be used as a 1287 suffix of a transient prefix command (as opposed to bindings 1288 in regular keymaps or by using `execute-extended-command')." 1289 (not (eq (get symbol 'completion-predicate) 'transient--suffix-only))) 1290 1291 (defalias 'transient--suffix-only #'ignore 1292 "Ignore ARGUMENTS, do nothing, and return nil. 1293 Also see `transient-command-completion-not-suffix-only-p'. 1294 Only use this alias as the value of the `completion-predicate' 1295 symbol property.") 1296 1297 (when (and (boundp 'read-extended-command-predicate) ; since Emacs 28.1 1298 (not read-extended-command-predicate)) 1299 (setq read-extended-command-predicate 1300 #'transient-command-completion-not-suffix-only-p)) 1301 1302 (defun transient-parse-suffix (prefix suffix) 1303 "Parse SUFFIX, to be added to PREFIX. 1304 PREFIX is a prefix command, a symbol. 1305 SUFFIX is a suffix command or a group specification (of 1306 the same forms as expected by `transient-define-prefix'). 1307 Intended for use in a group's `:setup-children' function." 1308 (cl-assert (and prefix (symbolp prefix))) 1309 (eval (car (transient--parse-child prefix suffix)) t)) 1310 1311 (defun transient-parse-suffixes (prefix suffixes) 1312 "Parse SUFFIXES, to be added to PREFIX. 1313 PREFIX is a prefix command, a symbol. 1314 SUFFIXES is a list of suffix command or a group specification 1315 (of the same forms as expected by `transient-define-prefix'). 1316 Intended for use in a group's `:setup-children' function." 1317 (cl-assert (and prefix (symbolp prefix))) 1318 (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes)) 1319 1320 ;;; Edit 1321 1322 (defun transient--insert-suffix (prefix loc suffix action &optional keep-other) 1323 (let* ((suf (cl-etypecase suffix 1324 (vector (transient--parse-group prefix suffix)) 1325 (list (transient--parse-suffix prefix suffix)) 1326 (string suffix))) 1327 (mem (transient--layout-member loc prefix)) 1328 (elt (car mem))) 1329 (setq suf (eval suf t)) 1330 (cond 1331 ((not mem) 1332 (message "Cannot insert %S into %s; %s not found" 1333 suffix prefix loc)) 1334 ((or (and (vectorp suffix) (not (vectorp elt))) 1335 (and (listp suffix) (vectorp elt)) 1336 (and (stringp suffix) (vectorp elt))) 1337 (message "Cannot place %S into %s at %s; %s" 1338 suffix prefix loc 1339 "suffixes and groups cannot be siblings")) 1340 (t 1341 (when-let* ((bindingp (listp suf)) 1342 (key (transient--spec-key suf)) 1343 (conflict (car (transient--layout-member key prefix))) 1344 (conflictp 1345 (and (not (and (eq action 'replace) 1346 (eq conflict elt))) 1347 (or (not keep-other) 1348 (eq (plist-get (nth 2 suf) :command) 1349 (plist-get (nth 2 conflict) :command))) 1350 (equal (transient--suffix-predicate suf) 1351 (transient--suffix-predicate conflict))))) 1352 (transient-remove-suffix prefix key)) 1353 (pcase-exhaustive action 1354 ('insert (setcdr mem (cons elt (cdr mem))) 1355 (setcar mem suf)) 1356 ('append (setcdr mem (cons suf (cdr mem)))) 1357 ('replace (setcar mem suf))))))) 1358 1359 ;;;###autoload 1360 (defun transient-insert-suffix (prefix loc suffix &optional keep-other) 1361 "Insert a SUFFIX into PREFIX before LOC. 1362 PREFIX is a prefix command, a symbol. 1363 SUFFIX is a suffix command or a group specification (of 1364 the same forms as expected by `transient-define-prefix'). 1365 LOC is a command, a key vector, a key description (a string 1366 as returned by `key-description'), or a coordination list 1367 (whose last element may also be a command or key). 1368 Remove a conflicting binding unless optional KEEP-OTHER is 1369 non-nil. 1370 See info node `(transient)Modifying Existing Transients'." 1371 (declare (indent defun)) 1372 (transient--insert-suffix prefix loc suffix 'insert keep-other)) 1373 1374 ;;;###autoload 1375 (defun transient-append-suffix (prefix loc suffix &optional keep-other) 1376 "Insert a SUFFIX into PREFIX after LOC. 1377 PREFIX is a prefix command, a symbol. 1378 SUFFIX is a suffix command or a group specification (of 1379 the same forms as expected by `transient-define-prefix'). 1380 LOC is a command, a key vector, a key description (a string 1381 as returned by `key-description'), or a coordination list 1382 (whose last element may also be a command or key). 1383 Remove a conflicting binding unless optional KEEP-OTHER is 1384 non-nil. 1385 See info node `(transient)Modifying Existing Transients'." 1386 (declare (indent defun)) 1387 (transient--insert-suffix prefix loc suffix 'append keep-other)) 1388 1389 ;;;###autoload 1390 (defun transient-replace-suffix (prefix loc suffix) 1391 "Replace the suffix at LOC in PREFIX with SUFFIX. 1392 PREFIX is a prefix command, a symbol. 1393 SUFFIX is a suffix command or a group specification (of 1394 the same forms as expected by `transient-define-prefix'). 1395 LOC is a command, a key vector, a key description (a string 1396 as returned by `key-description'), or a coordination list 1397 (whose last element may also be a command or key). 1398 See info node `(transient)Modifying Existing Transients'." 1399 (declare (indent defun)) 1400 (transient--insert-suffix prefix loc suffix 'replace)) 1401 1402 ;;;###autoload 1403 (defun transient-remove-suffix (prefix loc) 1404 "Remove the suffix or group at LOC in PREFIX. 1405 PREFIX is a prefix command, a symbol. 1406 LOC is a command, a key vector, a key description (a string 1407 as returned by `key-description'), or a coordination list 1408 (whose last element may also be a command or key). 1409 See info node `(transient)Modifying Existing Transients'." 1410 (declare (indent defun)) 1411 (transient--layout-member loc prefix 'remove)) 1412 1413 (defun transient-get-suffix (prefix loc) 1414 "Return the suffix or group at LOC in PREFIX. 1415 PREFIX is a prefix command, a symbol. 1416 LOC is a command, a key vector, a key description (a string 1417 as returned by `key-description'), or a coordination list 1418 (whose last element may also be a command or key). 1419 See info node `(transient)Modifying Existing Transients'." 1420 (if-let ((mem (transient--layout-member loc prefix))) 1421 (car mem) 1422 (error "%s not found in %s" loc prefix))) 1423 1424 (defun transient-suffix-put (prefix loc prop value) 1425 "Edit the suffix at LOC in PREFIX, setting PROP to VALUE. 1426 PREFIX is a prefix command, a symbol. 1427 SUFFIX is a suffix command or a group specification (of 1428 the same forms as expected by `transient-define-prefix'). 1429 LOC is a command, a key vector, a key description (a string 1430 as returned by `key-description'), or a coordination list 1431 (whose last element may also be a command or key). 1432 See info node `(transient)Modifying Existing Transients'." 1433 (let ((suf (transient-get-suffix prefix loc))) 1434 (setf (elt suf 2) 1435 (plist-put (elt suf 2) prop value)))) 1436 1437 (defun transient--layout-member (loc prefix &optional remove) 1438 (let ((val (or (get prefix 'transient--layout) 1439 (error "%s is not a transient command" prefix)))) 1440 (when (listp loc) 1441 (while (integerp (car loc)) 1442 (let* ((children (if (vectorp val) (aref val 3) val)) 1443 (mem (transient--nthcdr (pop loc) children))) 1444 (if (and remove (not loc)) 1445 (let ((rest (delq (car mem) children))) 1446 (if (vectorp val) 1447 (aset val 3 rest) 1448 (put prefix 'transient--layout rest)) 1449 (setq val nil)) 1450 (setq val (if loc (car mem) mem))))) 1451 (setq loc (car loc))) 1452 (if loc 1453 (transient--layout-member-1 (transient--kbd loc) val remove) 1454 val))) 1455 1456 (defun transient--layout-member-1 (loc layout remove) 1457 (cond ((listp layout) 1458 (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) 1459 layout)) 1460 ((vectorp (car (aref layout 3))) 1461 (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) 1462 (aref layout 3))) 1463 (remove 1464 (aset layout 3 1465 (delq (car (transient--group-member loc layout)) 1466 (aref layout 3))) 1467 nil) 1468 ((transient--group-member loc layout)))) 1469 1470 (defun transient--group-member (loc group) 1471 (cl-member-if (lambda (suffix) 1472 (and (listp suffix) 1473 (let* ((def (nth 2 suffix)) 1474 (cmd (plist-get def :command))) 1475 (if (symbolp loc) 1476 (eq cmd loc) 1477 (equal (transient--kbd 1478 (or (plist-get def :key) 1479 (transient--command-key cmd))) 1480 loc))))) 1481 (aref group 3))) 1482 1483 (defun transient--kbd (keys) 1484 (when (vectorp keys) 1485 (setq keys (key-description keys))) 1486 (when (stringp keys) 1487 (setq keys (kbd keys))) 1488 keys) 1489 1490 (defun transient--spec-key (spec) 1491 (let ((plist (nth 2 spec))) 1492 (or (plist-get plist :key) 1493 (transient--command-key 1494 (plist-get plist :command))))) 1495 1496 (defun transient--command-key (cmd) 1497 (and-let* ((obj (transient--suffix-prototype cmd))) 1498 (cond ((slot-boundp obj 'key) 1499 (oref obj key)) 1500 ((slot-exists-p obj 'shortarg) 1501 (if (slot-boundp obj 'shortarg) 1502 (oref obj shortarg) 1503 (transient--derive-shortarg (oref obj argument))))))) 1504 1505 (defun transient--nthcdr (n list) 1506 (nthcdr (if (< n 0) (- (length list) (abs n)) n) list)) 1507 1508 ;;; Variables 1509 1510 (defvar transient-current-prefix nil 1511 "The transient from which this suffix command was invoked. 1512 This is an object representing that transient, use 1513 `transient-current-command' to get the respective command.") 1514 1515 (defvar transient-current-command nil 1516 "The transient from which this suffix command was invoked. 1517 This is a symbol representing that transient, use 1518 `transient-current-prefix' to get the respective object.") 1519 1520 (defvar transient-current-suffixes nil 1521 "The suffixes of the transient from which this suffix command was invoked. 1522 This is a list of objects. Usually it is sufficient to instead 1523 use the function `transient-args', which returns a list of 1524 values. In complex cases it might be necessary to use this 1525 variable instead.") 1526 1527 (defvar transient-exit-hook nil 1528 "Hook run after exiting a transient.") 1529 1530 (defvar transient-setup-buffer-hook nil 1531 "Hook run when setting up the transient buffer. 1532 That buffer is current and empty when this hook runs.") 1533 1534 (defvar transient--prefix nil) 1535 (defvar transient--layout nil) 1536 (defvar transient--suffixes nil) 1537 1538 (defconst transient--stay t "Do not exit the transient.") 1539 (defconst transient--exit nil "Do exit the transient.") 1540 1541 (defvar transient--exitp nil "Whether to exit the transient.") 1542 (defvar transient--showp nil "Whether to show the transient popup buffer.") 1543 (defvar transient--helpp nil "Whether help-mode is active.") 1544 (defvar transient--editp nil "Whether edit-mode is active.") 1545 1546 (defvar transient--refreshp nil 1547 "Whether to refresh the transient completely.") 1548 1549 (defvar transient--all-levels-p nil 1550 "Whether temporary display of suffixes on all levels is active.") 1551 1552 (defvar transient--timer nil) 1553 1554 (defvar transient--stack nil) 1555 1556 (defvar transient--minibuffer-depth 0) 1557 1558 (defvar transient--buffer-name " *transient*" 1559 "Name of the transient buffer.") 1560 1561 (defvar transient--buffer nil 1562 "The transient menu buffer.") 1563 1564 (defvar transient--window nil 1565 "The window used to display the transient popup buffer.") 1566 1567 (defvar transient--original-window nil 1568 "The window that was selected before the transient was invoked. 1569 Usually it remains selected while the transient is active.") 1570 1571 (defvar transient--original-buffer nil 1572 "The buffer that was current before the transient was invoked. 1573 Usually it remains current while the transient is active.") 1574 1575 (defvar transient--restore-winconf nil 1576 "Window configuration to restore after exiting help.") 1577 1578 (defvar transient--shadowed-buffer nil 1579 "The buffer that is temporarily shadowed by the transient buffer. 1580 This is bound while the suffix predicate is being evaluated and while 1581 drawing in the transient buffer.") 1582 1583 (defvar transient--pending-suffix nil 1584 "The suffix that is currently being processed. 1585 This is bound while the suffix predicate is being evaluated, 1586 and while functions that return faces are being evaluated.") 1587 1588 (defvar transient--pending-group nil 1589 "The group that is currently being processed. 1590 This is bound while the suffixes are drawn in the transient buffer.") 1591 1592 (defvar transient--debug nil 1593 "Whether to put debug information into *Messages*.") 1594 1595 (defvar transient--history nil) 1596 1597 (defvar transient--scroll-commands 1598 '(transient-scroll-up 1599 transient-scroll-down 1600 mwheel-scroll 1601 scroll-bar-toolkit-scroll)) 1602 1603 ;;; Identities 1604 1605 (defun transient-prefix-object () 1606 "Return the current prefix as an object. 1607 1608 While a transient is being setup or refreshed (which involves 1609 preparing its suffixes) the variable `transient--prefix' can be 1610 used to access the prefix object. Thus this is what has to be 1611 used in suffix methods such as `transient-format-description', 1612 and in object-specific functions that are stored in suffix slots 1613 such as `description'. 1614 1615 When a suffix command is invoked (i.e., in its `interactive' form 1616 and function body) then the variable `transient-current-prefix' 1617 has to be used instead. 1618 1619 Two distinct variables are needed, because any prefix may itself 1620 be used as a suffix of another prefix, and such sub-prefixes have 1621 to be able to tell themselves apart from the prefix they were 1622 invoked from. 1623 1624 Regular suffix commands, which are not prefixes, do not have to 1625 concern themselves with this distinction, so they can use this 1626 function instead. In the context of a plain suffix, it always 1627 returns the value of the appropriate variable." 1628 (or transient--prefix transient-current-prefix)) 1629 1630 (defun transient-suffix-object (&optional command) 1631 "Return the object associated with the current suffix command. 1632 1633 Each suffix commands is associated with an object, which holds 1634 additional information about the suffix, such as its value (in 1635 the case of an infix command, which is a kind of suffix command). 1636 1637 This function is intended to be called by infix commands, which 1638 are usually aliases of `transient--default-infix-command', which 1639 is defined like this: 1640 1641 (defun transient--default-infix-command () 1642 (interactive) 1643 (let ((obj (transient-suffix-object))) 1644 (transient-infix-set obj (transient-infix-read obj))) 1645 (transient--show)) 1646 1647 \(User input is read outside of `interactive' to prevent the 1648 command from being added to `command-history'. See #23.) 1649 1650 Such commands need to be able to access their associated object 1651 to guide how `transient-infix-read' reads the new value and to 1652 store the read value. Other suffix commands (including non-infix 1653 commands) may also need the object to guide their behavior. 1654 1655 This function attempts to return the object associated with the 1656 current suffix command even if the suffix command was not invoked 1657 from a transient. (For some suffix command that is a valid thing 1658 to do, for others it is not.) In that case nil may be returned, 1659 if the command was not defined using one of the macros intended 1660 to define such commands. 1661 1662 The optional argument COMMAND is intended for internal use. If 1663 you are contemplating using it in your own code, then you should 1664 probably use this instead: 1665 1666 (get COMMAND \\='transient--suffix)" 1667 (when command 1668 (cl-check-type command command)) 1669 (cond 1670 (transient--pending-suffix) 1671 ((or transient--prefix 1672 transient-current-prefix) 1673 (let ((suffixes 1674 (cl-remove-if-not 1675 (lambda (obj) 1676 (eq (oref obj command) 1677 (or command 1678 (if (eq this-command 'transient-set-level) 1679 ;; This is how it can look up for which 1680 ;; command it is setting the level. 1681 this-original-command 1682 this-command)))) 1683 (or transient--suffixes 1684 transient-current-suffixes)))) 1685 (or (and (cdr suffixes) 1686 (cl-find-if 1687 (lambda (obj) 1688 (equal (listify-key-sequence (transient--kbd (oref obj key))) 1689 (listify-key-sequence (this-command-keys)))) 1690 suffixes)) 1691 (car suffixes)))) 1692 ((and-let* ((obj (transient--suffix-prototype (or command this-command))) 1693 (obj (clone obj))) 1694 (progn ; work around debbugs#31840 1695 (transient-init-scope obj) 1696 (transient-init-value obj) 1697 obj))))) 1698 1699 (defun transient--suffix-prototype (command) 1700 (or (get command 'transient--suffix) 1701 (seq-some (lambda (cmd) (get cmd 'transient--suffix)) 1702 (function-alias-p command)))) 1703 1704 ;;; Keymaps 1705 1706 (defvar-keymap transient-base-map 1707 :doc "Parent of other keymaps used by Transient. 1708 1709 This is the parent keymap of all the keymaps that are used in 1710 all transients: `transient-map' (which in turn is the parent 1711 of the transient-specific keymaps), `transient-edit-map' and 1712 `transient-sticky-map'. 1713 1714 If you change a binding here, then you might also have to edit 1715 `transient-sticky-map' and `transient-common-commands'. While 1716 the latter isn't a proper transient prefix command, it can be 1717 edited using the same functions as used for transients. 1718 1719 If you add a new command here, then you must also add a binding 1720 to `transient-predicate-map'." 1721 "ESC ESC ESC" #'transient-quit-all 1722 "C-g" #'transient-quit-one 1723 "C-q" #'transient-quit-all 1724 "C-z" #'transient-suspend 1725 "C-v" #'transient-scroll-up 1726 "C-M-v" #'transient-scroll-down 1727 "<next>" #'transient-scroll-up 1728 "<prior>" #'transient-scroll-down) 1729 1730 (defvar-keymap transient-map 1731 :doc "Top-level keymap used by all transients. 1732 1733 If you add a new command here, then you must also add a binding 1734 to `transient-predicate-map'. Also see `transient-base-map'." 1735 :parent transient-base-map 1736 "C-u" #'universal-argument 1737 "C--" #'negative-argument 1738 "C-t" #'transient-show 1739 "?" #'transient-help 1740 "C-h" #'transient-help 1741 ;; Also bound to "C-x p" and "C-x n" in transient-common-commands. 1742 "C-M-p" #'transient-history-prev 1743 "C-M-n" #'transient-history-next) 1744 1745 (defvar-keymap transient-edit-map 1746 :doc "Keymap that is active while a transient in is in \"edit mode\"." 1747 :parent transient-base-map 1748 "?" #'transient-help 1749 "C-h" #'transient-help 1750 "C-x l" #'transient-set-level) 1751 1752 (defvar-keymap transient-sticky-map 1753 :doc "Keymap that is active while an incomplete key sequence is active." 1754 :parent transient-base-map 1755 "C-g" #'transient-quit-seq) 1756 1757 (defvar transient--common-command-prefixes '(?\C-x)) 1758 1759 (put 'transient-common-commands 1760 'transient--layout 1761 (list 1762 (eval 1763 (car (transient--parse-child 1764 'transient-common-commands 1765 (vector 1766 :hide 1767 (lambda () 1768 (and (not (memq 1769 (car (bound-and-true-p transient--redisplay-key)) 1770 transient--common-command-prefixes)) 1771 (not transient-show-common-commands))) 1772 (vector 1773 "Value commands" 1774 (list "C-x s " "Set" #'transient-set) 1775 (list "C-x C-s" "Save" #'transient-save) 1776 (list "C-x C-k" "Reset" #'transient-reset) 1777 (list "C-x p " "Previous value" #'transient-history-prev) 1778 (list "C-x n " "Next value" #'transient-history-next)) 1779 (vector 1780 "Sticky commands" 1781 ;; Like `transient-sticky-map' except that 1782 ;; "C-g" has to be bound to a different command. 1783 (list "C-g" "Quit prefix or transient" #'transient-quit-one) 1784 (list "C-q" "Quit transient stack" #'transient-quit-all) 1785 (list "C-z" "Suspend transient stack" #'transient-suspend)) 1786 (vector 1787 "Customize" 1788 (list "C-x t" 'transient-toggle-common :description 1789 (lambda () 1790 (if transient-show-common-commands 1791 "Hide common commands" 1792 "Show common permanently"))) 1793 (list "C-x l" "Show/hide suffixes" #'transient-set-level) 1794 (list "C-x a" #'transient-toggle-level-limit))))) 1795 t))) 1796 1797 (defvar-keymap transient-popup-navigation-map 1798 :doc "One of the keymaps used when popup navigation is enabled. 1799 See `transient-enable-popup-navigation'." 1800 "<down-mouse-1>" #'transient-noop 1801 "<up>" #'transient-backward-button 1802 "<down>" #'transient-forward-button 1803 "C-r" #'transient-isearch-backward 1804 "C-s" #'transient-isearch-forward 1805 "M-RET" #'transient-push-button) 1806 1807 (defvar-keymap transient-button-map 1808 :doc "One of the keymaps used when popup navigation is enabled. 1809 See `transient-enable-popup-navigation'." 1810 "<mouse-1>" #'transient-push-button 1811 "<mouse-2>" #'transient-push-button) 1812 1813 (defvar-keymap transient-resume-mode-map 1814 :doc "Keymap for `transient-resume-mode'. 1815 1816 This keymap remaps every command that would usually just quit the 1817 documentation buffer to `transient-resume', which additionally 1818 resumes the suspended transient." 1819 "<remap> <Man-quit>" #'transient-resume 1820 "<remap> <Info-exit>" #'transient-resume 1821 "<remap> <quit-window>" #'transient-resume) 1822 1823 (defvar-keymap transient-predicate-map 1824 :doc "Base keymap used to map common commands to their transient behavior. 1825 1826 The \"transient behavior\" of a command controls, among other 1827 things, whether invoking the command causes the transient to be 1828 exited or not, and whether infix arguments are exported before 1829 doing so. 1830 1831 Each \"key\" is a command that is common to all transients and 1832 that is bound in `transient-map', `transient-edit-map', 1833 `transient-sticky-map' and/or `transient-common-command'. 1834 1835 Each binding is a \"pre-command\", a function that controls the 1836 transient behavior of the respective command. 1837 1838 For transient commands that are bound in individual transients, 1839 the transient behavior is specified using the `:transient' slot 1840 of the corresponding object." 1841 "<transient-suspend>" #'transient--do-suspend 1842 "<transient-help>" #'transient--do-stay 1843 "<transient-set-level>" #'transient--do-stay 1844 "<transient-history-prev>" #'transient--do-stay 1845 "<transient-history-next>" #'transient--do-stay 1846 "<universal-argument>" #'transient--do-stay 1847 "<universal-argument-more>" #'transient--do-stay 1848 "<negative-argument>" #'transient--do-minus 1849 "<digit-argument>" #'transient--do-stay 1850 "<top-level>" #'transient--do-quit-all 1851 "<transient-quit-all>" #'transient--do-quit-all 1852 "<transient-quit-one>" #'transient--do-quit-one 1853 "<transient-quit-seq>" #'transient--do-stay 1854 "<transient-show>" #'transient--do-stay 1855 "<transient-update>" #'transient--do-stay 1856 "<transient-toggle-common>" #'transient--do-stay 1857 "<transient-set>" #'transient--do-call 1858 "<transient-set-and-exit>" #'transient--do-exit 1859 "<transient-save>" #'transient--do-call 1860 "<transient-save-and-exit>" #'transient--do-exit 1861 "<transient-reset>" #'transient--do-call 1862 "<describe-key-briefly>" #'transient--do-stay 1863 "<describe-key>" #'transient--do-stay 1864 "<transient-scroll-up>" #'transient--do-stay 1865 "<transient-scroll-down>" #'transient--do-stay 1866 "<mwheel-scroll>" #'transient--do-stay 1867 "<scroll-bar-toolkit-scroll>" #'transient--do-stay 1868 "<transient-noop>" #'transient--do-noop 1869 "<transient-mouse-push-button>" #'transient--do-move 1870 "<transient-push-button>" #'transient--do-push-button 1871 "<transient-backward-button>" #'transient--do-move 1872 "<transient-forward-button>" #'transient--do-move 1873 "<transient-isearch-backward>" #'transient--do-move 1874 "<transient-isearch-forward>" #'transient--do-move 1875 ;; If a valid but incomplete prefix sequence is followed by 1876 ;; an unbound key, then Emacs calls the `undefined' command 1877 ;; but does not set `this-command', `this-original-command' 1878 ;; or `real-this-command' accordingly. Instead they are nil. 1879 "<nil>" #'transient--do-warn 1880 ;; Bound to the `mouse-movement' event, this command is similar 1881 ;; to `ignore'. 1882 "<ignore-preserving-kill-region>" #'transient--do-noop) 1883 1884 (defvar transient--transient-map nil) 1885 (defvar transient--predicate-map nil) 1886 (defvar transient--redisplay-map nil) 1887 (defvar transient--redisplay-key nil) 1888 1889 (defun transient--push-keymap (var) 1890 (let ((map (symbol-value var))) 1891 (transient--debug " push %s%s" var (if map "" " VOID")) 1892 (when map 1893 (with-demoted-errors "transient--push-keymap: %S" 1894 (internal-push-keymap map 'overriding-terminal-local-map))))) 1895 1896 (defun transient--pop-keymap (var) 1897 (let ((map (symbol-value var))) 1898 (when map 1899 (transient--debug " pop %s" var) 1900 (with-demoted-errors "transient--pop-keymap: %S" 1901 (internal-pop-keymap map 'overriding-terminal-local-map))))) 1902 1903 (defun transient--make-transient-map () 1904 (let ((map (make-sparse-keymap))) 1905 (set-keymap-parent map (if transient--editp 1906 transient-edit-map 1907 transient-map)) 1908 (dolist (obj transient--suffixes) 1909 (let ((key (oref obj key))) 1910 (when (vectorp key) 1911 (setq key (key-description key)) 1912 (oset obj key key)) 1913 (when transient-substitute-key-function 1914 (setq key (save-match-data 1915 (funcall transient-substitute-key-function obj))) 1916 (oset obj key key)) 1917 (let* ((kbd (kbd key)) 1918 (cmd (oref obj command)) 1919 (alt (transient--lookup-key map kbd))) 1920 (cond ((not alt) 1921 (define-key map kbd cmd)) 1922 ((eq alt cmd)) 1923 ((transient--inapt-suffix-p obj)) 1924 ((and-let* ((obj (transient-suffix-object alt))) 1925 (transient--inapt-suffix-p obj)) 1926 (define-key map kbd cmd)) 1927 (transient-detect-key-conflicts 1928 (error "Cannot bind %S to %s and also %s" 1929 (string-trim key) cmd alt)) 1930 ((define-key map kbd cmd)))))) 1931 (when-let ((b (keymap-lookup map "-"))) (keymap-set map "<kp-subtract>" b)) 1932 (when-let ((b (keymap-lookup map "="))) (keymap-set map "<kp-equal>" b)) 1933 (when-let ((b (keymap-lookup map "+"))) (keymap-set map "<kp-add>" b)) 1934 (when transient-enable-popup-navigation 1935 ;; `transient--make-redisplay-map' maps only over bindings that are 1936 ;; directly in the base keymap, so that cannot be a composed keymap. 1937 (set-keymap-parent 1938 map (make-composed-keymap 1939 (keymap-parent map) 1940 transient-popup-navigation-map))) 1941 map)) 1942 1943 (defun transient--make-predicate-map () 1944 (let* ((default (transient--resolve-pre-command 1945 (oref transient--prefix transient-suffix))) 1946 (return (and transient--stack (eq default t))) 1947 (map (make-sparse-keymap))) 1948 (set-keymap-parent map transient-predicate-map) 1949 (when (or (and (slot-boundp transient--prefix 'transient-switch-frame) 1950 (transient--resolve-pre-command 1951 (not (oref transient--prefix transient-switch-frame)))) 1952 (memq (transient--resolve-pre-command 1953 (oref transient--prefix transient-non-suffix)) 1954 '(nil transient--do-warn transient--do-noop))) 1955 (define-key map [handle-switch-frame] #'transient--do-suspend)) 1956 (dolist (obj transient--suffixes) 1957 (let* ((cmd (oref obj command)) 1958 (kind (cond ((get cmd 'transient--prefix) 'prefix) 1959 ((cl-typep obj 'transient-infix) 'infix) 1960 (t 'suffix)))) 1961 (cond 1962 ((oref obj inapt) 1963 (define-key map (vector cmd) #'transient--do-warn-inapt)) 1964 ((slot-boundp obj 'transient) 1965 (define-key map (vector cmd) 1966 (pcase (list kind 1967 (transient--resolve-pre-command (oref obj transient)) 1968 return) 1969 (`(prefix t ,_) #'transient--do-recurse) 1970 (`(prefix nil ,_) #'transient--do-stack) 1971 (`(infix t ,_) #'transient--do-stay) 1972 (`(suffix t ,_) #'transient--do-call) 1973 ('(suffix nil t) #'transient--do-return) 1974 (`(,_ nil ,_) #'transient--do-exit) 1975 (`(,_ ,do ,_) do)))) 1976 ((not (lookup-key transient-predicate-map (vector cmd))) 1977 (define-key map (vector cmd) 1978 (pcase (list kind default return) 1979 (`(prefix ,(or 'transient--do-stay 'transient--do-call) ,_) 1980 #'transient--do-recurse) 1981 (`(prefix t ,_) #'transient--do-recurse) 1982 (`(prefix ,_ ,_) #'transient--do-stack) 1983 (`(infix ,_ ,_) #'transient--do-stay) 1984 (`(suffix t ,_) #'transient--do-call) 1985 ('(suffix nil t) #'transient--do-return) 1986 (`(suffix nil ,_) #'transient--do-exit) 1987 (`(suffix ,do ,_) do))))))) 1988 map)) 1989 1990 (defun transient--make-redisplay-map () 1991 (setq transient--redisplay-key 1992 (pcase this-command 1993 ('transient-update 1994 (setq transient--showp t) 1995 (setq unread-command-events 1996 (listify-key-sequence (this-single-command-raw-keys)))) 1997 ('transient-quit-seq 1998 (setq unread-command-events 1999 (butlast (listify-key-sequence 2000 (this-single-command-raw-keys)) 2001 2)) 2002 (butlast transient--redisplay-key)) 2003 (_ nil))) 2004 (let ((topmap (make-sparse-keymap)) 2005 (submap (make-sparse-keymap))) 2006 (when transient--redisplay-key 2007 (define-key topmap (vconcat transient--redisplay-key) submap) 2008 (set-keymap-parent submap transient-sticky-map)) 2009 (map-keymap-internal 2010 (lambda (key def) 2011 (when (and (not (eq key ?\e)) 2012 (listp def) 2013 (keymapp def)) 2014 (define-key topmap (vconcat transient--redisplay-key (list key)) 2015 #'transient-update))) 2016 (if transient--redisplay-key 2017 (let ((key (vconcat transient--redisplay-key))) 2018 (or (lookup-key transient--transient-map key) 2019 (and-let* ((regular (lookup-key local-function-key-map key))) 2020 (lookup-key transient--transient-map (vconcat regular))))) 2021 transient--transient-map)) 2022 topmap)) 2023 2024 ;;; Setup 2025 2026 (defun transient-setup (&optional name layout edit &rest params) 2027 "Setup the transient specified by NAME. 2028 2029 This function is called by transient prefix commands to setup the 2030 transient. In that case NAME is mandatory, LAYOUT and EDIT must 2031 be nil and PARAMS may be (but usually is not) used to set, e.g., 2032 the \"scope\" of the transient (see `transient-define-prefix'). 2033 2034 This function is also called internally, in which case LAYOUT and 2035 EDIT may be non-nil." 2036 (transient--debug 'setup) 2037 (transient--with-emergency-exit :setup 2038 (cond 2039 ((not name) 2040 ;; Switching between regular and edit mode. 2041 (transient--pop-keymap 'transient--transient-map) 2042 (transient--pop-keymap 'transient--redisplay-map) 2043 (setq name (oref transient--prefix command)) 2044 (setq params (list :scope (oref transient--prefix scope)))) 2045 (transient--prefix 2046 ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}" 2047 ;; of an outer prefix. Unlike the usual `transient--do-stack', 2048 ;; these predicates fail to clean up after the outer prefix. 2049 (transient--pop-keymap 'transient--transient-map) 2050 (transient--pop-keymap 'transient--redisplay-map)) 2051 ((not (or layout ; resuming parent/suspended prefix 2052 transient-current-command)) ; entering child prefix 2053 (transient--stack-zap)) ; replace suspended prefix, if any 2054 (edit 2055 ;; Returning from help to edit. 2056 (setq transient--editp t))) 2057 (transient--init-objects name layout params) 2058 (transient--init-keymaps) 2059 (transient--history-init transient--prefix) 2060 (setq transient--original-window (selected-window)) 2061 (setq transient--original-buffer (current-buffer)) 2062 (setq transient--minibuffer-depth (minibuffer-depth)) 2063 (transient--redisplay) 2064 (transient--init-transient) 2065 (transient--suspend-which-key-mode))) 2066 2067 (cl-defgeneric transient-setup-children (group children) 2068 "Setup the CHILDREN of GROUP. 2069 If the value of the `setup-children' slot is non-nil, then call 2070 that function with CHILDREN as the only argument and return the 2071 value. Otherwise return CHILDREN as is." 2072 (if (slot-boundp group 'setup-children) 2073 (funcall (oref group setup-children) children) 2074 children)) 2075 2076 (defun transient--init-keymaps () 2077 (setq transient--predicate-map (transient--make-predicate-map)) 2078 (setq transient--transient-map (transient--make-transient-map)) 2079 (setq transient--redisplay-map (transient--make-redisplay-map))) 2080 2081 (defun transient--init-objects (&optional name layout params) 2082 (if name 2083 (setq transient--prefix (transient--init-prefix name params)) 2084 (setq name (oref transient--prefix command))) 2085 (setq transient--refreshp (oref transient--prefix refresh-suffixes)) 2086 (setq transient--layout (or layout (transient--init-suffixes name))) 2087 (setq transient--suffixes (transient--flatten-suffixes transient--layout))) 2088 2089 (defun transient--init-prefix (name &optional params) 2090 (let ((obj (let ((proto (get name 'transient--prefix))) 2091 (apply #'clone proto 2092 :prototype proto 2093 :level (or (alist-get t (alist-get name transient-levels)) 2094 transient-default-level) 2095 params)))) 2096 (transient--setup-recursion obj) 2097 (transient-init-value obj) 2098 obj)) 2099 2100 (defun transient--init-suffixes (name) 2101 (let ((levels (alist-get name transient-levels))) 2102 (cl-mapcan (lambda (c) (transient--init-child levels c nil)) 2103 (append (get name 'transient--layout) 2104 (and (not transient--editp) 2105 (get 'transient-common-commands 2106 'transient--layout)))))) 2107 2108 (defun transient--flatten-suffixes (layout) 2109 (cl-labels ((s (def) 2110 (cond 2111 ((stringp def) nil) 2112 ((cl-typep def 'transient-information) nil) 2113 ((listp def) (cl-mapcan #'s def)) 2114 ((cl-typep def 'transient-group) 2115 (cl-mapcan #'s (oref def suffixes))) 2116 ((cl-typep def 'transient-suffix) 2117 (list def))))) 2118 (cl-mapcan #'s layout))) 2119 2120 (defun transient--init-child (levels spec parent) 2121 (cl-etypecase spec 2122 (vector (transient--init-group levels spec parent)) 2123 (list (transient--init-suffix levels spec parent)) 2124 (string (list spec)))) 2125 2126 (defun transient--init-group (levels spec parent) 2127 (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) 2128 (and-let* (((transient--use-level-p level)) 2129 (obj (apply class :level level args)) 2130 ((transient--use-suffix-p obj)) 2131 ((prog1 t 2132 (when (or (and parent (oref parent inapt)) 2133 (transient--inapt-suffix-p obj)) 2134 (oset obj inapt t)))) 2135 (suffixes (cl-mapcan 2136 (lambda (c) (transient--init-child levels c obj)) 2137 (transient-setup-children obj children)))) 2138 (progn ; work around debbugs#31840 2139 (oset obj suffixes suffixes) 2140 (list obj))))) 2141 2142 (defun transient--init-suffix (levels spec parent) 2143 (pcase-let* ((`(,level ,class ,args) spec) 2144 (cmd (plist-get args :command)) 2145 (key (transient--kbd (plist-get args :key))) 2146 (level (or (alist-get (cons cmd key) levels nil nil #'equal) 2147 (alist-get cmd levels) 2148 level))) 2149 (let ((fn (and (symbolp cmd) 2150 (symbol-function cmd)))) 2151 (when (autoloadp fn) 2152 (transient--debug " autoload %s" cmd) 2153 (autoload-do-load fn))) 2154 (when (transient--use-level-p level) 2155 (let ((obj (if (child-of-class-p class 'transient-information) 2156 (apply class :level level args) 2157 (unless (and cmd (symbolp cmd)) 2158 (error "BUG: Non-symbolic suffix command: %s" cmd)) 2159 (if-let ((proto (and cmd (transient--suffix-prototype cmd)))) 2160 (apply #'clone proto :level level args) 2161 (apply class :command cmd :level level args))))) 2162 (cond ((not cmd)) 2163 ((commandp cmd)) 2164 ((or (cl-typep obj 'transient-switch) 2165 (cl-typep obj 'transient-option)) 2166 ;; As a temporary special case, if the package was compiled 2167 ;; with an older version of Transient, then we must define 2168 ;; "anonymous" switch and option commands here. 2169 (defalias cmd #'transient--default-infix-command)) 2170 ((transient--use-suffix-p obj) 2171 (error "Suffix command %s is not defined or autoloaded" cmd))) 2172 (unless (cl-typep obj 'transient-information) 2173 (transient--init-suffix-key obj)) 2174 (when (transient--use-suffix-p obj) 2175 (if (or (and parent (oref parent inapt)) 2176 (transient--inapt-suffix-p obj)) 2177 (oset obj inapt t) 2178 (transient-init-scope obj) 2179 (transient-init-value obj)) 2180 (list obj)))))) 2181 2182 (cl-defmethod transient--init-suffix-key ((obj transient-suffix)) 2183 (unless (slot-boundp obj 'key) 2184 (error "No key for %s" (oref obj command)))) 2185 2186 (cl-defmethod transient--init-suffix-key ((obj transient-argument)) 2187 (if (transient-switches--eieio-childp obj) 2188 (cl-call-next-method obj) 2189 (unless (slot-boundp obj 'shortarg) 2190 (when-let ((shortarg (transient--derive-shortarg (oref obj argument)))) 2191 (oset obj shortarg shortarg))) 2192 (unless (slot-boundp obj 'key) 2193 (if (slot-boundp obj 'shortarg) 2194 (oset obj key (oref obj shortarg)) 2195 (error "No key for %s" (oref obj command)))))) 2196 2197 (defun transient--use-level-p (level &optional edit) 2198 (or transient--all-levels-p 2199 (and transient--editp (not edit)) 2200 (and (>= level 1) 2201 (<= level (oref transient--prefix level))))) 2202 2203 (defun transient--use-suffix-p (obj) 2204 (let ((transient--shadowed-buffer (current-buffer)) 2205 (transient--pending-suffix obj)) 2206 (transient--do-suffix-p 2207 (oref obj if) 2208 (oref obj if-not) 2209 (oref obj if-nil) 2210 (oref obj if-non-nil) 2211 (oref obj if-mode) 2212 (oref obj if-not-mode) 2213 (oref obj if-derived) 2214 (oref obj if-not-derived) 2215 t))) 2216 2217 (defun transient--inapt-suffix-p (obj) 2218 (let ((transient--shadowed-buffer (current-buffer)) 2219 (transient--pending-suffix obj)) 2220 (transient--do-suffix-p 2221 (oref obj inapt-if) 2222 (oref obj inapt-if-not) 2223 (oref obj inapt-if-nil) 2224 (oref obj inapt-if-non-nil) 2225 (oref obj inapt-if-mode) 2226 (oref obj inapt-if-not-mode) 2227 (oref obj inapt-if-derived) 2228 (oref obj inapt-if-not-derived) 2229 nil))) 2230 2231 (defun transient--do-suffix-p 2232 (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived 2233 default) 2234 (cond 2235 (if (funcall if)) 2236 (if-not (not (funcall if-not))) 2237 (if-non-nil (symbol-value if-non-nil)) 2238 (if-nil (not (symbol-value if-nil))) 2239 (if-mode (if (atom if-mode) 2240 (eq major-mode if-mode) 2241 (memq major-mode if-mode))) 2242 (if-not-mode (not (if (atom if-not-mode) 2243 (eq major-mode if-not-mode) 2244 (memq major-mode if-not-mode)))) 2245 (if-derived (if (or (atom if-derived) 2246 (>= emacs-major-version 30)) 2247 (derived-mode-p if-derived) 2248 (apply #'derived-mode-p if-derived))) 2249 (if-not-derived (not (if (or (atom if-not-derived) 2250 (>= emacs-major-version 30)) 2251 (derived-mode-p if-not-derived) 2252 (apply #'derived-mode-p if-not-derived)))) 2253 (default))) 2254 2255 (defun transient--suffix-predicate (spec) 2256 (let ((plist (nth 2 spec))) 2257 (seq-some (lambda (prop) 2258 (and-let* ((pred (plist-get plist prop))) 2259 (list prop pred))) 2260 '( :if :if-not 2261 :if-nil :if-non-nil 2262 :if-mode :if-not-mode 2263 :if-derived :if-not-derived 2264 :inapt-if :inapt-if-not 2265 :inapt-if-nil :inapt-if-non-nil 2266 :inapt-if-mode :inapt-if-not-mode 2267 :inapt-if-derived :inapt-if-not-derived)))) 2268 2269 ;;; Flow-Control 2270 2271 (defun transient--init-transient () 2272 (transient--debug 'init-transient) 2273 (transient--push-keymap 'transient--transient-map) 2274 (transient--push-keymap 'transient--redisplay-map) 2275 (add-hook 'pre-command-hook #'transient--pre-command) 2276 (add-hook 'post-command-hook #'transient--post-command) 2277 (advice-add 'recursive-edit :around #'transient--recursive-edit) 2278 (when transient--exitp 2279 ;; This prefix command was invoked as the suffix of another. 2280 ;; Prevent `transient--post-command' from removing the hooks 2281 ;; that we just added. 2282 (setq transient--exitp 'replace))) 2283 2284 (defun transient--refresh-transient () 2285 (transient--debug 'refresh-transient) 2286 (transient--pop-keymap 'transient--predicate-map) 2287 (transient--pop-keymap 'transient--transient-map) 2288 (transient--pop-keymap 'transient--redisplay-map) 2289 (if (eq transient--refreshp 'updated-value) 2290 ;; Preserve the prefix value this once, because the 2291 ;; invoked suffix indicates that it has updated that. 2292 (setq transient--refreshp (oref transient--prefix refresh-suffixes)) 2293 ;; Otherwise update the prefix value from suffix values. 2294 (oset transient--prefix value (transient-get-value))) 2295 (transient--init-objects) 2296 (transient--init-keymaps) 2297 (transient--push-keymap 'transient--transient-map) 2298 (transient--push-keymap 'transient--redisplay-map) 2299 (transient--redisplay)) 2300 2301 (defun transient--pre-command () 2302 (transient--debug 'pre-command) 2303 (transient--with-emergency-exit :pre-command 2304 ;; The use of `overriding-terminal-local-map' does not prevent the 2305 ;; lookup of command remappings in the overridden maps, which can 2306 ;; lead to a suffix being remapped to a non-suffix. We have to undo 2307 ;; the remapping in that case. However, remapping a non-suffix to 2308 ;; another should remain possible. 2309 (when (and (transient--get-pre-command this-original-command 'suffix) 2310 (not (transient--get-pre-command this-command 'suffix))) 2311 (setq this-command this-original-command)) 2312 (cond 2313 ((memq this-command '(transient-update transient-quit-seq)) 2314 (transient--pop-keymap 'transient--redisplay-map)) 2315 ((and transient--helpp 2316 (not (memq this-command '(transient-quit-one 2317 transient-quit-all)))) 2318 (cond 2319 ((transient-help) 2320 (transient--do-suspend) 2321 (setq this-command 'transient-suspend) 2322 (transient--pre-exit)) 2323 ((not (transient--edebug-command-p)) 2324 (setq this-command 'transient-undefined)))) 2325 ((and transient--editp 2326 (transient-suffix-object) 2327 (not (memq this-command '(transient-quit-one 2328 transient-quit-all 2329 transient-help)))) 2330 (setq this-command 'transient-set-level) 2331 (transient--wrap-command)) 2332 (t 2333 (setq transient--exitp nil) 2334 (let ((exitp (eq (transient--call-pre-command) transient--exit))) 2335 (transient--wrap-command) 2336 (when exitp 2337 (transient--pre-exit))))))) 2338 2339 (defun transient--pre-exit () 2340 (transient--debug 'pre-exit) 2341 (transient--delete-window) 2342 (transient--timer-cancel) 2343 (transient--pop-keymap 'transient--transient-map) 2344 (transient--pop-keymap 'transient--redisplay-map) 2345 (unless transient--showp 2346 (let ((message-log-max nil)) 2347 (message ""))) 2348 (setq transient--transient-map nil) 2349 (setq transient--predicate-map nil) 2350 (setq transient--redisplay-map nil) 2351 (setq transient--redisplay-key nil) 2352 (setq transient--helpp nil) 2353 (setq transient--editp nil) 2354 (setq transient--prefix nil) 2355 (setq transient--layout nil) 2356 (setq transient--suffixes nil) 2357 (setq transient--original-window nil) 2358 (setq transient--original-buffer nil) 2359 (setq transient--window nil)) 2360 2361 (defun transient--delete-window () 2362 (when (window-live-p transient--window) 2363 (let ((remain-in-minibuffer-window 2364 (and (minibuffer-selected-window) 2365 (selected-window)))) 2366 ;; Only delete the window if it has never shown another buffer. 2367 (unless (eq (car (window-parameter transient--window 'quit-restore)) 2368 'other) 2369 (with-demoted-errors "Error while exiting transient: %S" 2370 (delete-window transient--window))) 2371 (when (buffer-live-p transient--buffer) 2372 (kill-buffer transient--buffer)) 2373 (setq transient--buffer nil) 2374 (when remain-in-minibuffer-window 2375 (select-window remain-in-minibuffer-window))))) 2376 2377 (defun transient--export () 2378 (setq transient-current-prefix transient--prefix) 2379 (setq transient-current-command (oref transient--prefix command)) 2380 (setq transient-current-suffixes transient--suffixes) 2381 (transient--history-push transient--prefix)) 2382 2383 (defun transient--suspend-override (&optional nohide) 2384 (transient--debug 'suspend-override) 2385 (transient--timer-cancel) 2386 (cond ((and (not nohide) transient-hide-during-minibuffer-read) 2387 (transient--delete-window)) 2388 ((and transient--prefix transient--redisplay-key) 2389 (setq transient--redisplay-key nil) 2390 (when transient--showp 2391 (if-let ((win (minibuffer-selected-window))) 2392 (with-selected-window win 2393 (transient--show)) 2394 (transient--show))))) 2395 (transient--pop-keymap 'transient--transient-map) 2396 (transient--pop-keymap 'transient--redisplay-map) 2397 (remove-hook 'pre-command-hook #'transient--pre-command) 2398 (remove-hook 'post-command-hook #'transient--post-command)) 2399 2400 (defun transient--resume-override (&optional _ignore) 2401 (transient--debug 'resume-override) 2402 (when (and transient--showp transient-hide-during-minibuffer-read) 2403 (transient--show)) 2404 (transient--push-keymap 'transient--transient-map) 2405 (transient--push-keymap 'transient--redisplay-map) 2406 (add-hook 'pre-command-hook #'transient--pre-command) 2407 (add-hook 'post-command-hook #'transient--post-command)) 2408 2409 (defun transient--recursive-edit (fn) 2410 (transient--debug 'recursive-edit) 2411 (if (not transient--prefix) 2412 (funcall fn) 2413 (transient--suspend-override (bound-and-true-p edebug-active)) 2414 (funcall fn) ; Already unwind protected. 2415 (cond ((memq this-command '(top-level abort-recursive-edit)) 2416 (setq transient--exitp t) 2417 (transient--post-exit) 2418 (transient--delete-window)) 2419 (transient--prefix 2420 (transient--resume-override))))) 2421 2422 (defmacro transient--with-suspended-override (&rest body) 2423 (let ((depth (make-symbol "depth")) 2424 (setup (make-symbol "setup")) 2425 (exit (make-symbol "exit"))) 2426 `(if (and transient--transient-map 2427 (memq transient--transient-map 2428 overriding-terminal-local-map)) 2429 (let ((,depth (1+ (minibuffer-depth))) ,setup ,exit) 2430 (setq ,setup 2431 (lambda () "@transient--with-suspended-override" 2432 (transient--debug 'minibuffer-setup) 2433 (remove-hook 'minibuffer-setup-hook ,setup) 2434 (transient--suspend-override))) 2435 (setq ,exit 2436 (lambda () "@transient--with-suspended-override" 2437 (transient--debug 'minibuffer-exit) 2438 (when (= (minibuffer-depth) ,depth) 2439 (transient--resume-override)))) 2440 (unwind-protect 2441 (progn 2442 (add-hook 'minibuffer-setup-hook ,setup) 2443 (add-hook 'minibuffer-exit-hook ,exit) 2444 ,@body) 2445 (remove-hook 'minibuffer-setup-hook ,setup) 2446 (remove-hook 'minibuffer-exit-hook ,exit))) 2447 ,@body))) 2448 2449 (static-if (>= emacs-major-version 30) ;transient--wrap-command 2450 (defun transient--wrap-command () 2451 (cl-assert 2452 (>= emacs-major-version 30) nil 2453 "Emacs was downgraded, making it necessary to recompile Transient") 2454 (letrec 2455 ((prefix transient--prefix) 2456 (suffix this-command) 2457 (advice 2458 (lambda (fn &rest args) 2459 (interactive 2460 (lambda (spec) 2461 (let ((abort t)) 2462 (unwind-protect 2463 (prog1 (let ((debugger #'transient--exit-and-debug)) 2464 (advice-eval-interactive-spec spec)) 2465 (setq abort nil)) 2466 (when abort 2467 (when-let ((unwind (oref prefix unwind-suffix))) 2468 (transient--debug 'unwind-interactive) 2469 (funcall unwind suffix)) 2470 (advice-remove suffix advice) 2471 (oset prefix unwind-suffix nil)))))) 2472 (unwind-protect 2473 (let ((debugger #'transient--exit-and-debug)) 2474 (apply fn args)) 2475 (when-let ((unwind (oref prefix unwind-suffix))) 2476 (transient--debug 'unwind-command) 2477 (funcall unwind suffix)) 2478 (advice-remove suffix advice) 2479 (oset prefix unwind-suffix nil))))) 2480 (when (symbolp this-command) 2481 (advice-add suffix :around advice '((depth . -99)))))) 2482 2483 (defun transient--wrap-command () 2484 (let* ((prefix transient--prefix) 2485 (suffix this-command) 2486 (advice nil) 2487 (advice-interactive 2488 (lambda (spec) 2489 (let ((abort t)) 2490 (unwind-protect 2491 (prog1 (let ((debugger #'transient--exit-and-debug)) 2492 (advice-eval-interactive-spec spec)) 2493 (setq abort nil)) 2494 (when abort 2495 (when-let ((unwind (oref prefix unwind-suffix))) 2496 (transient--debug 'unwind-interactive) 2497 (funcall unwind suffix)) 2498 (advice-remove suffix advice) 2499 (oset prefix unwind-suffix nil)))))) 2500 (advice-body 2501 (lambda (fn &rest args) 2502 (unwind-protect 2503 (let ((debugger #'transient--exit-and-debug)) 2504 (apply fn args)) 2505 (when-let ((unwind (oref prefix unwind-suffix))) 2506 (transient--debug 'unwind-command) 2507 (funcall unwind suffix)) 2508 (advice-remove suffix advice) 2509 (oset prefix unwind-suffix nil))))) 2510 (setq advice `(lambda (fn &rest args) 2511 (interactive ,advice-interactive) 2512 (apply ',advice-body fn args))) 2513 (when (symbolp this-command) 2514 (advice-add suffix :around advice '((depth . -99))))))) 2515 2516 (defun transient--premature-post-command () 2517 (and (equal (this-command-keys-vector) []) 2518 (= (minibuffer-depth) 2519 (1+ transient--minibuffer-depth)) 2520 (progn 2521 (transient--debug 'premature-post-command) 2522 (transient--suspend-override) 2523 (oset (or transient--prefix transient-current-prefix) 2524 unwind-suffix 2525 (if transient--exitp 2526 #'transient--post-exit 2527 #'transient--resume-override)) 2528 t))) 2529 2530 (defun transient--post-command () 2531 (unless (transient--premature-post-command) 2532 (transient--debug 'post-command) 2533 (transient--with-emergency-exit :post-command 2534 (cond (transient--exitp (transient--post-exit)) 2535 ;; If `this-command' is the current transient prefix, then we 2536 ;; have already taken care of updating the transient buffer... 2537 ((and (eq this-command (oref transient--prefix command)) 2538 ;; ... but if `prefix-arg' is non-nil, then the values 2539 ;; of `this-command' and `real-this-command' are untrue 2540 ;; because `prefix-command-preserve-state' changes them. 2541 ;; We cannot use `current-prefix-arg' because it is set 2542 ;; too late (in `command-execute'), and if it were set 2543 ;; earlier, then we likely still would not be able to 2544 ;; rely on it, and `prefix-command-preserve-state-hook' 2545 ;; would have to be used to record that a universal 2546 ;; argument is in effect. 2547 (not prefix-arg))) 2548 (transient--refreshp 2549 (transient--refresh-transient)) 2550 ((let ((old transient--redisplay-map) 2551 (new (transient--make-redisplay-map))) 2552 (unless (equal old new) 2553 (transient--pop-keymap 'transient--redisplay-map) 2554 (setq transient--redisplay-map new) 2555 (transient--push-keymap 'transient--redisplay-map)) 2556 (transient--redisplay))))))) 2557 2558 (defun transient--post-exit (&optional command) 2559 (transient--debug 'post-exit) 2560 (unless (and (eq transient--exitp 'replace) 2561 (or transient--prefix 2562 ;; The current command could act as a prefix, 2563 ;; but decided not to call `transient-setup', 2564 ;; or it is prevented from doing so because it 2565 ;; uses the minibuffer and the user aborted 2566 ;; that. 2567 (prog1 nil 2568 (if (let ((obj (transient-suffix-object command))) 2569 (and (slot-boundp obj 'transient) 2570 (oref obj transient))) 2571 ;; This sub-prefix is a transient suffix; 2572 ;; go back to outer prefix, by calling 2573 ;; `transient--stack-pop' further down. 2574 (setq transient--exitp nil) 2575 (transient--stack-zap))))) 2576 (remove-hook 'pre-command-hook #'transient--pre-command) 2577 (remove-hook 'post-command-hook #'transient--post-command) 2578 (advice-remove 'recursive-edit #'transient--recursive-edit)) 2579 (setq transient-current-prefix nil) 2580 (setq transient-current-command nil) 2581 (setq transient-current-suffixes nil) 2582 (let ((resume (and transient--stack 2583 (not (memq transient--exitp '(replace suspend)))))) 2584 (unless (or resume (eq transient--exitp 'replace)) 2585 (setq transient--showp nil)) 2586 (setq transient--exitp nil) 2587 (setq transient--helpp nil) 2588 (setq transient--editp nil) 2589 (setq transient--all-levels-p nil) 2590 (setq transient--minibuffer-depth 0) 2591 (run-hooks 'transient-exit-hook) 2592 (when resume 2593 (transient--stack-pop)))) 2594 2595 (defun transient--stack-push () 2596 (transient--debug 'stack-push) 2597 (push (list (oref transient--prefix command) 2598 transient--layout 2599 transient--editp 2600 :transient-suffix (oref transient--prefix transient-suffix) 2601 :scope (oref transient--prefix scope)) 2602 transient--stack)) 2603 2604 (defun transient--stack-pop () 2605 (transient--debug 'stack-pop) 2606 (and transient--stack 2607 (prog1 t (apply #'transient-setup (pop transient--stack))))) 2608 2609 (defun transient--stack-zap () 2610 (transient--debug 'stack-zap) 2611 (setq transient--stack nil)) 2612 2613 (defun transient--redisplay () 2614 (if (or (eq transient-show-popup t) 2615 transient--showp) 2616 (unless 2617 (or (memq this-command transient--scroll-commands) 2618 (and (or (memq this-command '(mouse-drag-region 2619 mouse-set-region)) 2620 (equal (key-description (this-command-keys-vector)) 2621 "<mouse-movement>")) 2622 (and (eq (current-buffer) transient--buffer)))) 2623 (transient--show)) 2624 (when (and (numberp transient-show-popup) 2625 (not (zerop transient-show-popup)) 2626 (not transient--timer)) 2627 (transient--timer-start)) 2628 (transient--show-brief))) 2629 2630 (defun transient--timer-start () 2631 (setq transient--timer 2632 (run-at-time (abs transient-show-popup) nil 2633 (lambda () 2634 (transient--timer-cancel) 2635 (transient--show) 2636 (let ((message-log-max nil)) 2637 (message "")))))) 2638 2639 (defun transient--timer-cancel () 2640 (when transient--timer 2641 (cancel-timer transient--timer) 2642 (setq transient--timer nil))) 2643 2644 (defun transient--debug (arg &rest args) 2645 (when transient--debug 2646 (let ((inhibit-message (not (eq transient--debug 'message)))) 2647 (if (symbolp arg) 2648 (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)" 2649 arg 2650 (cond ((and (symbolp this-command) this-command)) 2651 ((fboundp 'help-fns-function-name) 2652 (help-fns-function-name this-command)) 2653 ((byte-code-function-p this-command) 2654 "#[...]") 2655 (this-command)) 2656 (key-description (this-command-keys-vector)) 2657 transient--exitp 2658 (cond ((keywordp (car args)) 2659 (format ", from: %s" 2660 (substring (symbol-name (car args)) 1))) 2661 ((stringp (car args)) 2662 (concat ", " (apply #'format args))) 2663 ((functionp (car args)) 2664 (concat ", " (apply (car args) (cdr args)))) 2665 (""))) 2666 (apply #'message arg args))))) 2667 2668 (defun transient--emergency-exit (&optional id) 2669 "Exit the current transient command after an error occurred. 2670 When no transient is active (i.e., when `transient--prefix' is 2671 nil) then do nothing. Optional ID is a keyword identifying the 2672 exit." 2673 (transient--debug 'emergency-exit id) 2674 (when transient--prefix 2675 (setq transient--stack nil) 2676 (setq transient--exitp t) 2677 (transient--pre-exit) 2678 (transient--post-exit))) 2679 2680 ;;; Pre-Commands 2681 2682 (defun transient--call-pre-command () 2683 (if-let ((fn (transient--get-pre-command this-command))) 2684 (let ((action (funcall fn))) 2685 (when (eq action transient--exit) 2686 (setq transient--exitp (or transient--exitp t))) 2687 action) 2688 (if (let ((keys (this-command-keys-vector))) 2689 (eq (aref keys (1- (length keys))) ?\C-g)) 2690 (setq this-command 'transient-noop) 2691 (unless (transient--edebug-command-p) 2692 (setq this-command 'transient-undefined))) 2693 transient--stay)) 2694 2695 (defun transient--get-pre-command (&optional cmd enforce-type) 2696 (or (and (not (eq enforce-type 'non-suffix)) 2697 (symbolp cmd) 2698 (lookup-key transient--predicate-map (vector cmd))) 2699 (and (not (eq enforce-type 'suffix)) 2700 (transient--resolve-pre-command 2701 (oref transient--prefix transient-non-suffix) 2702 t)))) 2703 2704 (defun transient--resolve-pre-command (pre &optional resolve-boolean) 2705 (cond ((booleanp pre) 2706 (if resolve-boolean 2707 (if pre #'transient--do-stay #'transient--do-warn) 2708 pre)) 2709 ((string-match-p "--do-" (symbol-name pre)) pre) 2710 ((let ((sym (intern (format "transient--do-%s" pre)))) 2711 (if (functionp sym) sym pre))))) 2712 2713 (defun transient--do-stay () 2714 "Call the command without exporting variables and stay transient." 2715 transient--stay) 2716 2717 (defun transient--do-noop () 2718 "Call `transient-noop' and stay transient." 2719 (setq this-command 'transient-noop) 2720 transient--stay) 2721 2722 (defun transient--do-warn () 2723 "Call `transient-undefined' and stay transient." 2724 (setq this-command 'transient-undefined) 2725 transient--stay) 2726 2727 (defun transient--do-warn-inapt () 2728 "Call `transient-inapt' and stay transient." 2729 (setq this-command 'transient-inapt) 2730 transient--stay) 2731 2732 (defun transient--do-call () 2733 "Call the command after exporting variables and stay transient." 2734 (transient--export) 2735 transient--stay) 2736 2737 (defun transient--do-return () 2738 "Call the command after exporting variables and return to parent prefix. 2739 If there is no parent prefix, then behave like `transient--do-exit'." 2740 (if (not transient--stack) 2741 (transient--do-exit) 2742 (transient--export) 2743 transient--exit)) 2744 2745 (defun transient--do-exit () 2746 "Call the command after exporting variables and exit the transient." 2747 (transient--export) 2748 (transient--stack-zap) 2749 transient--exit) 2750 2751 (defun transient--do-leave () 2752 "Call the command without exporting variables and exit the transient." 2753 (transient--stack-zap) 2754 transient--exit) 2755 2756 (defun transient--do-push-button () 2757 "Call the command represented by the activated button. 2758 Use that command's pre-command to determine transient behavior." 2759 (if (and (mouse-event-p last-command-event) 2760 (not (eq (posn-window (event-start last-command-event)) 2761 transient--window))) 2762 transient--stay 2763 (setq this-command 2764 (with-selected-window transient--window 2765 (get-text-property (if (mouse-event-p last-command-event) 2766 (posn-point (event-start last-command-event)) 2767 (point)) 2768 'command))) 2769 (transient--call-pre-command))) 2770 2771 (defun transient--do-recurse () 2772 "Call the transient prefix command, preparing for return to active transient. 2773 If there is no parent prefix, then just call the command." 2774 (transient--do-stack)) 2775 2776 (defun transient--setup-recursion (prefix-obj) 2777 (when transient--stack 2778 (let ((command (oref prefix-obj command))) 2779 (when-let ((suffix-obj (transient-suffix-object command))) 2780 (when (memq (if (slot-boundp suffix-obj 'transient) 2781 (oref suffix-obj transient) 2782 (oref transient-current-prefix transient-suffix)) 2783 (list t #'transient--do-recurse)) 2784 (oset prefix-obj transient-suffix t)))))) 2785 2786 (defun transient--do-stack () 2787 "Call the transient prefix command, stacking the active transient. 2788 Push the active transient to the transient stack." 2789 (transient--export) 2790 (transient--stack-push) 2791 (setq transient--exitp 'replace) 2792 transient--exit) 2793 2794 (defun transient--do-replace () 2795 "Call the transient prefix command, replacing the active transient. 2796 Do not push the active transient to the transient stack." 2797 (transient--export) 2798 (setq transient--exitp 'replace) 2799 transient--exit) 2800 2801 (defun transient--do-suspend () 2802 "Suspend the active transient, saving the transient stack." 2803 (transient--stack-push) 2804 (setq transient--exitp 'suspend) 2805 transient--exit) 2806 2807 (defun transient--do-quit-one () 2808 "If active, quit help or edit mode, else exit the active transient." 2809 (cond (transient--helpp 2810 (setq transient--helpp nil) 2811 transient--stay) 2812 (transient--editp 2813 (setq transient--editp nil) 2814 (transient-setup) 2815 transient--stay) 2816 (prefix-arg 2817 transient--stay) 2818 (transient--exit))) 2819 2820 (defun transient--do-quit-all () 2821 "Exit all transients without saving the transient stack." 2822 (transient--stack-zap) 2823 transient--exit) 2824 2825 (defun transient--do-move () 2826 "Call the command if `transient-enable-popup-navigation' is non-nil. 2827 In that case behave like `transient--do-stay', otherwise similar 2828 to `transient--do-warn'." 2829 (unless transient-enable-popup-navigation 2830 (setq this-command 'transient-inhibit-move)) 2831 transient--stay) 2832 2833 (defun transient--do-minus () 2834 "Call `negative-argument' or pivot to `transient-update'. 2835 If `negative-argument' is invoked using \"-\" then preserve the 2836 prefix argument and pivot to `transient-update'." 2837 (when (equal (this-command-keys) "-") 2838 (setq this-command 'transient-update)) 2839 transient--stay) 2840 2841 (put 'transient--do-stay 'transient-face 'transient-key-stay) 2842 (put 'transient--do-noop 'transient-face 'transient-key-noop) 2843 (put 'transient--do-warn 'transient-face 'transient-key-noop) 2844 (put 'transient--do-warn-inapt 'transient-face 'transient-key-noop) 2845 (put 'transient--do-call 'transient-face 'transient-key-stay) 2846 (put 'transient--do-return 'transient-face 'transient-key-return) 2847 (put 'transient--do-exit 'transient-face 'transient-key-exit) 2848 (put 'transient--do-leave 'transient-face 'transient-key-exit) 2849 2850 (put 'transient--do-recurse 'transient-face 'transient-key-stay) 2851 (put 'transient--do-stack 'transient-face 'transient-key-stay) 2852 (put 'transient--do-replace 'transient-face 'transient-key-exit) 2853 (put 'transient--do-suspend 'transient-face 'transient-key-exit) 2854 2855 (put 'transient--do-quit-one 'transient-face 'transient-key-return) 2856 (put 'transient--do-quit-all 'transient-face 'transient-key-exit) 2857 (put 'transient--do-move 'transient-face 'transient-key-stay) 2858 (put 'transient--do-minus 'transient-face 'transient-key-stay) 2859 2860 ;;; Commands 2861 ;;;; Noop 2862 2863 (defun transient-noop () 2864 "Do nothing at all." 2865 (interactive)) 2866 2867 (defun transient-undefined () 2868 "Warn the user that the pressed key is not bound to any suffix." 2869 (interactive) 2870 (transient--invalid "Unbound suffix")) 2871 2872 (defun transient-inapt () 2873 "Warn the user that the invoked command is inapt." 2874 (interactive) 2875 (transient--invalid "Inapt command")) 2876 2877 (defun transient--invalid (msg) 2878 (ding) 2879 (message "%s: `%s' (Use `%s' to abort, `%s' for help)%s" 2880 msg 2881 (propertize (key-description (this-single-command-keys)) 2882 'face 'font-lock-warning-face) 2883 (propertize "C-g" 'face 'transient-key) 2884 (propertize "?" 'face 'transient-key) 2885 ;; `this-command' is `transient-undefined' or `transient-inapt'. 2886 ;; Show the command (`this-original-command') the user actually 2887 ;; tried to invoke. 2888 (if-let ((cmd (or (ignore-errors (symbol-name this-original-command)) 2889 (ignore-errors (symbol-name this-command))))) 2890 (format " [%s]" (propertize cmd 'face 'font-lock-warning-face)) 2891 "")) 2892 (unless (and transient--transient-map 2893 (memq transient--transient-map overriding-terminal-local-map)) 2894 (let ((transient--prefix (or transient--prefix 'sic))) 2895 (transient--emergency-exit)) 2896 (view-lossage) 2897 (other-window 1) 2898 (display-warning 'transient "Inconsistent transient state detected. 2899 This should never happen. 2900 Please open an issue and post the shown command log." :error))) 2901 2902 (defun transient-inhibit-move () 2903 "Warn the user that popup navigation is disabled." 2904 (interactive) 2905 (message "To enable use of `%s', please customize `%s'" 2906 this-original-command 2907 'transient-enable-popup-navigation)) 2908 2909 ;;;; Core 2910 2911 (defun transient-quit-all () 2912 "Exit all transients without saving the transient stack." 2913 (interactive)) 2914 2915 (defun transient-quit-one () 2916 "Exit the current transients, returning to outer transient, if any." 2917 (interactive)) 2918 2919 (defun transient-quit-seq () 2920 "Abort the current incomplete key sequence." 2921 (interactive)) 2922 2923 (defun transient-update () 2924 "Redraw the transient's state in the popup buffer." 2925 (interactive) 2926 (setq prefix-arg current-prefix-arg)) 2927 2928 (defun transient-show () 2929 "Show the transient's state in the popup buffer." 2930 (interactive) 2931 (setq transient--showp t)) 2932 2933 (defun transient-push-button () 2934 "Invoke the suffix command represented by this button." 2935 (interactive)) 2936 2937 ;;;; Suspend 2938 2939 (defun transient-suspend () 2940 "Suspend the current transient. 2941 It can later be resumed using `transient-resume', while no other 2942 transient is active." 2943 (interactive)) 2944 2945 (define-minor-mode transient-resume-mode 2946 "Auxiliary minor-mode used to resume a transient after viewing help.") 2947 2948 (defun transient-resume () 2949 "Resume a previously suspended stack of transients." 2950 (interactive) 2951 (cond (transient--stack 2952 (let ((winconf transient--restore-winconf)) 2953 (kill-local-variable 'transient--restore-winconf) 2954 (when transient-resume-mode 2955 (transient-resume-mode -1) 2956 (quit-window)) 2957 (when winconf 2958 (set-window-configuration winconf))) 2959 (transient--stack-pop)) 2960 (transient-resume-mode 2961 (kill-local-variable 'transient--restore-winconf) 2962 (transient-resume-mode -1) 2963 (quit-window)) 2964 (t 2965 (message "No suspended transient command")))) 2966 2967 ;;;; Help 2968 2969 (defun transient-help (&optional interactive) 2970 "Show help for the active transient or one of its suffixes.\n\n(fn)" 2971 (interactive (list t)) 2972 (if interactive 2973 (setq transient--helpp t) 2974 (with-demoted-errors "transient-help: %S" 2975 (when (lookup-key transient--transient-map 2976 (this-single-command-raw-keys)) 2977 (setq transient--helpp nil) 2978 (let ((winconf (current-window-configuration))) 2979 (transient-show-help 2980 (if (eq this-original-command 'transient-help) 2981 transient--prefix 2982 (or (transient-suffix-object) 2983 this-original-command))) 2984 (setq-local transient--restore-winconf winconf)) 2985 (fit-window-to-buffer nil (frame-height) (window-height)) 2986 (transient-resume-mode) 2987 (message (substitute-command-keys 2988 "Type \\`q' to resume transient command.")) 2989 t)))) 2990 2991 ;;;; Level 2992 2993 (defun transient-set-level (&optional command level) 2994 "Set the level of the transient or one of its suffix commands." 2995 (interactive 2996 (let ((command this-original-command) 2997 (prefix (oref transient--prefix command))) 2998 (and (or (not (eq command 'transient-set-level)) 2999 (and transient--editp 3000 (setq command prefix))) 3001 (list command 3002 (let ((keys (this-single-command-raw-keys))) 3003 (and (lookup-key transient--transient-map keys) 3004 (progn 3005 (transient--show) 3006 (string-to-number 3007 (transient--read-number-N 3008 (format "Set level for `%s': " command) 3009 nil nil (not (eq command prefix))))))))))) 3010 (cond 3011 ((not command) 3012 (setq transient--editp t) 3013 (transient-setup)) 3014 (level 3015 (let* ((prefix (oref transient--prefix command)) 3016 (alist (alist-get prefix transient-levels)) 3017 (akey command)) 3018 (cond ((eq command prefix) 3019 (oset transient--prefix level level) 3020 (setq akey t)) 3021 (t 3022 (oset (transient-suffix-object command) level level) 3023 (when (cdr (cl-remove-if-not (lambda (obj) 3024 (eq (oref obj command) command)) 3025 transient--suffixes)) 3026 (setq akey (cons command (this-command-keys)))))) 3027 (setf (alist-get akey alist) level) 3028 (setf (alist-get prefix transient-levels) alist)) 3029 (transient-save-levels) 3030 (transient--show)) 3031 (t 3032 (transient-undefined)))) 3033 3034 (transient-define-suffix transient-toggle-level-limit () 3035 "Toggle whether to temporarily displayed suffixes on all levels." 3036 :description 3037 (lambda () 3038 (cond 3039 ((= transient-default-level transient--max-level) 3040 "Always displaying all levels") 3041 (transient--all-levels-p 3042 (format "Hide suffix %s" 3043 (propertize 3044 (format "levels > %s" (oref (transient-prefix-object) level)) 3045 'face 'transient-higher-level))) 3046 ("Show all suffix levels"))) 3047 :inapt-if (lambda () (= transient-default-level transient--max-level)) 3048 :transient t 3049 (interactive) 3050 (setq transient--all-levels-p (not transient--all-levels-p)) 3051 (setq transient--refreshp t)) 3052 3053 ;;;; Value 3054 3055 (defun transient-set () 3056 "Set active transient's value for this Emacs session." 3057 (interactive) 3058 (transient-set-value (transient-prefix-object))) 3059 3060 (defalias 'transient-set-and-exit #'transient-set 3061 "Set active transient's value for this Emacs session and exit.") 3062 3063 (defun transient-save () 3064 "Save active transient's value for this and future Emacs sessions." 3065 (interactive) 3066 (transient-save-value (transient-prefix-object))) 3067 3068 (defalias 'transient-save-and-exit #'transient-save 3069 "Save active transient's value for this and future Emacs sessions and exit.") 3070 3071 (defun transient-reset () 3072 "Clear the set and saved values of the active transient." 3073 (interactive) 3074 (transient-reset-value (transient-prefix-object))) 3075 3076 (defun transient-history-next () 3077 "Switch to the next value used for the active transient." 3078 (interactive) 3079 (let* ((obj transient--prefix) 3080 (pos (1- (oref obj history-pos))) 3081 (hst (oref obj history))) 3082 (if (< pos 0) 3083 (user-error "End of history") 3084 (oset obj history-pos pos) 3085 (oset obj value (nth pos hst)) 3086 (mapc #'transient-init-value transient--suffixes)))) 3087 3088 (defun transient-history-prev () 3089 "Switch to the previous value used for the active transient." 3090 (interactive) 3091 (let* ((obj transient--prefix) 3092 (pos (1+ (oref obj history-pos))) 3093 (hst (oref obj history)) 3094 (len (length hst))) 3095 (if (> pos (1- len)) 3096 (user-error "End of history") 3097 (oset obj history-pos pos) 3098 (oset obj value (nth pos hst)) 3099 (mapc #'transient-init-value transient--suffixes)))) 3100 3101 (transient-define-suffix transient-preset () 3102 "Put this preset into action." 3103 :class transient-value-preset 3104 (interactive) 3105 (transient-prefix-set (oref (transient-suffix-object) set))) 3106 3107 ;;;; Auxiliary 3108 3109 (defun transient-toggle-common () 3110 "Toggle whether common commands are permanently shown." 3111 (interactive) 3112 (setq transient-show-common-commands (not transient-show-common-commands))) 3113 3114 (defun transient-toggle-debug () 3115 "Toggle debugging statements for transient commands." 3116 (interactive) 3117 (setq transient--debug (not transient--debug)) 3118 (message "Debugging transient %s" 3119 (if transient--debug "enabled" "disabled"))) 3120 3121 (transient-define-suffix transient-echo-arguments (arguments) 3122 "Show the transient's active ARGUMENTS in the echo area. 3123 Intended for use in prefixes used for demonstration purposes, 3124 such as when suggesting a new feature or reporting an issue." 3125 :transient t 3126 :description "Echo arguments" 3127 :key "x" 3128 (interactive (list (transient-args transient-current-command))) 3129 (message "%s: %s" 3130 (key-description (this-command-keys)) 3131 (mapconcat (lambda (arg) 3132 (propertize (if (string-match-p " " arg) 3133 (format "%S" arg) 3134 arg) 3135 'face 'transient-argument)) 3136 arguments " "))) 3137 3138 ;;; Value 3139 ;;;; Init 3140 3141 (cl-defgeneric transient-init-scope (obj) 3142 "Set the scope of the suffix object OBJ. 3143 3144 The scope is actually a property of the transient prefix, not of 3145 individual suffixes. However it is possible to invoke a suffix 3146 command directly instead of from a transient. In that case, if 3147 the suffix expects a scope, then it has to determine that itself 3148 and store it in its `scope' slot. 3149 3150 This function is called for all suffix commands, but unless a 3151 concrete method is implemented this falls through to the default 3152 implementation, which is a noop.") 3153 3154 (cl-defmethod transient-init-scope ((_ transient-suffix)) 3155 "Noop." nil) 3156 3157 (cl-defgeneric transient-init-value (_) 3158 "Set the initial value of the object OBJ. 3159 3160 This function is called for all prefix and suffix commands. 3161 3162 For suffix commands (including infix argument commands) the 3163 default implementation is a noop. Classes derived from the 3164 abstract `transient-infix' class must implement this function. 3165 Non-infix suffix commands usually don't have a value." 3166 nil) 3167 3168 (cl-defmethod transient-init-value :around ((obj transient-prefix)) 3169 "If bound, then call OBJ's `init-value' function. 3170 Otherwise call the primary method according to object's class." 3171 (if (slot-boundp obj 'init-value) 3172 (funcall (oref obj init-value) obj) 3173 (cl-call-next-method obj))) 3174 3175 (cl-defmethod transient-init-value :around ((obj transient-infix)) 3176 "If bound, then call OBJ's `init-value' function. 3177 Otherwise call the primary method according to object's class." 3178 (if (slot-boundp obj 'init-value) 3179 (funcall (oref obj init-value) obj) 3180 (cl-call-next-method obj))) 3181 3182 (cl-defmethod transient-init-value ((obj transient-prefix)) 3183 (if (slot-boundp obj 'value) 3184 (oref obj value) 3185 (oset obj value 3186 (if-let ((saved (assq (oref obj command) transient-values))) 3187 (cdr saved) 3188 (transient-default-value obj))))) 3189 3190 (cl-defmethod transient-init-value ((obj transient-argument)) 3191 (oset obj value 3192 (let ((value (oref transient--prefix value)) 3193 (argument (and (slot-boundp obj 'argument) 3194 (oref obj argument))) 3195 (multi-value (oref obj multi-value)) 3196 (case-fold-search nil) 3197 (regexp (if (slot-exists-p obj 'argument-regexp) 3198 (oref obj argument-regexp) 3199 (format "\\`%s\\(.*\\)" (oref obj argument))))) 3200 (if (memq multi-value '(t rest)) 3201 (cdr (assoc argument value)) 3202 (let ((match (lambda (v) 3203 (and (stringp v) 3204 (string-match regexp v) 3205 (match-string 1 v))))) 3206 (if multi-value 3207 (delq nil (mapcar match value)) 3208 (cl-some match value))))))) 3209 3210 (cl-defmethod transient-init-value ((obj transient-switch)) 3211 (oset obj value 3212 (car (member (oref obj argument) 3213 (oref transient--prefix value))))) 3214 3215 ;;;; Default 3216 3217 (cl-defgeneric transient-default-value (_) 3218 "Return the default value." 3219 nil) 3220 3221 (cl-defmethod transient-default-value ((obj transient-prefix)) 3222 (if-let ((default (and (slot-boundp obj 'default-value) 3223 (oref obj default-value)))) 3224 (if (functionp default) 3225 (funcall default) 3226 default) 3227 nil)) 3228 3229 ;;;; Read 3230 3231 (cl-defgeneric transient-infix-read (obj) 3232 "Determine the new value of the infix object OBJ. 3233 3234 This function merely determines the value; `transient-infix-set' 3235 is used to actually store the new value in the object. 3236 3237 For most infix classes this is done by reading a value from the 3238 user using the reader specified by the `reader' slot (using the 3239 `transient-infix' method described below). 3240 3241 For some infix classes the value is changed without reading 3242 anything in the minibuffer, i.e., the mere act of invoking the 3243 infix command determines what the new value should be, based 3244 on the previous value.") 3245 3246 (cl-defmethod transient-infix-read :around ((obj transient-infix)) 3247 "Refresh the transient buffer and call the next method. 3248 3249 Also wrap `cl-call-next-method' with two macros: 3250 - `transient--with-suspended-override' allows use of minibuffer. 3251 - `transient--with-emergency-exit' arranges for the transient to 3252 be exited in case of an error." 3253 (transient--show) 3254 (transient--with-emergency-exit :infix-read 3255 (transient--with-suspended-override 3256 (cl-call-next-method obj)))) 3257 3258 (cl-defmethod transient-infix-read ((obj transient-infix)) 3259 "Read a value while taking care of history. 3260 3261 This method is suitable for a wide variety of infix commands, 3262 including but not limited to inline arguments and variables. 3263 3264 If you do not use this method for your own infix class, then 3265 you should likely replicate a lot of the behavior of this 3266 method. If you fail to do so, then users might not appreciate 3267 the lack of history, for example. 3268 3269 Only for very simple classes that toggle or cycle through a very 3270 limited number of possible values should you replace this with a 3271 simple method that does not handle history. (E.g., for a command 3272 line switch the only possible values are \"use it\" and \"don't use 3273 it\", in which case it is pointless to preserve history.)" 3274 (with-slots (value multi-value always-read allow-empty choices) obj 3275 (if (and value 3276 (not multi-value) 3277 (not always-read) 3278 transient--prefix) 3279 (oset obj value nil) 3280 (let* ((enable-recursive-minibuffers t) 3281 (reader (oref obj reader)) 3282 (choices (if (functionp choices) (funcall choices) choices)) 3283 (prompt (transient-prompt obj)) 3284 (value (if multi-value (mapconcat #'identity value ",") value)) 3285 (history-key (or (oref obj history-key) 3286 (oref obj command))) 3287 (transient--history (alist-get history-key transient-history)) 3288 (transient--history (if (or (null value) 3289 (eq value (car transient--history))) 3290 transient--history 3291 (cons value transient--history))) 3292 (initial-input (and transient-read-with-initial-input 3293 (car transient--history))) 3294 (history (if initial-input 3295 (cons 'transient--history 1) 3296 'transient--history)) 3297 (value 3298 (cond 3299 (reader (funcall reader prompt initial-input history)) 3300 (multi-value 3301 (completing-read-multiple prompt choices nil nil 3302 initial-input history)) 3303 (choices 3304 (completing-read prompt choices nil t initial-input history)) 3305 ((read-string prompt initial-input history))))) 3306 (cond ((and (equal value "") (not allow-empty)) 3307 (setq value nil)) 3308 ((and (equal value "\"\"") allow-empty) 3309 (setq value ""))) 3310 (when value 3311 (when (and (bound-and-true-p ivy-mode) 3312 (stringp (car transient--history))) 3313 (set-text-properties 0 (length (car transient--history)) nil 3314 (car transient--history))) 3315 (setf (alist-get history-key transient-history) 3316 (delete-dups transient--history))) 3317 value)))) 3318 3319 (cl-defmethod transient-infix-read ((obj transient-switch)) 3320 "Toggle the switch on or off." 3321 (if (oref obj value) nil (oref obj argument))) 3322 3323 (cl-defmethod transient-infix-read ((obj transient-switches)) 3324 "Cycle through the mutually exclusive switches. 3325 The last value is \"don't use any of these switches\"." 3326 (let ((choices (mapcar (apply-partially #'format (oref obj argument-format)) 3327 (oref obj choices)))) 3328 (if-let ((value (oref obj value))) 3329 (cadr (member value choices)) 3330 (car choices)))) 3331 3332 (cl-defmethod transient-infix-read ((command symbol)) 3333 "Elsewhere use the reader of the infix command COMMAND. 3334 Use this if you want to share an infix's history with a regular 3335 stand-alone command." 3336 (if-let ((obj (transient--suffix-prototype command))) 3337 (cl-letf (((symbol-function #'transient--show) #'ignore)) 3338 (transient-infix-read obj)) 3339 (error "Not a suffix command: `%s'" command))) 3340 3341 ;;;; Readers 3342 3343 (defun transient-read-file (prompt _initial-input _history) 3344 "Read a file." 3345 (file-local-name (expand-file-name (read-file-name prompt)))) 3346 3347 (defun transient-read-existing-file (prompt _initial-input _history) 3348 "Read an existing file." 3349 (file-local-name (expand-file-name (read-file-name prompt nil nil t)))) 3350 3351 (defun transient-read-directory (prompt _initial-input _history) 3352 "Read a directory." 3353 (file-local-name (expand-file-name (read-directory-name prompt)))) 3354 3355 (defun transient-read-existing-directory (prompt _initial-input _history) 3356 "Read an existing directory." 3357 (file-local-name (expand-file-name (read-directory-name prompt nil nil t)))) 3358 3359 (defun transient-read-number-N0 (prompt initial-input history) 3360 "Read a natural number (including zero) and return it as a string." 3361 (transient--read-number-N prompt initial-input history t)) 3362 3363 (defun transient-read-number-N+ (prompt initial-input history) 3364 "Read a natural number (excluding zero) and return it as a string." 3365 (transient--read-number-N prompt initial-input history nil)) 3366 3367 (defun transient--read-number-N (prompt initial-input history include-zero) 3368 (save-match-data 3369 (cl-block nil 3370 (while t 3371 (let ((str (read-from-minibuffer prompt initial-input nil nil history))) 3372 (when (or (string-equal str "") 3373 (string-match-p (if include-zero 3374 "\\`\\(0\\|[1-9][0-9]*\\)\\'" 3375 "\\`[1-9][0-9]*\\'") 3376 str)) 3377 (cl-return str))) 3378 (message "Please enter a natural number (%s zero)." 3379 (if include-zero "including" "excluding")) 3380 (sit-for 1))))) 3381 3382 (defun transient-read-date (prompt default-time _history) 3383 "Read a date using `org-read-date' (which see)." 3384 (require 'org) 3385 (when (fboundp 'org-read-date) 3386 (org-read-date 'with-time nil nil prompt default-time))) 3387 3388 ;;;; Prompt 3389 3390 (cl-defgeneric transient-prompt (obj) 3391 "Return the prompt to be used to read infix object OBJ's value.") 3392 3393 (cl-defmethod transient-prompt ((obj transient-infix)) 3394 "Return the prompt to be used to read infix object OBJ's value. 3395 3396 This implementation should be suitable for almost all infix 3397 commands. 3398 3399 If the value of OBJ's `prompt' slot is non-nil, then it must be 3400 a string or a function. If it is a string, then use that. If 3401 it is a function, then call that with OBJ as the only argument. 3402 That function must return a string, which is then used as the 3403 prompt. 3404 3405 Otherwise, if the value of either the `argument' or `variable' 3406 slot of OBJ is a string, then base the prompt on that (preferring 3407 the former), appending either \"=\" (if it appears to be a 3408 command-line option) or \": \". 3409 3410 Finally fall through to using \"(BUG: no prompt): \" as the 3411 prompt." 3412 (if-let ((prompt (oref obj prompt))) 3413 (let ((prompt (if (functionp prompt) 3414 (funcall prompt obj) 3415 prompt))) 3416 (if (stringp prompt) 3417 prompt 3418 "(BUG: no prompt): ")) 3419 (or (and-let* ((arg (and (slot-boundp obj 'argument) (oref obj argument)))) 3420 (if (and (stringp arg) (string-suffix-p "=" arg)) 3421 arg 3422 (concat arg ": "))) 3423 (and-let* ((var (and (slot-boundp obj 'variable) (oref obj variable)))) 3424 (and (stringp var) 3425 (concat var ": "))) 3426 "(BUG: no prompt): "))) 3427 3428 ;;;; Set 3429 3430 (cl-defgeneric transient-infix-set (obj value) 3431 "Set the value of infix object OBJ to VALUE.") 3432 3433 (cl-defmethod transient-infix-set ((obj transient-infix) value) 3434 "Set the value of infix object OBJ to VALUE." 3435 (oset obj value value)) 3436 3437 (cl-defmethod transient-infix-set :after ((obj transient-argument) value) 3438 "Unset incompatible infix arguments." 3439 (when-let* ((value) 3440 (val (transient-infix-value obj)) 3441 (arg (if (slot-boundp obj 'argument) 3442 (oref obj argument) 3443 (oref obj argument-format))) 3444 (spec (oref transient--prefix incompatible)) 3445 (filter (lambda (x rule) 3446 (and (member x rule) 3447 (remove x rule)))) 3448 (incomp (nconc 3449 (cl-mapcan (apply-partially filter arg) spec) 3450 (and (not (equal val arg)) 3451 (cl-mapcan (apply-partially filter val) spec))))) 3452 (dolist (obj transient--suffixes) 3453 (when-let* (((cl-typep obj 'transient-argument)) 3454 (val (transient-infix-value obj)) 3455 (arg (if (slot-boundp obj 'argument) 3456 (oref obj argument) 3457 (oref obj argument-format))) 3458 ((if (equal val arg) 3459 (member arg incomp) 3460 (or (member val incomp) 3461 (member arg incomp))))) 3462 (transient-infix-set obj nil))))) 3463 3464 (defun transient-prefix-set (value) 3465 "Set the value of the active transient prefix to VALUE. 3466 Intended for use by transient suffix commands." 3467 (oset transient--prefix value value) 3468 (setq transient--refreshp 'updated-value)) 3469 3470 (cl-defgeneric transient-set-value (obj) 3471 "Persist the value of the transient prefix OBJ. 3472 Only intended for use by `transient-set'. 3473 Also see `transient-prefix-set'.") 3474 3475 (cl-defmethod transient-set-value ((obj transient-prefix)) 3476 (oset (oref obj prototype) value (transient-get-value)) 3477 (transient--history-push obj)) 3478 3479 ;;;; Save 3480 3481 (cl-defgeneric transient-save-value (obj) 3482 "Save the value of the transient prefix OBJ.") 3483 3484 (cl-defmethod transient-save-value ((obj transient-prefix)) 3485 (let ((value (transient-get-value))) 3486 (oset (oref obj prototype) value value) 3487 (setf (alist-get (oref obj command) transient-values) value) 3488 (transient-save-values)) 3489 (transient--history-push obj)) 3490 3491 ;;;; Reset 3492 3493 (cl-defgeneric transient-reset-value (obj) 3494 "Clear the set and saved values of the transient prefix OBJ.") 3495 3496 (cl-defmethod transient-reset-value ((obj transient-prefix)) 3497 (let ((value (transient-default-value obj))) 3498 (oset obj value value) 3499 (oset (oref obj prototype) value value) 3500 (setf (alist-get (oref obj command) transient-values nil 'remove) nil) 3501 (transient-save-values)) 3502 (transient--history-push obj) 3503 (mapc #'transient-init-value transient--suffixes)) 3504 3505 ;;;; Get 3506 3507 (defun transient-args (prefix) 3508 "Return the value of the transient prefix command PREFIX. 3509 If the current command was invoked from the transient prefix 3510 command PREFIX, then return the active infix arguments. If 3511 the current command was not invoked from PREFIX, then return 3512 the set, saved or default value for PREFIX." 3513 (cl-mapcan #'transient--get-wrapped-value (transient-suffixes prefix))) 3514 3515 (defun transient-suffixes (prefix) 3516 "Return the suffix objects of the transient prefix command PREFIX." 3517 (if (eq transient-current-command prefix) 3518 transient-current-suffixes 3519 (let ((transient--prefix (transient--init-prefix prefix))) 3520 (transient--flatten-suffixes 3521 (transient--init-suffixes prefix))))) 3522 3523 (defun transient-get-value () 3524 (transient--with-emergency-exit :get-value 3525 (cl-mapcan (lambda (obj) 3526 (and (or (not (slot-exists-p obj 'unsavable)) 3527 (not (oref obj unsavable))) 3528 (transient--get-wrapped-value obj))) 3529 (or transient--suffixes transient-current-suffixes)))) 3530 3531 (defun transient--get-wrapped-value (obj) 3532 (and-let* ((value (transient-infix-value obj))) 3533 (pcase-exhaustive (and (slot-exists-p obj 'multi-value) 3534 (oref obj multi-value)) 3535 ('nil (list value)) 3536 ((or 't 'rest) (list value)) 3537 ('repeat value)))) 3538 3539 (cl-defgeneric transient-infix-value (obj) 3540 "Return the value of the suffix object OBJ. 3541 3542 This function is called by `transient-args' (which see), meaning 3543 this function is how the value of a transient is determined so 3544 that the invoked suffix command can use it. 3545 3546 Currently most values are strings, but that is not set in stone. 3547 Nil is not a value, it means \"no value\". 3548 3549 Usually only infixes have a value, but see the method for 3550 `transient-suffix'.") 3551 3552 (cl-defmethod transient-infix-value ((_ transient-suffix)) 3553 "Return nil, which means \"no value\". 3554 3555 Infix arguments contribute the transient's value while suffix 3556 commands consume it. This function is called for suffixes anyway 3557 because a command that both contributes to the transient's value 3558 and also consumes it is not completely unconceivable. 3559 3560 If you define such a command, then you must define a derived 3561 class and implement this function because this default method 3562 does nothing." nil) 3563 3564 (cl-defmethod transient-infix-value ((obj transient-infix)) 3565 "Return the value of OBJ's `value' slot." 3566 (oref obj value)) 3567 3568 (cl-defmethod transient-infix-value ((obj transient-option)) 3569 "Return ARGUMENT and VALUE as a unit or nil if the latter is nil." 3570 (and-let* ((value (oref obj value))) 3571 (let ((arg (oref obj argument))) 3572 (pcase-exhaustive (oref obj multi-value) 3573 ('nil (concat arg value)) 3574 ((or 't 'rest) (cons arg value)) 3575 ('repeat (mapcar (lambda (v) (concat arg v)) value)))))) 3576 3577 (cl-defmethod transient-infix-value ((_ transient-variable)) 3578 "Return nil, which means \"no value\". 3579 3580 Setting the value of a variable is done by, well, setting the 3581 value of the variable. I.e., this is a side-effect and does 3582 not contribute to the value of the transient." 3583 nil) 3584 3585 ;;;; Utilities 3586 3587 (defun transient-arg-value (arg args) 3588 "Return the value of ARG as it appears in ARGS. 3589 3590 For a switch return a boolean. For an option return the value as 3591 a string, using the empty string for the empty value, or nil if 3592 the option does not appear in ARGS." 3593 (if (string-suffix-p "=" arg) 3594 (save-match-data 3595 (and-let* ((match (let ((case-fold-search nil) 3596 (re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'" 3597 (substring arg 0 -1)))) 3598 (cl-find-if (lambda (a) 3599 (and (stringp a) 3600 (string-match re a))) 3601 args)))) 3602 (or (match-string 1 match) ""))) 3603 (and (member arg args) t))) 3604 3605 (defun transient-scope () 3606 "Return the value of the `scope' slot of the current prefix." 3607 (oref (transient-prefix-object) scope)) 3608 3609 ;;; History 3610 3611 (cl-defgeneric transient--history-key (obj) 3612 "Return OBJ's history key. 3613 If the value of the `history-key' slot is non-nil, then return 3614 that. Otherwise return the value of the `command' slot." 3615 (or (oref obj history-key) 3616 (oref obj command))) 3617 3618 (cl-defgeneric transient--history-push (obj) 3619 "Push the current value of OBJ to its entry in `transient-history'." 3620 (let ((key (transient--history-key obj))) 3621 (setf (alist-get key transient-history) 3622 (let ((args (transient-get-value))) 3623 (cons args (delete args (alist-get key transient-history))))))) 3624 3625 (cl-defgeneric transient--history-init (obj) 3626 "Initialize OBJ's `history' slot. 3627 This is the transient-wide history; many individual infixes also 3628 have a history of their own.") 3629 3630 (cl-defmethod transient--history-init ((obj transient-prefix)) 3631 "Initialize OBJ's `history' slot from the variable `transient-history'." 3632 (let ((val (oref obj value))) 3633 (oset obj history 3634 (cons val (delete val (alist-get (transient--history-key obj) 3635 transient-history)))))) 3636 3637 ;;; Draw 3638 3639 (defun transient--show-brief () 3640 (let ((message-log-max nil)) 3641 (if (and transient-show-popup (<= transient-show-popup 0)) 3642 (message "%s-" (key-description (this-command-keys))) 3643 (message 3644 "%s- [%s] %s" 3645 (key-description (this-command-keys)) 3646 (oref transient--prefix command) 3647 (mapconcat 3648 #'identity 3649 (sort 3650 (cl-mapcan 3651 (lambda (suffix) 3652 (let ((key (kbd (oref suffix key)))) 3653 ;; Don't list any common commands. 3654 (and (not (memq (oref suffix command) 3655 `(,(lookup-key transient-map key) 3656 ,(lookup-key transient-sticky-map key) 3657 ;; From transient-common-commands: 3658 transient-set 3659 transient-save 3660 transient-history-prev 3661 transient-history-next 3662 transient-quit-one 3663 transient-toggle-common 3664 transient-set-level))) 3665 (list (propertize (oref suffix key) 'face 'transient-key))))) 3666 transient--suffixes) 3667 #'string<) 3668 (propertize "|" 'face 'transient-delimiter)))))) 3669 3670 (defun transient--show () 3671 (transient--timer-cancel) 3672 (setq transient--showp t) 3673 (let ((transient--shadowed-buffer (current-buffer)) 3674 (focus nil)) 3675 (setq transient--buffer (get-buffer-create transient--buffer-name)) 3676 (with-current-buffer transient--buffer 3677 (when transient-enable-popup-navigation 3678 (setq focus (or (button-get (point) 'command) 3679 (and (not (bobp)) 3680 (button-get (1- (point)) 'command)) 3681 (transient--heading-at-point)))) 3682 (erase-buffer) 3683 (run-hooks 'transient-setup-buffer-hook) 3684 (when transient-force-fixed-pitch 3685 (transient--force-fixed-pitch)) 3686 (setq window-size-fixed t) 3687 (when (bound-and-true-p tab-line-format) 3688 (setq tab-line-format nil)) 3689 (setq header-line-format nil) 3690 (setq mode-line-format 3691 (if (or (natnump transient-mode-line-format) 3692 (eq transient-mode-line-format 'line)) 3693 nil 3694 transient-mode-line-format)) 3695 (setq mode-line-buffer-identification 3696 (symbol-name (oref transient--prefix command))) 3697 (if transient-enable-popup-navigation 3698 (setq-local cursor-in-non-selected-windows 'box) 3699 (setq cursor-type nil)) 3700 (setq display-line-numbers nil) 3701 (setq show-trailing-whitespace nil) 3702 (transient--insert-groups) 3703 (when (or transient--helpp transient--editp) 3704 (transient--insert-help)) 3705 (when-let ((line (transient--separator-line))) 3706 (insert line))) 3707 (unless (window-live-p transient--window) 3708 (setq transient--window 3709 (display-buffer transient--buffer 3710 transient-display-buffer-action))) 3711 (when (window-live-p transient--window) 3712 (with-selected-window transient--window 3713 (goto-char (point-min)) 3714 (when transient-enable-popup-navigation 3715 (transient--goto-button focus)) 3716 (transient--fit-window-to-buffer transient--window))))) 3717 3718 (defun transient--fit-window-to-buffer (window) 3719 (let ((window-resize-pixelwise t) 3720 (window-size-fixed nil)) 3721 (if (eq (car (window-parameter window 'quit-restore)) 'other) 3722 ;; Grow but never shrink window that previously displayed 3723 ;; another buffer and is going to display that again. 3724 (fit-window-to-buffer window nil (window-height window)) 3725 (fit-window-to-buffer window nil 1)))) 3726 3727 (defun transient--separator-line () 3728 (and-let* ((height (cond ((not window-system) nil) 3729 ((natnump transient-mode-line-format) 3730 transient-mode-line-format) 3731 ((eq transient-mode-line-format 'line) 1))) 3732 (face `(,@(and (>= emacs-major-version 27) '(:extend t)) 3733 :background 3734 ,(or (face-foreground (transient--key-face nil 'non-suffix) 3735 nil t) 3736 "#gray60")))) 3737 (concat (propertize "__" 'face face 'display `(space :height (,height))) 3738 (propertize "\n" 'face face 'line-height t)))) 3739 3740 (defmacro transient-with-shadowed-buffer (&rest body) 3741 "While in the transient buffer, temporarily make the shadowed buffer current." 3742 (declare (indent 0) (debug t)) 3743 `(with-current-buffer (or transient--shadowed-buffer (current-buffer)) 3744 ,@body)) 3745 3746 (defun transient--insert-groups () 3747 (let ((groups (cl-mapcan (lambda (group) 3748 (let ((hide (oref group hide))) 3749 (and (not (and (functionp hide) 3750 (transient-with-shadowed-buffer 3751 (funcall hide)))) 3752 (list group)))) 3753 transient--layout))) 3754 (while-let ((group (pop groups))) 3755 (transient--insert-group group) 3756 (when groups 3757 (insert ?\n))))) 3758 3759 (defvar transient--max-group-level 1) 3760 3761 (cl-defgeneric transient--insert-group (group) 3762 "Format GROUP and its elements and insert the result.") 3763 3764 (cl-defmethod transient--insert-group :around ((group transient-group)) 3765 "Insert GROUP's description, if any." 3766 (when-let ((desc (transient-with-shadowed-buffer 3767 (transient-format-description group)))) 3768 (insert desc ?\n)) 3769 (let ((transient--max-group-level 3770 (max (oref group level) transient--max-group-level)) 3771 (transient--pending-group group)) 3772 (cl-call-next-method group))) 3773 3774 (cl-defmethod transient--insert-group ((group transient-row)) 3775 (transient--maybe-pad-keys group) 3776 (dolist (suffix (oref group suffixes)) 3777 (insert (transient-with-shadowed-buffer (transient-format suffix))) 3778 (insert " ")) 3779 (insert ?\n)) 3780 3781 (cl-defmethod transient--insert-group ((group transient-column)) 3782 (transient--maybe-pad-keys group) 3783 (dolist (suffix (oref group suffixes)) 3784 (let ((str (transient-with-shadowed-buffer (transient-format suffix)))) 3785 (insert str) 3786 (unless (string-match-p ".\n\\'" str) 3787 (insert ?\n))))) 3788 3789 (cl-defmethod transient--insert-group ((group transient-columns)) 3790 (let* ((columns 3791 (mapcar 3792 (lambda (column) 3793 (transient--maybe-pad-keys column group) 3794 (transient-with-shadowed-buffer 3795 (let* ((transient--pending-group column) 3796 (rows (mapcar #'transient-format (oref column suffixes)))) 3797 (if-let ((desc (transient-format-description column))) 3798 (cons desc rows) 3799 rows)))) 3800 (oref group suffixes))) 3801 (vp (or (oref transient--prefix variable-pitch) 3802 transient-align-variable-pitch)) 3803 (rs (apply #'max (mapcar #'length columns))) 3804 (cs (length columns)) 3805 (cw (mapcar (let ((widths (oref transient--prefix column-widths))) 3806 (lambda (col) 3807 (apply 3808 #'max 3809 (if-let ((min (pop widths))) 3810 (if vp (* min (transient--pixel-width " ")) min) 3811 0) 3812 (mapcar (if vp #'transient--pixel-width #'length) 3813 col)))) 3814 columns)) 3815 (cc (transient--seq-reductions-from 3816 (apply-partially #'+ (* 2 (if vp (transient--pixel-width " ") 1))) 3817 cw 0))) 3818 (if transient-force-single-column 3819 (dotimes (c cs) 3820 (dotimes (r rs) 3821 (when-let ((cell (nth r (nth c columns)))) 3822 (unless (equal cell "") 3823 (insert cell ?\n)))) 3824 (unless (= c (1- cs)) 3825 (insert ?\n))) 3826 (dotimes (r rs) 3827 (dotimes (c cs) 3828 (if vp 3829 (progn 3830 (when-let ((cell (nth r (nth c columns)))) 3831 (insert cell)) 3832 (if (= c (1- cs)) 3833 (insert ?\n) 3834 (insert (propertize " " 'display 3835 `(space :align-to (,(nth (1+ c) cc))))))) 3836 (when (> c 0) 3837 (insert (make-string (max 1 (- (nth c cc) (current-column))) 3838 ?\s))) 3839 (when-let ((cell (nth r (nth c columns)))) 3840 (insert cell)) 3841 (when (= c (1- cs)) 3842 (insert ?\n)))))))) 3843 3844 (cl-defmethod transient--insert-group ((group transient-subgroups)) 3845 (let ((subgroups (oref group suffixes))) 3846 (while-let ((subgroup (pop subgroups))) 3847 (transient--maybe-pad-keys subgroup group) 3848 (transient--insert-group subgroup) 3849 (when subgroups 3850 (insert ?\n))))) 3851 3852 (cl-defgeneric transient-format (obj) 3853 "Format and return OBJ for display. 3854 3855 When this function is called, then the current buffer is some 3856 temporary buffer. If you need the buffer from which the prefix 3857 command was invoked to be current, then do so by temporarily 3858 making `transient--original-buffer' current.") 3859 3860 (cl-defmethod transient-format ((arg string)) 3861 "Return the string ARG after applying the `transient-heading' face." 3862 (propertize arg 'face 'transient-heading)) 3863 3864 (cl-defmethod transient-format ((_ null)) 3865 "Return a string containing just the newline character." 3866 "\n") 3867 3868 (cl-defmethod transient-format ((arg integer)) 3869 "Return a string containing just the ARG character." 3870 (char-to-string arg)) 3871 3872 (cl-defmethod transient-format :around ((obj transient-suffix)) 3873 "Add additional formatting if appropriate. 3874 When reading user input for this infix, then highlight it. 3875 When edit-mode is enabled, then prepend the level information. 3876 When `transient-enable-popup-navigation' is non-nil then format 3877 as a button." 3878 (let ((str (cl-call-next-method obj))) 3879 (when (and (cl-typep obj 'transient-infix) 3880 (eq (oref obj command) this-original-command) 3881 (active-minibuffer-window)) 3882 (setq str (transient--add-face str 'transient-active-infix))) 3883 (when transient--editp 3884 (setq str (concat (let ((level (oref obj level))) 3885 (propertize (format " %s " level) 3886 'face (if (transient--use-level-p level t) 3887 'transient-enabled-suffix 3888 'transient-disabled-suffix))) 3889 str))) 3890 (when (and transient-enable-popup-navigation 3891 (slot-boundp obj 'command)) 3892 (setq str (make-text-button str nil 3893 'type 'transient 3894 'command (oref obj command)))) 3895 str)) 3896 3897 (cl-defmethod transient-format ((obj transient-infix)) 3898 "Return a string generated using OBJ's `format'. 3899 %k is formatted using `transient-format-key'. 3900 %d is formatted using `transient-format-description'. 3901 %v is formatted using `transient-format-value'." 3902 (format-spec (oref obj format) 3903 `((?k . ,(transient-format-key obj)) 3904 (?d . ,(transient-format-description obj)) 3905 (?v . ,(transient-format-value obj))))) 3906 3907 (cl-defmethod transient-format ((obj transient-suffix)) 3908 "Return a string generated using OBJ's `format'. 3909 %k is formatted using `transient-format-key'. 3910 %d is formatted using `transient-format-description'." 3911 (format-spec (oref obj format) 3912 `((?k . ,(transient-format-key obj)) 3913 (?d . ,(transient-format-description obj))))) 3914 3915 (cl-defgeneric transient-format-key (obj) 3916 "Format OBJ's `key' for display and return the result.") 3917 3918 (cl-defmethod transient-format-key :around ((obj transient-suffix)) 3919 "Add `transient-inapt-suffix' face if suffix is inapt." 3920 (let ((str (cl-call-next-method))) 3921 (if (oref obj inapt) 3922 (transient--add-face str 'transient-inapt-suffix) 3923 str))) 3924 3925 (cl-defmethod transient-format-key ((obj transient-suffix)) 3926 "Format OBJ's `key' for display and return the result." 3927 (let ((key (if (slot-boundp obj 'key) (oref obj key) "")) 3928 (cmd (and (slot-boundp obj 'command) (oref obj command)))) 3929 (when-let ((width (oref transient--pending-group pad-keys))) 3930 (setq key (truncate-string-to-width key width nil ?\s))) 3931 (if transient--redisplay-key 3932 (let ((len (length transient--redisplay-key)) 3933 (seq (cl-coerce (edmacro-parse-keys key t) 'list))) 3934 (cond 3935 ((member (seq-take seq len) 3936 (list transient--redisplay-key 3937 (thread-last transient--redisplay-key 3938 (cl-substitute ?- 'kp-subtract) 3939 (cl-substitute ?= 'kp-equal) 3940 (cl-substitute ?+ 'kp-add)))) 3941 (let ((pre (key-description (vconcat (seq-take seq len)))) 3942 (suf (key-description (vconcat (seq-drop seq len))))) 3943 (setq pre (string-replace "RET" "C-m" pre)) 3944 (setq pre (string-replace "TAB" "C-i" pre)) 3945 (setq suf (string-replace "RET" "C-m" suf)) 3946 (setq suf (string-replace "TAB" "C-i" suf)) 3947 ;; We use e.g., "-k" instead of the more correct "- k", 3948 ;; because the former is prettier. If we did that in 3949 ;; the definition, then we want to drop the space that 3950 ;; is reinserted above. False-positives are possible 3951 ;; for silly bindings like "-C-c C-c". 3952 (unless (string-search " " key) 3953 (setq pre (string-replace " " "" pre)) 3954 (setq suf (string-replace " " "" suf))) 3955 (concat (propertize pre 'face 'transient-unreachable-key) 3956 (and (string-prefix-p (concat pre " ") key) " ") 3957 (propertize suf 'face (transient--key-face cmd)) 3958 (save-excursion 3959 (and (string-match " +\\'" key) 3960 (propertize (match-string 0 key) 3961 'face 'fixed-pitch)))))) 3962 ((transient--lookup-key transient-sticky-map (kbd key)) 3963 (propertize key 'face (transient--key-face cmd))) 3964 (t 3965 (propertize key 'face 'transient-unreachable-key)))) 3966 (propertize key 'face (transient--key-face cmd))))) 3967 3968 (cl-defmethod transient-format-key :around ((obj transient-argument)) 3969 "Handle `transient-highlight-mismatched-keys'." 3970 (let ((key (cl-call-next-method obj))) 3971 (cond 3972 ((not transient-highlight-mismatched-keys) key) 3973 ((not (slot-boundp obj 'shortarg)) 3974 (transient--add-face key 'transient-nonstandard-key)) 3975 ((not (string-equal key (oref obj shortarg))) 3976 (transient--add-face key 'transient-mismatched-key)) 3977 (key)))) 3978 3979 (cl-defgeneric transient-format-description (obj) 3980 "Format OBJ's `description' for display and return the result.") 3981 3982 (cl-defmethod transient-format-description ((obj transient-suffix)) 3983 "The `description' slot may be a function, in which case that is 3984 called inside the correct buffer (see `transient--insert-group') 3985 and its value is returned to the caller." 3986 (transient--get-description obj)) 3987 3988 (cl-defmethod transient-format-description ((obj transient-value-preset)) 3989 (pcase-let* (((eieio description key set) obj) 3990 ((eieio value) transient--prefix) 3991 (active (seq-set-equal-p set value))) 3992 (format 3993 "%s %s" 3994 (propertize (or description (format "Preset %s" key)) 3995 'face (and active 'transient-argument)) 3996 (format (propertize "(%s)" 'face 'transient-delimiter) 3997 (mapconcat (lambda (arg) 3998 (propertize 3999 arg 'face (cond (active 'transient-argument) 4000 ((member arg value) 4001 '((:weight demibold) 4002 transient-inactive-argument)) 4003 ('transient-inactive-argument)))) 4004 set " "))))) 4005 4006 (cl-defmethod transient-format-description ((obj transient-group)) 4007 "Format the description by calling the next method. If the result 4008 doesn't use the `face' property at all, then apply the face 4009 `transient-heading' to the complete string." 4010 (and-let* ((desc (transient--get-description obj))) 4011 (cond ((oref obj inapt) 4012 (propertize desc 'face 'transient-inapt-suffix)) 4013 ((text-property-not-all 0 (length desc) 'face nil desc) 4014 desc) 4015 ((propertize desc 'face 'transient-heading))))) 4016 4017 (cl-defmethod transient-format-description :around ((obj transient-suffix)) 4018 "Format the description by calling the next method. If the result 4019 is nil, then use \"(BUG: no description)\" as the description. 4020 If the OBJ's `key' is currently unreachable, then apply the face 4021 `transient-unreachable' to the complete string." 4022 (let ((desc (or (cl-call-next-method obj) 4023 (and (slot-boundp transient--prefix 'suffix-description) 4024 (funcall (oref transient--prefix suffix-description) 4025 obj))))) 4026 (if desc 4027 (when-let ((face (transient--get-face obj 'face))) 4028 (setq desc (transient--add-face desc face t))) 4029 (setq desc (propertize "(BUG: no description)" 'face 'error))) 4030 (when (if transient--all-levels-p 4031 (> (oref obj level) transient--default-prefix-level) 4032 (and transient-highlight-higher-levels 4033 (> (max (oref obj level) transient--max-group-level) 4034 transient--default-prefix-level))) 4035 (setq desc (transient--add-face desc 'transient-higher-level))) 4036 (when-let ((inapt-face (and (oref obj inapt) 4037 (transient--get-face obj 'inapt-face)))) 4038 (setq desc (transient--add-face desc inapt-face))) 4039 (when (and (slot-boundp obj 'key) 4040 (transient--key-unreachable-p obj)) 4041 (setq desc (transient--add-face desc 'transient-unreachable))) 4042 desc)) 4043 4044 (cl-defgeneric transient-format-value (obj) 4045 "Format OBJ's value for display and return the result.") 4046 4047 (cl-defmethod transient-format-value ((obj transient-suffix)) 4048 (propertize (oref obj argument) 4049 'face (if (oref obj value) 4050 'transient-argument 4051 'transient-inactive-argument))) 4052 4053 (cl-defmethod transient-format-value ((obj transient-option)) 4054 (let ((argument (oref obj argument))) 4055 (if-let ((value (oref obj value))) 4056 (pcase-exhaustive (oref obj multi-value) 4057 ('nil 4058 (concat (propertize argument 'face 'transient-argument) 4059 (propertize value 'face 'transient-value))) 4060 ((or 't 'rest) 4061 (concat (propertize (if (string-suffix-p " " argument) 4062 argument 4063 (concat argument " ")) 4064 'face 'transient-argument) 4065 (propertize (mapconcat #'prin1-to-string value " ") 4066 'face 'transient-value))) 4067 ('repeat 4068 (mapconcat (lambda (value) 4069 (concat (propertize argument 'face 'transient-argument) 4070 (propertize value 'face 'transient-value))) 4071 value " "))) 4072 (propertize argument 'face 'transient-inactive-argument)))) 4073 4074 (cl-defmethod transient-format-value ((obj transient-switches)) 4075 (with-slots (value argument-format choices) obj 4076 (format (propertize argument-format 4077 'face (if value 4078 'transient-argument 4079 'transient-inactive-argument)) 4080 (format 4081 (propertize "[%s]" 'face 'transient-delimiter) 4082 (mapconcat 4083 (lambda (choice) 4084 (propertize choice 'face 4085 (if (equal (format argument-format choice) value) 4086 'transient-value 4087 'transient-inactive-value))) 4088 choices 4089 (propertize "|" 'face 'transient-delimiter)))))) 4090 4091 (cl-defmethod transient--get-description ((obj transient-child)) 4092 (and-let* ((desc (oref obj description))) 4093 (if (functionp desc) 4094 (if (= (car (transient--func-arity desc)) 1) 4095 (funcall desc obj) 4096 (funcall desc)) 4097 desc))) 4098 4099 (cl-defmethod transient--get-face ((obj transient-suffix) slot) 4100 (and-let* (((slot-boundp obj slot)) 4101 (face (slot-value obj slot))) 4102 (if (and (not (facep face)) 4103 (functionp face)) 4104 (let ((transient--pending-suffix obj)) 4105 (if (= (car (transient--func-arity face)) 1) 4106 (funcall face obj) 4107 (funcall face))) 4108 face))) 4109 4110 (defun transient--add-face (string face &optional append beg end) 4111 (let ((str (copy-sequence string))) 4112 (add-face-text-property (or beg 0) (or end (length str)) face append str) 4113 str)) 4114 4115 (defun transient--key-face (&optional cmd enforce-type) 4116 (or (and transient-semantic-coloring 4117 (not transient--helpp) 4118 (not transient--editp) 4119 (or (and cmd (get cmd 'transient-face)) 4120 (get (transient--get-pre-command cmd enforce-type) 4121 'transient-face))) 4122 (if cmd 'transient-key 'transient-key-noop))) 4123 4124 (defun transient--key-unreachable-p (obj) 4125 (and transient--redisplay-key 4126 (let ((key (oref obj key))) 4127 (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list) 4128 (length transient--redisplay-key)) 4129 transient--redisplay-key) 4130 (transient--lookup-key transient-sticky-map (kbd key))))))) 4131 4132 (defun transient--lookup-key (keymap key) 4133 (let ((val (lookup-key keymap key))) 4134 (and val (not (integerp val)) val))) 4135 4136 (defun transient--maybe-pad-keys (group &optional parent) 4137 (when-let ((pad (or (oref group pad-keys) 4138 (and parent (oref parent pad-keys))))) 4139 (oset group pad-keys 4140 (apply #'max 4141 (if (integerp pad) pad 0) 4142 (seq-keep (lambda (suffix) 4143 (and (eieio-object-p suffix) 4144 (slot-boundp suffix 'key) 4145 (length (oref suffix key)))) 4146 (oref group suffixes)))))) 4147 4148 (defun transient--pixel-width (string) 4149 (save-window-excursion 4150 (with-temp-buffer 4151 (insert string) 4152 (set-window-dedicated-p nil nil) 4153 (set-window-buffer nil (current-buffer)) 4154 (car (window-text-pixel-size 4155 nil (line-beginning-position) (point)))))) 4156 4157 (defun transient-command-summary-or-name (obj) 4158 "Return the summary or name of the command represented by OBJ. 4159 4160 If the command has a doc-string, then return the first line of 4161 that, else its name. 4162 4163 Intended to be temporarily used as the `:suffix-description' of 4164 a prefix command, while porting a regular keymap to a transient." 4165 (let ((command (oref obj command))) 4166 (if-let ((doc (documentation command))) 4167 (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face) 4168 (propertize (symbol-name command) 'face 'font-lock-function-name-face)))) 4169 4170 ;;; Help 4171 4172 (cl-defgeneric transient-show-help (obj) 4173 "Show documentation for the command represented by OBJ.") 4174 4175 (cl-defmethod transient-show-help ((obj transient-prefix)) 4176 "Call `show-help' if non-nil, else show `info-manual', 4177 if non-nil, else show the `man-page' if non-nil, else use 4178 `describe-function'." 4179 (with-slots (show-help info-manual man-page command) obj 4180 (cond (show-help (funcall show-help obj)) 4181 (info-manual (transient--show-manual info-manual)) 4182 (man-page (transient--show-manpage man-page)) 4183 ((transient--describe-function command))))) 4184 4185 (cl-defmethod transient-show-help ((obj transient-suffix)) 4186 "Call `show-help' if non-nil, else use `describe-function'. 4187 Also used to dispatch showing documentation for the current 4188 prefix. If the suffix is a sub-prefix, then also call the 4189 prefix method." 4190 (cond 4191 ((eq this-command 'transient-help) 4192 (transient-show-help transient--prefix)) 4193 ((let ((prefix (get (oref obj command) 4194 'transient--prefix))) 4195 (and prefix (not (eq (oref transient--prefix command) this-command)) 4196 (prog1 t (transient-show-help prefix))))) 4197 ((if-let ((show-help (oref obj show-help))) 4198 (funcall show-help obj) 4199 (transient--describe-function this-command))))) 4200 4201 (cl-defmethod transient-show-help ((obj transient-infix)) 4202 "Call `show-help' if non-nil, else show the `man-page' 4203 if non-nil, else use `describe-function'. When showing the 4204 manpage, then try to jump to the correct location." 4205 (if-let ((show-help (oref obj show-help))) 4206 (funcall show-help obj) 4207 (if-let ((man-page (oref transient--prefix man-page)) 4208 (argument (and (slot-boundp obj 'argument) 4209 (oref obj argument)))) 4210 (transient--show-manpage man-page argument) 4211 (transient--describe-function this-command)))) 4212 4213 ;; `cl-generic-generalizers' doesn't support `command' et al. 4214 (cl-defmethod transient-show-help (cmd) 4215 "Show the command doc-string." 4216 (transient--describe-function cmd)) 4217 4218 (defun transient--describe-function (fn) 4219 (describe-function fn) 4220 (unless (derived-mode-p 'help-mode) 4221 (when-let* ((buf (get-buffer "*Help*")) 4222 (win (or (and buf (get-buffer-window buf)) 4223 (cl-find-if (lambda (win) 4224 (with-current-buffer (window-buffer win) 4225 (derived-mode-p 'help-mode))) 4226 (window-list))))) 4227 (select-window win)))) 4228 4229 (defun transient--show-manual (manual) 4230 (info manual)) 4231 4232 (defun transient--show-manpage (manpage &optional argument) 4233 (require 'man) 4234 (let* ((Man-notify-method 'meek) 4235 (buf (Man-getpage-in-background manpage)) 4236 (proc (get-buffer-process buf))) 4237 (while (and proc (eq (process-status proc) 'run)) 4238 (accept-process-output proc)) 4239 (switch-to-buffer buf) 4240 (when argument 4241 (transient--goto-argument-description argument)))) 4242 4243 (defun transient--goto-argument-description (arg) 4244 (goto-char (point-min)) 4245 (let ((case-fold-search nil) 4246 ;; This matches preceding/proceeding options. Options 4247 ;; such as "-a", "-S[<keyid>]", and "--grep=<pattern>" 4248 ;; are matched by this regex without the shy group. 4249 ;; The ". " in the shy group is for options such as 4250 ;; "-m parent-number", and the "-[^[:space:]]+ " is 4251 ;; for options such as "--mainline parent-number" 4252 (others "-\\(?:. \\|-[^[:space:]]+ \\)?[^[:space:]]+")) 4253 (when (re-search-forward 4254 (if (equal arg "--") 4255 ;; Special case. 4256 "^[\t\s]+\\(--\\(?: \\|$\\)\\|\\[--\\]\\)" 4257 ;; Should start with whitespace and may have 4258 ;; any number of options before and/or after. 4259 (format 4260 "^[\t\s]+\\(?:%s, \\)*?\\(?1:%s\\)%s\\(?:, %s\\)*$" 4261 others 4262 ;; Options don't necessarily end in an "=" 4263 ;; (e.g., "--gpg-sign[=<keyid>]") 4264 (string-remove-suffix "=" arg) 4265 ;; Simple options don't end in an "=". Splitting this 4266 ;; into 2 cases should make getting false positives 4267 ;; less likely. 4268 (if (string-suffix-p "=" arg) 4269 ;; "[^[:space:]]*[^.[:space:]]" matches the option 4270 ;; value, which is usually after the option name 4271 ;; and either '=' or '[='. The value can't end in 4272 ;; a period, as that means it's being used at the 4273 ;; end of a sentence. The space is for options 4274 ;; such as '--mainline parent-number'. 4275 "\\(?: \\|\\[?=\\)[^[:space:]]*[^.[:space:]]" 4276 ;; Either this doesn't match anything (e.g., "-a"), 4277 ;; or the option is followed by a value delimited 4278 ;; by a "[", "<", or ":". A space might appear 4279 ;; before this value, as in "-f <file>". The 4280 ;; space alternative is for options such as 4281 ;; "-m parent-number". 4282 "\\(?:\\(?: \\| ?[\\[<:]\\)[^[:space:]]*[^.[:space:]]\\)?") 4283 others)) 4284 nil t) 4285 (goto-char (match-beginning 1))))) 4286 4287 (defun transient--insert-help () 4288 (unless (looking-back "\n\n" 2) 4289 (insert "\n")) 4290 (when transient--helpp 4291 (insert 4292 (format (propertize "\ 4293 Type a %s to show help for that suffix command, or %s to show manual. 4294 Type %s to exit help.\n" 4295 'face 'transient-heading) 4296 (propertize "<KEY>" 'face 'transient-key) 4297 (propertize "?" 'face 'transient-key) 4298 (propertize "C-g" 'face 'transient-key)))) 4299 (when transient--editp 4300 (unless transient--helpp 4301 (insert 4302 (format (propertize "\ 4303 Type a %s to set level for that suffix command. 4304 Type %s to set what levels are available for this prefix command.\n" 4305 'face 'transient-heading) 4306 (propertize "<KEY>" 'face 'transient-key) 4307 (propertize "C-x l" 'face 'transient-key)))) 4308 (with-slots (level) transient--prefix 4309 (insert 4310 (format (propertize " 4311 Suffixes on levels %s are available. 4312 Suffixes on levels %s and %s are unavailable.\n" 4313 'face 'transient-heading) 4314 (propertize (format "1-%s" level) 4315 'face 'transient-enabled-suffix) 4316 (propertize " 0 " 4317 'face 'transient-disabled-suffix) 4318 (propertize (format ">=%s" (1+ level)) 4319 'face 'transient-disabled-suffix)))))) 4320 4321 ;;; Popup Navigation 4322 4323 (defun transient-scroll-up (&optional arg) 4324 "Scroll text of transient popup window upward ARG lines. 4325 If ARG is nil scroll near full screen. This is a wrapper 4326 around `scroll-up-command' (which see)." 4327 (interactive "^P") 4328 (with-selected-window transient--window 4329 (scroll-up-command arg))) 4330 4331 (defun transient-scroll-down (&optional arg) 4332 "Scroll text of transient popup window down ARG lines. 4333 If ARG is nil scroll near full screen. This is a wrapper 4334 around `scroll-down-command' (which see)." 4335 (interactive "^P") 4336 (with-selected-window transient--window 4337 (scroll-down-command arg))) 4338 4339 (defun transient-backward-button (n) 4340 "Move to the previous button in the transient popup buffer. 4341 See `backward-button' for information about N." 4342 (interactive "p") 4343 (with-selected-window transient--window 4344 (backward-button n t))) 4345 4346 (defun transient-forward-button (n) 4347 "Move to the next button in the transient popup buffer. 4348 See `forward-button' for information about N." 4349 (interactive "p") 4350 (with-selected-window transient--window 4351 (forward-button n t))) 4352 4353 (define-button-type 'transient 4354 'face nil 4355 'keymap transient-button-map) 4356 4357 (defun transient--goto-button (command) 4358 (cond 4359 ((stringp command) 4360 (when (re-search-forward (concat "^" (regexp-quote command)) nil t) 4361 (goto-char (match-beginning 0)))) 4362 (command 4363 (while (and (ignore-errors (forward-button 1)) 4364 (not (eq (button-get (button-at (point)) 'command) command)))) 4365 (unless (eq (button-get (button-at (point)) 'command) command) 4366 (goto-char (point-min)) 4367 (forward-button 1))))) 4368 4369 (defun transient--heading-at-point () 4370 (and (eq (get-text-property (point) 'face) 'transient-heading) 4371 (let ((beg (line-beginning-position))) 4372 (buffer-substring-no-properties 4373 beg (next-single-property-change 4374 beg 'face nil (line-end-position)))))) 4375 4376 ;;; Compatibility 4377 ;;;; Popup Isearch 4378 4379 (defvar-keymap transient--isearch-mode-map 4380 :parent isearch-mode-map 4381 "<remap> <isearch-exit>" #'transient-isearch-exit 4382 "<remap> <isearch-cancel>" #'transient-isearch-cancel 4383 "<remap> <isearch-abort>" #'transient-isearch-abort) 4384 4385 (defun transient-isearch-backward (&optional regexp-p) 4386 "Do incremental search backward. 4387 With a prefix argument, do an incremental regular expression 4388 search instead." 4389 (interactive "P") 4390 (transient--isearch-setup) 4391 (let ((isearch-mode-map transient--isearch-mode-map)) 4392 (isearch-mode nil regexp-p))) 4393 4394 (defun transient-isearch-forward (&optional regexp-p) 4395 "Do incremental search forward. 4396 With a prefix argument, do an incremental regular expression 4397 search instead." 4398 (interactive "P") 4399 (transient--isearch-setup) 4400 (let ((isearch-mode-map transient--isearch-mode-map)) 4401 (isearch-mode t regexp-p))) 4402 4403 (defun transient-isearch-exit () 4404 "Like `isearch-exit' but adapted for `transient'." 4405 (interactive) 4406 (isearch-exit) 4407 (transient--isearch-exit)) 4408 4409 (defun transient-isearch-cancel () 4410 "Like `isearch-cancel' but adapted for `transient'." 4411 (interactive) 4412 (condition-case nil (isearch-cancel) (quit)) 4413 (transient--isearch-exit)) 4414 4415 (defun transient-isearch-abort () 4416 "Like `isearch-abort' but adapted for `transient'." 4417 (interactive) 4418 (let ((around (lambda (fn) 4419 (condition-case nil (funcall fn) (quit)) 4420 (transient--isearch-exit)))) 4421 (advice-add 'isearch-cancel :around around) 4422 (unwind-protect 4423 (isearch-abort) 4424 (advice-remove 'isearch-cancel around)))) 4425 4426 (defun transient--isearch-setup () 4427 (select-window transient--window) 4428 (transient--suspend-override t)) 4429 4430 (defun transient--isearch-exit () 4431 (select-window transient--original-window) 4432 (transient--resume-override)) 4433 4434 ;;;; Edebug 4435 4436 (defun transient--edebug-command-p () 4437 (and (bound-and-true-p edebug-active) 4438 (or (memq this-command '(top-level abort-recursive-edit)) 4439 (string-prefix-p "edebug" (symbol-name this-command))))) 4440 4441 ;;;; Miscellaneous 4442 4443 (cl-pushnew (list nil (concat "^\\s-*(" 4444 (eval-when-compile 4445 (regexp-opt 4446 '("transient-define-prefix" 4447 "transient-define-suffix" 4448 "transient-define-infix" 4449 "transient-define-argument") 4450 t)) 4451 "\\s-+\\(" lisp-mode-symbol-regexp "\\)") 4452 2) 4453 lisp-imenu-generic-expression :test #'equal) 4454 4455 (declare-function which-key-mode "which-key" (&optional arg)) 4456 4457 (defun transient--suspend-which-key-mode () 4458 (when (bound-and-true-p which-key-mode) 4459 (which-key-mode -1) 4460 (add-hook 'transient-exit-hook #'transient--resume-which-key-mode))) 4461 4462 (defun transient--resume-which-key-mode () 4463 (unless transient--prefix 4464 (which-key-mode 1) 4465 (remove-hook 'transient-exit-hook #'transient--resume-which-key-mode))) 4466 4467 (defun transient-bind-q-to-quit () 4468 "Modify some keymaps to bind \"q\" to the appropriate quit command. 4469 4470 \"C-g\" is the default binding for such commands now, but Transient's 4471 predecessor Magit-Popup used \"q\" instead. If you would like to get 4472 that binding back, then call this function in your init file like so: 4473 4474 (with-eval-after-load \\='transient 4475 (transient-bind-q-to-quit)) 4476 4477 Individual transients may already bind \"q\" to something else 4478 and such a binding would shadow the quit binding. If that is the 4479 case then \"Q\" is bound to whatever \"q\" would have been bound 4480 to by setting `transient-substitute-key-function' to a function 4481 that does that. Of course \"Q\" may already be bound to something 4482 else, so that function binds \"M-q\" to that command instead. 4483 Of course \"M-q\" may already be bound to something else, but 4484 we stop there." 4485 (keymap-set transient-base-map "q" #'transient-quit-one) 4486 (keymap-set transient-sticky-map "q" #'transient-quit-seq) 4487 (setq transient-substitute-key-function 4488 #'transient-rebind-quit-commands)) 4489 4490 (defun transient-rebind-quit-commands (obj) 4491 "See `transient-bind-q-to-quit'." 4492 (let ((key (oref obj key))) 4493 (cond ((string-equal key "q") "Q") 4494 ((string-equal key "Q") "M-q") 4495 (key)))) 4496 4497 (defun transient--force-fixed-pitch () 4498 (require 'face-remap) 4499 (face-remap-reset-base 'default) 4500 (face-remap-add-relative 'default 'fixed-pitch)) 4501 4502 (defun transient--func-arity (fn) 4503 (func-arity (advice--cd*r (if (symbolp fn) (symbol-function fn) fn)))) 4504 4505 (defun transient--seq-reductions-from (function sequence initial-value) 4506 (let ((acc (list initial-value))) 4507 (seq-doseq (elt sequence) 4508 (push (funcall function (car acc) elt) acc)) 4509 (nreverse acc))) 4510 4511 ;;; Font-Lock 4512 4513 (defconst transient-font-lock-keywords 4514 (eval-when-compile 4515 `((,(concat "(" 4516 (regexp-opt (list "transient-define-prefix" 4517 "transient-define-infix" 4518 "transient-define-argument" 4519 "transient-define-suffix") 4520 t) 4521 "\\_>[ \t'(]*" 4522 "\\(\\(?:\\sw\\|\\s_\\)+\\)?") 4523 (1 'font-lock-keyword-face) 4524 (2 'font-lock-function-name-face nil t))))) 4525 4526 (font-lock-add-keywords 'emacs-lisp-mode transient-font-lock-keywords) 4527 4528 ;;; Auxiliary Classes 4529 ;;;; `transient-lisp-variable' 4530 4531 (defclass transient-lisp-variable (transient-variable) 4532 ((reader :initform #'transient-lisp-variable--reader) 4533 (always-read :initform t) 4534 (set-value :initarg :set-value :initform #'set)) 4535 "[Experimental] Class used for Lisp variables.") 4536 4537 (cl-defmethod transient-init-value ((obj transient-lisp-variable)) 4538 (oset obj value (symbol-value (oref obj variable)))) 4539 4540 (cl-defmethod transient-infix-set ((obj transient-lisp-variable) value) 4541 (funcall (oref obj set-value) 4542 (oref obj variable) 4543 (oset obj value value))) 4544 4545 (cl-defmethod transient-format-description ((obj transient-lisp-variable)) 4546 (or (cl-call-next-method obj) 4547 (symbol-name (oref obj variable)))) 4548 4549 (cl-defmethod transient-format-value ((obj transient-lisp-variable)) 4550 (propertize (prin1-to-string (oref obj value)) 4551 'face 'transient-value)) 4552 4553 (cl-defmethod transient-prompt ((obj transient-lisp-variable)) 4554 (if (and (slot-boundp obj 'prompt) 4555 (oref obj prompt)) 4556 (cl-call-next-method obj) 4557 (format "Set %s: " (oref obj variable)))) 4558 4559 (defun transient-lisp-variable--reader (prompt initial-input _history) 4560 (read--expression prompt initial-input)) 4561 4562 ;;; _ 4563 (provide 'transient) 4564 ;; Local Variables: 4565 ;; indent-tabs-mode: nil 4566 ;; checkdoc-symbol-words: ("command-line" "edit-mode" "help-mode") 4567 ;; End: 4568 ;;; transient.el ends here