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