1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
;;; m4-mode.el --- m4 code editing commands for Emacs -*- lexical-binding:t -*-
;; Copyright (C) 1996-1997, 2001-2025 Free Software Foundation, Inc.
;; Author: Andrew Csillag <drew@thecsillags.com>
;; Keywords: languages, faces
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; 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:
;; A smart editing mode for m4 macro definitions. It seems to have most of the
;; syntax right (sexp motion commands work, but function motion commands don't).
;; It also sets the font-lock syntax stuff for colorization
;; To Do's:
;; * want to make m4-m4-(buffer|region) look sorta like M-x compile look&feel ?
;; * sexp motion commands don't seem to work right
;;; Thanks:
;;; to Akim Demaille and Terry Jones for the bug reports
;;; to Simon Marshall for the regexp tip
;;; to Martin Buchholz for some general fixes
;;; Code:
(defgroup m4 nil
"m4 code editing commands for Emacs."
:link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
:prefix "m4-"
:group 'languages)
(defcustom m4-program "m4"
"File name of the m4 executable.
If m4 is not in your PATH, set this to an absolute file name."
:version "24.4"
:type 'file)
;;options to m4
(defcustom m4-program-options nil
"Options to pass to `m4-program'."
:type '(repeat string))
;;to use --prefix-builtins, you can use
;;(defconst m4-program-options '("-P"))
;;or
;;(defconst m4-program-options '("--prefix-builtins"))
;; Needed at compile-time for `m4-font-lock-keywords' below.
(eval-and-compile
(defconst m4--macro-list
;; From (info "(m4) Macro index")
'("__file__" "__gnu__" "__line__" "__os2__" "__program__" "__unix__"
"__windows__" "argn" "array" "array_set" "builtin" "capitalize"
"changecom" "changequote" "changeword" "cleardivert" "cond" "copy"
"curry" "debugfile" "debugmode" "decr" "define" "define_blind"
"defn" "divert" "divnum" "dnl" "downcase" "dquote" "dquote_elt"
"dumpdef" "errprint" "esyscmd" "eval" "example" "exch"
"fatal_error" "file" "foreach" "foreachq" "forloop" "format" "gnu"
"ifdef" "ifelse" "include" "incr" "index" "indir" "join" "joinall"
"len" "line" "m4exit" "m4wrap" "maketemp" "mkstemp" "nargs" "os2"
"patsubst" "popdef" "pushdef" "quote" "regexp" "rename" "reverse"
"shift" "sinclude" "stack_foreach" "stack_foreach_lifo"
"stack_foreach_sep" "stack_foreach_sep_lifo" "substr" "syscmd"
"sysval" "traceoff" "traceon" "translit" "undefine" "undivert"
"unix" "upcase" "windows")
"List of valid m4 macros for M4 mode."))
(defvar m4-font-lock-keywords
(eval-when-compile
`(("\\(\\_<\\(m4_\\)?dnl\\_>\\).*$" (0 font-lock-comment-face t))
("\\$[*#@0-9]" . font-lock-variable-name-face)
("\\$@" . font-lock-variable-name-face)
("\\$\\*" . font-lock-variable-name-face)
(,(concat "\\_<\\(m4_\\)?" (regexp-opt m4--macro-list) "\\_>")
. font-lock-keyword-face)))
"Default `font-lock-keywords' for M4 mode.")
(defcustom m4-mode-hook nil
"Hook called by `m4-mode'."
:type 'hook)
;;this may still need some work
(defvar m4-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?` "('" table)
(modify-syntax-entry ?' ")`" table)
(modify-syntax-entry ?# "<\n" table)
(modify-syntax-entry ?\n ">#" table)
(modify-syntax-entry ?{ "." table)
(modify-syntax-entry ?} "." table)
(modify-syntax-entry ?_ "_" table)
(modify-syntax-entry ?* "." table)
(modify-syntax-entry ?\" "." table)
table)
"Syntax table used while in `m4-mode'.")
(defun m4--quoted-p (pos)
"Return non-nil if POS is inside a quoted string."
(let ((quoted nil))
(dolist (o (nth 9 (save-excursion (syntax-ppss pos))))
(if (eq (char-after o) ?\`) (setq quoted t)))
quoted))
(defconst m4-syntax-propertize
(syntax-propertize-rules
("#" (0 (when (m4--quoted-p (match-beginning 0))
(string-to-syntax "."))))))
(defvar-keymap m4-mode-map
:doc "Keymap for M4 Mode."
"C-c C-b" #'m4-m4-buffer
"C-c C-r" #'m4-m4-region
"C-c C-c" #'comment-region)
(easy-menu-define m4-mode-menu m4-mode-map
"Menu for M4 Mode."
'("M4"
["M4 Region" m4-m4-region
:help "Send contents of the current region to m4"]
["M4 Buffer" m4-m4-buffer
:help "Send contents of the current buffer to m4"]
["Comment Region" comment-region
:help "Comment Region"]))
(defun m4-m4-buffer ()
"Send contents of the current buffer to m4."
(interactive)
(shell-command-on-region
(point-min) (point-max)
(mapconcat #'identity (cons m4-program m4-program-options) "\s")
"*m4-output*" nil)
(switch-to-buffer-other-window "*m4-output*"))
(defun m4-m4-region ()
"Send contents of the current region to m4."
(interactive)
(shell-command-on-region
(point) (mark)
(mapconcat #'identity (cons m4-program m4-program-options) "\s")
"*m4-output*" nil)
(switch-to-buffer-other-window "*m4-output*"))
(defun m4-current-defun-name ()
"Return the name of the M4 function at point, or nil."
(save-excursion
(if (re-search-backward
"^\\(\\(m4_\\)?define\\|A._DEFUN\\)(\\[?\\([A-Za-z0-9_]+\\)" nil t)
(match-string-no-properties 3))))
;;;###autoload
(define-derived-mode m4-mode prog-mode "m4"
"A major mode to edit m4 macro files."
(setq-local comment-start "#")
(setq-local parse-sexp-ignore-comments t)
(setq-local add-log-current-defun-function #'m4-current-defun-name)
(setq-local syntax-propertize-function m4-syntax-propertize)
(setq-local font-lock-defaults '(m4-font-lock-keywords nil)))
(provide 'm4-mode)
;;stuff to play with for debugging
;(char-to-string (char-syntax ?`))
;;; m4-mode.el ends here
|