home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.emacs
- Path: sparky!uunet!panther!mothost!merlin.dev.cdx.mot.com!fendahl.dev.cdx.mot.com!mcook
- From: mcook@fendahl.dev.cdx.mot.com (Michael Cook)
- Subject: enhancements to compile.el
- Message-ID: <mcook.727827362@fendahl.dev.cdx.mot.com>
- Sender: news@merlin.dev.cdx.mot.com (Merlin News System)
- Nntp-Posting-Host: fendahl.dev.cdx.mot.com
- Organization: Motorola Codex, Canton, Massachusetts
- Date: Sat, 23 Jan 1993 22:16:02 GMT
- Lines: 463
-
- I've made some enhancements to compile.el to overcome some of its limitations.
-
- - Added (previous-error)
- - Added (rename-compilation-buffer) and made most variables buffer-local
- (to the *compilation* buffer) so that you can have multiple compilation
- processes active simultaneously.
- - "No more errors yet" also moves window-start to EOF (so that it becomes
- easier to see that new output is available).
- - (grep) remembers its last command, just like (compile) always did.
-
- Bug fixes:
- - Won't try to read a file named "" (the null string).
- - Gracefully handles the situation where a buffer that had markers in
- it has been deleted.
-
- Michael.
-
- #!/bin/sh
- # to extract, remove the header and type "sh filename"
- if `test ! -s ./compile.el`
- then
- echo "writing ./compile.el"
- cat > ./compile.el << '\End\Of\Shar\'
- ;; 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.
-
- ;;
- ;; mcook@merlin.dev.mot.com
- ;; January 22, 1993
- ;;
- ;; Enhancements:
- ;; - Added (previous-error)
- ;; - Added (rename-compilation-buffer) and made most variables buffer-local
- ;; (to the *compilation* buffer) so that you can have multiple compilation
- ;; processes active simultaneously.
- ;; - "No more errors yet" also moves window-start to EOF (so that it becomes
- ;; easier to see that new output is available).
- ;; - (grep) remembers its last command, just like (compile) always did.
- ;;
- ;; Bug fixes:
- ;; - Won't try to read a file named "" (the null string).
- ;; - Gracefully handles the situation where a buffer that had markers in
- ;; it has been deleted.
- ;;
-
- (provide 'compile)
-
- (defvar compilation-process nil
- "Process created by compile command, or nil if none exists now.
- Note that the process may have been \"deleted\" and still
- be the value of this variable.")
- (make-variable-buffer-local 'compilation-process)
-
- (defvar compilation-error-list nil
- "List of error message descriptors for visiting erring functions.
- Each error descriptor is a list of length two.
- Its car is a marker pointing to an error message.
- Its cadr is a marker pointing to the text of the line the message is about,
- or nil if that is not interesting.
- The value may be t instead of a list;
- this means that the buffer of error messages should be reparsed
- the next time the list of errors is wanted.")
- (make-variable-buffer-local 'compilation-error-list)
-
- (defvar compilation-error-index nil
- "Index into compilation-error-list of the current error.")
- (make-variable-buffer-local 'compilation-error-index)
-
- (defvar compilation-parsing-end nil
- "Position of end of buffer when last error messages parsed.")
- (make-variable-buffer-local 'compilation-parsing-end)
-
- (defvar compilation-error-message nil
- "Message to print when no more matches for compilation-error-regexp are found")
- (make-variable-buffer-local 'compilation-error-message)
-
- (defvar compilation-error-regexp
- "\\([^ \n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+ *of *[^ \n]+\\)"
- "Regular expression for filename/linenumber in error in compilation log.")
-
- (defun compile (command)
- "Compile the program including the current buffer. Default: run make.
- Runs COMMAND, a shell command, in a separate process asynchronously
- with output going to the buffer *compilation*.
- You can then use the command \\[next-error] to find the next error message
- and move to the source code that caused it."
- (interactive (list (read-string "Compile command: " compile-command)))
- (setq compile-command command)
- (compile1 compile-command "errors"))
-
- (defvar grep-command "grep -n "
- "*Last shell command used to do a grep class command;
- default for next grep class command.")
-
- (defun grep (command)
- "Run grep, with user-specified args, and collect output in a buffer.
- While grep runs asynchronously, you can use the \\[next-error] command
- to find the text that grep hits refer to."
- (interactive (list (read-string "Run grep (with args) " grep-command)))
- (setq grep-command command)
- (require 'compile)
- (compile1 (concat command " /dev/null") "grep hits" "grep"))
-
- (defun compile1 (command error-message &optional name-of-mode)
- (save-some-buffers)
- (let ((obuf (current-buffer))
- (this-dir default-directory)
- (cbuf (get-buffer-create "*compilation*")))
- (unwind-protect
- (progn
- (set-buffer cbuf)
-
- (if compilation-process
- (if (or (not (eq (process-status compilation-process) 'run))
- (yes-or-no-p
- "A compilation process is running; kill it? "))
- (condition-case ()
- (let ((comp-proc compilation-process))
- (interrupt-process comp-proc)
- (sit-for 1)
- (delete-process comp-proc))
- (error nil))
- (error "Cannot have two compilation processes")))
- (setq compilation-process nil)
-
- ;;
- ;; Setup the *compilation* buffer.
- ;;
- (buffer-flush-undo cbuf)
- (compilation-forget-errors)
- (setq compilation-error-list t)
- (setq compilation-error-index nil)
- (setq compilation-error-message error-message)
- (setq mode-name (or name-of-mode "Compilation"))
- (setq default-directory this-dir)
- (with-output-to-temp-buffer "*compilation*"
- (princ "cd ")
- (princ this-dir)
- (terpri)
- (princ command)
- (terpri))
- ;;
- ;; For all windows displaying the *compilation* buffer, set the
- ;; window-start and window-point attributes.
- ;;
- (let* ((orig-win (selected-window))
- (this-win orig-win)
- (pmin (point-min))
- (pmax (point-max)))
- (while (progn
- (if (eq (window-buffer this-win) cbuf)
- (progn
- (set-window-point this-win pmax)
- (set-window-start this-win pmin)))
- (setq this-win (next-window this-win t))
- (not (eq this-win orig-win)))))
-
- ;;
- ;; Start the process.
- ;;
- (setq compilation-process
- (start-process "compilation" cbuf
- shell-file-name
- "-c" (concat "exec " command)))
- (set-process-sentinel compilation-process 'compilation-sentinel)
-
- ;; Make log buffer's mode line show process state
- (setq mode-line-process '(": %s")))
- (set-buffer obuf))))
-
- (defun compilation-sentinel (proc msg)
- ;;
- ;; Called when compilation process changes state.
- ;;
- (cond ((null (buffer-name (process-buffer proc)))
- ;;
- ;; buffer killed
- ;;
- (set-process-buffer proc nil))
- ((memq (process-status proc) '(signal exit))
- (let* ((obuf (current-buffer))
- (omax (point-max))
- (opoint (point)))
- (unwind-protect
- (progn
- ;;
- ;; Write something in *compilation* and hack its mode line,
- ;;
- (set-buffer (process-buffer proc))
- (setq compilation-process nil)
- (goto-char (point-max))
- (insert ?\n mode-name " " msg)
- (forward-char -1)
- (insert " at "
- (substring (current-time-string) 0 -5))
- (forward-char 1)
- (if (< opoint omax)
- (goto-char opoint))
- (setq mode-line-process
- (concat ": "
- (symbol-name (process-status proc))))
- ;; Force mode line redisplay soon
- (set-buffer-modified-p (buffer-modified-p))
- ;;
- ;; If buffer and mode line will show that the process is
- ;; dead, we can delete it now. Otherwise it will stay around
- ;; until M-x list-processes.
- ;;
- (delete-process proc))
- (set-buffer obuf))))))
-
- (defun kill-compilation ()
- "Kill the process made by the \\[compile] command."
- (interactive)
- (save-excursion
- (set-buffer "*compilation*")
- (if compilation-process
- (interrupt-process compilation-process))))
-
- (defun kill-grep ()
- "Kill the process made by the \\[grep] command."
- (interactive)
- (save-excursion
- (set-buffer "*compilation*")
- (if compilation-process
- (interrupt-process compilation-process))))
-
- (defun 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")
- (compilation-next-error argp 1))
-
- (defun previous-error (&optional argp)
- "See \\[next-error]."
- (interactive "P")
- (compilation-next-error argp -1))
-
- (defun compilation-next-error (restart direction)
- "See \\[next-error]."
- (let ((obuf (current-buffer)))
- (unwind-protect
- (progn
- (set-buffer "*compilation*")
- (if (or (eq compilation-error-list t)
- restart)
- (compilation-forget-errors))
- (let ((i compilation-error-index))
- (while (progn
- (setq i (if i (+ i direction) 0))
- (if (< i 0)
- (error (concat "No previous "
- compilation-error-message)))
- (let ((n (length compilation-error-list)))
- (if (< i n)
- nil
- (set-buffer-modified-p nil)
- (compilation-parse-errors)
- (or (> (length compilation-error-list) n)
- (error (concat
- "No more "
- compilation-error-message
- (if (and compilation-process
- (eq (process-status
- compilation-process) 'run))
- (let ((w (get-buffer-window
- (current-buffer))))
- (if (not w)
- nil
- (set-window-point w (point-max))
- (set-window-start w (point-max)))
- " yet")))))))
- (not (marker-buffer
- (nth 1 (nth i compilation-error-list))))))
- (setq compilation-error-index i))
- (let ((e (nth compilation-error-index compilation-error-list)))
- (if (car (cdr e))
- (progn
- (setq obuf (marker-buffer (car (cdr e))))
- (switch-to-buffer obuf)
- (goto-char (car (cdr e)))))
- (let* ((pop-up-windows t)
- (w (display-buffer (marker-buffer (car e)))))
- (set-window-point w (car e))
- (set-window-start w (car e)))))
- (set-buffer obuf))))
-
- ;;
- ;; Set compilation-error-list to nil, and unchain the markers that point to
- ;; the error messages and their text, so that they no longer slow down gap
- ;; motion. This would happen anyway at the next garbage collection, but it is
- ;; better to do it right away.
- ;;
- (defun compilation-forget-errors ()
- (if (eq compilation-error-list t)
- (setq compilation-error-list nil))
- (while compilation-error-list
- (let ((e (car compilation-error-list)))
- (set-marker (car e) nil)
- (if (car (cdr e))
- (set-marker (car (cdr e)) nil)))
- (setq compilation-error-list (cdr compilation-error-list)))
- (setq compilation-error-index nil)
- (setq compilation-parsing-end 1))
-
- (defun compilation-parse-errors ()
- "Parse the current buffer as error messages. This makes a list of error
- descriptors, compilation-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
- compilation-error-list. The function next-error, assigned to
- \\[next-error], takes the next error off the list and visits its location."
- (message "Parsing error messages...")
- (let (text-buffer last-filename last-linenum error-list)
- ;;
- ;; 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))
- (while (re-search-forward compilation-error-regexp nil t)
- (let (linenum filename error-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]")
- ;;
- ;; If it's a lint message, use the last file(linenum) on the line.
- ;; Normally we use the first on the line.
- ;;
- (if (= (preceding-char) ?\()
- (progn
- (narrow-to-region (point-min) (1+ (buffer-size)))
- (end-of-line)
- (re-search-backward compilation-error-regexp)
- (skip-chars-backward "^ \t\n")
- (narrow-to-region (point) (match-end 0))
- (goto-char (point-max))
- (skip-chars-backward "[0-9]")))
- ;;
- ;; Are we looking at a "filename-first" or "line-number-first" form?
- ;;
- (if (looking-at "[0-9]")
- (progn
- (setq linenum (read (current-buffer)))
- (goto-char (point-min)))
- ;;
- ;; Line number at start, file name at end.
- ;;
- (goto-char (point-min))
- (setq linenum (read (current-buffer)))
- (goto-char (point-max))
- (skip-chars-backward "^ \t\n"))
- (setq filename (compilation-grab-filename)))
- ;; Locate the erring file and line.
- (if (or (and (equal filename last-filename)
- (= linenum last-linenum))
- (zerop (length filename)))
- nil
- (beginning-of-line 1)
- (setq error-marker (point-marker))
- ;; text-buffer gets the buffer containing this error's file.
- (if (equal filename last-filename)
- nil
- (setq last-filename filename
- last-linenum 0)
- (setq text-buffer
- (and (file-exists-p filename)
- (find-file-noselect filename))))
- (if (not text-buffer)
- nil
- ;; Go to that buffer and find the erring line.
- (setq error-list
- (cons (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)
- (list error-marker (point-marker)))
- error-list))
- (message "Parsing error messages...%d" (length error-list))))
- (forward-line 1)))
- (setq compilation-parsing-end (point-max))
- (setq compilation-error-list
- (nconc compilation-error-list (nreverse error-list))))
- (message "Parsing error messages...done"))
-
- (defun compilation-grab-filename ()
- "Return a string which is a filename, starting at point.
- Ignore quotes and parentheses around it, as well as trailing colons."
- (if (eq (following-char) ?\")
- (save-restriction
- (narrow-to-region (point)
- (progn (forward-sexp 1) (point)))
- (goto-char (point-min))
- (read (current-buffer)))
- (buffer-substring (point)
- (progn
- (skip-chars-forward "^ :,\n\t(")
- (point)))))
-
- (defun rename-compilation-buffer (arg)
- "Rename the *compilation* buffer (e.g. to *compilation-1*) so that
- a second compilation process may be started. If ARG is non-nil,
- rename the current buffer to *compilation*."
- (interactive "P")
- (let ((buf (get-buffer "*compilation*")))
- (if buf
- (let ((i 1) new-name)
- (while (get-buffer (setq new-name (concat "*compilation-" i "*")))
- (setq i (1+ i)))
- (save-excursion
- (set-buffer buf)
- (rename-buffer new-name))
- (message "Buffer *compilation* has been renamed %s" new-name))
- (or arg
- (error "There is no *compilation* buffer."))))
- (if (not arg)
- nil
- (or (not buffer-file-name)
- (y-or-n-p (format
- "Buffer \"%s\" is attached to a file; rename anyhow? "
- (buffer-name)))
- (error "Buffer \"%s\" is attached to a file." (buffer-name)))
- (rename-buffer "*compilation*"))
- (set-buffer-modified-p (buffer-modified-p))) ;force modeline update
-
- (define-key ctl-x-map "`" 'next-error)
- \End\Of\Shar\
- else
- echo "will not over write ./compile.el"
- fi
- echo "Finished archive 1 of 1"
- exit
-