Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

474 lines
20 KiB

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