home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / lisp / packages / compile.el < prev    next >
Encoding:
Text File  |  1993-02-17  |  19.0 KB  |  497 lines

  1. ;; Run compiler as inferior of Emacs, and parse its error messages.
  2. ;; Copyright (C) 1985-1993 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 2, 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-error-list nil
  23.   "List of error message descriptors for visiting erring functions.
  24. Each error descriptor is a list of length two.
  25. Its car is a marker pointing to an error message.
  26. Its cadr is a marker pointing to the text of the line the message is about,
  27.   or nil if that is not interesting.
  28. The value may be t instead of a list;
  29. this means that the buffer of error messages should be reparsed
  30. the next time the list of errors is wanted.")
  31.  
  32. (defvar compilation-old-error-list nil
  33.   "Value of `compilation-error-list' after errors were parsed.")
  34.  
  35. (defvar compilation-last-error nil
  36.   "List describing the error found by last call to \\[next-error].
  37. A list of two markers (ERROR-POS CODE-POS),
  38. pointing to the error message and the erroneous code, respectively.
  39. CODE-POS can be nil, if the error message has no specific source location.")
  40.  
  41. (defvar compilation-parse-errors-hook 'compilation-parse-errors
  42.   "Function to call (no args) to parse error messages from a compilation.
  43. It should read in the source files which have errors
  44. and set `compilation-error-list' to a list with an element
  45. for each error message found.  See that variable for more info.")
  46.  
  47. (defvar compilation-error-buffer nil
  48.   "Current compilation buffer for compilation error processing.") 
  49.  
  50. (defvar compilation-parsing-end nil
  51.   "Position of end of buffer when last error messages parsed.")
  52.  
  53. (defvar compilation-error-message nil
  54.   "Message to print when no more matches for compilation-error-regexp are found")
  55.  
  56. ;; The filename excludes colons to avoid confusion when error message
  57. ;; starts with digits.
  58. (defvar compilation-error-regexp
  59.   "\\([^ :\n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+ *of *[^ \n]+\\)\\|\\(\"[^ \n]+\",L[0-9]+\\)"
  60.   "Regular expression for filename/linenumber in error in compilation log.")
  61.  
  62. (defvar compile-window-height nil
  63.   "*Desired height of compilation window.  nil means use Emacs default.")
  64.  
  65. (defvar compile-command "make -k "
  66.   "Last shell command used to do a compilation; default for next compilation.
  67.  
  68. Sometimes it is useful for files to supply local values for this variable.
  69. You might also use mode hooks to specify it in certain modes, like this:
  70.  
  71.     (setq c-mode-hook
  72.       '(lambda () (or (file-exists-p \"makefile\") (file-exists-p \"Makefile\")
  73.               (progn (make-local-variable 'compile-command)
  74.                  (setq compile-command
  75.                     (concat \"make -k \"
  76.                         buffer-file-name))))))")
  77.  
  78. (defvar compilation-search-path '(nil)
  79.   "List of directories to search for source files named in error messages.
  80. Elements should be directory names, not file names of directories.
  81. nil as an element means to try the default directory.")
  82.  
  83. (defvar minibuffer-compile-history nil)
  84.  
  85. (defun compile (command)
  86.   "Compile the program including the current buffer.  Default: run `make'.
  87. Runs COMMAND, a shell command, in a separate process asynchronously
  88. with output going to the buffer `*compilation*'.
  89. You can then use the command \\[next-error] to find the next error message
  90. and move to the source code that caused it.
  91.  
  92. To run more than one compilation at once, start one and rename the
  93. `*compilation*' buffer to some other name.  Then start the next one."
  94.   (interactive (list (read-string "Compile command: " compile-command
  95.                   'minibuffer-compile-history)))
  96.   (setq compile-command command)
  97.   (save-some-buffers nil nil)
  98.   (compile-internal compile-command "No more errors")
  99.   (and compile-window-height
  100.        (= (window-width) (screen-width))
  101.        (enlarge-window (- (- (screen-height) (window-height))
  102.               compile-window-height) nil)))
  103.  
  104. (defun grep (command-args)
  105.   "Run grep, with user-specified args, and collect output in a buffer.
  106. While grep runs asynchronously, you can use the \\[next-error] command
  107. to find the text that grep hits refer to.  It is expected that `grep-command'
  108. has a `-n' flag, so that line numbers are displayed for each match."
  109.   (interactive
  110.    (list (read-shell-command
  111.       (concat "Run "
  112.           (substring grep-command 0
  113.                  (string-match "[\t ]+" grep-command))
  114.           " (with args): ")
  115.       ;; no need to insert default now that we have minibuf history
  116. ;      (if (string-match "-n[\t ]+" grep-command)
  117. ;          (substring grep-command (match-end 0))
  118. ;        "")
  119.       )))
  120.   ;; why a redundant string-match?  It might not be interactive ...
  121.   (setq grep-command (concat (substring grep-command 0
  122.                     (if (string-match "-n" grep-command)
  123.                         (match-end 0)
  124.                       (length grep-command)))
  125.                  " " command-args))
  126.   (compile-internal (concat grep-command " /dev/null")
  127.             "No more grep hits" "grep"))
  128.  
  129. (defun compile-internal (command error-message
  130.                  &optional name-of-mode parser regexp)
  131.   "Run compilation command COMMAND (low level interface).
  132. ERROR-MESSAGE is a string to print if the user asks to see another error
  133. and there are no more errors.  Third argument NAME-OF-MODE is the name
  134. to display as the major mode in the `*compilation*' buffer.
  135.  
  136. Fourth arg PARSER is the error parser function (nil means the default).
  137. Fifth arg REGEXP is the error message regexp to use (nil means the default).
  138. The defaults for these variables are the global values of
  139.  `compilation-parse-errors-hook' and `compilation-error-regexp'."
  140.   (save-excursion
  141.     (set-buffer (get-buffer-create "*compilation*"))
  142.     (setq buffer-read-only nil)
  143.     (let ((comp-proc (get-buffer-process (current-buffer))))
  144.       (if comp-proc
  145.       (if (or (not (eq (process-status comp-proc) 'run))
  146.           (yes-or-no-p "A compilation process is running; kill it? "))
  147.           (condition-case ()
  148.           (progn
  149.             (interrupt-process comp-proc)
  150.             (sit-for 1)
  151.             (delete-process comp-proc))
  152.         (error nil))
  153.       (error "Cannot have two processes in `*compilation*' at once"))))
  154.     ;; In case *compilation* is current buffer,
  155.     ;; make sure we get the global values of compilation-error-regexp, etc.
  156.     (kill-all-local-variables))
  157.   (compilation-forget-errors)
  158.   (start-process-shell-command "compilation" "*compilation*" command)
  159.   (with-output-to-temp-buffer "*compilation*"
  160.     (princ "cd ")
  161.     (princ default-directory)
  162.     (terpri)
  163.     (princ command)
  164.     (terpri))
  165.   (let* ((regexp (or regexp compilation-error-regexp))
  166.      (parser (or parser compilation-parse-errors-hook))
  167.      (thisdir default-directory)
  168.      (outbuf (get-buffer "*compilation*"))
  169.      (outwin (get-buffer-window outbuf)))
  170.     (if (eq outbuf (current-buffer))
  171.     (goto-char (point-max)))
  172.     (set-process-sentinel (get-buffer-process outbuf)
  173.               'compilation-sentinel)
  174.     (save-excursion
  175.       (set-buffer outbuf)
  176.       (if (or (eq compilation-error-buffer outbuf)
  177.           (eq compilation-error-list t)
  178.           (and (null compilation-error-list)
  179.            (not (and (get-buffer-process compilation-error-buffer)
  180.                  (eq (process-status compilation-error-buffer)
  181.                  'run)))))
  182.       (setq compilation-error-list t
  183.         compilation-error-buffer outbuf))
  184.       (setq default-directory thisdir)
  185.       (compilation-mode)
  186.       (set-window-start outwin (point-min))
  187.       (setq mode-name (or name-of-mode "Compilation"))
  188.       ;; (setq buffer-read-only t) ;no no no. -jwz.
  189.       (or (eq outwin (selected-window))
  190.       (set-window-point outwin (point-min))))))
  191.  
  192. (defvar compilation-mode-map
  193.   (let ((map (make-sparse-keymap)))
  194.     (define-key map "\C-c\C-c" 'compile-goto-error)
  195.     map)
  196.   "Keymap for compilation log buffers.")
  197.  
  198. (defun compilation-mode ()
  199.   "Major mode for compilation log buffers.
  200. \\<compilation-mode-map>To visit the source for a line-numbered error,
  201. move point to the error message line and type \\[compile-goto-error]."
  202.   (interactive)
  203.   (fundamental-mode)
  204.   (use-local-map compilation-mode-map)
  205.   (make-local-variable 'compilation-parse-errors-hook)
  206.   (setq compilation-parse-errors-hook parser)
  207.   (make-local-variable 'compilation-error-message)
  208.   (setq compilation-error-message error-message)
  209.   (make-local-variable 'compilation-error-regexp)
  210.   (setq compilation-error-regexp regexp)
  211.   (buffer-disable-undo (current-buffer))
  212.   (setq major-mode 'compilation-mode)
  213.   (setq mode-name "Compilation")
  214.   ;; Make log buffer's mode line show process state
  215.   (setq mode-line-process '(": %s")))
  216.  
  217. ;; Called when compilation process changes state.
  218.  
  219. (defun compilation-sentinel (proc msg)
  220.   (cond ((null (buffer-name (process-buffer proc)))
  221.      ;; buffer killed
  222.      (set-process-buffer proc nil))
  223.     ((memq (process-status proc) '(signal exit))
  224.      (let* ((obuf (current-buffer))
  225.         omax opoint)
  226.        ;; save-excursion isn't the right thing if
  227.        ;;  process-buffer is current-buffer
  228.        (unwind-protect
  229.            (progn
  230.          ;; Write something in *compilation* and hack its mode line,
  231.          (set-buffer (process-buffer proc))
  232.          (setq omax (point-max) opoint (point))
  233.          (goto-char (point-max))
  234.          (insert ?\n mode-name " " msg)
  235.          (forward-char -1)
  236.          (insert " at " (substring (current-time-string) 0 19))
  237.          (forward-char 1)
  238.          (setq mode-line-process
  239.                (concat ": "
  240.                    (symbol-name (process-status proc))))
  241.          ;; If buffer and mode line will show that the process
  242.          ;; is dead, we can delete it now.  Otherwise it
  243.          ;; will stay around until M-x list-processes.
  244.          (delete-process proc))
  245.          ;; Force mode line redisplay soon
  246.          (set-buffer-modified-p (buffer-modified-p)))
  247.        (if (and opoint (< opoint omax))
  248.            (goto-char opoint))
  249.        (set-buffer obuf)))))
  250.  
  251. (defun kill-compilation ()
  252.   "Kill the process made by the \\[compile] command."
  253.   (interactive)
  254.   (let ((buffer
  255.      (if (assq 'compilation-parse-errors-hook (buffer-local-variables))
  256.          (current-buffer)
  257.        (get-buffer "*compilation*"))))
  258.     (if (get-buffer-process buffer)
  259.     (interrupt-process (get-buffer-process buffer)))))
  260.  
  261. ;; Reparse errors or parse more/new errors, if appropriate.
  262. (defun compile-reinitialize-errors (argp)
  263.   ;; If we are out of errors, or if user says "reparse",
  264.   ;; or if we are in a different buffer from the known errors,
  265.   ;; discard the info we have, to force reparsing.
  266.   (if (or (eq compilation-error-list t)
  267.       (consp argp)
  268.       (if (assq 'compilation-parse-errors-hook (buffer-local-variables))
  269.           (not (eq compilation-error-buffer
  270.                (setq compilation-error-buffer (current-buffer))))))
  271.       (progn (compilation-forget-errors)
  272.          (setq compilation-parsing-end 1)))
  273.   (if (null compilation-error-buffer)
  274.       (error "no *compilation* buffer"))
  275.   (if (null (buffer-name compilation-error-buffer))
  276.       (error "*compilation* buffer has been killed"))
  277.   (if compilation-error-list
  278.       nil
  279.     (save-excursion
  280.       (switch-to-buffer compilation-error-buffer)
  281.       (set-buffer-modified-p nil)
  282.       (let ((at-start (= compilation-parsing-end 1)))
  283.     (run-hooks 'compilation-parse-errors-hook)
  284.     ;; Remember the entire list for compilation-forget-errors.
  285.     ;; If this is an incremental parse, append to previous list.
  286.     (if at-start
  287.         (setq compilation-old-error-list compilation-error-list)
  288.       (setq compilation-old-error-list
  289.         (nconc compilation-old-error-list compilation-error-list)))))))
  290.  
  291. (defun compile-goto-error (&optional argp)
  292.   "Visit the source for the error message point is on.
  293. Use this command in a compilation log buffer.
  294. C-u as a prefix arg means to reparse the buffer's error messages first;
  295. other kinds of prefix arguments are ignored."
  296.   (interactive "P")
  297.   (compile-reinitialize-errors argp)
  298.   (save-excursion
  299.     (beginning-of-line)
  300.     (setq compilation-error-list
  301.       (memq (assoc (point-marker) compilation-old-error-list)
  302.         compilation-old-error-list)))
  303.   ;; Move to another window, so that next-error's window changes
  304.   ;; result in the desired setup.
  305.   (or (one-window-p)
  306.       (other-window -1))
  307.   (next-error 1))
  308.  
  309. (defun next-error (&optional argp)
  310.   "Visit next compilation error message and corresponding source code.
  311. This operates on the output from the \\[compile] command.
  312. If all preparsed error messages have been processed,
  313. the error message buffer is checked for new ones.
  314.  
  315. A prefix arg specifies how many error messages to move;
  316. negative means move back to previous error messages.
  317. Just C-u as a prefix means reparse the error message buffer
  318. and start at the first error.
  319.  
  320. \\[next-error] normally applies to the most recent compilation started,
  321. but as long as you are in the middle of parsing errors from one compilation
  322. output buffer, you stay with that compilation output buffer.
  323.  
  324. Use \\[next-error] in a compilation output buffer to switch to
  325. processing errors from that compilation.
  326.  
  327. See variables `compilation-parse-errors-hook' and `compilation-error-regexp'
  328. for customization ideas.  When we return, `compilation-last-error'
  329. points to the error message and the erroneous code."
  330.   (interactive "P")
  331.   (compile-reinitialize-errors argp)
  332.   (if (consp argp)
  333.       (setq argp nil))
  334.   (let* ((next-errors (nthcdr (+ (- (length compilation-old-error-list)
  335.                     (length compilation-error-list)
  336.                     1)
  337.                  (prefix-numeric-value argp))
  338.                   compilation-old-error-list))
  339.      (next-error (car next-errors)))
  340.     (if (null next-error)
  341.     (save-excursion
  342.       (if argp (if (> (prefix-numeric-value argp) 0)
  343.                (error "Moved past last error")
  344.              (error "Moved back past first error")))
  345.       (set-buffer compilation-error-buffer)
  346.       (compilation-forget-errors)
  347.       (error (concat compilation-error-message
  348.              (if (and (get-buffer-process (current-buffer))
  349.                   (eq (process-status 
  350.                                        (get-buffer-process (current-buffer)))
  351.                       'run))
  352.                  " yet" "")))))
  353.     (setq compilation-error-list (cdr next-errors))
  354.     ;; If we have an error to go to, go there.
  355.     (if (null (car (cdr next-error)))
  356.     nil
  357.       (switch-to-buffer (marker-buffer (car (cdr next-error))))
  358.       (goto-char (car (cdr next-error)))
  359.       ;; If narrowing got in the way of going to the right place, widen.
  360.       (or (= (point) (car (cdr next-error)))
  361.       (progn
  362.         (widen)
  363.         (goto-char (car (cdr next-error))))))
  364.     ;; Show compilation buffer in other window, scrolled to this error.
  365.     (let* ((pop-up-windows t)
  366.        (w (display-buffer (marker-buffer (car next-error)))))
  367.       (set-window-point w (car next-error))
  368.       (set-window-start w (car next-error)))
  369.     (setq compilation-last-error next-error)))
  370.  
  371. (defun previous-error (&optional argp)
  372.   "\\[next-error] backwards."
  373.     (interactive "P")
  374.     (next-error (cond ((null argp) -1)
  375.               ((numberp argp) (- argp))
  376.               (t argp))))
  377.  
  378. ;; Set compilation-error-list to nil, and
  379. ;; unchain the markers that point to the error messages and their text,
  380. ;; so that they no longer slow down gap motion.
  381. ;; This would happen anyway at the next garbage collection,
  382. ;; but it is better to do it right away.
  383. (defun compilation-forget-errors ()
  384.   (while compilation-old-error-list
  385.     (let ((next-error (car compilation-old-error-list)))
  386.       (set-marker (car next-error) nil)
  387.       (if (car (cdr next-error))
  388.       (set-marker (car (cdr next-error)) nil)))
  389.     (setq compilation-old-error-list (cdr compilation-old-error-list)))
  390.   (setq compilation-error-list nil))
  391.  
  392. (defun compilation-parse-errors ()
  393.   "Parse the current buffer as grep, cc or lint error messages.
  394. See variable `compilation-parse-errors-hook' for the interface it uses."
  395.   (setq compilation-error-list nil)
  396.   (message "Parsing error messages...")
  397.   (let (text-buffer
  398.     last-filename last-linenum)
  399.     ;; Don't reparse messages already seen at last parse.
  400.     (goto-char compilation-parsing-end)
  401.     ;; Don't parse the first two lines as error messages.
  402.     ;; This matters for grep.
  403.     (if (bobp)
  404.     (forward-line 2))
  405.     (while (re-search-forward compilation-error-regexp nil t)
  406.       (let (linenum filename
  407.         error-marker text-marker)
  408.     ;; Extract file name and line number from error message.
  409.     (save-restriction
  410.       (narrow-to-region (match-beginning 0) (match-end 0))
  411.       (goto-char (point-max))
  412.       (skip-chars-backward "[0-9]")
  413.       ;; If it's a lint message, use the last file(linenum) on the line.
  414.       ;; Normally we use the first on the line.
  415.       (if (= (preceding-char) ?\()
  416.           (progn
  417.         (narrow-to-region (point-min) (1+ (buffer-size)))
  418.         (end-of-line)
  419.         (re-search-backward compilation-error-regexp)
  420.         (skip-chars-backward "^ \t\n")
  421.         (narrow-to-region (point) (match-end 0))
  422.         (goto-char (point-max))
  423.         (skip-chars-backward "[0-9]")))
  424.       ;; Are we looking at a "filename-first" or "line-number-first" form?
  425.       (if (looking-at "[0-9]")
  426.           (progn
  427.         (setq linenum (read (current-buffer)))
  428.         (goto-char (point-min)))
  429.         ;; Line number at start, file name at end.
  430.         (progn
  431.           (goto-char (point-min))
  432.           (setq linenum (read (current-buffer)))
  433.           (goto-char (point-max))
  434.           (skip-chars-backward "^ \t\n")))
  435.       (setq filename (compilation-grab-filename)))
  436.     ;; Locate the erring file and line.
  437.     (if (and (equal filename last-filename)
  438.          (= linenum last-linenum))
  439.         nil
  440.       (beginning-of-line 1)
  441.       (setq error-marker (point-marker))
  442.       ;; text-buffer gets the buffer containing this error's file.
  443.       (if (not (equal filename last-filename))
  444.           (setq last-filename filename
  445.             text-buffer (compilation-find-file filename)
  446.             last-linenum 0))
  447.       (if text-buffer
  448.           ;; Go to that buffer and find the erring line.
  449.           (save-excursion
  450.         (set-buffer text-buffer)
  451.         (if (zerop last-linenum)
  452.             (progn
  453.               (goto-char 1)
  454.               (setq last-linenum 1)))
  455.         (forward-line (- linenum last-linenum))
  456.         (setq last-linenum linenum)
  457.         (setq text-marker (point-marker))
  458.         (setq compilation-error-list
  459.               (cons (list error-marker text-marker)
  460.                 compilation-error-list)))))
  461.     (forward-line 1)))
  462.     (setq compilation-parsing-end (point-max)))
  463.   (message "Parsing error messages...done")
  464.   (setq compilation-error-list (nreverse compilation-error-list)))
  465.  
  466. ;; Find or create a buffer for file FILENAME.
  467. ;; Search the directories in compilation-search-path
  468. ;; after trying the current directory.
  469. (defun compilation-find-file (filename)
  470.   (let ((dirs compilation-search-path)
  471.     result)
  472.     (while (and dirs (null result))
  473.       (let ((name (if (car dirs)
  474.               (concat (car dirs) filename)
  475.             filename)))
  476.     (setq result
  477.           (and (file-exists-p name)
  478.            (find-file-noselect name))))
  479.       (setq dirs (cdr dirs)))
  480.     result))
  481.  
  482. (defun compilation-grab-filename ()
  483.   "Return a string which is a filename, starting at point.
  484. Ignore quotes and parentheses around it, as well as trailing colons."
  485.   (if (eq (following-char) ?\")
  486.       (save-restriction
  487.     (narrow-to-region (point)
  488.               (progn (forward-sexp 1) (point)))
  489.     (goto-char (point-min))
  490.     (read (current-buffer)))
  491.     (buffer-substring (point)
  492.               (progn
  493.             (skip-chars-forward "^ :,\n\t(")
  494.             (point)))))
  495.  
  496. (define-key ctl-x-map "`" 'next-error)
  497.