diff --git a/contrib/lisp/ob-clojure-literate.el b/contrib/lisp/ob-clojure-literate.el new file mode 100644 index 000000000..1a0ce31ff --- /dev/null +++ b/contrib/lisp/ob-clojure-literate.el @@ -0,0 +1,277 @@ +;;; ob-clojure-literate.el --- Clojure's Org-mode Literate Programming. + +;; Authors: stardiviner +;; Package-Requires: ((emacs "24.4") (org "9") (cider "0.16.0") (dash "2.12.0")) +;; Package-Version: 1.1 +;; Keywords: tools +;; homepage: https://github.com/stardiviner/ob-clojure-literate + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; Auto setup ob-clojure-literate scaffold and jack-in Clojure project. +;; +;; Usage: +;; +;; [M-x ob-clojure-literate-mode] to toggle this minor mode. + +;;; Code: + +(require 'ob-clojure) +(require 'cider) +(require 'dash) + +(defgroup ob-clojure-literate nil + "Clojure's Org-mode Literate Programming." + :prefix "ob-clojure-literate-" + :group 'ob-babel) + +;;;###autoload +(defcustom ob-clojure-literate-auto-jackin-p nil + "Auto jack in ob-clojure project. +Don't auto jack in by default for not rude." + :type 'boolean + :group 'ob-clojure-literate) + +(defcustom ob-clojure-literate-project-location (concat user-emacs-directory "Org-mode/") + "The location for `ob-clojure-literate' scaffold project." + :type 'string + :group 'ob-clojure-literate) + +(defvar ob-clojure-literate-session nil) +(defvar ob-clojure-literate-original-ns nil) +(defvar ob-clojure-literate-session-ns nil) +(defvar ob-clojure-literate-cider-connections nil) + +(defcustom ob-clojure-literate-default-session "*cider-repl ob-clojure*" + "The default session name for `ob-clojure-literate'." + :type 'string + :group 'ob-clojure-literate) + +(defun ob-clojure-literate-any-connection-p () + "Return t if have any CIDER connection." + (and + ;; handle the case `cider-jack-in' is not finished creating connection, but `ob-clojure-literate-mode' is enabled. + (not (null (cider-connections))) + (not (null ob-clojure-literate-session)) ; before mode enabled, it is nil. + (not (string-empty-p ob-clojure-literate-session)) ; after disable, it is "". + )) + +(defun ob-clojure-literate-get-session-list () + "Return a list of available started CIDER REPL sessions list." + (-map 'buffer-name cider-connections)) + +(defun ob-clojure-literate-set-session () + "Set session name for buffer local." + ;; if default session is the only one in connections list. + (if (and (= (length (ob-clojure-literate-get-session-list)) 1) + (-contains-p (ob-clojure-literate-get-session-list) ob-clojure-literate-default-session)) + (setq-local ob-clojure-literate-session ob-clojure-literate-default-session) + ;; if have any connections, choose one from them. + (if (ob-clojure-literate-any-connection-p) + (setq-local ob-clojure-literate-session + (completing-read "Choose ob-clojure-literate :session : " + (ob-clojure-literate-get-session-list))) + ;; if none, set to default session name to fix `ob-clojure-literate-mode' + ;; is enabled before `cider-jack-in' generated connections. + (setq-local ob-clojure-literate-session ob-clojure-literate-default-session)) + )) + +;;;###autoload +(defun ob-clojure-literate-specify-session-header-argument () + "Specify ob-clojure header argument :session with value selected from a list of available sessions." + (interactive) + (let ((lang (nth 0 (org-babel-get-src-block-info)))) + (if (and (string= lang "clojure") ; only in clojure src block. + (car (seq-filter ; only when :session is not specified yet. + (lambda (header-argument) + (if (eq (car header-argument) :session) + (not (null (cdr header-argument))))) + (nth 2 (org-babel-get-src-block-info))))) + (org-babel-insert-header-arg + "session" + (format "\"%s\"" + (completing-read + "Choose :session for ob-clojure-literate: " + (ob-clojure-literate-get-session-list)))) + (message "This function only used in `clojure' src block."))) + ) + +;;; Auto start CIDER REPL session in a complete Leiningen project environment for Org-mode Babel to jack-in. +;;;###autoload +(defun ob-clojure-literate-auto-jackin () + "Auto setup ob-clojure-literate scaffold and jack-in Clojure project." + (interactive) + (unless (file-directory-p (expand-file-name ob-clojure-literate-project-location)) + (make-directory ob-clojure-literate-project-location t) + (let ((default-directory ob-clojure-literate-project-location)) + (shell-command "lein new ob-clojure"))) + (unless (or + (and (cider-connected-p) + (if (not (null ob-clojure-literate-session)) + (seq-contains cider-connections (get-buffer ob-clojure-literate-session)))) + cider-connections + (not (null ob-clojure-literate-session))) + ;; return back to original file. + (if (not (and (= (length (ob-clojure-literate-get-session-list)) 1) + (-contains-p (ob-clojure-literate-get-session-list) ob-clojure-literate-default-session))) + (save-window-excursion + (find-file (expand-file-name (concat ob-clojure-literate-project-location "ob-clojure/src/ob_clojure/core.clj"))) + (with-current-buffer "core.clj" + (cider-jack-in)))))) + +(defun ob-clojure-literate-set-local-cider-connections (toggle?) + "Set buffer local `cider-connections' for `ob-clojure-literate-mode' `TOGGLE?'." + (if toggle? + (progn + (setq ob-clojure-literate-cider-connections cider-connections) + (unless (local-variable-if-set-p 'cider-connections) + (make-local-variable 'cider-connections)) + (setq-local cider-connections ob-clojure-literate-cider-connections)) + ;; store/restore emptied CIDER connections by `ob-clojure-literate-enable'. + (kill-local-variable 'cider-connections) ; kill local variable so that I can get the original global variable value. + ;; Empty all CIDER connections to avoid `cider-current-connection' return any connection. + ;; FIXME: when try to enable, `cider-connections' is local and nil. + ;; (if (and (= (length (ob-clojure-literate-get-session-list)) 1) + ;; (-contains-p (ob-clojure-literate-get-session-list) ob-clojure-literate-default-session))) + ;; (unless (local-variable-if-set-p 'cider-connections) + ;; (make-local-variable 'cider-connections)) + ;; (setq-local cider-connections '()) + )) + +(defun ob-clojure-literate-cider-do-not-find-ns (body params) + "Fix the issue that `cider-current-ns' try to invoke `clojure-find-ns' to extract ns from buffer." + ;; TODO: Is it possible to find ns in `body'? + (when (ob-clojure-literate-any-connection-p) + (setq ob-clojure-literate-original-ns (cider-current-ns)) + (with-current-buffer ob-clojure-literate-session + (setq ob-clojure-literate-session-ns cider-buffer-ns)) + (setq-local cider-buffer-ns ob-clojure-literate-session-ns)) + (message (format "ob-clojure-literate: current CIDER ns is [%s]." cider-buffer-ns))) + +(defun ob-clojure-literate-set-local-session (toggle?) + "Set buffer local `org-babel-default-header-args:clojure' for `ob-clojure-literate-mode' `TOGGLE?'." + (if toggle? + (progn + ;; set local default session for ob-clojure. + (setq ob-clojure-literate-session (ob-clojure-literate-set-session)) + (unless (local-variable-if-set-p 'org-babel-default-header-args:clojure) + (make-local-variable 'org-babel-default-header-args:clojure)) + (add-to-list 'org-babel-default-header-args:clojure + `(:session . ,ob-clojure-literate-session)) + ) + ;; remove :session from buffer local default header arguments list. + (unless (local-variable-if-set-p 'org-babel-default-header-args:clojure) + (make-local-variable 'org-babel-default-header-args:clojure)) + (setq org-babel-default-header-args:clojure + (delq t + (mapcar + (lambda (cons) (if (eq (car cons) :session) t cons)) + org-babel-default-header-args:clojure))) + )) + +;;; Support `org-babel-initiate-session' / [C-c C-v z] to initialize Clojure session. + +(defun org-babel-clojure-initiate-session (&optional session _params) + "Initiate a session named SESSION according to PARAMS." + (when (and session (not (string= session "none"))) + (save-window-excursion + (unless (org-babel-comint-buffer-livep session) + ;; CIDER jack-in to the Clojure project directory. + (cond + ((eq org-babel-clojure-backend 'cider) + (require 'cider) + (let ((session-buffer (save-window-excursion + (cider-jack-in t) + (current-buffer)))) + (if (org-babel-comint-buffer-livep session-buffer) + (progn (sit-for .25) session-buffer)))) + ((eq org-babel-clojure-backend 'slime) + (error "Session evaluation with SLIME is not supported")) + (t + (error "Session initiate failed"))) + ) + (get-buffer session) + ))) + +(defun org-babel-prep-session:clojure (session params) + "Prepare SESSION according to the header arguments specified in PARAMS." + (let* ((session (org-babel-clojure-initiate-session session)) + (var-lines (org-babel-variable-assignments:clojure params))) + (when session + (org-babel-comint-in-buffer session + (mapc (lambda (var) + (insert var) (comint-send-input nil t) + (org-babel-comint-wait-for-output session) + (sit-for .1) (goto-char (point-max))) var-lines))) + session)) + +(defun org-babel-clojure-var-to-clojure (var) + "Convert src block's `VAR' to Clojure variable." + ;; TODO: reference `org-babel-python-var-to-python' + ) + +(defun org-babel-variable-assignments:clojure (params) + "Return a list of Clojure statements assigning the block's variables in `PARAMS'." + (mapcar + (lambda (pair) + (format "(def %s %s)" + (car pair) + ;; (org-babel-clojure-var-to-clojure (cdr pair)) + (cdr pair))) + (org-babel--get-vars params))) + + +(defvar ob-clojure-literate-mode-map + (let ((map (make-sparse-keymap))) + map) + "Keymap for `ob-clojure-literate-mode'.") + +(define-key org-babel-map (kbd "M-s") 'ob-clojure-literate-specify-session-header-argument) +(define-key org-babel-map (kbd "M-j") 'ob-clojure-literate-auto-jackin) +;; (define-key org-babel-map (kbd "M-e") 'cider-eval-last-sexp) +;; (define-key org-babel-map (kbd "M-d") 'cider-doc) + +;;;###autoload +(defun ob-clojure-literate-enable () + "Enable Org-mode buffer locally for `ob-clojure-literate'." + (when (and (not (null cider-connections)) ; only enable `ob-clojure-literate-mode' when has CIDER connections. + (equal major-mode 'org-mode)) ; `ob-clojure-literate-mode' only works in `org-mode'. + (ob-clojure-literate-set-local-cider-connections ob-clojure-literate-mode) + (ob-clojure-literate-set-local-session ob-clojure-literate-mode) + (advice-add 'org-babel-execute:clojure :before #'ob-clojure-literate-cider-do-not-find-ns) + (message "ob-clojure-literate minor mode enabled."))) + +;;;###autoload +(defun ob-clojure-literate-disable () + "Disable Org-mode buffer locally for `ob-clojure-literate'." + (advice-remove 'org-babel-execute:clojure #'ob-clojure-literate-cider-do-not-find-ns) + (setq-local cider-buffer-ns ob-clojure-literate-original-ns) + (ob-clojure-literate-set-local-cider-connections ob-clojure-literate-mode) + (ob-clojure-literate-set-local-session ob-clojure-literate-mode) + (message "ob-clojure-literate minor mode disabled.")) + +;;;###autoload +(if ob-clojure-literate-auto-jackin-p (ob-clojure-literate-auto-jackin)) + +;;;###autoload +(define-minor-mode ob-clojure-literate-mode + "A minor mode to toggle `ob-clojure-literate'." + :require 'ob-clojure-literate + :init-value t + :lighter " clj-lp" + :group 'ob-clojure-literate + :keymap ob-clojure-literate-mode-map + :global nil + (if ob-clojure-literate-mode + (ob-clojure-literate-enable) + (ob-clojure-literate-disable)) + ) + + + +(provide 'ob-clojure-literate) + +;;; ob-clojure-literate.el ends here diff --git a/contrib/lisp/ob-lua.el b/contrib/lisp/ob-lua.el new file mode 100644 index 000000000..f33090ecc --- /dev/null +++ b/contrib/lisp/ob-lua.el @@ -0,0 +1,45 @@ +;;; ob-lua.el --- Execute Lua code within org-mode blocks. +;; Copyright 2016 stardiviner + +;; Author: stardiviner +;; Maintainer: stardiviner +;; Keywords: org babel lua +;; URL: https://github.com/stardiviner/ob-lua +;; Created: 12th April 2016 +;; Version: 0.0.1 +;; Package-Requires: ((org "8")) + +;;; Commentary: +;; +;; Execute Lua code within org-mode blocks. + +;;; Code: +(require 'org) +(require 'ob) + +(defgroup ob-lua nil + "org-mode blocks for Lua." + :group 'org) + +(defcustom ob-lua:default-session "*lua*" + "Default Lua session. + +It is lua inferior process from `run-lua'." + :group 'ob-lua + :type 'string) + +;;;###autoload +(defun org-babel-execute:lua (body params) + "org-babel lua hook." + (let* ((session (or (cdr (assoc :session params)) + ob-lua:default-session)) + (cmd (mapconcat 'identity (list "lua -") " "))) + (org-babel-eval cmd body))) + +;;;###autoload +(eval-after-load "org" + '(add-to-list 'org-src-lang-modes '("lua" . lua))) + +(provide 'ob-lua) + +;;; ob-lua.el ends here diff --git a/contrib/lisp/ob-php.el b/contrib/lisp/ob-php.el new file mode 100644 index 000000000..1befbd248 --- /dev/null +++ b/contrib/lisp/ob-php.el @@ -0,0 +1,48 @@ +;;; ob-php.el --- Execute PHP within org-mode blocks. +;; Copyright 2016 stardiviner + +;; Author: stardiviner +;; Maintainer: stardiviner +;; Keywords: org babel php +;; URL: https://github.com/stardiviner/ob-php +;; Created: 04th May 2016 +;; Version: 0.0.1 +;; Package-Requires: ((org "8")) + +;;; Commentary: +;; +;; Execute PHP within org-mode blocks. + +;;; Code: +(require 'org) +(require 'ob) + +(defgroup ob-php nil + "org-mode blocks for PHP." + :group 'org) + +(defcustom ob-php:inf-php-buffer "*php*" + "Default PHP inferior buffer." + :group 'ob-php + :type 'string) + +;;;###autoload +(defun org-babel-execute:php (body params) + "Orgmode Babel PHP evaluate function for `BODY' with `PARAMS'." + (let* ((cmd "php") + (body (concat ""))) + (org-babel-eval cmd body) + )) + +;;;###autoload +(eval-after-load "org" + '(add-to-list 'org-src-lang-modes '("php" . php))) + +(defvar org-babel-default-header-args:php '()) + +(add-to-list 'org-babel-default-header-args:php + '(:results . "output")) + +(provide 'ob-php) + +;;; ob-php.el ends here diff --git a/contrib/lisp/ob-redis.el b/contrib/lisp/ob-redis.el new file mode 100644 index 000000000..340b05029 --- /dev/null +++ b/contrib/lisp/ob-redis.el @@ -0,0 +1,44 @@ +;;; ob-redis.el --- Execute Redis queries within org-mode blocks. +;; Copyright 2016 stardiviner + +;; Author: stardiviner +;; Maintainer: stardiviner +;; Keywords: org babel redis +;; URL: https://github.com/stardiviner/ob-redis +;; Created: 28th Feb 2016 +;; Version: 0.0.1 +;; Package-Requires: ((org "8")) + +;;; Commentary: +;; +;; Execute Redis queries within org-mode blocks. + +;;; Code: +(require 'org) +(require 'ob) + +(defgroup ob-redis nil + "org-mode blocks for Redis." + :group 'org) + +(defcustom ob-redis:default-db "127.0.0.1:6379" + "Default Redis database." + :group 'ob-redis + :type 'string) + +;;;###autoload +(defun org-babel-execute:redis (body params) + "org-babel redis hook." + (let* ((db (or (cdr (assoc :db params)) + ob-redis:default-db)) + (cmd (mapconcat 'identity (list "redis-cli") " "))) + (org-babel-eval cmd body) + )) + +;;;###autoload +(eval-after-load "org" + '(add-to-list 'org-src-lang-modes '("redis" . redis))) + +(provide 'ob-redis) + +;;; ob-redis.el ends here diff --git a/contrib/lisp/ob-smiles.el b/contrib/lisp/ob-smiles.el new file mode 100644 index 000000000..ef2ab1529 --- /dev/null +++ b/contrib/lisp/ob-smiles.el @@ -0,0 +1,54 @@ +;;; ob-smiles.el --- Org-mode Babel support for SMILES. +;;; -*- coding: utf-8 -*- + +;; Keywords: org babel SMILES +;; Version: 0.0.1 +;; Package-Requires: ((smiles-mode "0.0.1") (org "8")) + +;;; Commentary: + +;;; I copy code from: +;;; http://kitchingroup.cheme.cmu.edu/blog/2016/03/26/A-molecule-link-for-org-mode + +;; Author: John Kitchin [jkitchin@andrew.cmu.edu] +;; Maintainer: stardiviner [numbchild@gmail.com] + +;;; Code: + +;; Org-mode Babel +(defun org-babel-execute:smiles (body params) + "Execute SMILES babel `BODY' with `PARAMS'." + (shell-command-to-string + (format "obabel -:\"%s\" -osvg 2> /dev/null" body))) + +;; Org-mode link +(defun molecule-jump (name) + "Jump to molecule `NAME' definition." + (org-mark-ring-push) + (org-open-link-from-string (format "[[%s]]" path))) + +(defun molecule-export (path desc backend) + "Export molecule to HTML format on `PATH' with `DESC' and `BACKEND'." + (let ((name (save-window-excursion + (molecule-jump path) + (org-element-property :name (org-element-context))))) + (cond + ((eq 'html backend) + (format "%s" name name))))) + +(org-add-link-type + "molecule" + 'molecule-jump + 'molecule-export) + +;; org-mode element +(org-element-map (org-element-parse-buffer) + 'src-block + (lambda (src) + (when (string= "smiles" (org-element-property :language src)) + (org-element-property :name src)))) + + +(provide 'ob-smiles) + +;;; ob-smiles.el ends here diff --git a/contrib/lisp/ob-spice.el b/contrib/lisp/ob-spice.el new file mode 100644 index 000000000..24ef3c874 --- /dev/null +++ b/contrib/lisp/ob-spice.el @@ -0,0 +1,182 @@ +;;; ob-spice.el --- org-babel functions for spice evaluation +;;; -*- coding: utf-8 -*- + +;; Author: Tiago Oliveira Weber +;; Maintainer: stardiviner (numbchild@gmail.com) +;; Version: 0.4 +;; Package-Requires: ((spice-mode "0.0.1") (org "8")) +;; Homepage: http://tiagoweber.github.io + +;; License: GPL v3, or any later version +;; +;; This file 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, or (at your option) +;; any later version. +;; +;; This file 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 this program. If not, see . + +;;; Commentary: + +;; Org-Babel support for evaluating spice script. +;; Inspired by Ian Yang's org-export-blocks-format-plantuml (http://www.emacswiki.org/emacs/org-export-blocks-format-plantuml.el) + +;;; Requirements: +;; +;; - ngspice + +;;; Code: +(require 'ob) + +(add-to-list 'org-babel-tangle-lang-exts '("spice" . "cir")) + +(defun ob-spice-concat (wordlist) + "Concatenate elements of a `WORDLIST' into a string separated by spaces." + ;; example of usage + ;; (ob-spice-concat '("This" "is" "a" "long" "journey")) + (setq newtext (car wordlist)) ; first word is without space before + (setq wordlist (rest wordlist)) ; exclude the first word from the list + (dolist (word wordlist newtext) ; loop through the list and concatenate the values + (setq newtext (concat newtext " " word)))) + +(defun org-babel-expand-body:spice (body params) + "Expand BODY according to PARAMS, return the expanded body." + (let* ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (setq newbody ""); + (setq bodylinelist (split-string body "\n")) + (dolist (line bodylinelist newbody) + (progn ;loop through list of lines + (setq wordlist (split-string line " ")) + (setq firstword 1) + (dolist (word wordlist) + (progn ;loop through the words + (if (string-match "\\$\\(.*\\)\\[\\(.*\\)\\]" word) + (progn + ;; if matchs a vector variable format + (setq varname (match-string 1 word)) + (setq varindex (match-string 2 word)) + ;; search varname in vars and use the value of varindex to word + (setq newword + (nth (string-to-number varindex) + (car (assoc-default varname vars + (lambda (key candidate) + (string= key candidate)))))) + (if (not (eq newword nil)) + (if (not (stringp newword)) + (setq word (number-to-string newword)) + (setq word newword))) + ) + ) ; end of (if (string-match "\\$\\(.*\\)\\[\\(.*\\)\\]" word)) + (if (string-match "\\$\\(.*\\)\\." word) ; if variable has a dot in the end + (progn + ;; if matchs a non-vector variable format + (setq varname (match-string 1 word)) + (setq newword + (assoc-default varname vars + (lambda (key candidate) + (string= key candidate)))) + (if (not (eq newword nil)) + (progn + (if (not (stringp newword)) + (setq newword (number-to-string newword))) + (setq word (replace-match (concat newword ".") nil nil word)) + ;(setq word word) + ) + )) + );; end of (if (string-match "\\$\\(.*\\)\\." word) + (if (string-match "\\$\\(.*\\)" word) + (progn + ;; if matchs a non-vector variable format + (setq varname (match-string 1 word)) + (setq newword + (assoc-default varname vars + (lambda (key candidate) + (string= key candidate)))) + (if (not (eq newword nil)) + (if (not (stringp newword)) + (setq word (number-to-string newword)) + (setq word newword) + )) + ) + ) ; end of (if (string-match "\\$\\(.*\\)" word) + + + (setq newbody (concat newbody + (if (not (eq firstword 1)) " ") + word)) + (setq firstword 0) + ) ; end of (progn + ) ; end of (dolist (word wordlist)) + + (setq newbody (concat newbody "\n")) + ) ; end of (progn ;; loop through list of lines ... ) + ) ; end of (dolist (line bodylinelist) ...function ...) + )) + +;;;###autoload +(defun org-babel-execute:spice (body params) + "Execute a block of Spice code `BODY' with org-babel and `PARAMS'." + (let ((body (org-babel-expand-body:spice body params)) + (vars (mapcar #'cdr (org-babel-get-header params :var)))) + + ;;****************************** + ;; clean temporary files + (mapc (lambda (pair) + (when (string= (car pair) "file") + (setq textfile (concat (cdr pair) ".txt")) + (setq imagefile (concat (cdr pair) ".png")) + ) + ) + vars) + ;; (if (file-readable-p textfile) (delete-file textfile)) + ;; (if (file-readable-p imagefile) (delete-file imagefile)) + ;;******************************* + + (org-babel-eval "ngspice -b " body) + + ;; loop through all pairs (elements) of the list vars and set text and image file if finds "file" var + (mapc (lambda (pair) + (when (string= (car pair) "file") + (setq textfile (concat (cdr pair) ".txt")) + (setq imagefile (concat (cdr pair) ".png")))) + vars) + ;; produce results + ;; THE FOLLOWING WAS COMMENTED TEMPORARILY + ;; (concat + ;; (if (file-readable-p textfile) + ;; (get-string-from-file textfile)) + ;; (if (file-readable-p imagefile) + ;; (concat '"#+ATTR_HTML: :width 600px \n [[file:./" imagefile "]]") + ;; ) + ;; ) + + ;; ;; Get measurement values from text-file by splitting comma separated values + (if (file-readable-p textfile) + (progn + (setq rawtext (get-string-from-file textfile)) + ;;(setq rawtext (replace-regexp-in-string "\n" "" rawtext)) + (setq rawtext (replace-regexp-in-string "\n" "" rawtext)) + (setq result (split-string rawtext ",")))) + (if (file-readable-p imagefile) + (progn + ;; test if result exist already + ;;(if (boundp 'result) + (add-to-list 'result (concat '"[[file:./" imagefile "]]") t) ;; add imagefile to last entry + ;;(concat '"[[file:./" imagefile "]]") + ;;) + )) + result + ;; Produce output like '(test test2) + ;;'(test test2) + + ) + ) + +(provide 'ob-spice) +;;; ob-spice.el ends here