| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- if exists('g:loaded_syntastic_c_autoload') || !exists('g:loaded_syntastic_plugin')
- finish
- endif
- let g:loaded_syntastic_c_autoload = 1
- let s:save_cpo = &cpo
- set cpo&vim
- " Public functions {{{1
- " convenience function to determine the 'null device' parameter
- " based on the current operating system
- function! syntastic#c#NullOutput() abort " {{{2
- let known_os = has('unix') || has('mac') || syntastic#util#isRunningWindows()
- return known_os ? '-o ' . syntastic#util#DevNull() : ''
- endfunction " }}}2
- " read additional compiler flags from the given configuration file
- " the file format and its parsing mechanism is inspired by clang_complete
- function! syntastic#c#ReadConfig(file) abort " {{{2
- call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: looking for', a:file)
- " search upwards from the current file's directory
- let config = findfile(a:file, '.;')
- if config ==# ''
- call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file not found')
- return ''
- endif
- call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: config file:', config)
- if !filereadable(config)
- call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file unreadable')
- return ''
- endif
- " convert filename into absolute path
- let filepath = fnamemodify(config, ':p:h')
- " try to read config file
- try
- let lines = readfile(config)
- catch /\m^Vim\%((\a\+)\)\=:E48[45]/
- call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: error reading file')
- return ''
- endtry
- " filter out empty lines and comments
- call filter(lines, 'v:val !~# ''\v^(\s*#|$)''')
- " remove leading and trailing spaces
- call map(lines, 'substitute(v:val, ''\m^\s\+'', "", "")')
- call map(lines, 'substitute(v:val, ''\m\s\+$'', "", "")')
- let parameters = []
- for line in lines
- let matches = matchstr(line, '\m\C^\s*-I\s*\zs.\+')
- if matches !=# ''
- " this one looks like an absolute path
- if match(matches, '\m^\%(/\|\a:\)') != -1
- call add(parameters, '-I' . matches)
- else
- call add(parameters, '-I' . filepath . syntastic#util#Slash() . matches)
- endif
- else
- call add(parameters, line)
- endif
- endfor
- return join(map(parameters, 'syntastic#util#shescape(v:val)'))
- endfunction " }}}2
- " GetLocList() for C-like compilers
- function! syntastic#c#GetLocList(filetype, subchecker, options) abort " {{{2
- try
- let flags = s:_get_cflags(a:filetype, a:subchecker, a:options)
- catch /\m\C^Syntastic: skip checks$/
- return []
- endtry
- let makeprg = syntastic#util#shexpand(g:syntastic_{a:filetype}_compiler) .
- \ ' ' . flags . ' ' . syntastic#util#shexpand('%')
- let errorformat = s:_get_checker_var('g', a:filetype, a:subchecker, 'errorformat', a:options['errorformat'])
- let postprocess = s:_get_checker_var('g', a:filetype, a:subchecker, 'remove_include_errors', 0) ?
- \ ['filterForeignErrors'] : []
- " process makeprg
- return SyntasticMake({
- \ 'makeprg': makeprg,
- \ 'errorformat': errorformat,
- \ 'postprocess': postprocess })
- endfunction " }}}2
- " }}}1
- " Private functions {{{1
- " initialize c/cpp syntax checker handlers
- function! s:_init() abort " {{{2
- let s:handlers = []
- let s:cflags = {}
- call s:_registerHandler('\m\<cairo', 's:_checkPackage', ['cairo', 'cairo'])
- call s:_registerHandler('\m\<freetype', 's:_checkPackage', ['freetype', 'freetype2', 'freetype'])
- call s:_registerHandler('\m\<glade', 's:_checkPackage', ['glade', 'libglade-2.0', 'libglade'])
- call s:_registerHandler('\m\<glib', 's:_checkPackage', ['glib', 'glib-2.0', 'glib'])
- call s:_registerHandler('\m\<gtk', 's:_checkPackage', ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib'])
- call s:_registerHandler('\m\<libsoup', 's:_checkPackage', ['libsoup', 'libsoup-2.4', 'libsoup-2.2'])
- call s:_registerHandler('\m\<libxml', 's:_checkPackage', ['libxml', 'libxml-2.0', 'libxml'])
- call s:_registerHandler('\m\<pango', 's:_checkPackage', ['pango', 'pango'])
- call s:_registerHandler('\m\<SDL', 's:_checkPackage', ['sdl', 'sdl'])
- call s:_registerHandler('\m\<opengl', 's:_checkPackage', ['opengl', 'gl'])
- call s:_registerHandler('\m\<webkit', 's:_checkPackage', ['webkit', 'webkit-1.0'])
- call s:_registerHandler('\m\<php\.h\>', 's:_checkPhp', [])
- call s:_registerHandler('\m\<Python\.h\>', 's:_checkPython', [])
- call s:_registerHandler('\m\<ruby', 's:_checkRuby', [])
- endfunction " }}}2
- " register a handler dictionary object
- function! s:_registerHandler(regex, function, args) abort " {{{2
- let handler = {}
- let handler['regex'] = a:regex
- let handler['func'] = function(a:function)
- let handler['args'] = a:args
- call add(s:handlers, handler)
- endfunction " }}}2
- " try to find library with 'pkg-config'
- " search possible libraries from first to last given
- " argument until one is found
- function! s:_checkPackage(name, ...) abort " {{{2
- if executable('pkg-config')
- if !has_key(s:cflags, a:name)
- for pkg in a:000
- let pkg_flags = syntastic#util#system('pkg-config --cflags ' . pkg)
- " since we cannot necessarily trust the pkg-config exit code
- " we have to check for an error output as well
- if v:shell_error == 0 && pkg_flags !~? 'not found'
- let pkg_flags = ' ' . substitute(pkg_flags, "\n", '', '')
- let s:cflags[a:name] = pkg_flags
- return pkg_flags
- endif
- endfor
- else
- return s:cflags[a:name]
- endif
- endif
- return ''
- endfunction " }}}2
- " try to find PHP includes with 'php-config'
- function! s:_checkPhp() abort " {{{2
- if executable('php-config')
- if !has_key(s:cflags, 'php')
- let s:cflags['php'] = syntastic#util#system('php-config --includes')
- let s:cflags['php'] = ' ' . substitute(s:cflags['php'], "\n", '', '')
- endif
- return s:cflags['php']
- endif
- return ''
- endfunction " }}}2
- " try to find the python headers with distutils
- function! s:_checkPython() abort " {{{2
- if executable('python')
- if !has_key(s:cflags, 'python')
- let s:cflags['python'] = syntastic#util#system('python -c ''from distutils import ' .
- \ 'sysconfig; import sys; sys.stdout.write(sysconfig.get_python_inc())''')
- let s:cflags['python'] = substitute(s:cflags['python'], "\n", '', '')
- let s:cflags['python'] = ' -I' . s:cflags['python']
- endif
- return s:cflags['python']
- endif
- return ''
- endfunction " }}}2
- " try to find the ruby headers with 'rbconfig'
- function! s:_checkRuby() abort " {{{2
- if executable('ruby')
- if !has_key(s:cflags, 'ruby')
- let s:cflags['ruby'] = syntastic#util#system('ruby -r rbconfig -e ' .
- \ '''puts RbConfig::CONFIG["rubyhdrdir"] || RbConfig::CONFIG["archdir"]''')
- let s:cflags['ruby'] = substitute(s:cflags['ruby'], "\n", '', '')
- let s:cflags['ruby'] = ' -I' . s:cflags['ruby']
- endif
- return s:cflags['ruby']
- endif
- return ''
- endfunction " }}}2
- " }}}1
- " Utilities {{{1
- " resolve checker-related user variables
- function! s:_get_checker_var(scope, filetype, subchecker, name, default) abort " {{{2
- let prefix = a:scope . ':' . 'syntastic_'
- if exists(prefix . a:filetype . '_' . a:subchecker . '_' . a:name)
- return {a:scope}:syntastic_{a:filetype}_{a:subchecker}_{a:name}
- elseif exists(prefix . a:filetype . '_' . a:name)
- return {a:scope}:syntastic_{a:filetype}_{a:name}
- else
- return a:default
- endif
- endfunction " }}}2
- " resolve user CFLAGS
- function! s:_get_cflags(ft, ck, opts) abort " {{{2
- " determine whether to parse header files as well
- if has_key(a:opts, 'header_names') && expand('%', 1) =~? a:opts['header_names']
- if s:_get_checker_var('g', a:ft, a:ck, 'check_header', 0)
- let flags = get(a:opts, 'header_flags', '') . ' -c ' . syntastic#c#NullOutput()
- else
- " checking headers when check_header is unset: bail out
- throw 'Syntastic: skip checks'
- endif
- else
- let flags = get(a:opts, 'main_flags', '')
- endif
- let flags .= ' ' . s:_get_checker_var('g', a:ft, a:ck, 'compiler_options', '') . ' ' . s:_get_include_dirs(a:ft)
- " check if the user manually set some cflags
- let b_cflags = s:_get_checker_var('b', a:ft, a:ck, 'cflags', '')
- if b_cflags ==# ''
- if a:ft ==# 'c' || a:ft ==# 'cpp'
- " check whether to search for include files at all
- if !s:_get_checker_var('g', a:ft, a:ck, 'no_include_search', 0)
- " refresh the include file search if desired
- if s:_get_checker_var('g', a:ft, a:ck, 'auto_refresh_includes', 0)
- let flags .= ' ' . s:_search_headers()
- else
- " search for header includes if not cached already
- if !exists('b:syntastic_' . a:ft . '_includes')
- let b:syntastic_{a:ft}_includes = s:_search_headers()
- endif
- let flags .= ' ' . b:syntastic_{a:ft}_includes
- endif
- endif
- endif
- else
- " user-defined cflags
- let flags .= ' ' . b_cflags
- endif
- " add optional config file parameters
- let config_file = s:_get_checker_var('g', a:ft, a:ck, 'config_file', '.syntastic_' . a:ft . '_config')
- let flags .= ' ' . syntastic#c#ReadConfig(config_file)
- return flags
- endfunction " }}}2
- " get the gcc include directory argument depending on the default
- " includes and the optional user-defined 'g:syntastic_c_include_dirs'
- function! s:_get_include_dirs(filetype) abort " {{{2
- let include_dirs = []
- if a:filetype =~# '\v^%(c|cpp|objc|objcpp)$' &&
- \ (!exists('g:syntastic_'.a:filetype.'_no_default_include_dirs') ||
- \ !g:syntastic_{a:filetype}_no_default_include_dirs)
- let include_dirs = copy(s:default_includes)
- endif
- if exists('g:syntastic_'.a:filetype.'_include_dirs')
- call extend(include_dirs, g:syntastic_{a:filetype}_include_dirs)
- endif
- return join(map(syntastic#util#unique(include_dirs), 'syntastic#util#shescape("-I" . v:val)'))
- endfunction " }}}2
- " search the first 100 lines for include statements that are
- " given in the handlers dictionary
- function! s:_search_headers() abort " {{{2
- let includes = ''
- let files = []
- let found = []
- let lines = filter(getline(1, 100), 'v:val =~# ''\m^\s*#\s*include''')
- " search current buffer
- for line in lines
- let file = matchstr(line, '\m"\zs\S\+\ze"')
- if file !=# ''
- call add(files, file)
- continue
- endif
- for handler in s:handlers
- if line =~# handler['regex']
- let includes .= call(handler['func'], handler['args'])
- call add(found, handler['regex'])
- break
- endif
- endfor
- endfor
- " search included headers
- for hfile in files
- if hfile !=# ''
- let filename = expand('%:p:h', 1) . syntastic#util#Slash() . hfile
- try
- let lines = readfile(filename, '', 100)
- catch /\m^Vim\%((\a\+)\)\=:E484/
- continue
- endtry
- call filter(lines, 'v:val =~# ''\m^\s*#\s*include''')
- for handler in s:handlers
- if index(found, handler['regex']) != -1
- continue
- endif
- for line in lines
- if line =~# handler['regex']
- let includes .= call(handler['func'], handler['args'])
- call add(found, handler['regex'])
- break
- endif
- endfor
- endfor
- endif
- endfor
- return includes
- endfunction " }}}2
- " }}}1
- " default include directories
- let s:default_includes = [
- \ '.',
- \ '..',
- \ 'include',
- \ 'includes',
- \ '..' . syntastic#util#Slash() . 'include',
- \ '..' . syntastic#util#Slash() . 'includes' ]
- call s:_init()
- let &cpo = s:save_cpo
- unlet s:save_cpo
- " vim: set sw=4 sts=4 et fdm=marker:
|