1
0

registry.vim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. if exists('g:loaded_syntastic_registry') || !exists('g:loaded_syntastic_plugin')
  2. finish
  3. endif
  4. let g:loaded_syntastic_registry = 1
  5. " Initialisation {{{1
  6. let s:_DEFAULT_CHECKERS = {
  7. \ 'actionscript': ['mxmlc'],
  8. \ 'ada': ['gcc'],
  9. \ 'apiblueprint': ['snowcrash'],
  10. \ 'applescript': ['osacompile'],
  11. \ 'asciidoc': ['asciidoc'],
  12. \ 'asm': ['gcc'],
  13. \ 'bro': ['bro'],
  14. \ 'bemhtml': ['bemhtmllint'],
  15. \ 'c': ['gcc'],
  16. \ 'cabal': ['cabal'],
  17. \ 'chef': ['foodcritic'],
  18. \ 'co': ['coco'],
  19. \ 'cobol': ['cobc'],
  20. \ 'coffee': ['coffee', 'coffeelint'],
  21. \ 'coq': ['coqtop'],
  22. \ 'cpp': ['gcc'],
  23. \ 'cs': ['mcs'],
  24. \ 'css': ['csslint'],
  25. \ 'cucumber': ['cucumber'],
  26. \ 'cuda': ['nvcc'],
  27. \ 'd': ['dmd'],
  28. \ 'dart': ['dartanalyzer'],
  29. \ 'docbk': ['xmllint'],
  30. \ 'dustjs': ['swiffer'],
  31. \ 'elixir': [],
  32. \ 'erlang': ['escript'],
  33. \ 'eruby': ['ruby'],
  34. \ 'fortran': ['gfortran'],
  35. \ 'glsl': ['cgc'],
  36. \ 'go': ['go'],
  37. \ 'haml': ['haml'],
  38. \ 'handlebars': ['handlebars'],
  39. \ 'haskell': ['ghc_mod', 'hdevtools', 'hlint'],
  40. \ 'haxe': ['haxe'],
  41. \ 'hss': ['hss'],
  42. \ 'html': ['tidy'],
  43. \ 'java': ['javac'],
  44. \ 'javascript': ['jshint', 'jslint'],
  45. \ 'json': ['jsonlint', 'jsonval'],
  46. \ 'less': ['lessc'],
  47. \ 'lex': ['flex'],
  48. \ 'limbo': ['limbo'],
  49. \ 'lisp': ['clisp'],
  50. \ 'llvm': ['llvm'],
  51. \ 'lua': ['luac'],
  52. \ 'markdown': ['mdl'],
  53. \ 'matlab': ['mlint'],
  54. \ 'mercury': ['mmc'],
  55. \ 'nasm': ['nasm'],
  56. \ 'nroff': ['mandoc'],
  57. \ 'objc': ['gcc'],
  58. \ 'objcpp': ['gcc'],
  59. \ 'ocaml': ['camlp4o'],
  60. \ 'perl': ['perlcritic'],
  61. \ 'php': ['php', 'phpcs', 'phpmd'],
  62. \ 'po': ['msgfmt'],
  63. \ 'pod': ['podchecker'],
  64. \ 'puppet': ['puppet', 'puppetlint'],
  65. \ 'python': ['python', 'flake8', 'pylint'],
  66. \ 'r': [],
  67. \ 'racket': ['racket'],
  68. \ 'rnc': ['rnv'],
  69. \ 'rst': ['rst2pseudoxml'],
  70. \ 'ruby': ['mri'],
  71. \ 'sass': ['sass'],
  72. \ 'scala': ['fsc', 'scalac'],
  73. \ 'scss': ['sass', 'scss_lint'],
  74. \ 'sh': ['sh', 'shellcheck'],
  75. \ 'slim': ['slimrb'],
  76. \ 'sml': ['smlnj'],
  77. \ 'spec': ['rpmlint'],
  78. \ 'tcl': ['nagelfar'],
  79. \ 'tex': ['lacheck', 'chktex'],
  80. \ 'texinfo': ['makeinfo'],
  81. \ 'text': [],
  82. \ 'twig': ['twiglint'],
  83. \ 'typescript': ['tsc'],
  84. \ 'vala': ['valac'],
  85. \ 'verilog': ['verilator'],
  86. \ 'vhdl': ['ghdl'],
  87. \ 'vim': ['vimlint'],
  88. \ 'xhtml': ['tidy'],
  89. \ 'xml': ['xmllint'],
  90. \ 'xslt': ['xmllint'],
  91. \ 'yacc': ['bison'],
  92. \ 'yaml': ['jsyaml'],
  93. \ 'z80': ['z80syntaxchecker'],
  94. \ 'zpt': ['zptlint'],
  95. \ 'zsh': ['zsh'],
  96. \ }
  97. lockvar! s:_DEFAULT_CHECKERS
  98. let s:_DEFAULT_FILETYPE_MAP = {
  99. \ 'gentoo-metadata': 'xml',
  100. \ 'groff': 'nroff',
  101. \ 'lhaskell': 'haskell',
  102. \ 'litcoffee': 'coffee',
  103. \ 'mail': 'text',
  104. \ 'mkd': 'markdown',
  105. \ 'pe-puppet': 'puppet',
  106. \ 'sgml': 'docbk',
  107. \ 'sgmllnx': 'docbk',
  108. \ }
  109. lockvar! s:_DEFAULT_FILETYPE_MAP
  110. let s:_ECLIM_TYPES = [
  111. \ 'c',
  112. \ 'cpp',
  113. \ 'html',
  114. \ 'java',
  115. \ 'php',
  116. \ 'python',
  117. \ 'ruby',
  118. \ ]
  119. lockvar! s:_ECLIM_TYPES
  120. let s:_YCM_TYPES = [
  121. \ 'c',
  122. \ 'cpp',
  123. \ 'objc',
  124. \ 'objcpp',
  125. \ ]
  126. lockvar! s:_YCM_TYPES
  127. let g:SyntasticRegistry = {}
  128. " }}}1
  129. " Public methods {{{1
  130. " Note: Handling of filetype aliases: all public methods take aliases as
  131. " parameters, all private methods take normalized filetypes. Public methods
  132. " are thus supposed to normalize filetypes before calling private methods.
  133. function! g:SyntasticRegistry.Instance() abort " {{{2
  134. if !exists('s:SyntasticRegistryInstance')
  135. let s:SyntasticRegistryInstance = copy(self)
  136. let s:SyntasticRegistryInstance._checkerMap = {}
  137. endif
  138. return s:SyntasticRegistryInstance
  139. endfunction " }}}2
  140. function! g:SyntasticRegistry.CreateAndRegisterChecker(args) abort " {{{2
  141. let checker = g:SyntasticChecker.New(a:args)
  142. let registry = g:SyntasticRegistry.Instance()
  143. call registry._registerChecker(checker)
  144. endfunction " }}}2
  145. " Given a list of checker names hints_list, return a map name --> checker.
  146. " If hints_list is empty, user settings are are used instead. Checkers are
  147. " not checked for availability (that is, the corresponding IsAvailable() are
  148. " not run).
  149. function! g:SyntasticRegistry.getCheckers(ftalias, hints_list) abort " {{{2
  150. let ft = s:_normalise_filetype(a:ftalias)
  151. call self._loadCheckersFor(ft)
  152. let checkers_map = self._checkerMap[ft]
  153. if empty(checkers_map)
  154. return []
  155. endif
  156. call self._checkDeprecation(ft)
  157. let names =
  158. \ !empty(a:hints_list) ? syntastic#util#unique(a:hints_list) :
  159. \ exists('b:syntastic_checkers') ? b:syntastic_checkers :
  160. \ exists('g:syntastic_' . ft . '_checkers') ? g:syntastic_{ft}_checkers :
  161. \ get(s:_DEFAULT_CHECKERS, ft, 0)
  162. return type(names) == type([]) ?
  163. \ self._filterCheckersByName(checkers_map, names) : [checkers_map[keys(checkers_map)[0]]]
  164. endfunction " }}}2
  165. " Same as getCheckers(), but keep only the checkers available. This runs the
  166. " corresponding IsAvailable() functions for all checkers.
  167. function! g:SyntasticRegistry.getCheckersAvailable(ftalias, hints_list) abort " {{{2
  168. return filter(self.getCheckers(a:ftalias, a:hints_list), 'v:val.isAvailable()')
  169. endfunction " }}}2
  170. function! g:SyntasticRegistry.getKnownFiletypes() abort " {{{2
  171. let types = keys(s:_DEFAULT_CHECKERS)
  172. call extend(types, keys(s:_DEFAULT_FILETYPE_MAP))
  173. if exists('g:syntastic_filetype_map')
  174. call extend(types, keys(g:syntastic_filetype_map))
  175. endif
  176. if exists('g:syntastic_extra_filetypes') && type(g:syntastic_extra_filetypes) == type([])
  177. call extend(types, g:syntastic_extra_filetypes)
  178. endif
  179. return syntastic#util#unique(types)
  180. endfunction " }}}2
  181. function! g:SyntasticRegistry.getNamesOfAvailableCheckers(ftalias) abort " {{{2
  182. let ft = s:_normalise_filetype(a:ftalias)
  183. call self._loadCheckersFor(ft)
  184. return keys(filter( copy(self._checkerMap[ft]), 'v:val.isAvailable()' ))
  185. endfunction " }}}2
  186. function! g:SyntasticRegistry.echoInfoFor(ftalias_list) abort " {{{2
  187. let ft_list = syntastic#util#unique(map( copy(a:ftalias_list), 's:_normalise_filetype(v:val)' ))
  188. if len(ft_list) != 1
  189. let available = []
  190. let active = []
  191. for ft in ft_list
  192. call extend(available, map( self.getNamesOfAvailableCheckers(ft), 'ft . "/" . v:val' ))
  193. call extend(active, map( self.getCheckersAvailable(ft, []), 'ft . "/" . v:val.getName()' ))
  194. endfor
  195. else
  196. let ft = ft_list[0]
  197. let available = self.getNamesOfAvailableCheckers(ft)
  198. let active = map(self.getCheckersAvailable(ft, []), 'v:val.getName()')
  199. endif
  200. let cnt = len(available)
  201. let plural = cnt != 1 ? 's' : ''
  202. let cklist = cnt ? join(sort(available)) : '-'
  203. echomsg 'Available checker' . plural . ': ' . cklist
  204. let cnt = len(active)
  205. let plural = cnt != 1 ? 's' : ''
  206. let cklist = cnt ? join(active) : '-'
  207. echomsg 'Currently enabled checker' . plural . ': ' . cklist
  208. " Eclim feels entitled to mess with syntastic's variables {{{3
  209. if exists(':EclimValidate') && get(g:, 'EclimFileTypeValidate', 1)
  210. let disabled = filter(copy(ft_list), 's:_disabled_by_eclim(v:val)')
  211. let cnt = len(disabled)
  212. if cnt
  213. let plural = cnt != 1 ? 's' : ''
  214. let cklist = join(disabled, ', ')
  215. echomsg 'Checkers for filetype' . plural . ' ' . cklist . ' possibly disabled by Eclim'
  216. endif
  217. endif
  218. " }}}3
  219. " So does YouCompleteMe {{{3
  220. if exists('g:loaded_youcompleteme') && get(g:, 'ycm_show_diagnostics_ui', get(g:, 'ycm_register_as_syntastic_checker', 1))
  221. let disabled = filter(copy(ft_list), 's:_disabled_by_ycm(v:val)')
  222. let cnt = len(disabled)
  223. if cnt
  224. let plural = cnt != 1 ? 's' : ''
  225. let cklist = join(disabled, ', ')
  226. echomsg 'Checkers for filetype' . plural . ' ' . cklist . ' possibly disabled by YouCompleteMe'
  227. endif
  228. endif
  229. " }}}3
  230. endfunction " }}}2
  231. " }}}1
  232. " Private methods {{{1
  233. function! g:SyntasticRegistry._registerChecker(checker) abort " {{{2
  234. let ft = a:checker.getFiletype()
  235. if !has_key(self._checkerMap, ft)
  236. let self._checkerMap[ft] = {}
  237. endif
  238. let name = a:checker.getName()
  239. if has_key(self._checkerMap[ft], name)
  240. throw 'Syntastic: Duplicate syntax checker name: ' . ft . '/' . name
  241. endif
  242. let self._checkerMap[ft][name] = a:checker
  243. endfunction " }}}2
  244. function! g:SyntasticRegistry._filterCheckersByName(checkers_map, list) abort " {{{2
  245. return filter( map(copy(a:list), 'get(a:checkers_map, v:val, {})'), '!empty(v:val)' )
  246. endfunction " }}}2
  247. function! g:SyntasticRegistry._loadCheckersFor(filetype) abort " {{{2
  248. if has_key(self._checkerMap, a:filetype)
  249. return
  250. endif
  251. execute 'runtime! syntax_checkers/' . a:filetype . '/*.vim'
  252. if !has_key(self._checkerMap, a:filetype)
  253. let self._checkerMap[a:filetype] = {}
  254. endif
  255. endfunction " }}}2
  256. " Check for obsolete variable g:syntastic_<filetype>_checker
  257. function! g:SyntasticRegistry._checkDeprecation(filetype) abort " {{{2
  258. if exists('g:syntastic_' . a:filetype . '_checker') && !exists('g:syntastic_' . a:filetype . '_checkers')
  259. let g:syntastic_{a:filetype}_checkers = [g:syntastic_{a:filetype}_checker]
  260. call syntastic#log#oneTimeWarn('variable g:syntastic_' . a:filetype . '_checker is deprecated')
  261. endif
  262. endfunction " }}}2
  263. " }}}1
  264. " Utilities {{{1
  265. "resolve filetype aliases, and replace - with _ otherwise we cant name
  266. "syntax checker functions legally for filetypes like "gentoo-metadata"
  267. function! s:_normalise_filetype(ftalias) abort " {{{2
  268. let ft = get(s:_DEFAULT_FILETYPE_MAP, a:ftalias, a:ftalias)
  269. let ft = get(g:syntastic_filetype_map, ft, ft)
  270. let ft = substitute(ft, '\m-', '_', 'g')
  271. return ft
  272. endfunction " }}}2
  273. function! s:_disabled_by_eclim(filetype) abort " {{{2
  274. if index(s:_ECLIM_TYPES, a:filetype) >= 0
  275. let lang = toupper(a:filetype[0]) . a:filetype[1:]
  276. let ft = a:filetype !=# 'cpp' ? lang : 'C'
  277. return get(g:, 'Eclim' . lang . 'Validate', 1) && !get(g:, 'Eclim' . ft . 'SyntasticEnabled', 0)
  278. endif
  279. return 0
  280. endfunction " }}}2
  281. function! s:_disabled_by_ycm(filetype) abort " {{{2
  282. return index(s:_YCM_TYPES, a:filetype) >= 0
  283. endfunction " }}}2
  284. " }}}1
  285. " vim: set sw=4 sts=4 et fdm=marker: