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.

770 lines
26 KiB

5 years ago
  1. ;;; org-ref-helm-cite.el --- Helm interface to insert citations from bibtex files 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: This is a competitor of `helm-bibtex'.
  16. ;; The main difference is the format of the candidates, which are full citations
  17. ;; in this package, and multiline. This package also makes the candidates
  18. ;; sortable in different ways, and provides different, context specific actions
  19. ;; depending on what buffer you call `org-ref-helm-cite' from, and depending
  20. ;; on the properties of the selected candidate.
  21. ;;
  22. ;; Another significant feature is persistent caching of bibtex files to make
  23. ;; startup fast.
  24. ;;
  25. (declare-function 'org-ref-find-bibliography "org-ref-core.el")
  26. (declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
  27. (defvar org-ref-pdf-directory)
  28. (defvar org-ref-notes-directory)
  29. (defvar org-ref-cite-types)
  30. (defvar org-ref-default-citation-link)
  31. (defvar org-ref-insert-link-function)
  32. (defvar org-ref-insert-cite-function)
  33. (defvar org-ref-insert-label-function)
  34. (defvar org-ref-insert-ref-function)
  35. (defvar org-ref-cite-onclick-function)
  36. (defvar org-ref-insert-cite-key)
  37. ;;; Code:
  38. (require 'org-ref-helm)
  39. (require 'org-ref-bibtex)
  40. (defun org-ref-helm-cite-completion ()
  41. "Use helm and org-ref for completion."
  42. (interactive)
  43. (setq org-ref-insert-link-function 'org-ref-insert-link
  44. org-ref-insert-cite-function 'org-ref-helm-cite
  45. org-ref-insert-label-function 'org-ref-helm-insert-label-link
  46. org-ref-insert-ref-function 'org-ref-helm-insert-ref-link
  47. org-ref-cite-onclick-function 'org-ref-helm-cite-click))
  48. (org-ref-helm-cite-completion)
  49. (define-key org-mode-map
  50. (kbd org-ref-insert-cite-key)
  51. org-ref-insert-link-function)
  52. ;;* Variables
  53. (defvar org-ref-helm-cite-from nil
  54. "Variable to store the mode `org-ref-helm-cite' was called
  55. from. This is used to provide some context specific actions.")
  56. (defvar org-ref-helm-cite-help-message
  57. "* Org-ref helm bibtex.
  58. M-<down> allows you to sort the entries by year or first author
  59. last name.")
  60. (defvar org-ref-helm-cite-map
  61. (let ((map (make-sparse-keymap)))
  62. (set-keymap-parent map helm-map)
  63. (define-key map (kbd "M-<down>") 'org-ref-helm-cite-sort)
  64. map))
  65. (defvar orhc-sort-fn nil
  66. "Function for sorting the helm entries.")
  67. ;;* Helm functions
  68. (defun orhc-helm-cite-sort-alphabetical-a (c1 c2)
  69. "Sort entries by first author last name from a to z."
  70. (let* ((a1 (cdr c1))
  71. (au1 (cdr (assoc "author" a1)))
  72. (a2 (cdr c2))
  73. (au2 (cdr (assoc "author" a2)))
  74. fa1 fa2)
  75. (if (or (not (stringp au1))
  76. (not (stringp au2)))
  77. nil
  78. ;; we need to parse names
  79. (setq fa1 (car (s-split " and " au1))
  80. fa2 (car (s-split " and " au2)))
  81. (if (string-match "," fa1)
  82. ;; last, first
  83. (setq fa1 (car (s-split "," fa1)))
  84. ;; first last, ignore von names and jr
  85. (setq fa1 (car (last (s-split " " fa1)))))
  86. (if (string-match "," fa2)
  87. ;; last, first
  88. (setq fa2 (car (s-split "," fa2)))
  89. ;; first last, ignore von names, and jr
  90. (setq fa2 (car (last (s-split " " fa2)))))
  91. (string< fa1 fa2))))
  92. (defun orhc-helm-cite-sort-alphabetical-z (c1 c2)
  93. "Sort entries by first author last name from z to a."
  94. (let* ((a1 (cdr c1))
  95. (au1 (cdr (assoc "author" a1)))
  96. (a2 (cdr c2))
  97. (au2 (cdr (assoc "author" a2)))
  98. fa1 fa2)
  99. (if (or (not (stringp au1))
  100. (not (stringp au2)))
  101. nil
  102. ;; we need to parse names to get lastname
  103. (setq fa1 (car (s-split " and " au1))
  104. fa2 (car (s-split " and " au2)))
  105. (if (string-match "," fa1)
  106. ;; last, first
  107. (setq fa1 (car (s-split "," fa1)))
  108. ;; first last, ignore von names and jr
  109. (setq fa1 (car (last (s-split " " fa1)))))
  110. (if (string-match "," fa2)
  111. ;; last, first
  112. (setq fa2 (car (s-split "," fa2)))
  113. ;; first last, ignore von names, and jr
  114. (setq fa2 (car (last (s-split " " fa2)))))
  115. (string< fa2 fa1))))
  116. (defun org-ref-helm-cite-sort ()
  117. "Sort interface for `org-ref-helm-cite'."
  118. (interactive)
  119. (let ((action (read-char "year↓ (y) year↑ (Y)
  120. 1st author (a) 1st author (z)
  121. key (k) key (K): ")))
  122. (cond
  123. ;; sort on year
  124. ((eq action ?y)
  125. (setq orhc-sort-fn
  126. (lambda (c1 c2)
  127. (let* ((a1 (cdr c1))
  128. (y1 (cdr (assoc "year" a1)))
  129. (a2 (cdr c2))
  130. (y2 (cdr (assoc "year" a2))))
  131. (if (or (null y1) (null y2))
  132. nil
  133. (> (string-to-number y1) (string-to-number y2)))))))
  134. ((eq action ?Y)
  135. (setq orhc-sort-fn
  136. (lambda (c1 c2)
  137. (let* ((a1 (cdr c1))
  138. (y1 (cdr (assoc "year" a1)))
  139. (a2 (cdr c2))
  140. (y2 (cdr (assoc "year" a2))))
  141. (if (or (null y1) (null y2))
  142. nil
  143. (< (string-to-number y1) (string-to-number y2)))))))
  144. ;; sort on key
  145. ((eq action ?k)
  146. (setq orhc-sort-fn
  147. (lambda (c1 c2)
  148. (let* ((a1 (cdr c1))
  149. (k1 (cdr (assoc "=key=" a1)))
  150. (a2 (cdr c2))
  151. (k2 (cdr (assoc "=key=" a2))))
  152. (string< k2 k1)))))
  153. ((eq action ?K)
  154. (setq orhc-sort-fn
  155. (lambda (c1 c2)
  156. (let* ((a1 (cdr c1))
  157. (k1 (cdr (assoc "=key=" a1)))
  158. (a2 (cdr c2))
  159. (k2 (cdr (assoc "=key=" a2))))
  160. (string< k1 k2)))))
  161. ;; sort on first author last name
  162. ((eq action ?a)
  163. (setq orhc-sort-fn #'orhc-helm-cite-sort-alphabetical-a))
  164. ((eq action ?z)
  165. (setq orhc-sort-fn #'orhc-helm-cite-sort-alphabetical-z))
  166. (t (setq orhc-sort-fn nil)))
  167. (helm-update)
  168. (setq orhc-sort-fn nil)))
  169. (defun org-ref-helm-candidate-transformer (candidates _source)
  170. "Transform CANDIDATES, sorting if needed.
  171. SOURCE is ignored, but required."
  172. (if orhc-sort-fn
  173. (-sort orhc-sort-fn candidates)
  174. candidates))
  175. (defun org-ref-helm-cite-action-transformer (actions candidate)
  176. "Compute ACTIONS for CANDIDATE."
  177. ;; Check for pdf and add open or get action.
  178. (setq actions (append
  179. actions
  180. '(("insert citation(s)" . org-ref-helm-cite-insert-citation)
  181. ("show entry" . org-ref-helm-cite-open-entry))))
  182. (let ((pdf (expand-file-name
  183. (concat (cdr (assoc "=key=" candidate)) ".pdf")
  184. org-ref-pdf-directory)))
  185. (if (file-exists-p pdf)
  186. (setq actions (append actions
  187. (list
  188. (cons
  189. (format "Open %s" pdf)
  190. (lambda (_candidate)
  191. (org-open-file pdf))))))
  192. (when (assoc "doi" candidate)
  193. (setq actions
  194. (append actions
  195. (list
  196. (cons
  197. (format "Get PDF")
  198. (lambda (candidate)
  199. (save-window-excursion
  200. (find-file (cdr (assoc "file" candidate)))
  201. (goto-char (cdr (assoc "position" candidate)))
  202. (doi-utils-get-bibtex-entry-pdf))))))))))
  203. ;; check for url/doi
  204. (when (assoc "url" candidate)
  205. (setq actions (append actions
  206. (list
  207. (cons (format "Open %s"
  208. (cdr (assoc "url" candidate)))
  209. (lambda (x)
  210. (browse-url (cdr (assoc "url" x)))))))))
  211. (when (assoc "doi" candidate)
  212. (setq actions (append actions
  213. (list
  214. (cons
  215. (format "Open doi (%s)"
  216. (cdr (assoc "doi" candidate)))
  217. (lambda (x)
  218. (browse-url
  219. (format "http://dx.doi.org/%s"
  220. (cdr (assoc "doi" x))))))))))
  221. ;; Notes, open or create.
  222. (let ((note-file (expand-file-name
  223. (concat (cdr (assoc "=key=" candidate)) ".org")
  224. org-ref-notes-directory)))
  225. (if (file-exists-p note-file)
  226. (setq actions (append actions (list (cons "Open notes"
  227. (lambda (_x)
  228. (find-file note-file))))))
  229. (setq actions (append actions (list (cons "Create notes"
  230. (lambda (_x)
  231. (find-file note-file))))))))
  232. (setq actions (append
  233. actions
  234. '(("Add keywords" . org-ref-helm-cite-set-keywords)
  235. ("copy to clipboard" . org-ref-helm-cite-copy-entries)
  236. ("email" . org-ref-helm-cite-email-entries)
  237. ("Insert formatted entries" . (lambda (_)
  238. (insert
  239. (mapconcat 'identity
  240. (cl-loop for key in (helm-marked-candidates)
  241. collect (org-ref-format-entry key))
  242. "\n\n"))))
  243. ("Copy formatted entry" . (lambda (_)
  244. (kill-new
  245. (mapconcat 'identity
  246. (cl-loop for key in (helm-marked-candidates)
  247. collect (org-ref-format-entry key))
  248. "\n\n")))))))
  249. ;; this is where we could add WOK/scopus functions
  250. actions)
  251. (defun org-ref-helm-cite-insert-citation (_candidate)
  252. "Insert selected CANDIDATE as cite link.
  253. This is an action for helm, and it actually works on
  254. `helm-marked-candidates'. Append KEYS if you are on a link.
  255. In the `org-ref-helm-cite' buffer, \\[universal-argument] will give you
  256. a helm menu to select a new link type for the selected entries.
  257. A double \\[universal-argument] \\[universal-argument] will
  258. change the key at point to the selected keys."
  259. (let* ((keys (cl-loop for entry in (helm-marked-candidates)
  260. collect (cdr (assoc "=key=" entry))))
  261. (object (org-element-context))
  262. (last-char (save-excursion
  263. (when (org-element-property :end object)
  264. (goto-char (org-element-property :end object))
  265. (unless (bobp)
  266. (backward-char))
  267. (if (looking-at " ")
  268. " "
  269. "")))))
  270. (cond
  271. ;; case where we are in a link
  272. ((and (equal (org-element-type object) 'link)
  273. (-contains?
  274. org-ref-cite-types
  275. (org-element-property :type object)))
  276. (cond
  277. ;; no prefix. insert or append keys
  278. ((equal helm-current-prefix-arg nil)
  279. (cond
  280. ;; point after :
  281. ((looking-back ":" (- (point) 2))
  282. (insert (concat (mapconcat 'identity keys ",") ",")))
  283. ;; point on :
  284. ((looking-at ":")
  285. (forward-char)
  286. (insert (concat (mapconcat 'identity keys ",") ",")))
  287. ;; point on the cite type
  288. ((-contains? org-ref-cite-types (thing-at-point 'word))
  289. (re-search-forward ":")
  290. (insert (concat (mapconcat 'identity keys ",") ",")))
  291. ;; after ,
  292. ((looking-back "," (- (point) 2))
  293. (insert (concat (mapconcat 'identity keys ",") ",")))
  294. ;; on comma
  295. ((looking-at ",")
  296. (forward-char)
  297. (insert (concat (mapconcat 'identity keys ",") ",")))
  298. ;; somewhere in the middle or end
  299. (t
  300. ;; goto next comma or end
  301. (re-search-forward
  302. ","
  303. (org-element-property :end object) t)
  304. (skip-chars-backward " ")
  305. (insert (mapconcat 'identity keys ","))
  306. (unless (looking-at ",") (insert ",")))))
  307. ;; double prefix, replace key at point
  308. ((equal helm-current-prefix-arg '(16))
  309. (setf (buffer-substring
  310. (org-element-property :begin object)
  311. (org-element-property :end object))
  312. (concat
  313. (replace-regexp-in-string
  314. (car (org-ref-get-bibtex-key-and-file)) ; key
  315. (mapconcat 'identity keys ",") ; new keys
  316. (org-element-property :raw-link object))
  317. ;; replace space at end to avoid collapsing into next word.
  318. last-char))
  319. ;; and we want to go to the end of the new link
  320. (goto-char
  321. (org-element-property :end (org-element-context))))
  322. (t
  323. (message "Not found"))))
  324. ;; We are next to a link, and we want to append
  325. ;; next to a link means one character back is on a link.
  326. ((save-excursion
  327. (unless (bobp) (backward-char))
  328. (and (equal (org-element-type (org-element-context)) 'link)
  329. (-contains?
  330. org-ref-cite-types
  331. (org-element-property :type (org-element-context)))))
  332. (skip-chars-backward " ")
  333. (insert (concat "," (mapconcat 'identity keys ","))))
  334. ;; insert fresh link
  335. (t
  336. ;;(message-box "fresh link")
  337. (insert
  338. (concat (if (equal helm-current-prefix-arg '(4))
  339. (helm :sources `((name . "link types")
  340. (candidates . ,org-ref-cite-types)
  341. (action . (lambda (x) x))))
  342. org-ref-default-citation-link)
  343. ":"
  344. (s-join "," keys)))))))
  345. (defun org-ref-helm-cite-init ()
  346. "Initializes the source, setting bibtex files from the
  347. originating buffer, and mode of originating buffer."
  348. (org-ref-save-all-bibtex-buffers)
  349. (setq org-ref-bibtex-files (org-ref-find-bibliography))
  350. ;; save major-mode we came from so we can do context specific things.
  351. (setq org-ref-helm-cite-from major-mode)
  352. (message "initialized."))
  353. (defun org-ref-helm-cite-open-entry (entry)
  354. "Open the selected bibtex entry in its file."
  355. (find-file (cdr (assoc "bibfile" entry)))
  356. (goto-char (cdr (assoc "position" entry)))
  357. (bibtex-beginning-of-entry))
  358. (defun org-ref-helm-cite-copy-entries (_candidate)
  359. "Copy selected bibtex entries to the clipboard."
  360. (with-temp-buffer
  361. (cl-loop for entry in (helm-marked-candidates)
  362. do
  363. (save-window-excursion
  364. (org-ref-helm-cite-open-entry entry)
  365. (bibtex-copy-entry-as-kill))
  366. (bibtex-yank)
  367. (insert "\n"))
  368. (kill-region (point-min) (point-max))))
  369. (defun org-ref-helm-cite-email-entries (_candidate)
  370. "Insert selected entries and attach pdf files to an email.
  371. Create email unless called from an email."
  372. (unless (or (eq org-ref-helm-cite-from 'message-mode)
  373. (eq org-ref-helm-cite-from 'mu4e-compose-mode))
  374. (compose-mail))
  375. (cl-loop for entry in (helm-marked-candidates)
  376. do
  377. (save-window-excursion
  378. (org-ref-helm-cite-open-entry entry)
  379. (bibtex-copy-entry-as-kill))
  380. (message-goto-body)
  381. (insert (pop bibtex-entry-kill-ring))
  382. (insert "\n")
  383. if (file-exists-p (expand-file-name
  384. (concat (cdr (assoc "=key=" entry)) ".pdf")
  385. org-ref-pdf-directory))
  386. do
  387. (mml-attach-file (expand-file-name
  388. (concat (cdr (assoc "=key=" entry)) ".pdf")
  389. org-ref-pdf-directory)))
  390. (message-goto-to))
  391. (defun org-ref-helm-cite-set-keywords (_candidate)
  392. "Prompt for keywords, and put them on the selected entries."
  393. (let ((keywords (read-string "Keyword(s) comma-separated: " ))
  394. entry-keywords)
  395. (cl-loop for entry in (helm-marked-candidates)
  396. do
  397. (save-window-excursion
  398. (org-ref-helm-cite-open-entry entry)
  399. (setq entry-keywords (bibtex-autokey-get-field "keywords"))
  400. (bibtex-set-field
  401. "keywords"
  402. (if (> (length entry-keywords) 0)
  403. (concat entry-keywords ", " keywords)
  404. keywords))))))
  405. ;;** Helm sources
  406. (defvar orhc-multiline t
  407. "Make helm-source multiline if non-nil.
  408. This adds a small separator between the candidates which is a
  409. little more readable.")
  410. (defvar org-ref-helm-user-candidates '()
  411. "List of user-defined candidates to act when clicking on a cite link.
  412. This is a list of cons cells '((\"description\" . action)). The
  413. action function should not take an argument, and should assume
  414. point is on the cite key of interest.")
  415. (defvar org-ref-helm-cite-source
  416. (helm-build-sync-source "org-ref Bibtex"
  417. :init #'org-ref-helm-cite-init
  418. :candidates #'orhc-bibtex-candidates
  419. :keymap 'org-ref-helm-cite-map
  420. :multiline orhc-multiline
  421. :help-message 'org-ref-helm-cite-help-message
  422. :filtered-candidate-transformer 'org-ref-helm-candidate-transformer
  423. :action-transformer 'org-ref-helm-cite-action-transformer
  424. :action '()))
  425. ;; Fallback sources
  426. ;; The candidates here are functions that work on `helm-pattern'.
  427. (defvar org-ref-helm-cite-fallback-source
  428. nil
  429. "Helm fallback source.")
  430. (setq org-ref-helm-cite-fallback-source
  431. (helm-build-sync-source "org-ref bibtex Fallbacks"
  432. :candidates '(("Google" . (lambda ()
  433. (browse-url
  434. (format "http://www.google.com/search?q=%s"
  435. (url-hexify-string helm-pattern)))))
  436. ("Google Scholar" . (lambda ()
  437. (browse-url
  438. (format "http://scholar.google.com/scholar?q=%s"
  439. (url-hexify-string helm-pattern)))))
  440. ("Crossref" . (lambda ()
  441. (browse-url
  442. (format
  443. "http://search.crossref.org/?q=%s"
  444. (url-hexify-string helm-pattern)))))
  445. ("Pubmed" . (lambda ()
  446. (browse-url
  447. (format
  448. "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s"
  449. (url-hexify-string helm-pattern)))))
  450. ("Arxiv" . (lambda ()
  451. (browse-url
  452. (format
  453. "http://arxiv.org/find/all/1/all:+AND+%s/0/1/0/all/0/1"
  454. (url-hexify-string helm-pattern)))))
  455. ("WebOfKnowledge" . (lambda ()
  456. (browse-url
  457. (format
  458. "http://gateway.webofknowledge.com/gateway/Gateway.cgi?topic=%s&GWVersion=2&SrcApp=WEB&SrcAuth=HSB&DestApp=UA&DestLinkType=GeneralSearchSummary"
  459. (url-hexify-string helm-pattern)))))
  460. ("Scopus" . (lambda ()
  461. (browse-url
  462. (format
  463. "http://www.scopus.com//search/submit/basic.url?field1=TITLE-ABS-KEY&searchterm1=%s"
  464. (url-hexify-string helm-pattern)))))
  465. )
  466. ;; This keeps all the fallback candidates available, by tricking it into
  467. ;; thinking every candidate is a match.
  468. :match (lambda (_candidate) t)
  469. :action (lambda (candidate) (funcall candidate))))
  470. (defun org-ref-helm-cite ()
  471. "Helm interface to bibtex files for `org-ref'."
  472. (interactive)
  473. (helm :sources '(org-ref-helm-cite-source
  474. org-ref-helm-cite-fallback-source)))
  475. (defalias 'orhc 'org-ref-helm-cite)
  476. ;; ** Onclick function
  477. ;; These are adapted from org-ref-helm-bibtex, and the dependencies on helm-bibtex removed.
  478. (defun org-ref-helm-cite-candidates ()
  479. "Generate the list of possible candidates for click actions on a cite link.
  480. Checks for pdf and doi, and add appropriate functions."
  481. (let* ((results (org-ref-get-bibtex-key-and-file))
  482. (key (car results))
  483. (bibfile (cdr results))
  484. (pdf-file (funcall org-ref-get-pdf-filename-function key))
  485. (url (save-excursion
  486. (with-temp-buffer
  487. (insert-file-contents bibfile)
  488. (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
  489. (bibtex-search-entry key)
  490. (bibtex-autokey-get-field "url"))))
  491. (doi (save-excursion
  492. (with-temp-buffer
  493. (insert-file-contents bibfile)
  494. (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
  495. (bibtex-search-entry key)
  496. ;; I like this better than bibtex-url which does not always find
  497. ;; the urls
  498. (bibtex-autokey-get-field "doi"))))
  499. (candidates `(("Quit" . org-ref-citation-at-point)
  500. ("Open bibtex entry" . org-ref-open-citation-at-point))))
  501. ;; for some reason, when there is no doi or url, they are returned as "". I
  502. ;; prefer nil so we correct this here.
  503. (when (string= doi "") (setq doi nil))
  504. (when (string= url "") (setq url nil))
  505. ;; Conditional pdf functions
  506. ;; try with org-ref first
  507. (cond ((file-exists-p pdf-file)
  508. (cl-pushnew
  509. '("Open pdf" . (lambda ()
  510. (funcall org-ref-open-pdf-function)))
  511. candidates))
  512. ;; try with doi
  513. (t
  514. (cl-pushnew
  515. '("Try to get pdf" . (lambda ()
  516. (save-window-excursion
  517. (org-ref-open-citation-at-point)
  518. (bibtex-beginning-of-entry)
  519. (doi-utils-get-bibtex-entry-pdf))))
  520. candidates)))
  521. (cl-pushnew
  522. '("Add/Open notes" . org-ref-open-notes-at-point)
  523. candidates)
  524. ;; conditional url and doi functions
  525. (when (or url doi)
  526. (cl-pushnew
  527. '("Open in browser" . org-ref-open-url-at-point)
  528. candidates))
  529. (when doi
  530. (mapc (lambda (x)
  531. (cl-pushnew x candidates))
  532. `(("WOS" . org-ref-wos-at-point)
  533. ("Related articles in WOS" . org-ref-wos-related-at-point)
  534. ("Citing articles in WOS" . org-ref-wos-citing-at-point)
  535. ("Google Scholar" . org-ref-google-scholar-at-point)
  536. ("Pubmed" . org-ref-pubmed-at-point)
  537. ("Crossref" . org-ref-crossref-at-point))))
  538. (cl-pushnew
  539. '("Insert new citation" . (lambda ()
  540. (org-ref-helm-insert-cite-link nil)))
  541. candidates)
  542. (cl-pushnew
  543. '("Delete key at point" . org-ref-delete-key-at-point)
  544. candidates)
  545. ;; This is kind of clunky. We store the key at point. Add the new ref. Get
  546. ;; it off the end, and put it in the original position.
  547. (cl-pushnew
  548. '("Replace key at point" . org-ref-replace-key-at-point)
  549. candidates)
  550. (cl-pushnew
  551. '("Delete citation at point" . org-ref-delete-cite-at-point)
  552. candidates)
  553. (cl-pushnew
  554. '("Sort keys by year" . org-ref-sort-citation-link)
  555. candidates)
  556. (cl-pushnew
  557. '("Copy formatted citation to clipboard" . org-ref-copy-entry-as-summary)
  558. candidates)
  559. (cl-pushnew
  560. '("Copy key to clipboard" . (lambda ()
  561. (kill-new
  562. (car (org-ref-get-bibtex-key-and-file)))))
  563. candidates)
  564. (cl-pushnew
  565. '("Copy bibtex entry to file" . org-ref-copy-entry-at-point-to-file)
  566. candidates)
  567. (cl-pushnew
  568. '("Email bibtex entry and pdf" . (lambda ()
  569. (save-excursion
  570. (org-ref-open-citation-at-point)
  571. (org-ref-email-bibtex-entry))))
  572. candidates)
  573. ;; add Scopus functions. These work by looking up a DOI to get a Scopus
  574. ;; EID. This may only work for Scopus articles. Not all DOIs are recognized
  575. ;; in the Scopus API. We only load these if you have defined a
  576. ;; `*scopus-api-key*', which is required to do the API queries. See
  577. ;; `scopus'. These functions are appended to the candidate list.
  578. (when (and (boundp '*scopus-api-key*) *scopus-api-key*)
  579. (cl-pushnew
  580. '("Open in Scopus" . (lambda ()
  581. (let ((eid (scopus-doi-to-eid (org-ref-get-doi-at-point))))
  582. (if eid
  583. (scopus-open-eid eid)
  584. (message "No EID found.")))))
  585. candidates)
  586. (cl-pushnew
  587. '("Scopus citing articles" . (lambda ()
  588. (let ((url (scopus-citing-url
  589. (org-ref-get-doi-at-point))))
  590. (if url
  591. (browse-url url)
  592. (message "No url found.")))))
  593. candidates)
  594. (cl-pushnew
  595. '("Scopus related by authors" . (lambda ()
  596. (let ((url (scopus-related-by-author-url
  597. (org-ref-get-doi-at-point))))
  598. (if url
  599. (browse-url url)
  600. (message "No url found.")))))
  601. candidates)
  602. (cl-pushnew
  603. '("Scopus related by references" . (lambda ()
  604. (let ((url (scopus-related-by-references-url
  605. (org-ref-get-doi-at-point))))
  606. (if url
  607. (browse-url url)
  608. (message "No url found.")))))
  609. candidates)
  610. (cl-pushnew
  611. '("Scopus related by keywords" . (lambda ()
  612. (let ((url (scopus-related-by-keyword-url
  613. (org-ref-get-doi-at-point))))
  614. (if url
  615. (browse-url url)
  616. (message "No url found.")))))
  617. candidates))
  618. ;; finally return a numbered list of the candidates
  619. (cl-loop for i from 0
  620. for cell in (reverse candidates)
  621. collect (cons (format "%2s. %s" i (car cell))
  622. (cdr cell)))))
  623. (defun org-ref-helm-cite-click (_key)
  624. "Open helm for actions on a cite link.
  625. subtle points.
  626. 1. get name and candidates before entering helm because we need
  627. the org-buffer.
  628. 2. switch back to the org buffer before evaluating the
  629. action. most of them need the point and buffer.
  630. KEY is returned for the selected item(s) in helm."
  631. (interactive)
  632. (let ((name (org-ref-format-entry (org-ref-get-bibtex-key-under-cursor)))
  633. (candidates (org-ref-helm-cite-candidates))
  634. (cb (current-buffer)))
  635. (helm :sources `(((name . ,name)
  636. (candidates . ,candidates)
  637. (action . (lambda (f)
  638. (switch-to-buffer ,cb)
  639. (funcall f))))
  640. ((name . "User functions")
  641. (candidates . ,org-ref-helm-user-candidates)
  642. (action . (lambda (f)
  643. (switch-to-buffer ,cb)
  644. (funcall f))))))))
  645. ;;* Formatted citations
  646. (defun orhc-formatted-citations (_candidate)
  647. "Return string containing formatted citations for entries in
  648. `helm-marked-candidates'."
  649. (load-library
  650. (completing-read "Style: " '("unsrt" "author-year") nil nil "unsrt"))
  651. (with-temp-buffer
  652. (cl-loop for i from 1 to (length (helm-marked-candidates))
  653. for entry in (helm-marked-candidates)
  654. do
  655. (insert (format "%s. %s\n\n" i (orhc-formatted-citation entry))))
  656. (buffer-string)))
  657. (defun orhc-insert-formatted-citations (candidate)
  658. "Insert formatted citations at point for selected entries."
  659. (insert (orhc-formatted-citations candidate)))
  660. (defun orhc-copy-formatted-citations (candidate)
  661. "Copy formatted citations to clipboard for selected entries."
  662. (with-temp-buffer
  663. (orhc-insert-formatted-citations candidate)
  664. (kill-ring-save (point-min) (point-max))))
  665. (provide 'org-ref-helm-cite)
  666. ;;; org-ref-helm-cite.el ends here