forked from aniani/vim
=============== LIMITATIONS AND OBSERVATIONS =============== * Remember that external-type names can only be found when they match filenames resolvable in "&path" with "import" declarations; load the source file of an external type to look up its nested types and sibling top types, if any. * Strive to narrow the search by assigning only relevant pathnames for directories *or* an archive to "&path", e.g. ":set path-=/usr/include". * Use "{Visual}gf" on fully-qualified names. * Accept the fact that "&define" cannot contain end-of-line characters (":help definition-search"). A declaration whose matchable header is not contained within a line can be found iff all of its non-optional components belong to the same line; for types, such components are a keyword, e.g. "class", followed by a run of blank characters and an identifier, e.g. "Test"; for methods: a return type, e.g. "String", or a keyword "void", followed by a run of blank characters and an identifier, e.g. "toString", that is followed by "(". * The members of the "java.lang" package are usually not associated with "import" declarations; to look up their declarations, load a source file for a member of that package, and then use, on a simple name of interest for a member, either "[-Ctrl-d" etc. for local declarations or "gf" for external declarations, assuming that "." *or* the appropriate pathname for a JDK archive is assigned to "&path". * Follow the above instruction made for the "java.lang" members for any type whose simple name is not associated with an "import" declaration, i.e. a member type of the same package that is declared in another compilation unit. * Append the "$" character to "&iskeyword" when looking up declarations of generated code. See zzzyxwvut/java-vim#4. closes: #17281 Co-authored-by: Konfekt <Konfekt@users.noreply.github.com> Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
419 lines
13 KiB
VimL
419 lines
13 KiB
VimL
" Vim filetype plugin file
|
|
" Language: Java
|
|
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
|
|
" Former Maintainer: Dan Sharp
|
|
" Repository: https://github.com/zzzyxwvut/java-vim.git
|
|
" Last Change: 2025 May 08
|
|
|
|
" Make sure the continuation lines below do not cause problems in
|
|
" compatibility mode.
|
|
let s:save_cpo = &cpo
|
|
set cpo-=C
|
|
|
|
if (exists("g:java_ignore_javadoc") || exists("g:java_ignore_markdown")) &&
|
|
\ exists("*javaformat#RemoveCommonMarkdownWhitespace")
|
|
delfunction javaformat#RemoveCommonMarkdownWhitespace
|
|
unlet! g:loaded_javaformat
|
|
endif
|
|
|
|
if exists("b:did_ftplugin")
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
finish
|
|
endif
|
|
|
|
let b:did_ftplugin = 1
|
|
|
|
" For filename completion, prefer the .java extension over the .class
|
|
" extension.
|
|
set suffixes+=.class
|
|
|
|
" Set up "&define" and "&include".
|
|
let s:peek = ''
|
|
|
|
try
|
|
" Since v7.3.1037.
|
|
if 'ab' !~ 'a\@1<!b'
|
|
let s:peek = string(strlen('instanceof') + 8)
|
|
endif
|
|
catch /\<E59:/
|
|
endtry
|
|
|
|
" Treat "s:common" as a non-backtracking unit to avoid matching constructor
|
|
" declarations whose package-private headers are indistinguishable from method
|
|
" invocation. Note that "[@-]" must not and "$" may not be in "&l:iskeyword".
|
|
let s:common = '\%(\%(\%(@\%(interface\)\@!\%(\K\k*\.\)*\K\k*\)\s\+\)*' .
|
|
\ '\%(p\%(rivate\|rotected\|ublic\)\s\+\)\=\)\@>'
|
|
let s:types = '\%(\%(abstract\|final\|non-sealed\|s\%(ealed\|tatic\|trictfp\)\)\s\+\)*' .
|
|
\ '\%(class\|enum\|@\=interface\|record\)\s\+\ze\K\k*\>'
|
|
let s:methods = '\%(\%(abstract\|default\|final\|native\|s\%(tatic\|trictfp\|ynchronized\)\)\s\+\)*' .
|
|
\ '\%(<.\{-1,}>\s\+\)\=\%(\K\k*\.\)*\K\k*\s*\%(<.\{-1,}>\%(\s\|\[\)\@=\)\=\s*\%(\[\]\s*\)*' .
|
|
\ '\s\+\ze\%(\<\%(assert\|case\|instanceof\|new\|return\|throw\|when\)\s\+\)\@' .
|
|
\ s:peek . '<!\K\k*\s*('
|
|
let &l:define = printf('\C\m^\s*%s\%%(%s\|%s\)', s:common, s:types, s:methods)
|
|
let &l:include = '\C\m^\s*import\s\+\ze\%(\K\k*\.\)\+\K\k*;'
|
|
unlet s:methods s:types s:common s:peek
|
|
|
|
" Enable gf on import statements. Convert . in the package
|
|
" name to / and append .java to the name, then search the path.
|
|
setlocal includeexpr=substitute(v:fname,'\\.','/','g')
|
|
setlocal suffixesadd=.java
|
|
|
|
" Clean up in case this file is sourced again.
|
|
unlet! s:zip_func_upgradable
|
|
|
|
"""" STRIVE TO REMAIN COMPATIBLE FOR AT LEAST VIM 7.0.
|
|
|
|
" Documented in ":help ft-java-plugin".
|
|
if exists("g:ftplugin_java_source_path") &&
|
|
\ type(g:ftplugin_java_source_path) == type("")
|
|
if filereadable(g:ftplugin_java_source_path)
|
|
if exists("#zip") &&
|
|
\ g:ftplugin_java_source_path =~# '.\.\%(jar\|zip\)$'
|
|
if !exists("s:zip_files")
|
|
let s:zip_files = {}
|
|
endif
|
|
|
|
let s:zip_files[bufnr('%')] = g:ftplugin_java_source_path
|
|
let s:zip_files[0] = g:ftplugin_java_source_path
|
|
let s:zip_func_upgradable = 1
|
|
|
|
function! JavaFileTypeZipFile() abort
|
|
let @/ = substitute(v:fname, '\.', '\\/', 'g') . '.java'
|
|
return get(s:zip_files, bufnr('%'), s:zip_files[0])
|
|
endfunction
|
|
|
|
" E120 for "inex=s:JavaFileTypeZipFile()" before v8.2.3900.
|
|
setlocal includeexpr=JavaFileTypeZipFile()
|
|
setlocal suffixesadd<
|
|
endif
|
|
else
|
|
let &l:path = g:ftplugin_java_source_path . ',' . &l:path
|
|
endif
|
|
endif
|
|
|
|
" Set 'formatoptions' to break comment lines but not other lines,
|
|
" and insert the comment leader when hitting <CR> or using "o".
|
|
setlocal formatoptions-=t formatoptions+=croql
|
|
|
|
" Set 'comments' to format Markdown Javadoc comments and dashed lists
|
|
" in other multi-line comments (it behaves just like C).
|
|
setlocal comments& comments^=:///,sO:*\ -,mO:*\ \ ,exO:*/
|
|
|
|
setlocal commentstring=//\ %s
|
|
|
|
" Change the :browse e filter to primarily show Java-related files.
|
|
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
|
|
let b:browsefilter="Java Files (*.java)\t*.java\n" .
|
|
\ "Properties Files (*.prop*)\t*.prop*\n" .
|
|
\ "Manifest Files (*.mf)\t*.mf\n"
|
|
if has("win32")
|
|
let b:browsefilter .= "All Files (*.*)\t*\n"
|
|
else
|
|
let b:browsefilter .= "All Files (*)\t*\n"
|
|
endif
|
|
endif
|
|
|
|
"""" Support pre- and post-compiler actions for SpotBugs.
|
|
if (!empty(get(g:, 'spotbugs_properties', {})) ||
|
|
\ !empty(get(b:, 'spotbugs_properties', {}))) &&
|
|
\ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim')
|
|
|
|
function! s:SpotBugsGetProperty(name, default) abort
|
|
return get(
|
|
\ {s:spotbugs_properties_scope}spotbugs_properties,
|
|
\ a:name,
|
|
\ a:default)
|
|
endfunction
|
|
|
|
function! s:SpotBugsHasProperty(name) abort
|
|
return has_key(
|
|
\ {s:spotbugs_properties_scope}spotbugs_properties,
|
|
\ a:name)
|
|
endfunction
|
|
|
|
function! s:SpotBugsGetProperties() abort
|
|
return {s:spotbugs_properties_scope}spotbugs_properties
|
|
endfunction
|
|
|
|
" Work around ":bar"s and ":autocmd"s.
|
|
function! JavaFileTypeExecuteActionOnce(cleanup_cmd, action_cmd) abort
|
|
try
|
|
execute a:cleanup_cmd
|
|
finally
|
|
execute a:action_cmd
|
|
endtry
|
|
endfunction
|
|
|
|
if exists("b:spotbugs_properties")
|
|
let s:spotbugs_properties_scope = 'b:'
|
|
|
|
" Merge global entries, if any, in buffer-local entries, favouring
|
|
" defined buffer-local ones.
|
|
call extend(
|
|
\ b:spotbugs_properties,
|
|
\ get(g:, 'spotbugs_properties', {}),
|
|
\ 'keep')
|
|
elseif exists("g:spotbugs_properties")
|
|
let s:spotbugs_properties_scope = 'g:'
|
|
endif
|
|
|
|
let s:commands = {}
|
|
|
|
for s:name in ['DefaultPreCompilerCommand',
|
|
\ 'DefaultPreCompilerTestCommand',
|
|
\ 'DefaultPostCompilerCommand']
|
|
if s:SpotBugsHasProperty(s:name)
|
|
let s:commands[s:name] = remove(
|
|
\ s:SpotBugsGetProperties(),
|
|
\ s:name)
|
|
endif
|
|
endfor
|
|
|
|
if s:SpotBugsHasProperty('compiler')
|
|
" XXX: Postpone loading the script until all state, if any, has been
|
|
" collected.
|
|
if !empty(s:commands)
|
|
let g:spotbugs#state = {
|
|
\ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
|
|
\ 'commands': copy(s:commands),
|
|
\ }
|
|
else
|
|
let g:spotbugs#state = {
|
|
\ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
|
|
\ }
|
|
endif
|
|
|
|
" Merge default entries in global (or buffer-local) entries, favouring
|
|
" defined global (or buffer-local) ones.
|
|
call extend(
|
|
\ {s:spotbugs_properties_scope}spotbugs_properties,
|
|
\ spotbugs#DefaultProperties(),
|
|
\ 'keep')
|
|
elseif !empty(s:commands)
|
|
" XXX: Postpone loading the script until all state, if any, has been
|
|
" collected.
|
|
let g:spotbugs#state = {'commands': copy(s:commands)}
|
|
endif
|
|
|
|
unlet s:commands s:name
|
|
let s:request = 0
|
|
|
|
if s:SpotBugsHasProperty('PostCompilerAction')
|
|
let s:request += 4
|
|
endif
|
|
|
|
if s:SpotBugsHasProperty('PreCompilerTestAction')
|
|
let s:dispatcher = printf('call call(%s, [])',
|
|
\ string(s:SpotBugsGetProperties().PreCompilerTestAction))
|
|
let s:request += 2
|
|
endif
|
|
|
|
if s:SpotBugsHasProperty('PreCompilerAction')
|
|
let s:dispatcher = printf('call call(%s, [])',
|
|
\ string(s:SpotBugsGetProperties().PreCompilerAction))
|
|
let s:request += 1
|
|
endif
|
|
|
|
" Adapt the tests for "s:FindClassFiles()" from "compiler/spotbugs.vim".
|
|
if (s:request == 3 || s:request == 7) &&
|
|
\ (!empty(s:SpotBugsGetProperty('sourceDirPath', [])) &&
|
|
\ !empty(s:SpotBugsGetProperty('classDirPath', [])) &&
|
|
\ !empty(s:SpotBugsGetProperty('testSourceDirPath', [])) &&
|
|
\ !empty(s:SpotBugsGetProperty('testClassDirPath', [])))
|
|
function! s:DispatchAction(paths_action_pairs) abort
|
|
let name = expand('%:p')
|
|
|
|
for [paths, Action] in a:paths_action_pairs
|
|
for path in paths
|
|
if name =~# (path . '.\{-}\.java\=$')
|
|
call Action()
|
|
return
|
|
endif
|
|
endfor
|
|
endfor
|
|
endfunction
|
|
|
|
let s:dir_cnt = min([
|
|
\ len(s:SpotBugsGetProperties().sourceDirPath),
|
|
\ len(s:SpotBugsGetProperties().classDirPath)])
|
|
let s:test_dir_cnt = min([
|
|
\ len(s:SpotBugsGetProperties().testSourceDirPath),
|
|
\ len(s:SpotBugsGetProperties().testClassDirPath)])
|
|
|
|
" Do not break up path pairs with filtering!
|
|
let s:dispatcher = printf('call s:DispatchAction(%s)',
|
|
\ string([[s:SpotBugsGetProperties().sourceDirPath[0 : s:dir_cnt - 1],
|
|
\ s:SpotBugsGetProperties().PreCompilerAction],
|
|
\ [s:SpotBugsGetProperties().testSourceDirPath[0 : s:test_dir_cnt - 1],
|
|
\ s:SpotBugsGetProperties().PreCompilerTestAction]]))
|
|
unlet s:test_dir_cnt s:dir_cnt
|
|
endif
|
|
|
|
if exists("s:dispatcher")
|
|
function! s:ExecuteActions(pre_action, post_action) abort
|
|
try
|
|
execute a:pre_action
|
|
catch /\<E42:/
|
|
execute a:post_action
|
|
endtry
|
|
endfunction
|
|
endif
|
|
|
|
if s:request
|
|
if exists("b:spotbugs_syntax_once") || empty(join(getline(1, 8), ''))
|
|
let s:actions = [{'event': 'User'}]
|
|
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': 'User',
|
|
\ }]
|
|
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 = printf('call s:ExecuteActions(%s, %s)',
|
|
\ string(s:dispatcher),
|
|
\ string(printf('compiler spotbugs | call call(%s, [])',
|
|
\ string(s:SpotBugsGetProperties().PostCompilerAction))))
|
|
elseif s:request == 4
|
|
let s:actions[s:idx].cmd = printf(
|
|
\ 'compiler spotbugs | call call(%s, [])',
|
|
\ string(s:SpotBugsGetProperties().PostCompilerAction))
|
|
elseif s:request == 3 || s:request == 2 || s:request == 1
|
|
let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)',
|
|
\ string(s:dispatcher),
|
|
\ string('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 User <buffer>
|
|
silent! autocmd! java_spotbugs Syntax <buffer>
|
|
|
|
for s:action in s:actions
|
|
if has_key(s:action, 'once')
|
|
execute printf('autocmd java_spotbugs %s <buffer> ' .
|
|
\ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
|
|
\ s:action.event,
|
|
\ string(printf('autocmd! java_spotbugs %s <buffer>',
|
|
\ s:action.event)),
|
|
\ string(s:action.cmd))
|
|
else
|
|
execute printf('autocmd java_spotbugs %s <buffer> %s',
|
|
\ s:action.event,
|
|
\ s:action.cmd)
|
|
endif
|
|
endfor
|
|
|
|
if s:SpotBugsHasProperty('PostCompilerActionExecutor') &&
|
|
\ (s:request == 7 || s:request == 6 ||
|
|
\ s:request == 5 || s:request == 4)
|
|
let s:augroup = s:SpotBugsGetProperty(
|
|
\ 'augroupForPostCompilerAction',
|
|
\ 'java_spotbugs_post')
|
|
let s:augroup = !empty(s:augroup) ? s:augroup : 'java_spotbugs_post'
|
|
|
|
for s:candidate in ['java_spotbugs_post', s:augroup]
|
|
if !exists("#" . s:candidate)
|
|
execute printf('augroup %s | augroup END', s:candidate)
|
|
endif
|
|
endfor
|
|
|
|
silent! autocmd! java_spotbugs_post User <buffer>
|
|
|
|
" Define a User ":autocmd" to define a once-only ShellCmdPost
|
|
" ":autocmd" that will invoke "PostCompilerActionExecutor" and let
|
|
" it decide whether to proceed with ":compiler spotbugs" etc.; and
|
|
" seek explicit synchronisation with ":doautocmd ShellCmdPost" by
|
|
" omitting "nested" for "java_spotbugs_post" and "java_spotbugs".
|
|
execute printf('autocmd java_spotbugs_post User <buffer> ' .
|
|
\ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
|
|
\ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
|
|
\ string(printf('autocmd %s ShellCmdPost <buffer> ' .
|
|
\ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
|
|
\ s:augroup,
|
|
\ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
|
|
\ string(printf('call call(%s, [%s])',
|
|
\ string(s:SpotBugsGetProperties().PostCompilerActionExecutor),
|
|
\ string(printf('compiler spotbugs | call call(%s, [])',
|
|
\ string(s:SpotBugsGetProperties().PostCompilerAction))))))))
|
|
endif
|
|
|
|
unlet! s:candidate s:augroup s:action s:actions s:idx s:dispatcher
|
|
endif
|
|
|
|
delfunction s:SpotBugsGetProperties
|
|
delfunction s:SpotBugsHasProperty
|
|
delfunction s:SpotBugsGetProperty
|
|
unlet! s:request s:spotbugs_properties_scope
|
|
endif
|
|
|
|
function! JavaFileTypeCleanUp() abort
|
|
setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr< include< define<
|
|
unlet! b:browsefilter
|
|
|
|
" The concatenated ":autocmd" removals may be misparsed as an ":autocmd".
|
|
" A _once-only_ ShellCmdPost ":autocmd" is always a call-site definition.
|
|
silent! autocmd! java_spotbugs User <buffer>
|
|
silent! autocmd! java_spotbugs Syntax <buffer>
|
|
silent! autocmd! java_spotbugs_post User <buffer>
|
|
endfunction
|
|
|
|
" Undo the stuff we changed.
|
|
let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp'
|
|
|
|
" See ":help vim9-mix".
|
|
if !has("vim9script")
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
finish
|
|
endif
|
|
|
|
if exists("s:zip_func_upgradable")
|
|
delfunction! JavaFileTypeZipFile
|
|
|
|
def! s:JavaFileTypeZipFile(): string
|
|
@/ = substitute(v:fname, '\.', '\\/', 'g') .. '.java'
|
|
return get(zip_files, bufnr('%'), zip_files[0])
|
|
enddef
|
|
|
|
setlocal includeexpr=s:JavaFileTypeZipFile()
|
|
setlocal suffixesadd<
|
|
endif
|
|
|
|
if exists("*s:DispatchAction")
|
|
def! s:DispatchAction(paths_action_pairs: list<list<any>>)
|
|
const name: string = expand('%:p')
|
|
|
|
for [paths: list<string>, Action: func: any] in paths_action_pairs
|
|
for path in paths
|
|
if name =~# (path .. '.\{-}\.java\=$')
|
|
Action()
|
|
return
|
|
endif
|
|
endfor
|
|
endfor
|
|
enddef
|
|
endif
|
|
|
|
" Restore the saved compatibility options.
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
" vim: fdm=syntax sw=4 ts=8 noet sta
|