ox-pluto/ox-pluto.el

202 lines
6.2 KiB
EmacsLisp

;;; ox-pluto.el --- Export to Pluto.jl notebooks -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2022 TEC
;;
;; Author: TEC <https://github.com/tecosaur>
;; Maintainer: TEC <tec@tecosaur.com>
;; Created: March 07, 2022
;; Modified: March 07, 2022
;; Version: 0.1.0
;; Keywords: tools
;; Homepage: https://github.com/tecosaur/ox-pluto
;; Package-Requires: ((emacs "26.3") (uuidgen "1.2"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; This library provides a Pluto.jl backend to
;; the Org exporter.
;;
;;; Code:
(require 'ox)
(require 'ox-org)
(defvar ox-pluto--preamble
"### A Pluto.jl notebook ###
# Generated by ox-pluto for Pluto v0.18+
# Created %s
using InteractiveUtils\n\n")
(defun ox-pluto--org-cell (content)
(cons (cons :folded (uuidgen-4))
(concat "org\"\"\"\n"
(replace-regexp-in-string
"\"\"\"" "\"\\quot\""
content)
"\"\"\"")))
(defun ox-pluto--julia-cell (content)
(cons (cons :unfolded (uuidgen-4))
content))
(defun ox-pluto--render-cell (cell)
(concat "# ╔═╡ " (cdar cell) "\n" (cdr cell) "\n"))
(defun ox-pluto--cell-list (cells)
(concat "# ╔═╡ Cell order:\n"
(mapconcat
(lambda (cell)
(concat
(if (eq :folded (caar cell))
"# ╟─" "# ╠═")
(cdar cell)))
cells
"\n")))
(defun ox-pluto--form-cells (content _backend _info)
(let (cells)
(push (cons (cons :folded (uuidgen-4)) "using Org")
cells)
(with-temp-buffer
(insert content)
(goto-char (point-min))
(re-search-forward "\\=# Created .*?\n" nil t)
(while (< (point) (point-max))
(if (and (looking-at-p "^[ \t]*#\\+begin_src julia")
(not (looking-at-p "^[^\n]*:eval no")))
(progn
(push
(ox-pluto--julia-cell
(buffer-substring-no-properties
(progn (forward-line 1) (point))
(progn
(re-search-forward "^[ \t]*#\\+end_src")
(forward-line -1)
(line-end-position))))
cells)
(forward-line 2))
(push
(ox-pluto--org-cell
(buffer-substring-no-properties
(point)
(goto-char
(or (and (re-search-forward
"^\\*+ \\|^[ \t]*#\\+begin_src julia" nil t)
(line-beginning-position))
(point-max)))))
cells))
(re-search-forward "\\=\\(?:[ \t\r]*\n\\)+" nil t)))
(when (file-exists-p "Project.toml")
(push
(cons (cons :unfolded "00000000-0000-0000-0000-000000000001")
(with-temp-buffer
(insert "PLUTO_PROJECT_TOML_CONTENTS = \"\"\"\n")
(insert-file-contents "Project.toml")
(insert "\n\"\"\"")
(buffer-string)))
cells))
(when (file-exists-p "Manifest.toml")
(push
(cons (cons :unfolded "00000000-0000-0000-0000-000000000002")
(with-temp-buffer
(insert "PLUTO_MANIFEST_TOML_CONTENTS = \"\"\"\n")
(insert-file-contents "Manifest.toml")
(insert "\n\"\"\"")
(buffer-string)))
cells))
(setq cells (nreverse cells))
(concat
(format ox-pluto--preamble (format-time-string "%Y-%m-%d %a %H:%M"))
(mapconcat #'ox-pluto--render-cell cells "\n")
"\n"
(ox-pluto--cell-list cells)
"\n")))
;; REVIEW it may be posslible to do this via AST manipulation,
;; which would be preferable.
(org-export-define-derived-backend 'pluto 'org
:filters-alist '((:filter-final-output . ox-pluto--form-cells)))
;;;###autoload
(defun ox-pluto-export-as-pluto
(&optional async subtreep visible-only body-only ext-plist)
"Export current buffer to an Pluto notebook buffer.
If narrowing is active in the current buffer, only export its
narrowed part.
If a region is active, export that region.
A non-nil optional argument ASYNC means the process should happen
asynchronously. The resulting buffer should be accessible
through the `org-export-stack' interface.
When optional argument SUBTREEP is non-nil, export the sub-tree
at point, extracting information from the headline properties
first.
When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.
When optional argument BODY-ONLY is non-nil, strip document
keywords from output.
EXT-PLIST, when provided, is a property list with external
parameters overriding Org default settings, but still inferior to
file-local settings.
Export is done in a buffer named \"*Org ORG Export*\", which will
be displayed when `org-export-show-temporary-export-buffer' is
non-nil."
(interactive)
;; TODO find a way to only disable Julia execution
(let (org-export-use-babel)
(org-export-to-buffer 'pluto "*Org Pluto Export*"
async subtreep visible-only body-only ext-plist
(lambda ()
(if (require 'julia-mode nil t)
(julia-mode)
(prog-mode))))))
;;;###autoload
(defun ox-pluto-export-to-pluto
(&optional async subtreep visible-only body-only ext-plist)
"Export current buffer to an Pluto notebook file.
If narrowing is active in the current buffer, only export its
narrowed part.
If a region is active, export that region.
A non-nil optional argument ASYNC means the process should happen
asynchronously. The resulting file should be accessible through
the `org-export-stack' interface.
When optional argument SUBTREEP is non-nil, export the sub-tree
at point, extracting information from the headline properties
first.
When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.
When optional argument BODY-ONLY is non-nil, strip document
keywords from output.
EXT-PLIST, when provided, is a property list with external
parameters overriding Org default settings, but still inferior to
file-local settings.
Return output file name."
(interactive)
(let ((outfile (org-export-output-file-name ".jl" subtreep))
org-export-use-babel)
(org-export-to-file 'pluto outfile
async subtreep visible-only body-only ext-plist)))
(provide 'ox-pluto)
;;; ox-pluto.el ends here