Fail-fast Transients like Hydra
Table of Contents
Problem: exwm choking on Transients with Unbound suffix
I have been gradually experimenting with replacing my hydras with Transients1, partly as a proof of concept. I have a show-stopping issue2 when I sometimes end up invoking the Transients from an EXWM window, however. The Transient pops up, but no key I press is then sent to the Transient and I end up trapped in a loop: Transient is expecting a key, which needs to be passed by EXWM, but EXWM can’t pass the right keys because Transient is intercepting them3. Fortunately I can click on an emacs buffer on one of my other monitors and this terminates the Transient. That is, the Transient is unhappy, but I can’t kill it because the Transient is not being passed the C-g
properly. I get an error message that resembles:
Unbound suffix: ‘u’ (Use ‘C-g’ to abort, ‘?’ for help) [self-insert-command]
Now, this isn’t reproducible on demand. It seems to be connected with an error in exwm that sometimes makes for a distinction between window focus and keyboard focus. And yes, this problem is not entirely Transient’s fault. However, with hydras, they automatically, quickly terminate if they receive something they can’t handle. Now, there are many of the more sophisticated cases with Transient where you wouldn’t want this behavior at all, such as magit, when you have multiple-event interfaces. But before I give up on using Transient for simple dispatch, is there a way I can tell certain Transients to fail fast and terminate on ANY unknown suffix?
Answer: transient–do-quit-one
Utilize :transient-non-suffix 'transient--do-quit-one
. This basically means, if Transient can’t match a keystroke to one of its bindings, just die C-g
style.
Full example of one that used to get me in trouble a lot:
(transient-define-prefix tsa/transient-shell
"Shell commands to be used"
:transient-non-suffix 'transient--do-quit-one
["Shell Commands"
[("z" "bettersh" better-shell-shell)
("r" "remote" better-shell-remote-open)
("e" "eshell" eshell)
("t" "vterm" vterm-toggle-cd)
]])
Footnotes
1 Transient’s are what Magit uses for its interface, providing a nice display and some truly impressive state management if you need it for sophisticated program interfaces that track state, have flags, or use multiple display steps. The repo is on github, but the excellent documentation is included in your emacs if you have magit. https://github.com/magit/transient and M-x info
C-s transient
2 Original issue posted here, revised over time as I came to better articulate it. https://github.com/magit/transient/issues/118
3 Okay, this makes no sense. Which tells you I’m confused about it too. Bottom line: I’m stuck in a loop that my keyboard can’t escape.