1
0

c.vim 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. if exists('g:loaded_syntastic_c_autoload') || !exists('g:loaded_syntastic_plugin')
  2. finish
  3. endif
  4. let g:loaded_syntastic_c_autoload = 1
  5. let s:save_cpo = &cpo
  6. set cpo&vim
  7. " Public functions {{{1
  8. " convenience function to determine the 'null device' parameter
  9. " based on the current operating system
  10. function! syntastic#c#NullOutput() abort " {{{2
  11. let known_os = has('unix') || has('mac') || syntastic#util#isRunningWindows()
  12. return known_os ? '-o ' . syntastic#util#DevNull() : ''
  13. endfunction " }}}2
  14. " read additional compiler flags from the given configuration file
  15. " the file format and its parsing mechanism is inspired by clang_complete
  16. function! syntastic#c#ReadConfig(file) abort " {{{2
  17. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: looking for', a:file)
  18. " search upwards from the current file's directory
  19. let config = findfile(a:file, '.;')
  20. if config ==# ''
  21. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file not found')
  22. return ''
  23. endif
  24. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: config file:', config)
  25. if !filereadable(config)
  26. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file unreadable')
  27. return ''
  28. endif
  29. " convert filename into absolute path
  30. let filepath = fnamemodify(config, ':p:h')
  31. " try to read config file
  32. try
  33. let lines = readfile(config)
  34. catch /\m^Vim\%((\a\+)\)\=:E48[45]/
  35. call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: error reading file')
  36. return ''
  37. endtry
  38. " filter out empty lines and comments
  39. call filter(lines, 'v:val !~# ''\v^(\s*#|$)''')
  40. " remove leading and trailing spaces
  41. call map(lines, 'substitute(v:val, ''\m^\s\+'', "", "")')
  42. call map(lines, 'substitute(v:val, ''\m\s\+$'', "", "")')
  43. let parameters = []
  44. for line in lines
  45. let matches = matchstr(line, '\m\C^\s*-I\s*\zs.\+')
  46. if matches !=# ''
  47. " this one looks like an absolute path
  48. if match(matches, '\m^\%(/\|\a:\)') != -1
  49. call add(parameters, '-I' . matches)
  50. else
  51. call add(parameters, '-I' . filepath . syntastic#util#Slash() . matches)
  52. endif
  53. else
  54. call add(parameters, line)
  55. endif
  56. endfor
  57. return join(map(parameters, 'syntastic#util#shescape(v:val)'))
  58. endfunction " }}}2
  59. " GetLocList() for C-like compilers
  60. function! syntastic#c#GetLocList(filetype, subchecker, options) abort " {{{2
  61. try
  62. let flags = s:_get_cflags(a:filetype, a:subchecker, a:options)
  63. catch /\m\C^Syntastic: skip checks$/
  64. return []
  65. endtry
  66. let makeprg = syntastic#util#shexpand(g:syntastic_{a:filetype}_compiler) .
  67. \ ' ' . flags . ' ' . syntastic#util#shexpand('%')
  68. let errorformat = s:_get_checker_var('g', a:filetype, a:subchecker, 'errorformat', a:options['errorformat'])
  69. let postprocess = s:_get_checker_var('g', a:filetype, a:subchecker, 'remove_include_errors', 0) ?
  70. \ ['filterForeignErrors'] : []
  71. " process makeprg
  72. return SyntasticMake({
  73. \ 'makeprg': makeprg,
  74. \ 'errorformat': errorformat,
  75. \ 'postprocess': postprocess })
  76. endfunction " }}}2
  77. " }}}1
  78. " Private functions {{{1
  79. " initialize c/cpp syntax checker handlers
  80. function! s:_init() abort " {{{2
  81. let s:handlers = []
  82. let s:cflags = {}
  83. call s:_registerHandler('\m\<cairo', 's:_checkPackage', ['cairo', 'cairo'])
  84. call s:_registerHandler('\m\<freetype', 's:_checkPackage', ['freetype', 'freetype2', 'freetype'])
  85. call s:_registerHandler('\m\<glade', 's:_checkPackage', ['glade', 'libglade-2.0', 'libglade'])
  86. call s:_registerHandler('\m\<glib', 's:_checkPackage', ['glib', 'glib-2.0', 'glib'])
  87. call s:_registerHandler('\m\<gtk', 's:_checkPackage', ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib'])
  88. call s:_registerHandler('\m\<libsoup', 's:_checkPackage', ['libsoup', 'libsoup-2.4', 'libsoup-2.2'])
  89. call s:_registerHandler('\m\<libxml', 's:_checkPackage', ['libxml', 'libxml-2.0', 'libxml'])
  90. call s:_registerHandler('\m\<pango', 's:_checkPackage', ['pango', 'pango'])
  91. call s:_registerHandler('\m\<SDL', 's:_checkPackage', ['sdl', 'sdl'])
  92. call s:_registerHandler('\m\<opengl', 's:_checkPackage', ['opengl', 'gl'])
  93. call s:_registerHandler('\m\<webkit', 's:_checkPackage', ['webkit', 'webkit-1.0'])
  94. call s:_registerHandler('\m\<php\.h\>', 's:_checkPhp', [])
  95. call s:_registerHandler('\m\<Python\.h\>', 's:_checkPython', [])
  96. call s:_registerHandler('\m\<ruby', 's:_checkRuby', [])
  97. endfunction " }}}2
  98. " register a handler dictionary object
  99. function! s:_registerHandler(regex, function, args) abort " {{{2
  100. let handler = {}
  101. let handler['regex'] = a:regex
  102. let handler['func'] = function(a:function)
  103. let handler['args'] = a:args
  104. call add(s:handlers, handler)
  105. endfunction " }}}2
  106. " try to find library with 'pkg-config'
  107. " search possible libraries from first to last given
  108. " argument until one is found
  109. function! s:_checkPackage(name, ...) abort " {{{2
  110. if executable('pkg-config')
  111. if !has_key(s:cflags, a:name)
  112. for pkg in a:000
  113. let pkg_flags = syntastic#util#system('pkg-config --cflags ' . pkg)
  114. " since we cannot necessarily trust the pkg-config exit code
  115. " we have to check for an error output as well
  116. if v:shell_error == 0 && pkg_flags !~? 'not found'
  117. let pkg_flags = ' ' . substitute(pkg_flags, "\n", '', '')
  118. let s:cflags[a:name] = pkg_flags
  119. return pkg_flags
  120. endif
  121. endfor
  122. else
  123. return s:cflags[a:name]
  124. endif
  125. endif
  126. return ''
  127. endfunction " }}}2
  128. " try to find PHP includes with 'php-config'
  129. function! s:_checkPhp() abort " {{{2
  130. if executable('php-config')
  131. if !has_key(s:cflags, 'php')
  132. let s:cflags['php'] = syntastic#util#system('php-config --includes')
  133. let s:cflags['php'] = ' ' . substitute(s:cflags['php'], "\n", '', '')
  134. endif
  135. return s:cflags['php']
  136. endif
  137. return ''
  138. endfunction " }}}2
  139. " try to find the python headers with distutils
  140. function! s:_checkPython() abort " {{{2
  141. if executable('python')
  142. if !has_key(s:cflags, 'python')
  143. let s:cflags['python'] = syntastic#util#system('python -c ''from distutils import ' .
  144. \ 'sysconfig; import sys; sys.stdout.write(sysconfig.get_python_inc())''')
  145. let s:cflags['python'] = substitute(s:cflags['python'], "\n", '', '')
  146. let s:cflags['python'] = ' -I' . s:cflags['python']
  147. endif
  148. return s:cflags['python']
  149. endif
  150. return ''
  151. endfunction " }}}2
  152. " try to find the ruby headers with 'rbconfig'
  153. function! s:_checkRuby() abort " {{{2
  154. if executable('ruby')
  155. if !has_key(s:cflags, 'ruby')
  156. let s:cflags['ruby'] = syntastic#util#system('ruby -r rbconfig -e ' .
  157. \ '''puts RbConfig::CONFIG["rubyhdrdir"] || RbConfig::CONFIG["archdir"]''')
  158. let s:cflags['ruby'] = substitute(s:cflags['ruby'], "\n", '', '')
  159. let s:cflags['ruby'] = ' -I' . s:cflags['ruby']
  160. endif
  161. return s:cflags['ruby']
  162. endif
  163. return ''
  164. endfunction " }}}2
  165. " }}}1
  166. " Utilities {{{1
  167. " resolve checker-related user variables
  168. function! s:_get_checker_var(scope, filetype, subchecker, name, default) abort " {{{2
  169. let prefix = a:scope . ':' . 'syntastic_'
  170. if exists(prefix . a:filetype . '_' . a:subchecker . '_' . a:name)
  171. return {a:scope}:syntastic_{a:filetype}_{a:subchecker}_{a:name}
  172. elseif exists(prefix . a:filetype . '_' . a:name)
  173. return {a:scope}:syntastic_{a:filetype}_{a:name}
  174. else
  175. return a:default
  176. endif
  177. endfunction " }}}2
  178. " resolve user CFLAGS
  179. function! s:_get_cflags(ft, ck, opts) abort " {{{2
  180. " determine whether to parse header files as well
  181. if has_key(a:opts, 'header_names') && expand('%', 1) =~? a:opts['header_names']
  182. if s:_get_checker_var('g', a:ft, a:ck, 'check_header', 0)
  183. let flags = get(a:opts, 'header_flags', '') . ' -c ' . syntastic#c#NullOutput()
  184. else
  185. " checking headers when check_header is unset: bail out
  186. throw 'Syntastic: skip checks'
  187. endif
  188. else
  189. let flags = get(a:opts, 'main_flags', '')
  190. endif
  191. let flags .= ' ' . s:_get_checker_var('g', a:ft, a:ck, 'compiler_options', '') . ' ' . s:_get_include_dirs(a:ft)
  192. " check if the user manually set some cflags
  193. let b_cflags = s:_get_checker_var('b', a:ft, a:ck, 'cflags', '')
  194. if b_cflags ==# ''
  195. if a:ft ==# 'c' || a:ft ==# 'cpp'
  196. " check whether to search for include files at all
  197. if !s:_get_checker_var('g', a:ft, a:ck, 'no_include_search', 0)
  198. " refresh the include file search if desired
  199. if s:_get_checker_var('g', a:ft, a:ck, 'auto_refresh_includes', 0)
  200. let flags .= ' ' . s:_search_headers()
  201. else
  202. " search for header includes if not cached already
  203. if !exists('b:syntastic_' . a:ft . '_includes')
  204. let b:syntastic_{a:ft}_includes = s:_search_headers()
  205. endif
  206. let flags .= ' ' . b:syntastic_{a:ft}_includes
  207. endif
  208. endif
  209. endif
  210. else
  211. " user-defined cflags
  212. let flags .= ' ' . b_cflags
  213. endif
  214. " add optional config file parameters
  215. let config_file = s:_get_checker_var('g', a:ft, a:ck, 'config_file', '.syntastic_' . a:ft . '_config')
  216. let flags .= ' ' . syntastic#c#ReadConfig(config_file)
  217. return flags
  218. endfunction " }}}2
  219. " get the gcc include directory argument depending on the default
  220. " includes and the optional user-defined 'g:syntastic_c_include_dirs'
  221. function! s:_get_include_dirs(filetype) abort " {{{2
  222. let include_dirs = []
  223. if a:filetype =~# '\v^%(c|cpp|objc|objcpp)$' &&
  224. \ (!exists('g:syntastic_'.a:filetype.'_no_default_include_dirs') ||
  225. \ !g:syntastic_{a:filetype}_no_default_include_dirs)
  226. let include_dirs = copy(s:default_includes)
  227. endif
  228. if exists('g:syntastic_'.a:filetype.'_include_dirs')
  229. call extend(include_dirs, g:syntastic_{a:filetype}_include_dirs)
  230. endif
  231. return join(map(syntastic#util#unique(include_dirs), 'syntastic#util#shescape("-I" . v:val)'))
  232. endfunction " }}}2
  233. " search the first 100 lines for include statements that are
  234. " given in the handlers dictionary
  235. function! s:_search_headers() abort " {{{2
  236. let includes = ''
  237. let files = []
  238. let found = []
  239. let lines = filter(getline(1, 100), 'v:val =~# ''\m^\s*#\s*include''')
  240. " search current buffer
  241. for line in lines
  242. let file = matchstr(line, '\m"\zs\S\+\ze"')
  243. if file !=# ''
  244. call add(files, file)
  245. continue
  246. endif
  247. for handler in s:handlers
  248. if line =~# handler['regex']
  249. let includes .= call(handler['func'], handler['args'])
  250. call add(found, handler['regex'])
  251. break
  252. endif
  253. endfor
  254. endfor
  255. " search included headers
  256. for hfile in files
  257. if hfile !=# ''
  258. let filename = expand('%:p:h', 1) . syntastic#util#Slash() . hfile
  259. try
  260. let lines = readfile(filename, '', 100)
  261. catch /\m^Vim\%((\a\+)\)\=:E484/
  262. continue
  263. endtry
  264. call filter(lines, 'v:val =~# ''\m^\s*#\s*include''')
  265. for handler in s:handlers
  266. if index(found, handler['regex']) != -1
  267. continue
  268. endif
  269. for line in lines
  270. if line =~# handler['regex']
  271. let includes .= call(handler['func'], handler['args'])
  272. call add(found, handler['regex'])
  273. break
  274. endif
  275. endfor
  276. endfor
  277. endif
  278. endfor
  279. return includes
  280. endfunction " }}}2
  281. " }}}1
  282. " default include directories
  283. let s:default_includes = [
  284. \ '.',
  285. \ '..',
  286. \ 'include',
  287. \ 'includes',
  288. \ '..' . syntastic#util#Slash() . 'include',
  289. \ '..' . syntastic#util#Slash() . 'includes' ]
  290. call s:_init()
  291. let &cpo = s:save_cpo
  292. unlet s:save_cpo
  293. " vim: set sw=4 sts=4 et fdm=marker: