34 |
|
|
35 |
;; Generic-mode is a meta-mode which can be used to define small modes |
;; Generic-mode is a meta-mode which can be used to define small modes |
36 |
;; which provide basic comment and font-lock support. These modes are |
;; which provide basic comment and font-lock support. These modes are |
37 |
;; intended for the many configuration files and such which are too small |
;; intended for the many configuration files and such which are too |
38 |
;; for a "real" mode, but still have a regular syntax, comment characters |
;; small for a "real" mode, but still have a regular syntax, comment |
39 |
;; and the like. |
;; characters and the like. |
40 |
;; |
;; |
41 |
;; Each generic mode can define the following: |
;; Each generic mode can define the following: |
42 |
;; |
;; |
43 |
;; * List of comment-characters. The entries in this list should be |
;; * List of comment-characters. The entries in this list should be |
44 |
;; either a character, a one or two character string or a cons pair. |
;; either a character, a one or two character string or a cons pair. |
45 |
;; If the entry is a character or a one-character string |
;; If the entry is a character or a string, it is added to the |
46 |
;; LIMITATIONS: Emacs does not support comment strings of more than |
;; mode's syntax table with `comment-start' syntax. If the entry is |
47 |
|
;; a cons pair, the elements of the pair are considered to be |
48 |
|
;; `comment-start' and `comment-end' respectively. (The latter |
49 |
|
;; should be nil if you want comments to end at end of line.) |
50 |
|
;; LIMITATIONS: Emacs does not support comment strings of more than |
51 |
;; two characters in length. |
;; two characters in length. |
52 |
;; |
;; |
53 |
;; * List of keywords to font-lock. Each keyword should be a string. |
;; * List of keywords to font-lock. Each keyword should be a string. |
54 |
;; If you have additional keywords which should be highlighted in a face |
;; If you have additional keywords which should be highlighted in a |
55 |
;; different from `font-lock-keyword-face', you can use the convenience |
;; face different from `font-lock-keyword-face', you can use the |
56 |
;; function `generic-make-keywords-list' (which see), and add the |
;; convenience function `generic-make-keywords-list' (which see), |
57 |
;; result to the following list: |
;; and add the result to the following list: |
58 |
;; |
;; |
59 |
;; * Additional expressions to font-lock. This should be a list of |
;; * Additional expressions to font-lock. This should be a list of |
60 |
;; expressions, each of which should be of the same form |
;; expressions, each of which should be of the same form as those in |
61 |
;; as those in `font-lock-keywords'. |
;; `font-lock-keywords'. |
62 |
;; |
;; |
63 |
;; * List of regular expressions to be placed in auto-mode-alist. |
;; * List of regular expressions to be placed in auto-mode-alist. |
64 |
;; |
;; |
83 |
;; Use the `define-generic-mode' function to define new modes. |
;; Use the `define-generic-mode' function to define new modes. |
84 |
;; For example: |
;; For example: |
85 |
;; |
;; |
|
;; (require 'generic) |
|
86 |
;; (define-generic-mode 'foo-generic-mode |
;; (define-generic-mode 'foo-generic-mode |
87 |
;; (list ?% ) |
;; (list ?%) |
88 |
;; (list "keyword") |
;; (list "keyword") |
89 |
;; nil |
;; nil |
90 |
;; (list "\\.FOO\\'") |
;; (list "\\.FOO\\'") |
91 |
;; (list 'foo-setup-function)) |
;; (list 'foo-setup-function)) |
92 |
;; |
;; |
93 |
;; defines a new generic-mode `foo-generic-mode', which has '%' as a |
;; defines a new generic-mode `foo-generic-mode', which has '%' as a |
94 |
;; comment character, and "keyword" as a keyword. When files which end in |
;; comment character, and "keyword" as a keyword. When files which |
95 |
;; '.FOO' are loaded, Emacs will go into foo-generic-mode and call |
;; end in '.FOO' are loaded, Emacs will go into foo-generic-mode and |
96 |
;; foo-setup-function. You can also use the function `foo-generic-mode' |
;; call foo-setup-function. You can also use the function |
97 |
;; (which is interactive) to put a buffer into foo-generic-mode. |
;; `foo-generic-mode' (which is interactive) to put a buffer into |
98 |
|
;; foo-generic-mode. |
99 |
;; |
;; |
100 |
;; AUTOMATICALLY ENTERING GENERIC MODE: |
;; AUTOMATICALLY ENTERING GENERIC MODE: |
101 |
;; |
;; |
102 |
;; Generic-mode provides a hook which automatically puts a |
;; Generic-mode provides a hook which automatically puts a file into |
103 |
;; file into default-generic-mode if the first few lines of a file in |
;; default-generic-mode if the first few lines of a file in |
104 |
;; fundamental mode start with a hash comment character. To disable |
;; fundamental mode start with a hash comment character. To disable |
105 |
;; this functionality, set the variable `generic-use-find-file-hook' |
;; this functionality, set the variable `generic-use-find-file-hook' |
106 |
;; to nil BEFORE loading generic-mode. See the variables |
;; to nil BEFORE loading generic-mode. See the variables |
107 |
;; `generic-lines-to-scan' and `generic-find-file-regexp' for customization |
;; `generic-lines-to-scan' and `generic-find-file-regexp' for |
108 |
;; options. |
;; customization options. |
109 |
;; |
;; |
110 |
;; GOTCHAS: |
;; GOTCHAS: |
111 |
;; |
;; |
112 |
;; Be careful that your font-lock definitions are correct. Getting them |
;; Be careful that your font-lock definitions are correct. Getting |
113 |
;; wrong can cause emacs to continually attempt to fontify! This problem |
;; them wrong can cause Emacs to continually attempt to fontify! This |
114 |
;; is not specific to generic-mode. |
;; problem is not specific to generic-mode. |
115 |
;; |
;; |
116 |
|
|
117 |
;; Credit for suggestions, brainstorming, help with debugging: |
;; Credit for suggestions, brainstorming, help with debugging: |
121 |
;; |
;; |
122 |
;;; Code: |
;;; Code: |
123 |
|
|
|
(eval-when-compile |
|
|
(require 'cl)) |
|
|
|
|
124 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
125 |
;; Internal Variables |
;; Internal Variables |
126 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
129 |
"Global defaults for font-lock in a generic mode.") |
"Global defaults for font-lock in a generic mode.") |
130 |
(make-variable-buffer-local 'generic-font-lock-defaults) |
(make-variable-buffer-local 'generic-font-lock-defaults) |
131 |
|
|
132 |
|
;;;###autoload |
133 |
(defvar generic-mode-list nil |
(defvar generic-mode-list nil |
134 |
"A list of mode names for `generic-mode'. |
"A list of mode names for `generic-mode'. |
135 |
Do not add entries to this list directly; use `define-generic-mode' |
Do not add entries to this list directly; use `define-generic-mode' |
145 |
:group 'extensions) |
:group 'extensions) |
146 |
|
|
147 |
(defcustom generic-use-find-file-hook t |
(defcustom generic-use-find-file-hook t |
148 |
"*If non-nil, add a hook to enter default-generic-mode automatically. |
"*If non-nil, add a hook to enter `default-generic-mode' automatically. |
149 |
This is done if the first few lines of a file in fundamental mode start |
This is done if the first few lines of a file in fundamental mode |
150 |
with a hash comment character." |
start with a hash comment character." |
151 |
:group 'generic |
:group 'generic |
152 |
:type 'boolean |
:type 'boolean) |
|
) |
|
153 |
|
|
154 |
(defcustom generic-lines-to-scan 3 |
(defcustom generic-lines-to-scan 3 |
155 |
"*Number of lines that `generic-mode-find-file-hook' looks at. |
"*Number of lines that `generic-mode-find-file-hook' looks at. |
156 |
Relevant when deciding whether to enter `generic-mode' automatically. |
Relevant when deciding whether to enter Default-Generic mode automatically. |
157 |
This variable should be set to a small positive number." |
This variable should be set to a small positive number." |
158 |
:group 'generic |
:group 'generic |
159 |
:type 'integer |
:type 'integer) |
|
) |
|
160 |
|
|
161 |
(defcustom generic-find-file-regexp "^#" |
(defcustom generic-find-file-regexp "^#" |
162 |
"*Regular expression used by `generic-mode-find-file-hook'. |
"*Regular expression used by `generic-mode-find-file-hook'. |
163 |
Files in fundamental mode whose first few lines contain a match for |
Files in fundamental mode whose first few lines contain a match |
164 |
this regexp, should be put into `default-generic-mode' instead. |
for this regexp, should be put into Default-Generic mode instead. |
165 |
The number of lines tested for the matches is specified by the value |
The number of lines tested for the matches is specified by the |
166 |
of the variable `generic-lines-to-scan', which see." |
value of the variable `generic-lines-to-scan', which see." |
167 |
:group 'generic |
:group 'generic |
168 |
:type 'regexp |
:type 'regexp) |
|
) |
|
169 |
|
|
170 |
(defcustom generic-ignore-files-regexp "[Tt][Aa][Gg][Ss]\\'" |
(defcustom generic-ignore-files-regexp "[Tt][Aa][Gg][Ss]\\'" |
171 |
"*Regular expression used by `generic-mode-find-file-hook'. |
"*Regular expression used by `generic-mode-find-file-hook'. |
172 |
Files whose names match this regular expression should not be put |
Files whose names match this regular expression should not be put |
173 |
into `default-generic-mode', even if they have lines which match the |
into Default-Generic mode, even if they have lines which match |
174 |
regexp in `generic-find-file-regexp'. If the value is nil, |
the regexp in `generic-find-file-regexp'. If the value is nil, |
175 |
`generic-mode-find-file-hook' does not check the file names." |
`generic-mode-find-file-hook' does not check the file names." |
176 |
:group 'generic |
:group 'generic |
177 |
:type '(choice (const :tag "Don't check file names" nil) regexp) |
:type '(choice (const :tag "Don't check file names" nil) regexp)) |
|
) |
|
178 |
|
|
179 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
180 |
;; Functions |
;; Functions |
181 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
182 |
|
|
183 |
;;;###autoload |
;;;###autoload |
184 |
(defun define-generic-mode (name comment-list keyword-list font-lock-list |
(defmacro define-generic-mode (mode comment-list keyword-list |
185 |
auto-mode-list function-list |
font-lock-list auto-mode-list |
186 |
&optional description) |
function-list &optional docstring) |
187 |
"Create a new generic mode with NAME. |
"Create a new generic mode MODE. |
188 |
|
|
189 |
NAME should be a symbol; its string representation is used as the function |
MODE is the name of the command for the generic mode; it need not |
190 |
name. If DESCRIPTION is provided, it is used as the docstring for the new |
be quoted. The optional DOCSTRING is the documentation for the |
191 |
function. |
mode command. If you do not supply it, a default documentation |
192 |
|
string will be used instead. |
193 |
COMMENT-LIST is a list, whose entries are either a single character, |
|
194 |
a one or two character string or a cons pair. If the entry is a character |
COMMENT-LIST is a list, whose entries are either a single |
195 |
or a one-character string, it is added to the mode's syntax table with |
character, a one or two character string or a cons pair. If the |
196 |
`comment-start' syntax. If the entry is a cons pair, the elements of the |
entry is a character or a string, it is added to the mode's |
197 |
pair are considered to be `comment-start' and `comment-end' respectively. |
syntax table with `comment-start' syntax. If the entry is a cons |
198 |
\(The latter should be nil if you want comments to end at end of line.) |
pair, the elements of the pair are considered to be |
199 |
Note that Emacs has limitations regarding comment characters. |
`comment-start' and `comment-end' respectively. (The latter |
200 |
|
should be nil if you want comments to end at end of line.) Note |
201 |
KEYWORD-LIST is a list of keywords to highlight with `font-lock-keyword-face'. |
that Emacs has limitations regarding comment characters. |
202 |
Each keyword should be a string. |
|
203 |
|
KEYWORD-LIST is a list of keywords to highlight with |
204 |
FONT-LOCK-LIST is a list of additional expressions to highlight. Each entry |
`font-lock-keyword-face'. Each keyword should be a string. |
205 |
in the list should have the same form as an entry in `font-lock-keywords'. |
|
206 |
|
FONT-LOCK-LIST is a list of additional expressions to highlight. |
207 |
AUTO-MODE-LIST is a list of regular expressions to add to `auto-mode-alist'. |
Each entry in the list should have the same form as an entry in |
208 |
These regexps are added to `auto-mode-alist' as soon as `define-generic-mode' |
`font-lock-keywords'. |
209 |
is called; any old regexps with the same name are removed. |
|
210 |
|
AUTO-MODE-LIST is a list of regular expressions to add to |
211 |
|
`auto-mode-alist'. These regexps are added to `auto-mode-alist' |
212 |
|
as soon as `define-generic-mode' is called. |
213 |
|
|
214 |
FUNCTION-LIST is a list of functions to call to do some additional setup. |
FUNCTION-LIST is a list of functions to call to do some |
215 |
|
additional setup. |
216 |
|
|
217 |
See the file generic-x.el for some examples of `define-generic-mode'." |
See the file generic-x.el for some examples of `define-generic-mode'." |
218 |
|
(let* ((name-unquoted (if (eq (car-safe mode) 'quote) ; Backward compatibility. |
219 |
|
(eval mode) |
220 |
|
mode)) |
221 |
|
(name-string (symbol-name name-unquoted)) |
222 |
|
(pretty-name (capitalize (replace-regexp-in-string |
223 |
|
"-mode\\'" "" name-string)))) |
224 |
|
|
225 |
|
`(progn |
226 |
|
;; Add a new entry. |
227 |
|
(add-to-list 'generic-mode-list ,name-string) |
228 |
|
|
229 |
|
;; Add it to auto-mode-alist |
230 |
|
(dolist (re ,auto-mode-list) |
231 |
|
(add-to-list 'auto-mode-alist (cons re ',name-unquoted))) |
232 |
|
|
233 |
|
(defun ,name-unquoted () |
234 |
|
,(or docstring |
235 |
|
(concat pretty-name " mode.\n" |
236 |
|
"This a generic mode defined with `define-generic-mode'.")) |
237 |
|
(interactive) |
238 |
|
(generic-mode-internal ',name-unquoted ,comment-list ,keyword-list |
239 |
|
,font-lock-list ,function-list))))) |
240 |
|
|
241 |
;; Add a new entry |
;;;###autoload |
|
(add-to-list 'generic-mode-list (symbol-name name)) |
|
|
|
|
|
;; Add it to auto-mode-alist |
|
|
(dolist (re auto-mode-list) |
|
|
(add-to-list 'auto-mode-alist (cons re name))) |
|
|
|
|
|
;; Define a function for it using `defalias' (not `fset') to make |
|
|
;; the mode appear on load-history. |
|
|
(defalias name |
|
|
`(lambda nil |
|
|
,(or description (concat "Generic mode for type " (symbol-name name))) |
|
|
(interactive) |
|
|
(generic-mode-internal ',name ',comment-list ',keyword-list |
|
|
',font-lock-list ',function-list))) |
|
|
) |
|
|
|
|
242 |
(defun generic-mode-internal (mode comments keywords font-lock-list funs) |
(defun generic-mode-internal (mode comments keywords font-lock-list funs) |
243 |
"Go into the generic-mode MODE." |
"Go into the generic mode MODE." |
244 |
(let* ((generic-mode-hooks (intern (concat (symbol-name mode) "-hook"))) |
(let* ((modename (symbol-name mode)) |
245 |
(modename (symbol-name mode)) |
(generic-mode-hooks (intern (concat modename "-hook"))) |
246 |
(name (if (string-match "-mode\\'" modename) |
(pretty-name (capitalize (replace-regexp-in-string |
247 |
(substring modename 0 (match-beginning 0)) |
"-mode\\'" "" modename)))) |
|
modename)) |
|
|
) |
|
248 |
|
|
|
;; Put this after the point where we read generic-mode-name! |
|
249 |
(kill-all-local-variables) |
(kill-all-local-variables) |
250 |
|
|
251 |
(setq |
(setq major-mode mode |
252 |
major-mode mode |
mode-name pretty-name) |
|
mode-name (capitalize name) |
|
|
) |
|
253 |
|
|
254 |
(generic-mode-set-comments comments) |
(generic-mode-set-comments comments) |
255 |
|
|
256 |
;; Font-lock functionality |
;; Font-lock functionality |
257 |
;; Font-lock-defaults are always set even if there are no keywords |
;; Font-lock-defaults are always set even if there are no keywords |
258 |
;; or font-lock expressions, so comments can be highlighted. |
;; or font-lock expressions, so comments can be highlighted. |
259 |
(setq generic-font-lock-defaults nil) |
(setq generic-font-lock-defaults nil) |
260 |
(generic-mode-set-font-lock keywords font-lock-list) |
(generic-mode-set-font-lock keywords font-lock-list) |
261 |
(make-local-variable 'font-lock-defaults) |
(make-local-variable 'font-lock-defaults) |
262 |
(setq font-lock-defaults (list 'generic-font-lock-defaults nil)) |
(setq font-lock-defaults (list 'generic-font-lock-defaults nil)) |
263 |
|
|
264 |
;; Call a list of functions |
;; Call a list of functions |
265 |
(mapcar 'funcall funs) |
(mapcar 'funcall funs) |
266 |
|
|
267 |
(run-hooks generic-mode-hooks) |
(run-hooks generic-mode-hooks))) |
|
) |
|
|
) |
|
268 |
|
|
269 |
;;;###autoload |
;;;###autoload |
270 |
(defun generic-mode (type) |
(defun generic-mode (mode) |
271 |
"Basic comment and font-lock functionality for `generic' files. |
"Enter generic mode MODE. |
272 |
\(Files which are too small to warrant their own mode, but have |
|
273 |
comment characters, keywords, and the like.) |
Generic modes provide basic comment and font-lock functionality |
274 |
|
for \"generic\" files. (Files which are too small to warrant their |
275 |
|
own mode, but have comment characters, keywords, and the like.) |
276 |
|
|
277 |
To define a generic-mode, use the function `define-generic-mode'. |
To define a generic-mode, use the function `define-generic-mode'. |
278 |
Some generic modes are defined in `generic-x.el'." |
Some generic modes are defined in `generic-x.el'." |
279 |
(interactive |
(interactive |
280 |
(list (completing-read "Generic Type: " generic-mode-list nil t))) |
(list (completing-read "Generic mode: " generic-mode-list nil t))) |
281 |
(funcall (intern type))) |
(funcall (intern mode))) |
282 |
|
|
283 |
;;; Comment Functionality |
;;; Comment Functionality |
284 |
(defun generic-mode-set-comments (comment-list) |
(defun generic-mode-set-comments (comment-list) |
286 |
(let ((st (make-syntax-table)) |
(let ((st (make-syntax-table)) |
287 |
(chars nil) |
(chars nil) |
288 |
(comstyles)) |
(comstyles)) |
289 |
(make-local-variable 'comment-start) |
(make-local-variable 'comment-start) |
290 |
(make-local-variable 'comment-start-skip) |
(make-local-variable 'comment-start-skip) |
291 |
(make-local-variable 'comment-end) |
(make-local-variable 'comment-end) |
292 |
|
|
293 |
;; Go through all the comments |
;; Go through all the comments |
294 |
(dolist (start comment-list) |
(dolist (start comment-list) |
295 |
(let ((end nil) (comstyle "")) |
(let (end (comstyle "")) |
296 |
;; Normalize |
;; Normalize |
297 |
(when (consp start) |
(when (consp start) |
298 |
(setq end (or (cdr start) end)) |
(setq end (cdr start)) |
299 |
(setq start (car start))) |
(setq start (car start))) |
300 |
(when (char-valid-p start) (setq start (char-to-string start))) |
(when (char-valid-p start) (setq start (char-to-string start))) |
301 |
(cond |
(cond |
363 |
imenu-case-fold-search t)) |
imenu-case-fold-search t)) |
364 |
|
|
365 |
;; This generic mode is always defined |
;; This generic mode is always defined |
366 |
(define-generic-mode 'default-generic-mode (list ?#) nil nil nil nil) |
(define-generic-mode default-generic-mode (list ?#) nil nil nil nil) |
367 |
|
|
368 |
;; A more general solution would allow us to enter generic-mode for |
;; A more general solution would allow us to enter generic-mode for |
369 |
;; *any* comment character, but would require us to synthesize a new |
;; *any* comment character, but would require us to synthesize a new |
370 |
;; generic-mode on the fly. I think this gives us most of what we |
;; generic-mode on the fly. I think this gives us most of what we |
371 |
;; want. |
;; want. |
372 |
(defun generic-mode-find-file-hook () |
(defun generic-mode-find-file-hook () |
373 |
"Hook function to enter `default-generic-mode' automatically. |
"Hook function to enter Default-Generic mode automatically. |
374 |
Done if the first few lines of a file in `fundamental-mode' start with |
|
375 |
a match for the regexp in `generic-find-file-regexp', unless the |
Done if the first few lines of a file in Fundamental mode start |
376 |
file's name matches the regexp which is the value of the variable |
with a match for the regexp in `generic-find-file-regexp', unless |
377 |
`generic-ignore-files-regexp'. |
the file's name matches the regexp which is the value of the |
378 |
|
variable `generic-ignore-files-regexp'. |
379 |
|
|
380 |
This hook will be installed if the variable |
This hook will be installed if the variable |
381 |
`generic-use-find-file-hook' is non-nil. The variable |
`generic-use-find-file-hook' is non-nil. The variable |
382 |
`generic-lines-to-scan' determines the number of lines to look at." |
`generic-lines-to-scan' determines the number of lines to look at." |
395 |
(default-generic-mode))))) |
(default-generic-mode))))) |
396 |
|
|
397 |
(defun generic-mode-ini-file-find-file-hook () |
(defun generic-mode-ini-file-find-file-hook () |
398 |
"Hook function to enter default-generic-mode automatically for INI files. |
"Hook function to enter Default-Generic mode automatically for INI files. |
399 |
Done if the first few lines of a file in `fundamental-mode' look like an |
Done if the first few lines of a file in Fundamental mode look like an |
400 |
INI file. This hook is NOT installed by default." |
INI file. This hook is NOT installed by default." |
401 |
(and (eq major-mode 'fundamental-mode) |
(and (eq major-mode 'fundamental-mode) |
402 |
(save-excursion |
(save-excursion |