org-babel-tangle.el 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. ;;; org-babel-tangle.el --- Extract source code from org-mode files
  2. ;; Copyright (C) 2009 Dan Davison, Eric Schulte
  3. ;; Author: Dan Davison, Eric Schulte
  4. ;; Keywords: literate programming, reproducible research
  5. ;; Homepage: http://orgmode.org
  6. ;; Version: 0.01
  7. ;;; License:
  8. ;; This program is free software; you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 3, or (at your option)
  11. ;; any later version.
  12. ;;
  13. ;; This program is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;;
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  20. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. ;; Boston, MA 02110-1301, USA.
  22. ;;; Commentary:
  23. ;; Extract the code from source blocks out into raw source-code files.
  24. ;;; Code:
  25. (require 'org-babel)
  26. (defvar org-babel-tangle-langs nil
  27. "Association list matching source-block languages. The car of
  28. each element should be a string indicating the source block
  29. language, and the cdr should be a list containing the extension
  30. shebang(#!) line to use when writing out the language to file,
  31. and an optional flag indicating that the language is not
  32. commentable.")
  33. ;; This is just a place holder until this variable (or comparable) is
  34. ;; inserted into org-mode
  35. (defcustom org-src-lang-modes
  36. '(:ocaml "tuareg")
  37. "Property list mapping languages to their major mode.
  38. The key is the language name, the value is the string that should
  39. be inserted as the name of the major mode."
  40. :type 'plist)
  41. (defun org-babel-load-file (file)
  42. "Load the contents of the Emacs Lisp source code blocks in the
  43. org-mode formatted FILE. This function will first export the
  44. source code using `org-babel-tangle' and then load the resulting
  45. file using `load-file'."
  46. (flet ((age (file)
  47. (time-to-seconds
  48. (time-subtract (current-time)
  49. (sixth (file-attributes file))))))
  50. (let* ((base-name (file-name-sans-extension file))
  51. (exported-file (concat base-name ".el")))
  52. ;; tangle if the org-mode file is newer than the elisp file
  53. (unless (and (file-exists-p exported-file) (> (age file) (age exported-file)))
  54. (org-babel-tangle-file file base-name "emacs-lisp"))
  55. (load-file exported-file)
  56. (message "loaded %s" exported-file))))
  57. (defun org-babel-tangle-file (file &optional target-file lang)
  58. "Extract the bodies of all source code blocks in FILE with
  59. `org-babel-tangle'. Optional argument TARGET-FILE can be used to
  60. specify a default export file for all source blocks. Optional
  61. argument LANG can be used to limit the exported source code
  62. blocks by language."
  63. (save-window-excursion (find-file file) (org-babel-tangle target-file lang)))
  64. (defun org-babel-tangle (&optional target-file lang)
  65. "Extract the bodies of all source code blocks from the current
  66. file into their own source-specific files. Optional argument
  67. TARGET-FILE can be used to specify a default export file for all
  68. source blocks. Optional argument LANG can be used to limit the
  69. exported source code blocks by language."
  70. (interactive)
  71. (save-excursion
  72. (let ((block-counter 0)
  73. path-collector)
  74. (mapc ;; map over all languages
  75. (lambda (by-lang)
  76. (let* ((lang (car by-lang))
  77. (specs (cdr by-lang))
  78. (lang-f (intern (concat
  79. (or (plist-get org-src-lang-modes
  80. (intern (concat ":" lang)))
  81. lang)
  82. "-mode")))
  83. (lang-specs (cdr (assoc lang org-babel-tangle-langs)))
  84. (ext (first lang-specs))
  85. (she-bang (second lang-specs))
  86. (commentable (not (third lang-specs))))
  87. (mapc
  88. (lambda (spec)
  89. (let* ((tangle (cdr (assoc :tangle (third spec))))
  90. (base-name (or (cond
  91. ((string= "yes" tangle)
  92. (file-name-sans-extension (buffer-file-name)))
  93. ((string= "no" tangle) nil)
  94. ((> (length tangle) 0) tangle))
  95. target-file))
  96. (file-name (when base-name
  97. (concat base-name "." ext))))
  98. ;; ;; debugging
  99. ;; (message "tangle=%S base-name=%S file-name=%S"
  100. ;; tangle base-name file-name)
  101. (when file-name
  102. ;; delete any old versions of file
  103. (when (and (file-exists-p file-name)
  104. (not (member file-name path-collector)))
  105. (delete-file file-name))
  106. ;; drop source-block to file
  107. (with-temp-buffer
  108. (funcall lang-f)
  109. (when she-bang (insert (concat she-bang "\n")))
  110. (when commentable
  111. (comment-region
  112. (point) (progn (insert "generated by org-babel-tangle") (point)))
  113. (move-end-of-line nil))
  114. (org-babel-spec-to-string spec)
  115. (append-to-file nil nil file-name))
  116. ;; update counter
  117. (setq block-counter (+ 1 block-counter))
  118. (add-to-list 'path-collector file-name))))
  119. specs)))
  120. (org-babel-tangle-collect-blocks lang))
  121. (message "tangled %d source-code block%s" block-counter
  122. (if (> block-counter 1) "s" ""))
  123. path-collector)))
  124. (defun org-babel-tangle-collect-blocks (&optional lang)
  125. "Collect all source blocks in the current org-mode file.
  126. Return an association list of source-code block specifications of
  127. the form used by `org-babel-spec-to-string' grouped by language.
  128. Optional argument LANG can be used to limit the collected source
  129. code blocks by language."
  130. (let ((block-counter 0) blocks)
  131. (org-babel-map-source-blocks (buffer-file-name)
  132. (setq block-counter (+ 1 block-counter))
  133. (let* ((link (progn (call-interactively 'org-store-link)
  134. (org-babel-clean-text-properties (car (pop org-stored-links)))))
  135. (source-name (intern (or (org-babel-get-src-block-name)
  136. (format "block-%d" block-counter))))
  137. (info (org-babel-get-src-block-info))
  138. (src-lang (first info))
  139. (body (org-babel-expand-noweb-references info))
  140. (params (third info))
  141. (spec (list link source-name params body (third (cdr (assoc src-lang org-babel-tangle-langs)))))
  142. by-lang)
  143. (unless (string= (cdr (assoc :tangle params)) "no") ;; maybe skip
  144. (unless (and lang (not (string= lang src-lang))) ;; maybe limit by language
  145. ;; add the spec for this block to blocks under it's language
  146. (setq by-lang (cdr (assoc src-lang blocks)))
  147. (setq blocks (delq (assoc src-lang blocks) blocks))
  148. (setq blocks (cons (cons src-lang (cons spec by-lang)) blocks))))))
  149. ;; ensure blocks in the correct order
  150. (setq blocks
  151. (mapcar (lambda (by-lang) (cons (car by-lang) (reverse (cdr by-lang)))) blocks))
  152. ;; blocks should contain all source-blocks organized by language
  153. ;; (message "blocks=%S" blocks) ;; debugging
  154. blocks))
  155. (defun org-babel-spec-to-string (spec)
  156. "Insert the source-code specified by SPEC into the current
  157. source code file. This function uses `comment-region' which
  158. assumes that the appropriate major-mode is set. SPEC has the
  159. form
  160. (link source-name params body)"
  161. (flet ((insert-comment (text)
  162. (when commentable
  163. (comment-region (point) (progn (insert text) (point)))
  164. (move-end-of-line nil))))
  165. (let ((link (first spec))
  166. (source-name (second spec))
  167. (body (fourth spec))
  168. (commentable (not (fifth spec))))
  169. (insert "\n\n")
  170. (insert-comment (format "[[%s][%s]]" (org-link-escape link) source-name))
  171. (insert (format "\n%s\n" (org-babel-chomp body)))
  172. (insert-comment (format "%s ends here" source-name))
  173. (insert "\n"))))
  174. (defun org-babel-expand-noweb-references (&optional info parent-buffer)
  175. "This function expands Noweb style references in the body of
  176. the current source-code block. The reference must be inside of a
  177. comment or it will be skipped. For example the following
  178. reference would be replaced with the body of the source-code
  179. block named 'example-block' (assuming the '#' character starts a
  180. comment) .
  181. # <<example-block>>
  182. This function must be called from inside of the buffer containing
  183. the source-code block which holds BODY."
  184. (let* ((parent-buffer (or parent-buffer (current-buffer)))
  185. (info (or info (org-babel-get-src-block-info)))
  186. (lang (first info))
  187. (body (second info))
  188. (new-body "") index source-name)
  189. (flet ((nb-add (text)
  190. (setq new-body (concat new-body text))))
  191. (with-temp-buffer
  192. (insert body) (goto-char (point-min))
  193. (funcall (intern (concat (or (plist-get org-src-lang-modes
  194. (intern (concat ":" lang)))
  195. lang) "-mode")))
  196. (setq index (point))
  197. (while (and (re-search-forward "<<\\(.+\\)>>" nil t))
  198. (save-match-data (setf source-name (match-string 1)))
  199. ;; add interval to new-body
  200. (goto-char (match-end 0)) (move-end-of-line nil)
  201. (nb-add (buffer-substring index (point)))
  202. (setq index (point))
  203. ;; if found, add body of referenced source-block
  204. (nb-add (save-excursion
  205. (set-buffer parent-buffer)
  206. (let ((point (org-babel-find-named-block source-name)))
  207. (if point
  208. (save-excursion
  209. (goto-char point)
  210. (concat "\n" (org-babel-expand-noweb-references
  211. (org-babel-get-src-block-info))))
  212. "")))))
  213. (nb-add (buffer-substring index (point-max)))))
  214. new-body))
  215. (provide 'org-babel-tangle)
  216. ;;; org-babel-tangle.el ends here