From 8dc90416d4f5da5dd42cfd35d13ba388f03b2a0e Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 25 Sep 2026 15:14:23 +0700 Subject: [PATCH] The daemon's buffer draws errors, warnings and notes in compilation's faces and the program's own output in a face of its own --- TODO.org | 6 ------ emacs/flan.el | 30 +++++++++++++++++++++++++++--- emacs/test-flan.el | 31 +++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/TODO.org b/TODO.org index 899c9b6e..e04e66a1 100644 --- a/TODO.org +++ b/TODO.org @@ -2079,12 +2079,6 @@ per-phase; making it per-form would need a resync point inside a body. CLOSED: [2026-09-25] A file with no =main= starts on a stub =main= that returns and parks; =load-file= (=C-c C-k=, already its key — the inspector stays on =C-c C-i=) keeps what compiles and lists the rest. Rules out =flan dev= with no file at all, and =--two-process= on a file with no =main=. -** NEXT The daemon buffer is navigable but not coloured -Decided 2026-09-25: errors, warnings and notes take compilation-mode's faces, and the program's own output takes a face of its own so it reads apart from the compiler's. -=*flan*= is all plain text. =compilation-minor-mode= is on (=emacs/flan.el:822=) -so =next-error= works, but a minor mode installs no font-lock. Open: whether the -program's output should look different from the compiler's. - ** DONE compilation-mode steps over the notes CLOSED: [2026-09-25] The daemon buffer and the diagnostics buffer set =compilation-skip-threshold= to diff --git a/emacs/flan.el b/emacs/flan.el index 92e42a89..36f78e31 100644 --- a/emacs/flan.el +++ b/emacs/flan.el @@ -412,6 +412,25 @@ nil keeps everything." (let ((inhibit-read-only t)) (delete-region (point-min) (line-beginning-position))))))) +(defface flan-output-face '((t :inherit font-lock-string-face)) + "Face for the running program's own output in the daemon's buffer. +It sets that output apart from what the compiler and the daemon say, whose +errors, warnings and notes take `compilation-mode''s faces." + :group 'flan) + +(defun flan--daemon-buffer-setup () + "Make the current buffer the daemon's log: navigable and coloured. +The daemon writes a diagnostic as `file:line:col: message', the shape +`compilation-minor-mode' already reads, so it only has to be switched on. +The minor mode adds its font-lock rules but turns nothing on, and a process +buffer is in `fundamental-mode', which global font-lock skips; so font-lock +is switched on here, first, or the rules would never be drawn." + (font-lock-mode 1) + ;; A log, not source: a quote the program printed opens no string. + (setq-local font-lock-keywords-only t) + (compilation-minor-mode 1) + (flan--navigable-notes)) + (defun flan--append-output (text) "Append TEXT, the running program's own output, where it can be read. Two places. The daemon's log always gets it, so output lands somewhere @@ -423,7 +442,13 @@ open, above its prompt, which is where whoever is typing there is looking." (inhibit-read-only t)) (save-excursion (goto-char (point-max)) - (insert text)) + ;; Marked as it is inserted, because nothing in the text says whose + ;; it is. A line the program prints in the diagnostic shape is + ;; still read as one by `compilation-minor-mode', and takes its + ;; face. `face' for a buffer with font-lock off, `font-lock-face' + ;; so fontification does not strip it. + (insert (propertize text 'face 'flan-output-face + 'font-lock-face 'flan-output-face))) (flan--trim-lines) ;; Follow the tail only for someone who was already at it; a reader ;; scrolled back is reading something. @@ -893,8 +918,7 @@ It builds the program first, which for a cold project is most of this." ;; program runs, and `compilation-mode' would claim it as the output of ;; one finished command — killing the process on a `recompile', among ;; other things it has no business doing to a live session. - (compilation-minor-mode 1) - (flan--navigable-notes)) + (flan--daemon-buffer-setup)) (make-process :name "flan-daemon" :buffer buf :command args diff --git a/emacs/test-flan.el b/emacs/test-flan.el index 74853a05..5fa5cd5d 100644 --- a/emacs/test-flan.el +++ b/emacs/test-flan.el @@ -533,6 +533,37 @@ already rely on it — so nothing here is a stand-in for the real thing." ;; The session is not poisoned by that: a good form still lands. (flan--eval "(defn step [] i64 (set ticks (+ ticks 100)) ticks)" "form") + ;; The daemon's buffer is coloured: the compiler's errors, warnings and + ;; notes take compilation's faces and the program's own output takes + ;; `flan-output-face'. Checked on `font-lock-face', which is what + ;; fontification writes; batch Emacs cannot turn `font-lock-mode' on, so + ;; nothing here aliases it to `face'. + (let ((flan-daemon-buffer " *flan-colour-test*")) + (with-current-buffer (get-buffer-create flan-daemon-buffer) + (insert "flan dev: built x.flan in 3ms\n" + "/tmp/a.flan:2:8: expected i32, found string\n" + "/tmp/a.flan:3:1: warning: w\n" + "/tmp/a.flan:4:1: note: n\n") + (flan--daemon-buffer-setup) + (flan--append-output "said hi\n") + (font-lock-ensure) + (let ((face-on (lambda (text) + (goto-char (point-min)) + (search-forward text) + (get-text-property (match-beginning 0) 'font-lock-face)))) + (test-flan--check + "the daemon's buffer draws an error, a warning and a note in compilation's faces" + (and (memq 'compilation-error (ensure-list (funcall face-on "a.flan:2"))) + (memq 'compilation-warning (ensure-list (funcall face-on "a.flan:3"))) + (memq 'compilation-info (ensure-list (funcall face-on "a.flan:4"))))) + (test-flan--check + "the program's output takes its own face" + (eq (funcall face-on "said") 'flan-output-face)) + (test-flan--check + "the daemon's own line is left plain" + (null (funcall face-on "flan dev:"))))) + (kill-buffer flan-daemon-buffer)) + ;; The program's own output arrives on replies and lands in the daemon's ;; buffer — no REPL is open yet, and the log is the fallback that makes a ;; println never depend on one.