magit / transient

Transient commands
https://magit.vc/manual/transient
GNU General Public License v3.0
690 stars 63 forks source link

Better support less modal uses #114

Closed tarsius closed 12 months ago

tarsius commented 3 years ago

One common criticism of Transient is that it is too modal. To some extend that is by design but despite that it is possible and makes sense to make certain transients less modal. And maybe it would also be a good idea to make all transients less modal, at least optionally.

It is possible to allow arbitrary non-suffix commands to be invoked while a certain transient is active. This was possible from the start but the documentation was nearly absent and still is very bad, but see #99.

This makes it possible to define "hydra-like" transients. Here's one that I use (though honestly I am not sure this particular transient really is a good idea):

(global-set-key (kbd "M-g") #'hydrant-goto)
(transient-define-prefix hydrant-goto ()
  "Transient command replacing and extending `goto-map'."
  :transient-suffix     'transient--do-call
  :transient-non-suffix 'transient--do-stay
  [  ...snip...
   ["Goto locus"
    ("M-u" "first"     first-error)
    ("M-i" "previous"  previous-error)
    ("M-k" "next"      next-error)]
   ["Goto"
    ("M-g" "line"      goto-line      :transient nil)
    ("M-p" "position"  goto-char      :transient nil)
    ("M-c" "column"    move-to-column :transient nil)]
   ["Do"
    ("M-," "recenter"  recenter-top-bottom)]])

This allows me to (among other things) to move between search results and edit surrounding text without having leave and re-enter the transient (as long as I don't need to use any regular M-... bindings).

Many Magit commands are highly context sensitive because they act on the thing at point or at least act on that by default. (This also applies to many of its "regular" commands that don't use transient at all.)

For this package at least, it makes a lot of sense to disallow the user from moving from the original context (in which the transient was invoked) to a different context, which may call for the transient to feature different context-sensitive suffix commands.

In most cases invoking a non-suffix causes a warning in the minibuffer but switching the frame can (by design) cause the transient to be aborted instead (suspended actually). When that has happened and the user returns to the frame, then we could resume it. (This would result in behavior that is similar to what happens when you switch to another frame, while in the minibuffer.) Suspended transients can already be resumed manually, so we would just have to automate this using some hook.

It is also possible to manually suspend the active transient. (We might have to document that more prominently.) And we could turn "Unbound suffix..." warnings into automatic suspensions. I wonder if offering options to enable such behavior would satisfy those users who consider Transient to be "un-emacsian".

pkryger commented 3 years ago

FWIW I use a similar solution for smerge-mode buffers. I know there exists a few hydras definition that do the same (I think I've seen one in DOOM), but it's a git operation, so I like the transient look and feel better for that.

tarsius commented 3 years ago

I was planning to add a smerge transient to my new tray package and I might just start that copying yours but before I look at that I would like to know if you would be willing and able to assign the copyright for that to the fsf should I decide to add tray or parts thereof to gnu elpa / emacs. Otherwise it would be simpler if I just duplicated the work from the get go.

pkryger commented 3 years ago

Recently I've gotten permission from my employer to do that. I'd need to ask for forms on emacs-devel I guess.

tarsius commented 3 years ago

I have no idea when I will start getting tray into emacs (after all I just started working on that), so there is no hurry. But you might want to just get it done now anyway, also to allow you to make unrelated contributions.

Ps: I'm gonna hide this as "off-topic" in a bit.

pkryger commented 3 years ago

Sure thing (hide).

I've been thinking about adding support for draft PRs (display, create, etc.) to forge. Hopefully in a few weeks I'll have some time to put some work into that and I guess the assignment may get handy there.

tarsius commented 3 years ago

Also see #17, in particular my two longer posts there.

tarsius commented 3 years ago

Making it possible to switch to another frame while a transient is active actually is as simple as:

(define-key transient-predicate-map [handle-switch-frame] 'transient--do-stay)

But keep in mind that if you invoke the transient prefix in context A, switch to context B, and then invoke a suffix, that the suffix will then be invoked in context B.

tarsius commented 12 months ago

IMO Transient is sufficiently non-modal now.