]> git.eshelyaron.com Git - emacs.git/commitdiff
Update to Transient v0.8.4-7-gabee7353
authorJonas Bernoulli <jonas@bernoul.li>
Sat, 1 Feb 2025 17:14:47 +0000 (18:14 +0100)
committerEshel Yaron <me@eshelyaron.com>
Mon, 3 Feb 2025 11:14:15 +0000 (12:14 +0100)
(cherry picked from commit 3a86774ce55e9dc4dc6de01f6aca19fcaa41a5d3)

doc/misc/transient.texi
lisp/transient.el

index fb8b6da145cc19cca406af87d0f6ba2e730a717b..4740663e9877eb92708e1fe1bb1c0046b1767f65 100644 (file)
@@ -31,7 +31,7 @@ General Public License for more details.
 @finalout
 @titlepage
 @title Transient User and Developer Manual
-@subtitle for version 0.8.3
+@subtitle for version 0.8.4
 @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.8.3.
+This manual is for Transient version 0.8.4.
 
 @insertcopying
 @end ifnottex
@@ -93,6 +93,7 @@ Defining New Commands
 * Binding Suffix and Infix Commands::
 * Defining Suffix and Infix Commands::
 * Using Infix Arguments::
+* Using Prefix Scope::
 * Current Suffix Command::
 * Current Prefix Command::
 * Transient State::
@@ -553,6 +554,48 @@ the level specified by @code{transient-default-level} are temporarily
 available anyway.
 @end table
 
+@defun transient-set-default-level suffix level
+This function sets the default level of the suffix COMMAND to LEVEL@.
+
+If a suffix command appears in multiple menus, it may make sense to
+consistently change its level in all those menus at once.  For
+example, the @code{--gpg-sign} argument (which is implemented using the
+command @code{magit:--gpg-sign}), is bound in all of Magit's menu which
+create commits.  Users who sometimes sign their commits would want
+that argument to be available in all of these menus, while for users
+who never sign it is just unnecessary noise in any menus.
+
+To always make @code{--gpg-sign} available, use:
+
+@lisp
+(transient-set-default-level 'magit:--gpg-sign 1)
+@end lisp
+
+To never make @code{--gpg-sign} available, use:
+
+@lisp
+(transient-set-default-level 'magit:--gpg-sign 0)
+@end lisp
+
+This sets the level in the suffix prototype object for this command.
+Commands only have a suffix prototype if they were defined using one
+of @code{transient-define-argument}, @code{transient-define-infix} and
+@code{transient-define-suffix}.  For all other commands this would signal
+an error.  (This is one of the reasons why package authors should
+use one of these functions to define shared suffix commands, and
+especially shared arguments.)
+
+If the user changes the level of a suffix in a particular menu,
+using @kbd{C-x l} as shown above, then that obviously shadows the default.
+
+It is also possible to set the level of a suffix binding in a
+particular menu, either when defining the menu using
+@code{transient-define-prefix,} or later using @code{transient-insert-suffix}.  If
+such bindings specify a level, then that also overrides the default.
+(Per-suffix default levels is a new feature, so you might encounter
+this quite often.)
+@end defun
+
 @node Other Commands
 @section Other Commands
 
@@ -1017,6 +1060,7 @@ signal an error.
 * Binding Suffix and Infix Commands::
 * Defining Suffix and Infix Commands::
 * Using Infix Arguments::
+* Using Prefix Scope::
 * Current Suffix Command::
 * Current Prefix Command::
 * Transient State::
@@ -1323,6 +1367,13 @@ be replaced with an error.
 The boolean @code{:pad-keys} argument controls whether keys of all suffixes
 contained in a group are right padded, effectively aligning the
 descriptions.
+
+@item
+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} expression and is implemented in the @code{llama} package) is also
+supported.  Inside group specifications, the use of @code{##} is not
+supported anywhere but directly following a keyword symbol.
 @end itemize
 
 The @var{ELEMENT}s are either all subgroups, or all suffixes and strings.
@@ -1446,6 +1497,12 @@ 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}.
 
+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}
+expression and is implemented in the @code{llama} package) is also supported.
+Inside suffix bindings, the use of @code{##} is not supported anywhere but
+directly following a keyword symbol.
+
 @node Defining Suffix and Infix Commands
 @section Defining Suffix and Infix Commands
 
@@ -1568,6 +1625,55 @@ used if you need the objects (as opposed to just their values) and
 if the current command is not being invoked from @var{PREFIX}.
 @end defun
 
+@node Using Prefix Scope
+@section Using Prefix Scope
+
+Some transients have a sort of secondary value, called a scope.  A
+prefix's scope can be accessed using @code{transient-scope}; similar to how
+its value can be accessed using @code{transient-args}.
+
+@defun transient-scope prefixes classes
+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
+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
+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:
+
+@itemize
+@item
+If the current suffix command was invoked from a prefix, which
+appears in PREFIXES, return the scope of that prefix.
+
+@item
+If the current suffix command was invoked from a prefix, and its
+class derives from one of the CLASSES, return the scope of that
+prefix.
+
+@item
+If a prefix is being setup and it appears in PREFIXES, return its
+scope.
+
+@item
+If a prefix is being setup and its class derives from one of the
+CLASSES, return its scope.
+
+@item
+Finally try to return the default scope of the first command in
+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.
+@end defun
+
 @node Current Suffix Command
 @section Current Suffix Command
 
@@ -2458,8 +2564,9 @@ being initialized.  This slot is still experimental.
 @code{transient-mode-line-format}.  It should have the same type.
 
 @item
-@code{column-width} is only respected inside @code{transient-columns} groups and
-allows aligning columns across separate instances of that.
+@code{column-widths} is only respected inside @code{transient-columns} groups and
+allows aligning columns across separate instances of that.  A list
+of integers.
 
 @item
 @code{variable-pitch} controls whether alignment is done pixel-wise to
@@ -2535,8 +2642,9 @@ Also see @ref{Suffix Classes}.
 @subheading Slots of @code{transient-child}
 
 This is the abstract superclass of @code{transient-suffix} and @code{transient-group}.
-This is where the shared @code{if*} and @code{inapt-if*} slots (see @ref{Predicate Slots})
-and the @code{level} slot (see @ref{Enabling and Disabling Suffixes}) are defined.
+This is where the shared @code{if*} and @code{inapt-if*} slots (see @ref{Predicate Slots}),
+the @code{level} slot (see @ref{Enabling and Disabling Suffixes}), and the @code{advice}
+and @code{advice*} slots (see @ref{Slots of @code{transient-suffix}}) are defined.
 
 @itemize
 @item
@@ -2595,6 +2703,24 @@ for details.
 defining a command using @code{transient-define-suffix}.
 @end itemize
 
+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.
+
+@itemize
+@item
+@code{advice} A function used to advise the command.  The advise is called
+using @code{(apply advice command args)}, i.e., it behaves like an "around"
+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
+used for the @code{interactive} form.  When advising the @code{interactive} spec,
+called using @code{(funcall advice #'advice-eval-interactive-spec spec)}.
+@end itemize
+
 @anchor{Slots of @code{transient-infix}}
 @subheading Slots of @code{transient-infix}
 
index 382de3d54886c9806de5fac7e72b0bc3afc1fe01..f9859e96fe493a3abfd1fcbec52ffa62f6e208e4 100644 (file)
@@ -5,7 +5,7 @@
 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
 ;; URL: https://github.com/magit/transient
 ;; Keywords: extensions
-;; Version: 0.8.3
+;; Version: 0.8.4
 
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 
@@ -32,7 +32,7 @@
 
 ;;; Code:
 
-(defconst transient-version "v0.8.3-2-gf0478b29-builtin")
+(defconst transient-version "v0.8.4-7-gabee7353-builtin")
 
 (require 'cl-lib)
 (require 'eieio)
@@ -281,6 +281,20 @@ number is positive, or hide the menu if it is negative."
                    :format "\n   %t: %v"
                    :value -20)))
 
+(defcustom transient-show-docstring-format "%s"
+  "How to display suffix docstrings.
+
+The command `transient-toggle-docstrings' toggles between showing suffix
+descriptions as usual, and instead or additionally displaying the suffix
+docstrings.  The format specified here controls how that is done.  %c is
+the description and %s is the docstring.  Use \"%-14c %s\" or similar to
+display both.
+
+This command is not bound by default, see its docstring for instructions."
+  :package-version '(transient . "0.8.4")
+  :group 'transient
+  :type 'string)
+
 (defcustom transient-read-with-initial-input nil
   "Whether to use the last history element as initial minibuffer input."
   :package-version '(transient . "0.2.0")
@@ -709,7 +723,7 @@ the prototype is stored in the clone's `prototype' slot.")
     :documentation "The parent group object.")
    (level
     :initarg :level
-    :initform (symbol-value 'transient--default-child-level)
+    :initform nil
     :documentation "Enable if level of prefix is equal or greater.")
    (if
     :initarg :if
@@ -779,7 +793,15 @@ the prototype is stored in the clone's `prototype' slot.")
    (inapt-if-not-derived
     :initarg :inapt-if-not-derived
     :initform nil
-    :documentation "Inapt if major-mode does not derive from value."))
+    :documentation "Inapt if major-mode does not derive from value.")
+   (advice
+    :initarg :advice
+    :initform nil
+    :documentation "Advise applied to the command body.")
+   (advice*
+    :initarg :advice*
+    :initform nil
+    :documentation "Advise applied to the command body and interactive spec."))
   "Abstract superclass for group and suffix classes.
 
 It is undefined which predicates are used if more than one `if*'
@@ -1188,14 +1210,15 @@ commands are aliases for."
         (cond ((eq key :class)
                (setq class val))
               ((or (symbolp val)
-                   (and (listp val) (not (eq (car val) 'lambda))))
+                   (and (listp val)
+                        (not (memq (car val) (list 'lambda (intern ""))))))
                (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)
+          level
           (list 'quote
                 (cond (class)
                       ((cl-typep (car spec)
@@ -1286,7 +1309,8 @@ commands are aliases for."
             ((guard (eq (car-safe val) '\,))
              (use key (cadr val)))
             ((guard (or (symbolp val)
-                        (and (listp val) (not (eq (car val) 'lambda)))))
+                        (and (listp val)
+                             (not (memq (car val) (list 'lambda (intern "")))))))
              (use key (macroexp-quote val)))
             (_ (use key val)))))
       (when spec
@@ -1295,7 +1319,7 @@ commands are aliases for."
                   (shortarg (plist-get args :shortarg)))
         (use :key shortarg)))
     (list 'list
-          (or level transient--default-child-level)
+          level
           (macroexp-quote (or class 'transient-suffix))
           (cons 'list args))))
 
@@ -1530,6 +1554,21 @@ See info node `(transient)Modifying Existing Transients'."
 (defun transient--nthcdr (n list)
   (nthcdr (if (< n 0) (- (length list) (abs n)) n) list))
 
+(defun transient-set-default-level (command level)
+  "Set the default level of suffix COMMAND to LEVEL.
+
+The default level is shadowed if the binding of the suffix in a
+prefix menu specifies a level, and also if the user changes the
+level of such a binding.
+
+The default level can only be set for commands that were defined
+using `transient-define-suffix', `transient-define-infix' or
+`transient-define-argument'."
+  (if-let ((proto (transient--suffix-prototype command)))
+      (oset proto level level)
+    (user-error "Cannot set level for `%s'; no prototype object exists"
+                command)))
+
 ;;; Variables
 
 (defvar transient-current-prefix nil
@@ -2216,7 +2255,8 @@ value.  Otherwise return CHILDREN as is.")
     (string  (list spec))))
 
 (defun transient--init-group (levels spec parent)
-  (pcase-let ((`(,level ,class ,args ,children) (append spec nil)))
+  (pcase-let* ((`(,level ,class ,args ,children) (append spec nil))
+               (level (or level transient--default-child-level)))
     (and-let* (((transient--use-level-p level))
                (obj (apply class :parent parent :level level args))
                ((transient--use-suffix-p obj))
@@ -2233,9 +2273,12 @@ value.  Otherwise return CHILDREN as is.")
   (pcase-let* ((`(,level ,class ,args) spec)
                (cmd (plist-get args :command))
                (key (transient--kbd (plist-get args :key)))
+               (proto (and cmd (transient--suffix-prototype cmd)))
                (level (or (alist-get (cons cmd key) levels nil nil #'equal)
                           (alist-get cmd levels)
-                          level)))
+                          level
+                          (and proto (oref proto level))
+                          transient--default-child-level)))
     (let ((fn (and (symbolp cmd)
                    (symbol-function cmd))))
       (when (autoloadp fn)
@@ -2246,7 +2289,7 @@ value.  Otherwise return CHILDREN as is.")
                      (apply class :parent parent :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))))
+                   (if proto
                        (apply #'clone proto :level level args)
                      (apply class :command cmd :parent parent :level level
                             args)))))
@@ -2436,6 +2479,8 @@ value.  Otherwise return CHILDREN as is.")
   (setq transient--redisplay-map nil)
   (setq transient--redisplay-key nil)
   (setq transient--helpp nil)
+  (unless (eq transient--docsp 'permanent)
+    (setq transient--docsp nil))
   (setq transient--editp nil)
   (setq transient--prefix nil)
   (setq transient--layout nil)
@@ -2563,7 +2608,13 @@ value.  Otherwise return CHILDREN as is.")
              (let ((abort t))
                (unwind-protect
                    (prog1 (let ((debugger #'transient--exit-and-debug))
-                            (advice-eval-interactive-spec spec))
+                            (if-let* ((obj (transient-suffix-object suffix))
+                                      (grp (oref obj parent))
+                                      (adv (or (oref obj advice*)
+                                               (oref grp advice*))))
+                                (funcall
+                                 adv #'advice-eval-interactive-spec spec)
+                              (advice-eval-interactive-spec spec)))
                      (setq abort nil))
                  (when abort
                    (when-let* ((unwind (oref prefix unwind-suffix)))
@@ -2573,7 +2624,14 @@ value.  Otherwise return CHILDREN as is.")
                    (oset prefix unwind-suffix nil))))))
           (unwind-protect
               (let ((debugger #'transient--exit-and-debug))
-                (apply fn args))
+                (if-let* ((obj (transient-suffix-object suffix))
+                          (grp (oref obj parent))
+                          (adv (or (oref obj advice)
+                                   (oref grp advice)
+                                   (oref obj advice*)
+                                   (oref grp advice*))))
+                    (apply adv fn args)
+                  (apply fn args)))
             (when-let* ((unwind (oref prefix unwind-suffix)))
               (transient--debug 'unwind-command)
               (funcall unwind suffix))
@@ -3212,12 +3270,21 @@ For example:
   (interactive)
   (setq transient-show-common-commands (not transient-show-common-commands)))
 
-(transient-define-suffix transient-toggle-docstrings ()
+(transient-define-suffix transient-toggle-docstrings (&optional permanent)
   "Toggle whether to show docstrings instead of suffix descriptions.
-To make this available in all menus, bind it in `transient-map'."
+
+By default this is only enabled temporarily for the current transient
+menu invocation.  With a prefix argument, enable this until explicitly
+disabled again.
+
+Infix arguments are not affected by this, because otherwise many menus
+would likely become unreadable.  To make this command available in all
+menus, bind it in `transient-map'.  `transient-show-docstring-format'
+controls how the docstrings are displayed and whether descriptions are
+also displayed."
   :transient t
-  (interactive)
-  (setq transient--docsp (not transient--docsp)))
+  (interactive (list current-prefix-arg))
+  (setq transient--docsp (if permanent 'permanent (not transient--docsp))))
 
 (defun transient-toggle-debug ()
   "Toggle debugging statements for transient commands."
@@ -3788,37 +3855,48 @@ a default implementation, which is a noop.")
 
 ;;;; Get
 
-(defun transient-scope (&optional prefixes)
+(defun transient-scope (&optional prefixes classes)
   "Return the scope of the active or current transient prefix command.
 
-If optional PREFIXES is nil, return the scope of the prefix currently
-being setup, making this variant useful, e.g., in `:if*' predicates.
-If no prefix is being setup, but the current command was invoked from
-some prefix, then return the scope of that.
-
-When this function is called from the body or `interactive' form of a
-suffix command, PREFIXES should be non-nil.
+If optional PREFIXES and CLASSES are both nil, return the scope of
+the prefix currently being setup, making this variation useful, e.g.,
+in `: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.  In this case try the following in order:
+commands.  If CLASSES is non-nil, it must be a prefix class or a list
+of such classes.  When this function is called from the body or the
+`interactive' form of a suffix command, PREFIXES and/or CLASSES should
+be non-nil.  If either is non-nil, try the following in order:
 
 - If the current suffix command was invoked from a prefix, which
-  appears in PREFIXES, then return the scope of that prefix.
+  appears in PREFIXES, return the scope of that prefix.
+
+- If the current suffix command was invoked from a prefix, and its
+  class derives from one of the CLASSES, return the scope of that
+  prefix.
+
+- If a prefix is being setup and it appears in PREFIXES, return its
+  scope.
 
-- If a prefix is being setup and it appears in PREFIXES, then return
-  its scope.
+- If a prefix is being setup and its class derives from one of the
+  CLASSES, return its scope.
 
-- Finally try to return the default scope of the first prefix in
+- Finally try to return the default scope of the first command in
   PREFIXES.  This only works if that slot is set in the respective
   class definition or using its `transient-init-scope' method.
 
 If no prefix matches, return nil."
-  (if prefixes
-      (let ((prefixes (ensure-list prefixes)))
-        (if-let* ((obj (or (and-let* ((obj transient-current-prefix))
-                             (and (memq (oref obj command) prefixes) obj))
-                           (and-let* ((obj transient--prefix))
-                             (and (memq (oref obj command) prefixes) obj)))))
+  (if (or prefixes classes)
+      (let ((prefixes (ensure-list prefixes))
+            (type (if (symbolp classes) classes (cons 'or classes))))
+        (if-let ((obj (cl-flet ((match (obj)
+                                  (and obj
+                                       (or (memq (oref obj command) prefixes)
+                                           (cl-typep obj type))
+                                       obj)))
+                        (or (match transient-current-prefix)
+                            (match transient--prefix)))))
             (oref obj scope)
           (and (get (car prefixes) 'transient--prefix)
                (oref (transient--init-prefix (car prefixes)) scope))))
@@ -4246,16 +4324,21 @@ face `transient-heading' to the complete string."
 If the result is nil, then use \"(BUG: no description)\" as the
 description.  If the OBJ's `key' is currently unreachable, then
 apply the face `transient-unreachable' to the complete string."
-  (let ((desc (if-let* ((transient--docsp)
-                        (cmd (oref obj command))
-                        (doc (ignore-errors (documentation cmd)))
-                        ((not (equal doc (documentation
-                                          'transient--default-infix-command)))))
-                  (substring doc 0 (string-match "\\.?\n" doc))
-                (or (cl-call-next-method obj)
-                    (and (slot-boundp transient--prefix 'suffix-description)
-                         (funcall (oref transient--prefix suffix-description)
-                                  obj))))))
+  (let ((desc (or (cl-call-next-method obj)
+                  (and (slot-boundp transient--prefix 'suffix-description)
+                       (funcall (oref transient--prefix suffix-description)
+                                obj)))))
+    (when-let* ((transient--docsp)
+                (cmd (oref obj command))
+                ((not (memq 'transient--default-infix-command
+                            (function-alias-p cmd))))
+                (docstr (ignore-errors (documentation cmd)))
+                (docstr (string-trim
+                         (substring docstr 0 (string-match "\\.?\n" docstr))))
+                ((not (equal docstr ""))))
+      (setq desc (format-spec transient-show-docstring-format
+                              `((?c . ,desc)
+                                (?s . ,docstr)))))
     (if desc
         (when-let* ((face (transient--get-face obj 'face)))
           (setq desc (transient--add-face desc face t)))
@@ -4567,34 +4650,44 @@ Select the help window, and make the help buffer current and return it."
     (insert "\n"))
   (when transient--helpp
     (insert
-     (format (propertize "\
+     (format
+      (propertize "\
 Type a %s to show help for that suffix command, or %s to show manual.
 Type %s to exit help.\n"
-                         'face 'transient-heading)
-             (propertize "<KEY>" 'face 'transient-key)
-             (propertize "?"     'face 'transient-key)
-             (propertize "C-g"   'face 'transient-key))))
+                  'face 'transient-heading)
+      (propertize "<KEY>" 'face 'transient-key)
+      (propertize "?"     'face 'transient-key)
+      (propertize "C-g"   'face 'transient-key))))
   (when transient--editp
     (unless transient--helpp
       (insert
-       (format (propertize "\
-Type a %s to set level for that suffix command.
-Type %s to set what levels are available for this prefix command.\n"
-                           'face 'transient-heading)
-               (propertize "<KEY>" 'face 'transient-key)
-               (propertize "C-x l" 'face 'transient-key))))
+       (format
+        (propertize "\
+Type %s and then %s to put the respective suffix command on level %s.
+Type %s and then %s to display suffixes up to level %s in this menu.
+Type %s and then %s to describe the respective suffix command.\n"
+                    'face 'transient-heading)
+        (propertize "<KEY>" 'face 'transient-key)
+        (propertize "<N>"   'face 'transient-key)
+        (propertize " N "   'face 'transient-enabled-suffix)
+        (propertize "C-x l" 'face 'transient-key)
+        (propertize "<N>"   'face 'transient-key)
+        (propertize " N "   'face 'transient-enabled-suffix)
+        (propertize "C-h"   'face 'transient-key)
+        (propertize "<KEY>" 'face 'transient-key))))
     (with-slots (level) transient--prefix
       (insert
-       (format (propertize "
-Suffixes on levels %s are available.
-Suffixes on levels %s and %s are unavailable.\n"
-                           'face 'transient-heading)
-               (propertize (format "1-%s" level)
-                           'face 'transient-enabled-suffix)
-               (propertize " 0 "
-                           'face 'transient-disabled-suffix)
-               (propertize (format ">=%s" (1+ level))
-                           'face 'transient-disabled-suffix))))))
+       (format
+        (propertize "
+The current level of this menu is %s, so
+  commands on levels %s are displayed, and
+  commands on levels %s and %s are not displayed.\n"
+                    'face 'transient-heading)
+        (propertize (format " %s " level)    'face 'transient-enabled-suffix)
+        (propertize (format " 1..%s " level) 'face 'transient-enabled-suffix)
+        (propertize (format " >= %s " (1+ level))
+                    'face 'transient-disabled-suffix)
+        (propertize " 0 " 'face 'transient-disabled-suffix))))))
 
 (cl-defgeneric transient-show-summary (obj &optional return)
   "Show brief summary about the command at point in the echo area.