From: Basil L. Contovounesios Date: Wed, 29 Jan 2025 13:05:39 +0000 (+0100) Subject: Improve autoconf-mode macro detection X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=04b13f1994feb8a0969e95b6e18cdd1b5dec2d77;p=emacs.git Improve autoconf-mode macro detection * doc/lispref/modes.texi (Search-based Fontification): Fix indentation of (MATCHER . FACESPEC) example. * doc/misc/cc-mode.texi (Performance Issues): Index defun-prompt-regexp under variables, not functions. * lisp/progmodes/autoconf.el (autoconf--symbol, autoconf--macro): New rx definitions. (autoconf-definition-regexp): Use an optional second capture group to indicate a function rather than variable definition. Detect AC_DEFINE defining a function-like CPP macro. Skip more shell syntax such as variable ${} expansion and command `` substitution in AC_DEFINE_UNQUOTED variable. Match AH_VERBATIM, AM_CONDITIONAL, and AM_MISSING_PROG as defining variables, and AC_DEFUN, AC_DEFUN_ONCE, AU_ALIAS, and AU_DEFUN as defining functions. Document first capture group in docstring. (autoconf-font-lock-keywords): Use autoconf--macro to match more Autoconf macros, such as those defined in the Autoconf Archive and Gnulib. Reserve font-lock-function-name-face for function definitions as determined by autoconf-definition-regexp, and use font-lock-variable-name-face for the rest instead. Use Font Lock face symbols directly in place of their corresponding variable. Fontify M4 changequote primitive only as a standalone symbol. (autoconf-imenu-generic-expression): Add commentary mentioning new submenu possibility. (autoconf-current-defun-function): Update docstring accuracy. Replace line-end-position with pos-eol since there are no fields. (autoconf-mode): Define defun-prompt-regexp in terms of autoconf--macro to support more toplevel macros, such as those defined in Autoheader, M4sh, etc. Set open-paren-in-column-0-is-defun-start to nil to avoid false positives when an Autoconf quote character is in column zero. * test/lisp/progmodes/autoconf-resources/configure.ac: New file. * test/lisp/progmodes/autoconf-tests.el (autoconf-tests-current-defun-function-define) (autoconf-tests-current-defun-function-subst): Replace character motion with search. (autoconf-tests-autoconf-mode-comment-syntax): Ditto. Test both dnl and # comments. Use syntax-ppss-context. (autoconf-tests-font-lock): New test. (cherry picked from commit 2e3cf73e055be69d42a6f84436d400caf0997abb) --- diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 1ada6a4cf64..e9833c301af 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -3384,7 +3384,7 @@ However, @var{facespec} can also evaluate to a list of this form: @example (@var{subexp} -(face @var{face} @var{prop1} @var{val1} @var{prop2} @var{val2}@dots{})) + (face @var{face} @var{prop1} @var{val1} @var{prop2} @var{val2}@dots{})) @end example @noindent diff --git a/doc/misc/cc-mode.texi b/doc/misc/cc-mode.texi index 3f460f3bdb9..665cf8e41a8 100644 --- a/doc/misc/cc-mode.texi +++ b/doc/misc/cc-mode.texi @@ -7531,7 +7531,7 @@ caches syntactic information much better, so that the delay caused by searching for such a brace when it's not in column 0 is minimal, except perhaps when you've just moved a long way inside the file. -@findex defun-prompt-regexp +@vindex defun-prompt-regexp @vindex c-Java-defun-prompt-regexp @vindex Java-defun-prompt-regexp @r{(c-)} A special note about @code{defun-prompt-regexp} in Java mode: The common diff --git a/lisp/progmodes/autoconf.el b/lisp/progmodes/autoconf.el index 8cee38cf033..c5b076bfacc 100644 --- a/lisp/progmodes/autoconf.el +++ b/lisp/progmodes/autoconf.el @@ -40,16 +40,58 @@ (defvar autoconf-mode-hook nil "Hook run by `autoconf-mode'.") +(rx-define autoconf--symbol (+ (| (syntax word) (syntax symbol)))) + +;; Any Autoconf macro name. +(rx-define autoconf--macro + (: (| "AC" ;; Autoconf. + "AH" ;; Autoheader. + "AM" ;; Automake. + "AS" ;; M4sh. + "AU" ;; Autoupdate. + "AX" ;; Autoconf Archive. + "LT" ;; Libtool. + "gl") ;; Gnulib. + ?_ autoconf--symbol)) + (defconst autoconf-definition-regexp - "A\\(?:H_TEMPLATE\\|C_\\(?:SUBST\\|DEFINE\\(?:_UNQUOTED\\)?\\)\\)(\\[*\\(\\(?:\\sw\\|\\s_\\)+\\)\\]*") + ;; Historically this `defconst' defined only group #1. + ;; For internal Font Lock use, the presence of an optional group #2 + ;; identifies a function rather than variable definition. + (rx-let ((argbeg (: ?\( (* ?\[))) + (argend (in "]),")) + (plaindef (: argbeg (group-n 1 autoconf--symbol)))) + (rx symbol-start + (| (: "AC_DEFINE" + ;; AC_DEFINE and AC_DEFINE_UNQUOTED can define object- and + ;; function-like CPP macros. An open-paren is easy to + ;; detect in the case of AC_DEFINE. Doing the same for + ;; AC_DEFINE_UNQUOTED in the general case requires + ;; knowledge of shell syntax, so don't bother for now. + (| (: plaindef (? (group-n 2 ?\())) + (: "_UNQUOTED" argbeg (group-n 1 (+ (not argend)))))) + (: (| "AC_SUBST" + "AH_TEMPLATE" + "AH_VERBATIM" + "AM_CONDITIONAL" + "AM_MISSING_PROG" + (group-n 2 (| "AC_DEFUN" + "AC_DEFUN_ONCE" + "AU_ALIAS" + "AU_DEFUN"))) + plaindef)))) + "Matches Autoconf macro calls that define something. +The thing being defined is captured in the first subexpression group.") (defvar autoconf-font-lock-keywords - `(("\\_<\\(?:A[CHMS]\\|LT\\)_\\(?:\\sw\\|\\s_\\)+" . font-lock-keyword-face) + `(,(rx symbol-start autoconf--macro) (,autoconf-definition-regexp - 1 font-lock-function-name-face) + 1 (if (match-beginning 2) + 'font-lock-function-name-face + 'font-lock-variable-name-face)) ;; Are any other M4 keywords really appropriate for configure.ac, ;; given that we do `dnl'? - ("changequote" . font-lock-keyword-face))) + "\\_")) (defvar autoconf-mode-syntax-table (let ((table (make-syntax-table))) @@ -59,15 +101,17 @@ table)) (defvar autoconf-imenu-generic-expression + ;; This lists both variable-like and function-like definitions in a + ;; flat list, but they could be distinguished if desired. (list (list nil autoconf-definition-regexp 1))) ;; It's not clear how best to implement this. (defun autoconf-current-defun-function () "Function to use for `add-log-current-defun-function' in Autoconf mode. -This version looks back for an AC_DEFINE or AC_SUBST. It will stop -searching backwards at another AC_... command." +This version looks back for a definition such as by AC_DEFINE or +AC_SUBST. It stops searching when it encounters other Autoconf macros." (save-excursion - (skip-syntax-forward "w_" (line-end-position)) + (skip-syntax-forward "w_" (pos-eol)) (if (re-search-backward autoconf-definition-regexp (save-excursion (beginning-of-defun) (point)) t) @@ -77,14 +121,15 @@ searching backwards at another AC_... command." (define-derived-mode autoconf-mode prog-mode "Autoconf" "Major mode for editing Autoconf configure.ac files." (setq-local parens-require-spaces nil) ; for M4 arg lists - (setq-local defun-prompt-regexp "^[ \t]*A[CM]_\\(\\sw\\|\\s_\\)+") + ;; FIXME: Should indented macro calls really count as defuns? + (setq-local defun-prompt-regexp (rx bol (* (in "\t ")) autoconf--macro)) + (setq-local open-paren-in-column-0-is-defun-start nil) (setq-local comment-start "dnl ") ;; We want to avoid matching "dnl" in other text. (setq-local comment-start-skip "\\(?:\\(\\W\\|^\\)dnl\\|#\\) +") (setq-local syntax-propertize-function (syntax-propertize-rules ("\\" (0 "<")))) - (setq-local font-lock-defaults - '(autoconf-font-lock-keywords nil nil)) + (setq-local font-lock-defaults '(autoconf-font-lock-keywords)) (setq-local imenu-generic-expression autoconf-imenu-generic-expression) (setq-local indent-line-function #'indent-relative) (setq-local add-log-current-defun-function diff --git a/test/lisp/progmodes/autoconf-resources/configure.ac b/test/lisp/progmodes/autoconf-resources/configure.ac new file mode 100644 index 00000000000..b05c556f029 --- /dev/null +++ b/test/lisp/progmodes/autoconf-resources/configure.ac @@ -0,0 +1,172 @@ +# Indentation. + +AC_PROG_CC +dnl <- font-lock-keyword-face + AC_PROG_CC + dnl <- font-lock-keyword-face + AC_PROG_CC + dnl <- font-lock-keyword-face + +# Quoting. + + [AC_PROG_CC] [[AC_PREREQ([2.70])]] +dnl ^^^^^^^^^^ ^^^^^^^^^ font-lock-keyword-face + +# Nesting. + +AS_VAR_IF([foo], [bar], [AC_MSG_FAILURE([baz])]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^ font-lock-keyword-face +AS_VAR_IF([foo], [bar], +dnl <- font-lock-keyword-face + [AC_MSG_FAILURE([baz])]) +dnl ^^^^^^^^^^^^^^ font-lock-keyword-face + +# Autoconf. + +AC_PROG_CC AC_PREREQ(2.70) AC_PREREQ([2.70]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^ ^^^^^^^^^ font-lock-keyword-face + +# Autoheader. + +AH_HEADER AH_TOP(foo) AH_TOP([foo]) +dnl <- font-lock-keyword-face +dnl ^^^^^^ ^^^^^^ font-lock-keyword-face + +# Automake. + +AM_PATH_LISPDIR AM_SILENT_RULES(yes) AM_SILENT_RULES([yes]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ font-lock-keyword-face + +# M4sh. + +AS_INIT AS_ECHO(foo) AS_ECHO([foo]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^ ^^^^^^^ font-lock-keyword-face + +# Autoconf Archive. + +AX_ADD_FORTIFY_SOURCE AX_SAVE_FLAGS(foo) AX_SAVE_FLAGS([foo]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ font-lock-keyword-face + +# Libtool. + +LT_OUTPUT LT_PREREQ(2.4.6) LT_PREREQ([2.4.6]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^ ^^^^^^^^^ font-lock-keyword-face + +# Gnulib. + +gl_EARLY gl_WARN_ADD(foo) gl_WARN_ADD([foo]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^ ^^^^^^^^^^^ font-lock-keyword-face + +# M4. + +changequote(<<, >>) m4_changequote(<<, >>) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^ !font-lock-keyword-face +changequote([, ]) m4_changequote([, ]) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^ !font-lock-keyword-face + +# AC_DEFINE object-like macro. + +AC_DEFINE(a) AC_DEFINE(a, d) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE([a]) AC_DEFINE([a], d) +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE([[a]]) AC_DEFINE([[a]], d) +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE(bc) AC_DEFINE(bc, d) +dnl ^^ ^^ font-lock-variable-name-face +AC_DEFINE([bc]) AC_DEFINE([bc], d) +dnl ^^ ^^ font-lock-variable-name-face +AC_DEFINE([[bc]]) AC_DEFINE([[bc]], d) +dnl ^^ ^^ font-lock-variable-name-face + +# AC_DEFINE function-like macro. + +AC_DEFINE(a()) AC_DEFINE(a(), d) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([a()]) AC_DEFINE([a()], d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([[a()]]) AC_DEFINE([[a()]], d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE(a(x)) AC_DEFINE(a(x), d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([a(x)]) AC_DEFINE([a(x)], d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([[a(x)]]) AC_DEFINE([[a(x)]], d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE(a(x, y)) AC_DEFINE(a(x, y), d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([a(x, y)]) AC_DEFINE([a(x, y)], d) +dnl ^ ^ font-lock-function-name-face +AC_DEFINE([[a(x, y)]]) AC_DEFINE([[a(x, y)]], d) +dnl ^ ^ font-lock-function-name-face + +# AC_DEFINE_UNQUOTED object-like macro. + +AC_DEFINE_UNQUOTED(a) AC_DEFINE_UNQUOTED(a, d) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([a]) AC_DEFINE_UNQUOTED([a], d) +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([[a]]) AC_DEFINE_UNQUOTED([[a]], d) +dnl ^ ^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED(bc) AC_DEFINE_UNQUOTED(bc, d) +dnl ^^ ^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([bc]) AC_DEFINE_UNQUOTED([bc], d) +dnl ^^ ^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([[bc]]) AC_DEFINE_UNQUOTED([[bc]], d) +dnl ^^ ^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED(\a`b`$c${d}) +dnl ^^^^^^^^^^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([\a`b`$c${d}]) +dnl ^^^^^^^^^^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([[\a`b`$c${d}]]) +dnl ^^^^^^^^^^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED(\a`b`$c${d}, e) +dnl ^^^^^^^^^^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([\a`b`$c${d}], e) +dnl ^^^^^^^^^^^ font-lock-variable-name-face +AC_DEFINE_UNQUOTED([[\a`b`$c${d}]], e) +dnl ^^^^^^^^^^^ font-lock-variable-name-face + +# Variable definition. + +AC_SUBST(a) AC_SUBST(a, b) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-variable-name-face +AH_TEMPLATE(a, b) AH_VERBATIM(a, b) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^ font-lock-keyword-face +dnl ^ ^font-lock-variable-name-face +AM_CONDITIONAL(a, b) AM_MISSING_PROG(a, b) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-variable-name-face + +# Function definition. + +AC_DEFUN(a) AC_DEFUN(a, b) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-function-name-face +AC_DEFUN_ONCE(a, b) +dnl <- font-lock-keyword-face +dnl ^ font-lock-function-name-face +AU_ALIAS(a, b) AU_DEFUN(a, b) +dnl <- font-lock-keyword-face +dnl ^^^^^^^^ font-lock-keyword-face +dnl ^ ^ font-lock-function-name-face diff --git a/test/lisp/progmodes/autoconf-tests.el b/test/lisp/progmodes/autoconf-tests.el index d4c99218fa0..df20986fc52 100644 --- a/test/lisp/progmodes/autoconf-tests.el +++ b/test/lisp/progmodes/autoconf-tests.el @@ -20,36 +20,44 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . -;;; Commentary: - -;; - ;;; Code: (require 'autoconf) (require 'ert) +(require 'ert-font-lock) (ert-deftest autoconf-tests-current-defun-function-define () (with-temp-buffer + (autoconf-mode) (insert "AC_DEFINE([HAVE_RSVG], [1], [Define to 1 if using librsvg.])") + (let ((def "HAVE_RSVG")) + (search-backward def) + (should (equal (autoconf-current-defun-function) def))) (goto-char (point-min)) - (should-not (autoconf-current-defun-function)) - (forward-char 11) - (should (equal (autoconf-current-defun-function) "HAVE_RSVG")))) + (should-not (autoconf-current-defun-function)))) (ert-deftest autoconf-tests-current-defun-function-subst () (with-temp-buffer + (autoconf-mode) (insert "AC_SUBST([srcdir])") + (let ((def "srcdir")) + (search-backward def) + (should (equal (autoconf-current-defun-function) "srcdir"))) (goto-char (point-min)) - (should-not (autoconf-current-defun-function)) - (forward-char 10) - (should (equal (autoconf-current-defun-function) "srcdir")))) + (should-not (autoconf-current-defun-function)))) (ert-deftest autoconf-tests-autoconf-mode-comment-syntax () (with-temp-buffer (autoconf-mode) - (insert "dnl Autoconf script for GNU Emacs") - (should (nth 4 (syntax-ppss))))) + (dolist (start '("dnl" "#")) + (insert start " Autoconf script for GNU Emacs") + (should (eq (syntax-ppss-context (syntax-ppss)) 'comment)) + (insert "\n") + (should-not (syntax-ppss-context (syntax-ppss)))))) + +(ert-font-lock-deftest-file autoconf-tests-font-lock + "Test `autoconf-mode' font lock." + autoconf-mode "configure.ac") (provide 'autoconf-tests) ;;; autoconf-tests.el ends here