diff --git a/autoload/syntastic/c.vim b/autoload/syntastic/c.vim index 9c2ad169..993982e6 100644 --- a/autoload/syntastic/c.vim +++ b/autoload/syntastic/c.vim @@ -15,41 +15,28 @@ function! syntastic#c#NullOutput() return known_os ? '-o ' . syntastic#util#DevNull() : '' endfunction -" get the gcc include directory argument depending on the default -" includes and the optional user-defined 'g:syntastic_c_include_dirs' -function! syntastic#c#GetIncludeDirs(filetype) - let include_dirs = [] - - if !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), '"-I" . v:val'), ' ') -endfunction - " 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) " search in the current file's directory upwards let config = findfile(a:file, '.;') - if config == '' || !filereadable(config) | return '' | endif + if config == '' || !filereadable(config) + return '' + endif " convert filename into absolute path - let filepath = substitute(fnamemodify(config, ':p:h'), '\', '/', 'g') + let filepath = fnamemodify(config, ':p:h') " try to read config file try - let lines = map(readfile(config), - \ 'substitute(v:val, ''\'', ''/'', ''g'')') - catch /E484/ + let lines = readfile(config) + catch /^Vim\%((\a\+)\)\=:E484/ return '' endtry + " filter out empty lines and comments + call filter(lines, 'v:val !~ ''\v^(\s*#|$)''') + let parameters = [] for line in lines let matches = matchlist(line, '\C^\s*-I\s*\(\S\+\)') @@ -58,67 +45,14 @@ function! syntastic#c#ReadConfig(file) if match(matches[1], '^\%(/\|\a:\)') != -1 call add(parameters, '-I' . matches[1]) else - call add(parameters, '-I' . filepath . '/' . matches[1]) + call add(parameters, '-I' . filepath . syntastic#util#Slash() . matches[1]) endif else call add(parameters, line) endif endfor - return join(parameters, ' ') -endfunction - -" search the first 100 lines for include statements that are -" given in the handlers dictionary -function! syntastic#c#SearchHeaders() - let includes = '' - let files = [] - let found = [] - let lines = filter(getline(1, 100), 'v:val =~# "^\s*#\s*include"') - - " search current buffer - for line in lines - let file = matchstr(line, '"\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') . (has('win32') ? - \ '\' : '/') . hfile - try - let lines = readfile(filename, '', 100) - catch /E484/ - continue - endtry - let lines = filter(lines, 'v:val =~# "^\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 + return join(map(parameters, 'shellescape(fnameescape(v:val))'), ' ') endfunction " GetLocList() for C-like compilers @@ -134,7 +68,7 @@ function! syntastic#c#GetLocList(filetype, options) \ g:syntastic_{ft}_compiler . \ ' ' . get(a:options, 'makeprg_headers', '') . \ ' ' . g:syntastic_{ft}_compiler_options . - \ ' ' . syntastic#c#GetIncludeDirs(ft) . + \ ' ' . s:GetIncludeDirs(ft) . \ ' ' . syntastic#c#NullOutput() . \ ' -c ' . shellescape(expand('%')) else @@ -145,7 +79,7 @@ function! syntastic#c#GetLocList(filetype, options) \ g:syntastic_{ft}_compiler . \ ' ' . get(a:options, 'makeprg_main', '') . \ ' ' . g:syntastic_{ft}_compiler_options . - \ ' ' . syntastic#c#GetIncludeDirs(ft) . + \ ' ' . s:GetIncludeDirs(ft) . \ ' ' . shellescape(expand('%')) endif @@ -156,11 +90,11 @@ function! syntastic#c#GetLocList(filetype, options) if ft ==# 'c' || ft ==# 'cpp' " refresh the include file search if desired if exists('g:syntastic_' . ft . '_auto_refresh_includes') && g:syntastic_{ft}_auto_refresh_includes - let makeprg .= ' ' . syntastic#c#SearchHeaders() + let makeprg .= ' ' . s:SearchHeaders() else " search for header includes if not cached already if !exists('b:syntastic_' . ft . '_includes') - let b:syntastic_{ft}_includes = syntastic#c#SearchHeaders() + let b:syntastic_{ft}_includes = s:SearchHeaders() endif let makeprg .= ' ' . b:syntastic_{ft}_includes endif @@ -192,31 +126,94 @@ function! s:Init() let s:handlers = [] let s:cflags = {} - call s:RegHandler('gtk', 'syntastic#c#CheckPKG', - \ ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib']) - call s:RegHandler('glib', 'syntastic#c#CheckPKG', - \ ['glib', 'glib-2.0', 'glib']) - call s:RegHandler('glade', 'syntastic#c#CheckPKG', - \ ['glade', 'libglade-2.0', 'libglade']) - call s:RegHandler('libsoup', 'syntastic#c#CheckPKG', - \ ['libsoup', 'libsoup-2.4', 'libsoup-2.2']) - call s:RegHandler('webkit', 'syntastic#c#CheckPKG', - \ ['webkit', 'webkit-1.0']) - call s:RegHandler('cairo', 'syntastic#c#CheckPKG', - \ ['cairo', 'cairo']) - call s:RegHandler('pango', 'syntastic#c#CheckPKG', - \ ['pango', 'pango']) - call s:RegHandler('libxml', 'syntastic#c#CheckPKG', - \ ['libxml', 'libxml-2.0', 'libxml']) - call s:RegHandler('freetype', 'syntastic#c#CheckPKG', - \ ['freetype', 'freetype2', 'freetype']) - call s:RegHandler('SDL', 'syntastic#c#CheckPKG', - \ ['sdl', 'sdl']) - call s:RegHandler('opengl', 'syntastic#c#CheckPKG', - \ ['opengl', 'gl']) - call s:RegHandler('ruby', 'syntastic#c#CheckRuby', []) + call s:RegHandler('cairo', 'syntastic#c#CheckPKG', ['cairo', 'cairo']) + call s:RegHandler('freetype', 'syntastic#c#CheckPKG', ['freetype', 'freetype2', 'freetype']) + call s:RegHandler('glade', 'syntastic#c#CheckPKG', ['glade', 'libglade-2.0', 'libglade']) + call s:RegHandler('glib', 'syntastic#c#CheckPKG', ['glib', 'glib-2.0', 'glib']) + call s:RegHandler('gtk', 'syntastic#c#CheckPKG', ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib']) + call s:RegHandler('libsoup', 'syntastic#c#CheckPKG', ['libsoup', 'libsoup-2.4', 'libsoup-2.2']) + call s:RegHandler('libxml', 'syntastic#c#CheckPKG', ['libxml', 'libxml-2.0', 'libxml']) + call s:RegHandler('pango', 'syntastic#c#CheckPKG', ['pango', 'pango']) + call s:RegHandler('SDL', 'syntastic#c#CheckPKG', ['sdl', 'sdl']) + call s:RegHandler('opengl', 'syntastic#c#CheckPKG', ['opengl', 'gl']) + call s:RegHandler('webkit', 'syntastic#c#CheckPKG', ['webkit', 'webkit-1.0']) + + call s:RegHandler('php\.h', 'syntastic#c#CheckPhp', []) call s:RegHandler('Python\.h', 'syntastic#c#CheckPython', []) - call s:RegHandler('php\.h', 'syntastic#c#CheckPhp', []) + call s:RegHandler('ruby', 'syntastic#c#CheckRuby', []) +endfunction + +" get the gcc include directory argument depending on the default +" includes and the optional user-defined 'g:syntastic_c_include_dirs' +function! s:GetIncludeDirs(filetype) + let include_dirs = [] + + if !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), 'shellescape(fnameescape("-I" . v:val))'), ' ') +endfunction + +" search the first 100 lines for include statements that are +" given in the handlers dictionary +function! s:SearchHeaders() + let includes = '' + let files = [] + let found = [] + let lines = filter(getline(1, 100), 'v:val =~# "^\s*#\s*include"') + + " search current buffer + for line in lines + let file = matchstr(line, '"\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') . syntastic#util#Slash() . hfile + + try + let lines = readfile(filename, '', 100) + catch /^Vim\%((\a\+)\)\=:E484/ + continue + endtry + + let lines = filter(lines, 'v:val =~# "^\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 " try to find library with 'pkg-config' @@ -225,14 +222,14 @@ endfunction function! syntastic#c#CheckPKG(name, ...) if executable('pkg-config') if !has_key(s:cflags, a:name) - for i in range(a:0) - let l:cflags = system('pkg-config --cflags '.a:000[i]) + for pkg in a:000 + let pkg_flags = 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 && l:cflags !~? 'not found' - let l:cflags = ' '.substitute(l:cflags, "\n", '', '') - let s:cflags[a:name] = l:cflags - return l:cflags + 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 @@ -245,11 +242,11 @@ endfunction " try to find PHP includes with 'php-config' function! syntastic#c#CheckPhp() if executable('php-config') - if !exists('s:php_flags') - let s:php_flags = system('php-config --includes') - let s:php_flags = ' ' . substitute(s:php_flags, "\n", '', '') + if !has_key(s:cflags, 'php') + let s:cflags['php'] = system('php-config --includes') + let s:cflags['php'] = ' ' . substitute(s:cflags['php'], "\n", '', '') endif - return s:php_flags + return s:cflags['php'] endif return '' endfunction @@ -257,13 +254,12 @@ endfunction " try to find the ruby headers with 'rbconfig' function! syntastic#c#CheckRuby() if executable('ruby') - if !exists('s:ruby_flags') - let s:ruby_flags = system('ruby -r rbconfig -e ' - \ . '''puts Config::CONFIG["archdir"]''') - let s:ruby_flags = substitute(s:ruby_flags, "\n", '', '') - let s:ruby_flags = ' -I' . s:ruby_flags + if !has_key(s:cflags, 'ruby') + let s:cflags['ruby'] = system('ruby -r rbconfig -e ''puts Config::CONFIG["archdir"]''') + let s:cflags['ruby'] = substitute(s:cflags['ruby'], "\n", '', '') + let s:cflags['ruby'] = ' -I' . s:cflags['ruby'] endif - return s:ruby_flags + return s:cflags['ruby'] endif return '' endfunction @@ -271,13 +267,13 @@ endfunction " try to find the python headers with distutils function! syntastic#c#CheckPython() if executable('python') - if !exists('s:python_flags') - let s:python_flags = system('python -c ''from distutils import ' - \ . 'sysconfig; import sys; sys.stdout.write(sysconfig.get_python_inc())''') - let s:python_flags = substitute(s:python_flags, "\n", '', '') - let s:python_flags = ' -I' . s:python_flags + if !has_key(s:cflags, 'python') + let s:cflags['python'] = 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:python_flags + return s:cflags['python'] endif return '' endfunction @@ -294,8 +290,13 @@ endfunction " }}}1 " default include directories -let s:default_includes = [ '.', '..', 'include', 'includes', - \ '../include', '../includes' ] +let s:default_includes = [ + \ '.', + \ '..', + \ 'include', + \ 'includes', + \ '..' . syntastic#util#Slash() . 'include', + \ '..' . syntastic#util#Slash() . 'includes' ] call s:Init() diff --git a/autoload/syntastic/util.vim b/autoload/syntastic/util.vim index 4e777b3d..b6f58d83 100644 --- a/autoload/syntastic/util.vim +++ b/autoload/syntastic/util.vim @@ -19,6 +19,11 @@ function! syntastic#util#DevNull() return '/dev/null' endfunction +" Get directory separator +function! syntastic#util#Slash() abort + return !exists("+shellslash") || &shellslash ? '/' : '\' +endfunction + "search the first 5 lines of the file for a magic number and return a map "containing the args and the executable "