Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / emacs-lisp / generic.el
CommitLineData
e8af40ee 1;;; generic.el --- defining simple major modes with comment and font-lock
89ada4dd 2;;
73b0cd50 3;; Copyright (C) 1997, 1999, 2001-2011 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;;
02f853b5 47;; * List of keywords to font-lock. Each keyword should be a string.
ea67f0ce
LK
48;; If you have additional keywords which should be highlighted in a
49;; face different from `font-lock-keyword-face', you can use the
50;; convenience function `generic-make-keywords-list' (which see),
51;; and add the result to the following list:
9a7f629d 52;;
02f853b5 53;; * Additional expressions to font-lock. This should be a list of
ea67f0ce
LK
54;; expressions, each of which should be of the same form as those in
55;; `font-lock-keywords'.
9a7f629d 56;;
89ada4dd
RS
57;; * List of regular expressions to be placed in auto-mode-alist.
58;;
59;; * List of functions to call to do some additional setup
60;;
61;; This should pretty much cover basic functionality; if you need much
62;; more than this, or you find yourself writing extensive customizations,
63;; perhaps you should be writing a major mode instead!
64;;
9aade6c6 65;; EXAMPLE:
89ada4dd 66;;
9aade6c6 67;; You can use `define-generic-mode' like this:
89ada4dd 68;;
89ada4dd 69;; (define-generic-mode 'foo-generic-mode
ea67f0ce
LK
70;; (list ?%)
71;; (list "keyword")
72;; nil
73;; (list "\\.FOO\\'")
74;; (list 'foo-setup-function))
89ada4dd 75;;
9aade6c6 76;; to define a new generic-mode `foo-generic-mode', which has '%' as a
ea67f0ce
LK
77;; comment character, and "keyword" as a keyword. When files which
78;; end in '.FOO' are loaded, Emacs will go into foo-generic-mode and
79;; call foo-setup-function. You can also use the function
80;; `foo-generic-mode' (which is interactive) to put a buffer into
81;; foo-generic-mode.
89ada4dd 82;;
89ada4dd
RS
83;; GOTCHAS:
84;;
ea67f0ce
LK
85;; Be careful that your font-lock definitions are correct. Getting
86;; them wrong can cause Emacs to continually attempt to fontify! This
87;; problem is not specific to generic-mode.
89ada4dd 88
5027054f 89;; Credit for suggestions, brainstorming, help with debugging:
89ada4dd 90;; ACorreir@pervasive-sw.com (Alfred Correira)
9a7f629d
PB
91;; Extensive cleanup by:
92;; Stefan Monnier (monnier+gnu/emacs@flint.cs.yale.edu)
9aade6c6 93
89ada4dd
RS
94;;; Code:
95
96;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
728f84dc 97;; Internal Variables
89ada4dd
RS
98;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
99
fbc5e359
LK
100(defvar generic-font-lock-keywords nil
101 "Keywords for `font-lock-defaults' in a generic mode.")
102(make-variable-buffer-local 'generic-font-lock-keywords)
bda2c1ff 103(define-obsolete-variable-alias 'generic-font-lock-defaults 'generic-font-lock-keywords "22.1")
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]))
7cbf93e0 153 (indent 1))
66e181ad
LK
154
155 ;; Backward compatibility.
156 (when (eq (car-safe mode) 'quote)
157 (setq mode (eval mode)))
c85465f9 158
5eee025d 159 (let* ((name (symbol-name mode))
ea67f0ce 160 (pretty-name (capitalize (replace-regexp-in-string
7e803501 161 "-mode\\'" "" name))))
ea67f0ce
LK
162
163 `(progn
164 ;; Add a new entry.
5eee025d 165 (add-to-list 'generic-mode-list ,name)
ea67f0ce
LK
166
167 ;; Add it to auto-mode-alist
168 (dolist (re ,auto-mode-list)
66e181ad 169 (add-to-list 'auto-mode-alist (cons re ',mode)))
ea67f0ce 170
66e181ad 171 (defun ,mode ()
ea67f0ce
LK
172 ,(or docstring
173 (concat pretty-name " mode.\n"
7e803501
LK
174 "This a generic mode defined with `define-generic-mode'.\n"
175 "It runs `" name "-hook' as the last thing it does."))
ea67f0ce 176 (interactive)
66e181ad 177 (generic-mode-internal ',mode ,comment-list ,keyword-list
ea67f0ce 178 ,font-lock-list ,function-list)))))
89ada4dd 179
ea67f0ce 180;;;###autoload
66e181ad
LK
181(defun generic-mode-internal (mode comment-list keyword-list
182 font-lock-list function-list)
ea67f0ce 183 "Go into the generic mode MODE."
5eee025d 184 (let* ((name (symbol-name mode))
ea67f0ce 185 (pretty-name (capitalize (replace-regexp-in-string
5eee025d
LK
186 "-mode\\'" "" name)))
187 (mode-hook (intern (concat name "-hook"))))
ea67f0ce 188
89ada4dd
RS
189 (kill-all-local-variables)
190
ea67f0ce
LK
191 (setq major-mode mode
192 mode-name pretty-name)
89ada4dd 193
66e181ad 194 (generic-mode-set-comments comment-list)
89ada4dd 195
fbc5e359
LK
196 ;; Font-lock functionality.
197 ;; Font-lock-defaults is always set even if there are no keywords
89ada4dd 198 ;; or font-lock expressions, so comments can be highlighted.
9aade6c6
LK
199 (setq generic-font-lock-keywords font-lock-list)
200 (when keyword-list
201 (push (concat "\\_<" (regexp-opt keyword-list t) "\\_>")
202 generic-font-lock-keywords))
8af0371d 203 (setq font-lock-defaults '(generic-font-lock-keywords))
89ada4dd
RS
204
205 ;; Call a list of functions
ba215373 206 (mapc 'funcall function-list)
9b544de1 207
66e181ad 208 (run-mode-hooks mode-hook)))
89ada4dd
RS
209
210;;;###autoload
ea67f0ce
LK
211(defun generic-mode (mode)
212 "Enter generic mode MODE.
213
214Generic modes provide basic comment and font-lock functionality
215for \"generic\" files. (Files which are too small to warrant their
216own mode, but have comment characters, keywords, and the like.)
89ada4dd
RS
217
218To define a generic-mode, use the function `define-generic-mode'.
25e928b0 219Some generic modes are defined in `generic-x.el'."
89ada4dd 220 (interactive
ea67f0ce
LK
221 (list (completing-read "Generic mode: " generic-mode-list nil t)))
222 (funcall (intern mode)))
89ada4dd
RS
223
224;;; Comment Functionality
225(defun generic-mode-set-comments (comment-list)
226 "Set up comment functionality for generic mode."
9a7f629d
PB
227 (let ((st (make-syntax-table))
228 (chars nil)
229 (comstyles))
ea67f0ce
LK
230 (make-local-variable 'comment-start)
231 (make-local-variable 'comment-start-skip)
232 (make-local-variable 'comment-end)
9a7f629d
PB
233
234 ;; Go through all the comments
235 (dolist (start comment-list)
ea67f0ce 236 (let (end (comstyle ""))
9a7f629d
PB
237 ;; Normalize
238 (when (consp start)
ea67f0ce 239 (setq end (cdr start))
9a7f629d 240 (setq start (car start)))
93fd8eeb 241 (when (characterp start) (setq start (char-to-string start)))
0e7acedf 242 (cond
000fc2b1 243 ((characterp end) (setq end (char-to-string end)))
0e7acedf 244 ((zerop (length end)) (setq end "\n")))
9a7f629d
PB
245
246 ;; Setup the vars for `comment-region'
247 (if comment-start
248 ;; We have already setup a comment-style, so use style b
249 (progn
250 (setq comstyle "b")
251 (setq comment-start-skip
252 (concat comment-start-skip "\\|" (regexp-quote start) "+\\s-*")))
253 ;; First comment-style
254 (setq comment-start start)
1ac1fa96 255 (setq comment-end (if (string-equal end "\n") "" end))
9a7f629d
PB
256 (setq comment-start-skip (concat (regexp-quote start) "+\\s-*")))
257
258 ;; Reuse comstyles if necessary
259 (setq comstyle
260 (or (cdr (assoc start comstyles))
261 (cdr (assoc end comstyles))
262 comstyle))
263 (push (cons start comstyle) comstyles)
264 (push (cons end comstyle) comstyles)
265
266 ;; Setup the syntax table
267 (if (= (length start) 1)
268 (modify-syntax-entry (string-to-char start)
269 (concat "< " comstyle) st)
270 (let ((c0 (elt start 0)) (c1 (elt start 1)))
271 ;; Store the relevant info but don't update yet
272 (push (cons c0 (concat (cdr (assoc c0 chars)) "1")) chars)
273 (push (cons c1 (concat (cdr (assoc c1 chars))
274 (concat "2" comstyle))) chars)))
275 (if (= (length end) 1)
276 (modify-syntax-entry (string-to-char end)
277 (concat ">" comstyle) st)
278 (let ((c0 (elt end 0)) (c1 (elt end 1)))
279 ;; Store the relevant info but don't update yet
280 (push (cons c0 (concat (cdr (assoc c0 chars))
281 (concat "3" comstyle))) chars)
282 (push (cons c1 (concat (cdr (assoc c1 chars)) "4")) chars)))))
283
284 ;; Process the chars that were part of a 2-char comment marker
285 (dolist (cs (nreverse chars))
286 (modify-syntax-entry (car cs)
287 (concat (char-to-string (char-syntax (car cs)))
288 " " (cdr cs))
289 st))
290 (set-syntax-table st)))
89ada4dd 291
89ada4dd 292(defun generic-bracket-support ()
fbc5e359 293 "Imenu support for [KEYWORD] constructs found in INF, INI and Samba files."
25e928b0 294 (setq imenu-generic-expression
c0b08eb0
DL
295 '((nil "^\\[\\(.*\\)\\]" 1))
296 imenu-case-fold-search t))
89ada4dd 297
ef300cff 298;;;###autoload
9aade6c6
LK
299(defun generic-make-keywords-list (keyword-list face &optional prefix suffix)
300 "Return a `font-lock-keywords' construct that highlights KEYWORD-LIST.
301KEYWORD-LIST is a list of keyword strings that should be
302highlighted with face FACE. This function calculates a regular
303expression that matches these keywords and concatenates it with
304PREFIX and SUFFIX. Then it returns a construct based on this
305regular expression that can be used as an element of
306`font-lock-keywords'."
307 (unless (listp keyword-list)
9a7f629d 308 (error "Keywords argument must be a list of strings"))
5c2137b8 309 (list (concat prefix "\\_<"
4eea26a9 310 ;; Use an optimized regexp.
9aade6c6 311 (regexp-opt keyword-list t)
5c2137b8 312 "\\_>" suffix)
4eea26a9 313 1
5204a3a0 314 face))
89ada4dd 315
8e9caa8f 316(provide 'generic)
89ada4dd 317
25e928b0 318;;; generic.el ends here