|
|
- ;;; 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
|