I am excited to announce the initial release of Transient.
Transient is the successor to Magit-Popup, which was created as part
of Magit at the beginning of 2014, and which was released as a
separate package at the end of 2017. Magit-Popup itself succeeded an
even older implementation, magit-key-mode.el
, whose first version
goes back all the way to 2010.
The next release of Magit is going to use Transient instead of Magit-Popup. The Magit snapshot from Melpa already uses Transient.
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 state is exited by invoking a suffix command.
Calling an infix command causes its value to be changed, possibly by reading a new value in the minibuffer.
Calling a suffix command usually causes the transient to be exited but suffix commands can also be configured to not exit the transient state.
A more detailed introduction is available in the manual. The manual is very extensive and I am not going to rehash everything here; not even every new feature.
The main technical differences are:
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
.
Transient uses classes and generic functions to implement different types of infix and suffix commands. That makes it much easier to add new types. With Magit-Popup that was so difficult that only one additional type was ever added, which was not part of the initial release.
Transient removes many arbitrary restrictions that Magit-Popup suffered from. Most importantly any key can be bound to any infix and suffix commands and infixes and suffixes can now placed freely into groups, making it possible to arrange them thematically instead of based on type as before.
Transient also adds many new features, some of which are described below. I strongly recommend that you read the Usage part of the manual to learn more.
Additionally you might want to see the section on Related Abstractions and Packages.
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 using M-p
and M-n
.
When reading the value of a command-line option, users are offered reasonable completion candidates much more often than before. Handling of minibuffer history was also improved. If, and only if, two arguments take the same kind of value, then they now share history.
Both transient-wide and per-infix history are preserved between Emacs sessions now.
In the past we got many requests to add this or that argument to some Magit popup. Magit’s user base is very diverse; some users have been using it and Git for a decade, while others are just getting started now. That means that we had to find a balance between adding every argument that at least one user wants to be added and not overwhelming beginners with a wall of arguments they never use.
To cope with that, Transient supports placing suffix commands on different “levels”. Users can select a level to control how many suffixes they want to see. They can also place individual suffixes on a different level. This is done interactively, directly from the transient. See Enabling and Disabling Suffixes for more information.
Note that Magit currently places nearly all suffixes on the default level. So for the time being changing the level does not have as much of an effect as one might hope. But that will be worked on.
Some users learn Git by using Magit. That involves learning about the
available arguments. For arguments we now try to always use key
sequences that are equal to the respective short argument.
Unfortunately that is not always possible, because Git does not
provide a short argument for every --long
argument.
Transient can now optionally highlight such mismatched key bindings so that you don’t learn any arguments that don’t actually exist.
The values of arguments are now stored differently. That means that all saved values are lost.
Some key bindings have changed. This includes changes to individual Magit transients (previously known as Magit popups), but also some of the commands that are common to all transients. For information about the latter changes see Notes on Common Key Bindings.
While using Magit-Popup, the key bindings for all switches had the
form -<char>
, while the key bindings for all options had the form
=<char>
. That was a hard-coded convention and now that this
restriction has been lifted we always prefer a key binding that
match the corresponding short argument.
Since Magit no longer depends on Magit-Popup, packages that depended on both packages but only specified the former as a dependency, now have to specify the latter dependency explicitly. In the long-run these packages should replace their own popups with transients but there is no hurry.
Users and packages who have added additional commands to existing popups have to adjust these bindings to add them to the respective transients instead.
The Magit commands previously known as popups (and now as transient
prefix commands) have been renamed from magit-WORD-popup
to
magit-WORD
.