[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: compilation-forget-errors still used by tex-mode.el

From: Markus Rost
Subject: Re: compilation-forget-errors still used by tex-mode.el
Date: Sat, 20 Mar 2004 18:01:33 +0100

As for ideas:  What about having for tex compilation parses an option

(defcustom tex-plus-error-parse-level nil
  "*If non-nil, consider also over/underfull hbox messages as errors.
This enables you to jump to the corresponding source locations."
  :type 'boolean
  :group 'tex-run)

I once wrote a corresponding hack and use it a lot.

It seems however that one can't use it directly with the new
compile.el and I don't have the time to adjust it.  In case someone is
interested, I append here the file containing the code.

===File ~/emacs/lisp/emacs-21/tex-plus.el===================
(let ((l0 (car ispell-tex-skip-alists))
      (l1 (cadr ispell-tex-skip-alists)))
  (add-to-list 'l0 '("\\\\\\(eq\\)?ref" ispell-tex-arg-end))
  (add-to-list 'l0 '("\\\\label" ispell-tex-arg-end))
  (add-to-list 'l0 '("[^\\]%.*$"))
  (add-to-list 'l1 '("gather\\*?" . "\\\\end[ \t\n]*{[ \t\n]*gather\\*?[ 
\t\n]*}") t)
  (add-to-list 'l1 '("align\\*?" . "\\\\end[ \t\n]*{[ \t\n]*align\\*?[ 
\t\n]*}") t)
  (setcar ispell-tex-skip-alists l0)
  (setcar (cdr ispell-tex-skip-alists) l1))

(require 'tex-mode)

(if (string-lessp emacs-version "21.3.49")
    (defvar tex-compile-commands nil))

(defvar tex-compile-commands-0 tex-compile-commands
  "The original value of `tex-compile-commands'
Used in combination with `tex-compile-commands-1'
Don't set this variable yourself.")

(defcustom tex-compile-commands-1 nil
  "List of additional commands for `tex-compile'.
Prepended to `tex-compile-commands-0' to set `tex-compile-commands'.
Set this variable with customize.
See `tex-compile-commands' for the syntax."
  :type '(repeat (list
                  (radio  :tag "Commands"
                          string sexp)
                  (choice :tag "Input File"
                          (string :tag "String")
                          (const :tag "TeX files of the document" t)
                          (const :tag "Don't know" nil))
                  (choice :tag "Output File"
                          (string :tag "String")
                          (const :tag "No Output File" nil))))
  :set (lambda (symbol value)
         (set-default symbol value)
         (setq tex-compile-commands
               (append value (copy-sequence tex-compile-commands-0))))
  :group 'tex-run)

;;; Customize the tex-shell.
;; I want it to use HISTFILE=~/.bash/.bash1_history

(defcustom tex-shell-hook nil
  "Hook for `tex-shell'."
  :type 'hook
  :group 'tex-run)

(defcustom tex-shell-file-name nil
  "*If non-nil, the shell file name to run in the subshell used to run TeX.
See also `tex-shell-arg-list'."
  :type '(choice (const :tag "None" nil)
  :group 'tex-run)

(defcustom tex-shell-arg-list nil
  "*Alist of options passed to subshell used to run TeX.
See also `tex-shell-file-name'."
  :type '(repeat :tag "Option list" string)
  :group 'tex-run)

(defun tex-start-shell ()
      (apply 'make-comint
       (or tex-shell-file-name explicit-shell-file-name
           (getenv "ESHELL") (getenv "SHELL") "/bin/sh")
       nil (or tex-shell-arg-list '("-i")))
    (let ((proc (get-process "tex-shell")))
      (set-process-sentinel proc 'tex-shell-sentinel)
      (process-kill-without-query proc)
      (while (zerop (buffer-size))
        (sleep-for 1)))))

;;; My error parsing.

(defadvice tex-send-tex-command (after tex-plus-compilation activate)
  (when (tex-shell-running)
    (with-current-buffer (process-buffer (tex-shell-running))
    (setq compilation-parse-errors-function
    ;; Clean up.
    (let ((buffer-undo-list t)
          (inhibit-read-only t)
      ;; Remove the text-properties added by
      ;; tex-plus-compilation-parse-errors.
      (if compilation-old-error-list
          (remove-text-properties (point-min) (point-max)
                                  '(syntax-table nil)))))))

(defvar tex-plus-start-tex-marker nil
  "Marker pointing after last TeX-running command in the TeX shell buffer.")

(defcustom tex-plus-error-parse-level nil
  "*If non-nil, consider also over/underfull hbox messages as errors.
This enables you to jump to the corresponding source locations."
  :type 'boolean
  :group 'tex-run)

;;; Error Parsing
 '((?\{ . "_")
   (?\} . "_")
   (?\[ . "_")
   (?\] . "_")
   (?\" . "_"))
 "A syntax table used by `tex-plus-compilation-parse-errors'.

Only \"(\" and \")\" are seen by backward-list.")

 '((?\( . "_")
   (?\) . "_")
   (?\{ . "_")
   (?\} . "_")
   (?\[ . "_")
   (?\] . "_")
   (?\" . "_"))
 "A syntax table used by `tex-plus-compilation-parse-errors'.

No characters are seen by backward-list.")

(defun tex-plus-compilation-parse-errors (limit-search find-at-least)
  "Parse the current buffer as TeX error messages.
See the variable `compilation-parse-errors-function' for the interface it uses.

This function parses only the last TeX compilation.

A special parsing routine is necessary, since TeX does not put file
names and line numbers on the same line as for the error messages."
  (require 'thingatpt)
  (setq compilation-error-list nil)
  (message "Parsing error messages...")
  (let (;; First a kind of buffer-save-state.
        (buffer-undo-list t) (inhibit-read-only t) deactivate-mark

        ;; Dir may have changed.
          (file-name-directory (buffer-file-name tex-last-buffer-texed)))

        ;; We use syntax-table text-properties.
        (parse-sexp-lookup-properties t)

        ;; To restore it later.
        (err-buf (current-buffer))

        ;; We need a table with only "(" and ")" seen by backward-list.
;;      (tex-plus-error-parse-syntax-table
;;       (let ((table (copy-syntax-table)))
;;         (modify-syntax-entry ?\{ "_" table)
;;         (modify-syntax-entry ?\} "_" table)
;;         (modify-syntax-entry ?\[ "_" table)
;;         (modify-syntax-entry ?\] "_" table)
;;         (modify-syntax-entry ?\" "_" table)
;;         table))

        ;; We need a table with no characters seen by backward-list.
;;      (tex-plus-error-skip-syntax-table
;;       (let ((table (copy-syntax-table)))
;;         (modify-syntax-entry ?\( "_" table)
;;         (modify-syntax-entry ?\) "_" table)
;;         (modify-syntax-entry ?\{ "_" table)
;;         (modify-syntax-entry ?\} "_" table)
;;         (modify-syntax-entry ?\[ "_" table)
;;         (modify-syntax-entry ?\] "_" table)
;;         (modify-syntax-entry ?\" "_" table)
;;         table))

        ;; We freeze point-max here to care about a running tex
        ;; process.
        (current-point-max (point-max)) output-finished

        ;; The variables to play with.
        (num-errors-found 0) parsing-end
        last-error last-linenum
        error-msg error-type error-start error-end error-text
        linenum filename error-source)

    ;; Do we reparse or parse the first time?
    (when (eq (marker-position compilation-parsing-end) 1)
      ;; If `compilation-forget-errors' would run a hook, that should
      ;; go there.  What about a compilation-forget-errors-function?
      (if (and (markerp tex-plus-start-tex-marker)
               (eq (current-buffer) (marker-buffer tex-plus-start-tex-marker)))
          (set-marker compilation-parsing-end tex-plus-start-tex-marker))
      (remove-text-properties (point-min) (point-max) '(syntax-table nil)))
    (goto-char compilation-parsing-end)

    ;; Is the tex run finished?
      (if (setq output-finished
                 "^Transcript written on " nil t))
          ;;  Then limit the parse to its output.
          (setq current-point-max output-finished)))

    ;; Parse messages.

    ;; To determine the error file we use backward-list with
    ;; syntax-table tex-plus-error-parse-syntax-table.  The messages may
    ;; contain characters "(" and ")" which irritate the scanning.
    ;; Therefore the messages receive the syntax-table text-property
    ;; tex-plus-error-skip-syntax-table.

    ;; There are basically two types of messages:  (I) true tex errors
    ;; and (II) messages about overfull hboxes and similar things.

    ;; If the user option `tex-plus-error-parse-level' is nil, type (II)
    ;; errors are neglected by default for
    ;; `compilation-error-list'. Otherwise they are treated in the
    ;; same way as type (I) errors.

    (catch 'parsing-end
      (while t

             ;; A regexp for all relevant messages.
             ;; It is probably incomplete.
             "^\\(! \\|\\(Over\\|Under\\)full \\\\.*lines? \\([0-9]+\\)\\)"
             current-point-max t)
          ;; No more messages: exit.
          (setq parsing-end current-point-max)
          (throw 'parsing-end nil))

        (goto-char (match-beginning 0))
        (setq error-msg (point-marker))
        (setq error-start error-msg)

         ((looking-at "^! Emergency stop\\.")
          ;; A special case:  We are at the end.
          (setq parsing-end current-point-max)
          (throw 'parsing-end nil))
         ((looking-at "^! ")
          ;; It is a type (I) error.
          (setq error-type t)
          ;; In a "Runaway argument?", the error lines start before the
          ;; error message "! Paragraph ended..."
          (if (looking-at "^! Paragraph ended before .* was complete.$")
                (forward-line -2)
                (if (looking-at "^Runaway argument\\?\n ")
                    (setq error-start (point-marker)))))
          (unless (re-search-forward
                   "^l\\.\\([0-9]+\\) \\(\\.\\.\\.\\)?\\(.*\\)$"
                   current-point-max t)
            ;; The output is not finished: forget this error and exit.
            (setq parsing-end error-msg)
            (throw 'parsing-end nil))
          ;; Find the end of this error.
          (goto-char (match-end 0))
          (setq error-end (point))
          ;; Get line number and text for searching the source.
          (setq linenum (string-to-int (match-string 1)))
          (setq error-text (regexp-quote (match-string 3))))

         ((looking-at "^\\(Over\\|Under\\)full \\\\.*lines? \\([0-9]*\\)")
          ;; It is a type (II) error.
          (setq error-type tex-plus-error-parse-level)
          ;; Find the end of this error.  Code in parts stolen from
          ;; TeX-warning in tex-buf.el from AUC TeX.
          (while (or (equal (current-column) 79)
                         ;; perhaps incomplete or/and incorrect.
                         ;; Maybe just exclude all lines with a \
                         ;; (except for "^! " lines)?
                         (looking-at "\\( \\\\\\|.*\\[\\]\\)"))))
          (setq error-end (point))
          (when (>= error-end current-point-max)
            ;; Output of this error is probably not finished:
            ;; forget this error and exit.
            (setq parsing-end error-msg)
            (throw 'parsing-end nil))
          ;; Get the line number of the source.
          (setq linenum (string-to-int (match-string 2)))
          ;; Getting the error-text is too hard.
          ;; It is anyway not interesting if `tex-plus-error-parse-level' is 
          (setq error-text nil)))

        ;; Put appropriate syntax around the message.
         error-start error-end
         (list 'syntax-table tex-plus-error-skip-syntax-table))

        (when error-type
          ;; Search for and in the source.
          (goto-char error-start)
            (with-syntax-table tex-plus-error-parse-syntax-table
              (if (or (null last-error)
                          (narrow-to-region (car last-error) (point))
                          (condition-case ()
                              (while (not (bobp)) (backward-list 1))
                            (error t))))
                    ;; Have to scan for the file.
                      (backward-up-list 1)
                      (skip-syntax-forward "(_")
                      (setq filename (thing-at-point 'filename))
                      (if (or (= 0 (length filename))
                              (not (file-exists-p filename)))
                          ;; We failed, but continue the parse.
                          ;; Here we could also give up with (error ...)
                          (message "Couldn't guess source file for error %s"
                      (if (equal filename (concat tex-zap-file ".tex"))
                          (set-buffer tex-last-buffer-texed)
                        (set-buffer (find-file-noselect filename)))
                        (goto-line linenum)
                        (or (null error-text)
                            (re-search-forward error-text nil t)
                            (re-search-backward error-text nil t))
                        (setq error-source (point-marker))))
                  ;; No new file.
                  (set-buffer (marker-buffer (cdr last-error)))
                  (goto-char (cdr last-error))
                  (forward-line (- linenum last-linenum))
                  ;; First try a forward search for the error text,
                  ;; then a backward search limited by the last error
                  ;; in this file, if it exists.
                  (or (null error-text)
                      (re-search-forward error-text nil t)
                      (re-search-backward error-text (cdr last-error) t))
                  (setq error-source (point-marker)))
                (set-buffer err-buf)))

          ;; Have we found more than enough errors?  Yes, if we are
          ;; beyond the requirements, if there is a previous error,
          ;; and if the following is true:  The error is in a new file
          ;; or we have parsed already 50 errors.

          ;; The limit 50 is a heuristic.  It avoids a long parse if
          ;; something really bad happened with the tex run and one
          ;; just wants to jump to the first few errors.

          ;; Such a break should be allowed by compilation-mode:  An
          ;; equivalent break happens once in a while when the grep
          ;; process is running while parsing.

          (when (and
                 (if limit-search
                     (>= error-msg limit-search) t)
                 (if find-at-least
                     (>= num-errors-found find-at-least) t)
                 (or (not (eq (marker-buffer error-source)
                              (marker-buffer (cdr last-error))))
                     (>= num-errors-found 50)))
            ;; We forget this error and exit.
            (setq parsing-end error-msg)
            (throw 'parsing-end nil))

          ;; Put it on the list and prepare for the next error.
          (setq last-error (cons error-msg error-source)
                compilation-error-list (nconc compilation-error-list
                                              (list last-error))
                num-errors-found (1+ num-errors-found)
                last-linenum linenum
                parsing-end error-end)

          (if (>= num-errors-found 10)
              (message "Parsing error messages...[%s]"
                       (+ num-errors-found
                          (length compilation-old-error-list)))))

        ;; Go to the end of message and continue the loop.
        (goto-char error-end)))

    ;; Set `compilation-parsing-end' only here, in case of an error
    ;; while parsing.
    (if (and output-finished (eq parsing-end current-point-max))
        ;; All errors are parsed.  Put compilation-parsing-end at the
        ;; very end of buffer, moving with insertions.  This avoids
        ;; unnecessary reparsing in some situations.
        (progn (set-marker compilation-parsing-end (point-max-marker))
               (set-marker-insertion-type compilation-parsing-end t))
      (set-marker compilation-parsing-end parsing-end)))

  (message "Parsing error messages...done"))

(provide 'tex-plus)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]