mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-21 7074318d7ab58aca124f590c42fd820e8eb258a5
commit | author | age
76bbd0 1 ;;; x86-lookup.el --- jump to x86 instruction documentation -*- lexical-binding: t; -*-
C 2
3 ;; This is free and unencumbered software released into the public domain.
4
5 ;; Author: Christopher Wellons <wellons@nullprogram.com>
6 ;; URL: https://github.com/skeeto/x86-lookup
7 ;; Package-Version: 20180528.1635
8 ;; Version: 1.2.0
9 ;; Package-Requires: ((emacs "24.3") (cl-lib "0.3"))
10
11 ;;; Commentary:
12
13 ;; Requires the following:
14 ;; * pdftotext command line program from Poppler
15 ;; * Intel 64 and IA-32 Architecture Software Developer Manual PDF
16
17 ;; http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
18
19 ;; Building the index specifically requires Poppler's pdftotext, not
20 ;; just any PDF to text converter. It has a critical feature over the
21 ;; others: conventional line feed characters (U+000C) are output
22 ;; between pages, allowing precise tracking of page numbers. These are
23 ;; the markers Emacs uses for `forward-page' and `backward-page'.
24
25 ;; Your copy of the manual must contain the full instruction set
26 ;; reference in a single PDF. Set `x86-lookup-pdf' to this file name.
27 ;; Intel optionally offers the instruction set reference in two
28 ;; separate volumes, but don't use that.
29
30 ;; Choose a PDF viewer by setting `x86-lookup-browse-pdf-function'. If
31 ;; you provide a custom function, your PDF viewer should support
32 ;; linking to a specific page (e.g. not supported by xdg-open,
33 ;; unfortunately). Otherwise there's no reason to use this package.
34
35 ;; Once configured, the main entrypoint is `x86-lookup'. You may want
36 ;; to bind this to a key. The interactive prompt will default to the
37 ;; mnemonic under the point. Here's a suggestion:
38
39 ;;   (global-set-key (kbd "C-h x") #'x86-lookup)
40
41 ;; This package pairs well with `nasm-mode'!
42
43 ;;; Code
44
45 (require 'cl-lib)
46 (require 'doc-view)
47
48 (defgroup x86-lookup ()
49   "Options for x86 instruction set lookup."
50   :group 'extensions)
51
52 (defcustom x86-lookup-pdf nil
53   "Path to Intel's manual containing the instruction set reference."
54   :group 'x86-lookup
55   :type '(choice (const nil)
56                  (file :must-match t)))
57
58 (defcustom x86-lookup-pdftotext-program "pdftotext"
59   "Path to pdftotext, part of Popper."
60   :group 'x86-lookup
61   :type 'string)
62
63 (defcustom x86-lookup-browse-pdf-function #'x86-lookup-browse-pdf-any
64   "A function that launches a PDF viewer at a specific page.
65 This function accepts two arguments: filename and page number."
66   :group 'x86-lookup
67   :type '(choice (function-item :tag "First suitable PDF reader" :value
68                                 x86-lookup-browse-pdf-any)
69                  (function-item :tag "Evince" :value
70                                 x86-lookup-browse-pdf-evince)
71                  (function-item :tag "Xpdf" :value
72                                 x86-lookup-browse-pdf-xpdf)
73                  (function-item :tag "Okular" :value
74                                 x86-lookup-browse-pdf-okular)
75                  (function-item :tag "gv" :value
76                                 x86-lookup-browse-pdf-gv)
77                  (function-item :tag "zathura" :value
78                                 x86-lookup-browse-pdf-zathura)
79                  (function-item :tag "MuPDF" :value
80                                 x86-lookup-browse-pdf-mupdf)
81                  (function-item :tag "Sumatra PDF" :value
82                                 x86-lookup-browse-pdf-sumatrapdf)
83                  (function-item :tag "browse-url"
84                                 :value x86-lookup-browse-pdf-browser)
85                  (function :tag "Your own function")))
86
87 (defcustom x86-lookup-cache-directory
88   (let ((base (or (getenv "XDG_CACHE_HOME")
89                   (getenv "LocalAppData")
90                   "~/.cache")))
91     (expand-file-name "x86-lookup" base))
92   "Directory where the PDF mnemonic index with be cached."
93   :type 'string)
94
95 (defvar x86-lookup-index nil
96   "Alist mapping instructions to page numbers.")
97
98 (defvar x86-lookup--expansions
99   '(("^PREFETCH\\(h\\)$"
100      "" "nta" "t0" "t1" "t2")
101     ("^J\\(cc\\)$"
102      "a" "ae" "b" "be" "c" "cxz" "e" "ecxz" "g" "ge" "l" "le" "na" "nae" "nb"
103      "nbe" "nc" "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe"
104      "po" "rcxz" "s" "z")
105     ("^SET\\(cc\\)$"
106      "a" "ae" "b" "be" "c" "e" "g" "ge" "l" "le" "na" "nae" "nb" "nbe" "nc"
107      "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe" "po" "s" "z")
108     ("^CMOV\\(cc\\)$"
109      "a" "ae" "b" "be" "c" "e" "g" "ge" "l" "le" "na" "nae" "nb" "nbe" "nc"
110      "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe" "po" "s" "z")
111     ("^FCMOV\\(cc\\)$"
112      "b" "e" "be" "u" "nb" "ne" "nbe" "nu")
113     ("^LOOP\\(cc\\)$"
114      "e" "ne")
115     ("^VBROADCAST\\(\\)$"
116      "" "ss" "sd" "f128")
117     ("^VMASKMOV\\(\\)$"
118      "" "ps" "pd")
119     ("^VPBROADCAST\\(\\)$"
120      "" "b" "w" "d" "q" "I128")
121     ("^VPMASKMOV\\(\\)$"
122      "" "d" "q")
123     ("\\(\\)" ; fallback "match"
124      ""))
125   "How to expand mnemonics into multiple mnemonics.")
126
127 (defun x86-lookup--expand (names page)
128   "Expand string of PDF-sourced mnemonics into user-friendly mnemonics."
129   (let ((case-fold-search nil)
130         (rev-string-match-p (lambda (s re) (string-match re s))))
131     (save-match-data
132       (cl-loop for mnemonic-raw in (split-string names " */ *")
133                ;; Collapse "int 3" and "int n" into "int"
134                for mnemonic = (replace-regexp-in-string " .+$" "" mnemonic-raw)
135                for (_ . tails) = (cl-assoc mnemonic x86-lookup--expansions
136                                            :test rev-string-match-p)
137                nconc (cl-loop for tail in tails
138                               for rep = (replace-match tail nil nil mnemonic 1)
139                               collect (cons (downcase rep) page))))))
140
141 (cl-defun x86-lookup-create-index (&optional (pdf x86-lookup-pdf))
142   "Create an index alist from PDF mapping mnemonics to page numbers.
143 This function requires the pdftotext command line program."
144   (let ((mnemonic (concat "\\(?:.*\n\n?\\)?"
145                           "\\([[:alnum:]/[:blank:]]+\\)[[:blank:]]*"
146                           "\\(?:--\\|—\\)\\(?:.*\n\n?\\)\\{1,3\\}"
147                           "[[:blank:]]*Opcode"))
148         (coding-system-for-read 'utf-8)
149         (coding-system-for-write 'utf-8)
150         (case-fold-search t))
151     (with-temp-buffer
152       (call-process x86-lookup-pdftotext-program nil t nil
153                     (file-truename pdf) "-")
154       (setf (point) (point-min))
155       (cl-loop for page upfrom 1
156                while (< (point) (point-max))
157                when (looking-at mnemonic)
158                nconc (x86-lookup--expand (match-string 1) page) into index
159                do (forward-page)
160                finally (cl-return
161                         (cl-remove-duplicates
162                          index :key #'car :test #'string= :from-end t))))))
163
164 (defun x86-lookup--index-file (pdf)
165   "Return index filename from PDF filename."
166   (concat (sha1 pdf) "_v3"))
167
168 (defun x86-lookup--save-index (pdf index)
169   "Save INDEX for PDF in `x86-lookup-cache-directory'."
170   (let* ((index-file (x86-lookup--index-file pdf))
171          (cache-path (expand-file-name index-file x86-lookup-cache-directory)))
172     (mkdir x86-lookup-cache-directory t)
173     (with-temp-file cache-path
174       (prin1 index (current-buffer)))
175     index))
176
177 (defun x86-lookup--load-index (pdf)
178   "Return index PDF from `x86-lookup-cache-directory'."
179   (let* ((index-file (x86-lookup--index-file pdf))
180          (cache-path (expand-file-name index-file x86-lookup-cache-directory)))
181     (when (file-exists-p cache-path)
182       (with-temp-buffer
183         (insert-file-contents cache-path)
184         (setf (point) (point-min))
185         (ignore-errors (read (current-buffer)))))))
186
187 (defun x86-lookup-ensure-index ()
188   "Ensure the PDF index has been created, returning the index."
189   (when (null x86-lookup-index)
190     (cond
191      ((null x86-lookup-pdf)
192       (error "No PDF available. Set `x86-lookup-pdf'."))
193      ((not (file-exists-p x86-lookup-pdf))
194       (error "PDF not found. Check `x86-lookup-pdf'."))
195      ((setf x86-lookup-index (x86-lookup--load-index x86-lookup-pdf))
196       x86-lookup-index)
197      ((progn
198         (message "Generating mnemonic index ...")
199         (setf x86-lookup-index (x86-lookup-create-index))
200         (x86-lookup--save-index x86-lookup-pdf x86-lookup-index)))))
201   x86-lookup-index)
202
203 (defun x86-lookup-ensure-and-update-index ()
204   "Ensure the PDF index has been created and (unconditionally) updated.
205 Useful for forcibly syncing the index with the current PDF without resorting
206 to manual deletion of index file on filesystem."
207   (interactive)
208   (cond
209    ((null x86-lookup-pdf)
210     (error "No PDF available. Set `x86-lookup-pdf'."))
211    ((not (file-exists-p x86-lookup-pdf))
212     (error "PDF not found. Check `x86-lookup-pdf'."))
213    ((message "Generating mnemonic index ...")
214     (setf x86-lookup-index (x86-lookup-create-index))
215     (x86-lookup--save-index x86-lookup-pdf x86-lookup-index)
216     (message "Finished generating mnemonic index."))))
217
218 (defun x86-lookup-browse-pdf (pdf page)
219   "Launch a PDF viewer using `x86-lookup-browse-pdf-function'."
220   (funcall x86-lookup-browse-pdf-function pdf page))
221
222 ;;;###autoload
223 (defun x86-lookup (mnemonic)
224   "Jump to the PDF documentation for MNEMONIC.
225 Defaults to the mnemonic under point."
226   (interactive
227    (progn
228      (x86-lookup-ensure-index)
229      (let* ((mnemonics (mapcar #'car x86-lookup-index))
230             (thing (thing-at-point 'word))
231             (mnemonic (if (member thing mnemonics) thing nil))
232             (prompt (if mnemonic
233                         (format "Mnemonic (default %s): " mnemonic)
234                       "Mnemonic: ")))
235        (list
236         (completing-read prompt mnemonics nil t nil nil mnemonic)))))
237   (let ((page (cdr (assoc mnemonic x86-lookup-index))))
238     (x86-lookup-browse-pdf (file-truename x86-lookup-pdf) page)))
239
240 ;; PDF viewers:
241
242 (defun x86-lookup-browse-pdf-pdf-tools (pdf page)
243   "View PDF at PAGE using Emacs' `pdf-view-mode' and `display-buffer'."
244   (require 'pdf-tools)
245   (prog1 t
246     (with-selected-window (display-buffer (find-file-noselect pdf :nowarn))
247       (with-no-warnings
248         (pdf-view-goto-page page)))))
249
250 (defun x86-lookup-browse-pdf-doc-view (pdf page)
251   "View PDF at PAGE using Emacs' `doc-view-mode' and `display-buffer'."
252   (prog1 t
253     (unless (doc-view-mode-p 'pdf)
254       (error "doc-view not available for PDF"))
255     (with-selected-window (display-buffer (find-file-noselect pdf :nowarn))
256       (doc-view-goto-page page))))
257
258 (defun x86-lookup-browse-pdf-xpdf (pdf page)
259   "View PDF at PAGE using xpdf."
260   (start-process "xpdf" nil "xpdf" "--" pdf (format "%d" page)))
261
262 (defun x86-lookup-browse-pdf-evince (pdf page)
263   "View PDF at PAGE using Evince."
264   (start-process "evince" nil "evince" "-p" (format "%d" page) "--" pdf))
265
266 (defun x86-lookup-browse-pdf-okular (pdf page)
267   "View PDF at PAGE file using Okular."
268   (start-process "okular" nil "okular" "-p" (format "%d" page) "--" pdf))
269
270 (defun x86-lookup-browse-pdf-gv (pdf page)
271   "View PDF at PAGE using gv."
272   (start-process "gv" nil "gv" "-nocenter" (format "-page=%d" page) "--" pdf))
273
274 (defun x86-lookup-browse-pdf-zathura (pdf page)
275   "View PDF at PAGE using zathura."
276   (start-process "zathura" nil "zathura" "-P" (format "%d" page) "--" pdf))
277
278 (defun x86-lookup-browse-pdf-sumatrapdf (pdf page)
279   "View PDF at PAGE using Sumatra PDF."
280   (start-process "sumatrapdf" nil "sumatrapdf" "-page" (format "%d" page) pdf))
281
282 (defun x86-lookup-browse-pdf-mupdf (pdf page)
283   "View PDF at PAGE using MuPDF."
284   ;; MuPDF doesn't have a consistent name across platforms.
285   ;; Furthermore, Debian ships with a broken "mupdf" wrapper shell
286   ;; script and must be avoided. Here we use `executable-find' to
287   ;; avoid calling it as mupdf-x11 on non-X11 platforms.
288   (let ((exe (or (executable-find "mupdf-x11") "mupdf")))
289     (start-process "mupdf" nil exe "--" pdf (format "%d" page))))
290
291 (defun x86-lookup-browse-pdf-browser (pdf page)
292   "Visit PDF using `browse-url' with a fragment for the PAGE."
293   (browse-url (format "file://%s#%d" pdf page)))
294
295 (defun x86-lookup-browse-pdf-any (pdf page)
296   "Try visiting PDF using the first viewer found."
297   (or (ignore-errors (x86-lookup-browse-pdf-pdf-tools pdf page))
298       (ignore-errors (x86-lookup-browse-pdf-doc-view pdf page))
299       (ignore-errors (x86-lookup-browse-pdf-evince pdf page))
300       (ignore-errors (x86-lookup-browse-pdf-xpdf pdf page))
301       (ignore-errors (x86-lookup-browse-pdf-okular pdf page))
302       (ignore-errors (x86-lookup-browse-pdf-gv pdf page))
303       (ignore-errors (x86-lookup-browse-pdf-zathura pdf page))
304       (ignore-errors (x86-lookup-browse-pdf-mupdf pdf page))
305       (ignore-errors (x86-lookup-browse-pdf-sumatrapdf pdf page))
306       (ignore-errors (x86-lookup-browse-pdf-browser pdf page))
307       (error "Could not find a PDF viewer.")))
308
309 (provide 'x86-lookup)
310
311 ;;; x86-lookup.el ends here