Transient User and Developer Manual

Table of Contents

Next: , Up: (dir)   [Contents][Index]

Transient User and Developer Manual

Taking inspiration from prefix keys and prefix arguments, Transient implements a similar abstraction involving a prefix command, infix arguments and suffix commands. We could call this abstraction a "transient command", but because it always involves at least two commands (a prefix and a suffix) we prefer to call it just a "transient".

When the user calls a transient prefix command, then a transient (temporary) keymap is activated, which binds the transient’s infix and suffix commands, and functions that control the transient state are added to pre-command-hook and post-command-hook. The available suffix and infix commands and their state are shown in the echo area until the transient is exited by invoking a suffix command.

Calling an infix command causes its value to be changed, possibly by reading a new value in the minibuffer.

Calling a suffix command usually causes the transient to be exited but suffix commands can also be configured to not exit the transient.

This manual is for Transient version 0.1.0.

Copyright (C) 2018-2019 Jonas Bernoulli <jonas@bernoul.li>

You can redistribute this document and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This document is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.


Next: , Previous: , Up: Top   [Contents][Index]

1 Introduction

Taking inspiration from prefix keys and prefix arguments, Transient implements a similar abstraction involving a prefix command, infix arguments and suffix commands. We could call this abstraction a "transient command", but because it always involves at least two commands (a prefix and a suffix) we prefer to call it just a "transient".

Transient keymaps are a feature provided by Emacs. Transients as implemented by this package involve the use of transient keymaps.

Emacs provides a feature that it calls "prefix commands". When we talk about "prefix commands" in this manual, then we mean our own kind of "prefix commands", unless specified otherwise. To avoid ambiguity we sometimes use the terms "transient prefix command" for our kind and "regular prefix command" for Emacs’ kind.

When the user calls a transient prefix command, then a transient (temporary) keymap is activated, which binds the transient’s infix and suffix commands, and functions that control the transient state are added to pre-command-hook and post-command-hook. The available suffix and infix commands and their state are shown in the echo area until the transient state is exited by invoking a suffix command.

Calling an infix command causes its value to be changed. How that is done depends on the type of the infix command. The simplest case is an infix command that represents a command-line argument that does not take a value. Invoking such an infix command causes the switch to be toggled on or off. More complex infix commands may read a value from the user, using the minibuffer.

Calling a suffix command usually causes the transient to be exited; the transient keymaps and hook functions are removed, the echo area no longer shows information about the (no longer bound) suffix commands, the values of some public global variables are set, while some internal global variables are unset, and finally the command is actually called. Suffix commands can also be configured to not exit the transient.

A suffix command can, but does not have to, use the infix arguments in much the same way it can choose to use or ignore the prefix arguments. For a suffix command that was invoked from a transient the variable current-transient-suffixes and the function transient-args serve about the same purpose as the variables prefix-arg and current-prefix-arg do for any command that was called after the prefix arguments have been set using a command such as universal-argument.

The information shown in the echo area while a transient is active looks a bit like this:

,-----------------------------------------
|Arguments
| -f Force (--force)
| -a Annotate (--annotate)
|
|Create
| t tag
| r telease
`-----------------------------------------

This is a simplified version of magit-tag. Info manuals do not support images or colored text, so the above "screenshot" lacks some information; in practice you would be able to tell whether the arguments --force and --annotate are enabled or not based on their color.

Transient can be used to implement simple "command dispatchers". The main benefit then is that the user can see all the available commands in the echo area. That is useful by itself because it frees the user from having to remember all the keys that are valid after a certain prefix key or command. Magit’s magit-dispatch command is an example of using Transient to merely implement a command dispatcher.

In addition to that, Transient also allows users to interactively pass arguments to commands. These arguments can be much more complex than what is reasonable when using prefix arguments. There is a limit to how many aspects of a command can be controlled using prefix arguments. Furthermore what a certain prefix argument means for different commands can be completely different, and users have to read documentation to learn and then commit to memory what a certain prefix argument means to a certain command.

Transient suffix commands on the other hand can accept dozens of different arguments without the user having to remember anything. When using Transient, then one can call a command with arguments that are just as complex as when calling the same function non-interactively using code.

Invoking a transient command with arguments is similar to invoking a command in a shell with command-line completion and history enabled. One benefit of the Transient interface is that it remembers history not only on a global level ("this command was invoked using these arguments and previously it was invoked using those other arguments"), but also remembers the values of individual arguments independently. see Using History.

After a transient prefix command is invoked C-h <key> can be used to show the documentation for the infix or suffix command that <key> is bound to (see Getting Help for Suffix Commands) and infixes and suffixes can be removed from the transient using C-x l <key>. Infixes and suffixes that are disabled by default can be enabled the same way. See Enabling and Disabling Suffixes.

Transient ships with support for a few different types of specialized infix commands. A command that sets a command line option for example has different needs than a command that merely toggles a boolean flag. Additionally Transient provides abstractions for defining new types, which the author of Transient did not anticipate (or didn’t get around to implement yet).


Next: , Previous: , Up: Top   [Contents][Index]

2 Usage


Next: , Up: Usage   [Contents][Index]

2.1 Invoking Transients

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 commands activates a transient keymap, which temporarily binds the transients infix and suffix commands. 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 the echo area), 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 arguments.

Instead of setting arguments to be used by a suffix command, infix commands may also set some value by side-effect.


Next: , Previous: , Up: Usage   [Contents][Index]

2.2 Aborting and Resuming Transients

To quit the transient without invoking a suffix command press C-g.

Key bindings in transient keymaps may be longer than a single event. After pressing a valid prefix key, all commands whose bindings do not begin with that prefix key are temporarily unavailable and grayed out. To abort the prefix key press C-g (which in this case only quits the prefix key, but not the complete transient).

A transient prefix command can be bound as a suffix of another transient. Invoking such a suffix replaces the current transient state with a new transient state, i.e. the available bindings change and the information displayed in the echo area is updated accordingly. Pressing C-g while a nested transient is active only quits the innermost transient, causing a return to the previous transient.

C-q and C-z on the other hand always exits all transients. If you use the latter, then you can later resume the stack of transients using M-x transient-resume.

C-g     (transient-quit-seq)
C-g     (transient-quit-one)

This key quits the currently active incomplete key sequence, if any, or else the current transient. When quitting the current transient, then it returns to the previous transient, if any.

C-q     (transient-quit-all)

This command quits the currently active incomplete key sequence, if any, and all transients, including the active transient and all suspended transients, if any.

C-z     (transient-suspend)

Like transient-quit-all, this command quits an incomplete key sequence, if any, and all transients. Additionally it saves the stack of transients so that it can easily be resumed (which is particularly useful if you quickly need to do "something else" and the stack is deeper than a single transient and/or you have already changed the values of some infix arguments).

Note that only a single stack of transients can be saved at a time. If another stack is already saved, then saving a new stack discards the previous stack.

M-x transient-resume     (transient-resume)

This command resumes the previously suspended stack of transients, if any.


Next: , Previous: , Up: Usage   [Contents][Index]

2.3 Common Suffix Commands

A few shared suffix commands are available in all transients. These suffix commands are not shown in the echo area by default.

Most of these commands are bound to C-x <key> and after pressing C-x a section featuring all common commands is temporarily show in the echo area. After invoking one of these commands that section disappears again. Note however that one of these commands is described as "Show common permanently"; invoke that if you want the common commands to always be shown for all transients.

C-x t     (transient-toggle-common)

This command toggles whether the generic commands that are common to all transients are always displayed or only after typing the incomplete prefix key sequence C-x. This only affects the current Emacs session.

User Option: 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 to many options.

While a transient is active, pressing C-x always shows the common command. The value of this option can be changed for the current Emacs session by typing C-x t while a transient is active.

The other common commands are describe in either the previous node or in one of the following nodes.

2.3.1 Notes on Common Key Bindings

You may have noticed that the bindings for some of the common commands do not have the prefix C-x and that furthermore some of these commands are grayed out while others are not. That unfortunately is a bit confusing if the section of common commands is not shown permanently, making the following explanation necessary.

The purpose of usually hiding that section but showing it after the user pressed the respective prefix key is to conserve space and not overwhelm users with too much noise, while allowing the user to quickly list common bindings on demand.

That however should not keep us from using the best possible key bindings. The bindings that do use a prefix do so to avoid wasting too many non-prefix bindings, keeping them available for use in individual transients. The bindings that do not use a prefix and that are not grayed out are very important bindings that are always available, even when invoking the "common command key prefix" or any other transient-specific prefix. The non-prefix keys that are grayed out however, are not available when any incomplete prefix key sequence is active. They do not use the "common command key prefix" because it is likely that users want to invoke them several times in a row and e.g. M-p M-p M-p is much more convenient than C-x M-p C-x M-p C-x M-p.

You may also have noticed that the "Set" command is bound to C-x s, while Magit-Popup used to bind C-c C-c instead. I have seen several users praise the latter binding (sic), so I did not change it willy-nilly. The reason that I changed it is that using different prefix keys for different common commands, would have made the temporary display of the common commands even more confusing, i.e. after pressing C-c all the C-x ... bindings would be grayed out.

Using a single prefix for common commands key means that all other potential prefix keys can be used for transient-specific commands without the section of common commands also popping up. C-c in particular is a prefix that I want (and already do) use for Magit, and also using that for a common command would prevent me from doing so.


Next: , Previous: , Up: Usage   [Contents][Index]

2.4 Saving Values

After setting the infix arguments in a transient, the user can save those arguments for future invocations.

Most transients will start out with the saved arguments when they are invoked. There are a few exceptions though. Some transients are designed so that the value that they use is stored externally as the buffer-local value of some variable. Invoking such a transient again uses the buffer-local value. 1

If the user does not save the value and just exits using a regular suffix command, then the value is merely saved to the transient’s history. That value won’t be used when the transient is next invoked but it is easily accessible (see Using History).

C-x s     (transient-set)

This command saves the value of the active transient for this Emacs session.

C-x C-s     (transient-save)

Save the value of the active transient persistently across Emacs sessions.

User Option: transient-values-file

This file is used to persist the values of transients between Emacs sessions.


Next: , Previous: , Up: Usage   [Contents][Index]

2.5 Using History

Every time the user invokes a suffix command the transient’s current value is saved to its history. This values can be cycled through the same way one can cycle through the history of commands that read user-input in the minibuffer.

M-p     (transient-history-prev)

This command switches to the previous value used for the active transient.

M-n     (transient-history-next)

This command switches to the next value used for the active transient.

In addition to the transient-wide history, Transient of course supports per-infix history. When an infix reads user-input using the minibuffer, then 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 (see Suffix Slots).

Both kinds of history are saved to a file when Emacs is exited.

User Option: transient-history-file

This file is used to persist the history of transients and their infixes between Emacs sessions.

User Option: transient-history-limit

This option controls how many history elements are kept at the time the history in saved in transient-history-file.


Next: , Previous: , Up: Usage   [Contents][Index]

2.6 Getting Help for Suffix Commands

Transients can have many suffixes and infixes that the user might not be familiar with. To make it trivial to get help for these, Transient provides access to the documentation directly from the active transient.

C-h     (transient-help)

This command enters help mode. When help mode is active, then typing <key> shows information about the suffix command that <key> normally is bound to (instead of invoking it). Pressing C-h a second time shows information about the prefix command.

After typing <key> the stack of transient states is suspended and information about the suffix command is shown instead. Typing q in the help buffer buries that buffer and resumes the transient state.

What sort of documentation is shown depends on how the transient was defined. For infix commands that represent command-line arguments this ideally shows the appropriate manpage. transient-help then tries to jump to the correct location within that. Info manuals are also supported. The fallback is to show the commands doc-string, for non-infix suffixes this is usually appropriate.


Previous: , Up: Usage   [Contents][Index]

2.7 Enabling and Disabling Suffixes

The user base of a package that uses transients can be very diverse. This is certainly the case for Magit; some users have been using it and Git for a decade, while others are just getting started now.

For that reason a mechanism is that authors can use to classify a transient’s infixes and suffixes along the essentials…everything spectrum. We use the term "levels" to describe that mechanism.

Each suffix command is placed on a level and each transient has a level (called transient-level), which controls which suffix commands are available. Integers between 1 and 7 (inclusive) are valid levels. For suffixes, 0 is also valid; it means that the suffix is not displayed at any level.

The levels of individual transient and/or their individual suffixes can be changed interactively, by invoking the transient and then pressing C-x l to enter the "edit" mode, see below.

The default level for both transients and their suffixes is 4. The transient-default-level option only controls the default for transients. The default suffix level is always 4. The authors of transients should place certain suffixes on a higher level, if they expect that it won’t be of use to most users, and they should place very important suffixes on a lower level, so that they remain available even if the user lowers the transient level.

(Magit currently places nearly all suffixes on level 4 and lower levels are not used at all yet. So for the time being you should not set a lower default level and using a higher level might not give you as many additional suffixes as you hoped.)

User Option: transient-default-level

This option controls which suffix levels are made available by default. It sets the transient-level for transients for which the user has not set that individually.

User Option: transient-levels-file

This file is used to persist the levels of transients and their suffix between Emacs sessions.

C-x l     (transient-set-level)

This command enters edit mode. When edit mode is active, then all infixes and suffixes that are currently usable are displayed along with their levels. The colors of the levels indicate whether they are enabled or not. The level of the transient is also displayed along with some usage information.

In edit mode, pressing the key that would usually invoke a certain suffix does instead prompt the user for the level that that suffix should be placed on.

Help mode is available in edit mode.

To change the transient level press C-x l again.

To exit edit mode press C-g.

Note that edit mode does not display any suffixes that are not currently usable. magit-rebase for example shows different suffixes depending on whether a rebase is already in progress or not. The predicates also apply in edit mode.

Therefore, to control which suffixes are available given a certain state, you have to make sure that that state is currently active.


Next: , Previous: , Up: Top   [Contents][Index]

3 Other Options

User Option: transient-show-popup

This option controls whether the current transient’s infix and suffix commands are shown in the echo area.

If t (the default), then the infix and suffix commands are shown as soon as the transient is invoked. If nil, only a one line summary is shown until the user presses a key that forms an incomplete key sequence. If a number, behave as for nil but also show the commands after that many seconds of inactivity.

User Option: transient-highlight-mismatched-keys

This option controls whether key bindings of infix commands that do not match the respective command-line argument should be highlighted. For other infix commands this option has no effect.

When this option is non-nil, then the key binding for infix argument are highlighted when only a long argument (e.g. --verbose) is specified but no shorthand (e.g -v). In the rare case that a shorthand is specified but the key binding does not match, then it is highlighted differently.

Highlighting mismatched key bindings is useful when learning the arguments of the underlying command-line tool; you wouldn’t want to learn any short-hands that do not actually exist.

The highlighting is done using one of the faces transient-mismatched-key and transient-nonstandard-key.

User Option: transient-substitute-key-function

This function is used to modify key bindings. It the value of this option is nil (the default), then no substitution is performed.

This function is called with one argument, the prefix object, and must return a key binding description, either the existing key description it finds in the key slot, or key description that replaces the prefix key. It could be used to make other substitutions, but that is discouraged.

For example, = is hard to reach using my custom keyboard layout, so I substitute ( for that, which is easy to reach using a layout optimized for lisp.

(setq transient-substitute-key-function
      (lambda (obj)
        (let ((key (oref obj key)))
          (if (string-match "\\`\\(=\\)[a-zA-Z]" key)
              (replace-match "(" t t key 1)
            key))))
User Option: transient-detect-key-conflicts

This option controls whether key binding conflicts should be detected at the time the transient is invoked. If so, then this results in an error, which prevents the transient from being used. Because of that, conflicts are ignored by default.

Conflicts cannot be determined earlier, i.e. when the transient is being defined and when new suffixes are being added, because at that time there can be false-positives. It is actually valid for multiple suffixes to share a common key binding, provided the predicates of those suffixes prevent that more than one of them is enabled at a time.


Next: , Previous: , Up: Top   [Contents][Index]

4 Modifying Existing Transients

To an extend transients can be customized interactively, see Enabling and Disabling Suffixes. This section explains how existing transients can be further modified non-interactively.

The following functions share a few arguments:

These functions operate on the information stored in the transient--layout property of the PREFIX symbol. Suffix entries in that tree are not objects but have the form (LEVEL CLASS PLIST), where plist should set at least :key, :description and :command.

Function: transient-insert-suffix prefix loc suffix

This function inserts SUFFIX into PREFIX before LOC.

Function: transient-append-suffix prefix loc suffix

This function inserts SUFFIX into PREFIX after LOC.

Function: transient-replace-suffix prefix loc suffix

This function replaces the suffix at LOC in PREFIX with SUFFIX.

Function: transient-remove-suffix prefix loc

This function removes the suffix at LOC in PREFIX.

Function: transient-get-suffix prefix loc

This function returns the suffix at LOC in PREFIX. The returned value has the form mentioned above.

Function: transient-suffix-put prefix loc prop value

This function edits the suffix at LOC in PREFIX, by setting the PROP of its plist to VALUE.

Most of these functions do not signal an error if they cannot perform the requested modification. The functions that insert new suffixes show a warning if LOC cannot be found in PREFIX, without signaling an error. The reason for doing it like this is that establishing a key binding (and that is what we essentially are trying to do here) should not prevent the rest of the configuration to fail also. Among these functions only transient-get-suffix and transient-suffix-put may signal an error.


Next: , Previous: , Up: Top   [Contents][Index]

5 Defining New Commands


Next: , Up: Defining New Commands   [Contents][Index]

5.1 Defining Transients

A transient consists of a prefix command and at least one suffix command, though usually a transient has several infix and suffix commands. The below macro defines the transient prefix command and it binds the transient’s infix and suffix commands. In other works, it defines the complete transient, not just the transient prefix command that is used to invoke that transient.

Macro: define-transient-command name arglist [docstring] [keyword value]… group… [body…]

This macro defines NAME as a transient prefix command and binds the transient’s infix and suffix commands.

ARGLIST are the arguments that the prefix command takes. DOCSTRING is the documentation string and is optional.

These arguments can optionally be followed by keyword-value pairs. Each key has to be a keyword symbol, either :class or a keyword argument supported by the constructor of that class. The transient-prefix class is used if the class is not specified explicitly.

GROUPs add key bindings for infix and suffix commands and specify how these bindings are presented in the echo area. At least one GROUP has to be specified. See Binding Suffix and Infix Commands.

The BODY is optional. If it is omitted, then ARGLIST is ignored and the function definition becomes:

(lambda ()
  (interactive)
  (transient-setup 'NAME))

If BODY is specified, then it must begin with an interactive form that matches ARGLIST, and it must call transient-setup. It may however call that function only when some condition is satisfied.

All transients have a (possibly nil) value, which is exported when suffix commands are called, so that they can consume that value. For some transients it might be necessary to have a sort of secondary value, called a "scope". Such a scope would usually be set in the command’s interactive form and has to be passed to the setup function:

(transient-setup 'NAME nil nil :scope SCOPE)

For example, the scope of the magit-branch-configure transient is the branch whose variables are being configured.


Next: , Previous: , Up: Defining New Commands   [Contents][Index]

5.2 Binding Suffix and Infix Commands

The macro define-transient-command is used to define a transient. This defines the actual transient prefix command (see Defining Transients) and adds the transient’s infix and suffix bindings, as described below.

Users and third-party packages can add additional bindings using functions such as transient-insert-suffix (See Modifying Existing Transients). These functions take a "suffix specification" as one of their arguments, which has the same form as the specifications used in define-transient-command.


Next: , Up: Binding Suffix and Infix Commands   [Contents][Index]

5.2.1 Group Specifications

The suffix and infix commands of a transient are organized in groups. The grouping controls how the descriptions of the suffixes are outlined visually but also makes it possible to set certain properties for a set of suffixes.

Several group classes exist, some of which organize suffixes in subgroups. In most cases the class does not have to be specified explicitly, but see Group Classes.

Groups are specified in the call to define-transient-command, using vectors. Because groups are represented using vectors, we cannot use square brackets to indicate an optional element and instead use curly brackets to do the latter.

Group specifications then have this form:

[{LEVEL} {DESCRIPTION} {KEYWORD VALUE}... ELEMENT...]

The LEVEL is optional and defaults to 4. See Enabling and Disabling Suffixes.

The DESCRIPTION is optional. If present it is used as the heading of the group.

The KEYWORD-VALUE pairs are optional. Each keyword has to be a keyword symbol, either :class or a keyword argument supported by the constructor of that class.

The ELEMENTs are either all subgroups (vectors), or all suffixes (lists) and strings. (At least currently no group type exists that would allow mixing subgroups with commands at the same level, though in principal there is nothing that prevents that.)

If the ELEMENTs are not subgroups, then they can be a mixture of lists that specify commands and strings. Strings are inserted verbatim. The empty string can be used to insert gaps between suffixes, which is particularly useful if the suffixes are outlined as a table.

The form of suffix specifications is documented in the next node.


Previous: , Up: Binding Suffix and Infix Commands   [Contents][Index]

5.2.2 Suffix Specifications

A transient’s suffix and infix commands are bound when the transient prefix command is defined using define-transient-command, see Defining Transients. The commands are organized into groups, see Group Specifications. Here we describe the form used to bind an individual suffix command.

The same form is also used when later binding additional commands using functions such as transient-insert-suffix, see Modifying Existing Transients.

Suffix specifications have this form:

([LEVEL] [KEY] [DESCRIPTION] COMMAND|ARGUMENT [KEYWORD VALUE]...)

LEVEL, KEY and DESCRIPTION can also be specified using the KEYWORDs :level, :key and :description. If the object that is associated with COMMAND sets these properties, then they do not have to be specified here. You can however specify them here anyway, possibly overriding the objects value just for the binding inside this transient.

The next element is either a command or an argument. This is the only argument that is mandatory in all cases.

Finally details can be specified using optional KEYWORD-VALUE pairs. Each keyword has to be a keyword symbol, either :class or a keyword argument supported by the constructor of that class. See Suffix Slots.


Next: , Previous: , Up: Defining New Commands   [Contents][Index]

5.3 Defining Suffix and Infix Commands

Macro: define-suffix-command name arglist [docstring] [keyword value]… body…

This macro defines NAME as a transient suffix command.

ARGLIST are the arguments that the command takes. DOCSTRING is the documentation string and is optional.

These arguments can optionally be followed by keyword-value pairs. Each keyword has to be a keyword symbol, either :class or a keyword argument supported by the constructor of that class. The transient-suffix class is used if the class is not specified explicitly.

The BODY must begin with an interactive form that matches ARGLIST. Use the function transient-args or the low-level variable current-transient-suffixes if the former does not give you all the required details. This should, but does not necessarily have to be, done inside the interactive form; just like for prefix-arg and current-prefix-arg.

Macro: define-infix-command name arglist [docstring] [keyword value]…

This macro defines NAME as a transient infix command.

ARGLIST is always ignored (but mandatory never-the-less) and reserved for future use. DOCSTRING is the documentation string and is optional.

The keyword-value pairs are mandatory. All transient infix commands are equal to each other (but not eq), so it is meaningless to define an infix command without also setting at least :class and one other keyword (which it is depends on the used class, usually :argument or :variable).

Each keyword has to be a keyword symbol, either :class or a keyword argument supported by the constructor of that class. The transient-switch class is used if the class is not specified explicitly.

The function definitions is always:

(lambda (obj value)
  (interactive
   (let ((obj (transient-suffix-object)))
     (list obj (transient-infix-read obj))))
  (transient-infix-set obj value)
  (transient--show))

transient-infix-read and transient-infix-set are generic functions. Different infix commands behave differently because the concrete methods are different for different infix command classes. In rare cases the above command function might not be suitable, even if you define your own infix command class. In that case you have to use transient-suffix-command to define the infix command and use t as the value of the :transient keyword.

Macro: define-infix-argument name arglist [docstring] [keyword value]…

This macro defines NAME as a transient infix command.

It is an alias for define-infix-command. Only use this alias to define an infix command that actually sets an infix argument. To define a infix command that, for example, sets a variable use define-infix-command instead.


Next: , Previous: , Up: Defining New Commands   [Contents][Index]

5.4 Using Infix Arguments

The function and the variables described below allow suffix commands to access the value of the transient from which they were invoked; which is the value of its infix arguments. These variables are set when the user invokes a suffix command that exits the transient, but before actually calling the command.

When returning to the command-loop after calling the suffix command, the arguments are reset to nil (which causes the function to return nil too).

Like for Emacs’ prefix arguments it is advisable, but not mandatory, to access the infix arguments inside the command’s interactive form. The preferred way of doing that is to call the transient-args function, which for infix arguments serves about the same purpose as prefix-arg serves for prefix arguments.

Function: transient-args &optional prefix separate

This function returns the value of the transient from which the current suffix was called. If the current suffix command was not called from a transient, then it returns nil.

If optional PREFIX is non-nil, then it should be a symbol, a transient prefix command. In that case the value of the transient is only returned if the suffix was invoked from that transient. Otherwise nil is returned. This function is also used internally, in which PREFIX can also be a transient-prefix object.

If optional SEPARATE is non-nil, then the arguments are separated into two groups. If SEPARATE is t, they are separated into atoms and conses (nil isn’t a valid value, so it doesn’t matter that that is both an atom and a cons).

SEPARATE can also be a predicate function, in which case the first element is a list of the values for which it returns non-nil and the second element is a list of the values for which it returns nil.

For transients that are used to pass arguments to a subprocess (such as git), stringp is a useful value for SEPARATE, it separates non-positional arguments from positional arguments. The value of Magit’s file argument ("--") for example looks like this: ("--" file...)."

Variable: current-transient-suffixes

The suffixes of the transient from which this suffix command was invoked. This is a list of objects. Usually it is sufficient to instead use the function transient-args, which returns a list of values. In complex cases it might be necessary to use this variable instead, i.e. if you need access to information beside the value.

Variable: current-transient-prefix

The transient from which this suffix command was invoked. The returned value is a transient-prefix object, which holds information associated with the transient prefix command.

Variable: current-transient-command

The transient from which this suffix command was invoked. The returned value is a symbol, the transient prefix command.


Previous: , Up: Defining New Commands   [Contents][Index]

5.5 Transient State

Invoking a transient prefix command "activates" the respective transient, i.e. it puts a transient keymap into effect, which binds the transient’s infix and suffix commands.

The default behavior while a transient is active is as follows:

But these are just the defaults. Whether a certain command deactivates or "exits" the transient is configurable. There is more than one way in which a command can be "transient" or "non-transient"; the exact behavior is implemented by calling a so-called "pre-command" function. Whether non-suffix commands are allowed to be called is configurable per transient.

The available pre-command functions are documented below. They are called by transient--pre-command, a function on pre-command-hook and the value that they return determines whether the transient is exited. To do so the value of one of the constants transient--exit or transient--stay is used (that way we don’t have to remember if t means "exit" or "stay").

Additionally these functions may change the value of this-command (which explains why they have to be called using pre-command-hook), call transient-export, transient--stack-zap or transient--stack-push; and set the values of transient--exitp, transient--helpp or transient--editp.

5.5.1 Pre-commands for Infixes

The default for infixes is transient--do-stay. This is also the only function that makes sense for infixes.

Function: transient--do-stay

Call the command without exporting variables and stay transient.

5.5.2 Pre-commands for Suffixes

The default for suffixes is transient--do-exit.

Function: transient--do-exit

Call the command after exporting variables and exit the transient.

Function: transient--do-call

Call the command after exporting variables and stay transient.

Function: transient--do-replace

Call the transient prefix command, replacing the active transient.

This is used for suffix that are prefixes themselves, i.e. for sub-prefixes.

5.5.3 Pre-commands for Non-Suffixes

The default for non-suffixes, i.e commands that are bound in other keymaps beside the transient keymap, is transient--do-warn. Silently ignoring the user-error is also an option, though probably not a good one.

If you want to let the user invoke non-suffix commands, then use transient--do-stay as the value of the prefix’s transient-non-suffix slot.

Function: transient--do-warn

Call transient-undefined and stay transient.

Function: transient--do-noop

Call transient-noop and stay transient.

5.5.4 Special Pre-Commands

Function: transient--do-quit-one

If active, quit help or edit mode, else exit the active transient.

This is used when the user pressed C-g.

Function: transient--do-quit-all

Exit all transients without saving the transient stack.

This is used when the user pressed C-q.

Function: transient--do-suspend

Suspend the active transient, saving the transient stack.

This is used when the user pressed C-z.


Next: , Previous: , Up: Top   [Contents][Index]

6 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 currently appears to be less important.

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.


Next: , Up: Classes and Methods   [Contents][Index]

6.1 Group Classes

The type of a group can be specified using the :class property at the beginning of the class specification, e.g. [:class transient-columns ...] in a call to define-transient-command.


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.2 Group Methods

Function: transient--insert-group group

This generic function formats the group and its elements and inserts the result into the current buffer, which is a temporary buffer. The contents of that buffer are later inserted into the echo area.

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 temporally make the transient--source-buffer the current buffer.


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.3 Prefix Classes

Currently the transient-prefix class is being used for all prefix command and there is only a single generic functions that can be specialized based on the class of a prefix command.

Function: transient--history-init obj

This generic function is called while setting up the transient and is responsible for initializing the 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 transient-history.

A transient prefix command’s object is stored in the transient--prefix property of the command symbol. While a transient is active, a clone of that object is stored in the variable transient--prefix. A clone is used because some changes that are made to the active transient’s object should not affect later invocations.


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.4 Suffix Classes

Magit defines additional classes, which can serve as examples for the fancy things you can do without modifying Transient. Some of these classes will likely get generalized and added to Transient, for now they are very much subject to change and not documented.


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.5 Suffix Methods

To get information about the methods implementing these generic functions use describe-function.


Next: , Up: Suffix Methods   [Contents][Index]

6.5.1 Suffix Value Methods

Function: transient-init-value obj

This generic function sets the initial value of the object OBJ.

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 transient-infix, so if your class derives from that directly, then you must implement a method.

Function: transient-infix-read obj

This generic function determines the new value of the infix object OBJ.

This function merely determines the value; transient-infix-set 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-value method described below).

For some infix classes the value is changed without reading anything in the minibuffer, i.e. the mere act of invoking the infix command determines what the new value should be, based on the previous value.

Function: transient-prompt obj

This generic function returns the prompt to be used to read infix object OBJ’s value.

Function: transient-infix-set obj value

This generic function sets the value of infix object OBJ to value.

Function: transient-infix-value obj

This generic function returns 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.

Currently most values are strings, but that is not set in stone. nil is not a value, it means "no value".

Usually only infixes have a value, but see the method for transient-suffix.

Function: transient-init-scope obj

This generic function sets 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.


Previous: , Up: Suffix Methods   [Contents][Index]

6.5.2 Suffix Format Methods

Function: transient-format obj

This generic function formats and returns OBJ for display.

When this function is called, then the current buffer is some temporary buffer. If you need the buffer from which the prefix command was invoked to be current, then do so by temporarily making transient--source-buffer current.

Function: transient-format-key obj

This generic function formats OBJ’s key for display and returns the result.

Function: transient-format-description obj

This generic function formats OBJ’s description for display and returns the result.

Function: transient-format-value obj

This generic function formats OBJ’s value for display and returns the result.

Function: transient-show-help obj

Show help for the prefix, infix or suffix command represented by OBJ.

For prefixes show the info manual, if that is specified using the info-manual slot. Otherwise show the manpage if that is specified using the man-page slot. Otherwise show the command’s doc-string.

For suffixes show the command’s doc-string.

For infixes show the manpage if that is specified. Otherwise show the command’s doc-string.


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.6 TODO Prefix Slots


Next: , Previous: , Up: Classes and Methods   [Contents][Index]

6.7 Suffix Slots

Here we document most of the slots that are only available for suffix objects. Some slots are shared by suffix and group objects, they are documented in Predicate Slots.

Also see Suffix Classes.

6.7.1 Slots of transient-suffix

6.7.2 Slots of transient-infix

Some of these slots are only meaningful for some of the subclasses. They are defined here anyway to allow sharing certain methods.

6.7.3 Slots of transient-variable

6.7.4 Slots of transient-switches


Previous: , Up: Classes and Methods   [Contents][Index]

6.8 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.

One more slot is shared between group and suffix classes, level. Like the slots documented above it is a predicate, but it is used for a different purpose. The value has to be an integer between 1 and 7. level controls whether it should be available depending on whether the user wants that or not. See Enabling and Disabling Suffixes.


Next: , Previous: , Up: Top   [Contents][Index]

7 Related Abstractions and Packages


Next: , Up: Related Abstractions and Packages   [Contents][Index]

7.1 Comparison With Prefix Keys and Prefix Arguments

While transient commands were inspired by regular prefix keys and prefix arguments, they are also quite different and much more complex.

The following diagrams illustrate some of the differences.

7.1.1 Regular Prefix Commands

See (elisp)Prefix Keys.

                                  ,--> command1 --> (c)
                                  |
(c)-(+)-> prefix command or key --+--> command2 --> (c)
                                  |
                                  `--> command3 --> (c)

7.1.2 Regular Prefix Arguments

See (elisp)Prefix Command Arguments.

        ,----------------------------------,
        |                                  |
        v                                  |
(c)-(+)---> prefix argument command --(c)-(+)-> any command --> (c)
               |                                        ^        |
               |                                        |        |
               `-- sets or changes --, ,-- maybe used --'        |
                                     | |                         |
                                     v |                         |
                          prefix argument state                  |
                                      ^                          |
                                      |                          |
                                      `-------- discards --------'

7.1.3 Transients

(∩`-´)⊃━☆゚.*・。゚

This diagram ignores the infix value and external state:

(c)
 |        ,- {stay} ------<-,-<------------<-,-<---,
(+)       |                 |                |     |
 |        |                 |                |     |
 |        |   ,--> infix1 --|                |     |
 |        |   |             |                |     |
 |        |   |--> infix2 --|                |     |
 v        v   |             |                |     |
 prefix -(c)-(+)-> infix3 --'                ^     |
              |                              |     |
              |---------------> suffix1 -->--|     |
              |                              |     |
              |---------------> suffix2 ----{1}------> {exit} --> (c)
              |                                    |
              |---------------> suffix3 -------------> {exit} --> (c)
              |                                    |
              `--> any command --{2}-> {warn} -->--|
                                  |                |
                                  |--> {noop} -->--|
                                  |                |
                                  |--> {call} -->--'
                                  |
                                  `------------------> {exit} --> (c)

This diagram takes the infix value into account to an extend, while still ignoring external state:

(c)
 |        ,- {stay} ------<-,-<------------<-,-<---,
(+)       |                 |                |     |
 |        |                 |                |     |
 |        |   ,--> infix1 --|                |     |
 |        |   |    |        |                |     |
 |        |   ,--> infix2 --|                |     |
 v        v   |    |        |                |     |
 prefix -(c)-(+)-> infix3 --'                |     |
              |    |                         ^     |
              |    |                         |     |
              |---------------> suffix1 -->--|     |
              |    |             ^           |     |
              |    |             |           |     |
              |---------------> suffix2 ----{1}------> {exit} --> (c)
              |    |             ^                 |     |
              |    |             |                 |     v
              |    |             |                 |     |
              |---------------> suffix3 -------------> {exit} --> (c)
              |    |             ^                 |     |
              | sets             |                 |     v
              |    |             maybe             |     |
              |    |             used              |     |
              |    |             |                 |     |
              |    |     infix --'                 |     |
              |    `---> value                     |     |
              |           ^                        |     |
              |           |                        |     |
              |       hides                        |     |
              |           |                        |     |
              |           `--------------------------<---|
              |                                    |     |
              `--> any command --{2}-> {warn} -->--|     |
                                  |                |     |
                                  |--> {noop} -->--|     |
                                  |                |     |
                                  |--> {call} -->--'     ^
                                  |                      |
                                  `------------------> {exit} --> (c)

This diagram provides more information about the infix value and also takes external state into account.

                                       ,----sets--- "anything"
                                       |
                                       v
                      ,---------> external
                      |           state
                      |            | |
                      |  initialized |                      ☉‿⚆
                   sets         from |
                      |            | maybe
                      | ,----------' used
                      | |            |
(c)                   | |            v
 |        ,- {stay} --|---<-,-<------|-----<-,-<---,
(+)       |           | |   |        |       |     |
 |        |           | v   |        |       |     |
 |        |   ,--> infix1 --|        |       |     |
 |        |   |       | |   |        |       |     |
 |        |   |       | v   |        |       |     |
 |        |   ,--> infix2 --|        |       |     |
 |        |   |    | ^      |        |       |     |
 v        v   |    | |      |        |       |     |
 prefix -(c)-(+)-> infix3 --'        |       |     |
              |    | ^               |       ^     |
              |    | |               v       |     |
              |---------------> suffix1 -->--|     |
              |    | |           ^   |       |     |
              |    | |           |   v       |     |
              |---------------> suffix2 ----{1}------> {exit} --> (c)
              |    | |           ^   |             |     |
              |    | |           |   |             |     v
              |    | |           |   v             |     |
              |---------------> suffix3 -------------> {exit} --> (c)
              |    | |           ^                 |     |
              | sets |           |                 |     v
              |    | initalized  maybe             |     |
              |    | from        used              |     |
              |    | |           |                 |     |
              |    | `-- infix --'                 |     |
              |    `---> value -----------------------------> persistent
              |           ^ ^                      |     |    across
              |           | |                      |     |    invocations -,
              |       hides |                      |     |                 |
              |           | `----------------------------------------------'
              |           |                        |     |
              |           `--------------------------<---|
              |                                    |     |
              `--> any command --{2}-> {warn} -->--|     |
                                  |                |     |
                                  |--> {noop} -->--|     |
                                  |                |     |
                                  |--> {call} -->--'     ^
                                  |                      |
                                  `------------------> {exit} --> (c)

Despite already being rather complex, even the last diagram leaves out many details. Most importantly it implies that the decision whether to remain transient is made later than it actually is made (for the most part a function on pre-command-hook is responsible). But such implementation details are of little relevance to users and are covered elsewhere.


Previous: , Up: Related Abstractions and Packages   [Contents][Index]

7.2 Comparison With Other Packages

7.2.1 Magit-Popup

Transient is the successor to Magit-Popup (see (magit-popup)Top).

One major difference between these two implementations of the same ideas is that while Transient uses transient keymaps and embraces the command-loop, Magit-Popup implemented an inferior mechanism that does not use transient keymaps and that instead of using the command-loop implements a naive alternative based on read-char.

Magit-Popup does not use classes and generic functions and defining a new command type is near impossible as it involves adding hard-coded special-cases to many functions. Because of that only a single new type was added, which was not already part of Magit-Popup’s initial release.

A lot of things are hard-coded in Magit-Popup. One random example is that the key bindings for switches must begin with "-" and those for options must begin with "=".

7.2.2 Hydra

Hydra (see https://github.com/abo-abo/hydra) is another package that provides features similar to those of Transient.

Both packages use transient keymaps to make a set of commands temporarily available and the lv library to show these commands in the echo area. (The author of Hydra is also the author of lv, which is maintained in the same repository.)

A Hydra "body" is equivalent to a Transient "prefix" and a Hydra "head" is equivalent to a Transient "suffix". Hydra has no equivalent of a Transient "infix".

Both hydras and transients can be used as simple command dispatchers. Used like this they are similar to regular prefix commands and prefix keys, except that the available commands are shown in the echo area.

(Another package that does this is which-key. It does so automatically for any incomplete key sequence. The advantage of that approach is that no additional work is necessary; the disadvantage is that the available commands are not organized semantically.)

Both Hydra and Transient provide features that go beyond simple command dispatchers:

Both packages make it possible to specify how exactly the available commands are outlined:


Next: , Previous: , Up: Top   [Contents][Index]

Appendix A Keystroke Index

Jump to:   C   M  
Index Entry  Section

C
C-g: Aborting and Resuming Transients
C-g: Aborting and Resuming Transients
C-h: Getting Help for Suffix Commands
C-q: Aborting and Resuming Transients
C-x C-s: Saving Values
C-x l: Enabling and Disabling Suffixes
C-x s: Saving Values
C-x t: Common Suffix Commands
C-z: Aborting and Resuming Transients

M
M-n: Using History
M-p: Using History
M-x transient-resume: Aborting and Resuming Transients

Jump to:   C   M  

Next: , Previous: , Up: Top   [Contents][Index]

Appendix B Command Index

Jump to:   T  
Index Entry  Section

T
transient-help: Getting Help for Suffix Commands
transient-history-next: Using History
transient-history-prev: Using History
transient-quit-all: Aborting and Resuming Transients
transient-quit-one: Aborting and Resuming Transients
transient-quit-seq: Aborting and Resuming Transients
transient-resume: Aborting and Resuming Transients
transient-save: Saving Values
transient-set: Saving Values
transient-set-level: Enabling and Disabling Suffixes
transient-suspend: Aborting and Resuming Transients
transient-toggle-common: Common Suffix Commands

Jump to:   T  

Next: , Previous: , Up: Top   [Contents][Index]

Appendix C Function Index

Jump to:   D   T  
Index Entry  Section

D
define-infix-argument: Defining Suffix and Infix Commands
define-infix-command: Defining Suffix and Infix Commands
define-suffix-command: Defining Suffix and Infix Commands
define-transient-command: Defining Transients

T
transient--do-call: Transient State
transient--do-exit: Transient State
transient--do-noop: Transient State
transient--do-quit-all: Transient State
transient--do-quit-one: Transient State
transient--do-replace: Transient State
transient--do-stay: Transient State
transient--do-suspend: Transient State
transient--do-warn: Transient State
transient--history-init: Prefix Classes
transient--insert-group: Group Methods
transient-append-suffix: Modifying Existing Transients
transient-args: Using Infix Arguments
transient-format: Suffix Format Methods
transient-format-description: Suffix Format Methods
transient-format-key: Suffix Format Methods
transient-format-value: Suffix Format Methods
transient-get-suffix: Modifying Existing Transients
transient-infix-read: Suffix Value Methods
transient-infix-set: Suffix Value Methods
transient-infix-value: Suffix Value Methods
transient-init-scope: Suffix Value Methods
transient-init-value: Suffix Value Methods
transient-insert-suffix: Modifying Existing Transients
transient-prompt: Suffix Value Methods
transient-remove-suffix: Modifying Existing Transients
transient-replace-suffix: Modifying Existing Transients
transient-show-help: Suffix Format Methods
transient-suffix-put: Modifying Existing Transients

Jump to:   D   T  

Previous: , Up: Top   [Contents][Index]

Appendix D Variable Index

Jump to:   C   T  
Index Entry  Section

C
current-transient-command: Using Infix Arguments
current-transient-prefix: Using Infix Arguments
current-transient-suffixes: Using Infix Arguments

T
transient-default-level: Enabling and Disabling Suffixes
transient-detect-key-conflicts: Other Options
transient-highlight-mismatched-keys: Other Options
transient-history-file: Using History
transient-history-limit: Using History
transient-levels-file: Enabling and Disabling Suffixes
transient-show-common-commands: Common Suffix Commands
transient-show-popup: Other Options
transient-substitute-key-function: Other Options
transient-values-file: Saving Values

Jump to:   C   T  

Footnotes

(1)

magit-diff and magit-log are two prominent examples, and their handling of buffer-local values is actually a bit more complicated than outlined above and even customizable. This is something I am rethinking, but I don’t want to rush any changes.)