1 graydon 1.1 ;;
2 ;;
3 ;;
4 ;; Snoweb is a literate programming tool integrated into xemacs, including the
5 ;; unique feature of hypertextual "perspective shift" between web and tangled
6 ;; files, which I think you'll really like. The name is short for "shiftable
7 ;; noweb", although perhaps it's also an allusion to the fact that I'm canadian
8 ;; and like the snow.
9 ;;
10 ;; Unlike noweb (or other WEB nockoffs), snoweb is coded entirely in elisp and
11 ;; operates entirely within the editor (well, it uses noweb's cpif, and requires
12 ;; compile.el to be installed at your site). As such, any other xemacs functions
13 ;; can interact with, or improve upon, the WEB editing experience. For instance,
14 ;; it it possible to have your TAGS table, the compiler output, ediff, etc. all
15 ;; use the temporary buffers generated by snoweb, which are put together in such
16 ;; a way that they propagate changes back into the master web file in the
17 ;; appropriate defs. It was written in reaction to noweb, and the fact that you
18 ;; really have very little feedback in a pipeline setting as to whether your
19 ;; program is coming out the right way. With snoweb, you just hit "C-ret" and
20 ;; the buffer tangles around point, placing you in an editable,
21 ;; debugging-friendly view of the code as the compiler will see it.
22 graydon 1.1 ;;
23 ;; That said, I like noweb, and have remained faithful to its syntax; snoweb
24 ;; files can be fed into noweb and tangled/woven without any modification. I
25 ;; just happen to want direct feedback when editing, which noweb couldn't
26 ;; provide.
27 ;;
28 ;; I apologize if you're using FSF emacs, but will not attempt to modify this to
29 ;; work with hidden regions, vectors, outlines and narrowing. I've seen some
30 ;; other noweb modes out there which take this approach, and frankly it is of no
31 ;; intrest. Tell your site admin to upgrade to xemacs. It's better. Furthermore
32 ;; if I receive patches which introduce or replace its existing approach with a
33 ;; narrowing/hiding one, I will discard them as I have no intrest in installing
34 ;; FSF emacs in order to maintain them. If you want to fork the code you can, by
35 ;; all means, but imho The Right Way to build this program was using extents and
36 ;; scratch buffers; If you have a BOFH running things and you can't get xemacs,
37 ;; you can always try implementing extents (or some workalike) in FSF emacs.
38 ;;
39 ;; Snoweb is copyright (C) 1999 Graydon Hoare <graydon@pobox.com>
40 ;; Released under the terms of the GNU General Public License vers. 2.1+
41 ;; updates can be found at http://www.pobox.com/~graydon/snoweb.html
42 ;;
43 graydon 1.1 ;; I'd appreciate it if you'd feed any patches, feature requests, comments,
44 ;; bugfixes, etc. back to me. Also I accept post cards and free food/beer
45 ;;
46 ;; In order to try out snoweb, add the following entries to your .emacs file:
47 ;;
48 ;; (require 'snoweb-mode "snoweb.el")
49 ;; (setq auto-mode-alist (append ("\\.nw$" . snoweb-mode) auto-mode-alist))
50 ;;
51 ;; then add in the following local variables to your noweb file:
52 ;;
53 ;; % snoweb-tangled-roots: ("root-a.cc" "root-a.hh" ... "root-z.hh")
54 ;; % snoweb-default-tangle-mode: cc-mode
55 ;;
56 ;; The tangled roots list is used by the compiler wrapper functions. These
57 ;; entries will be tangled out of the current buffer and cpif'ed onto existing
58 ;; files in $CWD when you go to run a snoweb-supervised compile job. Likewise
59 ;; when the error messages start flowing back from the compiler, these entries
60 ;; will be consulted as "available files" which the compile-find-file wrapper will
61 ;; then tangle to produce bug-fixing buffers for you. It should be reasonably
62 ;; smooth once you get the list of files worked out and you're used to it.
63 ;;
64 graydon 1.1 ;; The default tangle mode is, as you would guess, the code mode snoweb will
65 ;; throw your tangle buffers into if it cannot guess a mode using the chunk name.
66 ;; This is useful for those of you who name chunks <<things to show my cat>>
67 ;; rather than <<cat-stuff.hh>.
68 ;;
69 ;; Anyway, if you're just playing with this mode, load up a normal noweb file,
70 ;; move over top of a code chunk, and press "C-ret" to shift to a tangle view,
71 ;; and "C-ret" to shift back. Nifty, eh?
72 ;;
73 ;; Version History:
74 ;;
75 ;; 0.1 initial release
76 ;; 0.2 slight updates to fix chunk scanning bugz and wrong assumption in
77 ;; one-page guide to noweb
78 ;; 0.3 added in default mode (local variable) and integrated with compiler.
79 ;; 0.4 fixed bugs related to my not knowing how to localize things :)
80 ;;
81 ;;
82
83
84 (defun snoweb-mode ()
85 graydon 1.1
86 "Snoweb: a Hypertextual Literate Programming Mode for Xemacs"
87
88 (interactive)
89 (progn
90 (kill-all-local-variables)
91 (setq snoweb-mode t)
92 (if snoweb-mode-map nil (setq snoweb-mode-map (make-sparse-keymap)))
93 (make-local-hook 'after-change-functions)
94 (add-hook 'after-change-functions 'snoweb-repair-damage 't 't)
95 (snoweb-setup-keymap)
96 (snoweb-re-scan-extents)
97
98 ;; intercept the current definition of compile to go through our tangler
99 (load "compile.elc")
100 (make-local-variable 'compile)
101 (fset 'snoweb-surrogate-compile (symbol-function 'compile))
102 (fset 'compile 'snoweb-tangle-and-compile)
103
104 ;; intercept the current definition of compilation-find-file to go through our tangler
105 (make-local-variable 'compilation-find-file)
106 graydon 1.1 (fset 'snoweb-surrogate-compilation-find-file (symbol-function 'compilation-find-file))
107 (fset 'compilation-find-file 'snoweb-compilation-find-file)))
108
109 (defvar snoweb-mode nil "Buffer local variable, T iff this buffer is edited in snoweb mode.")
110
111
112 (defvar snoweb-mode-map nil "keymap for snoweb")
113
114 (defvar snoweb-compile-command "make -k"
115 "Initial value for snoweb supervised compile subprocess name")
116
117 (defun snoweb-extent-at (num)
118
119 "Returns the nearest-to-point extent which snoweb has placed on the text
120 (rather than, say, a font lock or edit marker extent)"
121
122 (extent-at num nil 'snoweb-extent-type))
123
124
125
126 (defun snoweb-repair-damage (start end len)
127 graydon 1.1
128 "Fixes up the snoweb-chunk-name extent attribute on a chunk tag extent when a
129 user happens to edit the text of the chunk tag. Registered with the after-changed-functions
130 hook list within a snoweb buffer. This is required because most functions in snoweb operate
131 on extent properties, not the text of the tags themselves."
132
133 (if (extentp (snoweb-extent-at start))
134 (let ((ex (snoweb-extent-at start)))
135 (cond
136 ((snoweb-extent-is-a ex 'code-tag) (snoweb-repair-chunk-name-tag ex))
137 ((snoweb-extent-is-a ex 'code) (snoweb-repair-code-def ex))))))
138
139
140
141 (defun snoweb-repair-chunk-name-tag (e)
142
143 "Sets the code tag and its guts extent to have the chunk-name extracted from the tag"
144
145 (let* ((guts (extent-property e 'snoweb-guts-ext))
146 (name (snoweb-extract-chunk-name e))
147 (renamer (function (lambda (x) (set-extent-property x 'snoweb-chunk-name name)))))
148 graydon 1.1 (map 'list renamer (list guts e))))
149
150
151
152 (defun snoweb-extract-chunk-name (ex)
153
154 "Returns a symbol for the snoweb-chunk-name extent property of the extent you hand it"
155
156 (make-symbol
157 (cond
158 ((snoweb-extent-is-a ex 'code-tag)
159 (buffer-string
160 (+ (extent-start-position ex) 2) (- (extent-end-position ex) 3) nil))
161 ((snoweb-extent-is-a ex 'code) (symbol-name (extent-property ex 'snoweb-chunk-name)))
162 ('t ""))))
163
164
165
166 (defun snoweb-repair-code-def (ex) "Dummy function for future use" )
167
168
169 graydon 1.1
170 (defun snoweb-electric-@ (arg)
171
172 "Starts a doc chunk (terminating a code chunk)"
173
174 (interactive "P")
175 (if arg
176 (self-insert-command (if (numberp arg) arg 1))
177 (if (snoweb-at-beginning-of-line)
178 (snoweb-make-new-chunk 'doc)
179 (self-insert-command 1))))
180
181
182
183 (defun snoweb-electric-< (arg)
184
185 "Starts a code chunk, or a code transclusion, depending on whether it is
186 invoked from within a doc chunk or a code chunk."
187
188 (interactive "P")
189 (if arg
190 graydon 1.1 (self-insert-command (if (numberp arg) arg 1))
191 (if (and (snoweb-at-beginning-of-line) (snoweb-not-in-code-extent))
192 (snoweb-make-new-chunk 'code)
193 (self-insert-command 1))))
194
195
196
197 (defun snoweb-make-new-chunk (chunk-type)
198
199 "If given 'code, sets up a tag extent and a code chunk extent. If given 'doc, sets up
200 a doc chunk extent. Tags are kept in separate chunks because they are markup and
201 should not be included in the tangled buffers."
202
203 (if (equal chunk-type 'code)
204 (progn
205 (insert "<<") (save-excursion (insert ">>=\n\n"))
206 (let ((my-tag-extent (make-extent (- (point) 2) (+ (point) 3)))
207 (my-code-extent (make-extent (+ (point) 4) (+ (point) 5))))
208 (snoweb-mark-extent-as my-tag-extent 'code-tag)
209 (snoweb-mark-extent-as my-code-extent 'code)
210 (snoweb-attach-tag-to-guts my-tag-extent my-code-extent)))
211 graydon 1.1
212 ;; else it's a doc chunk
213 (progn
214 (insert "@\n\n")
215 (let ((ex (snoweb-extent-at (point))))
216 (if (and (not (equal nil ex)) (snoweb-extent-is-a ex 'code))
217 (set-extent-endpoints ex (extent-start-position ex) (- (point) 3)))
218 (let ((ex (make-extent (- (point) 3) (point))))
219 (snoweb-mark-extent-as ex 'doc))))))
220
221
222
223 (defun snoweb-attach-tag-to-guts (tag-ext guts-ext)
224
225 "Sets up references between a tag and its \"guts\", i.e. the code chunk
226 immediately following it"
227
228 (progn
229 (set-extent-property tag-ext 'snoweb-guts-ext guts-ext)
230 (set-extent-property guts-ext 'snoweb-tag-ext tag-ext)))
231
232 graydon 1.1
233
234 (defun snoweb-mark-extent-as (ex extent-type)
235
236 "Marks an extent with a type specifier property, as well as customizing its appearance
237 and extent behaviour (open/closed ends, etc.) in the snoweb buffer"
238
239 (let (
240 (prop-setter (function (lambda (pair) (set-extent-property ex (car pair) (cdr pair)))))
241 (prop-list
242 (cons (list 'snoweb-extent-type extent-type)
243 (cond
244 ((equal extent-type 'code)
245 `((start-closed . t) (end-closed . nil) (face . secondary-selection)))
246 ((equal extent-type 'doc)
247 '((start-open . t) (end-closed . nil)))
248 ((equal extent-type 'code-tag)
249 '((start-open . t) (face . blue)))))))
250 (map 'list prop-setter prop-list)))
251
252
253 graydon 1.1
254 (defun snoweb-not-in-code-extent (&optional p)
255
256 "Returns true if p (defaulting to point) is anywhere not in a code extent, otherwise returns false."
257
258 (interactive "d")
259 (let ((pt (if p p (point))))
260 (if (extentp (snoweb-extent-at pt))
261 (not (snoweb-extent-is-a (snoweb-extent-at pt) 'code))
262 't)))
263
264
265
266 (defun snoweb-setup-keymap ()
267
268 "Setup the snoweb keymap"
269
270 (progn
271 (local-set-key "@" 'snoweb-electric-@)
272 (local-set-key "<" 'snoweb-electric-<))
273 (local-set-key '(control return) 'snoweb-tangle-perspective))
274 graydon 1.1
275
276
277 (defun snoweb-at-beginning-of-line ()
278
279 "Returns true of point is at the beginning of a line, else false"
280
281 (equal (save-excursion (beginning-of-line) (point)) (point)))
282
283
284
285 (defun snoweb-scan-one-chunk-pair ()
286
287 "Scans a single chunk pair, that is any text from point until a code tag, then the tag, then
288 the chunk following the tag (which is a code chunk). An extent is built for each of
289 these blocks of text, and they are marked appropriately using snoweb-mark-extent-as."
290
291 (let ((n (point)))
292 (while (and
293 (not (looking-at "^\\(<<\\(.*\\)>>=\\)"))
294 (not (equal (point) (point-max))))
295 graydon 1.1 (forward-line))
296 ;; mark what we scanned (if anything) as a doc extent
297 (cond ((not (equal n (point)))
298 (snoweb-mark-extent-as (make-extent n (point)) 'doc)))
299 ;; furthermore, if we hit a code-tag (rather than just EOF) we should
300 ;; set up a code-tag extent as well as a code extent. the code
301 ;; extent we delegate to snoweb-scan-one-code-extent for legibility sake
302 (cond ((looking-at "^\\(<<\\(.*\\)>>=\\)")
303 (progn
304 (forward-line) ;; move ahead
305 (let ((tag-ex (make-extent (match-beginning 1) (match-end 1)))
306 (code-ex (snoweb-scan-one-code-chunk)))
307 (snoweb-mark-extent-as tag-ex 'code-tag)
308 (snoweb-mark-extent-as code-ex 'code)
309 (snoweb-attach-tag-to-guts tag-ex code-ex)
310 ))))))
311
312
313
314 (defun snoweb-scan-one-code-chunk ()
315
316 graydon 1.1 "A helper for snoweb-scan-one-chunk-pair which advances over a code chunk
317 and returns an extent between the beginning of its scan and the end (the end of
318 a line before a line which begins with a @ and not an @@"
319
320 (progn
321 (beginning-of-line)
322 (let ((pt (point)))
323 (while (and
324 (not (looking-at "^@[^@]"))
325 (not (looking-at "^@$"))
326 (not (looking-at "^<<.*>>="))
327 (not (equal (point) (point-max))))
328 (forward-line))
329 (make-extent pt (point)))))
330
331
332
333 (defun snoweb-re-scan-extents ()
334
335 "Called when entering snoweb mode, or if for some reason you feel the
336 snoweb extent markers of the main snoweb document have been corrupted somehow"
337 graydon 1.1
338 (interactive)
339 (mapcar-extents (function (lambda (ex) (delete-extent ex))) nil nil nil nil nil 'snoweb-extent-type nil)
340 (save-excursion
341 (goto-char (point-min))
342 (while (not (equal (point-max) (point)))
343 (snoweb-scan-one-chunk-pair)))
344 (mapcar-extents (function (lambda (ex)
345 (if (snoweb-extent-is-a ex 'code-tag)
346 (snoweb-repair-chunk-name-tag ex))))
347 nil nil nil nil nil 'snoweb-extent-type nil))
348
349
350
351 (defun snoweb-chunk-nm ()
352
353 "Interactively informs you of the name of the chunk you happen to be in. Mostly for debugging
354 the chunk tag name extraction function."
355
356 (interactive)
357 (message (symbol-name (snoweb-extract-chunk-name (snoweb-extent-at (point))))))
358 graydon 1.1
359
360
361 (defun snoweb-extent-is-a (ex type)
362
363 "Helper function for determining extent type, with fewer keystrokes."
364
365 (equal (extent-property ex 'snoweb-extent-type) (list type)))
366
367
368
369 (defun snoweb-build-fragment-list (code-chunk-name)
370
371 "This is the heart of the hypertext system in snoweb. It extracts a list of (begin end) pairs
372 which sequentially mark the sections of text to extract from the source snoweb document in
373 order to construct the named chunk. This list can then be used either to tangle, or to
374 construct relative coordinates in the tangled document, or both."
375
376 (apply 'nconc
377 (mapcar-extents
378 (function
379 graydon 1.1 (lambda (ex)
380 (let ((fragment-list '())
381 (frag-start (extent-start-position ex)))
382 (save-excursion
383 (goto-char frag-start)
384 (while (re-search-forward "<<\\(.*\\)>>" (extent-end-position ex) 't)
385 (let ((frag-end (match-beginning 0))
386 (next-frag-start (match-end 0))
387 (sub-fragments (snoweb-build-fragment-list (match-string 1))))
388 (setq fragment-list (nconc fragment-list (list (list frag-start frag-end))))
389 (if (not (equal nil sub-fragments)) (setq fragment-list (nconc fragment-list sub-fragments)))
390 (setq frag-start next-frag-start)))
391 (if (not (equal frag-start (extent-end-position ex)))
392 (setq fragment-list (nconc fragment-list (list (list frag-start (extent-end-position ex))))))))))
393 (lambda (ex) (and (snoweb-extent-is-a ex 'code)
394 (equal (symbol-name (extent-property ex 'snoweb-chunk-name))
395 code-chunk-name)))
396 nil nil nil nil 'snoweb-extent-type nil)))
397
398
399
400 graydon 1.1 (defun snoweb-setup-tangled-buffer (dest-name)
401 "Tangles a given named chunk, reading fragments from current snoweb buffer. Returns the
402 tangle buffer object in question"
403
404 (progn
405 (if (get-buffer dest-name) (erase-buffer dest-name))
406 (let (
407 (web-buffer (current-buffer))
408 (tangle-buffer (get-buffer-create dest-name))
409 (fragment-list (snoweb-build-fragment-list dest-name)))
410
411 (save-excursion
412 (set-buffer tangle-buffer)
413 (message (concat "snoweb: tangling \"" dest-name "\""))
414 (map 'list
415 (function (lambda (pair)
416 ;; now we insert (copy) text, span it with a new extent
417 ;; and set a value in the extent pointing back into the source
418 ;; file so it's easy to propagate changes to the master snoweb doc
419 (insert-buffer-substring web-buffer (car pair) (cadr pair))
420 (let ((ex (make-extent (point) (- (point) (- (cadr pair) (car pair))))))
421 graydon 1.1 (set-extent-property ex 'snoweb-extent-type 'web-fragment)
422 (set-extent-property ex 'snoweb-web-offset (car pair))
423 (set-extent-property ex 'snoweb-web-buffer web-buffer))))
424 fragment-list )
425 (setq buffer-file-name dest-name))
426 tangle-buffer)))
427
428
429
430 (defvar snoweb-default-tangle-mode 'java-mode "default code mode for a tangled buffer with unguessable filename")
431
432
433
434 (defvar snoweb-tangled-roots '("*") "roots to tangle in a default file")
435
436
437
438 (defun snoweb-tangle-chunk (dest-name &optional dest)
439
440 "Tangles a given named chunk and moves to the \"dest\" position within the
441 tangled file (dest must be a character count, not a line number). You can use
442 graydon 1.1 this either in response to something like a TAGS file or a compiler error
443 message, or else interactively using the perspective shift keystrokes."
444
445 (let ((tangle-mode snoweb-default-tangle-mode)
446 (tangle-buf (snoweb-setup-tangled-buffer dest-name)))
447 (switch-to-buffer dest-name)
448 (if dest (goto-char dest))
449 (if (not (equal nil tangle-mode))
450 (funcall tangle-mode))
451 (set-auto-mode)
452 (run-hooks 'find-file-hooks)
453 (make-local-hook 'after-change-functions)
454 (add-hook 'after-change-functions 'snoweb-propagate-change 't 't)
455 (local-set-key '(control return) 'snoweb-untangle-perspective)
456 (set-buffer-modified-p nil)
457 tangle-buf))
458
459 (defun snoweb-tangle-perspective ()
460
461 "Interactively calls snoweb-tangle-chunk using the chunk currently around (point) and
462 the relative coordinates within the destination file. i.e. you don't feel like your cursor moves,
463 graydon 1.1 merely all the text \"moves around it\"."
464
465 (interactive)
466 (let ((dest-name (symbol-name (snoweb-extract-chunk-name (snoweb-extent-at (point)))))
467 (dest (snoweb-tangled-coord (point))))
468 (get-buffer-create dest-name)
469 (erase-buffer dest-name)
470 (snoweb-tangle-chunk dest-name dest)))
471
472
473
474 (defun snoweb-untangle-perspective ()
475
476 "Interactively untangles the scratch buffer you're editing, moving you back to
477 the current def in the source snoweb document, and discarding the scratch
478 buffer. The experience is that your cursor stays put and the snoweb document
479 \"re-appears\" around the text you're over top of"
480
481 (interactive)
482 (if (extentp (snoweb-extent-at (point)))
483 (let* (
484 graydon 1.1 (ex (snoweb-extent-at (point)))
485 (web-buf (extent-property ex 'snoweb-web-buffer))
486 (web-offset (extent-property ex 'snoweb-web-offset))
487 (tangle-buf (current-buffer))
488 (tangle-offset (extent-start-position ex))
489 (delta (- (point) tangle-offset)))
490 (switch-to-buffer web-buf)
491 (goto-char (+ web-offset delta))
492 )))
493
494
495
496 (defun snoweb-propagate-change (start end len)
497
498 "Propagates a change from a snoweb scratch buffer back to the source snoweb buffer
499 it came from. You should not consider the scratch buffer as \"a file\" you need
500 to save or do version control on or anything. Rather it is a temporary view of
501 the snoweb file; editing it edits the snoweb file directly."
502
503 (if (extentp (snoweb-extent-at start))
504 (let* (
505 graydon 1.1 (ex (snoweb-extent-at start))
506 (web-buf (extent-property ex 'snoweb-web-buffer))
507 (web-offset (extent-property ex 'snoweb-web-offset))
508 (tangle-buf (current-buffer))
509 (tangle-offset (extent-start-position ex))
510 (delta (- start tangle-offset)))
511 (if
512 (equal 0 len)
513 ;; it was an insertion
514 (save-excursion
515 (set-buffer web-buf)
516 (goto-char (+ web-offset delta))
517 (insert-buffer-substring tangle-buf start end))
518 ;; else it was a deletion
519 (delete-region (+ web-offset delta) (+ web-offset delta len) web-buf))
520 (set-buffer-modified-p nil))))
521
522
523
524
525 (defun snoweb-tangled-coord (pos)
526 graydon 1.1
527 "Returns the position of \"pos\" inside the tangled view of the chunk in which
528 pos resides. Used in mapping snoweb buffer coordinates into tangled buffers"
529
530 (let ((chunk-name (symbol-name (snoweb-extract-chunk-name (snoweb-extent-at pos))))
531 (past-pos nil))
532 (apply '+
533 (map 'list
534 (function
535 (lambda (pair)
536 (if
537 (and (<= (car pair) pos) (>= (cadr pair) pos))
538 (progn
539 (setq past-pos 't)
540 (- (+ pos 1) (car pair)))
541 (if (not past-pos)
542 (- (cadr pair) (car pair))
543 0))))
544 (snoweb-build-fragment-list chunk-name)))))
545
546
547 graydon 1.1
548 (defun snoweb-tangle-and-compile (command)
549
550 "Tangles all the buffers declared in snoweb-tangled-roots, then launches the
551 surrogate compile command, which is most likely the function you normally call
552 \"compile\""
553
554 (interactive (list (read-string "[Snoweb] Compile command: " snoweb-compile-command )))
555 (setq snoweb-compile-command command)
556 (snoweb-tangle-roots (current-buffer))
557 (snoweb-surrogate-compile command))
558
559
560
561 (defun snoweb-tangle-roots (src-buf)
562
563 "Reads the buffer-local variable snoweb-tangled-roots and tangles them all,
564 writing them out to files (via cpif) in the current working directory."
565
566 (map 'list
567 (function
568 graydon 1.1 (lambda (name)
569 (save-excursion
570 (set-buffer (snoweb-setup-tangled-buffer name))
571 (set-buffer-modified-p nil)
572 (call-process-region (point-min) (point-max) "cpif" nil nil nil name))))
573 (cdr (assoc 'snoweb-tangled-roots (buffer-local-variables src-buf)))))
574
575
576
577 (defun snoweb-compilation-find-file (marker filename dir &rest formats)
578
579 "Fakes out the compilation-find-file function by tangling an edit buffer on
580 demand (if such a buffer is to be found anywhere). If it fails to do this, it
581 just passes through to compilation-find-file. This takes the brute force approach
582 of checking every open snoweb buffer, which is both crude and error prone, but
583 it's simple to implement and you are welcome to fix it with some sort of hashtable
584 which tracks the associations. It doesn't take that long anyway."
585 (let ((source-buf
586 (member-if
587 (lambda (buf)
588 (and
589 graydon 1.1 (local-variable-p 'snoweb-tangled-roots buf)
590 (member filename (assoc 'snoweb-tangled-roots
591 (buffer-local-variables buf)))))
592 (buffer-list))))
593 (if (and (not (equal nil source-buf)) (bufferp (car source-buf)))
594 (progn
595 (if (get-buffer filename) (get-buffer filename))
596 (set-buffer (buffer-name (car source-buf)))
597 (snoweb-tangle-chunk filename))
598 ;; else delegate to existing file-finder.. sigh..
599 (apply 'snoweb-surrogate-compilation-find-file (append (list marker filename dir) formats)))))
600
601
602
603
604 (provide 'snoweb-mode)
|