;;; org-ref-helm.el --- Generic helm functions for org-ref -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2016 John Kitchin
|
|
|
|
;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
|
|
;; Keywords:
|
|
|
|
;; 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:
|
|
|
|
;; These are not specific to helm-bibtex.
|
|
|
|
;;; Code:
|
|
|
|
(declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
|
|
(declare-function 'org-ref-bad-file-link-candidates "org-ref-core.el")
|
|
(declare-function 'org-ref-get-labels "org-ref-core.el")
|
|
(declare-function 'org-ref-bad-cite-candidates "org-ref-core.el")
|
|
(declare-function 'org-ref-bad-ref-candidates "org-ref-core.el")
|
|
(declare-function 'org-ref-bad-label-candidates "org-ref-core.el")
|
|
|
|
(require 'helm)
|
|
(require 'org-element)
|
|
(require 'org-ref-core)
|
|
|
|
;;;###autoload
|
|
(defun org-ref-helm-insert-label-link ()
|
|
"Insert label link at point.
|
|
Helm will display existing labels in the current buffer to avoid
|
|
duplication. If you use a prefix arg insert a radio target
|
|
instead of a label."
|
|
(interactive)
|
|
(let ((labels (org-ref-get-labels)))
|
|
(helm :sources `(,(helm-build-sync-source "Existing labels"
|
|
:candidates labels
|
|
:action (lambda (label)
|
|
(with-helm-current-buffer
|
|
(org-open-link-from-string
|
|
(format "ref:%s" label)))))
|
|
,(helm-build-dummy-source "Create new label"
|
|
:action (lambda (label)
|
|
(with-helm-current-buffer
|
|
(if helm-current-prefix-arg
|
|
(insert (concat "<<" label ">>"))
|
|
(insert (concat "label:" label)))))))
|
|
:buffer "*helm labels*")))
|
|
|
|
|
|
;;;###autoload
|
|
(defun org-ref-helm-insert-ref-link ()
|
|
"Helm menu to insert ref links to labels in the document.
|
|
If you are on link, replace with newly selected label. Use
|
|
\\[universal-argument] to insert a different kind of ref link.
|
|
Use a double \\[universal-argument] \\[universal-argument] to insert a
|
|
[[#custom-id]] link"
|
|
(interactive)
|
|
(let* ((labels (org-ref-get-labels))
|
|
(contexts (mapcar 'org-ref-get-label-context labels))
|
|
(cb (current-buffer)))
|
|
|
|
(helm :input (thing-at-point 'word)
|
|
:sources `(((name . "Available labels to ref")
|
|
(multiline)
|
|
(candidates . ,(cl-loop for label in labels
|
|
for context in contexts
|
|
;; we do some kludgy adding spaces
|
|
;; and bars to make it "easier" to
|
|
;; see in helm.
|
|
collect (cons (concat
|
|
label "\n"
|
|
(mapconcat
|
|
(lambda (x)
|
|
(concat " |" x))
|
|
(split-string context "\n")
|
|
"\n"
|
|
) "\n\n") label)))
|
|
;; default action to replace or insert ref link.
|
|
(action . (lambda (label)
|
|
(switch-to-buffer ,cb)
|
|
|
|
(cond
|
|
;; no prefix or on a link
|
|
((equal helm-current-prefix-arg nil)
|
|
(let* ((object (org-element-context))
|
|
(last-char
|
|
(save-excursion
|
|
(goto-char (org-element-property :end object))
|
|
(backward-char)
|
|
(if (looking-at " ")
|
|
" "
|
|
""))))
|
|
(if (-contains? org-ref-ref-types
|
|
(org-element-property :type object))
|
|
;; we are on a link, so replace it.
|
|
(setf
|
|
(buffer-substring
|
|
(org-element-property :begin object)
|
|
(org-element-property :end object))
|
|
(concat
|
|
(replace-regexp-in-string
|
|
(org-element-property :path object)
|
|
label
|
|
(org-element-property :raw-link object))
|
|
last-char))
|
|
;; insert a new link
|
|
(insert
|
|
(concat
|
|
org-ref-default-ref-type ":" label))
|
|
)))
|
|
;; one prefix, alternate ref link
|
|
((equal helm-current-prefix-arg '(4))
|
|
(insert
|
|
(concat
|
|
(helm :sources `((name . "Ref link types")
|
|
(candidates . ,org-ref-ref-types)
|
|
(action . (lambda (x) x))))
|
|
":" label)))
|
|
;; two prefixes, insert section custom-id link
|
|
((equal helm-current-prefix-arg '(16))
|
|
(insert
|
|
(format "[[#%s]]" label)))))))))))
|
|
|
|
;;;###autoload
|
|
(defun org-ref ()
|
|
"Opens a helm interface to actions for `org-ref'.
|
|
Shows bad citations, ref links and labels.
|
|
This widens the file so that all links go to the right place."
|
|
(interactive)
|
|
;; (widen)
|
|
;; (org-cycle '(64))
|
|
(let ((cb (current-buffer))
|
|
(bad-citations (org-ref-bad-cite-candidates))
|
|
(bad-refs (org-ref-bad-ref-candidates))
|
|
(bad-labels (org-ref-bad-label-candidates))
|
|
(bad-files (org-ref-bad-file-link-candidates))
|
|
(bib-candidates '())
|
|
(unreferenced-labels '())
|
|
natbib-required
|
|
natbib-used
|
|
cleveref-required
|
|
cleveref-used
|
|
biblatex-required
|
|
biblatex-used
|
|
(org-latex-prefer-user-labels (and (boundp 'org-latex-prefer-user-labels)
|
|
org-latex-prefer-user-labels)))
|
|
|
|
;; See if natbib, biblatex or cleveref are required
|
|
(org-element-map (org-element-parse-buffer) 'link
|
|
(lambda (link)
|
|
(when (member (org-element-property :type link) org-ref-natbib-types)
|
|
(setq natbib-required t))
|
|
(when (member (org-element-property :type link) org-ref-biblatex-types)
|
|
(setq biblatex-required t))
|
|
(when (member (org-element-property :type link) '("cref" "Cref"))
|
|
(setq cleveref-required t)))
|
|
nil t)
|
|
|
|
;; See if natbib is probably used. This will miss a case where natbib is included somehow.
|
|
(setq natbib-used
|
|
(or
|
|
(member "natbib" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-default-packages-alist))
|
|
(member "natbib" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-packages-alist))
|
|
;; see of something like \usepackage{natbib} exists.
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(re-search-forward "{natbib}" nil t))))
|
|
|
|
(setq biblatex-used
|
|
(or
|
|
(member "biblatex" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-default-packages-alist))
|
|
(member "biblatex" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-packages-alist))
|
|
;; see of something like \usepackage{biblatex} exists.
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(re-search-forward "{biblatex}" nil t))))
|
|
|
|
(setq cleveref-used
|
|
(or
|
|
(member "cleveref" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-default-packages-alist))
|
|
(member "cleveref" (mapcar (lambda (x) (when (listp x) (nth 1 x))) org-latex-packages-alist))
|
|
;; see of something like \usepackage{cleveref} exists.
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(re-search-forward "{cleveref}" nil t))))
|
|
|
|
;; setup bib-candidates. This checks a variety of things in the
|
|
;; bibliography, bibtex files. check for which bibliographies are used
|
|
(cl-pushnew
|
|
(cons (format "Using these bibtex files: %s"
|
|
(org-ref-find-bibliography))
|
|
(lambda () nil))
|
|
bib-candidates)
|
|
|
|
;; Check bibliography style exists
|
|
(save-excursion
|
|
(goto-char 0)
|
|
(unless (re-search-forward "bibliographystyle:\\|\\\\bibliographystyle{" nil t)
|
|
(cl-pushnew
|
|
(cons "No bibliographystyle found."
|
|
(lambda ()
|
|
(switch-to-buffer "*org-ref*")
|
|
(erase-buffer)
|
|
(insert "No bibliography style found. This may be ok, if your latex class style sets that up, but if not this is an error. Try adding something like:
|
|
bibliographystyle:unsrt
|
|
at the end of you file.
|
|
")
|
|
(org-mode)))
|
|
bib-candidates)))
|
|
|
|
;; Check if latex knows of the bibliographystyle. We only check links here.
|
|
;; I also assume this style exists as a bst file that kpsewhich can find.
|
|
(save-excursion
|
|
(goto-char 0)
|
|
(when (re-search-forward "bibliographystyle:" nil t)
|
|
;; on a link. get style
|
|
(let ((path (org-element-property :path (org-element-context))))
|
|
(unless (= 0 (shell-command (format "kpsewhich %s.bst" path)))
|
|
(cl-pushnew
|
|
(cons (format "bibliographystyle \"%s\" may be unknown" path)
|
|
(lambda ()
|
|
(goto-char 0)
|
|
(re-search-forward "bibliographystyle:")))
|
|
bib-candidates)))))
|
|
|
|
;; check for multiple bibliography links
|
|
(let* ((bib-links (-filter
|
|
(lambda (el)
|
|
(string= (org-element-property :type el) "bibliography"))
|
|
(org-element-map (org-element-parse-buffer) 'link 'identity)))
|
|
(n-bib-links (length bib-links)))
|
|
|
|
(when (> n-bib-links 1)
|
|
(mapc (lambda (link)
|
|
(setq
|
|
bib-candidates
|
|
(append
|
|
bib-candidates
|
|
(list (cons (format "Multiple bibliography link: %s"
|
|
(org-element-property :raw-link link))
|
|
`(lambda ()
|
|
(goto-char ,(org-element-property :begin link))))))))
|
|
bib-links)))
|
|
|
|
;; Check for bibliography files existence.
|
|
(mapc (lambda (bibfile)
|
|
(unless (file-exists-p bibfile)
|
|
(cl-pushnew
|
|
(cons
|
|
(format "%s does not exist." bibfile)
|
|
(lambda ()
|
|
(message "Non-existent bibfile.")))
|
|
bib-candidates)))
|
|
(org-ref-find-bibliography))
|
|
|
|
;; check for spaces in bibliography
|
|
(let ((bibfiles (mapcar 'expand-file-name
|
|
(org-ref-find-bibliography))))
|
|
(mapc (lambda (bibfile)
|
|
(when (string-match " " bibfile)
|
|
(cl-pushnew
|
|
(cons (format "One or more spaces found in path to %s" bibfile)
|
|
(lambda ()
|
|
(message "No spaces are allowed in bibtex file paths. We recommend replacing them with -. Underscores usually cause other problems if you don't know what you are doing.")))
|
|
bib-candidates)))
|
|
bibfiles))
|
|
|
|
;; validate bibtex files
|
|
(let ((bibfiles (mapcar 'expand-file-name
|
|
(org-ref-find-bibliography))))
|
|
(mapc
|
|
(lambda (bibfile)
|
|
(unless (with-current-buffer
|
|
(find-file-noselect bibfile)
|
|
(bibtex-validate))
|
|
(cl-pushnew
|
|
(cons
|
|
(format "Invalid bibtex file found. %S" bibfile)
|
|
`(lambda ()
|
|
(find-file ,bibfile)))
|
|
bib-candidates)))
|
|
bibfiles))
|
|
|
|
;; unreferenced labels
|
|
(save-excursion
|
|
(save-restriction
|
|
(widen)
|
|
(goto-char (point-min))
|
|
(let ((matches '()))
|
|
;; these are the org-ref label:stuff kinds
|
|
(while (re-search-forward
|
|
"[^#+]label:\\([a-zA-Z0-9:-]*\\)" nil t)
|
|
(cl-pushnew (cons
|
|
(match-string-no-properties 1)
|
|
(point))
|
|
matches))
|
|
;; now add all the other kinds of labels.
|
|
;; #+label:
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "^#\\+label:\\s-+\\(.*\\)\\b" nil t)
|
|
;; do not do this for tables. We get those in `org-ref-get-tblnames'.
|
|
;; who would have thought you have save match data here? Trust me. When
|
|
;; I wrote this, you did.
|
|
(unless (save-match-data (equal (car (org-element-at-point)) 'table))
|
|
(cl-pushnew (cons (match-string-no-properties 1) (point)) matches))))
|
|
|
|
;; \label{}
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "\\\\label{\\([a-zA-Z0-9:-]*\\)}"
|
|
nil t)
|
|
(cl-pushnew (cons (match-string-no-properties 1) (point)) matches)))
|
|
|
|
;; #+tblname: and actually #+label
|
|
(cl-loop for cell in (org-element-map (org-element-parse-buffer 'element) 'table
|
|
(lambda (table)
|
|
(cons (org-element-property :name table)
|
|
(org-element-property :begin table))))
|
|
do
|
|
(cl-pushnew cell matches))
|
|
|
|
;; CUSTOM_IDs
|
|
(org-map-entries
|
|
(lambda ()
|
|
(let ((custom_id (org-entry-get (point) "CUSTOM_ID")))
|
|
(when (not (null custom_id))
|
|
(cl-pushnew (cons custom_id (point)) matches)))))
|
|
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "^#\\+name:\\s-+\\(.*\\)" nil t)
|
|
(cl-pushnew (cons (match-string 1) (point)) matches))
|
|
|
|
|
|
;; unreference labels
|
|
(let ((refs (org-element-map (org-element-parse-buffer) 'link
|
|
(lambda (el)
|
|
(when (or (string= "ref" (org-element-property :type el))
|
|
(string= "eqref" (org-element-property :type el))
|
|
(string= "pageref" (org-element-property :type el))
|
|
(string= "nameref" (org-element-property :type el))
|
|
(string= "autoref" (org-element-property :type el))
|
|
(string= "cref" (org-element-property :type el))
|
|
(string= "Cref" (org-element-property :type el)))
|
|
(org-element-property :path el))))))
|
|
(cl-loop for (label . p) in matches
|
|
do
|
|
(when (and label (not (-contains? refs label)))
|
|
(cl-pushnew
|
|
(cons label (set-marker (make-marker) p))
|
|
unreferenced-labels)))))))
|
|
|
|
|
|
(helm :sources `(((name . "Bad citations")
|
|
(candidates . ,bad-citations)
|
|
(action . (lambda (marker)
|
|
(switch-to-buffer (marker-buffer marker))
|
|
(goto-char marker)
|
|
(org-show-entry))))
|
|
|
|
((name . "Multiply defined labels")
|
|
(candidates . ,bad-labels)
|
|
(action . (lambda (marker)
|
|
(switch-to-buffer (marker-buffer marker))
|
|
(goto-char marker)
|
|
(org-show-entry))))
|
|
|
|
((name . "Bad ref links")
|
|
(candidates . ,bad-refs)
|
|
(action . (lambda (marker)
|
|
(switch-to-buffer (marker-buffer marker))
|
|
(goto-char marker)
|
|
(org-show-entry))))
|
|
|
|
((name . "Bad file links")
|
|
(candidates . ,bad-files)
|
|
(lambda (marker)
|
|
(switch-to-buffer (marker-buffer marker))
|
|
(goto-char marker)
|
|
(org-show-entry)))
|
|
|
|
((name . "Labels with no ref links")
|
|
(candidates . ,unreferenced-labels)
|
|
(action . (lambda (marker)
|
|
(switch-to-buffer (marker-buffer marker))
|
|
(goto-char marker)
|
|
(org-show-entry))))
|
|
|
|
((name . "Bibliography")
|
|
(candidates . ,bib-candidates)
|
|
(action . (lambda (x)
|
|
(switch-to-buffer ,cb)
|
|
(funcall x))))
|
|
|
|
((name . "Miscellaneous")
|
|
(candidates . (,(format "org-latex-prefer-user-labels = %s"
|
|
org-latex-prefer-user-labels)
|
|
,(format "bibtex-dialect = %s" bibtex-dialect)
|
|
,(format "biblatex is%srequired." (if biblatex-required " " " not "))
|
|
,(format "biblatex is%sused." (if biblatex-used " " " not "))
|
|
,(format "org-version = %s" (org-version))
|
|
,(format "completion backend = %s" org-ref-completion-library)
|
|
,(format "org-latex-pdf-process is defined as %s" org-latex-pdf-process)
|
|
,(format "natbib is%srequired." (if natbib-required " " " not "))
|
|
,(format "natbib is%sused." (if natbib-used " " " not "))
|
|
,(format "cleveref is%srequired." (if cleveref-required " " " not "))
|
|
,(format "cleveref is%sused." (if cleveref-used " " " not "))))
|
|
(action . nil))
|
|
|
|
((name . "Utilities")
|
|
(candidates . (("Check buffer again" . org-ref)
|
|
("Insert citation" . helm-bibtex)
|
|
("Insert label link" . org-ref-helm-insert-label-link)
|
|
("Insert ref link" . org-ref-helm-insert-ref-link)
|
|
("List of figures" . org-ref-list-of-figures)
|
|
("List of tables" . org-ref-list-of-tables)
|
|
("Table of contents" . helm-org-in-buffer-headings)))
|
|
(action . (lambda (x)
|
|
(switch-to-buffer ,cb)
|
|
(funcall x))))
|
|
|
|
((name . "Document utilities")
|
|
(candidates . (("Spell check document" . ispell)))
|
|
(action . (lambda (x)
|
|
(switch-to-buffer ,cb)
|
|
(funcall x))))
|
|
;; Exports
|
|
((name . "Export functions")
|
|
(candidates . (("Extract cited entries" . org-ref-extract-bibtex-entries)
|
|
("Export to html and open" . (lambda ()
|
|
(org-open-file
|
|
(org-html-export-to-html))))
|
|
("Export to pdf and open" . (lambda ()
|
|
(org-open-file
|
|
(org-latex-export-to-pdf))))
|
|
("Export to manuscript pdf and open" . ox-manuscript-export-and-build-and-open)
|
|
("Export submission manuscript pdf and open" . ox-manuscript-build-submission-manuscript-and-open)))
|
|
(action . (lambda (x)
|
|
(switch-to-buffer ,cb)
|
|
(funcall x))))))))
|
|
|
|
|
|
;;;###autoload
|
|
(defun helm-tag-bibtex-entry ()
|
|
"Helm interface to add keywords to a bibtex entry.
|
|
Run this with the point in a bibtex entry."
|
|
(interactive)
|
|
(let ((keyword-source `((name . "Existing keywords")
|
|
(candidates . ,(org-ref-bibtex-keywords))
|
|
(action . (lambda (candidate)
|
|
(org-ref-set-bibtex-keywords
|
|
(mapconcat
|
|
'identity
|
|
(helm-marked-candidates)
|
|
", "))))))
|
|
(fallback-source `((name . "Add new keywords")
|
|
(dummy)
|
|
(action . (lambda (candidate)
|
|
(org-ref-set-bibtex-keywords helm-pattern))))))
|
|
(helm :sources `(,keyword-source ,fallback-source))))
|
|
|
|
(provide 'org-ref-helm)
|
|
;;; org-ref-helm.el ends here
|