diff lisp/oobr/eif-ise-er.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/oobr/eif-ise-er.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,242 @@
+;;!emacs
+;;
+;; FILE:         eif-ise-er.el
+;; SUMMARY:      Parses ISE's Eiffel error messages; compiles Eiffel classes.
+;; USAGE:        GNU Emacs Lisp Library
+;; KEYWORDS:     oop, tools
+;;
+;; AUTHOR:       Bob Weiner
+;; ORG:          Motorola Inc.
+;;
+;; ORIG-DATE:     7-Dec-89 at 00:17:18
+;; LAST-MOD:     17-Apr-95 at 12:39:18 by Bob Weiner
+;;
+;; Copyright (C) 1989-1995  Free Software Foundation, Inc.
+;; See the file BR-COPY for license information.
+;;
+;; This file is part of the OO-Browser.
+;;
+;; DESCRIPTION:  
+;;
+;;   'eif-ec' compiles an Eiffel class.
+;;   'eif-es' compiles an Eiffel system.
+;;
+;;   Load this library and then invoke error parsing via {C-x `}.
+;;   See the GNU Emacs Manual for an explanation of error parsing.
+;;
+;;   'eif-ise-next-error' bound to {C-x `} parses ISE Eiffel compiler
+;;   error messages.  As in: 
+;;
+;;   "my_class", 16: syntax error : Keyword 'expanded' may not be used as identifier
+;;
+;;   Only handles compilation lines of the following form:
+;;
+;;      <compiler> [<option> ... <option>] <pathname>
+;;
+;;   Requires the 'br-class-path', 'br-build-sys-paths-htable', and
+;;   'br-build-paths-htable' functions from the OO-Browser 'br-lib' package.
+;;   This is used to determine the full pathname for the source code of each
+;;   class since ISE does not include any pathname information in its error
+;;   messages.
+;;
+;;
+;;   To reset the {C-x `} key to parse non-Eiffel error messages, use:
+;;
+;;           {M-x load-lib RTN compile RTN}
+;;
+;; DESCRIP-END.
+
+(require 'br-lib)
+(require 'br-eif)
+(require 'compile)
+
+(global-set-key "\C-x`" 'eif-ise-next-error)
+(and (boundp 'eiffel-mode-map) (define-key eiffel-mode-map "\C-c!" 'eif-ec))
+
+(setq compilation-error-regexp "\"\\([^ \t]+\\)\", \\([0-9]+\\):.*")
+
+(defconst eif-compile-dir nil
+  "Default directory in which to invoke an Eiffel compile command.")
+
+(defconst eif-compile-cmd "ec"
+  "Default command name with which to invoke the Eiffel compiler.")
+
+(defun eif-ise-next-error (&optional argp)
+  "Visit next compilation error message and corresponding source code.
+This operates on the output from the \\[compile] command.
+If all preparsed error messages have been processed,
+the error message buffer is checked for new ones.
+A non-nil argument (prefix arg, if interactive)
+means reparse the error message buffer and start at the first error."
+  (interactive "P")
+  (if (or (eq compilation-error-list t)
+	  argp)
+      (progn (compilation-forget-errors)
+	     (setq compilation-parsing-end 1)))
+  (if compilation-error-list
+      nil
+    (save-excursion
+      (switch-to-buffer "*compilation*")
+      (set-buffer-modified-p nil)
+      (eif-ise-compilation-parse-errors)))
+  (let ((next-error (car compilation-error-list)))
+    (if (null next-error)
+	(error (concat compilation-error-message
+		       (if (and compilation-process
+				(eq (process-status compilation-process)
+				    'run))
+			   " yet" ""))))
+    (setq compilation-error-list (cdr compilation-error-list))
+    (if (null (car (cdr next-error)))
+	nil
+      (switch-to-buffer (marker-buffer (car (cdr next-error))))
+      (goto-char (car (cdr next-error)))
+      (set-marker (car (cdr next-error)) nil))
+    (let* ((pop-up-windows t)
+	   (w (display-buffer (marker-buffer (car next-error)))))
+      (set-window-point w (car next-error))
+      (set-window-start w (car next-error)))
+    (set-marker (car next-error) nil)))
+
+(defun eif-ise-compilation-filename ()
+  "Return a string which is the last filename from the compilation command.
+Ignore quotes around it.  Return nil if no filename was given."
+  ;; First arg of compile cmd should be filename
+  (if (string-match "^.*[ \t]+\\([^ \t\"]+\\)" compile-command)
+      (substring compile-command (match-beginning 1) (match-end 1))))
+
+(defun eif-ise-compilation-parse-errors ()
+  "Parse the current buffer as error messages.
+This makes a list of error descriptors, compilation-error-list.  For each
+error line-number in the buffer, the source file is read in, and the text
+location is saved in compilation-error-list.  The function next-error,
+assigned to \\[next-error], takes the next error off the list and visits its
+location."
+  (setq compilation-error-list nil)
+  (message "Parsing error messages...")
+  (let (text-buffer
+	last-filename last-linenum)
+    ;; 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)
+	(forward-line 2))
+    (let (class-name case-fold-search linenum filename error-marker text-marker)
+      (while (re-search-forward compilation-error-regexp nil t)
+	;; Extract line number from error message.
+	(setq linenum (string-to-int (buffer-substring
+				       (match-beginning 2)
+				       (match-end 2))))
+	;; Extract class name from error message and convert to the full
+	;; pathname of the class' source file.
+	(setq class-name (downcase (buffer-substring (match-beginning 1) (match-end 1)))
+	      filename (br-class-path class-name))
+	(if (null filename) ; No matching class name in lookup table.
+	    (progn 
+	      (message "Rebuilding Eiffel system class locations table...")
+	      (sit-for 2)
+	      (call-interactively 'br-build-sys-classes-htable) ; Typically pretty fast
+	      (message "Rebuilding Eiffel system class locations table...Done")
+	      (setq filename (br-class-path class-name))
+	      (if (null filename)
+		  (error (format "'%s' not in lookup table, use {M-x br-build-paths-htable RTN} to update."
+				 class-name)))))
+	;; Locate the erring file and line.
+	(if (and (equal filename last-filename)
+		 (= linenum last-linenum))
+	    nil
+	  (beginning-of-line 1)
+	  (setq error-marker (point-marker))
+	  ;; text-buffer gets the buffer containing this error's file.
+	  (if (not (equal filename last-filename))
+	      (setq text-buffer
+		    (and (file-exists-p (setq last-filename filename))
+			 (if (boundp 'br-find-file-noselect-function)
+			     (set-buffer
+			       (funcall br-find-file-noselect-function
+					filename))
+			   (find-file-noselect filename)))
+		    last-linenum 0))
+	  (if text-buffer
+	      ;; Go to that buffer and find the erring line.
+	      (save-excursion
+		(set-buffer text-buffer)
+		(if (zerop last-linenum)
+		    (progn
+		      (goto-char 1)
+		      (setq last-linenum 1)))
+		(forward-line (- linenum last-linenum))
+		(setq last-linenum linenum)
+		(setq text-marker (point-marker))
+		(setq compilation-error-list
+		      (cons (list error-marker text-marker)
+			    compilation-error-list)))))
+	(forward-line 1)))
+    (setq compilation-parsing-end (point-max)))
+  (message "Parsing error messages...done")
+  (setq compilation-error-list (nreverse compilation-error-list)))
+
+
+;;; The following version of 'eif-ec' courtesy of:
+;;; Heinz W. Schmidt                                     hws@icsi.berkeley.edu
+;;; International Computer Science Institute             (415) 643-9153   x175
+;;; 1947 Center Street, Ste. 600                    /\/\|;; CLOS saves time and
+;;; Berkeley, CA 94704                              \/\/|-- Eiffel is faster
+;;; 2/11/90
+;;; With a number of Bob Weiner's modifications
+
+(defun str2argv (STR)
+  (if (string-match "[^ ]" STR)
+      (let ((arg1 (read-from-string STR)))
+        (cons (prin1-to-string (car arg1))
+              (str2argv (substring STR (cdr arg1)))))))
+
+(defvar eif-ec-args "" "Default arguments to send to the Eiffel ec class compiler.")
+
+(defun eif-ec (ARG &optional CMD DIR CLASS-NAME)
+  "Calls Eiffel compiler.  Compile with optional CMD, 'eif-compile-cmd' or \"ec\".
+By default, the compiler is called on the file associated with the current
+buffer.  With numeric argument 0 prompts for explicit command line arguments.
+Other numeric arguments allow you to insert options or further class names."
+  (interactive "P")
+  (setq CLASS-NAME (or CLASS-NAME
+		       (let ((fn (file-name-nondirectory buffer-file-name)))
+			 (substring fn 0 (- (length fn) 2))))
+	ec-dir (or DIR eif-compile-dir (file-name-directory buffer-file-name)))
+  (let* ((ec-output (get-buffer-create "*compilation*"))
+         (ec-process (get-buffer-process ec-output))
+	 (curr-buffer (current-buffer)))
+    (if ec-process
+        (if (y-or-n-p "Kill current Eiffel compilation process? ")
+            (delete-process ec-process)
+          (error "Can't ec concurrently.")))
+    (if (and (buffer-modified-p)
+             (y-or-n-p (format "Save file %s? " buffer-file-name)))
+        (progn (save-buffer) (message "")))
+    ;; Maybe prompt for args and dispatch according to numeric ARG.
+    (setq eif-ec-args (if ARG (read-string "ec args: " eif-ec-args) ""))
+    ;; Switch to shell buffer and run ec.
+    (set-buffer ec-output)
+    (erase-buffer)
+    ;; Move to directory and trim classname so ec works in situations
+    ;; like: ec -t class1 <CLASS-NAME>
+    (cd ec-dir)
+    (insert (or CMD eif-compile-cmd "ec")
+	    (if ARG (format " %s" eif-ec-args) "")
+	    (format " %s" (if (not (and ARG (zerop ARG))) CLASS-NAME ""))
+            "\n")
+    (set-buffer curr-buffer)
+    (display-buffer ec-output)
+    (eval   
+     (append '(start-process "ec" ec-output (or CMD eif-compile-cmd "ec"))
+             (str2argv eif-ec-args)
+             (if (not (and ARG (zerop ARG))) (list CLASS-NAME)))))) 
+
+(defun eif-es (&optional dir)
+  "Compile Eiffel system with es."
+  (interactive)
+  (eif-ec nil "es" dir ""))
+
+
+(provide 'eif-ise-er)