-Depending on how you enter citations, you may have citations with no corresponding bibtex entry. This function finds them and gives you a clickable table to navigate to them.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(require 'cl)
-
-(defun index (substring list)
- "return the index of string in a list of strings"
- (let ((i 0)
- (found nil))
- (dolist (arg list i)
- (if (string-match (concat "^" substring "$") arg)
- (progn
- (setq found t)
- (return i)))
- (setq i (+ i 1)))
- ;; return counter if found, otherwise return nil
- (if found i nil)))
-
-
-(defun org-ref-find-bad-citations ()
- "Create a list of citation keys in an org-file that do not have a bibtex entry in the known bibtex files.
-
-Makes a new buffer with clickable links."
- (interactive)
- ;; generate the list of bibtex-keys and cited keys
- (let* ((bibtex-files (org-ref-find-bibliography))
- (bibtex-file-path (mapconcat (lambda (x) (file-name-directory (file-truename x))) bibtex-files ":"))
- (bibtex-keys (mapcar (lambda (x) (car x)) (bibtex-global-key-alist)))
- (bad-citations '()))
-
- (org-element-map (org-element-parse-buffer) 'link
- (lambda (link)
- (let ((plist (nth 1 link)))
- (when (-contains? org-ref-cite-types (plist-get plist :type))
- (dolist (key (org-ref-split-and-strip-string (plist-get plist :path)))
- (when (not (index key bibtex-keys))
- (message-box "%s" link)
- (setq
- bad-citations
- (append
- bad-citations
- `(,(format "%s [[elisp:(progn (switch-to-buffer-other-frame \"%s\")(goto-char %s))][not found here]]\n"
- key
- (buffer-name)
- (plist-get plist :begin)))))
- )))))
- ;; set with-affilates to t to get citations in a caption
- nil nil nil t)
-
- (if bad-citations
- (progn
- (switch-to-buffer-other-window "*Missing citations*")
- (org-mode)
- (erase-buffer)
- (insert "* List of bad cite links\n")
- (insert (mapconcat 'identity bad-citations ""))
- ;(setq buffer-read-only t)
- (use-local-map (copy-keymap org-mode-map))
- (local-set-key "q" #'(lambda () (interactive) (kill-buffer))))
-
- (when (get-buffer "*Missing citations*")
- (kill-buffer "*Missing citations*"))
- (message "No bad cite links found"))))
-#+END_SRC
-
-** helm interface to org-ref
-In [[id:8515E800-EDA0-4B2A-85FD-55B6FF849203][Find bad cite links]] we wrote a function that finds bad links and creates a buffer of links to them.
-
-Here we develop a similar idea, but instead of an org-buffer with links, we create helm sources for bad cite links, bad ref links, and multiple labels.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-bad-cite-candidates ()
- "Returns a list of conses (key . marker) where key does not exist in the known bibliography files, and marker points to the key."
- (let* ((cp (point)) ; save to return to later
- (bibtex-files (org-ref-find-bibliography))
- (bibtex-file-path (mapconcat
- (lambda (x)
- (file-name-directory (file-truename x)))
- bibtex-files ":"))
- (bibtex-keys (mapcar (lambda (x) (car x))
- (bibtex-global-key-alist)))
- (bad-citations '()))
-
- (org-element-map (org-element-parse-buffer) 'link
- (lambda (link)
- (let ((plist (nth 1 link)))
- (when (-contains? org-ref-cite-types (plist-get plist :type))
- (dolist (key (org-ref-split-and-strip-string (plist-get plist :path)) )
- (when (not (index key bibtex-keys))
- (goto-char (plist-get plist :begin))
- (re-search-forward key)
- (push (cons key (point-marker)) bad-citations)))
- )))
- ;; add with-affiliates to get cites in caption
- nil nil nil t)
- (goto-char cp)
- bad-citations))
-
-
-(defun org-ref-bad-ref-candidates ()
- "Returns a list of conses (ref . marker) where ref is a ref link that does not point to anything (i.e. a label)."
- ;; first get a list of legitimate labels
- (let ((cp (point))
- (labels (org-ref-get-labels))
- (bad-refs '()))
- ;; now loop over ref links
- (goto-char (point-min))
- (org-element-map (org-element-parse-buffer) 'link
- (lambda (link)
- (let ((plist (nth 1 link)))
- (when (or (equal (plist-get plist ':type) "ref")
- (equal (plist-get plist ':type) "eqref")
- (equal (plist-get plist ':type) "pageref")
- (equal (plist-get plist ':type) "nameref"))
- (unless (-contains? labels (plist-get plist :path))
- (goto-char (plist-get plist :begin))
- (add-to-list
- 'bad-refs
- (cons (plist-get plist :path)
- (point-marker))))))))
- (goto-char cp)
- bad-refs))
-
-
-(defun org-ref-bad-label-candidates ()
- "Return a list of labels where label is multiply defined."
- (let ((labels (org-ref-get-labels))
- (multiple-labels '()))
- (when (not (= (length labels)
- (length (-uniq labels))))
- (dolist (label labels)
- (when (> (-count (lambda (a)
- (equal a label))
- labels) 1)
- ;; this is a multiply defined label.
- (let ((cp (point)))
- (goto-char (point-min))
- (while (re-search-forward
- (format "[^#+]label:%s\\s-" label) nil t)
- (push (cons label (point-marker)) multiple-labels))
-
- (goto-char (point-min))
- (while (re-search-forward
- (format "\\label{%s}\\s-?" label) nil t)
- (push (cons label (point-marker)) multiple-labels))
-
- (goto-char (point-min))
- (while (re-search-forward
- (format "^#\\+label:\\s-*%s" label) nil t)
- (push (cons label (point-marker)) multiple-labels))
-
- (goto-char (point-min))
- (while (re-search-forward
- (format "^#\\+tblname:\\s-*%s" label) nil t)
- (push (cons label (point-marker)) multiple-labels))
- (goto-char cp)))))
- multiple-labels))
-#+END_SRC
-
-#+RESULTS:
-: org-ref-bad-label-candidates
-
-Now, we have a functions for candidates, we can make helm sources for each one, and then run a helm command to view them.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref ()
- "Opens a helm interface to actions for org-ref.
-Shows bad citations, ref links and labels"
- (interactive)
- (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)))
-
- (helm :sources `(((name . "Bad citations")
- (candidates . ,bad-citations)
- (action . (lambda (marker)
- (switch-to-buffer (marker-buffer marker))
- (goto-char marker))))
- ;;
- ((name . "Bad Labels")
- (candidates . ,bad-labels)
- (action . (lambda (marker)
- (switch-to-buffer (marker-buffer marker))
- (goto-char marker))))
- ;;
- ((name . "Bad ref links")
- (candidates . ,bad-refs)
- (action . (lambda (marker)
- (switch-to-buffer (marker-buffer marker))
- (goto-char marker))))
- ;;
- ((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" . nil)
- ))
- (action . (lambda (x)
- (switch-to-buffer ,cb)
- (funcall x))))
- ;;
- ((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))))
- ))))
-#+END_SRC
-
-
-** Finding non-ascii characters
-I like my bibtex files to be 100% ascii. This function finds the non-ascii characters so you can replace them.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-find-non-ascii-characters ()
- "finds non-ascii characters in the buffer. Useful for cleaning up bibtex files"
- (interactive)
- (occur "[^[:ascii:]]"))
-#+END_SRC
-
-** Resort a bibtex entry
-I like neat and orderly bibtex entries.That means the fields are in a standard order that I like. This function reorders the fields in an entry for articles, and makes sure the fields are in lowercase.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-sort-bibtex-entry ()
- "sort fields of entry in standard order and downcase them"
- (interactive)
- (bibtex-beginning-of-entry)
- (let* ((master '("author" "title" "journal" "volume" "number" "pages" "year" "doi" "url"))
- (entry (bibtex-parse-entry))
- (entry-fields)
- (other-fields)
- (type (cdr (assoc "=type=" entry)))
- (key (cdr (assoc "=key=" entry))))
-
- ;; these are the fields we want to order that are in this entry
- (setq entry-fields (mapcar (lambda (x) (car x)) entry))
- ;; we do not want to reenter these fields
- (setq entry-fields (remove "=key=" entry-fields))
- (setq entry-fields (remove "=type=" entry-fields))
-
- ;;these are the other fields in the entry
- (setq other-fields (remove-if-not (lambda(x) (not (member x master))) entry-fields))
-
- (cond
- ;; right now we only resort articles
- ((string= (downcase type) "article")
- (bibtex-kill-entry)
- (insert
- (concat "@article{" key ",\n"
- (mapconcat
- (lambda (field)
- (when (member field entry-fields)
- (format "%s = %s," (downcase field) (cdr (assoc field entry))))) master "\n")
- (mapconcat
- (lambda (field)
- (format "%s = %s," (downcase field) (cdr (assoc field entry)))) other-fields "\n")
- "\n}\n\n"))
- (bibtex-find-entry key)
- (bibtex-fill-entry)
- (bibtex-clean-entry)
- ))))
-#+END_SRC
-
-** Clean a bibtex entry
- I like neat and orderly bibtex entries. This code will eventually replace the key with my style key, clean the entry, and sort the fields in the order I like them.
-see [[file:emacs-24.3/lisp/textmodes/bibtex.el::bibtex-autokey-before-presentation-function]] for how to set a function that checks for uniqueness of the key.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-clean-bibtex-entry(&optional keep-key)
- "clean and replace the key in a bibtex function. When keep-key is t, do not replace it. You can use a prefix to specify the key should be kept"
- (interactive "P")
- (bibtex-beginning-of-entry)
-(end-of-line)
- ;; some entries do not have a key or comma in first line. We check and add it, if needed.
- (unless (string-match ",$" (thing-at-point 'line))
- (end-of-line)
- (insert ","))
-
- ;; check for empty pages, and put eid or article id in its place
- (let ((entry (bibtex-parse-entry))
- (pages (bibtex-autokey-get-field "pages"))
- (year (bibtex-autokey-get-field "year"))
- (doi (bibtex-autokey-get-field "doi"))
- ;; The Journal of Chemical Physics uses eid
- (eid (bibtex-autokey-get-field "eid")))
-
- ;; replace http://dx.doi.org/ in doi. some journals put that in,
- ;; but we only want the doi.
- (when (string-match "^http://dx.doi.org/" doi)
- (bibtex-beginning-of-entry)
- (goto-char (car (cdr (bibtex-search-forward-field "doi" t))))
- (bibtex-kill-field)
- (bibtex-make-field "doi")
- (backward-char)
- (insert (replace-regexp-in-string "^http://dx.doi.org/" "" doi)))
-
- ;; asap articles often set year to 0, which messes up key
- ;; generation. fix that.
- (when (string= "0" year)
- (bibtex-beginning-of-entry)
- (goto-char (car (cdr (bibtex-search-forward-field "year" t))))
- (bibtex-kill-field)
- (bibtex-make-field "year")
- (backward-char)
- (insert (read-string "Enter year: ")))
-
- ;; fix pages if they are empty if there is an eid to put there.
- (when (string= "-" pages)
- (when eid
- (bibtex-beginning-of-entry)
- ;; this seems like a clunky way to set the pages field.But I
- ;; cannot find a better way.
- (goto-char (car (cdr (bibtex-search-forward-field "pages" t))))
- (bibtex-kill-field)
- (bibtex-make-field "pages")
- (backward-char)
- (insert eid)))
-
- ;; replace naked & with \&
- (save-restriction
- (bibtex-narrow-to-entry)
- (bibtex-beginning-of-entry)
- (message "checking &")
- (replace-regexp " & " " \\\\& ")
- (widen))
-
- ;; generate a key, and if it duplicates an existing key, edit it.
- (unless keep-key
- (let ((key (bibtex-generate-autokey)))
-
- ;; first we delete the existing key
- (bibtex-beginning-of-entry)
- (re-search-forward bibtex-entry-maybe-empty-head)
- (if (match-beginning bibtex-key-in-head)
- (delete-region (match-beginning bibtex-key-in-head)
- (match-end bibtex-key-in-head)))
- ;; check if the key is in the buffer
- (when (save-excursion
- (bibtex-search-entry key))
- (save-excursion
- (bibtex-search-entry key)
- (bibtex-copy-entry-as-kill)
- (switch-to-buffer-other-window "*duplicate entry*")
- (bibtex-yank))
- (setq key (bibtex-read-key "Duplicate Key found, edit: " key)))
-
- (insert key)
- (kill-new key))) ;; save key for pasting
-
- ;; run hooks. each of these operates on the entry with no arguments.
- ;; this did not work like i thought, it gives a symbolp error.
- ;; (run-hooks org-ref-clean-bibtex-entry-hook)
- (mapcar (lambda (x)
- (save-restriction
- (save-excursion
- (funcall x))))
- org-ref-clean-bibtex-entry-hook)
-
- ;; sort fields within entry
- (org-ref-sort-bibtex-entry)
- ;; check for non-ascii characters
- (occur "[^[:ascii:]]")
- ))
-#+END_SRC
-
-#+RESULTS:
-: org-ref-clean-bibtex-entry
-
-** Sort the entries in a citation link by year
-I prefer citations in chronological order within a grouping. These functions sort the link under the cursor by year.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-citation-year (key)
- "get the year of an entry with key. Returns year as a string."
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file key))
- (bibfile (cdr results)))
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key nil 0)
- (prog1 (reftex-get-bib-field "year" (bibtex-parse-entry t))
- ))))
-
-(defun org-ref-sort-citation-link ()
- "replace link at point with sorted link by year"
- (interactive)
- (let* ((object (org-element-context))
- (type (org-element-property :type object))
- (begin (org-element-property :begin object))
- (end (org-element-property :end object))
- (link-string (org-element-property :path object))
- keys years data)
- (setq keys (org-ref-split-and-strip-string link-string))
- (setq years (mapcar 'org-ref-get-citation-year keys))
- (setq data (mapcar* (lambda (a b) `(,a . ,b)) years keys))
- (setq data (cl-sort data (lambda (x y) (< (string-to-int (car x)) (string-to-int (car y))))))
- ;; now get the keys separated by commas
- (setq keys (mapconcat (lambda (x) (cdr x)) data ","))
- ;; and replace the link with the sorted keys
- (cl--set-buffer-substring begin end (concat type ":" keys))))
-#+END_SRC
-
-** Sort entries in citation links with shift-arrow keys
-Sometimes it may be helpful to manually change the order of citations. These functions define shift-arrow functions.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-swap-keys (i j keys)
- "swap the keys in a list with index i and j"
- (let ((tempi (nth i keys)))
- (setf (nth i keys) (nth j keys))
- (setf (nth j keys) tempi))
- keys)
-
-(defun org-ref-swap-citation-link (direction)
- "move citation at point in direction +1 is to the right, -1 to the left"
- (interactive)
- (let* ((object (org-element-context))
- (type (org-element-property :type object))
- (begin (org-element-property :begin object))
- (end (org-element-property :end object))
- (link-string (org-element-property :path object))
- key keys i)
- ;; We only want this to work on citation links
- (when (-contains? org-ref-cite-types type)
- (setq key (org-ref-get-bibtex-key-under-cursor))
- (setq keys (org-ref-split-and-strip-string link-string))
- (setq i (index key keys)) ;; defined in org-ref
- (if (> direction 0) ;; shift right
- (org-ref-swap-keys i (+ i 1) keys)
- (org-ref-swap-keys i (- i 1) keys))
- (setq keys (mapconcat 'identity keys ","))
- ;; and replace the link with the sorted keys
- (cl--set-buffer-substring begin end (concat type ":" keys " "))
- ;; now go forward to key so we can move with the key
- (re-search-forward key)
- (goto-char (match-beginning 0)))))
-
-;; add hooks to make it work
-(add-hook 'org-shiftright-hook (lambda () (org-ref-swap-citation-link 1)))
-(add-hook 'org-shiftleft-hook (lambda () (org-ref-swap-citation-link -1)))
-#+END_SRC
-
-** Lightweight messages about links
-To get a lighter weight message about the label, ref and cite links, we define a function that gives us the minibuffer message, without the menu. We run this in an idle timer.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-label-context (label)
- "Return a string of context around a label."
- (save-excursion
- (catch 'result
- (goto-char (point-min))
- (when (re-search-forward
- (format "label:%s\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "\\label{%s}" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "^#\\+label:\\s-*\\(%s\\)\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "^#\\+tblname:\\s-*\\(%s\\)\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
- (throw 'result "!!! NO CONTEXT FOUND !!!"))))
-
-
-(defun org-ref-link-message ()
- "Print a minibuffer message about the link that point is on."
- (interactive)
- (when (eq major-mode 'org-mode)
- (let* ((object (org-element-context))
- (type (org-element-property :type object)))
- (save-excursion
- (cond
- ;; cite links
- ((-contains? org-ref-cite-types type)
- (message (org-ref-get-citation-string-at-point)))
-
- ;; message some context about the label we are referring to
- ((string= type "ref")
- (message "%scount: %s"
- (org-ref-get-label-context
- (org-element-property :path object))
- (org-ref-count-labels
- (org-element-property :path object))))
-
- ((string= type "eqref")
- (message "%scount: %s"
- (org-ref-get-label-context
- (org-element-property :path object))
- (org-ref-count-labels
- (org-element-property :path object))))
-
- ;; message the count
- ((string= type "label")
- (let ((count (org-ref-count-labels
- (org-element-property :path object))))
- ;; get plurality on occurrence correct
- (message (concat
- (number-to-string count)
- " occurence"
- (when (or (= count 0)
- (> count 1))
- "s")))))
-
- ((string= type "custom-id")
- (save-excursion
- (org-open-link-from-string
- (format "[[#%s]]" (org-element-property :path object)))
- (message "%s" (org-get-heading))))
-
- ;; check if the bibliography files exist.
- ((string= type "bibliography")
- (let* ((bibfile)
- ;; object is the link you clicked on
- (object (org-element-context))
- (link-string (org-element-property :path object))
- (link-string-beginning)
- (link-string-end))
- (save-excursion
- (goto-char (org-element-property :begin object))
- (search-forward link-string nil nil 1)
- (setq link-string-beginning (match-beginning 0))
- (setq link-string-end (match-end 0)))
-
- ;; make sure we are in link and not before the :
- (when (> link-string-beginning (point))
- (goto-char link-string-beginning))
-
- ;; now if we have comma separated bibliographies
- ;; we find the one clicked on. we want to
- ;; search forward to next comma from point
- (save-excursion
- (if (search-forward "," link-string-end 1 1)
- (setq key-end (- (match-end 0) 1)) ; we found a match
- (setq key-end (point)))) ; no comma found so take the point
-
- ;; and backward to previous comma from point
- (save-excursion
- (if (search-backward "," link-string-beginning 1 1)
- (setq key-beginning (+ (match-beginning 0) 1)) ; we found a match
- (setq key-beginning (point)))) ; no match found
- ;; save the key we clicked on.
- (setq bibfile
- (org-ref-strip-string
- (buffer-substring key-beginning key-end)))
- (if (file-exists-p bibfile)
- (message "%s exists." bibfile)
- (message "!!! %s NOT FOUND !!!" bibfile))))
- )))))
-#+END_SRC
-
-* Aliases
-I like convenience. Here are some aliases for faster typing.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defalias 'oro 'org-ref-open-citation-at-point)
-(defalias 'orc 'org-ref-citation-at-point)
-(defalias 'orp 'org-ref-open-pdf-at-point)
-(defalias 'oru 'org-ref-open-url-at-point)
-(defalias 'orn 'org-ref-open-notes-at-point)
-(defalias 'ornr 'org-ref-open-notes-from-reftex)