The rejection goes away when you do anything else

An overlay that lasted until the next accepted evaluation was a durable
annotation on the source, which is not what it is: it is feedback about the
action that just failed, and the moment you move, type or evaluate it is
describing a program state nobody is in any more.

pre-command-hook rather than post-command-hook, which fires at the end of
the failing command and would take the overlay down before redisplay ever
drew it. Buffer-local and installed only while an overlay exists, so a
session of twenty buffers does not end up running this on every keystroke
in all of them.
This commit is contained in:
Joseph Ferano 2026-09-12 03:56:41 +07:00
parent 8eb6de15c5
commit c1a612abfb

View File

@ -731,9 +731,27 @@ Compared with `file-equal-p', so a symlinked or relative path still matches."
;; An error is shown where it is rather than only in the echo area, because the ;; An error is shown where it is rather than only in the echo area, because the
;; echo area is gone the moment you type and the location is the useful half of ;; echo area is gone the moment you type and the location is the useful half of
;; the message. It is cleared when the next evaluation of that buffer is ;; the message.
;; accepted: an overlay left behind after a fix is a lie about the program, and ;;
;; a stale one is worse than none. ;; It is feedback about the evaluation that just failed and not an annotation
;; on the source, so it lasts exactly as long as that: the next command in that
;; buffer takes it away, whatever the command was — a keystroke, a motion,
;; another evaluation. An overlay that survived until some later evaluation
;; was accepted outlived the thing it was about, and a stale one is worse than
;; none.
;;
;; `pre-command-hook' and not `post-command-hook': the hook has to run before
;; the *next* command, because `post-command-hook' fires at the end of the
;; failing command itself and would take the overlay down before redisplay had
;; ever drawn it.
;;
;; The hook is buffer-local and lives exactly as long as an overlay does —
;; added where one is drawn, removed where they are cleared. Globally it would
;; be a hook every buffer in the session runs on every keystroke for the sake
;; of a feature most of them will never use; buffer-locally it is also the
;; behaviour asked for, since the overlay belongs to the buffer the failed
;; evaluation came from and a command somewhere else is not "doing something
;; else in that buffer".
(defface flan-dev-error-face (defface flan-dev-error-face
'((t :inherit error :underline (:style wave))) '((t :inherit error :underline (:style wave)))
@ -749,7 +767,19 @@ Compared with `file-equal-p', so a symlinked or relative path still matches."
"Remove Flan error overlays from BUFFER, or from the current buffer." "Remove Flan error overlays from BUFFER, or from the current buffer."
(interactive) (interactive)
(with-current-buffer (or buffer (current-buffer)) (with-current-buffer (or buffer (current-buffer))
(remove-overlays (point-min) (point-max) 'flan-dev-error t))) (remove-overlays (point-min) (point-max) 'flan-dev-error t)
;; With nothing left to clear there is nothing for the hook to do, and a
;; hook that stays installed after the last overlay is gone is the half of
;; this that quietly accumulates.
(remove-hook 'pre-command-hook #'flan-dev--clear-errors-on-command t)))
(defun flan-dev--clear-errors-on-command ()
"Take this buffer's error overlays down, as a `pre-command-hook'.
Any command at all, because the overlay is about the evaluation that failed
and not about the text: moving, typing and evaluating are all something
else, and an overlay that survived a fix would be pointing at code that is
no longer wrong."
(flan-dev-clear-errors))
(defun flan-dev--show-error (loc msg) (defun flan-dev--show-error (loc msg)
"Mark MSG at LOC, if LOC names a file some buffer is visiting. "Mark MSG at LOC, if LOC names a file some buffer is visiting.
@ -771,6 +801,10 @@ Returns non-nil when it put an overlay somewhere."
(overlay-put ov 'after-string (overlay-put ov 'after-string
(propertize (concat " " msg) (propertize (concat " " msg)
'face 'flan-dev-error-message-face)) 'face 'flan-dev-error-message-face))
;; Local to this buffer, and installed only now that there is
;; something for it to remove.
(add-hook 'pre-command-hook
#'flan-dev--clear-errors-on-command nil t)
;; Point goes there too, but only in the buffer being looked at: ;; Point goes there too, but only in the buffer being looked at:
;; moving point in a buffer nobody is showing is a surprise the ;; moving point in a buffer nobody is showing is a surprise the
;; next time it is visited. ;; next time it is visited.
@ -1079,6 +1113,10 @@ of the tenth name tells you neither how many there were nor which."
(note (plist-get reply :note)) (note (plist-get reply :note))
(value (plist-get reply :value))) (value (plist-get reply :value)))
;; Accepted, so whatever the last rejection marked is no longer true. ;; Accepted, so whatever the last rejection marked is no longer true.
;; Redundant now that any command clears it — the command that ran this
;; evaluation already did — and kept because it is the claim being
;; made, not the mechanism: an accepted evaluation is never left with a
;; rejection drawn over it.
(flan-dev-clear-errors) (flan-dev-clear-errors)
;; ...and a name that was just installed should complete, and have a ;; ...and a name that was just installed should complete, and have a
;; signature, from this moment rather than from the next connect. ;; signature, from this moment rather than from the next connect.