summaryrefslogtreecommitdiff
path: root/lisp/ob-processing.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/ob-processing.el')
-rw-r--r--lisp/ob-processing.el197
1 files changed, 197 insertions, 0 deletions
diff --git a/lisp/ob-processing.el b/lisp/ob-processing.el
new file mode 100644
index 0000000..d983afe
--- /dev/null
+++ b/lisp/ob-processing.el
@@ -0,0 +1,197 @@
+;;; ob-processing.el --- Babel functions for evaluation of processing
+
+;; Copyright (C) 2015 Free Software Foundation, Inc.
+
+;; Author: Jarmo Hurri (adapted from ob-asymptote.el written by Eric Schulte)
+;; Keywords: literate programming, reproducible research
+;; Homepage: http://orgmode.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Babel support for evaluating processing source code.
+;;
+;; This differs from most standard languages in that
+;;
+;; 1) there is no such thing as a "session" in processing
+;;
+;; 2) results can only be exported as html; in this case, the
+;; processing code is embedded via a file into a javascript block
+;; using the processing.js module; the script then draws the
+;; resulting output when the web page is viewed in a browser; note
+;; that the user is responsible for making sure that processing.js
+;; is available on the website
+;;
+;; 3) it is possible to interactively view the sketch of the
+;; Processing code block via Processing 2.0 Emacs mode, using
+;; `org-babel-processing-view-sketch'. You can bind this command
+;; to, e.g., C-c C-v C-k with
+;;
+;; (define-key org-babel-map (kbd "C-k") 'org-babel-processing-view-sketch)
+
+
+;;; Requirements:
+
+;; - processing2-emacs mode :: https://github.com/ptrv/processing2-emacs
+;; - Processing.js module :: http://processingjs.org/
+
+;;; Code:
+(require 'ob)
+(require 'sha1)
+(eval-when-compile (require 'cl))
+
+(declare-function processing-sketch-run "ext:processing-mode" ())
+
+(defvar org-babel-temporary-directory)
+
+(defvar org-babel-tangle-lang-exts)
+(add-to-list 'org-babel-tangle-lang-exts '("processing" . "pde"))
+
+;; Default header tags depend on whether exporting html or not; if not
+;; exporting html, then no results are produced; otherwise results are
+;; HTML.
+(defvar org-babel-default-header-args:processing
+ '((:results . "html") (:exports . "results"))
+ "Default arguments when evaluating a Processing source block.")
+
+(defvar org-babel-processing-processing-js-filename "processing.js"
+ "Filename of the processing.js file.")
+
+(defun org-babel-processing-view-sketch ()
+ "Show the sketch of the Processing block under point in an external viewer."
+ (interactive)
+ (require 'processing-mode)
+ (let ((info (org-babel-get-src-block-info)))
+ (if (string= (nth 0 info) "processing")
+ (let* ((body (nth 1 info))
+ (params (org-babel-process-params (nth 2 info)))
+ (sketch-code
+ (org-babel-expand-body:generic
+ body
+ params
+ (org-babel-variable-assignments:processing params))))
+ ;; Note: sketch filename can not contain a hyphen, since it
+ ;; has to be a valid java class name; for this reason
+ ;; make-temp-file is repeated until no hyphen is in the
+ ;; name; also sketch dir name must be the same as the
+ ;; basename of the sketch file.
+ (let* ((temporary-file-directory org-babel-temporary-directory)
+ (sketch-dir
+ (let (sketch-dir-candidate)
+ (while
+ (progn
+ (setq sketch-dir-candidate
+ (make-temp-file "processing" t))
+ (when (org-string-match-p
+ "-"
+ (file-name-nondirectory sketch-dir-candidate))
+ (delete-directory sketch-dir-candidate)
+ t)))
+ sketch-dir-candidate))
+ (sketch-filename
+ (concat sketch-dir
+ "/"
+ (file-name-nondirectory sketch-dir)
+ ".pde")))
+ (with-temp-file sketch-filename (insert sketch-code))
+ (find-file sketch-filename)
+ (processing-sketch-run)
+ (kill-buffer)))
+ (message "Not inside a Processing source block."))))
+
+(defun org-babel-execute:processing (body params)
+ "Execute a block of Processing code.
+This function is called by `org-babel-execute-src-block'."
+ (let ((sketch-code
+ (org-babel-expand-body:generic
+ body
+ params
+ (org-babel-variable-assignments:processing params))))
+ ;; Results are HTML.
+ (let ((sketch-canvas-id (concat "ob-" (sha1 sketch-code))))
+ (concat "<script src=\""
+ org-babel-processing-processing-js-filename
+ "\"></script>\n <script type=\"text/processing\""
+ " data-processing-target=\""
+ sketch-canvas-id
+ "\">\n"
+ sketch-code
+ "\n</script> <canvas id=\""
+ sketch-canvas-id
+ "\"></canvas>"))))
+
+(defun org-babel-prep-session:processing (session params)
+ "Return an error if the :session header argument is set.
+Processing does not support sessions"
+ (error "Processing does not support sessions"))
+
+(defun org-babel-variable-assignments:processing (params)
+ "Return list of processing statements assigning the block's variables."
+ (mapcar #'org-babel-processing-var-to-processing
+ (mapcar #'cdr (org-babel-get-header params :var))))
+
+(defun org-babel-processing-var-to-processing (pair)
+ "Convert an elisp value into a Processing variable.
+The elisp value PAIR is converted into Processing code specifying
+a variable of the same value."
+ (let ((var (car pair))
+ (val (let ((v (cdr pair)))
+ (if (symbolp v) (symbol-name v) v))))
+ (cond
+ ((integerp val)
+ (format "int %S=%S;" var val))
+ ((floatp val)
+ (format "float %S=%S;" var val))
+ ((stringp val)
+ (format "String %S=\"%s\";" var val))
+ ((and (listp val) (not (listp (car val))))
+ (let* ((type (org-babel-processing-define-type val))
+ (fmt (if (eq 'String type) "\"%s\"" "%s"))
+ (vect (mapconcat (lambda (e) (format fmt e)) val ", ")))
+ (format "%s[] %S={%s};" type var vect)))
+ ((listp val)
+ (let* ((type (org-babel-processing-define-type val))
+ (fmt (if (eq 'String type) "\"%s\"" "%s"))
+ (array (mapconcat (lambda (row)
+ (concat "{"
+ (mapconcat (lambda (e) (format fmt e))
+ row ", ")
+ "}"))
+ val ",")))
+ (format "%S[][] %S={%s};" type var array))))))
+
+(defun org-babel-processing-define-type (data)
+ "Determine type of DATA.
+
+DATA is a list. Return type as a symbol.
+
+The type is `String' if any element in DATA is
+a string. Otherwise, it is either `float', if some elements are
+floats, or `int'."
+ (let* ((type 'int)
+ find-type ; For byte-compiler.
+ (find-type
+ (lambda (row)
+ (dolist (e row type)
+ (cond ((listp e) (setq type (funcall find-type e)))
+ ((stringp e) (throw 'exit 'String))
+ ((floatp e) (setq type 'float)))))))
+ (catch 'exit (funcall find-type data))))
+
+(provide 'ob-processing)
+
+;;; ob-processing.el ends here