-
Notifications
You must be signed in to change notification settings - Fork 55
/
jdee-flycheck.el
293 lines (243 loc) · 11.2 KB
/
jdee-flycheck.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
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
;;; jdee-flycheck.el -- Flycheck mode for JDEE -*- lexical-binding: t; -*-
;; Author: Matthew O. Smith <[email protected]>
;; Maintainer: Paul Landes <landes <at> mailc dt net>
;; Keywords: java, tools
;; Copyright (C) 1997, 2000, 2001, 2002, 2003, 2004, 2005 Paul Kinnucan.
;; Copyright (C) 2009 by Paul Landes
;; GNU Emacs 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 2, or (at your option)
;; any later version.
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, US
;;; Commentary:
;; Integrate flycheck mode with jdee mode. Creates a temporary buffer
;; and compiles that one as the user continues to edit the original buffer.
;;; Code:
(require 'flycheck)
(require 'cl-lib)
(require 'jdee-files)
(defclass jdee-flycheck-compiler (jdee-compile-compiler)
((post-fn :initarg :post-fn
:type function
:documentation
"Function to run after compilation is complete."))
"Compiler used by flycheck mode to compile in the background.
Hides the output buffer so the user can continue to edit the
file.")
(defmethod initialize-instance ((this jdee-flycheck-compiler) &rest fields)
;; Call parent initializer.
(call-next-method)
(let ((compiler (jdee-compile-get-the-compiler)))
(when (slot-boundp compiler 'use-server-p)
(oset this use-server-p (oref compiler use-server-p)))
(when (slot-boundp compiler 'name)
(oset this name (oref compiler name)))
(when (slot-boundp compiler 'version)
(oset this version (oref compiler version)))
(when (slot-boundp compiler 'path)
(oset this path (oref compiler path)))
(when (slot-boundp compiler 'buffer)
(oset this buffer (oref compiler buffer)))
(when (slot-boundp compiler 'window)
(oset this window (oref compiler window)))
(when (slot-boundp compiler 'interactive-args)
(oset this interactive-args (oref compiler interactive-args)))))
(defun jdee-flycheck-unmute (&rest _)
(set 'jdee-compile-mute nil))
(defmethod jdee-compile-compile ((this jdee-flycheck-compiler))
(if (oref this use-server-p)
(oset this buffer (jdee-compile-server-buffer "compilation buffer"))
(oset this buffer (jdee-compile-exec-buffer "compilation buffer")))
(with-current-buffer (oref (oref this buffer) buffer)
(add-hook 'jdee-compile-finish-hook
(oref this post-fn)
nil t)
(add-hook 'jdee-compile-finish-hook 'jdee-flycheck-unmute t))
;; Pop to compilation buffer.
(let* ((outbuf (oref (oref this buffer) buffer)))
;; (outwin (display-buffer outbuf)))
;; (compilation-set-window-height outwin)
;; (oset this :window outwin)
(if compilation-process-setup-function
(funcall compilation-process-setup-function))
(jdee-compile-launch this)
(setq compilation-last-buffer outbuf)))
(defun jdee-flycheck-javac-command (checker callback)
"Start JDEE syntax `CHECKER' with callback `CALLBACK' for reporting errors."
(condition-case err
(jdee-flycheck-compile-buffer checker callback)
(error
(funcall callback 'errored (error-message-string err)))))
(defun jdee-flycheck-compile-buffer-error (checker file line col message buffer)
"Expects a match with file line message"
(flycheck-error-new
:buffer buffer
:checker checker
:filename file
:line (string-to-number line)
:column col
:message message
:level 'error))
(defun jdee-flymake-get-col ()
"Get the column of the compiliation error.
An error looks like:
/Users/c08848/projects/jdee-server/src/main/java/jde/util/AntServer.java:48: error: incompatible types: int cannot be converted to Class
private static Class ant = 00;
^
The caret indicates the column of the error. This function looks
for the caret and converts it to a column number."
(when (looking-at "\\( *\\)^")
(1+ (length (match-string 1)))))
(defun jdee-flycheck--restore-original-filename (temp-name orig-name)
"Replace all occurances of `TEMP-NAME' with `ORIG-NAME' in current buffer."
(goto-char (point-min))
(while (re-search-forward temp-name nil t)
(replace-match orig-name)))
(defun jdee-flycheck--collect-errors (checker orig-buffer)
"Collect flycheck errors from `CHECKER' run `ORIG-BUFFER'."
(goto-char (point-min))
(let ((errors nil))
(while (jdee-flycheck-find-next-error)
(forward-line 2)
(let ((file (match-string 1))
(line (match-string 2))
(message (match-string 3))
;; jdee-flymake-get-col changes search data; do it last
(col (jdee-flymake-get-col)))
(cl-pushnew
(jdee-flycheck-compile-buffer-error checker
file
line
col
message
orig-buffer)
errors)))
errors))
(defun jdee-flycheck-compile-buffer-after (checker cback orig-file orig-buffer
temp-file temp-buffer)
"Callback function called when the compilation is complete.
Looks for errors and converts then to flycheck errors. Also
cleans up after the compilation."
(lambda (buf msg)
(with-current-buffer buf
(jdee-flycheck--restore-original-filename temp-file orig-file)
(let ((errors (jdee-flycheck--collect-errors checker orig-buffer)))
(flycheck-display-error-messages errors)
(kill-buffer temp-buffer)
(set (make-local-variable 'jdee-compile-jump-to-first-error) nil)
(set 'jdee-compile-mute t)
(funcall cback 'finished errors)))))
(defconst jdee-javac-error-line-regexp
"^\\(.*\\):\\([0-9]+\\): *error: \\(.*\\)$"
"Regexp matches compiler error messages and remembers file, line, and message.
Example line:
/src/Poligon.java:12: error: incompatible types: int cannot be converted to Another")
(defun jdee-flycheck-find-next-error ()
;; To avoid stack overflow while executing regex search over possibly long lines,
;; hone in on only those lines that contain the magic string "error:"
(when (search-forward "error:" nil t)
(beginning-of-line)
(or (re-search-forward jdee-javac-error-line-regexp
(save-excursion (end-of-line) (point))
t)
(progn
(forward-line)
(jdee-flycheck-find-next-error)))))
(defun jdee-flycheck-cleanup ()
"Cleans up after flycheck.
Deletes the temporary files listed in `jdee-flycheck-temp-files'"
(dolist (temp-file jdee-flycheck-temp-files)
(cond
((file-directory-p temp-file) (delete-directory temp-file t))
((file-exists-p temp-file) (delete-file temp-file)))))
(defvar jdee-flycheck-temp-files nil
"Files to delete whean the buffer is killed.")
(defun jdee-flycheck-compile-buffer (checker cback &optional buffer)
"Compile the buffer without saving it. Creates a temporary
file and buffer with the contents of the current buffer and compiles that one."
(let* ((name (file-name-nondirectory buffer-file-name))
(orig-file buffer-file-name)
(orig-buffer (or buffer (current-buffer)))
(dir (make-temp-file "JDEE_flycheck_" t))
(temp-file (expand-file-name name dir)))
(jdee-files-write-buffer-to-file orig-buffer temp-file)
(with-current-buffer (generate-new-buffer name)
(make-local-variable 'jdee-flycheck-temp-files)
(cl-pushnew dir jdee-flycheck-temp-files)
(insert-file-contents-literally temp-file)
(setq buffer-file-name temp-file)
(add-hook 'kill-buffer-hook 'jdee-flycheck-cleanup nil t)
;; Don't write the class file to the source directory
(unless jdee-compile-option-directory
(set (make-local-variable 'jdee-compile-option-directory)
(expand-file-name "classes" dir)))
(setq compilation-finish-functions
(lambda (buf msg)
(run-hook-with-args 'jdee-compile-finish-hook buf msg)
(setq compilation-finish-functions nil)))
(let ((compiler (jdee-flycheck-compiler "flycheck")))
(oset compiler post-fn
(jdee-flycheck-compile-buffer-after checker cback
orig-file orig-buffer
temp-file (current-buffer)))
(jdee-compile-compile compiler)))))
;; (defun jdee-flycheck-command-using-server ( checker cback )
;; "Use flycheck to search the current buffer for compiler errrors."
;; (if (not (comint-check-proc "*groovy*"))
;; (funcall cback 'finished nil)
;; (let* ((pom-path malabar-mode-project-file)
;; (pm malabar-mode-project-manager)
;; (buffer (current-buffer))
;; (func (if (buffer-modified-p) 'malabar-parse-scriptbody-raw 'malabar-parse-script-raw))
;; (script (if (buffer-modified-p) (buffer-string) (buffer-file-name))))
;; ;;(message "flycheck with func:%s" func)
;; (funcall func
;; (lambda (_status)
;; ;(message "%s %s %s" status (current-buffer) url-http-end-of-headers)
;; (condition-case err
;; (progn
;; (goto-char url-http-end-of-headers)
;; (let ((error-list (jdee-flycheck-error-parser (json-read) checker buffer)))
;; (kill-buffer (current-buffer))
;; ;(message "ERROR LIST:%s" error-list)
;; (with-current-buffer buffer
;; (funcall cback 'finished error-list))))
;; (error (let ((msg (error-message-string err)))
;; (message "flycheck error: %s" msg)
;; (pop-to-buffer (current-buffer))
;; (funcall cback 'errored msg)))))
;; pm pom-path script))))
;; (defun jdee-flycheck-error-new (checker error-info buffer)
;; "Create a flycheck error from "
;; (flycheck-error-new
;; :buffer buffer
;; :checker checker
;; :filename (buffer-file-name buffer)
;; :line (cdr (assq 'line error-info))
;; :column (cdr (assq 'startColumn error-info))
;; :message (cdr (assq 'message error-info))
;; :level 'error))
;; (defun jdee-flycheck-error-parser (output checker buffer)
;; "Parse errors in OUTPUT which is a JSON array"
;; (let ((rtnval (mapcar (lambda (e)
;; (jdee-flycheck-error-new checker e buffer))
;; output)))
;; rtnval))
;;;###autoload
(defun jdee-flycheck-mode ()
"Setup JDEE flycheck checker."
(flycheck-define-generic-checker 'jdee-flycheck-javac-checker
"Integrate flycheck with the jdee using javac."
:start #'jdee-flycheck-javac-command
:modes '(jdee-mode))
(cl-pushnew 'jdee-flycheck-javac-checker flycheck-checkers)
(flycheck-mode))
(provide 'jdee-flycheck)
;;; jdee-flycheck.el ends here