blob: 4a08c57dd40f258d2c22dca22f52611a2350369c [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim filetype plugin file
Doug Kearns93197fd2024-01-14 20:59:02 +01002" Language: Java
Aliaksei Budavei40524742024-04-14 19:57:00 +03003" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
4" Former Maintainer: Dan Sharp
5" Repository: https://github.com/zzzyxwvut/java-vim.git
Aliaksei Budavei73440242025-05-10 21:44:07 +02006" Last Change: 2025 May 08
Bram Moolenaar071d4272004-06-13 20:20:40 +00007
Bram Moolenaar071d4272004-06-13 20:20:40 +00008" Make sure the continuation lines below do not cause problems in
9" compatibility mode.
10let s:save_cpo = &cpo
11set cpo-=C
12
Aliaksei Budavei85f054a2024-09-30 19:40:04 +020013if (exists("g:java_ignore_javadoc") || exists("g:java_ignore_markdown")) &&
14 \ exists("*javaformat#RemoveCommonMarkdownWhitespace")
15 delfunction javaformat#RemoveCommonMarkdownWhitespace
16 unlet! g:loaded_javaformat
17endif
18
19if exists("b:did_ftplugin")
20 let &cpo = s:save_cpo
21 unlet s:save_cpo
22 finish
23endif
24
25let b:did_ftplugin = 1
26
Bram Moolenaar071d4272004-06-13 20:20:40 +000027" For filename completion, prefer the .java extension over the .class
28" extension.
29set suffixes+=.class
30
Aliaksei Budavei73440242025-05-10 21:44:07 +020031" Set up "&define" and "&include".
32let s:peek = ''
33
34try
35 " Since v7.3.1037.
36 if 'ab' !~ 'a\@1<!b'
37 let s:peek = string(strlen('instanceof') + 8)
38 endif
39catch /\<E59:/
40endtry
41
42" Treat "s:common" as a non-backtracking unit to avoid matching constructor
43" declarations whose package-private headers are indistinguishable from method
44" invocation. Note that "[@-]" must not and "$" may not be in "&l:iskeyword".
45let s:common = '\%(\%(\%(@\%(interface\)\@!\%(\K\k*\.\)*\K\k*\)\s\+\)*' .
46 \ '\%(p\%(rivate\|rotected\|ublic\)\s\+\)\=\)\@>'
47let s:types = '\%(\%(abstract\|final\|non-sealed\|s\%(ealed\|tatic\|trictfp\)\)\s\+\)*' .
48 \ '\%(class\|enum\|@\=interface\|record\)\s\+\ze\K\k*\>'
49let s:methods = '\%(\%(abstract\|default\|final\|native\|s\%(tatic\|trictfp\|ynchronized\)\)\s\+\)*' .
50 \ '\%(<.\{-1,}>\s\+\)\=\%(\K\k*\.\)*\K\k*\s*\%(<.\{-1,}>\%(\s\|\[\)\@=\)\=\s*\%(\[\]\s*\)*' .
51 \ '\s\+\ze\%(\<\%(assert\|case\|instanceof\|new\|return\|throw\|when\)\s\+\)\@' .
52 \ s:peek . '<!\K\k*\s*('
53let &l:define = printf('\C\m^\s*%s\%%(%s\|%s\)', s:common, s:types, s:methods)
54let &l:include = '\C\m^\s*import\s\+\ze\%(\K\k*\.\)\+\K\k*;'
55unlet s:methods s:types s:common s:peek
56
Bram Moolenaar071d4272004-06-13 20:20:40 +000057" Enable gf on import statements. Convert . in the package
58" name to / and append .java to the name, then search the path.
59setlocal includeexpr=substitute(v:fname,'\\.','/','g')
60setlocal suffixesadd=.java
Aliaksei Budavei36e667a2024-04-18 23:01:52 +020061
62" Clean up in case this file is sourced again.
63unlet! s:zip_func_upgradable
64
Aliaksei Budavei85f054a2024-09-30 19:40:04 +020065"""" STRIVE TO REMAIN COMPATIBLE FOR AT LEAST VIM 7.0.
66
Aliaksei Budavei36e667a2024-04-18 23:01:52 +020067" Documented in ":help ft-java-plugin".
68if exists("g:ftplugin_java_source_path") &&
69 \ type(g:ftplugin_java_source_path) == type("")
70 if filereadable(g:ftplugin_java_source_path)
71 if exists("#zip") &&
72 \ g:ftplugin_java_source_path =~# '.\.\%(jar\|zip\)$'
73 if !exists("s:zip_files")
74 let s:zip_files = {}
75 endif
76
77 let s:zip_files[bufnr('%')] = g:ftplugin_java_source_path
78 let s:zip_files[0] = g:ftplugin_java_source_path
79 let s:zip_func_upgradable = 1
80
81 function! JavaFileTypeZipFile() abort
82 let @/ = substitute(v:fname, '\.', '\\/', 'g') . '.java'
83 return get(s:zip_files, bufnr('%'), s:zip_files[0])
84 endfunction
85
86 " E120 for "inex=s:JavaFileTypeZipFile()" before v8.2.3900.
87 setlocal includeexpr=JavaFileTypeZipFile()
88 setlocal suffixesadd<
89 endif
90 else
91 let &l:path = g:ftplugin_java_source_path . ',' . &l:path
92 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000093endif
94
95" Set 'formatoptions' to break comment lines but not other lines,
96" and insert the comment leader when hitting <CR> or using "o".
97setlocal formatoptions-=t formatoptions+=croql
98
Aliaksei Budavei85f054a2024-09-30 19:40:04 +020099" Set 'comments' to format Markdown Javadoc comments and dashed lists
100" in other multi-line comments (it behaves just like C).
101setlocal comments& comments^=:///,sO:*\ -,mO:*\ \ ,exO:*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Riley Bruins0a083062024-06-03 20:40:45 +0200103setlocal commentstring=//\ %s
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104
105" Change the :browse e filter to primarily show Java-related files.
Doug Kearns93197fd2024-01-14 20:59:02 +0100106if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107 let b:browsefilter="Java Files (*.java)\t*.java\n" .
108 \ "Properties Files (*.prop*)\t*.prop*\n" .
Doug Kearns93197fd2024-01-14 20:59:02 +0100109 \ "Manifest Files (*.mf)\t*.mf\n"
110 if has("win32")
111 let b:browsefilter .= "All Files (*.*)\t*\n"
112 else
113 let b:browsefilter .= "All Files (*)\t*\n"
114 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115endif
116
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100117"""" Support pre- and post-compiler actions for SpotBugs.
118if (!empty(get(g:, 'spotbugs_properties', {})) ||
119 \ !empty(get(b:, 'spotbugs_properties', {}))) &&
120 \ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim')
Konfekt65311c62024-11-28 21:06:09 +0100121
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100122 function! s:SpotBugsGetProperty(name, default) abort
123 return get(
124 \ {s:spotbugs_properties_scope}spotbugs_properties,
125 \ a:name,
126 \ a:default)
127 endfunction
128
129 function! s:SpotBugsHasProperty(name) abort
130 return has_key(
131 \ {s:spotbugs_properties_scope}spotbugs_properties,
132 \ a:name)
133 endfunction
134
135 function! s:SpotBugsGetProperties() abort
136 return {s:spotbugs_properties_scope}spotbugs_properties
137 endfunction
138
139 " Work around ":bar"s and ":autocmd"s.
Aliaksei Budavei2e252472024-12-27 16:46:36 +0100140 function! JavaFileTypeExecuteActionOnce(cleanup_cmd, action_cmd) abort
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100141 try
142 execute a:cleanup_cmd
143 finally
144 execute a:action_cmd
145 endtry
146 endfunction
147
148 if exists("b:spotbugs_properties")
149 let s:spotbugs_properties_scope = 'b:'
150
151 " Merge global entries, if any, in buffer-local entries, favouring
152 " defined buffer-local ones.
153 call extend(
154 \ b:spotbugs_properties,
155 \ get(g:, 'spotbugs_properties', {}),
156 \ 'keep')
157 elseif exists("g:spotbugs_properties")
158 let s:spotbugs_properties_scope = 'g:'
159 endif
160
161 let s:commands = {}
162
163 for s:name in ['DefaultPreCompilerCommand',
164 \ 'DefaultPreCompilerTestCommand',
165 \ 'DefaultPostCompilerCommand']
166 if s:SpotBugsHasProperty(s:name)
167 let s:commands[s:name] = remove(
168 \ s:SpotBugsGetProperties(),
169 \ s:name)
170 endif
171 endfor
172
173 if s:SpotBugsHasProperty('compiler')
174 " XXX: Postpone loading the script until all state, if any, has been
175 " collected.
176 if !empty(s:commands)
177 let g:spotbugs#state = {
178 \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
179 \ 'commands': copy(s:commands),
180 \ }
181 else
182 let g:spotbugs#state = {
183 \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
184 \ }
185 endif
186
187 " Merge default entries in global (or buffer-local) entries, favouring
188 " defined global (or buffer-local) ones.
189 call extend(
190 \ {s:spotbugs_properties_scope}spotbugs_properties,
191 \ spotbugs#DefaultProperties(),
192 \ 'keep')
193 elseif !empty(s:commands)
194 " XXX: Postpone loading the script until all state, if any, has been
195 " collected.
196 let g:spotbugs#state = {'commands': copy(s:commands)}
197 endif
198
199 unlet s:commands s:name
Konfekt65311c62024-11-28 21:06:09 +0100200 let s:request = 0
201
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100202 if s:SpotBugsHasProperty('PostCompilerAction')
Konfekt65311c62024-11-28 21:06:09 +0100203 let s:request += 4
204 endif
205
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100206 if s:SpotBugsHasProperty('PreCompilerTestAction')
207 let s:dispatcher = printf('call call(%s, [])',
208 \ string(s:SpotBugsGetProperties().PreCompilerTestAction))
209 let s:request += 2
210 endif
211
212 if s:SpotBugsHasProperty('PreCompilerAction')
213 let s:dispatcher = printf('call call(%s, [])',
214 \ string(s:SpotBugsGetProperties().PreCompilerAction))
215 let s:request += 1
216 endif
217
218 " Adapt the tests for "s:FindClassFiles()" from "compiler/spotbugs.vim".
Konfekt65311c62024-11-28 21:06:09 +0100219 if (s:request == 3 || s:request == 7) &&
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100220 \ (!empty(s:SpotBugsGetProperty('sourceDirPath', [])) &&
221 \ !empty(s:SpotBugsGetProperty('classDirPath', [])) &&
222 \ !empty(s:SpotBugsGetProperty('testSourceDirPath', [])) &&
223 \ !empty(s:SpotBugsGetProperty('testClassDirPath', [])))
224 function! s:DispatchAction(paths_action_pairs) abort
Konfekt65311c62024-11-28 21:06:09 +0100225 let name = expand('%:p')
226
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100227 for [paths, Action] in a:paths_action_pairs
228 for path in paths
229 if name =~# (path . '.\{-}\.java\=$')
230 call Action()
231 return
232 endif
233 endfor
Konfekt65311c62024-11-28 21:06:09 +0100234 endfor
235 endfunction
236
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100237 let s:dir_cnt = min([
238 \ len(s:SpotBugsGetProperties().sourceDirPath),
239 \ len(s:SpotBugsGetProperties().classDirPath)])
240 let s:test_dir_cnt = min([
241 \ len(s:SpotBugsGetProperties().testSourceDirPath),
242 \ len(s:SpotBugsGetProperties().testClassDirPath)])
243
244 " Do not break up path pairs with filtering!
245 let s:dispatcher = printf('call s:DispatchAction(%s)',
246 \ string([[s:SpotBugsGetProperties().sourceDirPath[0 : s:dir_cnt - 1],
247 \ s:SpotBugsGetProperties().PreCompilerAction],
248 \ [s:SpotBugsGetProperties().testSourceDirPath[0 : s:test_dir_cnt - 1],
249 \ s:SpotBugsGetProperties().PreCompilerTestAction]]))
250 unlet s:test_dir_cnt s:dir_cnt
251 endif
252
253 if exists("s:dispatcher")
254 function! s:ExecuteActions(pre_action, post_action) abort
255 try
256 execute a:pre_action
257 catch /\<E42:/
258 execute a:post_action
259 endtry
260 endfunction
Konfekt65311c62024-11-28 21:06:09 +0100261 endif
262
263 if s:request
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100264 if exists("b:spotbugs_syntax_once") || empty(join(getline(1, 8), ''))
265 let s:actions = [{'event': 'User'}]
Konfekt65311c62024-11-28 21:06:09 +0100266 else
267 " XXX: Handle multiple FileType events when vimrc contains more
268 " than one filetype setting for the language, e.g.:
269 " :filetype plugin indent on
270 " :autocmd BufRead,BufNewFile *.java setlocal filetype=java ...
271 " XXX: DO NOT ADD b:spotbugs_syntax_once TO b:undo_ftplugin !
272 let b:spotbugs_syntax_once = 1
273 let s:actions = [{
274 \ 'event': 'Syntax',
275 \ 'once': 1,
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100276 \ }, {
277 \ 'event': 'User',
278 \ }]
Konfekt65311c62024-11-28 21:06:09 +0100279 endif
280
281 for s:idx in range(len(s:actions))
282 if s:request == 7 || s:request == 6 || s:request == 5
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100283 let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)',
284 \ string(s:dispatcher),
285 \ string(printf('compiler spotbugs | call call(%s, [])',
286 \ string(s:SpotBugsGetProperties().PostCompilerAction))))
Konfekt65311c62024-11-28 21:06:09 +0100287 elseif s:request == 4
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100288 let s:actions[s:idx].cmd = printf(
289 \ 'compiler spotbugs | call call(%s, [])',
290 \ string(s:SpotBugsGetProperties().PostCompilerAction))
Konfekt65311c62024-11-28 21:06:09 +0100291 elseif s:request == 3 || s:request == 2 || s:request == 1
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100292 let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)',
293 \ string(s:dispatcher),
294 \ string('compiler spotbugs'))
Konfekt65311c62024-11-28 21:06:09 +0100295 else
296 let s:actions[s:idx].cmd = ''
297 endif
298 endfor
299
300 if !exists("#java_spotbugs")
301 augroup java_spotbugs
302 augroup END
303 endif
304
305 " The events are defined in s:actions.
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100306 silent! autocmd! java_spotbugs User <buffer>
Konfekt65311c62024-11-28 21:06:09 +0100307 silent! autocmd! java_spotbugs Syntax <buffer>
308
309 for s:action in s:actions
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100310 if has_key(s:action, 'once')
311 execute printf('autocmd java_spotbugs %s <buffer> ' .
Aliaksei Budavei2e252472024-12-27 16:46:36 +0100312 \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
Konfekt65311c62024-11-28 21:06:09 +0100313 \ s:action.event,
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100314 \ string(printf('autocmd! java_spotbugs %s <buffer>',
315 \ s:action.event)),
316 \ string(s:action.cmd))
317 else
318 execute printf('autocmd java_spotbugs %s <buffer> %s',
319 \ s:action.event,
320 \ s:action.cmd)
321 endif
Konfekt65311c62024-11-28 21:06:09 +0100322 endfor
323
Aliaksei Budavei2e252472024-12-27 16:46:36 +0100324 if s:SpotBugsHasProperty('PostCompilerActionExecutor') &&
325 \ (s:request == 7 || s:request == 6 ||
326 \ s:request == 5 || s:request == 4)
327 let s:augroup = s:SpotBugsGetProperty(
328 \ 'augroupForPostCompilerAction',
329 \ 'java_spotbugs_post')
330 let s:augroup = !empty(s:augroup) ? s:augroup : 'java_spotbugs_post'
331
332 for s:candidate in ['java_spotbugs_post', s:augroup]
333 if !exists("#" . s:candidate)
334 execute printf('augroup %s | augroup END', s:candidate)
335 endif
336 endfor
337
338 silent! autocmd! java_spotbugs_post User <buffer>
339
340 " Define a User ":autocmd" to define a once-only ShellCmdPost
341 " ":autocmd" that will invoke "PostCompilerActionExecutor" and let
342 " it decide whether to proceed with ":compiler spotbugs" etc.; and
343 " seek explicit synchronisation with ":doautocmd ShellCmdPost" by
344 " omitting "nested" for "java_spotbugs_post" and "java_spotbugs".
345 execute printf('autocmd java_spotbugs_post User <buffer> ' .
346 \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
347 \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
348 \ string(printf('autocmd %s ShellCmdPost <buffer> ' .
349 \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
350 \ s:augroup,
351 \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
352 \ string(printf('call call(%s, [%s])',
353 \ string(s:SpotBugsGetProperties().PostCompilerActionExecutor),
354 \ string(printf('compiler spotbugs | call call(%s, [])',
355 \ string(s:SpotBugsGetProperties().PostCompilerAction))))))))
356 endif
357
358 unlet! s:candidate s:augroup s:action s:actions s:idx s:dispatcher
Konfekt65311c62024-11-28 21:06:09 +0100359 endif
360
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100361 delfunction s:SpotBugsGetProperties
362 delfunction s:SpotBugsHasProperty
363 delfunction s:SpotBugsGetProperty
364 unlet! s:request s:spotbugs_properties_scope
Konfekt65311c62024-11-28 21:06:09 +0100365endif
366
367function! JavaFileTypeCleanUp() abort
Aliaksei Budavei73440242025-05-10 21:44:07 +0200368 setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr< include< define<
Konfekt65311c62024-11-28 21:06:09 +0100369 unlet! b:browsefilter
370
Aliaksei Budavei2e252472024-12-27 16:46:36 +0100371 " The concatenated ":autocmd" removals may be misparsed as an ":autocmd".
372 " A _once-only_ ShellCmdPost ":autocmd" is always a call-site definition.
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100373 silent! autocmd! java_spotbugs User <buffer>
Konfekt65311c62024-11-28 21:06:09 +0100374 silent! autocmd! java_spotbugs Syntax <buffer>
Aliaksei Budavei2e252472024-12-27 16:46:36 +0100375 silent! autocmd! java_spotbugs_post User <buffer>
Konfekt65311c62024-11-28 21:06:09 +0100376endfunction
377
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378" Undo the stuff we changed.
Konfekt65311c62024-11-28 21:06:09 +0100379let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380
Aliaksei Budavei36e667a2024-04-18 23:01:52 +0200381" See ":help vim9-mix".
382if !has("vim9script")
383 let &cpo = s:save_cpo
384 unlet s:save_cpo
385 finish
386endif
387
388if exists("s:zip_func_upgradable")
389 delfunction! JavaFileTypeZipFile
390
391 def! s:JavaFileTypeZipFile(): string
392 @/ = substitute(v:fname, '\.', '\\/', 'g') .. '.java'
393 return get(zip_files, bufnr('%'), zip_files[0])
394 enddef
395
396 setlocal includeexpr=s:JavaFileTypeZipFile()
397 setlocal suffixesadd<
398endif
399
Konfekt65311c62024-11-28 21:06:09 +0100400if exists("*s:DispatchAction")
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100401 def! s:DispatchAction(paths_action_pairs: list<list<any>>)
Konfekt65311c62024-11-28 21:06:09 +0100402 const name: string = expand('%:p')
403
Aliaksei Budavei368ef5a2024-12-16 21:37:54 +0100404 for [paths: list<string>, Action: func: any] in paths_action_pairs
405 for path in paths
406 if name =~# (path .. '.\{-}\.java\=$')
407 Action()
408 return
409 endif
410 endfor
Konfekt65311c62024-11-28 21:06:09 +0100411 endfor
412 enddef
413endif
414
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415" Restore the saved compatibility options.
416let &cpo = s:save_cpo
Bram Moolenaar84f72352012-03-11 15:57:40 +0100417unlet s:save_cpo
Aliaksei Budavei85f054a2024-09-30 19:40:04 +0200418" vim: fdm=syntax sw=4 ts=8 noet sta