X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/f067044f476426b64f1444b82acc8d7911133550..8a9cad92b1c9eecbdc9268a885413b7c5d08c7fa:/lisp/progmodes/make-mode.el diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index a30f03a60e..c887b14496 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -1,17 +1,13 @@ -;;; 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, 2003 Free Software Foundation, Inc. ;; Author: Thomas Neumann ;; Eric S. Raymond +;; Maintainer: FSF ;; Adapted-By: ESR ;; Keywords: unix, tools -;; RMS: -;; This needs work. -;; 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 `...'. - ;; This file is part of GNU Emacs. ;; GNU Emacs is free software; you can redistribute it and/or modify @@ -25,8 +21,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: @@ -61,8 +58,7 @@ ;; ;; To Do: ;; -;; * makefile-backslash-region should be given better behavior. -;; * Consider binding C-c C-c to comment-region (like cc-mode). +;; * Add missing doc strings, improve terse doc strings. ;; * 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 @@ -88,82 +84,130 @@ ;;; 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") @@ -172,62 +216,102 @@ 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.") +(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) + ;; Variable references even in targets/strings/comments. + '("[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\|[@%\\)" + 1 font-lock-constant-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+#" makefile-space-face t) + '("^\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))) +(defconst makefile-font-lock-syntactic-keywords + ;; From sh-script.el. + ;; A `#' begins a comment in sh when it is unquoted and at the beginning + ;; of a word. In the shell, words are separated by metacharacters. + ;; The list of special chars is taken from the single-unix spec of the + ;; shell command language (under `quoting') but with `$' removed. + '(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 "_") + ;; Change the syntax of a quoted newline so that it does not end a comment. + ("\\\\\n" 0 "."))) + +(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 ;;; up-to-date overview . @@ -248,29 +332,42 @@ 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 - "Function to call to determine whether a make target is up to date. +(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.") @@ -287,6 +384,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) @@ -317,17 +415,17 @@ The function must satisfy this calling convention: () (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) @@ -338,7 +436,7 @@ The function must satisfy this calling convention: (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) @@ -384,6 +482,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") @@ -413,73 +512,76 @@ In the browser, use the following keys: 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'. \":\" 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'. 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 + (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 + 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." (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) @@ -487,22 +589,36 @@ makefile-special-targets-list: (make-local-variable 'makefile-need-macro-pickup) ;; Font lock. - (makefile-define-space-face) (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 + ((?$ . ".")) + backward-paragraph + (font-lock-syntactic-keywords . makefile-font-lock-syntactic-keywords))) ;; 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 "#") @@ -511,6 +627,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") @@ -559,7 +678,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) @@ -614,7 +733,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 @@ -648,7 +767,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."))) @@ -682,7 +801,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."))) @@ -691,15 +810,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. @@ -723,7 +842,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))) @@ -830,51 +949,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))))))) + + + +;; 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) @@ -949,7 +1167,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))) ;;; @@ -967,13 +1185,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 @@ -995,8 +1213,7 @@ 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"))) @@ -1041,11 +1258,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 () @@ -1138,7 +1354,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 @@ -1154,7 +1370,7 @@ and generates the overview, one line per target name." (delete-file filename)) ; remove the tmpfile (defun makefile-query-by-make-minus-q (target &optional filename) - (not (zerop + (not (eq 0 (call-process makefile-brave-make nil nil nil "-f" filename "-q" target)))) @@ -1166,11 +1382,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)))))) @@ -1179,21 +1395,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))))))))) ;;; ------------------------------------------------------------ @@ -1268,20 +1486,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))))) @@ -1305,76 +1523,35 @@ 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))) -;;; 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 - (or (eobp) (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))) - -(defun makefile-define-space-face () - (make-face 'makefile-space-face) - (or (face-differs-from-default-p 'makefile-space-face) - (let* ((light-bg (eq font-lock-background-mode 'light)) - (bg-color - (cond ((memq font-lock-display-type '(mono monochrome)) - (if light-bg "black" "white")) - ((memq font-lock-display-type '(grayscale greyscale - grayshade greyshade)) - (if light-bg "black" "white")) - (light-bg ; Light color background. - "hotpink") - (t ; Dark color background. - "hotpink")))) - (set-face-background 'makefile-space-face bg-color)))) - -;;; 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) + +;;; arch-tag: bd23545a-de91-44fb-b1b2-feafbb2635a0 +;;; make-mode.el ends here