[comp.emacs] makefile mode

jcgs@harlqn.UUCP (John Sturdy) (10/31/88)

Here is a simple makefile mode for GNUemacs. Most of it is concerned
with completion on target and macro names, and inserting the names with
the correct syntax. A lot could be added to it! In particular, a
version of M-x compile which reads its argument with a completing-read
on targets would be nice.
The indentation function could certainly be improved.
------------------------------ cut here for makefile.el ----------------
;;; makefile mode for GNUemacs
;;; Last edited: Thu Oct 13 15:30:01 1988 by jcgs (John Sturdy) on harlqn

(provide 'makefile)

(defvar makefile-mode-map (make-sparse-keymap)
  "Keymap used in makefile mode.")

(define-key makefile-mode-map "$" 'insert-macro-reference)
(define-key makefile-mode-map "\C-c:" 'insert-target-reference)
(define-key makefile-mode-map ":" 'insert-target)
(define-key makefile-mode-map "=" 'insert-macro)
(define-key makefile-mode-map "\C-c\C-c" 'compile)

(defun insert-macro-reference (remake-macro-table)
  "Insert a makefile macro reference. With prefix argument, scan the buffer
afresh for macro definitions."
  (interactive "P")
  (if remake-macro-table
      (make-macro-name-table))
  (insert (format "${%s}" (completing-read "Macro name: " macro-name-table))))

(defun insert-target-reference (remake-target-table)
  "Insert a makefile target reference. With prefix argument, scan the buffer
afresh for target definitions."
  (interactive "P")
  (if remake-target-table
      (make-target-name-table))
  (insert (format "%s"                  ; might change format later
                  (completing-read "Target name: " target-name-table))))

(defun makefile-mode ()
  "Major mode for editing makefiles. Similar to fundamental-mode, but with
the following commands added:
\\{makefile-mode-map}"
  (interactive)
  (setq major-mode 'makefile-mode)
  (setq mode-name "Makefile")
  (setq comment-start "# ")
  (setq comment-end "")
  (setq comment-column 0)
  (setq comment-multi-line nil)
  (setq comment-start-skip "#+ *")
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'makefile-indent-line)
  (make-local-variable 'fill-prefix)
  (setq fill-prefix "\C-i")
  (use-local-map makefile-mode-map)
  (make-variable-buffer-local 'macro-name-table)
  (make-variable-buffer-local 'target-name-table)
  (make-macro-name-table)
  (make-target-name-table))

(defvar macro-name-table
  ; From rmail.el! I couldn't find in the documentation any way of making
  ; obarrays, so I grepped around and found a solitary example of how to
  ; set them up. I don't know whether the size really matters. BTW how do
  ; make-variable-buffer-local and defvar interact?
  (make-vector 47 0)
  "Obarray of macro names defined in this makefile.  This is a
suitable format for completing-read.")

(defvar target-name-table (make-vector 47 0)
  "Obarray of target names defined in this makefile.  This is a
suitable format for completing-read.")

(defun make-macro-name-table ()
  "Prepare the macro name table."
  (interactive)
  (make-name-table "=" macro-name-table))

(defun make-target-name-table ()
  "Prepare the target name table."
  (interactive)
  (make-name-table ":" target-name-table))

(defun make-name-table (name-end-marker array)
  "Prepare a table of names (from the beginning of a line up to
NAME-END-MARKER) in the obarray ARRAY from the present buffer."
  (save-excursion
    (save-restriction
      (widen)
      (goto-char (point-min))
      (while (search-forward name-end-marker (point-max) t)
        (let ((end-of-name (1- (point))))
          (beginning-of-line 1)
          (intern (buffer-substring (point) end-of-name) array)
          (end-of-line 1))))))

(defun insert-target (target-name)      ; target is most likely a filename
  "Insert a target, TARGET-NAME, using the appropriate \"make(1)\"
syntax. Called interactively, TARGET-NAME is read as a filename, since
many targets are files. It is better to insert targets using this
command than simply to type them in, partly because this command
provides the right syntax, tabs and all; and also because it updates
the target table that GNUemacs uses for completions on target names."
  (interactive "FTarget name: ")
  (if (string-match
       (concat "^" (expand-file-name default-directory))
       (expand-file-name target-name))
      (setq target-name
            (substring (expand-file-name target-name) (match-end 0))))
  (intern target-name target-name-table)
  (end-of-line 1)
  (insert "\n" target-name ":\C-i;")
  (backward-char 1))

(defun insert-macro (macro-name)
  "Insert a macro, MACRO-NAME, using the appropriate \"make(1)\"
syntax. It is better to insert macros using this command than simply
to type them in, partly because this command provides the right
syntax; and also because it updates the macro table that GNUemacs uses
for completions on macro names."
  (interactive "sMacro name: ")
  (intern macro-name macro-name-table)
  (end-of-line 1)
  (insert "\n" macro-name "="))

(defun makefile-indent-line ()
  "Function to indent a makefile line. It doesn't yet really do the
right thing."
  (save-excursion
    (beginning-of-line 1)
    (let ((line-start (point)))
      (re-search-forward "[^\C-i ]")
      (delete-region line-start (1- (point)))
      (goto-char line-start)
      (if (not (looking-at "#"))
          (insert "\C-i")))))

;;; end of makefile.el
--------------------------------------- end of makefile.el -------------
-- 
__John            The Lord bless you and watch over you, The Lord make his face
         shine upon you and be gracious to you, The Lord look kindly on you and
   give you peace; My brothers, my sisters, God bless you. Amen.  (St. Francis)
         jcgs@uk.co.harlqn Harlequin Ltd,Barrington,Cambridge,UK +44-223-872522