javac.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. "============================================================================
  2. "File: javac.vim
  3. "Description: Syntax checking plugin for syntastic
  4. "Maintainer: Jochen Keil <jochen.keil at gmail dot com>
  5. " Dmitry Geurkov <d.geurkov at gmail dot com>
  6. "License: This program is free software. It comes without any warranty,
  7. " to the extent permitted by applicable law. You can redistribute
  8. " it and/or modify it under the terms of the Do What The Fuck You
  9. " Want To Public License, Version 2, as published by Sam Hocevar.
  10. " See http://sam.zoy.org/wtfpl/COPYING for more details.
  11. "============================================================================
  12. if exists('g:loaded_syntastic_java_javac_checker')
  13. finish
  14. endif
  15. let g:loaded_syntastic_java_javac_checker = 1
  16. let g:syntastic_java_javac_maven_pom_tags = ['build', 'properties']
  17. let g:syntastic_java_javac_maven_pom_properties = {}
  18. let s:has_maven = 0
  19. let s:save_cpo = &cpo
  20. set cpo&vim
  21. " Checker options {{{1
  22. if !exists('g:syntastic_java_javac_executable')
  23. let g:syntastic_java_javac_executable = 'javac'
  24. endif
  25. let g:syntastic_java_javac_exec = g:syntastic_java_javac_executable
  26. if !exists('g:syntastic_java_maven_executable')
  27. let g:syntastic_java_maven_executable = 'mvn'
  28. endif
  29. if !exists('g:syntastic_java_javac_options')
  30. let g:syntastic_java_javac_options = '-Xlint'
  31. endif
  32. if !exists('g:syntastic_java_maven_options')
  33. let g:syntastic_java_maven_options = ''
  34. endif
  35. if !exists('g:syntastic_java_javac_classpath')
  36. let g:syntastic_java_javac_classpath = ''
  37. endif
  38. if !exists('g:syntastic_java_javac_delete_output')
  39. let g:syntastic_java_javac_delete_output = 1
  40. endif
  41. if !exists('g:syntastic_java_javac_autoload_maven_classpath')
  42. let g:syntastic_java_javac_autoload_maven_classpath = 1
  43. endif
  44. if !exists('g:syntastic_java_javac_config_file_enabled')
  45. let g:syntastic_java_javac_config_file_enabled = 0
  46. endif
  47. if !exists('g:syntastic_java_javac_config_file')
  48. let g:syntastic_java_javac_config_file = '.syntastic_javac_config'
  49. endif
  50. if !exists('g:syntastic_java_javac_custom_classpath_command')
  51. let g:syntastic_java_javac_custom_classpath_command = ''
  52. endif
  53. if !exists('g:syntastic_java_javac_maven_pom_ftime')
  54. let g:syntastic_java_javac_maven_pom_ftime = {}
  55. endif
  56. if !exists('g:syntastic_java_javac_maven_pom_classpath')
  57. let g:syntastic_java_javac_maven_pom_classpath = {}
  58. endif
  59. " }}}1
  60. " Constants {{{1
  61. let s:_FILE_SHORTCUTS = {
  62. \ '%FILE_PATH%': '%:p',
  63. \ '%FILE_NAME%': '%:t',
  64. \ '%FILE_DIR%': '%:p:h',
  65. \ }
  66. lockvar! s:_FILE_SHORTCUTS
  67. " }}}1
  68. " Commands {{{1
  69. command! SyntasticJavacEditClasspath call s:EditClasspath()
  70. command! SyntasticJavacEditConfig call s:EditConfig()
  71. " }}}1
  72. function! SyntaxCheckers_java_javac_IsAvailable() dict " {{{1
  73. let s:has_maven = executable(expand(g:syntastic_java_maven_executable, 1))
  74. return executable(expand(g:syntastic_java_javac_executable, 1))
  75. endfunction " }}}1
  76. function! SyntaxCheckers_java_javac_GetLocList() dict " {{{1
  77. let javac_opts = g:syntastic_java_javac_options
  78. let output_dir = ''
  79. if g:syntastic_java_javac_delete_output
  80. let output_dir = syntastic#util#tmpdir()
  81. let javac_opts .= ' -d ' . syntastic#util#shescape(output_dir)
  82. endif
  83. " load classpath from config file
  84. if g:syntastic_java_javac_config_file_enabled
  85. call s:LoadConfigFile()
  86. endif
  87. " add classpathes to javac_classpath {{{2
  88. let javac_classpath = ''
  89. for path in split(g:syntastic_java_javac_classpath, s:ClassSep())
  90. if path !=# ''
  91. try
  92. let ps = glob(path, 1, 1)
  93. catch
  94. let ps = split(glob(path, 1), "\n")
  95. endtry
  96. if type(ps) == type([])
  97. for p in ps
  98. let javac_classpath = s:AddToClasspath(javac_classpath, p)
  99. endfor
  100. else
  101. let javac_classpath = s:AddToClasspath(javac_classpath, ps)
  102. endif
  103. endif
  104. endfor
  105. if s:has_maven && g:syntastic_java_javac_autoload_maven_classpath
  106. if !g:syntastic_java_javac_delete_output
  107. let javac_opts .= ' -d ' . syntastic#util#shescape(s:MavenOutputDirectory())
  108. endif
  109. let javac_classpath = s:AddToClasspath(javac_classpath, s:GetMavenClasspath())
  110. endif
  111. " }}}2
  112. " load custom classpath {{{2
  113. if g:syntastic_java_javac_custom_classpath_command !=# ''
  114. " Pre-process the classpath command string a little.
  115. let classpath_command = g:syntastic_java_javac_custom_classpath_command
  116. for [key, val] in items(s:_FILE_SHORTCUTS)
  117. let classpath_command = substitute(classpath_command, '\V' . key, syntastic#util#shexpand(val), 'g')
  118. endfor
  119. let lines = syntastic#util#system(classpath_command)
  120. if syntastic#util#isRunningWindows() || has('win32unix')
  121. let lines = substitute(lines, "\r\n", "\n", 'g')
  122. endif
  123. for l in split(lines, "\n")
  124. let javac_classpath = s:AddToClasspath(javac_classpath, l)
  125. endfor
  126. endif
  127. if javac_classpath !=# ''
  128. let javac_opts .= ' -cp ' . syntastic#util#shexpand(javac_classpath)
  129. endif
  130. " }}}2
  131. let fname = expand('%:p:h', 1) . syntastic#util#Slash() . expand ('%:t', 1)
  132. if has('win32unix')
  133. let fname = syntastic#util#CygwinPath(fname)
  134. endif
  135. let makeprg = self.makeprgBuild({
  136. \ 'args': javac_opts,
  137. \ 'fname': syntastic#util#shescape(fname) })
  138. " unashamedly stolen from *errorformat-javac* (quickfix.txt) and modified to include error types
  139. let errorformat =
  140. \ '%E%f:%l: error: %m,'.
  141. \ '%W%f:%l: warning: %m,'.
  142. \ '%E%f:%l: %m,'.
  143. \ '%Z%p^,'.
  144. \ '%-G%.%#'
  145. if output_dir !=# ''
  146. silent! call mkdir(output_dir, 'p')
  147. endif
  148. let errors = SyntasticMake({
  149. \ 'makeprg': makeprg,
  150. \ 'errorformat': errorformat,
  151. \ 'postprocess': ['cygwinRemoveCR'] })
  152. if output_dir !=# ''
  153. call syntastic#util#rmrf(output_dir)
  154. endif
  155. return errors
  156. endfunction " }}}1
  157. " Utilities {{{1
  158. function! s:RemoveCarriageReturn(line) " {{{2
  159. return substitute(a:line, "\r", '', 'g')
  160. endfunction " }}}2
  161. function! s:ClassSep() " {{{2
  162. return (syntastic#util#isRunningWindows() || has('win32unix')) ? ';' : ':'
  163. endfunction " }}}2
  164. function! s:AddToClasspath(classpath, path) " {{{2
  165. if a:path ==# ''
  166. return a:classpath
  167. endif
  168. return (a:classpath !=# '') ? a:classpath . s:ClassSep() . a:path : a:path
  169. endfunction " }}}2
  170. function! s:SplitClasspath(classpath) " {{{2
  171. return split(a:classpath, s:ClassSep())
  172. endfunction " }}}2
  173. function! s:LoadConfigFile() " {{{2
  174. if filereadable(expand(g:syntastic_java_javac_config_file, 1))
  175. execute 'source ' . fnameescape(expand(g:syntastic_java_javac_config_file, 1))
  176. endif
  177. endfunction " }}}2
  178. function! s:SaveClasspath() " {{{2
  179. " build classpath from lines
  180. let path = ''
  181. let lines = getline(1, '$')
  182. for l in lines
  183. let path = s:AddToClasspath(path, l)
  184. endfor
  185. " save classpath to config file
  186. if g:syntastic_java_javac_config_file_enabled
  187. if filereadable(expand(g:syntastic_java_javac_config_file, 1))
  188. " load lines from config file
  189. let lines = readfile(expand(g:syntastic_java_javac_config_file, 1))
  190. " strip g:syntastic_java_javac_classpath options from config file lines
  191. let i = 0
  192. while i < len(lines)
  193. if match(lines[i], 'g:syntastic_java_javac_classpath') != -1
  194. call remove(lines, i)
  195. else
  196. let i += 1
  197. endif
  198. endwhile
  199. else
  200. let lines = []
  201. endif
  202. " add new g:syntastic_java_javac_classpath option to config
  203. call add(lines, 'let g:syntastic_java_javac_classpath = ' . string(path))
  204. " save config file lines
  205. call writefile(lines, expand(g:syntastic_java_javac_config_file, 1))
  206. endif
  207. " set new classpath
  208. let g:syntastic_java_javac_classpath = path
  209. let &modified = 0
  210. endfunction " }}}2
  211. function! s:EditClasspath() " {{{2
  212. let command = 'syntastic javac classpath'
  213. let winnr = bufwinnr('^' . command . '$')
  214. if winnr < 0
  215. let path = []
  216. let pathlines = split(g:syntastic_java_javac_classpath, "\n")
  217. for p in pathlines
  218. call extend(path, s:SplitClasspath(p))
  219. endfor
  220. execute (len(path) + 5) . 'sp ' . fnameescape(command)
  221. augroup syntastic
  222. autocmd BufWriteCmd <buffer> call s:SaveClasspath() | bwipeout
  223. augroup END
  224. setlocal buftype=acwrite bufhidden=wipe nobuflisted noswapfile nowrap number
  225. for p in path
  226. call append(line('$') - 1, p)
  227. endfor
  228. let &modified = 0
  229. else
  230. execute winnr . 'wincmd w'
  231. endif
  232. endfunction " }}}2
  233. function! s:SaveConfig() " {{{2
  234. " get lines
  235. let lines = getline(1, '$')
  236. if g:syntastic_java_javac_config_file_enabled
  237. " save config file lines
  238. call writefile(lines, expand(g:syntastic_java_javac_config_file, 1))
  239. endif
  240. let &modified = 0
  241. endfunction " }}}2
  242. function! s:EditConfig() " {{{2
  243. if !g:syntastic_java_javac_config_file_enabled
  244. return
  245. endif
  246. let command = 'syntastic javac config'
  247. let winnr = bufwinnr('^' . command . '$')
  248. if winnr < 0
  249. let lines = []
  250. if filereadable(expand(g:syntastic_java_javac_config_file, 1))
  251. let lines = readfile(expand(g:syntastic_java_javac_config_file, 1))
  252. endif
  253. execute (len(lines) + 5) . 'sp ' . fnameescape(command)
  254. augroup syntastic
  255. autocmd BufWriteCmd <buffer> call s:SaveConfig() | bwipeout
  256. augroup END
  257. setlocal ft=vim buftype=acwrite bufhidden=wipe nobuflisted noswapfile nowrap number
  258. for l in lines
  259. call append(line('$') - 1, l)
  260. endfor
  261. let &modified = 0
  262. else
  263. execute winnr . 'wincmd w'
  264. endif
  265. endfunction " }}}2
  266. function! s:GetMavenProperties() " {{{2
  267. let mvn_properties = {}
  268. let pom = syntastic#util#findFileInParent('pom.xml', expand('%:p:h', 1))
  269. if s:has_maven && filereadable(pom)
  270. if !has_key(g:syntastic_java_javac_maven_pom_properties, pom)
  271. let mvn_cmd = syntastic#util#shexpand(g:syntastic_java_maven_executable) .
  272. \ ' -f ' . syntastic#util#shescape(pom) .
  273. \ ' ' . g:syntastic_java_maven_options
  274. let mvn_is_managed_tag = 1
  275. let mvn_settings_output = split(syntastic#util#system(mvn_cmd . ' help:effective-pom'), "\n")
  276. let current_path = 'project'
  277. for line in mvn_settings_output
  278. let matches = matchlist(line, '\m^\s*<\([a-zA-Z0-9\-\.]\+\)>\s*$')
  279. if mvn_is_managed_tag && !empty(matches)
  280. let mvn_is_managed_tag = index(g:syntastic_java_javac_maven_pom_tags, matches[1]) >= 0
  281. let current_path .= '.' . matches[1]
  282. else
  283. let matches = matchlist(line, '\m^\s*</\([a-zA-Z0-9\-\.]\+\)>\s*$')
  284. if !empty(matches)
  285. let mvn_is_managed_tag = index(g:syntastic_java_javac_maven_pom_tags, matches[1]) < 0
  286. let current_path = substitute(current_path, '\m\.' . matches[1] . '$', '', '')
  287. else
  288. let matches = matchlist(line, '\m^\s*<\([a-zA-Z0-9\-\.]\+\)>\(.\+\)</[a-zA-Z0-9\-\.]\+>\s*$')
  289. if mvn_is_managed_tag && !empty(matches)
  290. let mvn_properties[current_path . '.' . matches[1]] = matches[2]
  291. endif
  292. endif
  293. endif
  294. endfor
  295. let g:syntastic_java_javac_maven_pom_properties[pom] = mvn_properties
  296. endif
  297. return g:syntastic_java_javac_maven_pom_properties[pom]
  298. endif
  299. return mvn_properties
  300. endfunction " }}}2
  301. function! s:GetMavenClasspath() " {{{2
  302. let pom = syntastic#util#findFileInParent('pom.xml', expand('%:p:h', 1))
  303. if s:has_maven && filereadable(pom)
  304. if !has_key(g:syntastic_java_javac_maven_pom_ftime, pom) || g:syntastic_java_javac_maven_pom_ftime[pom] != getftime(pom)
  305. let mvn_cmd = syntastic#util#shexpand(g:syntastic_java_maven_executable) .
  306. \ ' -f ' . syntastic#util#shescape(pom) .
  307. \ ' ' . g:syntastic_java_maven_options
  308. let mvn_classpath_output = split(syntastic#util#system(mvn_cmd . ' dependency:build-classpath -DincludeScope=test'), "\n")
  309. let mvn_classpath = ''
  310. let class_path_next = 0
  311. for line in mvn_classpath_output
  312. if class_path_next == 1
  313. let mvn_classpath = s:RemoveCarriageReturn(line)
  314. break
  315. endif
  316. if stridx(line, 'Dependencies classpath:') >= 0
  317. let class_path_next = 1
  318. endif
  319. endfor
  320. let mvn_properties = s:GetMavenProperties()
  321. let sep = syntastic#util#Slash()
  322. let output_dir = get(mvn_properties, 'project.build.outputDirectory', join(['target', 'classes'], sep))
  323. let mvn_classpath = s:AddToClasspath(mvn_classpath, output_dir)
  324. let test_output_dir = get(mvn_properties, 'project.build.testOutputDirectory', join(['target', 'test-classes'], sep))
  325. let mvn_classpath = s:AddToClasspath(mvn_classpath, test_output_dir)
  326. let g:syntastic_java_javac_maven_pom_ftime[pom] = getftime(pom)
  327. let g:syntastic_java_javac_maven_pom_classpath[pom] = mvn_classpath
  328. endif
  329. return g:syntastic_java_javac_maven_pom_classpath[pom]
  330. endif
  331. return ''
  332. endfunction " }}}2
  333. function! s:MavenOutputDirectory() " {{{2
  334. let pom = syntastic#util#findFileInParent('pom.xml', expand('%:p:h', 1))
  335. if s:has_maven && filereadable(pom)
  336. let mvn_properties = s:GetMavenProperties()
  337. let output_dir = get(mvn_properties, 'project.properties.build.dir', getcwd())
  338. let sep = syntastic#util#Slash()
  339. let src_main_dir = get(mvn_properties, 'project.build.sourceDirectory', join(['src', 'main', 'java'], sep))
  340. let src_test_dir = get(mvn_properties, 'project.build.testsourceDirectory', join(['src', 'test', 'java'], sep))
  341. if stridx(expand('%:p:h', 1), src_main_dir) >= 0
  342. let output_dir = get(mvn_properties, 'project.build.outputDirectory', join ([output_dir, 'target', 'classes'], sep))
  343. endif
  344. if stridx(expand('%:p:h', 1), src_test_dir) >= 0
  345. let output_dir = get(mvn_properties, 'project.build.testOutputDirectory', join([output_dir, 'target', 'test-classes'], sep))
  346. endif
  347. if has('win32unix')
  348. let output_dir = syntastic#util#CygwinPath(output_dir)
  349. endif
  350. return output_dir
  351. endif
  352. return '.'
  353. endfunction " }}}2
  354. " }}}1
  355. call g:SyntasticRegistry.CreateAndRegisterChecker({
  356. \ 'filetype': 'java',
  357. \ 'name': 'javac'})
  358. let &cpo = s:save_cpo
  359. unlet s:save_cpo
  360. " vim: set sw=4 sts=4 et fdm=marker: