;;; org-ref-export.el --- org-ref-export library -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2021  John Kitchin

;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
;; Keywords: convenience

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
;;
;;; Commentary:
;; This is an export library that uses CSL to process the citations and bibliography.
;;
;; It is intended for non-LaTeX exports, but you can also use it with LaTeX if
;; you want to avoid using bibtex/biblatex for some reason.
;;
;; The default style is set by a CSL-STYLE keyword or
;; `org-ref-csl-default-style'. If this is an absolute or relative path that
;; exists, it is used. Otherwise, it looks in `org-cite-csl-styles-dir' from
;; `org-cite' if it is defined, and in the citeproc/csl-styles directory in
;; `org-ref' otherwise.
;;
;; The default locale is set by a CSL-LOCALE keyword or
;; `org-ref-csl-default-locale'. This is looked for in
;; `org-cite-csl-locales-dir' if it is defined, and otherwise in the csl-locales
;; directory of `org-ref'.
;;
;; Note that citeproc does not do anything for cross-references, so if non-LaTeX
;; export is your goal, you may want to use org-ref-refproc.el to handle
;; cross-references.
;;

;;; Code:
(eval-when-compile
  (require 'hydra))

(defvar hfy-user-sheet-assoc)  		; to quiet compiler

(require 'ox-org)
(if (executable-find "pandoc")
    (require 'ox-pandoc))

(require 'citeproc)

(defcustom org-ref-backend-csl-formats
  '((html . html)
    (latex . latex)
    (md . plain)
    (org . org)
    (ascii . plain)
    (odt . org-odt)
    (docx . org))
  "Mapping of export backend to csl-backends."
  :type '(alist :key-type (symbol) :value-type (symbol))
  :group 'org-ref)


(defcustom org-ref-cite-internal-links 'auto
  "Should be one of
- 'bib-links :: link cites to bibliography entries
- 'no-links :: do not link cites to bibliography entries
- nil or 'auto :: add links based on the style."
  :type '(choice bib-links no-links auto nil)
  :group 'org-ref)


(defcustom org-ref-csl-default-style "chicago-author-date-16th-edition.csl"
  "Default csl style to use.
Should be a csl filename, or an absolute path to a csl filename."
  :type 'string
  :group 'org-ref)


(defcustom org-ref-csl-default-locale "en-US"
  "Default csl locale to use."
  :type 'string
  :group 'org-ref)


(defcustom org-ref-csl-label-aliases
  '((("app" "apps") . "appendix")
    (("art" "arts") . "article-locator")
    (("bk" "bks") . "book")
    (("can") . "canon")
    (("ch" "chap" "chaps" "chapt") . "chapter")
    (("col" "cols") . "column")
    (("el") . "elocation")
    (("eq" "eqs") . "equation")
    (("fig" "figs") . "figure")
    (("fol" "fols") . "folio")
    (("iss") . "issue")
    (("l" "ll") . "line")
    (("n" "nn") . "note")
    ;; number is not listed in the url in the docstring
    (("no" "nos" "#") . "number")
    (("op" "opp") . "opus")
    (("p" "pp" "pg" "pgs") . "page")
    (("para" "paras" "¶" "¶¶" "§" "§§") . "paragraph")
    (("pt" "pts") . "part")
    (("sec" "secs") . "section")
    (("s.v" "s.vv") . "sub verbo")
    (("sup" "supp") . "supplement")
    (("tab" "tabs") . "table")
    (("ts") . "timestamp")
    (("ti" "tit") . "title")
    (("v" "vv") . "verse")
    (("vol" "vols") . "volume"))
  "A-list of aliases for a csl label.
The car is a list of possible aliases (including if they end in a .
This list was adapted from `org-cite-csl--label-alist'.
See https://github.com/citation-style-language/documentation/blob/master/specification.rst#locators"
  :type '(alist :key-type (list (repeat string)) :value-type string)
  :group 'org-ref)


(defcustom org-ref-export-suppress-affix-types 
  '("citet"
    "citet*"
    "citetitle"
    "citeyear"
    "citeauthor"
    "citenum"
    "textcite")
  "List of cite types to suppress affixes (usually parentheses) on."
  :type '(list (repeat string))
  :group 'org-ref)


(defun org-ref-dealias-label (alias)
  "Return the full, de-aliased label for ALIAS.
Looked up from `org-ref-csl-label-aliases'.

I added this because I think it is reasonable to expect if you
write pg. 2 that it will show that way when rendered. At the
moment that is not the case, and only page is accepted. This is
actually done in oc-csl too, although it uses a flat a-list."
  (or (cdr (assoc "page" org-ref-csl-label-aliases
		  (lambda (x1 _x2)
		    (or (member alias x1)
			(member (concat (downcase alias) ".") x1)))))
      alias))


(defun org-ref-get-cite-links ()
  "Return list of cite links in the order they appear in the buffer."
  (org-element-map (org-element-parse-buffer) 'link
    (lambda (lnk)
      (when (assoc (org-element-property :type lnk) org-ref-cite-types)
	lnk))))


(defun org-ref-ref-csl-data (ref type)
  "Return the CSL alist for a REF of TYPE.
REF is a plist data structure returned from `org-ref-parse-cite-path'."
  ;; I believe the suffix contains "label locator suffix"
  ;; where locator is a number, or maybe a range of numbers like 5-6.
  ;; label is something like page or chapter
  ;; and the rest is the suffix text.
  ;; For example: ch. 5, for example
  ;; would be label = ch., locator=5, ",for example" as suffix.
  (let* ((full-suffix (string-trim (or (plist-get ref :suffix) ""))) 
	 locator
	 label locator suffix)

    ;; org-cite is more sophisticated than this and would allow things like 5, 6
    ;; and 12. I am not sure about what all should be supported yet. I guess the
    ;; idea there is you use everything from the first to last number as the
    ;; locator, but that seems tricky, what about something like: 5, 6 and 12,
    ;; because he had 3 books. One solution might be some kind of delimiter,
    ;; e.g. {5, 6 and 12}, because he had 3 books.

    (if (and (string-match
	      (rx-to-string
	       `(seq
		 ;; optional label
		 (group-n 1 (optional
			     (regexp ,(regexp-opt (cl-loop for (abbrvs . full)
							   in org-ref-csl-label-aliases
							   append (append abbrvs (list full)))))))
		 (optional (one-or-more space))
		 ;; number or numeric ranges
		 (group-n 2 (one-or-more digit) (optional "-" (one-or-more digit)))
		 ;; everything else
		 (group-n 3 (* "."))))	      
	      full-suffix)
	     (match-string 2 full-suffix)
	     (not (string= "" (match-string 2 full-suffix)))) 
	;; We found a locator
	(setq label (match-string 1 full-suffix)
	      locator (match-string 2 full-suffix)
	      suffix (match-string 3 full-suffix))
      (setq label nil
	    locator nil
	    suffix full-suffix))
    
    ;; Let's assume if you have a locator but not a label that you mean page.
    (when (and locator (string= "" (string-trim label)))
      (setq label "page"))
    
    `((id . ,(plist-get ref :key))
      (prefix . ,(plist-get ref :prefix))
      (suffix . ,suffix)
      (locator . ,locator)
      (label . ,(when label (org-ref-dealias-label (string-trim label))))
      ;; TODO: proof of concept and not complete. I did not go through all the
      ;; types to see what else should be in here.
      (suppress-author . ,(not (null (member type
					     '("citenum"
					       "citeyear"
					       "citeyear*"
					       "citedate"
					       "citedate*"
					       "citetitle"
					       "citetitle*"
					       "citeurl"))))))))

(declare-function org-ref-find-bibliography "org-ref-core")

(defun org-ref-process-buffer (backend &optional subtreep)
  "Process the citations and bibliography in the org-buffer.
Usually run on a copy of the buffer during export.
BACKEND is the org export backend."
  (save-restriction
    (when subtreep
      (org-narrow-to-subtree))
    (let* ((csl-backend (or (cdr (assoc backend org-ref-backend-csl-formats)) 'plain))

	   (style (or (cadr (assoc "CSL-STYLE"
				   (org-collect-keywords
				    '("CSL-STYLE"))))
		      org-ref-csl-default-style))
	   (locale (or (cadr (assoc "CSL-LOCALE"
				    (org-collect-keywords
				     '("CSL-LOCALE"))))
		       org-ref-csl-default-locale))

	   (proc (citeproc-create
		  ;; The style
		  (cond
		   ((file-exists-p style)
		    style)
		   ;; In a user-dir
		   ((and (bound-and-true-p org-cite-csl-styles-dir)
			 (file-exists-p (f-join org-cite-csl-styles-dir style)))
		    (f-join org-cite-csl-styles-dir style))
		   ;; provided by org-ref
		   ((file-exists-p (expand-file-name style
						     (f-join (file-name-directory
							      (locate-library "org-ref"))
							     "citeproc/csl-styles")))
		    (expand-file-name style (f-join
					     (file-name-directory
					      (locate-library "org-ref"))
					     "citeproc/csl-styles")))
		   (t
		    (error "%s not found" style)))
		  ;; item-getter
		  ;; (citeproc-itemgetter-from-bibtex (org-ref-find-bibliography))
		  (citeproc-hash-itemgetter-from-any (org-ref-find-bibliography) t)
		  ;; locale getter
		  (citeproc-locale-getter-from-dir (if (bound-and-true-p org-cite-csl-locales-dir)
						       org-cite-csl-locales-dir
						     (f-join (file-name-directory
							      (locate-library "org-ref"))
							     "citeproc/csl-locales")))
		  ;; the actual locale
		  locale))

	   ;; list of links in the buffer
	   (cite-links (org-element-map (org-element-parse-buffer) 'link
			 (lambda (lnk)
			   (when (assoc (org-element-property :type lnk) org-ref-cite-types)
			     lnk))))

	   (cites (cl-loop for cl in cite-links collect
			   (let* ((cite-data (org-ref-parse-cite-path (org-element-property :path cl)))
				  (common-prefix (or (plist-get cite-data :prefix) ""))
				  (common-suffix (or (plist-get cite-data :suffix) ""))
				  (refs (plist-get cite-data :references))
				  (type (org-element-property :type cl))

				  (cites (cl-loop for ref in refs collect
						  (org-ref-ref-csl-data ref type))))
			     ;; TODO: update eventually
			     ;; https://github.com/andras-simonyi/citeproc-el/issues/46
			     ;; To handle common prefixes, suffixes, I just concat
			     ;; them with the first/last entries. That is all that
			     ;; is supported for now. Combine common/local prefix
			     (setf (cdr (assoc 'prefix (cl-first cites)))
				   (concat common-prefix
					   (cdr (assoc 'prefix (cl-first cites)))))
			     ;; Combine local/common suffix
			     (setf (cdr (assoc 'suffix (car (last cites))))
				   (concat (cdr (assoc 'suffix (car (last cites))))
					   common-suffix))

			     ;; https://github.com/andras-simonyi/citeproc-el#creating-citation-structures
			     (citeproc-citation-create
			      :cites cites
			      :suppress-affixes (let ((type (org-element-property :type cl)))
						  (when (member type
								org-ref-export-suppress-affix-types)
						    t))

			      ;; TODO: this is proof of concept, and not complete.
			      ;; mode is one of suppress-author, textual,
			      ;; author-only, year-only, or nil for default. These
			      ;; are not all clear to me.
			      :mode (let ((type (org-element-property :type cl)))
				      (cond
				       ((member type '("citet" "citet*"))
					'textual)
				       ((member type '("citeauthor" "citeauthor*"))
					'author-only)
				       ((member type '("citeyear" "citeyear*"))
					'year-only)
				       ((member type '("citedate" "citedate*"))
					'suppress-author)
				       (t
					nil)))

			      ;; I think the capitalized styles are what this is for
			      :capitalize-first (string-match
						 "[A-Z]"
						 (substring
						  (org-element-property :type cl) 0 1))
			      ;; I don't know where this information would come from.
			      :note-index nil
			      :ignore-et-al nil
			      :grouped nil))))

	   (rendered-citations (progn (citeproc-append-citations cites proc)
				      (citeproc-render-citations proc csl-backend org-ref-cite-internal-links)))
	   ;; I only use the returned bibliography string. citeproc returns a
	   ;; bunch of other things related to offsets and linespacing, but I
	   ;; don't know what you do with these, so just ignore them here.
	   (bibdata (citeproc-render-bib proc csl-backend))
	   (rendered-bib (car bibdata))
	   (bib-parameters (cdr bibdata))
	   ;; The idea is we will wrap each citation and the bibliography in
	   ;; org-code so it exports appropriately.
	   (cite-formatters '((html . "@@html:%s@@")
			      (latex . "@@latex:%s@@")
			      (odt . "@@odt:%s@@")))
	   (bib-formatters '((html .  "\n#+BEGIN_EXPORT html\n%s\n#+END_EXPORT\n")
			     (latex .  "\n#+BEGIN_EXPORT latex\n%s\n#+END_EXPORT\n")
			     (odt . "\n#+BEGIN_EXPORT ODT\n%s\n#+END_EXPORT\n"))))

      ;; replace the cite links
      (cl-loop for cl in (reverse cite-links) for rc in (reverse rendered-citations) do
	       (cl--set-buffer-substring (org-element-property :begin cl)
					 (org-element-property :end cl)
					 (format (or
						  (cdr (assoc backend cite-formatters))
						  "%s")
						 (concat
						  rc
						  ;; Add on extra spaces that were
						  ;; following it.
						  (make-string
						   (or
						    (org-element-property :post-blank cl) 0)
						   ? )))))

      ;; Decorate the bibliography for different outputs adapted from
      ;;  `org-cite-csl-render-bibliography' in oc-csl I don't know what all
      ;;  these do or how to use them in the CSS for html, or LaTeX. This is to
      ;;  fix an annoying HTML feature that has an extra line in the numbering.
      ;;  This seems to do the right thing. I don't support hanging-indent like
      ;;  oc-csl does, and I don't currently support LaTeX here. It already does
      ;;  a good job there.
      ;;
      ;; (find-library "oc-csl")
      ;;
      ;; Here are some examples of what is in the bib-parameters.
      ;;  (max-offset . 2) (hanging-indent) (second-field-align . flush)
      ;; (entry-spacing . 0)
      ;; (line-spacing . 2)
      ;;
      ;; Here we add style css for html output.
      (cond
       ((eq 'html backend)
	(let ((s1 "")
	      (s2 ""))
	  (when (cdr (assq 'second-field-align bib-parameters))
	    (setq s1 (format
		      "<style>.csl-left-margin{float: left; padding-right: 0em;}
 .csl-right-inline{margin: 0 0 0 %dem;}</style>"
		      ;; I hard coded this factor of 0.6 from the oc-csl code.  
		      (* 0.6  (or (cdr (assq 'max-offset bib-parameters)) 0)))))

	  ;; hard-coded the hanging indent. oc-csl uses a variable for this. I
	  ;; guess we could too, but this seems simpler.
	  (when (cdr (assq 'hanging-indent bib-parameters))
	    (setq s2 "<style>.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}</style>"))
	  
	  (setq rendered-bib (concat
			      s1 s2
			      rendered-bib)))))

      ;; replace the bibliography
      (org-element-map (org-element-parse-buffer) 'link
	(lambda (lnk)
	  (cond
	   ((string= (org-element-property :type lnk) "bibliography")
	    (cl--set-buffer-substring (org-element-property :begin lnk)
				      (org-element-property :end lnk)
				      (format (or
					       (cdr (assoc backend bib-formatters))
					       "%s")
					      rendered-bib)))
	   ;; We just get rid of nobibliography links.
	   ((string= (org-element-property :type lnk) "nobibliography")
	    (cl--set-buffer-substring (org-element-property :begin lnk)
				      (org-element-property :end lnk)
				      "")))))
      ;; For LaTeX we need to define the citeprocitem commands
      ;; see https://www.mail-archive.com/emacs-orgmode@gnu.org/msg138546.html
      (when (eq backend 'latex)
	(goto-char (point-min))
	(insert "#+latex_header: \\makeatletter
#+latex_header: \\newcommand{\\citeprocitem}[2]{\\hyper@linkstart{cite}{citeproc_bib_item_#1}#2\\hyper@linkend}
#+latex_header: \\makeatother\n")))))


(defun org-ref-export-to (backend &optional async subtreep visible-only
				  body-only info)
  "Export buffer to BACKEND.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (let* ((fname (buffer-file-name))
	 (extensions '((html . ".html")
		       (latex . ".tex")
		       (ascii . ".txt")
		       (md . ".md")
		       (odt . ".odf")))
	 (cp (point))
	 (mm) 				;marker to save place
	 (export-name (concat (file-name-sans-extension fname)
			      (or (cdr (assoc backend extensions)) ""))))

    (org-export-with-buffer-copy
     ;; Note I use a marker here to make sure we stay in the same place we were.
     ;; This is more robust than save-excursion I think, since processing moves
     ;; points around. In theory the marker should move too.
     (setq mm (make-marker))
     (move-marker mm cp)
     ;; make sure we expand includes
     (org-export-expand-include-keyword)
     (goto-char (marker-position mm))
     (org-ref-process-buffer backend subtreep)
     (set-marker mm nil)
     
     (pcase backend
       ;; odt is a little bit special, and is missing one argument
       ('odt (org-open-file (org-odt-export-to-odt async subtreep visible-only
						   info)
			    'system))
       ;; for pandoc, we mean make a docx via pandoc
       ('docx (org-open-file (plist-get (org-pandoc-export-to-docx async subtreep visible-only
								   body-only info)
					'output-file)
			     'system))
       (_
	(org-open-file (org-export-to-file backend export-name
			 async subtreep visible-only
			 body-only info)
		       'system))))))


;; I guess I tried to use apply-partially here, and it did not work, so these
;; are each defined manually

(defun org-ref-export-to-html (&optional async subtreep visible-only
					 body-only info)
  "Export the buffer to HTML and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (org-ref-export-to 'html async subtreep visible-only
		     body-only info))


(defun org-ref-export-to-ascii (&optional async subtreep visible-only
					  body-only info)
  "Export the buffer to ascii and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (org-ref-export-to 'ascii async subtreep visible-only
		     body-only info))


(defun org-ref-export-to-md (&optional async subtreep visible-only
				       body-only info)
  "Export the buffer to md and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (org-ref-export-to 'md async subtreep visible-only
		     body-only info))


(defun org-ref-export-to-pdf (&optional async subtreep visible-only
					body-only info)
  "Export the buffer to PDF via LaTeX and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (let ((org-export-before-parsing-hook (append
					 org-export-before-parsing-hook
					 '(org-ref-csl-preprocess-buffer))))
    (org-open-file (org-latex-export-to-pdf async subtreep visible-only
					    body-only info))))


(defun org-ref-export-to-latex (&optional async subtreep visible-only
					  body-only info)
  "Export the buffer to LaTeX and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (org-ref-export-to 'latex async subtreep visible-only
		     body-only info))


(defun org-ref-export-to-odt (&optional async subtreep visible-only
					body-only info)
  "Export the buffer to ODT and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (require 'htmlfontify)
  (unless (boundp 'hfy-user-sheet-assoc) (setq  hfy-user-sheet-assoc nil))
  (org-ref-export-to 'odt async subtreep visible-only
		     body-only info))


(defun org-ref-export-to-docx (&optional async subtreep visible-only
					 body-only info)
  "Export the buffer to docx via pandoc and open.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (org-ref-export-to 'docx async subtreep visible-only
		     body-only info))


(defun org-ref-export-as-org (&optional _async subtreep visible-only
					body-only info)
  "Export the buffer to an ORG buffer and open.
We only make a buffer here to avoid overwriting the original file.
See `org-export-as' for the meaning of ASYNC SUBTREEP
VISIBLE-ONLY BODY-ONLY and INFO."
  (let* ((export-buf "*org-ref ORG Export*")
	 export)

    (org-export-with-buffer-copy
     (org-ref-process-buffer 'org subtreep)
     (setq export (org-export-as 'org subtreep visible-only body-only info))
     (with-current-buffer (get-buffer-create export-buf)
       (erase-buffer)
       (org-mode)
       (insert export)))
    (pop-to-buffer export-buf)))


(defun org-ref-export-to-message (&optional _async subtreep visible-only
					    body-only info)
  "Export to ascii and insert in an email message."
  (let* ((backend 'ascii)
	 (content (org-export-with-buffer-copy
		   (org-ref-process-buffer backend subtreep)
		   (org-export-as backend subtreep visible-only
				  body-only info))))
    (compose-mail)
    (message-goto-body)
    (insert content)
    (message-goto-to)))


(org-export-define-derived-backend 'org-ref 'org
  :menu-entry
  '(?r "Org-ref export"
       ((?a "to Ascii" org-ref-export-to-ascii)
	(?m "to markdown" org-ref-export-to-md)
	(?h "to html" org-ref-export-to-html)
	(?l "to LaTeX" org-ref-export-to-latex)
	(?p "to PDF" org-ref-export-to-pdf)
	(?o "to ODT" org-ref-export-to-odt)
	(?O "to Org buffer" org-ref-export-as-org)
	(?e "to email" org-ref-export-to-message)
	(?w "to docx" org-ref-export-to-docx))))

;; An alternative to this exporter is to use an  `org-export-before-parsing-hook'
;; (add-hook 'org-export-before-parsing-hook 'org-ref-csl-preprocess-buffer)

(defun org-ref-csl-preprocess-buffer (backend)
  "Preprocess the buffer in BACKEND export.
Note this may not work as expected, what about subtreep? The hook
function just takes one argument. For now we rely on
`buffer-narrowed-p' and an org-heading at the beginning.
I am not positive on this though."
  (org-ref-process-buffer backend (and (buffer-narrowed-p)
				       (save-excursion
					 (goto-char (point-min))
					 (org-at-heading-p)))))


;; A hydra exporter with preprocessors
(defhydradio org-ref ()
  (natmove "natmove")
  (citeproc "CSL citations")
  (refproc "cross-references")
  (acrossproc "Acronyms, glossary")
  (idxproc "Index")
  (bblproc "BBL citations"))


(defun org-ref-export-from-hydra (&optional arg)
  "Run the export dispatcher with the desired hooks selected in `org-ref-export/body'."
  (interactive "P")

  (when (and org-ref/citeproc org-ref/bblproc)
    (error "You cannot use CSL and BBL at the same time."))
  
  (let ((org-export-before-parsing-hook org-export-before-parsing-hook))
    (when org-ref/citeproc
      (cl-pushnew 'org-ref-csl-preprocess-buffer org-export-before-parsing-hook))

    (when org-ref/refproc
      (cl-pushnew 'org-ref-refproc org-export-before-parsing-hook))

    (when org-ref/acrossproc
      (cl-pushnew 'org-ref-acrossproc org-export-before-parsing-hook))

    (when org-ref/idxproc
      (cl-pushnew 'org-ref-idxproc org-export-before-parsing-hook))

    (when org-ref/bblproc
      (unless (featurep 'org-ref-natbib-bbl-citeproc)
	(require 'org-ref-natbib-bbl-citeproc))
      (cl-pushnew 'org-ref-bbl-preprocess org-export-before-parsing-hook))
    
    ;; this goes last since it moves cites before they might get replaced.
    (when org-ref/natmove
      (cl-pushnew 'org-ref-cite-natmove org-export-before-parsing-hook))

    (org-export-dispatch arg)))


(defhydra org-ref-export (:color red)
  "
_C-n_: natmove % -15`org-ref/natmove       _C-c_: citeproc % -15`org-ref/citeproc^^^  _C-r_: refproc % -15`org-ref/refproc^^^
_C-a_: acrossproc % -15`org-ref/acrossproc    _C-i_: idxproc % -15`org-ref/idxproc^^^   _C-b_: bblproc % -15`org-ref/bblproc^^^
"
  ("C-n" (org-ref/natmove) nil)
  ("C-c" (org-ref/citeproc) nil)
  ("C-r" (org-ref/refproc) nil)
  ("C-a" (org-ref/acrossproc) nil)
  ("C-i" (org-ref/idxproc) nil)
  ("C-b" (org-ref/bblproc) nil)

  ("e" org-ref-export-from-hydra "Export" :color blue)
  ("q" nil "quit"))

(provide 'org-ref-export)

;;; org-ref-export.el ends here
