don't require grep in vc-git
[bpt/emacs.git] / lisp / emacs-lisp / generic.el
CommitLineData
e8af40ee 1;;; generic.el --- defining simple major modes with comment and font-lock
89ada4dd 2;;
ba318903 3;; Copyright (C) 1997, 1999, 2001-2014 Free Software Foundation, Inc.
89ada4dd 4;;
6fe8a37a 5;; Author: Peter Breton <pbreton@cs.umb.edu>
89ada4dd
RS
6;; Created: Fri Sep 27 1996
7;; Keywords: generic, comment, font-lock
bd78fa1d 8;; Package: emacs
89ada4dd
RS
9
10;; This file is part of GNU Emacs.
11
d6cba7ae 12;; GNU Emacs is free software: you can redistribute it and/or modify
89ada4dd 13;; it under the terms of the GNU General Public License as published by
d6cba7ae
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
89ada4dd
RS
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
d6cba7ae 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
89ada4dd 24
89ada4dd
RS
25;;; Commentary:
26
27;; INTRODUCTION:
9aade6c6
LK
28;;
29;; The macro `define-generic-mode' can be used to define small modes
02f853b5 30;; which provide basic comment and font-lock support. These modes are
ea67f0ce
LK
31;; intended for the many configuration files and such which are too
32;; small for a "real" mode, but still have a regular syntax, comment
33;; characters and the like.
89ada4dd
RS
34;;
35;; Each generic mode can define the following:
36;;
74919c6f
LK
37;; * List of comment-characters. The elements of this list should be
38;; either a character, a one or two character string, or a cons
39;; cell. If the entry is a character or a string, it is added to
40;; the mode's syntax table with "comment starter" syntax. If the
41;; entry is a cons cell, the `car' and `cdr' of the pair are
42;; considered the "comment starter" and "comment ender"
43;; respectively. (The latter should be nil if you want comments to
44;; end at the end of the line.) Emacs does not support comment
45;; strings of more than two characters in length.
89ada4dd 46;;
9445f99b
GM
47;; * List of keywords to font-lock in `font-lock-keyword-face'.
48;; Each keyword should be a string.
9a7f629d 49;;
02f853b5 50;; * Additional expressions to font-lock. This should be a list of
ea67f0ce
LK
51;; expressions, each of which should be of the same form as those in
52;; `font-lock-keywords'.
9a7f629d 53;;
89ada4dd
RS
54;; * List of regular expressions to be placed in auto-mode-alist.
55;;
56;; * List of functions to call to do some additional setup
57;;
58;; This should pretty much cover basic functionality; if you need much
59;; more than this, or you find yourself writing extensive customizations,
60;; perhaps you should be writing a major mode instead!
61;;
9aade6c6 62;; EXAMPLE:
89ada4dd 63;;
9aade6c6 64;; You can use `define-generic-mode' like this:
89ada4dd 65;;
89ada4dd 66;; (define-generic-mode 'foo-generic-mode
ea67f0ce
LK
67;; (list ?%)
68;; (list "keyword")
69;; nil
70;; (list "\\.FOO\\'")
71;; (list 'foo-setup-function))
89ada4dd 72;;
9aade6c6 73;; to define a new generic-mode `foo-generic-mode', which has '%' as a
ea67f0ce
LK
74;; comment character, and "keyword" as a keyword. When files which
75;; end in '.FOO' are loaded, Emacs will go into foo-generic-mode and
76;; call foo-setup-function. You can also use the function
77;; `foo-generic-mode' (which is interactive) to put a buffer into
78;; foo-generic-mode.
89ada4dd 79;;
89ada4dd
RS
80;; GOTCHAS:
81;;
ea67f0ce
LK
82;; Be careful that your font-lock definitions are correct. Getting
83;; them wrong can cause Emacs to continually attempt to fontify! This
84;; problem is not specific to generic-mode.
89ada4dd 85
5027054f 86;; Credit for suggestions, brainstorming, help with debugging:
89ada4dd 87;; ACorreir@pervasive-sw.com (Alfred Correira)
9a7f629d
PB
88;; Extensive cleanup by:
89;; Stefan Monnier (monnier+gnu/emacs@flint.cs.yale.edu)
9aade6c6 90
89ada4dd
RS
91;;; Code:
92
31119d63
SM
93(eval-when-compile (require 'pcase))
94
89ada4dd 95;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
728f84dc 96;; Internal Variables
89ada4dd
RS
97;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98
e5bd0a28
SM
99(define-obsolete-variable-alias 'generic-font-lock-defaults
100 'generic-font-lock-keywords "22.1")
fbc5e359
LK
101(defvar generic-font-lock-keywords nil
102 "Keywords for `font-lock-defaults' in a generic mode.")
103(make-variable-buffer-local 'generic-font-lock-keywords)
89ada4dd 104
ea67f0ce 105;;;###autoload
9a7f629d
PB
106(defvar generic-mode-list nil
107 "A list of mode names for `generic-mode'.
25e928b0 108Do not add entries to this list directly; use `define-generic-mode'
89ada4dd
RS
109instead (which see).")
110
89ada4dd
RS
111;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
112;; Functions
113;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
114
1d96c2ff 115;;;###autoload
ea67f0ce
LK
116(defmacro define-generic-mode (mode comment-list keyword-list
117 font-lock-list auto-mode-list
7e803501 118 function-list &optional docstring)
ea67f0ce
LK
119 "Create a new generic mode MODE.
120
7e803501
LK
121MODE is the name of the command for the generic mode; don't quote it.
122The optional DOCSTRING is the documentation for the mode command. If
123you do not supply it, `define-generic-mode' uses a default
124documentation string instead.
ea67f0ce 125
7e803501
LK
126COMMENT-LIST is a list in which each element is either a character, a
127string of one or two characters, or a cons cell. A character or a
128string is set up in the mode's syntax table as a \"comment starter\".
129If the entry is a cons cell, the `car' is set up as a \"comment
130starter\" and the `cdr' as a \"comment ender\". (Use nil for the
131latter if you want comments to end at the end of the line.) Note that
132the syntax table has limitations about what comment starters and
133enders are actually possible.
ea67f0ce
LK
134
135KEYWORD-LIST is a list of keywords to highlight with
136`font-lock-keyword-face'. Each keyword should be a string.
137
7e803501
LK
138FONT-LOCK-LIST is a list of additional expressions to highlight. Each
139element of this list should have the same form as an element of
140`font-lock-keywords'.
ea67f0ce
LK
141
142AUTO-MODE-LIST is a list of regular expressions to add to
7e803501
LK
143`auto-mode-alist'. These regular expressions are added when Emacs
144runs the macro expansion.
ea67f0ce 145
7e803501
LK
146FUNCTION-LIST is a list of functions to call to do some additional
147setup. The mode command calls these functions just before it runs the
148mode hook `MODE-hook'.
c85465f9 149
02f853b5 150See the file generic-x.el for some examples of `define-generic-mode'."
7cbf93e0 151 (declare (debug (sexp def-form def-form def-form form def-form
5eee025d 152 [&optional stringp] &rest [keywordp form]))
b581bb5c
SM
153 (indent 1)
154 (doc-string 7))
66e181ad
LK
155
156 ;; Backward compatibility.
157 (when (eq (car-safe mode) 'quote)
158 (setq mode (eval mode)))
c85465f9 159
5eee025d 160 (let* ((name (symbol-name mode))
ea67f0ce 161 (pretty-name (capitalize (replace-regexp-in-string
7e803501 162 "-mode\\'" "" name))))
ea67f0ce
LK
163
164 `(progn
165 ;; Add a new entry.
5eee025d 166 (add-to-list 'generic-mode-list ,name)
ea67f0ce
LK
167
168 ;; Add it to auto-mode-alist
169 (dolist (re ,auto-mode-list)
66e181ad 170 (add-to-list 'auto-mode-alist (cons re ',mode)))
ea67f0ce 171
66e181ad 172 (defun ,mode ()
ea67f0ce
LK
173 ,(or docstring
174 (concat pretty-name " mode.\n"
7e803501
LK
175 "This a generic mode defined with `define-generic-mode'.\n"
176 "It runs `" name "-hook' as the last thing it does."))
ea67f0ce 177 (interactive)
66e181ad 178 (generic-mode-internal ',mode ,comment-list ,keyword-list
ea67f0ce 179 ,font-lock-list ,function-list)))))
89ada4dd 180
ea67f0ce 181;;;###autoload
66e181ad
LK
182(defun generic-mode-internal (mode comment-list keyword-list
183 font-lock-list function-list)
ea67f0ce 184 "Go into the generic mode MODE."
5eee025d 185 (let* ((name (symbol-name mode))
ea67f0ce 186 (pretty-name (capitalize (replace-regexp-in-string
5eee025d
LK
187 "-mode\\'" "" name)))
188 (mode-hook (intern (concat name "-hook"))))
ea67f0ce 189
89ada4dd
RS
190 (kill-all-local-variables)
191
ea67f0ce
LK
192 (setq major-mode mode
193 mode-name pretty-name)
89ada4dd 194
66e181ad 195 (generic-mode-set-comments comment-list)
89ada4dd 196
fbc5e359
LK
197 ;; Font-lock functionality.
198 ;; Font-lock-defaults is always set even if there are no keywords
89ada4dd 199 ;; or font-lock expressions, so comments can be highlighted.
9aade6c6
LK
200 (setq generic-font-lock-keywords font-lock-list)
201 (when keyword-list
202 (push (concat "\\_<" (regexp-opt keyword-list t) "\\_>")
203 generic-font-lock-keywords))
8af0371d 204 (setq font-lock-defaults '(generic-font-lock-keywords))
89ada4dd
RS
205
206 ;; Call a list of functions
ba215373 207 (mapc 'funcall function-list)
9b544de1 208
66e181ad 209 (run-mode-hooks mode-hook)))
89ada4dd
RS
210
211;;;###autoload
ea67f0ce
LK
212(defun generic-mode (mode)
213 "Enter generic mode MODE.
214
215Generic modes provide basic comment and font-lock functionality
216for \"generic\" files. (Files which are too small to warrant their
217own mode, but have comment characters, keywords, and the like.)
89ada4dd
RS
218
219To define a generic-mode, use the function `define-generic-mode'.
25e928b0 220Some generic modes are defined in `generic-x.el'."
89ada4dd 221 (interactive
ea67f0ce
LK
222 (list (completing-read "Generic mode: " generic-mode-list nil t)))
223 (funcall (intern mode)))
89ada4dd
RS
224
225;;; Comment Functionality
9a7f629d 226
4fa51741 227(defun generic--normalize-comments (comment-list)
31119d63 228 (let ((normalized '()))
9a7f629d 229 (dolist (start comment-list)
31119d63 230 (let (end)
9a7f629d
PB
231 ;; Normalize
232 (when (consp start)
ea67f0ce 233 (setq end (cdr start))
9a7f629d 234 (setq start (car start)))
93fd8eeb 235 (when (characterp start) (setq start (char-to-string start)))
0e7acedf 236 (cond
000fc2b1 237 ((characterp end) (setq end (char-to-string end)))
0e7acedf 238 ((zerop (length end)) (setq end "\n")))
31119d63
SM
239 (push (cons start end) normalized)))
240 (nreverse normalized)))
9a7f629d 241
31119d63
SM
242(defun generic-set-comment-syntax (st comment-list)
243 "Set up comment functionality for generic mode."
244 (let ((chars nil)
245 (comstyles)
246 (comstyle "")
247 (comment-start nil))
248
249 ;; Go through all the comments.
250 (pcase-dolist (`(,start . ,end) comment-list)
251 (let ((comstyle
252 ;; Reuse comstyles if necessary.
9a7f629d
PB
253 (or (cdr (assoc start comstyles))
254 (cdr (assoc end comstyles))
31119d63
SM
255 ;; Otherwise, use a style not yet in use.
256 (if (not (rassoc "" comstyles)) "")
257 (if (not (rassoc "b" comstyles)) "b")
258 "c")))
9a7f629d
PB
259 (push (cons start comstyle) comstyles)
260 (push (cons end comstyle) comstyles)
261
31119d63 262 ;; Setup the syntax table.
9a7f629d 263 (if (= (length start) 1)
31119d63 264 (modify-syntax-entry (aref start 0)
9a7f629d 265 (concat "< " comstyle) st)
31119d63
SM
266 (let ((c0 (aref start 0)) (c1 (aref start 1)))
267 ;; Store the relevant info but don't update yet.
9a7f629d
PB
268 (push (cons c0 (concat (cdr (assoc c0 chars)) "1")) chars)
269 (push (cons c1 (concat (cdr (assoc c1 chars))
270 (concat "2" comstyle))) chars)))
271 (if (= (length end) 1)
31119d63 272 (modify-syntax-entry (aref end 0)
9a7f629d 273 (concat ">" comstyle) st)
31119d63
SM
274 (let ((c0 (aref end 0)) (c1 (aref end 1)))
275 ;; Store the relevant info but don't update yet.
9a7f629d
PB
276 (push (cons c0 (concat (cdr (assoc c0 chars))
277 (concat "3" comstyle))) chars)
278 (push (cons c1 (concat (cdr (assoc c1 chars)) "4")) chars)))))
279
280 ;; Process the chars that were part of a 2-char comment marker
31119d63 281 (with-syntax-table st ;For `char-syntax'.
9a7f629d
PB
282 (dolist (cs (nreverse chars))
283 (modify-syntax-entry (car cs)
284 (concat (char-to-string (char-syntax (car cs)))
285 " " (cdr cs))
31119d63
SM
286 st)))))
287
288(defun generic-set-comment-vars (comment-list)
289 (when comment-list
290 (setq-local comment-start (caar comment-list))
291 (setq-local comment-end
292 (let ((end (cdar comment-list)))
293 (if (string-equal end "\n") "" end)))
294 (setq-local comment-start-skip
295 (concat (regexp-opt (mapcar #'car comment-list))
296 "+[ \t]*"))
297 (setq-local comment-end-skip
298 (concat "[ \t]*" (regexp-opt (mapcar #'cdr comment-list))))))
299
300(defun generic-mode-set-comments (comment-list)
301 "Set up comment functionality for generic mode."
302 (let ((st (make-syntax-table))
4fa51741 303 (comment-list (generic--normalize-comments comment-list)))
31119d63
SM
304 (generic-set-comment-syntax st comment-list)
305 (generic-set-comment-vars comment-list)
9a7f629d 306 (set-syntax-table st)))
89ada4dd 307
89ada4dd 308(defun generic-bracket-support ()
fbc5e359 309 "Imenu support for [KEYWORD] constructs found in INF, INI and Samba files."
31119d63
SM
310 (setq-local imenu-generic-expression '((nil "^\\[\\(.*\\)\\]" 1)))
311 (setq-local imenu-case-fold-search t))
89ada4dd 312
ef300cff 313;;;###autoload
9aade6c6
LK
314(defun generic-make-keywords-list (keyword-list face &optional prefix suffix)
315 "Return a `font-lock-keywords' construct that highlights KEYWORD-LIST.
316KEYWORD-LIST is a list of keyword strings that should be
317highlighted with face FACE. This function calculates a regular
318expression that matches these keywords and concatenates it with
319PREFIX and SUFFIX. Then it returns a construct based on this
320regular expression that can be used as an element of
321`font-lock-keywords'."
31119d63 322 (declare (obsolete regexp-opt "24.4"))
9aade6c6 323 (unless (listp keyword-list)
9a7f629d 324 (error "Keywords argument must be a list of strings"))
5c2137b8 325 (list (concat prefix "\\_<"
4eea26a9 326 ;; Use an optimized regexp.
9aade6c6 327 (regexp-opt keyword-list t)
5c2137b8 328 "\\_>" suffix)
4eea26a9 329 1
5204a3a0 330 face))
89ada4dd 331
8e9caa8f 332(provide 'generic)
89ada4dd 333
25e928b0 334;;; generic.el ends here