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.

562 lines
18 KiB

5 years ago
  1. ;;; org-ref-ivy-cite.el --- Use ivy for completion in 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. ;;
  17. (declare-function 'org-ref-insert-key-at-point "org-ref-core.el")
  18. (declare-function 'org-ref-find-bibliography "org-ref-core.el")
  19. (declare-function 'org-ref-get-labels "org-ref-core.el")
  20. (declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
  21. ;;; Code:
  22. (require 'ivy)
  23. (require 'org-ref-bibtex)
  24. (require 'org-ref-citeproc)
  25. (require 'bibtex-completion)
  26. ;; This lets you customize how the completion for ivy is displayed. The default
  27. ;; is in the minibuffer. You may like to see something more like a popup though.
  28. (defcustom org-ref-ivy-display-function nil
  29. "ivy function to display completion with.
  30. Set to `ivy-display-function-overlay' to get popups at point."
  31. :type 'function
  32. :group 'org-ref)
  33. (when org-ref-ivy-display-function
  34. (add-to-list 'ivy-display-functions-alist
  35. `(org-ref-ivy-insert-cite-link . ,org-ref-ivy-display-function))
  36. (add-to-list 'ivy-display-functions-alist
  37. `(org-ref-ivy-insert-label-link . ,org-ref-ivy-display-function))
  38. (add-to-list 'ivy-display-functions-alist
  39. `(org-ref-ivy-insert-ref-link . ,org-ref-ivy-display-function)))
  40. (defvar org-ref-cite-types)
  41. (defvar org-ref-show-citation-on-enter)
  42. (defvar org-ref-ivy-cite-marked-candidates '()
  43. "Holds entries marked in `org-ref-ivy-insert-cite-link'.")
  44. ;;;###autoload
  45. (defun org-ref-ivy-cite-completion ()
  46. "Use ivy for completion."
  47. (interactive)
  48. ;; Define core functions for org-ref
  49. (setq org-ref-insert-link-function 'org-ref-insert-link
  50. org-ref-insert-cite-function 'org-ref-ivy-insert-cite-link
  51. org-ref-insert-label-function 'org-ref-ivy-insert-label-link
  52. org-ref-insert-ref-function 'org-ref-ivy-insert-ref-link
  53. org-ref-cite-onclick-function (lambda (_) (org-ref-cite-hydra/body))))
  54. (org-ref-ivy-cite-completion)
  55. (define-key org-mode-map
  56. (kbd org-ref-insert-cite-key)
  57. org-ref-insert-link-function)
  58. (defun or-looking-forward-cite ()
  59. "Return if point is in the position before a citation."
  60. (save-excursion
  61. (forward-char)
  62. (-contains? org-ref-cite-types
  63. (org-element-property
  64. :type
  65. (org-element-context)))))
  66. (defun or-looking-back-cite ()
  67. "Return if point is in the position after a citation."
  68. (save-excursion
  69. (forward-char -1)
  70. (-contains? org-ref-cite-types
  71. (org-element-property
  72. :type
  73. (org-element-context)))))
  74. (defun or-ivy-bibtex-insert-cite (entry)
  75. "Insert a citation for ENTRY.
  76. If `org-ref-ivy-cite-marked-candidates' is non-nil then they are added instead of ENTRY.
  77. ENTRY is selected from `orhc-bibtex-candidates'."
  78. (with-ivy-window
  79. (if org-ref-ivy-cite-marked-candidates
  80. (cl-loop for entry in org-ref-ivy-cite-marked-candidates
  81. do
  82. (if ivy-current-prefix-arg
  83. (let ((org-ref-default-citation-link (ivy-read "Type: " org-ref-cite-types)))
  84. (org-ref-insert-key-at-point (list (cdr (assoc "=key=" entry)))))
  85. (org-ref-insert-key-at-point (list (cdr (assoc "=key=" entry))))))
  86. (if ivy-current-prefix-arg
  87. (let ((org-ref-default-citation-link (ivy-read "Type: " org-ref-cite-types)))
  88. (org-ref-insert-key-at-point (list (cdr (assoc "=key=" entry)))))
  89. (org-ref-insert-key-at-point (list (cdr (assoc "=key=" entry))))))))
  90. (defun or-ivy-bibtex-open-pdf (entry)
  91. "Open the pdf associated with ENTRY.
  92. ENTRY is selected from `orhc-bibtex-candidates'."
  93. (with-ivy-window
  94. (let ((pdf (expand-file-name
  95. (format "%s.pdf"
  96. (cdr (assoc "=key=" entry)))
  97. org-ref-pdf-directory)))
  98. (if (file-exists-p pdf)
  99. (org-open-file pdf)
  100. (message "No pdf found for %s" (cdr (assoc "=key=" entry)))))))
  101. (defun or-ivy-bibtex-open-notes (entry)
  102. "Open the notes associated with ENTRY.
  103. ENTRY is selected from `orhc-bibtex-candidates'."
  104. (with-ivy-window
  105. (org-ref-open-notes-at-point
  106. (cdr (assoc "=key=" entry)))))
  107. (defun or-ivy-bibtex-open-entry (entry)
  108. "Open the bibtex file at ENTRY.
  109. ENTRY is selected from `orhc-bibtex-candidates'."
  110. (find-file (cdr (assoc "bibfile" entry)))
  111. (goto-char (cdr (assoc "position" entry)))
  112. (bibtex-beginning-of-entry))
  113. (defun or-ivy-bibtex-copy-entry (entry)
  114. "Copy selected bibtex ENTRY to the clipboard."
  115. (with-temp-buffer
  116. (save-window-excursion
  117. (or-ivy-bibtex-open-entry entry)
  118. (bibtex-copy-entry-as-kill))
  119. (bibtex-yank)
  120. (kill-region (point-min) (point-max))))
  121. (defun or-ivy-bibtex-open-url (entry)
  122. "Open the URL associated with ENTRY.
  123. ENTRY is selected from `orhc-bibtex-candidates'."
  124. (let ((url (cdr (assoc "url" entry))))
  125. (if url
  126. (browse-url url)
  127. (message "No url found for %s" (cdr (assoc "=key=" entry))))))
  128. (defun or-ivy-bibtex-open-doi (entry)
  129. "Open the DOI associated with ENTRY.
  130. ENTRY is selected from `orhc-bibtex-candidates'."
  131. (let ((doi (cdr (assoc "doi" entry))))
  132. (if doi
  133. (browse-url (format "http://dx.doi.org/%s" doi))
  134. (message "No doi found for %s" (cdr (assoc "=key=" entry))))))
  135. (defun or-ivy-bibtex-set-keywords (entry)
  136. "Prompt for keywords, and put them on the selected ENTRY."
  137. (let ((keywords (read-string "Keyword(s) comma-separated: " ))
  138. entry-keywords)
  139. (save-window-excursion
  140. (or-ivy-bibtex-open-entry entry)
  141. (setq entry-keywords (bibtex-autokey-get-field "keywords"))
  142. (bibtex-set-field
  143. "keywords"
  144. (if (> (length entry-keywords) 0)
  145. (concat entry-keywords ", " keywords)
  146. keywords)))))
  147. (defun or-ivy-bibtex-email-entry (entry)
  148. "Insert selected ENTRY and attach pdf file to an email.
  149. Create email unless called from an email."
  150. (with-ivy-window
  151. (let ((goto-to nil))
  152. (unless (memq major-mode '(message-mode mu4e-compose-mode))
  153. (setq goto-to t)
  154. (compose-mail)
  155. (message-goto-body))
  156. (save-window-excursion
  157. (or-ivy-bibtex-open-entry entry)
  158. (bibtex-copy-entry-as-kill))
  159. (insert (pop bibtex-entry-kill-ring))
  160. (insert "\n")
  161. (let ((pdf (expand-file-name
  162. (format "%s.pdf"
  163. (cdr (assoc "=key=" entry)))
  164. org-ref-pdf-directory)))
  165. (if (file-exists-p pdf)
  166. (mml-attach-file pdf)))
  167. (when goto-to
  168. (message-goto-to)))))
  169. (defun or-ivy-bibtex-formatted-citation (entry)
  170. "Return string containing formatted citations for ENTRY.
  171. This uses a citeproc library."
  172. (let ((enable-recursive-minibuffers t))
  173. (ivy-read "Style: " '("unsrt" "author-year")
  174. :action 'load-library
  175. :require-match t
  176. :preselect "unsrt"
  177. :caller 'or-ivy-formatted-citation)
  178. (format "%s\n\n" (orhc-formatted-citation entry))))
  179. (defun or-ivy-bibtex-insert-formatted-citation (entry)
  180. "Insert formatted citations at point for selected entries."
  181. (with-ivy-window
  182. (insert (mapconcat
  183. 'identity
  184. (cl-loop for entry in (or org-ref-ivy-cite-marked-candidates (list entry))
  185. collect (org-ref-format-bibtex-entry entry))
  186. "\n\n"))))
  187. (defun or-ivy-bibtex-copy-formatted-citation (entry)
  188. "Copy formatted citation to clipboard for ENTRY."
  189. (kill-new (org-ref-format-entry entry)))
  190. (defun or-ivy-bibtex-add-entry (_)
  191. "Open a bibliography file and move point to the end, in order
  192. to add a new bibtex entry. The arg is selected from
  193. `orhc-bibtex-candidates' but ignored."
  194. (ivy-read "bibtex file: " org-ref-bibtex-files
  195. :require-match t
  196. :action 'find-file
  197. :caller 'or-ivy-bibtex-add-entry)
  198. (widen)
  199. (goto-char (point-max))
  200. (unless (bolp)
  201. (insert "\n")))
  202. (defvar org-ref-ivy-cite-actions
  203. '(("b" or-ivy-bibtex-open-entry "Open bibtex entry")
  204. ("B" or-ivy-bibtex-copy-entry "Copy bibtex entry")
  205. ("p" or-ivy-bibtex-open-pdf "Open pdf")
  206. ("n" or-ivy-bibtex-open-notes "Open notes")
  207. ("u" or-ivy-bibtex-open-url "Open url")
  208. ("d" or-ivy-bibtex-open-doi "Open doi")
  209. ("k" or-ivy-bibtex-set-keywords "Add keywords")
  210. ("e" or-ivy-bibtex-email-entry "Email entry")
  211. ("f" or-ivy-bibtex-insert-formatted-citation "Insert formatted citation")
  212. ("F" or-ivy-bibtex-copy-formatted-citation "Copy formatted citation")
  213. ("a" or-ivy-bibtex-add-entry "Add bibtex entry"))
  214. "List of additional actions for `org-ref-ivy-insert-cite-link' (the default action being to insert a citation).")
  215. (defvar org-ref-ivy-cite-re-builder 'ivy--regex-ignore-order
  216. "Regex builder to use in `org-ref-ivy-insert-cite-link'. Can be set to nil to use Ivy's default).")
  217. (defun org-ref-swap (i j lst)
  218. "Swap index I and J in the list LST."
  219. (let ((tempi (nth i lst)))
  220. (setf (nth i lst) (nth j lst))
  221. (setf (nth j lst) tempi))
  222. lst)
  223. (defun org-ref-ivy-current ()
  224. (if (boundp 'ivy--current)
  225. ivy--current
  226. (ivy-state-current ivy-last)))
  227. (defun org-ref-ivy-move-up ()
  228. "Move ivy candidate up and update candidates."
  229. (interactive)
  230. (setf (ivy-state-collection ivy-last)
  231. (org-ref-swap ivy--index (1- ivy--index) (ivy-state-collection ivy-last)))
  232. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  233. (ivy--reset-state ivy-last))
  234. (defun org-ref-ivy-move-down ()
  235. "Move ivy candidate down."
  236. (interactive)
  237. (setf (ivy-state-collection ivy-last)
  238. (org-ref-swap ivy--index (1+ ivy--index) (ivy-state-collection ivy-last)))
  239. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  240. (ivy--reset-state ivy-last))
  241. (defun org-ref-ivy-sort-year-ascending ()
  242. "Sort entries by year in ascending order."
  243. (interactive)
  244. (setf (ivy-state-collection ivy-last)
  245. (cl-sort (copy-sequence (ivy-state-collection ivy-last))
  246. (lambda (a b)
  247. (let ((y1 (string-to-number (or (cdr (assoc "year" a)) "0")))
  248. (y2 (string-to-number (or (cdr (assoc "year" b)) "0"))))
  249. (< y1 y2)))))
  250. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  251. (ivy--reset-state ivy-last))
  252. (defun org-ref-ivy-sort-year-descending ()
  253. "sort entries by year in descending order."
  254. (interactive)
  255. (setf (ivy-state-collection ivy-last)
  256. (cl-sort (copy-sequence (ivy-state-collection ivy-last))
  257. (lambda (a b)
  258. (let ((y1 (string-to-number (or (cdr (assoc "year" a)) "0")))
  259. (y2 (string-to-number (or (cdr (assoc "year" b)) "0"))))
  260. (> y1 y2)))))
  261. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  262. (ivy--reset-state ivy-last))
  263. ;; * marking candidates
  264. (defun org-ref-ivy-mark-candidate ()
  265. "Add current candidate to `org-ref-ivy-cite-marked-candidates'.
  266. If candidate is already in, remove it."
  267. (interactive)
  268. (let ((cand (or (assoc (org-ref-ivy-current) (ivy-state-collection ivy-last))
  269. (org-ref-ivy-current))))
  270. (if (-contains? org-ref-ivy-cite-marked-candidates cand)
  271. ;; remove it from the marked list
  272. (setq org-ref-ivy-cite-marked-candidates
  273. (-remove-item cand org-ref-ivy-cite-marked-candidates))
  274. ;; add to list
  275. (setq org-ref-ivy-cite-marked-candidates
  276. (append org-ref-ivy-cite-marked-candidates (list cand)))))
  277. (ivy-next-line))
  278. (defun org-ref-ivy-show-marked-candidates ()
  279. "Show marked candidates."
  280. (interactive)
  281. (setf (ivy-state-collection ivy-last) org-ref-ivy-cite-marked-candidates)
  282. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  283. (ivy--reset-state ivy-last))
  284. (defun org-ref-ivy-show-all ()
  285. "Show all the candidates."
  286. (interactive)
  287. (setf (ivy-state-collection ivy-last)
  288. (orhc-bibtex-candidates))
  289. (ivy--reset-state ivy-last))
  290. ;; * org-ref-cite keymap
  291. (defvar org-ref-ivy-cite-keymap
  292. (let ((map (make-sparse-keymap)))
  293. (define-key map (kbd "C-<SPC>") 'org-ref-ivy-mark-candidate)
  294. (define-key map (kbd "C-,") 'org-ref-ivy-show-marked-candidates)
  295. (define-key map (kbd "C-.") 'org-ref-ivy-show-all)
  296. (define-key map (kbd "C-<up>") 'org-ref-ivy-move-up)
  297. (define-key map (kbd "C-<down>") 'org-ref-ivy-move-down)
  298. (define-key map (kbd "C-y") 'org-ref-ivy-sort-year-ascending)
  299. (define-key map (kbd "C-M-y") 'org-ref-ivy-sort-year-descending)
  300. (define-key map (kbd "C-k") (lambda ()
  301. (interactive)
  302. (beginning-of-line)
  303. (kill-visual-line)
  304. (setf (ivy-state-collection ivy-last)
  305. (orhc-bibtex-candidates))
  306. (setf (ivy-state-preselect ivy-last) (org-ref-ivy-current))
  307. (ivy--reset-state ivy-last)))
  308. (define-key map (kbd "C-<return>")
  309. (lambda ()
  310. "Apply action and move to next/previous candidate."
  311. (interactive)
  312. (ivy-call)
  313. (ivy-next-line)))
  314. ;; (define-key ivy-minibuffer-map (kbd "M-<return>")
  315. ;; (lambda ()
  316. ;; "Apply default action to all marked candidates."
  317. ;; (interactive)
  318. ;; (mapc (ivy--get-action ivy-last)
  319. ;; org-ref-ivy-cite-marked-candidates)
  320. ;; (ivy-exit-with-action (function (lambda (_) nil)))))
  321. map)
  322. "A key map for `org-ref-ivy-insert-cite-link'.")
  323. (ivy-set-actions
  324. 'org-ref-ivy-insert-cite-link
  325. org-ref-ivy-cite-actions)
  326. (defun org-ref-ivy-insert-cite-link (&optional arg)
  327. "ivy function for interacting with bibtex.
  328. Uses `org-ref-find-bibliography' for bibtex sources, unless a
  329. prefix ARG is used, which uses `org-ref-default-bibliography'."
  330. (interactive "P")
  331. (setq org-ref-bibtex-files (if arg
  332. org-ref-default-bibliography
  333. (org-ref-find-bibliography)))
  334. (setq org-ref-ivy-cite-marked-candidates '())
  335. (ivy-read "Open: " (orhc-bibtex-candidates)
  336. :require-match t
  337. :keymap org-ref-ivy-cite-keymap
  338. :re-builder org-ref-ivy-cite-re-builder
  339. :action 'or-ivy-bibtex-insert-cite
  340. :caller 'org-ref-ivy-insert-cite-link))
  341. (defun org-ref-ivy-cite-transformer (s)
  342. "Make entry red if it is marked."
  343. (let* ((fill-column (frame-width))
  344. (fill-prefix " ")
  345. (wrapped-s (with-temp-buffer
  346. (insert s)
  347. (fill-paragraph)
  348. (buffer-string))))
  349. (if (-contains?
  350. (if (listp (car org-ref-ivy-cite-marked-candidates))
  351. (mapcar 'car org-ref-ivy-cite-marked-candidates)
  352. org-ref-ivy-cite-marked-candidates)
  353. s)
  354. (propertize wrapped-s 'face 'font-lock-warning-face)
  355. (propertize wrapped-s 'face nil))))
  356. (ivy-set-display-transformer
  357. 'org-ref-ivy-insert-cite-link
  358. 'org-ref-ivy-cite-transformer )
  359. (defun org-ref-ivy-insert-label-link ()
  360. "Insert a label with ivy."
  361. (interactive)
  362. (insert
  363. (concat (if (not (looking-back "label:" 6)) "label:" "")
  364. (ivy-read "label: " (org-ref-get-labels)
  365. :caller 'org-ref-ivy-insert-label-link))))
  366. (defun org-ref-ivy-insert-ref-link ()
  367. "Insert a ref link with ivy.
  368. Use a prefix arg to select the ref type."
  369. (interactive)
  370. (let ((label (ivy-read "label: " (org-ref-get-labels) :require-match t
  371. :caller 'org-ref-ivy-insert-ref-link)))
  372. (cond
  373. ;; from a colon insert
  374. ((looking-back ":" 1)
  375. (insert label))
  376. ;; non-default
  377. (ivy-current-prefix-arg
  378. (insert
  379. (ivy-read "type: " org-ref-ref-types)
  380. ":"
  381. label))
  382. ;; default
  383. (t
  384. (insert
  385. (or (when (looking-at "$") " ") "")
  386. (concat org-ref-default-ref-type
  387. ":"
  388. label))))))
  389. (require 'hydra)
  390. (setq hydra-is-helpful t)
  391. (defhydra org-ref-cite-hydra (:color blue)
  392. "
  393. _p_: Open pdf _w_: WOS _g_: Google Scholar _K_: Copy citation to clipboard
  394. _u_: Open url _r_: WOS related _P_: Pubmed _k_: Copy key to clipboard
  395. _n_: Open notes _c_: WOS citing _C_: Crossref _f_: Copy formatted entry
  396. _o_: Open entry _e_: Email entry ^ ^ _q_: quit
  397. _i_: Insert cite _h_: change type
  398. "
  399. ("o" org-ref-open-citation-at-point nil)
  400. ("p" org-ref-open-pdf-at-point nil)
  401. ("n" org-ref-open-notes-at-point nil)
  402. ("u" org-ref-open-url-at-point nil)
  403. ("w" org-ref-wos-at-point nil)
  404. ("r" org-ref-wos-related-at-point nil)
  405. ("c" org-ref-wos-citing-at-point nil)
  406. ("g" org-ref-google-scholar-at-point nil)
  407. ("P" org-ref-pubmed-at-point nil)
  408. ("C" org-ref-crossref-at-point nil)
  409. ("K" org-ref-copy-entry-as-summary nil)
  410. ("k" (progn
  411. (kill-new
  412. (car (org-ref-get-bibtex-key-and-file))))
  413. nil)
  414. ("f" (kill-new
  415. (org-ref-format-entry (org-ref-get-bibtex-key-under-cursor)))
  416. nil)
  417. ("e" (kill-new (save-excursion
  418. (org-ref-open-citation-at-point)
  419. (org-ref-email-bibtex-entry)))
  420. nil)
  421. ("i" (funcall org-ref-insert-cite-function))
  422. ("h" org-ref-change-cite-type)
  423. ("q" nil))
  424. (defun org-ref-ivy-onclick-actions ()
  425. "An alternate click function that uses ivy for action selection.
  426. Each action is taken from `org-ref-ivy-cite-actions'. Each action
  427. should act on a bibtex entry that matches the key in
  428. `orhc-bibtex-candidates'. Set `org-ref-cite-onclick-function' to
  429. this function to use it."
  430. (interactive)
  431. (ivy-read
  432. "action: "
  433. (cl-loop for i from 0
  434. for (_ func s) in
  435. org-ref-ivy-cite-actions
  436. collect (cons (format "%2s. %s" i s) func))
  437. :action (lambda (f)
  438. (let* ((key (car (org-ref-get-bibtex-key-and-file)))
  439. (entry (cdr (elt (orhc-bibtex-candidates)
  440. (-elem-index
  441. key
  442. (cl-loop for entry in (orhc-bibtex-candidates)
  443. collect (cdr (assoc "=key=" entry ))))))))
  444. (funcall f entry)))))
  445. ;; * org-ref-ivy-set-keywords
  446. (defvar org-ref-ivy-set-keywords-keymap
  447. (let ((map (make-sparse-keymap)))
  448. (define-key map (kbd "C-<SPC>") 'org-ref-ivy-mark-candidate)
  449. (define-key map (kbd "C-,") 'org-ref-ivy-show-marked-candidates)
  450. (define-key map (kbd "C-.") 'org-ref-ivy-show-all)
  451. (define-key map (kbd "C-<up>") 'org-ref-ivy-move-up)
  452. (define-key map (kbd "C-<down>") 'org-ref-ivy-move-down)
  453. map)
  454. "A key map for `org-ref-ivy-set-keywords'.")
  455. (defun org-ref-ivy-set-keywords ()
  456. "Add keywords to bibtex entries selected by org-ref-ivy."
  457. (interactive)
  458. (setq org-ref-ivy-cite-marked-candidates '())
  459. (ivy-read "Keywords: " (org-ref-bibtex-keywords)
  460. :keymap org-ref-ivy-set-keywords-keymap
  461. :caller 'org-ref-ivy-set-keywords
  462. :action (lambda (key)
  463. (org-ref-set-bibtex-keywords
  464. (mapconcat
  465. 'identity
  466. (or org-ref-ivy-cite-marked-candidates (list key))
  467. ", ")))))
  468. (ivy-set-display-transformer
  469. 'org-ref-ivy-set-keywords
  470. 'org-ref-ivy-cite-transformer)
  471. (provide 'org-ref-ivy-cite)
  472. ;;; org-ref-ivy-cite.el ends here