checker.vim 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. if exists('g:loaded_syntastic_checker') || !exists('g:loaded_syntastic_plugin')
  2. finish
  3. endif
  4. let g:loaded_syntastic_checker = 1
  5. let g:SyntasticChecker = {}
  6. " Public methods {{{1
  7. function! g:SyntasticChecker.New(args) abort " {{{2
  8. let newObj = copy(self)
  9. let newObj._filetype = a:args['filetype']
  10. let newObj._name = a:args['name']
  11. let newObj._exec = get(a:args, 'exec', newObj._name)
  12. if has_key(a:args, 'redirect')
  13. let [filetype, name] = split(a:args['redirect'], '/')
  14. let prefix = 'SyntaxCheckers_' . filetype . '_' . name . '_'
  15. if exists('g:syntastic_' . filetype . '_' . name . '_sort') && !exists('g:syntastic_' . newObj._filetype . '_' . newObj._name . '_sort')
  16. let g:syntastic_{newObj._filetype}_{newObj._name}_sort = g:syntastic_{filetype}_{name}_sort
  17. endif
  18. else
  19. let prefix = 'SyntaxCheckers_' . newObj._filetype . '_' . newObj._name . '_'
  20. endif
  21. let newObj._locListFunc = function(prefix . 'GetLocList')
  22. if exists('*' . prefix . 'IsAvailable')
  23. let newObj._isAvailableFunc = function(prefix . 'IsAvailable')
  24. else
  25. let newObj._isAvailableFunc = function('s:_isAvailableDefault')
  26. endif
  27. if exists('*' . prefix . 'GetHighlightRegex')
  28. let newObj._highlightRegexFunc = function(prefix . 'GetHighlightRegex')
  29. endif
  30. return newObj
  31. endfunction " }}}2
  32. function! g:SyntasticChecker.getFiletype() abort " {{{2
  33. return self._filetype
  34. endfunction " }}}2
  35. function! g:SyntasticChecker.getName() abort " {{{2
  36. return self._name
  37. endfunction " }}}2
  38. " Synchronise _exec with user's setting. Force re-validation if needed.
  39. "
  40. " XXX: This function must be called at least once before calling either
  41. " getExec() or getExecEscaped(). Normally isAvailable() does that for you
  42. " automatically, but you should keep still this in mind if you change the
  43. " current checker workflow.
  44. function! g:SyntasticChecker.syncExec() dict " {{{2
  45. let user_exec =
  46. \ expand( exists('b:syntastic_' . self._name . '_exec') ? b:syntastic_{self._name}_exec :
  47. \ syntastic#util#var(self._filetype . '_' . self._name . '_exec'), 1 )
  48. if user_exec !=# '' && user_exec !=# self._exec
  49. let self._exec = user_exec
  50. if has_key(self, '_available')
  51. " we have a new _exec on the block, it has to be validated
  52. call remove(self, '_available')
  53. endif
  54. endif
  55. endfunction " }}}2
  56. function! g:SyntasticChecker.getExec() abort " {{{2
  57. return self._exec
  58. endfunction " }}}2
  59. function! g:SyntasticChecker.getExecEscaped() abort " {{{2
  60. return syntastic#util#shescape(self._exec)
  61. endfunction " }}}2
  62. function! g:SyntasticChecker.getLocListRaw() abort " {{{2
  63. let name = self._filetype . '/' . self._name
  64. try
  65. let list = self._locListFunc()
  66. if self._exec !=# ''
  67. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getLocList: checker ' . name . ' returned ' . v:shell_error)
  68. endif
  69. catch /\m\C^Syntastic: checker error$/
  70. let list = []
  71. if self._exec !=# ''
  72. call syntastic#log#error('checker ' . name . ' returned abnormal status ' . v:shell_error)
  73. else
  74. call syntastic#log#error('checker ' . name . ' aborted')
  75. endif
  76. endtry
  77. call self._populateHighlightRegexes(list)
  78. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, name . ' raw:', list)
  79. call self._quietMessages(list)
  80. return list
  81. endfunction " }}}2
  82. function! g:SyntasticChecker.getLocList() abort " {{{2
  83. return g:SyntasticLoclist.New(self.getLocListRaw())
  84. endfunction " }}}2
  85. function! g:SyntasticChecker.getVersion(...) abort " {{{2
  86. if !exists('self._version')
  87. let command = a:0 ? a:1 : self.getExecEscaped() . ' --version'
  88. let version_output = syntastic#util#system(command)
  89. call self.log('getVersion: ' . string(command) . ': ' .
  90. \ string(split(version_output, "\n", 1)) .
  91. \ (v:shell_error ? ' (exit code ' . v:shell_error . ')' : '') )
  92. call self.setVersion(syntastic#util#parseVersion(version_output))
  93. endif
  94. return get(self, '_version', [])
  95. endfunction " }}}2
  96. function! g:SyntasticChecker.setVersion(version) abort " {{{2
  97. if len(a:version)
  98. let self._version = copy(a:version)
  99. call self.log(self.getExec() . ' version =', a:version)
  100. else
  101. call syntastic#log#error("checker " . self._filetype . "/" . self._name . ": can't parse version string (abnormal termination?)")
  102. endif
  103. endfunction " }}}2
  104. function! g:SyntasticChecker.log(msg, ...) abort " {{{2
  105. let leader = self._filetype . '/' . self._name . ': '
  106. if a:0 > 0
  107. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, leader . a:msg, a:1)
  108. else
  109. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, leader . a:msg)
  110. endif
  111. endfunction " }}}2
  112. function! g:SyntasticChecker.makeprgBuild(opts) abort " {{{2
  113. let basename = self._filetype . '_' . self._name . '_'
  114. let parts = []
  115. call extend(parts, self._getOpt(a:opts, basename, 'exe', self.getExecEscaped()))
  116. call extend(parts, self._getOpt(a:opts, basename, 'args', ''))
  117. call extend(parts, self._getOpt(a:opts, basename, 'fname', syntastic#util#shexpand('%')))
  118. call extend(parts, self._getOpt(a:opts, basename, 'post_args', ''))
  119. call extend(parts, self._getOpt(a:opts, basename, 'tail', ''))
  120. return join(parts)
  121. endfunction " }}}2
  122. function! g:SyntasticChecker.isAvailable() abort " {{{2
  123. call self.syncExec()
  124. if !has_key(self, '_available')
  125. let self._available = self._isAvailableFunc()
  126. endif
  127. return self._available
  128. endfunction " }}}2
  129. function! g:SyntasticChecker.wantSort() abort " {{{2
  130. return syntastic#util#var(self._filetype . '_' . self._name . '_sort', 0)
  131. endfunction " }}}2
  132. " This method is no longer used by syntastic. It's here only to maintain
  133. " backwards compatibility with external checkers which might depend on it.
  134. function! g:SyntasticChecker.setWantSort(val) abort " {{{2
  135. if !exists('g:syntastic_' . self._filetype . '_' . self._name . '_sort')
  136. let g:syntastic_{self._filetype}_{self._name}_sort = a:val
  137. endif
  138. endfunction " }}}2
  139. " }}}1
  140. " Private methods {{{1
  141. function! g:SyntasticChecker._quietMessages(errors) abort " {{{2
  142. " wildcard quiet_messages
  143. let quiet_filters = copy(syntastic#util#var('quiet_messages', {}))
  144. if type(quiet_filters) != type({})
  145. call syntastic#log#warn('ignoring invalid syntastic_quiet_messages')
  146. unlet quiet_filters
  147. let quiet_filters = {}
  148. endif
  149. " per checker quiet_messages
  150. let name = self._filetype . '_' . self._name
  151. try
  152. call extend( quiet_filters, copy(syntastic#util#var(name . '_quiet_messages', {})), 'force' )
  153. catch /\m^Vim\%((\a\+)\)\=:E712/
  154. call syntastic#log#warn('ignoring invalid syntastic_' . name . '_quiet_messages')
  155. endtry
  156. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'quiet_messages filter:', quiet_filters)
  157. if !empty(quiet_filters)
  158. call syntastic#util#dictFilter(a:errors, quiet_filters)
  159. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'filtered by quiet_messages:', a:errors)
  160. endif
  161. endfunction " }}}2
  162. function! g:SyntasticChecker._populateHighlightRegexes(errors) abort " {{{2
  163. if has_key(self, '_highlightRegexFunc')
  164. for e in a:errors
  165. if e['valid']
  166. let term = self._highlightRegexFunc(e)
  167. if term !=# ''
  168. let e['hl'] = term
  169. endif
  170. endif
  171. endfor
  172. endif
  173. endfunction " }}}2
  174. function! g:SyntasticChecker._getOpt(opts, basename, name, default) abort " {{{2
  175. let ret = []
  176. call extend( ret, syntastic#util#argsescape(get(a:opts, a:name . '_before', '')) )
  177. call extend( ret, syntastic#util#argsescape(syntastic#util#var( a:basename . a:name, get(a:opts, a:name, a:default) )) )
  178. call extend( ret, syntastic#util#argsescape(get(a:opts, a:name . '_after', '')) )
  179. return ret
  180. endfunction " }}}2
  181. " }}}1
  182. " Private functions {{{1
  183. function! s:_isAvailableDefault() dict " {{{2
  184. return executable(self.getExec())
  185. endfunction " }}}2
  186. " }}}1
  187. " vim: set sw=4 sts=4 et fdm=marker: