Predicate Refreshing
I've found so far several examples of predicates not being reflected when they have changed. The entire prefix must be called to show the change. Workarounds with (transient-setup 'prefix) would badly interact with the state (for non-lisp variables). I tried a few other things like transient--redisplay along the way. If there's a right way, I couldn't find it.
(defvar transient-toys--position '(0 0) "A transient prefix location")
(transient-define-infix transient-toys--pos-infix ()
"A location, key, or command symbol"
:class 'transient-lisp-variable
:prompt "An expression such as (0 0), \"k\", 'my-suffix"
:variable 'transient-toys--position)
(transient-define-suffix transient-toys--msg-pos ()
"Message the element at location"
:if-non-nil 'transient-toys--position
:transient 'transient--do-call
(interactive)
;; lisp variables are not sent in the usual (transient-args) list. Just read
;; the value.
(message (concat (propertize "object at loc: \n" 'face 'success)
(format "%s" (transient-get-suffix transient-current-command
transient-toys--position)))))
(transient-define-prefix transient-toys-msg-location ()
"Message with the object at a location"
["Location Printing"
[("p" "position" transient-toys--pos-infix)]
[("m" "message" transient-toys--msg-pos)]])
(transient-toys-msg-location)
Currently only call path for the predicates is in transient-setup
Do we want support predicates that are static after setup? I think the user assumption is that predicates are live.
Magit simple :if predicates:
./lisp/magit-fetch.el:113: :if (lambda () (magit-get-current-remote t))
./lisp/magit-bisect.el:64: :if-not magit-bisect-in-progress-p
./lisp/magit-bisect.el:68: :if (lambda () (version<= "2.29" (magit-git-version))))
./lisp/magit-bisect.el:70: :if (lambda () (version<= "2.7" (magit-git-version))))
./lisp/magit-bisect.el:72: :if (lambda () (version<= "2.7" (magit-git-version))))]
./lisp/magit-bisect.el:77: :if magit-bisect-in-progress-p
./lisp/magit-bisect.el:81: :if (lambda () (version<= "2.7" (magit-git-version))))
./lisp/magit-sequence.el:139: :if-not magit-sequencer-in-progress-p
./lisp/magit-sequence.el:147: [:if-not magit-sequencer-in-progress-p
./lisp/magit-sequence.el:158: :if magit-sequencer-in-progress-p
./lisp/magit-sequence.el:359: :if-not magit-sequencer-in-progress-p
./lisp/magit-sequence.el:367: :if-not magit-sequencer-in-progress-p
./lisp/magit-sequence.el:371: :if magit-sequencer-in-progress-p
./lisp/magit-sequence.el:411: :if-not magit-am-in-progress-p
./lisp/magit-sequence.el:422: :if-not magit-am-in-progress-p
./lisp/magit-sequence.el:427: :if magit-am-in-progress-p
./lisp/magit-sequence.el:504: :if-not magit-rebase-in-progress-p
./lisp/magit-sequence.el:518: [:if-not magit-rebase-in-progress-p
./lisp/magit-sequence.el:527: :if-not magit-rebase-in-progress-p
./lisp/magit-sequence.el:536: :if magit-rebase-in-progress-p
./lisp/magit-sequence.el:567: :if 'magit-get-current-branch
./lisp/magit-sequence.el:581: :if 'magit-get-current-branch
./lisp/magit-notes.el:47: :if-not magit-notes-merging-p
./lisp/magit-notes.el:50: :if-not magit-notes-merging-p
./lisp/magit-notes.el:53: :if-not magit-notes-merging-p
./lisp/magit-notes.el:56: :if-not magit-notes-merging-p
./lisp/magit-notes.el:62: :if magit-notes-merging-p
./lisp/magit.el:316: ("H" "Section info" magit-describe-section :if-derived magit-mode)]
./lisp/magit.el:319: ("j" "Jump to section"magit-status-jump :if-mode magit-status-mode)
./lisp/magit.el:320: ("j" "Display status" magit-status-quick :if-not-mode magit-status-mode)
./lisp/magit.el:356: :if-derived magit-mode
./lisp/magit.el:365: :if-derived magit-mode
./lisp/magit-merge.el:45: :if-not magit-merge-in-progress-p
./lisp/magit-merge.el:55: :if-not magit-merge-in-progress-p
./lisp/magit-merge.el:65: :if magit-merge-in-progress-p
./lisp/magit-pull.el:64: :if-non-nil magit-pull-or-fetch
./lisp/magit-pull.el:68: :if-non-nil magit-pull-or-fetch
./lisp/magit-pull.el:73: ("r" magit-branch.<branch>.rebase :if magit-get-current-branch)
./lisp/magit-pull.el:88: :if 'magit-get-current-branch
./lisp/magit-pull.el:118: :if 'magit-get-current-branch
./lisp/magit-status.el:373: :if (lambda () (memq 'magit-insert-stashes magit-status-sections-hook)))
./lisp/magit-status.el:375: :if (lambda () (memq 'magit-insert-tracked-files magit-status-sections-hook)))
./lisp/magit-status.el:377: :if (lambda () (memq 'magit-insert-untracked-files magit-status-sections-hook)))
./lisp/magit-status.el:379: :if (lambda () (memq 'magit-insert-unstaged-changes magit-status-sections-hook)))
./lisp/magit-status.el:381: :if (lambda () (memq 'magit-insert-staged-changes magit-status-sections-hook)))]
./lisp/magit-status.el:383: :if (lambda () (memq 'magit-insert-unpulled-from-upstream magit-status-sections-hook)))
./lisp/magit-status.el:385: :if (lambda () (memq 'magit-insert-unpulled-from-pushremote magit-status-sections-hook)))
./lisp/magit-status.el:387: :if (lambda ()
./lisp/magit-status.el:397: :if (lambda () (memq 'magit-insert-unpushed-to-pushremote magit-status-sections-hook)))
./lisp/magit-status.el:399: :if (lambda () (memq 'magit-insert-assume-unchanged-files magit-status-sections-hook)))
./lisp/magit-status.el:401: :if (lambda () (memq 'magit-insert-skip-worktree-files magit-status-sections-hook)))]
./lisp/magit-blame.el:719: :if (lambda ()
./lisp/magit-blame.el:749: :if-nil 'buffer-file-name
./lisp/magit-blame.el:760: :if-nil 'buffer-file-name
./lisp/magit-blame.el:821: :if-non-nil 'magit-blame-mode
./lisp/magit-blame.el:908: :if-non-nil magit-blame-mode
./lisp/magit-gitignore.el:48: :if (lambda () (magit-get "core.excludesfile"))
./lisp/magit-push.el:47: [:if magit-get-current-branch
./lisp/magit-push.el:86: :if 'magit-get-current-branch
./lisp/magit-push.el:125: :if 'magit-get-current-branch
./lisp/magit-remote.el:74: :if (lambda ()
./lisp/magit-branch.el:220: :if (lambda () (version<= "2.13" (magit-git-version))))]
./lisp/magit-branch.el:222: :if (lambda ()
./lisp/magit-margin.el:70: :if-derived magit-refs-mode)])
./lisp/magit-log.el:454: [:if (lambda ()
./lisp/magit-log.el:468: [:if-mode magit-log-mode
./lisp/magit-log.el:499: [:if-not-mode magit-log-mode
./lisp/magit-log.el:515: [:if-mode magit-log-mode
./lisp/magit-diff.el:914: :if-derived magit-diff-mode)
./lisp/magit-diff.el:916: :if-derived magit-diff-mode)
./lisp/magit-diff.el:918: :if-derived magit-diff-mode)
./lisp/magit-diff.el:929: :if-mode (magit-diff-mode magit-revision-mode magit-stash-mode))]
./lisp/magit-diff.el:930: [:if-mode magit-diff-mode
I don't have a use case for static predicates, but there's all kinds of ways I want to use live predicates. Was it intentional that predicates are not live?
Hacked together an example that works. I don't think this is the shortest workaround (or appropriate for many cases).
What I determined from reading code is that (transient--insert-groups) uses (transient--layout) which was set in (transient--init-objects) via (transient--setup)
It makes sense that adding and removing children from the layout needs to be done at the same time as keymaps are created and bound or the keymap wouldn't reflect the visual information.
To hack the infix's command and re-enter the transient from scratch, the following hack works
(defvar transient-toys--position '(0 0) "A transient prefix location")
(transient-define-infix transient-toys--pos-infix ()
"A location, key, or command symbol"
:class 'transient-lisp-variable
:prompt "An expression such as (0 0), \"k\", 'my-suffix"
:transient 'transient--do-relace
:variable 'transient-toys--position)
;; we overwrite the infix's command so that it acts like a sub-prefix and is compaitble with
;; transient--do-replace
(setf (symbol-function 'transient-toys--pos-infix)
(lambda ()
(interactive)
(let ((obj (transient-suffix-object)))
(transient-infix-set obj (transient-infix-read obj)))
(transient-setup 'transient-toys-msg-location)))
(transient-define-suffix
transient-toys--msg-pos ()
"Message the element at location"
:if-non-nil 'transient-toys--position
:transient 't
(interactive)
;; lisp variables are not sent in the usual (transient-args) list. Just read
;; the value.
(message (concat (propertize "object at loc: \n" 'face 'success)
(format "%s" (transient-get-suffix transient-current-command
transient-toys--position)))))
(transient-define-prefix transient-toys-msg-location () "Message with the object
at a location" ["Location Printing" [("p" "position" transient-toys--pos-infix)]
[("m" "message" transient-toys--msg-pos)]])
(transient-toys-msg-location)
Can't think of a way to make this work purely via pre-command, but some combination of replace and recurse might force a full replacement.
transient-setup-children or adding and removing groups (because this does get picked up by transient--insert-groups) might be the more reliable pathway than predicates if groups need to be dynamically available.
Any progress on making :if* predicates live-updating? If that's too much, :inapt-if* seems like it should certainly be possible to update live (i.e., while a given prefix is still being displayed). This is presumably simpler since toggling a given infix apt/inapt won't require setting new key bindings.
Neither :if nor :inapt-if seem to live update. The only live updating slot that I've found (other than values of course) is :description. It would be very useful to have a list somewhere of which slots, if set to a functions, are update live, and which are setup only once when the prefix is laid out.
I've finally implemented this. Should have done so much earlier; it nearly worked on the first try.
This is currently opt-in per prefix, and probably will remain that way. See the documentation in the patch for details.
/cc @psionic-k @alphapapa @josephmturner
Great! Thanks, Jonas.