Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
2701 views
1
;;; ess-noweb-font-lock-mode.el --- edit noweb files with GNU Emacs
2
3
;; Copyright (C) 1999 by Adnan Yaqub ([email protected])
4
;; and Mark Lunt ([email protected]
5
;; Copyright (C) 2002 by A.J. Rossini <[email protected]>
6
;; Copyright (C) 2003--2004 A.J. Rossini, Richard M. Heiberger, Martin
7
;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
8
9
;; Maintainer: ESS-core <[email protected]>
10
11
;; This program is free software; you can redistribute it and/or modify
12
;; it under the terms of the GNU General Public License as published by
13
;; the Free Software Foundation; either version 2, or (at your option)
14
;; any later version.
15
;;
16
;; This program is distributed in the hope that it will be useful,
17
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
;; GNU General Public License for more details.
20
;;
21
;; A copy of the GNU General Public License is available at
22
;; http://www.r-project.org/Licenses/
23
;;
24
25
;;; Commentary:
26
27
;; Code-dependent highlighting
28
;; *****
29
;;
30
;; Adding highlighting to ess-noweb-mode.el
31
;;
32
;; Here is a description of how one can add highlighting via the
33
;; font-lock package to noweb buffers. It uses the hooks provided by
34
;; ess-noweb-mode.el. The solution provides the following features:
35
;; 1) The documentation chunks are highlighted in the ess-noweb-doc-mode
36
;; (e.g., LaTeX).
37
;; 2) The code chunks without mode comments (-*- mode -*-) are
38
;; highlighted in the ess-noweb-code-mode.
39
;; 3) The code chunks with mode comments (-*- mode -*-) on the first
40
;; line of the first chunk with this name are highlighted in the mode
41
;; in the comment.
42
;;
43
;; For example, given the file:
44
;;
45
;; % -*- mode: Noweb; ess-noweb-code-mode: c-mode -*-
46
;;
47
;; \begin{itemize}
48
;; \item a main routine written in C,
49
;; \item a log configuration file parser written in YACC, and
50
;; \item a lexical analyzer written in Lex.
51
;; \end{itemize}
52
;;
53
;; <<warning c comment>>=
54
;; /* DO NOT EDIT ME! */
55
;; /* This file was automatically generated from %W% (%G%). */
56
;; @
57
;;
58
;; <<warning nroff comment>>=
59
;; .\" -*- nroff -*-
60
;; .\" DO NOT EDIT ME!
61
;; .\" This file was automatically generated from %W% (%G%).
62
;; @
63
;;
64
;; The LaTeX list is highlighted in latex-mode (the default noweb doc
65
;; mode), the chunk <<warning c comment>> is highlighted in c-mode (the
66
;; default noweb code mode), and the chunk <<warning nroff comment>> is
67
;; highlighted in nroff-mode due to the "-*- nroff -*-" comment.
68
;;
69
;; Chunks are highlighted each time point moves into them from a
70
;; different mode. They are also fontified 'on the fly', but this is
71
;; less reliable, since the syntax can depend on the context. It's as
72
;; good as you would get outside ess-noweb-mode, though.
73
;;
74
;; To use it, you must add
75
;; (require 'ess-noweb-font-lock-mode) to your .emacs file.
76
;; Then, if you use either global-font-lock or turn-on-font-lock
77
;; statements, any ess-noweb-mode buffers will be fontified
78
;; appropriately. (We have to redefine turn-on-font-lock, but it
79
;; saves breaking other packages (in particular ESS, which I use a
80
;; lot), that assume that turn-on-font-lock is the way to turn on
81
;; font locking.
82
83
;; Alternatively, you can turn ess-noweb-font-lock-mode on and off by
84
;; using M-x ess-noweb-font-lock-mode. However, turning
85
;; ess-noweb-font-lock-mode off when global-font-lock-mode is t makes it
86
;; impossible to use font-locking in that buffer subsequently, other
87
;; than by turning ess-noweb-font-lock-mode back on.
88
89
;; 2) The highlighting sometimes get confused, but this is no longer
90
;; a noweb problem. Highlighting should work as well within a chunk
91
;; as it does without ess-noweb-mode.
92
;; There are some problems with, for example latex-mode: a `$' in a
93
;; verbatim environment with throw the font-locking out.
94
;; One slight blemish is that code-quotes are highlighted as comments
95
;; as they are being entered. They are only highlighted correctly
96
;; after `ess-noweb-font-lock-fontify-chunk' has been run, either as a
97
;; command or through changing to a different chunk and back again
98
;; (unless they lie on a single line, in which case they are
99
;; fontified correctly once they are completed).
100
101
;;; Code:
102
103
(require 'ess-noweb-mode)
104
(require 'font-lock)
105
106
(defvar ess-noweb-font-lock-mode nil
107
"Buffer local variable, t iff this buffer is using ess-noweb-font-lock-mode.")
108
109
(defvar ess-noweb-use-font-lock-mode t
110
"DO NOT CHANGE THIS VARIABLE
111
If you use nw-turn-on-font-lock to turn on font-locking, then turn it
112
off again, it would come back on again of its own accord when you
113
changed major-mode. This variable is used internally to stop it.")
114
115
(defvar ess-noweb-font-lock-mode-hook nil
116
"Hook that is run after entering ess-noweb-font-lock mode.")
117
118
(defvar ess-noweb-font-lock-max-initial-chunks 30
119
"Maximum number of chunks to fontify initially.
120
If nil, will fontify the entire buffer when
121
ess-noweb-font-lock-initial-fontify-buffer is called" )
122
123
(defvar old-beginning-of-syntax nil
124
"Stores the function used to find the beginning of syntax in the
125
current major mode. ess-noweb-font-lock-mode needs a different one." )
126
127
;; (AJR) the next two lines were originally font-lock-warning-face
128
;; methods; XEmacs 20.4 doesn't define this, sigh... -- KLUDGE --.
129
130
(defvar ess-noweb-font-lock-doc-start-face font-lock-reference-face
131
"Face to use to highlight the `@' at the start of each doc chunk")
132
133
(defvar ess-noweb-font-lock-brackets-face font-lock-reference-face
134
"Face to use to highlight `<<', `>>' `[[' and `]]' ")
135
136
(defvar ess-noweb-font-lock-chunk-name-face font-lock-keyword-face
137
"Face to use to highlight the between `<<' and `>>'")
138
139
(defvar ess-noweb-font-lock-code-quote-face font-lock-keyword-face
140
"Face to use to highlight the between `[[' and `]]'")
141
142
;; Now we add [[ess-noweb-font-lock-mode]] to the list of existing minor
143
;; modes. The string ``NWFL'' will be added to the mode-line: ugly, but
144
;; brief.
145
146
(if (not (assq 'ess-noweb-font-lock-mode minor-mode-alist))
147
(setq minor-mode-alist (append minor-mode-alist
148
(list '(ess-noweb-font-lock-mode " NWFL")))))
149
150
;; An ugly kludge to get around problems with global-font-lock, which
151
;; fontifies the entire buffer in the new major mode every time you
152
;; change mode, which is time-consuming and makes a pigs trotters of
153
;; it. Trying to stop it looks tricky, but using this function as your
154
;; `font-lock-fontify-buffer' function stops it wasting your time
155
156
(defun nwfl-donowt()
157
"This function does nothing at all")
158
159
;; The following function is just a wrapper for ess-noweb-font-lock-mode,
160
;; enabling it to be called as ess-noweb-font-lock-minor-mode instead.
161
162
(defun ess-noweb-font-lock-minor-mode ( &optional arg)
163
"Minor meta mode for managing syntax highlighting in noweb files.
164
See ess-noweb-font-lock-mode."
165
(interactive)
166
(ess-noweb-font-lock-mode arg))
167
168
;; Here we get to the meat of the problem
169
170
(defun ess-noweb-font-lock-mode ( &optional arg)
171
"Minor mode for syntax highlighting when using ess-noweb-mode to edit noweb files.
172
Each chunk is fontified in accordance with its own mode"
173
(interactive "P")
174
(if (or ess-noweb-mode ess-noweb-font-lock-mode)
175
(progn
176
;; This bit is tricky: copied almost verbatim from bib-cite-mode.el
177
;; It seems to ensure that the variable ess-noweb-font-lock-mode is made
178
;; local to this buffer. It then sets ess-noweb-font-lock-mode to `t' if
179
;; 1) It was called with a prefix argument greater than 0
180
;; or 2) It was called with no argument, and ess-noweb-font-lock-mode is
181
;; currently nil
182
;; ess-noweb-font-lock-mode is nil if the prefix argument was <= 0 or there
183
;; was no prefix argument and ess-noweb-font-lock-mode is currently `t'
184
(set (make-local-variable 'ess-noweb-font-lock-mode)
185
(if arg
186
(> (prefix-numeric-value arg) 0)
187
(not ess-noweb-font-lock-mode)))
188
;; Now, if ess-noweb-font-lock-mode is true, we want to turn
189
;; ess-noweb-font-lock-mode on
190
(cond
191
(ess-noweb-font-lock-mode ;Setup the minor-mode
192
(when (and (boundp 'global-font-lock-mode) global-font-lock-mode)
193
(mapc 'ess-noweb-make-variable-permanent-local
194
'(font-lock-fontify-buffer-function
195
font-lock-unfontify-buffer-function))
196
(setq font-lock-fontify-buffer-function 'nwfl-donowt)
197
(setq font-lock-unfontify-buffer-function 'nwfl-donowt))
198
(mapcar 'ess-noweb-make-variable-permanent-local
199
'(ess-noweb-font-lock-mode
200
font-lock-dont-widen
201
font-lock-beginning-of-syntax-function
202
syntax-begin-function
203
ess-noweb-use-font-lock-mode
204
after-change-functions))
205
(setq ess-noweb-font-lock-mode t
206
font-lock-dont-widen t)
207
(when (< emacs-major-version 21) ; needed for emacs < 21.1 only :
208
(make-local-hook 'after-change-functions))
209
(add-hook 'after-change-functions
210
'font-lock-after-change-function nil t)
211
(add-hook 'ess-noweb-font-lock-mode-hook 'ess-noweb-font-lock-mode-fn)
212
(add-hook 'ess-noweb-changed-chunk-hook
213
'ess-noweb-font-lock-fontify-this-chunk)
214
(run-hooks 'ess-noweb-font-lock-mode-hook)
215
(message "ess-noweb-font-lock mode: use `M-x ess-noweb-font-lock-describe-mode' for more info"))
216
;; If we didn't do the above, then we want to turn ess-noweb-font-lock-mode
217
;; off, no matter what (hence the condition `t')
218
(t
219
(when (and (boundp 'global-font-lock-mode) global-font-lock-mode)
220
;; (setq font-lock-fontify-buffer-function
221
;; 'font-lock-default-fontify-buffer)
222
;; Get back our unfontify buffer function
223
(setq font-lock-unfontify-buffer-function
224
'font-lock-default-unfontify-buffer))
225
(remove-hook 'ess-noweb-font-lock-mode-hook 'ess-noweb-font-lock-mode-fn)
226
(remove-hook 'ess-noweb-changed-chunk-hook
227
'ess-noweb-font-lock-fontify-this-chunk)
228
(remove-hook 'after-change-functions
229
'font-lock-after-change-function )
230
(font-lock-default-unfontify-buffer)
231
(setq ess-noweb-use-font-lock-mode nil)
232
(message "ess-noweb-font-lock-mode removed"))))
233
(message "ess-noweb-font-lock-mode can only be used with ess-noweb-mode")))
234
235
(defun ess-noweb-start-of-syntax ()
236
"Go to the place to start fontifying from"
237
(interactive)
238
(goto-char (car (ess-noweb-chunk-region))))
239
240
(defun ess-noweb-font-lock-fontify-chunk-by-number ( chunk-num )
241
"Fontify chunk chunk-num based on the current major mode."
242
(save-excursion
243
(font-lock-set-defaults)
244
(setq old-beginning-of-syntax font-lock-beginning-of-syntax-function)
245
(setq font-lock-beginning-of-syntax-function 'ess-noweb-start-of-syntax)
246
(setq syntax-begin-function 'ess-noweb-start-of-syntax)
247
(setq font-lock-keywords
248
;; (append font-lock-keywords
249
;; '(("\\(\\[\\[\\)\\([^]]*\\]*\\)\\(\\]\\]\\|\\$\\)"
250
;; (1 ess-noweb-font-lock-brackets-face prepend )
251
;; (2 ess-noweb-font-lock-code-quote-face prepend)
252
;; (3 ess-noweb-font-lock-brackets-face prepend))
253
;; ("^[ \t\n]*\\(<<\\)\\([^>]*\\)\\(>>=?\\)"
254
;; (1 ess-noweb-font-lock-brackets-face prepend )
255
;; (2 ess-noweb-font-lock-chunk-name-face prepend)
256
;; (3 ess-noweb-font-lock-brackets-face prepend))
257
;; ("^@[ \t\n]+"
258
;; (0 ess-noweb-font-lock-doc-start-face prepend )))))
259
(append font-lock-keywords
260
'(("^[ \t\n]*\\(<<\\)\\([^>]*\\)\\(>>=?\\)"
261
(1 font-lock-reference-face prepend )
262
(2 font-lock-keyword-face prepend)
263
(3 font-lock-reference-face prepend))
264
("^@[ \t\n]+"
265
(0 font-lock-reference-face prepend )))))
266
267
268
(let ((r (cons (marker-position (cdr (aref ess-noweb-chunk-vector
269
chunk-num)))
270
(marker-position (cdr (aref ess-noweb-chunk-vector
271
(1+ chunk-num))))))
272
(font-latex-extend-region-functions nil);; don't extend anything
273
(font-lock-extend-region-functions nil)) ;; this infloops :(
274
(save-restriction
275
(narrow-to-region (car r) (cdr r))
276
;; (sit-for 3)
277
(font-lock-fontify-region (car r) (cdr r)))
278
t)))
279
280
(defadvice font-lock-after-change-function (around ess-remove-fl-multiline-extension activate)
281
"Remove `font-lock-extend-region-multiline' from `font-lock-extend-region-multiline'.
282
On commenting the whole chunk, causes infloop in
283
`after-change-functions' for no clear reason."
284
(let ((font-lock-extend-region-functions font-lock-extend-region-functions)
285
;; (font-latex-extend-region-functions nil)
286
)
287
(delq 'font-lock-extend-region-multiline font-lock-extend-region-functions)
288
ad-do-it))
289
290
;; (ad-remove-advice 'font-lock-after-change-function 'around 'test-font)
291
;; (ad-activate 'font-lock-after-change-function)
292
293
294
(defun ess-noweb-font-lock-fontify-this-chunk ()
295
"Fontify this chunk according to its own major mode.
296
Since we are in the chunk, the major mode will already have been set
297
by ess-noweb-mode.el"
298
(interactive)
299
(ess-noweb-font-lock-fontify-chunk-by-number (ess-noweb-find-chunk-index-buffer)))
300
301
(defun ess-noweb-font-lock-initial-fontify-buffer ()
302
"Applies syntax highlighting to some or all chunks in a noweb buffer.
303
The number of chunks is set by ess-noweb-font-lock-max-initial-chunks: if
304
this is nil, the entire buffer is fontified.
305
It is intended to be called when first entering ess-noweb-font-lock-mode.
306
For other purposes, use ess-noweb-font-lock-fontify-chunks."
307
(interactive)
308
;; This will be tricky. It will be very slow to go throught the chunks
309
;; in order, switching major modes all the time.
310
;; So, we will do the documentation in one pass, the code in a second
311
;; pass. This could still be a little slow if we have to swap between
312
;; different code modes regularly, but it should be bearable. It should
313
;; only happen when the file is first read in, anyway
314
(save-excursion
315
(let (start-chunk end-chunk this-chunk chunk-counter)
316
(setq this-chunk (ess-noweb-find-chunk-index-buffer))
317
(if ess-noweb-font-lock-max-initial-chunks
318
(progn
319
(setq start-chunk
320
(max 0
321
(- this-chunk
322
(/ ess-noweb-font-lock-max-initial-chunks 2))))
323
;; Don't you just love hairy lisp syntax ? The above means set the
324
;; starting chunk to the current chunk minus half of
325
;; ess-noweb-font-lock-max-initial-chunks, unless that is negative in
326
;; which case set it to 0
327
(setq end-chunk (+ start-chunk ess-noweb-font-lock-max-initial-chunks))
328
(if (> end-chunk (- (length ess-noweb-chunk-vector) 2))
329
(setq end-chunk (- (length ess-noweb-chunk-vector) 2))))
330
;; If ess-noweb-font-lock-max-initial-chunks is nil, do the whole buffer
331
(progn
332
(setq start-chunk 0)
333
(setq end-chunk (- (length ess-noweb-chunk-vector) 2))))
334
(ess-noweb-font-lock-fontify-chunks start-chunk end-chunk))))
335
336
(defun ess-noweb-font-lock-fontify-buffer ()
337
"This function will fontify each chunk in the buffer appropriately."
338
(interactive)
339
(let ((start-chunk 0)
340
(end-chunk (- (length ess-noweb-chunk-vector) 2)))
341
(ess-noweb-font-lock-fontify-chunks start-chunk end-chunk)))
342
343
(defun ess-noweb-font-lock-fontify-chunks (start-chunk end-chunk)
344
"Fontify a noweb file from start-chunk to end-chunk"
345
(interactive)
346
(let (chunk-counter)
347
(save-excursion
348
(message "Fontifying from %d to %d" start-chunk end-chunk)
349
;; Want to set DOC mode for the first Doc chunk, not for the others
350
(setq chunk-counter start-chunk)
351
(while (stringp (car (aref ess-noweb-chunk-vector chunk-counter)))
352
(setq chunk-counter (+ chunk-counter 1)))
353
(goto-char (cdr (aref ess-noweb-chunk-vector chunk-counter)))
354
(ess-noweb-select-mode)
355
;; Now go through the chunks, fontifying the documentation ones.
356
(while (<= chunk-counter end-chunk)
357
(if (not (stringp (car (aref ess-noweb-chunk-vector chunk-counter))))
358
(ess-noweb-font-lock-fontify-chunk-by-number chunk-counter))
359
(message "Fontifying documentation chunks: chunk %d" chunk-counter)
360
(setq chunk-counter (+ 1 chunk-counter)))
361
;; Go back to the start and go through the chunks, fontifying the code ones.
362
(setq chunk-counter start-chunk)
363
(message "About to do code chunks")
364
(while (<= chunk-counter end-chunk)
365
(when (stringp (car (aref ess-noweb-chunk-vector chunk-counter)))
366
;; It's a code chunk: goto it to set the correct code mode, then
367
;; fontify it.
368
(message "Fontifying code chunks: chunk %d" chunk-counter)
369
(goto-char (cdr (aref ess-noweb-chunk-vector chunk-counter)))
370
(ess-noweb-select-mode)
371
(ess-noweb-font-lock-fontify-this-chunk))
372
(setq chunk-counter (1+ chunk-counter))))
373
(ess-noweb-select-mode)))
374
375
(defun ess-noweb-font-lock-mode-fn()
376
"Function that is intended to be attached to ess-noweb-font-lock-mode-hook."
377
(ess-noweb-font-lock-initial-fontify-buffer))
378
379
;; This is a wee bit of a hack. If people attach `turn-on-font-lock'
380
;; to their major mode hook, it will play hell with
381
;; ess-noweb-font-lock-mode. I had hoped that providing a replacement
382
;; `nw-turn-on-font-lock' would solve the problem, but it didn't
383
;; (sometimes turn-on-font-lock appears in places other than
384
;; `.emacs', such as in ESS). So rather than have it fall over if
385
;; turn-on-lock was around, I redefined turn-on-font-lock to do the
386
;; right thing.
387
388
(defvar ess-noweb-old-turn-on-font-lock nil)
389
390
(defun nw-turn-on-font-lock ()
391
"Turn on font-lock mode, with due regard to whether we are in ess-noweb-mode"
392
(if (not ess-noweb-mode)
393
(ess-noweb-old-turn-on-font-lock)
394
(if (and (not ess-noweb-font-lock-mode) ess-noweb-use-font-lock-mode)
395
(ess-noweb-font-lock-mode ))))
396
397
(unless (functionp 'ess-noweb-old-turn-on-font-lock)
398
(fset 'ess-noweb-old-turn-on-font-lock (symbol-function 'turn-on-font-lock))
399
(fset 'turn-on-font-lock (symbol-function 'nw-turn-on-font-lock)))
400
401
(provide 'ess-noweb-font-lock-mode)
402
;; *****
403
;;
404
;; Adnan Yaqub ([email protected])
405
;; ORGA Kartensysteme GmbH // An der Kapelle 2 // D-33104 Paderborn // Germany
406
;; Tel. +49 5254 991-823 //Fax. +49 5254 991-749
407
408
409
410
;; Local Variables:
411
;; mode:emacs-lisp
412
;; End:
413
414
;;; ess-noweb-font-lock-mode.el ends here
415
416