diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi index 71f35011496..d55244d0579 100644 --- a/doc/misc/transient.texi +++ b/doc/misc/transient.texi @@ -25,13 +25,13 @@ General Public License for more details. @dircategory Emacs misc features @direntry -* Transient: (transient). Transient Commands. +* Transient: (transient). Transient Commands. @end direntry @finalout @titlepage @title Transient User and Developer Manual -@subtitle for version 0.10.1 +@subtitle for version 0.11.0 @author Jonas Bernoulli @page @vskip 0pt plus 1filll @@ -53,7 +53,7 @@ resource to get over that hurdle is Psionic K's interactive tutorial, available at @uref{https://github.com/positron-solutions/transient-showcase}. @noindent -This manual is for Transient version 0.10.1. +This manual is for Transient version 0.11.0. @insertcopying @end ifnottex @@ -385,7 +385,7 @@ than outlined above and even customizable.} If the user does not save the value and just exits using a regular suffix command, then the value is merely saved to the transient's history. That value won't be used when the transient is next invoked, -but it is easily accessible (@pxref{Using History}). +but it is easily accessible (see @ref{Using History}). Option @code{transient-common-command-prefix} controls the prefix key used in the following bindings. For simplicity's sake the default, @kbd{C-x}, @@ -454,8 +454,8 @@ previously used values. Usually the same keys as those mentioned above are bound to those commands. Authors of transients should arrange for different infix commands that -read the same kind of value to also use the same history key -(@pxref{Suffix Slots}). +read the same kind of value to also use the same history key (see +@ref{Suffix Slots}). Both kinds of history are saved to a file when Emacs is exited. @@ -785,7 +785,7 @@ menu buffer. The menu buffer is displayed in a window using The value of this option has the form @code{(@var{FUNCTION} . @var{ALIST})}, where @var{FUNCTION} is a function or a list of functions. Each such function should accept two arguments: a buffer to display and an -alist of the same form as @var{ALIST}. @xref{Choosing Window,,,elisp,}, +alist of the same form as @var{ALIST}. See @ref{Choosing Window,,,elisp,}, for details. The default is: @@ -799,7 +799,7 @@ The default is: This displays the window at the bottom of the selected frame. For alternatives see @ref{Buffer Display Action Functions,,,elisp,}, -and @xref{Buffer Display Action Alists,,,elisp,}. +and @ref{Buffer Display Action Alists,,,elisp,}. When you switch to a different ACTION, you should keep the ALIST entries for @code{dedicated} and @code{inhibit-same-window} in most cases. @@ -861,7 +861,7 @@ used to draw the line. This user option may be overridden if @code{:mode-line-format} is passed when creating a new prefix with @code{transient-define-prefix}. -Otherwise this can be any mode-line format. @xref{Mode Line Format,,,elisp,}, for details. +Otherwise this can be any mode-line format. See @ref{Mode Line Format,,,elisp,}, for details. @end defopt @defopt transient-semantic-coloring @@ -879,6 +879,9 @@ This option controls whether key bindings of infix commands that do not match the respective command-line argument should be highlighted. For other infix commands this option has no effect. +This is mostly indended for autors of transient menus and disabled +by default. + When this option is non-@code{nil}, the key binding for an infix argument is highlighted when only a long argument (e.g., @code{--verbose}) is specified but no shorthand (e.g., @code{-v}). In the rare case that a @@ -1026,10 +1029,10 @@ which can be included in multiple prefixes. See TODO@. as expected by @code{transient-define-prefix}. Note that an infix is a special kind of suffix. Depending on context ``suffixes'' means ``suffixes (including infixes)'' or ``non-infix suffixes''. Here it -means the former. @xref{Suffix Specifications}. +means the former. See @ref{Suffix Specifications}. @var{SUFFIX} may also be a group in the same form as expected by -@code{transient-define-prefix}. @xref{Group Specifications}. +@code{transient-define-prefix}. See @ref{Group Specifications}. @item @var{LOC} is a key description (a string as returned by @code{key-description} @@ -1053,11 +1056,11 @@ the function @code{transient--get-layout}. These functions operate on the information stored in the @code{transient--layout} property of the @var{PREFIX} symbol. Elements in that tree are not objects but have the form @code{(@var{CLASS} @var{PLIST}) for suffixes} and -[CLASS PLIST CHILDREN] for groups. At the root of the tree is an -element [N nil CHILDREN], where N is the version of the layout format, +@code{[CLASS PLIST CHILDREN]} for groups. At the root of the tree is an +element @code{[N Nil CHILDREN]}, where @code{N} is the version of the layout format, currently and hopefully for a long time 2. While that element looks like a group vector, that element does not count when identifying a -group using a coordinate vector, i.e., [0] is its first child, not the +group using a coordinate vector, i.e., @code{[0]} is its first child, not the root element itself. @defun transient-insert-suffix prefix loc suffix &optional keep-other @@ -1202,14 +1205,14 @@ 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. @xref{Using History}. +independently. See @ref{Using History}. After a transient prefix command is invoked, @kbd{C-h @var{KEY}} can be used to show the documentation for the infix or suffix command that @kbd{@var{KEY}} is bound to (see @ref{Getting Help for Suffix Commands}), and infixes and suffixes can be removed from the transient using @kbd{C-x l @var{KEY}}. Infixes and suffixes that are disabled by default can be enabled the same way. -@xref{Enabling and Disabling Suffixes}. +See @ref{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, @@ -1260,7 +1263,7 @@ explicitly. @var{GROUP}s add key bindings for infix and suffix commands and specify how these bindings are presented in the menu buffer. At least one -@var{GROUP} has to be specified. @xref{Binding Suffix and Infix Commands}. +@var{GROUP} has to be specified. See @ref{Binding Suffix and Infix Commands}. The @var{BODY} is optional. If it is omitted, then @var{ARGLIST} is ignored and the function definition becomes: @@ -1311,13 +1314,11 @@ GROUPs have the same form as for @code{transient-define-prefix}. @section Binding Suffix and Infix Commands The macro @code{transient-define-prefix} is used to define a transient. -This defines the actual transient prefix command (@pxref{Defining -Transients}) and adds the transient's infix and suffix bindings, as +This defines the actual transient prefix command (see @ref{Defining 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 @code{transient-insert-suffix} (@pxref{Modifying Existing Transients}). -These functions take a ``suffix specification'' as one of +functions such as @code{transient-insert-suffix} (see @ref{Modifying Existing Transients}). These functions take a ``suffix specification'' as one of their arguments, which has the same form as the specifications used in @code{transient-define-prefix}. @@ -1343,13 +1344,10 @@ brackets to do the latter. Group specifications then have this form: @lisp -[@{@var{LEVEL}@} @{@var{DESCRIPTION}@} - @{@var{KEYWORD} @var{VALUE}@}... - @var{ELEMENT}...] +[@{LEVEL@} @{DESCRIPTION@} @{KEYWORD VALUE@}... ELEMENT...] @end lisp -The @var{LEVEL} is optional and defaults to 4. @xref{Enabling and -Disabling Suffixes}. +The @var{LEVEL} is optional and defaults to 4. See @ref{Enabling and Disabling Suffixes}. The @var{DESCRIPTION} is optional. If present, it is used as the heading of the group. @@ -1500,9 +1498,7 @@ suffixes''. Here it means the former. Suffix specifications have this form: @lisp -([@var{LEVEL}] - [@var{KEY} [@var{DESCRIPTION}]] - @var{COMMAND}|@var{ARGUMENT} [@var{KEYWORD} @var{VALUE}]...) +([LEVEL] [KEY [DESCRIPTION]] COMMAND|ARGUMENT [KEYWORD VALUE]...) @end lisp @var{LEVEL}, @var{KEY} and @var{DESCRIPTION} can also be specified using the @var{KEYWORD}s @@ -1513,8 +1509,8 @@ the object's values just for the binding inside this transient. @itemize @item -@var{LEVEL} is the suffix level, an integer between 1 and 7. -@xref{Enabling and Disabling Suffixes}. +@var{LEVEL} is the suffix level, an integer between 1 and 7. See +@ref{Enabling and Disabling Suffixes}. @item KEY is the key binding, a string in the format returned by @@ -1588,7 +1584,7 @@ guessed based on the long argument. If the argument ends with @samp{=} Finally, details can be specified using optional @var{KEYWORD}-@var{VALUE} pairs. Each keyword has to be a keyword symbol, either @code{:class} or a keyword -argument supported by the constructor of that class. @xref{Suffix Slots}. +argument supported by the constructor of that class. See @ref{Suffix Slots}. If a keyword argument accepts a function as value, you an use a @code{lambda} expression. As a special case, the @code{##} macro (which returns a @code{lambda} @@ -1742,16 +1738,16 @@ its value can be accessed using @code{transient-args}. This function returns the scope of the active or current transient prefix command. -If optional PREFIXES and CLASSES are both nil, return the scope of +If optional PREFIXES and CLASSES are both @code{nil}, return the scope of the prefix currently being setup, making this variation useful, e.g., in @code{:if*} predicates. If no prefix is being setup, but the current command was invoked from some prefix, then return the scope of that. -If PREFIXES is non-nil, it must be a prefix command or a list of such -commands. If CLASSES is non-nil, it must be a prefix class or a list +If PREFIXES is non-@code{nil}, it must be a prefix command or a list of such +commands. If CLASSES is non-@code{nil}, it must be a prefix class or a list of such classes. When this function is called from the body or the @code{interactive} form of a suffix command, PREFIXES and/or CLASSES should -be non-nil. If either is non-nil, try the following in order: +be non-@code{nil}. If either is non-@code{nil}, try the following in order: @itemize @item @@ -1777,7 +1773,7 @@ PREFIXES@. This only works if that slot is set in the respective class definition or using its `transient-init-scope' method. @end itemize -If no prefix matches, return nil. +If no prefix matches, return @code{nil}. @end defun @node Current Suffix Command @@ -1938,8 +1934,8 @@ means that all outer prefixes are exited at once. @item The behavior for non-suffixes can be set for a particular prefix, by the prefix's @code{transient-non-suffix} slot to a boolean, a suitable -pre-command function, or a shorthand for such a function. -@xref{Pre-commands for Non-Suffixes}. +pre-command function, or a shorthand for such a function. See +@ref{Pre-commands for Non-Suffixes}. @item The common behavior for the suffixes of a particular prefix can be @@ -2279,7 +2275,7 @@ object should not affect later invocations. @item All suffix and infix classes derive from @code{transient-suffix}, which in turn derives from @code{transient-child}, from which @code{transient-group} also -derives (@pxref{Group Classes}). +derives (see @ref{Group Classes}). @item All infix classes derive from the abstract @code{transient-infix} class, @@ -2293,7 +2289,7 @@ that does not do so. If you do that then you get to implement many methods. Also, infixes and non-infix suffixes are usually defined using -different macros (@pxref{Defining Suffix and Infix Commands}). +different macros (see @ref{Defining Suffix and Infix Commands}). @item Classes used for infix commands that represent arguments should @@ -2703,7 +2699,7 @@ secondary value, called a ``scope''. See @code{transient-define-prefix}. @code{transient-suffix}, @code{transient-non-suffix} and @code{transient-switch-frame} play a part when determining whether the currently active transient prefix command remains active/transient when a suffix or arbitrary -non-suffix command is invoked. @xref{Transient State}. +non-suffix command is invoked. See @ref{Transient State}. @item @code{refresh-suffixes} Normally suffix objects and keymaps are only setup @@ -2785,7 +2781,7 @@ of the same symbol. @item @code{level} The level of the prefix commands. The suffix commands whose -layer is equal or lower are displayed. @pxref{Enabling and Disabling Suffixes}. +layer is equal or lower are displayed. See @ref{Enabling and Disabling Suffixes}. @item @code{value} The likely outdated value of the prefix. Instead of accessing @@ -2843,7 +2839,7 @@ which is useful for alignment purposes. @code{command} The command, a symbol. @item -@code{transient} Whether to stay transient. @xref{Transient State}. +@code{transient} Whether to stay transient. See @ref{Transient State}. @item @code{format} The format used to display the suffix in the menu buffer. @@ -2886,7 +2882,7 @@ defining a command using @code{transient-define-suffix}. The following two slots are experimental. They can also be set for a group, in which case they apply to all suffixes in that group, except -for suffixes that set the same slot to a non-nil value. +for suffixes that set the same slot to a non-@code{nil} value. @itemize @item @@ -2897,7 +2893,7 @@ advice. @item @code{advice*} A function used to advise the command. Unlike @code{advice}, this advises not only the command body but also its @code{interactive} spec. If -both slots are non-nil, @code{advice} is used for the body and @code{advice*} is +both slots are non-@code{nil}, @code{advice} is used for the body and @code{advice*} is used for the @code{interactive} form. When advising the @code{interactive} spec, called using @code{(funcall advice #'advice-eval-interactive-spec spec)}. @end itemize @@ -3067,14 +3063,14 @@ currently cannot be invoked. By default these predicates run when the prefix command is invoked, but this can be changes, using the @code{refresh-suffixes} prefix slot. -@xref{Prefix Slots}. +See @ref{Prefix Slots}. One more slot is shared between group and suffix classes, @code{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. @code{level} controls whether a suffix or a group should be available depending on user preference. -@xref{Enabling and Disabling Suffixes}. +See @ref{Enabling and Disabling Suffixes}. @node FAQ @appendix FAQ diff --git a/lisp/transient.el b/lisp/transient.el index 520270972e8..0ca60c4ceea 100644 --- a/lisp/transient.el +++ b/lisp/transient.el @@ -5,7 +5,7 @@ ;; Author: Jonas Bernoulli ;; URL: https://github.com/magit/transient ;; Keywords: extensions -;; Version: 0.10.1 +;; Version: 0.11.0 ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -33,7 +33,7 @@ ;;; Code: ;;;; Frontmatter -(defconst transient-version "v0.10.1-8-g188ec9a1-builtin") +(defconst transient-version "v0.11.0-10-g6637364e-builtin") (require 'cl-lib) (require 'eieio) @@ -52,22 +52,6 @@ (defvar Man-notify-method) -(static-if (< emacs-major-version 30) - (progn - (defun internal--build-binding@backport-e680827e814 (fn binding prev-var) - "Backport not warning about `_' not being left unused. -Backport fix for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69108, -from Emacs commit e680827e814e155cf79175d87ff7c6ee3a08b69a." - (let ((binding (funcall fn binding prev-var))) - (if (eq (car binding) '_) - (cons (make-symbol "s") (cdr binding)) - binding))) - (advice-add 'internal--build-binding :around - #'internal--build-binding@backport-e680827e814))) - -(make-obsolete-variable 'transient-hide-during-minibuffer-read - 'transient-show-during-minibuffer-read "0.8.0") - (defvar transient-common-command-prefix) (defmacro transient--with-emergency-exit (id &rest body) @@ -94,9 +78,10 @@ from Emacs commit e680827e814e155cf79175d87ff7c6ee3a08b69a." (defcustom transient-show-popup t "Whether and when to show transient's menu in a buffer. -\\ -- If t, then show the buffer as soon as a transient prefix command - is invoked. + +\\\ +- If t (the default), then show the buffer as soon as a transient + prefix command is invoked. - If nil, then do not show the buffer unless the user explicitly requests it, by pressing \\[transient-show] or a prefix key. @@ -118,8 +103,8 @@ from Emacs commit e680827e814e155cf79175d87ff7c6ee3a08b69a." (defcustom transient-enable-popup-navigation 'verbose "Whether navigation commands are enabled in the menu buffer. -If the value is `verbose', additionally show brief documentation -about the command under point in the echo area. +If the value is `verbose' (the default), additionally show brief +documentation about the command under point in the echo area. While a transient is active transient's menu buffer is not the current buffer, making it necessary to use dedicated commands to @@ -208,6 +193,7 @@ want to change the value of `transient-mode-line-format'." (defcustom transient-minimal-frame-width 83 "Minimal width of dedicated frame used to display transient menu. + This is only used if the transient menu is actually displayed in a dedicated frame (see `transient-display-buffer-action'). The value is in characters." @@ -329,12 +315,15 @@ This command is not bound by default, see its docstring for instructions." (defcustom transient-highlight-mismatched-keys nil "Whether to highlight keys that do not match their argument. -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 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. +This is mostly intended for authors of transient menus and disabled by +default. + +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 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. The highlighting is done using `transient-mismatched-key' and `transient-nonstandard-key'." @@ -577,10 +566,9 @@ See info node `(transient)Enabling and Disabling Suffixes'." :group 'transient-faces) (defface transient-higher-level - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color ,(let ((color (face-attribute 'shadow :foreground t t))) - (or (and (not (eq color 'unspecified)) color) - "grey60"))))) + (let* ((color (face-attribute 'shadow :foreground t t)) + (color (if (eq color 'unspecified) "grey60" color))) + `((t :box (:line-width (-1 . -1) :color ,color)))) "Face optionally used to highlight suffixes on higher levels. See also option `transient-highlight-higher-levels'." :group 'transient-faces) @@ -661,15 +649,13 @@ character used to separate possible values from each other." :group 'transient-faces) (defface transient-nonstandard-key - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "cyan"))) + `((t :box (:line-width (-1 . -1) :color "cyan"))) "Face optionally used to highlight keys conflicting with short-argument. See also option `transient-highlight-mismatched-keys'." :group 'transient-faces) (defface transient-mismatched-key - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "magenta"))) + `((t :box (:line-width (-1 . -1) :color "magenta"))) "Face optionally used to highlight keys without a short-argument. See also option `transient-highlight-mismatched-keys'." :group 'transient-faces) @@ -1397,12 +1383,13 @@ commands are aliases for." (_ (use key val))))) (when spec (error "Need keyword, got %S" (car spec))) - (if-let* ((key (plist-get args :key))) - (when (string-match "\\`\\({p}\\)" key) - (use :key - (replace-match transient-common-command-prefix t t key 1))) - (when-let* ((shortarg (plist-get args :shortarg))) - (use :key shortarg)))) + (cond* + ((bind-and* (key (plist-get args :key))) + (when (string-match "\\`\\({p}\\)" key) + (use :key + (replace-match transient-common-command-prefix t t key 1)))) + ((bind-and* (shortarg (plist-get args :shortarg))) + (use :key shortarg)))) (list 'cons (macroexp-quote (or class 'transient-suffix)) (cons 'list args)))) @@ -1427,8 +1414,7 @@ See also `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)) +(unless read-extended-command-predicate (setq read-extended-command-predicate #'transient-command-completion-not-suffix-only-p)) @@ -1436,50 +1422,52 @@ symbol property.") (put prefix 'transient--layout (vector 2 nil layout))) (defun transient--get-layout (prefix) - (if-let* - ((layout - (or (get prefix 'transient--layout) - ;; Migrate unparsed legacy group definition. - (condition-case-unless-debug err - (and-let* ((value (symbol-value prefix))) - (transient--set-layout - prefix - (if (and (listp value) - (or (listp (car value)) - (vectorp (car value)))) - (transient-parse-suffixes prefix value) - (list (transient-parse-suffix prefix value))))) - (error - (message "Not a legacy group definition: %s: %S" prefix err) - nil))))) - (if (vectorp layout) - (let ((version (aref layout 0))) - (if (= version 2) - layout - (error "Unsupported layout version %s for %s" version prefix))) - ;; Upgrade from version 1. - (cl-labels - ((upgrade (spec) - (cond - ((vectorp spec) - (pcase-let ((`[,level ,class ,args ,children] spec)) - (when level - (setq args (plist-put args :level level))) - (vector class args (mapcar #'upgrade children)))) - ((and (listp spec) - (length= spec 3) - (or (null (car spec)) - (natnump (car spec))) - (symbolp (cadr spec))) - (pcase-let ((`(,level ,class ,args) spec)) - (when level - (setq args (plist-put args :level level))) - (cons class args))) - ((listp spec) - (mapcar #'upgrade spec)) - (t spec)))) - (transient--set-layout prefix (upgrade layout)))) - (error "Not a transient prefix command or group definition: %s" prefix))) + (cond* + ((bind* + (layout + (or (get prefix 'transient--layout) + ;; Migrate unparsed legacy group definition. + (condition-case-unless-debug err + (and-let* ((value (symbol-value prefix))) + (transient--set-layout + prefix + (if (and (listp value) + (or (listp (car value)) + (vectorp (car value)))) + (transient-parse-suffixes prefix value) + (list (transient-parse-suffix prefix value))))) + (error + (message "Not a legacy group definition: %s: %S" prefix err) + nil)))))) + ((not layout) + (error "Not a transient prefix command or group definition: %s" prefix)) + ((vectorp layout) + (let ((version (aref layout 0))) + (if (= version 2) + layout + (error "Unsupported layout version %s for %s" version prefix)))) + (t + ;; Upgrade from version 1. + (transient--set-layout + prefix + (named-let upgrade ((spec layout)) + (cond ((vectorp spec) + (pcase-let ((`[,level ,class ,args ,children] spec)) + (when level + (setq args (plist-put args :level level))) + (vector class args (mapcar #'upgrade children)))) + ((and (listp spec) + (length= spec 3) + (or (null (car spec)) + (natnump (car spec))) + (symbolp (cadr spec))) + (pcase-let ((`(,level ,class ,args) spec)) + (when level + (setq args (plist-put args :level level))) + (cons class args))) + ((listp spec) + (mapcar #'upgrade spec)) + (t spec))))))) (defun transient--get-children (prefix) (aref (transient--get-layout prefix) 2)) @@ -1674,11 +1662,12 @@ See info node `(transient)Modifying Existing Transients'." (defun transient--match-child (group loc child) (cl-etypecase child (string nil) - (symbol (if (symbolp loc) - (and (eq child loc) - (list child group)) - (and-let* ((include (transient--get-layout child))) - (transient--locate-child include loc)))) + (symbol (cond* + ((symbolp loc) + (and (eq child loc) + (list child group))) + ((bind-and* (include (transient--get-layout child))) + (transient--locate-child include loc)))) (vector (seq-some (lambda (subgroup) (transient--locate-child subgroup loc)) (aref group 2))) @@ -1924,47 +1913,46 @@ probably use this instead: (get COMMAND \\='transient--suffix)" (when command (cl-check-type command command)) - (cond - (transient--pending-suffix) - (transient--current-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)))) - (cond - ((length= suffixes 1) - (car suffixes)) - ((cl-find-if (lambda (obj) - (equal (listify-key-sequence (kbd (oref obj key))) - (listify-key-sequence (this-command-keys)))) - suffixes)) - ;; COMMAND is only provided if `this-command' is meaningless, in - ;; which case `this-command-keys' is also meaningless, making it - ;; impossible to disambiguate bindings for the same command. - (command (car suffixes)) - ;; If COMMAND is nil, then failure to disambiguate likely means - ;; that there is a bug somewhere. - ((length> suffixes 1) - (error "BUG: Cannot unambiguously determine suffix object")) - ;; It is legimate to use this function as a predicate of sorts. - ;; `transient--pre-command' and `transient-help' are examples. - (t nil)))) - ((and-let* ((obj (transient--suffix-prototype (or command this-command))) - (obj (clone obj))) - (progn - (transient-init-scope obj) - (transient-init-value obj) - obj))))) + (cond* + (transient--pending-suffix) + (transient--current-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)))) + (cond + ((length= suffixes 1) + (car suffixes)) + ((cl-find-if (lambda (obj) + (equal (listify-key-sequence (kbd (oref obj key))) + (listify-key-sequence (this-command-keys)))) + suffixes)) + ;; COMMAND is only provided if `this-command' is meaningless, in + ;; which case `this-command-keys' is also meaningless, making it + ;; impossible to disambiguate bindings for the same command. + (command (car suffixes)) + ;; If COMMAND is nil, then failure to disambiguate likely means + ;; that there is a bug somewhere. + ((length> suffixes 1) + (error "BUG: Cannot unambiguously determine suffix object")) + ;; It is legimate to use this function as a predicate of sorts. + ;; `transient--pre-command' and `transient-help' are examples. + (t nil)))) + ((bind-and* (obj (transient--suffix-prototype (or command this-command))) + (obj (clone obj))) + (transient-init-scope obj) + (transient-init-value obj) + obj))) (defun transient--suffix-prototype (command) (or (get command 'transient--suffix) @@ -1997,25 +1985,22 @@ to `transient-predicate-map'." "" #'transient-scroll-up "" #'transient-scroll-down) -(defvar transient-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map transient-base-map) - (keymap-set map "C-u" #'universal-argument) - (keymap-set map "C--" #'negative-argument) - (keymap-set map "C-t" #'transient-show) - (keymap-set map "?" #'transient-help) - (keymap-set map "C-h" #'transient-help) - ;; Next two have additional bindings in transient-common-commands. - (keymap-set map "C-M-p" #'transient-history-prev) - (keymap-set map "C-M-n" #'transient-history-next) - (when (fboundp 'other-frame-prefix) ;Emacs >= 28.1 - (keymap-set map "C-x 5 5" 'other-frame-prefix) - (keymap-set map "C-x 4 4" 'other-window-prefix)) - map) - "Top-level keymap used by all transients. +(defvar-keymap transient-map + :doc "Top-level keymap used by all transients. If you add a new command here, then you must also add a binding -to `transient-predicate-map'. See also `transient-base-map'.") +to `transient-predicate-map'. See also `transient-base-map'." + :parent transient-base-map + "C-u" #'universal-argument + "C--" #'negative-argument + "C-t" #'transient-show + "?" #'transient-help + "C-h" #'transient-help + "C-x 5 5" #'other-frame-prefix + "C-x 4 4" #'other-window-prefix + ;; These have additional bindings in transient-common-commands. + "C-M-p" #'transient-history-prev + "C-M-n" #'transient-history-next) (defvar-keymap transient-edit-map :doc "Keymap that is active while a transient in is in \"edit mode\"." @@ -2449,16 +2434,14 @@ value. Otherwise return CHILDREN as is.") (transient--get-children 'transient-common-commands)))))) (defun transient--flatten-suffixes (layout) - (cl-labels ((s (def) - (cond - ((stringp def) nil) - ((cl-typep def 'transient-information) nil) - ((listp def) (mapcan #'s def)) - ((cl-typep def 'transient-group) - (mapcan #'s (oref def suffixes))) - ((cl-typep def 'transient-suffix) - (list def))))) - (mapcan #'s layout))) + (named-let flatten ((def layout)) + (cond ((stringp def) nil) + ((cl-typep def 'transient-information) nil) + ((listp def) (mapcan #'flatten def)) + ((cl-typep def 'transient-group) + (mapcan #'flatten (oref def suffixes))) + ((cl-typep def 'transient-suffix) + (list def))))) (defun transient--init-child (levels spec parent) (cl-etypecase spec @@ -3825,20 +3808,22 @@ command-line option) or \": \". Finally fall through to using \"(BUG: no prompt): \" as the prompt." - (if-let* ((prompt (oref obj prompt))) - (let ((prompt (if (functionp prompt) - (funcall prompt obj) - prompt))) - (if (stringp prompt) - prompt - "[BUG: invalid prompt]: ")) - (if-let* ((name (or (and (slot-boundp obj 'argument) (oref obj argument)) - (and (slot-boundp obj 'variable) (oref obj variable))))) - (if (and (stringp name) - (string-suffix-p "=" name)) - name - (format "%s: " name)) - "[BUG: no prompt]: "))) + (cond* + ((bind-and* (prompt (oref obj prompt))) + (let ((prompt (if (functionp prompt) + (funcall prompt obj) + prompt))) + (if (stringp prompt) + prompt + "[BUG: invalid prompt]: "))) + ((bind-and* + (name (or (and (slot-boundp obj 'argument) (oref obj argument)) + (and (slot-boundp obj 'variable) (oref obj variable))))) + (if (and (stringp name) + (string-suffix-p "=" name)) + name + (format "%s: " name))) + ("[BUG: no prompt]: "))) ;;;; Set @@ -4415,8 +4400,7 @@ have a history of their own.") (height (cond ((not window-system) nil) ((natnump format) format) ((eq format 'line) 1))) - (face `(,@(and (>= emacs-major-version 27) '(:extend t)) - :background ,(transient--prefix-color)))) + (face `(:background ,(transient--prefix-color) :extend t))) (concat (propertize "__" 'face face 'display `(space :height (,height))) (propertize "\n" 'face face 'line-height t)))) @@ -4699,9 +4683,9 @@ apply the face `transient-unreachable' to the complete string." (when-let* ((face (transient--get-face obj 'face))) (setq desc (transient--add-face desc face t))) (setq desc (propertize "(BUG: no description)" 'face 'error))) - (when (if transient--all-levels-p - (> (oref obj level) transient--default-prefix-level) - (and transient-highlight-higher-levels + (when (cond (transient--all-levels-p + (> (oref obj level) transient--default-prefix-level)) + (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))) @@ -4887,28 +4871,28 @@ if non-nil, else show the `man-page' if non-nil, else use Also used to dispatch showing documentation for the current prefix. If the suffix is a sub-prefix, then also call the prefix method." - (cond - ((eq this-command 'transient-help) - (transient-show-help transient--prefix)) - ((let ((prefix (get (oref obj command) - 'transient--prefix))) - (and prefix (not (eq (oref transient--prefix command) this-command)) - (prog1 t (transient-show-help prefix))))) - ((if-let* ((show-help (oref obj show-help))) - (funcall show-help obj) - (transient--describe-function this-command))))) + (cond* + ((eq this-command 'transient-help) + (transient-show-help transient--prefix)) + ((bind-and* (prefix (get (oref obj command) 'transient--prefix)) + (n/a (not (eq (oref transient--prefix command) this-command)))) + (transient-show-help prefix)) + ((bind-and* (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' if non-nil, else use `describe-function'. When showing the manpage, then try to jump to the correct location." - (if-let* ((show-help (oref obj show-help))) - (funcall show-help obj) - (if-let* ((man-page (oref transient--prefix man-page)) - (argument (and (slot-boundp obj 'argument) - (oref obj argument)))) - (transient--show-manpage man-page argument) - (transient--describe-function this-command)))) + (cond* + ((bind-and* (show-help (oref obj show-help))) + (funcall show-help obj)) + ((bind-and* (man-page (oref transient--prefix man-page)) + (argument (and (slot-boundp obj 'argument) + (oref obj argument)))) + (transient--show-manpage man-page argument)) + ((transient--describe-function this-command)))) ;; `cl-generic-generalizers' doesn't support `command' et al. (cl-defmethod transient-show-help (cmd) @@ -5159,6 +5143,7 @@ See `forward-button' for information about N." (defvar-keymap transient--isearch-mode-map :parent isearch-mode-map + "" #'transient-isearch-exit " " #'transient-isearch-exit " " #'transient-isearch-cancel " " #'transient-isearch-abort)