Merge: a top-level expression evaluates, and its value is drawn where it was typed
This commit is contained in:
commit
e054de610a
@ -105,14 +105,36 @@ anything. The next time that function is called, the new one runs.
|
||||
It works on the *buffer text*, not the saved file, so you do not have to save
|
||||
first.
|
||||
|
||||
If the form point is in is not a declaration — a bare `(+ 1 1)` at column 1,
|
||||
say — it is evaluated as an expression instead, and its value comes back the
|
||||
way `C-x C-e`'s does. `C-M-x` is the same command on the binding SLIME and
|
||||
CIDER use, and does the same thing: whatever is under it, the obvious thing.
|
||||
Point *inside* a `defn` body still means the `defn` — the form chosen is the
|
||||
one point is in, and only then is it asked whether it is a definition.
|
||||
|
||||
### `C-x C-e` — evaluate an expression
|
||||
|
||||
The expression before point is compiled, run **inside the running program**, and
|
||||
its value printed in the echo area. Not a copy of the program, not a simulation —
|
||||
the actual process, with its actual state.
|
||||
its value shown beside it, as `=> 2` after the form you evaluated. Not a copy of
|
||||
the program, not a simulation — the actual process, with its actual state.
|
||||
|
||||
So in a game you can type `(len enemies)` and get the real number.
|
||||
|
||||
The value goes away on your next keystroke, like every other bit of feedback
|
||||
about a command that just ran. Set `flan-inline-result` to `nil` and it goes to
|
||||
the echo area instead; that is also where it goes when there is nowhere in a
|
||||
buffer to draw it, as at the REPL.
|
||||
|
||||
The two are different questions and the settings are not two spellings of one
|
||||
thing. `flan-echo-result` is about the sentence an *install* reports — which
|
||||
names landed and what it cost — and an install has no value to draw. Where they
|
||||
would overlap, on an expression, the inline value wins and the echo is not also
|
||||
written: the same number twice is how a reader learns to skip both.
|
||||
|
||||
The form before point can also be a declaration — a `defvar` typed at the top of
|
||||
a file — in which case `C-x C-e` installs it rather than refusing it, and says
|
||||
which names changed instead of printing a value.
|
||||
|
||||
### `C-u C-c C-c` — stop there
|
||||
|
||||
The same key with a prefix argument **marks a form as a breakpoint**. `C-u C-c
|
||||
@ -137,7 +159,10 @@ hit it as many times as you like; an ordinary `C-c C-c` over the same form (or
|
||||
|
||||
`C-u C-x C-e` does the same for the expression before point: it stops *at* the
|
||||
expression instead of printing its value. That one does not stick, because there
|
||||
is no definition for it to stick to.
|
||||
is no definition for it to stick to. `C-u C-c C-c` on a top-level form that is
|
||||
an expression rather than a declaration means exactly this — there is no inside
|
||||
for a position to point at, so `C-u` and `C-u C-u` say the one thing there is to
|
||||
say, and it does not stick either.
|
||||
|
||||
### `C-c C-m` — what a macro call expands to
|
||||
|
||||
@ -747,12 +772,12 @@ Use `C-c C-g` if you need frames.
|
||||
|
||||
| Key | Does |
|
||||
|---|---|
|
||||
| `C-c C-c` | the top-level form at point, recompiled and installed |
|
||||
| `C-c C-c` | the top-level form at point: a declaration installed, anything else evaluated |
|
||||
| `C-u C-c C-c` | ...and stop at the form point is inside (`C-u C-u`: on entry) |
|
||||
| `C-M-x` | the same as `C-c C-c`, on the binding SLIME and CIDER use |
|
||||
| `C-c C-k` | the whole buffer, as one module |
|
||||
| `C-x C-e` | the expression before point, evaluated in the running program |
|
||||
| `C-u C-x C-e` | ...and stop at it instead of printing its value |
|
||||
| `C-x C-e` | the form before point, evaluated — or installed, if it is a declaration |
|
||||
| `C-u C-x C-e` | ...and stop at it instead of showing its value |
|
||||
| `C-c C-z` | connect (finds `.flan-dev.sock` upward) |
|
||||
| `C-c C-q` | disconnect |
|
||||
| `C-c C-o` | the running program's own output |
|
||||
@ -785,7 +810,8 @@ Commands with no key: `M-x flan` (start a program), `M-x flan-quit`
|
||||
|---|---|---|
|
||||
| `flan-command` | `"flan"` | the compiler binary |
|
||||
| `flan-socket-name` | `".flan-dev.sock"` | what `C-c C-z` searches for |
|
||||
| `flan-echo-result` | `t` | print `C-x C-e`'s value in the echo area |
|
||||
| `flan-echo-result` | `t` | report an accepted evaluation in the echo area |
|
||||
| `flan-inline-result` | `t` | show an expression's value beside the form, not in the echo area |
|
||||
| `flan-names-shown` | `4` | how many names to list before summarising |
|
||||
| `flan-output-buffer` | `"*flan-output*"` | where the program's output goes |
|
||||
| `flan-poll-interval` | `1.0` | seconds between checks for whether it stopped |
|
||||
|
||||
476
emacs/flan.el
476
emacs/flan.el
@ -88,6 +88,19 @@ Turning this off makes a successful evaluation indistinguishable from one
|
||||
that quietly did nothing, which is why it is on."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom flan-inline-result t
|
||||
"Whether an expression's value is shown beside the form it came from.
|
||||
|
||||
The two knobs are not two ways of saying one thing. This one is about a
|
||||
*value*: nothing else an evaluation produces has a place in the buffer to sit
|
||||
beside, so an install still reports through `flan-echo-result' whatever this
|
||||
is set to. Where they do overlap — an expression whose value could go either
|
||||
place — the overlay wins and the echo is not also written, because saying the
|
||||
same number twice is how a reader learns to stop reading both. The echo is
|
||||
what happens when no overlay could be drawn: the REPL, a value that came back
|
||||
with no region behind it, or this turned off."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom flan-names-shown 4
|
||||
"How many installed names to name before falling back to counting them."
|
||||
:type 'integer)
|
||||
@ -1303,6 +1316,12 @@ Returns non-nil when it put an overlay somewhere."
|
||||
(when buf
|
||||
(with-current-buffer buf
|
||||
(flan-clear-errors buf)
|
||||
;; A refusal is not a value, and the two must never be drawn over
|
||||
;; one form at once. Ordinarily the command that ran this
|
||||
;; evaluation already cleared the last one through the hook; this
|
||||
;; is the case where it did not, because the value being replaced
|
||||
;; was drawn by *this* command in another buffer.
|
||||
(flan-clear-result buf)
|
||||
(let* ((beg (flan--position (nth 1 parts) (nth 2 parts)))
|
||||
(end (save-excursion (goto-char beg) (line-end-position)))
|
||||
(ov (make-overlay beg end buf t nil)))
|
||||
@ -1324,6 +1343,82 @@ Returns non-nil when it put an overlay somewhere."
|
||||
(when (eq buf (current-buffer)) (goto-char beg))
|
||||
t)))))))
|
||||
|
||||
;;; Inline results
|
||||
|
||||
;; The value of an expression, drawn after the form it came from, the way eros
|
||||
;; draws one in Emacs Lisp and CIDER in Clojure. The echo area is the older
|
||||
;; half of this and is still there; what it cannot do is answer "which form
|
||||
;; was that?", which is the whole question when two expressions on adjacent
|
||||
;; lines both return 2.
|
||||
;;
|
||||
;; The lifetime argument is the error overlay's, above, and it is the same
|
||||
;; argument rather than a similar one: this is feedback about the evaluation
|
||||
;; that just ran and not an annotation on the source, so the next command in
|
||||
;; the buffer takes it away, and it is a `pre-command-hook' for the reason
|
||||
;; given there — `post-command-hook' fires at the end of the command that drew
|
||||
;; it, before redisplay has ever shown it. That is also the convention people
|
||||
;; arrive with from eros.
|
||||
;;
|
||||
;; The shape is `flan-watch--ghost-text's, deliberately: " => " and a shadow
|
||||
;; face. A watched value at a spy site and an expression's value at the form
|
||||
;; you evaluated are the same thing seen two ways, and two spellings of it
|
||||
;; would read as two features. What keeps them from fighting is that they sit
|
||||
;; at different places — the watch's at end of line, this at end of form — and
|
||||
;; where a one-line top-level form makes those the same position, two
|
||||
;; after-strings at one position stack in priority order rather than one
|
||||
;; hiding the other. They are also on different properties, so neither
|
||||
;; clearing pass reaches into the other's overlays.
|
||||
|
||||
(defface flan-result-face '((t :inherit shadow))
|
||||
"Face for an expression's value, shown beside the form it came from."
|
||||
:group 'flan)
|
||||
|
||||
(defun flan--result-overlays (&optional buffer)
|
||||
"The Flan result overlays in BUFFER, or in the current buffer."
|
||||
(with-current-buffer (or buffer (current-buffer))
|
||||
(seq-filter (lambda (o) (overlay-get o 'flan-result))
|
||||
(overlays-in (point-min) (point-max)))))
|
||||
|
||||
(defun flan-clear-result (&optional buffer)
|
||||
"Remove inline result overlays from BUFFER, or from the current buffer."
|
||||
(interactive)
|
||||
(with-current-buffer (or buffer (current-buffer))
|
||||
(remove-overlays (point-min) (point-max) 'flan-result t)
|
||||
(remove-hook 'pre-command-hook #'flan--clear-result-on-command t)))
|
||||
|
||||
(defun flan--clear-result-on-command ()
|
||||
"Take this buffer's result overlays down, as a `pre-command-hook'."
|
||||
(flan-clear-result))
|
||||
|
||||
(defun flan--show-result (value at)
|
||||
"Draw VALUE after position AT. Returns non-nil when it drew one.
|
||||
|
||||
Empty overlays rather than a region: the value is not a property of any text,
|
||||
so nothing should be highlighted and nothing should move when the buffer is
|
||||
edited under it. Returning whether it drew is what lets the caller fall back
|
||||
to the echo area instead of losing the value entirely."
|
||||
(when (and flan-inline-result at (buffer-live-p (current-buffer)))
|
||||
;; The old one first, so a second evaluation in one command — which is
|
||||
;; what `flan-eval-buffer' and a macro of these amount to — leaves one
|
||||
;; value and not a column of them.
|
||||
(flan-clear-result)
|
||||
(let ((ov (make-overlay at at nil t nil)))
|
||||
(overlay-put ov 'flan-result t)
|
||||
(overlay-put ov 'after-string
|
||||
(propertize (concat " => " value)
|
||||
'face 'flan-result-face
|
||||
;; Without this, point at end of line lands on
|
||||
;; the value rather than on the buffer's own
|
||||
;; last column. `flan-watch' found this first.
|
||||
'cursor t))
|
||||
(overlay-put ov 'evaporate nil)
|
||||
;; Under an error overlay, which is about one command and should win
|
||||
;; while it is up, and over a pause mark, which is an annotation on the
|
||||
;; program rather than on this evaluation.
|
||||
(overlay-put ov 'priority 90)
|
||||
(add-hook 'pre-command-hook #'flan--clear-result-on-command nil t)
|
||||
t)))
|
||||
|
||||
;;; Pause marks
|
||||
|
||||
;; The other overlay in this file, and deliberately not the same thing. An
|
||||
@ -1705,8 +1800,12 @@ of the tenth name tells you neither how many there were nor which."
|
||||
(t (format "%d names (%s, …)" (length names)
|
||||
(string-join (seq-take names flan-names-shown) ", ")))))
|
||||
|
||||
(defun flan--report (reply what)
|
||||
"Report REPLY, describing WHAT was sent."
|
||||
(defun flan--report (reply what &optional at)
|
||||
"Report REPLY, describing WHAT was sent.
|
||||
AT, when given, is where in the current buffer an expression's value may be
|
||||
drawn — the end of the form that was sent. A reply with no value in it never
|
||||
reaches an overlay whatever AT says, because an install is a sentence and not
|
||||
a value and has nothing to sit beside."
|
||||
(if (equal (plist-get reply :status) "ok")
|
||||
(let ((fns (plist-get reply :fns))
|
||||
(names (plist-get reply :names))
|
||||
@ -1721,41 +1820,52 @@ of the tenth name tells you neither how many there were nor which."
|
||||
;; ...and a name that was just installed should complete, and have a
|
||||
;; signature, from this moment rather than from the next connect.
|
||||
(when (or fns names) (ignore-errors (flan-refresh-defs)))
|
||||
(when flan-echo-result
|
||||
(cond
|
||||
;; An expression's value, rendered inside the running program —
|
||||
;; nothing was marshalled back, because nothing could be.
|
||||
(value (message "=> %s" value))
|
||||
;; The daemon accepted it and had nothing to send. Say so rather
|
||||
;; than claiming an install that did not happen.
|
||||
(note (message "flan: %s — %s"
|
||||
(flan--names-phrase names what) note))
|
||||
(t
|
||||
;; `:fns' are the bodies that were installed and `:names' is
|
||||
;; everything the evaluation declared; a buffer of five functions
|
||||
;; and two vars should not report as "five".
|
||||
;;
|
||||
;; A declaration with no body at all — a `defvar', a `defstruct' —
|
||||
;; has none of the first, and leading with WHAT then read as "form
|
||||
;; installed in 4 ms (also ticks)": the one name that actually
|
||||
;; changed, parenthesised as an afterthought, beside a label that
|
||||
;; says nothing. Where there are no functions the names *are* what
|
||||
;; changed, so they are what the sentence is about, and the aside
|
||||
;; is then empty by construction rather than a repeat of it. Now
|
||||
;; that `C-x C-e' reaches this path too, that sentence is also how
|
||||
;; you tell an installed declaration from an expression's `=>'.
|
||||
(message "flan: %s installed in %.0f ms%s"
|
||||
(flan--names-phrase (or fns names) what)
|
||||
(or (plist-get reply :ms) 0)
|
||||
(let ((vars (and fns (seq-difference names fns))))
|
||||
(if vars (format " (also %s)"
|
||||
(flan--names-phrase vars ""))
|
||||
"")))))))
|
||||
;; The overlay first, because whether it drew is what decides the echo
|
||||
;; area's business: a value shown beside the form does not want saying
|
||||
;; again one line below it. Independent of `flan-echo-result' on
|
||||
;; purpose — turning the echo off is a statement about the echo, and
|
||||
;; it should not silently take the inline value with it.
|
||||
(let ((shown (and value (ignore-errors (flan--show-result value at)))))
|
||||
(when (and flan-echo-result (not shown))
|
||||
(cond
|
||||
;; An expression's value, rendered inside the running program —
|
||||
;; nothing was marshalled back, because nothing could be.
|
||||
(value (message "=> %s" value))
|
||||
;; The daemon accepted it and had nothing to send. Say so rather
|
||||
;; than claiming an install that did not happen.
|
||||
(note (message "flan: %s — %s"
|
||||
(flan--names-phrase names what) note))
|
||||
(t
|
||||
;; `:fns' are the bodies that were installed and `:names' is
|
||||
;; everything the evaluation declared; a buffer of five functions
|
||||
;; and two vars should not report as "five".
|
||||
;;
|
||||
;; A declaration with no body at all — a `defvar', a `defstruct' —
|
||||
;; has none of the first, and leading with WHAT then read as "form
|
||||
;; installed in 4 ms (also ticks)": the one name that actually
|
||||
;; changed, parenthesised as an afterthought, beside a label that
|
||||
;; says nothing. Where there are no functions the names *are* what
|
||||
;; changed, so they are what the sentence is about, and the aside
|
||||
;; is then empty by construction rather than a repeat of it. Now
|
||||
;; that `C-x C-e' reaches this path too, that sentence is also how
|
||||
;; you tell an installed declaration from an expression's `=>'.
|
||||
(message "flan: %s installed in %.0f ms%s"
|
||||
(flan--names-phrase (or fns names) what)
|
||||
(or (plist-get reply :ms) 0)
|
||||
(let ((vars (and fns (seq-difference names fns))))
|
||||
(if vars (format " (also %s)"
|
||||
(flan--names-phrase vars ""))
|
||||
""))))))))
|
||||
;; The daemon reports where, so mark it there. This must not itself
|
||||
;; signal: the error the caller is owed is the daemon's, and losing it to a
|
||||
;; bad location would report the wrong thing entirely.
|
||||
(let ((loc (plist-get reply :loc))
|
||||
(msg (plist-get reply :message)))
|
||||
;; A refusal is not a value. `flan--show-error' clears the buffer it
|
||||
;; marks, but a rejection the client cannot place — no `:loc', or a file
|
||||
;; nobody is visiting — marks nothing, and leaving the last value up
|
||||
;; beside a form that was just refused is the lie this prevents.
|
||||
(ignore-errors (flan-clear-result))
|
||||
(ignore-errors (flan--show-error loc (or msg "rejected")))
|
||||
(user-error "flan: %s%s" (or msg "rejected")
|
||||
(if loc (format " (%s)" loc) "")))))
|
||||
@ -1776,7 +1886,10 @@ mark is drawn over here. Nothing is inserted in the buffer; see docs/DISCUSS.md
|
||||
(list :op "eval" :code code :file (or buffer-file-name "<buffer>"))
|
||||
(when pause
|
||||
(list :pause (flan--wire-position (car pause))))))))
|
||||
(flan--report reply what)
|
||||
;; END as the place a value could go. Every caller of this sends a
|
||||
;; declaration and declarations have no value, so this is the path that
|
||||
;; stays open rather than one anybody takes today.
|
||||
(flan--report reply what end)
|
||||
;; `flan--report' signals on a rejection, so reaching here means it
|
||||
;; landed. Flashing the text that was sent answers "which form did that
|
||||
;; take?" — the question the echo area cannot, because point may be nowhere
|
||||
@ -1864,21 +1977,202 @@ daemon reads that as stopping on entry instead."
|
||||
(scan-error nil)))
|
||||
b))))
|
||||
|
||||
;; Which of the two evaluators a key runs is decided by the form it would
|
||||
;; send, not by where the cursor is sitting. Point inside a `defn' body, on
|
||||
;; `(+ ticks 1)', must still evaluate that expression under `C-x C-e' — the
|
||||
;; enclosing `defn' is not what was asked for and reinstalling it would be a
|
||||
;; different command. `C-c C-c' picks a different form, and asks the same
|
||||
;; question of it: the top-level form point is *in*, which for a bare
|
||||
;; `(+ 1 1)' written at column 1 is that expression and nothing else.
|
||||
;;
|
||||
;; Hardcoded, which is the thing to be careful about: the authority is the
|
||||
;; declaration arm of `Parse.expr' in lib/parse.ml (the "is a top-level
|
||||
;; declaration, not an expression" refusal, around line 458), plus the
|
||||
;; `declare'/`declare-c' arm a few forms below it. This list is that set and
|
||||
;; has to be changed with it. `defunion' is being renamed `defdata', with
|
||||
;; `defunion' becoming a C-style untagged union; both spellings are top-level
|
||||
;; declarations either way, so this list wants `defdata' adding when that
|
||||
;; lands rather than a swap.
|
||||
;;
|
||||
;; `package' is deliberately not here even though flan-mode.el's
|
||||
;; `flan--definers' has it. This is the set of heads that *fail* when sent to
|
||||
;; the expression evaluator, which is the bug `C-x C-e' was fixed for, and a
|
||||
;; `package' form does not fail — `Parse.expr' has no arm refusing it. It is
|
||||
;; still a declaration, so `C-c C-c' has to keep installing it; see
|
||||
;; `flan--defun-heads' below, which is this list plus that one head.
|
||||
(defconst flan--declaration-heads
|
||||
'("defmacro" "defn" "defvar" "defconst" "defstruct" "defunion" "defenum"
|
||||
"defalias" "import" "declare" "declare-c")
|
||||
"Heads whose form is a declaration, and never an expression.")
|
||||
|
||||
;; The two keys ask *nearly* the same question, and `package' is where the two
|
||||
;; readings come apart. `C-x C-e' asks "would this fail if I sent it to the
|
||||
;; expression evaluator?", and a `package' form would not: `Parse.expr' has no
|
||||
;; arm refusing it, so it falls through to a call of an unknown name and the
|
||||
;; message would be a worse one than the declaration path gives. `C-c C-c'
|
||||
;; asks the plain question — "is this a declaration?" — and `package' is one:
|
||||
;; `Parse.decl' has an arm for it, `C-c C-c' has always installed it, and
|
||||
;; routing it to the expression evaluator would take that away.
|
||||
(defconst flan--defun-heads (cons "package" flan--declaration-heads)
|
||||
"Heads `C-c C-c' recompiles and installs rather than evaluating.")
|
||||
|
||||
(defun flan--declaration-head-at (pos &optional heads)
|
||||
"HEAD when the form starting at POS is one of HEADS, at top level, else nil.
|
||||
HEADS defaults to `flan--declaration-heads'.
|
||||
|
||||
Two questions, and both have to answer yes. The depth at POS says whether
|
||||
the form is top-level — `syntax-ppss' at the open delimiter reports the depth
|
||||
*before* it, so a form nobody has nested reads as 0 — and that is what keeps
|
||||
an inner expression inside a `defn' body on the expression path even when the
|
||||
enclosing form is a declaration. The head then says whether the thing is a
|
||||
declaration at all, which is what leaves a bare `(+ 1 1)' at column 1 alone.
|
||||
|
||||
Requiring both rather than the head alone is the more conservative of the two
|
||||
readings, and the one the user asked for: a `defn' written inside a `let' is
|
||||
not a definition of anything, and installing it as one would quietly accept
|
||||
code the compiler is right to refuse. Sent as an expression it gets the
|
||||
parser's own message, which says exactly that.
|
||||
|
||||
The remaining condition is that the form is not inside a string or a comment,
|
||||
where a `defn' is prose and not a definition. The depth says nothing about
|
||||
that: a form at column 1 inside a comment is at depth 0 like any other.
|
||||
|
||||
One known limit, left as one: `syntax-ppss' reports depth from the accessible
|
||||
portion of the buffer, so in a narrowed buffer a form that is nested in the
|
||||
file reads as top-level here. Widening to ask would be a different decision
|
||||
about what `C-x C-e' means in a narrowed buffer, and it is not this one's to
|
||||
make."
|
||||
(save-excursion
|
||||
;; Every `syntax-ppss' call before the `looking-at' below: it moves point
|
||||
;; and clobbers the match data, and the head is read back out of that
|
||||
;; match.
|
||||
(let ((state (syntax-ppss pos)))
|
||||
(goto-char pos)
|
||||
(and (zerop (car state))
|
||||
(not (nth 3 state)) ; inside a string
|
||||
(not (nth 4 state)) ; inside a comment
|
||||
(looking-at "([ \t\n]*\\(\\(?:\\sw\\|\\s_\\)+\\)")
|
||||
(member (match-string-no-properties 1)
|
||||
(or heads flan--declaration-heads))
|
||||
(match-string-no-properties 1)))))
|
||||
|
||||
(defun flan--declaration-before-point ()
|
||||
"The top-level declaration `C-x C-e' would send, as (HEAD START END), or nil.
|
||||
|
||||
`flan--declaration-head-at' asks the two questions; this one finds the form to
|
||||
ask them about, and has a third of its own before either is worth asking:
|
||||
there must *be* a form before point. `backward-sexp' does not signal when
|
||||
there is nothing behind it — it goes to the beginning of the buffer and stays
|
||||
there, which at point-min is no movement at all — and the form it then looks
|
||||
at is the one *after* point, whose head is very likely a declaration and whose
|
||||
text is empty. `C-x C-e' at the top of a file used to install that: a `defn'
|
||||
by name, with no body, out of a region zero characters wide. So START must
|
||||
have moved."
|
||||
(save-excursion
|
||||
(let ((end (point)))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(backward-sexp)
|
||||
(let ((start (point)))
|
||||
(and (> end start)
|
||||
(let ((head (flan--declaration-head-at start)))
|
||||
(and head (list head start end))))))
|
||||
(scan-error nil)))))
|
||||
|
||||
(defun flan--eval-expression (start end arg)
|
||||
"Evaluate START..END in the running program as an expression, and report it.
|
||||
|
||||
One function and not one per key, because \"the two keys do the same thing on
|
||||
an expression\" is the whole of what routing buys: two call sites assembling
|
||||
their own `eval-expr' request is exactly how they drift apart again.
|
||||
|
||||
ARG is a flag rather than a position. The expression sent *is* the target, so
|
||||
the daemon wraps it in a `(pause)' before checking it and the thunk breaks
|
||||
where it stands; there is no inside for a position to point at, which is also
|
||||
why one `C-u' and two mean the same thing here.
|
||||
|
||||
`flan--text-at' rather than `flan--text': an expression is not a top-level
|
||||
form and does not start at column 1, so a refusal the daemon reports against
|
||||
it carries a column measured from the start of the snippet. Padding both ways
|
||||
is what puts the error overlay on the character it is about — and the overlay
|
||||
this draws on success would otherwise be competing with one drawn at line 1."
|
||||
(flan--report
|
||||
(flan--request
|
||||
(append
|
||||
(list :op "eval-expr" :code (flan--text-at start end)
|
||||
:file (or buffer-file-name "<buffer>"))
|
||||
(when arg (list :pause t))))
|
||||
"expression"
|
||||
end))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-eval-defun (&optional arg)
|
||||
"Recompile the top-level form at point and install it in the running program.
|
||||
"Evaluate the top-level form at point in the running program.
|
||||
|
||||
With a prefix ARG, also mark a form inside it so the program stops there when
|
||||
it next runs — `C-u' the form point is inside, `C-u C-u' the top-level form
|
||||
itself, which means stopping on entry. The buffer is not edited: the daemon
|
||||
is told where the form is and splices the call in after parsing, so every
|
||||
location in the file stays where it was. The mark sticks until the same form
|
||||
is evaluated without a prefix."
|
||||
A declaration — a `defn', a `defvar', anything in `flan--defun-heads' — is
|
||||
recompiled and installed, which is what this key has always done. Anything
|
||||
else is an expression, and is evaluated and its value shown, because a bare
|
||||
`(+ 1 1)' written at the top of a file is a form like any other and refusing
|
||||
it was an editor artifact rather than a property of the language. Every other
|
||||
Lisp's `C-M-x' does the natural thing with whatever is under it, and so does
|
||||
this one.
|
||||
|
||||
`C-c C-c' and `C-M-x' are one command and both route. Splitting them would
|
||||
mean two docstrings and a rule to remember, and nothing is bought by it:
|
||||
somebody who writes an expression and presses `C-c C-c' has exactly the
|
||||
complaint that made this change, and `C-c C-c' with point inside a `defn' is
|
||||
untouched either way — the form picked is still the enclosing declaration, and
|
||||
`C-c C-k' is still the key that means \"declarations, all of them\".
|
||||
|
||||
With a prefix ARG on the declaration path, also mark a form inside it so the
|
||||
program stops there when it next runs — `C-u' the form point is inside, `C-u
|
||||
C-u' the top-level form itself, which means stopping on entry. The buffer is
|
||||
not edited: the daemon is told where the form is and splices the call in after
|
||||
parsing, so every location in the file stays where it was. The mark sticks
|
||||
until the same form is evaluated without a prefix.
|
||||
|
||||
On the expression path a prefix is a flag and cannot be anything else, exactly
|
||||
as it is for `C-u C-x C-e': the expression sent is the target, and the mark
|
||||
does not stick because a thunk is built and thrown away, leaving no
|
||||
declaration for it to live in."
|
||||
(interactive "P")
|
||||
(let* ((b (flan--defun-bounds))
|
||||
(pause (flan--pause-bounds b arg)))
|
||||
(flan--eval (flan--text (car b) (cdr b)) "form" (car b) (cdr b)
|
||||
pause)))
|
||||
;; The head is asked of the form's *start* rather than of point, so
|
||||
;; the question is about the form `C-c C-c' already picked and the
|
||||
;; selection rule is untouched. `flan--defun-bounds' takes the form
|
||||
;; point is inside; `flan--declaration-before-point' takes the one
|
||||
;; behind point, and swapping this to that would silently turn
|
||||
;; `C-c C-c' from the middle of a `defn' body into `C-x C-e'.
|
||||
(head (and (< (car b) (cdr b))
|
||||
(flan--declaration-head-at (car b) flan--defun-heads))))
|
||||
(cond
|
||||
(head (flan--eval (flan--text (car b) (cdr b)) "form" (car b) (cdr b)
|
||||
(flan--pause-bounds b arg)))
|
||||
;; An empty buffer, or point past the last form in one with nothing at
|
||||
;; all behind it: `beginning-of-defun' and `end-of-defun' both stay put
|
||||
;; and the bounds come back zero characters wide. Sending that asks the
|
||||
;; daemon to evaluate the empty string, which it answers — an empty
|
||||
;; program is a valid one — and the echo area then reports a success for
|
||||
;; an evaluation nobody made. The same refusal `C-x C-e' gives.
|
||||
((= (car b) (cdr b))
|
||||
(user-error "flan: no top-level form at point to evaluate"))
|
||||
(t
|
||||
;; `end-of-defun' does not stop at the closing paren: `lisp.el' skips
|
||||
;; the blanks after it and then steps over the newline, so END is the
|
||||
;; start of the *next* line in every file that ends a line after a form
|
||||
;; — which is every file. The declaration path never noticed, because
|
||||
;; a newline more or less in the text sent changes nothing. This one
|
||||
;; draws a value at END, and at the untrimmed one it lands in column 0
|
||||
;; of the line below, over the gap before the next form.
|
||||
(let ((end (save-excursion (goto-char (cdr b))
|
||||
(skip-chars-backward " \t\n")
|
||||
(point))))
|
||||
(flan--eval-expression (car b) end arg)
|
||||
;; Flashed for the reason the declaration path flashes: point may be
|
||||
;; nowhere near the form `beginning-of-defun' actually found, and the
|
||||
;; value alone does not say which one that was. Only on success —
|
||||
;; `flan--report' signals on a rejection.
|
||||
(pulse-momentary-highlight-region (car b) end))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-eval-buffer ()
|
||||
@ -1895,81 +2189,6 @@ arrive in the same load or the first refers to storage that does not exist."
|
||||
;; buffer is not feedback, it is a flicker.
|
||||
(flan-clear-pause))
|
||||
|
||||
;; Which of the two evaluators `C-x C-e' runs is decided by the form it would
|
||||
;; send, not by where the cursor is sitting. Point inside a `defn' body, on
|
||||
;; `(+ ticks 1)', must still evaluate that expression — the enclosing `defn' is
|
||||
;; not what was asked for and reinstalling it would be a different command.
|
||||
;;
|
||||
;; Hardcoded, which is the thing to be careful about: the authority is the
|
||||
;; declaration arm of `Parse.expr' in lib/parse.ml (the "is a top-level
|
||||
;; declaration, not an expression" refusal, around line 458), plus the
|
||||
;; `declare'/`declare-c' arm a few forms below it. This list is that set and
|
||||
;; has to be changed with it. `defunion' is being renamed `defdata', with
|
||||
;; `defunion' becoming a C-style untagged union; both spellings are top-level
|
||||
;; declarations either way, so this list wants `defdata' adding when that
|
||||
;; lands rather than a swap.
|
||||
;;
|
||||
;; `package' is deliberately not here even though flan-mode.el's
|
||||
;; `flan--definers' has it. This is the set of heads that *fail* when sent to
|
||||
;; the expression evaluator, which is the bug being fixed; a package
|
||||
;; declaration is a statement about the file being read and not a change to
|
||||
;; make to a running program.
|
||||
(defconst flan--declaration-heads
|
||||
'("defmacro" "defn" "defvar" "defconst" "defstruct" "defunion" "defenum"
|
||||
"defalias" "import" "declare" "declare-c")
|
||||
"Heads whose form is a declaration, and never an expression.")
|
||||
|
||||
(defun flan--declaration-before-point ()
|
||||
"The top-level declaration `C-x C-e' would send, as (HEAD START END), or nil.
|
||||
|
||||
Two questions, and both have to answer yes. The depth at START says whether
|
||||
the form is top-level — `syntax-ppss' at the open delimiter reports the depth
|
||||
*before* it, so a form nobody has nested reads as 0 — and that is what keeps
|
||||
an inner expression inside a `defn' body on the expression path even when the
|
||||
enclosing form is a declaration. The head then says whether the thing is a
|
||||
declaration at all, which is what leaves a bare `(+ 1 1)' at column 1 alone.
|
||||
|
||||
Requiring both rather than the head alone is the more conservative of the two
|
||||
readings, and the one the user asked for: a `defn' written inside a `let' is
|
||||
not a definition of anything, and installing it as one would quietly accept
|
||||
code the compiler is right to refuse. Sent as an expression it gets the
|
||||
parser's own message, which says exactly that.
|
||||
|
||||
Three things have to be true before either question is worth asking, and the
|
||||
first of them is that there *is* a form before point. `backward-sexp' does
|
||||
not signal when there is nothing behind it — it goes to the beginning of the
|
||||
buffer and stays there, which at point-min is no movement at all — and the
|
||||
form it then looks at is the one *after* point, whose head is very likely a
|
||||
declaration and whose text is empty. `C-x C-e' at the top of a file used to
|
||||
install that: a `defn' by name, with no body, out of a region zero characters
|
||||
wide. So START must have moved. The other two are that the form is not
|
||||
inside a string or a comment, where a `defn' is prose and not a definition.
|
||||
|
||||
One known limit, left as one: `syntax-ppss' reports depth from the accessible
|
||||
portion of the buffer, so in a narrowed buffer a form that is nested in the
|
||||
file reads as top-level here. Widening to ask would be a different decision
|
||||
about what `C-x C-e' means in a narrowed buffer, and it is not this one's to
|
||||
make."
|
||||
(save-excursion
|
||||
(let ((end (point)))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(backward-sexp)
|
||||
(let* ((start (point))
|
||||
;; Every `syntax-ppss' call before the `looking-at' below:
|
||||
;; it moves point and clobbers the match data, and the head
|
||||
;; is read back out of that match.
|
||||
(state (syntax-ppss start)))
|
||||
(and (> end start)
|
||||
(zerop (car state))
|
||||
(not (nth 3 state)) ; inside a string
|
||||
(not (nth 4 state)) ; inside a comment
|
||||
(looking-at "([ \t\n]*\\(\\(?:\\sw\\|\\s_\\)+\\)")
|
||||
(member (match-string-no-properties 1)
|
||||
flan--declaration-heads)
|
||||
(list (match-string-no-properties 1) start end))))
|
||||
(scan-error nil)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-eval-last-sexp (&optional arg)
|
||||
"Evaluate the form before point in the running program and report it.
|
||||
@ -1984,9 +2203,10 @@ expression\" and the only way to evaluate it was `C-c C-c'. That was an
|
||||
editor artifact and not a property of the language.
|
||||
|
||||
`flan--declaration-before-point' decides which, off the form that would be
|
||||
sent rather than off where point is; `C-c C-c' is unchanged and stays the
|
||||
explicit \"reload the definition I am standing in\" command, which is still
|
||||
the one to use from inside a body.
|
||||
sent rather than off where point is. `C-c C-c' routes the same way now, but
|
||||
off a different form — the one point is *inside* — so it stays the explicit
|
||||
\"reload the definition I am standing in\" command and is still the one to use
|
||||
from inside a body.
|
||||
|
||||
With a prefix ARG, stop there instead. For an expression that is a flag and
|
||||
not a position — the expression sent *is* the target, and it is wrapped in a
|
||||
@ -2008,10 +2228,9 @@ does, until the same form is evaluated again without a prefix."
|
||||
;; daemon installed no bodies and declared no names.
|
||||
(flan--eval (flan--text start end) head start end
|
||||
(and arg (cons start end))))
|
||||
(let* ((start (save-excursion
|
||||
(condition-case nil (backward-sexp) (scan-error nil))
|
||||
(point)))
|
||||
(code (buffer-substring-no-properties start (point))))
|
||||
(let ((start (save-excursion
|
||||
(condition-case nil (backward-sexp) (scan-error nil))
|
||||
(point))))
|
||||
;; Nothing behind point is nothing to send. `backward-sexp' does not
|
||||
;; signal at the beginning of a buffer, it simply stays there, so the
|
||||
;; region measured out is empty and the daemon is asked to evaluate
|
||||
@ -2022,12 +2241,7 @@ does, until the same form is evaluated again without a prefix."
|
||||
;; file and the form meant was the one *after* it.
|
||||
(when (= start (point))
|
||||
(user-error "flan: no form before point to evaluate"))
|
||||
(flan--report
|
||||
(flan--request
|
||||
(append
|
||||
(list :op "eval-expr" :code code :file (or buffer-file-name "<buffer>"))
|
||||
(when arg (list :pause t))))
|
||||
"expression")))))
|
||||
(flan--eval-expression start (point) arg)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun flan-eval-region (start end)
|
||||
|
||||
@ -30,6 +30,17 @@ is written instead — the real `message' call the real command makes."
|
||||
(advice-remove 'message probe))
|
||||
said))
|
||||
|
||||
(defun test-flan--result ()
|
||||
"The text of the inline result overlay in this buffer, or nil.
|
||||
An expression's value now goes beside the form rather than into the echo
|
||||
area, so this is where the assertions that used to read `test-flan--said'
|
||||
read instead. Overlays exist under --batch — the error-overlay checks below
|
||||
already rely on it — so nothing here is a stand-in for the real thing."
|
||||
(let ((ovs (flan--result-overlays)))
|
||||
(and (= 1 (length ovs))
|
||||
(substring-no-properties
|
||||
(or (overlay-get (car ovs) 'after-string) "")))))
|
||||
|
||||
(defun test-flan--check (name ok)
|
||||
(if ok (message " ok %s" name)
|
||||
(setq test-flan--failures (1+ test-flan--failures))
|
||||
@ -484,11 +495,192 @@ is written instead — the real `message' call the real command makes."
|
||||
(test-flan--check "nor does a bare expression at top level"
|
||||
(null (flan--declaration-before-point)))
|
||||
;; ...and it still evaluates as one, which is the half of this key that
|
||||
;; was already working and must not have moved.
|
||||
;; was already working and must not have moved. The value is now drawn
|
||||
;; beside the form and no longer echoed, so both halves are asserted: an
|
||||
;; overlay that says it, and an echo area that does not say it twice.
|
||||
(let ((said (test-flan--said (flan-eval-last-sexp))))
|
||||
(test-flan--check "which C-x C-e evaluates and prints"
|
||||
(and said (string-match-p "5" said)
|
||||
(string-match-p "=>" said))))
|
||||
(test-flan--check "which C-x C-e evaluates and shows beside the form"
|
||||
(let ((r (test-flan--result)))
|
||||
(and r (string-match-p "5" r)
|
||||
(string-match-p "=>" r))))
|
||||
(test-flan--check "and does not also say it in the echo area"
|
||||
(not (and said (string-match-p "=>" said))))
|
||||
;; The overlay is anchored to the form that produced it, not to point
|
||||
;; and not to the start of the line: two expressions one under the
|
||||
;; other both returning 2 is the case the echo area cannot answer.
|
||||
(test-flan--check "the value is drawn at the end of the form"
|
||||
(let ((ovs (flan--result-overlays)))
|
||||
(and (= 1 (length ovs))
|
||||
(= (overlay-start (car ovs)) (point-max)))))
|
||||
;; It lasts exactly as long as the evaluation it is about. The hook is
|
||||
;; buffer-local and installed only while there is something to remove,
|
||||
;; the way the error overlays' is.
|
||||
(test-flan--check "and a command in the buffer takes it away"
|
||||
(progn
|
||||
(test-flan--check
|
||||
"with a hook installed while it is up"
|
||||
(memq #'flan--clear-result-on-command
|
||||
(buffer-local-value 'pre-command-hook
|
||||
(current-buffer))))
|
||||
(flan--clear-result-on-command)
|
||||
(and (null (flan--result-overlays))
|
||||
(not (memq #'flan--clear-result-on-command
|
||||
(buffer-local-value
|
||||
'pre-command-hook
|
||||
(current-buffer))))))))
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; Switched off, the echo area is what is left: the two settings are not two
|
||||
;; spellings of one thing, and a value has to come out somewhere.
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point)))
|
||||
(insert "\n(+ 7 8)")
|
||||
(let* ((flan-inline-result nil)
|
||||
(said (test-flan--said (flan-eval-last-sexp))))
|
||||
(test-flan--check "with the overlay off the value goes back to the echo area"
|
||||
(and said (string-match-p "15" said)
|
||||
(string-match-p "=>" said)
|
||||
(null (flan--result-overlays)))))
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; ── C-c C-c on what is not a declaration ─────────────────────────────
|
||||
;;
|
||||
;; The complaint this routing came from: `(+ 1 1)' written at the top level
|
||||
;; of a buffer, `C-M-x' pressed, and the parser's "is a top-level
|
||||
;; declaration, not an expression" coming back — because the key only ever
|
||||
;; had the one path. `C-M-x' and `C-c C-c' are one command and both route.
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point)))
|
||||
;; The trailing newline is the point, not tidiness: `end-of-defun' steps
|
||||
;; over it, so the bounds it hands back end at the start of the next line
|
||||
;; and a value drawn at that position appears in column 0 below the form.
|
||||
;; Without the newline here the bug is invisible.
|
||||
(insert "\n(+ 1 1)\n")
|
||||
;; Point *inside* the form, which is where `C-c C-c' is pressed from and
|
||||
;; the difference between this and `C-x C-e': the form is the one point is
|
||||
;; in, not the one behind it.
|
||||
(search-backward "1 1")
|
||||
(test-flan--check "C-M-x on a bare top-level expression evaluates it"
|
||||
(progn (flan-eval-defun)
|
||||
(let ((r (test-flan--result)))
|
||||
(and r (string-match-p "=> 2" r)))))
|
||||
(test-flan--check "and draws the value after the closing paren"
|
||||
(let ((ovs (flan--result-overlays)))
|
||||
(and (= 1 (length ovs))
|
||||
(= (char-before (overlay-start (car ovs))) ?\))
|
||||
(= (line-number-at-pos (overlay-start (car ovs)))
|
||||
(line-number-at-pos
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(search-forward "(+ 1 1)")))))))
|
||||
;; What a keystroke would do. There are no commands under --batch, so the
|
||||
;; `pre-command-hook' that takes a value down never runs and the next
|
||||
;; check below would be reading this one's overlay.
|
||||
(flan-clear-result)
|
||||
(goto-char (point-max))
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; And the other half, which is the one that must not have moved: point in
|
||||
;; the middle of a `defn' body still reinstalls the enclosing declaration.
|
||||
;; `flan--defun-bounds' takes the form point is *inside* and
|
||||
;; `flan--declaration-before-point' the one *before* point, and routing
|
||||
;; C-c C-c off the second would have turned this into C-x C-e.
|
||||
(goto-char (point-min))
|
||||
(search-forward "(+ ticks 41)")
|
||||
(test-flan--check "the head is asked of the form point is inside"
|
||||
(equal (flan--declaration-head-at
|
||||
(car (flan--defun-bounds)))
|
||||
"defn"))
|
||||
(let ((said (test-flan--said (flan-eval-defun))))
|
||||
(test-flan--check "C-c C-c from inside a defn body still installs it"
|
||||
(and said (string-match-p "\\_<step\\_>" said)
|
||||
(string-match-p "installed" said)))
|
||||
(test-flan--check "and draws no value beside it"
|
||||
(null (flan--result-overlays))))
|
||||
|
||||
;; `package' is the one head the two keys disagree about, and it is a real
|
||||
;; disagreement rather than an oversight: `Parse.expr' has no arm refusing
|
||||
;; it, so C-x C-e's "would this fail as an expression" says no, while
|
||||
;; `Parse.decl' has one and C-c C-c has always installed it. Routing both
|
||||
;; off one list would have quietly taken that away. Nothing is sent here —
|
||||
;; the claim is only about which path each key picks.
|
||||
(with-temp-buffer
|
||||
(flan-mode)
|
||||
(insert "(package demo)\n")
|
||||
(goto-char (point-min))
|
||||
(search-forward "demo")
|
||||
(test-flan--check "C-c C-c treats a package form as a declaration"
|
||||
(equal (flan--declaration-head-at
|
||||
(car (flan--defun-bounds)) flan--defun-heads)
|
||||
"package"))
|
||||
(goto-char (point-max))
|
||||
(test-flan--check "and C-x C-e does not, for the reason it never has"
|
||||
(null (flan--declaration-before-point))))
|
||||
|
||||
;; A prefix on the expression path is a flag and not a position — there is
|
||||
;; no inside for one to point at — so both `C-u' and `C-u C-u' mean the one
|
||||
;; thing, which is `C-u C-x C-e's behaviour reached through the other key.
|
||||
;;
|
||||
;; Checked by reading the request rather than by sending it: `:pause t' on a
|
||||
;; thunk parks the program in the break loop, and doing that here would hand
|
||||
;; every test below this line a stopped daemon. What the daemon then does
|
||||
;; with the flag is test_dev.ml's question anyway; this one is only whether
|
||||
;; the client sends a flag where it cannot send a position.
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point))
|
||||
(sent nil))
|
||||
(insert "\n(+ 3 4)")
|
||||
(search-backward "3 4")
|
||||
(let ((probe (lambda (req) (setq sent req) '(:status "ok" :value "7"))))
|
||||
(advice-add 'flan--request :override probe)
|
||||
(unwind-protect
|
||||
(progn
|
||||
(flan-eval-defun '(4))
|
||||
(test-flan--check "C-u C-M-x on an expression sends the flag"
|
||||
(and (equal (plist-get sent :op) "eval-expr")
|
||||
(eq (plist-get sent :pause) t)))
|
||||
(flan-eval-defun '(16))
|
||||
(test-flan--check "and C-u C-u means the same, having nowhere else to point"
|
||||
(eq (plist-get sent :pause) t))
|
||||
(flan-eval-defun)
|
||||
(test-flan--check "and no prefix sends no flag at all"
|
||||
(null (plist-member sent :pause))))
|
||||
(advice-remove 'flan--request probe)))
|
||||
(test-flan--check "none of which leaves a pause mark behind"
|
||||
(null (flan--pause-overlays)))
|
||||
(flan-clear-result)
|
||||
(goto-char (point-max))
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; A refusal must never be drawn as a value. This is the collision the two
|
||||
;; overlays could have had: one command, one form, and both paths wanting
|
||||
;; the end of the same line.
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point)))
|
||||
(insert "\n(+ 1 nonsense)")
|
||||
(search-backward "nonsense")
|
||||
(ignore-errors (flan-eval-defun))
|
||||
(test-flan--check "a refused expression gets no value overlay"
|
||||
(null (flan--result-overlays)))
|
||||
(test-flan--check "and is marked as an error instead"
|
||||
(let ((ovs (flan--error-overlays)))
|
||||
(and (= 1 (length ovs))
|
||||
(string-match-p
|
||||
"unknown name"
|
||||
(or (overlay-get (car ovs) 'help-echo) "")))))
|
||||
;; The padding argument, which is why the expression path sends
|
||||
;; `flan--text-at': the daemon numbers from the start of what it was sent,
|
||||
;; so an unpadded snippet puts this overlay on line 1 of the file. The
|
||||
;; column is the half `flan--text' alone would not have fixed, so the
|
||||
;; check is the token and not the line — a client that padded lines only
|
||||
;; would pass "not line 1" and still point at the open paren.
|
||||
(test-flan--check "and marked at the word it is about, not at line 1"
|
||||
(let ((ovs (flan--error-overlays)))
|
||||
(and ovs (save-excursion
|
||||
(goto-char (overlay-start (car ovs)))
|
||||
(looking-at-p "nonsense")))))
|
||||
(flan-clear-errors)
|
||||
(goto-char (point-max))
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; A declaration can be written where it is prose and not a declaration, and
|
||||
@ -685,9 +877,11 @@ is written instead — the real `message' call the real command makes."
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point)))
|
||||
(insert "\n(+ 20 3)")
|
||||
(let ((said (test-flan--said (flan-eval-last-sexp))))
|
||||
(test-flan--check "C-x C-e works while the program is stopped"
|
||||
(and said (string-match-p "23" said))))
|
||||
(flan-eval-last-sexp)
|
||||
(test-flan--check "C-x C-e works while the program is stopped"
|
||||
(let ((r (test-flan--result)))
|
||||
(and r (string-match-p "23" r))))
|
||||
(flan-clear-result)
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; And installing, which the break loop allows on purpose: there is no frame
|
||||
@ -719,9 +913,11 @@ is written instead — the real `message' call the real command makes."
|
||||
(goto-char (point-max))
|
||||
(let ((beg (point)))
|
||||
(insert "\n(+ 1 1)")
|
||||
(let ((said (test-flan--said (flan-eval-last-sexp))))
|
||||
(test-flan--check "and everything works again afterwards"
|
||||
(and said (string-match-p "2" said))))
|
||||
(flan-eval-last-sexp)
|
||||
(test-flan--check "and everything works again afterwards"
|
||||
(let ((r (test-flan--result)))
|
||||
(and r (string-match-p "2" r))))
|
||||
(flan-clear-result)
|
||||
(delete-region beg (point-max)))
|
||||
|
||||
;; ── The documentation buffer ──────────────────────────────────────────
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user