(command-line): Set `temporary-file-directory' based
[bpt/emacs.git] / lisp / progmodes / make-mode.el
index 566ae75..54fd598 100644 (file)
@@ -4,6 +4,7 @@
 
 ;; Author: Thomas Neumann <tom@smart.bo.open.de>
 ;;     Eric S. Raymond <esr@snark.thyrsus.com>
+;; Maintainer: FSF
 ;; Adapted-By: ESR
 ;; Keywords: unix, tools
 
@@ -62,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
@@ -89,7 +88,7 @@
 
 ;;; 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")
@@ -175,27 +217,31 @@ to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.")
     ("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
@@ -213,11 +259,11 @@ not be enclosed in { } or ( ).")
    ;; 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.
@@ -251,10 +297,13 @@ 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
+(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:
 
@@ -267,10 +316,14 @@ 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 ------------------
 
@@ -290,6 +343,7 @@ The function must satisfy this calling convention:
        (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)
@@ -387,6 +441,7 @@ The function must satisfy this calling convention:
     ("notdir" "Names")
     ("suffix" "Names")
     ("basename" "Names")
+    ("addprefix" "Prefix" "Names")
     ("addsuffix" "Suffix" "Names")
     ("join" "List 1" "List 2")
     ("word" "Index" "Text")
@@ -457,7 +512,7 @@ makefile-browser-auto-advance-after-selection-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
+   (i.e. it calls `makefile-pickup-filenames-as-targets'), otherwise
    filenames are omitted.
 
 makefile-cleanup-continuations-p:
@@ -490,8 +545,6 @@ makefile-special-targets-list:
   (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))
 
@@ -507,6 +560,10 @@ makefile-special-targets-list:
   (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 "#")
@@ -834,51 +891,141 @@ 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 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
 
@@ -953,7 +1100,7 @@ If called with a prefix argument, trailing backslashes 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)))
 
 ;;;
@@ -971,13 +1118,13 @@ If called with a prefix argument, trailing backslashes 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
@@ -1376,20 +1523,4 @@ If it isn't in one, return nil."
     (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