-Now, we can put all the labels together which will give us a list of candidates.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-labels ()
- "returns a list of labels in the buffer that you can make a ref link to. this is used to auto-complete ref links."
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (let ((matches '()))
- (while (re-search-forward "label:\\([a-zA-z0-9:-]*\\)" (point-max) t)
- (add-to-list 'matches (match-string-no-properties 1) t))
- (append matches (org-ref-get-org-labels) (org-ref-get-latex-labels) (org-ref-get-tblnames) (org-ref-get-custom-ids))))))
-#+END_SRC
-
-Now we create the completion function. This works from the org-machinery, e.g. if you type C-c C-l to insert a link, and use completion by pressing tab.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-complete-link (&optional arg)
- "Completion function for ref links"
- (let ((label))
- (setq label (completing-read "label: " (org-ref-get-labels)))
- (format "ref:%s" label)))
-#+END_SRC
-
-Alternatively, you may want to just call a function that inserts a link with completion:
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-insert-ref-link ()
- (interactive)
- (insert (org-ref-complete-link)))
-#+END_SRC
-
-** pageref
-
-This refers to the page of a label in LaTeX.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(org-add-link-type
- "pageref"
- (lambda (label)
- "on clicking goto the label. Navigate back with C-c &"
- (org-mark-ring-push)
- ;; next search from beginning of the buffer
- (widen)
- (unless
- (or
- ;; our label links
- (progn
- (goto-char (point-min))
- (re-search-forward (format "label:%s\\b" label) nil t))
-
- ;; a latex label
- (progn
- (goto-char (point-min))
- (re-search-forward (format "\\label{%s}" label) nil t))
-
- ;; #+label: name org-definition
- (progn
- (goto-char (point-min))
- (re-search-forward (format "^#\\+label:\\s-*\\(%s\\)\\b" label) nil t))
-
- ;; org tblname
- (progn
- (goto-char (point-min))
- (re-search-forward (format "^#\\+tblname:\\s-*\\(%s\\)\\b" label) nil t))
-
-;; Commented out because these ref links do not actually translate correctly in LaTeX.
-;; you need [[#label]] links.
- ;; CUSTOM_ID
-; (progn
-; (goto-char (point-min))
-; (re-search-forward (format ":CUSTOM_ID:\s-*\\(%s\\)" label) nil t))
- )
- ;; we did not find anything, so go back to where we came
- (org-mark-ring-goto)
- (error "%s not found" label))
- (message "go back with (org-mark-ring-goto) `C-c &`"))
- ;formatting
- (lambda (keyword desc format)
- (cond
- ((eq format 'html) (format "(<pageref>%s</pageref>)" path))
- ((eq format 'latex)
- (format "\\pageref{%s}" keyword)))))
-#+END_SRC
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-pageref-complete-link (&optional arg)
- "Completion function for ref links"
- (let ((label))
- (setq label (completing-read "label: " (org-ref-get-labels)))
- (format "ref:%s" label)))
-#+END_SRC
-
-Alternatively, you may want to just call a function that inserts a link with completion:
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-pageref-insert-ref-link ()
- (interactive)
- (insert (org-pageref-complete-link)))
-#+END_SRC
-
-** nameref
-
-The nameref link allows you make links to the text of a section with a label. Clicking on the link takes you to the label, and provides a mark to go back to. This only works if you put a raw latex label in the headline.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(org-add-link-type
- "nameref"
- (lambda (label)
- "on clicking goto the label. Navigate back with C-c &"
- (org-mark-ring-push)
- ;; next search from beginning of the buffer
- (widen)
- (unless
- (or
- ;; a latex label
- (progn
- (goto-char (point-min))
- (re-search-forward (format "\\label{%s}" label) nil t))
- )
- ;; we did not find anything, so go back to where we came
- (org-mark-ring-goto)
- (error "%s not found" label))
- (message "go back with (org-mark-ring-goto) `C-c &`"))
- ;formatting
- (lambda (keyword desc format)
- (cond
- ((eq format 'html) (format "(<nameref>%s</nameref>)" path))
- ((eq format 'latex)
- (format "\\nameref{%s}" keyword)))))
-#+END_SRC
-
-** eqref
-This is just the LaTeX ref for equations. On export, the reference is enclosed in parentheses.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(org-add-link-type
- "eqref"
- (lambda (label)
- "on clicking goto the label. Navigate back with C-c &"
- (org-mark-ring-push)
- ;; next search from beginning of the buffer
- (widen)
- (goto-char (point-min))
- (unless
- (or
- ;; search forward for the first match
- ;; our label links
- (re-search-forward (format "label:%s" label) nil t)
- ;; a latex label
- (re-search-forward (format "\\label{%s}" label) nil t)
- ;; #+label: name org-definition
- (re-search-forward (format "^#\\+label:\\s-*\\(%s\\)\\b" label) nil t))
- (org-mark-ring-goto)
- (error "%s not found" label))
- (message "go back with (org-mark-ring-goto) `C-c &`"))
- ;formatting
- (lambda (keyword desc format)
- (cond
- ((eq format 'html) (format "(<eqref>%s</eqref>)" path))
- ((eq format 'latex)
- (format "\\eqref{%s}" keyword)))))
-#+END_SRC
-
-** cite
-This is the main reason this library exists. We want the following behavior. A cite link should be able to contain multiple bibtex keys. You should be able to click on the link, and get a brief citation of the entry for that key, and a menu of options to open the bibtex file, open a pdf if you have it, open your notes on the entry, or open a url if it exists. You should be able to insert new references onto an existing cite link, or create new ones easily. The following code implements these features.
-
-*** Implementing the click actions of cite
-
-**** Getting the key we clicked on
-The first thing we need is to get the bibtex key we clicked on.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-bibtex-key-under-cursor ()
- "returns key under the bibtex cursor. We search forward from
-point to get a comma, or the end of the link, and then backwards
-to get a comma, or the beginning of the link. that delimits the
-keyword we clicked on. We also strip the text properties."
- (interactive)
- (let* ((object (org-element-context))
- (link-string (org-element-property :path object)))
- ;; you may click on the part before the citations. here we make
- ;; sure to move to the beginning so you get the first citation.
- (let ((cp (point)))
- (goto-char (org-element-property :begin object))
- (search-forward link-string (org-element-property :end object))
- (goto-char (match-beginning 0))
- ;; check if we clicked before the path and move as needed.
- (unless (< cp (point))
- (goto-char cp)))
-
- (if (not (org-element-property :contents-begin object))
- ;; this means no description in the link
- (progn
- ;; we need the link path start and 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)))
-
- ;; The key is the text between commas, or the link boundaries
- (save-excursion
- (if (search-forward "," link-string-end t 1)
- (setq key-end (- (match-end 0) 1)) ; we found a match
- (setq key-end link-string-end))) ; no comma found so take the end
- ;; and backward to previous comma from point which defines the start character
- (save-excursion
- (if (search-backward "," link-string-beginning 1 1)
- (setq key-beginning (+ (match-beginning 0) 1)) ; we found a match
- (setq key-beginning link-string-beginning))) ; no match found
- ;; save the key we clicked on.
- (setq bibtex-key (org-ref-strip-string (buffer-substring key-beginning key-end)))
- (set-text-properties 0 (length bibtex-key) nil bibtex-key)
- bibtex-key)
- ;; link with description. assume only one key
- link-string)))
-#+END_SRC
-
-We also need to find which bibliography file that key is in. For that, we need to know which bibliography files are referred to in the file. If none are specified with a bibliography link, we use the default bibliography. This function searches for a bibliography link, and then the LaTeX bibliography link. We also consider the addbibresource link which is used with biblatex.
-
-**** Getting the bibliographies
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-find-bibliography ()
- "find the bibliography in the buffer.
-This function sets and returns cite-bibliography-files, which is a list of files
-either from bibliography:f1.bib,f2.bib
-\bibliography{f1,f2}
-internal bibliographies
-
-falling back to what the user has set in org-ref-default-bibliography
-"
- (interactive)
- (catch 'result
- (save-excursion
- (goto-char (point-min))
- ;; look for a bibliography link
- (when (re-search-forward "\\<bibliography:\\([^\]\|\n]+\\)" nil t)
- (setq org-ref-bibliography-files
- (mapcar 'org-ref-strip-string (split-string (match-string 1) ",")))
- (throw 'result org-ref-bibliography-files))
-
-
- ;; we did not find a bibliography link. now look for \bibliography
- (goto-char (point-min))
- (when (re-search-forward "\\\\bibliography{\\([^}]+\\)}" nil t)
- ;; split, and add .bib to each file
- (setq org-ref-bibliography-files
- (mapcar (lambda (x) (concat x ".bib"))
- (mapcar 'org-ref-strip-string
- (split-string (match-string 1) ","))))
- (throw 'result org-ref-bibliography-files))
-
- ;; no bibliography found. maybe we need a biblatex addbibresource
- (goto-char (point-min))
- ;; look for a bibliography link
- (when (re-search-forward "addbibresource:\\([^\]\|\n]+\\)" nil t)
- (setq org-ref-bibliography-files
- (mapcar 'org-ref-strip-string (split-string (match-string 1) ",")))
- (throw 'result org-ref-bibliography-files))
-
- ;; we did not find anything. use defaults
- (setq org-ref-bibliography-files org-ref-default-bibliography)))
-
- ;; set reftex-default-bibliography so we can search
- (set (make-local-variable 'reftex-default-bibliography) org-ref-bibliography-files)
- org-ref-bibliography-files)
-#+END_SRC
-
-**** Finding the bibliography file a key is in
-Now, we can see if an entry is in a file.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-key-in-file-p (key filename)
- "determine if the key is in the file"
- (interactive "skey: \nsFile: ")
- (save-current-buffer
- (let ((bibtex-files (list filename)))
- (bibtex-search-entry key t))))
-#+END_SRC
-
-Finally, we want to know which file the key is in.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-bibtex-key-and-file (&optional key)
- "returns the bibtex key and file that it is in. If no key is provided, get one under point"
- (interactive)
- (let ((org-ref-bibliography-files (org-ref-find-bibliography))
- (file))
- (unless key
- (setq key (org-ref-get-bibtex-key-under-cursor)))
- (setq file (catch 'result
- (loop for file in org-ref-bibliography-files do
- (if (org-ref-key-in-file-p key (file-truename file))
- (throw 'result file)))))
- (cons key file)))
-#+END_SRC
-
-**** convenience functions to act on citation at point
- :PROPERTIES:
- :ID: af0b2a82-a7c9-4c08-9dac-09f93abc4a92
- :END:
-We need some convenience functions to open act on the citation at point. These will get the pdf, open the url, or open the notes.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-open-pdf-at-point ()
- "open the pdf for bibtex key under point if it exists"
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (pdf-file (format (concat org-ref-pdf-directory "%s.pdf") key)))
- (if (file-exists-p pdf-file)
- (org-open-file pdf-file)
-(message "no pdf found for %s" key))))
-
-
-(defun org-ref-open-url-at-point ()
- "open the url for bibtex key under point."
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- ;; I like this better than bibtex-url which does not always find
- ;; the urls
- (catch 'done
- (let ((url (bibtex-autokey-get-field "url")))
- (when url
- (browse-url url)
- (throw 'done nil)))
-
- (let ((doi (bibtex-autokey-get-field "doi")))
- (when doi
- (if (string-match "^http" doi)
- (browse-url doi)
- (browse-url (format "http://dx.doi.org/%s" doi)))
- (throw 'done nil))))))))
-
-
-(defun org-ref-open-notes-at-point ()
- "open the notes for bibtex key under point."
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (org-ref-open-bibtex-notes)))))
-
-
-(defun org-ref-citation-at-point ()
- "give message of current citation at point"
- (interactive)
- (let* ((cb (current-buffer))
- (results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (message "%s" (progn
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (org-ref-bib-citation))))))
-
-
-(defun org-ref-open-citation-at-point ()
- "open bibtex file to key at point"
- (interactive)
- (let* ((cb (current-buffer))
- (results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (find-file bibfile)
- (bibtex-search-entry key)))
-#+END_SRC
-
-**** the actual minibuffer menu
-Now, we create the menu. This is a rewrite of the cite action. This makes the function extendable by users.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defvar org-ref-cite-menu-funcs '()
- "Functions to run on cite click menu. Each entry is a list of (key menu-name function).
-The function must take no arguments and work on the key at point. Do not modify this variable, it is set to empty in the menu click function, and functions are conditionally added to it.")
-
-
-(defvar org-ref-user-cite-menu-funcs
- '(("C" "rossref" org-ref-crossref-at-point)
- ("y" "Copy entry to file" org-ref-copy-entry-at-point-to-file)
- ("s" "Copy summary" org-ref-copy-entry-as-summary))
- "user-defined functions to run on bibtex key at point.")
-
-
-(defun org-ref-copy-entry-as-summary ()
- "Copy the bibtex entry for the citation at point as a summary."
- (interactive)
- (save-window-excursion
- (org-ref-open-citation-at-point)
- (kill-new (org-ref-bib-citation))))
-
-
-(defun org-ref-copy-entry-at-point-to-file ()
- "Copy the bibtex entry for the citation at point to NEW-FILE.
-Prompt for NEW-FILE includes bib files in org-ref-default-bibliography, and bib files in current working directory. You can also specify a new file."
- (interactive)
- (let ((new-file (ido-completing-read
- "Copy to bibfile: "
- (append org-ref-default-bibliography
- (f-entries "." (lambda (f) (f-ext? f "bib"))))))
- (key (org-ref-get-bibtex-key-under-cursor)))
- (save-window-excursion
- (org-ref-open-citation-at-point)
- (bibtex-copy-entry-as-kill))
-
- (let ((bibtex-files (list (file-truename new-file))))
- (if (assoc key (bibtex-global-key-alist))
- (message "That key already exists in %s" new-file)
- ;; add to file
- (save-window-excursion
- (find-file new-file)
- (goto-char (point-max))
- ;; make sure we are at the beginning of a line.
- (unless (looking-at "^") (insert "\n\n"))
- (bibtex-yank)
- (save-buffer))))))
-
-
-(defun org-ref-get-doi-at-point ()
- "Get doi for key at point."
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results))
- doi)
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (setq doi (bibtex-autokey-get-field "doi"))
- ;; in case doi is a url, remove the url part.
- (replace-regexp-in-string "^http://dx.doi.org/" "" doi)))))
-
-
-;; functions that operate on key at point for click menu
-(defun org-ref-wos-at-point ()
- "open the doi in wos for bibtex key under point."
- (interactive)
- (doi-utils-wos (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-wos-citing-at-point ()
- "open the doi in wos citing articles for bibtex key under point."
- (interactive)
- (doi-utils-wos-citing (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-wos-related-at-point ()
- "open the doi in wos related articles for bibtex key under point."
- (interactive)
- (doi-utils-wos-related (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-google-scholar-at-point ()
- "open the doi in google scholar for bibtex key under point."
- (interactive)
- (doi-utils-google-scholar (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-pubmed-at-point ()
- "open the doi in pubmed for bibtex key under point."
- (interactive)
- (doi-utils-pubmed (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-crossref-at-point ()
- "open the doi in crossref for bibtex key under point."
- (interactive)
- (doi-utils-crossref (org-ref-get-doi-at-point)))
-
-
-(defun org-ref-cite-onclick-minibuffer-menu (&optional link-string)
- "action when a cite link is clicked on.
-Provides a menu of context sensitive actions. If the bibtex entry has a pdf, you get an option to open it. If there is a doi, you get a lot of options."
- (interactive)
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (pdf-file (format (concat org-ref-pdf-directory "%s.pdf") key))
- (bibfile (cdr results))
- (url (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (bibtex-autokey-get-field "url"))))
- (doi (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- ;; I like this better than bibtex-url which does not always find
- ;; the urls
- (bibtex-autokey-get-field "doi")))))
-
- (when (string= "" doi) (setq doi nil))
- (when (string= "" url) (setq url nil))
- (setq org-ref-cite-menu-funcs '())
-
- ;; open action
- (when
- bibfile
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("o" "pen" org-ref-open-citation-at-point)))
-
- ;; pdf
- (when (file-exists-p pdf-file)
- (add-to-list
- 'org-ref-cite-menu-funcs
- `("p" "df" ,org-ref-open-pdf-function) t))
-
- ;; notes
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("n" "otes" org-ref-open-notes-at-point) t)
-
- ;; url
- (when (or url doi)
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("u" "rl" org-ref-open-url-at-point) t))
-
- ;; doi funcs
- (when doi
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("w" "os" org-ref-wos-at-point) t)
-
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("c" "iting" org-ref-wos-citing-at-point) t)
-
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("r" "elated" org-ref-wos-related-at-point) t)
-
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("g" "oogle scholar" org-ref-google-scholar-at-point) t)
-
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("P" "ubmed" org-ref-pubmed-at-point) t))
-
- ;; add user functions
- (dolist (tup org-ref-user-cite-menu-funcs)
- (add-to-list
- 'org-ref-cite-menu-funcs
- tup t))
-
- ;; finally quit
- (add-to-list
- 'org-ref-cite-menu-funcs
- '("q" "uit" (lambda ())) t)
-
- ;; now we make a menu
- ;; construct menu string as a message
- (message
- (concat
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (org-ref-bib-citation))))
- "\n"
- (mapconcat
- (lambda (tup)
- (concat "[" (elt tup 0) "]"
- (elt tup 1) " "))
- org-ref-cite-menu-funcs "")))
- ;; get the input
- (let* ((input (read-char-exclusive))
- (choice (assoc
- (char-to-string input) org-ref-cite-menu-funcs)))
- ;; now run the function (2nd element in choice)
- (when choice
- (funcall
- (elt
- choice
- 2))))))