[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 58df9e272b9: Update to Transient v0.8.3-2-gf0478b29
From: |
Jonas Bernoulli |
Subject: |
master 58df9e272b9: Update to Transient v0.8.3-2-gf0478b29 |
Date: |
Fri, 3 Jan 2025 13:51:43 -0500 (EST) |
branch: master
commit 58df9e272b9a13f954990af9da66c11e5d141a05
Author: Jonas Bernoulli <jonas@bernoul.li>
Commit: Jonas Bernoulli <jonas@bernoul.li>
Update to Transient v0.8.3-2-gf0478b29
---
doc/misc/transient.texi | 713 +++++++++++++++++++------
lisp/transient.el | 1315 ++++++++++++++++++++++++++++++-----------------
2 files changed, 1397 insertions(+), 631 deletions(-)
diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi
index 702832a3014..2f2e4cf7edd 100644
--- a/doc/misc/transient.texi
+++ b/doc/misc/transient.texi
@@ -31,7 +31,7 @@ General Public License for more details.
@finalout
@titlepage
@title Transient User and Developer Manual
-@subtitle for version 0.7.4
+@subtitle for version 0.8.3
@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.7.4.
+This manual is for Transient version 0.8.3.
@insertcopying
@end ifnottex
@@ -93,31 +93,22 @@ Defining New Commands
* Binding Suffix and Infix Commands::
* Defining Suffix and Infix Commands::
* Using Infix Arguments::
+* Current Suffix Command::
+* Current Prefix Command::
* Transient State::
-Binding Suffix and Infix Commands
-
-* Group Specifications::
-* Suffix Specifications::
-
-
Classes and Methods
* Group Classes::
* Group Methods::
* Prefix Classes::
* Suffix Classes::
+* Prefix Methods::
* Suffix Methods::
* Prefix Slots::
* Suffix Slots::
* Predicate Slots::
-Suffix Methods
-
-* Suffix Value Methods::
-* Suffix Format Methods::
-
-
@end detailmenu
@end menu
@@ -224,12 +215,13 @@ A transient prefix command is invoked like any other
command by
pressing the key that is bound to that command. The main difference
to other commands is that a transient prefix command activates a
transient keymap, which temporarily binds the transient's infix and
-suffix commands. Bindings from other keymaps may, or may not, be
-disabled while the transient state is in effect.
+suffix commands, and that those bindings are displayed in a transient
+menu, displayed in a popup buffer. Bindings from other keymaps may,
+or may not, be disabled while the transient state is in effect.
There are two kinds of commands that are available after invoking a
transient prefix command; infix and suffix commands. Infix commands
-set some value (which is then shown in a popup buffer), without
+set some value (which is then shown in the popup buffer), without
leaving the transient. Suffix commands, on the other hand, usually
quit the transient and they may use the values set by the infix
commands, i.e., the infix @strong{arguments}.
@@ -349,9 +341,6 @@ Emacs session by typing @kbd{C-x t} while a transient is
active.
The other common commands are described in either the previous or in
one of the following sections.
-Some of Transient's key bindings differ from the respective bindings
-of Magit-Popup; see @ref{FAQ} for more information.
-
@node Saving Values
@section Saving Values
@@ -383,13 +372,13 @@ session.
@item @kbd{C-x C-s} (@code{transient-save})
@kindex C-x C-s
@findex transient-save
-Save the value of the active transient persistently across Emacs
-sessions.
+This command saves the value of the active transient persistently
+across Emacs sessions.
@item @kbd{C-x C-k} (@code{transient-reset})
@kindex C-x C-k
@findex transient-reset
-Clear the set and saved values of the active transient.
+This command clears the set and saved values of the active transient.
@end table
@defopt transient-values-file
@@ -403,8 +392,8 @@ transients between Emacs sessions.
@cindex value history
Every time the user invokes a suffix command the transient's current
-value is saved to its history. These values can be cycled through the
-same way one can cycle through the history of commands that read
+value is saved to its history. These values can be cycled through,
+the same way one can cycle through the history of commands that read
user-input in the minibuffer.
@table @asis
@@ -425,11 +414,11 @@ This command switches to the next value used for the
active
transient.
@end table
-In addition to the transient-wide history, Transient of course
-supports per-infix history. When an infix reads user-input using the
-minibuffer, the user can use the regular minibuffer history commands
-to cycle through previously used values. Usually the same keys as
-those mentioned above are bound to those commands.
+In addition to the transient-wide history, infixes can have their own
+history. When an infix reads user-input using the minibuffer, the
+user can use the regular minibuffer history commands to cycle through
+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
@@ -437,6 +426,11 @@ read the same kind of value to also use the same history
key
Both kinds of history are saved to a file when Emacs is exited.
+@defopt transient-save-history
+This option controls whether the history of transient commands is
+saved when exiting Emacs.
+@end defopt
+
@defopt transient-history-file
This option names the file that is used to persist the history of
transients and their infixes between Emacs sessions.
@@ -580,6 +574,23 @@ lines. If @var{ARG} is @code{nil}, then it scrolls near
full screen. This
is a wrapper around @code{scroll-down-command} (which see).
@end deffn
+The following commands are not available by default. If you would
+like to use them for all menus, bind them in @code{transient-map}.
+
+@deffn Command transient-copy-menu-text
+This command copies the contents of the menu buffer to the kill
+ring.
+@end deffn
+
+@deffn Command transient-toggle-docstrings
+This command toggle between showing suffix descriptions in the menu
+(as usual) or showing the first lines of the respective docstrings
+in their place. For commands that do not have a docstring, always
+display the suffix description. Because there likely isn't enough
+room to display multiple docstrings side-by-side, a single column
+is used when displaying docstrings.
+@end deffn
+
@node Configuration
@section Configuration
@@ -616,14 +627,69 @@ the absolute value).
@end itemize
@end defopt
+@defopt transient-show-common-commands
+This option controls whether shared suffix commands are shown
+alongside the transient-specific infix and suffix commands. By
+default, the shared commands are not shown to avoid overwhelming
+the user with too many options.
+
+While a transient is active, pressing @kbd{C-x} always shows the common
+commands. The value of this option can be changed for the current
+Emacs session by typing @kbd{C-x t} while a transient is active.
+@end defopt
+
+@defopt transient-show-during-minibuffer-read
+This option controls whether the transient menu continues to be
+displayed while the minibuffer is used to read user input.
+
+This is only relevant to commands that do not close the menu, such as
+commands that set infix arguments. If a command exits the menu, and
+uses the minibuffer, then the menu is always closed before the
+minibuffer is entered, irrespective of the value of this option.
+
+When @code{nil} (the default), hide the menu while the minibuffer is in use.
+When @code{t}, keep showing the menu, but allow for the menu window to be
+resized, to ensure that completion candidates can be displayed.
+
+When @code{fixed}, keep showing the menu and prevent it from being resized,
+which may make it impossible to display the completion candidates. If
+that ever happens for you, consider using @code{t} or an integer, as described
+below.
+
+If the value is @code{fixed} and the menu window uses the full height of its
+frame, then the former is ignored and resizing is allowed anyway. This
+is necessary because individual menus may use unusual display actions
+different from what @code{transient-display-buffer-action} specifies (likely
+to display that menu in a side-window).
+
+When using a third-party mode, which automatically resizes windows
+(e.g., by calling @code{balance-windows} on @code{post-command-hook}), then
+@code{fixed} (or @code{nil}) is likely a better choice than @code{t}.
+
+The value can also be an integer, in which case the behavior depends on
+whether at least that many lines are left to display windows other than
+the menu window. If that is the case, display the menu and preserve the
+size of that window. Otherwise, allow resizing the menu window if the
+number is positive, or hide the menu if it is negative.
+@end defopt
+
+@defopt transient-read-with-initial-input
+This option controls whether the last history element is used as the
+initial minibuffer input when reading the value of an infix argument
+from the user. If @code{nil}, there is no initial input and the first
+element has to be accessed the same way as the older elements.
+@end defopt
+
@defopt transient-enable-popup-navigation
This option controls whether navigation commands are enabled in the
-transient popup buffer.
+transient popup buffer. If the value is @code{verbose} (the default),
+brief documentation about the command under point is additionally
+show in the echo area.
While a transient is active the transient popup buffer is not the
current buffer, making it necessary to use dedicated commands to act
-on that buffer itself. This is disabled by default. If this option
-is non-@code{nil}, then the following features are available:
+on that buffer itself. If this option is non-@code{nil}, then the
+following features are available:
@itemize
@item
@@ -631,12 +697,17 @@ is non-@code{nil}, then the following features are
available:
@item
@kbd{@key{DOWN}} moves the cursor to the next suffix.
@item
-@kbd{@key{RET}} invokes the suffix the cursor is on.
+@kbd{M-@key{RET}} invokes the suffix the cursor is on.
@item
@kbd{mouse-1} invokes the clicked on suffix.
@item
@kbd{C-s} and @kbd{C-r} start isearch in the popup buffer.
@end itemize
+
+By default @kbd{M-@key{RET}} is bound to @code{transient-push-button}, instead
of
+@kbd{@key{RET}}, because if a transient allows the invocation of non-suffixes,
+then it is likely, that you would want @kbd{@key{RET}} to do what it would do
+if no transient were active."
@end defopt
@defopt transient-display-buffer-action
@@ -654,16 +725,14 @@ The default is:
@lisp
(display-buffer-in-side-window
- (side . bottom)
- (inhibit-same-window . t)
- (window-parameters (no-other-window . t)))
+ (side . bottom)
+ (dedicated . t)
+ (inhibit-same-window . t))
@end lisp
This displays the window at the bottom of the selected frame.
-Another useful @var{FUNCTION} is @code{display-buffer-below-selected}, which
-is what @code{magit-popup} used by default. For more alternatives see
-@ref{Buffer Display Action Functions,,,elisp,}, and see @ref{Buffer Display
-Action Alists,,,elisp,}.
+For alternatives see @ref{Buffer Display Action Functions,,,elisp,},
+and @xref{Buffer Display Action Alists,,,elisp,}.
Note that the buffer that was current before the transient buffer
is shown should remain the current buffer. Many suffix commands
@@ -679,6 +748,9 @@ then that unfortunately changes which buffer is current.
If you change the value of this option, then you might also
want to change the value of @code{transient-mode-line-format}.
+
+This user option may be overridden if @code{:display-action} is passed
+when creating a new prefix with @code{transient-define-prefix}.
@end defopt
@anchor{Accessibility Options}
@@ -702,21 +774,22 @@ If @code{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 @code{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 @code{line} and
-numbers are synonyms for @code{nil}.
+If @code{line} (the default) or a natural number, then the buffer has no
+mode-line, but a line 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 @code{line} and numbers are synonyms for
@code{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 @code{transient-key-noop} (if non-suffix are disallowed),
+color of @code{transient-key-noop} (if non-suffixes are disallowed),
@code{transient-key-stay} (if allowed and transient stays active), or
@code{transient-key-exit} (if allowed and they exit the transient) is
used to draw the line.
-Otherwise this can be any mode-line format. @xref{Mode Line
-Format,,,elisp,}, for details.
+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. See @ref{Mode Line
Format,,,elisp,}, for details.
@end defopt
@defopt transient-semantic-coloring
@@ -772,18 +845,6 @@ optimized for lisp.
@end lisp
@end defopt
-@defopt transient-read-with-initial-input
-This option controls whether the last history element is used as the
-initial minibuffer input when reading the value of an infix argument
-from the user. If @code{nil}, there is no initial input and the first
-element has to be accessed the same way as the older elements.
-@end defopt
-
-@defopt transient-hide-during-minibuffer-read
-This option controls whether the transient buffer is hidden while
-user input is being read in the minibuffer.
-@end defopt
-
@defopt transient-align-variable-pitch
This option controls whether columns are aligned pixel-wise in the
popup buffer.
@@ -836,6 +897,18 @@ suffixes that won't be available to users without them
making the
same customization.
@end defopt
+@anchor{Hook Variables}
+@subheading Hook Variables
+
+@defvar transient-exit-hook
+This hook is run after a transient is exited.
+@end defvar
+
+@defvar transient-setup-buffer-hook
+This hook is run when the transient buffer is being setup.
+That buffer is current and empty when this hook is runs.
+@end defvar
+
@node Modifying Existing Transients
@chapter Modifying Existing Transients
@@ -944,6 +1017,8 @@ signal an error.
* Binding Suffix and Infix Commands::
* Defining Suffix and Infix Commands::
* Using Infix Arguments::
+* Current Suffix Command::
+* Current Prefix Command::
* Transient State::
@end menu
@@ -987,7 +1062,7 @@ arguments have been set using a command such as
@code{universal-argument}.
@cindex command dispatchers
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
+in a popup buffer, which can be thought of as a ``menu''. 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 @code{magit-dispatch} (on @kbd{C-x M-g}) command is an example of using
@@ -1117,12 +1192,7 @@ 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}.
-@menu
-* Group Specifications::
-* Suffix Specifications::
-@end menu
-
-@node Group Specifications
+@anchor{Group Specifications}
@subsection Group Specifications
@cindex group specifications
@@ -1170,9 +1240,9 @@ consistency, or @var{DESCRIPTION} otherwise, because it
looks better.
Likewise @code{:level} is equivalent to @var{LEVEL}.
@item
-Other important keywords include the @code{:if...} keywords. These
-keywords control whether the group is available in a certain
-situation.
+Other important keywords include the @code{:if...} and @code{:inapt-if...}
+keywords. These keywords control whether the group is available
+in a certain situation.
For example, one group of the @code{magit-rebase} transient uses @code{:if
magit-rebase-in-progress-p}, which contains the suffixes that are
@@ -1273,7 +1343,7 @@ using @code{,}. This feature is experimental and should
be avoided.
The form of suffix specifications is documented in the next node.
-@node Suffix Specifications
+@anchor{Suffix Specifications}
@subsection Suffix Specifications
@cindex suffix specifications
@@ -1474,8 +1544,17 @@ command was not invoked from @var{PREFIX}, then it
returns the set, saved
or default value for @var{PREFIX}.
@end defun
+@defun transient-get-value
+This function returns the value of the current prefix.
+
+This is mostly intended for internal use, but may also be of use
+in @code{transient-set-value} and @code{transient-save-value} methods. Unlike
+@code{transient-args}, this does not include the values of suffixes whose
+@code{unsavable} slot is non-@code{nil}.
+@end defun
+
@defun transient-arg-value arg args
-This function return the value of @var{ARG} as it appears in @var{ARGS}.
+This function returns the value of @var{ARG} as it appears in @var{ARGS}.
For a switch a boolean is returned. For an option the value is
returned as a string, using the empty string for the empty value,
@@ -1489,6 +1568,53 @@ 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 Current Suffix Command
+@section Current Suffix Command
+
+@defun transient-suffix-object command
+This function returns the object associated with the current suffix
+command.
+
+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, which
+are usually aliases of @code{transient--default-infix-command}, which
+is defined like this:
+
+@lisp
+(defun transient--default-infix-command ()
+ (interactive)
+ (let ((obj (transient-suffix-object)))
+ (transient-infix-set obj (transient-infix-read obj)))
+ (transient--show))
+@end lisp
+
+(User input is read outside of @code{interactive} to prevent the
+command from being added to @code{command-history}.)
+
+Such commands need to be able to access their associated object
+to guide how @code{transient-infix-read} reads the new value and to
+store the read value. Other suffix commands (including non-infix
+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 @code{nil} may be returned,
+if the command was not defined using one of the macros intended
+to define such commands.
+
+The optional argument COMMAND is intended for internal use. If
+you are contemplating using it in your own code, then you should
+probably use this instead:
+
+@lisp
+(get COMMAND 'transient--suffix)
+@end lisp
+@end defun
+
@defvar transient-current-suffixes
The suffixes of the transient from which this suffix command was
invoked. This is a list of objects. Usually it is sufficient to
@@ -1497,21 +1623,49 @@ values. In complex cases it might be necessary to use
this variable
instead, i.e., if you need access to information beside the value.
@end defvar
-@defvar transient-current-command
-The transient from which this suffix command was invoked. The
-returned value is a symbol, the transient prefix command.
-@end defvar
+@node Current Prefix Command
+@section Current Prefix Command
+
+@defun transient-prefix-object
+This function returns the current prefix as an object.
+
+While a transient is being setup or refreshed (which involves
+preparing its suffixes) the variable @code{transient--prefix} can be
+used to access the prefix object. Thus this is what has to be
+used in suffix methods such as @code{transient-format-description},
+and in object-specific functions that are stored in suffix slots
+such as @code{description}.
+
+When a suffix command is invoked (i.e., in its @code{interactive} form
+and function body) then the variable @code{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.
+@end defun
@defvar transient-current-prefix
-The transient from which this suffix command was invoked. The
-returned value is a @code{transient-prefix} object, which holds information
-associated with the transient prefix command.
+The transient from which this suffix command was invoked. The value
+is a @code{transient-prefix} object, which holds information associated
+with the transient prefix command.
@end defvar
-@defvar transient-active-prefix
-This function returns the active transient object. Return @code{nil} if
-there is no active transient, if the transient buffer isn't shown,
-and while the active transient is suspended (e.g., while the
+@defvar transient-current-command
+The transient from which this suffix command was invoked. The value
+is a symbol, the transient prefix command.
+@end defvar
+
+@defun transient-active-prefix &optional prefixes
+This function returns the active transient object. It returns @code{nil}
+if there is no active transient, if the transient buffer isn't
+shown, and while the active transient is suspended (e.g., while the
minibuffer is in use).
Unlike @code{transient-current-prefix}, which is only ever non-@code{nil} in
code
@@ -1519,10 +1673,10 @@ that is run directly by a command that is invoked while
a transient
is current, this function is also suitable for use in asynchronous
code, such as timers and callbacks (this function's main use-case).
-If optional PREFIXES is non-@code{nil}, it must be a list of prefix command
-symbols, in which case the active transient object is only returned
-if it matches one of the PREFIXES."
-@end defvar
+If optional PREFIXES is non-@code{nil}, it must be a prefix command symbol
+or a list of symbols, in which case the active transient object is
+only returned if it matches one of the PREFIXES@.
+@end defun
@node Transient State
@section Transient State
@@ -1769,14 +1923,12 @@ This is used when the user pressed @kbd{C-z}.
@cindex classes and methods
Transient uses classes and generic functions to make it possible to
-define new types of suffix commands that are similar to existing
-types, but behave differently in some aspects. It does the same for
-groups and prefix commands, though at least for prefix commands that
-@strong{currently} appears to be less important.
+define new types of suffix and prefix commands, which are similar to
+existing types, but behave differently in some respects.
Every prefix, infix and suffix command is associated with an object,
-which holds information that controls certain aspects of its behavior.
-This happens in two ways.
+which holds information, which controls certain aspects of its
+behavior. This happens in two ways.
@itemize
@item
@@ -1786,9 +1938,9 @@ that have to be done differently depending on what type
of command
it acts on.
That in turn makes it possible for third-parties to add new types
-without having to convince the maintainer of Transient that that new
-type is important enough to justify adding a special case to a dozen
-or so functions.
+without having to convince the maintainer of Transient, that that
+new type is important enough to justify adding a special case to a
+dozen or so functions.
@item
Associating a command with an object makes it possible to easily
@@ -1814,6 +1966,7 @@ differ even between different commands of the same type.
* Group Methods::
* Prefix Classes::
* Suffix Classes::
+* Prefix Methods::
* Suffix Methods::
* Prefix Slots::
* Suffix Slots::
@@ -1896,25 +2049,17 @@ The contents of that buffer are later inserted into the
popup buffer.
Functions that are called by this function may need to operate in
the buffer from which the transient was called. To do so they can
-temporarily make the @code{transient--source-buffer} the current buffer.
+temporarily make the @code{transient--shadowed-buffer} the current buffer.
@end defun
@node Prefix Classes
@section Prefix Classes
-Currently the @code{transient-prefix} class is being used for all prefix
-commands and there is only a single generic function that can be
-specialized based on the class of a prefix command.
-
-@defun transient--history-init obj
-This generic function is called while setting up the transient and
-is responsible for initializing the @code{history} slot. This is the
-transient-wide history; many individual infixes also have a history
-of their own.
-
-The default (and currently only) method extracts the value from the
-global variable @code{transient-history}.
-@end defun
+Transient itself provides a single class for prefix commands,
+@code{transient-prefix}, but package authors may wish to define specialized
+classes. Doing so makes it possible to change the behavior of the set
+of prefix commands that use that class, by implementing specialized
+methods for certain generic functions (see @ref{Prefix Methods}).
A transient prefix command's object is stored in the @code{transient--prefix}
property of the command symbol. While a transient is active, a clone
@@ -1972,22 +2117,43 @@ Classes used for infix commands that represent
variables should
derived from the abstract @code{transient-variable} class.
@item
-The @code{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:
+The @code{transient-information} and @code{transient-information*} classes are
+special in that suffixes that use these 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 these form:
@lisp
([LEVEL] :info DESCRIPTION [KEYWORD VALUE]...)
+([LEVEL] :info* DESCRIPTION [KEYWORD VALUE]...)
@end lisp
-The @code{:info} keyword argument replaces the @code{:description} keyword
used for
-other suffix classes. Other keyword arguments that you might want to
-set, include @code{:face}, predicate keywords (such as @code{:if}), and
@code{:format}.
-By default the value of @code{:format} includes @code{%k}, which for this
class is
-replaced with the empty string or spaces, if keys are being padded in
-the containing group.
+The @code{:info} and @code{:info*} keyword arguments replaces the
@code{:description}
+keyword used for other suffix classes. Other keyword arguments that
+you might want to set, include @code{:face}, predicate keywords (such as
+@code{:if} and @code{:inapt-if}), and @code{:format}. By default the value of
@code{:format}
+includes @code{%k}, which for this class is replaced with the empty string
+or spaces, if keys are being padded in the containing group.
+
+The only difference between these two classes is that @code{:info*} aligns
+its description with the descriptions of suffix commands, while for
+@code{:info} the description bleeds into the area where suffixes display
+their key bindings.
+
+@item
+The @code{transient-lisp-variable} class can be used to show and change the
+value of lisp variables. This class is not fully featured yet and
+it is somewhat likely that future improvements won't be fully
+backward compatible.
+
+@item
+The @code{transient-describe-target} class is used by the command
+@code{transient-describe}.
+
+@item
+The @code{transient-value-preset} class is used to implement the command
+@code{transient-preset}, which activates a value preset.
@end itemize
Magit defines additional classes, which can serve as examples for the
@@ -1995,30 +2161,92 @@ fancy things you can do without modifying Transient.
Some of these
classes will likely get generalized and added to Transient. For now
they are very much subject to change and not documented.
+@node Prefix Methods
+@section Prefix Methods
+
+To get information about the methods implementing these generic
+functions use @code{describe-function}.
+
+@defun transient-init-value obj
+This generic function sets the initial value of the object @var{OBJ}.
+Methods exist for both prefix and suffix objects.
+
+The default method for prefix objects sets the value of OBJ's @code{value}
+slot to the set, saved or default value. The value that is set for
+the current session is preferred over the saved value, which is
+preferred over the default value.
+
+The default value is determined using the generic function
+@code{transient-default-value}. If you need to change how the value for a
+prefix class is determined, its usually sufficient to implement a
+method for that function.
+@end defun
+
+@defun transient-default-value obj
+This generic function returns the default value of the object @var{OBJ}.
+Methods exist for both prefix and suffix objects.
+
+The default method for prefix objects returns the value of the
+@code{default-value} slot if that is bound and not a function. If it is a
+function, that is called to get the value. If the slot is unbound,
+@code{nil} is returned.
+@end defun
+
+@defun transient-prefix-value obj
+This generic function returns the value of the prefix object @var{OBJ}.
+The respective generic function for infix and suffix objects is
+named @code{transient-infix-value}.
+@end defun
+
+@defun transient-init-scope obj
+This generic function sets the scope of the object @var{OBJ}. Methods
+exist for both prefix and suffix objects.
+
+This function is called for all prefix and suffix commands, but
+unless a concrete method is implemented this falls through to
+the default implementation, which is a noop.
+@end defun
+
+@code{transient-set-value}, @code{transient-save-value},
@code{transient-reset-value},
+@code{transient--history-key}, @code{transient--history-push} and
+@code{transient--history-init} are other generic functions dealing with the
+value of prefix objects. See their doc-strings for more information.
+
+@code{transient-show-help} is another generic function implemented for prefix
+commands. The default method effectively describes the command using
+@code{describe-function}.
+
@node Suffix Methods
@section Suffix Methods
To get information about the methods implementing these generic
functions use @code{describe-function}.
-@menu
-* Suffix Value Methods::
-* Suffix Format Methods::
-@end menu
-
-@node Suffix Value Methods
+@anchor{Suffix Value Methods}
@subsection Suffix Value Methods
@defun transient-init-value obj
This generic function sets the initial value of the object @var{OBJ}.
+Methods exist for both prefix and suffix objects.
+
+For @code{transient-argument} objects this function handles setting the
+value by itself.
-This function is called for all suffix commands, but unless a
-concrete method is implemented this falls through to the default
-implementation, which is a noop. In other words this usually
-only does something for infix commands, but note that this is
-not implemented for the abstract class @code{transient-infix}, so if
-your class derives from that directly, then you must implement
-a method.
+For other @code{transient-suffix} objects (including @code{transient-infix}
+objects), this calls @code{transient-default-value} and uses the value
+returned by that, unless it is the special value @code{eieio--unbound},
+which indicates that there is no default value. Since that is what
+the default method for @code{transient-suffix} objects does, both of these
+functions effectively are noops for these classes.
+
+If you implement a class that derives from @code{transient-infix} directly,
+then you must implement a dedicated method for this function and/or
+@code{transient-default-value}.
+@end defun
+
+@defun transient-default-value obj
+This generic function returns the default value of the object @var{OBJ}.
+Methods exist for both prefix and suffix objects.
@end defun
@defun transient-infix-read obj
@@ -2062,7 +2290,8 @@ Usually only infixes have a value, but see the method for
@end defun
@defun transient-init-scope obj
-This generic function sets the scope of the suffix object @var{OBJ}.
+This generic function sets the scope of the object @var{OBJ}. Methods
+exist for both prefix and suffix objects.
The scope is actually a property of the transient prefix, not of
individual suffixes. However it is possible to invoke a suffix
@@ -2070,12 +2299,12 @@ command directly instead of from a transient. In that
case, if
the suffix expects a scope, then it has to determine that itself
and store it in its @code{scope} slot.
-This function is called for all suffix commands, but unless a
-concrete method is implemented this falls through to the default
-implementation, which is a noop.
+This function is called for all prefix and suffix commands, but
+unless a concrete method is implemented, this falls through to
+the default implementation, which is a noop.
@end defun
-@node Suffix Format Methods
+@anchor{Suffix Format Methods}
@subsection Suffix Format Methods
@defun transient-format obj
@@ -2106,6 +2335,12 @@ the result.
Show help for the prefix, infix or suffix command represented by
@var{OBJ}.
+Regardless of OBJ's type, if its @code{show-help} slot is non-@code{nil}, that
+must be a function, which takes OBJ is its only argument. It must
+prepare, display and return a buffer, and select the window used to
+display it. The @code{transient-show-help-window} macro is intended for
+use in such functions.
+
For prefixes, show the info manual, if that is specified using the
@code{info-manual} slot. Otherwise, show the manpage if that is specified
using the @code{man-page} slot. Otherwise, show the command's
@@ -2117,35 +2352,54 @@ For infixes, show the manpage if that is specified.
Otherwise show
the command's documentation string.
@end defun
+@defmac transient-with-help-window &rest body
+Evaluate BODY, send output to @code{*Help*} buffer, and display it in a
+window. Select the help window, and make the help buffer current
+and return it.
+@end defmac
+
+@defun transient-show-summary obj &optional return
+This generic function shows or, if optional RETURN is non-@code{nil},
+returns a brief summary about the command at point or hovered with
+the mouse.
+
+This function is called when the mouse is moved over a command and
+(if the value of @code{transient-enable-popup-navigation} is @code{verbose})
when
+the user navigates to a command using the keyboard.
+
+If OBJ's @code{summary} slot is a string, that is used. If @code{summary} is a
+function, that is called with OBJ as the only argument and the
+returned string is used. If @code{summary} is or returns something other
+than a string or @code{nil}, no summary is shown. If @code{summary} is or
returns
+@code{nil}, the first line of the documentation string is used, if any.
+
+If RETURN is non-@code{nil}, this function returns the summary instead of
+showing it. This is used when a tooltip is needed.
+@end defun
+
@node Prefix Slots
@section Prefix Slots
+@anchor{Value and Scope}
+@subheading Value and Scope
+
@itemize
@item
-@code{show-help}, @code{man-page} or @code{info-manual} can be used to specify
the
-documentation for the prefix and its suffixes. The command
-@code{transient-help} uses the method @code{transient-show-help} (which see) to
-lookup and use these values.
+@code{default-value} The default value of the prefix. Use the keyword
+argument @code{:value} (sic) to set this slot in the definition of a
+prefix.
+
+@item
+@code{init-value} A function that is responsible for setting the object's
+value. If bound, then this is called with the object as the only
+argument. Usually this is not bound, in which case the object's
+primary @code{transient-init-value} method is called instead.
@item
@code{history-key} If multiple prefix commands should share a single value,
then this slot has to be set to the same value for all of them. You
probably don't want that.
-@item
-@code{transient-suffix} and @code{transient-non-suffix} 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}.
-
-@item
-@code{refresh-suffixes} Normally suffix objects and keymaps are only setup
-once, when the prefix is invoked. Setting this to @code{t}, causes them to
-be recreated after every command. This is useful when using @code{: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.
-
@item
@code{incompatible} A list of lists. Each sub-list specifies a set of
mutually exclusive arguments. Enabling one of these arguments
@@ -2162,8 +2416,77 @@ for example, @code{--option=one}.
secondary value, called a ``scope''. See @code{transient-define-prefix}.
@end itemize
-@anchor{Internal Prefix Slots}
-@subheading Internal Prefix Slots
+@anchor{Behavior}
+@subheading Behavior
+
+@itemize
+@item
+@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. See @ref{Transient State}.
+
+@item
+@code{refresh-suffixes} Normally suffix objects and keymaps are only setup
+once, when the prefix is invoked. Setting this to @code{t}, causes them to
+be recreated after every command. This is useful when using @code{: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.
+
+@item
+@code{environment} A function used to establish an environment while
+initializing, refreshing or redisplaying a transient prefix menu.
+This is useful to establish a cache, in case multiple suffixes
+require the same expensive work. The provided function is called
+with at least one argument, the function for which it establishes
+the environment. It must @code{funcall} that function with no arguments.
+During initialization the second argument is the prefix object
+being initialized. This slot is still experimental.
+@end itemize
+
+@anchor{Appearance}
+@subheading Appearance
+
+@itemize
+@item
+@code{display-action} determines how this prefix is displayed, overriding
+@code{transient-display-buffer-action}. It should have the same type.
+
+@item
+@code{mode-line-format} is this prefix's mode line format, overriding
+@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.
+
+@item
+@code{variable-pitch} controls whether alignment is done pixel-wise to
+account for use of variable-pitch characters, which is useful, e.g.,
+when using emoji.
+@end itemize
+
+@anchor{Documentation}
+@subheading Documentation
+
+@itemize
+@item
+@code{show-help}, @code{man-page} or @code{info-manual} can be used to specify
the
+documentation for the prefix and its suffixes. The command
+@code{transient-help} uses the function @code{transient-show-help} (which see)
to
+lookup and use these values.
+
+@item
+@code{suffix-description} can be used to specify a function which provides
+fallback descriptions for suffixes that lack a description. This
+is intended to be temporarily used when implementing of a new prefix
+command, at which time @code{transient-command-summary-or-name} is a useful
+value.
+@end itemize
+
+@anchor{Internal}
+@subheading Internal
These slots are mostly intended for internal use. They should not be
set in calls to @code{transient-define-prefix}.
@@ -2193,6 +2516,10 @@ which is guaranteed to return the up-to-date value.
@code{history} and @code{history-pos} are used to keep track of historic
values.
Unless you implement your own @code{transient-infix-read} method you should
not have to deal with these slots.
+
+@item
+@code{unwind-suffix} is used internally to ensure transient state is
+properly exited, even in case of an error.
@end itemize
@node Suffix Slots
@@ -2204,6 +2531,18 @@ documented in @ref{Predicate Slots}.
Also see @ref{Suffix Classes}.
+@anchor{Slots of @code{transient-child}}
+@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.
+
+@itemize
+@item
+@code{parent} The object for the parent group.
+@end itemize
+
@anchor{Slots of @code{transient-suffix}}
@subheading Slots of @code{transient-suffix}
@@ -2243,7 +2582,17 @@ the styling there. @code{face} is appended using
@code{add-face-text-property}.
@item
@code{show-help} A function used to display help for the suffix. If
unspecified, the prefix controls how help is displayed for its
-suffixes.
+suffixes. See also function @code{transient-show-help}.
+
+@item
+@code{summary} The summary displayed in the echo area, or as a tooltip.
+If this is @code{nil}, which it usually should be, the first line of the
+documentation string is used instead. See @code{transient-show-summary}
+for details.
+
+@item
+@code{definition} A command, which is used if the body is omitted when
+defining a command using @code{transient-define-suffix}.
@end itemize
@anchor{Slots of @code{transient-infix}}
@@ -2357,10 +2706,14 @@ E.g.,
@code{\\(--\\(topo\\|author-date\\|date\\)-order\\)}.
@node Predicate Slots
@section Predicate Slots
-Suffix and group objects share some predicate slots that control
-whether a group or suffix should be available depending on some state.
-Only one of these slots can be used at the same time. It is undefined
-what happens if you use more than one.
+Suffix and group objects share two sets of predicate slots that
+control whether a group or suffix should be available depending on
+some state. Only one slot from each set can be used at the same
+time. It is undefined which slot is honored if you use more than
+one.
+
+Predicates from the first group control whether the suffix is present
+in the menu at all.
@itemize
@item
@@ -2381,6 +2734,30 @@ what happens if you use more than one.
@code{if-not-derived} Enable if major-mode does not derive from value.
@end itemize
+Predicates from the second group control whether the suffix can be
+invoked. The suffix is shown in the menu regardless, but when it
+is considered "inapt", then it is grayed out to indicated that it
+currently cannot be invoked.
+
+@itemize
+@item
+@code{inapt-if} Inapt if predicate returns non-@code{nil}.
+@item
+@code{inapt-if-not} Inapt if predicate returns @code{nil}.
+@item
+@code{inapt-if-non-nil} Inapt if variable's value is non-@code{nil}.
+@item
+@code{inapt-if-nil} Inapt if variable's value is @code{nil}.
+@item
+@code{inapt-if-mode} Inapt if major-mode matches value.
+@item
+@code{inapt-if-not-mode} Inapt if major-mode does not match value.
+@item
+@code{inapt-if-derived} Inapt if major-mode derives from value.
+@item
+@code{inapt-if-not-derived} Inapt if major-mode does not derive from value.
+@end itemize
+
By default these predicates run when the prefix command is invoked,
but this can be changes, using the @code{refresh-suffixes} prefix slot.
See @ref{Prefix Slots}.
@@ -2398,7 +2775,9 @@ available depending on user preference.
@anchor{Can I control how the popup buffer is displayed?}
@appendixsec Can I control how the popup buffer is displayed?
-Yes, see @code{transient-display-buffer-action} in @ref{Configuration}.
+Yes, see @code{transient-display-buffer-action} in @ref{Configuration}. You
can
+also control how the popup buffer is displayed on a case-by-case basis
+by passing @code{:display-action} to @code{transient-define-prefix}.
@anchor{How can I copy text from the popup buffer?}
@appendixsec How can I copy text from the popup buffer?
@@ -2479,7 +2858,7 @@ potential prefix keys can be used for transient-specific
commands
particular is a prefix that I want to (and already do) use for Magit, and
also using that for a common command would prevent me from doing so.
-(Also see the next question.)
+(See also the next question.)
@anchor{Why does @kbd{q} not quit popups anymore?}
@appendixsec Why does @kbd{q} not quit popups anymore?
diff --git a/lisp/transient.el b/lisp/transient.el
index a75e44d07b9..bf64dd94e19 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -5,7 +5,7 @@
;; Author: Jonas Bernoulli <jonas@bernoul.li>
;; URL: https://github.com/magit/transient
;; Keywords: extensions
-;; Version: 0.7.4
+;; Version: 0.8.3
;; SPDX-License-Identifier: GPL-3.0-or-later
@@ -32,14 +32,15 @@
;;; Code:
-(defconst transient-version "0.7.2.2")
+(defconst transient-version "v0.8.3-2-gf0478b29-builtin")
(require 'cl-lib)
(require 'eieio)
(require 'edmacro)
(require 'format-spec)
-(require 'seq)
+(require 'pcase)
(require 'pp)
+(require 'seq)
(eval-when-compile (require 'subr-x))
@@ -50,6 +51,10 @@
(defvar Man-notify-method)
+(make-obsolete-variable 'transient-hide-during-minibuffer-read
+ "use `transient-show-during-minibuffer-read' instead."
+ "0.8.0")
+
(defmacro transient--with-emergency-exit (id &rest body)
(declare (indent defun))
(unless (keywordp id)
@@ -95,9 +100,12 @@
(const :tag "on demand (no summary)" 0)
(number :tag "after delay" 1)))
-(defcustom transient-enable-popup-navigation t
+(defcustom transient-enable-popup-navigation 'verbose
"Whether navigation commands are enabled in the transient popup.
+If the value is `verbose', additionally show brief documentation
+about the command under point in the echo area.
+
While a transient is active the transient popup buffer is not the
current buffer, making it necessary to use dedicated commands to
act on that buffer itself. If this is non-nil, then the following
@@ -117,19 +125,20 @@ bindings are available:
All other bindings are in `transient-popup-navigation-map'.
By default \\`M-RET' is bound to `transient-push-button', instead of
-\\`RET', because if a transient allows the invocation of non-suffixes
-then it is likely that you would want \\`RET' to do what it would do
+\\`RET', because if a transient allows the invocation of non-suffixes,
+then it is likely, that you would want \\`RET' to do what it would do
if no transient were active."
- :package-version '(transient . "0.4.0")
+ :package-version '(transient . "0.7.8")
:group 'transient
- :type 'boolean)
+ :type '(choice (const :tag "enable navigation and echo summary" verbose)
+ (const :tag "enable navigation commands" t)
+ (const :tag "disable navigation commands" nil)))
(defcustom transient-display-buffer-action
'(display-buffer-in-side-window
(side . bottom)
(dedicated . t)
- (inhibit-same-window . t)
- (window-parameters (no-other-window . t)))
+ (inhibit-same-window . t))
"The action used to display the transient popup buffer.
The transient popup buffer is displayed in a window using
@@ -147,14 +156,11 @@ The default is:
(display-buffer-in-side-window
(side . bottom)
(dedicated . t)
- (inhibit-same-window . t)
- (window-parameters (no-other-window . t)))
+ (inhibit-same-window . t))
This displays the window at the bottom of the selected frame.
-Another useful FUNCTION is `display-buffer-below-selected', which
-is what `magit-popup' used by default. For more alternatives see
-info node `(elisp)Display Action Functions' and info node
-`(elisp)Buffer Display Action Alists'.
+For alternatives see info node `(elisp)Display Action Functions'
+and info node `(elisp)Buffer Display Action Alists'.
Note that the buffer that was current before the transient buffer
is shown should remain the current buffer. Many suffix commands
@@ -163,6 +169,11 @@ buffer became the current buffer, then that would change
what is
at point. To that effect `inhibit-same-window' ensures that the
selected window is not used to show the transient buffer.
+The use of a horizontal split to display the menu window can lead
+to incompatibilities and is thus discouraged. Transient tries to
+mitigate such issue but cannot proactively deal with all possible
+configurations and combinations of third-party packages.
+
It may be possible to display the window in another frame, but
whether that works in practice depends on the window-manager.
If the window manager selects the new window (Emacs frame),
@@ -170,11 +181,20 @@ then that unfortunately changes which buffer is current.
If you change the value of this option, then you might also
want to change the value of `transient-mode-line-format'."
- :package-version '(transient . "0.3.0")
+ :package-version '(transient . "0.7.5")
:group 'transient
:type '(cons (choice function (repeat :tag "Functions" function))
alist))
+(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."
+ :package-version '(transient . "0.8.1")
+ :group 'transient
+ :type 'natnum)
+
(defcustom transient-mode-line-format 'line
"The mode-line format for the transient popup buffer.
@@ -182,15 +202,14 @@ 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) 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.
+If `line' (the default) or a natural number, then the buffer has no
+mode-line, but a line 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),
+color of `transient-key-noop' (if non-suffixes 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.
@@ -217,6 +236,51 @@ of this variable use \"C-x t\" when a transient is active."
:group 'transient
:type 'boolean)
+(defcustom transient-show-during-minibuffer-read nil
+ "Whether to show the transient menu while reading in the minibuffer.
+
+This is only relevant to commands that do not close the menu, such as
+commands that set infix arguments. If a command exits the menu, and
+uses the minibuffer, then the menu is always closed before the
+minibuffer is entered, irrespective of the value of this option.
+
+When nil (the default), hide the menu while the minibuffer is in use.
+When t, keep showing the menu, but allow for the menu window to be
+resized, to ensure that completion candidates can be displayed.
+
+When `fixed', keep showing the menu and prevent it from being resized,
+which may make it impossible to display the completion candidates. If
+that ever happens for you, consider using t or an integer, as described
+below.
+
+If the value is `fixed' and the menu window uses the full height of its
+frame, then the former is ignored and resizing is allowed anyway. This
+is necessary because individual menus may use unusual display actions
+different from what `transient-display-buffer-action' specifies (likely
+to display that menu in a side-window).
+
+When using a third-party mode, which automatically resizes windows
+\(e.g., by calling `balance-windows' on `post-command-hook'), then
+`fixed' (or nil) is likely a better choice than t.
+
+The value can also be an integer, in which case the behavior depends on
+whether at least that many lines are left to display windows other than
+the menu window. If that is the case, display the menu and preserve the
+size of that window. Otherwise, allow resizing the menu window if the
+number is positive, or hide the menu if it is negative."
+ :package-version '(transient . "0.8.0")
+ :group 'transient
+ :type '(choice
+ (const :tag "Hide menu" nil)
+ (const :tag "Show menu and preserve size" fixed)
+ (const :tag "Show menu and allow resizing" t)
+ (natnum :tag "Show menu, allow resizing if less than N lines left"
+ :format "\n %t: %v"
+ :value 20)
+ (integer :tag "Show menu, except if less than N lines left"
+ :format "\n %t: %v"
+ :value -20)))
+
(defcustom transient-read-with-initial-input nil
"Whether to use the last history element as initial minibuffer input."
:package-version '(transient . "0.2.0")
@@ -311,7 +375,7 @@ used that breaks that relationship.
This option is intended for users who use a variable-pitch
font for the `default' face.
-Also see `transient-force-fixed-pitch'."
+See also `transient-force-fixed-pitch'."
:package-version '(transient . "0.4.0")
:group 'transient
:type 'boolean)
@@ -324,7 +388,7 @@ you might still want to use a monospaced font in transient's
popup buffer. Setting this option to t causes `default' to
be remapped to `fixed-pitch' in that buffer.
-Also see `transient-align-variable-pitch'."
+See also `transient-align-variable-pitch'."
:package-version '(transient . "0.2.0")
:group 'transient
:type 'boolean)
@@ -338,12 +402,6 @@ text and might otherwise have to scroll in two dimensions."
:group 'transient
:type 'boolean)
-(defcustom transient-hide-during-minibuffer-read nil
- "Whether to hide the transient buffer while reading in the minibuffer."
- :package-version '(transient . "0.4.0")
- :group 'transient
- :type 'boolean)
-
(defconst transient--max-level 7)
(defconst transient--default-child-level 1)
(defconst transient--default-prefix-level 4)
@@ -470,7 +528,7 @@ See info node `(transient)Enabling and Disabling Suffixes'."
(or (and (not (eq color 'unspecified)) color)
"grey60")))))
"Face optionally used to highlight suffixes on higher levels.
-Also see option `transient-highlight-higher-levels'."
+See also option `transient-highlight-higher-levels'."
:group 'transient-faces)
(defface transient-delimiter '((t :inherit shadow))
@@ -532,14 +590,14 @@ character used to separate possible values from each
other."
`((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'."
+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")))
"Face optionally used to highlight keys without a short-argument.
-Also see option `transient-highlight-mismatched-keys'."
+See also option `transient-highlight-mismatched-keys'."
:group 'transient-faces)
;;; Persistence
@@ -611,7 +669,6 @@ If `transient-save-history' is nil, then do nothing."
((prototype :initarg :prototype)
(command :initarg :command)
(level :initarg :level)
- (variable :initarg :variable :initform nil)
(init-value :initarg :init-value)
(value) (default-value :initarg :value)
(scope :initarg :scope :initform nil)
@@ -625,8 +682,11 @@ If `transient-save-history' is nil, then do nothing."
(transient-non-suffix :initarg :transient-non-suffix :initform nil)
(transient-switch-frame :initarg :transient-switch-frame)
(refresh-suffixes :initarg :refresh-suffixes :initform nil)
+ (environment :initarg :environment :initform nil)
(incompatible :initarg :incompatible :initform nil)
(suffix-description :initarg :suffix-description)
+ (display-action :initarg :display-action :initform nil)
+ (mode-line-format :initarg :mode-line-format)
(variable-pitch :initarg :variable-pitch :initform nil)
(column-widths :initarg :column-widths :initform nil)
(unwind-suffix :documentation "Internal use." :initform nil))
@@ -643,7 +703,11 @@ the prototype is stored in the clone's `prototype' slot.")
;;;; Suffix
(defclass transient-child ()
- ((level
+ ((parent
+ :initarg :parent
+ :initform nil
+ :documentation "The parent group object.")
+ (level
:initarg :level
:initform (symbol-value 'transient--default-child-level)
:documentation "Enable if level of prefix is equal or greater.")
@@ -718,8 +782,8 @@ the prototype is stored in the clone's `prototype' slot.")
:documentation "Inapt if major-mode does not derive from value."))
"Abstract superclass for group and suffix classes.
-It is undefined what happens if more than one `if*' predicate
-slot is non-nil."
+It is undefined which predicates are used if more than one `if*'
+predicate slots or more than one `inapt-if*' slots are non-nil."
:abstract t)
(defclass transient-suffix (transient-child)
@@ -730,7 +794,8 @@ slot is non-nil."
(format :initarg :format :initform " %k %d")
(description :initarg :description :initform nil)
(face :initarg :face :initform nil)
- (show-help :initarg :show-help :initform nil))
+ (show-help :initarg :show-help :initform nil)
+ (summary :initarg :summary :initform nil))
"Superclass for suffix command.")
(defclass transient-information (transient-suffix)
@@ -797,6 +862,12 @@ They become the value of this argument.")
(set :initarg := :initform nil))
"Class used by the `transient-preset' suffix command.")
+(defclass transient-describe-target (transient-suffix)
+ ((transient :initform #'transient--do-suspend)
+ (helper :initarg :helper :initform nil)
+ (target :initarg := :initform nil))
+ "Class used by the `transient-describe' suffix command.")
+
;;;; Group
(defclass transient-group (transient-child)
@@ -893,8 +964,8 @@ to the setup function:
(put ',name 'transient--prefix
(,(or class 'transient-prefix) :command ',name ,@slots))
(put ',name 'transient--layout
- (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s))
- suffixes))))))
+ (list ,@(mapcan (lambda (s) (transient--parse-child name s))
+ suffixes))))))
(defmacro transient-define-suffix (name arglist &rest args)
"Define NAME as a transient suffix command.
@@ -1087,7 +1158,7 @@ commands are aliases for."
(if (and (listp value)
(or (listp (car value))
(vectorp (car value))))
- (cl-mapcan (lambda (s) (transient--parse-child prefix s))
value)
+ (mapcan (lambda (s) (transient--parse-child prefix s))
value)
(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)))
@@ -1095,134 +1166,134 @@ commands are aliases for."
(t (error "Invalid transient--parse-child spec: %s" spec))))
(defun transient--parse-group (prefix spec)
- (setq spec (append spec nil))
- (cl-symbol-macrolet
- ((car (car spec))
- (pop (pop spec)))
- (let (level class args)
- (when (integerp car)
- (setq level pop))
- (when (stringp car)
- (setq args (plist-put args :description pop)))
- (while (keywordp car)
- (let ((key pop)
- (val pop))
- (cond ((eq key :class)
- (setq class (macroexp-quote val)))
- ((or (symbolp val)
- (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)
- (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))
- spec))))))
+ (let ((spec (append spec nil))
+ level class args)
+ (when (integerp (car spec))
+ (setq level (pop spec)))
+ (when (stringp (car spec))
+ (setq args (plist-put args :description (pop spec))))
+ ;; Merge value of [... GROUP-VARIABLE], if any.
+ (let ((spec* spec))
+ (while (keywordp (car spec*))
+ (setq spec* (cddr spec*)))
+ (when (and (length= spec* 1) (symbolp (car spec*)))
+ (let ((rest (append (symbol-value (car spec*)) nil))
+ (args nil))
+ (while (keywordp (car rest))
+ (setq args (nconc (list (pop rest) (pop rest)) args)))
+ (setq spec (nconc args (butlast spec) rest)))))
+ (while (keywordp (car spec))
+ (let* ((key (pop spec))
+ (val (if spec (pop spec) (error "No value for `%s'" key))))
+ (cond ((eq key :class)
+ (setq class val))
+ ((or (symbolp val)
+ (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)
+ (list 'quote
+ (cond (class)
+ ((cl-typep (car spec)
+ '(or vector (and symbol (not null))))
+ 'transient-columns)
+ ('transient-column)))
+ (and args (cons 'list args))
+ (cons 'list
+ (mapcan (lambda (s) (transient--parse-child prefix s))
spec)))))
(defun transient--parse-suffix (prefix spec)
(let (level class args)
- (cl-symbol-macrolet
- ((car (car spec))
- (pop (pop spec)))
- (when (integerp car)
- (setq level pop))
- (when (or (stringp car)
- (vectorp car))
- (setq args (plist-put args :key pop)))
- (cond
- ((or (stringp car)
- (and (eq (car-safe car) 'lambda)
- (not (commandp car))))
- (setq args (plist-put args :description pop)))
- ((and (symbolp car)
- (not (keywordp car))
- (not (commandp car))
- (commandp (cadr spec)))
- (setq args (plist-put args :description (macroexp-quote pop)))))
- (cond
- ((memq car '(:info :info*)))
- ((keywordp car)
- (error "Need command, `:info' or `:info*', got `%s'" car))
- ((symbolp car)
- (setq args (plist-put args :command (macroexp-quote pop))))
- ((and (commandp car)
- (not (stringp car)))
- (let ((cmd pop)
- (sym (intern
- (format "transient:%s:%s"
- prefix
- (let ((desc (plist-get args :description)))
- (if (and (stringp desc)
- (length< desc 16))
- desc
- (plist-get args :key)))))))
- (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)
- (sym nil))
- (cl-typecase arg
- (list
- (setq args (plist-put args :shortarg (car arg)))
- (setq args (plist-put args :argument (cadr arg)))
- (setq arg (cadr arg)))
- (string
- (when-let* ((shortarg (transient--derive-shortarg arg)))
- (setq args (plist-put args :shortarg shortarg)))
- (setq args (plist-put args :argument 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))))
- ((not (string-suffix-p "=" arg))
- (setq class 'transient-switch))
- (t
- (setq class 'transient-option)))))
- (t
- (error "Needed command or argument, got %S" car)))
- (while (keywordp car)
- (let ((key pop)
- (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)
- (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 (plist-get args :key)
- (when-let* ((shortarg (plist-get args :shortarg)))
- (setq args (plist-put args :key shortarg))))
+ (cl-flet ((use (prop value)
+ (setq args (plist-put args prop value))))
+ (pcase (car spec)
+ ((cl-type integer)
+ (setq level (pop spec))))
+ (pcase (car spec)
+ ((cl-type (or string vector))
+ (use :key (pop spec))))
+ (pcase (car spec)
+ ((guard (or (stringp (car spec))
+ (and (eq (car-safe (car spec)) 'lambda)
+ (not (commandp (car spec))))))
+ (use :description (pop spec)))
+ ((and (cl-type (and symbol (not keyword) (not command)))
+ (guard (commandp (cadr spec))))
+ (use :description (macroexp-quote (pop spec)))))
+ (pcase (car spec)
+ ((or :info :info*))
+ ((and (cl-type keyword) invalid)
+ (error "Need command, argument, `:info' or `:info*'; got `%s'"
invalid))
+ ((cl-type symbol)
+ (use :command (macroexp-quote (pop spec))))
+ ;; During macro-expansion this is expected to be a `lambda'
+ ;; expression (i.e., source code). When this is called from a
+ ;; `:setup-children' function, it may also be a function object
+ ;; (a.k.a a function value). However, we never treat a string
+ ;; as a command, so we have to check for that explicitly.
+ ((cl-type (and command (not string)))
+ (let ((cmd (pop spec))
+ (sym (intern
+ (format
+ "transient:%s:%s:%d" prefix
+ (replace-regexp-in-string (plist-get args :key) " " "")
+ (prog1 gensym-counter (cl-incf gensym-counter))))))
+ (use :command
+ `(prog1 ',sym
+ (put ',sym 'interactive-only t)
+ (put ',sym 'completion-predicate #'transient--suffix-only)
+ (defalias ',sym ,cmd)))))
+ ((cl-type (or string (and list (not null))))
+ (let ((arg (pop spec)))
+ (cl-typecase arg
+ (list
+ (use :shortarg (car arg))
+ (use :argument (cadr arg))
+ (setq arg (cadr arg)))
+ (string
+ (when-let* ((shortarg (transient--derive-shortarg arg)))
+ (use :shortarg shortarg))
+ (use :argument arg)))
+ (use :command
+ (let ((sym (intern (format "transient:%s:%s" prefix arg))))
+ `(prog1 ',sym
+ (put ',sym 'interactive-only t)
+ (put ',sym 'completion-predicate #'transient--suffix-only)
+ (defalias ',sym #'transient--default-infix-command))))
+ (pcase (car spec)
+ ((cl-type (and (not null) (not keyword)))
+ (setq class 'transient-option)
+ (use :reader (macroexp-quote (pop spec))))
+ ((guard (string-suffix-p "=" arg))
+ (setq class 'transient-option))
+ (_ (setq class 'transient-switch)))))
+ (invalid
+ (error "Need command, argument, `:info' or `:info*'; got %s"
invalid)))
+ (while (keywordp (car spec))
+ (let* ((key (pop spec))
+ (val (if spec (pop spec) (error "No value for `%s'" key))))
+ (pcase key
+ (:class (setq class val))
+ (:level (setq level val))
+ (:info (setq class 'transient-information)
+ (use :description val))
+ (:info* (setq class 'transient-information*)
+ (use :description val))
+ ((guard (eq (car-safe val) '\,))
+ (use key (cadr val)))
+ ((guard (or (symbolp val)
+ (and (listp val) (not (eq (car val) 'lambda)))))
+ (use key (macroexp-quote val)))
+ (_ (use key val)))))
+ (when spec
+ (error "Need keyword, got %S" (car spec)))
+ (when-let* (((not (plist-get args :key)))
+ (shortarg (plist-get args :shortarg)))
+ (use :key shortarg)))
(list 'list
(or level transient--default-child-level)
(macroexp-quote (or class 'transient-suffix))
@@ -1244,7 +1315,7 @@ in regular keymaps or by using
`execute-extended-command')."
(defalias 'transient--suffix-only #'ignore
"Ignore ARGUMENTS, do nothing, and return nil.
-Also see `transient-command-completion-not-suffix-only-p'.
+See also `transient-command-completion-not-suffix-only-p'.
Only use this alias as the value of the `completion-predicate'
symbol property.")
@@ -1495,6 +1566,7 @@ That buffer is current and empty when this hook runs.")
(defvar transient--exitp nil "Whether to exit the transient.")
(defvar transient--showp nil "Whether to show the transient popup buffer.")
(defvar transient--helpp nil "Whether help-mode is active.")
+(defvar transient--docsp nil "Whether docstring-mode is active.")
(defvar transient--editp nil "Whether edit-mode is active.")
(defvar transient--refreshp nil
@@ -1568,13 +1640,13 @@ that is run directly by a command that is invoked while
a transient
is current, this function is also suitable for use in asynchronous
code, such as timers and callbacks (this function's main use-case).
-If optional PREFIXES is non-nil, it must be a list of prefix command
-symbols, in which case the active transient object is only returned
-if it matches one of the PREFIXES."
+If optional PREFIXES is non-nil, it must be a prefix command symbol
+or a list of symbols, in which case the active transient object is
+only returned if it matches one of PREFIXES."
(and transient--showp
transient--prefix
(or (not prefixes)
- (memq (oref transient--prefix command) prefixes))
+ (memq (oref transient--prefix command) (ensure-list prefixes)))
(or (memq 'transient--pre-command pre-command-hook)
(and (memq t pre-command-hook)
(memq 'transient--pre-command
@@ -1661,16 +1733,25 @@ probably use this instead:
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))))
+ (or (if (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))
+ ;; COMMAND is only provided if `this-command' is meaningless, in
+ ;; which case `this-command-keys' is also meaningless, making it
+ ;; impossible to disambiguate redundant bindings.
+ (if command
+ (car suffixes)
+ ;; TODO Decide whether it is legimate to use this function
+ ;; as a predicate, and also whether to return an object for
+ ;; suffixes common to all prefixes. See #29 and #337.
+ (error "BUG: Cannot determine suffix object")))))
((and-let* ((obj (transient--suffix-prototype (or command this-command)))
(obj (clone obj)))
- (progn ; work around debbugs#31840
+ (progn
(transient-init-scope obj)
(transient-init-value obj)
obj)))))
@@ -1706,20 +1787,25 @@ to `transient-predicate-map'."
"<next>" #'transient-scroll-up
"<prior>" #'transient-scroll-down)
-(defvar-keymap transient-map
- :doc "Top-level keymap used by all transients.
+(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)
+ ;; Also bound to "C-x p" and "C-x n" 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.
If you add a new command here, then you must also add a binding
-to `transient-predicate-map'. Also see `transient-base-map'."
- :parent transient-base-map
- "C-u" #'universal-argument
- "C--" #'negative-argument
- "C-t" #'transient-show
- "?" #'transient-help
- "C-h" #'transient-help
- ;; Also bound to "C-x p" and "C-x n" in transient-common-commands.
- "C-M-p" #'transient-history-prev
- "C-M-n" #'transient-history-next)
+to `transient-predicate-map'. See also `transient-base-map'.")
(defvar-keymap transient-edit-map
:doc "Keymap that is active while a transient in is in \"edit mode\"."
@@ -1764,11 +1850,7 @@ to `transient-predicate-map'. Also see
`transient-base-map'."
(list "C-z" "Suspend transient stack" #'transient-suspend))
(vector
"Customize"
- (list "C-x t" 'transient-toggle-common :description
- (lambda ()
- (if transient-show-common-commands
- "Hide common commands"
- "Show common permanently")))
+ (list "C-x t" #'transient-toggle-common)
(list "C-x l" "Show/hide suffixes" #'transient-set-level)
(list "C-x a" #'transient-toggle-level-limit)))))
t)))
@@ -1826,13 +1908,14 @@ of the corresponding object."
"<universal-argument-more>" #'transient--do-stay
"<negative-argument>" #'transient--do-minus
"<digit-argument>" #'transient--do-stay
+ "<other-frame-prefix>" #'transient--do-stay
+ "<other-window-prefix>" #'transient--do-stay
"<top-level>" #'transient--do-quit-all
"<transient-quit-all>" #'transient--do-quit-all
"<transient-quit-one>" #'transient--do-quit-one
"<transient-quit-seq>" #'transient--do-stay
"<transient-show>" #'transient--do-stay
"<transient-update>" #'transient--do-stay
- "<transient-toggle-common>" #'transient--do-stay
"<transient-set>" #'transient--do-call
"<transient-set-and-exit>" #'transient--do-exit
"<transient-save>" #'transient--do-call
@@ -1851,6 +1934,8 @@ of the corresponding object."
"<transient-forward-button>" #'transient--do-move
"<transient-isearch-backward>" #'transient--do-move
"<transient-isearch-forward>" #'transient--do-move
+ "<transient-copy-menu-text>" #'transient--do-stay
+ "<transient-toggle-docstrings>" #'transient--do-stay
;; If a valid but incomplete prefix sequence is followed by
;; an unbound key, then Emacs calls the `undefined' command
;; but does not set `this-command', `this-original-command'
@@ -1934,36 +2019,40 @@ of the corresponding object."
(define-key map [handle-switch-frame] #'transient--do-suspend))
(dolist (obj transient--suffixes)
(let* ((cmd (oref obj command))
+ (id (vector cmd))
(kind (cond ((get cmd 'transient--prefix) 'prefix)
((cl-typep obj 'transient-infix) 'infix)
- (t 'suffix))))
- (cond
- ((oref obj inapt)
- (define-key map (vector cmd) #'transient--do-warn-inapt))
- ((slot-boundp obj 'transient)
- (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)))))))
+ (t 'suffix)))
+ (pre (cond
+ ((oref obj inapt) #'transient--do-warn-inapt)
+ ((slot-boundp obj 'transient)
+ (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 id))
+ (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))))))
+ (when pre
+ (if-let* ((alt (lookup-key map id)))
+ (unless (eq alt pre)
+ (define-key map (vconcat (oref obj key) id) pre))
+ (define-key map id pre)))))
map))
(defun transient--make-redisplay-map ()
@@ -1971,8 +2060,9 @@ of the corresponding object."
(pcase this-command
('transient-update
(setq transient--showp t)
- (setq unread-command-events
- (listify-key-sequence (this-single-command-raw-keys))))
+ (let ((keys (listify-key-sequence (this-single-command-raw-keys))))
+ (setq unread-command-events (mapcar (lambda (key) (cons t key))
keys))
+ keys))
('transient-quit-seq
(setq unread-command-events
(butlast (listify-key-sequence
@@ -2033,25 +2123,46 @@ EDIT may be non-nil."
(edit
;; 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--original-window (selected-window))
- (setq transient--original-buffer (current-buffer))
- (setq transient--minibuffer-depth (minibuffer-depth))
- (transient--redisplay)
- (transient--init-transient)
+ (transient--env-apply
+ (lambda ()
+ (transient--init-transient name layout params)
+ (transient--history-init transient--prefix)
+ (setq transient--original-window (selected-window))
+ (setq transient--original-buffer (current-buffer))
+ (setq transient--minibuffer-depth (minibuffer-depth))
+ (transient--redisplay))
+ (get name 'transient--prefix))
+ (transient--setup-transient)
(transient--suspend-which-key-mode)))
(cl-defgeneric transient-setup-children (group children)
"Setup the CHILDREN of GROUP.
If the value of the `setup-children' slot is non-nil, then call
that function with CHILDREN as the only argument and return the
-value. Otherwise return CHILDREN as is."
+value. Otherwise return CHILDREN as is.")
+
+(cl-defmethod transient-setup-children ((group transient-group) children)
(if (slot-boundp group 'setup-children)
(funcall (oref group setup-children) children)
children))
+(defun transient--env-apply (fn &optional prefix)
+ (if-let* ((env (oref (or prefix transient--prefix) environment)))
+ (funcall env fn)
+ (funcall fn)))
+
+(defun transient--init-transient (&optional name layout params)
+ (unless name
+ ;; Re-init.
+ (if (eq transient--refreshp 'updated-value)
+ ;; Preserve the prefix value this once, because the
+ ;; invoked suffix indicates that it has updated that.
+ (setq transient--refreshp (oref transient--prefix refresh-suffixes))
+ ;; Otherwise update the prefix value from suffix values.
+ (oset transient--prefix value (transient-get-value))))
+ (transient--init-objects name layout params)
+ (transient--init-keymaps))
+
(defun transient--init-keymaps ()
(setq transient--predicate-map (transient--make-predicate-map))
(setq transient--transient-map (transient--make-transient-map))
@@ -2062,7 +2173,8 @@ value. Otherwise return CHILDREN as is."
(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--layout (or (and (not transient--refreshp) layout)
+ (transient--init-suffixes name)))
(setq transient--suffixes (transient--flatten-suffixes transient--layout)))
(defun transient--init-prefix (name &optional params)
@@ -2073,28 +2185,29 @@ value. Otherwise return CHILDREN as is."
transient-default-level)
params))))
(transient--setup-recursion obj)
+ (transient-init-scope obj)
(transient-init-value obj)
obj))
(defun transient--init-suffixes (name)
(let ((levels (alist-get name transient-levels)))
- (cl-mapcan (lambda (c) (transient--init-child levels c nil))
- (append (get name 'transient--layout)
- (and (not transient--editp)
- (get 'transient-common-commands
- 'transient--layout))))))
+ (mapcan (lambda (c) (transient--init-child levels c nil))
+ (append (get name 'transient--layout)
+ (and (not transient--editp)
+ (get 'transient-common-commands
+ 'transient--layout))))))
(defun transient--flatten-suffixes (layout)
(cl-labels ((s (def)
(cond
((stringp def) nil)
((cl-typep def 'transient-information) nil)
- ((listp def) (cl-mapcan #'s def))
+ ((listp def) (mapcan #'s def))
((cl-typep def 'transient-group)
- (cl-mapcan #'s (oref def suffixes)))
+ (mapcan #'s (oref def suffixes)))
((cl-typep def 'transient-suffix)
(list def)))))
- (cl-mapcan #'s layout)))
+ (mapcan #'s layout)))
(defun transient--init-child (levels spec parent)
(cl-etypecase spec
@@ -2105,16 +2218,14 @@ value. Otherwise return CHILDREN as is."
(defun transient--init-group (levels spec parent)
(pcase-let ((`(,level ,class ,args ,children) (append spec nil)))
(and-let* (((transient--use-level-p level))
- (obj (apply class :level level args))
+ (obj (apply class :parent parent :level level args))
((transient--use-suffix-p obj))
((prog1 t
- (when (or (and parent (oref parent inapt))
- (transient--inapt-suffix-p obj))
+ (when (transient--inapt-suffix-p obj)
(oset obj inapt t))))
- (suffixes (cl-mapcan
- (lambda (c) (transient--init-child levels c obj))
- (transient-setup-children obj children))))
- (progn ; work around debbugs#31840
+ (suffixes (mapcan (lambda (c) (transient--init-child levels c
obj))
+ (transient-setup-children obj children))))
+ (progn
(oset obj suffixes suffixes)
(list obj)))))
@@ -2132,12 +2243,13 @@ value. Otherwise return CHILDREN as is."
(autoload-do-load fn)))
(when (transient--use-level-p level)
(let ((obj (if (child-of-class-p class 'transient-information)
- (apply class :level level args)
+ (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))))
(apply #'clone proto :level level args)
- (apply class :command cmd :level level args)))))
+ (apply class :command cmd :parent parent :level level
+ args)))))
(cond ((not cmd))
((commandp cmd))
((or (cl-typep obj 'transient-switch)
@@ -2151,8 +2263,7 @@ value. Otherwise return CHILDREN as is."
(unless (cl-typep obj 'transient-information)
(transient--init-suffix-key obj))
(when (transient--use-suffix-p obj)
- (if (or (and parent (oref parent inapt))
- (transient--inapt-suffix-p obj))
+ (if (transient--inapt-suffix-p obj)
(oset obj inapt t)
(transient-init-scope obj)
(transient-init-value obj))
@@ -2165,9 +2276,9 @@ value. Otherwise return CHILDREN as is."
(cl-defmethod transient--init-suffix-key ((obj transient-argument))
(if (transient-switches--eieio-childp obj)
(cl-call-next-method obj)
- (unless (slot-boundp obj 'shortarg)
- (when-let* ((shortarg (transient--derive-shortarg (oref obj argument))))
- (oset obj shortarg shortarg)))
+ (when-let* (((not (slot-boundp obj 'shortarg)))
+ (shortarg (transient--derive-shortarg (oref obj argument))))
+ (oset obj shortarg shortarg))
(unless (slot-boundp obj 'key)
(if (slot-boundp obj 'shortarg)
(oset obj key (oref obj shortarg))
@@ -2194,18 +2305,20 @@ value. Otherwise return CHILDREN as is."
t)))
(defun transient--inapt-suffix-p (obj)
- (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)))
+ (or (and-let* ((parent (oref obj parent)))
+ (oref parent inapt))
+ (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
@@ -2247,8 +2360,8 @@ value. Otherwise return CHILDREN as is."
;;; Flow-Control
-(defun transient--init-transient ()
- (transient--debug 'init-transient)
+(defun transient--setup-transient ()
+ (transient--debug 'setup-transient)
(transient--push-keymap 'transient--transient-map)
(transient--push-keymap 'transient--redisplay-map)
(add-hook 'pre-command-hook #'transient--pre-command)
@@ -2265,14 +2378,7 @@ value. Otherwise return CHILDREN as is."
(transient--pop-keymap 'transient--predicate-map)
(transient--pop-keymap 'transient--transient-map)
(transient--pop-keymap 'transient--redisplay-map)
- (if (eq transient--refreshp 'updated-value)
- ;; Preserve the prefix value this once, because the
- ;; invoked suffix indicates that it has updated that.
- (setq transient--refreshp (oref transient--prefix refresh-suffixes))
- ;; Otherwise update the prefix value from suffix values.
- (oset transient--prefix value (transient-get-value)))
- (transient--init-objects)
- (transient--init-keymaps)
+ (transient--init-transient)
(transient--push-keymap 'transient--transient-map)
(transient--push-keymap 'transient--redisplay-map)
(transient--redisplay))
@@ -2285,8 +2391,8 @@ value. Otherwise return CHILDREN as is."
;; 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-pre-command this-original-command 'suffix)
- (not (transient--get-pre-command this-command 'suffix)))
+ (when (and (transient--get-pre-command this-original-command nil 'suffix)
+ (not (transient--get-pre-command this-command nil 'suffix)))
(setq this-command this-original-command))
(cond
((memq this-command '(transient-update transient-quit-seq))
@@ -2302,7 +2408,8 @@ value. Otherwise return CHILDREN as is."
((not (transient--edebug-command-p))
(setq this-command 'transient-undefined))))
((and transient--editp
- (transient-suffix-object)
+ ;; See TODO in that function.
+ (ignore-errors (transient-suffix-object))
(not (memq this-command '(transient-quit-one
transient-quit-all
transient-help))))
@@ -2339,19 +2446,25 @@ value. Otherwise return CHILDREN as is."
(defun transient--delete-window ()
(when (window-live-p transient--window)
- (let ((remain-in-minibuffer-window
+ (let ((win transient--window)
+ (remain-in-minibuffer-window
(and (minibuffer-selected-window)
(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)))
- (when (buffer-live-p transient--buffer)
- (kill-buffer transient--buffer))
- (setq transient--buffer nil)
+ (cond
+ ((eq (car (window-parameter win 'quit-restore)) 'other)
+ ;; Window used to display another buffer.
+ (set-window-parameter win 'no-other-window
+ (window-parameter win 'prev--no-other-window))
+ (set-window-parameter win 'prev--no-other-window nil))
+ ((with-demoted-errors "Error while exiting transient: %S"
+ (if (window-parent win)
+ (delete-window win)
+ (delete-frame (window-frame win) t)))))
(when remain-in-minibuffer-window
- (select-window remain-in-minibuffer-window)))))
+ (select-window remain-in-minibuffer-window))))
+ (when (buffer-live-p transient--buffer)
+ (kill-buffer transient--buffer))
+ (setq transient--buffer nil))
(defun transient--export ()
(setq transient-current-prefix transient--prefix)
@@ -2362,15 +2475,27 @@ value. Otherwise return CHILDREN as is."
(defun transient--suspend-override (&optional nohide)
(transient--debug 'suspend-override)
(transient--timer-cancel)
- (cond ((and (not nohide) transient-hide-during-minibuffer-read)
- (transient--delete-window))
- ((and transient--prefix transient--redisplay-key)
- (setq transient--redisplay-key nil)
- (when transient--showp
- (if-let* ((win (minibuffer-selected-window)))
- (with-selected-window win
- (transient--show))
- (transient--show)))))
+ (let ((show (if nohide 'fixed transient-show-during-minibuffer-read)))
+ (when (and (integerp show)
+ (< (frame-height (window-frame transient--window))
+ (+ (abs show)
+ (window-height transient--window))))
+ (setq show (natnump show)))
+ (cond ((not show)
+ (transient--delete-window))
+ ((and transient--prefix transient--redisplay-key)
+ (setq transient--redisplay-key nil)
+ (when transient--showp
+ (if-let* ((win (minibuffer-selected-window)))
+ (with-selected-window win
+ (transient--show))
+ (transient--show)))))
+ (when (and (window-live-p transient--window)
+ (and show
+ (or (not (eq show 'fixed))
+ (window-full-height-p transient--window))))
+ (set-window-parameter transient--window 'window-preserved-size
+ (list (window-buffer transient--window) nil nil))))
(transient--pop-keymap 'transient--transient-map)
(transient--pop-keymap 'transient--redisplay-map)
(remove-hook 'pre-command-hook #'transient--pre-command)
@@ -2378,8 +2503,10 @@ value. Otherwise return CHILDREN as is."
(defun transient--resume-override (&optional _ignore)
(transient--debug 'resume-override)
- (when (and transient--showp transient-hide-during-minibuffer-read)
- (transient--show))
+ (cond ((and transient--showp (not (window-live-p transient--window)))
+ (transient--show))
+ ((window-live-p transient--window)
+ (transient--fit-window-to-buffer transient--window)))
(transient--push-keymap 'transient--transient-map)
(transient--push-keymap 'transient--redisplay-map)
(add-hook 'pre-command-hook #'transient--pre-command)
@@ -2393,7 +2520,7 @@ value. Otherwise return CHILDREN as is."
(funcall fn) ; Already unwind protected.
(cond ((memq this-command '(top-level abort-recursive-edit))
(setq transient--exitp t)
- (transient--post-exit)
+ (transient--post-exit this-command)
(transient--delete-window))
(transient--prefix
(transient--resume-override)))))
@@ -2488,14 +2615,14 @@ value. Otherwise return CHILDREN as is."
;; argument is in effect.
(not prefix-arg)))
(transient--refreshp
- (transient--refresh-transient))
+ (transient--env-apply #'transient--refresh-transient))
((let ((old transient--redisplay-map)
(new (transient--make-redisplay-map)))
(unless (equal old new)
(transient--pop-keymap 'transient--redisplay-map)
(setq transient--redisplay-map new)
(transient--push-keymap 'transient--redisplay-map))
- (transient--redisplay)))))
+ (transient--env-apply #'transient--redisplay)))))
(setq transient-current-prefix nil)
(setq transient-current-command nil)
(setq transient-current-suffixes nil)))
@@ -2531,6 +2658,10 @@ value. Otherwise return CHILDREN as is."
(setq transient--all-levels-p nil)
(setq transient--minibuffer-depth 0)
(run-hooks 'transient-exit-hook)
+ (when command
+ (setq transient-current-prefix nil)
+ (setq transient-current-command nil)
+ (setq transient-current-suffixes nil))
(when resume
(transient--stack-pop))))
@@ -2540,7 +2671,8 @@ value. Otherwise return CHILDREN as is."
transient--layout
transient--editp
:transient-suffix (oref transient--prefix transient-suffix)
- :scope (oref transient--prefix scope))
+ :scope (oref transient--prefix scope)
+ :value (transient-get-value))
transient--stack))
(defun transient--stack-pop ()
@@ -2617,12 +2749,13 @@ exit."
(setq transient--stack nil)
(setq transient--exitp t)
(transient--pre-exit)
- (transient--post-exit)))
+ (transient--post-exit this-command)))
;;; Pre-Commands
(defun transient--call-pre-command ()
- (if-let* ((fn (transient--get-pre-command this-command)))
+ (if-let* ((fn (transient--get-pre-command this-command
+ (this-command-keys-vector))))
(let ((action (funcall fn)))
(when (eq action transient--exit)
(setq transient--exitp (or transient--exitp t)))
@@ -2634,10 +2767,14 @@ exit."
(setq this-command 'transient-undefined)))
transient--stay))
-(defun transient--get-pre-command (&optional cmd enforce-type)
+(defun transient--get-pre-command (&optional cmd key enforce-type)
(or (and (not (eq enforce-type 'non-suffix))
(symbolp cmd)
- (lookup-key transient--predicate-map (vector cmd)))
+ (or (and key
+ (let ((def (lookup-key transient--predicate-map
+ (vconcat key (list cmd)))))
+ (and (symbolp def) def)))
+ (lookup-key transient--predicate-map (vector cmd))))
(and (not (eq enforce-type 'suffix))
(transient--resolve-pre-command
(oref transient--prefix transient-non-suffix)
@@ -2742,6 +2879,9 @@ Do not push the active transient to the transient stack."
(defun transient--do-suspend ()
"Suspend the active transient, saving the transient stack."
+ ;; Export so that `transient-describe' instances can use
+ ;; `transient-suffix-object' to get their respective object.
+ (transient--export)
(transient--stack-push)
(setq transient--exitp 'suspend)
transient--exit)
@@ -2917,18 +3057,32 @@ transient is active."
(when (lookup-key transient--transient-map
(this-single-command-raw-keys))
(setq transient--helpp nil)
- (let ((winconf (current-window-configuration)))
- (transient-show-help
- (if (eq this-original-command 'transient-help)
- transient--prefix
- (or (transient-suffix-object)
- this-original-command)))
- (setq-local transient--restore-winconf winconf))
- (fit-window-to-buffer nil (frame-height) (window-height))
- (transient-resume-mode)
- (message (substitute-command-keys
- "Type \\`q' to resume transient command."))
- t))))
+ (transient--display-help #'transient-show-help
+ (if (eq this-original-command 'transient-help)
+ transient--prefix
+ (or (transient-suffix-object)
+ this-original-command)))))))
+
+(transient-define-suffix transient-describe ()
+ "From a transient menu, describe something in another buffer.
+
+This command can be bound multiple times to describe different targets.
+Each binding must specify the thing it describes, be setting the value
+of its `target' slot, using the keyword argument `:='.
+
+The `helper' slot specifies the low-level function used to describe the
+target, and can be omitted, in which case `transient--describe-function'
+is used for a symbol, `transient--show-manual' is used for a string
+beginning with a parenthesis, and `transient--show-manpage' is used for
+any other string.
+
+For example:
+ [(\"e\" \"about emacs\" transient-describe := \"(emacs)\")
+ (\"g\" \"about git\" transient-describe := \"git\")]"
+ :class 'transient-describe-target
+ (interactive)
+ (with-slots (helper target) (transient-suffix-object)
+ (transient--display-help helper target)))
;;;; Level
@@ -3048,11 +3202,23 @@ transient is active."
;;;; Auxiliary
-(defun transient-toggle-common ()
+(transient-define-suffix transient-toggle-common ()
"Toggle whether common commands are permanently shown."
+ :transient t
+ :description (lambda ()
+ (if transient-show-common-commands
+ "Hide common commands"
+ "Show common permanently"))
(interactive)
(setq transient-show-common-commands (not transient-show-common-commands)))
+(transient-define-suffix transient-toggle-docstrings ()
+ "Toggle whether to show docstrings instead of suffix descriptions.
+To make this available in all menus, bind it in `transient-map'."
+ :transient t
+ (interactive)
+ (setq transient--docsp (not transient--docsp)))
+
(defun transient-toggle-debug ()
"Toggle debugging statements for transient commands."
(interactive)
@@ -3060,6 +3226,14 @@ transient is active."
(message "Debugging transient %s"
(if transient--debug "enabled" "disabled")))
+(defun transient-copy-menu-text ()
+ "Copy the contents of the menu buffer to the kill ring.
+To make this available in all menus, bind it in `transient-map'"
+ (interactive)
+ (transient--show)
+ (with-current-buffer (get-buffer transient--buffer-name)
+ (copy-region-as-kill (point-min) (point-max))))
+
(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,
@@ -3080,56 +3254,54 @@ such as when suggesting a new feature or reporting an
issue."
;;; Value
;;;; Init
-(cl-defgeneric transient-init-scope (obj)
- "Set the scope of the suffix object OBJ.
-
-The scope is actually a property of the transient prefix, not of
-individual suffixes. However it is possible to invoke a suffix
-command directly instead of from a transient. In that case, if
-the suffix expects a scope, then it has to determine that itself
-and store it in its `scope' slot.
-
-This function is called for all suffix commands, but unless a
-concrete method is implemented this falls through to the default
-implementation, which is a noop.")
-
-(cl-defmethod transient-init-scope ((_ transient-suffix))
- "Noop." nil)
-
-(cl-defgeneric transient-init-value (_)
- "Set the initial value of the object OBJ.
+(cl-defgeneric transient-init-value (obj)
+ "Set the initial value of the prefix or suffix object OBJ.
This function is called for all prefix and suffix commands.
-For suffix commands (including infix argument commands) the
-default implementation is a noop. Classes derived from the
-abstract `transient-infix' class must implement this function.
-Non-infix suffix commands usually don't have a value."
- nil)
+Third-party subclasses of `transient-infix' must implement a primary
+method.")
(cl-defmethod transient-init-value :around ((obj transient-prefix))
- "If bound, then call OBJ's `init-value' function.
-Otherwise call the primary method according to object's class."
+ "If bound, use the value returned by OBJ' `init-value' function.
+If the value of OBJ's `init-value' is non-nil, call that function to
+determine the value. Otherwise call the primary method according to
+OBJ's class."
(if (slot-boundp obj 'init-value)
(funcall (oref obj init-value) obj)
(cl-call-next-method obj)))
(cl-defmethod transient-init-value :around ((obj transient-infix))
- "If bound, then call OBJ's `init-value' function.
-Otherwise call the primary method according to object's class."
+ "If bound, use the value returned by OBJ's `init-value' function.
+If the value of OBJ's `init-value' is non-nil, call that function to
+determine the value. Otherwise call the primary method according to
+OBJ's class."
(if (slot-boundp obj 'init-value)
(funcall (oref obj init-value) obj)
(cl-call-next-method obj)))
(cl-defmethod transient-init-value ((obj transient-prefix))
+ "Set OBJ's initial value to the set, saved or default value.
+Use `transient-default-value' to determine the default value."
(if (slot-boundp obj 'value)
+ ;; Already set because the live object is cloned from
+ ;; the prototype, were the set (if any) value is stored.
(oref obj value)
(oset obj value
(if-let* ((saved (assq (oref obj command) transient-values)))
(cdr saved)
(transient-default-value obj)))))
+(cl-defmethod transient-init-value ((obj transient-suffix))
+ "Non-infix suffixes usually don't have a value.
+Call `transient-default-value' but because that is a noop for
+`transient-suffix', this function is effectively also a noop."
+ (let ((value (transient-default-value obj)))
+ (unless (eq value eieio--unbound)
+ (oset obj value value))))
+
(cl-defmethod transient-init-value ((obj transient-argument))
+ "Extract OBJ's value from the value of the prefix object."
(oset obj value
(let ((value (oref transient--prefix value))
(argument (and (slot-boundp obj 'argument)
@@ -3150,17 +3322,21 @@ Otherwise call the primary method according to object's
class."
(cl-some match value)))))))
(cl-defmethod transient-init-value ((obj transient-switch))
+ "Extract OBJ's value from the value of the prefix object."
(oset obj value
(car (member (oref obj argument)
(oref transient--prefix value)))))
;;;; Default
-(cl-defgeneric transient-default-value (_)
- "Return the default value."
- nil)
+(cl-defgeneric transient-default-value (obj)
+ "Return the default value.")
(cl-defmethod transient-default-value ((obj transient-prefix))
+ "Return the default value as specified by the `default-value' slot.
+If the value of the `default-value' slot is a function, call it to
+determine the value. If the slot's value isn't a function, return
+that. If the slot is unbound, return nil."
(if-let* ((default (and (slot-boundp obj 'default-value)
(oref obj default-value))))
(if (functionp default)
@@ -3168,6 +3344,11 @@ Otherwise call the primary method according to object's
class."
default)
nil))
+(cl-defmethod transient-default-value ((_ transient-suffix))
+ "Return `eieio--unbound' to indicate that there is no default value.
+Doing so causes `transient-init-value' to skip setting the `value' slot."
+ eieio--unbound)
+
;;;; Read
(cl-defgeneric transient-infix-read (obj)
@@ -3178,7 +3359,7 @@ is used to actually store the new value in the object.
For most infix classes this is done by reading a value from the
user using the reader specified by the `reader' slot (using the
-`transient-infix' method described below).
+method for `transient-infix', described below).
For some infix classes the value is changed without reading
anything in the minibuffer, i.e., the mere act of invoking the
@@ -3388,9 +3569,9 @@ prompt."
(and (member x rule)
(remove x rule))))
(incomp (nconc
- (cl-mapcan (apply-partially filter arg) spec)
+ (mapcan (apply-partially filter arg) spec)
(and (not (equal val arg))
- (cl-mapcan (apply-partially filter val) spec)))))
+ (mapcan (apply-partially filter val) spec)))))
(dolist (obj transient--suffixes)
(when-let* (((cl-typep obj 'transient-argument))
(val (transient-infix-value obj))
@@ -3412,7 +3593,7 @@ Intended for use by transient suffix commands."
(cl-defgeneric transient-set-value (obj)
"Persist the value of the transient prefix OBJ.
Only intended for use by `transient-set'.
-Also see `transient-prefix-set'.")
+See also `transient-prefix-set'.")
(cl-defmethod transient-set-value ((obj transient-prefix))
(oset (oref obj prototype) value (transient-get-value))
@@ -3448,14 +3629,39 @@ Also see `transient-prefix-set'.")
(defun transient-args (prefix)
"Return the value of the transient prefix command PREFIX.
-If the current command was invoked from the transient prefix
-command PREFIX, then return the active infix arguments. If
-the current command was not invoked from PREFIX, then return
-the set, saved or default value for PREFIX."
- (cl-mapcan #'transient--get-wrapped-value (transient-suffixes prefix)))
+
+If the current command was invoked from the transient prefix command
+PREFIX, then return the active infix arguments. If the current command
+was not invoked from PREFIX, then return the set, saved or default value
+for PREFIX.
+
+PREFIX may also be a list of prefixes. If no prefix is active, the
+fallback value of the first of these prefixes is used.
+
+The generic function `transient-prefix-value' is used to determine the
+returned value."
+ (when (listp prefix)
+ (setq prefix (car (or (memq transient-current-command prefix) prefix))))
+ (if-let* ((obj (get prefix 'transient--prefix)))
+ (transient-prefix-value obj)
+ (error "Not a transient prefix: %s" prefix)))
+
+(cl-defgeneric transient-prefix-value (obj)
+ "Return the value of the prefix object OBJ.
+This function is used by `transient-args'.")
+
+(cl-defmethod transient-prefix-value ((obj transient-prefix))
+ "Return a list of the values of the suffixes the prefix object OBJ.
+Use `transient-infix-value' to collect the values of individual suffix
+objects."
+ (mapcan #'transient--get-wrapped-value
+ (transient-suffixes (oref obj command))))
(defun transient-suffixes (prefix)
- "Return the suffix objects of the transient prefix command PREFIX."
+ "Return the suffix objects of the transient prefix command PREFIX.
+
+If PREFIX is not the current prefix, initialize the suffixes so that
+they can be returned. Doing so doesn't have any side-effects."
(if (eq transient-current-command prefix)
transient-current-suffixes
(let ((transient--prefix (transient--init-prefix prefix)))
@@ -3463,14 +3669,27 @@ the set, saved or default value for PREFIX."
(transient--init-suffixes prefix)))))
(defun transient-get-value ()
+ "Return the value of the current prefix.
+
+This is mostly intended for internal use, but may also be of use
+in `transient-set-value' and `transient-save-value' methods. Unlike
+`transient-args', this does not include the values of suffixes whose
+`unsavable' slot is non-nil."
(transient--with-emergency-exit :get-value
- (cl-mapcan (lambda (obj)
- (and (or (not (slot-exists-p obj 'unsavable))
- (not (oref obj unsavable)))
- (transient--get-wrapped-value obj)))
- (or transient--suffixes transient-current-suffixes))))
+ (mapcan (lambda (obj)
+ (and (or (not (slot-exists-p obj 'unsavable))
+ (not (oref obj unsavable)))
+ (transient--get-wrapped-value obj)))
+ (or transient--suffixes transient-current-suffixes))))
(defun transient--get-wrapped-value (obj)
+ "Return a list of the value(s) of suffix object OBJ.
+
+Internally a suffix only ever has one value, stored in its `value'
+slot, but callers of `transient-args', wish to treat the values of
+certain suffixes as multiple values. That translation is handled
+here. The object's `multi-value' slot specifies whether and how
+to interpret the `value' as multiple values."
(and-let* ((value (transient-infix-value obj)))
(pcase-exhaustive (and (slot-exists-p obj 'multi-value)
(oref obj multi-value))
@@ -3481,9 +3700,9 @@ the set, saved or default value for PREFIX."
(cl-defgeneric transient-infix-value (obj)
"Return the value of the suffix object OBJ.
-This function is called by `transient-args' (which see), meaning
-this function is how the value of a transient is determined so
-that the invoked suffix command can use it.
+By default this function is involved when determining the prefix's
+overall value, returned by `transient-args' (which see), so that
+the invoked suffix command can use that.
Currently most values are strings, but that is not set in stone.
Nil is not a value, it means \"no value\".
@@ -3531,7 +3750,9 @@ not contribute to the value of the transient."
For a switch return a boolean. For an option return the value as
a string, using the empty string for the empty value, or nil if
-the option does not appear in ARGS."
+the option does not appear in ARGS.
+
+Append \"=\ to ARG to indicate that it is an option."
(if (string-suffix-p "=" arg)
(save-match-data
(and-let* ((match (let ((case-fold-search nil)
@@ -3544,21 +3765,82 @@ 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))
+;;; Scope
+;;;; Init
+
+(cl-defgeneric transient-init-scope (obj)
+ "Set the scope of the prefix or suffix object OBJ.
+
+The scope is actually a property of the transient prefix, not of
+individual suffixes. However it is possible to invoke a suffix
+command directly instead of from a transient. In that case, if
+the suffix expects a scope, then it has to determine that itself
+and store it in its `scope' slot.
+
+This function is called for all prefix and suffix commands, but
+unless a concrete method is implemented, this falls through to
+a default implementation, which is a noop.")
+
+(cl-defmethod transient-init-scope ((_ transient-prefix))
+ "Noop." nil)
+
+(cl-defmethod transient-init-scope ((_ transient-suffix))
+ "Noop." nil)
+
+;;;; Get
+
+(defun transient-scope (&optional prefixes)
+ "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 PREFIXES is non-nil, it must be a prefix command or a list of such
+commands. In this case 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.
+
+- If a prefix is being setup and it appears in PREFIXES, then return
+ its scope.
+
+- Finally try to return the default scope of the first prefix 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)))))
+ (oref obj scope)
+ (and (get (car prefixes) 'transient--prefix)
+ (oref (transient--init-prefix (car prefixes)) scope))))
+ (and-let* ((obj (transient-prefix-object)))
+ (oref obj scope))))
;;; History
(cl-defgeneric transient--history-key (obj)
- "Return OBJ's history key.
-If the value of the `history-key' slot is non-nil, then return
-that. Otherwise return the value of the `command' slot."
+ "Return OBJ's history key.")
+
+(cl-defmethod transient--history-key ((obj transient-prefix))
+ "If the value of the `history-key' slot is non-nil, return that.
+Otherwise return the value of the `command' slot."
(or (oref obj history-key)
(oref obj command)))
(cl-defgeneric transient--history-push (obj)
- "Push the current value of OBJ to its entry in `transient-history'."
+ "Push the current value of OBJ to its entry in `transient-history'.")
+
+(cl-defmethod transient--history-push ((obj transient-prefix))
(let ((key (transient--history-key obj)))
(setf (alist-get key transient-history)
(let ((args (transient-get-value)))
@@ -3589,7 +3871,7 @@ have a history of their own.")
(mapconcat
#'identity
(sort
- (cl-mapcan
+ (mapcan
(lambda (suffix)
(let ((key (kbd (oref suffix key))))
;; Don't list any common commands.
@@ -3613,6 +3895,7 @@ have a history of their own.")
(transient--timer-cancel)
(setq transient--showp t)
(let ((transient--shadowed-buffer (current-buffer))
+ (setup (not (get-buffer transient--buffer-name)))
(focus nil))
(setq transient--buffer (get-buffer-create transient--buffer-name))
(with-current-buffer transient--buffer
@@ -3622,25 +3905,23 @@ 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 (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
- (setq-local cursor-in-non-selected-windows 'box)
- (setq cursor-type nil))
- (setq display-line-numbers nil)
- (setq show-trailing-whitespace nil)
+ (when setup
+ (when transient-force-fixed-pitch
+ (transient--force-fixed-pitch))
+ (when (bound-and-true-p tab-line-format)
+ (setq tab-line-format nil))
+ (setq header-line-format nil)
+ (setq mode-line-format
+ (let ((format (transient--mode-line-format)))
+ (if (or (natnump format) (eq format 'line)) nil format)))
+ (setq mode-line-buffer-identification
+ (symbol-name (oref transient--prefix command)))
+ (if transient-enable-popup-navigation
+ (setq-local cursor-in-non-selected-windows 'box)
+ (setq cursor-type nil))
+ (setq display-line-numbers nil)
+ (setq show-trailing-whitespace nil)
+ (run-hooks 'transient-setup-buffer-hook))
(transient--insert-groups)
(when (or transient--helpp transient--editp)
(transient--insert-help))
@@ -3649,36 +3930,75 @@ have a history of their own.")
(unless (window-live-p transient--window)
(setq transient--window
(display-buffer transient--buffer
- transient-display-buffer-action)))
+ (transient--display-action)))
+ (with-selected-window transient--window
+ (set-window-parameter nil 'prev--no-other-window
+ (window-parameter nil 'no-other-window))))
(when (window-live-p transient--window)
(with-selected-window transient--window
+ (set-window-parameter nil 'no-other-window t)
(goto-char (point-min))
(when transient-enable-popup-navigation
(transient--goto-button focus))
(transient--fit-window-to-buffer transient--window)))))
+(defun transient--display-action ()
+ (let ((action
+ (cond ((oref transient--prefix display-action))
+ ((memq 'display-buffer-full-frame
+ (ensure-list (car transient-display-buffer-action)))
+ (user-error "%s disallowed in %s"
+ 'display-buffer-full-frame
+ 'transient-display-buffer-action))
+ (transient-display-buffer-action))))
+ (when (and (assq 'pop-up-frame-parameters (cdr action))
+ (fboundp 'buffer-line-statistics)) ; Emacs >= 28.1
+ (setq action (copy-tree action))
+ (pcase-let ((`(,height ,width)
+ (buffer-line-statistics transient--buffer))
+ (params (assq 'pop-up-frame-parameters (cdr action))))
+ (setf (alist-get 'height params) height)
+ (setf (alist-get 'width params)
+ (max width (or transient-minimal-frame-width 0)))))
+ action))
+
(defun transient--fit-window-to-buffer (window)
- (let ((window-resize-pixelwise t)
+ (set-window-parameter window 'window-preserved-size nil)
+ (let ((fit-window-to-buffer-horizontally t)
+ (window-resize-pixelwise t)
(window-size-fixed nil))
- (if (eq (car (window-parameter window 'quit-restore)) 'other)
- ;; Grow but never shrink window that previously displayed
- ;; another buffer and is going to display that again.
- (fit-window-to-buffer window nil (window-height window))
- (fit-window-to-buffer window nil 1))))
+ (cond ((not (window-parent window))
+ (fit-frame-to-buffer (window-frame window) nil nil nil
+ transient-minimal-frame-width))
+ ((eq (car (window-parameter window 'quit-restore)) 'other)
+ ;; Grow but never shrink window that previously displayed
+ ;; another buffer and is going to display that again.
+ (fit-window-to-buffer window nil (window-height window)))
+ ((fit-window-to-buffer window nil 1))))
+ (set-window-parameter window 'window-preserved-size
+ (list (window-buffer window)
+ (window-body-width window t)
+ (window-body-height window t))))
+
+(defun transient--mode-line-format ()
+ (if (slot-boundp transient--prefix 'mode-line-format)
+ (oref transient--prefix mode-line-format)
+ transient-mode-line-format))
(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)))
+ (and-let* ((format (transient--mode-line-format))
+ (height (cond ((not window-system) nil)
+ ((natnump format) format)
+ ((eq format 'line) 1)))
(face `(,@(and (>= emacs-major-version 27) '(:extend t))
- :background
- ,(or (face-foreground (transient--key-face nil
'non-suffix)
- nil t)
- "#gray60"))))
+ :background ,(transient--prefix-color))))
(concat (propertize "__" 'face face 'display `(space :height (,height)))
(propertize "\n" 'face face 'line-height t))))
+(defun transient--prefix-color ()
+ (or (face-foreground (transient--key-face nil nil 'non-suffix) nil t)
+ "#gray60"))
+
(defmacro transient-with-shadowed-buffer (&rest body)
"While in the transient buffer, temporarily make the shadowed buffer
current."
(declare (indent 0) (debug t))
@@ -3686,13 +4006,13 @@ have a history of their own.")
,@body))
(defun transient--insert-groups ()
- (let ((groups (cl-mapcan (lambda (group)
- (let ((hide (oref group hide)))
- (and (not (and (functionp hide)
- (transient-with-shadowed-buffer
- (funcall hide))))
- (list group))))
- transient--layout)))
+ (let ((groups (mapcan (lambda (group)
+ (let ((hide (oref group hide)))
+ (and (not (and (functionp hide)
+ (transient-with-shadowed-buffer
+ (funcall hide))))
+ (list group))))
+ transient--layout)))
(while-let ((group (pop groups)))
(transient--insert-group group)
(when groups
@@ -3703,7 +4023,8 @@ have a history of their own.")
(cl-defgeneric transient--insert-group (group)
"Format GROUP and its elements and insert the result.")
-(cl-defmethod transient--insert-group :around ((group transient-group))
+(cl-defmethod transient--insert-group :around ((group transient-group)
+ &optional _)
"Insert GROUP's description, if any."
(when-let* ((desc (transient-with-shadowed-buffer
(transient-format-description group))))
@@ -3731,7 +4052,7 @@ have a history of their own.")
(insert ?\n))))))
(cl-defmethod transient--insert-group ((group transient-columns))
- (if transient-force-single-column
+ (if (or transient-force-single-column transient--docsp)
(dolist (group (oref group suffixes))
(transient--insert-group group t))
(let* ((columns
@@ -3804,6 +4125,7 @@ as a button."
(slot-boundp obj 'command))
(setq str (make-text-button str nil
'type 'transient
+ 'suffix obj
'command (oref obj command))))
str))
@@ -3828,13 +4150,6 @@ as a button."
(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 (if (slot-boundp obj 'key) (oref obj key) ""))
@@ -3867,16 +4182,16 @@ as a button."
(setq suf (string-replace " " "" suf)))
(concat (propertize pre 'face 'transient-unreachable-key)
(and (string-prefix-p (concat pre " ") key) " ")
- (propertize suf 'face (transient--key-face cmd))
+ (propertize suf 'face (transient--key-face cmd key))
(save-excursion
(and (string-match " +\\'" key)
(propertize (match-string 0 key)
'face 'fixed-pitch))))))
((transient--lookup-key transient-sticky-map (kbd key))
- (propertize key 'face (transient--key-face cmd)))
+ (propertize key 'face (transient--key-face cmd key)))
(t
(propertize key 'face 'transient-unreachable-key))))
- (propertize key 'face (transient--key-face cmd)))))
+ (propertize key 'face (transient--key-face cmd key)))))
(cl-defmethod transient-format-key :around ((obj transient-argument))
"Handle `transient-highlight-mismatched-keys'."
@@ -3932,10 +4247,16 @@ 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 (or (cl-call-next-method obj)
- (and (slot-boundp transient--prefix 'suffix-description)
- (funcall (oref transient--prefix suffix-description)
- obj)))))
+ (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))))))
(if desc
(when-let* ((face (transient--get-face obj 'face)))
(setq desc (transient--add-face desc face t)))
@@ -4025,13 +4346,12 @@ apply the face `transient-unreachable' to the complete
string."
(add-face-text-property (or beg 0) (or end (length str)) face append str)
str))
-(defun transient--key-face (&optional cmd enforce-type)
+(defun transient--key-face (cmd key &optional 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)))
+ (get (transient--get-pre-command cmd key enforce-type)
+ 'transient-face))
(if cmd 'transient-key 'transient-key-noop)))
(defun transient--key-unreachable-p (obj)
@@ -4150,16 +4470,40 @@ manpage, then try to jump to the correct location."
"Show the command doc-string."
(transient--describe-function cmd))
+(defmacro transient-with-help-window (&rest body)
+ "Evaluate BODY, send output to *Help* buffer, and display it in a window.
+Select the help window, and make the help buffer current and return it."
+ (declare (indent 0))
+ `(let ((buffer nil)
+ (help-window-select t))
+ (with-help-window (help-buffer)
+ ,@body
+ (setq buffer (current-buffer)))
+ (set-buffer buffer)))
+
+(defun transient--display-help (helper target)
+ (let ((winconf (current-window-configuration)))
+ (funcall (cond (helper)
+ ((symbolp target) #'transient--describe-function)
+ ((stringp target)
+ (if (string-prefix-p "(" target)
+ #'transient--show-manual
+ #'transient--show-manpage))
+ ((error "Unknown how to show help for %S" target)))
+ target)
+ (setq-local transient--restore-winconf winconf))
+ (fit-window-to-buffer nil (frame-height) (window-height))
+ (transient-resume-mode)
+ (message (substitute-command-keys "Type \\`q' to resume transient
command.")))
+
(defun transient--describe-function (fn)
- (describe-function fn)
- (unless (derived-mode-p 'help-mode)
- (when-let* ((buf (get-buffer "*Help*"))
- (win (or (and buf (get-buffer-window buf))
- (cl-find-if (lambda (win)
- (with-current-buffer (window-buffer win)
- (derived-mode-p 'help-mode)))
- (window-list)))))
- (select-window win))))
+ (let* ((buffer nil)
+ (help-window-select t)
+ (temp-buffer-window-setup-hook
+ (cons (lambda () (setq buffer (current-buffer)))
+ temp-buffer-window-setup-hook)))
+ (describe-function fn)
+ (set-buffer buffer)))
(defun transient--show-manual (manual)
(info manual))
@@ -4238,7 +4582,7 @@ Type %s to exit help.\n"
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 "<KEY>" 'face 'transient-key)
(propertize "C-x l" 'face 'transient-key))))
(with-slots (level) transient--prefix
(insert
@@ -4253,6 +4597,37 @@ Suffixes on levels %s and %s are unavailable.\n"
(propertize (format ">=%s" (1+ level))
'face 'transient-disabled-suffix))))))
+(cl-defgeneric transient-show-summary (obj &optional return)
+ "Show brief summary about the command at point in the echo area.
+
+If OBJ's `summary' slot is a string, use that. If it is a function,
+call that with OBJ as the only argument and use the returned string.
+If `summary' is or returns something other than a string or nil,
+show no summary. If `summary' is or returns nil, use the first line
+of the documentation string, if any.
+
+If RETURN is non-nil, return the summary instead of showing it.
+This is used when a tooltip is needed.")
+
+(cl-defmethod transient-show-summary ((obj transient-suffix) &optional return)
+ (with-slots (command summary) obj
+ (when-let*
+ ((doc (cond ((functionp summary)
+ (funcall summary obj))
+ (summary)
+ ((car (split-string (documentation command) "\n")))))
+ ((stringp doc))
+ ((not (equal doc
+ (car (split-string (documentation
+ 'transient--default-infix-command)
+ "\n"))))))
+ (when (string-suffix-p "." doc)
+ (setq doc (substring doc 0 -1)))
+ (if return
+ doc
+ (let ((message-log-max nil))
+ (message "%s" doc))))))
+
;;; Popup Navigation
(defun transient-scroll-up (&optional arg)
@@ -4276,18 +4651,27 @@ around `scroll-down-command' (which see)."
See `backward-button' for information about N."
(interactive "p")
(with-selected-window transient--window
- (backward-button n t)))
+ (backward-button n t)
+ (when (eq transient-enable-popup-navigation 'verbose)
+ (transient-show-summary (get-text-property (point) 'suffix)))))
(defun transient-forward-button (n)
"Move to the next button in the transient popup buffer.
See `forward-button' for information about N."
(interactive "p")
(with-selected-window transient--window
- (forward-button n t)))
+ (forward-button n t)
+ (when (eq transient-enable-popup-navigation 'verbose)
+ (transient-show-summary (get-text-property (point) 'suffix)))))
(define-button-type 'transient
'face nil
- 'keymap transient-button-map)
+ 'keymap transient-button-map
+ 'help-echo (lambda (win buf pos)
+ (with-selected-window win
+ (with-current-buffer buf
+ (transient-show-summary
+ (get-text-property pos 'suffix) t)))))
(defun transient--goto-button (command)
(cond
@@ -4295,11 +4679,14 @@ See `forward-button' for information about N."
(when (re-search-forward (concat "^" (regexp-quote command)) nil t)
(goto-char (match-beginning 0))))
(command
- (while (and (ignore-errors (forward-button 1))
- (not (eq (button-get (button-at (point)) 'command) command))))
- (unless (eq (button-get (button-at (point)) 'command) command)
- (goto-char (point-min))
- (forward-button 1)))))
+ (cl-flet ((found () (eq (button-get (button-at (point)) 'command)
command)))
+ (while (and (ignore-errors (forward-button 1))
+ (not (found))))
+ (unless (found)
+ (goto-char (point-min))
+ (ignore-errors (forward-button 1))
+ (unless (found)
+ (goto-char (point-min))))))))
(defun transient--heading-at-point ()
(and (eq (get-text-property (point) 'face) 'transient-heading)
@@ -4387,7 +4774,7 @@ search instead."
2)
lisp-imenu-generic-expression :test #'equal)
-(declare-function which-key-mode "which-key" (&optional arg))
+(declare-function which-key-mode "ext:which-key" (&optional arg))
(defun transient--suspend-which-key-mode ()
(when (bound-and-true-p which-key-mode)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master 58df9e272b9: Update to Transient v0.8.3-2-gf0478b29,
Jonas Bernoulli <=