The conditions buffer names and types the fields it used to refuse
C-c C-b asks layout with the condition's own name and draws the fields under it. The values stay refused, by name, because the shape of a condition is a fact about the build and its contents are a fact about the stopped frame — and only one of those is knowable today. A layout the daemon refuses is nil rather than an error: the buffer already draws a section saying why one is empty, and failing the whole command would take away the restarts over an annotation.
This commit is contained in:
parent
a8f08eda6d
commit
404b8559c5
@ -160,6 +160,20 @@ silently omitted.
|
||||
**`C-c C-M-b`** is the same choice as a quick one-key prompt, when you already
|
||||
know which restart you want and do not need the buffer.
|
||||
|
||||
**The condition's fields are named and typed, and have no values.** Under the
|
||||
condition you get the struct it is — `:path string`, `:tried i32` — because the
|
||||
daemon compiled the program and knows what that type looks like without asking
|
||||
the program anything. What is beside each field is a note saying the value is
|
||||
not available, not a blank: a value lives in the stopped frame, and nothing yet
|
||||
hands the break loop's condition pointer back. Knowing the shape is still worth
|
||||
having — it tells you whether the field you were about to blame is a field of
|
||||
this condition at all.
|
||||
|
||||
If that section says it could not resolve the name, read it: a package
|
||||
qualifies what it declares, so two packages' `Missing` are `a/Missing` and
|
||||
`b/Missing`. The daemon refuses a bare name and says what it could have meant
|
||||
rather than picking one.
|
||||
|
||||
After you choose, the program carries on from the restart. It never unwound, so
|
||||
everything it had is still there.
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ from fixtures, and so `flan-dev.el' is named in one place.")
|
||||
|
||||
(defconst flan-cnr-unavailable
|
||||
'((layout
|
||||
. "which struct the class name refers to [needs a daemon op: `layout'. The field names and types are in `Tast.structs', which the daemon holds because it owns the build — no running program is involved. It is the one piece of this buffer that is a day's work on one side of the socket]")
|
||||
. "the daemon could not resolve this condition's name to a struct [the `layout' op answers out of `Tast.structs' and needs a *qualified* name. A package qualifies what it declares, so two packages' `Missing' are two names and neither is `Missing'; the daemon refuses a bare one and lists what it could have meant rather than picking. A name it cannot place at all is a program built from source this daemon did not compile]")
|
||||
(value
|
||||
. "the break loop is handed the condition as an opaque pointer, and `break_loop' currently discards it; nothing at run time can render a value whose type it does not know [needs an agent verb: `condition', to stash and hand back the pointer, and a daemon-built render thunk aimed at it]")
|
||||
(site
|
||||
@ -382,20 +382,51 @@ list already says it."
|
||||
"What a stopped Flan program is offering."
|
||||
(setq buffer-read-only t))
|
||||
|
||||
(defun flan-cnr-state-from-reply (reply)
|
||||
(defun flan-cnr-state-from-reply (reply &optional fields)
|
||||
"The buffer's state, out of a `break' REPLY.
|
||||
FIELDS is the condition's layout, if it was asked for and answered — a list of
|
||||
(NAME TYPE VALUE), where VALUE is nil because no running program was consulted
|
||||
to get it.
|
||||
|
||||
Everything this cannot fill in is left nil deliberately: the renderer draws a
|
||||
section saying why rather than leaving one out, and a section that is absent
|
||||
cannot be told from one that is empty."
|
||||
cannot be told from one that is empty.
|
||||
|
||||
Takes the layout rather than fetching it, so this stays a function from data to
|
||||
data and the fixture-driven tests can drive it without a socket."
|
||||
(list :condition (plist-get reply :condition)
|
||||
:restarts (plist-get reply :restarts)
|
||||
;; Both nil deliberately: `break' answers a class name and a list of
|
||||
;; restart names, and nothing else exists to put here yet. The renderer
|
||||
;; draws a section saying why rather than leaving one out.
|
||||
:fields nil
|
||||
:fields fields
|
||||
;; Still nil deliberately: nothing here is attached to the stopped
|
||||
;; program's frames, and the renderer says so by name.
|
||||
:stack nil
|
||||
:locals nil))
|
||||
|
||||
(defun flan-cnr-layout (type)
|
||||
"The fields of TYPE, as the renderer wants them, or nil.
|
||||
`layout' is answered out of the daemon's own `Tast.structs' — it owns the
|
||||
build — so this costs no round trip to the program and works while it is
|
||||
stopped, which is the only moment this buffer exists in.
|
||||
|
||||
TYPE is the string `break' reported as `:condition', and that string is the
|
||||
qualified struct name the compiler put into the error: `Emit' takes it from
|
||||
`Types.Named', the agent holds it in `condition_name', and the daemon looks it
|
||||
up by the same name. So it resolves without anything here knowing what package
|
||||
it came from.
|
||||
|
||||
A refusal is not an error here: nil means the layout section draws the reason
|
||||
it is empty, which is the degradation the buffer is already built for."
|
||||
(when (and type (not (string-empty-p type)))
|
||||
(let ((r (funcall flan-cnr-request-function (list :op "layout" :type type))))
|
||||
(when (equal (plist-get r :status) "ok")
|
||||
(mapcar (lambda (f)
|
||||
;; The value is nil, and that is the honest half: the shape
|
||||
;; of a condition is a fact about the build, its contents
|
||||
;; are a fact about the stopped frame, and only the first is
|
||||
;; knowable today.
|
||||
(list (nth 0 f) (nth 1 f) nil))
|
||||
(plist-get r :fields))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-cnr-show ()
|
||||
"Show what the stopped program is offering, in a buffer.
|
||||
@ -410,7 +441,8 @@ walk from a running program."
|
||||
(let ((buf (get-buffer-create flan-cnr-buffer)))
|
||||
(with-current-buffer buf
|
||||
(unless (derived-mode-p 'flan-cnr-mode) (flan-cnr-mode))
|
||||
(flan-cnr--render (flan-cnr-state-from-reply r)))
|
||||
(flan-cnr--render
|
||||
(flan-cnr-state-from-reply r (flan-cnr-layout (plist-get r :condition)))))
|
||||
(pop-to-buffer buf)
|
||||
buf)))
|
||||
|
||||
|
||||
@ -465,6 +465,50 @@
|
||||
(test-flan--check "and n goes back to abort"
|
||||
(get-text-property (point) 'flan-cnr-abort)))
|
||||
|
||||
|
||||
;; The `layout' op, from this side: the condition's own name goes out as
|
||||
;; `:type' and comes back as fields with no values. Two requests are made for
|
||||
;; one `C-c C-b' — `break' then `layout' — so the stub records both.
|
||||
(let ((asked nil))
|
||||
(let ((flan-cnr-request-function
|
||||
(lambda (form)
|
||||
(push form asked)
|
||||
(pcase (plist-get form :op)
|
||||
("break" '(:status "ok" :stopped t :condition "sim/Missing"
|
||||
:restarts ("retry")))
|
||||
("layout" '(:status "ok" :type "sim/Missing"
|
||||
:fields (("path" "string") ("tried" "i32"))))))))
|
||||
(let ((text (with-current-buffer (save-window-excursion (flan-cnr-show))
|
||||
(buffer-string))))
|
||||
(test-flan--check "the condition's name is what `layout' is asked for"
|
||||
(equal (plist-get (car (last asked)) :op) "break"))
|
||||
(test-flan--check "and it is sent back verbatim, qualified as it came"
|
||||
(equal (plist-get (car asked) :type) "sim/Missing"))
|
||||
(test-flan--check "the fields are drawn, named and typed"
|
||||
(string-match-p ":path *string" text))
|
||||
;; Shape and contents are two different questions, and only the first is
|
||||
;; answerable without the pointer the break loop discards.
|
||||
(test-flan--check "and every value still says why it is missing"
|
||||
(string-match-p ":tried *i32 *value not available" text)))))
|
||||
|
||||
;; A layout the daemon refuses — a bare name it will not guess between two
|
||||
;; packages, or a type it cannot place — leaves the section drawing its reason
|
||||
;; rather than turning `C-c C-b' into an error. The restarts are the decision
|
||||
;; in front of you and they are still there.
|
||||
(let ((flan-cnr-request-function
|
||||
(lambda (form)
|
||||
(pcase (plist-get form :op)
|
||||
("break" '(:status "ok" :stopped t :condition "Missing"
|
||||
:restarts ("retry")))
|
||||
("layout" '(:status "error" :message "Missing is not a qualified name"
|
||||
:candidates ("a/Missing" "b/Missing")))))))
|
||||
(let ((text (with-current-buffer (save-window-excursion (flan-cnr-show))
|
||||
(buffer-string))))
|
||||
(test-flan--check "a refused layout is a section that says so"
|
||||
(string-match-p "not available.*needs a \\*qualified\\* name" text))
|
||||
(test-flan--check "and the restarts are drawn anyway"
|
||||
(string-match-p "\\[retry\\]" text))))
|
||||
|
||||
(let ((flan-cnr-request-function
|
||||
(lambda (_) '(:status "ok" :restarts nil :stopped nil))))
|
||||
(test-flan--check "a running program is refused, by name"
|
||||
|
||||
@ -105,6 +105,17 @@ is written instead — the real `message' call the real command makes."
|
||||
(test-flan--check "describe lists the program's globals"
|
||||
(member "ticks" (plist-get r :globals))))
|
||||
|
||||
;; `layout' against the real daemon, through `flan-cnr-layout', which is how
|
||||
;; the conditions buffer gets it. The reply is the first one with a list of
|
||||
;; lists in it, so `read' on this side is doing something it does nowhere
|
||||
;; else — and the daemon answers it without asking the program anything.
|
||||
(require 'flan-cnr)
|
||||
(let ((flan-cnr-request-function #'flan-dev--request))
|
||||
(test-flan--check "a struct's fields come back named and typed"
|
||||
(equal (flan-cnr-layout "Missing") '(("id" "i32" nil))))
|
||||
(test-flan--check "and a type the daemon cannot place is nil, not an error"
|
||||
(null (flan-cnr-layout "Nonesuch"))))
|
||||
|
||||
;; C-c C-c on the form at point: put point inside `step' and send it. The
|
||||
;; text comes from the buffer, so this exercises `beginning-of-defun' against
|
||||
;; Flan's own syntax table as much as it does the wire.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user