;;; ox-pluto.el --- Export to Pluto.jl notebooks -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2022 TEC ;; ;; Author: TEC ;; Maintainer: TEC ;; 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