Skip to content

partially unwinding a recursive edit leaves transient in a bad state #425

@Zoybean

Description

@Zoybean

Summary:

the advice function transient--recursive-edit incorrectly handles unwinds from using e.g. (throw 'exit t) from within a recursive edit.
This appeared for me when using a function similar to read-string-from-buffer.
Reproduced in emacs -q with only (require 'transient)

Steps:

first, define a modified read-string-from-buffer. the key difference is the (throw 'exit "Aborted edit"), which causes the recursive edit to signal an error when the user presses C-c C-k.

(defun my/read-string-from-buffer (prompt string)
  (string-edit
   prompt
   string
   (lambda (edited)
     (setq string edited)
     (exit-recursive-edit))
   :abort-callback (lambda ()
                     (throw 'exit "Aborted edit")))
  (recursive-edit)
  string)

next, define a transient that relies on this behaviour, handling the error with some specific behaviour:

(transient-define-prefix foo-prefix ()
  ["args"
   ("-m" "message" "--message="
    :reader (lambda (p s h)
              (condition-case er
                  (my/read-string-from-buffer p (or s ""))
                (error (prog1 "cancel"
                         (princ er))))))
   ("-o" "other" "--other-infix")
   ("-a" "another" "--another-infix=")])

finally, invoke the foo-prefix, invoke the -m infix, and cancel the input buffer with C-c C-k.

Expected:

The transient returns to the foo-prefix with --message=cancel.

Actual:

The transient buffer is left in a broken state and does not accept further interaction. typing simply types into the active buffer rather than affecting the transient.

Remarks:

  • I have not bothered with a backtrace as the relevant error never reaches the toplevel.
  • I have a patch incoming to resolve this, but it is fairly simple. The issue seems to be the lack of an unwind-protect in the tail end of transient--recursive-edit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions