changeset 17623:a09fd9348b0d

Support compilers that give a message each time the file being compiled changes but don't include a file name each error message. Speed up by searching for regexps one by one instead of combining. (compile-internal): Takes more optional arguments. All five regexp alists can be given as argument. Change name of variable regexp-alist to error-regexp-alist. Change some local variables directly by setq instead of rebinding by let. (compilation-shell-minor-mode): New minor mode. Similar to compilation-minor-mode, but key bindings don't collide with shell mode. (compilation-shell-minor-mode-map, compilation-shell-minor-mode): New variables. (compile-auto-highlight): Doc fix. (compilation-error-regexp-alist): Removed unnecessary line break in first regexp. Replaced \\(\\|.* on \\) by \\(.* on \\)? in regexp for Absoft FORTRAN 77 Compiler 3.1.3. Added regexp for SPARCcompiler Pascal. Divided long line in regexp for Cray C compiler error messages. Made comment fit in line at regexp for Sun Ada (VADS, Solaris). FILE-IDX may be nil, meaning an error message with no file name, so the file name must be taken from an earlier message. LINE-IDX may be a function which is called with two arguments the file name and column strings and returns an error position descriptor. (compilation-enter-directory-regexp-alist) (compilation-leave-directory-regexp-alist): New variables. (compilation-file-regexp-alist) (compilation-nomessage-regexp-alist): New variables. (grep-regexp-alist): Removed unnecessary ^ at beginning of regexp. (compilation-enter-directory-regexp) (compilation-leave-directory-regexp): Variables deleted. Replaced by compilation-enter-directory-regexp-alist and compilation-leave-directory-regexp-alist. (compilation-buffer-p): Return true also for buffer in compilation-shell-minor-mode. (compilation-next-error-locus): Split a long line. (count-regexp-groupings): Comment about this function not being needed any more. (compilation-current-file, compilation-regexps); New variables. (compilation-parse-errors): Large parts rewritten. Don't put the regexps together in one large regexp, instead match them one by one. Support the generalized subexpression indices. (compile-collect-regexps, compile-buffer-substring): New functions supporting compilation-parse-errors.
author Richard M. Stallman <rms@gnu.org>
date Sat, 03 May 1997 04:37:52 +0000
parents 742a57e66f7d
children 7634c31da26e
files lisp/progmodes/compile.el
diffstat 1 files changed, 379 insertions(+), 280 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/progmodes/compile.el	Fri May 02 07:27:07 1997 +0000
+++ b/lisp/progmodes/compile.el	Sat May 03 04:37:52 1997 +0000
@@ -40,7 +40,7 @@
 
 (defvar compile-auto-highlight nil
   "*Specify how many compiler errors to highlight (and parse) initially.
-\(Highlighting applies to ean error message when the mouse is over it.)
+\(Highlighting applies to an error message when the mouse is over it.)
 If this is a number N, all compiler error messages in the first N lines
 are highlighted and parsed as soon as they arrive in Emacs.
 If t, highlight and parse the whole compilation output as soon as it arrives.
@@ -140,8 +140,7 @@
     ;; We'll insist that the number be followed by a colon or closing
     ;; paren, because otherwise this matches just about anything
     ;; containing a number with spaces around it.
-    ("\
-\\([a-zA-Z]?:?[^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)\\([) \t]\\|\
+    ("\\([a-zA-Z]?:?[^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)\\([) \t]\\|\
 :\\([^0-9\n]\\|\\([0-9]+:\\)\\)\\)" 1 2 5)
 
     ;; Microsoft C/C++:
@@ -184,7 +183,7 @@
     ;; Absoft FORTRAN 77 Compiler 3.1.3
     ;;  error on line 19 of fplot.f: spelling error?
     ;;  warning on line 17 of fplot.f: data type is undefined for variable d
-    ("\\(\\|.* on \\)[Ll]ine[ \t]+\\([0-9]+\\)[ \t]+\
+    ("\\(.* on \\)?[Ll]ine[ \t]+\\([0-9]+\\)[ \t]+\
 of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2)
 
     ;; Apollo cc, 4.3BSD fc:
@@ -214,6 +213,16 @@
     ;; IBM AIX lint is too painful to do right this way.  File name
     ;; prefixes entire sections rather than being on each line.
 
+    ;; SPARCcompiler Pascal:
+    ;;           20      linjer      : array[1..4] of linje;
+    ;; e 18480-----------^---  Inserted ';'
+    ;; and
+    ;; E 18520 line 61 -  0 is undefined
+    ;; These messages don't contain a file name. Instead the compiler gives
+    ;; a message whenever the file being compiled is changed.
+    (" +\\([0-9]+\\) +.*\n[ew] [0-9]+-+" nil 1)
+    ("[Ew] +[0-9]+ line \\([0-9]+\\) -  " nil 1)
+
     ;; Lucid Compiler, lcc 3.x
     ;; E, file.cc(35,52) Illegal operation on pointers
     ("[EW], \\([^(\n]*\\)(\\([0-9]+\\),[ \t]*\\([0-9]+\\)" 1 2 3)
@@ -223,7 +232,8 @@
 \\([0-9]+\\):\\(\\([0-9]+\\)[: \t]\\)?" 1 2 4)
 
     ;; Cray C compiler error messages
-    ("\\(cc\\| cft\\)-[0-9]+ c\\(c\\|f77\\): ERROR \\([^,\n]+, \\)* File = \\([^,\n]+\\), Line = \\([0-9]+\\)" 4 5)
+    ("\\(cc\\| cft\\)-[0-9]+ c\\(c\\|f77\\): ERROR \\([^,\n]+, \\)* File = \
+\\([^,\n]+\\), Line = \\([0-9]+\\)" 4 5)
 
     ;; IBM C/C++ Tools 2.01:
     ;;  foo.c(2:0) : informational EDC0804: Function foo is not referenced.
@@ -237,7 +247,7 @@
 
     ;; Perl -w:
     ;; syntax error at automake line 922, near "':'"
-    ("\n.* at \\([^ ]+\\) line \\([0-9]+\\)," 1 2)
+    (".* at \\([^ ]+\\) line \\([0-9]+\\)," 1 2)
     )
   "Alist that specifies how to match errors in compiler output.
 Each elt has the form (REGEXP FILE-IDX LINE-IDX [COLUMN-IDX FILE-FORMAT...])
@@ -248,6 +258,56 @@
 try; %s in the string is replaced by the text matching the FILE-IDX'th
 subexpression.")
 
+(defvar compilation-enter-directory-regexp-alist
+  '(
+    ;; Matches lines printed by the `-w' option of GNU Make.
+    (".*: Entering directory `\\(.*\\)'$" 1)
+    )
+  "Alist specifying how to match lines that indicate a new current directory.
+Note that the match is done at the beginning of lines.
+Each elt has the form (REGEXP IDX).
+If REGEXP matches, the IDX'th subexpression gives the directory name.
+
+The default value matches lines printed by the `-w' option of GNU Make.")
+
+(defvar compilation-leave-directory-regexp-alist
+  '(
+    ;; Matches lines printed by the `-w' option of GNU Make.
+    (".*: Leaving directory `\\(.*\\)'$" 1)
+    )
+"Alist specifying how to match lines that indicate restoring current directory.
+Note that the match is done at the beginning of lines.
+Each elt has the form (REGEXP IDX).
+If REGEXP matches, the IDX'th subexpression gives the name of the directory
+being moved from. If IDX is nil, the last directory entered \(by a line
+matching `compilation-enter-directory-regexp-alist'\) is assumed.
+
+The default value matches lines printed by the `-w' option of GNU Make.")
+
+(defvar compilation-file-regexp-alist
+  '(
+    ;; This matches entries with date time year file-name: like
+    ;; Thu May 14 10:46:12 1992  mom3.p:
+    ("\\w\\w\\w \\w\\w\\w +[0-9]+ [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]  \\(.*\\):$" 1)
+    )
+  "Alist specifying how to match lines that indicate a new current file.
+Note that the match is done at the beginning of lines.
+Each elt has the form (REGEXP IDX).
+If REGEXP matches, the IDX'th subexpression gives the file name. This is
+used with compilers that don't indicate file name in every error message.")
+
+;; There is no generally useful regexp that will match non messages, but
+;; in special cases there might be one. The lines that are not matched by
+;; a regexp take much longer time than the ones that are recognized so if
+;; you have same regexeps here, parsing is faster.
+(defvar compilation-nomessage-regexp-alist
+  '(
+    )
+  "Alist specifying how to match lines that have no message.
+Note that the match is done at the beginning of lines.
+Each elt has the form (REGEXP). This alist is by default empty, but if
+you have some good regexps here, the parsing of messages will be faster.")
+
 (defvar compilation-read-command t
   "If not nil, M-x compile reads the compilation command to use.
 Otherwise, M-x compile just uses the value of `compile-command'.")
@@ -257,7 +317,7 @@
 Otherwise, it saves all modified buffers without asking.")
 
 (defvar grep-regexp-alist
-  '(("^\\([a-zA-Z]?:?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
+  '(("\\([a-zA-Z]?:?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
   "Regexp used to match grep hits.  See `compilation-error-regexp-alist'.")
 
 ;; The system null device. (Should reference NULL_DEVICE from C.)
@@ -329,22 +389,6 @@
 				    (concat \"make -k \"
 					    buffer-file-name))))))")
 
-(defvar compilation-enter-directory-regexp
-  ".*: Entering directory `\\(.*\\)'$"
-  "Regular expression matching lines that indicate a new current directory.
-This must contain one \\(, \\) pair around the directory name.
-
-The default value matches lines printed by the `-w' option of GNU Make.")
-
-(defvar compilation-leave-directory-regexp
-  ".*: Leaving directory `\\(.*\\)'$"
-  "Regular expression matching lines that indicate restoring current directory.
-This may contain one \\(, \\) pair around the name of the directory
-being moved from.  If it does not, the last directory entered \(by a
-line matching `compilation-enter-directory-regexp'\) is assumed.
-
-The default value matches lines printed by the `-w' option of GNU Make.")
-
 (defvar compilation-directory-stack nil
   "Stack of previous directories for `compilation-leave-directory-regexp'.
 The head element is the directory the compilation was started in.")
@@ -373,7 +417,7 @@
 	   compilation-error-regexp-alist)
    (list
     ;;
-    ;; Compiler output lines.  Recognise `make[n]:' lines too.
+    ;; Compiler output lines.  Recognize `make[n]:' lines too.
     '("^\\([A-Za-z_0-9/\.+-]+\\)\\(\\[\\([0-9]+\\)\\]\\)?[ \t]*:"
       (1 font-lock-function-name-face) (3 font-lock-comment-face nil t)))
    ))
@@ -464,19 +508,27 @@
     (grep command-args)))
 
 (defun compile-internal (command error-message
-				 &optional name-of-mode parser regexp-alist
-				 name-function)
+				 &optional name-of-mode parser
+				 error-regexp-alist name-function
+				 enter-regexp-alist leave-regexp-alist
+				 file-regexp-alist nomessage-regexp-alist)
   "Run compilation command COMMAND (low level interface).
 ERROR-MESSAGE is a string to print if the user asks to see another error
-and there are no more errors.  Third argument NAME-OF-MODE is the name
-to display as the major mode in the compilation buffer.
-
-Fourth arg PARSER is the error parser function (nil means the default).  Fifth
-arg REGEXP-ALIST is the error message regexp alist to use (nil means the
-default).  Sixth arg NAME-FUNCTION is a function called to name the buffer (nil
-means the default).  The defaults for these variables are the global values of
-\`compilation-parse-errors-function', `compilation-error-regexp-alist', and
-\`compilation-buffer-name-function', respectively.
+and there are no more errors.  The rest of the arguments, 3-10 are optional.
+For them nil means use the default.
+NAME-OF-MODE is the name to display as the major mode in the compilation
+buffer.  PARSER is the error parser function.  ERROR-REGEXP-ALIST is the error
+message regexp alist to use.  NAME-FUNCTION is a function called to name the
+buffer.  ENTER-REGEXP-ALIST is the enter directory message regexp alist to use.
+LEAVE-REGEXP-ALIST is the leave directory message regexp alist to use.
+FILE-REGEXP-ALIST is the change current file message regexp alist to use.
+NOMESSAGE-REGEXP-ALIST is the nomessage regexp alist to use.
+  The defaults for these variables are the global values of
+\`compilation-parse-errors-function', `compilation-error-regexp-alist',
+\`compilation-buffer-name-function', `compilation-enter-directory-regexp-alist',
+\`compilation-leave-directory-regexp-alist', `compilation-file-regexp-alist',
+\ and `compilation-nomessage-regexp-alist', respectively.
+For arg 7-10 a value `t' means an empty alist.
 
 Returns the compilation buffer created."
   (let (outbuf)
@@ -508,9 +560,18 @@
       ;; In case the compilation buffer is current, make sure we get the global
       ;; values of compilation-error-regexp-alist, etc.
       (kill-all-local-variables))
-    (let ((regexp-alist (or regexp-alist compilation-error-regexp-alist))
-	  (parser (or parser compilation-parse-errors-function))
-	  (thisdir default-directory)
+    (or error-regexp-alist
+	(setq error-regexp-alist compilation-error-regexp-alist))
+    (or enter-regexp-alist
+	(setq enter-regexp-alist compilation-enter-directory-regexp-alist))
+    (or leave-regexp-alist
+	(setq leave-regexp-alist compilation-leave-directory-regexp-alist))
+    (or file-regexp-alist
+	(setq file-regexp-alist compilation-file-regexp-alist))
+    (or nomessage-regexp-alist
+	(setq nomessage-regexp-alist compilation-nomessage-regexp-alist))
+    (or parser (setq parser compilation-parse-errors-function))
+    (let ((thisdir default-directory)
 	  outwin)
       (save-excursion
 	;; Clear out the compilation buffer and make it writable.
@@ -536,7 +597,16 @@
 	;; (setq buffer-read-only t)  ;;; Non-ergonomic.
 	(set (make-local-variable 'compilation-parse-errors-function) parser)
 	(set (make-local-variable 'compilation-error-message) error-message)
-	(set (make-local-variable 'compilation-error-regexp-alist) regexp-alist)
+	(set (make-local-variable 'compilation-error-regexp-alist)
+	     error-regexp-alist)
+	(set (make-local-variable 'compilation-enter-directory-regexp-alist)
+	     enter-regexp-alist)
+	(set (make-local-variable 'compilation-leave-directory-regexp-alist)
+	     leave-regexp-alist)
+	(set (make-local-variable 'compilation-file-regexp-alist)
+	     file-regexp-alist)
+	(set (make-local-variable 'compilation-nomessage-regexp-alist)
+	     nomessage-regexp-alist)
 	(setq default-directory thisdir
 	      compilation-directory-stack (list default-directory))
 	(set-window-start outwin (point-min))
@@ -612,6 +682,30 @@
     map)
   "Keymap for `compilation-minor-mode'.")
 
+(defvar compilation-shell-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [mouse-2] 'compile-mouse-goto-error)
+    (define-key map "\M-\C-m" 'compile-goto-error)
+    (define-key map "\M-\C-n" 'compilation-next-error)
+    (define-key map "\M-\C-p" 'compilation-previous-error)
+    (define-key map "\M-{" 'compilation-previous-file)
+    (define-key map "\M-}" 'compilation-next-file)
+    ;; Set up the menu-bar
+    (define-key map [menu-bar errors-menu]
+      (cons "Errors" (make-sparse-keymap "Errors")))
+    (define-key map [menu-bar errors-menu stop-subjob]
+      '("Stop" . comint-interrupt-subjob))
+    (define-key map [menu-bar errors-menu compilation-mode-separator2]
+      '("----" . nil))
+    (define-key map [menu-bar errors-menu compilation-mode-first-error]
+      '("First Error" . first-error))
+    (define-key map [menu-bar errors-menu compilation-mode-previous-error]
+      '("Previous Error" . previous-error))
+    (define-key map [menu-bar errors-menu compilation-mode-next-error]
+      '("Next Error" . next-error))
+    map)
+  "Keymap for `compilation-shell-minor-mode'.")
+
 (defvar compilation-mode-map
   (let ((map (cons 'keymap compilation-minor-mode-map)))
     (define-key map " " 'scroll-up)
@@ -670,6 +764,22 @@
   (set (make-local-variable 'compilation-directory-stack) nil)
   (setq compilation-last-buffer (current-buffer)))
 
+(defvar compilation-shell-minor-mode nil
+  "Non-nil when in compilation-shell-minor-mode.
+In this minor mode, all the error-parsing commands of the
+Compilation major mode are available but bound to keys that don't
+collide with Shell mode.")
+(make-variable-buffer-local 'compilation-shell-minor-mode)
+
+(or (assq 'compilation-shell-minor-mode minor-mode-alist)
+    (setq minor-mode-alist
+	  (cons '(compilation-shell-minor-mode " Shell-Compile")
+		minor-mode-alist)))
+(or (assq 'compilation-shell-minor-mode minor-mode-map-alist)
+    (setq minor-mode-map-alist (cons (cons 'compilation-shell-minor-mode
+					   compilation-shell-minor-mode-map)
+				     minor-mode-map-alist)))
+
 (defvar compilation-minor-mode nil
   "Non-nil when in compilation-minor-mode.
 In this minor mode, all the error-parsing commands of the
@@ -787,7 +897,8 @@
 (defsubst compilation-buffer-p (buffer)
   (save-excursion
     (set-buffer buffer)
-    (or compilation-minor-mode (eq major-mode 'compilation-mode))))
+    (or compilation-shell-minor-mode compilation-minor-mode
+	(eq major-mode 'compilation-mode))))
 
 (defun compilation-next-error (n)
   "Move point to the next error in the compilation buffer.
@@ -979,7 +1090,7 @@
     (setq compilation-last-buffer (current-buffer))
     ;; `compile-reinitialize-errors' needs to see the complete filename
     ;; on the line where they clicked the mouse.  Since it only looks
-    ;; upto point, moving point to eol makes sure the filename is
+    ;; up to point, moving point to eol makes sure the filename is
     ;; visible to `compile-reinitialize-errors'.
     (end-of-line)
     (compile-reinitialize-errors nil (point))
@@ -1262,7 +1373,8 @@
 	;; Skip over multiple error messages for the same source location,
 	;; so the next C-x ` won't go to an error in the same place.
 	(while (and compilation-error-list
-		    (equal (cdr (car compilation-error-list)) (cdr next-error)))
+		    (equal (cdr (car compilation-error-list))
+			   (cdr next-error)))
 	  (setq compilation-error-list (cdr compilation-error-list))))
 
     ;; We now have a marker for the position of the error source code.
@@ -1361,6 +1473,8 @@
   )
 
 
+;; This function is not needed any more by compilation mode.
+;; Does anyone else need it or can it be deleted?
 (defun count-regexp-groupings (regexp)
   "Return the number of \\( ... \\) groupings in REGEXP (a string)."
   (let ((groupings 0)
@@ -1386,265 +1500,250 @@
 		       (setq groupings (1+ groupings))))))))
     groupings))
 
+(defvar compilation-current-file nil
+  "Used by compilation-parse-errors to store filename for file being compiled")
+
+;; This variable is not used as a global variable. It's defined here just to
+;; shut up the byte compiler. It's bound and used by compilation-parse-errors
+;; and set by compile-collect-regexps.
+(defvar compilation-regexps nil)
+
 (defun compilation-parse-errors (limit-search find-at-least)
-  "Parse the current buffer as grep, cc or lint error messages.
+  "Parse the current buffer as grep, cc, lint or other error messages.
 See variable `compilation-parse-errors-function' for the interface it uses."
   (setq compilation-error-list nil)
   (message "Parsing error messages...")
-  (let (text-buffer orig orig-expanded parent-expanded
-	regexp enter-group leave-group error-group
-	alist subexpr error-regexp-groups
-	(found-desired nil)
-	(compilation-num-errors-found 0))
+  (if (null compilation-error-regexp-alist)
+      (error "compilation-error-regexp-alist is empty!"))
+  (let* ((compilation-regexps nil) ; Variable set by compile-collect-regexps.
+	 (found-desired nil)
+	 (compilation-num-errors-found 0)
+	 ;; Set up now the expanded, abbreviated directory variables
+	 ;; that compile-abbreviate-directory will need, so we can
+	 ;; compute them just once here.
+	 (orig (abbreviate-file-name default-directory))
+	 (orig-expanded (abbreviate-file-name
+			 (file-truename default-directory)))
+	 (parent-expanded (abbreviate-file-name
+			   (expand-file-name "../" orig-expanded))))
+
+    ;; Make a list of all the regexps. Each element has the form
+    ;; (REGEXP TYPE IDX1 IDX2 ...)
+    ;; where TYPE is one of leave, enter, file, error or nomessage.
+    (compile-collect-regexps 'leave compilation-leave-directory-regexp-alist)
+    (compile-collect-regexps 'enter compilation-enter-directory-regexp-alist)
+    (compile-collect-regexps 'file compilation-file-regexp-alist)
+    (compile-collect-regexps 'nomessage compilation-nomessage-regexp-alist)
+    (compile-collect-regexps 'error compilation-error-regexp-alist)
 
     ;; Don't reparse messages already seen at last parse.
     (goto-char compilation-parsing-end)
-    ;; Don't parse the first two lines as error messages.
-    ;; This matters for grep.
     (if (bobp)
 	(progn
-	  (forward-line 2)
-	  ;; Move back so point is before the newline.
-	  ;; This matters because some error regexps use \n instead of ^
-	  ;; to be faster.
-	  (forward-char -1)))
-
-    ;; Compile all the regexps we want to search for into one.
-    (setq regexp (concat "\\(" compilation-enter-directory-regexp "\\)\\|"
-			 "\\(" compilation-leave-directory-regexp "\\)\\|"
-			 "\\(" (mapconcat (function
-					   (lambda (elt)
-					     (concat "\\(" (car elt) "\\)")))
-					  compilation-error-regexp-alist
-					  "\\|") "\\)"))
-
-    ;; Find out how many \(...\) groupings are in each of the regexps, and set
-    ;; *-GROUP to the grouping containing each constituent regexp (whose
-    ;; subgroups will come immediately thereafter) of the big regexp we have
-    ;; just constructed.
-    (setq enter-group 1
-	  leave-group (+ enter-group
-			 (count-regexp-groupings
-			  compilation-enter-directory-regexp)
-			 1)
-	  error-group (+ leave-group
-			 (count-regexp-groupings
-			  compilation-leave-directory-regexp)
-			 1))
-
-    ;; Compile an alist (IDX FILE LINE [COL]), where IDX is the number of
-    ;; the subexpression for an entire error-regexp, and FILE and LINE (and
-    ;; possibly COL) are the numbers for the subexpressions giving the file
-    ;; name and line number (and possibly column number).
-    (setq alist (or compilation-error-regexp-alist
-		    (error "compilation-error-regexp-alist is empty!"))
-	  subexpr (1+ error-group))
-    (while alist
-      (setq error-regexp-groups
-	    (cons (list subexpr
-			(+ subexpr (nth 1 (car alist)))
-			(+ subexpr (nth 2 (car alist)))
-			(and (nth 3 (car alist))
-			     (+ subexpr (nth 3 (car alist)))))
-		  error-regexp-groups))
-      (setq subexpr (+ subexpr 1 (count-regexp-groupings (car (car alist)))))
-      (setq alist (cdr alist)))
+	  (setq compilation-current-file nil) ; No current file at start.
+	  ;; Don't parse the first two lines as error messages.
+	  ;; This matters for grep.
+	  (forward-line 2)))
 
-    ;; Set up now the expanded, abbreviated directory variables
-    ;; that compile-abbreviate-directory will need, so we can
-    ;; compute them just once here.
-    (setq orig (abbreviate-file-name default-directory)
-	  orig-expanded (abbreviate-file-name
-			 (file-truename default-directory))
-	  parent-expanded (abbreviate-file-name
-			   (expand-file-name "../" orig-expanded)))
-
-    (while (and (not found-desired)
-		;; Instead of using re-search-forward,
-		;; we use this loop which tries only at each line.
-		(progn
-		  (while (and (not (eobp))
-			      (not (looking-at regexp)))
-		    (forward-line 1))
-		  (not (eobp))))
-
-      ;; Move to the end of the match we just found.
-      (goto-char (match-end 0))
+    ;; Parse messages.
+    (while (not (or found-desired (eobp)))
+      (let ((this compilation-regexps) (prev nil) (alist nil) type)
+	;; Go through the regular expressions. If a match is found,
+	;; variable alist is set to the corresponding alist and the
+	;; matching regexp is moved to the front of compilation-regexps
+	;; to make it match faster next time.
+	(while (and this (null alist))
+	  (if (not (looking-at (car (car this))))
+	      (progn (setq prev this)		; No match, go to next.
+		     (setq this (cdr this)))
+	    (setq alist (cdr (car this))) ; Got a match.
+;;;	    (if prev			; If not the first regexp,
+;;;		(progn			; move it to the front.
+;;;		  (setcdr prev (cdr this))
+;;;		  (setcdr this compilation-regexps)
+;;;		  (setq compilation-regexps this)))
+	    ))
+	(if (and alist			; Seen a match and not to
+		 (not (eq (setq type (car alist)) 'nomessage)))	; be ignored.
+	    (let* ((end-of-match (match-end 0))
+		   (filename
+		    (compile-buffer-substring (car (setq alist (cdr alist)))))
+		   stack)
+	      (if (eq type 'error)	; error message
+		  (let* ((linenum (if (numberp (car (setq alist (cdr alist))))
+				      (string-to-int
+				       (compile-buffer-substring (car alist)))
+				    ;; (car alist) is not a number, must be a
+				    ;; function that is called below to return
+				    ;; an error position descriptor.
+				    (car alist)))
+			 ;; Convert to integer later if linenum not a function.
+			 (column (compile-buffer-substring
+				  (car (setq  alist (cdr alist)))))
+			 this-error)
 
-      ;; Figure out which constituent regexp matched.
-      (cond ((match-beginning enter-group)
-	     ;; The match was the enter-directory regexp.
-	     (let ((dir
-		    (file-name-as-directory
-		     (expand-file-name
-		      (buffer-substring (match-beginning (+ enter-group 1))
-					(match-end (+ enter-group 1)))))))
-	       ;; The directory name in the "entering" message
-	       ;; is a truename.  Try to convert it to a form
-	       ;; like what the user typed in.
-	       (setq dir
-		     (compile-abbreviate-directory dir orig orig-expanded
-						   parent-expanded))
-	       (setq compilation-directory-stack
-		     (cons dir compilation-directory-stack))
-	       (and (file-directory-p dir)
-		    (setq default-directory dir)))
+		    ;; Check that we have a file name.
+		    (or filename
+			;; No file name in message, we must have seen it before
+			(setq filename compilation-current-file)
+			(error "\
+An error message with no file name and no file name has been seen earlier."))
 
-	     (and limit-search (>= (point) limit-search)
-		  ;; The user wanted a specific error, and we're past it.
-		  ;; We do this check here (and in the leave-group case)
-		  ;; rather than at the end of the loop because if the last
-		  ;; thing seen is an error message, we must carefully
-		  ;; discard the last error when it is the first in a new
-		  ;; file (see below in the error-group case).
-		  (setq found-desired t)))
+		    ;; Check for a comint-file-name-prefix and prepend it if
+		    ;; appropriate.  (This is very useful for
+		    ;; compilation-minor-mode in an rlogin-mode buffer.)
+		    (and (boundp 'comint-file-name-prefix)
+			 ;; If file name is relative, default-directory will
+			 ;; already contain the comint-file-name-prefix (done
+			 ;; by compile-abbreviate-directory).
+			 (file-name-absolute-p filename)
+			 (setq filename
+			       (concat comint-file-name-prefix filename)))
+
+		    ;; Some compilers (e.g. Sun's java compiler, reportedly)
+		    ;; produce bogus file names like "./bar//foo.c" for file
+		    ;; "bar/foo.c"; expand-file-name will collapse these into
+		    ;; "/foo.c" and fail to find the appropriate file.  So we
+		    ;; look for doubled slashes in the file name and fix them
+		    ;; up in the buffer.
+		    (setq filename (command-line-normalize-file-name filename))
+
+		    (setq filename
+			  (cons filename (cons default-directory (cdr alist))))
 
-	    ((match-beginning leave-group)
-	     ;; The match was the leave-directory regexp.
-	     (let ((beg (match-beginning (+ leave-group 1)))
-		   (stack compilation-directory-stack))
-	       (if beg
-		   (let ((dir
-			  (file-name-as-directory
-			   (expand-file-name
-			    (buffer-substring beg
-					      (match-end (+ leave-group
-							    1)))))))
-		     ;; The directory name in the "leaving" message
-		     ;; is a truename.  Try to convert it to a form
-		     ;; like what the user typed in.
-		     (setq dir
-			   (compile-abbreviate-directory dir orig orig-expanded
-							 parent-expanded))
-		     (while (and stack
-				 (not (string-equal (car stack) dir)))
-		       (setq stack (cdr stack)))))
-	       (setq compilation-directory-stack (cdr stack))
-	       (setq stack (car compilation-directory-stack))
-	       (if stack
-		   (setq default-directory stack))
-	       )
-
-	     (and limit-search (>= (point) limit-search)
-		  ;; The user wanted a specific error, and we're past it.
-		  ;; We do this check here (and in the enter-group case)
-		  ;; rather than at the end of the loop because if the last
-		  ;; thing seen is an error message, we must carefully
-		  ;; discard the last error when it is the first in a new
-		  ;; file (see below in the error-group case).
-		  (setq found-desired t)))
-
-	    ((match-beginning error-group)
-	     ;; The match was the composite error regexp.
-	     ;; Find out which individual regexp matched.
-	     (setq alist error-regexp-groups)
-	     (while (and alist
-			 (null (match-beginning (car (car alist)))))
-	       (setq alist (cdr alist)))
-	     (if alist
-		 (setq alist (car alist))
-	       (error "compilation-parse-errors: impossible regexp match!"))
-
-	     ;; Extract the file name and line number from the error message.
-	     (let ((beginning-of-match (match-beginning 0)) ;looking-at nukes
-		   (filename (buffer-substring (match-beginning (nth 1 alist))
-					       (match-end (nth 1 alist))))
-		   (linenum (string-to-int
-			     (buffer-substring
-			      (match-beginning (nth 2 alist))
-			      (match-end (nth 2 alist)))))
-		   (column (and (nth 3 alist)
-				(match-beginning (nth 3 alist))
-				(string-to-int
-				 (buffer-substring
-				  (match-beginning (nth 3 alist))
-				  (match-end (nth 3 alist)))))))
+		    ;; Locate the erring file and line.
+		    ;; Make this-error a new elt for compilation-error-list,
+		    ;; giving a marker for the current compilation buffer
+		    ;; location, and the file and line number of the error.
+		    ;; Save, as the start of the error, the beginning of the
+		    ;; line containing the match.
+		    (if (setq this-error
+			      (if (numberp linenum)
+				  (list (point-marker) filename linenum
+					(and column (string-to-int column)))
+				;; If linenum is not a number then it must be
+				;; a function returning an error position
+				;; descriptor or nil (meaning no position).
+				(save-excursion
+				  (funcall linenum filename column))))
+			
+			;; We have an error position descriptor.
+			;; If we have found as many new errors as the user
+			;; wants, or if we are past the buffer position he
+			;; indicated, then we continue to parse until we have
+			;; seen all consecutive errors in the same file. This
+			;; means that all the errors of a source file will be
+			;; seen in one parsing run, so that the error positions
+			;; will be recorded as markers in the source file
+			;; buffer that will move when the buffer is changed.
+			(if (and (or (and find-at-least
+					  (>= compilation-num-errors-found
+					      find-at-least))
+				     (and limit-search
+					  (>= end-of-match limit-search)))
+				 compilation-error-list ;At least one previous.
+				 (not (equal ; Same filename?
+				       (car (cdr (car compilation-error-list)))
+				       (car (cdr this-error)))))
+			    ;; We are past the limits and the last error
+			    ;; parsed, didn't belong to the same source file
+			    ;; as the earlier ones i.e. we have seen all the
+			    ;; errors belonging to the earlier file. We don't
+			    ;; add the error just parsed so that the next
+			    ;; parsing run can get it and the following errors
+			    ;; in the same file all at once.
+			    (setq found-desired t)
 
-	       ;; Check for a comint-file-name-prefix and prepend it if
-	       ;; appropriate.  (This is very useful for
-	       ;; compilation-minor-mode in an rlogin-mode buffer.)
-	       (and (boundp 'comint-file-name-prefix)
-		    ;; If the file name is relative, default-directory will
-		    ;; already contain the comint-file-name-prefix (done by
-		    ;; compile-abbreviate-directory).
-		    (file-name-absolute-p filename)
-		    (setq filename (concat comint-file-name-prefix filename)))
-
-	       ;; Some compilers (e.g. Sun's java compiler, reportedly)
-	       ;; produce bogus file names like "./bar//foo.c" for the file
-	       ;; "bar/foo.c"; expand-file-name will collapse these into
-	       ;; "/foo.c" and fail to find the appropriate file.  So we look
-	       ;; for doubled slashes in the file name and fix them up in the
-	       ;; buffer.
-	       (setq filename (command-line-normalize-file-name filename))
-	       (setq filename (cons filename (cons default-directory
-						   (nthcdr 4 alist))))
-
+			  (goto-char end-of-match) ; Prepare for next message.
+			  ;; Don't add the same source line more than once.
+			  (and (not (and
+				     compilation-error-list
+				     (equal (cdr (car compilation-error-list))
+					    (cdr this-error))))
+			       (setq compilation-error-list
+				     (cons this-error compilation-error-list)
+				     compilation-num-errors-found
+				     (1+ compilation-num-errors-found))))))
 
-	       ;; Locate the erring file and line.
-	       ;; Cons a new elt onto compilation-error-list,
-	       ;; giving a marker for the current compilation buffer
-	       ;; location, and the file and line number of the error.
-	       (save-excursion
-		 ;; Save as the start of the error the beginning of the
-		 ;; line containing the match unless the match starts at a
-		 ;; newline, in which case the beginning of the next line.
-		 (goto-char beginning-of-match)
-		 (forward-line (if (eolp) 1 0))
-		 (let ((this (cons (point-marker)
-				   (list filename linenum column))))
-		   ;; Don't add the same source line more than once.
-		   (if (and compilation-skip-to-next-location
-			    (equal (cdr this)
-				   (cdr (car compilation-error-list))))
-		       nil
-		     (setq compilation-error-list
-			   (cons this
-				 compilation-error-list))
-		     (setq compilation-num-errors-found
-			   (1+ compilation-num-errors-found)))))
-	       (and (or (and find-at-least (> compilation-num-errors-found
-					      find-at-least))
-			(and limit-search (>= (point) limit-search)))
-		    ;; We have found as many new errors as the user wants,
-		    ;; or past the buffer position he indicated.  We
-		    ;; continue to parse until we have seen all the
-		    ;; consecutive errors in the same file, so the error
-		    ;; positions will be recorded as markers in this buffer
-		    ;; that might change.
-		    (cdr compilation-error-list) ; Must check at least two.
-		    (not (equal (car (cdr (nth 0 compilation-error-list)))
-				(car (cdr (nth 1 compilation-error-list)))))
-		    (progn
-		      ;; Discard the error just parsed, so that the next
-		      ;; parsing run can get it and the following errors in
-		      ;; the same file all at once.  If we didn't do this, we
-		      ;; would have the same problem we are trying to avoid
-		      ;; with the test above, just delayed until the next run!
-		      (setq compilation-error-list
-			    (cdr compilation-error-list))
-		      (goto-char beginning-of-match)
-		      (setq found-desired t)))
-	       )
-	     )
-	    (t
-	     (error "compilation-parse-errors: known groups didn't match!")))
+		;; Not an error message.
+		(if (eq type `file)	; Change current file.
+		    (and filename (setq compilation-current-file filename))
+		  ;; Enter or leave directory.
+		  (setq stack compilation-directory-stack)
+		  (and filename
+		       (file-directory-p
+			(setq filename
+			      ;; The directory name in the message
+			      ;; is a truename.  Try to convert it to a form
+			      ;; like what the user typed in.
+			      (compile-abbreviate-directory
+			       (file-name-as-directory
+				(expand-file-name filename))
+			       orig orig-expanded parent-expanded)))
+		       (if (eq type 'leave)
+			   (while (and stack
+				       (not (string-equal (car stack)
+							  filename)))
+			     (setq stack (cdr stack)))
+			 (setq compilation-directory-stack
+			       (cons filename compilation-directory-stack)
+			       default-directory filename)))
+		  (and (eq type 'leave
+			   stack
+			   (setq compilation-directory-stack (cdr stack))
+			   (setq stack (car compilation-directory-stack))
+			   (setq default-directory stack)))
+		  (goto-char end-of-match) ; Prepare to look at next message.
+		  (and limit-search (>= end-of-match limit-search)
+		       ;; The user wanted a specific error, and we're past it.
+		       ;; We do this check here rather than at the end of the
+		       ;; loop because if the last thing seen is an error
+		       ;; message, we must carefully discard the last error
+		       ;; when it is the first in a new file (see above in
+		       ;; the error-message case)
+		       (setq found-desired t))))
 
-      (message "Parsing error messages...%d (%.0f%% of buffer)"
+	      ;; Go to before the last character in the message so that we will
+	      ;; see the next line also when the message ended at end of line.
+	      ;; When we ignore the last error message above, this will
+	      ;; cancel the effect of forward-line below so that point
+	      ;; doesn't move.
+	      (forward-char -1)
+
+	      ;; Is this message necessary any more?  Parsing is now so fast
+	      ;; that you might not need to know how it proceeds.
+	      (message
+	       "Parsing error messages...%d found. %.0f%% of buffer seen."
 	       compilation-num-errors-found
 	       ;; Use floating-point because (* 100 (point)) frequently
 	       ;; exceeds the range of Emacs Lisp integers.
 	       (/ (* 100.0 (point)) (point-max)))
+	      ))
 
-      (and limit-search (>= (point) limit-search)
-	   ;; The user wanted a specific error, and we're past it.
-	   (setq found-desired t)))
-    (setq compilation-parsing-end (if found-desired
-				      (point)
-				    ;; We have searched the whole buffer.
-				    (point-max))))
-  (setq compilation-error-list (nreverse compilation-error-list))
-  (message "Parsing error messages...done"))
+	(forward-line 1)))		; End of while loop. Look at next line.
+
+    (setq compilation-parsing-end (point))
+    (setq compilation-error-list (nreverse compilation-error-list))
+;;; (message "Parsing error messages...done. %d found. %.0f%% of buffer seen."
+;;;	     compilation-num-errors-found
+;;;	     (/ (* 100.0 (point)) (point-max)))
+    (message "Parsing error messages...done.")))
+
+(defun compile-collect-regexps (type this)
+  ;; Add elements to variable compilation-regexps that is bound in
+  ;; compilation-parse-errors.
+  (and (not (eq this t))
+       (while this
+	 (setq compilation-regexps
+	       (cons (cons (car (car this)) (cons type (cdr (car this))))
+		     compilation-regexps))
+	 (setq this (cdr this)))))
+
+(defun compile-buffer-substring (index)
+  ;; Get substring matched by INDEXth subexpression.
+  (if index
+      (let ((beg (match-beginning index)))
+	(if beg (buffer-substring beg (match-end index))))))
 
 ;; If directory DIR is a subdir of ORIG or of ORIG's parent,
 ;; return a relative name for it starting from ORIG or its parent.