emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [babel] Painless integration of source blocks with language
@ 2011-01-08 22:29 Seth Burleigh
  2011-01-09  1:54 ` Eric Schulte
  0 siblings, 1 reply; 31+ messages in thread
From: Seth Burleigh @ 2011-01-08 22:29 UTC (permalink / raw)
  To: emacs-orgmode


[-- Attachment #1.1: Type: text/plain, Size: 3070 bytes --]

Preface: I hope attachments show up, i dont know if they are allowed ....

Im currently interested in using babel for a medium size clojure project. I
think the below propositions would greatly benefit babel in accomplishing
literate programming.

First part of the proposal to make this painless:

In a literate document, you might very well have small chunks of code for
one ns scattered around and then finally combined using noweb into one file
and tangle the output. Currently, keys like
compile-file (C-c C-k), goto function definition (M-.), do not work at all
in the source blocks. This is unfortunate, and makes life very painful. So,
first we need to get these keys to work.
Heres how it can possibly be done.

Lets say we want to compile the code in block A. Block B uses noweb syntax
to import block A, and then tangles to file src/B.clj. So we need to search
for  the presence of <<A>> in a tangled code chunk , tangle the chunk to a
file, go to the file buffer and position the cursor at the correct relative
position, and then invoke the appropriate function (in this case,
slime-compile-and-load-file).

Ive attached two files which do this, the first one is an example org file
in which you can use the compile keystrokes on (after opening a slime server
for clojure). However, it doesnt position the cursor in the tangled file
correctly, but this is not needed for compile/load file. Its not a very good
implementation, but it works for this case. Also, it doesn't work when the
indirect buffer created by C-c '  is open. The advantage of this method is
that it can easily be generalized to any language, since the compiler gets
what it expects - a file of code to operate on.

Second part of proposal:

Literate documents are good for documentation, but if you're the author, you
dont need the documentation and it will certainly get in the way of you
writing code to keep having to type C-c ' to open various chunks of code.
Ideally, we would like to have our file of code (that is, the tangled file
output for one ns in clojure) as the top buffer, and our documentation at
the bottom. Changes to the code will automatically reflect itself in the
documentation (after a save). In order to accomplish this, there has to be a
method to map from tangled file line number, to the correct chunk name and
line number in the .org file. This might be accomplished by tangling code
like this:
;;#chunk-name
..code
;;#

I think this would be much more natural than editing each chunk separately.
Of course, the ;;# might become an annoyance, but im sure there might  be a
better way.
With this mapping , we can develop the code until we get it right. Then we
can hit a keystroke and have our documentation jump to the correct line
number in the .org file, and then do all the documentation for that.

With the line mapping ability also comes the ability to map errors in the
line number to numbers in the org file. Although, this probably wouldn't be
necessary, since we would be looking at the combined chunk file.


Any thoughts/ideas how to implement this?

[-- Attachment #1.2: Type: text/html, Size: 3598 bytes --]

[-- Attachment #2: test.org --]
[-- Type: application/octet-stream, Size: 730 bytes --]

#+results:silent
#+noweb:yes 

* Functions
** Add
This is a function which adds numbers together 
#+srcname:add
#+begin_src clojure
  (defn add [a b]
    (+ a b))
#+end_src
*** TODO make work with complex numbers
** Subtract
This subtracts numbers
#+srcname:subtract
#+begin_src clojure
  (defn subtract [a b]
    (- a b))
#+end_src
*** TODO complex numbers
** Multiply
#+srcname:multiply
#+begin_src clojure
  (defn multiply [a b]
    (* a b))
#+end_src
*** TODO complex numbers
** Divide
#+srcname:divide
#+begin_src clojure
  (defn divide [a b]   
    (/ a b))
#+end_src
** All 
#+srcname:all
#+begin_src clojure :tangle src/functions.clj
  (ns functions) 
  <<add>> 
  <<subtract>>
  <<multiply>>  
  <<divide>>
#+end_src
 



[-- Attachment #3: seth-obj-clj.el --]
[-- Type: application/octet-stream, Size: 5760 bytes --]

(require 'cl)

;;UTILS
(defun char (i str)
  "get char at index"
  (substring str i (+ i 1)))
;;cant get multiline regex to work for some reason. grr
(defun string-trim-right (test-str)
  "trim all right hand whitespace,newlines"
  (let ((place (- (length test-str) 1)) (continue t))
    (while continue
      (let ((c (char place test-str))) 
	(if (or (equalp c "\n")
		(equalp c "\r")
		(equalp c " "))
	    (decf place)
	  (progn (setq continue nil) (incf place))) 
	(if (= place 0) (setq continue nil))))
    (substring test-str 0 place)))
(defmacro with-file (file &rest body)
  "open up file in a buffer, and set current buffer to it"
  `(with-current-buffer (find-file-noselect ,file t) ,@body))
(defun info (&rest args)
  "message which returns nil"
  (apply #'message args)
  nil)
(defmacro comment (&rest body) nil)
(defun mkdir (file)
  "given file name, make a directory if needed"
   (let ((dir (file-name-directory file-name)))
     (if dir (make-directory dir t))))
;;;

(defun ob-expand (&optional arg info params)
  "Expand the current source code block.
Expand according to the source code block's header
arguments and pop open the results in a preview buffer.
Trims whitespaces at the very right of text"
  (interactive) 
  (let* ((info (or info (org-babel-get-src-block-info)))
         (lang (nth 0 info))
	 (params (setf (nth 2 info)
                       (sort (org-babel-merge-params (nth 2 info) params)
                             (lambda (el1 el2) (string< (symbol-name (car el1))
                                                   (symbol-name (car el2)))))))
         (body (setf (nth 1 info)
		     (if (and (cdr (assoc :noweb params))
                              (string= "yes" (cdr (assoc :noweb params))))
			 (org-babel-expand-noweb-references info) (nth 1 info))))
         (cmd (intern (concat "org-babel-expand-body:" lang)))
         (expanded (funcall (if (fboundp cmd) cmd 'org-babel-expand-body:generic)
                            body params)))
    (string-trim-right expanded)))

;;TODO: more efficient
(defun ob-name ()
  (nth 4 (ob-info)))
(defun ob-info ()
  "return info, making sure to trim right hand whitespaces. The reason to trim is so that , in the indirect macro, we can search for the beginning and end
of the chunk. i was getting some problems with newlines at the very end messing things up - so i trimmed them away. im sure theres a better way."
  (let ((info (org-babel-get-src-block-info)))
    (if info `(,(first info) ,(string-trim-right (second info)) ,@(cddr info)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun tangle-chunk (tag)
  "find code block with the tag: if found, tangle it to file"
  (let* ((chunk (find-chunk tag))
	 (file-name (first chunk)))
    (when chunk
      (if (file-exists-p file-name) 
	  (delete-file file-name))
      (mkdir file-name)
      (with-temp-buffer
	(insert (second chunk))
	;; We avoid append-to-file as it does not work with tramp.
	(let ((content (buffer-string)))
	  (with-temp-buffer
	    (goto-char (point-max))
	    (insert content)
	    (write-region nil nil file-name))))
      chunk)))

(defun find-chunk (tag)
  "find the src block with the appropriate tag (i.e. srcname)"
  (when tag
    (flet ((chunk! 
	    (tag prev)
	    (save-excursion
	      (goto-char (point-min))
	      (let (chunks)
		(if (re-search-forward (format "<<[ \t]*%s[ \t]*>>" tag) nil t)
		    (let ((info (ob-info)))
		      (if (member info prev)
			  (message "when finding chunk, infinite loop detected")
			(if (and (equalp (first info) "clojure")
				 info (not (equalp "no"
						   (cdr (assoc :tangle (third info))))))
			    (list (cdr (assoc :tangle (third info)))
				  (ob-expand (second info))
				  (point))
			  (chunk! (ob-name) (append prev (list info)))))))))))
      (let ((info (ob-info)))
	(if (not (equalp "no" (cdr (assoc :tangle (third info)))))
	    (list (cdr (assoc :tangle (third info)))
		  (ob-expand (second info))
		  (point))	  
	  (chunk! tag nil))))))



(defun indirect-helper (operation type)
  "Given the operation and language type, return a list of the info for the tangled chunk, the tangled chunk, and the original untangled source"
  (let ((info (ob-info)))
    (when (equalp (first info) type)
      (let ((srcname (ob-name))
	    (src-untangle (ob-expand)))
	(if srcname
	    (let ((chunk (tangle-chunk srcname))) 
	      (if chunk
		  (list info chunk src-untangle)
		(info "cant %s - chunk not associated with file" operation) nil))
	  (info "cant %s - no srcname" operation))))))

(defmacro* indirect ((type operation chunk  start end) &rest body)
  (let ((lst-gen (gensym)) (orig-str (gensym)))
    `(let* ((,lst-gen (indirect-helper ,operation ,type))
	    (,orig-str (second (cdr ,lst-gen)))
	    (,chunk (second ,lst-gen)))
       (if ,chunk
	   (with-file (first ,chunk)
		      (save-excursion (goto-char (point-min)) (search-forward  ,orig-str))
		      (let ((,start (match-beginning 0)) (,end (match-end 0)))
			(progn ,@body)))))))
(defun ob-clj-compile ()
  (interactive) 
  (indirect ("clojure" "compile" chunk start end)
	    (slime-compile-and-load-file)))

(defun ob-clj-load ()
  (interactive)
  (indirect ("clojure" "compile" chunk start end)
	    (slime-load-file (buffer-file-name))))

(comment
 (defun obj-clj-slime-edit-definition () 
   (interactive)
   (indirect ("clojure" "edit definition" chunk start end offset) ;;once i can get the offset of the cursor within the original src chunk
	     (goto-char offset)
	     (slime-edit-definition))))

;;TODO: if block is in language x, use the bindings for that language automatically
(define-key org-mode-map (kbd "C-c C-k") 'ob-clj-compile)
(define-key org-mode-map (kbd "C-c C-l") 'ob-clj-load)

[-- Attachment #4: Type: text/plain, Size: 201 bytes --]

_______________________________________________
Emacs-orgmode mailing list
Please use `Reply All' to send replies to the list.
Emacs-orgmode@gnu.org
http://lists.gnu.org/mailman/listinfo/emacs-orgmode

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2011-01-26 10:43 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-08 22:29 [babel] Painless integration of source blocks with language Seth Burleigh
2011-01-09  1:54 ` Eric Schulte
2011-01-09  9:40   ` Štěpán Němec
2011-01-09 17:59     ` Eric Schulte
2011-01-10  0:59       ` Seth Burleigh
2011-01-10  2:13         ` Eric Schulte
2011-01-10  3:49           ` Seth Burleigh
2011-01-10  4:01             ` Seth Burleigh
2011-01-11 17:00             ` Eric Schulte
2011-01-10 18:46   ` Eric S Fraga
2011-01-11 17:12     ` Eric Schulte
     [not found]       ` <AANLkTi=dNTn6HBeR4wV7039FDDyPGtmWbmL0biFwT-ta@mail.gmail.com>
2011-01-11 23:09         ` Seth Burleigh
2011-01-13  9:11       ` Eric S Fraga
2011-01-13 15:23         ` Seth Burleigh
2011-01-13 21:23           ` Eric Schulte
2011-01-13 23:44             ` Seth Burleigh
2011-01-16 15:31               ` Eric Schulte
2011-01-17  9:29                 ` Sébastien Vauban
2011-01-17 16:18                   ` Eric Schulte
2011-01-17 19:32                     ` Sébastien Vauban
2011-01-17 22:15                 ` Seth Burleigh
2011-01-17 22:44                   ` Sébastien Vauban
2011-01-18 18:11                     ` Seth Burleigh
2011-01-18 18:14                     ` Seth Burleigh
2011-01-18 18:38                       ` Seth Burleigh
2011-01-19  7:28                         ` Eric Schulte
2011-01-24 14:49                           ` Seth Burleigh
2011-01-18 19:53                       ` Bastien
2011-01-24 11:56         ` Dan Davison
2011-01-24 18:56           ` Eric S Fraga
2011-01-26 10:43           ` Sébastien Vauban

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).