;;; make-mode.el --- makefile editing commands for Emacs
-;; Copyright (C) 1992, 1994, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1994, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+;; 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Thomas Neumann <tom@smart.bo.open.de>
;; Eric S. Raymond <esr@snark.thyrsus.com>
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
(t (:reverse-video t)))
"Face to use for highlighting leading spaces in Font-Lock mode."
:group 'makefile)
-(put 'makefile-space-face 'face-alias 'makefile-space)
+(define-obsolete-face-alias 'makefile-space-face 'makefile-space "22.1")
(defface makefile-targets
;; This needs to go along both with foreground and background colors (i.e. shell)
;; Special targets for DMake, Sun's make ...
;;
(defcustom makefile-special-targets-list
- '(("DEFAULT") ("DONE") ("ERROR") ("EXPORT")
- ("FAILED") ("GROUPEPILOG") ("GROUPPROLOG") ("IGNORE")
- ("IMPORT") ("INCLUDE") ("INCLUDEDIRS") ("INIT")
- ("KEEP_STATE") ("MAKEFILES") ("MAKE_VERSION") ("NO_PARALLEL")
- ("PARALLEL") ("PHONY") ("PRECIOUS") ("REMOVE")
- ("SCCS_GET") ("SILENT") ("SOURCE") ("SUFFIXES")
- ("WAIT") ("c.o") ("C.o") ("m.o")
- ("el.elc") ("y.c") ("s.o"))
- "*List of special targets.
+ '("DEFAULT" "DONE" "ERROR" "EXPORT"
+ "FAILED" "GROUPEPILOG" "GROUPPROLOG" "IGNORE"
+ "IMPORT" "INCLUDE" "INCLUDEDIRS" "INIT"
+ "KEEP_STATE" "MAKEFILES" "MAKE_VERSION" "NO_PARALLEL"
+ "PARALLEL" "PHONY" "PRECIOUS" "REMOVE"
+ "SCCS_GET" "SILENT" "SOURCE" "SUFFIXES"
+ "WAIT" "c.o" "C.o" "m.o"
+ "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'."
:type '(repeat (list string))
"Characters to skip to find a line that might be a dependency.")
(defvar makefile-rule-action-regex
- "^\t[ \t]*\\([-@]*\\)[ \t]*\\(\\(?:.*\\\\\n\\)*.*\\)"
+ "^\t[ \t]*\\(?:\\([-@]+\\)[ \t]*\\)\\(.*\\(?:\\\\\n.*\\)*\\)"
"Regex used to highlight rule action lines in font lock mode.")
(defconst makefile-makepp-rule-action-regex
"Regex used to highlight makepp rule action lines in font lock mode.")
(defconst makefile-bsdmake-rule-action-regex
- (progn (string-match "-@" makefile-rule-action-regex)
- (replace-match "-+@" t t makefile-rule-action-regex))
+ (replace-regexp-in-string "-@" "-+@" makefile-rule-action-regex)
"Regex used to highlight BSD rule action lines in font lock mode.")
;; Note that the first and second subexpression is used by font lock. Note
(3 'font-lock-string-face prepend t))
;; Rule actions.
+ ;; FIXME: When this spans multiple lines we need font-lock-multiline.
(makefile-match-action
- (1 font-lock-type-face)
+ (1 font-lock-type-face nil t)
(2 'makefile-shell prepend)
;; Only makepp has builtin commands.
(3 font-lock-builtin-face prepend t))
cpp-font-lock-keywords))
-(defconst makefile-font-lock-syntactic-keywords
- ;; From sh-script.el.
- ;; A `#' begins a comment in sh when it is unquoted and at the beginning
- ;; of a word. In the shell, words are separated by metacharacters.
- ;; The list of special chars is taken from the single-unix spec of the
- ;; shell command language (under `quoting') but with `$' removed.
- '(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 "_")
- ;; Change the syntax of a quoted newline so that it does not end a comment.
- ("\\\\\n" 0 ".")))
+(defconst makefile-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; From sh-script.el.
+ ;; A `#' begins a comment in sh when it is unquoted and at the beginning
+ ;; of a word. In the shell, words are separated by metacharacters.
+ ;; The list of special chars is taken from the single-unix spec of the
+ ;; shell command language (under `quoting') but with `$' removed.
+ ("[^|&;<>()`\\\"' \t\n]\\(#+\\)" (1 "_"))
+ ;; Change the syntax of a quoted newline so that it does not end a comment.
+ ("\\\\\n" (0 "."))))
(defvar makefile-imenu-generic-expression
`(("Dependencies" makefile-previous-dependency 1)
("Macro Assignment" ,makefile-macroassign-regex 1))
"Imenu generic expression for Makefile mode. See `imenu-generic-expression'.")
-;;; ------------------------------------------------------------
-;;; The following configurable variables are used in the
-;;; up-to-date overview .
-;;; The standard configuration assumes that your `make' program
-;;; can be run in question/query mode using the `-q' option, this
-;;; means that the command
-;;;
-;;; make -q foo
-;;;
-;;; should return an exit status of zero if the target `foo' is
-;;; up to date and a nonzero exit status otherwise.
-;;; Many makes can do this although the docs/manpages do not mention
-;;; it. Try it with your favourite one. GNU make, System V make, and
-;;; Dennis Vadura's DMake have no problems.
-;;; Set the variable `makefile-brave-make' to the name of the
-;;; make utility that does this on your system.
-;;; To understand what this is all about see the function definition
-;;; of `makefile-query-by-make-minus-q' .
-;;; ------------------------------------------------------------
+;; ------------------------------------------------------------
+;; The following configurable variables are used in the
+;; up-to-date overview .
+;; The standard configuration assumes that your `make' program
+;; can be run in question/query mode using the `-q' option, this
+;; means that the command
+;;
+;; make -q foo
+;;
+;; should return an exit status of zero if the target `foo' is
+;; up to date and a nonzero exit status otherwise.
+;; Many makes can do this although the docs/manpages do not mention
+;; it. Try it with your favourite one. GNU make, System V make, and
+;; Dennis Vadura's DMake have no problems.
+;; Set the variable `makefile-brave-make' to the name of the
+;; make utility that does this on your system.
+;; To understand what this is all about see the function definition
+;; of `makefile-query-by-make-minus-q' .
+;; ------------------------------------------------------------
(defcustom makefile-brave-make "make"
"*How to invoke make, for `makefile-query-targets'.
;;; --- end of up-to-date-overview configuration ------------------
-(defvar makefile-mode-abbrev-table nil
+(define-abbrev-table 'makefile-mode-abbrev-table ()
"Abbrev table in use in Makefile buffers.")
-(if makefile-mode-abbrev-table
- ()
- (define-abbrev-table 'makefile-mode-abbrev-table ()))
(defvar makefile-mode-map
- (let ((map (make-sparse-keymap)))
+ (let ((map (make-sparse-keymap))
+ (opt-map (make-sparse-keymap)))
;; set up the keymap
(define-key map "\C-c:" 'makefile-insert-target-ref)
(if makefile-electric-keys
(define-key map [menu-bar makefile-mode]
(cons "Makefile" (make-sparse-keymap "Makefile")))
+ (define-key map [menu-bar makefile-mode makefile-type]
+ (cons "Switch Makefile Type" opt-map))
+ (define-key opt-map [makefile-makepp-mode]
+ '(menu-item "Makepp" makefile-makepp-mode
+ :help "An adapted `makefile-mode' that knows about makepp"
+ :button (:radio . (eq major-mode 'makefile-makepp-mode))))
+ (define-key opt-map [makefile-imake-mode]
+ '(menu-item "Imake" makefile-imake-mode
+ :help "An adapted `makefile-mode' that knows about imake"
+ :button (:radio . (eq major-mode 'makefile-imake-mode))))
+ (define-key opt-map [makefile-mode]
+ '(menu-item "Classic" makefile-mode
+ :help "`makefile-mode' with no special functionality"
+ :button (:radio . (eq major-mode 'makefile-mode))))
+ (define-key opt-map [makefile-bsdmake-mode]
+ '(menu-item "BSD" makefile-bsdmake-mode
+ :help "An adapted `makefile-mode' that knows about BSD make"
+ :button (:radio . (eq major-mode 'makefile-bsdmake-mode))))
+ (define-key opt-map [makefile-automake-mode]
+ '(menu-item "Automake" makefile-automake-mode
+ :help "An adapted `makefile-mode' that knows about automake"
+ :button (:radio . (eq major-mode 'makefile-automake-mode))))
+ (define-key opt-map [makefile-gmake-mode]
+ '(menu-item "GNU make" makefile-gmake-mode
+ :help "An adapted `makefile-mode' that knows about GNU make"
+ :button (:radio . (eq major-mode 'makefile-gmake-mode))))
(define-key map [menu-bar makefile-mode browse]
- '("Pop up Makefile Browser" . makefile-switch-to-browser))
- (define-key map [menu-bar makefile-mode complete]
- '("Complete Target or Macro" . makefile-complete))
+ '(menu-item "Pop up Makefile Browser" makefile-switch-to-browser
+ ;; XXX: this needs a better string, the function is not documented...
+ :help "Pop up Makefile Browser"))
+ (define-key map [menu-bar makefile-mode overview]
+ '(menu-item "Up To Date Overview" makefile-create-up-to-date-overview
+ :help "Create a buffer containing an overview of the state of all known targets"))
+ ;; Target related
+ (define-key map [menu-bar makefile-mode separator1] '("----"))
+ (define-key map [menu-bar makefile-mode pickup-file]
+ '(menu-item "Pick File Name as Target" makefile-pickup-filenames-as-targets
+ :help "Scan the current directory for filenames to use as targets"))
+ (define-key map [menu-bar makefile-mode function]
+ '(menu-item "Insert GNU make function" makefile-insert-gmake-function
+ :help "Insert a GNU make function call"))
(define-key map [menu-bar makefile-mode pickup]
- '("Find Targets and Macros" . makefile-pickup-everything))
-
+ '(menu-item "Find Targets and Macros" makefile-pickup-everything
+ :help "Notice names of all macros and targets in Makefile"))
+ (define-key map [menu-bar makefile-mode complete]
+ '(menu-item "Complete Target or Macro" makefile-complete
+ :help "Perform completion on Makefile construct preceding point"))
+ (define-key map [menu-bar makefile-mode backslash]
+ '(menu-item "Backslash Region" makefile-backslash-region
+ :help "Insert, align, or delete end-of-line backslashes on the lines in the region"))
+ ;; Motion
+ (define-key map [menu-bar makefile-mode separator] '("----"))
(define-key map [menu-bar makefile-mode prev]
- '("Move to Previous Dependency" . makefile-previous-dependency))
+ '(menu-item "Move to Previous Dependency" makefile-previous-dependency
+ :help "Move point to the beginning of the previous dependency line"))
(define-key map [menu-bar makefile-mode next]
- '("Move to Next Dependency" . makefile-next-dependency))
+ '(menu-item "Move to Next Dependency" makefile-next-dependency
+ :help "Move point to the beginning of the next dependency line"))
map)
"The keymap that is used in Makefile mode.")
(modify-syntax-entry ?\n "> " st)
st))
-(defvar makefile-imake-mode-syntax-table (copy-syntax-table
- makefile-mode-syntax-table))
-(if makefile-imake-mode-syntax-table
- ()
- (modify-syntax-entry ?/ ". 14" makefile-imake-mode-syntax-table)
- (modify-syntax-entry ?* ". 23" makefile-imake-mode-syntax-table)
- (modify-syntax-entry ?# "'" makefile-imake-mode-syntax-table)
- (modify-syntax-entry ?\n ". b" makefile-imake-mode-syntax-table))
-
+(defvar makefile-imake-mode-syntax-table
+ (let ((st (make-syntax-table makefile-mode-syntax-table)))
+ (modify-syntax-entry ?/ ". 14" st)
+ (modify-syntax-entry ?* ". 23" st)
+ (modify-syntax-entry ?# "'" st)
+ (modify-syntax-entry ?\n ". b" st)
+ st))
;;; ------------------------------------------------------------
;;; Internal variables.
;;; ------------------------------------------------------------
;;;###autoload
-(defun makefile-mode ()
+(define-derived-mode makefile-mode prog-mode "Makefile"
"Major mode for editing standard Makefiles.
If you are editing a file for a different make, try one of the
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."
-
- (interactive)
- (kill-all-local-variables)
(add-hook 'write-file-functions
'makefile-warn-suspicious-lines nil t)
(add-hook 'write-file-functions
(make-local-variable 'makefile-need-macro-pickup)
;; Font lock.
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults
- ;; SYNTAX-BEGIN set to backward-paragraph to avoid slow-down
- ;; near the end of a large buffer, due to parse-partial-sexp's
- ;; trying to parse all the way till the beginning of buffer.
- '(makefile-font-lock-keywords
- nil nil
- ((?$ . "."))
- backward-paragraph
- (font-lock-syntactic-keywords
- . makefile-font-lock-syntactic-keywords)))
+ (set (make-local-variable 'font-lock-defaults)
+ ;; SYNTAX-BEGIN set to backward-paragraph to avoid slow-down
+ ;; near the end of a large buffer, due to parse-partial-sexp's
+ ;; trying to parse all the way till the beginning of buffer.
+ '(makefile-font-lock-keywords
+ nil nil
+ ((?$ . "."))
+ backward-paragraph))
+ (set (make-local-variable 'syntax-propertize-function)
+ makefile-syntax-propertize-function)
;; Add-log.
- (make-local-variable 'add-log-current-defun-function)
- (setq add-log-current-defun-function 'makefile-add-log-defun)
+ (set (make-local-variable 'add-log-current-defun-function)
+ 'makefile-add-log-defun)
;; Imenu.
- (make-local-variable 'imenu-generic-expression)
- (setq imenu-generic-expression makefile-imenu-generic-expression)
+ (set (make-local-variable 'imenu-generic-expression)
+ makefile-imenu-generic-expression)
;; Dabbrev.
- (make-local-variable 'dabbrev-abbrev-skip-leading-regexp)
- (setq dabbrev-abbrev-skip-leading-regexp "\\$")
+ (set (make-local-variable 'dabbrev-abbrev-skip-leading-regexp) "\\$")
;; Other abbrevs.
(setq local-abbrev-table makefile-mode-abbrev-table)
;; Filling.
- (make-local-variable 'fill-paragraph-function)
- (setq fill-paragraph-function 'makefile-fill-paragraph)
+ (set (make-local-variable 'fill-paragraph-function) 'makefile-fill-paragraph)
;; Comment stuff.
- (make-local-variable 'comment-start)
- (setq comment-start "#")
- (make-local-variable 'comment-end)
- (setq comment-end "")
- (make-local-variable 'comment-start-skip)
- (setq comment-start-skip "#+[ \t]*")
+ (set (make-local-variable 'comment-start) "#")
+ (set (make-local-variable 'comment-end) "")
+ (set (make-local-variable 'comment-start-skip) "#+[ \t]*")
;; Make sure TAB really inserts \t.
(set (make-local-variable 'indent-line-function) 'indent-to-left-margin)
- ;; become the current major mode
- (setq major-mode 'makefile-mode)
- (setq mode-name "Makefile")
-
- ;; Activate keymap and syntax table.
- (use-local-map makefile-mode-map)
- (set-syntax-table makefile-mode-syntax-table)
-
;; Real TABs are important in makefiles
- (setq indent-tabs-mode t)
- (run-mode-hooks 'makefile-mode-hook))
+ (setq indent-tabs-mode t))
;; These should do more than just differentiate font-lock.
;;;###autoload
(define-derived-mode makefile-imake-mode makefile-mode "Imakefile"
"An adapted `makefile-mode' that knows about imake."
:syntax-table makefile-imake-mode-syntax-table
- (let ((base `(makefile-imake-font-lock-keywords ,@(cdr font-lock-defaults)))
- new)
- ;; Remove `font-lock-syntactic-keywords' entry from font-lock-defaults.
- (mapc (lambda (elt)
- (unless (and (consp elt)
- (eq (car elt) 'font-lock-syntactic-keywords))
- (setq new (cons elt new))))
- base)
- (setq font-lock-defaults (nreverse new))))
+ (set (make-local-variable 'syntax-propertize-function) nil)
+ (setq font-lock-defaults
+ `(makefile-imake-font-lock-keywords ,@(cdr font-lock-defaults))))
\f
(skip-chars-backward "^$(){}:#= \t\n")
(point)))
(try (buffer-substring beg (point)))
- (do-macros nil)
- (paren nil))
-
- (save-excursion
- (goto-char beg)
- (let ((pc (preceding-char)))
- (cond
- ;; Beginning of line means anything.
- ((bolp)
- ())
-
- ;; Preceding "$" means macros only.
- ((= pc ?$)
- (setq do-macros t))
-
- ;; Preceding "$(" or "${" means macros only.
- ((and (or (= pc ?{)
- (= pc ?\())
- (progn
- (setq paren pc)
- (backward-char)
- (and (not (bolp))
- (= (preceding-char) ?$))))
- (setq do-macros t)))))
-
- ;; Try completion.
- (let* ((table (append (if do-macros
- '()
- makefile-target-table)
- makefile-macro-table))
- (completion (try-completion try table)))
- (cond
- ;; Exact match, so insert closing paren or colon.
- ((eq completion t)
- (insert (if do-macros
- (if (eq paren ?{)
- ?}
- ?\))
- (if (save-excursion
- (goto-char beg)
- (bolp))
- ":"
- " "))))
-
- ;; No match.
- ((null completion)
- (message "Can't find completion for \"%s\"" try)
- (ding))
-
- ;; Partial completion.
- ((not (string= try completion))
- ;; FIXME it would be nice to supply the closing paren if an
- ;; exact, unambiguous match were found. That is not possible
- ;; right now. Ditto closing ":" for targets.
- (delete-region beg (point))
-
- ;; DO-MACROS means doing macros only. If not that, then check
- ;; to see if this completion is a macro. Special insertion
- ;; must be done for macros.
- (if (or do-macros
- (assoc completion makefile-macro-table))
- (let ((makefile-use-curly-braces-for-macros-p
- (or (eq paren ?{)
- makefile-use-curly-braces-for-macros-p)))
- (delete-backward-char 2)
- (makefile-do-macro-insertion completion)
- (delete-backward-char 1))
-
- ;; Just insert targets.
- (insert completion)))
-
- ;; Can't complete any more, so make completion list. FIXME
- ;; this doesn't do the right thing when the completion is
- ;; actually inserted. I don't think there is an easy way to do
- ;; that.
- (t
- (message "Making completion list...")
- (let ((list (all-completions try table)))
- (with-output-to-temp-buffer "*Completions*"
- (display-completion-list list try)))
- (message "Making completion list...done"))))))
+ (paren nil)
+ (do-macros
+ (save-excursion
+ (goto-char beg)
+ (let ((pc (preceding-char)))
+ (cond
+ ;; Preceding "$" means macros only.
+ ((= pc ?$)
+ t)
+
+ ;; Preceding "$(" or "${" means macros only.
+ ((and (memq pc '(?\{ ?\())
+ (progn
+ (setq paren (if (eq paren ?\{) ?\} ?\)))
+ (backward-char)
+ (= (preceding-char) ?$)))
+ t)))))
+
+ (table (apply-partially 'completion-table-with-terminator
+ (cond
+ (do-macros (or paren ""))
+ ((save-excursion (goto-char beg) (bolp)) ":")
+ (t " "))
+ (append (if do-macros
+ '()
+ makefile-target-table)
+ makefile-macro-table))))
+ (completion-in-region beg (point) table)))
\f
(save-excursion
(beginning-of-line)
(cond
- ((looking-at "^#+\\s-*")
+ ((looking-at "^[ \t]*#+\\s-*")
;; Found a comment. Return nil to let normal filling take place.
nil)
(save-restriction
(narrow-to-region beginning end)
(makefile-backslash-region (point-min) (point-max) t)
- (let ((fill-paragraph-function nil))
+ (let ((fill-paragraph-function nil)
+ ;; Adjust fill-column to allow space for the backslash.
+ (fill-column (- fill-column 1)))
(fill-paragraph nil))
(makefile-backslash-region (point-min) (point-max) nil)
(goto-char (point-max))
;; resulting region.
(save-restriction
(narrow-to-region (point) (line-beginning-position 2))
- (let ((fill-paragraph-function nil))
+ (let ((fill-paragraph-function nil)
+ ;; Adjust fill-column to allow space for the backslash.
+ (fill-column (- fill-column 1)))
(fill-paragraph nil))
(makefile-backslash-region (point-min) (point-max) nil))
;; Return non-nil to indicate it's been filled.
(let ((this-line (count-lines (point-min) (point))))
(setq this-line (max 1 this-line))
(makefile-browser-toggle-state-for-line this-line)
- (goto-line this-line)
+ (goto-char (point-min))
+ (forward-line (1- this-line))
(let ((inhibit-read-only t))
- (beginning-of-line)
+ (beginning-of-line) ; redundant?
(if (makefile-browser-on-macro-line-p)
(let ((macro-name (makefile-browser-this-line-macro-name)))
(delete-region (point) (progn (end-of-line) (point)))
Insertion takes place at point."
(interactive)
(save-excursion
- (goto-line 1)
+ (goto-char (point-min))
(let ((current-line 1))
(while (not (eobp))
(if (makefile-browser-get-state-for-line current-line)
;;; ------------------------------------------------------------
(defun makefile-cleanup-continuations ()
- (if (eq major-mode 'makefile-mode)
+ (if (derived-mode-p 'makefile-mode)
(if (and makefile-cleanup-continuations
(not buffer-read-only))
(save-excursion
(defun makefile-warn-suspicious-lines ()
;; Returning non-nil cancels the save operation
- (if (eq major-mode 'makefile-mode)
+ (if (derived-mode-p 'makefile-mode)
(save-excursion
(goto-char (point-min))
(if (re-search-forward "^\\(\t+$\\| +\t\\)" nil t)
(count-lines (point-min) (point)))))))))
(defun makefile-warn-continuations ()
- (if (eq major-mode 'makefile-mode)
+ (if (derived-mode-p 'makefile-mode)
(save-excursion
(goto-char (point-min))
(if (re-search-forward "\\\\[ \t]+$" nil t)
"To be called as an anchored matcher by font-lock.
The anchor must have matched the opening parens in the first group."
(let ((s (match-string-no-properties 1)))
- (setq s (cond ((string= s "(") "\\(.*?\\)[ \t]*)")
- ((string= s "{") "\\(.*?\\)[ \t]*}")
- ((string= s "((") "\\(.*?\\)[ \t]*))")
- ((string= s "{{") "\\(.*?\\)[ \t]*}}")))
- (if s (looking-at s))))
+ ;; FIXME forward-sexp or somesuch would be better?
+ (if (setq s (cond ((string= s "(") ")")
+ ((string= s "{") "}")
+ ((string= s "((") "))")
+ ((string= s "{{") "}}")))
+ (re-search-forward (concat "\\(.*\\)[ \t]*" s) (line-end-position) t))))
(defun makefile-match-dependency (bound)
"Search for `makefile-dependency-regex' up to BOUND.