(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)))
+ "\\_<changequote\\_>"))
(defvar autoconf-mode-syntax-table
(let ((table (make-syntax-table)))
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)
(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 ("\\<dnl\\>" (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
--- /dev/null
+# 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
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
-;;; 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