733 lines
27 KiB
EmacsLisp
733 lines
27 KiB
EmacsLisp
;;; 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
|
||
(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:]]*,"))
|
||
(bibtex-mark-entry)
|
||
(cl-pushnew (buffer-substring (point) (mark)) 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 bibtext files, the intial 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-entry-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
|