1 ;; noweb-mode.el - edit noweb files with GNU Emacs
3 ;; Copyright (C) 1995 by Thorsten.Ohl @ Physik.TH-Darmstadt.de
4 ;; with a little help from Norman Ramsey <norman@bellcore.com>
5 ;; and Mark Lunt <mark.lunt@mrc-bsu.cam.ac.uk>
6 ;; and A.J. Rossini <rossini@biostat.washington.edu>
7 ;; Copyright (C) 1999--2004 A.J. Rossini, Rich M. Heiberger, Martin
8 ;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
10 ;; ESS-related Changes for ESS added by Mark Lunt and A.J. Rossini,
11 ;; starting March, 1999.
13 ;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>
15 ;; This program is free software; you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
20 ;; This program is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;; GNU General Public License for more details.
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with this program; if not, write to the Free Software
27 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 ;; See bottom of this file for information on language-dependent
30 ;; highlighting, and recent changes.
33 ;; BASED ON: (from Mark Lunt).
34 ;; -- Id: noweb-mode.el,v 1.11 1999/03/21 20:14:41 root Exp --
37 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
38 ;; THIS IS UNRELEASED CODE: IT IS MISSING FUNCTIONALITY AND IT NEEDS CLEANUP ;;
39 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 ;; Put this into your ~/.emacs to use this mode automagically.
43 ;; (autoload 'noweb-mode "noweb-mode" "Editing noweb files." t)
44 ;; (setq auto-mode-alist (append (list (cons "\\.nw$" 'noweb-mode))
49 ;; * [tho] M-n q, aka: M-x noweb-fill-chunk
51 ;; * [tho] `M-n TAB', aka: `M-x noweb-complete-chunk'
53 ;; * [tho] noweb-occur
55 ;; * [nr] use `M-n' instead of `C-c n' as default command prefix
57 ;; * [nr] don't be fooled by
63 ;; Here starts a new documentation chunk!
67 ;; * [nr] switch mode changing commands off during isearch-mode
69 ;; * [tho] noweb-goto-chunk proposes a default
74 ;; * replace obscure hacks like `(stringp (car (noweb-find-chunk)))'
75 ;; by something more reasonable like `(noweb-code-chunkp)'.
77 ;; * _maybe_ replace our `noweb-chunk-vector' by text properties. We
78 ;; could then use highlighting to jazz up the visual appearance.
79 ;; (Highlighting is sorted: `noweb-chunk-vector' can be
80 ;; ditched. It is simple to determine if we are in a doc or code
83 ;; * wrapped `noweb-goto-next' and `noweb-goto-previous'
85 ;; * more range checks and error exits
87 ;; * commands for tangling, weaving, etc.
89 ;; * `noweb-hide-code-quotes' should be superfluous now, and could
98 (defconst noweb-mode-RCS-Id
99 "Imported to ESS Subversion repository and RCS ids not maintained.")
101 (defconst noweb-mode-RCS-Name
104 (defvar noweb-mode-prefix
"\M-n"
105 "*Prefix key to use for noweb mode commands.
106 The value of this variable is checked as part of loading noweb mode.
107 After that, changing the prefix key requires manipulating keymaps.")
109 (defvar noweb-mode-load-hook nil
110 "Hook that is run after noweb mode is loaded.")
112 (defvar noweb-mode-hook nil
113 "Hook that is run after entering noweb mode.")
115 (defvar noweb-select-code-mode-hook nil
116 "Hook that is run after the code mode is selected.
117 This is the place to overwrite keybindings of the NOWEB-CODE-MODE.")
119 (defvar noweb-select-doc-mode-hook nil
120 "Hook that is run after the documentation mode is selected.
121 This is the place to overwrite keybindings of the NOWEB-DOC-MODE.")
123 (defvar noweb-select-mode-hook nil
124 "Hook that is run after the documentation or the code mode is selected.
125 This is the place to overwrite keybindings of the other modes.")
127 (defvar noweb-changed-chunk-hook nil
128 "Hook that is run every time point moves from one chunk to another.
129 It will be run whether or not the major-mode changes.")
131 (defvar noweb-default-code-mode
'fundamental-mode
132 "Default major mode for editing code chunks.
133 This is set to FUNDAMENTAL-MODE by default, but you might want to
134 change this in the Local Variables section of your file to something
135 more appropriate, like C-MODE, FORTRAN-MODE, or even
136 INDENTED-TEXT-MODE.")
138 (defvar noweb-code-mode
'c-mode
139 "Major mode for editing this particular code chunk.
140 It defaults to noweb-default-code-mode, but can be reset by a comment
141 on the first line of the chunk containing the string
142 \"-*- NEWMODE -*-\" or
143 \"-*- NEWMODE-mode -*-\" or
144 \"-*- mode: NEWMODE -*- \" or
145 \"-*- mode: NEWMODE-mode -*- \"
146 Option three is recommended, as it is the closest to standard emacs usage.")
148 (defvar noweb-default-doc-mode
'latex-mode
149 "Major mode for editing documentation chunks.
150 Sensible choices would be tex-mode, latex-mode, sgml-mode, or
151 html-mode. Maybe others will exist someday.")
153 (defvar noweb-doc-mode-syntax-table nil
154 "A syntax-table syntax table that makes quoted code in doc chunks to
157 (defvar noweb-last-chunk-index
0
158 "This keeps track of the chunk we have just been in. If this is not
159 the same as the current chunk, we have to check if we need to change
162 (defvar noweb-chunk-vector nil
163 "Vector of the chunks in this buffer.")
165 (defvar noweb-narrowing nil
166 "If not NIL, the display will always be narrowed to the
167 current chunk pair.")
169 (defvar noweb-electric-
@-and-
< t
170 "If not nil, the keys `@' and `<' will be bound to NOWEB-ELECTRIC-@
171 and NOWEB-ELECTRIC-<, respectively.")
173 (defvar noweb-use-mouse-navigation t
174 "If not nil, enables moving between chunks using mouse-1.
175 Clicking on the '<<' at the beginning of a chunk name takes you to the
176 previous occurence of that chunk name, clicking on the '>>' takes you
178 Assumes mouse-1 is bound to mouse-set-point, so if you have rebound
179 mouse-1, this will override your binding.")
183 ;; The following is apparently broken -- dangling code that was
184 ;; commented out. Need to see if we can get it working?
186 (defvar noweb-weave-options
"-delay")
187 (defvar noweb-latex-viewer
"xdvi")
188 (defvar noweb-html-viewer
"netscape")
190 (defun noweb-weave (&optional name
)
192 (let ((buffer (get-buffer-create "Weave Buffer")))
195 ;; Assume latex documentation, but set to html if appropriate
196 (if (eq noweb-doc-mode html-mode
)
197 (setq name
(concat (substring (buffer-file-name) 0
198 (string-match ".nw" name
))
200 (setq name
(concat (substring (buffer-file-name) 0
201 (string-match ".nw" name
))
203 (setq name
(concat "> " name
))
204 (setq noweb-weave-options
(concat noweb-weave-options name
))
205 (start-process weave-process buffer
"noweave" noweb-weave-options
)))
206 ;;(defun noweb-view ())
210 (defvar noweb-mode nil
211 "Buffer local variable, T iff this buffer is edited in noweb mode.")
213 ;; For some reason that I do not understand, `newline' does not do the
214 ;; right thing in quoted code. If point is not preceded by whitespace,
215 ;; it moves to the beginning of the current line, not the beginning of
216 ;; the new line. `newline 1' works fine, hence the kludge. I'd love to
217 ;; understand what's going on, though. Try running M-x newline in the
218 ;; middle of a code quote in a doc chunk to see
219 ;; what I mean: its odd.
221 (defun noweb-newline (&optional arg
)
222 "A kludge to get round very odd behaviour of newline in quoted code."
224 (if arg
(newline arg
) (newline 1))
227 (defvar noweb-mode-prefix-map
228 (let ((map (if (featurep 'xemacs
)
229 (make-keymap) ;; XEmacs/Emacs problems...
230 (make-sparse-keymap))))
231 (define-key map
"\C-n" 'noweb-next-chunk
)
232 (define-key map
"\C-p" 'noweb-previous-chunk
)
233 (define-key map
"\M-n" 'noweb-goto-next
)
234 (define-key map
"\M-m" 'noweb-insert-default-mode-line
)
235 (define-key map
"\M-p" 'noweb-goto-previous
)
236 (define-key map
"c" 'noweb-next-code-chunk
)
237 (define-key map
"C" 'noweb-previous-code-chunk
)
238 (define-key map
"d" 'noweb-next-doc-chunk
)
239 (define-key map
"D" 'noweb-previous-doc-chunk
)
240 (define-key map
"g" 'noweb-goto-chunk
)
241 (define-key map
"\C-l" 'noweb-update-chunk-vector
)
242 (define-key map
"\M-l" 'noweb-update-chunk-vector
)
243 (define-key map
"w" 'noweb-copy-chunk-as-kill
)
244 (define-key map
"W" 'noweb-copy-chunk-pair-as-kill
)
245 (define-key map
"k" 'noweb-kill-chunk
)
246 (define-key map
"K" 'noweb-kill-chunk-pair
)
247 (define-key map
"m" 'noweb-mark-chunk
)
248 (define-key map
"M" 'noweb-mark-chunk-pair
)
249 (define-key map
"n" 'noweb-narrow-to-chunk
)
250 (define-key map
"N" 'noweb-narrow-to-chunk-pair
)
251 (define-key map
"t" 'noweb-toggle-narrowing
)
252 (define-key map
"\t" 'noweb-complete-chunk
)
253 (define-key map
"q" 'noweb-fill-chunk
)
254 (define-key map
"i" 'noweb-new-chunk
)
255 (define-key map
"o" 'noweb-occur
)
256 (define-key map
"v" 'noweb-mode-version
)
257 (define-key map
"h" 'noweb-describe-mode
)
258 ;; do *NOT* override C-h (give all keybindings startings with M-n!
260 "noweb minor-mode prefix keymap")
262 (defvar noweb-minor-mode-map
263 (let ((map (make-sparse-keymap)))
264 (if noweb-electric-
@-and-
<
266 (define-key map
"@" 'noweb-electric-
@)
267 (define-key map
"<" 'noweb-electric-
<)))
268 (define-key map
"\M-q" 'noweb-fill-paragraph-chunk
)
269 ;;(define-key map "\C-c\C-n" 'noweb-indent-line) ; Override TeX-normal!
270 (define-key map
[tab] 'noweb-indent-line)
271 (define-key map [return] 'noweb-newline)
272 (define-key map [mouse-1] 'noweb-mouse-first-button)
273 (define-key map noweb-mode-prefix noweb-mode-prefix-map)
275 "Noweb minor mode keymap")
278 noweb-minor-mode-menu noweb-minor-mode-map
279 "Menu keymap for noweb."
282 ["Previous chunk" noweb-previous-chunk t]
283 ["Next chunk" noweb-next-chunk t]
284 ["Previous chunk of same name" noweb-goto-previous t]
285 ["Next chunk of same name" noweb-goto-next t]
286 ["Goto chunk" noweb-goto-chunk t]
287 ["Previous code chunk" noweb-previous-code-chunk t]
288 ["Next code chunk" noweb-next-code-chunk t]
289 ["Previous documentation chunk" noweb-previous-doc-chunk t]
290 ["Next documentation chunk" noweb-next-doc-chunk t])
292 ["Copy chunk" noweb-copy-chunk-as-kill t]
293 ["Copy chunk pair" noweb-copy-chunk-pair-as-kill t]
294 ["Kill chunk" noweb-kill-chunk t]
295 ["Kill chunk pair" noweb-kill-chunk-pair t]
296 ["Mark chunk" noweb-mark-chunk t]
297 ["Mark chunk pair" noweb-mark-chunk-pair t])
299 ["Narrow to chunk" noweb-narrow-to-chunk t]
300 ["Narrow to chunk pair" noweb-narrow-to-chunk-pair t]
301 ["Toggle auto narrowing" noweb-toggle-narrowing t]
304 ["Set documentation mode" noweb-set-doc-mode t]
305 ["Set default code mode" noweb-set-code-mode t]
306 ["Set code mode for this chunk" noweb-set-this-code-mode t]
307 ["Insert default mode line" noweb-insert-default-mode-line t])
309 ["Tangle current chunk" noweb-tangle-chunk t]
310 ["Tangle current thread" noweb-tangle-current-thread t]
311 ["Tangle named thread" noweb-tangle-thread t])
313 ["Complete chunk name" noweb-complete-chunk t]
314 ["Fill current chunk" noweb-fill-chunk t]
315 ["Insert new chunk" noweb-new-chunk t]
316 ["Update the chunk vector" noweb-update-chunk-vector t]
317 ["Chunk occurrences" noweb-occur t])
319 ["Help" noweb-describe-mode t]
320 ["Version" noweb-mode-version t]))
322 ;; Add noweb-mode to the list of minor modes
323 (if (not (assq 'noweb-mode minor-mode-alist))
324 (setq minor-mode-alist (append minor-mode-alist
325 (list '(noweb-mode " Noweb")))))
326 ;; Add noweb-minor-mode-map to the list of minor-mode keymaps
327 ;; available. Then, whenever noweb-mode is activated, the keymap is
328 ;; automatically activated
329 (if (not (assq 'noweb-mode minor-mode-map-alist))
330 (setq minor-mode-map-alist
331 (cons (cons 'noweb-mode noweb-minor-mode-map)
332 minor-mode-map-alist)))
335 (defun noweb-mode-xemacs-menu ()
336 "Hook to install noweb-mode menu for XEmacs (w/ easymenu)."
338 (easy-menu-add noweb-minor-mode-menu)
339 (easy-menu-remove noweb-minor-mode-menu)
342 (if (string-match "XEmacs" emacs-version)
344 (add-hook 'noweb-select-mode-hook 'noweb-mode-xemacs-menu)
345 ;; Next line handles some random problems...
346 (easy-menu-add noweb-minor-mode-menu)))
348 (defun noweb-minor-mode (&optional arg)
349 "Minor meta mode for editing noweb files. See NOWEB-MODE."
351 (noweb-mode arg)) ; this was noweb-minor-mode??? (truly recursive)
353 (defun noweb-mode ( &optional arg )
354 "Minor meta mode for editing noweb files.
355 `Meta' refers to the fact that this minor mode is switching major
356 modes depending on the location of point.
358 The following special keystrokes are available in noweb mode:
361 \\[noweb-next-chunk] \tgoto the next chunk
362 \\[noweb-previous-chunk] \tgoto the previous chunk
363 \\[noweb-goto-previous] \tgoto the previous chunk of the same name
364 \\[noweb-goto-next] \tgoto the next chunk of the same name
365 \\[noweb-goto-chunk] \t\tgoto a chunk
366 \\[noweb-next-code-chunk] \t\tgoto the next code chunk
367 \\[noweb-previous-code-chunk] \t\tgoto the previous code chunk
368 \\[noweb-next-doc-chunk] \t\tgoto the next documentation chunk
369 \\[noweb-previous-doc-chunk] \t\tgoto the previous documentation chunk
371 Copying/Killing/Marking/Narrowing:
372 \\[noweb-copy-chunk-as-kill] \t\tcopy the chunk the point is in into the kill ring
373 \\[noweb-copy-chunk-pair-as-kill] \t\tcopy the pair of doc/code chunks the point is in
374 \\[noweb-kill-chunk] \t\tkill the chunk the point is in
375 \\[noweb-kill-chunk-pair] \t\tkill the pair of doc/code chunks the point is in
376 \\[noweb-mark-chunk] \t\tmark the chunk the point is in
377 \\[noweb-mark-chunk-pair] \t\tmark the pair of doc/code chunks the point is in
378 \\[noweb-narrow-to-chunk] \t\tnarrow to the chunk the point is in
379 \\[noweb-narrow-to-chunk-pair] \t\tnarrow to the pair of doc/code chunks the point is in
381 \\[noweb-toggle-narrowing] \t\ttoggle auto narrowing
383 Filling and Indenting:
384 \\[noweb-fill-chunk] \tfill (or indent) the chunk at point according to mode
385 \\[noweb-fill-paragraph-chunk] \tfill the paragraph at point, restricted to chunk
386 \\[noweb-indent-line] \tindent the line at point according to mode
389 \\[noweb-insert-default-mode-line] \tinsert a line to set this file's code mode
390 \\[noweb-new-chunk] \t\tinsert a new chunk at point
391 \\[noweb-complete-chunk] \tcomplete the chunk name before point
392 \\[noweb-electric-@] \t\tinsert a `@' or start a new doc chunk
393 \\[noweb-electric-<] \t\tinsert a `<' or start a new code chunk
396 \\[noweb-set-doc-mode] \t\tset the major mode for editing doc chunks
397 \\[noweb-set-code-mode] \tset the major mode for editing code chunks
398 \\[noweb-set-this-code-mode] \tset the major mode for editing this code chunk
401 \\[noweb-occur] \t\tfind all occurrences of the current chunk
402 \\[noweb-update-chunk-vector] \tupdate the markers for chunks
403 \\[noweb-describe-mode] \tdescribe noweb-mode
404 \\[noweb-mode-version] \t\tshow noweb-mode's version in the minibuffer
406 ; This bit is tricky: copied almost verbatim from bib-cite-mode.el
407 ; It seems to ensure that the variable noweb-mode is made
408 ; local to this buffer. It then sets noweb-mode to `t' if
409 ; 1) It was called with an argument greater than 0
410 ; or 2) It was called with no argument, and noweb-mode is
412 ; noweb-mode is nil if the argument was <= 0 or there
413 ; was no argument and noweb-mode is currently `t'
414 (set (make-local-variable 'noweb-mode)
416 (> (prefix-numeric-value arg) 0)
418 ; Now, if noweb-mode is true, we want to turn
421 (noweb-mode ;Setup the minor-mode
422 (mapcar 'noweb-make-variable-permanent-local
424 after-change-functions
429 isearch-mode-end-hook
432 noweb-default-code-mode
433 noweb-last-chunk-index))
434 (noweb-update-chunk-vector)
435 (if (equal 0 (noweb-find-chunk-index-buffer))
436 (setq noweb-last-chunk-index 1)
437 (setq noweb-last-chunk-index 0))
441 (noweb-font-lock-mode 1)))
442 (add-hook 'post-command-hook 'noweb-post-command-function)
443 (add-hook 'after-change-functions 'noweb-after-change-function)
444 (add-hook 'noweb-select-doc-mode-hook 'noweb-auto-fill-doc-mode)
445 (add-hook 'noweb-select-code-mode-hook 'noweb-auto-fill-code-mode)
446 (add-hook 'isearch-mode-hook 'noweb-note-isearch-mode)
447 (add-hook 'isearch-mode-end-hook 'noweb-note-isearch-mode-end)
448 (setq noweb-doc-mode-syntax-table nil)
449 (run-hooks 'noweb-mode-hook)
451 "noweb mode: use `M-x noweb-describe-mode' for further information"))
452 ;; If we didn't do the above, then we want to turn noweb-mode
453 ;; off, no matter what (hence the condition `t')
455 (remove-hook 'post-command-hook 'noweb-post-command-function)
456 (remove-hook 'after-change-functions 'noweb-after-change-function)
457 (remove-hook 'noweb-select-doc-mode-hook 'noweb-auto-fill-doc-mode)
458 (remove-hook 'noweb-select-code-mode-hook 'noweb-auto-fill-code-mode)
459 (remove-hook 'isearch-mode-hook 'noweb-note-isearch-mode)
460 (remove-hook 'isearch-mode-end-hook 'noweb-note-isearch-mode-end)
461 (if noweb-font-lock-mode
463 (noweb-font-lock-mode -1)
464 (message "Noweb and Noweb-Font-Lock Modes Removed"))
465 (message "Noweb mode removed")))))
467 (defun noweb-make-variable-permanent-local (var)
468 "Declare VAR buffer local, but protect it from beeing killed
469 by major mode changes."
470 (make-variable-buffer-local var)
471 (put var 'permanent-local 't))
473 (defun noweb-note-isearch-mode ()
474 "Take note of an incremental search in progress"
475 (remove-hook 'post-command-hook 'noweb-post-command-function))
477 (defun noweb-note-isearch-mode-end ()
478 "Take note of an incremental search having ended"
479 (add-hook 'post-command-hook 'noweb-post-command-function))
481 (defun noweb-post-command-function ()
482 "The hook being run after each command in noweb mode."
485 (defun noweb-after-change-function (begin end length)
486 "Function to run after every change in a noweb buffer.
487 If the changed region contains a chunk start (^@ or ^<<), it will
488 update the chunk vector"
491 (if (re-search-forward "^\\(@[^@]\\)\\|\\(<<\\)" end t)
492 (noweb-update-chunk-vector))))
497 (defun noweb-update-chunk-vector ()
498 "Scan the whole buffer and place a marker at each \"^@\" and \"^<<\".
499 Record them in NOWEB-CHUNK-VECTOR."
502 (goto-char (point-min))
503 (let ((chunk-list (list (cons 'doc (point-marker)))))
504 (while (re-search-forward "^\\(@\\( \\|$\\|\\( %def\\)\\)\\|<<\\(.*\\)>>=\\)" nil t)
505 (goto-char (match-beginning 0))
506 ;; If the 3rd subexpression matched @ %def, we're still in a code
507 ;; chunk (sort of), so don't place a marker here.
508 (if (not (match-beginning 3))
510 ;; If the 4th subexpression matched inside <<...>>,
511 ;; we're seeing a new code chunk.
512 (cons (cons (if (match-beginning 4)
513 ;;buffer-substring-no-properties better
514 ;;than buffer-substring if highlighting
516 (buffer-substring-no-properties
517 (match-beginning 4) (match-end 4))
521 ;; Scan forward either to !/^@ %def/, which will start a docs chunk,
522 ;; or to /^<<.*>>=$/, which will start a code chunk.
525 (while (looking-at "@ %def")
528 ;; Now we can tell code vs docs
529 (cons (cons (if (looking-at "<<\\(.*\\)>>=")
530 (buffer-substring-no-properties
531 (match-beginning 1) (match-end 1))
536 (setq chunk-list (cons (cons 'doc (point-max-marker)) chunk-list))
537 (setq noweb-chunk-vector (vconcat (reverse chunk-list))))))
539 (defun noweb-find-chunk ()
540 "Return a pair consisting of the name (or 'DOC) and the
541 marker of the current chunk."
542 (if (not noweb-chunk-vector)
543 (noweb-update-chunk-vector))
544 (aref noweb-chunk-vector (noweb-find-chunk-index-buffer)))
546 (defun noweb-chunk-is-code (index)
547 "Return t if the chunk 'index' is a code chunk, nil otherwise"
549 (stringp (car (noweb-chunk-vector-aref index))))
551 (defun noweb-in-code-chunk ()
552 "Return t if we are in a code chunk, nil otherwise."
554 (noweb-chunk-is-code (noweb-find-chunk-index-buffer)))
556 (defun noweb-in-mode-line ()
557 "Return the name of the mode to use if we are in a mode line, nil
562 (beginning-of-line 1)
564 (ess-write-to-dribble-buffer
565 (format "(n-i-m-l: 1)"))
566 (search-forward "-*-"
567 (save-excursion (end-of-line) (point))
570 (ess-write-to-dribble-buffer
571 (format "(n-i-m-l: 2)"))
572 (skip-chars-forward " \t")
574 (search-forward "-*-"
575 (save-excursion (end-of-line) (point))
578 (ess-write-to-dribble-buffer
579 (format "(n-i-m-l: 3)"))
581 (skip-chars-backward " \t")
585 (downcase (buffer-substring beg end))
587 (if (and (>= (length mode) 11))
590 (equal (substring mode -10 -5) "-mode")
591 (setq mode (substring mode 0 -5)))
593 (equal (substring mode 0 5) "mode:")
594 (setq mode (substring mode 6))))))
596 (ess-write-to-dribble-buffer
597 (format "(n-i-m-l: 3) mode=%s" mode))
600 (defun noweb-find-chunk-index-buffer ()
601 "Return the index of the current chunk in NOWEB-CHUNK-VECTOR."
602 (noweb-find-chunk-index 0 (1- (length noweb-chunk-vector))))
604 (defun noweb-find-chunk-index (low hi)
607 (let ((med (/ (+ low hi) 2)))
608 (if (< (point) (cdr (aref noweb-chunk-vector med)))
609 (noweb-find-chunk-index low med)
610 (noweb-find-chunk-index med hi)))))
612 (defun noweb-chunk-region ()
613 "Return a pair consisting of the beginning and end of the current chunk."
615 (let ((start (noweb-find-chunk-index-buffer)))
616 (cons (marker-position (cdr (aref noweb-chunk-vector start)))
617 (marker-position (cdr (aref noweb-chunk-vector (1+ start)))))))
619 (defun noweb-copy-code-chunk ()
620 "Copy the current code chunk to the kill ring, excluding the chunk name.
621 This will be particularly useful when interfacing with ESS."
623 (let ((r (noweb-chunk-region)))
626 (if (noweb-in-code-chunk)
628 (beginning-of-line 2)
629 (copy-region-as-kill (point) (cdr r)))))))
631 (defun noweb-extract-code-chunk ()
632 "Create a new buffer with the same name as the current code chunk,
633 and copy all code from chunks of the same name to it."
636 (if (noweb-in-code-chunk)
638 (let ((chunk-name (car (noweb-find-chunk)))
641 (this-chunk) (oldbuf (current-buffer)))
642 (if (get-buffer chunk-name)
644 (set-buffer-modified-p nil)
645 (kill-buffer chunk-name)))
646 (get-buffer-create chunk-name)
647 (message "Created buffer %s" chunk-name)
648 (while (< chunk-counter (- (length noweb-chunk-vector) 2))
649 (setq this-chunk (noweb-chunk-vector-aref
651 (message "Current buffer is %s" (car this-chunk))
652 (if (equal chunk-name (car this-chunk))
654 (setq copy-counter (+ copy-counter 1))
655 (goto-char (cdr this-chunk))
656 (noweb-copy-code-chunk)
657 (set-buffer chunk-name)
658 (goto-char (point-max))
660 (set-buffer oldbuf)))
661 (setq chunk-counter (+ chunk-counter 1)))
662 (message "Copied %d bits" copy-counter)
663 (set-buffer chunk-name)
664 (copy-region-as-kill (point-min)(point-max)))))))
666 (defun noweb-chunk-pair-region ()
667 "Return a pair consisting of the beginning and end of the current pair of
668 documentation and code chunks."
670 (let* ((start (noweb-find-chunk-index-buffer))
672 (if (noweb-chunk-is-code start)
673 (cons (marker-position (cdr (aref noweb-chunk-vector (1- start))))
674 (marker-position (cdr (aref noweb-chunk-vector end))))
675 (while (not (noweb-chunk-is-code end))
677 (cons (marker-position (cdr (aref noweb-chunk-vector start)))
678 (marker-position (cdr (aref noweb-chunk-vector (1+ end))))))))
680 (defun noweb-chunk-vector-aref (i)
682 (error "Before first chunk."))
683 (if (>= i (length noweb-chunk-vector))
684 (error "Beyond last chunk."))
685 (aref noweb-chunk-vector i))
687 (defun noweb-complete-chunk ()
688 "Complete the chunk name before point, if any."
690 (if (noweb-in-code-chunk)
693 (if (re-search-backward "<<"
701 (let* ((pattern (buffer-substring beg end))
702 (alist (noweb-build-chunk-alist))
703 (completion (try-completion pattern alist)))
704 (cond ((eq completion t))
706 (message "Can't find completion for \"%s\"" pattern)
708 ((not (string= pattern completion))
709 (delete-region beg end)
711 (if (not (looking-at ">>"))
714 (message "Making completion list...")
715 (with-output-to-temp-buffer "*Completions*"
716 (display-completion-list (all-completions pattern alist)))
717 (message "Making completion list...%s" "done"))))
718 (message "Not at chunk name...")))
719 (message "Not in code chunk...")))
724 (defun noweb-hide-code-quotes ()
725 "Replace all non blank characters in [[...]] code quotes
726 in the current buffer (you might want to narrow to the interesting
727 region first) by `*'. Return a list of pairs with the position and
728 value of the original strings."
730 (let ((quote-list nil))
731 (goto-char (point-min))
732 (while (re-search-forward "\\[\\[" nil 'move)
733 (let ((beg (match-end 0))
734 (end (if (re-search-forward "\\]\\]" nil t)
738 (while (< (point) end)
739 ;; Move on to the next word:
741 (skip-chars-forward " \t\n" end)
744 (skip-chars-forward "^ \t\n" end)
747 ;; Save the string and a marker to the end of the
748 ;; replacement text. A marker to the beginning is
749 ;; useless. See NOWEB-RESTORE-CODE-QUOTES.
751 (setq quote-list (cons (cons (copy-marker e)
752 (buffer-substring b e))
755 (insert-char ?* (- e b) t)
756 (delete-char (- e b))))))))
757 (reverse quote-list))))
759 (defun noweb-restore-code-quotes (quote-list)
760 "Reinsert the strings modified by `noweb-hide-code-quotes'."
763 (let* ((e (marker-position (car q)))
764 ;; Slightly inefficient, but correct way to find
765 ;; the beginning of the word to be replaced.
766 ;; Using the marker at the beginning will loose
767 ;; if whitespace has been rearranged
770 (skip-chars-backward "*")
777 (defun noweb-fill-chunk ()
778 "Fill the current chunk according to mode.
779 Run `fill-region' on documentation chunks and `indent-region' on code
784 (noweb-narrow-to-chunk)
785 (if (noweb-in-code-chunk)
787 ;; Narrow to the code section proper; w/o the first and any
788 ;; index declaration lines.
789 (narrow-to-region (progn
790 (goto-char (point-min))
794 (goto-char (point-max))
796 (while (looking-at "@")
800 (if (or indent-region-function indent-line-function)
801 (indent-region (point-min) (point-max) nil)
802 (error "No indentation functions defined in %s!" major-mode)))
803 (let ((quote-list (noweb-hide-code-quotes)))
804 (fill-region (point-min) (point-max))
805 (noweb-restore-code-quotes quote-list))))))
807 (defun noweb-indent-line ()
808 "Indent the current line according to mode, after narrowing to this chunk."
811 (noweb-narrow-to-chunk)
812 (if (stringp (car (noweb-find-chunk)))
814 ;; Narrow to the code section proper; w/o the first and any
815 ;; index declaration lines.
817 (narrow-to-region (progn
818 (goto-char (point-min))
822 (goto-char (point-max))
824 (while (looking-at "@")
828 (indent-according-to-mode)))
830 (defun noweb-fill-paragraph-chunk (&optional justify)
831 "Fill a paragraph in the current chunk."
833 (noweb-update-chunk-vector)
836 (noweb-narrow-to-chunk)
837 (if (stringp (car (noweb-find-chunk)))
839 ;; Narrow to the code section proper; w/o the first and any
840 ;; index declaration lines.
841 (narrow-to-region (progn
842 (goto-char (point-min))
846 (goto-char (point-max))
848 (while (looking-at "@")
852 (fill-paragraph justify))
853 (let ((quote-list (noweb-hide-code-quotes)))
854 (fill-paragraph justify)
855 (noweb-restore-code-quotes quote-list))))))
857 (defun noweb-auto-fill-doc-chunk ()
858 "Replacement for `do-auto-fill'."
860 (narrow-to-region (car (noweb-chunk-region))
864 (let ((quote-list (noweb-hide-code-quotes)))
866 (noweb-restore-code-quotes quote-list))))
868 (defun noweb-auto-fill-doc-mode ()
869 "Install the improved auto fill function, iff necessary."
870 (if auto-fill-function
871 (setq auto-fill-function 'noweb-auto-fill-doc-chunk)))
873 (defun noweb-auto-fill-code-mode ()
874 "Install the default auto fill function, iff necessary."
875 (if auto-fill-function
876 (setq auto-fill-function 'do-auto-fill)))
880 (defun noweb-mark-chunk ()
881 "Mark the current chunk."
883 (let ((r (noweb-chunk-region)))
885 (push-mark (cdr r) nil t)))
887 (defun noweb-mark-chunk-pair ()
888 "Mark the current pair of documentation and code chunks."
890 (let ((r (noweb-chunk-pair-region)))
892 (push-mark (cdr r) nil t)))
897 (defun noweb-toggle-narrowing (&optional arg)
898 "Toggle if we should narrow the display to the current pair of
899 documentation and code chunks after each movement. With argument:
900 switch narrowing on."
902 (if (or arg (not noweb-narrowing))
904 (setq noweb-narrowing t)
905 (noweb-narrow-to-chunk-pair))
906 (setq noweb-narrowing nil)
909 (defun noweb-narrow-to-chunk ()
910 "Narrow the display to the current chunk."
912 (let ((r (noweb-chunk-region)))
913 (narrow-to-region (car r) (cdr r))))
915 (defun noweb-narrow-to-chunk-pair ()
916 "Narrow the display to the current pair of documentation and code chunks."
918 (let ((r (noweb-chunk-pair-region)))
919 (narrow-to-region (car r) (cdr r))))
924 (defun noweb-kill-chunk ()
925 "Kill the current chunk."
927 (let ((r (noweb-chunk-region)))
928 (kill-region (car r) (cdr r))))
930 (defun noweb-kill-chunk-pair ()
931 "Kill the current pair of chunks."
933 (let ((r (noweb-chunk-pair-region)))
934 (kill-region (car r) (cdr r))))
936 (defun noweb-copy-chunk-as-kill ()
937 "Place the current chunk on the kill ring."
939 (let ((r (noweb-chunk-region)))
940 (copy-region-as-kill (car r) (cdr r))))
942 (defun noweb-copy-chunk-pair-as-kill ()
943 "Place the current pair of chunks on the kill ring."
945 (let ((r (noweb-chunk-pair-region)))
946 (copy-region-as-kill (car r) (cdr r))))
951 (defun noweb-sign (n)
952 "Return the sign of N."
955 (defun noweb-next-doc-chunk (&optional cnt)
956 "Goto to the Nth documentation chunk from point."
959 (let ((start (noweb-find-chunk-index-buffer))
961 (while (<= i (abs cnt))
962 (setq start (+ (noweb-sign cnt) start))
963 (while (noweb-chunk-is-code start)
964 (setq start (+ (noweb-sign cnt) start)))
966 (goto-char (marker-position (cdr (noweb-chunk-vector-aref start))))
969 (noweb-narrow-to-chunk-pair)))
971 (defun noweb-previous-doc-chunk (&optional n)
972 "Goto to the -Nth documentation chunk from point."
974 (noweb-next-doc-chunk (- n)))
976 (defun noweb-next-code-chunk (&optional cnt)
977 "Goto to the Nth code chunk from point."
980 (let ((start (noweb-find-chunk-index-buffer))
982 (while (<= i (abs cnt))
983 (setq start (+ (noweb-sign cnt) start))
984 (while (not (noweb-chunk-is-code start))
985 (setq start (+ (noweb-sign cnt) start)))
987 (goto-char (marker-position (cdr (noweb-chunk-vector-aref start))))
990 (noweb-narrow-to-chunk-pair)))
992 (defun noweb-previous-code-chunk (&optional n)
993 "Goto to the -Nth code chunk from point."
995 (noweb-next-code-chunk (- n)))
997 (defun noweb-next-chunk (&optional n)
998 "If in a documentation chunk, goto to the Nth documentation
999 chunk from point, else goto to the Nth code chunk from point."
1001 (if (noweb-in-code-chunk)
1002 (noweb-next-code-chunk n)
1003 (noweb-next-doc-chunk n)))
1005 (defun noweb-previous-chunk (&optional n)
1006 "If in a documentation chunk, goto to the -Nth documentation
1007 chunk from point, else goto to the -Nth code chunk from point."
1009 (noweb-next-chunk (- n)))
1011 (defvar noweb-chunk-history nil
1014 (defun noweb-goto-chunk ()
1015 "Goto the named chunk."
1018 (let* ((completion-ignore-case t)
1019 (alist (noweb-build-chunk-alist))
1020 (chunk (completing-read
1021 "Chunk: " alist nil t
1022 (noweb-goto-chunk-default)
1023 noweb-chunk-history)))
1024 (goto-char (cdr (assoc chunk alist))))
1026 (noweb-narrow-to-chunk-pair)))
1028 (defun noweb-goto-chunk-default ()
1030 (if (re-search-backward "<<"
1035 (goto-char (match-beginning 0)))
1036 (if (re-search-forward "<<\\(.*\\)>>"
1041 (buffer-substring (match-beginning 1) (match-end 1))
1044 (defun noweb-build-chunk-alist ()
1045 (if (not noweb-chunk-vector)
1046 (noweb-update-chunk-vector))
1047 ;; The naive recursive solution will exceed MAX-LISP-EVAL-DEPTH in
1048 ;; buffers w/ many chunks. Maybe there is a tail recursivce solution,
1049 ;; but iterative solutions should be acceptable for dealing with vectors.
1051 (i (1- (length noweb-chunk-vector))))
1053 (let* ((chunk (aref noweb-chunk-vector i))
1055 (marker (cdr chunk)))
1056 (if (and (stringp name)
1057 (not (assoc name alist)))
1058 (setq alist (cons (cons name marker) alist))))
1062 (defun noweb-goto-next (&optional cnt)
1063 "Goto the continuation of the current chunk."
1066 (if (not noweb-chunk-vector)
1067 (noweb-update-chunk-vector))
1068 (let ((start (noweb-find-chunk-index-buffer)))
1069 (if (not (noweb-chunk-is-code start))
1070 (setq start (1+ start)))
1071 (if (noweb-chunk-is-code start)
1072 (let ((name (car (noweb-chunk-vector-aref start)))
1074 (while (<= i (abs cnt))
1075 (setq start (+ (noweb-sign cnt) start))
1076 (while (not (equal (car (noweb-chunk-vector-aref start))
1078 (setq start (+ (noweb-sign cnt) start)))
1080 (goto-char (marker-position
1081 (cdr (noweb-chunk-vector-aref start))))
1084 (noweb-narrow-to-chunk-pair)))
1086 (defun noweb-goto-previous (&optional cnt)
1087 "Goto the previous chunk."
1089 (noweb-goto-next (- cnt)))
1091 (defun noweb-occur (arg)
1092 "Find all occurences of the current chunk.
1093 This function simply runs OCCUR on \"<<NAME>>\"."
1095 (let ((n (if (and arg
1099 (idx (noweb-find-chunk-index-buffer)))
1100 (if (noweb-chunk-is-code idx)
1101 (occur (regexp-quote (concat "<<"
1102 (car (aref noweb-chunk-vector idx))
1106 (while (not (noweb-chunk-is-code idx))
1107 (setq idx (1+ idx)))
1108 (occur (regexp-quote (concat "<<"
1109 (car (aref noweb-chunk-vector idx))
1116 (defun noweb-new-chunk (name)
1117 "Insert a new chunk."
1118 (interactive "sChunk name: ")
1119 (insert "@ \n<<" name ">>=\n")
1121 (insert "@ %def \n"))
1122 (noweb-update-chunk-vector))
1124 (defun noweb-at-beginning-of-line ()
1125 (equal (save-excursion
1130 (defun noweb-electric-@ (arg)
1131 "Smart incarnation of `@', starting a new documentation chunk, maybe.
1132 If given an numerical argument, it will act just like the dumb `@'.
1133 Otherwise and if at the beginning of a line in a code chunk:
1134 insert \"@ \" and update the chunk vector."
1137 (self-insert-command (if (numberp arg) arg 1))
1138 (if (and (noweb-at-beginning-of-line)
1139 (noweb-in-code-chunk))
1142 (noweb-update-chunk-vector))
1143 (self-insert-command 1))))
1145 (defun noweb-electric-< (arg)
1146 "Smart incarnation of `<', starting a new code chunk, maybe.
1147 If given an numerical argument, it will act just like the dumb `<'.
1148 Otherwise and if at the beginning of a line in a documentation chunk:
1149 insert \"<<>>=\" and a newline if necessary. Leave point in the middle
1150 and and update the chunk vector."
1153 (self-insert-command (if (numberp arg) arg 1))
1154 (if (and (noweb-at-beginning-of-line)
1155 (not (stringp (car (noweb-find-chunk)))))
1160 (if (not (looking-at "\\s *$"))
1162 (noweb-update-chunk-vector))
1163 (self-insert-command 1))))
1168 (defun noweb-set-chunk-code-mode ()
1169 "Set the noweb-code-mode for the current chunk"
1171 (if (noweb-in-code-chunk)
1173 ;; Reset code-mode to default and then check for a mode comment.
1174 (setq noweb-code-mode noweb-default-code-mode)
1175 (let (mode chunk-name)
1179 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1180 (setq chunk-name (match-string 1))
1182 (goto-char (point-min))
1183 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1184 (beginning-of-line 2)
1185 (setq mode (noweb-in-mode-line))
1186 (if (functionp mode)
1187 (setq noweb-code-mode mode))))))
1188 (error "This only makes sense in a code chunk")))
1190 (defun noweb-set-doc-syntax-table ()
1191 "Sets the doc-mode syntax-table to treat code quotes as comments."
1193 (let ((square-bracket-string (char-to-string (char-syntax ?\[))))
1194 (if (string= square-bracket-string "(")
1196 (modify-syntax-entry ?\[ "(]12b" noweb-doc-mode-syntax-table)
1197 (modify-syntax-entry ?\] ")[34b" noweb-doc-mode-syntax-table))
1199 (modify-syntax-entry ?\[
1200 (concat square-bracket-string " 12b")
1201 noweb-doc-mode-syntax-table)
1202 (modify-syntax-entry ?\]
1203 (concat square-bracket-string " 34b")
1204 noweb-doc-mode-syntax-table)))))
1206 (defun noweb-select-mode ()
1207 "Select NOWEB-DOC-MODE or NOWEB-CODE-MODE, as appropriate."
1209 (let ((this-chunk-index (noweb-find-chunk-index-buffer)))
1210 ;; Has the last change to the buffer taken us into a different
1212 (if (not (equal this-chunk-index noweb-last-chunk-index))
1214 (setq noweb-last-chunk-index this-chunk-index)
1215 (if (noweb-in-code-chunk)
1216 ;; Inside a code chunk
1218 ;; Find out which code mode to use
1219 (noweb-set-chunk-code-mode)
1220 ;; If we aren't already using it, use it.
1221 (if (not (equal major-mode noweb-code-mode))
1223 (funcall noweb-code-mode)
1224 (run-hooks 'noweb-select-mode-hook)
1225 (run-hooks 'noweb-select-code-mode-hook))))
1226 ;; Inside a documentation chunk
1228 (if (not (equal major-mode noweb-doc-mode))
1230 (funcall noweb-doc-mode)))
1231 (if (not noweb-doc-mode-syntax-table)
1233 (message "Setting up syntax table")
1234 (setq noweb-doc-mode-syntax-table
1235 (make-syntax-table (syntax-table)))
1236 (noweb-set-doc-syntax-table)))
1237 (set-syntax-table noweb-doc-mode-syntax-table)
1238 (run-hooks 'noweb-select-mode-hook)
1239 (run-hooks 'noweb-select-doc-mode-hook)))
1240 (run-hooks 'noweb-changed-chunk-hook)))))
1242 (defvar noweb-doc-mode noweb-default-doc-mode
1243 "Default major mode for editing noweb documentation chunks.
1244 It is not possible to have more than one doc-mode in a file.
1245 However, this variable is used to determine whether the doc-mode needs
1246 to by added to the mode-line")
1248 (defun noweb-set-doc-mode (mode)
1249 "Change the major mode for editing documentation chunks."
1250 (interactive "CNew major mode for documentation chunks: ")
1251 (setq noweb-doc-mode mode)
1252 (setq noweb-doc-mode-syntax-table nil)
1253 ;;Pretend we've changed chunk, so the mode will be reset if necessary
1254 (setq noweb-last-chunk-index (1- noweb-last-chunk-index))
1255 (noweb-select-mode))
1257 (defun noweb-set-code-mode (mode)
1258 "Change the major mode for editing all code chunks."
1259 (interactive "CNew major mode for all code chunks: ")
1260 (setq noweb-default-code-mode mode)
1261 ;;Pretend we've changed chunk, so the mode will be reset if necessary
1262 (setq noweb-last-chunk-index (1- noweb-last-chunk-index))
1263 (noweb-select-mode))
1265 (defun noweb-set-this-code-mode (mode)
1266 "Change the major mode for editing this code chunk.
1267 The only sensible way to do this is to add a mode line to the chunk"
1268 (interactive "CNew major mode for this code chunk: ")
1269 (if (noweb-in-code-chunk)
1271 (setq noweb-code-mode mode)
1277 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1278 (setq chunk-name (match-string 1))
1279 (goto-char (point-min))
1280 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1281 (beginning-of-line 2))
1282 ;; remove mode-line, if there is one
1283 (if (noweb-in-mode-line)
1287 (if (not (equal noweb-code-mode noweb-default-code-mode))
1289 (setq mode (substring (symbol-name mode) 0 -5))
1290 ;; Need to set major mode so that we can comment out
1292 (funcall noweb-code-mode)
1293 (if (not (boundp 'comment-start))
1294 (setq comment-start "#"))
1295 (insert comment-start
1297 " -*- " comment-end "\n")))
1298 (setq noweb-last-chunk-index (1- noweb-last-chunk-index)))))
1299 (message "This only makes sense in a code chunk.")))
1303 (defun noweb-mode-version ()
1304 "Echo the RCS identification of noweb mode."
1306 (message "Thorsten's noweb-mode (PRERELEASE). RCS: %s"
1309 (defun noweb-describe-mode ()
1310 "Describe noweb mode."
1312 (describe-function 'noweb-mode))
1314 (defun noweb-insert-default-mode-line ()
1315 "Insert line that will set the noweb mode of this file in emacs.
1316 The file is set to use the current doc and default-code modes, so
1317 ensure they are set correctly (with noweb-set-code-mode and
1318 noweb-set-doc-mode) before calling this function"
1322 (if (noweb-in-mode-line)
1326 (if (not (eq major-mode noweb-doc-mode))
1327 (noweb-select-mode))
1328 (insert comment-start " -*- mode: noweb; noweb-default-code-mode: "
1329 (symbol-name noweb-default-code-mode)
1330 (if (not (eq noweb-doc-mode noweb-default-doc-mode))
1331 (concat "; noweb-doc-mode: " (symbol-name
1332 noweb-doc-mode) ";")
1334 " -*-" comment-end "\n"))
1335 (noweb-select-mode))
1337 (defun noweb-mouse-first-button (event)
1339 (mouse-set-point event)
1340 (if (and noweb-use-mouse-navigation
1343 (re-search-backward "^[\t ]*\\(<<\\)\\(.*\\)\\(>>\\)" nil t))
1345 (beginning-of-line) (point))))
1347 (if (< (point) (match-beginning 2))
1348 (let ((chunk-name (buffer-substring-no-properties
1351 (re-search-backward (concat "<<" chunk-name ">>") nil t))
1352 (if (and (<= (match-end 2) (point))
1353 (> (+ 2 (match-end 2)) (point)))
1354 (let ((chunk-name (buffer-substring-no-properties
1357 (re-search-forward (concat "<<" chunk-name ">>") nil t)))))))
1362 (defun noweb-log (s)
1363 (let ((b (current-buffer)))
1364 (switch-to-buffer (get-buffer-create "*noweb-log*"))
1365 (goto-char (point-max))
1366 (setq buffer-read-only nil)
1368 (setq buffer-read-only t)
1369 (switch-to-buffer b)))
1375 (defvar noweb-thread-alist nil
1376 "A list of threads in the current buffer.
1377 Each entry in the list contains 5 elements:
1378 1) The name of the threads
1379 2) The name of the immdiate parent thread in which it is used (nil if
1380 it is a \"top-level\" thread which is not used anywhere).
1381 3) The name of the top-level parent thread in which it is used (i.e. a
1382 thread in which it is used but which is not itself used anywhere:
1383 nil if this thread is not used anywhere.
1384 4) The format string to use to define line numbers in the output
1385 file of this thread. Should only be set if this thread is not used
1386 anywhere: if a thread is used as part of another thread, the parent
1387 thread's format string should be used.
1388 5) If this is nil, tabs are converted to spaces in the tangled
1389 file. If it is a number, tabs are copied to the tangled file
1390 unchanged, and tabs are also used for indentation, with the number
1391 of spaces per tab defined by this number. This MUST be set in order
1392 to tangle makefiles, which depend on tabs.Should only be set if
1393 this thread is not used anywhere. otherwise set to nil. "
1396 (defun noweb-update-thread-alist ()
1397 "Updates the list of threads in the current buffer.
1398 Each entry in the list contains 5 elements:
1399 1) The name of the thread
1400 2) The name of the immdiate parent thread in which it is used (nil if
1401 it is a \"top-level\" thread which is not used anywhere).
1402 3) The name of the top-level parent thread in which it is used (i.e. a
1403 thread in which it is used but which is not itself used anywhere:
1404 nil if this thread is not used anywhere.
1405 4) The format string to use to define line numbers in the output
1406 file of this thread. Should only be set if this thread is not used
1407 anywhere: if a thread is used as part of another thread, the parent
1408 thread's format string should be used.
1409 5) If this is nil, tabs are converted to spaces in the tangled
1410 file. If it is a number, tabs are copied to the tangled file
1411 unchanged, and tabs are also used for indentation, with the number
1412 of spaces per tab defined by this number. This MUST be set in order
1413 to tangle makefiles, which depend on tabs.Should only be set if
1414 this thread is not used anywhere. otherwise set to nil. "
1417 (goto-char (point-min))
1418 (let ((thread-alist) (thread-list-entry) (chunk-use-name)
1419 (current-thread) (new-thread-alist))
1420 (while (re-search-forward
1421 "^[ \t]*<<\\(.*\\)>>\\(=\\)?" nil t)
1422 (goto-char (match-beginning 0))
1423 ;; Is this the definition of a chunk ?
1424 (if (match-beginning 2)
1425 ;;We have a chunk definition
1427 ;; Get the thread name
1428 (setq current-thread
1429 (buffer-substring-no-properties (match-beginning 1)
1431 ;; Is this thread already in our list ?
1432 (if (assoc current-thread thread-alist)
1435 ;; If not, create an entry with 4 nils at the end
1436 (setq thread-list-entry
1437 (list (cons current-thread
1438 (make-list 4 nil))))
1439 ;; And add it to the list
1441 (append thread-alist thread-list-entry)))))
1443 ;; Not a definition but a use
1445 ;; Get the thread name
1446 (setq chunk-use-name
1447 (buffer-substring-no-properties (match-beginning 1)
1449 ;; Has the thread already been defined before being used ?
1450 (if (setq thread-list-entry (assoc chunk-use-name
1452 ;; If it has, set its parent to be the thread we are in at the moment
1453 (setcar (cdr thread-list-entry) current-thread)
1454 ;; If not, add it to the list, with its parent name and 3 nils
1456 (setq thread-list-entry
1457 (list (cons chunk-use-name
1458 (cons current-thread
1459 (make-list 3 nil)))))
1460 (setq thread-alist (append thread-alist thread-list-entry)))))
1462 ;;Go to the next line
1463 (beginning-of-line 2))
1464 ;; Now, the second element of each entry points to that thread's
1465 ;; immediate parent. Need to set it to the thread's ultimate
1467 (let ((thread-counter 0)
1469 (this-thread-parent))
1470 (while (<= thread-counter (1- (length thread-alist)))
1471 (setq this-thread (nth thread-counter thread-alist))
1472 (setq this-thread-parent (assoc
1473 (car (cdr this-thread))
1475 (while (not (equal nil (car (cdr this-thread-parent))))
1476 (setq this-thread-parent (assoc
1477 (car (cdr this-thread-parent))
1479 (setq this-thread (cons (car this-thread)
1480 (cons (car (cdr this-thread))
1481 (cons (car this-thread-parent)
1482 (nthcdr 2 this-thread)))))
1483 (setq new-thread-alist (append new-thread-alist (list this-thread)))
1484 (setq thread-counter (1+ thread-counter))))
1486 (setq noweb-thread-alist new-thread-alist))))
1489 ; Option setting functions to go here
1491 (defun noweb-set-thread-line-format ())
1493 (defun noweb-set-thread-tabs ())
1496 (defvar noweb-default-line-number-format nil
1497 "The format string to use to define line numbers in this thread.
1498 If nil, do not use line numbers.")
1500 (defvar noweb-default-line-number-skip-lines 0
1501 "The number of initial lines to output before the line number.
1502 This may be useful in shell scripts, where the first line (or two) must have a
1505 (defvar noweb-default-tab-width 8
1506 "If a number, convert tabs to that number of spaces in the output. If nil, let tabs through to the output unaltered.")
1508 (defvar noweb-line-number-format noweb-default-line-number-format
1509 "The format string to use to define line numbers in this thread.
1510 If nil, do not use line numbers.")
1512 (defvar noweb-line-number-skip-lines noweb-default-line-number-skip-lines
1513 "The number of initial lines to output before the line number.
1514 This may be useful in shell scripts, where the first line (or two) must have a
1517 (defvar noweb-tab-width noweb-default-tab-width
1518 "If a number, convert tabs to that number of spaces in the output. If nil, let tabs through to the output unaltered.")
1520 (defun noweb-get-thread-local-variables ()
1521 "Get the values of the variables that are local to a thread."
1526 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1527 (let ((chunk-name (match-string 1)))
1529 (goto-char (point-min))
1530 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1531 (beginning-of-line 2)
1532 (while (looking-at ".*-\*-.*-\*-")
1533 (let ((this-line (buffer-substring-no-properties
1535 (progn (end-of-line) (point)))))
1537 "mode:[ \t]*\\([^\t ]*\\)" this-line)
1538 (setq noweb-code-mode
1539 (if (featurep 'xemacs)
1540 (match-string 1 this-line)
1541 (match-string-no-properties 1 this-line))
1544 "noweb-line-number-format:[ \t]*\"\\([^\"]*\\)\"" this-line)
1545 (setq noweb-line-number-format
1546 (if (featurep 'xemacs)
1547 (match-string 1 this-line)
1548 (match-string-no-properties 1 this-line))
1551 "noweb-line-number-skip-lines:[ \t]*\\([^\t ]*\\)" this-line)
1552 (setq noweb-line-number-skip-lines
1554 (if (featurep 'xemacs)
1555 (match-string 1 this-line)
1556 (match-string-no-properties 1 this-line)))))
1558 "noweb-tab-width:[ \t]*\\([^\t ]*\\)" this-line)
1559 (setq noweb-tab-width
1561 (if (featurep 'xemacs)
1562 (match-string 1 this-line)
1563 (match-string-no-properties 1 this-line)))))
1564 (beginning-of-line 2)))))))
1566 (defun noweb-reset-thread-local-variables ()
1567 "Resets the thread-local variables to their default values"
1568 (setq noweb-tab-width noweb-default-tab-width)
1569 (setq noweb-line-number-format noweb-default-line-number-format)
1570 (setq noweb-line-number-skip-lines noweb-default-line-number-skip-lines))
1572 (defun noweb-write-line-number (line-number-format buffer)
1573 (if line-number-format
1575 (let ((this-line (count-lines (point-min)(point))))
1576 (while (string-match ".*\\(%L\\).*" line-number-format)
1577 (setq line-number-format
1579 (format "%d" this-line) t t line-number-format 1)))
1580 (while (string-match ".*\\(%F\\).*" line-number-format)
1581 (setq line-number-format
1583 (format "%s" (buffer-file-name)) t t line-number-format 1)))
1584 (while (string-match ".*\\(%N\\).*" line-number-format)
1585 (setq line-number-format
1586 (replace-match "\n" t t line-number-format 1)))
1589 (insert line-number-format))))))
1592 (defun noweb-tangle-chunk ( &optional buffer prefix-string)
1593 "Generate the code produced by this chunk, & any threads used in this chunk."
1596 (noweb-reset-thread-local-variables)
1597 (noweb-get-thread-local-variables)
1598 (noweb-update-chunk-vector)
1602 (re-search-forward "^@" nil t)
1605 ;;get name and start point of this chunk
1607 (re-search-backward "^<<\\([^>]*\\)>>=$" nil t)
1608 (beginning-of-line 2)
1610 (chunk-name (buffer-substring-no-properties
1612 (match-beginning 1)))
1613 ;; get end of this chunk
1614 ;; Get information we need about this thread
1615 (thread-info (assoc chunk-name noweb-thread-alist))
1616 (thread-tabs (nth 4 thread-info))
1617 (line-number-format (nth 3 thread-info))
1618 (thread-name-re) (post-chunk) (pre-chunk)
1620 (tangle-buffer (generate-new-buffer "Tangle Buffer")))
1623 (goto-char chunk-start)
1624 ;; If this is a mode-line, ignore it
1625 (while (looking-at ".*-\\*-.*-\\*-")
1626 (beginning-of-line 2))
1627 ;; If we want to include line numbers, write one
1628 (if line-number-format
1629 (while (> noweb-line-number-skip-lines 0)
1630 (append-to-buffer tangle-buffer
1636 (beginning-of-line 2)
1637 (1- noweb-line-number-skip-lines))
1638 (noweb-write-line-number line-number-format buffer))
1639 (message "Now at %d" (point))
1641 (while (< (point) chunk-end)
1642 (untabify (point) (save-excursion (beginning-of-line 2)(point)))
1643 ;; This RE gave me trouble. Without the `\"', it
1644 ;; recognised itself and so could not copy itself
1647 "\\([^\n\"@]*\\)<<\\(.*\\)\\(>>\\)\\([^\n\"]*\\)$")
1651 (setq thread-name-re
1655 (setq pre-chunk (match-string 1))
1657 (setq pre-chunk (concat prefix-string
1659 (setq post-chunk (match-string 4))
1661 (goto-char (point-min))
1662 (while (re-search-forward thread-name-re nil t)
1663 (noweb-tangle-chunk tangle-buffer pre-chunk)
1667 (set-buffer tangle-buffer)
1670 (beginning-of-line 2)))))
1672 ;; Otherwise, just copy this line
1677 (beginning-of-line 2)
1679 ;; Add a prefix if necessary
1680 (if (and prefix-string
1681 (> (length pre-chunk) 1))
1682 (setq pre-chunk (concat prefix-string
1684 ;; And copy it to the buffer
1686 (set-buffer tangle-buffer)
1687 (insert pre-chunk)))
1688 ;; If this is the first line of the chunk, we need to change
1689 ;; prefix-string to consist solely of spaces
1694 (make-string (length prefix-string) ?\ ))
1695 (setq first-line nil)))
1696 ;; Either way, go to the next line
1697 (beginning-of-line 2))
1700 (set-buffer tangle-buffer)
1701 (goto-char (point-min))
1702 (while (re-search-forward "\@\<<" nil t)
1703 (replace-match "<<" nil nil)
1707 (setq tab-width thread-tabs)
1708 (tabify (point-min)(point-max)))
1709 (untabify (point-min)(point-max))))
1714 (insert-buffer-substring tangle-buffer)
1715 (kill-buffer tangle-buffer)))
1718 (defun noweb-tangle-thread ( name &optional buffer)
1719 "Given the name of a thread, tangles the thread to buffer.
1720 If no buffer is given, create a new one with the same name as the
1722 (interactive "sWhich thread ? ")
1725 (setq buffer (get-buffer-create name))
1730 (goto-char (point-min))
1731 (let ((chunk-counter 0))
1732 (while (re-search-forward
1733 "^<<\\(.*\\)>>=[\t ]*" nil t)
1734 (if (string= (match-string 1)
1737 (setq chunk-counter (1+ chunk-counter))
1738 (message "Found %d chunks" chunk-counter)
1739 (noweb-tangle-chunk buffer)))))))
1741 (defun noweb-tangle-current-thread ( &optional buffer)
1746 (re-search-backward "^<<\\([^>]*\\)>>=[\t ]*$"
1748 (beginning-of-line 2)
1750 (chunk-name (buffer-substring-no-properties
1752 (match-beginning 1))))
1753 (noweb-tangle-thread chunk-name buffer))))
1759 (run-hooks 'noweb-mode-load-hook)
1760 (provide 'noweb-mode)
1762 ;; Changes made by Mark Lunt (mark.lunt@mrc-bsu.cam.ac.uk) 22/03/1999
1764 ;; The possibility of having code chunks using more than one language
1765 ;; was added. This was first developed by Adnan Yaqub
1766 ;; (AYaqub@orga.com) for syntax highlighting, but even people who hate
1767 ;; highlighting may like to maintain their Makefile with their code,
1768 ;; or test-scripts with their programs, or even user documentation as
1769 ;; latex-mode code chunks.
1770 ;; This required quite a few changes to noweb-mode:
1771 ;; 1) A new variable `noweb-default-code-mode' was create to do the job
1772 ;; `noweb-code-mode' used to.
1773 ;; 2) noweb-code-mode now contains the code-mode of the current chunk
1774 ;; 3) Each chunk can now have its own mode-line to tell emacs what
1775 ;; mode to use to edit it. The function `noweb-in-mode-line'
1776 ;; recognises such mode-lines, and the function
1777 ;; `noweb-set-this-code-mode' sets the code mode for the current
1778 ;; chunk and adds a mode-line if necessary. If several chunks have
1779 ;; the same name, the mode-line must appear in the first chunk with
1781 ;; 4) The mechanism for deciding whether to change mode was altered,
1782 ;; since the old method assumed a single code mode. Now,
1783 ;; `noweb-last-chunk-index' keeps track of which chunk we were in
1784 ;; last. If we have moved to a different chunk, we have to check
1785 ;; which mode we should be in, and change if necessary.
1787 ;; The keymap and menu-map handling was changed. Easymenu was used to
1788 ;; define the menu, and it the keymap was attached to the 'official'
1789 ;; minor-modes-keymaps list. This means that
1790 ;; 1) It was automatically loaded when noweb-mode was active and
1791 ;; unloaded when it was inactive.
1792 ;; 2) There was no need to worry about the major mode map clobbering
1793 ;; it , since it takes precedence over the major mode
1794 ;; map. `noweb-setup-keymap' is therefore now superfluous
1795 ;; The menu was also reorganised to make it less cluttered, so there
1796 ;; would be room for adding tangling and weaving commands (one day).
1798 ;; Mouse navigation (at least under Emacs (AJR)) is supported, in so
1799 ;; far as clicking mouse-1 on the '<<' of a chunk name moves to the
1800 ;; previous instance of that chunk name, and clicking in the '>>'
1801 ;; moves to the next instance. They are not mouse-hightlighted,
1802 ;; though: too much hassle for zero added functionality.
1804 ;; noweb-doc-mode has been given its own syntax-table. It is the same
1805 ;; as the current doc-mode syntax-table, except that [[ is a comment
1806 ;; start and ]] a comment end. Fixes some ugliness in LaTeX-mode if
1807 ;; `$' or `%' appear in quoted code (or even `<<', which happens often
1809 ;; (This should make noweb-hide-code-quotes and
1810 ;; noweb-restore-code-quotes unnecessary, but I have not yet removed
1811 ;; them, nor the calls to them).
1813 ;; A new function `noweb-indent-line' was defined and bound by default
1814 ;; to the tab key. This should indent the current line correctly in
1815 ;; whichever mode we are currently in. Previously, c-mode in
1816 ;; particular did not behave well with indentation (although
1817 ;; `noweb-fill-chunk' worked fine). Indentation is only accurate
1818 ;; within the chunk: it does not know the syntax at the end of the
1819 ;; previous chunk, so it does not know where to start indenting in
1820 ;; this chunk. However, provided the indentation within each chunk is correct,
1821 ;; notangle will correctly indented code.
1823 ;; (I think it would be good to separate filling and indenting,
1824 ;; though, since `indent-region' and `fill-region' have completely
1825 ;; different meanings in LaTeX-mode (and both are useful))
1827 ;; noweb-mode and noweb-minor-mode were given an optional argument, so
1828 ;; that (noweb-mode -1) turns it off, (noweb-mode 1) turns it on, and
1829 ;; (noweb-mode) toggles it. This is considered normal for minor modes.
1831 ;; buffer-substring changed to buffer-substring-no-properties:
1832 ;; comparisons with buffer-substring can be unreliable if highlighting
1835 ;; New functions `noweb-in-code-chunk' & `noweb-chunk-is-code' created
1836 ;; to replace (if (stringp (car (noweb-find-chunk)))) and
1837 ;; (if (stringp (car (noweb-chunk-vector-aref index)))).
1839 ;; `noweb-insert-mode-line' was renamed
1840 ;; `noweb-insert-default-mode-line' and modified to put the mode-line
1841 ;; at the start of the file and remove any existing mode-line.
1843 ;; a '<=' in `noweb-find-chunk-index' changed to '<', so we get the
1844 ;; right answer if point is on the first character in a chunk
1846 ;; The name of `noweb-post-command-hook' changed to
1847 ;; `noweb-post-command-function', since it is a function.
1849 ;; All the highlighting code moved to a separate file:
1850 ;; (noweb-font-lock-mode.el)
1852 ;; Menu driven tangling is in the process of being added. It can
1853 ;; currently tangle a single chunk or a series of chunks with the
1854 ;; same name (which I refer to as a thread) into a separate
1855 ;; buffer. This buffer can then be saved to a file, sent to an
1856 ;; interpreter, whatever. I haven't tested using line-numbers as yet.