diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-11-29 08:54:47 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2024-11-29 08:57:00 +0800 |
commit | 8d7d225caa12e5a25f6853a54a2fd6d144342d3c (patch) | |
tree | f456f05a86d76bcb97f3b276d743fab227ef8def | |
parent | 146b8300a145efa64e579527da8606546a36162b (diff) | |
download | rneovim-8d7d225caa12e5a25f6853a54a2fd6d144342d3c.tar.gz rneovim-8d7d225caa12e5a25f6853a54a2fd6d144342d3c.tar.bz2 rneovim-8d7d225caa12e5a25f6853a54a2fd6d144342d3c.zip |
vim-patch:65311c6: runtime(compiler): include spotbugs Java linter
closes: vim/vim#16001
https://github.com/vim/vim/commit/65311c6f472de67b368d83441ca5e93da86161f4
Co-authored-by: Konfekt <Konfekt@users.noreply.github.com>
Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
-rw-r--r-- | runtime/autoload/spotbugs.vim | 250 | ||||
-rw-r--r-- | runtime/compiler/javac.vim | 8 | ||||
-rw-r--r-- | runtime/compiler/maven.vim | 2 | ||||
-rw-r--r-- | runtime/compiler/spotbugs.vim | 189 | ||||
-rw-r--r-- | runtime/doc/quickfix.txt | 112 | ||||
-rw-r--r-- | runtime/ftplugin/java.vim | 138 |
6 files changed, 686 insertions, 13 deletions
diff --git a/runtime/autoload/spotbugs.vim b/runtime/autoload/spotbugs.vim new file mode 100644 index 0000000000..9161395794 --- /dev/null +++ b/runtime/autoload/spotbugs.vim @@ -0,0 +1,250 @@ +" Default pre- and post-compiler actions for SpotBugs +" Maintainers: @konfekt and @zzzyxwvut +" Last Change: 2024 Nov 27 + +let s:save_cpo = &cpo +set cpo&vim + +if v:version > 900 + + function! spotbugs#DeleteClassFiles() abort + if !exists('b:spotbugs_class_files') + return + endif + + for pathname in b:spotbugs_class_files + let classname = pathname =~# "^'.\\+\\.class'$" + \ ? eval(pathname) + \ : pathname + + if classname =~# '\.class$' && filereadable(classname) + " Since v9.0.0795. + let octad = readblob(classname, 0, 8) + + " Test the magic number and the major version number (45 for v1.0). + " Since v9.0.2027. + if len(octad) == 8 && octad[0 : 3] == 0zcafe.babe && + \ or((octad[6] << 8), octad[7]) >= 45 + echomsg printf('Deleting %s: %d', classname, delete(classname)) + endif + endif + endfor + + let b:spotbugs_class_files = [] + endfunction + +else + + function! s:DeleteClassFilesWithNewLineCodes(classname) abort + " The distribution of "0a"s in class file versions 2560 and 2570: + " + " 0zca.fe.ba.be.00.00.0a.00 0zca.fe.ba.be.00.00.0a.0a + " 0zca.fe.ba.be.00.0a.0a.00 0zca.fe.ba.be.00.0a.0a.0a + " 0zca.fe.ba.be.0a.00.0a.00 0zca.fe.ba.be.0a.00.0a.0a + " 0zca.fe.ba.be.0a.0a.0a.00 0zca.fe.ba.be.0a.0a.0a.0a + let numbers = [0, 0, 0, 0, 0, 0, 0, 0] + let offset = 0 + let lines = readfile(a:classname, 'b', 4) + + " Track NL byte counts to handle files of less than 8 bytes. + let nl_cnt = len(lines) + " Track non-NL byte counts for "0zca.fe.ba.be.0a.0a.0a.0a". + let non_nl_cnt = 0 + + for line in lines + for idx in range(strlen(line)) + " Remap NLs to Nuls. + let numbers[offset] = (line[idx] == "\n") ? 0 : char2nr(line[idx]) % 256 + let non_nl_cnt += 1 + let offset += 1 + + if offset > 7 + break + endif + endfor + + let nl_cnt -= 1 + + if offset > 7 || (nl_cnt < 1 && non_nl_cnt > 4) + break + endif + + " Reclaim NLs. + let numbers[offset] = 10 + let offset += 1 + + if offset > 7 + break + endif + endfor + + " Test the magic number and the major version number (45 for v1.0). + if offset > 7 && numbers[0] == 0xca && numbers[1] == 0xfe && + \ numbers[2] == 0xba && numbers[3] == 0xbe && + \ (numbers[6] * 256 + numbers[7]) >= 45 + echomsg printf('Deleting %s: %d', a:classname, delete(a:classname)) + endif + endfunction + + function! spotbugs#DeleteClassFiles() abort + if !exists('b:spotbugs_class_files') + return + endif + + let encoding = &encoding + + try + set encoding=latin1 + + for pathname in b:spotbugs_class_files + let classname = pathname =~# "^'.\\+\\.class'$" + \ ? eval(pathname) + \ : pathname + + if classname =~# '\.class$' && filereadable(classname) + let line = get(readfile(classname, 'b', 1), 0, '') + let length = strlen(line) + + " Test the magic number and the major version number (45 for v1.0). + if length > 3 && line[0 : 3] == "\xca\xfe\xba\xbe" + if length > 7 && ((line[6] == "\n" ? 0 : char2nr(line[6]) % 256) * 256 + + \ (line[7] == "\n" ? 0 : char2nr(line[7]) % 256)) >= 45 + echomsg printf('Deleting %s: %d', classname, delete(classname)) + else + call s:DeleteClassFilesWithNewLineCodes(classname) + endif + endif + endif + endfor + finally + let &encoding = encoding + endtry + + let b:spotbugs_class_files = [] + endfunction + +endif + +function! spotbugs#DefaultPostCompilerAction() abort + " Since v7.4.191. + make %:S +endfunction + +" Look for "spotbugs#compiler" in "ftplugin/java.vim". +let s:compiler = exists('spotbugs#compiler') ? spotbugs#compiler : '' +let s:readable = filereadable($VIMRUNTIME . '/compiler/' . s:compiler . '.vim') + +if s:readable && s:compiler ==# 'maven' && executable('mvn') + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make compile + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make test-compile + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': 'src/main/java', + \ 'classDirPath': 'target/classes', + \ 'testSourceDirPath': 'src/test/java', + \ 'testClassDirPath': 'target/test-classes', + \ } + endfunction + + unlet s:readable s:compiler +elseif s:readable && s:compiler ==# 'ant' && executable('ant') + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler ant + make compile + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler ant + make compile-test + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': 'src', + \ 'classDirPath': 'build/classes', + \ 'testSourceDirPath': 'test', + \ 'testClassDirPath': 'build/test/classes', + \ } + endfunction + + unlet s:readable s:compiler +elseif s:readable && s:compiler ==# 'javac' && executable('javac') + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler javac + + if get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')) =~ '\s@\S' + " Read options and filenames from @options [@sources ...]. + make + else + " Let Javac figure out what files to compile. + execute 'make ' . join(map(filter(copy(v:argv), + \ "v:val =~# '\\.java\\=$'"), + \ 'shellescape(v:val)'), ' ') + endif + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DefaultPreCompilerAction() + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ } + endfunction + + unlet s:readable s:compiler +else + + function! spotbugs#DefaultPreCompilerAction() abort + echomsg printf('Not supported: "%s"', s:compiler) + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DefaultPreCompilerAction() + endfunction + + function! spotbugs#DefaultProperties() abort + return {} + endfunction + + unlet s:readable +endif + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim: set foldmethod=syntax shiftwidth=2 expandtab: diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim index 9bd4cdf270..53cd772ed8 100644 --- a/runtime/compiler/javac.vim +++ b/runtime/compiler/javac.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Java Development Kit Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2024 Jun 14 +" Last Change: 2024 Nov 19 (enable local javac_makeprg_params) if exists("current_compiler") finish @@ -11,11 +11,7 @@ let current_compiler = "javac" let s:cpo_save = &cpo set cpo&vim -if exists("g:javac_makeprg_params") - execute $'CompilerSet makeprg=javac\ {escape(g:javac_makeprg_params, ' \|"')}' -else - CompilerSet makeprg=javac -endif +execute $'CompilerSet makeprg=javac\ {escape(get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')), ' \|"')}' CompilerSet errorformat=%E%f:%l:\ error:\ %m, \%W%f:%l:\ warning:\ %m, diff --git a/runtime/compiler/maven.vim b/runtime/compiler/maven.vim index ef8d8a6fb2..72e74e301d 100644 --- a/runtime/compiler/maven.vim +++ b/runtime/compiler/maven.vim @@ -14,7 +14,7 @@ if exists("current_compiler") endif let current_compiler = "maven" -CompilerSet makeprg=mvn\ --batch-mode +execute $'CompilerSet makeprg=mvn\ --batch-mode\ {escape(get(b:, 'maven_makeprg_params', get(g:, 'maven_makeprg_params', '')), ' \|"')}' " Error message for POM CompilerSet errorformat=[FATAL]\ Non-parseable\ POM\ %f:\ %m%\\s%\\+@%.%#line\ %l\\,\ column\ %c%.%#, diff --git a/runtime/compiler/spotbugs.vim b/runtime/compiler/spotbugs.vim new file mode 100644 index 0000000000..72a5084976 --- /dev/null +++ b/runtime/compiler/spotbugs.vim @@ -0,0 +1,189 @@ +" Vim compiler file +" Compiler: Spotbugs (Java static checker; needs javac compiled classes) +" Maintainer: @konfekt and @zzzyxwvut +" Last Change: 2024 Nov 27 + +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') + + 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('g:spotbugs_properties') && + \ (has_key(g:spotbugs_properties, 'sourceDirPath') && + \ has_key(g:spotbugs_properties, 'classDirPath')) || + \ (has_key(g:spotbugs_properties, 'testSourceDirPath') && + \ has_key(g:spotbugs_properties, 'testClassDirPath')) + +function! s:FindClassFiles(src_type_name) abort + let class_files = [] + " Match pairwise the components of source and class pathnames + for [src_dir, bin_dir] in filter([ + \ [get(g:spotbugs_properties, 'sourceDirPath', ''), + \ get(g:spotbugs_properties, 'classDirPath', '')], + \ [get(g:spotbugs_properties, 'testSourceDirPath', ''), + \ get(g:spotbugs_properties, 'testClassDirPath', '')]], + \ '!(empty(v:val[0]) || empty(v:val[1]))') + " Since only the rightmost "src" is sought, while there can be any number of + " such filenames, no "fnamemodify(a:src_type_name, ':p:s?src?bin?')" is used + let tail_idx = strridx(a:src_type_name, src_dir) + " No such directory or no such inner type (i.e. without "$") + if tail_idx < 0 | continue | endif + " Substitute "bin_dir" for the rightmost "src_dir" + let candidate_type_name = strpart(a:src_type_name, 0, tail_idx).. + \ bin_dir.. + \ strpart(a:src_type_name, (tail_idx + strlen(src_dir))) + 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 + 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 + +function! s:CollectClassFiles() abort + " Get a platform-independent pathname prefix, cf. "expand('%:p:h')..'/'" + let pathname = expand('%:p') + 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 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 %:p'..s:package_dir_heads..':S -sourcepath %:p'..s:package_dir_heads..':S '.. + \ 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:FindClassFiles +delfunction s:GlobClassFiles +delfunction s:GetDeclaredTypeNames +let &cpo = s:cpo_save +unlet s:package_dir_heads s:package s:package_names s:type_names s:keywords s:cpo_save + +" vim: set foldmethod=syntax shiftwidth=2 expandtab: diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index b3399b2766..4811a51a87 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1317,9 +1317,117 @@ g:compiler_gcc_ignore_unmatched_lines JAVAC *compiler-javac* Commonly used compiler options can be added to 'makeprg' by setting the -g:javac_makeprg_params variable. For example: > +b/g:javac_makeprg_params variable. For example: > + let g:javac_makeprg_params = "-Xlint:all -encoding utf-8" -< + +MAVEN *compiler-maven* + +Commonly used compiler options can be added to 'makeprg' by setting the +b/g:maven_makeprg_params variable. For example: > + + let g:maven_makeprg_params = "-DskipTests -U -X" + +SPOTBUGS *compiler-spotbugs* + +SpotBugs is a static analysis tool that can be used to find bugs in Java. +It scans the Java bytecode of all classes in the currently open buffer. +(Therefore, `:compiler! spotbugs` is not supported.) + +Commonly used compiler options can be added to 'makeprg' by setting the +"b:" or "g:spotbugs_makeprg_params" variable. For example: > + + let b:spotbugs_makeprg_params = "-longBugCodes -effort:max -low" + +The global default is "-workHard -experimental". + +By default, the class files are searched in the directory where the source +files are placed. However, typical Java projects use distinct directories +for source files and class files. To make both known to SpotBugs, assign +their paths (distinct and relative to their common root directory) to the +following properties (using the example of a common Maven project): > + + let g:spotbugs_properties = { + \ 'sourceDirPath': 'src/main/java', + \ 'classDirPath': 'target/classes', + \ 'testSourceDirPath': 'src/test/java', + \ 'testClassDirPath': 'target/test-classes', + \ } + +Note that values for the path keys describe only for SpotBugs where to look +for files; refer to the documentation for particular compiler plugins for more +information. + +The default pre- and post-compiler actions are provided for Ant, Maven, and +Javac compiler plugins and can be selected by assigning the name of a compiler +plugin to the "compiler" key: > + + let g:spotbugs_properties = { + \ 'compiler': 'maven', + \ } + +This single setting is essentially equivalent to all the settings below, with +the exception made for the "PreCompilerAction" and "PreCompilerTestAction" +values: their listed |Funcref|s will obtain no-op implementations whereas the +implicit Funcrefs of the "compiler" key will obtain the requested defaults if +available. > + + let g:spotbugs_properties = { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': 'src/main/java', + \ 'classDirPath': 'target/classes', + \ 'testSourceDirPath': 'src/test/java', + \ 'testClassDirPath': 'target/test-classes', + \ } + +With default actions, the compiler of choice will attempt to rebuild the class +files for the buffer (and possibly for the whole project) as soon as a Java +syntax file is loaded; then, `spotbugs` will attempt to analyze the quality of +the compilation unit of the buffer. + +When default actions are not suited to a desired workflow, consider writing +arbitrary functions yourself and matching their |Funcref|s to the supported +keys: "PreCompilerAction", "PreCompilerTestAction", and "PostCompilerAction". + +The next example re-implements the default pre-compiler actions for a Maven +project and requests other default Maven settings with the "compiler" entry: > + + function! MavenPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make compile + endfunction + + function! MavenPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make test-compile + endfunction + + let g:spotbugs_properties = { + \ 'compiler': 'maven', + \ 'PreCompilerAction': + \ function('MavenPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('MavenPreCompilerTestAction'), + \ } + +Note that all entered custom settings will take precedence over the matching +default settings in "g:spotbugs_properties". + +The "g:spotbugs_properties" variable is consulted by the Java filetype plugin +(|ft-java-plugin|) to arrange for the described automation, and, therefore, it +must be defined before |FileType| events can take place for the buffers loaded +with Java source files. It could, for example, be set in a project-local +|vimrc| loaded by [0]. + +[0] https://github.com/MarcWeber/vim-addon-local-vimrc/ + GNU MAKE *compiler-make* Since the default make program is "make", the compiler plugin for make, diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim index 55b358374f..6e12fe2fe5 100644 --- a/runtime/ftplugin/java.vim +++ b/runtime/ftplugin/java.vim @@ -3,7 +3,7 @@ " Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> " Former Maintainer: Dan Sharp " Repository: https://github.com/zzzyxwvut/java-vim.git -" Last Change: 2024 Sep 26 +" Last Change: 2024 Nov 24 " 2024 Jan 14 by Vim Project (browsefilter) " 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring') @@ -90,10 +90,127 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") endif endif +" The support for pre- and post-compiler actions for SpotBugs. +if exists("g:spotbugs_properties") && has_key(g:spotbugs_properties, 'compiler') + try + let spotbugs#compiler = g:spotbugs_properties.compiler + let g:spotbugs_properties = extend( + \ spotbugs#DefaultProperties(), + \ g:spotbugs_properties, + \ 'force') + catch + echomsg v:errmsg + finally + call remove(g:spotbugs_properties, 'compiler') + endtry +endif + +if exists("g:spotbugs_properties") && + \ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim') + let s:request = 0 + + if has_key(g:spotbugs_properties, 'PreCompilerAction') + let s:dispatcher = 'call g:spotbugs_properties.PreCompilerAction() | ' + let s:request += 1 + endif + + if has_key(g:spotbugs_properties, 'PreCompilerTestAction') + let s:dispatcher = 'call g:spotbugs_properties.PreCompilerTestAction() | ' + let s:request += 2 + endif + + if has_key(g:spotbugs_properties, 'PostCompilerAction') + let s:request += 4 + endif + + if (s:request == 3 || s:request == 7) && + \ has_key(g:spotbugs_properties, 'sourceDirPath') && + \ has_key(g:spotbugs_properties, 'testSourceDirPath') + function! s:DispatchAction(path_action_pairs) abort + let name = expand('%:p') + + for [path, Action] in a:path_action_pairs + if name =~# (path . '.\{-}\.java\=$') + call Action() + break + endif + endfor + endfunction + + let s:dispatcher = printf('call s:DispatchAction(%s) | ', + \ string([[g:spotbugs_properties.sourceDirPath, + \ g:spotbugs_properties.PreCompilerAction], + \ [g:spotbugs_properties.testSourceDirPath, + \ g:spotbugs_properties.PreCompilerTestAction]])) + endif + + if s:request + if exists("b:spotbugs_syntax_once") + let s:actions = [{'event': 'BufWritePost'}] + else + " XXX: Handle multiple FileType events when vimrc contains more + " than one filetype setting for the language, e.g.: + " :filetype plugin indent on + " :autocmd BufRead,BufNewFile *.java setlocal filetype=java ... + " XXX: DO NOT ADD b:spotbugs_syntax_once TO b:undo_ftplugin ! + let b:spotbugs_syntax_once = 1 + let s:actions = [{ + \ 'event': 'Syntax', + \ 'once': 1, + \ }, { + \ 'event': 'BufWritePost', + \ }] + endif + + for s:idx in range(len(s:actions)) + if s:request == 7 || s:request == 6 || s:request == 5 + let s:actions[s:idx].cmd = s:dispatcher . 'compiler spotbugs | ' . + \ 'call g:spotbugs_properties.PostCompilerAction()' + elseif s:request == 4 + let s:actions[s:idx].cmd = 'compiler spotbugs | ' . + \ 'call g:spotbugs_properties.PostCompilerAction()' + elseif s:request == 3 || s:request == 2 || s:request == 1 + let s:actions[s:idx].cmd = s:dispatcher . 'compiler spotbugs' + else + let s:actions[s:idx].cmd = '' + endif + endfor + + if !exists("#java_spotbugs") + augroup java_spotbugs + augroup END + endif + + " The events are defined in s:actions. + silent! autocmd! java_spotbugs BufWritePost <buffer> + silent! autocmd! java_spotbugs Syntax <buffer> + + for s:action in s:actions + execute printf('autocmd java_spotbugs %s <buffer> %s', + \ s:action.event, + \ s:action.cmd . (has_key(s:action, 'once') + \ ? printf(' | autocmd! java_spotbugs %s <buffer>', + \ s:action.event) + \ : '')) + endfor + + unlet! s:action s:actions s:idx s:dispatcher + endif + + unlet s:request +endif + +function! JavaFileTypeCleanUp() abort + setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr< + unlet! b:browsefilter + + " The concatenated removals may be misparsed as a BufWritePost autocmd. + silent! autocmd! java_spotbugs BufWritePost <buffer> + silent! autocmd! java_spotbugs Syntax <buffer> +endfunction + " Undo the stuff we changed. -let b:undo_ftplugin = "setlocal suffixes< suffixesadd<" . - \ " formatoptions< comments< commentstring< path< includeexpr<" . - \ " | unlet! b:browsefilter" +let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp' " See ":help vim9-mix". if !has("vim9script") @@ -114,6 +231,19 @@ if exists("s:zip_func_upgradable") setlocal suffixesadd< endif +if exists("*s:DispatchAction") + def! s:DispatchAction(path_action_pairs: list<list<any>>) + const name: string = expand('%:p') + + for [path: string, Action: func: any] in path_action_pairs + if name =~# (path .. '.\{-}\.java\=$') + Action() + break + endif + endfor + enddef +endif + " Restore the saved compatibility options. let &cpo = s:save_cpo unlet s:save_cpo |