]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/generic.el
Derive secrets-mode from special-mode
[gnu-emacs] / lisp / emacs-lisp / generic.el
1 ;;; generic.el --- defining simple major modes with comment and font-lock
2 ;;
3 ;; Copyright (C) 1997, 1999, 2001-2016 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Peter Breton <pbreton@cs.umb.edu>
6 ;; Created: Fri Sep 27 1996
7 ;; Keywords: generic, comment, font-lock
8 ;; Package: emacs
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
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
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; INTRODUCTION:
28 ;;
29 ;; The macro `define-generic-mode' can be used to define small modes
30 ;; which provide basic comment and font-lock support. These modes are
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.
34 ;;
35 ;; Each generic mode can define the following:
36 ;;
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.
46 ;;
47 ;; * List of keywords to font-lock in `font-lock-keyword-face'.
48 ;; Each keyword should be a string.
49 ;;
50 ;; * Additional expressions to font-lock. This should be a list of
51 ;; expressions, each of which should be of the same form as those in
52 ;; `font-lock-keywords'.
53 ;;
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 ;;
62 ;; EXAMPLE:
63 ;;
64 ;; You can use `define-generic-mode' like this:
65 ;;
66 ;; (define-generic-mode 'foo-generic-mode
67 ;; (list ?%)
68 ;; (list "keyword")
69 ;; nil
70 ;; (list "\\.FOO\\'")
71 ;; (list 'foo-setup-function))
72 ;;
73 ;; to define a new generic-mode `foo-generic-mode', which has '%' as a
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.
79 ;;
80 ;; GOTCHAS:
81 ;;
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.
85
86 ;; Credit for suggestions, brainstorming, help with debugging:
87 ;; ACorreir@pervasive-sw.com (Alfred Correira)
88 ;; Extensive cleanup by:
89 ;; Stefan Monnier (monnier+gnu/emacs@flint.cs.yale.edu)
90
91 ;;; Code:
92
93 (eval-when-compile (require 'pcase))
94
95 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
96 ;; Internal Variables
97 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98
99 (define-obsolete-variable-alias 'generic-font-lock-defaults
100 'generic-font-lock-keywords "22.1")
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)
104
105 ;;;###autoload
106 (defvar generic-mode-list nil
107 "A list of mode names for `generic-mode'.
108 Do not add entries to this list directly; use `define-generic-mode'
109 instead (which see).")
110
111 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
112 ;; Functions
113 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
114
115 ;;;###autoload
116 (defmacro define-generic-mode (mode comment-list keyword-list
117 font-lock-list auto-mode-list
118 function-list &optional docstring)
119 "Create a new generic mode MODE.
120
121 MODE is the name of the command for the generic mode; don't quote it.
122 The optional DOCSTRING is the documentation for the mode command. If
123 you do not supply it, `define-generic-mode' uses a default
124 documentation string instead.
125
126 COMMENT-LIST is a list in which each element is either a character, a
127 string of one or two characters, or a cons cell. A character or a
128 string is set up in the mode's syntax table as a \"comment starter\".
129 If the entry is a cons cell, the `car' is set up as a \"comment
130 starter\" and the `cdr' as a \"comment ender\". (Use nil for the
131 latter if you want comments to end at the end of the line.) Note that
132 the syntax table has limitations about what comment starters and
133 enders are actually possible.
134
135 KEYWORD-LIST is a list of keywords to highlight with
136 `font-lock-keyword-face'. Each keyword should be a string.
137
138 FONT-LOCK-LIST is a list of additional expressions to highlight. Each
139 element of this list should have the same form as an element of
140 `font-lock-keywords'.
141
142 AUTO-MODE-LIST is a list of regular expressions to add to
143 `auto-mode-alist'. These regular expressions are added when Emacs
144 runs the macro expansion.
145
146 FUNCTION-LIST is a list of functions to call to do some additional
147 setup. The mode command calls these functions just before it runs the
148 mode hook `MODE-hook'.
149
150 See the file generic-x.el for some examples of `define-generic-mode'."
151 (declare (debug (sexp def-form def-form def-form form def-form
152 [&optional stringp] &rest [keywordp form]))
153 (indent 1)
154 (doc-string 7))
155
156 ;; Backward compatibility.
157 (when (eq (car-safe mode) 'quote)
158 (setq mode (eval mode)))
159
160 (let* ((name (symbol-name mode))
161 (pretty-name (capitalize (replace-regexp-in-string
162 "-mode\\'" "" name))))
163
164 `(progn
165 ;; Add a new entry.
166 (add-to-list 'generic-mode-list ,name)
167
168 ;; Add it to auto-mode-alist
169 (dolist (re ,auto-mode-list)
170 (add-to-list 'auto-mode-alist (cons re ',mode)))
171
172 (defun ,mode ()
173 ,(or docstring
174 (concat pretty-name " mode.\n"
175 "This a generic mode defined with `define-generic-mode'.\n"
176 "It runs `" name "-hook' as the last thing it does."))
177 (interactive)
178 (generic-mode-internal ',mode ,comment-list ,keyword-list
179 ,font-lock-list ,function-list)))))
180
181 ;;;###autoload
182 (defun generic-mode-internal (mode comment-list keyword-list
183 font-lock-list function-list)
184 "Go into the generic mode MODE."
185 (let* ((name (symbol-name mode))
186 (pretty-name (capitalize (replace-regexp-in-string
187 "-mode\\'" "" name)))
188 (mode-hook (intern (concat name "-hook"))))
189
190 (kill-all-local-variables)
191
192 (setq major-mode mode
193 mode-name pretty-name)
194
195 (generic-mode-set-comments comment-list)
196
197 ;; Font-lock functionality.
198 ;; Font-lock-defaults is always set even if there are no keywords
199 ;; or font-lock expressions, so comments can be highlighted.
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))
204 (setq font-lock-defaults '(generic-font-lock-keywords))
205
206 ;; Call a list of functions
207 (mapc 'funcall function-list)
208
209 (run-mode-hooks mode-hook)))
210
211 ;;;###autoload
212 (defun generic-mode (mode)
213 "Enter generic mode MODE.
214
215 Generic modes provide basic comment and font-lock functionality
216 for \"generic\" files. (Files which are too small to warrant their
217 own mode, but have comment characters, keywords, and the like.)
218
219 To define a generic-mode, use the function `define-generic-mode'.
220 Some generic modes are defined in `generic-x.el'."
221 (interactive
222 (list (completing-read "Generic mode: " generic-mode-list nil t)))
223 (funcall (intern mode)))
224
225 ;;; Comment Functionality
226
227 (defun generic--normalize-comments (comment-list)
228 (let ((normalized '()))
229 (dolist (start comment-list)
230 (let (end)
231 ;; Normalize
232 (when (consp start)
233 (setq end (cdr start))
234 (setq start (car start)))
235 (when (characterp start) (setq start (char-to-string start)))
236 (cond
237 ((characterp end) (setq end (char-to-string end)))
238 ((zerop (length end)) (setq end "\n")))
239 (push (cons start end) normalized)))
240 (nreverse normalized)))
241
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.
253 (or (cdr (assoc start comstyles))
254 (cdr (assoc end comstyles))
255 ;; Otherwise, use a style not yet in use.
256 (if (not (rassoc "" comstyles)) "")
257 (if (not (rassoc "b" comstyles)) "b")
258 "c")))
259 (push (cons start comstyle) comstyles)
260 (push (cons end comstyle) comstyles)
261
262 ;; Setup the syntax table.
263 (if (= (length start) 1)
264 (modify-syntax-entry (aref start 0)
265 (concat "< " comstyle) st)
266 (let ((c0 (aref start 0)) (c1 (aref start 1)))
267 ;; Store the relevant info but don't update yet.
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)
272 (modify-syntax-entry (aref end 0)
273 (concat ">" comstyle) st)
274 (let ((c0 (aref end 0)) (c1 (aref end 1)))
275 ;; Store the relevant info but don't update yet.
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
281 (with-syntax-table st ;For `char-syntax'.
282 (dolist (cs (nreverse chars))
283 (modify-syntax-entry (car cs)
284 (concat (char-to-string (char-syntax (car cs)))
285 " " (cdr cs))
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))
303 (comment-list (generic--normalize-comments comment-list)))
304 (generic-set-comment-syntax st comment-list)
305 (generic-set-comment-vars comment-list)
306 (set-syntax-table st)))
307
308 (defun generic-bracket-support ()
309 "Imenu support for [KEYWORD] constructs found in INF, INI and Samba files."
310 (setq-local imenu-generic-expression '((nil "^\\[\\(.*\\)\\]" 1)))
311 (setq-local imenu-case-fold-search t))
312
313 ;;;###autoload
314 (defun generic-make-keywords-list (keyword-list face &optional prefix suffix)
315 "Return a `font-lock-keywords' construct that highlights KEYWORD-LIST.
316 KEYWORD-LIST is a list of keyword strings that should be
317 highlighted with face FACE. This function calculates a regular
318 expression that matches these keywords and concatenates it with
319 PREFIX and SUFFIX. Then it returns a construct based on this
320 regular expression that can be used as an element of
321 `font-lock-keywords'."
322 (declare (obsolete regexp-opt "24.4"))
323 (unless (listp keyword-list)
324 (error "Keywords argument must be a list of strings"))
325 (list (concat prefix "\\_<"
326 ;; Use an optimized regexp.
327 (regexp-opt keyword-list t)
328 "\\_>" suffix)
329 1
330 face))
331
332 (provide 'generic)
333
334 ;;; generic.el ends here