(file) Return to snoweb.el CVS log (file) (dir) Up to [venge] / src / snoweb

  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)

graydon hoare
Powered by
ViewCVS 0.9.2