end-of-defun steps over the newline, and the value was drawn on the next line

lisp.el finishes end-of-defun by skipping blanks and stepping over the
newline after the closing paren, so the bounds C-c C-c works from end at the
start of the next line. The declaration path never cared -- a newline more or
less in the text sent changes nothing -- but a value drawn at that position
sits in column 0 below the form, in the gap before the next one.

The fixture that missed it inserted the expression at point-max with nothing
after it, which is the one shape where end-of-defun has no newline to step
over. It has one now, and the check is the closing paren rather than a line
number.

The error-overlay check was the same kind of weak: it asserted the line was
not 1, which flan--text alone would have given. It asserts the token now,
which is what flan--text-at is for -- and eval-expr's replies do respect the
column padding, so the switch stands.
This commit is contained in:
Joseph Ferano 2026-09-19 03:52:41 +07:00
parent 9417fdec42
commit 8d64eda2d4
2 changed files with 35 additions and 8 deletions

View File

@ -2156,12 +2156,23 @@ declaration for it to live in."
;; 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 (flan--eval-expression (car b) (cdr b) arg)
(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) (cdr b))))))
(pulse-momentary-highlight-region (car b) end))))))
;;;###autoload
(defun flan-eval-buffer ()

View File

@ -551,7 +551,11 @@ already rely on it — so nothing here is a stand-in for the real thing."
;; 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)))
(insert "\n(+ 1 1)")
;; 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.
@ -560,6 +564,15 @@ already rely on it — so nothing here is a stand-in for the real thing."
(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.
@ -657,12 +670,15 @@ already rely on it — so nothing here is a stand-in for the real thing."
(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.
(test-flan--check "and marked where it is written, not at line 1"
;; 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 (> (line-number-at-pos
(overlay-start (car ovs)))
1))))
(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)))