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

Chizi123
2018-11-18 76bbd07de7add0f9d13c6914f158d19630fe2f62
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
;;; x86-lookup.el --- jump to x86 instruction documentation -*- lexical-binding: t; -*-
 
;; This is free and unencumbered software released into the public domain.
 
;; Author: Christopher Wellons <wellons@nullprogram.com>
;; URL: https://github.com/skeeto/x86-lookup
;; Package-Version: 20180528.1635
;; Version: 1.2.0
;; Package-Requires: ((emacs "24.3") (cl-lib "0.3"))
 
;;; Commentary:
 
;; Requires the following:
;; * pdftotext command line program from Poppler
;; * Intel 64 and IA-32 Architecture Software Developer Manual PDF
 
;; http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
 
;; Building the index specifically requires Poppler's pdftotext, not
;; just any PDF to text converter. It has a critical feature over the
;; others: conventional line feed characters (U+000C) are output
;; between pages, allowing precise tracking of page numbers. These are
;; the markers Emacs uses for `forward-page' and `backward-page'.
 
;; Your copy of the manual must contain the full instruction set
;; reference in a single PDF. Set `x86-lookup-pdf' to this file name.
;; Intel optionally offers the instruction set reference in two
;; separate volumes, but don't use that.
 
;; Choose a PDF viewer by setting `x86-lookup-browse-pdf-function'. If
;; you provide a custom function, your PDF viewer should support
;; linking to a specific page (e.g. not supported by xdg-open,
;; unfortunately). Otherwise there's no reason to use this package.
 
;; Once configured, the main entrypoint is `x86-lookup'. You may want
;; to bind this to a key. The interactive prompt will default to the
;; mnemonic under the point. Here's a suggestion:
 
;;   (global-set-key (kbd "C-h x") #'x86-lookup)
 
;; This package pairs well with `nasm-mode'!
 
;;; Code
 
(require 'cl-lib)
(require 'doc-view)
 
(defgroup x86-lookup ()
  "Options for x86 instruction set lookup."
  :group 'extensions)
 
(defcustom x86-lookup-pdf nil
  "Path to Intel's manual containing the instruction set reference."
  :group 'x86-lookup
  :type '(choice (const nil)
                 (file :must-match t)))
 
(defcustom x86-lookup-pdftotext-program "pdftotext"
  "Path to pdftotext, part of Popper."
  :group 'x86-lookup
  :type 'string)
 
(defcustom x86-lookup-browse-pdf-function #'x86-lookup-browse-pdf-any
  "A function that launches a PDF viewer at a specific page.
This function accepts two arguments: filename and page number."
  :group 'x86-lookup
  :type '(choice (function-item :tag "First suitable PDF reader" :value
                                x86-lookup-browse-pdf-any)
                 (function-item :tag "Evince" :value
                                x86-lookup-browse-pdf-evince)
                 (function-item :tag "Xpdf" :value
                                x86-lookup-browse-pdf-xpdf)
                 (function-item :tag "Okular" :value
                                x86-lookup-browse-pdf-okular)
                 (function-item :tag "gv" :value
                                x86-lookup-browse-pdf-gv)
                 (function-item :tag "zathura" :value
                                x86-lookup-browse-pdf-zathura)
                 (function-item :tag "MuPDF" :value
                                x86-lookup-browse-pdf-mupdf)
                 (function-item :tag "Sumatra PDF" :value
                                x86-lookup-browse-pdf-sumatrapdf)
                 (function-item :tag "browse-url"
                                :value x86-lookup-browse-pdf-browser)
                 (function :tag "Your own function")))
 
(defcustom x86-lookup-cache-directory
  (let ((base (or (getenv "XDG_CACHE_HOME")
                  (getenv "LocalAppData")
                  "~/.cache")))
    (expand-file-name "x86-lookup" base))
  "Directory where the PDF mnemonic index with be cached."
  :type 'string)
 
(defvar x86-lookup-index nil
  "Alist mapping instructions to page numbers.")
 
(defvar x86-lookup--expansions
  '(("^PREFETCH\\(h\\)$"
     "" "nta" "t0" "t1" "t2")
    ("^J\\(cc\\)$"
     "a" "ae" "b" "be" "c" "cxz" "e" "ecxz" "g" "ge" "l" "le" "na" "nae" "nb"
     "nbe" "nc" "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe"
     "po" "rcxz" "s" "z")
    ("^SET\\(cc\\)$"
     "a" "ae" "b" "be" "c" "e" "g" "ge" "l" "le" "na" "nae" "nb" "nbe" "nc"
     "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe" "po" "s" "z")
    ("^CMOV\\(cc\\)$"
     "a" "ae" "b" "be" "c" "e" "g" "ge" "l" "le" "na" "nae" "nb" "nbe" "nc"
     "ne" "ng" "nge" "nl" "nle" "no" "np" "ns" "nz" "o" "p" "pe" "po" "s" "z")
    ("^FCMOV\\(cc\\)$"
     "b" "e" "be" "u" "nb" "ne" "nbe" "nu")
    ("^LOOP\\(cc\\)$"
     "e" "ne")
    ("^VBROADCAST\\(\\)$"
     "" "ss" "sd" "f128")
    ("^VMASKMOV\\(\\)$"
     "" "ps" "pd")
    ("^VPBROADCAST\\(\\)$"
     "" "b" "w" "d" "q" "I128")
    ("^VPMASKMOV\\(\\)$"
     "" "d" "q")
    ("\\(\\)" ; fallback "match"
     ""))
  "How to expand mnemonics into multiple mnemonics.")
 
(defun x86-lookup--expand (names page)
  "Expand string of PDF-sourced mnemonics into user-friendly mnemonics."
  (let ((case-fold-search nil)
        (rev-string-match-p (lambda (s re) (string-match re s))))
    (save-match-data
      (cl-loop for mnemonic-raw in (split-string names " */ *")
               ;; Collapse "int 3" and "int n" into "int"
               for mnemonic = (replace-regexp-in-string " .+$" "" mnemonic-raw)
               for (_ . tails) = (cl-assoc mnemonic x86-lookup--expansions
                                           :test rev-string-match-p)
               nconc (cl-loop for tail in tails
                              for rep = (replace-match tail nil nil mnemonic 1)
                              collect (cons (downcase rep) page))))))
 
(cl-defun x86-lookup-create-index (&optional (pdf x86-lookup-pdf))
  "Create an index alist from PDF mapping mnemonics to page numbers.
This function requires the pdftotext command line program."
  (let ((mnemonic (concat "\\(?:.*\n\n?\\)?"
                          "\\([[:alnum:]/[:blank:]]+\\)[[:blank:]]*"
                          "\\(?:--\\|—\\)\\(?:.*\n\n?\\)\\{1,3\\}"
                          "[[:blank:]]*Opcode"))
        (coding-system-for-read 'utf-8)
        (coding-system-for-write 'utf-8)
        (case-fold-search t))
    (with-temp-buffer
      (call-process x86-lookup-pdftotext-program nil t nil
                    (file-truename pdf) "-")
      (setf (point) (point-min))
      (cl-loop for page upfrom 1
               while (< (point) (point-max))
               when (looking-at mnemonic)
               nconc (x86-lookup--expand (match-string 1) page) into index
               do (forward-page)
               finally (cl-return
                        (cl-remove-duplicates
                         index :key #'car :test #'string= :from-end t))))))
 
(defun x86-lookup--index-file (pdf)
  "Return index filename from PDF filename."
  (concat (sha1 pdf) "_v3"))
 
(defun x86-lookup--save-index (pdf index)
  "Save INDEX for PDF in `x86-lookup-cache-directory'."
  (let* ((index-file (x86-lookup--index-file pdf))
         (cache-path (expand-file-name index-file x86-lookup-cache-directory)))
    (mkdir x86-lookup-cache-directory t)
    (with-temp-file cache-path
      (prin1 index (current-buffer)))
    index))
 
(defun x86-lookup--load-index (pdf)
  "Return index PDF from `x86-lookup-cache-directory'."
  (let* ((index-file (x86-lookup--index-file pdf))
         (cache-path (expand-file-name index-file x86-lookup-cache-directory)))
    (when (file-exists-p cache-path)
      (with-temp-buffer
        (insert-file-contents cache-path)
        (setf (point) (point-min))
        (ignore-errors (read (current-buffer)))))))
 
(defun x86-lookup-ensure-index ()
  "Ensure the PDF index has been created, returning the index."
  (when (null x86-lookup-index)
    (cond
     ((null x86-lookup-pdf)
      (error "No PDF available. Set `x86-lookup-pdf'."))
     ((not (file-exists-p x86-lookup-pdf))
      (error "PDF not found. Check `x86-lookup-pdf'."))
     ((setf x86-lookup-index (x86-lookup--load-index x86-lookup-pdf))
      x86-lookup-index)
     ((progn
        (message "Generating mnemonic index ...")
        (setf x86-lookup-index (x86-lookup-create-index))
        (x86-lookup--save-index x86-lookup-pdf x86-lookup-index)))))
  x86-lookup-index)
 
(defun x86-lookup-ensure-and-update-index ()
  "Ensure the PDF index has been created and (unconditionally) updated.
Useful for forcibly syncing the index with the current PDF without resorting
to manual deletion of index file on filesystem."
  (interactive)
  (cond
   ((null x86-lookup-pdf)
    (error "No PDF available. Set `x86-lookup-pdf'."))
   ((not (file-exists-p x86-lookup-pdf))
    (error "PDF not found. Check `x86-lookup-pdf'."))
   ((message "Generating mnemonic index ...")
    (setf x86-lookup-index (x86-lookup-create-index))
    (x86-lookup--save-index x86-lookup-pdf x86-lookup-index)
    (message "Finished generating mnemonic index."))))
 
(defun x86-lookup-browse-pdf (pdf page)
  "Launch a PDF viewer using `x86-lookup-browse-pdf-function'."
  (funcall x86-lookup-browse-pdf-function pdf page))
 
;;;###autoload
(defun x86-lookup (mnemonic)
  "Jump to the PDF documentation for MNEMONIC.
Defaults to the mnemonic under point."
  (interactive
   (progn
     (x86-lookup-ensure-index)
     (let* ((mnemonics (mapcar #'car x86-lookup-index))
            (thing (thing-at-point 'word))
            (mnemonic (if (member thing mnemonics) thing nil))
            (prompt (if mnemonic
                        (format "Mnemonic (default %s): " mnemonic)
                      "Mnemonic: ")))
       (list
        (completing-read prompt mnemonics nil t nil nil mnemonic)))))
  (let ((page (cdr (assoc mnemonic x86-lookup-index))))
    (x86-lookup-browse-pdf (file-truename x86-lookup-pdf) page)))
 
;; PDF viewers:
 
(defun x86-lookup-browse-pdf-pdf-tools (pdf page)
  "View PDF at PAGE using Emacs' `pdf-view-mode' and `display-buffer'."
  (require 'pdf-tools)
  (prog1 t
    (with-selected-window (display-buffer (find-file-noselect pdf :nowarn))
      (with-no-warnings
        (pdf-view-goto-page page)))))
 
(defun x86-lookup-browse-pdf-doc-view (pdf page)
  "View PDF at PAGE using Emacs' `doc-view-mode' and `display-buffer'."
  (prog1 t
    (unless (doc-view-mode-p 'pdf)
      (error "doc-view not available for PDF"))
    (with-selected-window (display-buffer (find-file-noselect pdf :nowarn))
      (doc-view-goto-page page))))
 
(defun x86-lookup-browse-pdf-xpdf (pdf page)
  "View PDF at PAGE using xpdf."
  (start-process "xpdf" nil "xpdf" "--" pdf (format "%d" page)))
 
(defun x86-lookup-browse-pdf-evince (pdf page)
  "View PDF at PAGE using Evince."
  (start-process "evince" nil "evince" "-p" (format "%d" page) "--" pdf))
 
(defun x86-lookup-browse-pdf-okular (pdf page)
  "View PDF at PAGE file using Okular."
  (start-process "okular" nil "okular" "-p" (format "%d" page) "--" pdf))
 
(defun x86-lookup-browse-pdf-gv (pdf page)
  "View PDF at PAGE using gv."
  (start-process "gv" nil "gv" "-nocenter" (format "-page=%d" page) "--" pdf))
 
(defun x86-lookup-browse-pdf-zathura (pdf page)
  "View PDF at PAGE using zathura."
  (start-process "zathura" nil "zathura" "-P" (format "%d" page) "--" pdf))
 
(defun x86-lookup-browse-pdf-sumatrapdf (pdf page)
  "View PDF at PAGE using Sumatra PDF."
  (start-process "sumatrapdf" nil "sumatrapdf" "-page" (format "%d" page) pdf))
 
(defun x86-lookup-browse-pdf-mupdf (pdf page)
  "View PDF at PAGE using MuPDF."
  ;; MuPDF doesn't have a consistent name across platforms.
  ;; Furthermore, Debian ships with a broken "mupdf" wrapper shell
  ;; script and must be avoided. Here we use `executable-find' to
  ;; avoid calling it as mupdf-x11 on non-X11 platforms.
  (let ((exe (or (executable-find "mupdf-x11") "mupdf")))
    (start-process "mupdf" nil exe "--" pdf (format "%d" page))))
 
(defun x86-lookup-browse-pdf-browser (pdf page)
  "Visit PDF using `browse-url' with a fragment for the PAGE."
  (browse-url (format "file://%s#%d" pdf page)))
 
(defun x86-lookup-browse-pdf-any (pdf page)
  "Try visiting PDF using the first viewer found."
  (or (ignore-errors (x86-lookup-browse-pdf-pdf-tools pdf page))
      (ignore-errors (x86-lookup-browse-pdf-doc-view pdf page))
      (ignore-errors (x86-lookup-browse-pdf-evince pdf page))
      (ignore-errors (x86-lookup-browse-pdf-xpdf pdf page))
      (ignore-errors (x86-lookup-browse-pdf-okular pdf page))
      (ignore-errors (x86-lookup-browse-pdf-gv pdf page))
      (ignore-errors (x86-lookup-browse-pdf-zathura pdf page))
      (ignore-errors (x86-lookup-browse-pdf-mupdf pdf page))
      (ignore-errors (x86-lookup-browse-pdf-sumatrapdf pdf page))
      (ignore-errors (x86-lookup-browse-pdf-browser pdf page))
      (error "Could not find a PDF viewer.")))
 
(provide 'x86-lookup)
 
;;; x86-lookup.el ends here