;;; org-ref-helm-bibtex.el --- Customization of helm-bibtex 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:
|
|
;; This file defines the completion engine for org-ref using `helm-bibtex'.
|
|
|
|
|
|
(declare-function 'org-ref-find-bibliography "org-ref-core.el")
|
|
(declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
|
|
|
|
(defvar org-ref-get-pdf-filename-function)
|
|
(defvar org-ref-default-citation-link)
|
|
(defvar org-ref-cite-types)
|
|
(defvar org-ref-insert-link-function)
|
|
(defvar org-ref-insert-cite-function)
|
|
(defvar org-ref-insert-label-function)
|
|
(defvar org-ref-insert-ref-function)
|
|
(defvar org-ref-cite-onclick-function)
|
|
(defvar org-ref-insert-cite-key)
|
|
|
|
;;; Code:
|
|
(require 'helm-config)
|
|
(require 'helm)
|
|
(require 'helm-bibtex)
|
|
(require 'helm-utils)
|
|
(require 'org-ref-helm)
|
|
(require 'async)
|
|
(require 'package)
|
|
|
|
;;;###autoload
|
|
(defun org-ref-bibtex-completion-completion ()
|
|
"Use helm and ‘helm-bibtex’ for completion."
|
|
(interactive)
|
|
;; Define core functions for org-ref
|
|
(setq org-ref-insert-link-function 'org-ref-helm-insert-cite-link
|
|
org-ref-insert-cite-function 'org-ref-helm-insert-cite-link
|
|
org-ref-insert-label-function 'org-ref-helm-insert-label-link
|
|
org-ref-insert-ref-function 'org-ref-helm-insert-ref-link
|
|
org-ref-cite-onclick-function 'org-ref-cite-click-helm))
|
|
|
|
(org-ref-bibtex-completion-completion)
|
|
(define-key org-mode-map
|
|
(kbd org-ref-insert-cite-key)
|
|
org-ref-insert-link-function)
|
|
|
|
(defcustom org-ref-bibtex-completion-actions
|
|
'(("Insert citation" . helm-bibtex-insert-citation)
|
|
("Open PDF, URL or DOI" . helm-bibtex-open-any)
|
|
("Open URL or DOI in browser" . helm-bibtex-open-url-or-doi)
|
|
("Insert reference" . helm-bibtex-insert-reference)
|
|
("Insert BibTeX key" . helm-bibtex-insert-key)
|
|
("Insert BibTeX entry" . helm-bibtex-insert-bibtex)
|
|
("Insert formatted citation(s)" . (lambda (_)
|
|
(insert
|
|
(mapconcat 'identity
|
|
(cl-loop for key in (helm-marked-candidates)
|
|
collect (org-ref-format-entry key))
|
|
"\n\n"))))
|
|
("Attach PDF to email" . helm-bibtex-add-PDF-attachment)
|
|
("Edit notes" . helm-bibtex-edit-notes)
|
|
("Show entry" . helm-bibtex-show-entry)
|
|
("Add keywords to entries" . org-ref-helm-tag-entries)
|
|
("Copy entry to clipboard" . bibtex-completion-copy-candidate)
|
|
("Add PDF to library" . helm-bibtex-add-pdf-to-library))
|
|
"Cons cells of string and function to set the actions of `helm-bibtex' to.
|
|
The car of cons cell is the string describing the function.
|
|
The cdr of the the cons cell is the function to use."
|
|
:type '(alist :key-type (string) :value-type (function))
|
|
:group 'org-ref)
|
|
|
|
|
|
(cl-loop for i from 0 to (length org-ref-bibtex-completion-actions)
|
|
for ccell in org-ref-bibtex-completion-actions
|
|
do
|
|
(helm-delete-action-from-source (car ccell) helm-source-bibtex)
|
|
(helm-add-action-to-source
|
|
(car ccell)
|
|
(cdr ccell)
|
|
helm-source-bibtex))
|
|
|
|
|
|
(defcustom org-ref-bibtex-completion-format-org
|
|
'org-ref-bibtex-completion-format-org
|
|
"Function for how `helm-bibtex' inserts citations."
|
|
:type 'function
|
|
:group 'org-ref)
|
|
|
|
|
|
(setf (cdr (assoc 'org-mode bibtex-completion-format-citation-functions))
|
|
org-ref-bibtex-completion-format-org)
|
|
|
|
|
|
(setq org-ref-insert-cite-function 'org-ref-helm-insert-cite-link
|
|
org-ref-cite-onclick-function 'org-ref-cite-click-helm)
|
|
|
|
|
|
;;* Helm bibtex setup.
|
|
(setq bibtex-completion-additional-search-fields '(keywords))
|
|
|
|
(setq bibtex-completion-display-formats
|
|
'((t . "${author:36} ${title:*} ${year:4} ${=has-pdf=:1}${=has-note=:1} ${=type=:7} ${keywords:31}")))
|
|
|
|
(defun bibtex-completion-copy-candidate (_candidate)
|
|
"Copy the selected bibtex entries to the clipboard.
|
|
Used as a new action in `helm-bibtex'.
|
|
CANDIDATE is ignored."
|
|
(with-temp-buffer
|
|
(bibtex-mode)
|
|
(mapc #'insert-file-contents
|
|
(-flatten (list bibtex-completion-bibliography)))
|
|
|
|
(let ((entries '()))
|
|
(cl-loop for bibtex-key in (helm-marked-candidates)
|
|
do
|
|
(goto-char (point-min))
|
|
(re-search-forward (concat "^@\\(" parsebib--bibtex-identifier
|
|
"\\)[[:space:]]*[\(\{][[:space:]]*"
|
|
(regexp-quote bibtex-key)
|
|
"[[:space:]]*,"))
|
|
|
|
(cl-pushnew (buffer-substring
|
|
(bibtex-beginning-of-entry)
|
|
(bibtex-end-of-entry))
|
|
entries))
|
|
|
|
(with-temp-buffer
|
|
(dolist (entry entries)
|
|
(insert (format "%s\n\n" entry)))
|
|
(kill-new (buffer-string))))))
|
|
|
|
|
|
|
|
(defun org-ref-helm-tag-entries (_candidates)
|
|
"Set tags on selected bibtex entries from `helm-bibtex'.
|
|
User is prompted for tags. This function is called from `helm-bibtex'.
|
|
Argument CANDIDATES helm candidates."
|
|
(message "")
|
|
(let* ((keys (helm-marked-candidates))
|
|
(entry (bibtex-completion-get-entry (car keys)))
|
|
(field (cdr (assoc-string "keywords" entry)))
|
|
(value (when field (replace-regexp-in-string "^{\\|}$" "" field)))
|
|
(keywords (read-string "Keywords (comma separated): "
|
|
(when (and value (not (equal "" value)))
|
|
(concat value ", ")))))
|
|
(cl-loop for key in keys
|
|
do
|
|
(save-window-excursion
|
|
(bibtex-completion-show-entry (list key))
|
|
;; delete keyword field if empty
|
|
(if (equal "" keywords)
|
|
(save-restriction
|
|
(bibtex-narrow-to-entry)
|
|
(goto-char (car (cdr (bibtex-search-forward-field "keywords" t))))
|
|
(bibtex-kill-field))
|
|
(bibtex-set-field
|
|
"keywords"
|
|
(concat
|
|
(if (listp keywords)
|
|
(if (string-match value keywords)
|
|
(and (replace-match "")
|
|
(mapconcat 'identity keywords ", "))
|
|
(mapconcat 'identity keywords ", "))
|
|
;; remove trailing comma
|
|
(replace-regexp-in-string ", $" "" keywords)))))
|
|
(save-buffer)))))
|
|
|
|
|
|
(defun org-ref-bibtex-completion-format-org (keys)
|
|
"Insert selected KEYS as cite link.
|
|
Append KEYS if you are on a link.
|
|
|
|
Technically, this function should return a string that is
|
|
inserted by helm. This function does the insertion and gives helm
|
|
an empty string to insert. This lets us handle appending to a
|
|
link properly.
|
|
|
|
In the `helm-bibtex' buffer, \\[universal-argument] will give you a helm menu to
|
|
select a new link type for the selected entries.
|
|
|
|
A double \\[universal-argument] \\[universal-argument] will
|
|
change the key at point to the selected keys."
|
|
(let* ((object (org-element-context))
|
|
(last-char (save-excursion
|
|
(when (org-element-property :end object)
|
|
(goto-char (org-element-property :end object))
|
|
(unless (bobp)
|
|
(backward-char))
|
|
(if (looking-at " ")
|
|
" "
|
|
"")))))
|
|
(cond
|
|
;; case where we are in a link
|
|
((and (equal (org-element-type object) 'link)
|
|
(-contains?
|
|
org-ref-cite-types
|
|
(org-element-property :type object)))
|
|
(cond
|
|
;; no prefix. insert or append keys
|
|
((equal helm-current-prefix-arg nil)
|
|
(cond
|
|
;; point after :
|
|
((looking-back ":" (- (point) 2))
|
|
(insert (concat (mapconcat 'identity keys ",") ",")))
|
|
;; point on :
|
|
((looking-at ":")
|
|
(forward-char)
|
|
(insert (concat (mapconcat 'identity keys ",") ",")))
|
|
;; point on the cite type
|
|
((-contains? org-ref-cite-types (thing-at-point 'word))
|
|
(re-search-forward ":")
|
|
(insert (concat (mapconcat 'identity keys ",") ",")))
|
|
;; after ,
|
|
((looking-back "," (- (point) 2))
|
|
(insert (concat (mapconcat 'identity keys ",") ",")))
|
|
;; on comma
|
|
((looking-at ",")
|
|
(forward-char)
|
|
(insert (concat (mapconcat 'identity keys ",") ",")))
|
|
;; somewhere in the middle or end
|
|
(t
|
|
;; goto next comma or end
|
|
(re-search-forward
|
|
","
|
|
(org-element-property :end object) 'mv)
|
|
(skip-chars-backward " ")
|
|
(skip-chars-backward "]")
|
|
(unless (looking-at ",") (insert ","))
|
|
(insert (mapconcat 'identity keys ",")))))
|
|
;; double prefix, replace key at point
|
|
((equal helm-current-prefix-arg '(16))
|
|
(setf (buffer-substring
|
|
(org-element-property :begin object)
|
|
(org-element-property :end object))
|
|
(concat
|
|
(replace-regexp-in-string
|
|
(car (org-ref-get-bibtex-key-and-file)) ; key
|
|
(mapconcat 'identity keys ",") ; new keys
|
|
(org-element-property :raw-link object))
|
|
;; replace space at end to avoid collapsing into next word.
|
|
last-char))
|
|
;; and we want to go to the end of the new link
|
|
(goto-char
|
|
(org-element-property :end (org-element-context))))
|
|
(t
|
|
(message "Not found"))))
|
|
|
|
;; We are next to a link, and we want to append
|
|
;; next to a link means one character back is on a link.
|
|
((save-excursion
|
|
(unless (bobp) (backward-char))
|
|
(and (equal (org-element-type (org-element-context)) 'link)
|
|
(-contains?
|
|
org-ref-cite-types
|
|
(org-element-property :type (org-element-context)))))
|
|
(skip-chars-backward " ")
|
|
(insert (concat "," (mapconcat 'identity keys ","))))
|
|
|
|
;; insert fresh link
|
|
(t
|
|
(insert
|
|
(concat
|
|
(when org-ref-prefer-bracket-links "[[")
|
|
(if (equal helm-current-prefix-arg '(4))
|
|
(helm :sources `((name . "link types")
|
|
(candidates . ,org-ref-cite-types)
|
|
(action . (lambda (x) x))))
|
|
org-ref-default-citation-link)
|
|
":"
|
|
(s-join "," keys)
|
|
(when org-ref-prefer-bracket-links "]]"))))))
|
|
;; return empty string for helm
|
|
"")
|
|
|
|
|
|
(defun org-ref-format-citation (keys)
|
|
"Formatter for org-ref citation commands.
|
|
Prompt for the command and additional arguments if the commands can
|
|
take any. If point is inside a citation link, append KEYS. Otherwise
|
|
prompt for pre/post text. Prompts can also be switched off by setting
|
|
the variable `bibtex-completion-cite-prompt-for-optional-arguments' to
|
|
nil. To enable this formatter, add it to
|
|
`bibtex-completion-format-citation-functions'. For example:
|
|
|
|
\(setf (cdr (assoc 'org-mode bibtex-completion-format-citation-functions)) 'org-ref-format-citation)
|
|
|
|
Note also that pre text is preceded by a double colon, for example:
|
|
|
|
\[[cite:key][See::Chapter 1]], which exports to:
|
|
|
|
\\cite[See][Chapter 1]{key}."
|
|
;; Check if point is inside a cite link
|
|
(let ((link (org-element-context))
|
|
end path)
|
|
(if (-contains? org-ref-cite-types (org-element-property :type link))
|
|
(progn
|
|
(setq end (org-element-property :end link)
|
|
path (org-element-property :path link))
|
|
(goto-char end)
|
|
(skip-chars-backward " ")
|
|
;; Check if link has pre/post text
|
|
(if (looking-back "\]" (line-beginning-position))
|
|
(progn
|
|
(re-search-backward path nil t)
|
|
(re-search-forward "\]" nil t)
|
|
(backward-char 1)
|
|
(format ",%s" (s-join "," keys))))
|
|
(format ",%s" (s-join "," keys)))
|
|
(let* ((initial (when bibtex-completion-cite-default-as-initial-input bibtex-completion-cite-default-command))
|
|
(default
|
|
(unless bibtex-completion-cite-default-as-initial-input bibtex-completion-cite-default-command))
|
|
(default-info
|
|
(if default (format " (default \"%s\")" default) ""))
|
|
(cite-command
|
|
(completing-read (format "Cite command%s: " default-info)
|
|
bibtex-completion-cite-commands nil nil initial
|
|
'bibtex-completion-cite-command-history default nil)))
|
|
(if (member cite-command '("nocite" "supercite")) ; These don't want arguments.
|
|
(format "%s:%s" cite-command (s-join "," keys))
|
|
(let ((text (if bibtex-completion-cite-prompt-for-optional-arguments
|
|
(read-from-minibuffer "Pre/post text: ")
|
|
"")))
|
|
(if (string= "" text)
|
|
(format "%s:%s" cite-command (s-join "," keys))
|
|
(format "[[%s:%s][%s]]" cite-command (s-join "," keys) text))))))))
|
|
|
|
|
|
(defvar bibtex-completion-cached-candidates)
|
|
(defvar bibtex-completion-bibliography-hash)
|
|
|
|
;;;###autoload
|
|
(defun org-ref-helm-load-completions-async ()
|
|
"Load the bibtex files into helm sources asynchronously.
|
|
For large bibtex files, the initial call to ‘org-ref-helm-insert-cite-link’
|
|
can take a long time to load the completion sources. This function loads
|
|
the completion sources in the background so the initial call to ‘org-ref-helm-insert-cite-link’ is much faster."
|
|
(interactive)
|
|
(async-start
|
|
`(lambda (&optional formatter)
|
|
(require 'package)
|
|
(package-initialize)
|
|
(require 'helm-bibtex)
|
|
,(async-inject-variables "bibtex-compl.*")
|
|
|
|
(with-temp-buffer
|
|
(mapc #'insert-file-contents
|
|
(-flatten (list bibtex-completion-bibliography)))
|
|
;; Check hash of bibliography and reparse if necessary:
|
|
(let ((bibliography-hash (secure-hash 'sha256 (current-buffer))))
|
|
(unless (and bibtex-completion-cached-candidates
|
|
(string= bibtex-completion-bibliography-hash bibliography-hash))
|
|
(message "Loading bibliography ...")
|
|
(let* ((entries (bibtex-completion-parse-bibliography))
|
|
(entries (bibtex-completion-resolve-crossrefs entries))
|
|
(entries (bibtex-completion-prepare-entries entries))
|
|
(entries (nreverse entries))
|
|
(entries
|
|
(--map (cons (bibtex-completion-clean-string
|
|
(s-join " " (-map #'cdr it))) it)
|
|
entries)))
|
|
(setq bibtex-completion-cached-candidates
|
|
(if (functionp formatter)
|
|
(funcall formatter entries)
|
|
entries)))
|
|
(setq bibtex-completion-bibliography-hash bibliography-hash))
|
|
(cons bibliography-hash bibtex-completion-cached-candidates))))
|
|
(lambda (result)
|
|
(setq bibtex-completion-cached-candidates (cdr result))
|
|
(setq bibtex-completion-bibliography-hash (car result))
|
|
(message "Finished loading org-ref completions"))))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
(defun org-ref-helm-insert-cite-link (&optional arg)
|
|
"Insert a citation link with `helm-bibtex'.
|
|
With one prefix ARG, insert a ref link.
|
|
With two prefix ARGs, insert a label link."
|
|
(interactive "P")
|
|
;; save all bibtex buffers so we get the most up-to-date selection. I find
|
|
;; that I often edit a bibliography and forget to save it, so the newest entry
|
|
;; does not show in helm-bibtex.
|
|
(org-ref-save-all-bibtex-buffers)
|
|
(cond
|
|
((equal arg nil)
|
|
(let ((bibtex-completion-bibliography (org-ref-find-bibliography)))
|
|
(helm-bibtex)))
|
|
((equal arg '(4))
|
|
(org-ref-helm-insert-ref-link))
|
|
((equal arg '(16))
|
|
(org-ref-helm-insert-label-link))))
|
|
|
|
|
|
;; add our own fallback entries where we want them. These appear in reverse
|
|
;; order of adding in the menu
|
|
(setq bibtex-completion-fallback-options
|
|
(-insert-at 1 '("Crossref" . "http://search.crossref.org/?q=%s") bibtex-completion-fallback-options))
|
|
|
|
|
|
(setq bibtex-completion-fallback-options
|
|
(-insert-at
|
|
1
|
|
'("Scopus" . "http://www.scopus.com/scopus/search/submit/xadvanced.url?searchfield=TITLE-ABS-KEY(%s)")
|
|
bibtex-completion-fallback-options))
|
|
|
|
|
|
(setq bibtex-completion-fallback-options
|
|
(-insert-at 1 '("WOS" . "http://gateway.webofknowledge.com/gateway/Gateway.cgi?topic=%s&GWVersion=2&SrcApp=WEB&SrcAuth=HSB&DestApp=UA&DestLinkType=GeneralSearchSummary") bibtex-completion-fallback-options))
|
|
|
|
(defun org-ref-cite-candidates ()
|
|
"Generate the list of possible candidates for click actions on a cite link.
|
|
Checks for pdf and doi, and add appropriate functions."
|
|
(let* ((results (org-ref-get-bibtex-key-and-file))
|
|
(key (car results))
|
|
(bibfile (cdr results))
|
|
(bibtex-completion-bibliography (list bibfile))
|
|
(entry (bibtex-completion-get-entry key))
|
|
(pdf-file (funcall org-ref-get-pdf-filename-function key))
|
|
(pdf-bibtex-completion (car (bibtex-completion-find-pdf key)))
|
|
(notes-p (cdr (assoc "=has-note=" entry)))
|
|
(url (save-excursion
|
|
(with-temp-buffer
|
|
(insert-file-contents bibfile)
|
|
(bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
|
|
(bibtex-search-entry key)
|
|
(bibtex-autokey-get-field "url"))))
|
|
(doi (save-excursion
|
|
(with-temp-buffer
|
|
(insert-file-contents bibfile)
|
|
(bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
|
|
(bibtex-search-entry key)
|
|
;; I like this better than bibtex-url which does not always find
|
|
;; the urls
|
|
(bibtex-autokey-get-field "doi"))))
|
|
(candidates `(("Quit" . org-ref-citation-at-point)
|
|
("Open bibtex entry" . org-ref-open-citation-at-point))))
|
|
;; for some reason, when there is no doi or url, they are returned as "". I
|
|
;; prefer nil so we correct this here.
|
|
(when (string= doi "") (setq doi nil))
|
|
(when (string= url "") (setq url nil))
|
|
|
|
;; Conditional pdf functions
|
|
;; try with org-ref first
|
|
(cond ((file-exists-p pdf-file)
|
|
(cl-pushnew
|
|
'("Open pdf" . (lambda ()
|
|
(funcall org-ref-open-pdf-function)))
|
|
candidates))
|
|
|
|
;; try with bibtex-completion
|
|
(pdf-bibtex-completion
|
|
(cl-pushnew
|
|
'("Open pdf" . (lambda ()
|
|
(funcall org-ref-open-pdf-function)))
|
|
candidates))
|
|
|
|
;; try with doi
|
|
(t
|
|
(cl-pushnew
|
|
'("Try to get pdf" . (lambda ()
|
|
(save-window-excursion
|
|
(org-ref-open-citation-at-point)
|
|
(bibtex-beginning-of-entry)
|
|
(doi-utils-get-bibtex-entry-pdf))))
|
|
candidates)))
|
|
|
|
(if notes-p
|
|
(cl-pushnew
|
|
'("Open notes" . org-ref-open-notes-at-point)
|
|
candidates)
|
|
(cl-pushnew
|
|
'("Add notes" . org-ref-open-notes-at-point)
|
|
candidates))
|
|
|
|
;; conditional url and doi functions
|
|
(when (or url doi)
|
|
(cl-pushnew
|
|
'("Open in browser" . org-ref-open-url-at-point)
|
|
candidates))
|
|
|
|
(when doi
|
|
(mapc (lambda (x)
|
|
(cl-pushnew x candidates))
|
|
`(("WOS" . org-ref-wos-at-point)
|
|
("Related articles in WOS" . org-ref-wos-related-at-point)
|
|
("Citing articles in WOS" . org-ref-wos-citing-at-point)
|
|
("Google Scholar" . org-ref-google-scholar-at-point)
|
|
("Pubmed" . org-ref-pubmed-at-point)
|
|
("Crossref" . org-ref-crossref-at-point))))
|
|
|
|
(cl-pushnew
|
|
'("Insert new citation" . (lambda ()
|
|
(org-ref-helm-insert-cite-link nil)))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Delete key at point" . org-ref-delete-key-at-point)
|
|
candidates)
|
|
|
|
;; This is kind of clunky. We store the key at point. Add the new ref. Get
|
|
;; it off the end, and put it in the original position.
|
|
(cl-pushnew
|
|
'("Replace key at point" . org-ref-replace-key-at-point)
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Delete citation at point" . org-ref-delete-cite-at-point)
|
|
candidates)
|
|
|
|
(when bibtex-completion-cite-prompt-for-optional-arguments
|
|
(cl-pushnew
|
|
'("Update pre/post text" . org-ref-update-pre-post-text)
|
|
candidates))
|
|
|
|
(cl-pushnew
|
|
'("Sort keys by year" . org-ref-sort-citation-link)
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Copy formatted citation to clipboard" . org-ref-copy-cite-as-summary)
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Copy key to clipboard" . (lambda ()
|
|
(kill-new
|
|
(car (org-ref-get-bibtex-key-and-file)))))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Copy bibtex entry to file" . org-ref-copy-entry-at-point-to-file)
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Email bibtex entry and pdf" . (lambda ()
|
|
(save-excursion
|
|
(org-ref-open-citation-at-point)
|
|
(org-ref-email-bibtex-entry))))
|
|
candidates)
|
|
|
|
;; add Scopus functions. These work by looking up a DOI to get a Scopus
|
|
;; EID. This may only work for Scopus articles. Not all DOIs are recognized
|
|
;; in the Scopus API. We only load these if you have defined a
|
|
;; `*scopus-api-key*', which is required to do the API queries. See
|
|
;; `scopus'. These functions are appended to the candidate list.
|
|
(when (and (boundp '*scopus-api-key*) *scopus-api-key*)
|
|
(cl-pushnew
|
|
'("Open in Scopus" . (lambda ()
|
|
(let ((eid (scopus-doi-to-eid (org-ref-get-doi-at-point))))
|
|
(if eid
|
|
(scopus-open-eid eid)
|
|
(message "No EID found.")))))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Scopus citing articles" . (lambda ()
|
|
(let ((url (scopus-citing-url
|
|
(org-ref-get-doi-at-point))))
|
|
(if url
|
|
(browse-url url)
|
|
(message "No url found.")))))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Scopus related by authors" . (lambda ()
|
|
(let ((url (scopus-related-by-author-url
|
|
(org-ref-get-doi-at-point))))
|
|
(if url
|
|
(browse-url url)
|
|
(message "No url found.")))))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Scopus related by references" . (lambda ()
|
|
(let ((url (scopus-related-by-references-url
|
|
(org-ref-get-doi-at-point))))
|
|
(if url
|
|
(browse-url url)
|
|
(message "No url found.")))))
|
|
candidates)
|
|
|
|
(cl-pushnew
|
|
'("Scopus related by keywords" . (lambda ()
|
|
(let ((url (scopus-related-by-keyword-url
|
|
(org-ref-get-doi-at-point))))
|
|
(if url
|
|
(browse-url url)
|
|
(message "No url found.")))))
|
|
candidates))
|
|
|
|
;; finally return a numbered list of the candidates
|
|
(cl-loop for i from 0
|
|
for cell in (reverse candidates)
|
|
collect (cons (format "%2s. %s" i (car cell))
|
|
(cdr cell)))))
|
|
|
|
|
|
(defvar org-ref-helm-user-candidates '()
|
|
"List of user-defined candidates to act when clicking on a cite link.
|
|
This is a list of cons cells '((\"description\" . action)). The
|
|
action function should not take an argument, and should assume
|
|
point is on the cite key of interest.")
|
|
|
|
;; example of adding your own function
|
|
(add-to-list
|
|
'org-ref-helm-user-candidates
|
|
'("Open pdf in emacs" . (lambda ()
|
|
(find-file
|
|
(concat
|
|
(file-name-as-directory org-ref-pdf-directory)
|
|
(car (org-ref-get-bibtex-key-and-file))
|
|
".pdf"))))
|
|
t)
|
|
|
|
;;;###autoload
|
|
(defun org-ref-cite-click-helm (_key)
|
|
"Open helm for actions on a cite link.
|
|
subtle points.
|
|
|
|
1. get name and candidates before entering helm because we need
|
|
the org-buffer.
|
|
|
|
2. switch back to the org buffer before evaluating the
|
|
action. most of them need the point and buffer.
|
|
|
|
KEY is returned for the selected item(s) in helm."
|
|
(interactive)
|
|
(let ((name (org-ref-format-entry (org-ref-get-bibtex-key-under-cursor)))
|
|
(candidates (org-ref-cite-candidates))
|
|
(cb (current-buffer)))
|
|
|
|
(helm :sources `(((name . ,name)
|
|
(candidates . ,candidates)
|
|
(action . (lambda (f)
|
|
(switch-to-buffer ,cb)
|
|
(funcall f))))
|
|
((name . "User functions")
|
|
(candidates . ,org-ref-helm-user-candidates)
|
|
(action . (lambda (f)
|
|
(switch-to-buffer ,cb)
|
|
(funcall f))))))))
|
|
|
|
|
|
;; browse labels
|
|
|
|
(defun org-ref-browser-label-source ()
|
|
(let ((labels (org-ref-get-labels)))
|
|
(helm-build-sync-source "Browse labels"
|
|
:follow 1
|
|
:candidates labels
|
|
:action '(("Browse labels" . (lambda (label)
|
|
(with-selected-window (selected-window)
|
|
(org-open-link-from-string
|
|
(format "ref:%s" label)))))))))
|
|
|
|
;; browse citation links
|
|
|
|
(defun org-ref-browser-transformer (candidates)
|
|
"Add counter to candidates."
|
|
(let ((counter 0))
|
|
(cl-loop for i in candidates
|
|
collect (format "%s %s" (cl-incf counter) i))))
|
|
|
|
(defun org-ref-browser-display (candidate)
|
|
"Strip counter from candidates."
|
|
(replace-regexp-in-string "^[0-9]+? " "" candidate))
|
|
|
|
;;;###autoload
|
|
(defun org-ref-browser (&optional arg)
|
|
"Quickly browse label links in helm.
|
|
With a prefix ARG, browse citation links."
|
|
(interactive "P")
|
|
(if arg
|
|
(let ((keys nil)
|
|
(alist nil))
|
|
(widen)
|
|
(outline-show-all)
|
|
(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))
|
|
(let ((start (org-element-property :begin link)))
|
|
(dolist (key
|
|
(org-ref-split-and-strip-string (plist-get plist ':path)))
|
|
(setq keys (append keys (list key)))
|
|
(setq alist (append alist (list (cons key start))))))))))
|
|
(let ((counter 0)
|
|
count-key-pos)
|
|
;; the idea here is to create an alist with ("counter key" .
|
|
;; position) to produce unique candidates
|
|
(setq count-key-pos (mapcar (lambda (x)
|
|
(cons
|
|
(format "%s %s" (cl-incf counter) (car x)) (cdr x)))
|
|
alist))
|
|
;; push mark to restore position with C-u C-SPC
|
|
(push-mark (point))
|
|
;; move point to the first citation link in the buffer
|
|
(goto-char (cdr (assoc (caar alist) alist)))
|
|
(helm :sources
|
|
(helm-build-sync-source "Browse citation links"
|
|
:follow 1
|
|
:candidates keys
|
|
:candidate-transformer 'org-ref-browser-transformer
|
|
:real-to-display 'org-ref-browser-display
|
|
:persistent-action (lambda (candidate)
|
|
(helm-goto-char
|
|
(cdr (assoc candidate count-key-pos))))
|
|
:action `(("Open menu" . ,(lambda (candidate)
|
|
(helm-goto-char
|
|
(cdr (assoc candidate count-key-pos)))
|
|
(org-open-at-point)))))
|
|
:candidate-number-limit 10000
|
|
:buffer "*helm browser*")))
|
|
(helm :sources (org-ref-browser-label-source)
|
|
:buffer "*helm labels*")))
|
|
|
|
|
|
|
|
(provide 'org-ref-helm-bibtex)
|
|
;;; org-ref-helm-bibtex.el ends here
|