A patch so that AUCTeX properly parses ConTeXt LMTX error messages.

Aside from the required lisp code, I took the liberty of adding some
comments in relevant places to assist the next person who might try to
modify this code.

                        Jim        
>From 7877108bde8e1655678d4ba5ac13c39d806ffb3d Mon Sep 17 00:00:00 2001
From: Jim Diamond <[email protected]>
Date: Mon, 2 Mar 2026 18:33:46 -0400
Subject: [PATCH] Fix parsing of error messages for ConTeXt LMTX.

* tex.el (TeX-parse-error, TeX-fin-display-help, TeX-error)
TeX-help-error): many changes to these functions to handle error
messages output by ConTeXt LMTX.  Some illuminating comments added
for the next person who touches this code.  (Bug#80350)
* context.el (TeX-ConTeXt-sentinel): rewrite to reflect ConTeXt
LMTX error messages and to use `TeX-ConTeXt-sentinel-check'
instead of `TeX-TeX-sentinel-check'.  Add function
`TeX-ConTeXt-sentinel-check' rather than adapting
`TeX-TeX-sentinel-check' since the ConTeXt LMTX error message
syntax is so different from that of pdftex et al.  (Bug#80350)
---
 context.el | 125 +++++++++++++++++++++++++++++++++++++----------------
 tex.el     | 121 ++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 182 insertions(+), 64 deletions(-)

diff --git a/context.el b/context.el
index 84b3efef..3f78d4a8 100644
--- a/context.el
+++ b/context.el
@@ -567,45 +567,94 @@ for a label to be inserted after the sectioning command."
 
 
 ;; Various
+
+;; This function is called with (current-buffer) = the output buffer.
+(defun TeX-ConTeXt-sentinel-check (process name)
+  "Check ConTeXt (LMTX) output buffer after running TeX.
+  Return  t  if errors were found."
+  ;; Set TeX-current-page to (effectively) match what
+  ;; TeX-TeX-sentinel-check does:
+  ;; -> if no errors, the number of pages shipped out; and
+  ;; -> if errors, the number of pages shipped out + 1.
+  ;; Also determine the extension of the output file.
+  (save-excursion
+    (goto-char (point-max))
+    (if (re-search-backward " > flushing realpage \\([0-9]+\\), " nil t)
+       (setq TeX-current-page (TeX-match-buffer 1))
+      (setq TeX-current-page "0"))
+    ;; If running in "don't quit on error" mode, there will be a
+    ;; '^pages .* > flushing realpage' message after the '^tex error'
+    ;; message.  In this case, no need to add 1.
+    (goto-char (point-min))
+    (if (re-search-forward "^tex error" nil t)
+       (if (re-search-forward "^pages .* > flushing realpage" nil t)
+           nil
+         (setq TeX-current-page (number-to-string
+                                 (+ 1 (string-to-number TeX-current-page))))))
+    (setq TeX-current-page (concat "{" TeX-current-page "}"))
+    (setq TeX-output-extension "pdf"))   ;; for the last 200 years now, +/-.
+
+  (if process (TeX-format-mode-line process))
+
+  (if (catch 'found
+        (while (re-search-forward
+               "^\\(?:.*tex error on line [0-9]+ in file \\(.+?\\):\\)" nil t)
+          (if (or (not (match-beginning 1))
+                  ;; Ignore non-error warning. (bug#55065)
+                  (file-exists-p (TeX-match-buffer 1)))
+              (throw 'found t))))
+      (progn
+        (if TeX-error-overview-open-after-TeX-run
+           ;; Don't leave inconsistent message.
+           (message nil)
+         (message "%s errors in `%s'. Use %s to display."
+                  name (buffer-name)
+                  (substitute-command-keys
+                   "\\<TeX-mode-map>\\[TeX-next-error]"))
+         )
+        (setq TeX-command-next TeX-command-default)
+        ;; error reported to TeX-error-report-switches
+        (setq TeX-error-report-switches
+              (plist-put TeX-error-report-switches
+                         (intern (plist-get TeX-error-report-switches
+                                            'TeX-current-master))
+                         t))
+        t)
+    ;; In case that there were only non-error warnings of type
+    ;; bug#55065, restore point to the initial position.
+    (goto-char (point-min))
+    (setq TeX-command-next TeX-command-Show)
+    nil))
+
+
 (defun TeX-ConTeXt-sentinel (process name)
-  "Cleanup TeX output buffer after running ConTeXt."
-  (cond
-   ;; Mark IV
-   ((with-current-buffer TeX-command-buffer
-      (string= ConTeXt-Mark-version "IV"))
-    (cond ((TeX-TeX-sentinel-check process name))
-          ((re-search-forward "fatal error: " nil t)
-           (message (concat name ": problems after "
-                            (TeX-current-pages)))
-           (setq TeX-command-next TeX-command-default))
-          (t
-           (message (concat name ": successfully formatted "
-                            (TeX-current-pages)))
-           (setq TeX-command-next TeX-command-Show))))
-   ;; Mark II
-   (t
-    (cond ((TeX-TeX-sentinel-check process name))
-          ((save-excursion
-             ;; in a full ConTeXt run there will multiple texutil
-             ;; outputs.  Just looking for "another run needed" would
-             ;; find the first occurence
-             (goto-char (point-max))
-             (re-search-backward "TeXUtil " nil t)
-             (re-search-forward "another run needed" nil t))
-           (message (concat "You should run ConTeXt again "
-                            "to get references right, "
-                            (TeX-current-pages)))
-           (setq TeX-command-next TeX-command-default))
-          ((re-search-forward "removed files :" nil t)
-           (message "sucessfully cleaned up"))
-          ((re-search-forward "^ ?TeX\\(Exec\\|Util\\)" nil t) ;; strange 
regexp --pg
-           (message (concat name ": successfully formatted "
-                            (TeX-current-pages)))
-           (setq TeX-command-next TeX-command-Show))
-          (t
-           (message (concat name ": problems after "
-                            (TeX-current-pages)))
-           (setq TeX-command-next TeX-command-default)))))
+  "Examine the TeX output buffer after running ConTeXt.
+
+   Parse the output buffer to collect errors and warnings if the
+   variable `TeX-parse-all-errors' is non-nil.
+
+   Open the error overview if
+   `TeX-error-overview-open-after-TeX-run' is non-nil and there are
+   errors or warnings to show."
+
+  (if (TeX-ConTeXt-sentinel-check process name)
+      (progn
+        ;; ConTeXt LMTX stops after 1 error (unless "heroic" efforts are
+        ;; made by the user to do otherwise) and if such efforts are made
+        ;; the error messages in the log file are not complete (at least
+        ;; as of ConTeXt Version 2026.02.12).  Arguably it might make
+        ;; sense to just call (TeX-parse-error) once (and set the other
+        ;; variables as seen in (TeX-parse-all-errors)).  But for now, ...
+        (if TeX-parse-all-errors
+           (TeX-parse-all-errors))
+        (if (and (with-current-buffer TeX-command-buffer
+                   TeX-error-overview-open-after-TeX-run)
+                 (TeX-error-overview-make-entries
+                 (TeX-master-directory) (TeX-active-buffer)))
+           (TeX-error-overview)))
+
+    (message (concat name ": formatted " (TeX-current-pages)))
+    (setq TeX-command-next TeX-command-Show))
   (unless TeX-error-list
     (run-hook-with-args 'TeX-after-compilation-finished-functions
                         (with-current-buffer TeX-command-buffer
diff --git a/tex.el b/tex.el
index 91d29d63..ce78d47c 100644
--- a/tex.el
+++ b/tex.el
@@ -9654,24 +9654,28 @@ displaying the issue.
 Return non-nil if an error or warning is found."
   (let ((regexp
          (concat
-          ;; TeX error
+          ;; TeX error: grab (1) filename:line-number and (2) filename:
           "^\\(!\\|\\(.+?\\):[0-9]+:\\) \\|"
-          ;; New file
+          ;; New file (or parenthesized comment): match 3
           "(\n?\\([^\n()]+\\)\\|"
-          ;; End of file.
+          ;; End of file (or comment): "match" 4
           "\\()\\)\\|"
-          ;; Hook to change line numbers
+          ;; Hook to change line numbers: match 5
           " !\\(?:offset(\\([---0-9]+\\))\\|"
-          ;; Hook to change file name
+          ;; Hook to change file name: match 6
           "name(\\([^)]+\\))\\)\\|"
-          ;; Start of LaTeX bad box
+          ;; Start of LaTeX bad box: match 7
           "^\\(\\(?:Overfull\\|Underfull\\|Tight\\|Loose\\) "
           ;;   Horizontal bad box
           "\\(?:\\\\hbox.* at lines? [0-9]+\\(?:--[0-9]+\\)?$\\|"
           ;;   Vertical bad box.  See also `TeX-warning'.
           "\\\\vbox ([ a-z0-9]+) has occurred while \\\\output is active 
\\[[^]]+\\]\\)\\)\\|"
-          ;; LaTeX warning
-          "^\\(" LaTeX-warnings-regexp ".*\\)"))
+          ;; LaTeX warning: match 8
+          "^\\(" LaTeX-warnings-regexp ".*\\)"
+         ;; ConTeXt LMTX error; sample output line:
+         ;; tex error       > tex error on line 5 in file ./bbb.tex: ! 
Undefined control sequence
+          ;; Grab (9) entire line and (10) filename
+         "\\|^\\(tex error .* in file \\([^:]*\\): \\)"))
         (error-found nil))
     (while
         (cond
@@ -9683,18 +9687,21 @@ Return non-nil if an error or warning is found."
             (beep)
             (TeX-pop-to-buffer old))
           nil)
-         ;; TeX error
-         ((match-beginning 1)
-          (if (or (not (match-beginning 2))
-                  ;; Ignore non-error warning. (bug#55065)
-                  (file-exists-p (TeX-match-buffer 2)))
+         ;; TeX/LaTeX (1) or ConTeXt LMTX (9) error:
+         ((or (match-beginning 1) (match-beginning 9))
+          (if (or ;; Ignore non-error warning. (bug#55065)
+                  (file-exists-p (TeX-match-buffer 2))
+                  (file-exists-p (TeX-match-buffer 10)))
               (progn
-                (when (match-beginning 2)
-                  (unless TeX-error-file
-                    (push nil TeX-error-file)
-                    (push nil TeX-error-offset))
-                  (unless (car TeX-error-offset)
-                    (rplaca TeX-error-file (TeX-match-buffer 2))))
+                (unless TeX-error-file
+                  (push nil TeX-error-file)
+                  (push nil TeX-error-offset))
+                (unless (car TeX-error-offset)
+                  ;; match 2 or 10 is the .tex file name.
+                  (rplaca TeX-error-file
+                   (if (match-beginning 2)
+                     (TeX-match-buffer 2)
+                     (TeX-match-buffer 10))))
                 (setq error-found t)
                 (if (looking-at "Preview ")
                     t
@@ -9815,6 +9822,7 @@ value is not used here."
           (setq-local TeX-command-buffer command-buffer)
 
           ;; Find the location of the error or warning.
+         ;; Note: we are searching in the TeX/ConTeXt *source file*.
           (let ((narrowed (buffer-narrowed-p))
                 (visible-min (point-min))
                 (visible-max (point-max))
@@ -9868,6 +9876,21 @@ value is not used here."
           (t
            (message "! %s" TeX-translate-location-error)))))
 
+
+;; TeX and ConTeXt LMTX error messages are formatted differently (and thus
+;; some tweakings are necessary for ConTeXt error messages to be parsed).
+;; E.g., in TeX:
+;; ./file.tex:9: Undefined control sequence.
+;; l.9 blah blah \undefinedINplainTEX
+;;                                       <<< line with many initial spaces
+;; and in ConTeXt:
+;; tex error       > tex error on line 9 in file ./file.tex: Undefined control 
sequence
+;;                                       <<<--- empty line here
+;; <line 3.9> 
+;;    aardvark \undefinedINcontext
+;;                                       <<<--- empty line here
+;; Because of how the parsing is done below, these formatting differences
+;; must be handled.
 (defun TeX-error (&optional store)
   "Display an error.
 
@@ -9894,16 +9917,48 @@ information in `TeX-error-list' instead of displaying 
the error."
                    (re-search-backward ":\\([0-9]+\\): "
                                        (line-beginning-position) t))
                  (string-to-number (TeX-match-buffer 1)))
+               ;; ConTeXt: ^<line n.[linenum]>\n +[error message]'
+               ((re-search-forward "^<line [^.]+[.]\\([0-9]+\\)>" nil t)
+                ;; Need this in the ConTeXt case for the 'string' search
+                ;; (just below here) to work correctly (FWIW):
+                (forward-line)
+                 (setq context-available t)
+                ;; ConTeXt has a blank line where pdftex does not; adjust:
+                (setq context-start (1+ context-start))
+                 (string-to-number (TeX-match-buffer 1)))
                 ;; nothing found
                 (t 1)))
 
-         ;; And a string of the context to search for.
+         ;; Save a string of the error token, used to position the cursor
+         ;; in the source file.
+        ;; plain TeX example of what we are looking at:
+        ;; ((point) is before ' aard...')
+        ;; l.9 aardvark \undefinedINplainTEX
+        ;;                                  \n
+        ;; ConTeXt:
+        ;; ((point) is before '    aard...')
+        ;;     aardvark \undefinedINcontext
+        ;; \n   
+        ;; The pdftex error message *always* has a line starting with
+        ;; spaces after the error line.
+        ;; ConTeXt LMTX (V 2026.02.12 anyway) does not have such a thing
+        ;; when the error token is at the end of a line.  Because of the
+        ;; space chars on the line following the pdftex error message, the
+         ;; (following) regexp in the previous version of this function 
+         ;; didn't include the \n at the end of the line(s).
+        ;; However, since ConTeXt may have no space chars on the next
+        ;; line, the match includes the \n, which causes TeX-next-error to
+        ;; position the cursor incorrectly.
+        ;; This regexp deals with this problem.
          (string (progn
                    (beginning-of-line)
-                   (re-search-forward " \\(\\([^ \t]*$\\)\\|\\($\\)\\)")
+                   (re-search-forward " \\(\\([^ \t\n\r]*$\\)\\|\\($\\)\\)")
                    (TeX-match-buffer 1)))
 
-         ;; And we have now found to the end of the context.
+         ;; (point) is now positioned at the end of the input line
+         ;; which caused this error (TeX) or the beginning of the next 
+         ;; line (ConTeXt).  To find the error context, rather than searching 
+         ;; just use (point) and locations we saved above.
          (context (if context-available
                       (buffer-substring context-start (progn (forward-line 1)
                                                              (end-of-line)
@@ -9917,7 +9972,7 @@ information in `TeX-error-list' instead of displaying the 
error."
          (file (car TeX-error-file))
          info-list)
 
-    ;; Remember where we was.
+    ;; Remember where we were.
     (setq TeX-error-point (point)
           info-list (list 'error file line error offset context string nil nil
                           TeX-error-point nil))
@@ -10204,15 +10259,29 @@ a bad box."
                         (insert-file-contents log-file nil nil nil 'replace)
                         (setq TeX--log-file-readin-modtime modtime)))
                     (goto-char (point-min))
-                    (when (and (search-forward error nil t 1)
+                    (if (and (search-forward error nil t 1)
                                (re-search-forward "^l\\." nil t)
                                (re-search-forward "^ [^\n]+$" nil t))
                       (let ((start (1+ (point))))
                         (forward-char 1)
                         (re-search-forward "^$")
                         (concat "From the .log file...\n\n"
-                                (buffer-substring start (point)))))))
-             help))))
+                                (buffer-substring start (point))))
+                     ;; ConTeXt case: assume just one error per run,
+                      ;; since it requires some ConTeXt wizardry to not
+                      ;; stop on the first error, and, if the run
+                      ;; doesn't stop on the first error, getting
+                      ;; meaningful info from the log file is very
+                      ;; difficult.
+                     (goto-char (point-max))
+                     (if (re-search-backward "^[0-9]+ ")
+                       (progn
+                         (end-of-line)
+                         (let ((start (1+ (point))))
+                           (goto-char (point-max))
+                            (concat "From the .log file...\n\n"
+                              (buffer-substring start (point)))))))))
+           help))))
     (goto-char (point-min))
     (TeX-special-mode)
     (TeX-pop-to-buffer old-buffer nil t)))
-- 
2.46.4

_______________________________________________
bug-auctex mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/bug-auctex

Reply via email to