diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /runtime/compiler/spotbugs.vim | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.gz rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.bz2 rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.zip |
Diffstat (limited to 'runtime/compiler/spotbugs.vim')
-rw-r--r-- | runtime/compiler/spotbugs.vim | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/runtime/compiler/spotbugs.vim b/runtime/compiler/spotbugs.vim new file mode 100644 index 0000000000..8ed45f8ee0 --- /dev/null +++ b/runtime/compiler/spotbugs.vim @@ -0,0 +1,254 @@ +" Vim compiler file +" Compiler: Spotbugs (Java static checker; needs javac compiled classes) +" Maintainers: @konfekt and @zzzyxwvut +" Last Change: 2024 Dec 20 + +if exists('g:current_compiler') || bufname() !~# '\.java\=$' || wordcount().chars < 9 + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Unfortunately Spotbugs does not output absolute paths, so you need to +" pass the directory of the files being checked as `-sourcepath` parameter. +" The regex, auxpath and glob try to include all dependent classes of the +" current buffer. See https://github.com/spotbugs/spotbugs/issues/856 + +" FIXME: When "search()" is used with the "e" flag, it makes no _further_ +" progress after claiming an EOL match (i.e. "\_" or "\n", but not "$"). +" XXX: Omit anonymous class declarations +let s:keywords = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\|package\)\%(\s\|$\)' +let s:type_names = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\)\s*\(\K\k*\)\>' +" Capture ";" for counting a class file directory (see s:package_dir_heads below) +let s:package_names = '\C\<package\s*\(\K\%(\k*\.\=\)\+;\)' +let s:package = '' + +if has('syntax') && exists('g:syntax_on') && + \ exists('b:current_syntax') && b:current_syntax == 'java' && + \ hlexists('javaClassDecl') && hlexists('javaExternal') + + function! s:GetDeclaredTypeNames() abort + if bufname() =~# '\<\%(module\|package\)-info\.java\=$' + return [expand('%:t:r')] + endif + defer execute('silent! normal! g``') + call cursor(1, 1) + let type_names = [] + let lnum = search(s:keywords, 'eW') + while lnum > 0 + let name_attr = synIDattr(synID(lnum, (col('.') - 1), 0), 'name') + if name_attr ==# 'javaClassDecl' + let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:type_names) + if !empty(tokens) | call add(type_names, tokens[1]) | endif + elseif name_attr ==# 'javaExternal' + let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:package_names) + if !empty(tokens) | let s:package = tokens[1] | endif + endif + let lnum = search(s:keywords, 'eW') + endwhile + return type_names + endfunction + +else + function! s:GetDeclaredTypeNames() abort + if bufname() =~# '\<\%(module\|package\)-info\.java\=$' + return [expand('%:t:r')] + endif + " Undo the unsetting of &hls, see below + if &hls + defer execute('set hls') + endif + " Possibly restore the current values for registers '"' and "y", see below + defer call('setreg', ['"', getreg('"'), getregtype('"')]) + defer call('setreg', ['y', getreg('y'), getregtype('y')]) + defer execute('silent bwipeout') + " Copy buffer contents for modification + silent %y y + new + " Apply ":help scratch-buffer" effects and match "$" in Java (generated) + " type names (see s:type_names) + setlocal iskeyword+=$ buftype=nofile bufhidden=hide noswapfile nohls + 0put y + " Discard text blocks and strings + silent keeppatterns %s/\\\@<!"""\_.\{-}\\\@<!"""\|\\"//ge + silent keeppatterns %s/".*"//ge + " Discard comments + silent keeppatterns %s/\/\/.\+$//ge + silent keeppatterns %s/\/\*\_.\{-}\*\///ge + call cursor(1, 1) + let type_names = [] + let lnum = search(s:keywords, 'eW') + while lnum > 0 + let line = getline(lnum) + if line =~# '\<package\>' + let tokens = matchlist(line..getline(lnum + 1), s:package_names) + if !empty(tokens) | let s:package = tokens[1] | endif + else + let tokens = matchlist(line..getline(lnum + 1), s:type_names) + if !empty(tokens) | call add(type_names, tokens[1]) | endif + endif + let lnum = search(s:keywords, 'eW') + endwhile + return type_names + endfunction +endif + +if has('win32') + + function! s:GlobClassFiles(src_type_name) abort + return glob(a:src_type_name..'$*.class', 1, 1) + endfunction + +else + function! s:GlobClassFiles(src_type_name) abort + return glob(a:src_type_name..'\$*.class', 1, 1) + endfunction +endif + +if exists('b:spotbugs_properties') + " Let "ftplugin/java.vim" merge global entries, if any, in buffer-local + " entries + + function! s:GetProperty(name, default) abort + return get(b:spotbugs_properties, a:name, a:default) + endfunction + +elseif exists('g:spotbugs_properties') + + function! s:GetProperty(name, default) abort + return get(g:spotbugs_properties, a:name, a:default) + endfunction + +else + function! s:GetProperty(dummy, default) abort + return a:default + endfunction +endif + +if (exists('g:spotbugs_properties') || exists('b:spotbugs_properties')) && + \ ((!empty(s:GetProperty('sourceDirPath', [])) && + \ !empty(s:GetProperty('classDirPath', []))) || + \ (!empty(s:GetProperty('testSourceDirPath', [])) && + \ !empty(s:GetProperty('testClassDirPath', [])))) + + function! s:CommonIdxsAndDirs() abort + let src_dir_path = s:GetProperty('sourceDirPath', []) + let bin_dir_path = s:GetProperty('classDirPath', []) + let test_src_dir_path = s:GetProperty('testSourceDirPath', []) + let test_bin_dir_path = s:GetProperty('testClassDirPath', []) + let dir_cnt = min([len(src_dir_path), len(bin_dir_path)]) + let test_dir_cnt = min([len(test_src_dir_path), len(test_bin_dir_path)]) + " Do not break up path pairs with filtering! + return [[range(dir_cnt), + \ src_dir_path[0 : dir_cnt - 1], + \ bin_dir_path[0 : dir_cnt - 1]], + \ [range(test_dir_cnt), + \ test_src_dir_path[0 : test_dir_cnt - 1], + \ test_bin_dir_path[0 : test_dir_cnt - 1]]] + endfunction + + let s:common_idxs_and_dirs = s:CommonIdxsAndDirs() + delfunction s:CommonIdxsAndDirs + + function! s:FindClassFiles(src_type_name) abort + let class_files = [] + " Match pairwise the components of source and class pathnames + for [idxs, src_dirs, bin_dirs] in s:common_idxs_and_dirs + " Do not use "fnamemodify(a:src_type_name, ':p:s?src?bin?')" because + " only the rightmost "src" is looked for + for idx in idxs + let tail_idx = strridx(a:src_type_name, src_dirs[idx]) + " No such directory or no such inner type (i.e. without "$") + if tail_idx < 0 | continue | endif + " Substitute "bin_dirs[idx]" for the rightmost "src_dirs[idx]" + let candidate_type_name = strpart(a:src_type_name, 0, tail_idx).. + \ bin_dirs[idx].. + \ strpart(a:src_type_name, (tail_idx + strlen(src_dirs[idx]))) + for candidate in insert(s:GlobClassFiles(candidate_type_name), + \ candidate_type_name..'.class') + if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif + endfor + if !empty(class_files) | break | endif + endfor + if !empty(class_files) | break | endif + endfor + return class_files + endfunction + +else + function! s:FindClassFiles(src_type_name) abort + let class_files = [] + for candidate in insert(s:GlobClassFiles(a:src_type_name), + \ a:src_type_name..'.class') + if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif + endfor + return class_files + endfunction +endif + +if exists('g:spotbugs_alternative_path') && + \ !empty(get(g:spotbugs_alternative_path, 'fromPath', '')) && + \ !empty(get(g:spotbugs_alternative_path, 'toPath', '')) + + " See https://github.com/spotbugs/spotbugs/issues/909 + function! s:ResolveAbsolutePathname() abort + let pathname = expand('%:p') + let head_idx = stridx(pathname, g:spotbugs_alternative_path.toPath) + " No such file: a mismatched path request for a project + if head_idx < 0 | return pathname | endif + " Settle for failure with file readability tests _in s:FindClassFiles()_ + return strpart(pathname, 0, head_idx).. + \ g:spotbugs_alternative_path.fromPath.. + \ strpart(pathname, (head_idx + strlen(g:spotbugs_alternative_path.toPath))) + endfunction + +else + function! s:ResolveAbsolutePathname() abort + return expand('%:p') + endfunction +endif + +function! s:CollectClassFiles() abort + " Possibly obtain a symlinked path for an unsupported directory name + let pathname = s:ResolveAbsolutePathname() + " Get a platform-independent pathname prefix, cf. "expand('%:p:h')..'/'" + let tail_idx = strridx(pathname, expand('%:t')) + let src_pathname = strpart(pathname, 0, tail_idx) + let all_class_files = [] + " Get all type names in the current buffer and let the filename globbing + " discover inner type names from arbitrary type names + for type_name in s:GetDeclaredTypeNames() + call extend(all_class_files, s:FindClassFiles(src_pathname..type_name)) + endfor + return all_class_files +endfunction + +" Expose class files for removal etc. +let b:spotbugs_class_files = s:CollectClassFiles() +let s:package_dir_heads = repeat(':h', (1 + strlen(substitute(s:package, '[^.;]', '', 'g')))) +let s:package_root_dir = fnamemodify(s:ResolveAbsolutePathname(), s:package_dir_heads..':S') +let g:current_compiler = 'spotbugs' +" CompilerSet makeprg=spotbugs +let &l:makeprg = 'spotbugs'..(has('win32') ? '.bat' : '')..' '.. + \ get(b:, 'spotbugs_makeprg_params', get(g:, 'spotbugs_makeprg_params', '-workHard -experimental')).. + \ ' -textui -emacs -auxclasspath '..s:package_root_dir..' -sourcepath '..s:package_root_dir..' '.. + \ join(b:spotbugs_class_files, ' ') +" Emacs expects doubled line numbers +setlocal errorformat=%f:%l:%*[0-9]\ %m,%f:-%*[0-9]:-%*[0-9]\ %m + +" " This compiler is meant to be used for a single buffer only +" exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') +" exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"') + +delfunction s:CollectClassFiles +delfunction s:ResolveAbsolutePathname +delfunction s:FindClassFiles +delfunction s:GetProperty +delfunction s:GlobClassFiles +delfunction s:GetDeclaredTypeNames +let &cpo = s:cpo_save +unlet! s:package_root_dir s:package_dir_heads s:common_idxs_and_dirs s:package +unlet! s:package_names s:type_names s:keywords s:cpo_save + +" vim: set foldmethod=syntax shiftwidth=2 expandtab: |