Commit 92984345 authored by Daniel Pfeiffer's avatar Daniel Pfeiffer
Browse files

(makefile-dependency-regex): Turn it into a var, and refine it to mask one...

(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.
parent 228b550a
...@@ -257,26 +257,24 @@ not be enclosed in { } or ( )." ...@@ -257,26 +257,24 @@ not be enclosed in { } or ( )."
;; Note that the first big subexpression is used by font lock. Note ;; 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 ;; that if you change this regexp you might have to fix the imenu
;; index in makefile-imenu-generic-expression. ;; index in makefile-imenu-generic-expression.
(defconst makefile-dependency-regex (defvar makefile-dependency-regex
;; Allow for one nested level $($(var:a=b):c=d) ;; Allow for two nested levels $(v1:$(v2:$(v3:a=b)=c)=d)
;; Scan non-$ constructs char by char, so we don't miss a $ while allowing $$. "^ *\\(\\(?: *\\$\\(?:[({]\\(?:\\$\\(?:[({]\\(?:\\$\\(?:[^({]\\|.[^\n$#})]+?[})]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\|[^\n$#)}]\\)+?[})]\\|[^({]\\)\\| *[^ \n$#:=]+\\)+?\\)[ \t]*\\(:\\)\\(?:[ \t]*$\\|[^=\n]\\(?:[^#\n]*?;[ \t]*\\(.+\\)\\)?\\)"
;; 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].*$\\)"
"Regex used to find dependency lines in a makefile.") "Regex used to find dependency lines in a makefile.")
(defconst makefile-rule-action-regex (defvar makefile-rule-action-regex
"^\t[ \t]*\\([-@]*\\)[ \t]*\\(.+\\)" "^\t[ \t]*\\([-@]*\\)[ \t]*\\(\\(?:.+\\\\\n\\)*.+\\)"
"Regex used to highlight rule action lines in font lock mode.") "Regex used to highlight rule action lines in font lock mode.")
;; Note that the first and second subexpression is used by font lock. Note ;; 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 ;; that if you change this regexp you might have to fix the imenu index in
;; makefile-imenu-generic-expression. ;; makefile-imenu-generic-expression.
(defconst makefile-macroassign-regex (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.") "Regex used to find macro assignment lines in a makefile.")
(defconst makefile-var-use-regex (defconst makefile-var-use-regex
"[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\|[@%<?^+*][FD]?\\)[}):]" "[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\|[@%<?^+*][FD]?\\)"
"Regex used to find $(macro) uses in a makefile.") "Regex used to find $(macro) uses in a makefile.")
(defconst makefile-ignored-files-in-pickup-regex (defconst makefile-ignored-files-in-pickup-regex
...@@ -323,7 +321,7 @@ not be enclosed in { } or ( )." ...@@ -323,7 +321,7 @@ not be enclosed in { } or ( )."
".endif" ".for" ".if" ".ifdef" ".ifmake" ".ifndef" ".ifnmake" ".undef") ".endif" ".for" ".if" ".ifdef" ".ifmake" ".ifndef" ".ifnmake" ".undef")
"List of keywords understood by BSD make.") "List of keywords understood by BSD make.")
(defun makefile-make-font-lock-keywords (dependency action var keywords space (defun makefile-make-font-lock-keywords (var keywords space
&optional negation &optional negation
&rest font-lock-keywords) &rest font-lock-keywords)
`(;; Do macro assignments. These get the "variable-name" face. `(;; Do macro assignments. These get the "variable-name" face.
...@@ -332,11 +330,8 @@ not be enclosed in { } or ( )." ...@@ -332,11 +330,8 @@ not be enclosed in { } or ( )."
;; This is for after != ;; This is for after !=
(2 'makefile-shell-face prepend t)) (2 'makefile-shell-face prepend t))
;; Do dependencies.
(,dependency 1 'makefile-targets-face prepend)
;; Rule actions. ;; Rule actions.
(,action (makefile-match-action
(1 font-lock-type-face) (1 font-lock-type-face)
(2 'makefile-shell-face prepend) (2 'makefile-shell-face prepend)
;; Only makepp has builtin commands. ;; Only makepp has builtin commands.
...@@ -359,9 +354,9 @@ not be enclosed in { } or ( )." ...@@ -359,9 +354,9 @@ not be enclosed in { } or ( )."
"\\>[ \t]*\\([^: \t\n#]*\\)") "\\>[ \t]*\\([^: \t\n#]*\\)")
(1 font-lock-keyword-face) (2 font-lock-variable-name-face)) (1 font-lock-keyword-face) (2 font-lock-variable-name-face))
,(if negation ,@(if negation
`(,negation (1 font-lock-negation-char-face prepend) `((,negation (1 font-lock-negation-char-face prepend)
(2 font-lock-negation-char-face prepend t))) (2 font-lock-negation-char-face prepend t))))
,@(if space ,@(if space
'(;; Highlight lines that contain just whitespace. '(;; Highlight lines that contain just whitespace.
...@@ -376,28 +371,27 @@ not be enclosed in { } or ( )." ...@@ -376,28 +371,27 @@ not be enclosed in { } or ( )."
;; They can make a tab fail to be effective. ;; They can make a tab fail to be effective.
("^\\( +\\)\t" 1 makefile-space-face))) ("^\\( +\\)\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 (defconst makefile-font-lock-keywords
(makefile-make-font-lock-keywords (makefile-make-font-lock-keywords
makefile-dependency-regex
makefile-rule-action-regex
makefile-var-use-regex makefile-var-use-regex
makefile-statements makefile-statements
t)) t))
(defconst makefile-automake-font-lock-keywords (defconst makefile-automake-font-lock-keywords
(makefile-make-font-lock-keywords (makefile-make-font-lock-keywords
makefile-dependency-regex
makefile-rule-action-regex
makefile-var-use-regex makefile-var-use-regex
makefile-automake-statements makefile-automake-statements
t)) t))
(defconst makefile-gmake-font-lock-keywords (defconst makefile-gmake-font-lock-keywords
(makefile-make-font-lock-keywords (makefile-make-font-lock-keywords
makefile-dependency-regex
makefile-rule-action-regex
makefile-var-use-regex makefile-var-use-regex
makefile-gmake-statements makefile-gmake-statements
t t
...@@ -407,8 +401,8 @@ not be enclosed in { } or ( )." ...@@ -407,8 +401,8 @@ not be enclosed in { } or ( )."
1 'makefile-targets-face prepend) 1 'makefile-targets-face prepend)
;; $(function ...) ${function ...} ;; $(function ...) ${function ...}
'("[^$]\\$[({]\\(\\S +\\s \\)" '("[^$]\\$[({]\\([-a-zA-Z0-9_.]+\\s \\)"
1 font-lock-function-name-face) 1 font-lock-function-name-face prepend)
;; $(shell ...) ${shell ...} ;; $(shell ...) ${shell ...}
'("[^$]\\$\\([({]\\)shell[ \t]+" '("[^$]\\$\\([({]\\)shell[ \t]+"
...@@ -417,24 +411,23 @@ not be enclosed in { } or ( )." ...@@ -417,24 +411,23 @@ not be enclosed in { } or ( )."
(defconst makefile-makepp-font-lock-keywords (defconst makefile-makepp-font-lock-keywords
(makefile-make-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-var-use-regex
makefile-makepp-statements makefile-makepp-statements
nil nil
"^\\(?: [ \t]*\\)?\\(?:and[ \t]+\\|else[ \t]+\\|or[ \t]+\\)?if\\(n\\)\\(?:def\\|eq\\|sys\\)\\>" "^\\(?: [ \t]*\\)?\\(?:and[ \t]+\\|else[ \t]+\\|or[ \t]+\\)?if\\(n\\)\\(?:def\\|eq\\|sys\\)\\>"
'("[^$]\\(\\$[({]\\(?:target\\|output\\)s?[})]\\)" '("[^$]\\(\\$[({]\\(?:target\\|output\\)s?\\_>.*?[})]\\)"
1 'makefile-targets-face prepend) 1 'makefile-targets-face prepend)
;; Colon modifier keywords. ;; Colon modifier keywords.
'(":\\s *\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>" '("\\(:\\s *\\)\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>\\([^:\n]*\\)"
1 font-lock-keyword-face t) (1 font-lock-type-face t)
(2 font-lock-keyword-face t)
(3 font-lock-variable-name-face t))
;; $(function ...) $((function ...)) ${function ...} ${{function ...}} ;; $(function ...) $((function ...)) ${function ...} ${{function ...}}
'("[^$]\\$\\(((?\\|{{?\\)\\(\\S +\\s \\)" '("[^$]\\$\\(?:((?\\|{{?\\)\\([-a-zA-Z0-9_.]+\\s \\)"
2 font-lock-function-name-face) 1 font-lock-function-name-face prepend)
;; $(shell ...) $((shell ...)) ${shell ...} ${{shell ...}} ;; $(shell ...) $((shell ...)) ${shell ...} ${{shell ...}}
'("[^$]\\$\\(((?\\|{{?\\)shell\\(?:[-_]\\(?:global[-_]\\)?once\\)?[ \t]+" '("[^$]\\$\\(((?\\|{{?\\)shell\\(?:[-_]\\(?:global[-_]\\)?once\\)?[ \t]+"
...@@ -466,11 +459,9 @@ not be enclosed in { } or ( )." ...@@ -466,11 +459,9 @@ not be enclosed in { } or ( )."
'("perl[-_]begin\\s *\\(?:\\s #.*\\)?\n\\(\\(?:.*\n\\)+?\\)\\s *perl[-_]end\\>" '("perl[-_]begin\\s *\\(?:\\s #.*\\)?\n\\(\\(?:.*\n\\)+?\\)\\s *perl[-_]end\\>"
1 'makefile-makepp-perl-face t))) 1 'makefile-makepp-perl-face t)))
;; A lot more could be done for variables here:
(defconst makefile-bsdmake-font-lock-keywords (defconst makefile-bsdmake-font-lock-keywords
(makefile-make-font-lock-keywords (makefile-make-font-lock-keywords
"^ *\\(\\(?:\\$[({]\\(?:\\$[({][^\n#})]+?[})]\\|[^\n#]\\)+?[})]\\|[^\n#:=]\\)+?\\)[ \t]*[:!]\\(?:[ \t]*$\\|[^=\n].*$\\)" ;; A lot more could be done for variables here:
"^\t[ \t]*\\([-+@]*\\)[ \t]*\\(.+\\)"
makefile-var-use-regex makefile-var-use-regex
makefile-bsdmake-statements makefile-bsdmake-statements
t t
...@@ -489,9 +480,8 @@ not be enclosed in { } or ( )." ...@@ -489,9 +480,8 @@ not be enclosed in { } or ( )."
("\\\\\n" 0 "."))) ("\\\\\n" 0 ".")))
(defvar makefile-imenu-generic-expression (defvar makefile-imenu-generic-expression
(list `(("Dependencies" ,makefile-dependency-regex 1)
(list "Dependencies" makefile-dependency-regex 1) ("Macro Assignment" ,makefile-macroassign-regex 1))
(list "Macro Assignment" makefile-macroassign-regex 1))
"Imenu generic expression for Makefile mode. See `imenu-generic-expression'.") "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 ...@@ -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) it is *.mk. This function ends by invoking the function(s)
`makefile-mode-hook'. `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} \\{makefile-mode-map}
In the browser, use the following keys: In the browser, use the following keys:
...@@ -849,14 +844,26 @@ Makefile mode can be configured by modifying the following variables: ...@@ -849,14 +844,26 @@ Makefile mode can be configured by modifying the following variables:
;;;###autoload ;;;###autoload
(define-derived-mode makefile-makepp-mode makefile-mode "Makeppfile" (define-derived-mode makefile-makepp-mode makefile-mode "Makeppfile"
"An adapted `makefile-mode' that knows about makepp." "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 (setq font-lock-defaults
`(makefile-makepp-font-lock-keywords ,@(cdr font-lock-defaults)))) `(makefile-makepp-font-lock-keywords ,@(cdr font-lock-defaults))))
;;;###autoload ;;;###autoload
(define-derived-mode makefile-bsdmake-mode makefile-mode "BSDmakefile" (define-derived-mode makefile-bsdmake-mode makefile-mode "BSDmakefile"
"An adapted `makefile-mode' that knows about BSD make." "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 (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: ...@@ -867,7 +874,7 @@ Makefile mode can be configured by modifying the following variables:
(interactive) (interactive)
(let ((here (point))) (let ((here (point)))
(end-of-line) (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 (progn (beginning-of-line) t) ; indicate success
(goto-char here) nil))) (goto-char here) nil)))
...@@ -876,7 +883,7 @@ Makefile mode can be configured by modifying the following variables: ...@@ -876,7 +883,7 @@ Makefile mode can be configured by modifying the following variables:
(interactive) (interactive)
(let ((here (point))) (let ((here (point)))
(beginning-of-line) (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 (progn (beginning-of-line) t) ; indicate success
(goto-char here) nil))) (goto-char here) nil)))
...@@ -983,7 +990,7 @@ Anywhere else just self-inserts." ...@@ -983,7 +990,7 @@ Anywhere else just self-inserts."
(setq makefile-has-prereqs nil) (setq makefile-has-prereqs nil)
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(while (re-search-forward makefile-dependency-regex nil t) (while (makefile-match-dependency nil)
(makefile-add-this-line-targets))) (makefile-add-this-line-targets)))
(message "Read targets OK."))) (message "Read targets OK.")))
...@@ -1676,6 +1683,23 @@ The anchor must have matched the opening parens in the first group." ...@@ -1676,6 +1683,23 @@ The anchor must have matched the opening parens in the first group."
((string= s "{{") "\\(.*?\\)[ \t]*}}"))) ((string= s "{{") "\\(.*?\\)[ \t]*}}")))
(if s (looking-at s)))) (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) (defun makefile-do-macro-insertion (macro-name)
"Insert a macro reference." "Insert a macro reference."
(if (not (zerop (length macro-name))) (if (not (zerop (length macro-name)))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment