This repository was archived by the owner on Feb 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinspirehep-pdf.el
168 lines (143 loc) · 9.58 KB
/
inspirehep-pdf.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
;;; inspirehep-pdf.el --- Companion to use inspirehep.el with pdf-mode -*- lexical-binding: t; -*-
;;
;; Created: March 09, 2022
;; Modified: March 09, 2022
;; Version: 0.0.1
;; Keywords: bib docs
;; Homepage: https://github.com/aikrahguzar/inspirehep.el
;; Package-Requires: ((emacs "27.1"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;; A companion package to use the search provided by inspirehep.el with the
;; pdf-mode.
;;
;;; Code:
(require 'inspirehep)
(require 'pdf-tools)
(require 'pdf-links)
(require 'companion-mode)
;;;; Variables
(defvar inspirehep-pdf-manipulate-record-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "a") #'inspirehep-pdf-search-author)
(define-key map (kbd "b") #'inspirehep-pdf-select-target-buffer)
(define-key map (kbd "B") #'inspirehep-pdf-copy-bibtex)
(define-key map (kbd "c") #'inspirehep-pdf-search-citations)
(define-key map (kbd "d") #'inspirehep-pdf-download-pdf)
(define-key map (kbd "f") #'inspirehep-pdf-search)
(define-key map (kbd "i") #'inspirehep-pdf-insert-bibtex)
(define-key map (kbd "s") #'inspirehep-insert-and-download)
(define-key map (kbd "u") #'inspirehep-pdf-copy-arxiv-url)
(define-key map (kbd "v") #'inspirehep-pdf-view-entry)
(define-key map (kbd "w") #'inspirehep-pdf-save)
map)
"A keymap for commands that act on the inspirehep record from a pdf buffer.")
(defvar-local inspirehep-pdf--references-cache 'uninitialized)
;;;; Utilities
(defun inspirehep-pdf--parse-ref (str) "Parse STR into a list of references."
(cond ((string-match-p "," str) (seq-mapcat #'inspirehep-pdf--parse-ref (split-string str "," t (rx space))))
((string-match-p "[–—-]" str) (let ((numlist (seq-map #'string-to-number (split-string str "[–—-]"))))
(seq-map #'number-to-string (number-sequence (car numlist) (cadr numlist)))))
(t (list str))))
(defun inspirehep-pdf--refs-on-page (page) "Find the references on the PAGE of the pdf."
(let ((page-text (pdf-info-gettext page '(0 0 1 1)))
(beg-pos 1) (refs))
(while (string-match (rx space ?\[ (0+ (or alnum ?- ?– ?— ?, space)) ?\]) page-text beg-pos)
(setq beg-pos (match-end 0))
(cl-callf2 seq-concatenate 'list refs (inspirehep-pdf--parse-ref (string-trim (match-string 0 page-text) (rx (0+ space) ?\[) (rx (0+ space) ?\])))))
refs))
(defun inspirehep-pdf--find-references () "Return the list of the references with annotation."
(let ((this-page (pdf-info-number-of-pages))
(refs nil))
(while this-page (cl-callf2 map-merge 'hash-table refs (inspirehep-pdf--format-refs-on-page this-page))
(setq this-page (unless (or (pdf-info-search-regexp "^[[:space:]]*References[[:space:]]*$" this-page) (= this-page 1)) (1- this-page))))
(setq inspirehep-pdf--references-cache refs)))
;; (rxt-elisp-to-pcre (rx line-start (0+ space) "References" (0+ space) line-end))
(defun inspirehep-pdf--format-refs-on-page (page) "Format the references on the PAGE for disaply in `completing-read'."
(let ((refs (pdf-info-search-regexp "^[[:space:]]*\\[[[:alnum:]]+\\][[:space:]]+.*$" page)))
(seq-map (lambda (res) (let* ((text (substring-no-properties (map-elt res 'text)))
(ann (progn (string-match (rx (0+ space) ?\[ (0+ alnum) ?\]) text) (substring text (match-end 0))))
(key (string-trim (match-string 0 text) (rx (0+ space) ?\[) (rx (0+ space) ?\]))))
(cons key (concat (propertize (truncate-string-to-width key 16 nil ?\s) 'face 'bold)
(propertize (string-trim ann) 'face 'font-lock-comment-face)))))
refs)))
;; (rxt-elisp-to-pcre (rx line-start (0+ space) ?\[ (1+ alnum) ?\] (0+ not-newline) line-end))
(defun inspirehep-pdf--select-ref (refs) "Select a reference among REFS using completing read."
(when (eq inspirehep-pdf--references-cache 'uninitialized) (inspirehep-pdf--find-references))
(car (split-string (completing-read "Select a reference: "
(seq-map (lambda (cand) (gethash cand inspirehep-pdf--references-cache (propertize cand 'face 'bold))) refs)))))
;;;; Searching from a PDFView buffer
;;;###autoload
(defun inspirehep-pdf-uri-action (link)
"If the LINK is from arxiv search inspire else use default action.
This functions can be used as a replacement for `pdf-links-browse-uri-function'"
(let* ((url (url-generic-parse-url link))
(host (url-host url))
(file (url-filename url))
(arxiv-id (when (string-match-p "arxiv" host) (string-remove-prefix "/abs/" file)))
(doi (when (string-match-p "doi" host) (string-remove-prefix "/" file))))
(if arxiv-id (inspirehep-lookup (concat "arxiv:" arxiv-id))
(if doi (inspirehep-lookup (concat "doi:" doi)) (pdf-links-browse-uri-default link)))))
;;;###autoload
(defun inspirehep-pdf-search (char str) "Search inspire for the selected STR choosing type based on CHAR."
(interactive (list (read-char "Search for selections as: [a] Author [d] DOI [e] Edit [t] Title [x] arXiv id (Otherwise freeform)")
(car (pdf-view-active-region-text))))
(if (equal char ?e) (inspirehep-lookup (read-string "InspireHEP: " str))
(inspirehep-lookup (concat (pcase char (?a "a \"") (?d "doi:") (?t "t \"") (?x "arxiv:") (_ "")) str "\"")))
(pdf-view-deactivate-region))
(defun inspirehep-arxiv-id () "Get the arxiv id from the first page of an arxiv paper."
(when-let ((str (map-elt (car (pdf-info-search-string "arXiv" 1)) 'text))
(beg (+ 6 (string-match "arXiv" str))))
(substring-no-properties str beg (string-match "v" str beg))))
;;;###autoload
(defun inspirehep-pdf-copy-arxiv-url () "Copy arxiv url using the id on first page or inspire record." (interactive)
(if-let (id (inspirehep-arxiv-id))
(kill-new (concat "https://arxiv.org/abs/" id))
(or (companion-mode-with-companion (save-excursion (goto-char (point-min)) (inspirehep-copy-arxiv-url)))
(message "No arxiv url found for this pdf."))))
;;;###autoload
(defun inspirehep-pdf-get-record () "Get the inspire record using the arxiv id on the first page." (interactive)
(if-let ((id (inspirehep-arxiv-id))
(url (concat "https://inspirehep.net/api/arxiv/" id))
(buf (get-buffer-create (concat "* INSPIREHEP PDF *" id))))
(progn (with-current-buffer buf (inspirehep-mode)) (inspirehep--lookup-url url 'single-record (concat "arxiv:" id) buf) buf)
(message "No arxiv id found for the current PDF.") nil))
;;;###autoload
(defun inspirehep-pdf-companion-hook () "Hook to add to `pdf-view-mode'."
(companion-mode-set #'inspirehep-pdf-get-record nil t))
;;;; Acting on the buffer showing the record
(defun inspirehep-pdf-jump-to-reference (ref) "Jump to reference REF in the record buffer."
(interactive (list (pcase current-prefix-arg
(0 (string-join (pdf-view-active-region-text)))
((pred numberp) (number-to-string current-prefix-arg))
(_ (inspirehep-pdf--select-ref (inspirehep-pdf--refs-on-page (pdf-view-current-page)))))))
(companion-mode-with-companion nil #'inspirehep-hide-jump-show ref t))
;;;;;; Bibtex and download
(defun inspirehep-pdf-copy-bibtex () "Insert bibtex for the currently selected entry into the target buffer." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-copy-bibtex)))
(defun inspirehep-pdf-insert-bibtex () "Insert bibtex for the currently selected entry into the target buffer." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-insert-bibtex)))
(defun inspirehep-pdf-download-pdf () "Download the pdf for the currently selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-download-pdf)))
(defun inspirehep-pdf-insert-and-download () "Download the pdf and insert bibtex for the currently selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-insert-and-download)))
(defun inspirehep-pdf-select-target-buffer () "Select the buffer to insert bibtex entries in." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-select-target-buffer)))
;;;;;; Search
(defun inspirehep-pdf-search-author () "Search an author of the selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-search-author)))
(defun inspirehep-pdf-search-citations () "Search for literature citing the selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-search-citations)))
(defun inspirehep-pdf-view-entry () "View detailed record for the selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-view-entry)))
;;;; Misc
(defun inspirehep-pdf-toggle-details () "Toggle details for the selected entry." (interactive)
(companion-mode-with-companion (call-interactively #'inspirehep-toggle-details)))
(defun inspirehep-pdf-save () "Save the current file." (interactive)
(let ((name (companion-mode-with-companion
(save-excursion (goto-char (point-min)) (inspirehep-lookup-at-point 'filename)))))
(write-file (expand-file-name name inspirehep-download-directory))))
(provide 'inspirehep-pdf)
;;; inspirehep-pdf.el ends here