-- UPDATE -- Declare what my code is trying to do:
I want to let org-babel insert an inline image link which is generated by
Clojure code. Here the image is generated by Incanter's function save
. Here is
the problem, somehow in my following elisp advice code, the image file is not
saved, it is empty (The file is generated).
The following org-mode babel block is what I wish to implement:
#+begin_src clojure :dir "data/images" :results file :file "clojure-babel-figure-result.png"
(use '(incanter core stats datasets charts io pdf))
(def my-plot (histogram (sample-normal 1000)))
(save my-plot output-file)
(.close output-file)
#+end_src
#+RESULTS:
[[file:data/images/ob-clojure-literate.png]]
Here is my ob-clojure-literate code: https://code.orgmode.org/bzg/org-mode/src/master/contrib/lisp/ob-clojure-literate.el (without the following advice code).
I try add advice as I posted at here How to write this advice for inject code into let-binding?
And here is my final code (some lines commented out for easy debug):
;;; Support header arguments :results file :file "image.png"
(defun ob-clojure-literate-inject-code (args)
"Inject Clojure code into `BODY' in `ARGS'.
This can be used as :filter-args advice of `org-babel-expand-body:clojure'.
It is used to change Clojure currently working directory and generate graphics image file result."
(let* ((body (nth 0 args))
(params (nth 1 args))
(dir (cdr (assq :dir params)))
(default-directory (and (buffer-file-name) (file-name-directory (buffer-file-name))))
(directory (and dir (file-name-as-directory (expand-file-name dir))))
(result-type (cdr (assq :results params)))
(out-file (cdr (assq :file params)))
(add-to-body (lambda (code)
(setq body (concat code "\n" body))))
(append-to-body (lambda (code)
(setq body (concat body "\n" code))))
)
(unless (file-directory-p (expand-file-name directory))
(warn (format "ob-clojure-literate directory %s does not exist, please create it." dir)))
;; (when directory
;; (funcall add-to-body (format "(System/setProperty \"user.dir\" \"%s\")" directory)))
(when (string-match-p (regexp-opt '("graphics" "file")) result-type)
;; (member "graphics" (cdr (assq :result-params params)))
;; (funcall add-to-body "(import 'java.io.FileOutputStream)")
;; use static global variable `ob-clojure-literate-incanter-plot' as convention.
;; (funcall append-to-body
;; (format "(def ob-clojure-literate-incanter-plot-file (FileOutputStream. \"%s\"))"
;; (concat directory out-file)))
;; (funcall append-to-body
;; "(save ob-clojure-literate-incanter-plot ob-clojure-literate-incanter-plot-file)")
)
(when out-file
;; (funcall append-to-body
;; (format "(view ob-clojure-literate-incanter-plot)"))
(funcall append-to-body
(format "(save ob-clojure-literate-incanter-plot \"%s\")"
(concat directory out-file)))
)
(list body params) ; return modified argument list
))
(advice-add 'org-babel-expand-body:clojure :filter-args #'ob-clojure-literate-inject-code)
When I add upper advice code. And execute the following org-babel src block:
#+begin_src clojure :cache no :dir "data/images" :results graphics :file "ob-clojure-literate.png"
(use '(incanter core stats datasets charts io pdf))
(def ob-clojure-literate-incanter-plot (histogram (sample-normal 1000)))
;; (save ob-clojure-literate-incanter-plot "/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png")
#+end_src
Emacs reports error:
Not a PNG file: ‘/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png’ [4 times]
But I execute the literate Clojure code:
(save ob-clojure-literate-incanter-plot "/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png")
in CIDER REPL buffer is fine. The plot image is generated correctly.
I tried to Edebug the advice function. But can't find out where is wrong. Please someone can help me out. Thanks in advance.
-- UPDATE --
Here is the debug workflow steps:
ob-clojure-literate create a project if not exist yet, then CIDER jack-in.
[M-x ob-clojure-literate-auto-jackin]
Now you have a session, then try the following org block:
#+begin_src clojure :cache no :dir "data/images" :results graphics :file "ob-clojure-literate.png" (use '(incanter core stats datasets charts io pdf)) (def ob-clojure-literate-incanter-plot (histogram (sample-normal 1000))) (save ob-clojure-literate-incanter-plot "<path-to>/data/images/ob-clojure-literate.png") #+end_src
The upper path is relative to your test path.
Because you already have a CIDER session, you can make debug simpler with just:
#+begin_src clojure :cache no :dir "data/images" :results file :file "ob-clojure-literate.png" (save ob-clojure-literate-incanter-plot "<path-to>/data/images/ob-clojure-literate.png") #+end_src
The upper path is relative to your test path.
I tried toggle Edebug on following functions:
- org-babel-expand-body:clojure (expanding seems correct)
- org-babel-execute:clojure (this seems correct too)
- nrepl-sync-request:eval (correct)
- nrepl-send-sync-request (correct)
- nrepl-send-request (correct)
- after
(process-send-string nil message)
, the image is save (seems Incanter save works correct as @xuchunyang said.) I have not found this step works.
After @xuchunyang's answer hint, I try to edebug deeper, found indeed with :file
header argument
will cause image be empty. Then I found he is right. This part of code in
org-babel-execute:clojure
caused image file to be empty.
And my environment info:
- Arch Linux
- Java version: java-9-jdk
- CIDER version: CIDER 0.17.0snapshot (package: 20180219.1846)
- Emacs version: GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.26) of 2018-02-10
- Org-mode version: Org mode version 9.1.6 (9.1.6-48-gfe7619-elpaplus @ mixed installation! /home/stardiviner/.emacs.d/elpa/org-plus-contrib-20180212/ and /home/stardiviner/Code/Emacs/org-mode/lisp/)
-- UPDATE --
found why the image file is empty by org-babel now.
The code is in org-babel-execute-src-block
:
(let ((file (cdr (assq :file params))))
;; If non-empty result and :file then write to :file.
(when file
(when result
(with-temp-file file
(insert (org-babel-format-result
result (cdr (assq :sep params))))))
(setq result file))
In my case the result
is empty string ""
, it try to write this empty string
result
to file
. So this is the problem.
org-babel-exec:clojure
I think your png file is overwritten with the return value of thesave
command. That would explain the error message. Could you open the png file withM-x
find-file-literally
and check? Thanks.:file
header argument with something other like:graphics-file
and process that in the advice. Please avoid the:file
header argument for now. Just visit the png file withC-x C-f
for a test. We can later see whether we can adapt the:file
header argument for our purposes when the result typegraphics
is given.:file
header.:graphics-file
in org-mode Info manual. And I tried some groups:results none :file
(works, fixed upper declareresult
issue). And:results file :graphics-file
and:results graphics :graphics-file
all avoid org-babel to override the image file. But seems will not insert a full inline image link. It looks like this:[[file:/home/stardiviner/Org/Projects/Programming Projects/data/images/]]
:graphics-file
. Have to say, FINALLY. THANKS. Problem solved. You need to post answer. I will accept your answer. and clear monster question and some comments.