Subject: Re: bug#43651: 27.1; compile.el should not parse its own header for errors

From: David Landell <[email-masked]>
Date: Mon, 28 Sep 2020 16:40:45 +0200

Hi,
This is actually not restricted to font locking as demonstrated by the
test case. My initial report was a bit unclear about that. It also
affects settings like:

(setq compilation-scroll-output 'first-error)

where the jump is done to the header instead of first error. My
impression from reading the code is that we would want to avoid running
(compilation--parse-region) on the header to avoid problems like this.
I am attaching a patch that superficially seems to fix these issues.
I have a hard time judging if this fix has any bad side effects or
similar though.

I am definitely missing a bit of context how the related functions are
invoked. In testing of my own package I have seen what seems to be
multiple rounds of font locking that erase previous rounds, probably
coming from buffer changes in my downstream filter-function. In that
setup, the jump to first error has been a more stable way of reproducing.

/David

>From 4f30436f99537925f4ec83ed759e3ae098f63d3c Mon Sep 17 00:00:00 2001
From: David Landell <[email-masked]>
Date: Sun, 27 Sep 2020 16:52:44 +0200
Subject: [PATCH] Don't parse compilation buffer header for errors

 * lisp/progmodes/compile.el (compilation-header-size): New var.
 (compilation--ensure-parse): Init compilation--parsed with start of
 process output instead of (point-min).
 (compilation-start): Calculate header size.
---
 lisp/progmodes/compile.el | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index a408d16e37..4fd2ce88bc 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -1573,7 +1573,7 @@ compilation--ensure-parse
       ;; grep.el) don't need to flush-parse when they modify the buffer
       ;; in a way that impacts buffer positions but does not require
       ;; re-parsing.
-      (setq compilation--parsed (point-min-marker)))
+      (setq compilation--parsed (copy-marker (+ (point-min) compilation-header-size))))
     (when (< compilation--parsed limit)
       (let ((start (max compilation--parsed (point-min))))
         (move-marker compilation--parsed limit)
@@ -1710,6 +1710,9 @@ compilation--update-in-progress-mode-line
   ;; buffers when it changes from nil to non-nil or vice-versa.
   (unless compilation-in-progress (force-mode-line-update t)))
 
+(defvar compilation-header-size 0
+  "Size of the inserted compilation header.")
+
 ;;;###autoload
 (defun compilation-start (command &optional mode name-function highlight-regexp)
   "Run compilation command COMMAND (low level interface).
@@ -1810,14 +1813,16 @@ compilation-start
 		(eq compilation-scroll-output 'first-error))
             (set (make-local-variable 'compilation-auto-jump-to-next) t))
 	;; Output a mode setter, for saving and later reloading this buffer.
-	(insert "-*- mode: " name-of-mode
-		"; default-directory: "
-                (prin1-to-string (abbreviate-file-name default-directory))
-		" -*-\n"
-		(format "%s started at %s\n\n"
-			mode-name
-			(substring (current-time-string) 0 19))
-		command "\n")
+        (let ((header (concat "-*- mode: " name-of-mode
+                              "; default-directory: "
+                              (prin1-to-string (abbreviate-file-name default-directory))
+                              " -*-\n"
+                              (format "%s started at %s\n\n"
+                                      mode-name
+			              (substring (current-time-string) 0 19))
+		              command "\n")))
+          (insert header)
+          (setq compilation-header-size (length header)))
 	(setq thisdir default-directory))
       (set-buffer-modified-p nil))
     ;; Pop up the compilation buffer.
-- 
2.17.1