The prompt numbers its choices, because a name could not say which
now lists the restarts by position and sends the position, with the name alongside as the receipt the program checks. A restart below the evaluation the break is inside is shown marked rather than hidden: someone who can see a restart in their own source and not on this list has been told nothing, and the refusal carries the reason.
This commit is contained in:
parent
4a6a8fa0f7
commit
ac7d4a0e95
@ -609,12 +609,51 @@ every bit as connected and nothing like running."
|
||||
;; first. `completing-read' over them is the natural shape: they are a closed
|
||||
;; set the program computed, so require-match is exactly right.
|
||||
;;
|
||||
;; What is chosen is a *position* on that list, not a name. §4 says lookup
|
||||
;; takes the first frame offering a name, so when two frames offer `retry' the
|
||||
;; outer one is real, is on this list, and by name is unreachable — the old
|
||||
;; prompt showed `retry' twice and sent the string either way, and the inner
|
||||
;; frame took it silently. A position cannot be ambiguous, which is why SBCL
|
||||
;; identifies restarts positionally too. So the candidates are numbered and
|
||||
;; the number is what goes on the wire.
|
||||
;;
|
||||
;; `abort' is on the same list rather than on a separate key, because it is the
|
||||
;; same decision: it is what you pick when none of the restarts is the answer.
|
||||
;; It is last, and it is not the default.
|
||||
|
||||
(defun flan-dev--restart-candidates (restarts unreachable)
|
||||
"Label each of RESTARTS by its position, marking those in UNREACHABLE.
|
||||
An alist of label to index. The index leads the label because it is the
|
||||
identity: two entries may read the same and mean different frames."
|
||||
(let ((i -1))
|
||||
(mapcar (lambda (name)
|
||||
(setq i (1+ i))
|
||||
(cons (format "%d. %s%s" i name
|
||||
(if (memq i unreachable)
|
||||
" (below this break; cannot be taken)"
|
||||
""))
|
||||
i))
|
||||
restarts)))
|
||||
|
||||
(defun flan-dev-restart-at (index name)
|
||||
"Resume the stopped program at the restart at position INDEX.
|
||||
NAME is sent with it and is not the lookup: the program checks it against
|
||||
the name it holds at that position and refuses if the two have drifted
|
||||
apart, so a prompt cannot take a different restart than the one it showed."
|
||||
(let ((r (flan-dev--request (list :op "restart-at" :index index :name name))))
|
||||
(if (equal (plist-get r :status) "ok")
|
||||
(progn
|
||||
;; Accepted, not resumed — see `flan-dev-restart'.
|
||||
(setq flan-dev--stopped nil)
|
||||
(force-mode-line-update t)
|
||||
(message "flan: %s — %s" name (or (plist-get r :note) "accepted")))
|
||||
(user-error "flan: %s" (or (plist-get r :message) "refused")))))
|
||||
|
||||
(defun flan-dev-restart (name)
|
||||
"Resume the stopped program at the restart called NAME."
|
||||
"Resume the stopped program at the restart called NAME.
|
||||
The first frame offering NAME, which is §4's own rule and therefore cannot
|
||||
reach a shadowed one. `flan-break' chooses by position instead; this is
|
||||
here for a name known in advance."
|
||||
(interactive (list (completing-read "Restart: " (flan-dev-restarts) nil t)))
|
||||
(let ((r (flan-dev--request (list :op "restart" :name name))))
|
||||
(if (equal (plist-get r :status) "ok")
|
||||
@ -651,6 +690,17 @@ nothing left to serve once it has gone."
|
||||
(user-error "flan: %s" (or (plist-get r :message) "refused")))
|
||||
(plist-get r :restarts)))
|
||||
|
||||
(defun flan-dev-unreachable-restarts ()
|
||||
"Positions on the restart list that cannot be chosen.
|
||||
A restart below the evaluation a break is inside has nowhere for a transfer
|
||||
to land — the thunk holds its own channel and drops it on return. They are
|
||||
listed and marked rather than hidden, because someone who can see a restart
|
||||
in their own source and not on this list has been told nothing."
|
||||
(let ((r (flan-dev--request '(:op "break"))))
|
||||
(unless (equal (plist-get r :status) "ok")
|
||||
(user-error "flan: %s" (or (plist-get r :message) "refused")))
|
||||
(append (plist-get r :unreachable) nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-break ()
|
||||
"Show what the stopped program is offering, and choose one.
|
||||
@ -664,12 +714,21 @@ than being told so."
|
||||
(unless flan-dev--stopped
|
||||
(user-error "flan: the program is running; nothing is stopped"))
|
||||
(let* ((restarts (plist-get r :restarts))
|
||||
(unreachable (append (plist-get r :unreachable) nil))
|
||||
(table (flan-dev--restart-candidates restarts unreachable))
|
||||
(choice
|
||||
(completing-read
|
||||
(format "flan: stopped on %s%s — " flan-dev--stopped
|
||||
(if restarts "" " (no restarts are active)"))
|
||||
(append restarts '("abort")) nil t)))
|
||||
(if (equal choice "abort") (flan-dev-abort) (flan-dev-restart choice)))))
|
||||
(append (mapcar #'car table) '("abort")) nil t))
|
||||
(index (cdr (assoc choice table))))
|
||||
(cond
|
||||
((equal choice "abort") (flan-dev-abort))
|
||||
;; `require-match' over a table this built, so a choice outside it is
|
||||
;; not something a person can type — but deriving the table wrongly
|
||||
;; should say so rather than put nil on the wire as an index.
|
||||
((null index) (user-error "flan: %s is not on the list" choice))
|
||||
(t (flan-dev-restart-at index (nth index restarts)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-show-output ()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user