From 748598b7beadd5d43cf82ca2e96f8f104d25323f Mon Sep 17 00:00:00 2001 From: jackkamm Date: Sun, 28 Apr 2019 13:17:19 -0700 Subject: [PATCH] Add pandoc conversion of rich outputs (#97) --- README.org | 7 +++++++ jupyter-mime.el | 32 ++++++++++++++++++++++++++++++++ jupyter-org-client.el | 26 +++++++++++++++++++++----- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/README.org b/README.org index 010a7b3..404fd25 100644 --- a/README.org +++ b/README.org @@ -490,6 +490,13 @@ generated image file names. Whenever you run a code block multiple times and replace its results, before the results are replaced, any generated files will be deleted to reduce the clutter in =org-babel-jupyter-resource-directory=. +**** Convert rich kernel output with the =:pandoc= header argument + +By default html, markdown, and latex results are wrapped in a =BEGIN_EXPORT= +block. If the header argument =:pandoc t= is set, they are instead +converted to org-mode format with [[https://pandoc.org/][pandoc]]. You can control which outputs get +converted with the custom variable =jupyter-org-pandoc-convertable=. + *** Editing the contents of a code block When editing a Jupyter code block's contents, i.e. by pressing =C-c '= when at diff --git a/jupyter-mime.el b/jupyter-mime.el index 4356a87..cdccb46 100644 --- a/jupyter-mime.el +++ b/jupyter-mime.el @@ -625,6 +625,38 @@ message." (when (= (point) (point-min)) (error "No display matching id (%s)" id))))) +;;; Pandoc + +(defun jupyter-pandoc-convert (from to from-string &optional callback) + "Use pandoc to convert a string in FROM format to TO format. +Starts a process and converts FROM-STRING, assumed to be in FROM +format, to a string in TO format and returns the converted +string. + +If CALLBACK is specified, return the process object. When the +process exits, call CALLBACK with zero arguments and with the +buffer containing the converted string current." + (cl-assert (executable-find "pandoc")) + (let* ((process-connection-type nil) + (proc (start-process + "jupyter-pandoc" + (generate-new-buffer " *jupyter-pandoc*") + "pandoc" "-f" from "-t" to "--"))) + (set-process-sentinel + proc (lambda (proc _) + (when (memq (process-status proc) '(exit signal)) + (with-current-buffer (process-buffer proc) + (funcall callback) + (kill-buffer (process-buffer proc)))))) + (process-send-string proc from-string) + (process-send-eof proc) + (if callback proc + (let ((to-string "")) + (setq callback (lambda () (setq to-string (buffer-string)))) + (while (zerop (length to-string)) + (accept-process-output nil 1)) + to-string)))) + (provide 'jupyter-mime) ;;; jupyter-mime.el ends here diff --git a/jupyter-org-client.el b/jupyter-org-client.el index 238faef..f5c1a58 100644 --- a/jupyter-org-client.el +++ b/jupyter-org-client.el @@ -66,6 +66,12 @@ automatically be shown if this is non-nil." :group 'ob-jupyter :type 'boolean) +(defcustom jupyter-org-pandoc-convertable + '("html" "markdown" "latex") + "Export blocks to convert to org-mode when ':pandoc t' header is set." + :group 'ob-jupyter + :type 'string) + (defconst jupyter-org-mime-types '(:text/org ;; Prioritize images over html :image/svg+xml :image/jpeg :image/png @@ -648,6 +654,16 @@ inserted without modification as the result of a code block." "Return a comment `org-element' with VALUE." (list 'comment (list :value value))) +(defun jupyter-org-export-block-or-pandoc (type value params) + "Returns VALUE, either converted with pandoc or in an export block. +If PARAMS has non-nil value for key ':pandoc' and TYPE is in +`jupyter-org-pandoc-convertable', convert the result with pandoc. +Otherwise, wrap it in an export block." + (if (and (alist-get :pandoc params) + (member type jupyter-org-pandoc-convertable)) + (jupyter-org-raw-string (jupyter-pandoc-convert type "org" value)) + (jupyter-org-export-block type value))) + (defun jupyter-org-export-block (type value) "Return an export-block `org-element'. The block will export TYPE and the contents of the block will be @@ -955,9 +971,9 @@ passed to Jupyter org-mode source blocks." &optional metadata) (jupyter-org--image-result mime params nil data metadata)) -(cl-defmethod jupyter-org-result ((_mime (eql :text/markdown)) _params data +(cl-defmethod jupyter-org-result ((_mime (eql :text/markdown)) params data &optional _metadata) - (jupyter-org-export-block "markdown" data)) + (jupyter-org-export-block-or-pandoc "markdown" data params)) (defun jupyter-org--parse-latex-element (data) "Return a latex-fragment or latex-environment org-element obtained from DATA. @@ -989,11 +1005,11 @@ parsed, wrap DATA in a minipage environment and return it." &optional _metadata) (if (member "raw" (alist-get :result-params params)) (jupyter-org--parse-latex-element data) - (jupyter-org-export-block "latex" data))) + (jupyter-org-export-block-or-pandoc "latex" data params))) -(cl-defmethod jupyter-org-result ((_mime (eql :text/html)) _params data +(cl-defmethod jupyter-org-result ((_mime (eql :text/html)) params data &optional _metadata) - (jupyter-org-export-block "html" data)) + (jupyter-org-export-block-or-pandoc "html" data params)) ;; NOTE: The order of :around methods is that the more specialized wraps the ;; more general, this makes sense since it is how the primary methods work as