;; Run compiler as inferior of Emacs, and parse its error messages. ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. ;; This file is part of GNU Emacs. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; GNU Emacs, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. ;; November 1990: Modified original compile.el to work on greport output. ;; It uses code from gdb.el to display an arrow at a particular line. ;; Bugs: You can have only one gedit buffer at a time. This would ;; not be difficult to fix, but it's more than just adding a ;; make-local-variable, because gedit-error-list is used by a function ;; that hops around from buffer to buffer. ;; $Header: /export/swap/overflow/gct/src/RCS/gedit.el,v 1.2 91/01/07 08:37:22 marick Exp $ ;; $Log: gedit.el,v $ ;; Revision 1.2 91/01/07 08:37:22 marick ;; Bugfix: Used to get confused if the buffer was reverted in the middle ;; of processing. ;; (provide 'gedit) (require 'compile) ;; We still use some utilities from compile.el. (defvar gedit-mode-map nil) (if gedit-mode-map nil (setq gedit-mode-map (make-sparse-keymap)) (define-key gedit-mode-map " " 'gedit-next-error) (define-key gedit-mode-map "e" 'gedit-end) ) ;;; Variables. ;; Unlike C-x`, our "next error" command replays the sequence if ;; you continue past the end. This variable is used to pause for a ;; "you've hit the end" message. (defvar gedit-restart t) ;; The list of parsed errors. (defvar gedit-error-list nil) (defun gedit-mode () "This file is used to step through output from greport. It has these special commands: \\{gedit-map} " (interactive) (fundamental-mode) (setq major-mode 'gedit-mode) (setq mode-name "Gedit") (use-local-map gedit-mode-map) ;; Arrange for restart if file changed on disk. (make-local-variable 'revert-buffer-function) (setq revert-buffer-function 'gedit-revert-buffer) (run-hooks 'gedit-mode-hook) (setq gedit-restart t) (setq gedit-error-list nil) ) (defun gedit-next-error () "Visit next greport message and corresponding source code." (interactive) (if (and (null gedit-error-list) (not gedit-restart)) (progn (setq gedit-restart t) (setq overlay-arrow-string nil) (setq overlay-arrow-position nil) (error "No more unsatisfied conditions"))) (setq gedit-restart nil) (if (null gedit-error-list) (gedit-parse-errors)) (let ((next-error (car gedit-error-list))) (setq gedit-error-list (cdr gedit-error-list)) (if (null (car (cdr next-error))) nil (gedit-display-line (marker-buffer (car (cdr next-error))) (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)) ;; if greport -edit generated this, position to place where user ;; might want to add an 'S'. (let ((line-end (save-excursion (end-of-line) (point)))) (if (and (re-search-forward "line [0-9]+: \\[[0-9]+:" line-end t) (re-search-forward " 0]\\| 0 [0-9 Ss]" line-end t)) (goto-char (+ (match-beginning 0) 2))))) (set-marker (car next-error) nil))) ;; Taken from gdb.el: put a "=>" on the appropriate line. (defun gedit-display-line (buffer pos) (let* ((window (display-buffer buffer t))) (save-excursion (set-buffer buffer) (save-restriction (widen) (goto-char pos) (setq overlay-arrow-string "=>") (or overlay-arrow-position (setq overlay-arrow-position (make-marker))) (set-marker overlay-arrow-position (point) (current-buffer))) (cond ((or (< pos (point-min)) (> pos (point-max))) (widen) (goto-char pos)))) (set-window-point window overlay-arrow-position))) (defun gedit-end () "Use this if you want to stop before the end of the greport output. It erases the '=>' and moves point to the beginning of the output." (interactive) (setq overlay-arrow-string nil) (setq overlay-arrow-position nil) (setq gedit-error-list nil) (goto-char (point-min)) (setq gedit-restart t)) (defun gedit-parse-errors () "Parse the current buffer. This makes a list of descriptors, gedit-error-list. For each source-file, line-number pair in the buffer, the source file is read in, and the text location is saved in gedit-error-list." (setq gedit-error-list nil) (message "Parsing greport messages...") (let (text-buffer last-filename last-linenum) (goto-char 0) (while (re-search-forward compilation-error-regexp nil t) (let (linenum filename error-marker text-marker) ;; Extract file name and line number from error message. (save-restriction (narrow-to-region (match-beginning 0) (match-end 0)) (goto-char (point-max)) (skip-chars-backward "[0-9]") (setq linenum (read (current-buffer))) (goto-char (point-min))) (setq filename (compilation-grab-filename)) ;; Locate the erring file and line. (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)) (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 gedit-error-list (cons (list error-marker text-marker) gedit-error-list)))) (forward-line 1)))) (message "Parsing greport messages...done") (setq gedit-error-list (nreverse gedit-error-list))) ;;; Essentially, we add a hook to revert-buffer that positions us at ;;; the first line and resets local variables. (defun gedit-revert-buffer (&optional no-auto-save-offer-p noconfirm) (let ((revert-buffer-function nil)) (revert-buffer no-auto-save-offer-p noconfirm)) (setq gedit-restart t) (setq gedit-error-list nil) (goto-char 0))