From 92984345bcd7d07d6527de73f4e66af5b7386d64 Mon Sep 17 00:00:00 2001 From: Daniel Pfeiffer Date: Mon, 16 May 2005 20:13:09 +0000 Subject: [PATCH] (makefile-dependency-regex): Turn it into a var, and refine it to mask one more level of nested vars. (makefile-rule-action-regex): Turn it into a var, and refine it so it recognizes backslashed continuation lines as belonging to the same command. (makefile-macroassign-regex): Refine it so it recognizes backslashed continuation lines as belonging to the same command. (makefile-var-use-regex): Don't look at the next char, because it might be the same one to be skipped by the initial [^$], leading to an overlooked variable use. (makefile-make-font-lock-keywords): Remove two parameters, which are now variables that some of the modes set locally. Handle dependency and rule action matching through functions, because regexps alone match too often. Dependency matching now comes last, so it can check, whether a colon already matched something else. (makefile-mode): Inform that font-lock improves makefile parsing capabilities. (makefile-match-dependency, makefile-match-action): New functions. --- lisp/progmodes/make-mode.el | 116 ++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 46 deletions(-) diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index bc30bfdfc56..8467310d1da 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -257,26 +257,24 @@ not be enclosed in { } or ( )." ;; Note that the first big 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-dependency-regex - ;; Allow for one nested level $($(var:a=b):c=d) - ;; Scan non-$ constructs char by char, so we don't miss a $ while allowing $$. - ;; Since we must allow space between targets, I then don't know how to evite a command starting with :. - "^ *\\(\\(?:\\$[({]\\(?:\\$[({][^ \t\n#})]+?[})]\\|[^\n#]\\)+?[})]\\|[^\n#:=]\\)+?\\)[ \t]*:\\(?:[ \t]*$\\|[^=\n].*$\\)" +(defvar makefile-dependency-regex + ;; Allow for two nested levels $(v1:$(v2:$(v3:a=b)=c)=d) + "^ *\\(\\(?: *\\$\\(?:[({]\\(?:\\$\\(?:[({]\\(?:\\$\\(?:[^({]\\|.[^\n$#})]+?[})]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\| *[^ \n$#:=]+\\)+?\\)[ \t]*\\(:\\)\\(?:[ \t]*$\\|[^=\n]\\(?:[^#\n]*?;[ \t]*\\(.+\\)\\)?\\)" "Regex used to find dependency lines in a makefile.") -(defconst makefile-rule-action-regex - "^\t[ \t]*\\([-@]*\\)[ \t]*\\(.+\\)" +(defvar makefile-rule-action-regex + "^\t[ \t]*\\([-@]*\\)[ \t]*\\(\\(?:.+\\\\\n\\)*.+\\)" "Regex used to highlight rule action lines in font lock mode.") ;; Note that the first and second 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]*\\(?:!=[ \t]*\\(.*\\)\\|[*:+]?[:?]?=\\)" + "^ *\\([^ \n\t][^:#= \t\n]*\\)[ \t]*\\(?:!=[ \t]*\\(\\(?:.+\\\\\n\\)*.+\\)\\|[*:+]?[:?]?=\\)" "Regex used to find macro assignment lines in a makefile.") (defconst makefile-var-use-regex - "[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\|[@%[ \t]*\\([^: \t\n#]*\\)") (1 font-lock-keyword-face) (2 font-lock-variable-name-face)) - ,(if negation - `(,negation (1 font-lock-negation-char-face prepend) - (2 font-lock-negation-char-face prepend t))) + ,@(if negation + `((,negation (1 font-lock-negation-char-face prepend) + (2 font-lock-negation-char-face prepend t)))) ,@(if space '(;; Highlight lines that contain just whitespace. @@ -376,28 +371,27 @@ not be enclosed in { } or ( )." ;; They can make a tab fail to be effective. ("^\\( +\\)\t" 1 makefile-space-face))) - ,@font-lock-keywords)) + ,@font-lock-keywords + + ;; Do dependencies. + (makefile-match-dependency + (1 'makefile-targets-face prepend) + (3 'makefile-shell-face prepend t)))) (defconst makefile-font-lock-keywords (makefile-make-font-lock-keywords - makefile-dependency-regex - makefile-rule-action-regex makefile-var-use-regex makefile-statements t)) (defconst makefile-automake-font-lock-keywords (makefile-make-font-lock-keywords - makefile-dependency-regex - makefile-rule-action-regex makefile-var-use-regex makefile-automake-statements t)) (defconst makefile-gmake-font-lock-keywords (makefile-make-font-lock-keywords - makefile-dependency-regex - makefile-rule-action-regex makefile-var-use-regex makefile-gmake-statements t @@ -407,8 +401,8 @@ not be enclosed in { } or ( )." 1 'makefile-targets-face prepend) ;; $(function ...) ${function ...} - '("[^$]\\$[({]\\(\\S +\\s \\)" - 1 font-lock-function-name-face) + '("[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\s \\)" + 1 font-lock-function-name-face prepend) ;; $(shell ...) ${shell ...} '("[^$]\\$\\([({]\\)shell[ \t]+" @@ -417,24 +411,23 @@ not be enclosed in { } or ( )." (defconst makefile-makepp-font-lock-keywords (makefile-make-font-lock-keywords - makefile-dependency-regex - ;; Don't care about initial tab, but I don't know how to font-lock correctly without. - "^\t[ \t]*\\(\\(?:[ \t]*noecho\\>\\|[ \t]*ignore[-_]error\\>\\|[ \t]*[-@]+\\)*\\)[ \t]*\\(\\(&\\S +\\)?.+\\)" makefile-var-use-regex makefile-makepp-statements nil "^\\(?: [ \t]*\\)?\\(?:and[ \t]+\\|else[ \t]+\\|or[ \t]+\\)?if\\(n\\)\\(?:def\\|eq\\|sys\\)\\>" - '("[^$]\\(\\$[({]\\(?:target\\|output\\)s?[})]\\)" + '("[^$]\\(\\$[({]\\(?:target\\|output\\)s?\\_>.*?[})]\\)" 1 'makefile-targets-face prepend) ;; Colon modifier keywords. - '(":\\s *\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>" - 1 font-lock-keyword-face t) + '("\\(:\\s *\\)\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>\\([^:\n]*\\)" + (1 font-lock-type-face t) + (2 font-lock-keyword-face t) + (3 font-lock-variable-name-face t)) ;; $(function ...) $((function ...)) ${function ...} ${{function ...}} - '("[^$]\\$\\(((?\\|{{?\\)\\(\\S +\\s \\)" - 2 font-lock-function-name-face) + '("[^$]\\$\\(?:((?\\|{{?\\)\\([-a-zA-Z0-9_.]+\\s \\)" + 1 font-lock-function-name-face prepend) ;; $(shell ...) $((shell ...)) ${shell ...} ${{shell ...}} '("[^$]\\$\\(((?\\|{{?\\)shell\\(?:[-_]\\(?:global[-_]\\)?once\\)?[ \t]+" @@ -466,11 +459,9 @@ not be enclosed in { } or ( )." '("perl[-_]begin\\s *\\(?:\\s #.*\\)?\n\\(\\(?:.*\n\\)+?\\)\\s *perl[-_]end\\>" 1 'makefile-makepp-perl-face t))) -;; A lot more could be done for variables here: (defconst makefile-bsdmake-font-lock-keywords (makefile-make-font-lock-keywords - "^ *\\(\\(?:\\$[({]\\(?:\\$[({][^\n#})]+?[})]\\|[^\n#]\\)+?[})]\\|[^\n#:=]\\)+?\\)[ \t]*[:!]\\(?:[ \t]*$\\|[^=\n].*$\\)" - "^\t[ \t]*\\([-+@]*\\)[ \t]*\\(.+\\)" + ;; A lot more could be done for variables here: makefile-var-use-regex makefile-bsdmake-statements t @@ -489,9 +480,8 @@ not be enclosed in { } or ( )." ("\\\\\n" 0 "."))) (defvar makefile-imenu-generic-expression - (list - (list "Dependencies" makefile-dependency-regex 1) - (list "Macro Assignment" makefile-macroassign-regex 1)) + `(("Dependencies" ,makefile-dependency-regex 1) + ("Macro Assignment" ,makefile-macroassign-regex 1)) "Imenu generic expression for Makefile mode. See `imenu-generic-expression'.") ;;; ------------------------------------------------------------ @@ -695,6 +685,11 @@ last should be correctly chosen based on the file name, except if it is *.mk. This function ends by invoking the function(s) `makefile-mode-hook'. +It is strongly recommended to use `font-lock-mode', because that +provides additional parsing information. This is used for +example to see that a rule action `echo foo: bar' is a not rule +dependency, despite the colon. + \\{makefile-mode-map} In the browser, use the following keys: @@ -849,14 +844,26 @@ Makefile mode can be configured by modifying the following variables: ;;;###autoload (define-derived-mode makefile-makepp-mode makefile-mode "Makeppfile" "An adapted `makefile-mode' that knows about makepp." + (set (make-local-variable 'makefile-rule-action-regex) + ;; Don't care about initial tab, but I don't know how to font-lock correctly without. + "^\t[ \t]*\\(\\(?:\\(?:noecho\\|ignore[-_]error\\|[-@]+\\)[ \t]*\\)*\\)\\(\\(&\\S +\\)?\\(?:.+\\\\\n\\)*.+\\)") + (setq font-lock-defaults `(makefile-makepp-font-lock-keywords ,@(cdr font-lock-defaults)))) ;;;###autoload (define-derived-mode makefile-bsdmake-mode makefile-mode "BSDmakefile" "An adapted `makefile-mode' that knows about BSD make." + (set (make-local-variable 'makefile-dependency-regex) + ;; Identical to default, except allows `!' instead of `:'. + "^ *\\(\\(?: *\\$\\(?:[({]\\(?:\\$\\(?:[({]\\(?:\\$\\(?:[^({]\\|.[^\n$#})]+?[})]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\| *[^ \n$#:=]+\\)+?\\)[ \t]*\\([:!]\\)\\(?:[ \t]*$\\|[^=\n]\\(?:[^#\n]*?;[ \t]*\\(.+\\)\\)?\\)") + (set (make-local-variable 'makefile-rule-action-regex) + "^\t[ \t]*\\([-+@]*\\)[ \t]*\\(\\(?:.+\\\\\n\\)*.+\\)") (setq font-lock-defaults - `(makefile-bsdmake-font-lock-keywords ,@(cdr font-lock-defaults)))) + `(makefile-bsdmake-font-lock-keywords ,@(cdr font-lock-defaults)) + imenu-generic-expression + `(("Dependencies" ,makefile-dependency-regex 1) + ,@(cdr imenu-generic-expression)))) @@ -867,7 +874,7 @@ Makefile mode can be configured by modifying the following variables: (interactive) (let ((here (point))) (end-of-line) - (if (re-search-forward makefile-dependency-regex (point-max) t) + (if (makefile-match-dependency (point-max)) (progn (beginning-of-line) t) ; indicate success (goto-char here) nil))) @@ -876,7 +883,7 @@ Makefile mode can be configured by modifying the following variables: (interactive) (let ((here (point))) (beginning-of-line) - (if (re-search-backward makefile-dependency-regex (point-min) t) + (if (makefile-match-dependency (point-min) t) (progn (beginning-of-line) t) ; indicate success (goto-char here) nil))) @@ -983,7 +990,7 @@ Anywhere else just self-inserts." (setq makefile-has-prereqs nil) (save-excursion (goto-char (point-min)) - (while (re-search-forward makefile-dependency-regex nil t) + (while (makefile-match-dependency nil) (makefile-add-this-line-targets))) (message "Read targets OK."))) @@ -1676,6 +1683,23 @@ The anchor must have matched the opening parens in the first group." ((string= s "{{") "\\(.*?\\)[ \t]*}}"))) (if s (looking-at s)))) +(defun makefile-match-dependency (bound &optional backward) + "Search for `makefile-dependency-regex' up to BOUND. +Optionally search BACKWARD. +Checks that the colon has not already been fontified, else we +matched in a rule action." + (catch 'found + (while (funcall (if backward 're-search-backward 're-search-forward) + makefile-dependency-regex bound t) + (or (get-text-property (match-beginning 2) 'face) + (throw 'found t))))) + +(defun makefile-match-action (bound) + (catch 'found + (while (re-search-forward makefile-rule-action-regex bound t) + (or (eq ?\\ (char-after (- (match-beginning 0) 2))) + (throw 'found t))))) + (defun makefile-do-macro-insertion (macro-name) "Insert a macro reference." (if (not (zerop (length macro-name))) -- 2.39.2