From 856118ffe20c7203378be666fabf4cd20733cb32 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Thu, 18 Jul 2024 10:29:34 +0200 Subject: [PATCH] update transient Signed-off-by: Andreas Gerler --- elpa/transient-20230602.2121/transient-pkg.el | 15 - .../dir | 0 .../gpl.info | 0 .../transient-autoloads.el | 42 +- elpa/transient-20240421.1737/transient-pkg.el | 16 + .../transient.el | 2038 ++++++++++------- .../transient.info | 1142 ++++----- 7 files changed, 1715 insertions(+), 1538 deletions(-) delete mode 100644 elpa/transient-20230602.2121/transient-pkg.el rename elpa/{transient-20230602.2121 => transient-20240421.1737}/dir (100%) rename elpa/{transient-20230602.2121 => transient-20240421.1737}/gpl.info (100%) rename elpa/{transient-20230602.2121 => transient-20240421.1737}/transient-autoloads.el (81%) create mode 100644 elpa/transient-20240421.1737/transient-pkg.el rename elpa/{transient-20230602.2121 => transient-20240421.1737}/transient.el (72%) rename elpa/{transient-20230602.2121 => transient-20240421.1737}/transient.info (82%) diff --git a/elpa/transient-20230602.2121/transient-pkg.el b/elpa/transient-20230602.2121/transient-pkg.el deleted file mode 100644 index 84937cce..00000000 --- a/elpa/transient-20230602.2121/transient-pkg.el +++ /dev/null @@ -1,15 +0,0 @@ -(define-package "transient" "20230602.2121" "Transient commands" - '((emacs "25.1") - (compat "29.1.4.1")) - :commit "a1575e58331b1ff85ac97667df0130ae822ccf5f" :authors - '(("Jonas Bernoulli" . "jonas@bernoul.li")) - :maintainers - '(("Jonas Bernoulli" . "jonas@bernoul.li")) - :maintainer - '("Jonas Bernoulli" . "jonas@bernoul.li") - :keywords - '("extensions") - :url "https://github.com/magit/transient") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/transient-20230602.2121/dir b/elpa/transient-20240421.1737/dir similarity index 100% rename from elpa/transient-20230602.2121/dir rename to elpa/transient-20240421.1737/dir diff --git a/elpa/transient-20230602.2121/gpl.info b/elpa/transient-20240421.1737/gpl.info similarity index 100% rename from elpa/transient-20230602.2121/gpl.info rename to elpa/transient-20240421.1737/gpl.info diff --git a/elpa/transient-20230602.2121/transient-autoloads.el b/elpa/transient-20240421.1737/transient-autoloads.el similarity index 81% rename from elpa/transient-20230602.2121/transient-autoloads.el rename to elpa/transient-20240421.1737/transient-autoloads.el index e578fc36..93700ed1 100644 --- a/elpa/transient-20230602.2121/transient-autoloads.el +++ b/elpa/transient-20240421.1737/transient-autoloads.el @@ -1,12 +1,14 @@ -;;; transient-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- -;; +;;; transient-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + ;;; Code: -(add-to-list 'load-path (directory-file-name - (or (file-name-directory #$) (car load-path)))) +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + -;;;### (autoloads nil "transient" "transient.el" (0 0 0 0)) ;;; Generated autoloads from transient.el (autoload 'transient-insert-suffix "transient" "\ @@ -21,10 +23,8 @@ Remove a conflicting binding unless optional KEEP-OTHER is non-nil. See info node `(transient)Modifying Existing Transients'. -\(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)" nil nil) - +(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)") (function-put 'transient-insert-suffix 'lisp-indent-function 'defun) - (autoload 'transient-append-suffix "transient" "\ Insert a SUFFIX into PREFIX after LOC. PREFIX is a prefix command, a symbol. @@ -37,10 +37,8 @@ Remove a conflicting binding unless optional KEEP-OTHER is non-nil. See info node `(transient)Modifying Existing Transients'. -\(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)" nil nil) - +(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)") (function-put 'transient-append-suffix 'lisp-indent-function 'defun) - (autoload 'transient-replace-suffix "transient" "\ Replace the suffix at LOC in PREFIX with SUFFIX. PREFIX is a prefix command, a symbol. @@ -51,10 +49,8 @@ LOC is a command, a key vector, a key description (a string (whose last element may also be a command or key). See info node `(transient)Modifying Existing Transients'. -\(fn PREFIX LOC SUFFIX)" nil nil) - +(fn PREFIX LOC SUFFIX)") (function-put 'transient-replace-suffix 'lisp-indent-function 'defun) - (autoload 'transient-remove-suffix "transient" "\ Remove the suffix or group at LOC in PREFIX. PREFIX is a prefix command, a symbol. @@ -63,22 +59,20 @@ LOC is a command, a key vector, a key description (a string (whose last element may also be a command or key). See info node `(transient)Modifying Existing Transients'. -\(fn PREFIX LOC)" nil nil) - +(fn PREFIX LOC)") (function-put 'transient-remove-suffix 'lisp-indent-function 'defun) - -(register-definition-prefixes "transient" '("transient")) - -;;;*** +(register-definition-prefixes "transient" '("static-if" "transient")) -;;;### (autoloads nil nil ("transient-pkg.el") (0 0 0 0)) +;;; End of scraped data + +(provide 'transient-autoloads) -;;;*** - ;; Local Variables: ;; version-control: never ;; no-byte-compile: t ;; no-update-autoloads: t -;; coding: utf-8 +;; no-native-compile: t +;; coding: utf-8-emacs-unix ;; End: + ;;; transient-autoloads.el ends here diff --git a/elpa/transient-20240421.1737/transient-pkg.el b/elpa/transient-20240421.1737/transient-pkg.el new file mode 100644 index 00000000..5aac0067 --- /dev/null +++ b/elpa/transient-20240421.1737/transient-pkg.el @@ -0,0 +1,16 @@ +(define-package "transient" "20240421.1737" "Transient commands" + '((emacs "26.1") + (compat "29.1.4.4") + (seq "2.24")) + :commit "2dd0102ec3df901d421fc338e5c768467cc54ecf" :authors + '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) + :maintainers + '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev") + :keywords + '("extensions") + :url "https://github.com/magit/transient") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/transient-20230602.2121/transient.el b/elpa/transient-20240421.1737/transient.el similarity index 72% rename from elpa/transient-20230602.2121/transient.el rename to elpa/transient-20240421.1737/transient.el index 3e9924e5..a58eb2e8 100644 --- a/elpa/transient-20230602.2121/transient.el +++ b/elpa/transient-20240421.1737/transient.el @@ -1,13 +1,13 @@ ;;; transient.el --- Transient commands -*- lexical-binding:t -*- -;; Copyright (C) 2018-2023 Free Software Foundation, Inc. +;; Copyright (C) 2018-2024 Free Software Foundation, Inc. -;; Author: Jonas Bernoulli +;; Author: Jonas Bernoulli ;; Homepage: https://github.com/magit/transient ;; Keywords: extensions -;; Package-Version: 0.4.1 -;; Package-Requires: ((emacs "25.1") (compat "29.1.4.1")) +;; Package-Version: 0.6.0 +;; Package-Requires: ((emacs "26.1") (compat "29.1.4.4") (seq "2.24")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -28,27 +28,9 @@ ;;; Commentary: -;; Taking inspiration from prefix keys and prefix arguments, Transient -;; implements a similar abstraction involving a prefix command, infix -;; arguments and suffix commands. We could call this abstraction a -;; "transient command", but because it always involves at least two -;; commands (a prefix and a suffix) we prefer to call it just a -;; "transient". - -;; When the user calls a transient prefix command, then a transient -;; (temporary) keymap is activated, which binds the transient's infix -;; and suffix commands, and functions that control the transient state -;; are added to `pre-command-hook' and `post-command-hook'. The -;; available suffix and infix commands and their state are shown in -;; the echo area until the transient is exited by invoking a suffix -;; command. - -;; Calling an infix command causes its value to be changed, possibly -;; by reading a new value in the minibuffer. - -;; Calling a suffix command usually causes the transient to be exited -;; but suffix commands can also be configured to not exit the -;; transient state. +;; Transient is the library used to implement the keyboard-driven menus +;; in Magit. It is distributed as a separate package, so that it can be +;; used to implement similar menus in other packages. ;;; Code: @@ -57,7 +39,41 @@ (require 'eieio) (require 'edmacro) (require 'format-spec) + +(eval-and-compile + (when (and (featurep' seq) + (not (fboundp 'seq-keep))) + (unload-feature 'seq 'force))) (require 'seq) +(unless (fboundp 'seq-keep) + (display-warning 'transient (substitute-command-keys "\ +Transient requires `seq' >= 2.24, +but due to bad defaults, Emacs' package manager, refuses to +upgrade this and other built-in packages to higher releases +from GNU Elpa, when a package specifies that this is needed. + +To fix this, you have to add this to your init file: + + (setq package-install-upgrade-built-in t) + +Then evaluate that expression by placing the cursor after it +and typing \\[eval-last-sexp]. + +Once you have done that, you have to explicitly upgrade `seq': + + \\[package-upgrade] seq \\`RET' + +Then you also must make sure the updated version is loaded, +by evaluating this form: + + (progn (unload-feature 'seq t) (require 'seq)) + +Until you do this, you will get random errors about `seq-keep' +being undefined while using Transient. + +If you don't use the `package' package manager but still get +this warning, then your chosen package manager likely has a +similar defect.") :emergency)) (eval-when-compile (require 'subr-x)) @@ -66,20 +82,34 @@ (declare-function Man-next-section "man" (n)) (declare-function Man-getpage-in-background "man" (topic)) -(defvar display-line-numbers) ; since Emacs 26.1 (defvar Man-notify-method) - -(defmacro transient--with-emergency-exit (&rest body) +(defvar pp-default-function) ; since Emacs 29.1 + +(defmacro static-if (condition then-form &rest else-forms) + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is non-nil, +expand the macro to THEN-FORM. Otherwise expand it to ELSE-FORMS +enclosed in a `progn' form. ELSE-FORMS may be empty." + (declare (indent 2) + (debug (sexp sexp &rest sexp))) + (if (eval condition lexical-binding) + then-form + (cons 'progn else-forms))) + +(defmacro transient--with-emergency-exit (id &rest body) (declare (indent defun)) + (unless (keywordp id) + (setq body (cons id body)) + (setq id nil)) `(condition-case err (let ((debugger #'transient--exit-and-debug)) ,(macroexp-progn body)) ((debug error) - (transient--emergency-exit) + (transient--emergency-exit ,id) (signal (car err) (cdr err))))) (defun transient--exit-and-debug (&rest args) - (transient--emergency-exit) + (transient--emergency-exit :debugger) (apply #'debug args)) ;;; Options @@ -198,21 +228,30 @@ If nil, then the buffer has no mode-line. If the buffer is not displayed right above the echo area, then this probably is not a good value. -If `line' (the default), then the buffer also has no mode-line, -but a thin line is drawn instead, using the background color of -the face `transient-separator'. Termcap frames cannot display -thin lines and therefore fallback to treating `line' like nil. +If `line' (the default) or a natural number, then the buffer +has no mode-line, but a line is drawn is drawn in its place. +If a number is used, that specifies the thickness of the line. +On termcap frames we cannot draw lines, so there `line' and +numbers are synonyms for nil. + +The color of the line is used to indicate if non-suffixes are +allowed and whether they exit the transient. The foreground +color of `transient-key-noop' (if non-suffix are disallowed), +`transient-key-stay' (if allowed and transient stays active), or +`transient-key-exit' (if allowed and they exit the transient) is +used to draw the line. Otherwise this can be any mode-line format. See `mode-line-format' for details." :package-version '(transient . "0.2.0") :group 'transient - :type '(choice (const :tag "hide mode-line" nil) - (const :tag "substitute thin line" line) - (const :tag "name of prefix command" - ("%e" mode-line-front-space - mode-line-buffer-identification)) - (sexp :tag "custom mode-line format"))) + :type '(choice (const :tag "hide mode-line" nil) + (const :tag "substitute thin line" line) + (number :tag "substitute line with thickness") + (const :tag "name of prefix command" + ("%e" mode-line-front-space + mode-line-buffer-identification)) + (sexp :tag "custom mode-line format"))) (defcustom transient-show-common-commands nil "Whether to show common transient suffixes in the popup buffer. @@ -236,7 +275,7 @@ of this variable use \"C-x t\" when a transient is active." This only affects infix arguments that represent command-line arguments. When this option is non-nil, then the key binding for infix argument are highlighted when only a long argument -\(e.g. \"--verbose\") is specified but no shor-thand (e.g \"-v\"). +\(e.g., \"--verbose\") is specified but no shorthand (e.g., \"-v\"). In the rare case that a short-hand is specified but does not match the key binding, then it is highlighted differently. @@ -285,19 +324,14 @@ using a layout optimized for Lisp. :group 'transient :type '(choice (const :tag "Transform no keys (nil)" nil) function)) -(defcustom transient-semantic-coloring nil - "Whether to color prefixes and suffixes in Hydra-like fashion. -This feature is experimental. +(defcustom transient-semantic-coloring t + "Whether to use colors to indicate transient behavior. If non-nil, then the key binding of each suffix is colorized to -indicate whether it exits the transient state or not. The color -of the prefix is indicated using the line that is drawn when the -value of `transient-mode-line-format' is `line'. - -For more information about how Hydra uses colors see -https://github.com/abo-abo/hydra#color and -https://oremacs.com/2015/02/19/hydra-colors-reloaded." - :package-version '(transient . "0.3.0") +indicate whether it exits the transient state or not, and the +line that is drawn below the transient popup buffer is used to +indicate the behavior of non-suffix commands." + :package-version '(transient . "0.5.0") :group 'transient :type 'boolean) @@ -356,8 +390,8 @@ text and might otherwise have to scroll in two dimensions." :group 'transient :type 'boolean) +(defconst transient--max-level 7) (defconst transient--default-child-level 1) - (defconst transient--default-prefix-level 4) (defcustom transient-default-level transient--default-prefix-level @@ -436,22 +470,18 @@ give you as many additional suffixes as you hoped.)" "Face used for headings." :group 'transient-faces) -(defface transient-key '((t :inherit font-lock-builtin-face)) - "Face used for keys." - :group 'transient-faces) - -(defface transient-argument '((t :inherit font-lock-warning-face)) +(defface transient-argument '((t :inherit font-lock-string-face :weight bold)) "Face used for enabled arguments." :group 'transient-faces) -(defface transient-value '((t :inherit font-lock-string-face)) - "Face used for values." - :group 'transient-faces) - (defface transient-inactive-argument '((t :inherit shadow)) "Face used for inactive arguments." :group 'transient-faces) +(defface transient-value '((t :inherit font-lock-string-face :weight bold)) + "Face used for values." + :group 'transient-faces) + (defface transient-inactive-value '((t :inherit shadow)) "Face used for inactive values." :group 'transient-faces) @@ -460,28 +490,14 @@ give you as many additional suffixes as you hoped.)" "Face used for suffixes unreachable from the current prefix sequence." :group 'transient-faces) -(defface transient-active-infix '((t :inherit secondary-selection)) - "Face used for the infix for which the value is being read." - :group 'transient-faces) - -(defface transient-unreachable-key '((t :inherit (transient-key shadow))) - "Face used for keys unreachable from the current prefix sequence." - :group 'transient-faces) - -(defface transient-nonstandard-key '((t :underline t)) - "Face optionally used to highlight keys conflicting with short-argument. -Also see option `transient-highlight-mismatched-keys'." - :group 'transient-faces) - -(defface transient-mismatched-key '((t :underline t)) - "Face optionally used to highlight keys without a short-argument. -Also see option `transient-highlight-mismatched-keys'." - :group 'transient-faces) - (defface transient-inapt-suffix '((t :inherit shadow :italic t)) "Face used for suffixes that are inapt at this time." :group 'transient-faces) +(defface transient-active-infix '((t :inherit highlight)) + "Face used for the infix for which the value is being read." + :group 'transient-faces) + (defface transient-enabled-suffix '((t :background "green" :foreground "black" :weight bold)) "Face used for enabled levels while editing suffix levels. @@ -494,63 +510,83 @@ See info node `(transient)Enabling and Disabling Suffixes'." See info node `(transient)Enabling and Disabling Suffixes'." :group 'transient-faces) -(defface transient-higher-level '((t :underline t)) +(defface transient-higher-level + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color ,(let ((color (face-attribute 'shadow :foreground nil t))) + (or (and (not (eq color 'unspecified)) color) + "grey60"))))) "Face optionally used to highlight suffixes on higher levels. Also see option `transient-highlight-higher-levels'." :group 'transient-faces) -(defface transient-separator - `((((class color) (background light)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :background "grey80") - (((class color) (background dark)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :background "grey30")) - "Face used to draw line below transient popup window. -This is only used if `transient-mode-line-format' is `line'. -Only the background color is significant." +(defface transient-delimiter '((t :inherit shadow)) + "Face used for delimiters and separators. +This includes the parentheses around values and the pipe +character used to separate possible values from each other." :group 'transient-faces) -(defgroup transient-color-faces - '((transient-semantic-coloring custom-variable)) - "Faces used by Transient for Hydra-like command coloring. -These faces are only used if `transient-semantic-coloring' -\(which see) is non-nil." +(defface transient-key '((t :inherit font-lock-builtin-face)) + "Face used for keys." :group 'transient-faces) -(defface transient-red - '((t :inherit transient-key :foreground "red")) - "Face used for red prefixes and suffixes." - :group 'transient-color-faces) +(defface transient-key-stay + `((((class color) (background light)) + :inherit transient-key + :foreground "#22aa22") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ddffdd")) + "Face used for keys of suffixes that don't exit transient state." + :group 'transient-faces) -(defface transient-blue - '((t :inherit transient-key :foreground "blue")) - "Face used for blue prefixes and suffixes." - :group 'transient-color-faces) +(defface transient-key-noop + `((((class color) (background light)) + :inherit transient-key + :foreground "grey80") + (((class color) (background dark)) + :inherit transient-key + :foreground "grey30")) + "Face used for keys of suffixes that currently cannot be invoked." + :group 'transient-faces) -(defface transient-amaranth - '((t :inherit transient-key :foreground "#E52B50")) - "Face used for amaranth prefixes." - :group 'transient-color-faces) +(defface transient-key-return + `((((class color) (background light)) + :inherit transient-key + :foreground "#aaaa11") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ffffcc")) + "Face used for keys of suffixes that return to the parent transient." + :group 'transient-faces) -(defface transient-pink - '((t :inherit transient-key :foreground "#FF6EB4")) - "Face used for pink prefixes." - :group 'transient-color-faces) +(defface transient-key-exit + `((((class color) (background light)) + :inherit transient-key + :foreground "#aa2222") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ffdddd")) + "Face used for keys of suffixes that exit transient state." + :group 'transient-faces) -(defface transient-teal - '((t :inherit transient-key :foreground "#367588")) - "Face used for teal prefixes." - :group 'transient-color-faces) +(defface transient-unreachable-key + '((t :inherit (shadow transient-key) :weight normal)) + "Face used for keys unreachable from the current prefix sequence." + :group 'transient-faces) -(defface transient-purple - '((t :inherit transient-key :foreground "#a020f0")) - "Face used for purple prefixes. +(defface transient-nonstandard-key + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color "cyan"))) + "Face optionally used to highlight keys conflicting with short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) -This is an addition to the colors supported by Hydra. It is -used by suffixes that quit the current prefix but return to -the previous prefix." - :group 'transient-color-faces) +(defface transient-mismatched-key + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color "magenta"))) + "Face optionally used to highlight keys without a short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) ;;; Persistence @@ -566,7 +602,9 @@ the previous prefix." (setq list (cl-sort (copy-sequence list) #'string< :key #'car)) (with-temp-file file (let ((print-level nil) - (print-length nil)) + (print-length nil) + (pp-default-function 'pp-28) + (fill-column 999)) (pp list (current-buffer))))) (defvar transient-values @@ -631,9 +669,12 @@ If `transient-save-history' is nil, then do nothing." (man-page :initarg :man-page :initform nil) (transient-suffix :initarg :transient-suffix :initform nil) (transient-non-suffix :initarg :transient-non-suffix :initform nil) + (transient-switch-frame :initarg :transient-switch-frame) + (refresh-suffixes :initarg :refresh-suffixes :initform nil) (incompatible :initarg :incompatible :initform nil) (suffix-description :initarg :suffix-description) (variable-pitch :initarg :variable-pitch :initform nil) + (column-widths :initarg :column-widths :initform nil) (unwind-suffix :documentation "Internal use." :initform nil)) "Transient prefix command. @@ -691,12 +732,15 @@ slot is non-nil." :abstract t) (defclass transient-suffix (transient-child) - ((key :initarg :key) + ((definition :allocation :class :initform nil) + (key :initarg :key) (command :initarg :command) (transient :initarg :transient) (format :initarg :format :initform " %k %d") (description :initarg :description :initform nil) + (face :initarg :face :initform nil) (show-help :initarg :show-help :initform nil) + (inapt-face :initarg :inapt-face :initform 'transient-inapt-suffix) (inapt :initform nil) (inapt-if :initarg :inapt-if @@ -732,6 +776,17 @@ slot is non-nil." :documentation "Inapt if major-mode does not derive from value.")) "Superclass for suffix command.") +(defclass transient-information (transient-suffix) + ((format :initform " %k %d") + (key :initform " ")) + "Display-only information, aligned with suffix keys. +Technically a suffix object with no associated command.") + +(defclass transient-information* (transient-information) + ((format :initform " %d")) + "Display-only information, aligned with suffix descriptions. +Technically a suffix object with no associated command.") + (defclass transient-infix (transient-suffix) ((transient :initform t) (argument :initarg :argument) @@ -786,8 +841,9 @@ They become the value of this argument.") ((suffixes :initarg :suffixes :initform nil) (hide :initarg :hide :initform nil) (description :initarg :description :initform nil) - (setup-children :initarg :setup-children) - (pad-keys :initarg :pad-keys)) + (pad-keys :initarg :pad-keys :initform nil) + (info-format :initarg :info-format :initform nil) + (setup-children :initarg :setup-children)) "Abstract superclass of all group classes." :abstract t) @@ -860,7 +916,7 @@ to the setup function: [&optional ("interactive" interactive) def-body])) (indent defun) (doc-string 3)) - (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body) + (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body ,interactive-only) (transient--expand-define-args args arglist))) `(progn (defalias ',name @@ -869,7 +925,7 @@ to the setup function: `(lambda () (interactive) (transient-setup ',name)))) - (put ',name 'interactive-only t) + (put ',name 'interactive-only ,interactive-only) (put ',name 'function-documentation ,docstr) (put ',name 'transient--prefix (,(or class 'transient-prefix) :command ',name ,@slots)) @@ -893,19 +949,21 @@ The BODY must begin with an `interactive' form that matches ARGLIST. The infix arguments are usually accessed by using `transient-args' inside `interactive'. -\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... BODY...)" +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... [BODY...])" (declare (debug ( &define name lambda-list [&optional lambda-doc] [&rest keywordp sexp] - ("interactive" interactive) - def-body)) + [&optional ("interactive" interactive) def-body])) (indent defun) (doc-string 3)) - (pcase-let ((`(,class ,slots ,_ ,docstr ,body) + (pcase-let ((`(,class ,slots ,_ ,docstr ,body ,interactive-only) (transient--expand-define-args args arglist))) `(progn - (defalias ',name (lambda ,arglist ,@body)) - (put ',name 'interactive-only t) + (defalias ',name + ,(if (and (not body) class (oref-default class definition)) + `(oref-default ',class definition) + `(lambda ,arglist ,@body))) + (put ',name 'interactive-only ,interactive-only) (put ',name 'function-documentation ,docstr) (put ',name 'transient--suffix (,(or class 'transient-suffix) :command ',name ,@slots))))) @@ -916,24 +974,20 @@ ARGLIST. The infix arguments are usually accessed by using ARGLIST is always ignored and reserved for future use. DOCSTRING is the documentation string and is optional. -The key-value pairs are mandatory. All transient infix commands -are equal to each other (but not eq), so it is meaningless to -define an infix command without also setting at least `:class' -and one other keyword (which it is depends on the used class, -usually `:argument' or `:variable'). - -Each key has to be a keyword symbol, either `:class' or a keyword -argument supported by the constructor of that class. The -`transient-switch' class is used if the class is not specified -explicitly. +At least one key-value pair is required. All transient infix +commands are equal to each other (but not eq). It is meaning- +less to define an infix command, without providing at least one +keyword argument (usually `:argument' or `:variable', depending +on the class). The suffix class defaults to `transient-switch' +and can be set using the `:class' keyword. The function definitions is always: - (lambda () - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) `transient-infix-read' and `transient-infix-set' are generic functions. Different infix commands behave differently because @@ -944,18 +998,19 @@ that case you have to use `transient-define-suffix' to define the infix command and use t as the value of the `:transient' keyword. -\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)" +\(fn NAME ARGLIST [DOCSTRING] KEYWORD VALUE [KEYWORD VALUE]...)" (declare (debug ( &define name lambda-list [&optional lambda-doc] + keywordp sexp [&rest keywordp sexp])) (indent defun) (doc-string 3)) - (pcase-let ((`(,class ,slots ,_ ,docstr ,_) - (transient--expand-define-args args arglist))) + (pcase-let ((`(,class ,slots ,_ ,docstr ,_ ,interactive-only) + (transient--expand-define-args args arglist t))) `(progn - (defalias ',name ,(transient--default-infix-command)) - (put ',name 'interactive-only t) - (put ',name 'command-modes (list 'not-a-mode)) + (defalias ',name #'transient--default-infix-command) + (put ',name 'interactive-only ,interactive-only) + (put ',name 'completion-predicate #'transient--suffix-only) (put ',name 'function-documentation ,docstr) (put ',name 'transient--suffix (,(or class 'transient-switch) :command ',name ,@slots))))) @@ -969,33 +1024,86 @@ example, sets a variable, use `transient-define-infix' instead. \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)") -(defun transient--expand-define-args (args &optional arglist) - (unless (listp arglist) - (error "Mandatory ARGLIST is missing")) - (let (class keys suffixes docstr) - (when (stringp (car args)) - (setq docstr (pop args))) - (while (keywordp (car args)) - (let ((k (pop args)) - (v (pop args))) - (if (eq k :class) - (setq class v) - (push k keys) - (push v keys)))) - (while (let ((arg (car args))) - (or (vectorp arg) - (and arg (symbolp arg)))) - (push (pop args) suffixes)) - (list (if (eq (car-safe class) 'quote) - (cadr class) - class) - (nreverse keys) - (nreverse suffixes) - docstr - args))) +(defun transient--default-infix-command () + ;; Most infix commands are but an alias for this command. + "Cannot show any documentation for this transient infix command. + +When you request help for an infix command using `transient-help', that +usually shows the respective man-page and tries to jump to the location +where the respective argument is being described. + +If no man-page is specified for the containing transient menu, then the +docstring is displayed instead, if any. + +If the infix command doesn't have a docstring, as is the case here, then +this docstring is displayed instead, because technically infix commands +are aliases for `transient--default-infix-command'. + +`describe-function' also shows the docstring of the infix command, +falling back to that of the same aliased command." + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) +(put 'transient--default-infix-command 'interactive-only t) +(put 'transient--default-infix-command 'completion-predicate + #'transient--suffix-only) + +(defun transient--find-function-advised-original (fn func) + "Return nil instead of `transient--default-infix-command'. +When using `find-function' to jump to the definition of a transient +infix command/argument, then we want to actually jump to that, not to +the definition of `transient--default-infix-command', which all infix +commands are aliases for." + (let ((val (funcall fn func))) + (and val (not (eq val 'transient--default-infix-command)) val))) +(advice-add 'find-function-advised-original :around + #'transient--find-function-advised-original) + +(eval-and-compile + (defun transient--expand-define-args (args &optional arglist nobody) + (unless (listp arglist) + (error "Mandatory ARGLIST is missing")) + (let (class keys suffixes docstr declare (interactive-only t)) + (when (stringp (car args)) + (setq docstr (pop args))) + (while (keywordp (car args)) + (let ((k (pop args)) + (v (pop args))) + (if (eq k :class) + (setq class v) + (push k keys) + (push v keys)))) + (while (let ((arg (car args))) + (or (vectorp arg) + (and arg (symbolp arg)))) + (push (pop args) suffixes)) + (when (eq (car-safe (car args)) 'declare) + (setq declare (car args)) + (setq args (cdr args)) + (when-let ((int (assq 'interactive-only declare))) + (setq interactive-only (cadr int)) + (delq int declare)) + (unless (cdr declare) + (setq declare nil))) + (cond + ((not args)) + (nobody + (error "transient-define-infix: No function body allowed")) + ((not (eq (car-safe (nth (if declare 1 0) args)) 'interactive)) + (error "transient-define-*: Interactive form missing"))) + (list (if (eq (car-safe class) 'quote) + (cadr class) + class) + (nreverse keys) + (nreverse suffixes) + docstr + (if declare (cons declare args) args) + interactive-only)))) (defun transient--parse-child (prefix spec) - (cl-etypecase spec + (cl-typecase spec + (null (error "Invalid transient--parse-child spec: %s" spec)) (symbol (let ((value (symbol-value spec))) (if (and (listp value) (or (listp (car value)) @@ -1004,7 +1112,8 @@ example, sets a variable, use `transient-define-infix' instead. (transient--parse-child prefix value)))) (vector (and-let* ((c (transient--parse-group prefix spec))) (list c))) (list (and-let* ((c (transient--parse-suffix prefix spec))) (list c))) - (string (list spec)))) + (string (list spec)) + (t (error "Invalid transient--parse-child spec: %s" spec)))) (defun transient--parse-group (prefix spec) (setq spec (append spec nil)) @@ -1025,12 +1134,16 @@ example, sets a variable, use `transient-define-infix' instead. (and (listp val) (not (eq (car val) 'lambda)))) (setq args (plist-put args key (macroexp-quote val)))) ((setq args (plist-put args key val)))))) + (unless (or spec class (not (plist-get args :setup-children))) + (message "WARNING: %s: When %s is used, %s must also be specified" + 'transient-define-prefix :setup-children :class)) (list 'vector (or level transient--default-child-level) - (or class - (if (vectorp car) - (quote 'transient-columns) - (quote 'transient-column))) + (cond (class) + ((or (vectorp car) + (and car (symbolp car))) + (quote 'transient-columns)) + ((quote 'transient-column))) (and args (cons 'list args)) (cons 'list (cl-mapcan (lambda (s) (transient--parse-child prefix s)) @@ -1057,8 +1170,9 @@ example, sets a variable, use `transient-define-infix' instead. (commandp (cadr spec))) (setq args (plist-put args :description (macroexp-quote pop))))) (cond + ((memq car '(:info :info*))) ((keywordp car) - (error "Need command, got `%s'" car)) + (error "Need command, `:info' or `:info*', got `%s'" car)) ((symbolp car) (setq args (plist-put args :command (macroexp-quote pop)))) ((and (commandp car) @@ -1068,14 +1182,23 @@ example, sets a variable, use `transient-define-infix' instead. (format "transient:%s:%s" prefix (let ((desc (plist-get args :description))) - (if (and desc (or (stringp desc) (symbolp desc))) + (if (and (stringp desc) + (length< desc 16)) desc (plist-get args :key))))))) - (setq args (plist-put args :command - `(defalias ',sym ,(macroexp-quote cmd)))))) + (setq args (plist-put + args :command + `(prog1 ',sym + (put ',sym 'interactive-only t) + (put ',sym 'completion-predicate #'transient--suffix-only) + (defalias ',sym + ,(if (eq (car-safe cmd) 'lambda) + cmd + (macroexp-quote cmd)))))))) ((or (stringp car) (and car (listp car))) - (let ((arg pop)) + (let ((arg pop) + (sym nil)) (cl-typecase arg (list (setq args (plist-put args :shortarg (car arg))) @@ -1085,9 +1208,13 @@ example, sets a variable, use `transient-define-infix' instead. (when-let ((shortarg (transient--derive-shortarg arg))) (setq args (plist-put args :shortarg shortarg))) (setq args (plist-put args :argument arg)))) - (setq args (plist-put args :command - (list 'quote (intern (format "transient:%s:%s" - prefix arg))))) + (setq sym (intern (format "transient:%s:%s" prefix arg))) + (setq args (plist-put + args :command + `(prog1 ',sym + (put ',sym 'interactive-only t) + (put ',sym 'completion-predicate #'transient--suffix-only) + (defalias ',sym #'transient--default-infix-command)))) (cond ((and car (not (keywordp car))) (setq class 'transient-option) (setq args (plist-put args :reader (macroexp-quote pop)))) @@ -1102,6 +1229,12 @@ example, sets a variable, use `transient-define-infix' instead. (val pop)) (cond ((eq key :class) (setq class val)) ((eq key :level) (setq level val)) + ((eq key :info) + (setq class 'transient-information) + (setq args (plist-put args :description val))) + ((eq key :info*) + (setq class 'transient-information*) + (setq args (plist-put args :description val))) ((eq (car-safe val) '\,) (setq args (plist-put args key (cadr val)))) ((or (symbolp val) @@ -1116,37 +1249,38 @@ example, sets a variable, use `transient-define-infix' instead. (macroexp-quote (or class 'transient-suffix)) (cons 'list args)))) -(defun transient--default-infix-command () - (cons 'lambda - '(() - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)))) - -(defun transient--ensure-infix-command (obj) - (let ((cmd (oref obj command))) - (unless (or (commandp cmd) - (get cmd 'transient--infix-command)) - (if (or (cl-typep obj 'transient-switch) - (cl-typep obj 'transient-option)) - (put cmd 'transient--infix-command - (transient--default-infix-command)) - ;; This is not an anonymous infix argument. - (when (transient--use-suffix-p obj) - (error "Suffix %s is not defined or autoloaded as a command" cmd)))))) - (defun transient--derive-shortarg (arg) (save-match-data (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) (match-string 1 arg)))) +(defun transient-command-completion-not-suffix-only-p (symbol _buffer) + "Say whether SYMBOL should be offered as a completion. +If the value of SYMBOL's `completion-predicate' property is +`transient--suffix-only', then return nil, otherwise return t. +This is the case when a command should only ever be used as a +suffix of a transient prefix command (as opposed to bindings +in regular keymaps or by using `execute-extended-command')." + (not (eq (get symbol 'completion-predicate) 'transient--suffix-only))) + +(defalias 'transient--suffix-only #'ignore + "Ignore ARGUMENTS, do nothing, and return nil. +Also see `transient-command-completion-not-suffix-only-p'. +Only use this alias as the value of the `completion-predicate' +symbol property.") + +(when (and (boundp 'read-extended-command-predicate) ; since Emacs 28.1 + (not read-extended-command-predicate)) + (setq read-extended-command-predicate + 'transient-command-completion-not-suffix-only-p)) + (defun transient-parse-suffix (prefix suffix) "Parse SUFFIX, to be added to PREFIX. PREFIX is a prefix command, a symbol. SUFFIX is a suffix command or a group specification (of the same forms as expected by `transient-define-prefix'). Intended for use in a group's `:setup-children' function." + (cl-assert (and prefix (symbolp prefix))) (eval (car (transient--parse-child prefix suffix)))) (defun transient-parse-suffixes (prefix suffixes) @@ -1155,6 +1289,7 @@ PREFIX is a prefix command, a symbol. SUFFIXES is a list of suffix command or a group specification (of the same forms as expected by `transient-define-prefix'). Intended for use in a group's `:setup-children' function." + (cl-assert (and prefix (symbolp prefix))) (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes)) ;;; Edit @@ -1190,11 +1325,11 @@ Intended for use in a group's `:setup-children' function." (equal (transient--suffix-predicate suf) (transient--suffix-predicate conflict))))) (transient-remove-suffix prefix key)) - (cl-ecase action - (insert (setcdr mem (cons elt (cdr mem))) - (setcar mem suf)) - (append (setcdr mem (cons suf (cdr mem)))) - (replace (setcar mem suf))))))) + (pcase-exhaustive action + ('insert (setcdr mem (cons elt (cdr mem))) + (setcar mem suf)) + ('append (setcdr mem (cons suf (cdr mem)))) + ('replace (setcar mem suf))))))) ;;;###autoload (defun transient-insert-suffix (prefix loc suffix &optional keep-other) @@ -1305,7 +1440,7 @@ See info node `(transient)Modifying Existing Transients'." (delq (car (transient--group-member loc layout)) (aref layout 3))) nil) - (t (transient--group-member loc layout)))) + ((transient--group-member loc layout)))) (defun transient--group-member (loc group) (cl-member-if (lambda (suffix) @@ -1334,7 +1469,7 @@ See info node `(transient)Modifying Existing Transients'." (plist-get plist :command))))) (defun transient--command-key (cmd) - (and-let* ((obj (get cmd 'transient--suffix))) + (and-let* ((obj (transient--suffix-prototype cmd))) (cond ((slot-boundp obj 'key) (oref obj key)) ((slot-exists-p obj 'shortarg) @@ -1367,6 +1502,10 @@ variable instead.") (defvar transient-exit-hook nil "Hook run after exiting a transient.") +(defvar transient-setup-buffer-hook nil + "Hook run when setting up the transient buffer. +That buffer is current and empty when this hook runs.") + (defvar transient--prefix nil) (defvar transient--layout nil) (defvar transient--suffixes nil) @@ -1375,11 +1514,15 @@ variable instead.") (defconst transient--exit nil "Do exit the transient.") (defvar transient--exitp nil "Whether to exit the transient.") -(defvar transient--showp nil "Whether the transient is show in a popup buffer.") +(defvar transient--showp nil "Whether to show the transient popup buffer.") (defvar transient--helpp nil "Whether help-mode is active.") (defvar transient--editp nil "Whether edit-mode is active.") -(defvar transient--active-infix nil "The active infix awaiting user input.") +(defvar transient--refreshp nil + "Whether to refresh the transient completely.") + +(defvar transient--all-levels-p nil + "Whether temporary display of suffixes on all levels is active.") (defvar transient--timer nil) @@ -1391,7 +1534,7 @@ variable instead.") "Name of the transient buffer.") (defvar transient--window nil - "The window used to display the transient popup.") + "The window used to display the transient popup buffer.") (defvar transient--original-window nil "The window that was selected before the transient was invoked. @@ -1401,7 +1544,25 @@ Usually it remains selected while the transient is active.") "The buffer that was current before the transient was invoked. Usually it remains current while the transient is active.") -(defvar transient--debug nil "Whether put debug information into *Messages*.") +(defvar transient--restore-winconf nil + "Window configuration to restore after exiting help.") + +(defvar transient--shadowed-buffer nil + "The buffer that is temporarily shadowed by the transient buffer. +This is bound while the suffix predicate is being evaluated and while +drawing in the transient buffer.") + +(defvar transient--pending-suffix nil + "The suffix that is currently being processed. +This is bound while the suffix predicate is being evaluated, +and while functions that return faces are being evaluated.") + +(defvar transient--pending-group nil + "The group that is currently being processed. +This is bound while the suffixes are drawn in the transient buffer.") + +(defvar transient--debug nil + "Whether to put debug information into *Messages*.") (defvar transient--history nil) @@ -1413,6 +1574,31 @@ Usually it remains current while the transient is active.") ;;; Identities +(defun transient-prefix-object () + "Return the current prefix as an object. + +While a transient is being setup or refreshed (which involves +preparing its suffixes) the variable `transient--prefix' can be +used to access the prefix object. Thus this is what has to be +used in suffix methods such as `transient-format-description', +and in object-specific functions that are stored in suffix slots +such as `description'. + +When a suffix command is invoked (i.e., in its `interactive' form +and function body) then the variable `transient-current-prefix' +has to be used instead. + +Two distinct variables are needed, because any prefix may itself +be used as a suffix of another prefix, and such sub-prefixes have +to be able to tell themselves apart from the prefix they were +invoked from. + +Regular suffix commands, which are not prefixes, do not have to +concern themselves with this distinction, so they can use this +function instead. In the context of a plain suffix, it always +returns the value of the appropriate variable." + (or transient--prefix transient-current-prefix)) + (defun transient-suffix-object (&optional command) "Return the object associated with the current suffix command. @@ -1420,15 +1606,15 @@ Each suffix commands is associated with an object, which holds additional information about the suffix, such as its value (in the case of an infix command, which is a kind of suffix command). -This function is intended to be called by infix commands, whose -command definition usually (at least when defined using -`transient-define-infix') is this: +This function is intended to be called by infix commands, which +are usually aliases of `transient--default-infix-command', which +is defined like this: - (lambda () - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) + (defun transient--default-infix-command () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) \(User input is read outside of `interactive' to prevent the command from being added to `command-history'. See #23.) @@ -1441,7 +1627,7 @@ commands) may also need the object to guide their behavior. This function attempts to return the object associated with the current suffix command even if the suffix command was not invoked from a transient. (For some suffix command that is a valid thing -to do, for others it is not.) In that case nil may be returned +to do, for others it is not.) In that case nil may be returned, if the command was not defined using one of the macros intended to define such commands. @@ -1452,65 +1638,40 @@ probably use this instead: (get COMMAND \\='transient--suffix)" (when command (cl-check-type command command)) - (if (or transient--prefix - transient-current-prefix) - (let ((suffixes - (cl-remove-if-not - (lambda (obj) - (eq (transient--suffix-command obj) - (or command - (if (eq this-command 'transient-set-level) - ;; This is how it can look up for which - ;; command it is setting the level. - this-original-command - this-command)))) - (or transient--suffixes - transient-current-suffixes)))) - (or (and (cdr suffixes) - (cl-find-if - (lambda (obj) - (equal (listify-key-sequence (transient--kbd (oref obj key))) - (listify-key-sequence (this-command-keys)))) - suffixes)) - (car suffixes))) - (when-let* ((obj (get (or command this-command) 'transient--suffix)) - (obj (clone obj))) - ;; Cannot use and-let* because of debbugs#31840. - (transient-init-scope obj) - (transient-init-value obj) - obj))) - -(defun transient--suffix-command (object) - "Return the command represented by OBJECT. - -If the value of OBJECT's `command' slot is a command, then return -that. Otherwise it is a symbol whose `transient--infix-command' -property holds an anonymous command, which is returned instead." - (cl-check-type object transient-suffix) - (let ((sym (oref object command))) - (if (commandp sym) - sym - (get sym 'transient--infix-command)))) - -(defun transient--suffix-symbol (arg) - "Return a symbol representing ARG. - -ARG must be a command and/or a symbol. If it is a symbol, -then just return it. Otherwise return the symbol whose -`transient--infix-command' property's value is ARG." - (or (cl-typep arg 'command) - (cl-typep arg 'symbol) - (signal 'wrong-type-argument `((command symbol) ,arg))) - (if (symbolp arg) - arg - (let* ((obj (transient-suffix-object)) - (sym (oref obj command))) - (if (eq (get sym 'transient--infix-command) arg) - sym - (catch 'found - (mapatoms (lambda (sym) - (when (eq (get sym 'transient--infix-command) arg) - (throw 'found sym))))))))) + (cond + (transient--pending-suffix) + ((or transient--prefix + transient-current-prefix) + (let ((suffixes + (cl-remove-if-not + (lambda (obj) + (eq (oref obj command) + (or command + (if (eq this-command 'transient-set-level) + ;; This is how it can look up for which + ;; command it is setting the level. + this-original-command + this-command)))) + (or transient--suffixes + transient-current-suffixes)))) + (or (and (cdr suffixes) + (cl-find-if + (lambda (obj) + (equal (listify-key-sequence (transient--kbd (oref obj key))) + (listify-key-sequence (this-command-keys)))) + suffixes)) + (car suffixes)))) + ((and-let* ((obj (transient--suffix-prototype (or command this-command))) + (obj (clone obj))) + (progn ; work around debbugs#31840 + (transient-init-scope obj) + (transient-init-value obj) + obj))))) + +(defun transient--suffix-prototype (command) + (or (get command 'transient--suffix) + (seq-some (lambda (cmd) (get cmd 'transient--suffix)) + (function-alias-p command)))) ;;; Keymaps @@ -1601,7 +1762,8 @@ to `transient-predicate-map'. Also see `transient-base-map'." (if transient-show-common-commands "Hide common commands" "Show common permanently"))) - (list "C-x l" "Show/hide suffixes" #'transient-set-level)))))))) + (list "C-x l" "Show/hide suffixes" #'transient-set-level) + (list "C-x a" #'transient-toggle-level-limit)))))))) (defvar-keymap transient-popup-navigation-map :doc "One of the keymaps used when popup navigation is enabled. @@ -1619,6 +1781,16 @@ See `transient-enable-popup-navigation'." "" #'transient-push-button "" #'transient-push-button) +(defvar-keymap transient-resume-mode-map + :doc "Keymap for `transient-resume-mode'. + +This keymap remaps every command that would usually just quit the +documentation buffer to `transient-resume', which additionally +resumes the suspended transient." + " " #'transient-resume + " " #'transient-resume + " " #'transient-resume) + (defvar-keymap transient-predicate-map :doc "Base keymap used to map common commands to their transient behavior. @@ -1654,7 +1826,9 @@ of the corresponding object." "" #'transient--do-stay "" #'transient--do-stay "" #'transient--do-call + "" #'transient--do-exit "" #'transient--do-call + "" #'transient--do-exit "" #'transient--do-call "" #'transient--do-stay "" #'transient--do-stay @@ -1673,7 +1847,10 @@ of the corresponding object." ;; an unbound key, then Emacs calls the `undefined' command ;; but does not set `this-command', `this-original-command' ;; or `real-this-command' accordingly. Instead they are nil. - "" #'transient--do-warn) + "" #'transient--do-warn + ;; Bound to the `mouse-movement' event, this command is similar + ;; to `ignore'. + "" #'transient--do-noop) (defvar transient--transient-map nil) (defvar transient--predicate-map nil) @@ -1708,15 +1885,20 @@ of the corresponding object." (setq key (save-match-data (funcall transient-substitute-key-function obj))) (oset obj key key)) - (let ((kbd (kbd key)) - (cmd (transient--suffix-command obj))) - (when-let ((conflict (and transient-detect-key-conflicts - (transient--lookup-key map kbd)))) - (unless (eq cmd conflict) - (error "Cannot bind %S to %s and also %s" - (string-trim key) - cmd conflict))) - (define-key map kbd cmd)))) + (let* ((kbd (kbd key)) + (cmd (oref obj command)) + (alt (transient--lookup-key map kbd))) + (cond ((not alt) + (define-key map kbd cmd)) + ((eq alt cmd)) + ((transient--inapt-suffix-p obj)) + ((and-let* ((obj (transient-suffix-object alt))) + (transient--inapt-suffix-p obj)) + (define-key map kbd cmd)) + (transient-detect-key-conflicts + (error "Cannot bind %S to %s and also %s" + (string-trim key) cmd alt)) + ((define-key map kbd cmd)))))) (when-let ((b (keymap-lookup map "-"))) (keymap-set map "" b)) (when-let ((b (keymap-lookup map "="))) (keymap-set map "" b)) (when-let ((b (keymap-lookup map "+"))) (keymap-set map "" b)) @@ -1730,51 +1912,66 @@ of the corresponding object." map)) (defun transient--make-predicate-map () - (let ((map (make-sparse-keymap))) + (let* ((default (transient--resolve-pre-command + (oref transient--prefix transient-suffix))) + (return (and transient--stack (eq default t))) + (map (make-sparse-keymap))) (set-keymap-parent map transient-predicate-map) - (when (memq (oref transient--prefix transient-non-suffix) - '(nil transient--do-warn transient--do-noop)) - (keymap-set map "" #'transient--do-suspend)) + (when (or (and (slot-boundp transient--prefix 'transient-switch-frame) + (transient--resolve-pre-command + (not (oref transient--prefix transient-switch-frame)))) + (memq (transient--resolve-pre-command + (oref transient--prefix transient-non-suffix)) + '(nil transient--do-warn transient--do-noop))) + (define-key map [handle-switch-frame] #'transient--do-suspend)) (dolist (obj transient--suffixes) (let* ((cmd (oref obj command)) - (sub-prefix (and (symbolp cmd) (get cmd 'transient--prefix) t)) - (sym (transient--suffix-symbol cmd))) + (kind (cond ((get cmd 'transient--prefix) 'prefix) + ((cl-typep obj 'transient-infix) 'infix) + (t 'suffix)))) (cond ((oref obj inapt) - (define-key map (vector sym) #'transient--do-warn-inapt)) + (define-key map (vector cmd) #'transient--do-warn-inapt)) ((slot-boundp obj 'transient) - (define-key map (vector sym) - (let ((do (oref obj transient))) - (pcase (list do sub-prefix) - ('(t t) #'transient--do-recurse) - ('(t nil) (if (cl-typep obj 'transient-infix) - #'transient--do-stay - #'transient--do-call)) - ('(nil t) #'transient--do-replace) - ('(nil nil) #'transient--do-exit) - (_ do))))) - ((not (lookup-key transient-predicate-map (vector sym))) - (define-key map (vector sym) - (if sub-prefix - #'transient--do-replace - (or (oref transient--prefix transient-suffix) - #'transient--do-exit))))))) + (define-key map (vector cmd) + (pcase (list kind + (transient--resolve-pre-command (oref obj transient)) + return) + (`(prefix t ,_) #'transient--do-recurse) + (`(prefix nil ,_) #'transient--do-stack) + (`(infix t ,_) #'transient--do-stay) + (`(suffix t ,_) #'transient--do-call) + ('(suffix nil t) #'transient--do-return) + (`(,_ nil ,_) #'transient--do-exit) + (`(,_ ,do ,_) do)))) + ((not (lookup-key transient-predicate-map (vector cmd))) + (define-key map (vector cmd) + (pcase (list kind default return) + (`(prefix ,(or 'transient--do-stay 'transient--do-call) ,_) + #'transient--do-recurse) + (`(prefix t ,_) #'transient--do-recurse) + (`(prefix ,_ ,_) #'transient--do-stack) + (`(infix ,_ ,_) #'transient--do-stay) + (`(suffix t ,_) #'transient--do-call) + ('(suffix nil t) #'transient--do-return) + (`(suffix nil ,_) #'transient--do-exit) + (`(suffix ,do ,_) do))))))) map)) (defun transient--make-redisplay-map () (setq transient--redisplay-key - (cl-case this-command - (transient-update + (pcase this-command + ('transient-update (setq transient--showp t) (setq unread-command-events (listify-key-sequence (this-single-command-raw-keys)))) - (transient-quit-seq + ('transient-quit-seq (setq unread-command-events (butlast (listify-key-sequence (this-single-command-raw-keys)) 2)) (butlast transient--redisplay-key)) - (t nil))) + (_ nil))) (let ((topmap (make-sparse-keymap)) (submap (make-sparse-keymap))) (when transient--redisplay-key @@ -1808,7 +2005,7 @@ the \"scope\" of the transient (see `transient-define-prefix'). This function is also called internally in which case LAYOUT and EDIT may be non-nil." (transient--debug 'setup) - (transient--with-emergency-exit + (transient--with-emergency-exit :setup (cond ((not name) ;; Switching between regular and edit mode. @@ -1818,7 +2015,7 @@ EDIT may be non-nil." (setq params (list :scope (oref transient--prefix scope)))) (transient--prefix ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}" - ;; of an outer prefix. Unlike the usual `transient--do-replace', + ;; of an outer prefix. Unlike the usual `transient--do-stack', ;; these predicates fail to clean up after the outer prefix. (transient--pop-keymap 'transient--transient-map) (transient--pop-keymap 'transient--redisplay-map)) @@ -1829,10 +2026,8 @@ EDIT may be non-nil." ;; Returning from help to edit. (setq transient--editp t))) (transient--init-objects name layout params) + (transient--init-keymaps) (transient--history-init transient--prefix) - (setq transient--predicate-map (transient--make-predicate-map)) - (setq transient--transient-map (transient--make-transient-map)) - (setq transient--redisplay-map (transient--make-redisplay-map)) (setq transient--original-window (selected-window)) (setq transient--original-buffer (current-buffer)) (setq transient--minibuffer-depth (minibuffer-depth)) @@ -1849,8 +2044,16 @@ value. Otherwise return CHILDREN as is." (funcall (oref group setup-children) children) children)) -(defun transient--init-objects (name layout params) - (setq transient--prefix (transient--init-prefix name params)) +(defun transient--init-keymaps () + (setq transient--predicate-map (transient--make-predicate-map)) + (setq transient--transient-map (transient--make-transient-map)) + (setq transient--redisplay-map (transient--make-redisplay-map))) + +(defun transient--init-objects (&optional name layout params) + (if name + (setq transient--prefix (transient--init-prefix name params)) + (setq name (oref transient--prefix command))) + (setq transient--refreshp (oref transient--prefix refresh-suffixes)) (setq transient--layout (or layout (transient--init-suffixes name))) (setq transient--suffixes (transient--flatten-suffixes transient--layout))) @@ -1877,10 +2080,11 @@ value. Otherwise return CHILDREN as is." (cl-labels ((s (def) (cond ((stringp def) nil) + ((cl-typep def 'transient-information) nil) ((listp def) (cl-mapcan #'s def)) - ((transient-group--eieio-childp def) + ((cl-typep def 'transient-group) (cl-mapcan #'s (oref def suffixes))) - ((transient-suffix--eieio-childp def) + ((cl-typep def 'transient-suffix) (list def))))) (cl-mapcan #'s layout))) @@ -1892,19 +2096,21 @@ value. Otherwise return CHILDREN as is." (defun transient--init-group (levels spec) (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) - (when-let* ((- (transient--use-level-p level)) - (obj (apply class :level level args)) - (- (transient--use-suffix-p obj)) - (suffixes (cl-mapcan (lambda (c) (transient--init-child levels c)) - (transient-setup-children obj children)))) - ;; Cannot use and-let* because of debbugs#31840. - (oset obj suffixes suffixes) - (list obj)))) + (and-let* (((transient--use-level-p level)) + (obj (apply class :level level args)) + ((transient--use-suffix-p obj)) + (suffixes (cl-mapcan (lambda (c) (transient--init-child levels c)) + (transient-setup-children obj children)))) + (progn ; work around debbugs#31840 + (oset obj suffixes suffixes) + (list obj))))) (defun transient--init-suffix (levels spec) (pcase-let* ((`(,level ,class ,args) spec) (cmd (plist-get args :command)) - (level (or (alist-get (transient--suffix-symbol cmd) levels) + (key (transient--kbd (plist-get args :key))) + (level (or (alist-get (cons cmd key) levels nil nil #'equal) + (alist-get cmd levels) level))) (let ((fn (and (symbolp cmd) (symbol-function cmd)))) @@ -1912,13 +2118,25 @@ value. Otherwise return CHILDREN as is." (transient--debug " autoload %s" cmd) (autoload-do-load fn))) (when (transient--use-level-p level) - (let ((obj (if-let ((proto (and cmd - (symbolp cmd) - (get cmd 'transient--suffix)))) - (apply #'clone proto :level level args) - (apply class :level level args)))) - (transient--init-suffix-key obj) - (transient--ensure-infix-command obj) + (let ((obj (if (child-of-class-p class 'transient-information) + (apply class :level level args) + (unless (and cmd (symbolp cmd)) + (error "BUG: Non-symbolic suffix command: %s" cmd)) + (if-let ((proto (and cmd (transient--suffix-prototype cmd)))) + (apply #'clone proto :level level args) + (apply class :command cmd :level level args))))) + (cond ((not cmd)) + ((commandp cmd)) + ((or (cl-typep obj 'transient-switch) + (cl-typep obj 'transient-option)) + ;; As a temporary special case, if the package was compiled + ;; with an older version of Transient, then we must define + ;; "anonymous" switch and option commands here. + (defalias cmd #'transient--default-infix-command)) + ((transient--use-suffix-p obj) + (error "Suffix command %s is not defined or autoloaded" cmd))) + (unless (cl-typep obj 'transient-information) + (transient--init-suffix-key obj)) (when (transient--use-suffix-p obj) (if (transient--inapt-suffix-p obj) (oset obj inapt t) @@ -1942,33 +2160,38 @@ value. Otherwise return CHILDREN as is." (error "No key for %s" (oref obj command)))))) (defun transient--use-level-p (level &optional edit) - (or (and transient--editp (not edit)) + (or transient--all-levels-p + (and transient--editp (not edit)) (and (>= level 1) (<= level (oref transient--prefix level))))) (defun transient--use-suffix-p (obj) - (transient--do-suffix-p - (oref obj if) - (oref obj if-not) - (oref obj if-nil) - (oref obj if-non-nil) - (oref obj if-mode) - (oref obj if-not-mode) - (oref obj if-derived) - (oref obj if-not-derived) - t)) + (let ((transient--shadowed-buffer (current-buffer)) + (transient--pending-suffix obj)) + (transient--do-suffix-p + (oref obj if) + (oref obj if-not) + (oref obj if-nil) + (oref obj if-non-nil) + (oref obj if-mode) + (oref obj if-not-mode) + (oref obj if-derived) + (oref obj if-not-derived) + t))) (defun transient--inapt-suffix-p (obj) - (transient--do-suffix-p - (oref obj inapt-if) - (oref obj inapt-if-not) - (oref obj inapt-if-nil) - (oref obj inapt-if-non-nil) - (oref obj inapt-if-mode) - (oref obj inapt-if-not-mode) - (oref obj inapt-if-derived) - (oref obj inapt-if-not-derived) - nil)) + (let ((transient--shadowed-buffer (current-buffer)) + (transient--pending-suffix obj)) + (transient--do-suffix-p + (oref obj inapt-if) + (oref obj inapt-if-not) + (oref obj inapt-if-nil) + (oref obj inapt-if-non-nil) + (oref obj inapt-if-mode) + (oref obj inapt-if-not-mode) + (oref obj inapt-if-derived) + (oref obj inapt-if-not-derived) + nil))) (defun transient--do-suffix-p (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived @@ -1984,13 +2207,15 @@ value. Otherwise return CHILDREN as is." (if-not-mode (not (if (atom if-not-mode) (eq major-mode if-not-mode) (memq major-mode if-not-mode)))) - (if-derived (if (atom if-derived) + (if-derived (if (or (atom if-derived) + (>= emacs-major-version 30)) (derived-mode-p if-derived) (apply #'derived-mode-p if-derived))) - (if-not-derived (not (if (atom if-not-derived) + (if-not-derived (not (if (or (atom if-not-derived) + (>= emacs-major-version 30)) (derived-mode-p if-not-derived) (apply #'derived-mode-p if-not-derived)))) - (t default))) + (default))) (defun transient--suffix-predicate (spec) (let ((plist (nth 2 spec))) @@ -2021,16 +2246,27 @@ value. Otherwise return CHILDREN as is." ;; that we just added. (setq transient--exitp 'replace))) +(defun transient--refresh-transient () + (transient--debug 'refresh-transient) + (transient--pop-keymap 'transient--predicate-map) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (transient--init-objects) + (transient--init-keymaps) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (transient--redisplay)) + (defun transient--pre-command () (transient--debug 'pre-command) - (transient--with-emergency-exit + (transient--with-emergency-exit :pre-command ;; The use of `overriding-terminal-local-map' does not prevent the ;; lookup of command remappings in the overridden maps, which can ;; lead to a suffix being remapped to a non-suffix. We have to undo ;; the remapping in that case. However, remapping a non-suffix to ;; another should remain possible. - (when (and (transient--get-predicate-for this-original-command 'suffix) - (not (transient--get-predicate-for this-command 'suffix))) + (when (and (transient--get-pre-command this-original-command 'suffix) + (not (transient--get-pre-command this-command 'suffix))) (setq this-command this-original-command)) (cond ((memq this-command '(transient-update transient-quit-seq)) @@ -2054,35 +2290,11 @@ value. Otherwise return CHILDREN as is." (transient--wrap-command)) (t (setq transient--exitp nil) - (let ((exitp (eq (transient--do-pre-command) transient--exit))) + (let ((exitp (eq (transient--call-pre-command) transient--exit))) (transient--wrap-command) (when exitp (transient--pre-exit))))))) -(defun transient--do-pre-command () - (if-let ((fn (transient--get-predicate-for this-command))) - (let ((action (funcall fn))) - (when (eq action transient--exit) - (setq transient--exitp (or transient--exitp t))) - action) - (if (let ((keys (this-command-keys-vector))) - (eq (aref keys (1- (length keys))) ?\C-g)) - (setq this-command 'transient-noop) - (unless (transient--edebug-command-p) - (setq this-command 'transient-undefined))) - transient--stay)) - -(defun transient--get-predicate-for (cmd &optional suffix-only) - (or (ignore-errors - (lookup-key transient--predicate-map - (vector (transient--suffix-symbol cmd)))) - (and (not suffix-only) - (let ((pred (oref transient--prefix transient-non-suffix))) - (pcase pred - ('t #'transient--do-stay) - ('nil #'transient--do-warn) - (_ pred)))))) - (defun transient--pre-exit () (transient--debug 'pre-exit) (transient--delete-window) @@ -2109,13 +2321,14 @@ value. Otherwise return CHILDREN as is." (when (window-live-p transient--window) (let ((remain-in-minibuffer-window (and (minibuffer-selected-window) - (selected-window))) - (buf (window-buffer transient--window))) - ;; Only delete the window if it never showed another buffer. - (unless (eq (car (window-parameter transient--window 'quit-restore)) 'other) + (selected-window)))) + ;; Only delete the window if it has never shown another buffer. + (unless (eq (car (window-parameter transient--window 'quit-restore)) + 'other) (with-demoted-errors "Error while exiting transient: %S" (delete-window transient--window))) - (kill-buffer buf) + (when-let ((buffer (get-buffer transient--buffer-name))) + (kill-buffer buffer)) (when remain-in-minibuffer-window (select-window remain-in-minibuffer-window))))) @@ -2133,7 +2346,10 @@ value. Otherwise return CHILDREN as is." ((and transient--prefix transient--redisplay-key) (setq transient--redisplay-key nil) (when transient--showp - (transient--show)))) + (if-let ((win (minibuffer-selected-window))) + (with-selected-window win + (transient--show)) + (transient--show))))) (transient--pop-keymap 'transient--transient-map) (transient--pop-keymap 'transient--redisplay-map) (remove-hook 'pre-command-hook #'transient--pre-command) @@ -2188,41 +2404,72 @@ value. Otherwise return CHILDREN as is." (remove-hook 'minibuffer-exit-hook ,exit))) ,@body))) -(defun transient--wrap-command () - (let* ((prefix transient--prefix) - (suffix this-command) - (advice nil) - (advice-interactive - (lambda (spec) - (let ((abort t)) +(static-if (>= emacs-major-version 30) ;transient--wrap-command + (defun transient--wrap-command () + (cl-assert + (>= emacs-major-version 30) nil + "Emacs was downgraded, making it necessary to recompile Transient") + (letrec + ((prefix transient--prefix) + (suffix this-command) + (advice + (lambda (fn &rest args) + (interactive + (lambda (spec) + (let ((abort t)) + (unwind-protect + (prog1 (let ((debugger #'transient--exit-and-debug)) + (advice-eval-interactive-spec spec)) + (setq abort nil)) + (when abort + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-interactive) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil)))))) (unwind-protect - (prog1 (advice-eval-interactive-spec spec) - (setq abort nil)) - (when abort - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-interactive) - (funcall unwind suffix)) - (if (symbolp suffix) - (advice-remove suffix advice) - (remove-function suffix advice)) - (oset prefix unwind-suffix nil)))))) - (advice-body - (lambda (fn &rest args) - (unwind-protect - (apply fn args) - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-command) - (funcall unwind suffix)) - (if (symbolp suffix) - (advice-remove suffix advice) - (remove-function suffix advice)) - (oset prefix unwind-suffix nil))))) - (setq advice `(lambda (fn &rest args) - (interactive ,advice-interactive) - (apply ',advice-body fn args))) - (if (symbolp suffix) - (advice-add suffix :around advice '((depth . -99))) - (add-function :around (var suffix) advice '((depth . -99)))))) + (let ((debugger #'transient--exit-and-debug)) + (apply fn args)) + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-command) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil))))) + (when (symbolp this-command) + (advice-add suffix :around advice '((depth . -99)))))) + + (defun transient--wrap-command () + (let* ((prefix transient--prefix) + (suffix this-command) + (advice nil) + (advice-interactive + (lambda (spec) + (let ((abort t)) + (unwind-protect + (prog1 (let ((debugger #'transient--exit-and-debug)) + (advice-eval-interactive-spec spec)) + (setq abort nil)) + (when abort + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-interactive) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil)))))) + (advice-body + (lambda (fn &rest args) + (unwind-protect + (let ((debugger #'transient--exit-and-debug)) + (apply fn args)) + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-command) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil))))) + (setq advice `(lambda (fn &rest args) + (interactive ,advice-interactive) + (apply ',advice-body fn args))) + (when (symbolp this-command) + (advice-add suffix :around advice '((depth . -99))))))) (defun transient--premature-post-command () (and (equal (this-command-keys-vector) []) @@ -2241,9 +2488,23 @@ value. Otherwise return CHILDREN as is." (defun transient--post-command () (unless (transient--premature-post-command) (transient--debug 'post-command) - (transient--with-emergency-exit + (transient--with-emergency-exit :post-command (cond (transient--exitp (transient--post-exit)) - ((eq this-command (oref transient--prefix command))) + ;; If `this-command' is the current transient prefix, then we + ;; have already taken care of updating the transient buffer... + ((and (eq this-command (oref transient--prefix command)) + ;; ... but if `prefix-arg' is non-nil, then the values + ;; of `this-command' and `real-this-command' are untrue + ;; because `prefix-command-preserve-state' changes them. + ;; We cannot use `current-prefix-arg' because it is set + ;; too late (in `command-execute'), and if it were set + ;; earlier, then we likely still would not be able to + ;; rely on it and `prefix-command-preserve-state-hook' + ;; would have to be used to record that a universal + ;; argument is in effect. + (not prefix-arg))) + (transient--refreshp + (transient--refresh-transient)) ((let ((old transient--redisplay-map) (new (transient--make-redisplay-map))) (unless (equal old new) @@ -2283,6 +2544,7 @@ value. Otherwise return CHILDREN as is." (setq transient--exitp nil) (setq transient--helpp nil) (setq transient--editp nil) + (setq transient--all-levels-p nil) (setq transient--minibuffer-depth 0) (run-hooks 'transient-exit-hook) (when resume @@ -2293,6 +2555,7 @@ value. Otherwise return CHILDREN as is." (push (list (oref transient--prefix command) transient--layout transient--editp + :transient-suffix (oref transient--prefix transient-suffix) :scope (oref transient--prefix scope)) transient--stack)) @@ -2343,24 +2606,28 @@ value. Otherwise return CHILDREN as is." (if (symbolp arg) (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)" arg - (or (ignore-errors (transient--suffix-symbol this-command)) + (or (and (symbolp this-command) this-command) (if (byte-code-function-p this-command) "#[...]" this-command)) (key-description (this-command-keys-vector)) transient--exitp - (cond ((stringp (car args)) + (cond ((keywordp (car args)) + (format ", from: %s" + (substring (symbol-name (car args)) 1))) + ((stringp (car args)) (concat ", " (apply #'format args))) - (args + ((functionp (car args)) (concat ", " (apply (car args) (cdr args)))) - (t ""))) + (""))) (apply #'message arg args))))) -(defun transient--emergency-exit () +(defun transient--emergency-exit (&optional id) "Exit the current transient command after an error occurred. -When no transient is active (i.e. when `transient--prefix' is -nil) then do nothing." - (transient--debug 'emergency-exit) +When no transient is active (i.e., when `transient--prefix' is +nil) then do nothing. Optional ID is a keyword identifying the +exit." + (transient--debug 'emergency-exit id) (when transient--prefix (setq transient--stack nil) (setq transient--exitp t) @@ -2369,6 +2636,37 @@ nil) then do nothing." ;;; Pre-Commands +(defun transient--call-pre-command () + (if-let ((fn (transient--get-pre-command this-command))) + (let ((action (funcall fn))) + (when (eq action transient--exit) + (setq transient--exitp (or transient--exitp t))) + action) + (if (let ((keys (this-command-keys-vector))) + (eq (aref keys (1- (length keys))) ?\C-g)) + (setq this-command 'transient-noop) + (unless (transient--edebug-command-p) + (setq this-command 'transient-undefined))) + transient--stay)) + +(defun transient--get-pre-command (&optional cmd enforce-type) + (or (and (not (eq enforce-type 'non-suffix)) + (symbolp cmd) + (lookup-key transient--predicate-map (vector cmd))) + (and (not (eq enforce-type 'suffix)) + (transient--resolve-pre-command + (oref transient--prefix transient-non-suffix) + t)))) + +(defun transient--resolve-pre-command (pre &optional resolve-boolean) + (cond ((booleanp pre) + (if resolve-boolean + (if pre #'transient--do-stay #'transient--do-warn) + pre)) + ((string-match-p "--do-" (symbol-name pre)) pre) + ((let ((sym (intern (format "transient--do-%s" pre)))) + (if (functionp sym) sym pre))))) + (defun transient--do-stay () "Call the command without exporting variables and stay transient." transient--stay) @@ -2409,7 +2707,8 @@ If there is no parent prefix, then behave like `transient--do-exit'." (defun transient--do-leave () "Call the command without exporting variables and exit the transient." - transient--stay) + (transient--stack-zap) + transient--exit) (defun transient--do-push-button () "Call the command represented by the activated button. @@ -2424,26 +2723,35 @@ Use that command's pre-command to determine transient behavior." (posn-point (event-start last-command-event)) (point)) 'command))) - (transient--do-pre-command))) + (transient--call-pre-command))) (defun transient--do-recurse () "Call the transient prefix command, preparing for return to active transient. If there is no parent prefix, then just call the command." - (transient--do-replace)) + (transient--do-stack)) (defun transient--setup-recursion (prefix-obj) (when transient--stack (let ((command (oref prefix-obj command))) (when-let ((suffix-obj (transient-suffix-object command))) - (when (and (slot-boundp suffix-obj 'transient) - (memq (oref suffix-obj transient) - (list t #'transient--do-recurse))) - (oset prefix-obj transient-suffix 'transient--do-return)))))) + (when (memq (if (slot-boundp suffix-obj 'transient) + (oref suffix-obj transient) + (oref transient-current-prefix transient-suffix)) + (list t #'transient--do-recurse)) + (oset prefix-obj transient-suffix t)))))) + +(defun transient--do-stack () + "Call the transient prefix command, stacking the active transient. +Push the active transient to the transient stack." + (transient--export) + (transient--stack-push) + (setq transient--exitp 'replace) + transient--exit) (defun transient--do-replace () - "Call the transient prefix command, replacing the active transient." + "Call the transient prefix command, replacing the active transient. +Do not push the active transient to the transient stack." (transient--export) - (transient--stack-push) (setq transient--exitp 'replace) transient--exit) @@ -2462,7 +2770,9 @@ If there is no parent prefix, then just call the command." (setq transient--editp nil) (transient-setup) transient--stay) - (t transient--exit))) + (prefix-arg + transient--stay) + (transient--exit))) (defun transient--do-quit-all () "Exit all transients without saving the transient stack." @@ -2474,7 +2784,7 @@ If there is no parent prefix, then just call the command." In that case behave like `transient--do-stay', otherwise similar to `transient--do-warn'." (unless transient-enable-popup-navigation - (setq this-command 'transient-popup-navigation-help)) + (setq this-command 'transient-inhibit-move)) transient--stay) (defun transient--do-minus () @@ -2485,22 +2795,27 @@ prefix argument and pivot to `transient-update'." (setq this-command 'transient-update)) transient--stay) -(put 'transient--do-stay 'transient-color 'transient-red) -(put 'transient--do-noop 'transient-color 'transient-red) -(put 'transient--do-warn 'transient-color 'transient-red) -(put 'transient--do-warn-inapt 'transient-color 'transient-red) -(put 'transient--do-call 'transient-color 'transient-red) -(put 'transient--do-return 'transient-color 'transient-purple) -(put 'transient--do-exit 'transient-color 'transient-blue) -(put 'transient--do-recurse 'transient-color 'transient-red) -(put 'transient--do-replace 'transient-color 'transient-blue) -(put 'transient--do-suspend 'transient-color 'transient-blue) -(put 'transient--do-quit-one 'transient-color 'transient-blue) -(put 'transient--do-quit-all 'transient-color 'transient-blue) -(put 'transient--do-move 'transient-color 'transient-red) -(put 'transient--do-minus 'transient-color 'transient-red) +(put 'transient--do-stay 'transient-face 'transient-key-stay) +(put 'transient--do-noop 'transient-face 'transient-key-noop) +(put 'transient--do-warn 'transient-face 'transient-key-noop) +(put 'transient--do-warn-inapt 'transient-face 'transient-key-noop) +(put 'transient--do-call 'transient-face 'transient-key-stay) +(put 'transient--do-return 'transient-face 'transient-key-return) +(put 'transient--do-exit 'transient-face 'transient-key-exit) +(put 'transient--do-leave 'transient-face 'transient-key-exit) + +(put 'transient--do-recurse 'transient-face 'transient-key-stay) +(put 'transient--do-stack 'transient-face 'transient-key-stay) +(put 'transient--do-replace 'transient-face 'transient-key-exit) +(put 'transient--do-suspend 'transient-face 'transient-key-exit) + +(put 'transient--do-quit-one 'transient-face 'transient-key-return) +(put 'transient--do-quit-all 'transient-face 'transient-key-exit) +(put 'transient--do-move 'transient-face 'transient-key-stay) +(put 'transient--do-minus 'transient-face 'transient-key-stay) ;;; Commands +;;;; Noop (defun transient-noop () "Do nothing at all." @@ -2526,12 +2841,9 @@ prefix argument and pivot to `transient-update'." (propertize "?" 'face 'transient-key) ;; `this-command' is `transient-undefined' or `transient-inapt'. ;; Show the command (`this-original-command') the user actually - ;; tried to invoke. For an anonymous inapt command that is a - ;; lambda expression, which cannot be mapped to a symbol, so - ;; forgo displaying the command. - (if-let ((cmd (ignore-errors - (symbol-name (transient--suffix-symbol - this-original-command))))) + ;; tried to invoke. + (if-let ((cmd (or (ignore-errors (symbol-name this-original-command)) + (ignore-errors (symbol-name this-command))))) (format " [%s]" (propertize cmd 'face 'font-lock-warning-face)) "")) (unless (and transient--transient-map @@ -2542,27 +2854,23 @@ prefix argument and pivot to `transient-update'." (other-window 1) (display-warning 'transient "Inconsistent transient state detected. This should never happen. -Please open an issue and post the shown command log. -This is a heisenbug, so any additional details might help. -Thanks!" :error))) +Please open an issue and post the shown command log." :error))) -(defun transient-toggle-common () - "Toggle whether common commands are always shown." +(defun transient-inhibit-move () + "Warn the user that popup navigation is disabled." (interactive) - (setq transient-show-common-commands (not transient-show-common-commands))) + (message "To enable use of `%s', please customize `%s'" + this-original-command + 'transient-enable-popup-navigation)) -(defun transient-suspend () - "Suspend the current transient. -It can later be resumed using `transient-resume' while no other -transient is active." - (interactive)) +;;;; Core (defun transient-quit-all () "Exit all transients without saving the transient stack." (interactive)) (defun transient-quit-one () - "Exit the current transients, possibly returning to the previous." + "Exit the current transients, returning to outer transient, if any." (interactive)) (defun transient-quit-seq () @@ -2572,17 +2880,48 @@ transient is active." (defun transient-update () "Redraw the transient's state in the popup buffer." (interactive) - (when (equal this-original-command 'negative-argument) - (setq prefix-arg current-prefix-arg))) + (setq prefix-arg current-prefix-arg)) (defun transient-show () "Show the transient's state in the popup buffer." (interactive) (setq transient--showp t)) -(defvar-local transient--restore-winconf nil) +(defun transient-push-button () + "Invoke the suffix command represented by this button." + (interactive)) + +;;;; Suspend + +(defun transient-suspend () + "Suspend the current transient. +It can later be resumed using `transient-resume', while no other +transient is active." + (interactive)) + +(define-minor-mode transient-resume-mode + "Auxiliary minor-mode used to resume a transient after viewing help.") + +(defun transient-resume () + "Resume a previously suspended stack of transients." + (interactive) + (cond (transient--stack + (let ((winconf transient--restore-winconf)) + (kill-local-variable 'transient--restore-winconf) + (when transient-resume-mode + (transient-resume-mode -1) + (quit-window)) + (when winconf + (set-window-configuration winconf))) + (transient--stack-pop)) + (transient-resume-mode + (kill-local-variable 'transient--restore-winconf) + (transient-resume-mode -1) + (quit-window)) + (t + (message "No suspended transient command")))) -(defvar transient-resume-mode) +;;;; Help (defun transient-help (&optional interactive) "Show help for the active transient or one of its suffixes.\n\n(fn)" @@ -2599,12 +2938,15 @@ transient is active." transient--prefix (or (transient-suffix-object) this-original-command))) - (setq transient--restore-winconf winconf)) + (setq-local transient--restore-winconf winconf)) (fit-window-to-buffer nil (frame-height) (window-height)) (transient-resume-mode) - (message "Type \"q\" to resume transient command.") + (message (substitute-command-keys + "Type \\`q' to resume transient command.")) t)))) +;;;; Level + (defun transient-set-level (&optional command level) "Set the level of the transient or one of its suffix commands." (interactive @@ -2616,13 +2958,11 @@ transient is active." (list command (let ((keys (this-single-command-raw-keys))) (and (lookup-key transient--transient-map keys) - (string-to-number - (let ((transient--active-infix - (transient-suffix-object command))) - (transient--show) + (progn + (transient--show) + (string-to-number (transient--read-number-N - (format "Set level for `%s': " - (transient--suffix-symbol command)) + (format "Set level for `%s': " command) nil nil (not (eq command prefix))))))))))) (cond ((not command) @@ -2631,32 +2971,64 @@ transient is active." (level (let* ((prefix (oref transient--prefix command)) (alist (alist-get prefix transient-levels)) - (sym (transient--suffix-symbol command))) - (if (eq command prefix) - (progn (oset transient--prefix level level) - (setq sym t)) - (oset (transient-suffix-object command) level level)) - (setf (alist-get sym alist) level) + (akey command)) + (cond ((eq command prefix) + (oset transient--prefix level level) + (setq akey t)) + (t + (oset (transient-suffix-object command) level level) + (when (cdr (cl-remove-if-not (lambda (obj) + (eq (oref obj command) command)) + transient--suffixes)) + (setq akey (cons command (this-command-keys)))))) + (setf (alist-get akey alist) level) (setf (alist-get prefix transient-levels) alist)) (transient-save-levels) (transient--show)) (t (transient-undefined)))) +(transient-define-suffix transient-toggle-level-limit () + "Toggle whether to temporarily displayed suffixes on all levels." + :description + (lambda () + (cond + ((= transient-default-level transient--max-level) + "Always displaying all levels") + (transient--all-levels-p + (format "Hide suffix %s" + (propertize + (format "levels > %s" (oref (transient-prefix-object) level)) + 'face 'transient-higher-level))) + ("Show all suffix levels"))) + :inapt-if (lambda () (= transient-default-level transient--max-level)) + :transient t + (interactive) + (setq transient--all-levels-p (not transient--all-levels-p)) + (setq transient--refreshp t)) + +;;;; Value + (defun transient-set () - "Save the value of the active transient for this Emacs session." + "Set active transient's value for this Emacs session." (interactive) - (transient-set-value (or transient--prefix transient-current-prefix))) + (transient-set-value (transient-prefix-object))) + +(defalias 'transient-set-and-exit 'transient-set + "Set active transient's value for this Emacs session and exit.") (defun transient-save () - "Save the value of the active transient persistenly across Emacs sessions." + "Save active transient's value for this and future Emacs sessions." (interactive) - (transient-save-value (or transient--prefix transient-current-prefix))) + (transient-save-value (transient-prefix-object))) + +(defalias 'transient-save-and-exit 'transient-save + "Save active transient's value for this and future Emacs sessions and exit.") (defun transient-reset () "Clear the set and saved values of the active transient." (interactive) - (transient-reset-value (or transient--prefix transient-current-prefix))) + (transient-reset-value (transient-prefix-object))) (defun transient-history-next () "Switch to the next value used for the active transient." @@ -2683,44 +3055,36 @@ transient is active." (oset obj value (nth pos hst)) (mapc #'transient-init-value transient--suffixes)))) -(defun transient-scroll-up (&optional arg) - "Scroll text of transient popup window upward ARG lines. -If ARG is nil scroll near full screen. This is a wrapper -around `scroll-up-command' (which see)." - (interactive "^P") - (with-selected-window transient--window - (scroll-up-command arg))) - -(defun transient-scroll-down (&optional arg) - "Scroll text of transient popup window down ARG lines. -If ARG is nil scroll near full screen. This is a wrapper -around `scroll-down-command' (which see)." - (interactive "^P") - (with-selected-window transient--window - (scroll-down-command arg))) +;;;; Auxiliary -(defun transient-push-button () - "Invoke the suffix command represented by this button." - (interactive)) +(defun transient-toggle-common () + "Toggle whether common commands are permanently shown." + (interactive) + (setq transient-show-common-commands (not transient-show-common-commands))) -(defun transient-resume () - "Resume a previously suspended stack of transients." +(defun transient-toggle-debug () + "Toggle debugging statements for transient commands." (interactive) - (cond (transient--stack - (let ((winconf transient--restore-winconf)) - (kill-local-variable 'transient--restore-winconf) - (when transient-resume-mode - (transient-resume-mode -1) - (quit-window)) - (when winconf - (set-window-configuration winconf))) - (transient--stack-pop)) - (transient-resume-mode - (kill-local-variable 'transient--restore-winconf) - (transient-resume-mode -1) - (quit-window)) - (t - (message "No suspended transient command")))) + (setq transient--debug (not transient--debug)) + (message "Debugging transient %s" + (if transient--debug "enabled" "disabled"))) + +(transient-define-suffix transient-echo-arguments (arguments) + "Show the transient's active ARGUMENTS in the echo area. +Intended for use in prefixes used for demonstration purposes, +such as when suggesting a new feature or reporting an issue." + :transient t + :description "Echo arguments" + :key "x" + (interactive (list (transient-args transient-current-command))) + (message "%s: %s" + (key-description (this-command-keys)) + (mapconcat (lambda (arg) + (propertize (if (string-match-p " " arg) + (format "%S" arg) + arg) + 'face 'transient-argument)) + arguments " "))) ;;; Value ;;;; Init @@ -2826,29 +3190,19 @@ user using the reader specified by the `reader' slot (using the `transient-infix' method described below). For some infix classes the value is changed without reading -anything in the minibuffer, i.e. the mere act of invoking the +anything in the minibuffer, i.e., the mere act of invoking the infix command determines what the new value should be, based on the previous value.") (cl-defmethod transient-infix-read :around ((obj transient-infix)) - "Highlight the infix in the popup buffer. - -This also wraps the call to `cl-call-next-method' with two -macros. - -`transient--with-suspended-override' is necessary to allow -reading user input using the minibuffer. - -`transient--with-emergency-exit' arranges for the transient to -be exited in case of an error because otherwise Emacs would get -stuck in an inconsistent state, which might make it necessary to -kill it from the outside. - -If you replace this method, then you must make sure to always use -the latter macro and most likely also the former." - (let ((transient--active-infix obj)) - (transient--show)) - (transient--with-emergency-exit + "Refresh the transient buffer and call the next method. + +Also wrap `cl-call-next-method' with two macros: +- `transient--with-suspended-override' allows use of minibuffer. +- `transient--with-emergency-exit' arranges for the transient to + be exited in case of an error." + (transient--show) + (transient--with-emergency-exit :infix-read (transient--with-suspended-override (cl-call-next-method obj)))) @@ -2865,7 +3219,7 @@ the lack of history, for example. Only for very simple classes that toggle or cycle through a very limited number of possible values should you replace this with a -simple method that does not handle history. (E.g. for a command +simple method that does not handle history. (E.g., for a command line switch the only possible values are \"use it\" and \"don't use it\", in which case it is pointless to preserve history.)" (with-slots (value multi-value always-read allow-empty choices) obj @@ -2876,6 +3230,7 @@ it\", in which case it is pointless to preserve history.)" (oset obj value nil) (let* ((enable-recursive-minibuffers t) (reader (oref obj reader)) + (choices (if (functionp choices) (funcall choices) choices)) (prompt (transient-prompt obj)) (value (if multi-value (mapconcat #'identity value ",") value)) (history-key (or (oref obj history-key) @@ -2898,7 +3253,7 @@ it\", in which case it is pointless to preserve history.)" initial-input history)) (choices (completing-read prompt choices nil t initial-input history)) - (t (read-string prompt initial-input history))))) + ((read-string prompt initial-input history))))) (cond ((and (equal value "") (not allow-empty)) (setq value nil)) ((and (equal value "\"\"") allow-empty) @@ -2929,8 +3284,10 @@ The last value is \"don't use any of these switches\"." "Elsewhere use the reader of the infix command COMMAND. Use this if you want to share an infix's history with a regular stand-alone command." - (cl-letf (((symbol-function #'transient--show) #'ignore)) - (transient-infix-read (get command 'transient--suffix)))) + (if-let ((obj (transient--suffix-prototype command))) + (cl-letf (((symbol-function #'transient--show) #'ignore)) + (transient-infix-read obj)) + (error "Not a suffix command: `%s'" command))) ;;;; Readers @@ -3021,8 +3378,6 @@ prompt." ;;;; Set -(defvar transient--unset-incompatible t) - (cl-defgeneric transient-infix-set (obj value) "Set the value of infix object OBJ to value.") @@ -3030,29 +3385,32 @@ prompt." "Set the value of infix object OBJ to value." (oset obj value value)) -(cl-defmethod transient-infix-set :around ((obj transient-argument) value) +(cl-defmethod transient-infix-set :after ((obj transient-argument) value) "Unset incompatible infix arguments." - (let ((arg (if (slot-boundp obj 'argument) - (oref obj argument) - (oref obj argument-regexp)))) - (if-let ((sic (and value arg transient--unset-incompatible)) - (spec (oref transient--prefix incompatible)) - (incomp (cl-mapcan (lambda (rule) - (and (member arg rule) - (remove arg rule))) - spec))) - (progn - (cl-call-next-method obj value) - (dolist (arg incomp) - (when-let ((obj (cl-find-if - (lambda (obj) - (and (slot-exists-p obj 'argument) - (slot-boundp obj 'argument) - (equal (oref obj argument) arg))) - transient--suffixes))) - (let ((transient--unset-incompatible nil)) - (transient-infix-set obj nil))))) - (cl-call-next-method obj value)))) + (when-let* ((value) + (val (transient-infix-value obj)) + (arg (if (slot-boundp obj 'argument) + (oref obj argument) + (oref obj argument-format))) + (spec (oref transient--prefix incompatible)) + (filter (lambda (x rule) + (and (member x rule) + (remove x rule)))) + (incomp (nconc + (cl-mapcan (apply-partially filter arg) spec) + (and (not (equal val arg)) + (cl-mapcan (apply-partially filter val) spec))))) + (dolist (obj transient--suffixes) + (when-let* (((cl-typep obj 'transient-argument)) + (val (transient-infix-value obj)) + (arg (if (slot-boundp obj 'argument) + (oref obj argument) + (oref obj argument-format))) + ((if (equal val arg) + (member arg incomp) + (or (member val incomp) + (member arg incomp))))) + (transient-infix-set obj nil))))) (cl-defgeneric transient-set-value (obj) "Set the value of the transient prefix OBJ.") @@ -3106,7 +3464,7 @@ the set, saved or default value for PREFIX." (transient--init-suffixes prefix))))) (defun transient-get-value () - (transient--with-emergency-exit + (transient--with-emergency-exit :get-value (cl-mapcan (lambda (obj) (and (or (not (slot-exists-p obj 'unsavable)) (not (oref obj unsavable))) @@ -3115,11 +3473,11 @@ the set, saved or default value for PREFIX." (defun transient--get-wrapped-value (obj) (and-let* ((value (transient-infix-value obj))) - (cl-ecase (and (slot-exists-p obj 'multi-value) - (oref obj multi-value)) - ((nil) (list value)) - ((t rest) (list value)) - (repeat value)))) + (pcase-exhaustive (and (slot-exists-p obj 'multi-value) + (oref obj multi-value)) + ('nil (list value)) + ((or 't 'rest) (list value)) + ('repeat value)))) (cl-defgeneric transient-infix-value (obj) "Return the value of the suffix object OBJ. @@ -3154,17 +3512,17 @@ does nothing." nil) "Return ARGUMENT and VALUE as a unit or nil if the latter is nil." (and-let* ((value (oref obj value))) (let ((arg (oref obj argument))) - (cl-ecase (oref obj multi-value) - ((nil) (concat arg value)) - ((t rest) (cons arg value)) - (repeat (mapcar (lambda (v) (concat arg v)) value)))))) + (pcase-exhaustive (oref obj multi-value) + ('nil (concat arg value)) + ((or 't 'rest) (cons arg value)) + ('repeat (mapcar (lambda (v) (concat arg v)) value)))))) (cl-defmethod transient-infix-value ((_ transient-variable)) "Return nil, which means \"no value\". Setting the value of a variable is done by, well, setting the -value of the variable. I.e. this is a side-effect and does not -contribute to the value of the transient." +value of the variable. I.e., this is a side-effect and does +not contribute to the value of the transient." nil) ;;;; Utilities @@ -3187,6 +3545,10 @@ the option does not appear in ARGS." (or (match-string 1 match) ""))) (and (member arg args) t))) +(defun transient-scope () + "Return the value of the `scope' slot of the current prefix." + (oref (transient-prefix-object) scope)) + ;;; History (cl-defgeneric transient--history-key (obj) @@ -3246,12 +3608,13 @@ have a history of their own.") (list (propertize (oref suffix key) 'face 'transient-key))))) transient--suffixes) #'string<) - (propertize "|" 'face 'transient-unreachable-key)))))) + (propertize "|" 'face 'transient-delimiter)))))) (defun transient--show () (transient--timer-cancel) (setq transient--showp t) - (let ((buf (get-buffer-create transient--buffer-name)) + (let ((transient--shadowed-buffer (current-buffer)) + (buf (get-buffer-create transient--buffer-name)) (focus nil)) (with-current-buffer buf (when transient-enable-popup-navigation @@ -3260,13 +3623,18 @@ have a history of their own.") (button-get (1- (point)) 'command)) (transient--heading-at-point)))) (erase-buffer) + (run-hooks 'transient-setup-buffer-hook) + (when transient-force-fixed-pitch + (transient--force-fixed-pitch)) (setq window-size-fixed t) (when (bound-and-true-p tab-line-format) (setq tab-line-format nil)) (setq header-line-format nil) - (setq mode-line-format (if (eq transient-mode-line-format 'line) - nil - transient-mode-line-format)) + (setq mode-line-format + (if (or (natnump transient-mode-line-format) + (eq transient-mode-line-format 'line)) + nil + transient-mode-line-format)) (setq mode-line-buffer-identification (symbol-name (oref transient--prefix command))) (if transient-enable-popup-navigation @@ -3277,18 +3645,8 @@ have a history of their own.") (transient--insert-groups) (when (or transient--helpp transient--editp) (transient--insert-help)) - (when (and (eq transient-mode-line-format 'line) - window-system) - (let ((face - (if-let ((f (and (transient--semantic-coloring-p) - (transient--prefix-color transient--prefix)))) - `(,@(and (>= emacs-major-version 27) '(:extend t)) - :background ,(face-foreground f)) - 'transient-separator))) - (insert (propertize "__" 'face face 'display '(space :height (1)))) - (insert (propertize "\n" 'face face 'line-height t)))) - (when transient-force-fixed-pitch - (transient--force-fixed-pitch))) + (when-let ((line (transient--separator-line))) + (insert line))) (unless (window-live-p transient--window) (setq transient--window (display-buffer buf transient-display-buffer-action))) @@ -3308,11 +3666,31 @@ have a history of their own.") (fit-window-to-buffer window nil (window-height window)) (fit-window-to-buffer window nil 1)))) +(defun transient--separator-line () + (and-let* ((height (cond ((not window-system) nil) + ((natnump transient-mode-line-format) + transient-mode-line-format) + ((eq transient-mode-line-format 'line) 1))) + (face `(,@(and (>= emacs-major-version 27) '(:extend t)) + :background + ,(or (face-foreground (transient--key-face nil 'non-suffix) + nil t) + "#gray60")))) + (concat (propertize "__" 'face face 'display `(space :height (,height))) + (propertize "\n" 'face face 'line-height t)))) + +(defmacro transient-with-shadowed-buffer (&rest body) + "While in the transient buffer, temporarly make the shadowed buffer current." + (declare (indent 0) (debug t)) + `(with-current-buffer (or transient--shadowed-buffer (current-buffer)) + ,@body)) + (defun transient--insert-groups () (let ((groups (cl-mapcan (lambda (group) (let ((hide (oref group hide))) (and (not (and (functionp hide) - (funcall hide))) + (transient-with-shadowed-buffer + (funcall hide)))) (list group)))) transient--layout)) group) @@ -3328,23 +3706,25 @@ have a history of their own.") (cl-defmethod transient--insert-group :around ((group transient-group)) "Insert GROUP's description, if any." - (when-let ((desc (transient-format-description group))) + (when-let ((desc (transient-with-shadowed-buffer + (transient-format-description group)))) (insert desc ?\n)) (let ((transient--max-group-level - (max (oref group level) transient--max-group-level))) + (max (oref group level) transient--max-group-level)) + (transient--pending-group group)) (cl-call-next-method group))) (cl-defmethod transient--insert-group ((group transient-row)) (transient--maybe-pad-keys group) (dolist (suffix (oref group suffixes)) - (insert (transient-format suffix)) + (insert (transient-with-shadowed-buffer (transient-format suffix))) (insert " ")) (insert ?\n)) (cl-defmethod transient--insert-group ((group transient-column)) (transient--maybe-pad-keys group) (dolist (suffix (oref group suffixes)) - (let ((str (transient-format suffix))) + (let ((str (transient-with-shadowed-buffer (transient-format suffix)))) (insert str) (unless (string-match-p ".\n\\'" str) (insert ?\n))))) @@ -3354,19 +3734,26 @@ have a history of their own.") (mapcar (lambda (column) (transient--maybe-pad-keys column group) - (let ((rows (mapcar #'transient-format (oref column suffixes)))) - (when-let ((desc (transient-format-description column))) - (push desc rows)) - (flatten-tree rows))) + (transient-with-shadowed-buffer + (let* ((transient--pending-group column) + (rows (mapcar #'transient-format (oref column suffixes)))) + (when-let ((desc (transient-format-description column))) + (push desc rows)) + (flatten-tree rows)))) (oref group suffixes))) (vp (or (oref transient--prefix variable-pitch) transient-align-variable-pitch)) (rs (apply #'max (mapcar #'length columns))) (cs (length columns)) - (cw (mapcar (lambda (col) - (apply #'max - (mapcar (if vp #'transient--pixel-width #'length) - col))) + (cw (mapcar (let ((widths (oref transient--prefix column-widths))) + (lambda (col) + (apply + #'max + (if-let ((min (pop widths))) + (if vp (* min (transient--pixel-width " ")) min) + 0) + (mapcar (if vp #'transient--pixel-width #'length) + col)))) columns)) (cc (transient--seq-reductions-from (apply-partially #'+ (* 3 (if vp (transient--pixel-width " ") 1))) @@ -3397,15 +3784,6 @@ have a history of their own.") (when (= c (1- cs)) (insert ?\n)))))))) -(defun transient--pixel-width (string) - (save-window-excursion - (with-temp-buffer - (insert string) - (set-window-dedicated-p nil nil) - (set-window-buffer nil (current-buffer)) - (car (window-text-pixel-size - nil (line-beginning-position) (point)))))) - (cl-defmethod transient--insert-group ((group transient-subgroups)) (let* ((subgroups (oref group suffixes)) (n (length subgroups))) @@ -3436,36 +3814,31 @@ making `transient--original-buffer' current.") "Return a string containing just the ARG character." (char-to-string arg)) -(cl-defmethod transient-format :around ((obj transient-infix)) - "When reading user input for this infix, then highlight it." +(cl-defmethod transient-format :around ((obj transient-suffix)) + "Add additional formatting if appropriate. +When reading user input for this infix, then highlight it. +When edit-mode is enabled, then prepend the level information. +When `transient-enable-popup-navigation' is non-nil then format +as a button." (let ((str (cl-call-next-method obj))) - (when (eq obj transient--active-infix) - (setq str (concat str "\n")) - (add-face-text-property - (if (eq this-command 'transient-set-level) 3 0) - (length str) - 'transient-active-infix nil str)) + (when (and (cl-typep obj 'transient-infix) + (eq (oref obj command) this-original-command) + (active-minibuffer-window)) + (setq str (transient--add-face str 'transient-active-infix))) + (when transient--editp + (setq str (concat (let ((level (oref obj level))) + (propertize (format " %s " level) + 'face (if (transient--use-level-p level t) + 'transient-enabled-suffix + 'transient-disabled-suffix))) + str))) + (when (and transient-enable-popup-navigation + (slot-boundp obj 'command)) + (setq str (make-text-button str nil + 'type 'transient + 'command (oref obj command)))) str)) -(cl-defmethod transient-format :around ((obj transient-suffix)) - "When edit-mode is enabled, then prepend the level information. -Optional support for popup buttons is also implemented here." - (let ((str (concat - (and transient--editp - (let ((level (oref obj level))) - (propertize (format " %s " level) - 'face (if (transient--use-level-p level t) - 'transient-enabled-suffix - 'transient-disabled-suffix)))) - (cl-call-next-method obj)))) - (when (oref obj inapt) - (add-face-text-property 0 (length str) 'transient-inapt-suffix nil str)) - (if transient-enable-popup-navigation - (make-text-button str nil - 'type 'transient - 'command (transient--suffix-command obj)) - str))) - (cl-defmethod transient-format ((obj transient-infix)) "Return a string generated using OBJ's `format'. %k is formatted using `transient-format-key'. @@ -3487,10 +3860,19 @@ Optional support for popup buttons is also implemented here." (cl-defgeneric transient-format-key (obj) "Format OBJ's `key' for display and return the result.") +(cl-defmethod transient-format-key :around ((obj transient-suffix)) + "Add `transient-inapt-suffix' face if suffix is inapt." + (let ((str (cl-call-next-method))) + (if (oref obj inapt) + (transient--add-face str 'transient-inapt-suffix) + str))) + (cl-defmethod transient-format-key ((obj transient-suffix)) "Format OBJ's `key' for display and return the result." - (let ((key (oref obj key)) - (cmd (oref obj command))) + (let ((key (if (slot-boundp obj 'key) (oref obj key) "")) + (cmd (and (slot-boundp obj 'command) (oref obj command)))) + (when-let ((width (oref transient--pending-group pad-keys))) + (setq key (truncate-string-to-width key width nil ?\s))) (if transient--redisplay-key (let ((len (length transient--redisplay-key)) (seq (cl-coerce (edmacro-parse-keys key t) 'list))) @@ -3507,7 +3889,7 @@ Optional support for popup buttons is also implemented here." (setq pre (string-replace "TAB" "C-i" pre)) (setq suf (string-replace "RET" "C-m" suf)) (setq suf (string-replace "TAB" "C-i" suf)) - ;; We use e.g. "-k" instead of the more correct "- k", + ;; We use e.g., "-k" instead of the more correct "- k", ;; because the former is prettier. If we did that in ;; the definition, then we want to drop the space that ;; is reinserted above. False-positives are possible @@ -3517,33 +3899,27 @@ Optional support for popup buttons is also implemented here." (setq suf (string-replace " " "" suf))) (concat (propertize pre 'face 'transient-unreachable-key) (and (string-prefix-p (concat pre " ") key) " ") - (transient--colorize-key suf cmd) + (propertize suf 'face (transient--key-face cmd)) (save-excursion (and (string-match " +\\'" key) (propertize (match-string 0 key) 'face 'fixed-pitch)))))) ((transient--lookup-key transient-sticky-map (kbd key)) - (transient--colorize-key key cmd)) + (propertize key 'face (transient--key-face cmd))) (t (propertize key 'face 'transient-unreachable-key)))) - (transient--colorize-key key cmd)))) - -(defun transient--colorize-key (key command) - (propertize key 'face - (or (and (transient--semantic-coloring-p) - (transient--suffix-color command)) - 'transient-key))) + (propertize key 'face (transient--key-face cmd))))) (cl-defmethod transient-format-key :around ((obj transient-argument)) + "Handle `transient-highlight-mismatched-keys'." (let ((key (cl-call-next-method obj))) - (cond ((not transient-highlight-mismatched-keys)) - ((not (slot-boundp obj 'shortarg)) - (add-face-text-property - 0 (length key) 'transient-nonstandard-key nil key)) - ((not (string-equal key (oref obj shortarg))) - (add-face-text-property - 0 (length key) 'transient-mismatched-key nil key))) - key)) + (cond + ((not transient-highlight-mismatched-keys) key) + ((not (slot-boundp obj 'shortarg)) + (transient--add-face key 'transient-nonstandard-key)) + ((not (string-equal key (oref obj shortarg))) + (transient--add-face key 'transient-mismatched-key)) + (key)))) (cl-defgeneric transient-format-description (obj) "Format OBJ's `description' for display and return the result.") @@ -3552,10 +3928,14 @@ Optional support for popup buttons is also implemented here." "The `description' slot may be a function, in which case that is called inside the correct buffer (see `transient--insert-group') and its value is returned to the caller." - (and-let* ((desc (oref obj description))) - (if (functionp desc) - (with-current-buffer transient--original-buffer - (funcall desc)) + (and-let* ((desc (oref obj description)) + (desc (if (functionp desc) + (if (= (car (transient--func-arity desc)) 1) + (funcall desc obj) + (funcall desc)) + desc))) + (if-let* ((face (transient--get-face obj 'face))) + (transient--add-face desc face t) desc))) (cl-defmethod transient-format-description ((obj transient-group)) @@ -3577,16 +3957,19 @@ If the OBJ's `key' is currently unreachable, then apply the face (funcall (oref transient--prefix suffix-description) obj)) (propertize "(BUG: no description)" 'face 'error)))) - (cond ((transient--key-unreachable-p obj) - (propertize desc 'face 'transient-unreachable)) - ((and transient-highlight-higher-levels - (> (max (oref obj level) transient--max-group-level) - transient--default-prefix-level)) - (add-face-text-property - 0 (length desc) 'transient-higher-level nil desc) - desc) - (t - desc)))) + (when (if transient--all-levels-p + (> (oref obj level) transient--default-prefix-level) + (and transient-highlight-higher-levels + (> (max (oref obj level) transient--max-group-level) + transient--default-prefix-level))) + (setq desc (transient--add-face desc 'transient-higher-level))) + (when-let ((inapt-face (and (oref obj inapt) + (transient--get-face obj 'inapt-face)))) + (setq desc (transient--add-face desc inapt-face))) + (when (and (slot-boundp obj 'key) + (transient--key-unreachable-p obj)) + (setq desc (transient--add-face desc 'transient-unreachable))) + desc)) (cl-defgeneric transient-format-value (obj) "Format OBJ's value for display and return the result.") @@ -3600,24 +3983,32 @@ If the OBJ's `key' is currently unreachable, then apply the face (cl-defmethod transient-format-value ((obj transient-option)) (let ((argument (oref obj argument))) (if-let ((value (oref obj value))) - (propertize - (cl-ecase (oref obj multi-value) - ((nil) (concat argument value)) - ((t rest) (concat argument - (and (not (string-suffix-p " " argument)) " ") - (mapconcat #'prin1-to-string value " "))) - (repeat (mapconcat (lambda (v) (concat argument v)) value " "))) - 'face 'transient-value) - (propertize argument 'face 'transient-inactive-value)))) + (pcase-exhaustive (oref obj multi-value) + ('nil + (concat (propertize argument 'face 'transient-argument) + (propertize value 'face 'transient-value))) + ((or 't 'rest) + (concat (propertize (if (string-suffix-p " " argument) + argument + (concat argument " ")) + 'face 'transient-argument) + (propertize (mapconcat #'prin1-to-string value " ") + 'face 'transient-value))) + ('repeat + (mapconcat (lambda (value) + (concat (propertize argument 'face 'transient-argument) + (propertize value 'face 'transient-value))) + value " "))) + (propertize argument 'face 'transient-inactive-argument)))) (cl-defmethod transient-format-value ((obj transient-switches)) (with-slots (value argument-format choices) obj (format (propertize argument-format 'face (if value - 'transient-value - 'transient-inactive-value)) - (concat - (propertize "[" 'face 'transient-inactive-value) + 'transient-argument + 'transient-inactive-argument)) + (format + (propertize "[%s]" 'face 'transient-delimiter) (mapconcat (lambda (choice) (propertize choice 'face @@ -3625,8 +4016,33 @@ If the OBJ's `key' is currently unreachable, then apply the face 'transient-value 'transient-inactive-value))) choices - (propertize "|" 'face 'transient-inactive-value)) - (propertize "]" 'face 'transient-inactive-value))))) + (propertize "|" 'face 'transient-delimiter)))))) + +(defun transient--add-face (string face &optional append beg end) + (let ((str (copy-sequence string))) + (add-face-text-property (or beg 0) (or end (length str)) face append str) + str)) + +(defun transient--get-face (obj slot) + (and-let* (((slot-exists-p obj slot)) + ((slot-boundp obj slot)) + (face (slot-value obj slot))) + (if (and (not (facep face)) + (functionp face)) + (let ((transient--pending-suffix obj)) + (if (= (car (transient--func-arity face)) 1) + (funcall face obj) + (funcall face))) + face))) + +(defun transient--key-face (&optional cmd enforce-type) + (or (and transient-semantic-coloring + (not transient--helpp) + (not transient--editp) + (or (and cmd (get cmd 'transient-face)) + (get (transient--get-pre-command cmd enforce-type) + 'transient-face))) + (if cmd 'transient-key 'transient-key-noop))) (defun transient--key-unreachable-p (obj) (and transient--redisplay-key @@ -3641,19 +4057,24 @@ If the OBJ's `key' is currently unreachable, then apply the face (and val (not (integerp val)) val))) (defun transient--maybe-pad-keys (group &optional parent) - (when-let ((pad (if (slot-boundp group 'pad-keys) - (oref group pad-keys) - (and parent - (slot-boundp parent 'pad-keys) - (oref parent pad-keys))))) - (let ((width (apply #'max - (cons (if (integerp pad) pad 0) - (mapcar (lambda (suffix) - (length (oref suffix key))) - (oref group suffixes)))))) - (dolist (suffix (oref group suffixes)) - (oset suffix key - (truncate-string-to-width (oref suffix key) width nil ?\s)))))) + (when-let ((pad (or (oref group pad-keys) + (and parent (oref parent pad-keys))))) + (oset group pad-keys + (apply #'max (cons (if (integerp pad) pad 0) + (seq-keep (lambda (suffix) + (and (eieio-object-p suffix) + (slot-boundp suffix 'key) + (length (oref suffix key)))) + (oref group suffixes))))))) + +(defun transient--pixel-width (string) + (save-window-excursion + (with-temp-buffer + (insert string) + (set-window-dedicated-p nil nil) + (set-window-buffer nil (current-buffer)) + (car (window-text-pixel-size + nil (line-beginning-position) (point)))))) (defun transient-command-summary-or-name (obj) "Return the summary or name of the command represented by OBJ. @@ -3663,7 +4084,7 @@ that, else its name. Intended to be temporarily used as the `:suffix-description' of a prefix command, while porting a regular keymap to a transient." - (let ((command (transient--suffix-symbol (oref obj command)))) + (let ((command (oref obj command))) (if-let ((doc (documentation command))) (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face) (propertize (symbol-name command) 'face 'font-lock-function-name-face)))) @@ -3681,7 +4102,7 @@ if non-nil, else show the `man-page' if non-nil, else use (cond (show-help (funcall show-help obj)) (info-manual (transient--show-manual info-manual)) (man-page (transient--show-manpage man-page)) - (t (transient--describe-function command))))) + ((transient--describe-function command))))) (cl-defmethod transient-show-help ((obj transient-suffix)) "Call `show-help' if non-nil, else use `describe-function'. @@ -3691,13 +4112,13 @@ prefix method." (cond ((eq this-command 'transient-help) (transient-show-help transient--prefix)) - ((let ((prefix (get (transient--suffix-command obj) + ((let ((prefix (get (oref obj command) 'transient--prefix))) (and prefix (not (eq (oref transient--prefix command) this-command)) (prog1 t (transient-show-help prefix))))) - (t (if-let ((show-help (oref obj show-help))) - (funcall show-help obj) - (transient--describe-function this-command))))) + ((if-let ((show-help (oref obj show-help))) + (funcall show-help obj) + (transient--describe-function this-command))))) (cl-defmethod transient-show-help ((obj transient-infix)) "Call `show-help' if non-nil, else show the `man-page' @@ -3717,7 +4138,7 @@ manpage, then try to jump to the correct location." (transient--describe-function cmd)) (defun transient--describe-function (fn) - (describe-function (if (symbolp fn) fn 'transient--anonymous-infix-argument)) + (describe-function fn) (unless (derived-mode-p 'help-mode) (when-let* ((buf (get-buffer "*Help*")) (win (or (and buf (get-buffer-window buf)) @@ -3727,21 +4148,6 @@ manpage, then try to jump to the correct location." (window-list))))) (select-window win)))) -(defun transient--anonymous-infix-argument () - "Cannot show any documentation for this anonymous infix command. - -The infix command in question was defined anonymously, i.e., -it was define when the prefix command that it belongs to was -defined, which means that it gets no docstring and also that -no symbol is bound to it. - -When you request help for an infix command, then we usually -show the respective man-page and jump to the location where -the respective argument is being described. - -Because the containing prefix command does not specify any -man-page, we cannot do that in this case. Sorry about that.") - (defun transient--show-manual (manual) (info manual)) @@ -3834,37 +4240,23 @@ Suffixes on levels %s and %s are unavailable.\n" (propertize (format ">=%s" (1+ level)) 'face 'transient-disabled-suffix)))))) -(defvar-keymap transient-resume-mode-map - :doc "Keymap for `transient-resume-mode'. - -This keymap remaps every command that would usually just quit the -documentation buffer to `transient-resume', which additionally -resumes the suspended transient." - " " #'transient-resume - " " #'transient-resume - " " #'transient-resume) - -(define-minor-mode transient-resume-mode - "Auxiliary minor-mode used to resume a transient after viewing help.") - -(defun transient-toggle-debug () - "Toggle debugging statements for transient commands." - (interactive) - (setq transient--debug (not transient--debug)) - (message "Debugging transient %s" - (if transient--debug "enabled" "disabled"))) - ;;; Popup Navigation -(defun transient-popup-navigation-help () - "Inform the user how to enable popup navigation commands." - (interactive) - (message "This command is only available if `%s' is non-nil" - 'transient-enable-popup-navigation)) +(defun transient-scroll-up (&optional arg) + "Scroll text of transient popup window upward ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-up-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-up-command arg))) -(define-button-type 'transient - 'face nil - 'keymap transient-button-map) +(defun transient-scroll-down (&optional arg) + "Scroll text of transient popup window down ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-down-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-down-command arg))) (defun transient-backward-button (n) "Move to the previous button in the transient popup buffer. @@ -3880,6 +4272,10 @@ See `forward-button' for information about N." (with-selected-window transient--window (forward-button n t))) +(define-button-type 'transient + 'face nil + 'keymap transient-button-map) + (defun transient--goto-button (command) (cond ((stringp command) @@ -3957,36 +4353,6 @@ search instead." (select-window transient--original-window) (transient--resume-override)) -;;;; Hydra Color Emulation - -(defun transient--semantic-coloring-p () - (and transient-semantic-coloring - (not transient--helpp) - (not transient--editp))) - -(defun transient--suffix-color (command) - (or (get command 'transient-color) - (get (transient--get-predicate-for command) 'transient-color))) - -(defun transient--prefix-color (command) - (let* ((nonsuf (or (oref command transient-non-suffix) - 'transient--do-warn)) - (nonsuf (if (memq nonsuf '(transient--do-noop transient--do-warn)) - 'disallow - (get nonsuf 'transient-color))) - (suffix (if-let ((pred (oref command transient-suffix))) - (get pred 'transient-color) - (if (eq nonsuf 'transient-red) - 'transient-red - 'transient-blue)))) - (pcase (list suffix nonsuf) - (`(transient-purple ,_) 'transient-purple) - ('(transient-red disallow) 'transient-amaranth) - ('(transient-blue disallow) 'transient-teal) - ('(transient-red transient-red) 'transient-pink) - ('(transient-red transient-blue) 'transient-red) - ('(transient-blue transient-blue) 'transient-blue)))) - ;;;; Edebug (defun transient--edebug-command-p () @@ -4048,14 +4414,15 @@ we stop there." (let ((key (oref obj key))) (cond ((string-equal key "q") "Q") ((string-equal key "Q") "M-q") - (t key)))) + (key)))) (defun transient--force-fixed-pitch () (require 'face-remap) (face-remap-reset-base 'default) (face-remap-add-relative 'default 'fixed-pitch)) -;;;; Missing from Emacs +(defun transient--func-arity (fn) + (func-arity (advice--cd*r (if (symbolp fn) (symbol-function fn) fn)))) (defun transient--seq-reductions-from (function sequence initial-value) (let ((acc (list initial-value))) @@ -4063,18 +4430,6 @@ we stop there." (push (funcall function (car acc) elt) acc)) (nreverse acc))) -(defun transient-plist-to-alist (plist) - (let (alist) - (while plist - (push (cons (let* ((symbol (pop plist)) - (name (symbol-name symbol))) - (if (eq (aref name 0) ?:) - (intern (substring name 1)) - symbol)) - (pop plist)) - alist)) - (nreverse alist))) - ;;; Font-Lock (defconst transient-font-lock-keywords @@ -4083,8 +4438,7 @@ we stop there." (regexp-opt (list "transient-define-prefix" "transient-define-infix" "transient-define-argument" - "transient-define-suffix" - "transient-define-groups") + "transient-define-suffix") t) "\\_>[ \t'(]*" "\\(\\(?:\\sw\\|\\s_\\)+\\)?") diff --git a/elpa/transient-20230602.2121/transient.info b/elpa/transient-20240421.1737/transient.info similarity index 82% rename from elpa/transient-20230602.2121/transient.info rename to elpa/transient-20240421.1737/transient.info index 6809231a..b293993a 100644 --- a/elpa/transient-20230602.2121/transient.info +++ b/elpa/transient-20240421.1737/transient.info @@ -1,7 +1,7 @@ This is transient.info, produced by makeinfo version 6.7 from transient.texi. - Copyright (C) 2018–2023 Free Software Foundation, Inc. + Copyright (C) 2018–2024 Free Software Foundation, Inc. You can redistribute this document and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -24,36 +24,17 @@ File: transient.info, Node: Top, Next: Introduction, Up: (dir) Transient User and Developer Manual *********************************** -Taking inspiration from prefix keys and prefix arguments, Transient -implements a similar abstraction involving a prefix command, infix -arguments and suffix commands. We could call this abstraction a -“transient command”, but because it always involves at least two -commands (a prefix and a suffix) we prefer to call it just a -“transient”. - - When the user calls a transient prefix command, a transient -(temporary) keymap is activated, which binds the transient’s infix and -suffix commands, and functions that control the transient state are -added to ‘pre-command-hook’ and ‘post-command-hook’. The available -suffix and infix commands and their state are shown in a popup buffer -until the transient is exited by invoking a suffix command. +Transient is the library used to implement the keyboard-driven “menus” +in Magit. It is distributed as a separate package, so that it can be +used to implement similar menus in other packages. - Calling an infix command causes its value to be changed, possibly by -reading a new value in the minibuffer. + This manual can be bit hard to digest when getting started. A useful +resource to get over that hurdle is Psionic K’s interactive tutorial, +available at . - Calling a suffix command usually causes the transient to be exited -but suffix commands can also be configured to not exit the transient. +This manual is for Transient version 0.6.0. - The second part of this manual, which describes how to modify - existing transients and create new transients from scratch, can be - hard to digest if you are just getting started. A useful resource - to get over that hurdle is Psionic K’s interactive tutorial, - available at - . - -This manual is for Transient version 0.4.1. - - Copyright (C) 2018–2023 Free Software Foundation, Inc. + Copyright (C) 2018–2024 Free Software Foundation, Inc. You can redistribute this document and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -72,7 +53,6 @@ This manual is for Transient version 0.4.1. * Modifying Existing Transients:: * Defining New Commands:: * Classes and Methods:: -* Related Abstractions and Packages:: * FAQ:: * Keystroke Index:: * Command and Function Index:: @@ -96,6 +76,7 @@ Usage Defining New Commands +* Technical Introduction:: * Defining Transients:: * Binding Suffix and Infix Commands:: * Defining Suffix and Infix Commands:: @@ -125,11 +106,6 @@ Suffix Methods * Suffix Format Methods:: -Related Abstractions and Packages - -* Comparison With Prefix Keys and Prefix Arguments:: -* Comparison With Other Packages:: -  File: transient.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top @@ -137,112 +113,72 @@ File: transient.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top 1 Introduction ************** -Taking inspiration from prefix keys and prefix arguments, Transient -implements a similar abstraction involving a prefix command, infix -arguments and suffix commands. We could call this abstraction a -“transient command”, but because it always involves at least two -commands (a prefix and a suffix) we prefer to call it just a -“transient”. +Transient is the library used to implement the keyboard-driven “menus” +in Magit. It is distributed as a separate package, so that it can be +used to implement similar menus in other packages. - Transient keymaps are a feature provided by Emacs. Transients as - implemented by this package involve the use of transient keymaps. + This manual can be bit hard to digest when getting started. A useful +resource to get over that hurdle is Psionic K’s interactive tutorial, +available at . - Emacs provides a feature that it calls “prefix commands”. When we - talk about “prefix commands” in this manual, then we mean our own - kind of “prefix commands”, unless specified otherwise. To avoid - ambiguity we sometimes use the terms “transient prefix command” for - our kind and “regular prefix command” for Emacs’ kind. +Some things that Transient can do +================================= - When the user calls a transient prefix command, a transient -(temporary) keymap is activated, which binds the transient’s infix and -suffix commands, and functions that control the transient state are -added to ‘pre-command-hook’ and ‘post-command-hook’. The available -suffix and infix commands and their state are shown in a popup buffer -until the transient state is exited by invoking a suffix command. - - Calling an infix command causes its value to be changed. How that is -done depends on the type of the infix command. The simplest case is an -infix command that represents a command-line argument that does not take -a value. Invoking such an infix command causes the switch to be toggled -on or off. More complex infix commands may read a value from the user, -using the minibuffer. + • Display current state of arguments + • Display and manage lifecycle of modal bindings + • Contextual user interface + • Flow control for wizard-like composition of interactive forms + • History & persistence + • Rendering arguments for controlling CLI programs - Calling a suffix command usually causes the transient to be exited; -the transient keymaps and hook functions are removed, the popup buffer -no longer shows information about the (no longer bound) suffix commands, -the values of some public global variables are set, while some internal -global variables are unset, and finally the command is actually called. -Suffix commands can also be configured to not exit the transient. - - A suffix command can, but does not have to, use the infix arguments -in much the same way any command can choose to use or ignore the prefix -arguments. For a suffix command that was invoked from a transient, the -variable ‘transient-current-suffixes’ and the function ‘transient-args’ -serve about the same purpose as the variables ‘prefix-arg’ and -‘current-prefix-arg’ do for any command that was called after the prefix -arguments have been set using a command such as ‘universal-argument’. - - The information shown in the popup buffer while a transient is active -looks a bit like this: - - ,----------------------------------------- - |Arguments - | -f Force (--force) - | -a Annotate (--annotate) - | - |Create - | t tag - | r release - `----------------------------------------- - - This is a simplified version of ‘magit-tag’. Info manuals do not - support images or colored text, so the above “screenshot” lacks - some information; in practice you would be able to tell whether the - arguments ‘--force’ and ‘--annotate’ are enabled or not based on - their color. - - Transient can be used to implement simple “command dispatchers”. The -main benefit then is that the user can see all the available commands in -a popup buffer. That is useful by itself because it frees the user from -having to remember all the keys that are valid after a certain prefix -key or command. Magit’s ‘magit-dispatch’ (on ‘C-x M-g’) command is an -example of using Transient to merely implement a command dispatcher. - - In addition to that, Transient also allows users to interactively -pass arguments to commands. These arguments can be much more complex -than what is reasonable when using prefix arguments. There is a limit -to how many aspects of a command can be controlled using prefix -arguments. Furthermore, what a certain prefix argument means for -different commands can be completely different, and users have to read -documentation to learn and then commit to memory what a certain prefix -argument means to a certain command. - - Transient suffix commands, on the other hand, can accept dozens of -different arguments without the user having to remember anything. When -using Transient, one can call a command with arguments that are just as -complex as when calling the same function non-interactively from Lisp. - - Invoking a transient command with arguments is similar to invoking a -command in a shell with command-line completion and history enabled. -One benefit of the Transient interface is that it remembers history not -only on a global level (“this command was invoked using these arguments, -and previously it was invoked using those other arguments”), but also -remembers the values of individual arguments independently. See *note -Using History::. - - After a transient prefix command is invoked, ‘C-h KEY’ can be used to -show the documentation for the infix or suffix command that KEY is bound -to (see *note Getting Help for Suffix Commands::), and infixes and -suffixes can be removed from the transient using ‘C-x l KEY’. Infixes -and suffixes that are disabled by default can be enabled the same way. -See *note Enabling and Disabling Suffixes::. +Complexity in CLI programs +========================== - Transient ships with support for a few different types of specialized -infix commands. A command that sets a command line option, for example, -has different needs than a command that merely toggles a boolean flag. -Additionally, Transient provides abstractions for defining new types, -which the author of Transient did not anticipate (or didn’t get around -to implementing yet). +Complexity tends to grow with time. How do you manage the complexity of +commands? Consider the humble shell command ‘ls’. It now has over +_fifty_ command line options. Some of these are boolean flags (‘ls +-l’). Some take arguments (‘ls --sort=s’). Some have no effect unless +paired with other flags (‘ls -lh’). Some are mutually exclusive. Some +shell commands even have so many options that they introduce +_subcommands_ (‘git branch’, ‘git commit’), each with their own rich set +of options (‘git branch -f’). + +Using Transient for composing interactive commands +================================================== + +What about Emacs commands used interactively? How do these handle +options? One solution is to make many versions of the same command, so +you don’t need to! Consider: ‘delete-other-windows’ vs. +‘delete-other-windows-vertically’ (among many similar examples). + + Some Emacs commands will simply prompt you for the next "argument" +(‘M-x switch-to-buffer’). Another common solution is to use prefix +arguments which usually start with ‘C-u’. Sometimes these are sensibly +numerical in nature (‘C-u 4 M-x forward-paragraph’ to move forward 4 +paragraphs). But sometimes they function instead as boolean "switches" +(‘C-u C-SPACE’ to jump to the last mark instead of just setting it, ‘C-u +C-u C-SPACE’ to unconditionally set the mark). Since there aren’t many +standards for the use of prefix options, you have to read the command’s +documentation to find out what the possibilities are. + + But when an Emacs command grows to have a truly large set of options +and arguments, with dependencies between them, lots of option values, +etc., these simple approaches just don’t scale. Transient is designed +to solve this issue. Think of it as the humble prefix argument ‘C-u’, +_raised to the power of 10_. Like ‘C-u’, it is key driven. Like the +shell, it supports boolean "flag" options, options that take arguments, +and even "sub-commands", with their own options. But instead of +searching through a man page or command documentation, well-designed +transients _guide_ their users to the relevant set of options (and even +their possible values!) directly, taking into account any important +pre-existing Emacs settings. And while for shell commands like ‘ls’, +there is only one way to "execute" (hit ‘Return’!), transients can +"execute" using multiple different keys tied to one of many +self-documenting _actions_ (imagine having 5 different colored return +keys on your keyboard!). Transients make navigating and setting large, +complex groups of command options and arguments easy. Fun even. Once +you’ve tried it, it’s hard to go back to the ‘C-u what can I do here +again?’ way.  File: transient.info, Node: Usage, Next: Modifying Existing Transients, Prev: Introduction, Up: Top @@ -319,7 +255,7 @@ use the latter, then you can later resume the stack of transients using Transient’s predecessor bound ‘q’ instead of ‘C-g’ to the quit command. To learn how to get that binding back see -‘transient-bind-q-to-quit’’s doc string. +‘transient-bind-q-to-quit’’s documentation string. ‘C-q’ (‘transient-quit-all’) This command quits the currently active incomplete key sequence, if @@ -488,8 +424,8 @@ provides access to the documentation directly from the active transient. defined. For infix commands that represent command-line arguments this ideally shows the appropriate manpage. ‘transient-help’ then tries to jump to the correct location within that. Info manuals are also -supported. The fallback is to show the command’s doc string, for -non-infix suffixes this is usually appropriate. +supported. The fallback is to show the command’s documentation string, +for non-infix suffixes this is usually appropriate.  File: transient.info, Node: Enabling and Disabling Suffixes, Next: Other Commands, Prev: Getting Help for Suffix Commands, Up: Usage @@ -558,6 +494,11 @@ if the user lowers the transient level. Therefore, to control which suffixes are available given a certain state, you have to make sure that that state is currently active. +‘C-x a’ (‘transient-toggle-level-limit’) + This command toggle whether suffixes that are on levels higher than + the level specified by ‘transient-default-level’ are temporarily + available anyway. +  File: transient.info, Node: Other Commands, Next: Configuration, Prev: Enabling and Disabling Suffixes, Up: Usage @@ -690,27 +631,31 @@ Auxiliary Options displayed right above the echo area, then this probably is not a good value. - If ‘line’ (the default), then the buffer also has no mode-line, but - a thin line is drawn instead, using the background color of the - face ‘transient-separator’. Text-mode frames cannot display thin - lines, and therefore fall back to treating ‘line’ like ‘nil’. + If ‘line’ (the default) or a natural number, then the buffer has no + mode-line, but a line is drawn is drawn in its place. If a number + is used, that specifies the thickness of the line. On termcap + frames we cannot draw lines, so there ‘line’ and numbers are + synonyms for ‘nil’. + + The color of the line is used to indicate if non-suffixes are + allowed and whether they exit the transient. The foreground color + of ‘transient-key-noop’ (if non-suffix are disallowed), + ‘transient-key-stay’ (if allowed and transient stays active), or + ‘transient-key-exit’ (if allowed and they exit the transient) is + used to draw the line. Otherwise this can be any mode-line format. See *note (elisp)Mode Line Format::, for details. -- User Option: transient-semantic-coloring - This option controls whether prefixes and suffixes are colored in a - Hydra-like fashion. + This option controls whether colors are used to indicate the + transient behavior of commands. If non-‘nil’, then the key binding of each suffix is colorized to indicate whether it exits the transient state or not. The color of the prefix is indicated using the line that is drawn when the value of ‘transient-mode-line-format’ is ‘line’. - For more information about how Hydra uses colors see - and - . - -- User Option: transient-highlight-mismatched-keys This option controls whether key bindings of infix commands that do not match the respective command-line argument should be @@ -907,6 +852,7 @@ File: transient.info, Node: Defining New Commands, Next: Classes and Methods, * Menu: +* Technical Introduction:: * Defining Transients:: * Binding Suffix and Infix Commands:: * Defining Suffix and Infix Commands:: @@ -914,9 +860,109 @@ File: transient.info, Node: Defining New Commands, Next: Classes and Methods, * Transient State::  -File: transient.info, Node: Defining Transients, Next: Binding Suffix and Infix Commands, Up: Defining New Commands +File: transient.info, Node: Technical Introduction, Next: Defining Transients, Up: Defining New Commands + +4.1 Technical Introduction +========================== + +Taking inspiration from prefix keys and prefix arguments, Transient +implements a similar abstraction involving a prefix command, infix +arguments and suffix commands. + + When the user calls a transient prefix command, a transient +(temporary) keymap is activated, which binds the transient’s infix and +suffix commands, and functions that control the transient state are +added to ‘pre-command-hook’ and ‘post-command-hook’. The available +suffix and infix commands and their state are shown in a popup buffer +until the transient state is exited by invoking a suffix command. + + Calling an infix command causes its value to be changed. How that is +done depends on the type of the infix command. The simplest case is an +infix command that represents a command-line argument that does not take +a value. Invoking such an infix command causes the switch to be toggled +on or off. More complex infix commands may read a value from the user, +using the minibuffer. + + Calling a suffix command usually causes the transient to be exited; +the transient keymaps and hook functions are removed, the popup buffer +no longer shows information about the (no longer bound) suffix commands, +the values of some public global variables are set, while some internal +global variables are unset, and finally the command is actually called. +Suffix commands can also be configured to not exit the transient. + + A suffix command can, but does not have to, use the infix arguments +in much the same way any command can choose to use or ignore the prefix +arguments. For a suffix command that was invoked from a transient, the +variable ‘transient-current-suffixes’ and the function ‘transient-args’ +serve about the same purpose as the variables ‘prefix-arg’ and +‘current-prefix-arg’ do for any command that was called after the prefix +arguments have been set using a command such as ‘universal-argument’. + + Transient can be used to implement simple “command dispatchers”. The +main benefit then is that the user can see all the available commands in +a popup buffer, which can be thought of as a “menus”. That is useful by +itself because it frees the user from having to remember all the keys +that are valid after a certain prefix key or command. Magit’s +‘magit-dispatch’ (on ‘C-x M-g’) command is an example of using Transient +to merely implement a command dispatcher. + + In addition to that, Transient also allows users to interactively +pass arguments to commands. These arguments can be much more complex +than what is reasonable when using prefix arguments. There is a limit +to how many aspects of a command can be controlled using prefix +arguments. Furthermore, what a certain prefix argument means for +different commands can be completely different, and users have to read +documentation to learn and then commit to memory what a certain prefix +argument means to a certain command. + + Transient suffix commands, on the other hand, can accept dozens of +different arguments without the user having to remember anything. When +using Transient, one can call a command with arguments that are just as +complex as when calling the same function non-interactively from Lisp. + + Invoking a transient suffix command with arguments is similar to +invoking a command in a shell with command-line completion and history +enabled. One benefit of the Transient interface is that it remembers +history not only on a global level (“this command was invoked using +these arguments, and previously it was invoked using those other +arguments”), but also remembers the values of individual arguments +independently. See *note Using History::. + + After a transient prefix command is invoked, ‘C-h KEY’ can be used to +show the documentation for the infix or suffix command that ‘KEY’ is +bound to (see *note Getting Help for Suffix Commands::), and infixes and +suffixes can be removed from the transient using ‘C-x l KEY’. Infixes +and suffixes that are disabled by default can be enabled the same way. +See *note Enabling and Disabling Suffixes::. + + Transient ships with support for a few different types of specialized +infix commands. A command that sets a command line option, for example, +has different needs than a command that merely toggles a boolean flag. +Additionally, Transient provides abstractions for defining new types, +which the author of Transient did not anticipate (or didn’t get around +to implementing yet). + + Note that suffix commands also support regular prefix arguments. A +suffix command may even be called with both infix and prefix arguments +at the same time. If you invoke a command as a suffix of a transient +prefix command, but also want to pass prefix arguments to it, then first +invoke the prefix command, and only after doing that invoke the prefix +arguments, before finally invoking the suffix command. If you instead +began by providing the prefix arguments, then those would apply to the +prefix command, not the suffix command. Likewise, if you want to change +infix arguments before invoking a suffix command with prefix arguments, +then change the infix arguments before invoking the prefix arguments. +In other words, regular prefix arguments always apply to the next +command, and since transient prefix, infix and suffix commands are just +regular commands, the same applies to them. (Regular prefix keys behave +differently because they are not commands at all, instead they are just +incomplete key sequences, and those cannot be interrupted with prefix +commands.) + + +File: transient.info, Node: Defining Transients, Next: Binding Suffix and Infix Commands, Prev: Technical Introduction, Up: Defining New Commands -4.1 Defining Transients +4.2 Defining Transients ======================= A transient consists of a prefix command and at least one suffix @@ -971,7 +1017,7 @@ that is used to invoke that transient.  File: transient.info, Node: Binding Suffix and Infix Commands, Next: Defining Suffix and Infix Commands, Prev: Defining Transients, Up: Defining New Commands -4.2 Binding Suffix and Infix Commands +4.3 Binding Suffix and Infix Commands ===================================== The macro ‘transient-define-prefix’ is used to define a transient. This @@ -980,7 +1026,7 @@ Transients::) and adds the transient’s infix and suffix bindings, as described below. Users and third-party packages can add additional bindings using -functions such as ‘transient-insert-suffix’ (See *note Modifying +functions such as ‘transient-insert-suffix’ (see *note Modifying Existing Transients::). These functions take a “suffix specification” as one of their arguments, which has the same form as the specifications used in ‘transient-define-prefix’. @@ -993,7 +1039,7 @@ used in ‘transient-define-prefix’.  File: transient.info, Node: Group Specifications, Next: Suffix Specifications, Up: Binding Suffix and Infix Commands -4.2.1 Group Specifications +4.3.1 Group Specifications -------------------------- The suffix and infix commands of a transient are organized in groups. @@ -1065,10 +1111,20 @@ constructor of that class. prefix’s ‘transient--layout’ property, but it is often more convenient to use the same form as understood by ‘transient-define-prefix’, described below. If you use the latter - approach, you can use the ‘transient-parse-child’ and - ‘transient-parse-children’ functions to transform them from the + approach, you can use the ‘transient-parse-suffixes’ and + ‘transient-parse-suffix’ functions to transform them from the convenient to the expected form. + If you explicitly specify children and then transform them using + ‘:setup-chilren’, then the class of the group is determined as + usual, based on explicitly specified children. + + If you do not explicitly specify children and thus rely solely on + ‘:setup-children’, then you must specify the class using ‘:class’. + For backward compatibility, if you fail to do so, + ‘transient-column’ is used and a warning is displayed. This + warning will eventually be replaced with an error. + • The boolean ‘:pad-keys’ argument controls whether keys of all suffixes contained in a group are right padded, effectively aligning the descriptions. @@ -1079,10 +1135,10 @@ subgroups with commands at the same level, though in principle there is nothing that prevents that.) If the ELEMENTs are not subgroups, then they can be a mixture of -lists that specify commands and strings. Strings are inserted verbatim -into the buffer. The empty string can be used to insert gaps between -suffixes, which is particularly useful if the suffixes are outlined as a -table. +lists, which specify commands, and strings. Strings are inserted +verbatim into the buffer. The empty string can be used to insert gaps +between suffixes, which is particularly useful if the suffixes are +outlined as a table. Inside group specifications, including inside contained suffix specifications, nothing has to be quoted and quoting anyway is invalid. @@ -1094,7 +1150,7 @@ This feature is experimental and should be avoided.  File: transient.info, Node: Suffix Specifications, Prev: Group Specifications, Up: Binding Suffix and Infix Commands -4.2.2 Suffix Specifications +4.3.2 Suffix Specifications --------------------------- A transient’s suffix and infix commands are bound when the transient @@ -1128,10 +1184,11 @@ transient. • KEY is the key binding, either a vector or key description string. • DESCRIPTION is the description, either a string or a function that - returns a string. The function should be a lambda expression to - avoid ambiguity. In some cases a symbol that is bound as a - function would also work but to be safe you should use - ‘:description’ in that case. + takes zero or one arguments (the suffix object) and returns a + string. The function should be a lambda expression to avoid + ambiguity. In some cases a symbol that is bound as a function + would also work but to be safe you should use ‘:description’ in + that case. The next element is either a command or an argument. This is the only argument that is mandatory in all cases. @@ -1187,7 +1244,7 @@ Slots::.  File: transient.info, Node: Defining Suffix and Infix Commands, Next: Using Infix Arguments, Prev: Binding Suffix and Infix Commands, Up: Defining New Commands -4.3 Defining Suffix and Infix Commands +4.4 Defining Suffix and Infix Commands ====================================== Note that an infix is a special kind of suffix. Depending on context @@ -1218,16 +1275,12 @@ Note that an infix is a special kind of suffix. Depending on context reserved for future use. DOCSTRING is the documentation string and is optional. - The keyword-value pairs are mandatory. All transient infix - commands are ‘equal’ to each other (but not ‘eq’), so it is - meaningless to define an infix command without also setting at - least ‘:class’ and one other keyword (which it is depends on the - used class, usually ‘:argument’ or ‘:variable’). - - Each keyword has to be a keyword symbol, either ‘:class’ or a - keyword argument supported by the constructor of that class. The - ‘transient-switch’ class is used if the class is not specified - explicitly. + At least one key-value pair is required. All transient infix + commands are ‘equal’ to each other (but not ‘eq’). It is + meaningless to define an infix command, without providing at least + one keyword argument (usually ‘:argument’ or ‘:variable’, depending + on the class). The suffix class defaults to ‘transient-switch’ and + can be set using the ‘:class’ keyword. The function definition is always: @@ -1257,7 +1310,7 @@ Note that an infix is a special kind of suffix. Depending on context  File: transient.info, Node: Using Infix Arguments, Next: Transient State, Prev: Defining Suffix and Infix Commands, Up: Defining New Commands -4.4 Using Infix Arguments +4.5 Using Infix Arguments ========================= The functions and the variables described below allow suffix commands to @@ -1318,7 +1371,7 @@ function, which for infix arguments serves about the same purpose as  File: transient.info, Node: Transient State, Prev: Using Infix Arguments, Up: Defining New Commands -4.5 Transient State +4.6 Transient State =================== Invoking a transient prefix command “activates” the respective @@ -1338,53 +1391,51 @@ transient’s infix and suffix commands. transient keymap is disallowed and trying to do so results in a warning. This does not “deactivate” the transient. - But these are just the defaults. Whether a certain command -deactivates or “exits” the transient is configurable. There is more -than one way in which a command can be “transient” or “non-transient”; -the exact behavior is implemented by calling a so-called “pre-command” -function. Whether non-suffix commands are allowed to be called is -configurable per transient. - - • The transient-ness of suffix commands (including infix commands) is - controlled by the value of their ‘transient’ slot, which can be set - either when defining the command or when adding a binding to a - transient while defining the respective transient prefix command. - - Valid values are booleans and the pre-commands described below. - - • ‘t’ is equivalent to ‘transient--do-stay’. - • ‘nil’ is equivalent to ‘transient--do-exit’. - • If ‘transient’ is unbound (and that is actually the default - for non-infix suffixes) then the value of the prefix’s - ‘transient-suffix’ slot is used instead. The default value of - that slot is ‘nil’, so the suffix’s ‘transient’ slot being - unbound is essentially equivalent to it being ‘nil’. - - • A suffix command can be a prefix command itself, i.e., a - “sub-prefix”. While a sub-prefix is active we nearly always want - ‘C-g’ to take the user back to the “super-prefix”. However in rare - cases this may not be desirable, and that makes the following - complication necessary: - - For ‘transient-suffix’ objects the ‘transient’ slot is unbound. We - can ignore that for the most part because, as stated above, ‘nil’ - and the slot being unbound are equivalent, and mean “do exit”. - That isn’t actually true for suffixes that are sub-prefixes though. - For such suffixes unbound means “do exit but allow going back”, - which is the default, while ‘nil’ means “do exit permanently”, - which requires that slot to be explicitly set to that value. + The behavior can be changed for all suffixes of a particular prefix +and/or for individual suffixes. The values should nearly always be +booleans, but certain functions, called “pre-commands”, can also be +used. These functions are named ‘transient--do-VERB’, and the symbol +‘VERB’ can be used as a shorthand. + + A boolean is interpreted as answering the question "does the +transient stay active, when this command is invoked?" ‘t’ means that +the transient stays active, while ‘nil’ means that invoking the command +exits the transient. + + Note that when the suffix is a “sub-prefix”, invoking that command +always activates that sub-prefix, causing the outer prefix to no longer +be active and displayed. Here ‘t’ means that when you exit the inner +prefix, then the outer prefix becomes active again, while ‘nil’ means +that all outer prefixes are exited at once. + + • The behavior for non-suffixes can be set for a particular prefix, + by the prefix’s ‘transient-non-suffix’ slot to a boolean, a + suitable pre-command function, or a shorthand for such a function. + See *note Pre-commands for Non-Suffixes::. + + • The common behavior for the suffixes of a particular prefix can be + set using the prefix’s ‘transient-suffixes’ slot. + + The value specified in this slot does *not* affect infixes. + Because it affects both regular suffixes as well as sub-prefixes, + which have different needs, it is best to avoid explicitly + specifying a function. + + • The behavior of an individual suffix can be changed using its + ‘transient’ slot. While it is usually best to use a boolean, for + this slot it can occasionally make sense to specify a function + explicitly. - • The transient-ness of certain built-in suffix commands is specified - using ‘transient-predicate-map’. This is a special keymap, which - binds commands to pre-commands (as opposed to keys to commands) and - takes precedence over the ‘transient’ slot. + Note that this slot can be set when defining a suffix command using + ‘transient-define-suffix’ and/or in the definition of the prefix. + If set in both places, then the latter takes precedence, as usual. - The available pre-command functions are documented below. They are -called by ‘transient--pre-command’, a function on ‘pre-command-hook’ and -the value that they return determines whether the transient is exited. -To do so the value of one of the constants ‘transient--exit’ or -‘transient--stay’ is used (that way we don’t have to remember if ‘t’ -means “exit” or “stay”). + The available pre-command functions are documented in the following +sub-sections. They are called by ‘transient--pre-command’, a function +on ‘pre-command-hook’, and the value that they return determines whether +the transient is exited. To do so the value of one of the constants +‘transient--exit’ or ‘transient--stay’ is used (that way we don’t have +to remember if ‘t’ means “exit” or “stay”). Additionally, these functions may change the value of ‘this-command’ (which explains why they have to be called using ‘pre-command-hook’), @@ -1392,11 +1443,35 @@ call ‘transient-export’, ‘transient--stack-zap’ or ‘transient--stack-push’; and set the values of ‘transient--exitp’, ‘transient--helpp’ or ‘transient--editp’. + For completeness sake, some notes about complications: + + • The transient-ness of certain built-in suffix commands is specified + using ‘transient-predicate-map’. This is a special keymap, which + binds commands to pre-commands (as opposed to keys to commands) and + takes precedence over the prefix’s ‘transient-suffix’ slot, but not + the suffix’s ‘transient’ slot. + + • While a sub-prefix is active we nearly always want ‘C-g’ to take + the user back to the “super-prefix”, even when the other suffixes + don’t do that. However, in rare cases this may not be desirable, + and that makes the following complication necessary: + + For ‘transient-suffix’ objects the ‘transient’ slot is unbound. We + can ignore that for the most part because ‘nil’ and the slot being + unbound are treated as equivalent, and mean “do exit”. That isn’t + actually true for suffixes that are sub-prefixes though. For such + suffixes unbound means “do exit but allow going back”, which is the + default, while ‘nil’ means “do exit permanently”, which requires + that slot to be explicitly set to that value. + Pre-commands for Infixes ------------------------ The default for infixes is ‘transient--do-stay’. This is also the only -function that makes sense for infixes. +function that makes sense for infixes, which is why this predicate is +used even if the value of the prefix’s ‘transient-suffix’ slot is ‘t’. +In extremely rare cases, one might want to use something else, which can +be done by setting the infix’s ‘transient’ slot directly. -- Function: transient--do-stay Call the command without exporting variables and stay transient. @@ -1406,31 +1481,25 @@ Pre-commands for Suffixes By default, invoking a suffix causes the transient to be exited. - If you want a different default behavior for a certain transient -prefix command, then set its ‘:transient-suffix’ slot. The value can be -a boolean, answering the question "does the transient stay active, when -a suffix command is invoked?" ‘t’ means that the transient stays -active, while ‘nil’ means that invoking a suffix exits the transient. -In either case, the exact behavior depends on whether the suffix is -itself a prefix (i.e., a sub-prefix), an infix or a regular suffix. - The behavior for an individual suffix command can be changed by -setting its ‘transient’ slot to one of the following pre-commands. +setting its ‘transient’ slot to a boolean (which is highly recommended), +or to one of the following pre-commands. -- Function: transient--do-exit Call the command after exporting variables and exit the transient. -- Function: transient--do-return - Call the command after exporting variables and return to parent + Call the command after exporting variables and return to the parent prefix. If there is no parent prefix, then call ‘transient--do-exit’. -- Function: transient--do-call Call the command after exporting variables and stay transient. - The following pre-commands are suitable for sub-prefixes. Only the -first should ever explicitly be set as the value of the ‘transient’ -slot. + The following pre-commands are only suitable for sub-prefixes. It is +not necessary to explicitly use these predicates because the correct +predicate is automatically picked based on the value of the ‘transient’ +slot for the sub-prefix itself. -- Function: transient--do-recurse Call the transient prefix command, preparing for return to active @@ -1438,14 +1507,23 @@ slot. Whether we actually return to the parent transient is ultimately under the control of each invoked suffix. The difference between - this pre-command and ‘transient--do-replace’ is that it changes the - value of the ‘transient-suffix’ slot to ‘transient--do-return’. + this pre-command and ‘transient--do-stack’ is that it changes the + value of the ‘transient-suffix’ slot to ‘t’. If there is no parent transient, then only call this command and skip the second step. + -- Function: transient--do-stack + Call the transient prefix command, stacking the active transient. + Push the active transient to the transient stack. + + Unless ‘transient--do-recurse’ is explicitly used, this pre-command + is automatically used for suffixes that are prefixes themselves, + i.e., for sub-prefixes. + -- Function: transient--do-replace Call the transient prefix command, replacing the active transient. + Do not push the active transient to the transient stack. Unless ‘transient--do-recurse’ is explicitly used, this pre-command is automatically used for suffixes that are prefixes themselves, @@ -1465,18 +1543,17 @@ By default, non-suffixes (commands that are bound in other keymaps beside the transient keymap) cannot be invoked. Trying to invoke such a command results in a warning and the transient stays active. - If you want a different behavior, then set the -‘:transient-non-suffix’ slot of the transient prefix command. The value -can be a boolean, answering the question, "is it allowed to invoke -non-suffix commands?" + If you want a different behavior, then set the ‘transient-non-suffix’ +slot of the transient prefix command. The value should be a boolean, +answering the question, "is it allowed to invoke non-suffix commands?, a +pre-command function, or a shorthand for such a function. - If the value is ‘t’ or ‘transient--do-stay’, then non-suffixes can be -invoked, when it is ‘nil’ or ‘transient--do-warn’ (the default) then -they cannot be invoked. + If the value is ‘t’, then non-suffixes can be invoked, when it is +‘nil’ (the default) then they cannot be invoked. - The only other recommended value is ‘transient--do-leave’. If that -is used, then non-suffixes can be invoked, but if one is invoked, then -that exits the transient. + The only other recommended value is ‘leave’. If that is used, then +non-suffixes can be invoked, but if one is invoked, then that exits the +transient. -- Function: transient--do-warn Call ‘transient-undefined’ and stay transient. @@ -1507,7 +1584,7 @@ Special Pre-Commands This is used when the user pressed ‘C-z’.  -File: transient.info, Node: Classes and Methods, Next: Related Abstractions and Packages, Prev: Defining New Commands, Up: Top +File: transient.info, Node: Classes and Methods, Next: FAQ, Prev: Defining New Commands, Up: Top 5 Classes and Methods ********************* @@ -1706,6 +1783,21 @@ File: transient.info, Node: Suffix Classes, Next: Suffix Methods, Prev: Prefi • Classes used for infix commands that represent variables should derived from the abstract ‘transient-variable’ class. + • The ‘transient-information’ class is special in that suffixes that + use this class are not associated with a command and thus also not + with any key binding. Such suffixes are only used to display + arbitrary information, and that anywhere a suffix can appear. + Display-only suffix specifications take this form: + + ([LEVEL] :info DESCRIPTION [KEYWORD VALUE]...) + + The ‘:info’ keyword argument replaces the ‘:description’ keyword + used for other suffix classes. Other keyword arguments that you + might want to set, include ‘:face’, predicate keywords (such as + ‘:if’), and ‘:format’. By default the value of ‘:format’ includes + ‘%k’, which for this class is replaced with the empty string or + spaces, if keys are being padded in the containing group. + Magit defines additional classes, which can serve as examples for the fancy things you can do without modifying Transient. Some of these classes will likely get generalized and added to Transient. For now @@ -1823,12 +1915,12 @@ File: transient.info, Node: Suffix Format Methods, Prev: Suffix Value Methods, For prefixes, show the info manual, if that is specified using the ‘info-manual’ slot. Otherwise, show the manpage if that is specified using the ‘man-page’ slot. Otherwise, show the command’s - doc string. + documentation string. - For suffixes, show the command’s doc string. + For suffixes, show the command’s documentation string. For infixes, show the manpage if that is specified. Otherwise show - the command’s doc string. + the command’s documentation string.  File: transient.info, Node: Prefix Slots, Next: Suffix Slots, Prev: Suffix Methods, Up: Classes and Methods @@ -1850,10 +1942,23 @@ File: transient.info, Node: Prefix Slots, Next: Suffix Slots, Prev: Suffix Me remains active/transient when a suffix or arbitrary non-suffix command is invoked. See *note Transient State::. + • ‘refresh-suffixes’ Normally suffix objects and keymaps are only + setup once, when the prefix is invoked. Setting this to ‘t’, + causes them to be recreated after every command. This is useful + when using ‘:if...’ predicates, and those need to be rerun for some + reason. Doing this is somewhat costly, and there is a risk of + losing state, so this is disabled by default and still considered + experimental. + • ‘incompatible’ A list of lists. Each sub-list specifies a set of mutually exclusive arguments. Enabling one of these arguments causes the others to be disabled. An argument may appear in - multiple sub-lists. + multiple sub-lists. Arguments must me given in the same form as + used in the ‘argument’ or ‘argument-format’ slot of the respective + suffix objects, usually something like ‘--switch’ or ‘--option=%s’. + For options and ‘transient-switches’ suffixes it is also possible + to match against a specific value, as returned by + ‘transient-infix-value’, for example, ‘--option=one’. • ‘scope’ For some transients it might be necessary to have a sort of secondary value, called a “scope”. See ‘transient-define-prefix’. @@ -1917,8 +2022,14 @@ Slots of ‘transient-suffix’ • ‘%v’ For the infix value. Non-infix suffixes don’t have a value. - • ‘description’ The description, either a string or a function that - is called with no argument and returns a string. + • ‘description’ The description, either a string or a function, which + is called with zero or one argument (the suffix object), and + returns a string. + + • ‘face’ Face used for the description. In simple cases it is easier + to use this instead of using a function as ‘description’ and adding + the styling there. ‘face’ is appended using + ‘add-face-text-property’. • ‘show-help’ A function used to display help for the suffix. If unspecified, the prefix controls how help is displayed for its @@ -1994,8 +2105,10 @@ They are defined here anyway to allow sharing certain methods. a function that takes the object as the only argument and which returns a prompt string. - • ‘choices’ A list of valid values. How exactly that is used depends - on the class of the object. + • ‘choices’ A list of valid values, or a function that returns such a + list. The latter is not implemented for ‘transient-switches’, + because I couldn’t think of a use-case. How exactly the choices + are used varies depending on the class of the suffix. Slots of ‘transient-variable’ ----------------------------- @@ -2031,6 +2144,10 @@ happens if you use more than one. • ‘if-derived’ Enable if major-mode derives from value. • ‘if-not-derived’ Enable if major-mode does not derive from value. + By default these predicates run when the prefix command is invoked, +but this can be changes, using the ‘refresh-suffixes’ prefix slot. See +*note Prefix Slots::. + One more slot is shared between group and suffix classes, ‘level’. Like the slots documented above, it is a predicate, but it is used for a different purpose. The value has to be an integer between 1 and 7. @@ -2039,311 +2156,7 @@ depending on user preference. See *note Enabling and Disabling Suffixes::.  -File: transient.info, Node: Related Abstractions and Packages, Next: FAQ, Prev: Classes and Methods, Up: Top - -6 Related Abstractions and Packages -*********************************** - -* Menu: - -* Comparison With Prefix Keys and Prefix Arguments:: -* Comparison With Other Packages:: - - -File: transient.info, Node: Comparison With Prefix Keys and Prefix Arguments, Next: Comparison With Other Packages, Up: Related Abstractions and Packages - -6.1 Comparison With Prefix Keys and Prefix Arguments -==================================================== - -While transient commands were inspired by regular prefix keys and prefix -arguments, they are also quite different and much more complex. - - The following diagrams illustrate some of the differences. - - • ‘(c)’ represents a return to the command loop. - • ‘(+)’ represents the user’s choice to press one key or another. - • ‘{WORD}’ are possible behaviors. - • ‘{NUMBER}’ is a footnote. - -Regular Prefix Commands ------------------------ - -See *note (elisp)Prefix Keys::. - - ,--> command1 --> (c) - | - (c)-(+)-> prefix command or key --+--> command2 --> (c) - | - `--> command3 --> (c) - -Regular Prefix Arguments ------------------------- - -See *note (elisp)Prefix Command Arguments::. - - ,----------------------------------, - | | - v | - (c)-(+)---> prefix argument command --(c)-(+)-> any command --> (c) - | ^ | - | | | - `-- sets or changes --, ,-- maybe used --' | - | | | - v | | - prefix argument state | - ^ | - | | - `-------- discards --------' - -Transients ----------- - -(∩`-´)⊃━☆゚.*・。゚ - - This diagram ignores the infix value and external state: - - (c) - | ,- {stay} ------<-,-<------------<-,-<---, - (+) | | | | - | | | | | - | | ,--> infix1 --| | | - | | | | | | - | | |--> infix2 --| | | - v v | | | | - prefix -(c)-(+)-> infix3 --' ^ | - | | | - |---------------> suffix1 -->--| | - | | | - |---------------> suffix2 ----{1}------> {exit} --> (c) - | | - |---------------> suffix3 -------------> {exit} --> (c) - | | - `--> any command --{2}-> {warn} -->--| - | | - |--> {noop} -->--| - | | - |--> {call} -->--' - | - `------------------> {exit} --> (c) - - This diagram takes the infix value into account to an extend, while -still ignoring external state: - - (c) - | ,- {stay} ------<-,-<------------<-,-<---, - (+) | | | | - | | | | | - | | ,--> infix1 --| | | - | | | | | | | - | | ,--> infix2 --| | | - v v | | | | | - prefix -(c)-(+)-> infix3 --' | | - | | ^ | - | | | | - |---------------> suffix1 -->--| | - | | ^ | | - | | | | | - |---------------> suffix2 ----{1}------> {exit} --> (c) - | | ^ | | - | | | | v - | | | | | - |---------------> suffix3 -------------> {exit} --> (c) - | | ^ | | - | sets | | v - | | maybe | | - | | used | | - | | | | | - | | infix --' | | - | `---> value | | - | ^ | | - | | | | - | hides | | - | | | | - | `--------------------------<---| - | | | - `--> any command --{2}-> {warn} -->--| | - | | | - |--> {noop} -->--| | - | | | - |--> {call} -->--' ^ - | | - `------------------> {exit} --> (c) - - This diagram provides more information about the infix value and also -takes external state into account. - - ,----sets--- "anything" - | - v - ,---------> external - | state - | | | - | initialized | ☉‿⚆ - sets from | - | | maybe - | ,----------' used - | | | - (c) | | v - | ,- {stay} --|---<-,-<------|-----<-,-<---, - (+) | | | | | | | - | | | v | | | | - | | ,--> infix1 --| | | | - | | | | | | | | | - | | | | v | | | | - | | ,--> infix2 --| | | | - | | | | ^ | | | | - v v | | | | | | | - prefix -(c)-(+)-> infix3 --' | | | - | | ^ | ^ | - | | | v | | - |---------------> suffix1 -->--| | - | | | ^ | | | - | | | | v | | - |---------------> suffix2 ----{1}------> {exit} --> (c) - | | | ^ | | | - | | | | | | v - | | | | v | | - |---------------> suffix3 -------------> {exit} --> (c) - | | | ^ | | - | sets | | | v - | | initialized maybe | | - | | from used | | - | | | | | | - | | `-- infix ---' | | - | `---> value -----------------------------> persistent - | ^ ^ | | across - | | | | | invocations -, - | hides | | | | - | | `----------------------------------------------' - | | | | - | `--------------------------<---| - | | | - `--> any command --{2}-> {warn} -->--| | - | | | - |--> {noop} -->--| | - | | | - |--> {call} -->--' ^ - | | - `------------------> {exit} --> (c) - - • ‘{1}’ Transients can be configured to be exited when a suffix - command is invoked. The default is to do so for all suffixes - except for those that are common to all transients and which are - used to perform tasks such as providing help and saving the value - of the infix arguments for future invocations. The behavior can - also be specified for individual suffix commands and may even - depend on state. - - • ‘{2}’ Transients can be configured to allow the user to invoke - non-suffix commands. The default is to not allow that and instead - warn the user. - - Despite already being rather complex, even the last diagram leaves -out many details. Most importantly it implies that the decision whether -to remain transient is made later than it actually is made (for the most -part a function on ‘pre-command-hook’ is responsible). But such -implementation details are of little relevance to users and are covered -elsewhere. - - -File: transient.info, Node: Comparison With Other Packages, Prev: Comparison With Prefix Keys and Prefix Arguments, Up: Related Abstractions and Packages - -6.2 Comparison With Other Packages -================================== - -Magit-Popup ------------ - -Transient is the successor to Magit-Popup (see *note -(magit-popup)Top::). - - One major difference between these two implementations of the same -ideas is that while Transient uses transient keymaps and embraces the -command-loop, Magit-Popup implemented an inferior mechanism that does -not use transient keymaps and that instead of using the command-loop -implements a naive alternative based on ‘read-char’. - - Magit-Popup does not use classes and generic functions and defining a -new command type is near impossible as it involves adding hard-coded -special-cases to many functions. Because of that only a single new type -was added, which was not already part of Magit-Popup’s initial release. - - A lot of things are hard-coded in Magit-Popup. One random example is -that the key bindings for switches must begin with ‘-’ and those for -options must begin with ‘=’. - -Hydra ------ - -Hydra (see ) is another package that -provides features similar to those of Transient. - - Both packages use transient keymaps to make a set of commands -temporarily available and show the available commands in a popup buffer. - - A Hydra “body” is equivalent to a Transient “prefix” and a Hydra -“head” is equivalent to a Transient “suffix”. Hydra has no equivalent -of a Transient “infix”. - - Both hydras and transients can be used as simple command dispatchers. -Used like this they are similar to regular prefix commands and prefix -keys, except that the available commands are shown in the popup buffer. - - (Another package that does this is ‘which-key’. It does so -automatically for any incomplete key sequence. The advantage of that -approach is that no additional work is necessary; the disadvantage is -that the available commands are not organized semantically.) - - Both Hydra and Transient provide features that go beyond simple -command dispatchers: - - • Invoking a command from a hydra does not necessarily exit the - hydra. That makes it possible to invoke the same command again, - but using a shorter key sequence (i.e., the key that was used to - enter the hydra does not have to be pressed again). - - Transient supports that too, but for now this feature is not a - focus and the interface is a bit more complicated. A very basic - example using the current interface: - - (transient-define-prefix outline-navigate () - :transient-suffix 'transient--do-stay - :transient-non-suffix 'transient--do-warn - [("p" "previous visible heading" outline-previous-visible-heading) - ("n" "next visible heading" outline-next-visible-heading)]) - - • Transient supports infix arguments; values that are set by infix - commands and then consumed by the invoked suffix command(s). - - To my knowledge, Hydra does not support that. - - Both packages make it possible to specify how exactly the available -commands are outlined: - - • With Hydra this is often done using an explicit format string, - which gives authors a lot of flexibility and makes it possible to - do fancy things. - - The downside of this is that it becomes harder for a user to add - additional commands to an existing hydra and to change key - bindings. - - • Transient allows the author of a transient to organize the commands - into groups and the use of generic functions allows authors of - transients to control exactly how a certain command type is - displayed. - - However while Transient supports giving sections a heading it does - not currently support giving the displayed information more - structure by, for example, using box-drawing characters. - - That could be implemented by defining a new group class, which lets - the author specify a format string. It should be possible to - implement that without modifying any existing code, but it does not - currently exist. - - -File: transient.info, Node: FAQ, Next: Keystroke Index, Prev: Related Abstractions and Packages, Up: Top +File: transient.info, Node: FAQ, Next: Keystroke Index, Prev: Classes and Methods, Up: Top Appendix A FAQ ************** @@ -2356,16 +2169,28 @@ Yes, see ‘transient-display-buffer-action’ in *note Configuration::. A.2 How can I copy text from the popup buffer? ============================================== -To be able to mark text in any transient popup buffer using the mouse, -you have to add the following binding. Note that the region won’t be -visualized, while doing so. After you have quit the transient popup, -you will be able to yank it another buffer. +To be able to mark text in Transient’s popup buffer using the mouse, you +have to add the below binding. Note that for technical reasons, the +region won’t be visualized, while doing so. After you have quit the +transient popup, you will be able to yank it in another buffer. (keymap-set transient-predicate-map "" #'transient--do-stay) -A.3 Why did some of the key bindings change? +A.3 How does Transient compare to prefix keys and universal arguments? +====================================================================== + +See +. + +A.4 How does Transient compare to Magit-Popup and Hydra? +======================================================== + +See +. + +A.5 Why did some of the key bindings change? ============================================ You may have noticed that the bindings for some of the common commands @@ -2408,7 +2233,7 @@ and also using that for a common command would prevent me from doing so. (Also see the next question.) -A.4 Why does ‘q’ not quit popups anymore? +A.6 Why does ‘q’ not quit popups anymore? ========================================= I agree that ‘q’ is a good binding for commands that quit something. @@ -2427,7 +2252,7 @@ for ‘q’. If you want to get ‘q’’s old binding back then you can do so. Doing that is a bit more complicated than changing a single key binding, so I have implemented a function, ‘transient-bind-q-to-quit’ that makes the -necessary changes. See its doc string for more information. +necessary changes. See its documentation string for more information.  File: transient.info, Node: Keystroke Index, Next: Command and Function Index, Prev: FAQ, Up: Top @@ -2448,6 +2273,8 @@ Appendix B Keystroke Index * C-M-p: Using History. (line 13) * C-q: Aborting and Resuming Transients. (line 36) +* C-x a: Enabling and Disabling Suffixes. + (line 68) * C-x C-k: Saving Values. (line 29) * C-x C-s: Saving Values. (line 25) * C-x l: Enabling and Disabling Suffixes. @@ -2469,19 +2296,20 @@ Appendix C Command and Function Index [index] * Menu: -* transient--do-call: Transient State. (line 110) -* transient--do-exit: Transient State. (line 102) -* transient--do-leave: Transient State. (line 169) -* transient--do-quit-all: Transient State. (line 181) -* transient--do-quit-one: Transient State. (line 176) -* transient--do-recurse: Transient State. (line 117) -* transient--do-replace: Transient State. (line 129) -* transient--do-return: Transient State. (line 105) -* transient--do-stay: Transient State. (line 83) -* transient--do-stay <1>: Transient State. (line 166) -* transient--do-suspend: Transient State. (line 136) -* transient--do-suspend <1>: Transient State. (line 186) -* transient--do-warn: Transient State. (line 163) +* transient--do-call: Transient State. (line 125) +* transient--do-exit: Transient State. (line 117) +* transient--do-leave: Transient State. (line 193) +* transient--do-quit-all: Transient State. (line 205) +* transient--do-quit-one: Transient State. (line 200) +* transient--do-recurse: Transient State. (line 133) +* transient--do-replace: Transient State. (line 153) +* transient--do-return: Transient State. (line 120) +* transient--do-stack: Transient State. (line 145) +* transient--do-stay: Transient State. (line 105) +* transient--do-stay <1>: Transient State. (line 190) +* transient--do-suspend: Transient State. (line 161) +* transient--do-suspend <1>: Transient State. (line 210) +* transient--do-warn: Transient State. (line 187) * transient--history-init: Prefix Classes. (line 10) * transient--insert-group: Group Methods. (line 19) * transient-append-suffix: Modifying Existing Transients. @@ -2491,7 +2319,7 @@ Appendix C Command and Function Index * transient-args: Using Infix Arguments. (line 22) * transient-define-argument: Defining Suffix and Infix Commands. - (line 61) + (line 57) * transient-define-infix: Defining Suffix and Infix Commands. (line 26) * transient-define-prefix: Defining Transients. (line 13) @@ -2555,6 +2383,8 @@ Appendix C Command and Function Index (line 41) * transient-toggle-common: Common Suffix Commands. (line 18) +* transient-toggle-level-limit: Enabling and Disabling Suffixes. + (line 68)  File: transient.info, Node: Variable Index, Next: Concept Index, Prev: Command and Function Index, Up: Top @@ -2565,7 +2395,7 @@ Appendix D Variable Index [index] * Menu: -* transient-align-variable-pitch: Configuration. (line 181) +* transient-align-variable-pitch: Configuration. (line 185) * transient-current-command: Using Infix Arguments. (line 57) * transient-current-prefix: Using Infix Arguments. @@ -2574,25 +2404,25 @@ Appendix D Variable Index (line 44) * transient-default-level: Enabling and Disabling Suffixes. (line 33) -* transient-detect-key-conflicts: Configuration. (line 206) +* transient-detect-key-conflicts: Configuration. (line 210) * transient-display-buffer-action: Configuration. (line 51) * transient-enable-popup-navigation: Configuration. (line 36) -* transient-force-fixed-pitch: Configuration. (line 194) +* transient-force-fixed-pitch: Configuration. (line 198) * transient-force-single-column: Configuration. (line 93) -* transient-hide-during-minibuffer-read: Configuration. (line 177) -* transient-highlight-higher-levels: Configuration. (line 219) -* transient-highlight-mismatched-keys: Configuration. (line 131) +* transient-hide-during-minibuffer-read: Configuration. (line 181) +* transient-highlight-higher-levels: Configuration. (line 223) +* transient-highlight-mismatched-keys: Configuration. (line 135) * transient-history-file: Using History. (line 33) * transient-history-limit: Using History. (line 37) * transient-levels-file: Enabling and Disabling Suffixes. (line 38) * transient-mode-line-format: Configuration. (line 102) -* transient-read-with-initial-input: Configuration. (line 170) -* transient-semantic-coloring: Configuration. (line 118) +* transient-read-with-initial-input: Configuration. (line 174) +* transient-semantic-coloring: Configuration. (line 126) * transient-show-common-commands: Common Suffix Commands. (line 23) * transient-show-popup: Configuration. (line 15) -* transient-substitute-key-function: Configuration. (line 149) +* transient-substitute-key-function: Configuration. (line 153) * transient-values-file: Saving Values. (line 31)  @@ -2607,7 +2437,8 @@ Appendix E Concept Index * aborting transients: Aborting and Resuming Transients. (line 6) * classes and methods: Classes and Methods. (line 6) -* command dispatchers: Introduction. (line 70) +* command dispatchers: Technical Introduction. + (line 39) * common suffix commands: Common Suffix Commands. (line 6) * defining infix commands: Defining Suffix and Infix Commands. @@ -2634,7 +2465,6 @@ Appendix E Concept Index * scope of a transient: Defining Transients. (line 43) * suffix specifications: Suffix Specifications. (line 6) -* transient prefix command: Introduction. (line 13) * transient state: Transient State. (line 6) * transient-level: Enabling and Disabling Suffixes. (line 15) @@ -3360,69 +3190,67 @@ please read .  Tag Table: Node: Top763 -Node: Introduction4070 -Node: Usage9912 -Node: Invoking Transients10280 -Node: Aborting and Resuming Transients11359 -Node: Common Suffix Commands13970 -Node: Saving Values15806 -Ref: Saving Values-Footnote-117177 -Node: Using History17370 -Node: Getting Help for Suffix Commands18944 -Node: Enabling and Disabling Suffixes20312 -Node: Other Commands23380 -Node: Configuration24356 -Ref: Essential Options24636 -Ref: Accessibility Options28297 -Ref: Auxiliary Options28620 -Ref: Developer Options33347 -Node: Modifying Existing Transients34595 -Node: Defining New Commands38787 -Node: Defining Transients39123 -Node: Binding Suffix and Infix Commands41559 -Node: Group Specifications42417 -Node: Suffix Specifications46986 -Node: Defining Suffix and Infix Commands51142 -Node: Using Infix Arguments54348 -Node: Transient State57182 -Ref: Pre-commands for Infixes61145 -Ref: Pre-commands for Suffixes61416 -Ref: Pre-commands for Non-Suffixes63837 -Ref: Special Pre-Commands64981 -Node: Classes and Methods65489 -Node: Group Classes67703 -Node: Group Methods69630 -Node: Prefix Classes70883 -Node: Suffix Classes71974 -Node: Suffix Methods74220 -Node: Suffix Value Methods74541 -Node: Suffix Format Methods77299 -Node: Prefix Slots78748 -Ref: Internal Prefix Slots80028 -Node: Suffix Slots81285 -Ref: Slots of transient-suffix81653 -Ref: Slots of transient-infix82507 -Ref: Slots of transient-variable85633 -Ref: Slots of transient-switches85735 -Node: Predicate Slots86098 -Node: Related Abstractions and Packages87363 -Node: Comparison With Prefix Keys and Prefix Arguments87650 -Ref: Regular Prefix Commands88335 -Ref: Regular Prefix Arguments88683 -Ref: Transients89652 -Node: Comparison With Other Packages97923 -Ref: Magit-Popup98154 -Ref: Hydra99061 -Node: FAQ102117 -Ref: Can I control how the popup buffer is displayed?102260 -Ref: How can I copy text from the popup buffer?102441 -Ref: Why did some of the key bindings change?102913 -Ref: Why does q not quit popups anymore?105266 -Node: Keystroke Index106359 -Node: Command and Function Index108077 -Node: Variable Index114449 -Node: Concept Index116722 -Node: GNU General Public License119466 +Node: Introduction2976 +Ref: Some things that Transient can do3504 +Ref: Complexity in CLI programs3857 +Ref: Using Transient for composing interactive commands4458 +Node: Usage6700 +Node: Invoking Transients7068 +Node: Aborting and Resuming Transients8147 +Node: Common Suffix Commands10768 +Node: Saving Values12604 +Ref: Saving Values-Footnote-113975 +Node: Using History14168 +Node: Getting Help for Suffix Commands15742 +Node: Enabling and Disabling Suffixes17120 +Node: Other Commands20408 +Node: Configuration21384 +Ref: Essential Options21664 +Ref: Accessibility Options25325 +Ref: Auxiliary Options25648 +Ref: Developer Options30611 +Node: Modifying Existing Transients31859 +Node: Defining New Commands36051 +Node: Technical Introduction36414 +Node: Defining Transients42115 +Node: Binding Suffix and Infix Commands44582 +Node: Group Specifications45440 +Node: Suffix Specifications50541 +Node: Defining Suffix and Infix Commands54754 +Node: Using Infix Arguments57802 +Node: Transient State60636 +Ref: Pre-commands for Infixes65451 +Ref: Pre-commands for Suffixes65971 +Ref: Pre-commands for Non-Suffixes68425 +Ref: Special Pre-Commands69561 +Node: Classes and Methods70069 +Node: Group Classes72253 +Node: Group Methods74180 +Node: Prefix Classes75433 +Node: Suffix Classes76524 +Node: Suffix Methods79611 +Node: Suffix Value Methods79932 +Node: Suffix Format Methods82690 +Node: Prefix Slots84169 +Ref: Internal Prefix Slots86304 +Node: Suffix Slots87561 +Ref: Slots of transient-suffix87929 +Ref: Slots of transient-infix89066 +Ref: Slots of transient-variable92362 +Ref: Slots of transient-switches92464 +Node: Predicate Slots92827 +Node: FAQ94262 +Ref: Can I control how the popup buffer is displayed?94391 +Ref: How can I copy text from the popup buffer?94572 +Ref: How does Transient compare to prefix keys and universal arguments?95066 +Ref: How does Transient compare to Magit-Popup and Hydra?95309 +Ref: Why did some of the key bindings change?95503 +Ref: Why does q not quit popups anymore?97856 +Node: Keystroke Index98959 +Node: Command and Function Index100824 +Node: Variable Index107416 +Node: Concept Index109689 +Node: GNU General Public License112425  End Tag Table -- 2.47.3