*** empty log message ***
[ess.git] / lisp / ess-latex.el
blobcc522e66ecacc332867c331bd21f08907bb5fe2d
1 ;; ess-latex.el - Interface ESS and LaTeX.
3 ;; Copyright (C) 1999 by A.J. Rossini <rossini@biostat.washington.edu>
4 ;; based on noweb-mode by: Thorsten.Ohl @ Physik.TH-Darmstadt.de
5 ;; with a little help from Norman Ramsey <norman@bellcore.com>
6 ;; and Mark Lunt <mark.lunt@mrc-bsu.cam.ac.uk>
8 ;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>
10 ;; This file is part of ESS.
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program; if not, write to the Free Software
24 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 ;; See bottom of this file for information on language-dependent
27 ;; highlighting, and recent changes.
31 ;;; Variables
33 (defconst ess-latex-minor-mode-RCS-Id
34 "Imported to ESS Subversion repository and RCS ids not maintained.")
36 (defconst ess-latex-minor-mode-RCS-Name
37 " ")
39 (defvar ess-latex-minor-mode-prefix "\M-n"
40 "*Prefix key to use for noweb mode commands.
41 The value of this variable is checked as part of loading noweb mode.
42 After that, changing the prefix key requires manipulating keymaps.")
44 (defvar ess-latex-minor-mode-load-hook nil
45 "Hook that is run after noweb mode is loaded.")
47 (defvar ess-latex-mode-hook nil
48 "Hook that is run after entering ess-latex mode.")
50 (defvar ess-latex-select-code-mode-hook nil
51 "Hook that is run after the code mode is selected.
52 This is the place to overwrite keybindings of the ESS-LATEX-CODE-MODE.")
54 (defvar ess-latex-select-doc-mode-hook nil
55 "Hook that is run after the documentation mode is selected.
56 This is the place to overwrite keybindings of the ESS-LATEX-DOC-MODE.")
58 (defvar ess-latex-select-mode-hook nil
59 "Hook that is run after the documentation or the code mode is selected.
60 This is the place to overwrite keybindings of the other modes.")
62 (defvar ess-latex-changed-chunk-hook nil
63 "Hook that is run every time point moves from one chunk to another.
64 It will be run whether or not the major-mode changes.")
66 (defvar ess-latex-default-code-mode 'fundamental-mode
67 "Default major mode for editing code chunks.
68 This is set to FUNDAMENTAL-MODE by default, but you might want to
69 change this in the Local Variables section of your file to something
70 more appropriate, like C-MODE, FORTRAN-MODE, or even
71 INDENTED-TEXT-MODE.")
73 (defvar ess-latex-code-mode 'c-mode
74 "Major mode for editing this particular code chunk.
75 It defaults to ess-latex-default-code-mode, but can be reset by a comment
76 on the first line of the chunk containing the string
77 \"-*- NEWMODE -*-\" or
78 \"-*- NEWMODE-mode -*-\" or
79 \"-*- mode: NEWMODE -*- \" or
80 \"-*- mode: NEWMODE-mode -*- \"
81 Option three is recommended, as it is the closest to standard emacs usage.")
83 (defvar ess-latex-default-doc-mode 'latex-mode
84 "Major mode for editing documentation chunks.")
86 (defvar ess-latex-doc-mode-syntax-table nil
87 "A syntax-table syntax table that makes quoted code in doc chunks to behave.")
89 (defvar ess-latex-last-chunk-index 0
90 "This keeps track of the chunk we have just been in. If this is not the same as the current chunk, we have to check if we need to change major mode.")
92 (defvar ess-latex-chunk-vector nil
93 "Vector of the chunks in this buffer.")
95 (defvar ess-latex-narrowing nil
96 "If not NIL, the display will always be narrowed to the
97 current chunk pair.")
99 (defvar ess-latex-electric-@-and-< t
100 "If not nil, the keys `@' and `<' will be bound to ESS-LATEX-ELECTRIC-@
101 and ESS-LATEX-ELECTRIC-<, respectively.")
103 (defvar ess-latex-use-mouse-navigation t
104 "If not nil, enables moving between chunks using mouse-1.
105 Clicking on the '<<' at the beginning of a chunk name takes you to the
106 previous occurence of that chunk name, clicking on the '>>' takes you
107 to the next.
108 Assumes mouse-1 is bound to mouse-set-point, so if you have rebound
109 mouse-1, this will override your binding.")
111 ; \f
112 ; (defvar ess-latex-weave-options "-delay")
113 ; (defvar ess-latex-latex-viewer "xdvi")
114 ; (defvar ess-latex-html-viewer "netscape")
116 ; (defun ess-latex-weave (&optional name)
117 ; (interactive)
118 ; (let ((buffer (get-buffer-create "Weave Buffer")))
119 ; (if (not name)
120 ; (progn
121 ; ;; Assume latex documentation, but set to html if appropriate
122 ; (if (eq ess-latex-doc-mode html-mode)
123 ; (setq name (concat (substring (buffer-file-name) 0
124 ; (string-match ".nw" name))
125 ; ".html"))
126 ; (setq name (concat (substring (buffer-file-name) 0
127 ; (string-match ".nw" name))
128 ; ".tex")))))
129 ; (setq name (concat "> " name))
130 ; (setq ess-latex-weave-options (concat ess-latex-weave-options name))
131 ; (start-process weave-process buffer "noweave" ess-latex-weave-options)))
133 ; (defun ess-latex-view ())
135 ;;; Setup
136 (defvar ess-latex-mode nil
137 "Buffer local variable, T iff this buffer is edited in ess-latex mode.")
139 ;; For some reason that I do not understand, `newline' does not do the
140 ;; right thing in quoted code. If point is not preceded by whitespace,
141 ;; it moves to the beginning of the current line, not the beginning of
142 ;; the new line. `newline 1' works fine, hence the kludge. I'd love to
143 ;; understand what's going on, though. Try running M-x newline in the
144 ;; middle of a code quote in a doc chunk to see
145 ;; what I mean: its odd.
147 (defun ess-latex-newline (&optional arg)
148 "A kludge to get round very odd behaviour of newline in quoted code."
149 (interactive "p")
150 (if arg (newline arg) (newline 1)))
152 (defvar ess-latex-mode-prefix-map
153 (let ((map (make-sparse-keymap)))
154 (define-key map "\C-n" 'ess-latex-next-chunk)
155 (define-key map "\C-p" 'ess-latex-previous-chunk)
156 (define-key map "\M-n" 'ess-latex-goto-next)
157 (define-key map "\M-m" 'ess-latex-insert-default-mode-line)
158 (define-key map "\M-p" 'ess-latex-goto-previous)
159 (define-key map "c" 'ess-latex-next-code-chunk)
160 (define-key map "C" 'ess-latex-previous-code-chunk)
161 (define-key map "d" 'ess-latex-next-doc-chunk)
162 (define-key map "D" 'ess-latex-previous-doc-chunk)
163 (define-key map "g" 'ess-latex-goto-chunk)
164 (define-key map "\C-l" 'ess-latex-update-chunk-vector)
165 (define-key map "\M-l" 'ess-latex-update-chunk-vector)
166 (define-key map "w" 'ess-latex-copy-chunk-as-kill)
167 (define-key map "W" 'ess-latex-copy-chunk-pair-as-kill)
168 (define-key map "k" 'ess-latex-kill-chunk)
169 (define-key map "K" 'ess-latex-kill-chunk-pair)
170 (define-key map "m" 'ess-latex-mark-chunk)
171 (define-key map "M" 'ess-latex-mark-chunk-pair)
172 (define-key map "n" 'ess-latex-narrow-to-chunk)
173 (define-key map "N" 'ess-latex-narrow-to-chunk-pair)
174 (define-key map "t" 'ess-latex-toggle-narrowing)
175 (define-key map "\t" 'ess-latex-complete-chunk)
176 (define-key map "q" 'ess-latex-fill-chunk)
177 (define-key map "i" 'ess-latex-new-chunk)
178 (define-key map "o" 'ess-latex-occur)
179 (define-key map "v" 'ess-latex-mode-version)
180 (define-key map "h" 'ess-latex-describe-mode)
181 (define-key map "\C-h" 'ess-latex-describe-mode)
182 map)
183 "ess-latex minor-mode prefix keymap")
185 (defvar ess-latex-minor-mode-map
186 (let ((map (make-sparse-keymap)))
187 (if ess-latex-electric-@-and-<
188 (progn
189 (define-key map "@" 'ess-latex-electric-@)
190 (define-key map "<" 'ess-latex-electric-<)))
191 (define-key map "\M-q" 'ess-latex-fill-paragraph-chunk)
192 (define-key map [tab] 'ess-latex-indent-line)
193 (define-key map [return] 'ess-latex-newline)
194 (define-key map [mouse-1] 'ess-latex-mouse-first-button)
195 (define-key map ess-latex-mode-prefix ess-latex-mode-prefix-map)
196 map)
197 "Ess-Latex minor mode keymap")
199 (easy-menu-define
200 ess-latex-minor-mode-menu ess-latex-minor-mode-map "Menu keymap for ess-latex."
201 '("Ess-Latex"
202 ("Movement"
203 ["Previous chunk" ess-latex-previous-chunk t]
204 ["Next chunk" ess-latex-next-chunk t]
205 ["Previous chunk of same name" ess-latex-goto-previous t]
206 ["Next chunk of same name" ess-latex-goto-next t]
207 ["Goto chunk" ess-latex-goto-chunk t]
208 ["Previous code chunk" ess-latex-previous-code-chunk t]
209 ["Next code chunk" ess-latex-next-code-chunk t]
210 ["Previous documentation chunk" ess-latex-previous-doc-chunk t]
211 ["Next documentation chunk" ess-latex-next-doc-chunk t])
212 ("Editing"
213 ["Copy chunk" ess-latex-copy-chunk-as-kill t]
214 ["Copy chunk pair" ess-latex-copy-chunk-pair-as-kill t]
215 ["Kill chunk" ess-latex-kill-chunk t]
216 ["Kill chunk pair" ess-latex-kill-chunk-pair t]
217 ["Mark chunk" ess-latex-mark-chunk t]
218 ["Mark chunk pair" ess-latex-mark-chunk-pair t])
219 ("Narrowing"
220 ["Narrow to chunk" ess-latex-narrow-to-chunk t]
221 ["Narrow to chunk pair" ess-latex-narrow-to-chunk-pair t]
222 ["Toggle auto narrowing" ess-latex-toggle-narrowing t]
223 ["Widen" widen t])
224 ("Modes"
225 ["Set documentation mode" ess-latex-set-doc-mode t]
226 ["Set default code mode" ess-latex-set-code-mode t]
227 ["Set code mode for this chunk" ess-latex-set-this-code-mode t]
228 ["Insert default mode line" ess-latex-insert-default-mode-line t])
229 ("Tangling"
230 ["Tangle current chunk" ess-latex-tangle-chunk t]
231 ["Tangle current thread" ess-latex-tangle-current-thread t]
232 ["Tangle named thread" ess-latex-tangle-thread t])
233 ("Miscellaneous"
234 ["Complete chunk name" ess-latex-complete-chunk t]
235 ["Fill current chunk" ess-latex-fill-chunk t]
236 ["Insert new chunk" ess-latex-new-chunk t]
237 ["Update the chunk vector" ess-latex-update-chunk-vector t]
238 ["Chunk occurrences" ess-latex-occur t])
239 "--"
240 ["Help" ess-latex-describe-mode t]
241 ["Version" ess-latex-mode-version t]))
243 ;; Add ess-latex-mode to the list of minor modes
244 (if (not (assq 'ess-latex-mode minor-mode-alist))
245 (setq minor-mode-alist (append minor-mode-alist
246 (list '(ess-latex-mode " Ess-Latex")))))
247 ;; Add ess-latex-minor-mode-map to the list of minor-mode keymaps
248 ;; available. Then, whenever ess-latex-mode is activated, the keymap is
249 ;; automatically activated
250 (if (not (assq 'ess-latex-mode minor-mode-map-alist))
251 (setq minor-mode-map-alist
252 (cons (cons 'ess-latex-mode ess-latex-minor-mode-map)
253 minor-mode-map-alist)))
255 (defun ess-latex-minor-mode (&optional arg)
256 "Minor meta mode for editing ess-latex files. See ESS-LATEX-MODE."
257 (interactive)
258 (ess-latex-minor-mode arg))
260 (defun ess-latex-mode ( &optional arg )
261 "Minor meta mode for editing ess-latex files.
262 `Meta' refers to the fact that this minor mode is switching major
263 modes depending on the location of point.
265 The following special keystrokes are available in ess-latex mode:
267 Movement:
268 \\[ess-latex-next-chunk] \tgoto the next chunk
269 \\[ess-latex-previous-chunk] \tgoto the previous chunk
270 \\[ess-latex-goto-previous] \tgoto the previous chunk of the same name
271 \\[ess-latex-goto-next] \tgoto the next chunk of the same name
272 \\[ess-latex-goto-chunk] \t\tgoto a chunk
273 \\[ess-latex-next-code-chunk] \t\tgoto the next code chunk
274 \\[ess-latex-previous-code-chunk] \t\tgoto the previous code chunk
275 \\[ess-latex-next-doc-chunk] \t\tgoto the next documentation chunk
276 \\[ess-latex-previous-doc-chunk] \t\tgoto the previous documentation chunk
278 Copying/Killing/Marking/Narrowing:
279 \\[ess-latex-copy-chunk-as-kill] \t\tcopy the chunk the point is in into the kill ring
280 \\[ess-latex-copy-chunk-pair-as-kill] \t\tcopy the pair of doc/code chunks the point is in
281 \\[ess-latex-kill-chunk] \t\tkill the chunk the point is in
282 \\[ess-latex-kill-chunk-pair] \t\tkill the pair of doc/code chunks the point is in
283 \\[ess-latex-mark-chunk] \t\tmark the chunk the point is in
284 \\[ess-latex-mark-chunk-pair] \t\tmark the pair of doc/code chunks the point is in
285 \\[ess-latex-narrow-to-chunk] \t\tnarrow to the chunk the point is in
286 \\[ess-latex-narrow-to-chunk-pair] \t\tnarrow to the pair of doc/code chunks the point is in
287 \\[widen] \twiden
288 \\[ess-latex-toggle-narrowing] \t\ttoggle auto narrowing
290 Filling and Indenting:
291 \\[ess-latex-fill-chunk] \tfill (or indent) the chunk at point according to mode
292 \\[ess-latex-fill-paragraph-chunk] \tfill the paragraph at point, restricted to chunk
293 \\[ess-latex-indent-line] \tindent the line at point according to mode
295 Insertion:
296 \\[ess-latex-insert-default-mode-line] \tinsert a line to set this file's code mode
297 \\[ess-latex-new-chunk] \t\tinsert a new chunk at point
298 \\[ess-latex-complete-chunk] \tcomplete the chunk name before point
299 \\[ess-latex-electric-@] \t\tinsert a `@' or start a new doc chunk
300 \\[ess-latex-electric-<] \t\tinsert a `<' or start a new code chunk
302 Modes:
303 \\[ess-latex-set-doc-mode] \t\tset the major mode for editing doc chunks
304 \\[ess-latex-set-code-mode] \tset the major mode for editing code chunks
305 \\[ess-latex-set-this-code-mode] \tset the major mode for editing this code chunk
307 Misc:
308 \\[ess-latex-occur] \t\tfind all occurrences of the current chunk
309 \\[ess-latex-update-chunk-vector] \tupdate the markers for chunks
310 \\[ess-latex-describe-mode] \tdescribe ess-latex-mode
311 \\[ess-latex-mode-version] \t\tshow ess-latex-mode's version in the minibuffer
312 " (interactive "P")
313 ; This bit is tricky: copied almost verbatim from bib-cite-mode.el
314 ; It seems to ensure that the variable ess-latex-mode is made
315 ; local to this buffer. It then sets ess-latex-mode to `t' if
316 ; 1) It was called with an argument greater than 0
317 ; or 2) It was called with no argument, and ess-latex-mode is
318 ; currently nil
319 ; ess-latex-mode is nil if the argument was <= 0 or there
320 ; was no argument and ess-latex-mode is currently `t'
321 (set (make-local-variable 'ess-latex-mode)
322 (if arg
323 (> (prefix-numeric-value arg) 0)
324 (not ess-latex-mode)))
325 ; Now, if ess-latex-mode is true, we want to turn
326 ; ess-latex-mode on
327 (cond
328 (ess-latex-mode ;Setup the minor-mode
329 (mapcar 'ess-latex-make-variable-permanent-local
330 '(ess-latex-mode
331 after-change-functions
332 ess-latex-narrowing
333 ess-latex-chunk-vector
334 post-command-hook
335 isearch-mode-hook
336 isearch-mode-end-hook
337 ess-latex-doc-mode
338 ess-latex-code-mode
339 ess-latex-default-code-mode
340 ess-latex-last-chunk-index))
341 (ess-latex-update-chunk-vector)
342 (if (equal 0 (ess-latex-find-chunk-index-buffer))
343 (setq ess-latex-last-chunk-index 1)
344 (setq ess-latex-last-chunk-index 0))
345 (if font-lock-mode
346 (progn
347 (font-lock-mode -1)
348 (ess-latex-font-lock-mode 1)))
349 (add-hook 'post-command-hook 'ess-latex-post-command-function)
350 (add-hook 'after-change-functions 'ess-latex-after-change-function)
351 (add-hook 'ess-latex-select-doc-mode-hook 'ess-latex-auto-fill-doc-mode)
352 (add-hook 'ess-latex-select-code-mode-hook 'ess-latex-auto-fill-code-mode)
353 (add-hook 'isearch-mode-hook 'ess-latex-note-isearch-mode)
354 (add-hook 'isearch-mode-end-hook 'ess-latex-note-isearch-mode-end)
355 (setq ess-latex-doc-mode-syntax-table nil)
356 (run-hooks 'ess-latex-mode-hook)
357 (message
358 "ess-latex mode: use `M-x ess-latex-describe-mode' for further information"))
359 ;; If we didn't do the above, then we want to turn ess-latex-mode
360 ;; off, no matter what (hence the condition `t')
362 (remove-hook 'post-command-hook 'ess-latex-post-command-function)
363 (remove-hook 'after-change-functions 'ess-latex-after-change-function)
364 (remove-hook 'ess-latex-select-doc-mode-hook 'ess-latex-auto-fill-doc-mode)
365 (remove-hook 'ess-latex-select-code-mode-hook 'ess-latex-auto-fill-code-mode)
366 (remove-hook 'isearch-mode-hook 'ess-latex-note-isearch-mode)
367 (remove-hook 'isearch-mode-end-hook 'ess-latex-note-isearch-mode-end)
368 (if ess-latex-font-lock-mode
369 (progn
370 (ess-latex-font-lock-mode -1)
371 (message "Ess-Latex and Ess-Latex-Font-Lock Modes Removed"))
372 (message "Ess-Latex mode removed")))))
374 (defun ess-latex-make-variable-permanent-local (var)
375 "Declare VAR buffer local, but protect it from beeing killed
376 by major mode changes."
377 (make-variable-buffer-local var)
378 (put var 'permanent-local 't))
380 (defun ess-latex-note-isearch-mode ()
381 "Take note of an incremental search in progress"
382 (remove-hook 'post-command-hook 'ess-latex-post-command-function))
384 (defun ess-latex-note-isearch-mode-end ()
385 "Take note of an incremental search having ended"
386 (add-hook 'post-command-hook 'ess-latex-post-command-function))
388 (defun ess-latex-post-command-function ()
389 "The hook being run after each command in ess-latex mode."
390 (ess-latex-select-mode))
392 (defun ess-latex-after-change-function (begin end length)
393 "Function to run after every change in a ess-latex buffer.
394 If the changed region contains a chunk start (^@ or ^<<), it will
395 update the chunk vector"
396 (save-excursion
397 (goto-char begin)
398 (if (re-search-forward "^\\(@[^@]\\)\\|\\(<<\\)" end t)
399 (ess-latex-update-chunk-vector))))
402 ;;; Chunks
404 (defun ess-latex-update-chunk-vector ()
405 "Scan the whole buffer and place a marker at each \"^@\" and \"^<<\".
406 Record them in ESS-LATEX-CHUNK-VECTOR."
407 (interactive)
408 (save-excursion
409 (goto-char (point-min))
410 (let ((chunk-list (list (cons 'doc (point-marker)))))
411 (while (re-search-forward "^\\(@\\( \\|$\\|\\( %def\\)\\)\\|<<\\(.*\\)>>=\\)" nil t)
412 (goto-char (match-beginning 0))
413 ;; If the 3rd subexpression matched @ %def, we're still in a code
414 ;; chunk (sort of), so don't place a marker here.
415 (if (not (match-beginning 3))
416 (setq chunk-list
417 ;; If the 4th subexpression matched inside <<...>>,
418 ;; we're seeing a new code chunk.
419 (cons (cons (if (match-beginning 4)
420 ;;buffer-substring-no-properties better
421 ;;than buffer-substring if highlighting
422 ;;may be used
423 (buffer-substring-no-properties
424 (match-beginning 4) (match-end 4))
425 'doc)
426 (point-marker))
427 chunk-list))
428 ;; Scan forward either to !/^@ %def/, which will start a docs chunk,
429 ;; or to /^<<.*>>=$/, which will start a code chunk.
430 (progn
431 (next-line 1)
432 (while (looking-at "@ %def")
433 (next-line 1))
434 (setq chunk-list
435 ;; Now we can tell code vs docs
436 (cons (cons (if (looking-at "<<\\(.*\\)>>=")
437 (buffer-substring-no-properties
438 (match-beginning 1) (match-end 1))
439 'doc)
440 (point-marker))
441 chunk-list))))
442 (next-line 1))
443 (setq chunk-list (cons (cons 'doc (point-max-marker)) chunk-list))
444 (setq ess-latex-chunk-vector (vconcat (reverse chunk-list))))))
446 (defun ess-latex-find-chunk ()
447 "Return a pair consisting of the name (or 'DOC) and the
448 marker of the current chunk."
449 (if (not ess-latex-chunk-vector)
450 (ess-latex-update-chunk-vector))
451 (aref ess-latex-chunk-vector (ess-latex-find-chunk-index-buffer)))
453 (defun ess-latex-chunk-is-code (index)
454 "Return t if the chunk 'index' is a code chunk, nil otherwise"
455 (interactive)
456 (stringp (car (ess-latex-chunk-vector-aref index))))
458 (defun ess-latex-in-code-chunk ()
459 "Return t if we are in a code chunk, nil otherwise."
460 (interactive)
461 (ess-latex-chunk-is-code (ess-latex-find-chunk-index-buffer)))
463 (defun ess-latex-in-mode-line ()
464 "Return the name of the mode to use if we are in a mode line, nil
465 otherwise."
466 (interactive)
467 (let (beg end mode)
468 (save-excursion
469 (beginning-of-line 1)
470 (and (search-forward "-*-"
471 (save-excursion (end-of-line) (point))
473 (progn
474 (skip-chars-forward " \t")
475 (setq beg (point))
476 (search-forward "-*-"
477 (save-excursion (end-of-line) (point))
479 (progn
480 (forward-char -3)
481 (skip-chars-backward " \t")
482 (setq end (point))
483 (goto-char beg)
484 (setq mode (concat
485 (downcase (buffer-substring beg end))
486 "-mode"))
487 (if ;(and
488 (>= (length mode) 11)
489 (progn
491 (equal (substring mode -10 -5) "-mode")
492 (setq mode (substring mode 0 -5)))
494 (equal (substring mode 0 5) "mode:")
495 (setq mode (substring mode 6)))))
496 (intern mode))))))
498 (defun ess-latex-find-chunk-index-buffer ()
499 "Return the index of the current chunk in ESS-LATEX-CHUNK-VECTOR."
500 (ess-latex-find-chunk-index 0 (1- (length ess-latex-chunk-vector))))
502 (defun ess-latex-find-chunk-index (low hi)
503 (if (= hi (1+ low))
505 (let ((med (/ (+ low hi) 2)))
506 (if (< (point) (cdr (aref ess-latex-chunk-vector med)))
507 (ess-latex-find-chunk-index low med)
508 (ess-latex-find-chunk-index med hi)))))
510 (defun ess-latex-chunk-region ()
511 "Return a pair consisting of the beginning and end of the current chunk."
512 (interactive)
513 (let ((start (ess-latex-find-chunk-index-buffer)))
514 (cons (marker-position (cdr (aref ess-latex-chunk-vector start)))
515 (marker-position (cdr (aref ess-latex-chunk-vector (1+ start)))))))
517 (defun ess-latex-copy-code-chunk ()
518 "Copy the current code chunk to the kill ring, excluding the chunk name.
519 This will be particularly useful when interfacing with ESS."
520 (interactive)
521 (let ((r (ess-latex-chunk-region)))
522 (save-excursion
523 (goto-char (car r))
524 (if (ess-latex-in-code-chunk)
525 (progn
526 (beginning-of-line 2)
527 (copy-region-as-kill (point) (cdr r)))))))
529 (defun ess-latex-extract-code-chunk ()
530 "Create a new buffer with the same name as the current code chunk,
531 and copy all code from chunks of the same name to it."
532 (interactive)
533 (save-excursion
534 (if (ess-latex-in-code-chunk)
535 (progn
536 (let ((chunk-name (car (ess-latex-find-chunk)))
537 (chunk-counter 0)
538 (copy-counter 0)
539 (this-chunk) (oldbuf (current-buffer)))
540 (if (get-buffer chunk-name)
541 (progn
542 (set-buffer-modified-p nil)
543 (kill-buffer chunk-name)))
544 (get-buffer-create chunk-name)
545 (message "Created buffer %s" chunk-name)
546 (while (< chunk-counter (- (length ess-latex-chunk-vector) 2))
547 (setq this-chunk (ess-latex-chunk-vector-aref
548 chunk-counter))
549 (message "Current buffer is %s" (car this-chunk))
550 (if (equal chunk-name (car this-chunk))
551 (progn
552 (setq copy-counter (+ copy-counter 1))
553 (goto-char (cdr this-chunk))
554 (ess-latex-copy-code-chunk)
555 (set-buffer chunk-name)
556 (goto-char (point-max))
557 (yank)
558 (set-buffer oldbuf)))
559 (setq chunk-counter (+ chunk-counter 1)))
560 (message "Copied %d bits" copy-counter)
561 (set-buffer chunk-name)
562 (copy-region-as-kill (point-min)(point-max)))))))
564 (defun ess-latex-chunk-pair-region ()
565 "Return a pair consisting of the beginning and end of the current pair of
566 documentation and code chunks."
567 (interactive)
568 (let* ((start (ess-latex-find-chunk-index-buffer))
569 (end (1+ start)))
570 (if (ess-latex-chunk-is-code start)
571 (cons (marker-position (cdr (aref ess-latex-chunk-vector (1- start))))
572 (marker-position (cdr (aref ess-latex-chunk-vector end))))
573 (while (not (ess-latex-chunk-is-code end))
574 (setq end (1+ end)))
575 (cons (marker-position (cdr (aref ess-latex-chunk-vector start)))
576 (marker-position (cdr (aref ess-latex-chunk-vector (1+ end))))))))
578 (defun ess-latex-chunk-vector-aref (i)
579 (if (< i 0)
580 (error "Before first chunk."))
581 (if (>= i (length ess-latex-chunk-vector))
582 (error "Beyond last chunk."))
583 (aref ess-latex-chunk-vector i))
585 (defun ess-latex-complete-chunk ()
586 "Complete the chunk name before point, if any."
587 (interactive)
588 (if (ess-latex-in-code-chunk)
589 (let ((end (point))
590 (beg (save-excursion
591 (if (re-search-backward "<<"
592 (save-excursion
593 (beginning-of-line)
594 (point))
596 (match-end 0)
597 nil))))
598 (if beg
599 (let* ((pattern (buffer-substring beg end))
600 (alist (ess-latex-build-chunk-alist))
601 (completion (try-completion pattern alist)))
602 (cond ((eq completion t))
603 ((null completion)
604 (message "Can't find completion for \"%s\"" pattern)
605 (ding))
606 ((not (string= pattern completion))
607 (delete-region beg end)
608 (insert completion)
609 (if (not (looking-at ">>"))
610 (insert ">>")))
612 (message "Making completion list...")
613 (with-output-to-temp-buffer "*Completions*"
614 (display-completion-list (all-completions pattern alist)))
615 (message "Making completion list...%s" "done"))))
616 (message "Not at chunk name...")))
617 (message "Not in code chunk...")))
620 ;;; Filling, etc
622 (defun ess-latex-hide-code-quotes ()
623 "Replace all non blank characters in [[...]] code quotes
624 in the current buffer (you might want to narrow to the interesting
625 region first) by `*'. Return a list of pairs with the position and
626 value of the original strings."
627 (save-excursion
628 (let ((quote-list nil))
629 (goto-char (point-min))
630 (while (re-search-forward "\\[\\[" nil 'move)
631 (let ((beg (match-end 0))
632 (end (if (re-search-forward "\\]\\]" nil t)
633 (match-beginning 0)
634 (point-max))))
635 (goto-char beg)
636 (while (< (point) end)
637 ;; Move on to the next word:
638 (let ((b (progn
639 (skip-chars-forward " \t\n" end)
640 (point)))
641 (e (progn
642 (skip-chars-forward "^ \t\n" end)
643 (point))))
644 (if (> e b)
645 ;; Save the string and a marker to the end of the
646 ;; replacement text. A marker to the beginning is
647 ;; useless. See ESS-LATEX-RESTORE-CODE-QUOTES.
648 (save-excursion
649 (setq quote-list (cons (cons (copy-marker e)
650 (buffer-substring b e))
651 quote-list))
652 (goto-char b)
653 (insert-char ?* (- e b) t)
654 (delete-char (- e b))))))))
655 (reverse quote-list))))
657 (defun ess-latex-restore-code-quotes (quote-list)
658 "Reinsert the strings modified by `ess-latex-hide-code-quotes'."
659 (save-excursion
660 (mapcar '(lambda (q)
661 (let* ((e (marker-position (car q)))
662 ;; Slightly inefficient, but correct way to find
663 ;; the beginning of the word to be replaced.
664 ;; Using the marker at the beginning will loose
665 ;; if whitespace has been rearranged
666 (b (save-excursion
667 (goto-char e)
668 (skip-chars-backward "*")
669 (point))))
670 (delete-region b e)
671 (goto-char b)
672 (insert (cdr q))))
673 quote-list)))
675 (defun ess-latex-fill-chunk ()
676 "Fill the current chunk according to mode.
677 Run `fill-region' on documentation chunks and `indent-region' on code
678 chunks."
679 (interactive)
680 (save-excursion
681 (save-restriction
682 (ess-latex-narrow-to-chunk)
683 (if (ess-latex-in-code-chunk)
684 (progn
685 ;; Narrow to the code section proper; w/o the first and any
686 ;; index declaration lines.
687 (narrow-to-region (progn
688 (goto-char (point-min))
689 (forward-line 1)
690 (point))
691 (progn
692 (goto-char (point-max))
693 (forward-line -1)
694 (while (looking-at "@")
695 (forward-line -1))
696 (forward-line 1)
697 (point)))
698 (if (or indent-region-function indent-line-function)
699 (indent-region (point-min) (point-max) nil)
700 (error "No indentation functions defined in %s!" major-mode)))
701 (let ((quote-list (ess-latex-hide-code-quotes)))
702 (fill-region (point-min) (point-max))
703 (ess-latex-restore-code-quotes quote-list))))))
705 (defun ess-latex-indent-line ()
706 "Indent the current line according to mode, after narrowing to this chunk."
707 (interactive)
708 (save-restriction
709 (ess-latex-narrow-to-chunk)
710 (if (stringp (car (ess-latex-find-chunk)))
711 (progn
712 ;; Narrow to the code section proper; w/o the first and any
713 ;; index declaration lines.
714 (save-excursion
715 (narrow-to-region (progn
716 (goto-char (point-min))
717 (forward-line 1)
718 (point))
719 (progn
720 (goto-char (point-max))
721 (forward-line -1)
722 (while (looking-at "@")
723 (forward-line -1))
724 (forward-line 1)
725 (point))))))
726 (indent-according-to-mode)))
728 (defun ess-latex-fill-paragraph-chunk (&optional justify)
729 "Fill a paragraph in the current chunk."
730 (interactive "P")
731 (ess-latex-update-chunk-vector)
732 (save-restriction
733 (save-excursion
734 (ess-latex-narrow-to-chunk)
735 (if (stringp (car (ess-latex-find-chunk)))
736 (progn
737 ;; Narrow to the code section proper; w/o the first and any
738 ;; index declaration lines.
739 (narrow-to-region (progn
740 (goto-char (point-min))
741 (forward-line 1)
742 (point))
743 (progn
744 (goto-char (point-max))
745 (forward-line -1)
746 (while (looking-at "@")
747 (forward-line -1))
748 (forward-line 1)
749 (point)))
750 (fill-paragraph justify))
751 (let ((quote-list (ess-latex-hide-code-quotes)))
752 (fill-paragraph justify)
753 (ess-latex-restore-code-quotes quote-list))))))
755 (defun ess-latex-auto-fill-doc-chunk ()
756 "Replacement for `do-auto-fill'."
757 (save-restriction
758 (narrow-to-region (car (ess-latex-chunk-region))
759 (save-excursion
760 (end-of-line)
761 (point)))
762 (let ((quote-list (ess-latex-hide-code-quotes)))
763 (do-auto-fill)
764 (ess-latex-restore-code-quotes quote-list))))
766 (defun ess-latex-auto-fill-doc-mode ()
767 "Install the improved auto fill function, iff necessary."
768 (if auto-fill-function
769 (setq auto-fill-function 'ess-latex-auto-fill-doc-chunk)))
771 (defun ess-latex-auto-fill-code-mode ()
772 "Install the default auto fill function, iff necessary."
773 (if auto-fill-function
774 (setq auto-fill-function 'do-auto-fill)))
776 ;;; Marking
778 (defun ess-latex-mark-chunk ()
779 "Mark the current chunk."
780 (interactive)
781 (let ((r (ess-latex-chunk-region)))
782 (goto-char (car r))
783 (push-mark (cdr r) nil t)))
785 (defun ess-latex-mark-chunk-pair ()
786 "Mark the current pair of documentation and code chunks."
787 (interactive)
788 (let ((r (ess-latex-chunk-pair-region)))
789 (goto-char (car r))
790 (push-mark (cdr r) nil t)))
793 ;;; Narrowing
795 (defun ess-latex-toggle-narrowing (&optional arg)
796 "Toggle if we should narrow the display to the current pair of
797 documentation and code chunks after each movement. With argument:
798 switch narrowing on."
799 (interactive "P")
800 (if (or arg (not ess-latex-narrowing))
801 (progn
802 (setq ess-latex-narrowing t)
803 (ess-latex-narrow-to-chunk-pair))
804 (setq ess-latex-narrowing nil)
805 (widen)))
807 (defun ess-latex-narrow-to-chunk ()
808 "Narrow the display to the current chunk."
809 (interactive)
810 (let ((r (ess-latex-chunk-region)))
811 (narrow-to-region (car r) (cdr r))))
813 (defun ess-latex-narrow-to-chunk-pair ()
814 "Narrow the display to the current pair of documentation and code chunks."
815 (interactive)
816 (let ((r (ess-latex-chunk-pair-region)))
817 (narrow-to-region (car r) (cdr r))))
820 ;;; Killing
822 (defun ess-latex-kill-chunk ()
823 "Kill the current chunk."
824 (interactive)
825 (let ((r (ess-latex-chunk-region)))
826 (kill-region (car r) (cdr r))))
828 (defun ess-latex-kill-chunk-pair ()
829 "Kill the current pair of chunks."
830 (interactive)
831 (let ((r (ess-latex-chunk-pair-region)))
832 (kill-region (car r) (cdr r))))
834 (defun ess-latex-copy-chunk-as-kill ()
835 "Place the current chunk on the kill ring."
836 (interactive)
837 (let ((r (ess-latex-chunk-region)))
838 (copy-region-as-kill (car r) (cdr r))))
840 (defun ess-latex-copy-chunk-pair-as-kill ()
841 "Place the current pair of chunks on the kill ring."
842 (interactive)
843 (let ((r (ess-latex-chunk-pair-region)))
844 (copy-region-as-kill (car r) (cdr r))))
847 ;;; Movement
849 (defun ess-latex-sign (n)
850 "Return the sign of N."
851 (if (< n 0) -1 1))
853 (defun ess-latex-next-doc-chunk (&optional cnt)
854 "Goto to the Nth documentation chunk from point."
855 (interactive "p")
856 (widen)
857 (let ((start (ess-latex-find-chunk-index-buffer))
858 (i 1))
859 (while (<= i (abs cnt))
860 (setq start (+ (ess-latex-sign cnt) start))
861 (while (ess-latex-chunk-is-code start)
862 (setq start (+ (ess-latex-sign cnt) start)))
863 (setq i (1+ i)))
864 (goto-char (marker-position (cdr (ess-latex-chunk-vector-aref start))))
865 (forward-char 1))
866 (if ess-latex-narrowing
867 (ess-latex-narrow-to-chunk-pair)))
869 (defun ess-latex-previous-doc-chunk (&optional n)
870 "Goto to the -Nth documentation chunk from point."
871 (interactive "p")
872 (ess-latex-next-doc-chunk (- n)))
874 (defun ess-latex-next-code-chunk (&optional cnt)
875 "Goto to the Nth code chunk from point."
876 (interactive "p")
877 (widen)
878 (let ((start (ess-latex-find-chunk-index-buffer))
879 (i 1))
880 (while (<= i (abs cnt))
881 (setq start (+ (ess-latex-sign cnt) start))
882 (while (not (ess-latex-chunk-is-code start))
883 (setq start (+ (ess-latex-sign cnt) start)))
884 (setq i (1+ i)))
885 (goto-char (marker-position (cdr (ess-latex-chunk-vector-aref start))))
886 (next-line 1))
887 (if ess-latex-narrowing
888 (ess-latex-narrow-to-chunk-pair)))
890 (defun ess-latex-previous-code-chunk (&optional n)
891 "Goto to the -Nth code chunk from point."
892 (interactive "p")
893 (ess-latex-next-code-chunk (- n)))
895 (defun ess-latex-next-chunk (&optional n)
896 "If in a documentation chunk, goto to the Nth documentation
897 chunk from point, else goto to the Nth code chunk from point."
898 (interactive "p")
899 (if (ess-latex-in-code-chunk)
900 (ess-latex-next-code-chunk n)
901 (ess-latex-next-doc-chunk n)))
903 (defun ess-latex-previous-chunk (&optional n)
904 "If in a documentation chunk, goto to the -Nth documentation
905 chunk from point, else goto to the -Nth code chunk from point."
906 (interactive "p")
907 (ess-latex-next-chunk (- n)))
909 (defvar ess-latex-chunk-history nil
912 (defun ess-latex-goto-chunk ()
913 "Goto the named chunk."
914 (interactive)
915 (widen)
916 (let* ((completion-ignore-case t)
917 (alist (ess-latex-build-chunk-alist))
918 (chunk (completing-read
919 "Chunk: " alist nil t
920 (ess-latex-goto-chunk-default)
921 ess-latex-chunk-history)))
922 (goto-char (cdr (assoc chunk alist))))
923 (if ess-latex-narrowing
924 (ess-latex-narrow-to-chunk-pair)))
926 (defun ess-latex-goto-chunk-default ()
927 (save-excursion
928 (if (re-search-backward "<<"
929 (save-excursion
930 (beginning-of-line)
931 (point))
932 'move)
933 (goto-char (match-beginning 0)))
934 (if (re-search-forward "<<\\(.*\\)>>"
935 (save-excursion
936 (end-of-line)
937 (point))
939 (buffer-substring (match-beginning 1) (match-end 1))
940 nil)))
942 (defun ess-latex-build-chunk-alist ()
943 (if (not ess-latex-chunk-vector)
944 (ess-latex-update-chunk-vector))
945 ;; The naive recursive solution will exceed MAX-LISP-EVAL-DEPTH in
946 ;; buffers w/ many chunks. Maybe there is a tail recursivce solution,
947 ;; but iterative solutions should be acceptable for dealing with vectors.
948 (let ((alist nil)
949 (i (1- (length ess-latex-chunk-vector))))
950 (while (>= i 0)
951 (let* ((chunk (aref ess-latex-chunk-vector i))
952 (name (car chunk))
953 (marker (cdr chunk)))
954 (if (and (stringp name)
955 (not (assoc name alist)))
956 (setq alist (cons (cons name marker) alist))))
957 (setq i (1- i)))
958 alist))
960 (defun ess-latex-goto-next (&optional cnt)
961 "Goto the continuation of the current chunk."
962 (interactive "p")
963 (widen)
964 (if (not ess-latex-chunk-vector)
965 (ess-latex-update-chunk-vector))
966 (let ((start (ess-latex-find-chunk-index-buffer)))
967 (if (not (ess-latex-chunk-is-code start))
968 (setq start (1+ start)))
969 (if (ess-latex-chunk-is-code start)
970 (let ((name (car (ess-latex-chunk-vector-aref start)))
971 (i 1))
972 (while (<= i (abs cnt))
973 (setq start (+ (ess-latex-sign cnt) start))
974 (while (not (equal (car (ess-latex-chunk-vector-aref start))
975 name))
976 (setq start (+ (ess-latex-sign cnt) start)))
977 (setq i (1+ i)))
978 (goto-char (marker-position
979 (cdr (ess-latex-chunk-vector-aref start))))
980 (next-line 1))))
981 (if ess-latex-narrowing
982 (ess-latex-narrow-to-chunk-pair)))
984 (defun ess-latex-goto-previous (&optional cnt)
985 "Goto the previous chunk."
986 (interactive "p")
987 (ess-latex-goto-next (- cnt)))
989 (defun ess-latex-occur (arg)
990 "Find all occurences of the current chunk.
991 This function simply runs OCCUR on \"<<NAME>>\"."
992 (interactive "P")
993 (let ((n (if (and arg
994 (numberp arg))
997 (idx (ess-latex-find-chunk-index-buffer)))
998 (if (ess-latex-chunk-is-code idx)
999 (occur (regexp-quote (concat "<<"
1000 (car (aref ess-latex-chunk-vector idx))
1001 ">>"))
1003 (setq idx (1+ idx))
1004 (while (not (ess-latex-chunk-is-code idx))
1005 (setq idx (1+ idx)))
1006 (occur (regexp-quote (concat "<<"
1007 (car (aref ess-latex-chunk-vector idx))
1008 ">>"))
1009 n))))
1012 ;;; Insertion
1014 (defun ess-latex-new-chunk (name)
1015 "Insert a new chunk."
1016 (interactive "sChunk name: ")
1017 (insert "@ \n<<" name ">>=\n")
1018 (save-excursion
1019 (insert "@ %def \n"))
1020 (ess-latex-update-chunk-vector))
1022 (defun ess-latex-at-beginning-of-line ()
1023 (equal (save-excursion
1024 (beginning-of-line)
1025 (point))
1026 (point)))
1028 (defun ess-latex-electric-@ (arg)
1029 "Smart incarnation of `@', starting a new documentation chunk, maybe.
1030 If given an numerical argument, it will act just like the dumb `@'.
1031 Otherwise and if at the beginning of a line in a code chunk:
1032 insert \"@ \" and update the chunk vector."
1033 (interactive "P")
1034 (if arg
1035 (self-insert-command (if (numberp arg) arg 1))
1036 (if (and (ess-latex-at-beginning-of-line)
1037 (ess-latex-in-code-chunk))
1038 (progn
1039 (insert "@ ")
1040 (ess-latex-update-chunk-vector))
1041 (self-insert-command 1))))
1043 (defun ess-latex-electric-< (arg)
1044 "Smart incarnation of `<', starting a new code chunk, maybe.
1045 If given an numerical argument, it will act just like the dumb `<'.
1046 Otherwise and if at the beginning of a line in a documentation chunk:
1047 insert \"<<>>=\" and a newline if necessary. Leave point in the middle
1048 and and update the chunk vector."
1049 (interactive "P")
1050 (if arg
1051 (self-insert-command (if (numberp arg) arg 1))
1052 (if (and (ess-latex-at-beginning-of-line)
1053 (not (stringp (car (ess-latex-find-chunk)))))
1054 (progn
1055 (insert "<<")
1056 (save-excursion
1057 (insert ">>=")
1058 (if (not (looking-at "\\s *$"))
1059 (newline)))
1060 (ess-latex-update-chunk-vector))
1061 (self-insert-command 1))))
1064 ;;; Modes
1066 (defun ess-latex-set-chunk-code-mode ()
1067 "Set the ess-latex-code-mode for the current chunk"
1068 (interactive)
1069 (if (ess-latex-in-code-chunk)
1070 (progn
1071 ;; Reset code-mode to default and then check for a mode comment.
1072 (setq ess-latex-code-mode ess-latex-default-code-mode)
1073 (let (mode chunk-name)
1074 (save-restriction
1075 (save-excursion
1076 (end-of-line)
1077 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1078 (setq chunk-name (match-string 1))
1079 (widen)
1080 (goto-char (point-min))
1081 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1082 (beginning-of-line 2)
1083 (setq mode (ess-latex-in-mode-line))
1084 (if (functionp mode)
1085 (setq ess-latex-code-mode mode))))))
1086 (error "This only makes sense in a code chunk")))
1088 (defun ess-latex-set-doc-syntax-table ()
1089 "Sets the doc-mode syntax-table to treat code quotes as comments."
1090 (interactive)
1091 (let ((square-bracket-string (char-to-string (char-syntax ?\[))))
1092 (if (string= square-bracket-string "(")
1093 (progn
1094 (modify-syntax-entry ?\[ "(]12b" ess-latex-doc-mode-syntax-table)
1095 (modify-syntax-entry ?\] ")[34b" ess-latex-doc-mode-syntax-table))
1096 (progn
1097 (modify-syntax-entry ?\[
1098 (concat square-bracket-string " 12b")
1099 ess-latex-doc-mode-syntax-table)
1100 (modify-syntax-entry ?\]
1101 (concat square-bracket-string " 34b")
1102 ess-latex-doc-mode-syntax-table)))))
1104 (defun ess-latex-select-mode ()
1105 "Select ESS-LATEX-DOC-MODE or ESS-LATEX-CODE-MODE, as appropriate."
1106 (interactive)
1107 (let ((this-chunk-index (ess-latex-find-chunk-index-buffer)))
1108 ;; Has the last change to the buffer taken us into a different
1109 ;; chunk ?
1110 (if (not (equal this-chunk-index ess-latex-last-chunk-index))
1111 (progn
1112 (setq ess-latex-last-chunk-index this-chunk-index)
1113 (if (ess-latex-in-code-chunk)
1114 ;; Inside a code chunk
1115 (progn
1116 ;; Find out which code mode to use
1117 (ess-latex-set-chunk-code-mode)
1118 ;; If we aren't already using it, use it.
1119 (if (not (equal major-mode ess-latex-code-mode))
1120 (progn
1121 (funcall ess-latex-code-mode)
1122 (run-hooks 'ess-latex-select-mode-hook)
1123 (run-hooks 'ess-latex-select-code-mode-hook))))
1124 ;; Inside a documentation chunk
1125 (progn
1126 (if (not (equal major-mode ess-latex-doc-mode))
1127 (progn
1128 (funcall ess-latex-doc-mode)))
1129 (if (not ess-latex-doc-mode-syntax-table)
1130 (progn
1131 (message "Setting up syntax table")
1132 (setq ess-latex-doc-mode-syntax-table
1133 (make-syntax-table (syntax-table)))
1134 (ess-latex-set-doc-syntax-table)))
1135 (set-syntax-table ess-latex-doc-mode-syntax-table)
1136 (run-hooks 'ess-latex-select-mode-hook)
1137 (run-hooks 'ess-latex-select-doc-mode-hook)))
1138 (run-hooks 'ess-latex-changed-chunk-hook)))))
1140 (defvar ess-latex-doc-mode ess-latex-default-doc-mode
1141 "Default major mode for editing ess-latex documentation chunks.
1142 It is not possible to have more than one doc-mode in a file.
1143 However, this variable is used to determine whether the doc-mode needs
1144 to by added to the mode-line")
1146 (defun ess-latex-set-doc-mode (mode)
1147 "Change the major mode for editing documentation chunks."
1148 (interactive "CNew major mode for documentation chunks: ")
1149 (setq ess-latex-doc-mode mode)
1150 (setq ess-latex-doc-mode-syntax-table nil)
1151 ;;Pretend we've changed chunk, so the mode will be reset if necessary
1152 (setq ess-latex-last-chunk-index (1- ess-latex-last-chunk-index))
1153 (ess-latex-select-mode))
1155 (defun ess-latex-set-code-mode (mode)
1156 "Change the major mode for editing all code chunks."
1157 (interactive "CNew major mode for all code chunks: ")
1158 (setq ess-latex-default-code-mode mode)
1159 ;;Pretend we've changed chunk, so the mode will be reset if necessary
1160 (setq ess-latex-last-chunk-index (1- ess-latex-last-chunk-index))
1161 (ess-latex-select-mode))
1163 (defun ess-latex-set-this-code-mode (mode)
1164 "Change the major mode for editing this code chunk.
1165 The only sensible way to do this is to add a mode line to the chunk"
1166 (interactive "CNew major mode for this code chunk: ")
1167 (if (ess-latex-in-code-chunk)
1168 (progn
1169 (setq ess-latex-code-mode mode)
1170 (save-restriction
1171 (save-excursion
1172 (let (chunk-name)
1173 (widen)
1174 (end-of-line)
1175 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1176 (setq chunk-name (match-string 1))
1177 (goto-char (point-min))
1178 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1179 (beginning-of-line 2))
1180 ;; remove mode-line, if there is one
1181 (if (ess-latex-in-mode-line)
1182 (progn
1183 (kill-line)
1184 (kill-line)))
1185 (if (not (equal ess-latex-code-mode ess-latex-default-code-mode))
1186 (progn
1187 (setq mode (substring (symbol-name mode) 0 -5))
1188 ;; Need to set major mode so that we can comment out
1189 ;; the mode line
1190 (funcall ess-latex-code-mode)
1191 (insert comment-start
1192 " -*- " mode
1193 " -*- " comment-end "\n")))
1194 (setq ess-latex-last-chunk-index (1- ess-latex-last-chunk-index)))))
1195 (message "This only makes sense in a code chunk.")))
1197 ;;; Misc
1199 (defun ess-latex-mode-version ()
1200 "Echo the RCS identification of ess-latex mode."
1201 (interactive)
1202 (message "Thorsten's ess-latex-mode (PRERELEASE). RCS: %s"
1203 ess-latex-mode-RCS-Id))
1205 (defun ess-latex-describe-mode ()
1206 "Describe ess-latex mode."
1207 (interactive)
1208 (describe-function 'ess-latex-mode))
1210 (defun ess-latex-insert-default-mode-line ()
1211 "Insert line that will set the ess-latex mode of this file in emacs.
1212 The file is set to use the current doc and default-code modes, so
1213 ensure they are set correctly (with ess-latex-set-code-mode and
1214 ess-latex-set-doc-mode) before calling this function"
1215 (interactive)
1216 (save-excursion
1217 (goto-char 1)
1218 (if (ess-latex-in-mode-line)
1219 (progn
1220 (kill-line)
1221 (kill-line)))
1222 (if (not (eq major-mode ess-latex-doc-mode))
1223 (ess-latex-select-mode))
1224 (insert comment-start " -*- mode: ess-latex; ess-latex-default-code-mode: "
1225 (symbol-name ess-latex-default-code-mode)
1226 (if (not (eq ess-latex-doc-mode ess-latex-default-doc-mode))
1227 (concat "; ess-latex-doc-mode: " (symbol-name
1228 ess-latex-doc-mode) ";")
1229 ";")
1230 " -*-" comment-end "\n"))
1231 (ess-latex-select-mode))
1233 (defun ess-latex-mouse-first-button (event)
1234 (interactive "e")
1235 (mouse-set-point event)
1236 (if (and ess-latex-use-mouse-navigation
1237 (eq (save-excursion
1238 (end-of-line)
1239 (re-search-backward "^[\t ]*\\(<<\\)\\(.*\\)\\(>>\\)" nil t))
1240 (save-excursion
1241 (beginning-of-line) (point))))
1242 (progn
1243 (if (< (point) (match-beginning 2))
1244 (let ((chunk-name (buffer-substring-no-properties
1245 (match-beginning 2)
1246 (match-end 2))))
1247 (re-search-backward (concat "<<" chunk-name ">>") nil t))
1248 (if (and (<= (match-end 2) (point))
1249 (> (+ 2 (match-end 2)) (point)))
1250 (let ((chunk-name (buffer-substring-no-properties
1251 (match-beginning 2)
1252 (match-end 2))))
1253 (re-search-forward (concat "<<" chunk-name ">>") nil t)))))))
1256 ;;; Debugging
1258 (defun ess-latex-log (s)
1259 (let ((b (current-buffer)))
1260 (switch-to-buffer (get-buffer-create "*ess-latex-log*"))
1261 (goto-char (point-max))
1262 (setq buffer-read-only nil)
1263 (insert s)
1264 (setq buffer-read-only t)
1265 (switch-to-buffer b)))
1271 (defvar ess-latex-thread-alist nil
1272 "A list of threads in the current buffer.
1273 Each entry in the list contains 5 elements:
1274 1) The name of the threads
1275 2) The name of the immdiate parent thread in which it is used (nil if
1276 it is a \"top-level\" thread which is not used anywhere).
1277 3) The name of the top-level parent thread in which it is used (i.e. a
1278 thread in which it is used but which is not itself used anywhere:
1279 nil if this thread is not used anywhere.
1280 4) The format string to use to define line numbers in the output
1281 file of this thread. Should only be set if this thread is not used
1282 anywhere: if a thread is used as part of another thread, the parent
1283 thread's format string should be used.
1284 5) If this is nil, tabs are converted to spaces in the tangled
1285 file. If it is a number, tabs are copied to the tangled file
1286 unchanged, and tabs are also used for indentation, with the number
1287 of spaces per tab defined by this number. This MUST be set in order
1288 to tangle makefiles, which depend on tabs.Should only be set if
1289 this thread is not used anywhere. otherwise set to nil. "
1292 (defun ess-latex-update-thread-alist ()
1293 "Updates the list of threads in the current buffer.
1294 Each entry in the list contains 5 elements:
1295 1) The name of the thread
1296 2) The name of the immdiate parent thread in which it is used (nil if
1297 it is a \"top-level\" thread which is not used anywhere).
1298 3) The name of the top-level parent thread in which it is used (i.e. a
1299 thread in which it is used but which is not itself used anywhere:
1300 nil if this thread is not used anywhere.
1301 4) The format string to use to define line numbers in the output
1302 file of this thread. Should only be set if this thread is not used
1303 anywhere: if a thread is used as part of another thread, the parent
1304 thread's format string should be used.
1305 5) If this is nil, tabs are converted to spaces in the tangled
1306 file. If it is a number, tabs are copied to the tangled file
1307 unchanged, and tabs are also used for indentation, with the number
1308 of spaces per tab defined by this number. This MUST be set in order
1309 to tangle makefiles, which depend on tabs.Should only be set if
1310 this thread is not used anywhere. otherwise set to nil. "
1311 (interactive)
1312 (save-excursion
1313 (goto-char (point-min))
1314 (let ((thread-alist) (thread-list-entry) (chunk-use-name)
1315 (current-thread) (new-thread-alist))
1316 (while (re-search-forward
1317 "^[ \t]*<<\\(.*\\)>>\\(=\\)?" nil t)
1318 (goto-char (match-beginning 0))
1319 ;; Is this the definition of a chunk ?
1320 (if (match-beginning 2)
1321 ;;We have a chunk definition
1322 (progn
1323 ;; Get the thread name
1324 (setq current-thread
1325 (buffer-substring-no-properties (match-beginning 1)
1326 (match-end 1)))
1327 ;; Is this thread already in our list ?
1328 (if (assoc current-thread thread-alist)
1330 (progn
1331 ;; If not, create an entry with 4 nils at the end
1332 (setq thread-list-entry
1333 (list (cons current-thread
1334 (make-list 4 nil))))
1335 ;; And add it to the list
1336 (setq thread-alist
1337 (append thread-alist thread-list-entry)))))
1339 ;; Not a definition but a use
1340 (progn
1341 ;; Get the thread name
1342 (setq chunk-use-name
1343 (buffer-substring-no-properties (match-beginning 1)
1344 (match-end 1)))
1345 ;; Has the thread already been defined before being used ?
1346 (if (setq thread-list-entry (assoc chunk-use-name
1347 thread-alist))
1348 ;; If it has, set its parent to be the thread we are in at the moment
1349 (setcar (cdr thread-list-entry) current-thread)
1350 ;; If not, add it to the list, with its parent name and 3 nils
1351 (progn
1352 (setq thread-list-entry
1353 (list (cons chunk-use-name
1354 (cons current-thread
1355 (make-list 3 nil)))))
1356 (setq thread-alist (append thread-alist thread-list-entry)))))
1358 ;;Go to the next line
1359 (beginning-of-line 2))
1360 ;; Now, the second element of each entry points to that thread's
1361 ;; immediate parent. Need to set it to the thread's ultimate
1362 ;; parent.
1363 (let ((thread-counter 0)
1364 (this-thread)
1365 (this-thread-parent))
1366 (while (<= thread-counter (1- (length thread-alist)))
1367 (setq this-thread (nth thread-counter thread-alist))
1368 (setq this-thread-parent (assoc
1369 (car (cdr this-thread))
1370 thread-alist))
1371 (while (not (equal nil (car (cdr this-thread-parent))))
1372 (setq this-thread-parent (assoc
1373 (car (cdr this-thread-parent))
1374 thread-alist)))
1375 (setq this-thread (cons (car this-thread)
1376 (cons (car (cdr this-thread))
1377 (cons (car this-thread-parent)
1378 (nthcdr 2 this-thread)))))
1379 (setq new-thread-alist (append new-thread-alist (list this-thread)))
1380 (setq thread-counter (1+ thread-counter))))
1382 (setq ess-latex-thread-alist new-thread-alist))))
1385 ; Option setting functions to go here
1387 (defun ess-latex-set-thread-line-format ())
1389 (defun ess-latex-set-thread-tabs ())
1392 (defvar ess-latex-default-line-number-format nil
1393 "The format string to use to define line numbers in this thread.
1394 If nil, do not use line numbers.")
1396 (defvar ess-latex-default-line-number-skip-lines 0
1397 "The number of initial lines to output before the line number.
1398 This may be useful in shell scripts, where the first line (or two) must have a
1399 specific form.")
1401 (defvar ess-latex-default-tab-width 8
1402 "If a number, convert tabs to that number of spaces in the output. If nil, let tabs through to the output unaltered.")
1404 (defvar ess-latex-line-number-format ess-latex-default-line-number-format
1405 "The format string to use to define line numbers in this thread.
1406 If nil, do not use line numbers.")
1408 (defvar ess-latex-line-number-skip-lines ess-latex-default-line-number-skip-lines
1409 "The number of initial lines to output before the line number.
1410 This may be useful in shell scripts, where the first line (or two) must have a
1411 specific form.")
1413 (defvar ess-latex-tab-width ess-latex-default-tab-width
1414 "If a number, convert tabs to that number of spaces in the output. If nil, let tabs through to the output unaltered.")
1416 (defun ess-latex-get-thread-local-variables ()
1417 "Get the values of the variables that are local to a thread."
1418 (interactive)
1419 (save-restriction
1420 (save-excursion
1421 (end-of-line)
1422 (re-search-backward "^[ \t]*<<\\(.*\\)>>=" nil t)
1423 (let ((chunk-name (match-string 1)))
1424 (widen)
1425 (goto-char (point-min))
1426 (re-search-forward (concat "^<<" chunk-name ">>=") nil t)
1427 (beginning-of-line 2)
1428 (while (looking-at ".*-\*-.*-\*-")
1429 (let ((this-line (buffer-substring-no-properties
1430 (point)
1431 (progn (end-of-line) (point)))))
1432 (if (string-match
1433 "mode:[ \t]*\\([^\t ]*\\)" this-line)
1434 (setq ess-latex-code-mode (match-string-no-properties 1 this-line)))
1435 (if (string-match
1436 "ess-latex-line-number-format:[ \t]*\"\\([^\"]*\\)\"" this-line)
1437 (setq ess-latex-line-number-format
1438 (match-string-no-properties 1 this-line)))
1439 (if (string-match
1440 "ess-latex-line-number-skip-lines:[ \t]*\\([^\t ]*\\)" this-line)
1441 (setq ess-latex-line-number-skip-lines
1442 (string-to-number
1443 (match-string-no-properties 1 this-line))))
1444 (if (string-match
1445 "ess-latex-tab-width:[ \t]*\\([^\t ]*\\)" this-line)
1446 (setq ess-latex-tab-width
1447 (string-to-number
1448 (match-string-no-properties 1 this-line))))
1449 (beginning-of-line 2)))))))
1451 (defun ess-latex-reset-thread-local-variables ()
1452 "Resets the thread-local variables to their default values"
1453 (setq ess-latex-tab-width ess-latex-default-tab-width)
1454 (setq ess-latex-line-number-format ess-latex-default-line-number-format)
1455 (setq ess-latex-line-number-skip-lines ess-latex-default-line-number-skip-lines))
1457 (defun ess-latex-write-line-number (line-number-format buffer)
1458 (if line-number-format
1459 (progn
1460 (let ((this-line (count-lines (point-min)(point))))
1461 (while (string-match ".*\\(%L\\).*" line-number-format)
1462 (setq line-number-format
1463 (replace-match
1464 (format "%d" this-line) t t line-number-format 1)))
1465 (while (string-match ".*\\(%F\\).*" line-number-format)
1466 (setq line-number-format
1467 (replace-match
1468 (format "%s" (buffer-file-name)) t t line-number-format 1)))
1469 (while (string-match ".*\\(%N\\).*" line-number-format)
1470 (setq line-number-format
1471 (replace-match "\n" t t line-number-format 1)))
1472 (save-excursion
1473 (set-buffer buffer)
1474 (insert line-number-format))))))
1477 (defun ess-latex-tangle-chunk ( &optional buffer prefix-string)
1478 "Generate the code produced by this chunk, & any threads used in this chunk."
1479 (interactive)
1480 (save-excursion
1481 (ess-latex-reset-thread-local-variables)
1482 (ess-latex-get-thread-local-variables)
1483 (ess-latex-update-chunk-vector)
1484 (let*
1485 ((chunk-end (progn
1486 (end-of-line)
1487 (re-search-forward "^@" nil t)
1488 (beginning-of-line)
1489 (point)))
1490 ;;get name and start point of this chunk
1491 (chunk-start (progn
1492 (re-search-backward "^<<\\([^>]*\\)>>=$" nil t)
1493 (beginning-of-line 2)
1494 (point)))
1495 (chunk-name (buffer-substring-no-properties
1496 (match-end 1)
1497 (match-beginning 1)))
1498 ;; get end of this chunk
1499 ;; Get information we need about this thread
1500 (thread-info (assoc chunk-name ess-latex-thread-alist))
1501 (thread-tabs (nth 4 thread-info))
1502 (line-number-format (nth 3 thread-info))
1503 (thread-name-re) (post-chunk) (pre-chunk)
1504 (first-line t)
1505 (tangle-buffer (generate-new-buffer "Tangle Buffer")))
1507 (progn
1508 (goto-char chunk-start)
1509 ;; If this is a mode-line, ignore it
1510 (while (looking-at ".*-\\*-.*-\\*-")
1511 (beginning-of-line 2))
1512 ;; If we want to include line numbers, write one
1513 (if line-number-format
1514 (while (> ess-latex-line-number-skip-lines 0)
1515 (append-to-buffer tangle-buffer
1516 (point)
1517 (save-excursion (progn
1518 (end-of-line) (point))))
1519 (beginning-of-line 2)
1520 (1- ess-latex-line-number-skip-lines))
1521 (ess-latex-write-line-number line-number-format buffer))
1522 (message "Now at %d" (point))
1524 (while (< (point) chunk-end)
1525 (untabify (point) (save-excursion (beginning-of-line 2)(point)))
1526 ;; This RE gave me trouble. Without the `\"', it recognised itself
1527 ;; and so could not copy itself correctly.
1528 (if (looking-at "\\([^\n\"@]*\\)<<\\(.*\\)\\(>>\\)\\([^\n\"]*\\)$")
1529 (progn
1530 (save-restriction
1531 (save-excursion
1532 (setq thread-name-re (concat "<<"
1533 (match-string 2)
1534 ">>="))
1535 (setq pre-chunk (match-string 1))
1536 (if prefix-string (setq pre-chunk (concat prefix-string pre-chunk)))
1537 (setq post-chunk (match-string 4))
1538 (widen)
1539 (goto-char (point-min))
1540 (while (re-search-forward thread-name-re nil t)
1541 (ess-latex-tangle-chunk tangle-buffer pre-chunk)
1542 (next-line 1)))
1543 (if post-chunk
1544 (save-excursion
1545 (set-buffer tangle-buffer)
1546 (backward-char)
1547 (insert post-chunk)
1548 (beginning-of-line 2)))))
1550 ;; Otherwise, just copy this line
1551 (setq pre-chunk
1552 (buffer-substring
1553 (point)
1554 (save-excursion
1555 (beginning-of-line 2)
1556 (point))))
1557 ;; Add a prefix if necessary
1558 (if (and prefix-string
1559 (> (length pre-chunk) 1))
1560 (setq pre-chunk (concat prefix-string pre-chunk)))
1561 ;; And copy it to the buffer
1562 (save-excursion
1563 (set-buffer tangle-buffer)
1564 (insert pre-chunk)))
1565 ;; If this is the first line of the chunk, we need to change
1566 ;; prefix-string to consist solely of spaces
1567 (if (and first-line
1568 prefix-string)
1569 (progn
1570 (setq prefix-string (make-string (length prefix-string) ?\ ))
1571 (setq first-line nil)))
1572 ;; Either way, go to the next line
1573 (beginning-of-line 2))
1575 (save-excursion
1576 (set-buffer tangle-buffer)
1577 (goto-char (point-min))
1578 (while (re-search-forward "\@\<<" nil t)
1579 (replace-match "<<" nil nil)
1580 (forward-char 3))
1581 (if thread-tabs
1582 (progn
1583 (setq tab-width thread-tabs)
1584 (tabify (point-min)(point-max)))
1585 (untabify (point-min)(point-max))))
1587 (if buffer
1588 (save-excursion
1589 (set-buffer buffer)
1590 (insert-buffer-substring tangle-buffer)
1591 (kill-buffer tangle-buffer)))
1592 ))))
1594 (defun ess-latex-tangle-thread ( name &optional buffer)
1595 "Given the name of a thread, tangles the thread to buffer.
1596 If no buffer is given, create a new one with the same name as the thread."
1597 (interactive "sWhich thread ? ")
1598 (if (not buffer)
1599 (progn
1600 (setq buffer (get-buffer-create name))
1601 (save-excursion
1602 (set-buffer buffer)
1603 (erase-buffer))))
1604 (save-excursion
1605 (goto-char (point-min))
1606 (let ((chunk-counter 0))
1607 (while (re-search-forward
1608 "^<<\\(.*\\)>>=[\t ]*" nil t)
1609 (if (string= (match-string 1)
1610 name)
1611 (progn
1612 (setq chunk-counter (1+ chunk-counter))
1613 (message "Found %d chunks" chunk-counter)
1614 (ess-latex-tangle-chunk buffer)))))))
1616 (defun ess-latex-tangle-current-thread ( &optional buffer)
1617 (interactive)
1618 (save-excursion
1619 (let* ((chunk-start (progn
1620 (re-search-backward "^<<\\([^>]*\\)>>=[\t ]*$"
1621 nil t)
1622 (beginning-of-line 2)
1623 (point)))
1624 (chunk-name (buffer-substring-no-properties
1625 (match-end 1)
1626 (match-beginning 1))))
1627 (ess-latex-tangle-thread chunk-name buffer))))
1628 ;menu functions
1631 ;;; Finale
1633 (run-hooks 'ess-latex-mode-load-hook)
1634 (provide 'ess-latex-mode)
1636 ;; Changes made by Mark Lunt (mark.lunt@mrc-bsu.cam.ac.uk) 22/03/1999
1638 ;; The possibility of having code chunks using more than one language
1639 ;; was added. This was first developed by Adnan Yaqub
1640 ;; (AYaqub@orga.com) for syntax highlighting, but even people who hate
1641 ;; highlighting may like to maintain their Makefile with their code,
1642 ;; or test-scripts with their programs, or even user documentation as
1643 ;; latex-mode code chunks.
1644 ;; This required quite a few changes to ess-latex-mode:
1645 ;; 1) A new variable `ess-latex-default-code-mode' was create to do the job
1646 ;; `ess-latex-code-mode' used to.
1647 ;; 2) ess-latex-code-mode now contains the code-mode of the current chunk
1648 ;; 3) Each chunk can now have its own mode-line to tell emacs what
1649 ;; mode to use to edit it. The function `ess-latex-in-mode-line'
1650 ;; recognises such mode-lines, and the function
1651 ;; `ess-latex-set-this-code-mode' sets the code mode for the current
1652 ;; chunk and adds a mode-line if necessary. If several chunks have
1653 ;; the same name, the mode-line must appear in the first chunk with
1654 ;; that name.
1655 ;; 4) The mechanism for deciding whether to change mode was altered,
1656 ;; since the old method assumed a single code mode. Now,
1657 ;; `ess-latex-last-chunk-index' keeps track of which chunk we were in
1658 ;; last. If we have moved to a different chunk, we have to check
1659 ;; which mode we should be in, and change if necessary.
1661 ;; The keymap and menu-map handling was changed. Easymenu was used to
1662 ;; define the menu, and it the keymap was attached to the 'official'
1663 ;; minor-modes-keymaps list. This means that
1664 ;; 1) It was automatically loaded when ess-latex-mode was active and
1665 ;; unloaded when it was inactive.
1666 ;; 2) There was no need to worry about the major mode map clobbering
1667 ;; it , since it takes precedence over the major mode
1668 ;; map. `ess-latex-setup-keymap' is therefore now superfluous
1669 ;; The menu was also reorganised to make it less cluttered, so there
1670 ;; would be room for adding tangling and weaving commands (one day).
1672 ;; Mouse navigation is supported, in so far as clicking mouse-1 on the
1673 ;; '<<' of a chunk name moves to the previous instance of that chunk
1674 ;; name, and clicking in the '>>' moves to the next instance. They are
1675 ;; not mouse-hightlighted, though: too much hassle for zero added
1676 ;; functionality.
1678 ;; ess-latex-doc-mode has been given its own syntax-table. It is the same
1679 ;; as the current doc-mode syntax-table, except that [[ is a comment
1680 ;; start and ]] a comment end. Fixes some ugliness in LaTeX-mode if
1681 ;; `$' or `%' appear in quoted code (or even `<<', which happens often
1682 ;; in C++).
1683 ;; (This should make ess-latex-hide-code-quotes and
1684 ;; ess-latex-restore-code-quotes unnecessary, but I have not yet removed
1685 ;; them, nor the calls to them).
1687 ;; A new function `ess-latex-indent-line' was defined and bound by default
1688 ;; to the tab key. This should indent the current line correctly in
1689 ;; whichever mode we are currently in. Previously, c-mode in
1690 ;; particular did not behave well with indentation (although
1691 ;; `ess-latex-fill-chunk' worked fine). Indentation is only accurate
1692 ;; within the chunk: it does not know the syntax at the end of the
1693 ;; previous chunk, so it does not know where to start indenting in
1694 ;; this chunk. However, provided the indentation within each chunk is correct,
1695 ;; notangle will correctly indented code.
1697 ;; (I think it would be good to separate filling and indenting,
1698 ;; though, since `indent-region' and `fill-region' have completely
1699 ;; different meanings in LaTeX-mode (and both are useful))
1701 ;; ess-latex-mode and ess-latex-minor-mode were given an optional argument, so
1702 ;; that (ess-latex-mode -1) turns it off, (ess-latex-mode 1) turns it on, and
1703 ;; (ess-latex-mode) toggles it. This is considered normal for minor modes.
1705 ;; buffer-substring changed to buffer-substring-no-properties:
1706 ;; comparisons with buffer-substring can be unreliable if highlighting
1707 ;; is used.
1709 ;; New functions `ess-latex-in-code-chunk' & `ess-latex-chunk-is-code' created
1710 ;; to replace (if (stringp (car (ess-latex-find-chunk)))) and
1711 ;; (if (stringp (car (ess-latex-chunk-vector-aref index)))).
1713 ;; `ess-latex-insert-mode-line' was renamed
1714 ;; `ess-latex-insert-default-mode-line' and modified to put the mode-line
1715 ;; at the start of the file and remove any existing mode-line.
1717 ;; a '<=' in `ess-latex-find-chunk-index' changed to '<', so we get the
1718 ;; right answer if point is on the first character in a chunk
1720 ;; The name of `ess-latex-post-command-hook' changed to
1721 ;; `ess-latex-post-command-function', since it is a function.
1723 ;; All the highlighting code moved to a separate file (ess-latex-font-lock-mode.el)
1725 ;; Menu driven tangling is in the process of being added. It can
1726 ;; currently tangle a single chunk or a series of chunks with the
1727 ;; same name (which I refer to as a thread) into a separate
1728 ;; buffer. This buffer can then be saved to a file, sent to an
1729 ;; interpreter, whatever. I haven't tested using line-numbers as yet.