160 lines
7.7 KiB
EmacsLisp
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)
|