Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

736 lines
27 KiB

5 years ago
  1. ;;; org-ref-helm-bibtex.el --- Customization of helm-bibtex for org-ref -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2016 John Kitchin
  3. ;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
  4. ;; Keywords:
  5. ;; This program is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; This file defines the completion engine for org-ref using `helm-bibtex'.
  17. (declare-function 'org-ref-find-bibliography "org-ref-core.el")
  18. (declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
  19. (defvar org-ref-get-pdf-filename-function)
  20. (defvar org-ref-default-citation-link)
  21. (defvar org-ref-cite-types)
  22. (defvar org-ref-insert-link-function)
  23. (defvar org-ref-insert-cite-function)
  24. (defvar org-ref-insert-label-function)
  25. (defvar org-ref-insert-ref-function)
  26. (defvar org-ref-cite-onclick-function)
  27. (defvar org-ref-insert-cite-key)
  28. ;;; Code:
  29. (require 'helm-config)
  30. (require 'helm)
  31. (require 'helm-bibtex)
  32. (require 'helm-utils)
  33. (require 'org-ref-helm)
  34. (require 'async)
  35. (require 'package)
  36. ;;;###autoload
  37. (defun org-ref-bibtex-completion-completion ()
  38. "Use helm and ‘helm-bibtex’ for completion."
  39. (interactive)
  40. ;; Define core functions for org-ref
  41. (setq org-ref-insert-link-function 'org-ref-helm-insert-cite-link
  42. org-ref-insert-cite-function 'org-ref-helm-insert-cite-link
  43. org-ref-insert-label-function 'org-ref-helm-insert-label-link
  44. org-ref-insert-ref-function 'org-ref-helm-insert-ref-link
  45. org-ref-cite-onclick-function 'org-ref-cite-click-helm))
  46. (org-ref-bibtex-completion-completion)
  47. (define-key org-mode-map
  48. (kbd org-ref-insert-cite-key)
  49. org-ref-insert-link-function)
  50. (defcustom org-ref-bibtex-completion-actions
  51. '(("Insert citation" . helm-bibtex-insert-citation)
  52. ("Open PDF, URL or DOI" . helm-bibtex-open-any)
  53. ("Open URL or DOI in browser" . helm-bibtex-open-url-or-doi)
  54. ("Insert reference" . helm-bibtex-insert-reference)
  55. ("Insert BibTeX key" . helm-bibtex-insert-key)
  56. ("Insert BibTeX entry" . helm-bibtex-insert-bibtex)
  57. ("Insert formatted citation(s)" . (lambda (_)
  58. (insert
  59. (mapconcat 'identity
  60. (cl-loop for key in (helm-marked-candidates)
  61. collect (org-ref-format-entry key))
  62. "\n\n"))))
  63. ("Attach PDF to email" . helm-bibtex-add-PDF-attachment)
  64. ("Edit notes" . helm-bibtex-edit-notes)
  65. ("Show entry" . helm-bibtex-show-entry)
  66. ("Add keywords to entries" . org-ref-helm-tag-entries)
  67. ("Copy entry to clipboard" . bibtex-completion-copy-candidate)
  68. ("Add PDF to library" . helm-bibtex-add-pdf-to-library))
  69. "Cons cells of string and function to set the actions of `helm-bibtex' to.
  70. The car of cons cell is the string describing the function.
  71. The cdr of the the cons cell is the function to use."
  72. :type '(alist :key-type (string) :value-type (function))
  73. :group 'org-ref)
  74. (cl-loop for i from 0 to (length org-ref-bibtex-completion-actions)
  75. for ccell in org-ref-bibtex-completion-actions
  76. do
  77. (helm-delete-action-from-source (car ccell) helm-source-bibtex)
  78. (helm-add-action-to-source
  79. (car ccell)
  80. (cdr ccell)
  81. helm-source-bibtex))
  82. (defcustom org-ref-bibtex-completion-format-org
  83. 'org-ref-bibtex-completion-format-org
  84. "Function for how `helm-bibtex' inserts citations."
  85. :type 'function
  86. :group 'org-ref)
  87. (setf (cdr (assoc 'org-mode bibtex-completion-format-citation-functions))
  88. org-ref-bibtex-completion-format-org)
  89. (setq org-ref-insert-cite-function 'org-ref-helm-insert-cite-link
  90. org-ref-cite-onclick-function 'org-ref-cite-click-helm)
  91. ;;* Helm bibtex setup.
  92. (setq bibtex-completion-additional-search-fields '(keywords))
  93. (setq bibtex-completion-display-formats
  94. '((t . "${author:36} ${title:*} ${year:4} ${=has-pdf=:1}${=has-note=:1} ${=type=:7} ${keywords:31}")))
  95. (defun bibtex-completion-copy-candidate (_candidate)
  96. "Copy the selected bibtex entries to the clipboard.
  97. Used as a new action in `helm-bibtex'.
  98. CANDIDATE is ignored."
  99. (with-temp-buffer
  100. (bibtex-mode)
  101. (mapc #'insert-file-contents
  102. (-flatten (list bibtex-completion-bibliography)))
  103. (let ((entries '()))
  104. (cl-loop for bibtex-key in (helm-marked-candidates)
  105. do
  106. (goto-char (point-min))
  107. (re-search-forward (concat "^@\\(" parsebib--bibtex-identifier
  108. "\\)[[:space:]]*[\(\{][[:space:]]*"
  109. (regexp-quote bibtex-key)
  110. "[[:space:]]*,"))
  111. (cl-pushnew (buffer-substring
  112. (bibtex-beginning-of-entry)
  113. (bibtex-end-of-entry))
  114. entries))
  115. (with-temp-buffer
  116. (dolist (entry entries)
  117. (insert (format "%s\n\n" entry)))
  118. (kill-new (buffer-string))))))
  119. (defun org-ref-helm-tag-entries (_candidates)
  120. "Set tags on selected bibtex entries from `helm-bibtex'.
  121. User is prompted for tags. This function is called from `helm-bibtex'.
  122. Argument CANDIDATES helm candidates."
  123. (message "")
  124. (let* ((keys (helm-marked-candidates))
  125. (entry (bibtex-completion-get-entry (car keys)))
  126. (field (cdr (assoc-string "keywords" entry)))
  127. (value (when field (replace-regexp-in-string "^{\\|}$" "" field)))
  128. (keywords (read-string "Keywords (comma separated): "
  129. (when (and value (not (equal "" value)))
  130. (concat value ", ")))))
  131. (cl-loop for key in keys
  132. do
  133. (save-window-excursion
  134. (bibtex-completion-show-entry (list key))
  135. ;; delete keyword field if empty
  136. (if (equal "" keywords)
  137. (save-restriction
  138. (bibtex-narrow-to-entry)
  139. (goto-char (car (cdr (bibtex-search-forward-field "keywords" t))))
  140. (bibtex-kill-field))
  141. (bibtex-set-field
  142. "keywords"
  143. (concat
  144. (if (listp keywords)
  145. (if (string-match value keywords)
  146. (and (replace-match "")
  147. (mapconcat 'identity keywords ", "))
  148. (mapconcat 'identity keywords ", "))
  149. ;; remove trailing comma
  150. (replace-regexp-in-string ", $" "" keywords)))))
  151. (save-buffer)))))
  152. (defun org-ref-bibtex-completion-format-org (keys)
  153. "Insert selected KEYS as cite link.
  154. Append KEYS if you are on a link.
  155. Technically, this function should return a string that is
  156. inserted by helm. This function does the insertion and gives helm
  157. an empty string to insert. This lets us handle appending to a
  158. link properly.
  159. In the `helm-bibtex' buffer, \\[universal-argument] will give you a helm menu to
  160. select a new link type for the selected entries.
  161. A double \\[universal-argument] \\[universal-argument] will
  162. change the key at point to the selected keys."
  163. (let* ((object (org-element-context))
  164. (last-char (save-excursion
  165. (when (org-element-property :end object)
  166. (goto-char (org-element-property :end object))
  167. (unless (bobp)
  168. (backward-char))
  169. (if (looking-at " ")
  170. " "
  171. "")))))
  172. (cond
  173. ;; case where we are in a link
  174. ((and (equal (org-element-type object) 'link)
  175. (-contains?
  176. org-ref-cite-types
  177. (org-element-property :type object)))
  178. (cond
  179. ;; no prefix. insert or append keys
  180. ((equal helm-current-prefix-arg nil)
  181. (cond
  182. ;; point after :
  183. ((looking-back ":" (- (point) 2))
  184. (insert (concat (mapconcat 'identity keys ",") ",")))
  185. ;; point on :
  186. ((looking-at ":")
  187. (forward-char)
  188. (insert (concat (mapconcat 'identity keys ",") ",")))
  189. ;; point on the cite type
  190. ((-contains? org-ref-cite-types (thing-at-point 'word))
  191. (re-search-forward ":")
  192. (insert (concat (mapconcat 'identity keys ",") ",")))
  193. ;; after ,
  194. ((looking-back "," (- (point) 2))
  195. (insert (concat (mapconcat 'identity keys ",") ",")))
  196. ;; on comma
  197. ((looking-at ",")
  198. (forward-char)
  199. (insert (concat (mapconcat 'identity keys ",") ",")))
  200. ;; somewhere in the middle or end
  201. (t
  202. ;; goto next comma or end
  203. (re-search-forward
  204. ","
  205. (org-element-property :end object) 'mv)
  206. (skip-chars-backward " ")
  207. (skip-chars-backward "]")
  208. (unless (looking-at ",") (insert ","))
  209. (insert (mapconcat 'identity keys ",")))))
  210. ;; double prefix, replace key at point
  211. ((equal helm-current-prefix-arg '(16))
  212. (setf (buffer-substring
  213. (org-element-property :begin object)
  214. (org-element-property :end object))
  215. (concat
  216. (replace-regexp-in-string
  217. (car (org-ref-get-bibtex-key-and-file)) ; key
  218. (mapconcat 'identity keys ",") ; new keys
  219. (org-element-property :raw-link object))
  220. ;; replace space at end to avoid collapsing into next word.
  221. last-char))
  222. ;; and we want to go to the end of the new link
  223. (goto-char
  224. (org-element-property :end (org-element-context))))
  225. (t
  226. (message "Not found"))))
  227. ;; We are next to a link, and we want to append
  228. ;; next to a link means one character back is on a link.
  229. ((save-excursion
  230. (unless (bobp) (backward-char))
  231. (and (equal (org-element-type (org-element-context)) 'link)
  232. (-contains?
  233. org-ref-cite-types
  234. (org-element-property :type (org-element-context)))))
  235. (skip-chars-backward " ")
  236. (insert (concat "," (mapconcat 'identity keys ","))))
  237. ;; insert fresh link
  238. (t
  239. (insert
  240. (concat
  241. (when org-ref-prefer-bracket-links "[[")
  242. (if (equal helm-current-prefix-arg '(4))
  243. (helm :sources `((name . "link types")
  244. (candidates . ,org-ref-cite-types)
  245. (action . (lambda (x) x))))
  246. org-ref-default-citation-link)
  247. ":"
  248. (s-join "," keys)
  249. (when org-ref-prefer-bracket-links "]]"))))))
  250. ;; return empty string for helm
  251. "")
  252. (defun org-ref-format-citation (keys)
  253. "Formatter for org-ref citation commands.
  254. Prompt for the command and additional arguments if the commands can
  255. take any. If point is inside a citation link, append KEYS. Otherwise
  256. prompt for pre/post text. Prompts can also be switched off by setting
  257. the variable `bibtex-completion-cite-prompt-for-optional-arguments' to
  258. nil. To enable this formatter, add it to
  259. `bibtex-completion-format-citation-functions'. For example:
  260. \(setf (cdr (assoc 'org-mode bibtex-completion-format-citation-functions)) 'org-ref-format-citation)
  261. Note also that pre text is preceded by a double colon, for example:
  262. \[[cite:key][See::Chapter 1]], which exports to:
  263. \\cite[See][Chapter 1]{key}."
  264. ;; Check if point is inside a cite link
  265. (let ((link (org-element-context))
  266. end path)
  267. (if (-contains? org-ref-cite-types (org-element-property :type link))
  268. (progn
  269. (setq end (org-element-property :end link)
  270. path (org-element-property :path link))
  271. (goto-char end)
  272. (skip-chars-backward " ")
  273. ;; Check if link has pre/post text
  274. (if (looking-back "\]" (line-beginning-position))
  275. (progn
  276. (re-search-backward path nil t)
  277. (re-search-forward "\]" nil t)
  278. (backward-char 1)
  279. (format ",%s" (s-join "," keys))))
  280. (format ",%s" (s-join "," keys)))
  281. (let* ((initial (when bibtex-completion-cite-default-as-initial-input bibtex-completion-cite-default-command))
  282. (default
  283. (unless bibtex-completion-cite-default-as-initial-input bibtex-completion-cite-default-command))
  284. (default-info
  285. (if default (format " (default \"%s\")" default) ""))
  286. (cite-command
  287. (completing-read (format "Cite command%s: " default-info)
  288. bibtex-completion-cite-commands nil nil initial
  289. 'bibtex-completion-cite-command-history default nil)))
  290. (if (member cite-command '("nocite" "supercite")) ; These don't want arguments.
  291. (format "%s:%s" cite-command (s-join "," keys))
  292. (let ((text (if bibtex-completion-cite-prompt-for-optional-arguments
  293. (read-from-minibuffer "Pre/post text: ")
  294. "")))
  295. (if (string= "" text)
  296. (format "%s:%s" cite-command (s-join "," keys))
  297. (format "[[%s:%s][%s]]" cite-command (s-join "," keys) text))))))))
  298. (defvar bibtex-completion-cached-candidates)
  299. (defvar bibtex-completion-bibliography-hash)
  300. ;;;###autoload
  301. (defun org-ref-helm-load-completions-async ()
  302. "Load the bibtex files into helm sources asynchronously.
  303. For large bibtex files, the initial call to org-ref-helm-insert-cite-link
  304. can take a long time to load the completion sources. This function loads
  305. the completion sources in the background so the initial call to org-ref-helm-insert-cite-link is much faster."
  306. (interactive)
  307. (async-start
  308. `(lambda (&optional formatter)
  309. (require 'package)
  310. (package-initialize)
  311. (require 'helm-bibtex)
  312. ,(async-inject-variables "bibtex-compl.*")
  313. (with-temp-buffer
  314. (mapc #'insert-file-contents
  315. (-flatten (list bibtex-completion-bibliography)))
  316. ;; Check hash of bibliography and reparse if necessary:
  317. (let ((bibliography-hash (secure-hash 'sha256 (current-buffer))))
  318. (unless (and bibtex-completion-cached-candidates
  319. (string= bibtex-completion-bibliography-hash bibliography-hash))
  320. (message "Loading bibliography ...")
  321. (let* ((entries (bibtex-completion-parse-bibliography))
  322. (entries (bibtex-completion-resolve-crossrefs entries))
  323. (entries (bibtex-completion-prepare-entries entries))
  324. (entries (nreverse entries))
  325. (entries
  326. (--map (cons (bibtex-completion-clean-string
  327. (s-join " " (-map #'cdr it))) it)
  328. entries)))
  329. (setq bibtex-completion-cached-candidates
  330. (if (functionp formatter)
  331. (funcall formatter entries)
  332. entries)))
  333. (setq bibtex-completion-bibliography-hash bibliography-hash))
  334. (cons bibliography-hash bibtex-completion-cached-candidates))))
  335. (lambda (result)
  336. (setq bibtex-completion-cached-candidates (cdr result))
  337. (setq bibtex-completion-bibliography-hash (car result))
  338. (message "Finished loading org-ref completions"))))
  339. ;;;###autoload
  340. (defun org-ref-helm-insert-cite-link (&optional arg)
  341. "Insert a citation link with `helm-bibtex'.
  342. With one prefix ARG, insert a ref link.
  343. With two prefix ARGs, insert a label link."
  344. (interactive "P")
  345. ;; save all bibtex buffers so we get the most up-to-date selection. I find
  346. ;; that I often edit a bibliography and forget to save it, so the newest entry
  347. ;; does not show in helm-bibtex.
  348. (org-ref-save-all-bibtex-buffers)
  349. (cond
  350. ((equal arg nil)
  351. (let ((bibtex-completion-bibliography (org-ref-find-bibliography)))
  352. (helm-bibtex)))
  353. ((equal arg '(4))
  354. (org-ref-helm-insert-ref-link))
  355. ((equal arg '(16))
  356. (org-ref-helm-insert-label-link))))
  357. ;; add our own fallback entries where we want them. These appear in reverse
  358. ;; order of adding in the menu
  359. (setq bibtex-completion-fallback-options
  360. (-insert-at 1 '("Crossref" . "http://search.crossref.org/?q=%s") bibtex-completion-fallback-options))
  361. (setq bibtex-completion-fallback-options
  362. (-insert-at
  363. 1
  364. '("Scopus" . "http://www.scopus.com/scopus/search/submit/xadvanced.url?searchfield=TITLE-ABS-KEY(%s)")
  365. bibtex-completion-fallback-options))
  366. (setq bibtex-completion-fallback-options
  367. (-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))
  368. (defun org-ref-cite-candidates ()
  369. "Generate the list of possible candidates for click actions on a cite link.
  370. Checks for pdf and doi, and add appropriate functions."
  371. (let* ((results (org-ref-get-bibtex-key-and-file))
  372. (key (car results))
  373. (bibfile (cdr results))
  374. (bibtex-completion-bibliography (list bibfile))
  375. (entry (bibtex-completion-get-entry key))
  376. (pdf-file (funcall org-ref-get-pdf-filename-function key))
  377. (pdf-bibtex-completion (car (bibtex-completion-find-pdf key)))
  378. (notes-p (cdr (assoc "=has-note=" entry)))
  379. (url (save-excursion
  380. (with-temp-buffer
  381. (insert-file-contents bibfile)
  382. (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
  383. (bibtex-search-entry key)
  384. (bibtex-autokey-get-field "url"))))
  385. (doi (save-excursion
  386. (with-temp-buffer
  387. (insert-file-contents bibfile)
  388. (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
  389. (bibtex-search-entry key)
  390. ;; I like this better than bibtex-url which does not always find
  391. ;; the urls
  392. (bibtex-autokey-get-field "doi"))))
  393. (candidates `(("Quit" . org-ref-citation-at-point)
  394. ("Open bibtex entry" . org-ref-open-citation-at-point))))
  395. ;; for some reason, when there is no doi or url, they are returned as "". I
  396. ;; prefer nil so we correct this here.
  397. (when (string= doi "") (setq doi nil))
  398. (when (string= url "") (setq url nil))
  399. ;; Conditional pdf functions
  400. ;; try with org-ref first
  401. (cond ((file-exists-p pdf-file)
  402. (cl-pushnew
  403. '("Open pdf" . (lambda ()
  404. (funcall org-ref-open-pdf-function)))
  405. candidates))
  406. ;; try with bibtex-completion
  407. (pdf-bibtex-completion
  408. (cl-pushnew
  409. '("Open pdf" . (lambda ()
  410. (funcall org-ref-open-pdf-function)))
  411. candidates))
  412. ;; try with doi
  413. (t
  414. (cl-pushnew
  415. '("Try to get pdf" . (lambda ()
  416. (save-window-excursion
  417. (org-ref-open-citation-at-point)
  418. (bibtex-beginning-of-entry)
  419. (doi-utils-get-bibtex-entry-pdf))))
  420. candidates)))
  421. (if notes-p
  422. (cl-pushnew
  423. '("Open notes" . org-ref-open-notes-at-point)
  424. candidates)
  425. (cl-pushnew
  426. '("Add notes" . org-ref-open-notes-at-point)
  427. candidates))
  428. ;; conditional url and doi functions
  429. (when (or url doi)
  430. (cl-pushnew
  431. '("Open in browser" . org-ref-open-url-at-point)
  432. candidates))
  433. (when doi
  434. (mapc (lambda (x)
  435. (cl-pushnew x candidates))
  436. `(("WOS" . org-ref-wos-at-point)
  437. ("Related articles in WOS" . org-ref-wos-related-at-point)
  438. ("Citing articles in WOS" . org-ref-wos-citing-at-point)
  439. ("Google Scholar" . org-ref-google-scholar-at-point)
  440. ("Pubmed" . org-ref-pubmed-at-point)
  441. ("Crossref" . org-ref-crossref-at-point))))
  442. (cl-pushnew
  443. '("Insert new citation" . (lambda ()
  444. (org-ref-helm-insert-cite-link nil)))
  445. candidates)
  446. (cl-pushnew
  447. '("Delete key at point" . org-ref-delete-key-at-point)
  448. candidates)
  449. ;; This is kind of clunky. We store the key at point. Add the new ref. Get
  450. ;; it off the end, and put it in the original position.
  451. (cl-pushnew
  452. '("Replace key at point" . org-ref-replace-key-at-point)
  453. candidates)
  454. (cl-pushnew
  455. '("Delete citation at point" . org-ref-delete-cite-at-point)
  456. candidates)
  457. (when bibtex-completion-cite-prompt-for-optional-arguments
  458. (cl-pushnew
  459. '("Update pre/post text" . org-ref-update-pre-post-text)
  460. candidates))
  461. (cl-pushnew
  462. '("Sort keys by year" . org-ref-sort-citation-link)
  463. candidates)
  464. (cl-pushnew
  465. '("Copy formatted citation to clipboard" . org-ref-copy-cite-as-summary)
  466. candidates)
  467. (cl-pushnew
  468. '("Copy key to clipboard" . (lambda ()
  469. (kill-new
  470. (car (org-ref-get-bibtex-key-and-file)))))
  471. candidates)
  472. (cl-pushnew
  473. '("Copy bibtex entry to file" . org-ref-copy-entry-at-point-to-file)
  474. candidates)
  475. (cl-pushnew
  476. '("Email bibtex entry and pdf" . (lambda ()
  477. (save-excursion
  478. (org-ref-open-citation-at-point)
  479. (org-ref-email-bibtex-entry))))
  480. candidates)
  481. ;; add Scopus functions. These work by looking up a DOI to get a Scopus
  482. ;; EID. This may only work for Scopus articles. Not all DOIs are recognized
  483. ;; in the Scopus API. We only load these if you have defined a
  484. ;; `*scopus-api-key*', which is required to do the API queries. See
  485. ;; `scopus'. These functions are appended to the candidate list.
  486. (when (and (boundp '*scopus-api-key*) *scopus-api-key*)
  487. (cl-pushnew
  488. '("Open in Scopus" . (lambda ()
  489. (let ((eid (scopus-doi-to-eid (org-ref-get-doi-at-point))))
  490. (if eid
  491. (scopus-open-eid eid)
  492. (message "No EID found.")))))
  493. candidates)
  494. (cl-pushnew
  495. '("Scopus citing articles" . (lambda ()
  496. (let ((url (scopus-citing-url
  497. (org-ref-get-doi-at-point))))
  498. (if url
  499. (browse-url url)
  500. (message "No url found.")))))
  501. candidates)
  502. (cl-pushnew
  503. '("Scopus related by authors" . (lambda ()
  504. (let ((url (scopus-related-by-author-url
  505. (org-ref-get-doi-at-point))))
  506. (if url
  507. (browse-url url)
  508. (message "No url found.")))))
  509. candidates)
  510. (cl-pushnew
  511. '("Scopus related by references" . (lambda ()
  512. (let ((url (scopus-related-by-references-url
  513. (org-ref-get-doi-at-point))))
  514. (if url
  515. (browse-url url)
  516. (message "No url found.")))))
  517. candidates)
  518. (cl-pushnew
  519. '("Scopus related by keywords" . (lambda ()
  520. (let ((url (scopus-related-by-keyword-url
  521. (org-ref-get-doi-at-point))))
  522. (if url
  523. (browse-url url)
  524. (message "No url found.")))))
  525. candidates))
  526. ;; finally return a numbered list of the candidates
  527. (cl-loop for i from 0
  528. for cell in (reverse candidates)
  529. collect (cons (format "%2s. %s" i (car cell))
  530. (cdr cell)))))
  531. (defvar org-ref-helm-user-candidates '()
  532. "List of user-defined candidates to act when clicking on a cite link.
  533. This is a list of cons cells '((\"description\" . action)). The
  534. action function should not take an argument, and should assume
  535. point is on the cite key of interest.")
  536. ;; example of adding your own function
  537. (add-to-list
  538. 'org-ref-helm-user-candidates
  539. '("Open pdf in emacs" . (lambda ()
  540. (find-file
  541. (concat
  542. (file-name-as-directory org-ref-pdf-directory)
  543. (car (org-ref-get-bibtex-key-and-file))
  544. ".pdf"))))
  545. t)
  546. ;;;###autoload
  547. (defun org-ref-cite-click-helm (_key)
  548. "Open helm for actions on a cite link.
  549. subtle points.
  550. 1. get name and candidates before entering helm because we need
  551. the org-buffer.
  552. 2. switch back to the org buffer before evaluating the
  553. action. most of them need the point and buffer.
  554. KEY is returned for the selected item(s) in helm."
  555. (interactive)
  556. (let ((name (org-ref-format-entry (org-ref-get-bibtex-key-under-cursor)))
  557. (candidates (org-ref-cite-candidates))
  558. (cb (current-buffer)))
  559. (helm :sources `(((name . ,name)
  560. (candidates . ,candidates)
  561. (action . (lambda (f)
  562. (switch-to-buffer ,cb)
  563. (funcall f))))
  564. ((name . "User functions")
  565. (candidates . ,org-ref-helm-user-candidates)
  566. (action . (lambda (f)
  567. (switch-to-buffer ,cb)
  568. (funcall f))))))))
  569. ;; browse labels
  570. (defun org-ref-browser-label-source ()
  571. (let ((labels (org-ref-get-labels)))
  572. (helm-build-sync-source "Browse labels"
  573. :follow 1
  574. :candidates labels
  575. :action '(("Browse labels" . (lambda (label)
  576. (with-selected-window (selected-window)
  577. (org-open-link-from-string
  578. (format "ref:%s" label)))))))))
  579. ;; browse citation links
  580. (defun org-ref-browser-transformer (candidates)
  581. "Add counter to candidates."
  582. (let ((counter 0))
  583. (cl-loop for i in candidates
  584. collect (format "%s %s" (cl-incf counter) i))))
  585. (defun org-ref-browser-display (candidate)
  586. "Strip counter from candidates."
  587. (replace-regexp-in-string "^[0-9]+? " "" candidate))
  588. ;;;###autoload
  589. (defun org-ref-browser (&optional arg)
  590. "Quickly browse label links in helm.
  591. With a prefix ARG, browse citation links."
  592. (interactive "P")
  593. (if arg
  594. (let ((keys nil)
  595. (alist nil))
  596. (widen)
  597. (outline-show-all)
  598. (org-element-map (org-element-parse-buffer) 'link
  599. (lambda (link)
  600. (let ((plist (nth 1 link)))
  601. (when (-contains? org-ref-cite-types (plist-get plist ':type))
  602. (let ((start (org-element-property :begin link)))
  603. (dolist (key
  604. (org-ref-split-and-strip-string (plist-get plist ':path)))
  605. (setq keys (append keys (list key)))
  606. (setq alist (append alist (list (cons key start))))))))))
  607. (let ((counter 0)
  608. count-key-pos)
  609. ;; the idea here is to create an alist with ("counter key" .
  610. ;; position) to produce unique candidates
  611. (setq count-key-pos (mapcar (lambda (x)
  612. (cons
  613. (format "%s %s" (cl-incf counter) (car x)) (cdr x)))
  614. alist))
  615. ;; push mark to restore position with C-u C-SPC
  616. (push-mark (point))
  617. ;; move point to the first citation link in the buffer
  618. (goto-char (cdr (assoc (caar alist) alist)))
  619. (helm :sources
  620. (helm-build-sync-source "Browse citation links"
  621. :follow 1
  622. :candidates keys
  623. :candidate-transformer 'org-ref-browser-transformer
  624. :real-to-display 'org-ref-browser-display
  625. :persistent-action (lambda (candidate)
  626. (helm-goto-char
  627. (cdr (assoc candidate count-key-pos))))
  628. :action `(("Open menu" . ,(lambda (candidate)
  629. (helm-goto-char
  630. (cdr (assoc candidate count-key-pos)))
  631. (org-open-at-point)))))
  632. :candidate-number-limit 10000
  633. :buffer "*helm browser*")))
  634. (helm :sources (org-ref-browser-label-source)
  635. :buffer "*helm labels*")))
  636. (provide 'org-ref-helm-bibtex)
  637. ;;; org-ref-helm-bibtex.el ends here