magit / transient

Transient commands

Home Page:https://magit.vc/manual/transient

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reached Inconsistent Transient State Detected in cc-isearch-menu

kickingvegas opened this issue · comments

Description

A sequence of key presses will trigger an “Error: Inconsistent Transient State Detected” window in the package cc-isearch-menu v1.4.0.

Environment

Steps to Reproduce

  1. In a buffer place the point on any word or symbol.
  2. Press <f2> to invoke cc-isearch-transient-menu.
  3. Press t which invokes isearch-forward-thing-at-point.
  4. Press r to invoke isearch-query-replace.

Expected Result

The “thing” selected should be set as the query string to be replaced. This would be consistent with the other menu items that allow for pulling in a word (w), symbol (s), or line (l).

Actual Result

Instead an inconsistent transient state detected error is raised.

Error Window:

⛔ Error (transient): Inconsistent transient state detected.
This should never happen.
Please open an issue and post the shown command log.
⛔ Error (transient): Inconsistent transient state detected.
This should never happen.
Please open an issue and post the shown command log.
⛔ Error (transient): Inconsistent transient state detected.
This should never happen.
Please open an issue and post the shown command log.
⛔ Error (transient): Inconsistent transient state detected.
This should never happen.
Please open an issue and post the shown command log.
⛔ Error (transient): Inconsistent transient state detected.
This should never happen.
Please open an issue and post the shown command log.

The command log:

 C-s                    ;; isearch-forward
 <f2>                   ;; cc-isearch-menu-transient
 y                      ;; isearch-yank-kill
 r                      ;; isearch-query-replace
 C-g                    ;; minibuffer-keyboard-quit
 C-a                    ;; back-to-indentation
 C-s                    ;; isearch-forward
 <f2>                   ;; cc-isearch-menu-transient
 t                      ;; isearch-forward-thing-at-point
 r                      ;; isearch-printing-char
[back]

Thanks for the report!

I remember thinking to myself when I saw that someone had created a transient for isearch "I did not expect that to work, I guess I got lucky". But now it turns out I did not.

I don't remember the details, but I think the problem is that, like transient, isearch uses transient keymaps and does unusual things. Each one in isolation works well with other things, but once they meet, things get odd. There is already a lot more compatibility code in transient to deal with isearch, than for any other package.

Fixing that will require quite a bit of work I suspect. I probably won't have the time to do that for a few more weeks.

I see, you are the author of cc-search-menu; and maybe you want to dig in yourself.

If I remember correctly, the issue is that both transient and isearch need to suspend themselves in certain situations, and when isearch suspends itself and later resumes, then that happens in a way that transient cannot notice, so isearch commands have to be adviced to inform transient that it also has to undergo a state change. Something like that.

@tarsius Thanks much for the feedback and for making Transient! As it turns out, I’ve run into this issue also in developing Casual, where if I chose to persist a Transient suffix (e.g. :transient t), some Calc functions will trigger the inconsistent state error. These functions take arguments which I’d like to implement as Transient prefix arguments so the user can quickly repeat a command with different prefix values while the menu is still raised. An example where this does work is with the calc depreciation functions.

@tarsius On wrapping isearch and Calc commands to inform a Transient of state changes, I'd appreciate any guidance on how to do this. Particularly with Calc, it seems likely that I will wrap all those functions as they do not have docstrings, which renders useless the Transient help feature. I've already started on this path with the financial functions.

From a quick look, in the case of Calc, you might be able to get this working by using funcall instead of call-interactively (which, also from a quick look, in this case seems pointless anyway).

@tarsius - So it seems that it doesn’t matter if I invoke funcall or call-interactively wrt to triggering the inconsistent state error for Calc. But further investigation shows that calling calc-hyperbolic or calc-inverse in combination with a calc-* function will trigger the error. Looking at both calc-hyperbolic and calc-inverse, they mess with the keymaps so they look suspect in using them with persisted Transient menus. Further corroborating this is that the depreciation functions don’t involve either the hyperbolic or inverse key modifiers.

This looks promising. OTOH, to avoid using the modifiers means refactoring so that I explicitly call the real calc function to do the work. For example with calc-fin-fv, I’d have to enumerate each call to calc-enter-result as a separate function.

(defun calc-fin-fv ()
  (interactive)
  (calc-slow-wrapper
   (if (calc-is-hyperbolic)
       (calc-enter-result 3 "fvl" (cons 'calcFunc-fvl (calc-top-list-n 3)))
     (let ((n (if (calc-is-option) 4 3)))
       (if (calc-is-inverse)
           (calc-enter-result n "fvb" (cons 'calcFunc-fvb (calc-top-list-n n)))
         (calc-enter-result n "fv" (cons 'calcFunc-fv (calc-top-list-n n))))))))

Well... it seems that you got very unlucky trying to add transients to two packages for which it is particularly hard to do so.

This looks promising. OTOH, to avoid using the modifiers means refactoring so that I explicitly call the real calc function to do the work.

(I haven't looked at any of this very long.) It might indeed be the case that avoiding Calc's own "fancy prefix" functionality entirely and reimplementing its logic using Transient, is your only option.

Out of curiosity, what does this "fancy prefix" thing look like? Does it only add temporary, "modal" key bindings, or does it visualize the available commands, similar (to some extend) to what Transient does?

@tarsius Looking at the Calc source, it looks like inverse and hyperbolic operations only add modal keybindings, but take no effort to visualize commands like Transient.

(defun calc-inverse (&optional n)
  (interactive "P")
  (let* ((hyp-flag (if (or
                        (eq major-mode 'calc-keypad-mode)
                        (eq major-mode 'calc-trail-mode))
                       (with-current-buffer calc-main-buffer
                         calc-hyperbolic-flag)
                     calc-hyperbolic-flag))
         (opt-flag (if (or
                        (eq major-mode 'calc-keypad-mode)
                        (eq major-mode 'calc-trail-mode))
                       (with-current-buffer calc-main-buffer
                         calc-option-flag)
                     calc-option-flag))
         (msg
          (cond
           ((and opt-flag hyp-flag) "Option Inverse Hyperbolic...")
           (hyp-flag "Inverse Hyperbolic...")
           (opt-flag "Option Inverse...")
           (t "Inverse..."))))
    (calc-fancy-prefix 'calc-inverse-flag msg n)))

(defun calc-fancy-prefix (flag msg n)
  (let (prefix)
    (calc-wrapper
     (calc-set-command-flag 'keep-flags)
     (calc-set-command-flag 'no-align)
     (setq prefix (set flag (not (symbol-value flag)))
           prefix-arg n)
     (message "%s" (if prefix msg "")))
    (and prefix
         (not calc-is-keypad-press)
         (if (boundp 'overriding-terminal-local-map)
             (setq overriding-terminal-local-map calc-fancy-prefix-map)
           (let ((event (read-event)))
             (if (eq (setq last-command-event event) ?\C-u)
                 (universal-argument)
               (if (or (not (integerp last-command-event))
                       (and (>= last-command-event 0) (< last-command-event ? )
                            (not (memq last-command-event '(?\e)))))
                   (calc-wrapper))  ; clear flags if not a Calc command.
               (setq last-command-event event)
               (if (or (not (integerp last-command-event))
                       (eq last-command-event ?-))
                   (calc-unread-command)
                 (digit-argument n))))))))

Followup on avoiding Calc inverse and hyperbolic modifiers: Made code changes to avoid using them, particularly for the financial functions and it seems like works well with persisted Transient menu behavior. Released with Casual v1.4.0.
kickingvegas/casual-calc#124