Initial Commit
[packages] / xemacs-packages / prog-modes / make-mode.el
1 ;;; make-mode.el --- makefile editing commands for Emacs
2
3 ;; Copyright (C) 1992, 1994, 1999, 2000 Free Software Foundation, Inc.
4
5 ;; Author: Thomas Neumann <tom@smart.bo.open.de>
6 ;;      Eric S. Raymond <esr@snark.thyrsus.com>
7 ;; Maintainer: XEmacs Development Team <xemacs-beta@xemacs.org>
8 ;; Adapted-By: ESR
9 ;; Keywords: unix, tools
10
11 ;; RMS:
12 ;; This needs work.
13 ;; Also, the doc strings need fixing: the first line doesn't stand alone,
14 ;; and other usage is not high quality.  Symbol names don't have `...'.
15
16 ;; This file is part of XEmacs.
17
18 ;; XEmacs is free software; you can redistribute it and/or modify it
19 ;; under the terms of the GNU General Public License as published by
20 ;; the Free Software Foundation; either version 2, or (at your option)
21 ;; any later version.
22
23 ;; XEmacs is distributed in the hope that it will be useful, but
24 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
25 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26 ;; General Public License for more details.
27
28 ;; You should have received a copy of the GNU General Public License
29 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
30 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
31 ;; 02111-1307, USA.
32
33 ;;; Synched up with: GNU Emacs 19.34, partially with 21.2.
34
35 ;;; Commentary:
36
37 ;; A major mode for editing makefiles.  The mode knows about Makefile
38 ;; syntax and defines M-n and M-p to move to next and previous productions.
39 ;;
40 ;; The keys $, =, : and . are electric; they try to help you fill in a
41 ;; macro reference, macro definition, ordinary target name, or special
42 ;; target name, respectively.  Such names are completed using a list of
43 ;; targets and macro names parsed out of the makefile.  This list is
44 ;; automatically updated, if necessary, whenever you invoke one of
45 ;; these commands.  You can force it to be updated with C-c C-p.
46 ;;
47 ;; The command C-c C-f adds certain filenames in the current directory
48 ;; as targets.  You can filter out filenames by setting the variable
49 ;; makefile-ignored-files-in-pickup-regex.
50 ;;
51 ;; The command C-c C-u grinds for a bit, then pops up a report buffer
52 ;; showing which target names are up-to-date with respect to their
53 ;; prerequisites, which targets are out-of-date, and which have no
54 ;; prerequisites.
55 ;;
56 ;; The command C-c C-b pops up a browser window listing all target and
57 ;; macro names.  You can mark or unmark items wit C-c SPC, and insert
58 ;; all marked items back in the Makefile with C-c TAB.
59 ;;
60 ;; The command C-c TAB in the makefile buffer inserts a GNU make builtin.
61 ;; You will be prompted for the builtin's args.
62 ;;
63 ;; There are numerous other customization variables.
64
65 ;;
66 ;; To Do:
67 ;;
68 ;; * makefile-backslash-region should be given better behavior.
69 ;; * Consider binding C-c C-c to comment-region (like cc-mode).
70 ;; * Eliminate electric stuff entirely.
71 ;; * It might be nice to highlight targets differently depending on
72 ;;   whether they are up-to-date or not.  Not sure how this would
73 ;;   interact with font-lock.
74 ;; * Would be nice to edit the commands in ksh-mode and have
75 ;;   indentation and slashification done automatically.  Hard.
76 ;; * Consider removing browser mode.  It seems useless.
77 ;; * ":" should notice when a new target is made and add it to the
78 ;;   list (or at least set makefile-need-target-pickup).
79 ;; * Make browser into a major mode.
80 ;; * Clean up macro insertion stuff.  It is a mess.
81 ;; * Browser entry and exit is weird.  Normalize.
82 ;; * Browser needs to be rewritten.  Right now it is kind of a crock.
83 ;;   Should at least:
84 ;;    * Act more like dired/buffer menu/whatever.
85 ;;    * Highlight as mouse traverses.
86 ;;    * B2 inserts.
87 ;; * Update documentation above.
88 ;; * Update texinfo manual.
89 ;; * Update files.el.
90
91 \f
92
93 ;;; Code:
94
95 ;; Sadly we need this for a macro.
96 (eval-when-compile
97   (unless (featurep 'xemacs)
98     (require 'imenu))
99   (require 'dabbrev)
100   (require 'add-log))
101
102 ;; Used to generate some fancy regexps
103 (require 'regexp-opt)
104
105 ;;; ------------------------------------------------------------
106 ;;; Configurable stuff
107 ;;; ------------------------------------------------------------
108
109 (defgroup makefile nil
110   "Makefile editing commands for XEmacs."
111   :group 'tools
112   :prefix "makefile-")
113
114 (defface makefile-space-face
115    '((((class color)) (:background  "hotpink"))
116      (t (:reverse-video t)))
117   "Face to use for highlighting leading spaces in Font-Lock mode."
118   :group 'faces
119   :group 'makemode)
120
121 (defcustom makefile-browser-buffer-name "*Macros and Targets*"
122   "*Name of the macro- and target browser buffer."
123   :type 'string
124   :group 'makefile)
125
126 (defcustom makefile-target-colon ":"
127   "*String to append to all target names inserted by `makefile-insert-target'.
128 \":\" or \"::\" are common values."
129   :type 'string
130   :group 'makefile)
131
132 (defcustom makefile-macro-assign " = "
133   "*String to append to all macro names inserted by `makefile-insert-macro'.
134 The normal value should be \" = \", since this is what
135 standard make expects.  However, newer makes such as dmake
136 allow a larger variety of different macro assignments, so you
137 might prefer to use \" += \" or \" := \" ."
138   :type 'string
139   :group 'makefile)
140
141 (defcustom makefile-electric-keys nil
142   "*If non-nil, Makefile mode should install electric keybindings.
143 Default is nil."
144   :type 'boolean
145   :group 'makefile)
146
147 (defcustom makefile-use-curly-braces-for-macros-p nil
148   "*Controls the style of generated macro references.
149 Non-nil means macro references should use curly braces, like `${this}'.
150 nil means use parentheses, like `$(this)'."
151   :type 'boolean
152   :group 'makefile)
153
154 (defcustom makefile-tab-after-target-colon t
155   "*If non-nil, insert a TAB after a target colon.
156 Otherwise, a space is inserted.
157 The default is t."
158   :type 'boolean
159   :group 'makefile)
160
161 (defcustom makefile-browser-leftmost-column 10
162   "*Number of blanks to the left of the browser selection mark."
163   :type 'integer
164   :group 'makefile)
165
166 (defcustom makefile-browser-cursor-column 10
167   "*Column the cursor goes to when it moves up or down in the Makefile browser."
168   :type 'integer
169   :group 'makefile)
170
171 (defcustom makefile-backslash-column 48
172   "*Column in which `makefile-backslash-region' inserts backslashes."
173   :type 'integer
174   :group 'makefile)
175
176 (defcustom makefile-browser-selected-mark "+  "
177   "*String used to mark selected entries in the Makefile browser."
178   :type 'string
179   :group 'makefile)
180
181 (defcustom makefile-browser-unselected-mark "   "
182   "*String used to mark unselected entries in the Makefile browser."
183   :type 'string
184   :group 'makefile)
185
186 (defcustom makefile-browser-auto-advance-after-selection-p t
187   "*If non-nil, cursor will move after item is selected in Makefile browser."
188   :type 'boolean
189   :group 'makefile)
190
191 (defcustom makefile-pickup-everything-picks-up-filenames-p nil
192   "*If non-nil, `makefile-pickup-everything' picks up filenames as targets.
193 This means it calls `makefile-pickup-filenames-as-targets'.
194 Otherwise filenames are omitted."
195   :type 'boolean
196   :group 'makefile)
197
198 (defcustom makefile-cleanup-continuations-p t
199   "*If non-nil, automatically clean up continuation lines when saving.
200 A line is cleaned up by removing all whitespace following a trailing
201 backslash.  This is done silently.
202 IMPORTANT: Please note that enabling this option causes Makefile mode
203 to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\"."
204   :type 'boolean
205   :group 'makefile)
206
207 ;;; those suspicious line warnings are really annoying and
208 ;;; seem to be generated for every makefile I've ever seen.
209 ;;; add a simple mechanism to disable them.  -gk
210 (defcustom makefile-warn-suspicious-lines-p t
211   "In non-nil, warn about suspicious lines when saving the makefile"
212   :type 'boolean
213   :group 'makefile)
214
215 (defcustom makefile-browser-hook '()
216   "The hook to run when entering makefile browser."
217   :type 'hook
218   :group 'makefile)
219
220 ;;
221 ;; Special targets for DMake, Sun's make ...
222 ;; 
223 (defcustom makefile-special-targets-list
224   '(("DEFAULT")      ("DONE")        ("ERROR")        ("EXPORT")
225     ("FAILED")       ("GROUPEPILOG") ("GROUPPROLOG")  ("IGNORE")
226     ("IMPORT")       ("INCLUDE")     ("INCLUDEDIRS")  ("INIT")
227     ("KEEP_STATE")   ("MAKEFILES")   ("MAKE_VERSION") ("NO_PARALLEL")
228     ("PARALLEL")     ("PHONY")       ("PRECIOUS")     ("REMOVE")
229     ("SCCS_GET")     ("SILENT")      ("SOURCE")       ("SUFFIXES")
230     ("WAIT")         ("c.o")         ("C.o")          ("m.o")
231     ("el.elc")       ("y.c")         ("s.o"))
232   "*List of special targets.
233 You will be offered to complete on one of those in the minibuffer whenever
234 you enter a \".\" at the beginning of a line in `makefile-mode'."
235   :type '(repeat (list string))
236   :group 'makefile)
237
238 (defcustom makefile-runtime-macros-list
239   '(("@") ("&") (">") ("<") ("*") ("^") ("+") ("?") ("%") ("$"))
240   "*List of macros that are resolved by make at runtime.
241 If you insert a macro reference using `makefile-insert-macro-ref', the name
242 of the macro is checked against this list.  If it can be found its name will
243 not be enclosed in { } or ( )."
244   :type '(repeat (list string))
245   :group 'makefile)
246
247 ;;; Some sample lines from one of the glibc makefiles:
248 ;;
249 ;; $(services:%=$(objpfx)libnss_%.so) $(objpfx)libnsl.so: $(common-objpfx)libc.so
250 ;; $(objpfx)libnss_compat.so: $(objpfx)libnsl.so$(libnsl.so-version)
251 ;; subdir-dirs = $(services:%=nss_%) ---> mustn't match.
252 ;; ordinary: blah blah
253 ;;
254 ;; Note that the first big subexpression (the only match capturing
255 ;; one) is used by font lock, imenu, and makefile-mode.
256 (defconst makefile-dependency-regex
257   (eval-when-compile
258    (concat
259     "^"
260 ;;;   "\\(?:vpath[ \t]\\)\\{0,0\\}" ; doesn't work as intended
261 ;;; I want it to NOT highlight lines like:
262 ;;;vpath %.h $(subst $(empty) ,:,$(strip $(common-objpfx) $(objpfx) \
263 ;;;                                   $(+sysdep_dirs) $(..)))
264 ;;; ... where the colon between the two commas makes it look like a
265 ;;; target.  The workaround is to write the line like:
266 ;;;colon := :
267 ;;;vpath %.h $(subst $(empty) ,$(colon),$(strip $(common-objpfx) $(objpfx) \
268 ;;;                                   $(+sysdep_dirs) $(..)))
269 ;;; ... putting a space before vpath works too, but I highlight vpath later,
270 ;;; and that expects it to be anchored left.
271 ;;; I wonder if the "zero length negative assertion" (the \\{0,0\\} is
272 ;;; moving the scan cursor forward?  Bet that's it.  I should look
273 ;;; into the regexp engine at some point and see if I can fix it.
274 ;;; The libpcre is not too shabby.  Dunno if it's right for an Emacs.
275     "\\("
276       " *"
277       "\\(?:"
278             "\\$[\(\{][^\)\}]+[\)\}][^ \n\t#=:]+"
279         "\\|"
280             "[^ \n\t#=:]+"
281        "\\)"
282        "\\(?:"
283              "[ \t]+"
284              "\\(?:"
285                    "\\$[\(\}][^\)\}]+[\)\}][^ \n\t#=:]+"
286                "\\|"
287                    "[^ \n\t#=:]+"
288               "\\)"
289        "\\)*"
290    "\\)" ; end match-string 1 (the only one we use.)
291    "[ \t]*:"
292    "\\(?:"
293           "[ \t]*$"
294      "\\|"
295           "[^=\n].*$"
296    "\\)"))
297   "Regex used to find dependency lines in a makefile.")
298
299 ;; Note that the first match capturing subexpression is used by font
300 ;; lock, Imenu, and makefile-mode.
301 (defconst makefile-macroassign-regex
302     "^ *\\(?:export[ \t]+\\)?\\([^# \t\n][^#=: \t\n]*\\)[ \t]*[*:+?]?:?="
303   "Regex used to find macro assignment lines in a makefile.")
304
305 (defconst makefile-imenu-generic-expression
306     `(("Targets" ,makefile-dependency-regex 1)
307       ("Macros" ,makefile-macroassign-regex 1)
308       ("Defines" "^define[ \t]+\\([-.a-zA-Z0-9_+]+\\)" 1)
309       ("Includes" "^[-s]?include[ \t]+\\([-.,/a-zA-Z0-9_+]+\\)" 1)
310       )
311   "See: `imenu-generic-expression'")
312
313 (defconst makefile-ignored-files-in-pickup-regex
314   "\\(^\\..*\\)\\|\\(.*~$\\)\\|\\(.*,v$\\)\\|\\(\\.[chy]\\)"
315   "Regex for filenames that will NOT be included in the target list.")
316
317 ;Older version of same.
318 ;(defconst makefile-font-lock-keywords (purecopy
319 ;  (list
320 ;   '("^#.*$" . font-lock-comment-face)
321 ;   '("[^$]#.*$" . font-lock-comment-face)
322 ;   ;; rules
323 ;   '("^\\([^ \t\n]*%?[^ \t\n]*[ \t]*::?\\)[ \t]" 1 font-lock-type-face t)
324 ;   '("^\\(\\.[A-Za-z][A-Za-z]?\\..[ \t]*::?\\)" 1 font-lock-type-face t)
325 ;   '("^[^ \t\n]+[ \t]*:;?\\(.*\\)$" 1 font-lock-doc-string-face t)
326 ;   ;; variable definition
327 ;   '("^[_A-Za-z0-9]+[ \t]*\+?=" . font-lock-function-name-face)
328 ;   '("\\( \\|:=\\)[_A-Za-z0-9]+[ \t]*\\+=" . font-lock-function-name-face)
329 ;   ;; variable references
330 ;   '("\\(\\$\\$?\\([^ \t\n{(]\\|[{(][^ \t\n)}]+[)}]\\)\\)" 
331 ;     1 font-lock-keyword-face t)
332 ;   '("^include " . font-lock-string-face)
333 ;   ))
334
335 (defconst makefile-font-lock-keywords
336   (list
337    ;; conditionals, vpath & include
338    (list
339     (concat "^ *\\("
340             (regexp-opt
341              (mapcar #'symbol-name
342                      '(ifeq ifneq else endif endef include sinclude)))
343             "\\|-include\\)[ \t\n]+")
344     1 'font-lock-preprocessor-face)
345
346    '("^ *\\(ifn?def\\)[ \t]+\\(.*$\\)"
347      (1 font-lock-preprocessor-face) (2 font-lock-reference-face nil t))
348
349    ;; define
350    '("^ *\\(define\\)[ \t]+\\(.*$\\)"
351      (1 font-lock-preprocessor-face) (2 font-lock-variable-name-face nil t))
352
353    ;; vpath export unexport override
354    (list
355     (concat "^ *\\("
356             (regexp-opt
357              (mapcar #'symbol-name
358                      '(vpath export unexport override)))
359             "\\)[ \t]+") 1 'font-lock-keyword-face 'prepend)
360
361    ;; Do dependencies.  These get the function name face.
362    (list makefile-dependency-regex 1 'font-lock-function-name-face 'prepend)
363
364    ;; Do macro assignments.  These get the "variable-name" face rather
365    ;; arbitrarily.
366    (list makefile-macroassign-regex 1 'font-lock-variable-name-face 'keep)
367
368    ;; Variable references even in targets/strings/comments:
369    (list
370     (concat "\\$[\(\{][ \t]*"
371 ;;; Commented off because
372 ;;; $($(var)-ext) still doesn't work.
373 ;;;  I want to highlight
374 ;;; ${${var}-ext} blah blah ${var2}
375 ;;;   ++^^^+^^^^              ^^^^
376 ;;; *not*
377 ;;; ${${var}-ext} blah blah ${var2}
378 ;;;   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
379 ;;; or just
380 ;;; ${${var}-ext} blah blah ${var2}
381 ;;;     ^^^                   ^^^^
382 ;;;            "\\(?:"
383 ;;;              "\\$[\(\{]"
384 ;;;              "[^\)\}]+"
385 ;;;              "[\)\}]"
386 ;;;            "\\)*"
387             "\\("
388             "[-<@.a-zA-Z0-9+_]+"        ; match-string 1
389             "\\)"
390             "\\(?:"
391             "[^-.a-zA-Z0-9+_]\\{0,0\\}"
392             "\\|"
393             "[ \t]*[:\$].*?"
394             "\\)"
395             "[ \t]*[\}\)]")
396     1 'font-lock-reference-face 'prepend)
397
398    ;; and builtin special variable names
399    (list
400     (concat "\\$" (regexp-opt (mapcar #'car makefile-runtime-macros-list)))
401     '(0 font-lock-reference-face t))
402
403    ;; Make $(functions ...)
404    (list
405     (concat
406      "\\$[\(\{][ \t]*\\("
407      (regexp-opt
408       (mapcar #'symbol-name
409               '(subst patsubst strip findstring filter filter-out sort
410                       dir notdir suffix basename addsuffix addprefix join word words
411                       wordlist firstword wildcard error warning shell origin foreach
412                       call)))
413      "\\)[ \t\n]+:\\{0,0\\}")
414     1 'font-lock-keyword-face 'prepend)
415
416    ;; Todo:, Fixme,and ### through ###### in comments.
417    '("#.*?[ \t]+\\([Tt][Oo][Dd][Oo]:?\\)" (1 font-lock-warning-face t))
418    '("#.*?[ \t]+\\(#\\{3,6\\}\\)[ \t\n]+" (1 font-lock-warning-face t))
419    '("#.*?[ \t]+\\([Ff][Ii][Xx] *[Mm][Ee]:?\\)" (1 font-lock-warning-face t))
420
421    ;; Highlight lines that contain just whitespace.
422    ;; They can cause trouble, especially if they start with a tab.
423    '("^[ \t]+$" . makefile-space-face)
424
425    ;; Highlight shell comments that Make treats as commands,
426    ;; since these can fool people.
427    '("^\t+#" 0 makefile-space-face t)
428
429    ;; Highlight spaces that precede tabs.
430    ;; They can make a tab fail to be effective.
431    '("^\\( +\\)\t" 1 makefile-space-face))
432   "Additional expressions to highlight in makefiles")
433
434 (put 'makefile-mode 'font-lock-defaults '(makefile-font-lock-keywords))
435
436 ;;; ------------------------------------------------------------
437 ;;; The following configurable variables are used in the
438 ;;; up-to-date overview .
439 ;;; The standard configuration assumes that your `make' program
440 ;;; can be run in question/query mode using the `-q' option, this
441 ;;; means that the command
442 ;;;
443 ;;;    make -q foo
444 ;;;
445 ;;; should return an exit status of zero if the target `foo' is
446 ;;; up to date and a nonzero exit status otherwise.
447 ;;; Many makes can do this although the docs/manpages do not mention
448 ;;; it. Try it with your favourite one.  GNU make, System V make, and
449 ;;; Dennis Vadura's DMake have no problems.
450 ;;; Set the variable `makefile-brave-make' to the name of the
451 ;;; make utility that does this on your system.
452 ;;; To understand what this is all about see the function definition
453 ;;; of `makefile-query-by-make-minus-q' .
454 ;;; ------------------------------------------------------------
455
456 (defcustom makefile-brave-make "make"
457   "*How to invoke make, for `makefile-query-targets'.
458 This should identify a `make' command that can handle the `-q' option."
459   :type 'string
460   :group 'makefile)
461
462 (defcustom makefile-query-one-target-method 'makefile-query-by-make-minus-q
463   "*Function to call to determine whether a make target is up to date.
464 The function must satisfy this calling convention:
465
466 * As its first argument, it must accept the name of the target to
467   be checked, as a string.
468
469 * As its second argument, it may accept the name of a makefile
470   as a string.  Depending on what you're going to do you may
471   not need this.
472
473 * It must return the integer value 0 (zero) if the given target
474   should be considered up-to-date in the context of the given
475   makefile, any nonzero integer value otherwise."
476   :type 'function
477   :group 'makefile)
478
479 (defcustom makefile-up-to-date-buffer-name "*Makefile Up-to-date overview*"
480   "*Name of the Up-to-date overview buffer."
481   :type 'string
482   :group 'makefile)
483
484 ;;; --- end of up-to-date-overview configuration ------------------
485
486 (defvar makefile-mode-abbrev-table nil
487   "Abbrev table in use in Makefile buffers.")
488 (if makefile-mode-abbrev-table
489     ()
490   (define-abbrev-table 'makefile-mode-abbrev-table ()))
491
492 (defvar makefile-mode-map nil
493   "The keymap that is used in Makefile mode.")
494
495 (if makefile-mode-map
496     ()
497   (setq makefile-mode-map (make-sparse-keymap 'makefile-mode-map))
498   ;; set up the keymap
499   (define-key makefile-mode-map "\C-c:" 'makefile-insert-target-ref)
500   (if makefile-electric-keys
501       (progn
502         (define-key makefile-mode-map "$" 'makefile-insert-macro-ref)
503         (define-key makefile-mode-map ":" 'makefile-electric-colon)
504         (define-key makefile-mode-map "=" 'makefile-electric-equal)
505         (define-key makefile-mode-map "." 'makefile-electric-dot)))
506   (define-key makefile-mode-map "\C-c\C-f" 'makefile-pickup-filenames-as-targets)
507   (define-key makefile-mode-map "\C-c\C-b" 'makefile-switch-to-browser)
508   (define-key makefile-mode-map "\C-c\C-c" 'comment-region)
509   (define-key makefile-mode-map "\C-c\C-p" 'makefile-pickup-everything)
510   (define-key makefile-mode-map "\C-c\C-u" 'makefile-create-up-to-date-overview)
511   (define-key makefile-mode-map "\C-c\C-i" 'makefile-insert-gmake-function)
512   (define-key makefile-mode-map "\C-c\C-\\" 'makefile-backslash-region)
513   (define-key makefile-mode-map "\M-p"     'makefile-previous-dependency)
514   (define-key makefile-mode-map "\M-n"     'makefile-next-dependency)
515   (define-key makefile-mode-map "\e\t"     'makefile-complete))
516
517 ;; XEmacs change
518 (defconst makefile-menubar-menu
519     '("Makefile"
520       ["Move to Next Dependency" makefile-next-dependency t]
521       ["Move to Previous Dependency" makefile-previous-dependency t]
522       "---"
523       ["Find Targets and Macros" makefile-pickup-everything t]
524       ["Complete Target or Macro" makefile-complete t]
525       ["Pop up Makefile Browser" makefile-switch-to-browser t]))
526
527 ;; XEmacs change
528 (defconst makefile-popup-menu
529     (cons "Makefile Mode Commands"
530           (cdr makefile-menubar-menu)))
531
532 (defvar makefile-browser-map nil
533   "The keymap that is used in the macro- and target browser.")
534 (if makefile-browser-map
535     ()
536   (setq makefile-browser-map (make-sparse-keymap))
537   (define-key makefile-browser-map "n"    'makefile-browser-next-line)
538   (define-key makefile-browser-map "\C-n" 'makefile-browser-next-line)
539   (define-key makefile-browser-map "p"    'makefile-browser-previous-line)
540   (define-key makefile-browser-map "\C-p" 'makefile-browser-previous-line)
541   (define-key makefile-browser-map " "    'makefile-browser-toggle)
542   (define-key makefile-browser-map "i"    'makefile-browser-insert-selection)
543   (define-key makefile-browser-map "I"    'makefile-browser-insert-selection-and-quit)
544   (define-key makefile-browser-map "\C-c\C-m" 'makefile-browser-insert-continuation)
545   (define-key makefile-browser-map "q"    'makefile-browser-quit)
546   ;; disable horizontal movement
547   (define-key makefile-browser-map "\C-b" 'undefined)
548   (define-key makefile-browser-map "\C-f" 'undefined))
549
550
551 (defvar makefile-mode-syntax-table nil)
552 (if makefile-mode-syntax-table
553     ()
554   (setq makefile-mode-syntax-table (make-syntax-table))
555   (modify-syntax-entry ?\( "()    " makefile-mode-syntax-table)
556   (modify-syntax-entry ?\) ")(    " makefile-mode-syntax-table)
557   (modify-syntax-entry ?\[ "(]    " makefile-mode-syntax-table)
558   (modify-syntax-entry ?\] ")[    " makefile-mode-syntax-table)
559   (modify-syntax-entry ?\{ "(}    " makefile-mode-syntax-table)
560   (modify-syntax-entry ?\} "){    " makefile-mode-syntax-table)
561   (modify-syntax-entry ?\' "\"     " makefile-mode-syntax-table)
562   (modify-syntax-entry ?\` "\"     " makefile-mode-syntax-table)
563   (modify-syntax-entry ?#  "<     " makefile-mode-syntax-table)
564   (modify-syntax-entry ?\n ">     " makefile-mode-syntax-table))
565
566
567 ;;; ------------------------------------------------------------
568 ;;; Internal variables.
569 ;;; You don't need to configure below this line.
570 ;;; ------------------------------------------------------------
571
572 (defvar makefile-target-table nil
573   "Table of all target names known for this buffer.")
574
575 (defvar makefile-macro-table nil
576   "Table of all macro names known for this buffer.")
577
578 (defvar makefile-browser-client
579   "A buffer in Makefile mode that is currently using the browser.")
580
581 (defvar makefile-browser-selection-vector nil)
582 (defvar makefile-has-prereqs nil)
583 (defvar makefile-need-target-pickup t)
584 (defvar makefile-need-macro-pickup t)
585
586 (defvar makefile-mode-hook '())
587
588 ;; Each element looks like '("GNU MAKE FUNCTION" "ARG" "ARG" ... )
589 ;; Each "ARG" is used as a prompt for a required argument.
590 (defconst makefile-gnumake-functions-alist
591   '(
592     ;; Text functions
593     ("subst" "From" "To" "In")
594     ("patsubst" "Pattern" "Replacement" "In")
595     ("strip" "Text")
596     ("findstring" "Find what" "In")
597     ("filter" "Pattern" "Text")
598     ("filter-out" "Pattern" "Text")
599     ("sort" "List")
600     ;; Filename functions
601     ("dir" "Names")
602     ("notdir" "Names")
603     ("suffix" "Names")
604     ("basename" "Names")
605     ("addprefix" "Prefix" "Names")
606     ("addsuffix" "Suffix" "Names")
607     ("join" "List 1" "List 2")
608     ("word" "Index" "Text")
609     ("words" "Text")
610     ("firstword" "Text")
611     ("wildcard" "Pattern")
612     ;; Misc functions
613     ("foreach" "Variable" "List" "Text")
614     ("origin" "Variable")
615     ("shell" "Command")))
616
617
618 ;;; ------------------------------------------------------------
619 ;;; The mode function itself.
620 ;;; ------------------------------------------------------------
621
622 ;;;###autoload
623 (defun makefile-mode ()
624   "Major mode for editing Makefiles.
625 This function ends by invoking the function(s) `makefile-mode-hook'.
626
627 \\{makefile-mode-map}
628
629 In the browser, use the following keys:
630
631 \\{makefile-browser-map}
632
633 Makefile mode can be configured by modifying the following variables:
634
635 makefile-browser-buffer-name:
636     Name of the macro- and target browser buffer.
637
638 makefile-target-colon:
639     The string that gets appended to all target names
640     inserted by `makefile-insert-target'.
641     \":\" or \"::\" are quite common values.
642
643 makefile-macro-assign:
644    The string that gets appended to all macro names
645    inserted by `makefile-insert-macro'.
646    The normal value should be \" = \", since this is what
647    standard make expects.  However, newer makes such as dmake
648    allow a larger variety of different macro assignments, so you
649    might prefer to use \" += \" or \" := \" .
650
651 makefile-tab-after-target-colon:
652    If you want a TAB (instead of a space) to be appended after the
653    target colon, then set this to a non-nil value.
654
655 makefile-browser-leftmost-column:
656    Number of blanks to the left of the browser selection mark.
657
658 makefile-browser-cursor-column:
659    Column in which the cursor is positioned when it moves
660    up or down in the browser.
661
662 makefile-browser-selected-mark:
663    String used to mark selected entries in the browser.
664
665 makefile-browser-unselected-mark:
666    String used to mark unselected entries in the browser.
667
668 makefile-browser-auto-advance-after-selection-p:
669    If this variable is set to a non-nil value the cursor
670    will automagically advance to the next line after an item
671    has been selected in the browser.
672
673 makefile-pickup-everything-picks-up-filenames-p:
674    If this variable is set to a non-nil value then
675    `makefile-pickup-everything' also picks up filenames as targets
676    (i.e. it calls `makefile-pickup-filenames-as-targets'), otherwise
677    filenames are omitted.
678
679 makefile-cleanup-continuations-p:
680    If this variable is set to a non-nil value then Makefile mode
681    will assure that no line in the file ends with a backslash
682    (the continuation character) followed by any whitespace.
683    This is done by silently removing the trailing whitespace, leaving
684    the backslash itself intact.
685    IMPORTANT: Please note that enabling this option causes Makefile mode
686    to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\".
687
688 makefile-browser-hook:
689    A function or list of functions to be called just before the
690    browser is entered. This is executed in the makefile buffer.
691
692 makefile-special-targets-list:
693    List of special targets. You will be offered to complete
694    on one of those in the minibuffer whenever you enter a `.'.
695    at the beginning of a line in Makefile mode."
696
697   (interactive)
698   (kill-all-local-variables)
699   (make-local-variable 'local-write-file-hooks)
700   (setq local-write-file-hooks
701         '(makefile-cleanup-continuations makefile-warn-suspicious-lines))
702   (make-local-variable 'makefile-target-table)
703   (make-local-variable 'makefile-macro-table)
704   (make-local-variable 'makefile-has-prereqs)
705   (make-local-variable 'makefile-need-target-pickup)
706   (make-local-variable 'makefile-need-macro-pickup)
707
708   ;; Font lock.
709   (make-local-variable 'font-lock-defaults)
710   (setq font-lock-defaults '(makefile-font-lock-keywords))
711
712   ;; Add-log.
713   (make-local-variable 'add-log-current-defun-function)
714   (setq add-log-current-defun-function 'makefile-add-log-defun)
715
716   ;; Imenu.
717   (make-local-variable 'imenu-generic-expression)
718   (setq imenu-generic-expression makefile-imenu-generic-expression)
719
720   ;; Dabbrev.
721   (make-local-variable 'dabbrev-abbrev-skip-leading-regexp)
722   (setq dabbrev-abbrev-skip-leading-regexp "\\$")
723
724   ;; Other abbrevs.
725   (setq local-abbrev-table makefile-mode-abbrev-table)
726
727   ;; Filling.
728   (make-local-variable 'fill-paragraph-function)
729   (setq fill-paragraph-function 'makefile-fill-paragraph)
730
731   ;; Comment stuff.
732   (make-local-variable 'comment-start)
733   (setq comment-start "#")
734   (make-local-variable 'comment-end)
735   (setq comment-end "")
736   (make-local-variable 'comment-start-skip)
737   (setq comment-start-skip "#+[ \t]*")
738
739   ;; become the current major mode
740   (setq major-mode 'makefile-mode)
741   (setq mode-name "Makefile")
742
743   ;; Activate keymap and syntax table.
744   (use-local-map makefile-mode-map)
745   (set-syntax-table makefile-mode-syntax-table)
746
747   ;; Set menu
748   ;; XEmacs addition
749   (setq mode-popup-menu makefile-popup-menu)
750   (if (and (featurep 'menubar)
751            (or (not (featurep 'infodock))
752                (not (eq infodock-menubar-type 'menubar-infodock))))
753       (progn
754         ;; make a local copy of the menubar, so our modes don't
755         ;; change the global menubar
756         (set-buffer-menubar current-menubar)
757         (add-submenu nil makefile-menubar-menu)))
758
759   ;; Real TABs are important in makefiles
760   (setq indent-tabs-mode t)
761   (run-hooks 'makefile-mode-hook))
762
763 \f
764
765 ;;; Motion code.
766
767 (defun makefile-next-dependency ()
768   "Move point to the beginning of the next dependency line."
769   (interactive)
770   (let ((here (point)))
771     (end-of-line)
772     (if (re-search-forward makefile-dependency-regex (point-max) t)
773         (progn (beginning-of-line) t)   ; indicate success
774       (goto-char here) nil)))
775
776 (defun makefile-previous-dependency ()
777   "Move point to the beginning of the previous dependency line."
778   (interactive)
779   (let ((here (point)))
780     (beginning-of-line)
781     (if (re-search-backward makefile-dependency-regex (point-min) t)
782         (progn (beginning-of-line) t)   ; indicate success
783       (goto-char here) nil)))
784
785 \f
786
787 ;;; Electric keys.  Blech.
788
789 (defun makefile-electric-dot (arg)
790   "Prompt for the name of a special target to insert.
791 Only does electric insertion at beginning of line.
792 Anywhere else just self-inserts."
793   (interactive "p")
794   (if (bolp)
795       (makefile-insert-special-target)
796     (self-insert-command arg)))
797
798 (defun makefile-insert-special-target ()
799   "Prompt for and insert a special target name.
800 Uses `makefile-special-targets' list."
801   (interactive)
802   (makefile-pickup-targets)
803   (let ((special-target
804          (completing-read "Special target: "
805                           makefile-special-targets-list nil nil nil)))
806     (if (zerop (length special-target))
807         ()
808       (insert "." special-target ":")
809       (makefile-forward-after-target-colon))))
810
811 (defun makefile-electric-equal (arg)
812   "Prompt for name of a macro to insert.
813 Only does prompting if point is at beginning of line.
814 Anywhere else just self-inserts."
815   (interactive "p")
816   (makefile-pickup-macros)
817   (if (bolp)
818       (call-interactively 'makefile-insert-macro)
819     (self-insert-command arg)
820     ;; from here down is new -- if they inserted a macro without using
821     ;; the electric behavior, pick it up anyway   -gk
822     (save-excursion
823       (beginning-of-line)
824       (if (looking-at makefile-macroassign-regex)
825           (makefile-add-this-line-macro)))))
826
827 (defun makefile-insert-macro (macro-name)
828   "Prepare definition of a new macro."
829   (interactive "sMacro Name: ")
830   (makefile-pickup-macros)
831   (if (not (zerop (length macro-name)))
832       (progn
833         (beginning-of-line)
834         (insert macro-name makefile-macro-assign)
835         (setq makefile-need-macro-pickup t)
836         (makefile-remember-macro macro-name))))
837
838 (defun makefile-insert-macro-ref (macro-name)
839   "Complete on a list of known macros, then insert complete ref at point."
840   (interactive
841    (list
842     (progn
843       (makefile-pickup-macros)
844       (completing-read "Refer to macro: " makefile-macro-table nil nil nil))))
845   (makefile-do-macro-insertion macro-name))
846
847 (defun makefile-insert-target (target-name)
848   "Prepare definition of a new target (dependency line)."
849   (interactive "sTarget: ")
850   (if (not (zerop (length target-name)))
851       (progn
852         (beginning-of-line)
853         (insert target-name makefile-target-colon)
854         (makefile-forward-after-target-colon)
855         (end-of-line)
856         (setq makefile-need-target-pickup t)
857         (makefile-remember-target target-name))))
858
859 (defun makefile-insert-target-ref (target-name)
860   "Complete on a list of known targets, then insert TARGET-NAME at point."
861   (interactive
862    (list
863     (progn
864      (makefile-pickup-targets)
865      (completing-read "Refer to target: " makefile-target-table nil nil nil))))
866    (if (not (zerop (length target-name)))
867        (insert target-name " ")))
868
869 (defun makefile-electric-colon (arg)
870   "Prompt for name of new target.
871 Prompting only happens at beginning of line.
872 Anywhere else just self-inserts."
873   (interactive "p")
874   (if (bolp)
875       (call-interactively 'makefile-insert-target)
876     (self-insert-command arg)))
877
878 \f
879
880 ;;; ------------------------------------------------------------
881 ;;; Extracting targets and macros from an existing makefile
882 ;;; ------------------------------------------------------------
883
884 (defun makefile-pickup-targets ()
885   "Notice names of all target definitions in Makefile."
886   (interactive)
887   (if (not makefile-need-target-pickup)
888       nil
889     (setq makefile-need-target-pickup nil)
890     (setq makefile-target-table nil)
891     (setq makefile-has-prereqs nil)
892     (save-excursion
893       (goto-char (point-min))
894       (while (re-search-forward makefile-dependency-regex (point-max) t)
895         (makefile-add-this-line-targets)))
896     (message "Read targets OK.")))
897
898 (defun makefile-add-this-line-targets ()
899   (save-excursion
900     (beginning-of-line)
901     (let ((done-with-line nil)
902           (line-number (1+ (count-lines (point-min) (point)))))
903       (while (not done-with-line)
904         (skip-chars-forward " \t")
905         (if (not (setq done-with-line (or (eolp)
906                                           (char-equal (char-after (point)) ?:))))
907             (progn
908               (let* ((start-of-target-name (point))
909                      (target-name
910                       (progn
911                         (skip-chars-forward "^ \t:#")
912                         (buffer-substring start-of-target-name (point))))
913                      (has-prereqs
914                       (not (looking-at ":[ \t]*$"))))
915                 (if (makefile-remember-target target-name has-prereqs)
916                     (message "Picked up target \"%s\" from line %d"
917                              target-name line-number)))))))))
918
919 (defun makefile-pickup-macros ()
920   "Notice names of all macro definitions in Makefile."
921   (interactive)
922   (if (not makefile-need-macro-pickup)
923       nil
924     (setq makefile-need-macro-pickup nil)
925     ;; changed the nil in the next line to makefile-runtime-macros-list
926     ;; so you don't have to confirm on every runtime macro entered...  -gk
927     (setq makefile-macro-table makefile-runtime-macros-list) 
928     (save-excursion
929       (goto-char (point-min))
930       (while (re-search-forward makefile-macroassign-regex (point-max) t)
931         (makefile-add-this-line-macro)
932         (forward-line 1)))
933     (message "Read macros OK.")))
934
935 (defun makefile-add-this-line-macro ()
936   (save-excursion
937     (beginning-of-line)
938     (skip-chars-forward " \t")
939     (if (not (eolp))
940         (let* ((start-of-macro-name (point))
941                (line-number (1+ (count-lines (point-min) (point))))
942                (macro-name (progn
943                              (skip-chars-forward "^ \t:#=*")
944                              (buffer-substring start-of-macro-name (point)))))
945           (if (makefile-remember-macro macro-name)
946               (message "Picked up macro \"%s\" from line %d"
947                        macro-name line-number))))))
948
949 (defun makefile-pickup-everything (arg)
950   "Notice names of all macros and targets in Makefile.
951 Prefix arg means force pickups to be redone."
952   (interactive "P")
953   (if arg
954       (progn
955         (setq makefile-need-target-pickup t)
956         (setq makefile-need-macro-pickup t)))
957   (makefile-pickup-macros)
958   (makefile-pickup-targets)
959   (if makefile-pickup-everything-picks-up-filenames-p
960       (makefile-pickup-filenames-as-targets)))
961
962 (defun makefile-pickup-filenames-as-targets ()
963   "Scan the current directory for filenames to use as targets.
964 Checks each filename against `makefile-ignored-files-in-pickup-regex'
965 and adds all qualifying names to the list of known targets."
966   (interactive)
967   (let* ((dir (file-name-directory (buffer-file-name)))
968          (raw-filename-list (if dir
969                                 (file-name-all-completions "" dir)
970                               (file-name-all-completions "" ""))))
971     (mapcar '(lambda (name)
972                (if (and (not (file-directory-p name))
973                         (not (string-match makefile-ignored-files-in-pickup-regex
974                                            name)))
975                    (if (makefile-remember-target name)
976                        (message "Picked up file \"%s\" as target" name))))
977             raw-filename-list)))
978
979 \f
980
981 ;;; Completion.
982
983 (defun makefile-complete ()
984   "Perform completion on Makefile construct preceding point.
985 Can complete variable and target names.
986 The context determines which are considered."
987   (interactive)
988   (let* ((beg (save-excursion
989                 (skip-chars-backward "^$(){}:#= \t\n")
990                 (point)))
991          (try (buffer-substring beg (point)))
992          (do-macros nil)
993          (paren nil))
994
995     (save-excursion
996       (goto-char beg)
997       (let ((pc (preceding-char)))
998         (cond
999          ;; Beginning of line means anything.
1000          ((bolp)
1001           ())
1002
1003          ;; Preceding "$" means macros only.
1004          ((= pc ?$)
1005           (setq do-macros t))
1006
1007          ;; Preceding "$(" or "${" means macros only.
1008          ((and (or (= pc ?{)
1009                    (= pc ?\())
1010                (progn
1011                  (setq paren pc)
1012                  (backward-char)
1013                  (and (not (bolp))
1014                       (= (preceding-char) ?$))))
1015           (setq do-macros t)))))
1016
1017     ;; Try completion.
1018     (let* ((table (append (if do-macros
1019                               '()
1020                             makefile-target-table)
1021                           makefile-macro-table))
1022            (completion (try-completion try table)))
1023       (cond
1024        ;; Exact match, so insert closing paren or colon.
1025        ((eq completion t)
1026         (insert (if do-macros
1027                     (if (eq paren ?{)
1028                         ?}
1029                       ?\))
1030                   (if (save-excursion
1031                         (goto-char beg)
1032                         (bolp))
1033                       ":"
1034                     " "))))
1035
1036        ;; No match.
1037        ((null completion)
1038         (message "Can't find completion for \"%s\"" try)
1039         (ding))
1040
1041        ;; Partial completion.
1042        ((not (string= try completion))
1043         ;; FIXME it would be nice to supply the closing paren if an
1044         ;; exact, unambiguous match were found.  That is not possible
1045         ;; right now.  Ditto closing ":" for targets.
1046         (delete-region beg (point))
1047
1048         ;; DO-MACROS means doing macros only.  If not that, then check
1049         ;; to see if this completion is a macro.  Special insertion
1050         ;; must be done for macros.
1051         (if (or do-macros
1052                 (assoc completion makefile-macro-table))
1053             (let ((makefile-use-curly-braces-for-macros-p
1054                    (or (eq paren ?{)
1055                        makefile-use-curly-braces-for-macros-p)))
1056               (delete-backward-char 2)
1057               (makefile-do-macro-insertion completion)
1058               (delete-backward-char 1))
1059
1060           ;; Just insert targets.
1061           (insert completion)))
1062
1063        ;; Can't complete any more, so make completion list.  FIXME
1064        ;; this doesn't do the right thing when the completion is
1065        ;; actually inserted.  I don't think there is an easy way to do
1066        ;; that.
1067        (t
1068         (message "Making completion list...")
1069         (let ((list (all-completions try table)))
1070           (with-output-to-temp-buffer "*Completions*"
1071             (display-completion-list list)))
1072         (message "Making completion list...done"))))))
1073
1074 \f
1075
1076 ;; Backslashification.  Stolen from cc-mode.el.
1077
1078 (defun makefile-backslashify-current-line (doit)
1079   (end-of-line)
1080   (if doit
1081       (if (not (save-excursion
1082                  (forward-char -1)
1083                  (eq (char-after (point)) ?\\ )))
1084           (progn
1085             (if (>= (current-column) makefile-backslash-column)
1086                 (insert " \\")
1087               (while (<= (current-column) makefile-backslash-column)
1088                 (insert "\t")
1089                 (end-of-line))
1090               (delete-char -1)
1091               (while (< (current-column) makefile-backslash-column)
1092                 (insert " ")
1093                 (end-of-line))
1094               (insert "\\"))))
1095     (if (not (bolp))
1096         (progn
1097           (forward-char -1)
1098           (if (eq (char-after (point)) ?\\ )
1099               (let ((saved (save-excursion
1100                             (end-of-line)
1101                             (point))))
1102                 (skip-chars-backward " \t")
1103                 (delete-region (point) saved)))))))
1104
1105 (defun makefile-backslash-region (beg end arg)
1106   "Insert backslashes at end of every line in region.
1107 Useful for defining multi-line rules.
1108 If called with a prefix argument, trailing backslashes are removed."
1109   (interactive "r\nP")
1110   (save-excursion
1111     (let ((do-lastline-p (progn (goto-char end) (not (bolp))))
1112           ;; dynamic binding relied on here.
1113           (makefile-backslash-column makefile-backslash-column))
1114       (save-restriction
1115         (narrow-to-region beg end)
1116         (goto-char (point-min))
1117         (while (not (eobp))
1118           (end-of-line)
1119           (and (> (current-column) (- makefile-backslash-column 1))
1120                (setq makefile-backslash-column (+ (current-column) 1)))
1121           (forward-line 1))
1122         (goto-char (point-min))
1123         (while (not (save-excursion
1124                       (forward-line 1)
1125                       (eobp)))
1126           (makefile-backslashify-current-line (null arg))
1127           (forward-line 1)))
1128       (and do-lastline-p
1129            (progn (goto-char end)
1130                   (makefile-backslashify-current-line (null arg)))))))
1131
1132 \f
1133
1134 ;; Filling
1135
1136 (defun makefile-fill-paragraph (arg)
1137   ;; Fill comments, backslashed lines, and variable definitions
1138   ;; specially.
1139   (save-excursion
1140     (beginning-of-line)
1141     (cond
1142      ((looking-at "^#+ ")
1143       ;; Found a comment.  Set the fill prefix and then fill.
1144       (let ((fill-prefix (buffer-substring-no-properties (match-beginning 0)
1145                                                          (match-end 0)))
1146             (fill-paragraph-function nil))
1147         (fill-paragraph nil)
1148         t))
1149
1150      ;; Must look for backslashed-region before looking for variable
1151      ;; assignment.
1152      ((save-excursion
1153         (end-of-line)
1154         (or
1155          (= (preceding-char) ?\\)
1156          (progn
1157            (end-of-line -1)
1158            (= (preceding-char) ?\\))))
1159       ;; A backslash region.  Find beginning and end, remove
1160       ;; backslashes, fill, and then reapply backslahes.
1161       (end-of-line)
1162       (let ((beginning
1163              (save-excursion
1164                (end-of-line 0)
1165                (while (= (preceding-char) ?\\)
1166                  (end-of-line 0))
1167                (forward-char)
1168                (point)))
1169             (end
1170              (save-excursion
1171                (while (= (preceding-char) ?\\)
1172                  (end-of-line 2))
1173                (point))))
1174         (save-restriction
1175           (narrow-to-region beginning end)
1176           (makefile-backslash-region (point-min) (point-max) t)
1177           (let ((fill-paragraph-function nil))
1178             (fill-paragraph nil))
1179           (makefile-backslash-region (point-min) (point-max) nil)
1180           (goto-char (point-max))
1181           (if (< (skip-chars-backward "\n") 0)
1182               (delete-region (point) (point-max))))))
1183
1184      ((looking-at makefile-macroassign-regex)
1185       ;; Have a macro assign.  Fill just this line, and then backslash
1186       ;; resulting region.
1187       (save-restriction
1188         (narrow-to-region (point) (save-excursion
1189                                     (end-of-line)
1190                                     (forward-char)
1191                                     (point)))
1192         (let ((fill-paragraph-function nil))
1193           (fill-paragraph nil))
1194         (makefile-backslash-region (point-min) (point-max) nil)))))
1195
1196   ;; Always return non-nil so we don't fill anything else.
1197   t)
1198
1199 \f
1200
1201 ;;; ------------------------------------------------------------
1202 ;;; Browser mode.
1203 ;;; ------------------------------------------------------------
1204
1205 (defun makefile-browser-format-target-line (target selected)
1206   (format
1207    (concat (make-string makefile-browser-leftmost-column ?\ )
1208            (if selected
1209                makefile-browser-selected-mark
1210              makefile-browser-unselected-mark)
1211            "%s%s")
1212    target makefile-target-colon))
1213
1214 (defun makefile-browser-format-macro-line (macro selected)
1215    (concat (make-string makefile-browser-leftmost-column ?\ )
1216            (if selected
1217                makefile-browser-selected-mark
1218              makefile-browser-unselected-mark)
1219            (makefile-format-macro-ref macro)))
1220
1221 (defun makefile-browser-fill (targets macros)
1222   (let ((inhibit-read-only t))
1223     (goto-char (point-min))
1224     (erase-buffer)
1225     (mapconcat
1226      (function
1227       (lambda (item) (insert (makefile-browser-format-target-line (car item) nil) "\n")))
1228      targets
1229      "")
1230     (mapconcat
1231      (function
1232       (lambda (item) (insert (makefile-browser-format-macro-line (car item) nil) "\n")))
1233      macros
1234      "")
1235     (sort-lines nil (point-min) (point-max))
1236     (goto-char (1- (point-max)))
1237     (delete-char 1)                     ; remove unnecessary newline at eob
1238     (goto-char (point-min))
1239     (forward-char makefile-browser-cursor-column)))
1240
1241 ;;;
1242 ;;; Moving up and down in the browser
1243 ;;;
1244
1245 (defun makefile-browser-next-line ()
1246   "Move the browser selection cursor to the next line."
1247   (interactive)
1248   (if (not (makefile-last-line-p))
1249       (progn
1250         (forward-line 1)
1251         (forward-char makefile-browser-cursor-column))))
1252
1253 (defun makefile-browser-previous-line ()
1254   "Move the browser selection cursor to the previous line."
1255   (interactive)
1256   (if (not (makefile-first-line-p))
1257       (progn
1258         (forward-line -1)
1259         (forward-char makefile-browser-cursor-column))))
1260
1261 ;;;
1262 ;;; Quitting the browser (returns to client buffer)
1263 ;;;
1264
1265 (defun makefile-browser-quit ()
1266   "Leave the browser and return to the makefile buffer."
1267   (interactive)
1268   (let ((my-client makefile-browser-client))
1269     (setq makefile-browser-client nil)  ; we quitted, so NO client!
1270     (set-buffer-modified-p nil)
1271     (kill-buffer (current-buffer))
1272     (pop-to-buffer my-client)))
1273
1274 ;;;
1275 ;;; Toggle state of a browser item
1276 ;;;
1277
1278 (defun makefile-browser-toggle ()
1279   "Toggle the selection state of the browser item at the cursor position."
1280   (interactive)
1281   (let ((this-line (count-lines (point-min) (point))))
1282     (setq this-line (max 1 this-line))
1283     (makefile-browser-toggle-state-for-line this-line)
1284     (goto-line this-line)
1285     (let ((inhibit-read-only t))
1286       (beginning-of-line)
1287       (if (makefile-browser-on-macro-line-p)
1288           (let ((macro-name (makefile-browser-this-line-macro-name)))
1289             (delete-region (point) (progn (end-of-line) (point)))
1290             (insert
1291              (makefile-browser-format-macro-line
1292                 macro-name
1293                 (makefile-browser-get-state-for-line this-line))))
1294         (let ((target-name (makefile-browser-this-line-target-name)))
1295           (delete-region (point) (progn (end-of-line) (point)))
1296           (insert
1297            (makefile-browser-format-target-line
1298               target-name
1299               (makefile-browser-get-state-for-line this-line))))))
1300     (beginning-of-line)
1301     (forward-char makefile-browser-cursor-column)
1302     (if makefile-browser-auto-advance-after-selection-p
1303         (makefile-browser-next-line))))
1304
1305 ;;;
1306 ;;; Making insertions into the client buffer
1307 ;;;
1308
1309 (defun makefile-browser-insert-continuation ()
1310   "Insert a makefile continuation.
1311 In the makefile buffer, go to (end-of-line), insert a \'\\\'
1312 character, insert a new blank line, go to that line and indent by one TAB.
1313 This is most useful in the process of creating continued lines when copying
1314 large dependencies from the browser to the client buffer.
1315 \(point) advances accordingly in the client buffer."
1316   (interactive)
1317   (save-excursion
1318     (set-buffer makefile-browser-client)
1319     (end-of-line)
1320     (insert "\\\n\t")))
1321
1322 (defun makefile-browser-insert-selection ()
1323   "Insert all selected targets and/or macros in the makefile buffer.
1324 Insertion takes place at point."
1325   (interactive)
1326   (save-excursion
1327     (goto-line 1)
1328     (let ((current-line 1))
1329       (while (not (eobp))
1330         (if (makefile-browser-get-state-for-line current-line)
1331             (makefile-browser-send-this-line-item))
1332         (forward-line 1)
1333         (setq current-line (1+ current-line))))))
1334
1335 (defun makefile-browser-insert-selection-and-quit ()
1336   (interactive)
1337   (makefile-browser-insert-selection)
1338   (makefile-browser-quit))
1339
1340 (defun makefile-browser-send-this-line-item ()
1341   (if (makefile-browser-on-macro-line-p)
1342       (save-excursion
1343         (let ((macro-name (makefile-browser-this-line-macro-name)))
1344           (set-buffer makefile-browser-client)
1345           (insert (makefile-format-macro-ref macro-name) " ")))
1346     (save-excursion
1347       (let ((target-name (makefile-browser-this-line-target-name)))
1348         (set-buffer makefile-browser-client)
1349         (insert target-name " ")))))
1350
1351 (defun makefile-browser-start-interaction ()
1352   (use-local-map makefile-browser-map)
1353   (setq buffer-read-only t))
1354
1355 (defun makefile-browse (targets macros)
1356   (if (zerop (+ (length targets) (length macros)))
1357       (progn
1358         (beep)
1359         (message "No macros or targets to browse! Consider running 'makefile-pickup-everything\'"))
1360     (let ((browser-buffer (get-buffer-create makefile-browser-buffer-name)))
1361         (pop-to-buffer browser-buffer)
1362         (make-variable-buffer-local 'makefile-browser-selection-vector)
1363         (makefile-browser-fill targets macros)
1364         (shrink-window-if-larger-than-buffer)
1365         (setq makefile-browser-selection-vector
1366               (make-vector (+ (length targets) (length macros)) nil))
1367         (makefile-browser-start-interaction))))
1368
1369 (defun makefile-switch-to-browser ()
1370   (interactive)
1371   (run-hooks 'makefile-browser-hook)
1372   (setq makefile-browser-client (current-buffer))
1373   (makefile-pickup-targets)
1374   (makefile-pickup-macros)
1375   (makefile-browse makefile-target-table
1376                    ;; take out the runtime macros which were added for completion sake -gk
1377                    (set-difference makefile-macro-table makefile-runtime-macros-list)))
1378
1379 \f
1380
1381 ;;; ------------------------------------------------------------
1382 ;;; Up-to-date overview buffer
1383 ;;; ------------------------------------------------------------
1384
1385 (defun makefile-create-up-to-date-overview ()
1386   "Create a buffer containing an overview of the state of all known targets.
1387 Known targets are targets that are explicitly defined in that makefile;
1388 in other words, all targets that appear on the left hand side of a
1389 dependency in the makefile."
1390   (interactive)
1391   (if (y-or-n-p "Are you sure that the makefile being edited is consistent? ")
1392       ;;
1393       ;; The rest of this function operates on a temporary makefile, created by
1394       ;; writing the current contents of the makefile buffer.
1395       ;;
1396       (let ((saved-target-table makefile-target-table)
1397             (this-buffer (current-buffer))
1398             (makefile-up-to-date-buffer
1399              (get-buffer-create makefile-up-to-date-buffer-name))
1400             (filename (makefile-save-temporary))
1401             ;;
1402             ;; Forget the target table because it may contain picked-up filenames
1403             ;; that are not really targets in the current makefile.
1404             ;; We don't want to query these, so get a new target-table with just the
1405             ;; targets that can be found in the makefile buffer.
1406             ;; The 'old' target table will be restored later.
1407             ;;
1408             (real-targets (progn
1409                             (makefile-pickup-targets)
1410                             makefile-target-table))
1411             (prereqs makefile-has-prereqs)
1412             )
1413
1414         (set-buffer makefile-up-to-date-buffer)
1415         (setq buffer-read-only nil)
1416         (erase-buffer)
1417         (makefile-query-targets filename real-targets prereqs)
1418         (if (zerop (buffer-size))               ; if it did not get us anything
1419             (progn
1420               (kill-buffer (current-buffer))
1421               (message "No overview created!")))
1422         (set-buffer this-buffer)
1423         (setq makefile-target-table saved-target-table)
1424         (if (get-buffer makefile-up-to-date-buffer-name)
1425             (progn
1426               (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name))
1427               (shrink-window-if-larger-than-buffer)
1428               (sort-lines nil (point-min) (point-max))
1429               (setq buffer-read-only t))))))
1430
1431 (defun makefile-save-temporary ()
1432   "Create a temporary file from the current makefile buffer."
1433   (let ((filename (makefile-generate-temporary-filename)))
1434     (write-region (point-min) (point-max) filename nil 0)
1435     filename))                          ; return the filename
1436
1437 (defun makefile-generate-temporary-filename ()
1438   "Create a filename suitable for use in `makefile-save-temporary'.
1439 Be careful to allow brain-dead file systems (DOS, SYSV ...) to cope
1440 with the generated name!"
1441   (let ((my-name (user-login-name))
1442         (my-uid (int-to-string (user-uid))))
1443     (concat "mktmp"
1444           (if (> (length my-name) 3)
1445               (substring my-name 0 3)
1446             my-name)
1447           "."
1448           (if (> (length my-uid) 3)
1449               (substring my-uid 0 3)
1450             my-uid))))
1451
1452 (defun makefile-query-targets (filename target-table prereq-list)
1453   "Fill the up-to-date overview buffer.
1454 Checks each target in TARGET-TABLE using `makefile-query-one-target-method'
1455 and generates the overview, one line per target name."
1456   (insert
1457    (mapconcat
1458     (function (lambda (item)
1459                 (let* ((target-name (car item))
1460                        (no-prereqs (not (member target-name prereq-list)))
1461                        (needs-rebuild (or no-prereqs
1462                                           (funcall
1463                                            makefile-query-one-target-method
1464                                            target-name
1465                                            filename))))
1466                   (format "\t%s%s"
1467                           target-name
1468                           (cond (no-prereqs "  .. has no prerequisites")
1469                                 (needs-rebuild "  .. NEEDS REBUILD")
1470                                 (t "  .. is up to date"))))
1471                 ))
1472     target-table "\n"))
1473   (goto-char (point-min))
1474   (delete-file filename))               ; remove the tmpfile
1475
1476 (defun makefile-query-by-make-minus-q (target &optional filename)
1477   (not (zerop
1478         (call-process makefile-brave-make nil nil nil
1479                       "-f" filename "-q" target))))
1480
1481 \f
1482
1483 ;;; ------------------------------------------------------------
1484 ;;; Continuation cleanup
1485 ;;; ------------------------------------------------------------
1486
1487 (defun makefile-cleanup-continuations ()
1488   (if (eq major-mode 'makefile-mode)
1489       (if (and makefile-cleanup-continuations-p
1490                (not buffer-read-only))
1491           (save-excursion
1492             (goto-char (point-min))
1493             (while (re-search-forward "\\\\[ \t]+$" (point-max) t)
1494               (replace-match "\\" t t))))))
1495
1496
1497 ;;; ------------------------------------------------------------
1498 ;;; Warn of suspicious lines
1499 ;;; ------------------------------------------------------------
1500
1501 (defun makefile-warn-suspicious-lines ()
1502   (let ((dont-save nil))
1503     (if (and (eq major-mode 'makefile-mode)
1504              makefile-warn-suspicious-lines-p)  ; -gk
1505         (let ((suspicious
1506                (save-excursion
1507                  (goto-char (point-min))
1508                  (re-search-forward
1509                   "\\(^[\t]+$\\)\\|\\(^[ ]+[\t]\\)" (point-max) t))))
1510           (if suspicious
1511               (let ((line-nr (count-lines (point-min) suspicious)))
1512                 (setq dont-save
1513                       (not (y-or-n-p
1514                             (format "Suspicious line %d. Save anyway "
1515                                     line-nr))))))))
1516     dont-save))
1517           
1518 \f
1519
1520 ;;; ------------------------------------------------------------
1521 ;;; GNU make function support
1522 ;;; ------------------------------------------------------------
1523
1524 (defun makefile-insert-gmake-function ()
1525   "Insert a GNU make function call.
1526 Asks for the name of the function to use (with completion).
1527 Then prompts for all required parameters."
1528   (interactive)
1529   (let* ((gm-function-name (completing-read
1530                              "Function: "
1531                              makefile-gnumake-functions-alist
1532                              nil t nil))
1533          (gm-function-prompts
1534           (cdr (assoc gm-function-name makefile-gnumake-functions-alist))))
1535     (if (not (zerop (length gm-function-name)))
1536         (insert (makefile-format-macro-ref
1537                  (concat gm-function-name " "
1538                          (makefile-prompt-for-gmake-funargs
1539                             gm-function-name gm-function-prompts)))
1540                 " "))))
1541
1542 (defun makefile-prompt-for-gmake-funargs (function-name prompt-list)
1543   (mapconcat
1544    (function (lambda (one-prompt)
1545                (read-string (format "[%s] %s: " function-name one-prompt)
1546                             nil)))
1547    prompt-list
1548    ","))
1549
1550 \f
1551
1552 ;;; ------------------------------------------------------------
1553 ;;; Utility functions
1554 ;;; ------------------------------------------------------------
1555
1556 (defun makefile-do-macro-insertion (macro-name)
1557   "Insert a macro reference."
1558   (if (not (zerop (length macro-name)))
1559       (if (assoc macro-name makefile-runtime-macros-list)
1560           (insert "$" macro-name)
1561         (insert (makefile-format-macro-ref macro-name)))))
1562
1563 (defun makefile-remember-target (target-name &optional has-prereqs)
1564   "Remember a given target if it is not already remembered for this buffer."
1565   (if (not (zerop (length target-name)))
1566       (progn
1567       (if (not (assoc target-name makefile-target-table))
1568           (setq makefile-target-table
1569                 (cons (list target-name) makefile-target-table)))
1570       (if has-prereqs
1571           (setq makefile-has-prereqs
1572                 (cons target-name makefile-has-prereqs))))))
1573
1574 (defun makefile-remember-macro (macro-name)
1575   "Remember a given macro if it is not already remembered for this buffer."
1576   (if (not (zerop (length macro-name)))
1577       (if (not (assoc macro-name makefile-macro-table))
1578           (setq makefile-macro-table
1579                 (cons (list macro-name) makefile-macro-table)))))
1580
1581 (defun makefile-forward-after-target-colon ()
1582   "Move point forward after inserting the terminating colon of a target.
1583 This acts according to the value of `makefile-tab-after-target-colon'."
1584   (if makefile-tab-after-target-colon
1585       (insert "\t")
1586     (insert " ")))
1587
1588 (defun makefile-browser-on-macro-line-p ()
1589   "Determine if point is on a macro line in the browser."
1590   (save-excursion
1591     (beginning-of-line)
1592     (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)))
1593
1594 (defun makefile-browser-this-line-target-name ()
1595   "Extract the target name from a line in the browser."
1596   (save-excursion
1597     (end-of-line)
1598     (skip-chars-backward "^ \t")
1599     (buffer-substring (point) (1- (makefile-end-of-line-point)))))
1600
1601 (defun makefile-browser-this-line-macro-name ()
1602   "Extract the macro name from a line in the browser."
1603   (save-excursion
1604     (beginning-of-line)
1605     (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)
1606     (let ((macro-start (point)))
1607       (skip-chars-forward "^})")
1608       (buffer-substring macro-start (point)))))
1609
1610 (defun makefile-format-macro-ref (macro-name)
1611   "Format a macro reference.
1612 Uses `makefile-use-curly-braces-for-macros-p'."
1613   (if (or (char-equal ?\( (string-to-char macro-name))
1614           (char-equal ?\{ (string-to-char macro-name)))
1615       (format "$%s" macro-name)
1616     (if makefile-use-curly-braces-for-macros-p
1617         (format "${%s}" macro-name)
1618       (format "$(%s)" macro-name))))
1619
1620 (defun makefile-browser-get-state-for-line (n)
1621   (aref makefile-browser-selection-vector (1- n)))
1622
1623 (defun makefile-browser-set-state-for-line (n to-state)
1624   (aset makefile-browser-selection-vector (1- n) to-state))
1625
1626 (defun makefile-browser-toggle-state-for-line (n)
1627   (makefile-browser-set-state-for-line n (not (makefile-browser-get-state-for-line n))))
1628
1629 (defun makefile-beginning-of-line-point ()
1630   (save-excursion
1631     (beginning-of-line)
1632     (point)))
1633
1634 (defun makefile-end-of-line-point ()
1635   (save-excursion
1636     (end-of-line)
1637     (point)))
1638
1639 (defun makefile-last-line-p ()
1640   (= (makefile-end-of-line-point) (point-max)))
1641
1642 (defun makefile-first-line-p ()
1643   (= (makefile-beginning-of-line-point) (point-min)))
1644
1645 \f
1646
1647 ;;; Support for other packages, like add-log and imenu.
1648
1649 (defun makefile-add-log-defun ()
1650   "Return name of target or variable assignment that point is in.
1651 If it isn't in one, return nil."
1652   (save-excursion
1653     (let (found)
1654       (beginning-of-line)
1655       ;; Scan back line by line, noticing when we come to a
1656       ;; variable or rule definition, and giving up when we see
1657       ;; a line that is not part of either of those.
1658       (while (not found)
1659         (cond
1660          ((looking-at makefile-macroassign-regex)
1661           (setq found (buffer-substring-no-properties (match-beginning 1)
1662                                                       (match-end 1))))
1663          ((looking-at makefile-dependency-regex)
1664           (setq found (buffer-substring-no-properties (match-beginning 1)
1665                                                       (match-end 1))))
1666          ;; Don't keep looking across a blank line or comment.  Give up.
1667          ((looking-at "$\\|#")
1668           (setq found 'bobp))
1669          ((bobp)
1670           (setq found 'bobp)))
1671         (or found
1672             (forward-line -1)))
1673       (if (stringp found) found))))
1674
1675 ;; XEmacs additions
1676 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.ma?ke?\\'" . makefile-mode))
1677 ;;;###autoload(add-to-list 'auto-mode-alist '("\\(GNU\\)?[Mm]akefile\\(\\.in\\)*\\'" . makefile-mode))
1678 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.am\\'" . makefile-mode))
1679 ;;;###autoload(add-to-list 'interpreter-mode-alist '("make" . makefile-mode))
1680
1681 ;; provide 'makefile for bug-compatibility
1682 (provide 'makefile)
1683 (provide 'make-mode)
1684 ;;; make-mode.el ends here