blob: 94907e66cc592404a54cfa48da71af215d440153 (
plain)
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
|
;;; flymake-elisp.el --- Flymake backends for emacs-lisp-mode -*- lexical-binding: t; -*-
;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
;; Author: João Távora <joaotavora@gmail.com>
;; Keywords: languages tools
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Flymake backends for elisp work.
;;; Code:
(require 'flymake)
(require 'checkdoc)
(eval-when-compile (require 'cl-lib))
(require 'bytecomp)
(defun flymake-elisp--checkdoc-1 ()
"Do actual work for `flymake-elisp-checkdoc'."
(let (collected)
(cl-letf (((symbol-function 'checkdoc-create-error)
(lambda (text start end &optional unfixable)
(push (list text start end unfixable) collected)
nil)))
(let* ((checkdoc-autofix-flag nil)
(checkdoc-generate-compile-warnings-flag nil)
(buf (generate-new-buffer " *checkdoc-temp*"))
(checkdoc-diagnostic-buffer buf))
(unwind-protect
(save-excursion
(checkdoc-current-buffer t))
(kill-buffer buf))))
collected))
(defun flymake-elisp-checkdoc (report-fn)
"A Flymake backend for `checkdoc'.
Calls REPORT-FN directly."
(unless (derived-mode-p 'emacs-lisp-mode)
(error "Can only work on `emacs-lisp-mode' buffers"))
(funcall report-fn
(cl-loop for (text start end _unfixable) in
(flymake-elisp--checkdoc-1)
collect
(flymake-make-diagnostic
(current-buffer)
start end :note text))))
(defun flymake-elisp--byte-compile-done (report-fn
origin-buffer
output-buffer
temp-file)
(unwind-protect
(with-current-buffer
origin-buffer
(save-excursion
(save-restriction
(widen)
(funcall
report-fn
(ignore-errors
(cl-loop with data =
(with-current-buffer output-buffer
(goto-char (point-min))
(search-forward ":flymake-elisp-output-start")
(read (point-marker)))
for (string pos _fill level) in data
do (goto-char pos)
for beg = (if (< (point) (point-max))
(point)
(line-beginning-position))
for end = (min
(line-end-position)
(or (cdr
(bounds-of-thing-at-point 'sexp))
(point-max)))
collect (flymake-make-diagnostic
(current-buffer)
(if (= beg end) (1- beg) beg)
end
level
string)))))))
(kill-buffer output-buffer)
(ignore-errors (delete-file temp-file))))
(defvar-local flymake-elisp--byte-compile-process nil
"Buffer-local process started for byte-compiling the buffer.")
(defun flymake-elisp-byte-compile (report-fn)
"A Flymake backend for elisp byte compilation.
Spawn an Emacs process that byte-compiles a file representing the
current buffer state and calls REPORT-FN when done."
(interactive (list (lambda (stuff)
(message "aha %s" stuff))))
(unless (derived-mode-p 'emacs-lisp-mode)
(error "Can only work on `emacs-lisp-mode' buffers"))
(when flymake-elisp--byte-compile-process
(process-put flymake-elisp--byte-compile-process 'flymake-elisp--obsolete t)
(when (process-live-p flymake-elisp--byte-compile-process)
(kill-process flymake-elisp--byte-compile-process)))
(let ((temp-file (make-temp-file "flymake-elisp-byte-compile"))
(origin-buffer (current-buffer)))
(save-restriction
(widen)
(write-region (point-min) (point-max) temp-file nil 'nomessage))
(let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*")))
(setq
flymake-elisp--byte-compile-process
(make-process
:name "flymake-elisp-byte-compile"
:buffer output-buffer
:command (list (expand-file-name invocation-name invocation-directory)
"-Q"
"--batch"
;; "--eval" "(setq load-prefer-newer t)" ; for testing
"-L" default-directory
"-l" "flymake-elisp"
"-f" "flymake-elisp--batch-byte-compile"
temp-file)
:connection-type 'pipe
:sentinel
(lambda (proc _event)
(unless (process-live-p proc)
(unwind-protect
(cond
((zerop (process-exit-status proc))
(flymake-elisp--byte-compile-done report-fn
origin-buffer
output-buffer
temp-file))
((process-get proc 'flymake-elisp--obsolete)
(flymake-log 3 "proc %s considered obsolete" proc))
(t
(funcall report-fn
:panic
:explanation (format "proc %s died violently" proc)))))))))
:stderr null-device
:noquery t)))
(defun flymake-elisp--batch-byte-compile (&optional file)
"Helper for `flymake-elisp-byte-compile'.
Runs in a batch-mode Emacs. Interactively use variable
`buffer-file-name' for FILE."
(interactive (list buffer-file-name))
(let* ((file (or file
(car command-line-args-left)))
(dummy-elc-file)
(byte-compile-log-buffer
(generate-new-buffer " *dummy-byte-compile-log-buffer*"))
(byte-compile-dest-file-function
(lambda (source)
(setq dummy-elc-file (make-temp-file (file-name-nondirectory source)))))
(collected))
(unwind-protect
(cl-letf (((symbol-function 'byte-compile-log-warning)
(lambda (string &optional fill level)
(push (list string byte-compile-last-position fill level)
collected)
t)))
(byte-compile-file file))
(ignore-errors
(delete-file dummy-elc-file)
(kill-buffer byte-compile-log-buffer)))
(prin1 :flymake-elisp-output-start)
(terpri)
(pp collected)))
(defun flymake-elisp-setup-backends ()
"Setup Flymake for elisp work."
(add-hook 'flymake-diagnostic-functions 'flymake-elisp-checkdoc t t)
(add-hook 'flymake-diagnostic-functions 'flymake-elisp-byte-compile t t))
(add-hook 'emacs-lisp-mode-hook
'flymake-elisp-setup-backends)
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (derived-mode-p 'emacs-lisp-mode)
(flymake-elisp-setup-backends))))
(provide 'flymake-elisp)
;;; flymake-elisp.el ends here
|