Trailing whitepace deleted.
[bpt/emacs.git] / lisp / progmodes / make-mode.el
index 948b490..4d825f8 100644 (file)
@@ -1,9 +1,10 @@
-;;; makefile.el --- makefile editing commands for Emacs
+;;; make-mode.el --- makefile editing commands for Emacs
 
-;; Copyright (C) 1992, 1994 Free Software Foundation, Inc.
+;; Copyright (C) 1992,94,99,2000,2001, 2002  Free Software Foundation, Inc.
 
 ;; Author: Thomas Neumann <tom@smart.bo.open.de>
 ;;     Eric S. Raymond <esr@snark.thyrsus.com>
+;; Maintainer: FSF
 ;; Adapted-By: ESR
 ;; Keywords: unix, tools
 
 ;; Also, the doc strings need fixing: the first line doesn't stand alone,
 ;; and other usage is not high quality.  Symbol names don't have `...'.
 
-;; So, for the meantime, this is not the default mode for makefiles.
-
-;; $Id: makefile.el,v 1.19 1994/10/30 21:20:42 rms Exp rms $
-
 ;; This file is part of GNU Emacs.
 
 ;; 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 1, or (at your option)
+;; the Free Software Foundation; either version 2, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -29,8 +26,9 @@
 ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
@@ -65,8 +63,6 @@
 ;;
 ;; 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)
-
 ;; Sadly we need this for a macro.
 (eval-when-compile
-  (require 'imenu))
+  (require 'imenu)
+  (require 'dabbrev)
+  (require 'add-log))
 
 ;;; ------------------------------------------------------------
 ;;; Configurable stuff
 ;;; ------------------------------------------------------------
 
-(defvar makefile-browser-buffer-name "*Macros and Targets*"
-  "Name of the macro- and target browser buffer.")
-
-(defvar makefile-target-colon ":"
-  "String to append to all target names inserted by `makefile-insert-target'.
-\":\" or \"::\" are common values.")
-
-(defvar makefile-macro-assign " = "
-  "String to append to all macro names inserted by `makefile-insert-macro'.
+(defgroup makefile nil
+  "Makefile editing commands for Emacs."
+  :group 'tools
+  :prefix "makefile-")
+
+(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 'makefile)
+
+(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."
+  :type 'string
+  :group 'makefile)
+
+(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
+standard make expects.  However, newer makes such as dmake
 allow a larger variety of different macro assignments, so you
-might prefer to use \" += \" or \" := \" .")
-
-(defvar makefile-electric-keys nil
-  "If non-nil, install electric keybindings.
-Default is nil.")
-
-(defvar 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)'.")
-
-(defvar makefile-tab-after-target-colon t
-  "If non-nil, insert a TAB after a target colon.
+might prefer to use \" += \" or \" := \" ."
+  :type 'string
+  :group 'makefile)
+
+(defcustom makefile-electric-keys nil
+  "*If non-nil, Makefile mode should install electric keybindings.
+Default is nil."
+  :type 'boolean
+  :group 'makefile)
+
+(defcustom makefile-use-curly-braces-for-macros-p nil
+  "*Controls the style of generated macro references.
+Non-nil means macro references should use curly braces, like `${this}'.
+nil means use parentheses, like `$(this)'."
+  :type 'boolean
+  :group 'makefile)
+
+(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
-  "If non-nil, `makefile-pickup-everything' picks up filenames as targets.
-\(i.e. it calls `makefile-find-filenames-as-targets').
-Otherwise filenames are omitted.")
-
-(defvar makefile-cleanup-continuations-p t
-  "If non-nil, automatically clean up continuation lines when saving.
+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.
+This means it calls `makefile-pickup-filenames-as-targets'.
+Otherwise filenames are omitted."
+  :type 'boolean
+  :group 'makefile)
+
+(defcustom makefile-cleanup-continuations nil
+  "*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\'.")
+IMPORTANT: Please note that enabling this option causes Makefile mode
+to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\"."
+  :type 'boolean
+  :group 'makefile)
+
+(defcustom makefile-mode-hook nil
+  "*Normal hook run by `makefile-mode'."
+  :type 'hook
+  :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")
@@ -176,62 +220,89 @@ to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.")
     ("SCCS_GET")     ("SILENT")      ("SOURCE")       ("SUFFIXES")
     ("WAIT")         ("c.o")         ("C.o")          ("m.o")
     ("el.elc")       ("y.c")         ("s.o"))
-  "List of special targets.
+  "*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.")
-
-(defvar 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 ( ).")
+you enter a \".\" at the beginning of a line in `makefile-mode'."
+  :type '(repeat (list string))
+  :group 'makefile)
+
+(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 ( )."
+  :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.
+;; that if you change this regexp you might have to fix the imenu
+;; index in makefile-imenu-generic-expression.
 (defconst makefile-dependency-regex
-  "^\\([^ \n\t#:]+\\([ \t]+[^ \t\n#:]+\\)*\\)[ \t]*:\\([ \t]*$\\|\\([^=\n].*$\\)\\)" 
+  "^ *\\([^ \n\t#:=]+\\([ \t]+\\([^ \t\n#:=]+\\|\\$[({][^ \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.
+;; Note that the first subexpression is used by font lock.  Note
+;; that if you change this regexp you might have to fix the imenu
+;; index in makefile-imenu-generic-expression.
 (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
   "\\(^\\..*\\)\\|\\(.*~$\\)\\|\\(.*,v$\\)\\|\\(\\.[chy]\\)"
   "Regex for filenames that will NOT be included in the target list.")
 
-;; TABs are important in Makefiles.  So highlight them in font-lock.
-;; Thanks to Job Ganzevoort <Job.Ganzevoort@cwi.nl> for this.
-(defvar makefile-tab-face
-  (if (fboundp 'make-face)
-      (progn
-       (make-face 'makefile-tab-face)
-       (or (face-differs-from-default-p 'makefile-tab-face)
-           (set-face-background 'makefile-tab-face "hotpink"))
-       'makefile-tab-face)
-    '())
-  "Face to use for highlighting leading tabs in font-lock-mode.")
+(if (fboundp 'facemenu-unlisted-faces)
+    (add-to-list 'facemenu-unlisted-faces 'makefile-space-face))
+(defvar makefile-space-face 'makefile-space-face
+  "Face to use for highlighting leading spaces in Font-Lock mode.")
 
 (defconst makefile-font-lock-keywords
   (list
+
    ;; Do macro assignments.  These get the "variable-name" face rather
    ;; 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)
 
-   ;; Highlight leading tab.  Maybe highlighting all leading TABs
-   ;; would be nice?  I don't know.
-   '("^\t" . makefile-tab-face)))
+   ;; Variable references even in targets/strings/comments:
+   '("\\$[({]\\([-a-zA-Z0-9_.]+\\)[}):]" 1 font-lock-constant-face prepend)
+
+   ;; Automatic variable references.
+   '("\\$\\([@%<?^+*]\\)" 1 font-lock-reference-face prepend)
+   '("\\$[({]\\([@%<?^+*][FD]?\\)[}):]" 1 font-lock-reference-face prepend)
+
+   ;; Fontify conditionals and includes.
+   ;; Note that plain `if' is an automake conditional, and not a bug.
+   (list
+    (concat "^\\(?: [ \t]*\\)?"
+           (regexp-opt '("-include" "-sinclude" "include" "sinclude" "ifeq"
+                         "if" "ifneq" "ifdef" "ifndef" "endif" "else"
+                         "define" "endef" "override"
+                         "export" "unexport" "vpath") t)
+           "\\>[ \t]*\\([^: \t\n#]*\\)")
+    '(1 font-lock-keyword-face) '(2 font-lock-variable-name-face))
+
+   ;; Highlight lines that contain just whitespace.
+   ;; They can cause trouble, especially if they start with a tab.
+   '("^[ \t]+$" . makefile-space-face)
+
+   ;; Highlight shell comments that Make treats as commands,
+   ;; since these can fool people.
+   '("^\t+#" 0 makefile-space-face t)
+
+   ;; Highlight spaces that precede tabs.
+   ;; They can make a tab fail to be effective.
+   '("^\\( +\\)\t" 1 makefile-space-face)))
+
+(defvar makefile-imenu-generic-expression
+  (list
+   (list "Dependencies" makefile-dependency-regex  1)
+   (list "Macro Assignment" makefile-macroassign-regex 1))
+  "Imenu generic expression for Makefile mode.  See `imenu-generic-expression'.")
 
 ;;; ------------------------------------------------------------
 ;;; The following configurable variables are used in the
@@ -253,32 +324,45 @@ not be enclosed in { } or ( ).")
 ;;; 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
-  "A function symbol [one that can be used as the first argument to
-funcall] that provides a function that must conform to the following
-interface:
+(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:
 
 * As its first argument, it must accept the name of the target to
   be checked, as a string.
 
 * As its second argument, it may accept the name of a makefile
-  as a string. Depending on what you're going to do you may
+  as a string.  Depending on what you're going to do you may
   not need this.
 
 * 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 ------------------
 
+(defvar makefile-mode-abbrev-table nil
+  "Abbrev table in use in Makefile buffers.")
+(if makefile-mode-abbrev-table
+    ()
+  (define-abbrev-table 'makefile-mode-abbrev-table ()))
+
 (defvar makefile-mode-map nil
-  "The keymap that is used in makefile-mode.")
+  "The keymap that is used in Makefile mode.")
+
 (if makefile-mode-map
     ()
   (setq makefile-mode-map (make-sparse-keymap))
@@ -292,6 +376,7 @@ interface:
        (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)
@@ -305,16 +390,16 @@ interface:
     (cons "Makefile" (make-sparse-keymap "Makefile")))
 
   (define-key makefile-mode-map [menu-bar makefile-mode browse]
-    '("Pop up Makefile browser" . makefile-switch-to-browser))
+    '("Pop up Makefile Browser" . makefile-switch-to-browser))
   (define-key makefile-mode-map [menu-bar makefile-mode complete]
-    '("Complete target or macro" . makefile-complete))
+    '("Complete Target or Macro" . makefile-complete))
   (define-key makefile-mode-map [menu-bar makefile-mode pickup]
-    '("Find targets and macros" . makefile-pickup-everything))
+    '("Find Targets and Macros" . makefile-pickup-everything))
 
   (define-key makefile-mode-map [menu-bar makefile-mode prev]
-    '("Move to previous dependency" . makefile-previous-dependency))
+    '("Move to Previous Dependency" . makefile-previous-dependency))
   (define-key makefile-mode-map [menu-bar makefile-mode next]
-    '("Move to next dependency" . makefile-next-dependency)))
+    '("Move to Next Dependency" . makefile-next-dependency)))
 
 (defvar makefile-browser-map nil
   "The keymap that is used in the macro- and target browser.")
@@ -322,17 +407,17 @@ interface:
     ()
   (setq makefile-browser-map (make-sparse-keymap))
   (define-key makefile-browser-map "n"    'makefile-browser-next-line)
-  (define-key makefile-browser-map "\C-n" 'makefile-browser-next-line)    
+  (define-key makefile-browser-map "\C-n" 'makefile-browser-next-line)
   (define-key makefile-browser-map "p"    'makefile-browser-previous-line)
   (define-key makefile-browser-map "\C-p" 'makefile-browser-previous-line)
   (define-key makefile-browser-map " "    'makefile-browser-toggle)
   (define-key makefile-browser-map "i"    'makefile-browser-insert-selection)
-  (define-key makefile-browser-map "I"    'makefile-browser-insert-selection-and-quit)  
+  (define-key makefile-browser-map "I"    'makefile-browser-insert-selection-and-quit)
   (define-key makefile-browser-map "\C-c\C-m" 'makefile-browser-insert-continuation)
   (define-key makefile-browser-map "q"    'makefile-browser-quit)
   ;; disable horizontal movement
   (define-key makefile-browser-map "\C-b" 'undefined)
-  (define-key makefile-browser-map "\C-f" 'undefined))  
+  (define-key makefile-browser-map "\C-f" 'undefined))
 
 
 (defvar makefile-mode-syntax-table nil)
@@ -342,8 +427,8 @@ interface:
   (modify-syntax-entry ?\( "()    " makefile-mode-syntax-table)
   (modify-syntax-entry ?\) ")(    " makefile-mode-syntax-table)
   (modify-syntax-entry ?\[ "(]    " makefile-mode-syntax-table)
-  (modify-syntax-entry ?\] "([    " makefile-mode-syntax-table)
-  (modify-syntax-entry ?\{ "(}    " makefile-mode-syntax-table)  
+  (modify-syntax-entry ?\] ")[    " makefile-mode-syntax-table)
+  (modify-syntax-entry ?\{ "(}    " makefile-mode-syntax-table)
   (modify-syntax-entry ?\} "){    " makefile-mode-syntax-table)
   (modify-syntax-entry ?\' "\"     " makefile-mode-syntax-table)
   (modify-syntax-entry ?\` "\"     " makefile-mode-syntax-table)
@@ -363,7 +448,7 @@ interface:
   "Table of all macro names known for this buffer.")
 
 (defvar makefile-browser-client
-  "A buffer in makefile-mode that is currently using the browser.")
+  "A buffer in Makefile mode that is currently using the browser.")
 
 (defvar makefile-browser-selection-vector nil)
 (defvar makefile-has-prereqs nil)
@@ -389,6 +474,7 @@ interface:
     ("notdir" "Names")
     ("suffix" "Names")
     ("basename" "Names")
+    ("addprefix" "Prefix" "Names")
     ("addsuffix" "Suffix" "Names")
     ("join" "List 1" "List 2")
     ("word" "Index" "Text")
@@ -408,8 +494,7 @@ interface:
 ;;;###autoload
 (defun makefile-mode ()
   "Major mode for editing Makefiles.
-Calling this function invokes the function(s) \"makefile-mode-hook\" before
-doing anything else.
+This function ends by invoking the function(s) `makefile-mode-hook'.
 
 \\{makefile-mode-map}
 
@@ -417,76 +502,78 @@ In the browser, use the following keys:
 
 \\{makefile-browser-map}
 
-makefile-mode can be configured by modifying the following
-variables:
+Makefile mode can be configured by modifying the following variables:
 
-makefile-browser-buffer-name:
+`makefile-browser-buffer-name':
     Name of the macro- and target browser buffer.
 
-makefile-target-colon:
+`makefile-target-colon':
     The string that gets appended to all target names
-    inserted by makefile-insert-target.
+    inserted by `makefile-insert-target'.
     \":\" or \"::\" are quite common values.
 
-makefile-macro-assign:
+`makefile-macro-assign':
    The string that gets appended to all macro names
-   inserted by makefile-insert-macro.
+   inserted by `makefile-insert-macro'.
    The normal value should be \" = \", since this is what
-   standard make expects. However, newer makes such as dmake
+   standard make expects.  However, newer makes such as dmake
    allow a larger variety of different macro assignments, so you
    might prefer to use \" += \" or \" := \" .
 
-makefile-tab-after-target-colon:
+`makefile-tab-after-target-colon':
    If you want a TAB (instead of a space) to be appended after the
    target colon, then set this to a non-nil value.
 
-makefile-browser-leftmost-column:
+`makefile-browser-leftmost-column':
    Number of blanks to the left of the browser selection mark.
 
-makefile-browser-cursor-column:
+`makefile-browser-cursor-column':
    Column in which the cursor is positioned when it moves
    up or down in the browser.
 
-makefile-browser-selected-mark:
+`makefile-browser-selected-mark':
    String used to mark selected entries in the browser.
 
-makefile-browser-unselected-mark:
+`makefile-browser-unselected-mark':
    String used to mark unselected entries in the browser.
 
-makefile-browser-auto-advance-after-selection-p:
+`makefile-browser-auto-advance-after-selection-p':
    If this variable is set to a non-nil value the cursor
    will automagically advance to the next line after an item
    has been selected in the browser.
 
-makefile-pickup-everything-picks-up-filenames-p:
+`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
+   `makefile-pickup-everything' also picks up filenames as targets
+   (i.e. it calls `makefile-pickup-filenames-as-targets'), otherwise
    filenames are omitted.
 
-makefile-cleanup-continuations-p:
-   If this variable is set to a non-nil value then makefile-mode
+`makefile-cleanup-continuations':
+   If this variable is set to a non-nil value then Makefile mode
    will assure that no line in the file ends with a backslash
    (the continuation character) followed by any whitespace.
    This is done by silently removing the trailing whitespace, leaving
    the backslash itself intact.
-   IMPORTANT: Please note that enabling this option causes makefile-mode
-   to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.
+   IMPORTANT: Please note that enabling this option causes Makefile mode
+   to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\".
 
-makefile-browser-hook:
+`makefile-browser-hook':
    A function or list of functions to be called just before the
    browser is entered. This is executed in the makefile buffer.
 
-makefile-special-targets-list:
+`makefile-special-targets-list':
    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."
+   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)
-  (make-local-variable 'local-write-file-hooks)
-  (setq local-write-file-hooks
-       '(makefile-cleanup-continuations makefile-warn-suspicious-lines))
+  (add-hook 'write-file-functions
+           'makefile-warn-suspicious-lines nil t)
+  (add-hook 'write-file-functions
+           'makefile-warn-continuations nil t)
+  (add-hook 'write-file-functions
+           'makefile-cleanup-continuations nil t)
   (make-local-variable 'makefile-target-table)
   (make-local-variable 'makefile-macro-table)
   (make-local-variable 'makefile-has-prereqs)
@@ -495,20 +582,31 @@ makefile-special-targets-list:
 
   ;; Font lock.
   (make-local-variable 'font-lock-defaults)
-  (setq font-lock-defaults '(makefile-font-lock-keywords))
+  (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 nil backward-paragraph))
 
   ;; Add-log.
   (make-local-variable 'add-log-current-defun-function)
   (setq add-log-current-defun-function 'makefile-add-log-defun)
 
   ;; Imenu.
-  (make-local-variable 'imenu-create-index-function)
-  (setq imenu-create-index-function 'makefile-menu-index-function)
+  (make-local-variable 'imenu-generic-expression)
+  (setq imenu-generic-expression makefile-imenu-generic-expression)
 
   ;; Dabbrev.
   (make-local-variable 'dabbrev-abbrev-skip-leading-regexp)
   (setq 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)
+
   ;; Comment stuff.
   (make-local-variable 'comment-start)
   (setq comment-start "#")
@@ -517,6 +615,9 @@ makefile-special-targets-list:
   (make-local-variable 'comment-start-skip)
   (setq 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")
@@ -534,7 +635,7 @@ makefile-special-targets-list:
 ;;; Motion code.
 
 (defun makefile-next-dependency ()
-  "Move (point) to the beginning of the next dependency line below (point)."
+  "Move point to the beginning of the next dependency line."
   (interactive)
   (let ((here (point)))
     (end-of-line)
@@ -543,7 +644,7 @@ makefile-special-targets-list:
       (goto-char here) nil)))
 
 (defun makefile-previous-dependency ()
-  "Move (point) to the beginning of the next dependency line above (point)."
+  "Move point to the beginning of the previous dependency line."
   (interactive)
   (let ((here (point)))
     (beginning-of-line)
@@ -565,7 +666,7 @@ Anywhere else just self-inserts."
     (self-insert-command arg)))
 
 (defun makefile-insert-special-target ()
-  "Propmt for and insert a special target name.
+  "Prompt for and insert a special target name.
 Uses `makefile-special-targets' list."
   (interactive)
   (makefile-pickup-targets)
@@ -599,7 +700,7 @@ Anywhere else just self-inserts."
        (makefile-remember-macro macro-name))))
 
 (defun makefile-insert-macro-ref (macro-name)
-  "Complete on a list of known macros, then insert complete ref at (point)."
+  "Complete on a list of known macros, then insert complete ref at point."
   (interactive
    (list
     (progn
@@ -620,7 +721,7 @@ Anywhere else just self-inserts."
        (makefile-remember-target target-name))))
 
 (defun makefile-insert-target-ref (target-name)
-  "Complete on a list of known targets, then insert target-ref at (point) ."
+  "Complete on a list of known targets, then insert TARGET-NAME at point."
   (interactive
    (list
     (progn
@@ -654,7 +755,7 @@ Anywhere else just self-inserts."
     (setq makefile-has-prereqs nil)
     (save-excursion
       (goto-char (point-min))
-      (while (re-search-forward makefile-dependency-regex (point-max) t)
+      (while (re-search-forward makefile-dependency-regex nil t)
        (makefile-add-this-line-targets)))
     (message "Read targets OK.")))
 
@@ -688,7 +789,7 @@ Anywhere else just self-inserts."
     (setq makefile-macro-table nil)
     (save-excursion
       (goto-char (point-min))
-      (while (re-search-forward makefile-macroassign-regex (point-max) t)
+      (while (re-search-forward makefile-macroassign-regex nil t)
        (makefile-add-this-line-macro)
        (forward-line 1)))
     (message "Read macros OK.")))
@@ -697,15 +798,15 @@ Anywhere else just self-inserts."
   (save-excursion
     (beginning-of-line)
     (skip-chars-forward " \t")
-    (if (not (eolp))
-       (let* ((start-of-macro-name (point))
-              (line-number (1+ (count-lines (point-min) (point))))
-              (macro-name (progn
-                            (skip-chars-forward "^ \t:#=*")
-                            (buffer-substring start-of-macro-name (point)))))
-         (if (makefile-remember-macro macro-name)
-             (message "Picked up macro \"%s\" from line %d"
-                      macro-name line-number))))))
+    (unless (eolp)
+      (let* ((start-of-macro-name (point))
+            (line-number (1+ (count-lines (point-min) (point))))
+            (macro-name (progn
+                          (skip-chars-forward "^ \t:#=*")
+                          (buffer-substring start-of-macro-name (point)))))
+       (if (makefile-remember-macro macro-name)
+           (message "Picked up macro \"%s\" from line %d"
+                    macro-name line-number))))))
 
 (defun makefile-pickup-everything (arg)
   "Notice names of all macros and targets in Makefile.
@@ -729,7 +830,7 @@ and adds all qualifying names to the list of known targets."
         (raw-filename-list (if dir
                                (file-name-all-completions "" dir)
                              (file-name-all-completions "" ""))))
-    (mapcar '(lambda (name)
+    (mapcar (lambda (name)
               (if (and (not (file-directory-p name))
                        (not (string-match makefile-ignored-files-in-pickup-regex
                                           name)))
@@ -836,51 +937,150 @@ The context determines which are considered."
 
 ;; 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 backslahes 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 find the paragraph
+      ;; boundaries by searching for lines that look like comment-only
+      ;; lines.
+      (let ((fill-prefix (match-string-no-properties 0))
+           (fill-paragraph-function nil))
+       (save-excursion
+         (save-restriction
+           (narrow-to-region
+            ;; Search backwards.
+            (save-excursion
+              (while (and (zerop (forward-line -1))
+                          (looking-at "^#")))
+              ;; We may have gone too far.  Go forward again.
+              (or (looking-at "^#")
+                  (forward-line 1))
+              (point))
+            ;; Search forwards.
+            (save-excursion
+              (while (looking-at "^#")
+                (forward-line))
+              (point)))
+           (fill-paragraph nil)
+           t))))
+
+     ;; Must look for backslashed-region before looking for variable
+     ;; assignment.
+     ((or (eq (char-before (line-end-position 1)) ?\\)
+         (eq (char-before (line-end-position 0)) ?\\))
+      ;; 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) (line-beginning-position 2))
+       (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
 
@@ -955,7 +1155,7 @@ If called with a prefix argument, trailing backslahes are removed."
   (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)))
 
 ;;;
@@ -973,13 +1173,13 @@ If called with a prefix argument, trailing backslahes are removed."
       (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
@@ -1001,14 +1201,13 @@ This is most useful in the process of creating continued lines when copying
 large dependencies from the browser to the client buffer.
 \(point) advances accordingly in the client buffer."
   (interactive)
-  (save-excursion
-    (set-buffer makefile-browser-client)
+  (with-current-buffer makefile-browser-client
     (end-of-line)
     (insert "\\\n\t")))
 
 (defun makefile-browser-insert-selection ()
   "Insert all selected targets and/or macros in the makefile buffer.
-Insertion takes place at (point)."
+Insertion takes place at point."
   (interactive)
   (save-excursion
     (goto-line 1)
@@ -1047,11 +1246,10 @@ Insertion takes place at (point)."
        (message "No macros or targets to browse! Consider running 'makefile-pickup-everything\'"))
     (let ((browser-buffer (get-buffer-create makefile-browser-buffer-name)))
        (pop-to-buffer browser-buffer)
-       (make-variable-buffer-local 'makefile-browser-selection-vector)
        (makefile-browser-fill targets macros)
        (shrink-window-if-larger-than-buffer)
-       (setq makefile-browser-selection-vector
-             (make-vector (+ (length targets) (length macros)) nil))
+       (set (make-local-variable 'makefile-browser-selection-vector)
+            (make-vector (+ (length targets) (length macros)) nil))
        (makefile-browser-start-interaction))))
 
 (defun makefile-switch-to-browser ()
@@ -1136,7 +1334,7 @@ with the generated name!"
            my-uid))))
 
 (defun makefile-query-targets (filename target-table prereq-list)
-  "Fill the up-to-date-overview-buffer.
+  "Fill the up-to-date overview buffer.
 Checks each target in TARGET-TABLE using `makefile-query-one-target-method'
 and generates the overview, one line per target name."
   (insert
@@ -1144,7 +1342,7 @@ and generates the overview, one line per target name."
     (function (lambda (item)
                (let* ((target-name (car item))
                       (no-prereqs (not (member target-name prereq-list)))
-                      (needs-rebuild (or no-prereqs 
+                      (needs-rebuild (or no-prereqs
                                          (funcall
                                           makefile-query-one-target-method
                                           target-name
@@ -1172,11 +1370,11 @@ and generates the overview, one line per target name."
 
 (defun makefile-cleanup-continuations ()
   (if (eq major-mode 'makefile-mode)
-      (if (and makefile-cleanup-continuations-p
+      (if (and makefile-cleanup-continuations
               (not buffer-read-only))
          (save-excursion
            (goto-char (point-min))
-           (while (re-search-forward "\\\\[ \t]+$" (point-max) t)
+           (while (re-search-forward "\\\\[ \t]+$" nil t)
              (replace-match "\\" t t))))))
 
 
@@ -1185,21 +1383,23 @@ and generates the overview, one line per target name."
 ;;; ------------------------------------------------------------
 
 (defun makefile-warn-suspicious-lines ()
-  (let ((dont-save nil))
-    (if (eq major-mode 'makefile-mode)
-       (let ((suspicious
-              (save-excursion
-                (goto-char (point-min))
-                (re-search-forward
-                 "\\(^[\t]+$\\)\\|\\(^[ ]+[\t]\\)" (point-max) t))))
-         (if suspicious
-             (let ((line-nr (count-lines (point-min) suspicious)))
-               (setq dont-save
-                     (not (y-or-n-p
-                           (format "Suspicious line %d. Save anyway "
-                                   line-nr))))))))
-    dont-save))
-         
+  ;; Returning non-nil cancels the save operation
+  (if (eq major-mode 'makefile-mode)
+      (save-excursion
+       (goto-char (point-min))
+       (if (re-search-forward "^\\(\t+$\\| +\t\\)" nil t)
+           (not (y-or-n-p
+                 (format "Suspicious line %d. Save anyway? "
+                         (count-lines (point-min) (point)))))))))
+
+(defun makefile-warn-continuations ()
+  (if (eq major-mode 'makefile-mode)
+      (save-excursion
+       (goto-char (point-min))
+       (if (re-search-forward "\\\\[ \t]+$" nil t)
+           (not (y-or-n-p
+                 (format "Suspicious continuation in line %d. Save anyway? "
+                         (count-lines (point-min) (point)))))))))
 \f
 
 ;;; ------------------------------------------------------------
@@ -1274,20 +1474,20 @@ This acts according to the value of `makefile-tab-after-target-colon'."
   "Determine if point is on a macro line in the browser."
   (save-excursion
     (beginning-of-line)
-    (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)))
+    (re-search-forward "\\$[{(]" (line-end-position) t)))
 
 (defun makefile-browser-this-line-target-name ()
   "Extract the target name from a line in the browser."
   (save-excursion
     (end-of-line)
     (skip-chars-backward "^ \t")
-    (buffer-substring (point) (1- (makefile-end-of-line-point)))))
+    (buffer-substring (point) (1- (line-end-position)))))
 
 (defun makefile-browser-this-line-macro-name ()
   "Extract the macro name from a line in the browser."
   (save-excursion
     (beginning-of-line)
-    (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)
+    (re-search-forward "\\$[{(]" (line-end-position) t)
     (let ((macro-start (point)))
       (skip-chars-forward "^})")
       (buffer-substring macro-start (point)))))
@@ -1311,60 +1511,34 @@ Uses `makefile-use-curly-braces-for-macros-p'."
 (defun makefile-browser-toggle-state-for-line (n)
   (makefile-browser-set-state-for-line n (not (makefile-browser-get-state-for-line n))))
 
-(defun makefile-beginning-of-line-point ()
-  (save-excursion
-    (beginning-of-line)
-    (point)))
-
-(defun makefile-end-of-line-point ()
-  (save-excursion
-    (end-of-line)
-    (point)))
-
 (defun makefile-last-line-p ()
-  (= (makefile-end-of-line-point) (point-max)))
+  (= (line-end-position) (point-max)))
 
 (defun makefile-first-line-p ()
-  (= (makefile-beginning-of-line-point) (point-min)))
+  (= (line-beginning-position) (point-min)))
 
 \f
 
-;;; Support for other packages, like add-log and imenu.
+;;; Support for other packages, like add-log.
 
 (defun makefile-add-log-defun ()
-  ;; "Return name of target or macro point is in, or nil."
+  "Return name of target or variable assignment that point is in.
+If it isn't in one, return nil."
   (save-excursion
-    (beginning-of-line)
-    (cond
-     ((looking-at makefile-macroassign-regex)
-      (buffer-substring (match-beginning 1)
-                       (match-end 1)))
-     ((progn
-       (forward-char)
-       (re-search-backward makefile-dependency-regex nil t))
-      (buffer-substring (match-beginning 1)
-                       (match-end 1)))
-     (t nil))))
-
-;; FIXME it might be nice to have them separated by macro vs target.
-(defun makefile-menu-index-function ()
-  ;; "Generate alist of indices for imenu."
-  (let (alist
-       stupid
-       (re (concat makefile-dependency-regex
-                   "\\|"
-                   makefile-macroassign-regex)))
-    (imenu-progress-message stupid 0)
-    (goto-char (point-min))
-    (while (re-search-forward re nil t)
-      (imenu-progress-message stupid)
-      (let ((n (if (match-beginning 1) 1 5)))
-       (setq alist (cons
-                    (cons (buffer-substring (match-beginning n)
-                                            (match-end n))
-                          (match-beginning n))
-                    alist))))
-    (imenu-progress-message stupid 100)
-    (nreverse alist)))
-
-;;; makefile.el ends here
+    (let (found)
+      (beginning-of-line)
+      ;; Scan back line by line, noticing when we come to a
+      ;; variable or rule definition, and giving up when we see
+      ;; a line that is not part of either of those.
+      (while (not (or (setq found
+                           (when (or (looking-at makefile-macroassign-regex)
+                                     (looking-at makefile-dependency-regex))
+                             (match-string-no-properties 1)))
+                     ;; Don't keep looking across a blank line or comment.
+                     (looking-at "$\\|#")
+                     (not (zerop (forward-line -1))))))
+      found)))
+
+(provide 'make-mode)
+
+;;; make-mode.el ends here