A Transient Matter

2000 years ago, I wrote this for my present self:


It's 5:20 AM and I have been debugging for what has been 2 or maybe some odd hours while tired. My sleep schedule is no more. Seriously, why didn't you reach out with a hand that can traverse temporal dimensions and stop me from doing this? All for ergonomic keybindings. The calculus is unjustified. I'm going to bed and I'll finish this post later.

This is obscure. It applies to users of xah-fly-keys and actions on a transient.


Jump to tl;dr if you'd like


What's the problem I couldn't tear away from aeons ago? The transient UI in rg.el–I've had this installed for probably close to a decade yet I never realized it came with a transient UI. In this UI, you can set flags. An example: typing - m lets you change the --max-count flag to only get that many matches per file. You're dandily presented a minibuffer with --max-count= as a prompt. You type in 2, because you only want two matches per file.

I pressed <Enter>. And this is where suffering begins…


Look behind you! How did you get here? What walls – wha? You were out simply noodling with your fishing rod off by the village pond, with your evil vim-loser friend friend trying to tell you about how great Rust is, and how you should rewrite all your programs in Rust!

But these walls. How long have they been there, these mossy, dust covered slabs? A summon spell must have brought you here. Well, into the dark maze you go then. As you step into sure abyss, you lean one hand on the wall for stability. Wait. Runes? You touch the stonework for a closer look. A magic panel lights up! Oooh–quickly! Touch all the buttons!

You hear what must be fated rumbling. An inevitability you've activated. Is it the low hum of menace? Is it a growl of a bear? Mayhap a god or spirit or troglodyte you've angered. You try to turn on the light to see clearly. In the middle of twiddling with a wet match, you hear a squeak escape your throat.

"Oh. Fs" You hear demon song:

Unbound suffix: ‘g’ (Use ‘C-g’ to abort, ‘?’ for help) [set-mark-command] [5 times]

Why isn't it working? The runes–there–'g' means go. I'm 'g'ing!

The bass section of the demon chant grows louder. Somewhere in the labyrinth you think you hear an undead angel soprano singing the sweeping melody.

Unbound suffix: ‘r’ (Use ‘C-g’ to abort, ‘?’ for help) [open-line]
Unbound suffix: ‘t’ (Use ‘C-g’ to abort, ‘?’ for help) [xah-fly-insert-mode-activate]
Unbound suffix: ‘f’ (Use ‘C-g’ to abort, ‘?’ for help) [backward-kill-word]
Unbound suffix: ‘d’ (Use ‘C-g’ to abort, ‘?’ for help) [xah-delete-current-text-block]
Unbound suffix: ‘b’ (Use ‘C-g’ to abort, ‘?’ for help) [xah-toggle-letter-case]
Unbound suffix: ‘w’ (Use ‘C-g’ to abort, ‘?’ for help) [xah-shrink-whitespaces]

Your jaw clenches. The adrenaline jolts your heart like a flat punch. For the first moment in your life you wonder if it would matter if you died, if anyone besides your pet goldfish would ever care.

You've bought all these democracies, set other countries to war for profit, destroyed the healthcare system for trillions, and this is this how you die?

No time to think–once more with feeling!

Unbound suffix: ‘e’ (Use ‘C-g’ to abort, ‘?’ for help) [wgrep-change-to-wgrep-mode]

With your mistap wgrep squirms out of your pocket. You shove it back in, thankful the safety mechanism prevented it from discharging over your buffers. It had cost your grandmother's life to tame that beast, trap it in a jar, and distill its vengeful spirit in a homunculus fashionably bound to your 'e'. Like a piece of bijoux.

Keep calm! Keep cool!

What about C-h k?

Unbound suffix: ‘k’ (Use ‘C-g’ to abort, ‘?’ for help) [isearch-forward]

k? Why had it been orphaned from C-h?

Keep calm! Keep cool!

You'll C-h m and see what–.

You're brought to a man page for rg. No! Quit! How'd you get here, manpage?

You successfully quit the man page, but why was your trusty C-h m not working? Was it caused by a flipped bit from gamma radiation? You try again.

Oh no! It won't stop!

Your beloved C-h m (describe-mode)! Not even help can be summoned!

You thrash your fingers in counter rhythm to the doomful dirge. Something, you have a feeling, wants your bones and organs. Wants them more badly than you do. A stroke of panic invades every cell in your body. Screw what the rg runes say!

Unbound suffix: ‘e’ (Use ‘C-g’ to abort, ‘?’ for help) [wgrep-change-to-wgrep-mode]
Unbound suffix: ‘n’ (Use ‘C-g’ to abort, ‘?’ for help) [next-error-no-select]
Unbound suffix: ‘i’ (Use ‘C-g’ to abort, ‘?’ for help) [rg-rerun-toggle-ignore]
Unbound suffix: ‘u’ (Use ‘C-g’ to abort, ‘?’ for help) [self-insert-command]
Unbound suffix: ‘e’ (Use ‘C-g’ to abort, ‘?’ for help) [wgrep-change-to-wgrep-mode]
Unbound suffix: ‘i’ (Use ‘C-g’ to abort, ‘?’ for help) [rg-rerun-toggle-ignore]
Unbound suffix: ‘n’ (Use ‘C-g’ to abort, ‘?’ for help) [next-error-no-select]

But your legs are pinned. The inverted T navigation keys prove no use. A crimson light floods the hall, dialates your pupils into saucers, blinds you. A dark shadow clad in glowing rune armor begins sprinting at you from about a hundred meters away. You know only from sound. The rumbling deepens; you feel the hollowness of your chest vibrating. The floor quakes from each footstep and dust from the ceiling irritates your eyes. Your whole body tightens and your forearms flex like steel cables as you pull out your enchanted nunchucks. You don't know how dicey this is going to get, but there is no way, not through all the recursion-depth-limit-exceeded hell you've been through, that you're going to press abort.

Your trump card!

Unbound suffix: ‘<esc>’ (Use ‘C-g’ to abort, ‘?’ for help) [xah-fly-command-mode-activate] [3 times]

You hear the sinister voice mock you.

You cannot escape from me! Your spell chest is mine! Mwahahaha...! Without your spells you would be a spec of dust, a
life sentenced to the drudgery of working paycheck to paycheck! Soon your people will be devoured, they will--

Blast! But before you have time to scrounge into your pocket, the demon has zipped a spear through space-time through your abdomen. Oddly you don't feel physical pain.

You look down and see the dark knight lifting your impaled body at the hilt. Your senses start to warp; you taste pain, smell light, hear death.

"Hey! Are you still there? Don't die on me," you hear from me, the narrator.

Your eyes start to float gently away from your skull as you see your hand feebly itching for one last motion.

You think to cast the forbidden emergency town portal: C-g. Is it pride, luck, or complete discombobulation? Your ring finger slips and C-z activates. The shadow is gone with nothing but quiet remaining. You find yourself panting, unsure if your heart was going to explode. You take a moment to read through *Messages* as you bleed out. It's cold, but it'll be over soon.

"Take the rest of me, Death" you declare. You check C-h l for your spell history, which seems to work. Ahah, there!

C-z            ;; transient-suspend

"Suspend? So that which has stripped me of all my glory yet still lives." You lose consciousness. Softly as you fade, you become aware of a jolly tune by Mariah Carrey.

You skip the scene where death delays your passing because of a bureaucratic Christmas technicality. Unfortunately the Death judiciary has slowed dramatically to curb "illegal immigration." You may not seek asylum, and awaiting trial will at least a few years. You skip this story until this paragraph:

You finally escape the labyrinthian world of clashing mode maps, where the swerve of emacs physics was fast melting your hands and eyes. Catch your breath. Recompose. Echoes of your dead spells haunt your mind. You are only a few of the survivors of the Ritual of Unbound Suffix, nemesis of all holy and just.

How would you, dear reader, belay this labyrinth, and bring balance to Emacs Land?

tl;dr

For those who use xah-fly-keys (there's less than 200 of you on planet Earth), and you struggle with transients messing with your keybindings, I've found a temporary solution to get around the issue of the keys being snagged.

;; UPDATED 
;; 2024-12-25, I found some bugs that I fixed

;; If you want to add this code to your config, you want to make sure
;; xfk is loaded FIRST, because we need to remove a hook that it adds
;; (xah-fly-keys 1)

;; xfk adds this hook in library, you want to remove it since it triggers command mode on ANY minibuffer exit
;; and when it happens in transients, you get stuck. You want to be in insert mode still
(remove-hook 'minibuffer-exit-hook 'xah-fly-command-mode-activate)
(add-hook 'minibuffer-exit-hook #'jwow/transient-aware-activate-xfk)
;; This variable transien--showp actually is truthy when transient is activet.
;; So if it is active, we don't want to switch out of insert mode
;; In all other cases, this is just activating command hook on minibuffer exit
(defun jwow/transient-aware-activate-xfk ()
  (when (not transient--showp)
    (xah-fly-command-mode-activate)))

;; The transient-exti/setup hooks are are optional, 
;; they don't always do the most convenient thing
;; Bring yourself back to command mode once ALL transients are popped out of
;; don't use xah-fly-command-mode-activate, as leaving a nested transient will exit
;; into command mode when you're in the parent transient
(add-hook 'transient-exit-hook #'jwow/transient-aware-activate-xfk)
;; Always switch to insert when activating a transient
(add-hook 'transient-setup-buffer-hook #'xah-fly-insert-mode-activate)

;; git-commit from magit for example, will put you into command mode when you're in the
;; git commit buffer, you can use this if desired:
;; (add-hook 'git-commit-mode-hook #'xah-fly-insert-mode-activate)

transient--showp is a private variable, but it comes with a doc string so this should be relatively safe to use externally for now.

Whether to show the transient popup buffer.

Does it enable popup buffers? When is it truthy? I wasn't sure if it was it, so I had to just try it.

How did I verify that this was the variable to use among all of transient variables? I instrumented jwow/transient-aware-activate-xfk with edebug, used my workflow for rg, the breakpoint snagged, and this let me look for all variables matching ^transient- and check their state during the transient's activation. After exiting the transient, I then refreshed each help buffer for each variable. Yep, mildly painful.

Anyway that's all. Frankly I'm surprised I solved this issue. Time to sleep, and hopefully to dream, free from the Curse of the Unbound Suffix :D.


In the shadows, magit-transient promises to avenge its fallen kin…


UPDATE 2024-12-25: Changed some code. I ran into a nested transient issue (try magit, ?, M), which brings up the remote transient, after the help transient. I got stuck in command mode but needed to be in insert mode.