org-pandoc-import/org-pandoc-import-transient.el

160 lines
7.7 KiB
EmacsLisp

;;; org-pandoc-import-transient.el -*- lexical-binding: t; -*-
;; SPDX-License-Identifier: GPL-3.0-or-later
;; TODO add backups, better folder names, cleanup on emacs exit
(require 'org-pandoc-import)
(require 'org)
(require 'ox-pandoc nil t)
(defcustom org-import-transient-async-export nil
"Whether or not to use Org's async export mode.
This trades a short blocking period for a long non-blocking period."
:type 'list
:group 'org-pandoc-import)
(defcustom org-pandoc-import-transient-associations
(append
`(("md" . ,(if (featurep 'ox-gfm) "gfm" "md"))
("odt" . "odt")
("csv" . (lambda ()
(unless (org-at-table-p)
(user-error "Point is not inside a table."))
(org-table-export (concat (file-name-base (buffer-file-name)) ".csv")
"orgtbl-to-csv")))
("tsv" . (lambda ()
(unless (org-at-table-p)
(user-error "Point is not inside a table."))
(org-table-export (concat (file-name-base (buffer-file-name)) ".tsv")
"orgtbl-to-tsv"))))
(when (featurep 'ox-pandoc)
'(("rst" . org-pandoc-export-to-rst)
("docx" . org-pandoc-export-to-docx))))
"An alist of file extensions, and associated exporters.
Exporters can either be a string correspanding to a particular org exporter,
or some funcion.
An importer backend that recognises the given extension must exist."
:type 'list
:group 'org-pandoc-import)
;;;###autoload
(define-minor-mode org-pandoc-import-transient-mode
"On-the-fly convert other markups to org and back."
:global t
:init-value nil)
(defun org-pandoc-import-transient--register-file-handlers ()
"Modify `file-name-handler-alist' to have the transient handler associated with
active extensions."
(let* ((supported-extensions (mapcar #'car org-pandoc-import-transient-associations))
(extension-regex (concat "\\." (regexp-opt supported-extensions) "\\'"))
(entry (rassoc 'org-pandoc-import-transient--file-handler file-name-handler-alist))
(org-entry (rassoc 'org-pandoc-import-transient--maybe-converted-org-file-handler file-name-handler-alist))
(org-transient-file-regex "\\.org\\+.+\\'")
(org-alist (assoc org-transient-file-regex auto-mode-alist)))
(if entry
(setcar entry extension-regex)
(nconc file-name-handler-alist (list (cons extension-regex #'org-pandoc-import-transient--file-handler))))
(unless org-entry
(nconc file-name-handler-alist (list (cons org-transient-file-regex #'org-pandoc-import-transient--maybe-converted-org-file-handler))))
(unless org-alist
(nconc auto-mode-alist (list (cons org-transient-file-regex #'org-mode))))))
(defun org-pandoc-import-transient--file-handler (operation &rest args)
(let ((inhibit-file-name-handlers
(cons 'org-pandoc-import-transient--file-handler
(and (eq inhibit-file-name-operation operation)
inhibit-file-name-handlers)))
(inhibit-file-name-operation operation))
(if org-pandoc-import-transient-mode
(if (eq operation 'expand-file-name)
(org-pandoc-import-transient--convert-file-name
(apply operation args))
(apply operation args))
(apply operation args))))
(defvar org-pandoc-import-transient--files nil
"Global alist of special files and their properties.")
(defvar org-pandoc-import-transient--currently-processing nil
"Global state variable to indicate that an the this package is performing
actions that may undesirably trigger the file watcher.")
(defun org-pandoc-import-transient--convert-file-name (file)
(if org-pandoc-import-transient--currently-processing
file
(unless (assoc file org-pandoc-import-transient--files)
(let* ((temp-dir (make-temp-file "org-transients/" t)) ; org pandoc transient file(s)
(org-file (expand-file-name (concat (file-name-base file)
".org+"
(file-name-extension file))
temp-dir))
(symlink-file (expand-file-name (file-name-nondirectory file)
temp-dir))
(org-pandoc-import-transient--currently-processing t))
(push (cons file (list :target org-file)) org-pandoc-import-transient--files)
(push (cons symlink-file (list :symlink file)) org-pandoc-import-transient--files)
(push (cons org-file (list :initialised nil :source file :source-symlink symlink-file)) org-pandoc-import-transient--files)
(write-region "" nil org-file)
(make-symbolic-link file symlink-file)
(message "Created org-file stub for %s (%s)" file org-file)))
(if-let ((org-file
(plist-get (cdr (assoc file org-pandoc-import-transient--files)) :target)))
org-file file)))
(defun org-pandoc-import-transient--maybe-converted-org-file-handler (operation &rest args)
(let ((inhibit-file-name-handlers
(cons 'org-pandoc-import-transient--maybe-converted-org-file-handler
(and (eq inhibit-file-name-operation operation)
inhibit-file-name-handlers)))
(inhibit-file-name-operation operation))
(if (and org-pandoc-import-transient-mode (not org-pandoc-import-transient--currently-processing))
(cond
((eq operation 'insert-file-contents)
(when-let* ((org-file (car args))
(file-info (cdr (assoc org-file org-pandoc-import-transient--files)))
(source-file (plist-get file-info :source))
(start-time (time-to-seconds (current-time)))
(org-pandoc-import-transient--currently-processing t))
(unless (plist-get file-info :initialised)
(delete-file org-file) ; to avoid the overwrite prompt
(message "Initialising...")
(org-pandoc-import-to-org nil source-file org-file t)
(plist-put file-info :initialised t)
(setq-local kill-buffer-hook (append kill-buffer-hook '(org-pandoc-import-transient--killed)))
(message "Created org file from %s in %2fs." source-file (- (time-to-seconds (current-time)) start-time))))
(apply operation args))
((eq operation 'write-region)
(let ((org-pandoc-import-transient--currently-processing t)
(org-export-with-broken-links t)
(org-export-with-toc nil))
(apply operation args)
(when-let* ((file (nth 2 args))
(source-file (plist-get (cdr (assoc file org-pandoc-import-transient--files)) :source))
(exporter (cdr (assoc (file-name-extension source-file) org-pandoc-import-transient-associations)))
(start-time (time-to-seconds (current-time))))
(if (functionp exporter)
(funcall exporter)
(org-export-to-file (intern exporter) source-file org-import-transient-async-export))
(message "Updated %s in %2fs." source-file (- (time-to-seconds (current-time)) start-time)))))
(t (apply operation args)))
(apply operation args))))
(defun org-pandoc-import-transient--killed ()
"When this buffer is closed, we assume that the source file is liable to be modified.
Thus, if we re-open the file with `org-pandoc-import-transient-mode' enabled,
we want to re-create the associated org file."
(plist-put (cdr (assoc (buffer-file-name) org-pandoc-import-transient--files)) :initialised nil))
(org-pandoc-import-transient--register-file-handlers)
(make-directory (expand-file-name "org-transients" temporary-file-directory) t)
(provide 'org-pandoc-import-transient)