mirror of
https://github.com/neovim/neovim.git
synced 2026-08-14 11:29:31 +00:00
Problem:
`zipfile://{archive}::{entry}` is ambiguous: `::` is legal in both an
archive path and an entry path, so the separator cannot be identified.
Splitting at the last `::` reads archives correctly but breaks entries
that contain it, and the plugin then emits buffer names it cannot read
back.
Solution:
Address entries as `zip://{archive}/{entry}`, joining the two paths.
Resolve the split by walking components: the first one that is a regular
file is the archive, because a regular file cannot have children on disk.
Entry paths may then contain any character, and no escaping is needed.
zipPlugin.vim keeps its own scheme, so the Java ftplugin emits whichever
form matches the active plugin until the legacy package is removed.
427 lines
14 KiB
VimL
427 lines
14 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") || exists("#nvim.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 l:member = substitute(v:fname, '\.', '/', 'g') . '.java'
|
|
let l:archive = get(s:zip_files, bufnr('%'), s:zip_files[0])
|
|
" The builtin plugin joins the paths; zipPlugin.vim separates them with "::".
|
|
return exists('#nvim.zip')
|
|
\ ? 'zip://' . l:archive . '/' . l:member
|
|
\ : 'zipfile://' . l:archive . '::' . l:member
|
|
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
|
|
const member: string = substitute(v:fname, '\.', '/', 'g') .. '.java'
|
|
const archive: string = get(zip_files, bufnr('%'), zip_files[0])
|
|
# The builtin plugin joins the paths; zipPlugin.vim separates them with "::".
|
|
return exists('#nvim.zip')
|
|
? 'zip://' .. archive .. '/' .. member
|
|
: 'zipfile://' .. archive .. '::' .. member
|
|
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
|