Commit | Line | Data |
---|---|---|
afe98dfa CD |
1 | ;;; ob-js.el --- org-babel functions for Javascript |
2 | ||
ba318903 | 3 | ;; Copyright (C) 2010-2014 Free Software Foundation, Inc. |
afe98dfa CD |
4 | |
5 | ;; Author: Eric Schulte | |
6 | ;; Keywords: literate programming, reproducible research, js | |
7 | ;; Homepage: http://orgmode.org | |
afe98dfa | 8 | |
c7557a0f | 9 | ;; This file is part of GNU Emacs. |
afe98dfa | 10 | |
c7557a0f | 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
afe98dfa | 12 | ;; it under the terms of the GNU General Public License as published by |
c7557a0f GM |
13 | ;; the Free Software Foundation, either version 3 of the License, or |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
afe98dfa CD |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
c7557a0f | 20 | |
afe98dfa | 21 | ;; You should have received a copy of the GNU General Public License |
c7557a0f | 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
afe98dfa CD |
23 | |
24 | ;;; Commentary: | |
25 | ||
26 | ;; Now working with SBCL for both session and external evaluation. | |
27 | ;; | |
28 | ;; This certainly isn't optimally robust, but it seems to be working | |
29 | ;; for the basic use cases. | |
30 | ||
31 | ;;; Requirements: | |
32 | ||
33 | ;; - a non-browser javascript engine such as node.js http://nodejs.org/ | |
34 | ;; or mozrepl http://wiki.github.com/bard/mozrepl/ | |
c7557a0f | 35 | ;; |
afe98dfa CD |
36 | ;; - for session based evaluation mozrepl and moz.el are required see |
37 | ;; http://wiki.github.com/bard/mozrepl/emacs-integration for | |
38 | ;; configuration instructions | |
39 | ||
40 | ;;; Code: | |
41 | (require 'ob) | |
afe98dfa CD |
42 | (eval-when-compile (require 'cl)) |
43 | ||
44 | (declare-function run-mozilla "ext:moz" (arg)) | |
45 | ||
46 | (defvar org-babel-default-header-args:js '() | |
47 | "Default header arguments for js code blocks.") | |
48 | ||
49 | (defvar org-babel-js-eoe "org-babel-js-eoe" | |
50 | "String to indicate that evaluation has completed.") | |
51 | ||
52 | (defcustom org-babel-js-cmd "node" | |
53 | "Name of command used to evaluate js blocks." | |
54 | :group 'org-babel | |
372d7b21 | 55 | :version "24.1" |
afe98dfa CD |
56 | :type 'string) |
57 | ||
58 | (defvar org-babel-js-function-wrapper | |
59 | "require('sys').print(require('sys').inspect(function(){%s}()));" | |
60 | "Javascript code to print value of body.") | |
61 | ||
62 | (defun org-babel-execute:js (body params) | |
63 | "Execute a block of Javascript code with org-babel. | |
64 | This function is called by `org-babel-execute-src-block'" | |
65 | (let* ((org-babel-js-cmd (or (cdr (assoc :cmd params)) org-babel-js-cmd)) | |
66 | (result-type (cdr (assoc :result-type params))) | |
67 | (full-body (org-babel-expand-body:generic | |
271672fa BG |
68 | body params (org-babel-variable-assignments:js params))) |
69 | (result (if (not (string= (cdr (assoc :session params)) "none")) | |
70 | ;; session evaluation | |
71 | (let ((session (org-babel-prep-session:js | |
72 | (cdr (assoc :session params)) params))) | |
73 | (nth 1 | |
74 | (org-babel-comint-with-output | |
75 | (session (format "%S" org-babel-js-eoe) t body) | |
76 | (mapc | |
77 | (lambda (line) | |
78 | (insert (org-babel-chomp line)) | |
79 | (comint-send-input nil t)) | |
80 | (list body (format "%S" org-babel-js-eoe)))))) | |
81 | ;; external evaluation | |
82 | (let ((script-file (org-babel-temp-file "js-script-"))) | |
83 | (with-temp-file script-file | |
84 | (insert | |
85 | ;; return the value or the output | |
86 | (if (string= result-type "value") | |
87 | (format org-babel-js-function-wrapper full-body) | |
88 | full-body))) | |
89 | (org-babel-eval | |
90 | (format "%s %s" org-babel-js-cmd | |
91 | (org-babel-process-file-name script-file)) ""))))) | |
92 | (org-babel-result-cond (cdr (assoc :result-params params)) | |
93 | result (org-babel-js-read result)))) | |
afe98dfa CD |
94 | |
95 | (defun org-babel-js-read (results) | |
96 | "Convert RESULTS into an appropriate elisp value. | |
97 | If RESULTS look like a table, then convert them into an | |
98 | Emacs-lisp table, otherwise return the results as a string." | |
99 | (org-babel-read | |
100 | (if (and (stringp results) (string-match "^\\[.+\\]$" results)) | |
101 | (org-babel-read | |
102 | (concat "'" | |
103 | (replace-regexp-in-string | |
104 | "\\[" "(" (replace-regexp-in-string | |
105 | "\\]" ")" (replace-regexp-in-string | |
106 | ", " " " (replace-regexp-in-string | |
107 | "'" "\"" results)))))) | |
108 | results))) | |
109 | ||
110 | (defun org-babel-js-var-to-js (var) | |
111 | "Convert VAR into a js variable. | |
112 | Convert an elisp value into a string of js source code | |
113 | specifying a variable of the same value." | |
114 | (if (listp var) | |
115 | (concat "[" (mapconcat #'org-babel-js-var-to-js var ", ") "]") | |
116 | (format "%S" var))) | |
117 | ||
118 | (defun org-babel-prep-session:js (session params) | |
119 | "Prepare SESSION according to the header arguments specified in PARAMS." | |
120 | (let* ((session (org-babel-js-initiate-session session)) | |
121 | (var-lines (org-babel-variable-assignments:js params))) | |
122 | (when session | |
123 | (org-babel-comint-in-buffer session | |
124 | (sit-for .5) (goto-char (point-max)) | |
125 | (mapc (lambda (var) | |
126 | (insert var) (comint-send-input nil t) | |
127 | (org-babel-comint-wait-for-output session) | |
128 | (sit-for .1) (goto-char (point-max))) var-lines))) | |
129 | session)) | |
130 | ||
131 | (defun org-babel-variable-assignments:js (params) | |
8223b1d2 | 132 | "Return list of Javascript statements assigning the block's variables." |
afe98dfa CD |
133 | (mapcar |
134 | (lambda (pair) (format "var %s=%s;" | |
135 | (car pair) (org-babel-js-var-to-js (cdr pair)))) | |
136 | (mapcar #'cdr (org-babel-get-header params :var)))) | |
137 | ||
138 | (defun org-babel-js-initiate-session (&optional session) | |
139 | "If there is not a current inferior-process-buffer in SESSION | |
140 | then create. Return the initialized session." | |
141 | (unless (string= session "none") | |
142 | (cond | |
143 | ((string= "mozrepl" org-babel-js-cmd) | |
144 | (require 'moz) | |
145 | (let ((session-buffer (save-window-excursion | |
146 | (run-mozilla nil) | |
147 | (rename-buffer session) | |
148 | (current-buffer)))) | |
149 | (if (org-babel-comint-buffer-livep session-buffer) | |
150 | (progn (sit-for .25) session-buffer) | |
151 | (sit-for .5) | |
152 | (org-babel-js-initiate-session session)))) | |
153 | ((string= "node" org-babel-js-cmd ) | |
8223b1d2 | 154 | (error "Session evaluation with node.js is not supported")) |
afe98dfa | 155 | (t |
8223b1d2 | 156 | (error "Sessions are only supported with mozrepl add \":cmd mozrepl\""))))) |
afe98dfa CD |
157 | |
158 | (provide 'ob-js) | |
159 | ||
5b409b39 | 160 | |
afe98dfa CD |
161 | |
162 | ;;; ob-js.el ends here |