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