;; Author: Thomas Neumann <tom@smart.bo.open.de>
;; Eric S. Raymond <esr@snark.thyrsus.com>
+;; Maintainer: FSF
;; Adapted-By: ESR
;; Keywords: unix, tools
;;
;; To Do:
;;
-;; * makefile-backslash-region should be given better behavior.
-;; * Consider binding C-c C-c to comment-region (like cc-mode).
;; * Eliminate electric stuff entirely.
;; * It might be nice to highlight targets differently depending on
;; whether they are up-to-date or not. Not sure how this would
;;; Code:
-(provide 'makefile)
+(provide 'make-mode)
;; Sadly we need this for a macro.
(eval-when-compile
;;; Configurable stuff
;;; ------------------------------------------------------------
-(defvar makefile-browser-buffer-name "*Macros and Targets*"
- "Name of the macro- and target browser buffer.")
+(defgroup makefile nil
+ "Makefile editing commands for Emacs."
+ :group 'tools
+ :prefix "makefile-")
-(defvar makefile-target-colon ":"
+(defface makefile-space-face
+ '((((class color)) (:background "hotpink"))
+ (t (:reverse-video t)))
+ "Face to use for highlighting leading spaces in Font-Lock mode."
+ :group 'faces
+ :group 'makemode)
+
+(defcustom makefile-browser-buffer-name "*Macros and Targets*"
+ "Name of the macro- and target browser buffer."
+ :type 'string
+ :group 'makefile)
+
+(defcustom makefile-target-colon ":"
"String to append to all target names inserted by `makefile-insert-target'.
-\":\" or \"::\" are common values.")
+\":\" or \"::\" are common values."
+ :type 'string
+ :group 'makefile)
-(defvar makefile-macro-assign " = "
+(defcustom makefile-macro-assign " = "
"String to append to all macro names inserted by `makefile-insert-macro'.
The normal value should be \" = \", since this is what
standard make expects. However, newer makes such as dmake
allow a larger variety of different macro assignments, so you
-might prefer to use \" += \" or \" := \" .")
+might prefer to use \" += \" or \" := \" ."
+ :type 'string
+ :group 'makefile)
-(defvar makefile-electric-keys nil
- "If non-nil, install electric keybindings.
-Default is nil.")
+(defcustom makefile-electric-keys nil
+ "If non-nil, Makefile mode should install electric keybindings.
+Default is nil."
+ :type 'boolean
+ :group 'makefile)
-(defvar makefile-use-curly-braces-for-macros-p nil
+(defcustom makefile-use-curly-braces-for-macros-p nil
"Controls the style of generated macro references.
-t (actually non-nil) means macro references should use curly braces,
-like `${this}'.
-nil means use parentheses, like `$(this)'.")
+Non-nil means macro references should use curly braces, like `${this}'.
+nil means use parentheses, like `$(this)'."
+ :type 'boolean
+ :group 'makefile)
-(defvar makefile-tab-after-target-colon t
+(defcustom makefile-tab-after-target-colon t
"If non-nil, insert a TAB after a target colon.
Otherwise, a space is inserted.
-The default is t.")
-
-(defvar makefile-browser-leftmost-column 10
- "Number of blanks to the left of the browser selection mark.")
-
-(defvar makefile-browser-cursor-column 10
- "Column in which the cursor is positioned when it moves
-up or down in the browser.")
-
-(defvar makefile-backslash-column 48
- "*Column in which `makefile-backslash-region' inserts backslashes.")
-
-(defvar makefile-browser-selected-mark "+ "
- "String used to mark selected entries in the browser.")
-
-(defvar makefile-browser-unselected-mark " "
- "String used to mark unselected entries in the browser.")
-
-(defvar makefile-browser-auto-advance-after-selection-p t
- "If non-nil, cursor will move after item is selected in browser.")
-
-(defvar makefile-pickup-everything-picks-up-filenames-p nil
+The default is t."
+ :type 'boolean
+ :group 'makefile)
+
+(defcustom makefile-browser-leftmost-column 10
+ "Number of blanks to the left of the browser selection mark."
+ :type 'integer
+ :group 'makefile)
+
+(defcustom makefile-browser-cursor-column 10
+ "Column the cursor goes to when it moves up or down in the Makefile browser."
+ :type 'integer
+ :group 'makefile)
+
+(defcustom makefile-backslash-column 48
+ "*Column in which `makefile-backslash-region' inserts backslashes."
+ :type 'integer
+ :group 'makefile)
+
+(defcustom makefile-backslash-align t
+ "If non-nil, `makefile-backslash-region' will align backslashes."
+ :type 'boolean
+ :group 'makefile)
+
+(defcustom makefile-browser-selected-mark "+ "
+ "String used to mark selected entries in the Makefile browser."
+ :type 'string
+ :group 'makefile)
+
+(defcustom makefile-browser-unselected-mark " "
+ "String used to mark unselected entries in the Makefile browser."
+ :type 'string
+ :group 'makefile)
+
+(defcustom makefile-browser-auto-advance-after-selection-p t
+ "If non-nil, cursor will move after item is selected in Makefile browser."
+ :type 'boolean
+ :group 'makefile)
+
+(defcustom makefile-pickup-everything-picks-up-filenames-p nil
"If non-nil, `makefile-pickup-everything' picks up filenames as targets.
-\(i.e. it calls `makefile-find-filenames-as-targets').
-Otherwise filenames are omitted.")
+This means it calls `makefile-pickup-filenames-as-targets'.
+Otherwise filenames are omitted."
+ :type 'boolean
+ :group 'makefile)
-(defvar makefile-cleanup-continuations-p t
+(defcustom makefile-cleanup-continuations-p t
"If non-nil, automatically clean up continuation lines when saving.
A line is cleaned up by removing all whitespace following a trailing
backslash. This is done silently.
IMPORTANT: Please note that enabling this option causes makefile-mode
-to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.")
+to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\"."
+ :type 'boolean
+ :group 'makefile)
(defvar makefile-browser-hook '())
;;
;; Special targets for DMake, Sun's make ...
;;
-(defvar makefile-special-targets-list
+(defcustom makefile-special-targets-list
'(("DEFAULT") ("DONE") ("ERROR") ("EXPORT")
("FAILED") ("GROUPEPILOG") ("GROUPPROLOG") ("IGNORE")
("IMPORT") ("INCLUDE") ("INCLUDEDIRS") ("INIT")
("el.elc") ("y.c") ("s.o"))
"List of special targets.
You will be offered to complete on one of those in the minibuffer whenever
-you enter a \".\" at the beginning of a line in makefile-mode.")
+you enter a \".\" at the beginning of a line in makefile-mode."
+ :type '(repeat (list string))
+ :group 'makefile)
-(defvar makefile-runtime-macros-list
- '(("@") ("&") (">") ("<") ("*") ("^") ("?") ("%") ("$"))
+(defcustom makefile-runtime-macros-list
+ '(("@") ("&") (">") ("<") ("*") ("^") ("+") ("?") ("%") ("$"))
"List of macros that are resolved by make at runtime.
If you insert a macro reference using makefile-insert-macro-ref, the name
of the macro is checked against this list. If it can be found its name will
-not be enclosed in { } or ( ).")
+not be enclosed in { } or ( )."
+ :type '(repeat (list string))
+ :group 'makefile)
;; Note that the first big subexpression is used by font lock. Note
;; that if you change this regexp you must fix the imenu index
;; function defined at the end of the file.
(defconst makefile-dependency-regex
- "^\\([^ \n\t#:]+\\([ \t]+[^ \t\n#:]+\\)*\\)[ \t]*:\\([ \t]*$\\|\\([^=\n].*$\\)\\)"
+ "^ *\\([^ \n\t#:=]+\\([ \t]+[^ \t\n#:=]+\\)*\\)[ \t]*:\\([ \t]*$\\|\\([^=\n].*$\\)\\)"
"Regex used to find dependency lines in a makefile.")
;; Note that the first subexpression is used by font lock. Note that
;; if you change this regexp you must fix the imenu index function
;; defined at the end of the file.
(defconst makefile-macroassign-regex
- "^\\([^ \n\t][^:#= \t\n]*\\)[ \t]*[*:+]?:?="
+ "^ *\\([^ \n\t][^:#= \t\n]*\\)[ \t]*[*:+]?:?="
"Regex used to find macro assignment lines in a makefile.")
(defconst makefile-ignored-files-in-pickup-regex
;; arbitrarily.
(list makefile-macroassign-regex 1 'font-lock-variable-name-face)
;;
- ;; Variable references even in targets/strings/comments:
- '("\\$[({]\\([a-zA-Z0-9_]+\\)[})]" 1 font-lock-reference-face t)
- ;;
;; Do dependencies. These get the function name face.
(list makefile-dependency-regex 1 'font-lock-function-name-face)
+ ;;
+ ;; Variable references even in targets/strings/comments:
+ '("\\$[({]\\([-a-zA-Z0-9_.]+\\)[}):]" 1 font-lock-constant-face prepend)
;; Highlight lines that contain just whitespace.
;; They can cause trouble, especially if they start with a tab.
;;; of `makefile-query-by-make-minus-q' .
;;; ------------------------------------------------------------
-(defvar makefile-brave-make "make"
- "A make that can handle the `-q' option.")
+(defcustom makefile-brave-make "make"
+ "How to invoke make, for `makefile-query-targets'.
+This should identify a `make' command that can handle the `-q' option."
+ :type 'string
+ :group 'makefile)
-(defvar makefile-query-one-target-method 'makefile-query-by-make-minus-q
+(defcustom makefile-query-one-target-method 'makefile-query-by-make-minus-q
"Function to call to determine whether a make target is up to date.
The function must satisfy this calling convention:
* It must return the integer value 0 (zero) if the given target
should be considered up-to-date in the context of the given
- makefile, any nonzero integer value otherwise.")
+ makefile, any nonzero integer value otherwise."
+ :type 'function
+ :group 'makefile)
-(defvar makefile-up-to-date-buffer-name "*Makefile Up-to-date overview*"
- "Name of the Up-to-date overview buffer.")
+(defcustom makefile-up-to-date-buffer-name "*Makefile Up-to-date overview*"
+ "Name of the Up-to-date overview buffer."
+ :type 'string
+ :group 'makefile)
;;; --- end of up-to-date-overview configuration ------------------
(define-key makefile-mode-map "." 'makefile-electric-dot)))
(define-key makefile-mode-map "\C-c\C-f" 'makefile-pickup-filenames-as-targets)
(define-key makefile-mode-map "\C-c\C-b" 'makefile-switch-to-browser)
+ (define-key makefile-mode-map "\C-c\C-c" 'comment-region)
(define-key makefile-mode-map "\C-c\C-p" 'makefile-pickup-everything)
(define-key makefile-mode-map "\C-c\C-u" 'makefile-create-up-to-date-overview)
(define-key makefile-mode-map "\C-c\C-i" 'makefile-insert-gmake-function)
("notdir" "Names")
("suffix" "Names")
("basename" "Names")
+ ("addprefix" "Prefix" "Names")
("addsuffix" "Suffix" "Names")
("join" "List 1" "List 2")
("word" "Index" "Text")
makefile-pickup-everything-picks-up-filenames-p:
If this variable is set to a non-nil value then
`makefile-pickup-everything' also picks up filenames as targets
- (i.e. it calls `makefile-find-filenames-as-targets'), otherwise
+ (i.e. it calls `makefile-pickup-filenames-as-targets'), otherwise
filenames are omitted.
makefile-cleanup-continuations-p:
(make-local-variable 'makefile-need-macro-pickup)
;; Font lock.
- (if (fboundp 'makefile-define-space-face)
- (makefile-define-space-face))
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults '(makefile-font-lock-keywords))
(make-local-variable 'dabbrev-abbrev-skip-leading-regexp)
(setq dabbrev-abbrev-skip-leading-regexp "\\$")
+ ;; Filling.
+ (make-local-variable 'fill-paragraph-function)
+ (setq fill-paragraph-function 'makefile-fill-paragraph)
+
;; Comment stuff.
(make-local-variable 'comment-start)
(setq comment-start "#")
;; Backslashification. Stolen from cc-mode.el.
-(defun makefile-backslashify-current-line (doit)
- (end-of-line)
- (if doit
- (if (not (save-excursion
- (forward-char -1)
- (eq (char-after (point)) ?\\ )))
- (progn
- (if (>= (current-column) makefile-backslash-column)
- (insert " \\")
- (while (<= (current-column) makefile-backslash-column)
- (insert "\t")
- (end-of-line))
- (delete-char -1)
- (while (< (current-column) makefile-backslash-column)
- (insert " ")
- (end-of-line))
- (insert "\\"))))
- (if (not (bolp))
- (progn
- (forward-char -1)
- (if (eq (char-after (point)) ?\\ )
- (let ((saved (save-excursion
- (end-of-line)
- (point))))
- (skip-chars-backward " \t")
- (delete-region (point) saved)))))))
-
-(defun makefile-backslash-region (beg end arg)
- "Insert backslashes at end of every line in region.
-Useful for defining multi-line rules.
-If called with a prefix argument, trailing backslashes are removed."
+(defun makefile-backslash-region (from to delete-flag)
+ "Insert, align, or delete end-of-line backslashes on the lines in the region.
+With no argument, inserts backslashes and aligns existing backslashes.
+With an argument, deletes the backslashes.
+
+This function does not modify the last line of the region if the region ends
+right at the start of the following line; it does not modify blank lines
+at the start of the region. So you can put the region around an entire macro
+definition and conveniently use this command."
(interactive "r\nP")
(save-excursion
- (let ((do-lastline-p (progn (goto-char end) (not (bolp)))))
+ (goto-char from)
+ (let ((column makefile-backslash-column)
+ (endmark (make-marker)))
+ (move-marker endmark to)
+ ;; Compute the smallest column number past the ends of all the lines.
+ (if makefile-backslash-align
+ (progn
+ (if (not delete-flag)
+ (while (< (point) to)
+ (end-of-line)
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (skip-chars-backward " \t")))
+ (setq column (max column (1+ (current-column))))
+ (forward-line 1)))
+ ;; Adjust upward to a tab column, if that doesn't push
+ ;; past the margin.
+ (if (> (% column tab-width) 0)
+ (let ((adjusted (* (/ (+ column tab-width -1) tab-width)
+ tab-width)))
+ (if (< adjusted (window-width))
+ (setq column adjusted))))))
+ ;; Don't modify blank lines at start of region.
+ (goto-char from)
+ (while (and (< (point) endmark) (eolp))
+ (forward-line 1))
+ ;; Add or remove backslashes on all the lines.
+ (while (and (< (point) endmark)
+ ;; Don't backslashify the last line
+ ;; if the region ends right at the start of the next line.
+ (save-excursion
+ (forward-line 1)
+ (< (point) endmark)))
+ (if (not delete-flag)
+ (makefile-append-backslash column)
+ (makefile-delete-backslash))
+ (forward-line 1))
+ (move-marker endmark nil))))
+
+(defun makefile-append-backslash (column)
+ (end-of-line)
+ ;; Note that "\\\\" is needed to get one backslash.
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (delete-horizontal-space)
+ (indent-to column (if makefile-backslash-align nil 1)))
+ (indent-to column (if makefile-backslash-align nil 1))
+ (insert "\\")))
+
+(defun makefile-delete-backslash ()
+ (end-of-line)
+ (or (bolp)
+ (progn
+ (forward-char -1)
+ (if (looking-at "\\\\")
+ (delete-region (1+ (point))
+ (progn (skip-chars-backward " \t") (point)))))))
+
+\f
+
+;; Filling
+
+(defun makefile-fill-paragraph (arg)
+ ;; Fill comments, backslashed lines, and variable definitions
+ ;; specially.
+ (save-excursion
+ (beginning-of-line)
+ (cond
+ ((looking-at "^#+ ")
+ ;; Found a comment. Set the fill prefix and then fill.
+ (let ((fill-prefix (buffer-substring-no-properties (match-beginning 0)
+ (match-end 0)))
+ (fill-paragraph-function nil))
+ (fill-paragraph nil)
+ t))
+
+ ;; Must look for backslashed-region before looking for variable
+ ;; assignment.
+ ((save-excursion
+ (end-of-line)
+ (or
+ (= (preceding-char) ?\\)
+ (progn
+ (end-of-line -1)
+ (= (preceding-char) ?\\))))
+ ;; A backslash region. Find beginning and end, remove
+ ;; backslashes, fill, and then reapply backslahes.
+ (end-of-line)
+ (let ((beginning
+ (save-excursion
+ (end-of-line 0)
+ (while (= (preceding-char) ?\\)
+ (end-of-line 0))
+ (forward-char)
+ (point)))
+ (end
+ (save-excursion
+ (while (= (preceding-char) ?\\)
+ (end-of-line 2))
+ (point))))
+ (save-restriction
+ (narrow-to-region beginning end)
+ (makefile-backslash-region (point-min) (point-max) t)
+ (let ((fill-paragraph-function nil))
+ (fill-paragraph nil))
+ (makefile-backslash-region (point-min) (point-max) nil)
+ (goto-char (point-max))
+ (if (< (skip-chars-backward "\n") 0)
+ (delete-region (point) (point-max))))))
+
+ ((looking-at makefile-macroassign-regex)
+ ;; Have a macro assign. Fill just this line, and then backslash
+ ;; resulting region.
(save-restriction
- (narrow-to-region beg end)
- (goto-char (point-min))
- (while (not (save-excursion
- (forward-line 1)
- (eobp)))
- (makefile-backslashify-current-line (null arg))
- (forward-line 1)))
- (and do-lastline-p
- (progn (goto-char end)
- (makefile-backslashify-current-line (null arg)))))))
+ (narrow-to-region (point) (save-excursion
+ (end-of-line)
+ (forward-char)
+ (point)))
+ (let ((fill-paragraph-function nil))
+ (fill-paragraph nil))
+ (makefile-backslash-region (point-min) (point-max) nil)))))
+
+ ;; Always return non-nil so we don't fill anything else.
+ t)
\f
(let ((my-client makefile-browser-client))
(setq makefile-browser-client nil) ; we quitted, so NO client!
(set-buffer-modified-p nil)
- (kill-buffer (current-buffer))
+ (quit-window t)
(pop-to-buffer my-client)))
;;;
(beginning-of-line)
(if (makefile-browser-on-macro-line-p)
(let ((macro-name (makefile-browser-this-line-macro-name)))
- (kill-line)
+ (delete-region (point) (progn (end-of-line) (point)))
(insert
(makefile-browser-format-macro-line
macro-name
(makefile-browser-get-state-for-line this-line))))
(let ((target-name (makefile-browser-this-line-target-name)))
- (kill-line)
+ (delete-region (point) (progn (end-of-line) (point)))
(insert
(makefile-browser-format-target-line
target-name
(imenu-progress-message stupid 100)
(nreverse alist)))
-(defun makefile-define-space-face ()
- (make-face 'makefile-space-face)
- (or (not (eq window-system 'x))
- (face-differs-from-default-p 'makefile-space-face)
- (let* ((params (frame-parameters))
- (light-bg (cdr (assq 'background-mode params)))
- (bg-color (cond ((eq (cdr (assq 'display-type params)) 'mono)
- (if light-bg "black" "white"))
- ((eq (cdr (assq 'display-type params)) 'grayscale)
- (if light-bg "black" "white"))
- (light-bg ; Light color background.
- "hotpink")
- (t ; Dark color background.
- "hotpink"))))
- (set-face-background 'makefile-space-face bg-color))))
-
;;; make-mode.el ends here