-** Extract bibtex entries cited in an org-file
-When you use your default bibliography file, and you want to send an org-file to a collaborator, you may need to include bibtex entries so the other person can see them. This function does that and puts the entries in a section at the end of the document that can be tangled to a bib-file.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-extract-bibtex-entries ()
- "extract the bibtex entries referred to by cite links in the current buffer into a src block at the bottom of the current buffer.
-
-If no bibliography is in the buffer the `reftex-default-bibliography' is used."
- (interactive)
- (let* ((temporary-file-directory (file-name-directory (buffer-file-name)))
- (tempname (make-temp-file "extract-bib"))
- (contents (buffer-string))
- (cb (current-buffer))
- basename texfile bibfile results)
-
- ;; open tempfile and insert org-buffer contents
- (find-file tempname)
- (insert contents)
- (setq basename (file-name-sans-extension
- (file-name-nondirectory buffer-file-name))
- texfile (concat tempname ".tex")
- bibfile (concat tempname ".bib"))
-
- ;; see if we have a bibliography, and insert the default one if not.
- (save-excursion
- (goto-char (point-min))
- (unless (re-search-forward "^bibliography:" (point-max) 'end)
- (insert (format "\nbibliography:%s"
- (mapconcat 'identity reftex-default-bibliography ",")))))
- (save-buffer)
-
- ;; get a latex file and extract the references
- (org-latex-export-to-latex)
- (find-file texfile)
- (reftex-parse-all)
- (reftex-create-bibtex-file bibfile)
- (save-buffer)
- ;; save results of the references
- (setq results (buffer-string))
-
- ;; kill buffers. these are named by basename, not full path
- (kill-buffer (concat basename ".bib"))
- (kill-buffer (concat basename ".tex"))
- (kill-buffer basename)
-
- (delete-file bibfile)
- (delete-file texfile)
- (delete-file tempname)
-
- ;; Now back to the original org buffer and insert the results
- (switch-to-buffer cb)
- (when (not (string= "" results))
- (save-excursion
- (goto-char (point-max))
- (insert "\n\n")
- (org-insert-heading)
- (insert (format " Bibtex entries
-
-,#+BEGIN_SRC text :tangle %s
-%s
-,#+END_SRC" (concat (file-name-sans-extension (file-name-nondirectory (buffer-file-name))) ".bib") results))))))
-#+END_SRC
-
-** Find bad cite links
- :PROPERTIES:
- :ID: 8515E800-EDA0-4B2A-85FD-55B6FF849203
- :END:
-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:]]")
- ))