vim-patch:65311c6: runtime(compiler): include spotbugs Java linter

closes: vim/vim#16001

65311c6f47

Co-authored-by: Konfekt <Konfekt@users.noreply.github.com>
Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
This commit is contained in:
zeertzjq 2024-11-29 08:54:47 +08:00
parent 146b8300a1
commit 8d7d225caa
6 changed files with 686 additions and 13 deletions

View File

@ -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:

View File

@ -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,

View File

@ -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%.%#,

View File

@ -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:

View File

@ -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,

View File

@ -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