blob: e2262074ed4f7e3f2629e6075eba2f42b4d7aff4 [file] [log] [blame]
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +01001" Default pre- and post-compiler actions and commands for SpotBugs
Konfekt65311c62024-11-28 21:06:09 +01002" Maintainers: @konfekt and @zzzyxwvut
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +01003" Last Change: 2024 Dec 08
Konfekt65311c62024-11-28 21:06:09 +01004
5let s:save_cpo = &cpo
6set cpo&vim
7
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +01008" Look for the setting of "g:spotbugs#state" in "ftplugin/java.vim".
9let s:state = get(g:, 'spotbugs#state', {})
10let s:commands = get(s:state, 'commands', {})
11let s:compiler = get(s:state, 'compiler', '')
12let s:readable = filereadable($VIMRUNTIME . '/compiler/' . s:compiler . '.vim')
13
14if has_key(s:commands, 'DefaultPreCompilerCommand')
15 let g:SpotBugsPreCompilerCommand = s:commands.DefaultPreCompilerCommand
16else
17
18 function! s:DefaultPreCompilerCommand(arguments) abort
19 execute 'make ' . a:arguments
20 cc
21 endfunction
22
23 let g:SpotBugsPreCompilerCommand = function('s:DefaultPreCompilerCommand')
24endif
25
26if has_key(s:commands, 'DefaultPreCompilerTestCommand')
27 let g:SpotBugsPreCompilerTestCommand = s:commands.DefaultPreCompilerTestCommand
28else
29
30 function! s:DefaultPreCompilerTestCommand(arguments) abort
31 execute 'make ' . a:arguments
32 cc
33 endfunction
34
35 let g:SpotBugsPreCompilerTestCommand = function('s:DefaultPreCompilerTestCommand')
36endif
37
38if has_key(s:commands, 'DefaultPostCompilerCommand')
39 let g:SpotBugsPostCompilerCommand = s:commands.DefaultPostCompilerCommand
40else
41
42 function! s:DefaultPostCompilerCommand(arguments) abort
43 execute 'make ' . a:arguments
44 endfunction
45
46 let g:SpotBugsPostCompilerCommand = function('s:DefaultPostCompilerCommand')
47endif
48
Konfekt65311c62024-11-28 21:06:09 +010049if v:version > 900
50
51 function! spotbugs#DeleteClassFiles() abort
52 if !exists('b:spotbugs_class_files')
53 return
54 endif
55
56 for pathname in b:spotbugs_class_files
57 let classname = pathname =~# "^'.\\+\\.class'$"
58 \ ? eval(pathname)
59 \ : pathname
60
61 if classname =~# '\.class$' && filereadable(classname)
62 " Since v9.0.0795.
63 let octad = readblob(classname, 0, 8)
64
65 " Test the magic number and the major version number (45 for v1.0).
66 " Since v9.0.2027.
67 if len(octad) == 8 && octad[0 : 3] == 0zcafe.babe &&
68 \ or((octad[6] << 8), octad[7]) >= 45
69 echomsg printf('Deleting %s: %d', classname, delete(classname))
70 endif
71 endif
72 endfor
73
74 let b:spotbugs_class_files = []
75 endfunction
76
77else
78
79 function! s:DeleteClassFilesWithNewLineCodes(classname) abort
80 " The distribution of "0a"s in class file versions 2560 and 2570:
81 "
82 " 0zca.fe.ba.be.00.00.0a.00 0zca.fe.ba.be.00.00.0a.0a
83 " 0zca.fe.ba.be.00.0a.0a.00 0zca.fe.ba.be.00.0a.0a.0a
84 " 0zca.fe.ba.be.0a.00.0a.00 0zca.fe.ba.be.0a.00.0a.0a
85 " 0zca.fe.ba.be.0a.0a.0a.00 0zca.fe.ba.be.0a.0a.0a.0a
86 let numbers = [0, 0, 0, 0, 0, 0, 0, 0]
87 let offset = 0
88 let lines = readfile(a:classname, 'b', 4)
89
90 " Track NL byte counts to handle files of less than 8 bytes.
91 let nl_cnt = len(lines)
92 " Track non-NL byte counts for "0zca.fe.ba.be.0a.0a.0a.0a".
93 let non_nl_cnt = 0
94
95 for line in lines
96 for idx in range(strlen(line))
97 " Remap NLs to Nuls.
98 let numbers[offset] = (line[idx] == "\n") ? 0 : char2nr(line[idx]) % 256
99 let non_nl_cnt += 1
100 let offset += 1
101
102 if offset > 7
103 break
104 endif
105 endfor
106
107 let nl_cnt -= 1
108
109 if offset > 7 || (nl_cnt < 1 && non_nl_cnt > 4)
110 break
111 endif
112
113 " Reclaim NLs.
114 let numbers[offset] = 10
115 let offset += 1
116
117 if offset > 7
118 break
119 endif
120 endfor
121
122 " Test the magic number and the major version number (45 for v1.0).
123 if offset > 7 && numbers[0] == 0xca && numbers[1] == 0xfe &&
124 \ numbers[2] == 0xba && numbers[3] == 0xbe &&
125 \ (numbers[6] * 256 + numbers[7]) >= 45
126 echomsg printf('Deleting %s: %d', a:classname, delete(a:classname))
127 endif
128 endfunction
129
130 function! spotbugs#DeleteClassFiles() abort
131 if !exists('b:spotbugs_class_files')
132 return
133 endif
134
135 let encoding = &encoding
136
137 try
138 set encoding=latin1
139
140 for pathname in b:spotbugs_class_files
141 let classname = pathname =~# "^'.\\+\\.class'$"
142 \ ? eval(pathname)
143 \ : pathname
144
145 if classname =~# '\.class$' && filereadable(classname)
146 let line = get(readfile(classname, 'b', 1), 0, '')
147 let length = strlen(line)
148
149 " Test the magic number and the major version number (45 for v1.0).
150 if length > 3 && line[0 : 3] == "\xca\xfe\xba\xbe"
151 if length > 7 && ((line[6] == "\n" ? 0 : char2nr(line[6]) % 256) * 256 +
152 \ (line[7] == "\n" ? 0 : char2nr(line[7]) % 256)) >= 45
153 echomsg printf('Deleting %s: %d', classname, delete(classname))
154 else
155 call s:DeleteClassFilesWithNewLineCodes(classname)
156 endif
157 endif
158 endif
159 endfor
160 finally
161 let &encoding = encoding
162 endtry
163
164 let b:spotbugs_class_files = []
165 endfunction
166
167endif
168
169function! spotbugs#DefaultPostCompilerAction() abort
170 " Since v7.4.191.
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100171 call call(g:SpotBugsPostCompilerCommand, ['%:S'])
Konfekt65311c62024-11-28 21:06:09 +0100172endfunction
173
Konfekt65311c62024-11-28 21:06:09 +0100174if s:readable && s:compiler ==# 'maven' && executable('mvn')
175
176 function! spotbugs#DefaultPreCompilerAction() abort
177 call spotbugs#DeleteClassFiles()
178 compiler maven
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100179 call call(g:SpotBugsPreCompilerCommand, ['compile'])
Konfekt65311c62024-11-28 21:06:09 +0100180 endfunction
181
182 function! spotbugs#DefaultPreCompilerTestAction() abort
183 call spotbugs#DeleteClassFiles()
184 compiler maven
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100185 call call(g:SpotBugsPreCompilerTestCommand, ['test-compile'])
Konfekt65311c62024-11-28 21:06:09 +0100186 endfunction
187
188 function! spotbugs#DefaultProperties() abort
189 return {
190 \ 'PreCompilerAction':
191 \ function('spotbugs#DefaultPreCompilerAction'),
192 \ 'PreCompilerTestAction':
193 \ function('spotbugs#DefaultPreCompilerTestAction'),
194 \ 'PostCompilerAction':
195 \ function('spotbugs#DefaultPostCompilerAction'),
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100196 \ 'sourceDirPath': ['src/main/java'],
197 \ 'classDirPath': ['target/classes'],
198 \ 'testSourceDirPath': ['src/test/java'],
199 \ 'testClassDirPath': ['target/test-classes'],
Konfekt65311c62024-11-28 21:06:09 +0100200 \ }
201 endfunction
202
203 unlet s:readable s:compiler
204elseif s:readable && s:compiler ==# 'ant' && executable('ant')
205
206 function! spotbugs#DefaultPreCompilerAction() abort
207 call spotbugs#DeleteClassFiles()
208 compiler ant
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100209 call call(g:SpotBugsPreCompilerCommand, ['compile'])
Konfekt65311c62024-11-28 21:06:09 +0100210 endfunction
211
212 function! spotbugs#DefaultPreCompilerTestAction() abort
213 call spotbugs#DeleteClassFiles()
214 compiler ant
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100215 call call(g:SpotBugsPreCompilerTestCommand, ['compile-test'])
Konfekt65311c62024-11-28 21:06:09 +0100216 endfunction
217
218 function! spotbugs#DefaultProperties() abort
219 return {
220 \ 'PreCompilerAction':
221 \ function('spotbugs#DefaultPreCompilerAction'),
222 \ 'PreCompilerTestAction':
223 \ function('spotbugs#DefaultPreCompilerTestAction'),
224 \ 'PostCompilerAction':
225 \ function('spotbugs#DefaultPostCompilerAction'),
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100226 \ 'sourceDirPath': ['src'],
227 \ 'classDirPath': ['build/classes'],
228 \ 'testSourceDirPath': ['test'],
229 \ 'testClassDirPath': ['build/test/classes'],
Konfekt65311c62024-11-28 21:06:09 +0100230 \ }
231 endfunction
232
233 unlet s:readable s:compiler
234elseif s:readable && s:compiler ==# 'javac' && executable('javac')
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100235 let s:filename = tempname()
Konfekt65311c62024-11-28 21:06:09 +0100236
237 function! spotbugs#DefaultPreCompilerAction() abort
238 call spotbugs#DeleteClassFiles()
239 compiler javac
240
241 if get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')) =~ '\s@\S'
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100242 " Only read options and filenames from @options [@sources ...] and do
243 " not update these files when filelists change.
244 call call(g:SpotBugsPreCompilerCommand, [''])
Konfekt65311c62024-11-28 21:06:09 +0100245 else
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100246 " Collect filenames so that Javac can figure out what to compile.
247 let filelist = []
248
249 for arg_num in range(argc(-1))
250 let arg_name = argv(arg_num)
251
252 if arg_name =~# '\.java\=$'
253 call add(filelist, fnamemodify(arg_name, ':p:S'))
254 endif
255 endfor
256
257 for buf_num in range(1, bufnr('$'))
258 if !buflisted(buf_num)
259 continue
260 endif
261
262 let buf_name = bufname(buf_num)
263
264 if buf_name =~# '\.java\=$'
265 let buf_name = fnamemodify(buf_name, ':p:S')
266
267 if index(filelist, buf_name) < 0
268 call add(filelist, buf_name)
269 endif
270 endif
271 endfor
272
273 noautocmd call writefile(filelist, s:filename)
274 call call(g:SpotBugsPreCompilerCommand, [shellescape('@' . s:filename)])
Konfekt65311c62024-11-28 21:06:09 +0100275 endif
276 endfunction
277
278 function! spotbugs#DefaultPreCompilerTestAction() abort
279 call spotbugs#DefaultPreCompilerAction()
280 endfunction
281
282 function! spotbugs#DefaultProperties() abort
283 return {
284 \ 'PreCompilerAction':
285 \ function('spotbugs#DefaultPreCompilerAction'),
Konfekt65311c62024-11-28 21:06:09 +0100286 \ 'PostCompilerAction':
287 \ function('spotbugs#DefaultPostCompilerAction'),
288 \ }
289 endfunction
290
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100291 unlet s:readable s:compiler g:SpotBugsPreCompilerTestCommand
292 delfunction! s:DefaultPreCompilerTestCommand
Konfekt65311c62024-11-28 21:06:09 +0100293else
294
295 function! spotbugs#DefaultPreCompilerAction() abort
296 echomsg printf('Not supported: "%s"', s:compiler)
297 endfunction
298
299 function! spotbugs#DefaultPreCompilerTestAction() abort
300 call spotbugs#DefaultPreCompilerAction()
301 endfunction
302
303 function! spotbugs#DefaultProperties() abort
304 return {}
305 endfunction
306
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100307 " XXX: Keep "s:compiler" around for "spotbugs#DefaultPreCompilerAction()",
308 " "s:DefaultPostCompilerCommand" -- "spotbugs#DefaultPostCompilerAction()".
309 unlet s:readable g:SpotBugsPreCompilerCommand g:SpotBugsPreCompilerTestCommand
310 delfunction! s:DefaultPreCompilerCommand
311 delfunction! s:DefaultPreCompilerTestCommand
Konfekt65311c62024-11-28 21:06:09 +0100312endif
313
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100314function! s:DefineBufferAutocmd(event, ...) abort
315 if !exists('#java_spotbugs#User')
316 return 1
317 endif
318
319 for l:event in insert(copy(a:000), a:event)
320 if l:event != 'User'
321 execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event)
322 execute printf('autocmd java_spotbugs %s <buffer> doautocmd User', l:event)
323 endif
324 endfor
325
326 return 0
327endfunction
328
329function! s:RemoveBufferAutocmd(event, ...) abort
330 if !exists('#java_spotbugs')
331 return 1
332 endif
333
334 for l:event in insert(copy(a:000), a:event)
335 if l:event != 'User'
336 execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event)
337 endif
338 endfor
339
340 return 0
341endfunction
342
343" Documented in ":help compiler-spotbugs".
344command! -bar -nargs=+ -complete=event SpotBugsDefineBufferAutocmd
345 \ call s:DefineBufferAutocmd(<f-args>)
346command! -bar -nargs=+ -complete=event SpotBugsRemoveBufferAutocmd
347 \ call s:RemoveBufferAutocmd(<f-args>)
348
Konfekt65311c62024-11-28 21:06:09 +0100349let &cpo = s:save_cpo
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100350unlet s:commands s:state s:save_cpo
Konfekt65311c62024-11-28 21:06:09 +0100351
352" vim: set foldmethod=syntax shiftwidth=2 expandtab: