|
|
1.1 root 1: ;; Run compiler as inferior of Emacs, and parse its error messages.
2: ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is free software; you can redistribute it and/or modify
7: ;; it under the terms of the GNU General Public License as published by
8: ;; the Free Software Foundation; either version 1, or (at your option)
9: ;; any later version.
10:
11: ;; GNU Emacs is distributed in the hope that it will be useful,
12: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: ;; GNU General Public License for more details.
15:
16: ;; You should have received a copy of the GNU General Public License
17: ;; along with GNU Emacs; see the file COPYING. If not, write to
18: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19:
20: (provide 'compile)
21:
22: (defvar compilation-process nil
23: "Process created by compile command, or nil if none exists now.
24: Note that the process may have been \"deleted\" and still
25: be the value of this variable.")
26:
27: (defvar compilation-error-list nil
28: "List of error message descriptors for visiting erring functions.
29: Each error descriptor is a list of length two.
30: Its car is a marker pointing to an error message.
31: Its cadr is a marker pointing to the text of the line the message is about,
32: or nil if that is not interesting.
33: The value may be t instead of a list;
34: this means that the buffer of error messages should be reparsed
35: the next time the list of errors is wanted.")
36:
37: (defvar compilation-parsing-end nil
38: "Position of end of buffer when last error messages parsed.")
39:
40: (defvar compilation-error-message nil
41: "Message to print when no more matches for compilation-error-regexp are found")
42:
43: ;; The filename excludes colons to avoid confusion when error message
44: ;; starts with digits.
45: (defvar compilation-error-regexp
46: "\\([^ :\n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+ *of *[^ \n]+\\)"
47: "Regular expression for filename/linenumber in error in compilation log.")
48:
49: (defun compile (command)
50: "Compile the program including the current buffer. Default: run `make'.
51: Runs COMMAND, a shell command, in a separate process asynchronously
52: with output going to the buffer *compilation*.
53: You can then use the command \\[next-error] to find the next error message
54: and move to the source code that caused it."
55: (interactive (list (read-string "Compile command: " compile-command)))
56: (setq compile-command command)
57: (compile1 compile-command "No more errors"))
58:
59: (defun grep (command)
60: "Run grep, with user-specified args, and collect output in a buffer.
61: While grep runs asynchronously, you can use the \\[next-error] command
62: to find the text that grep hits refer to."
63: (interactive "sRun grep (with args): ")
64: (compile1 (concat "grep -n " command " /dev/null")
65: "No more grep hits" "grep"))
66:
67: (defun compile1 (command error-message &optional name-of-mode)
68: (save-some-buffers)
69: (if compilation-process
70: (if (or (not (eq (process-status compilation-process) 'run))
71: (yes-or-no-p "A compilation process is running; kill it? "))
72: (condition-case ()
73: (if compilation-process
74: (let ((comp-proc compilation-process))
75: (interrupt-process comp-proc)
76: (sit-for 1)
77: (delete-process comp-proc)))
78: (error nil))
79: (error "Cannot have two compilation processes")))
80: (setq compilation-process nil)
81: (compilation-forget-errors)
82: (setq compilation-error-list t)
83: (setq compilation-error-message error-message)
84: (setq compilation-process
85: (start-process "compilation" "*compilation*"
86: shell-file-name
87: "-c" (concat "exec " command)))
88: (with-output-to-temp-buffer "*compilation*"
89: (princ "cd ")
90: (princ default-directory)
91: (terpri)
92: (princ command)
93: (terpri))
94: (set-process-sentinel compilation-process 'compilation-sentinel)
95: (let* ((thisdir default-directory)
96: (outbuf (process-buffer compilation-process))
97: (outwin (get-buffer-window outbuf))
98: (regexp compilation-error-regexp))
99: (if (eq outbuf (current-buffer))
100: (goto-char (point-max)))
101: (save-excursion
102: (set-buffer outbuf)
103: (buffer-flush-undo outbuf)
104: (let ((start (save-excursion (set-buffer outbuf) (point-min))))
105: (set-window-start outwin start)
106: (or (eq outwin (selected-window))
107: (set-window-point outwin start)))
108: (setq default-directory thisdir)
109: (fundamental-mode)
110: (make-local-variable 'compilation-error-regexp)
111: (setq compilation-error-regexp regexp)
112: (setq mode-name (or name-of-mode "Compilation"))
113: ;; Make log buffer's mode line show process state
114: (setq mode-line-process '(": %s")))))
115:
116: ;; Called when compilation process changes state.
117:
118: (defun compilation-sentinel (proc msg)
119: (cond ((null (buffer-name (process-buffer proc)))
120: ;; buffer killed
121: (set-process-buffer proc nil))
122: ((memq (process-status proc) '(signal exit))
123: (let* ((obuf (current-buffer))
124: omax opoint)
125: ;; save-excursion isn't the right thing if
126: ;; process-buffer is current-buffer
127: (unwind-protect
128: (progn
129: ;; Write something in *compilation* and hack its mode line,
130: (set-buffer (process-buffer proc))
131: (setq omax (point-max) opoint (point))
132: (goto-char (point-max))
133: (insert ?\n mode-name " " msg)
134: (forward-char -1)
135: (insert " at "
136: (substring (current-time-string) 0 -5))
137: (forward-char 1)
138: (setq mode-line-process
139: (concat ": "
140: (symbol-name (process-status proc))))
141: ;; If buffer and mode line will show that the process
142: ;; is dead, we can delete it now. Otherwise it
143: ;; will stay around until M-x list-processes.
144: (delete-process proc))
145: (setq compilation-process nil)
146: ;; Force mode line redisplay soon
147: (set-buffer-modified-p (buffer-modified-p)))
148: (if (and opoint (< opoint omax))
149: (goto-char opoint))
150: (set-buffer obuf)))))
151:
152: (defun kill-compilation ()
153: "Kill the process made by the \\[compile] command."
154: (interactive)
155: (if compilation-process
156: (interrupt-process compilation-process)))
157:
158: (defun kill-grep ()
159: "Kill the process made by the \\[grep] command."
160: (interactive)
161: (if compilation-process
162: (interrupt-process compilation-process)))
163:
164: (defun next-error (&optional argp)
165: "Visit next compilation error message and corresponding source code.
166: This operates on the output from the \\[compile] command.
167: If all preparsed error messages have been processed,
168: the error message buffer is checked for new ones.
169: A non-nil argument (prefix arg, if interactive)
170: means reparse the error message buffer and start at the first error."
171: (interactive "P")
172: (if (or (eq compilation-error-list t)
173: argp)
174: (progn (compilation-forget-errors)
175: (setq compilation-parsing-end 1)))
176: (if compilation-error-list
177: nil
178: (save-excursion
179: (set-buffer "*compilation*")
180: (set-buffer-modified-p nil)
181: (compilation-parse-errors)))
182: (let ((next-error (car compilation-error-list)))
183: (if (null next-error)
184: (error (concat compilation-error-message
185: (if (and compilation-process
186: (eq (process-status compilation-process)
187: 'run))
188: " yet" ""))))
189: (setq compilation-error-list (cdr compilation-error-list))
190: (if (null (car (cdr next-error)))
191: nil
192: (switch-to-buffer (marker-buffer (car (cdr next-error))))
193: (goto-char (car (cdr next-error)))
194: (set-marker (car (cdr next-error)) nil))
195: (let* ((pop-up-windows t)
196: (w (display-buffer (marker-buffer (car next-error)))))
197: (set-window-point w (car next-error))
198: (set-window-start w (car next-error)))
199: (set-marker (car next-error) nil)))
200:
201: ;; Set compilation-error-list to nil, and
202: ;; unchain the markers that point to the error messages and their text,
203: ;; so that they no longer slow down gap motion.
204: ;; This would happen anyway at the next garbage collection,
205: ;; but it is better to do it right away.
206: (defun compilation-forget-errors ()
207: (if (eq compilation-error-list t)
208: (setq compilation-error-list nil))
209: (while compilation-error-list
210: (let ((next-error (car compilation-error-list)))
211: (set-marker (car next-error) nil)
212: (if (car (cdr next-error))
213: (set-marker (car (cdr next-error)) nil)))
214: (setq compilation-error-list (cdr compilation-error-list))))
215:
216: (defun compilation-parse-errors ()
217: "Parse the current buffer as error messages.
218: This makes a list of error descriptors, compilation-error-list.
219: For each source-file, line-number pair in the buffer,
220: the source file is read in, and the text location is saved in compilation-error-list.
221: The function next-error, assigned to \\[next-error], takes the next error off the list
222: and visits its location."
223: (setq compilation-error-list nil)
224: (message "Parsing error messages...")
225: (let (text-buffer
226: last-filename last-linenum)
227: ;; Don't reparse messages already seen at last parse.
228: (goto-char compilation-parsing-end)
229: ;; Don't parse the first two lines as error messages.
230: ;; This matters for grep.
231: (if (bobp)
232: (forward-line 2))
233: (while (re-search-forward compilation-error-regexp nil t)
234: (let (linenum filename
235: error-marker text-marker)
236: ;; Extract file name and line number from error message.
237: (save-restriction
238: (narrow-to-region (match-beginning 0) (match-end 0))
239: (goto-char (point-max))
240: (skip-chars-backward "[0-9]")
241: ;; If it's a lint message, use the last file(linenum) on the line.
242: ;; Normally we use the first on the line.
243: (if (= (preceding-char) ?\()
244: (progn
245: (narrow-to-region (point-min) (1+ (buffer-size)))
246: (end-of-line)
247: (re-search-backward compilation-error-regexp)
248: (skip-chars-backward "^ \t\n")
249: (narrow-to-region (point) (match-end 0))
250: (goto-char (point-max))
251: (skip-chars-backward "[0-9]")))
252: ;; Are we looking at a "filename-first" or "line-number-first" form?
253: (if (looking-at "[0-9]")
254: (progn
255: (setq linenum (read (current-buffer)))
256: (goto-char (point-min)))
257: ;; Line number at start, file name at end.
258: (progn
259: (goto-char (point-min))
260: (setq linenum (read (current-buffer)))
261: (goto-char (point-max))
262: (skip-chars-backward "^ \t\n")))
263: (setq filename (compilation-grab-filename)))
264: ;; Locate the erring file and line.
265: (if (and (equal filename last-filename)
266: (= linenum last-linenum))
267: nil
268: (beginning-of-line 1)
269: (setq error-marker (point-marker))
270: ;; text-buffer gets the buffer containing this error's file.
271: (if (not (equal filename last-filename))
272: (setq text-buffer
273: (and (file-exists-p (setq last-filename filename))
274: (find-file-noselect filename))
275: last-linenum 0))
276: (if text-buffer
277: ;; Go to that buffer and find the erring line.
278: (save-excursion
279: (set-buffer text-buffer)
280: (if (zerop last-linenum)
281: (progn
282: (goto-char 1)
283: (setq last-linenum 1)))
284: ;; Move the right number of lines from the old position.
285: ;; If we can't move that many, put 0 in last-linenum
286: ;; so the next error message will be handled starting from
287: ;; scratch.
288: (if (eq selective-display t)
289: (or (re-search-forward "[\n\C-m]" nil 'end
290: (- linenum last-linenum))
291: (setq last-linenum 0))
292: (or (= 0 (forward-line (- linenum last-linenum)))
293: (setq last-linenum 0)))
294: (setq last-linenum linenum)
295: (setq text-marker (point-marker))
296: (setq compilation-error-list
297: (cons (list error-marker text-marker)
298: compilation-error-list)))))
299: (forward-line 1)))
300: (setq compilation-parsing-end (point-max)))
301: (message "Parsing error messages...done")
302: (setq compilation-error-list (nreverse compilation-error-list)))
303:
304: (defun compilation-grab-filename ()
305: "Return a string which is a filename, starting at point.
306: Ignore quotes and parentheses around it, as well as trailing colons."
307: (if (eq (following-char) ?\")
308: (save-restriction
309: (narrow-to-region (point)
310: (progn (forward-sexp 1) (point)))
311: (goto-char (point-min))
312: (read (current-buffer)))
313: (buffer-substring (point)
314: (progn
315: (skip-chars-forward "^ :,\n\t(")
316: (point)))))
317:
318: (define-key ctl-x-map "`" 'next-error)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.