diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index ddec4fcb..48ea76dd 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -14,10 +14,10 @@ function! ale_linters#c#clang#GetCommand(buffer, output) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'c_clang_options') . ' -' + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'c_clang_options')) . ' -' endfunction call ale#linter#Define('c', { diff --git a/ale_linters/c/clangtidy.vim b/ale_linters/c/clangtidy.vim index 47faa1ef..d456a5a2 100644 --- a/ale_linters/c/clangtidy.vim +++ b/ale_linters/c/clangtidy.vim @@ -35,7 +35,7 @@ function! s:GetBuildDirectory(buffer) abort return l:build_dir endif - return ale#c#FindCompileCommands(a:buffer) + return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) endfunction function! ale_linters#c#clangtidy#GetCommand(buffer) abort diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 98563952..63fe87a6 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -14,10 +14,10 @@ function! ale_linters#c#gcc#GetCommand(buffer, output) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'c_gcc_options') . ' -' + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -' endfunction call ale#linter#Define('c', { diff --git a/ale_linters/cpp/clang.vim b/ale_linters/cpp/clang.vim index e8d96187..4f3c5b6f 100644 --- a/ale_linters/cpp/clang.vim +++ b/ale_linters/cpp/clang.vim @@ -14,10 +14,10 @@ function! ale_linters#cpp#clang#GetCommand(buffer, output) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer)) - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'cpp_clang_options') . ' -' + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'cpp_clang_options')) . ' -' endfunction call ale#linter#Define('cpp', { diff --git a/ale_linters/cpp/clangcheck.vim b/ale_linters/cpp/clangcheck.vim index a109d5d3..687991bd 100644 --- a/ale_linters/cpp/clangcheck.vim +++ b/ale_linters/cpp/clangcheck.vim @@ -16,7 +16,7 @@ function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort let l:build_dir = ale#Var(a:buffer, 'c_build_dir') if empty(l:build_dir) - let l:build_dir = ale#c#FindCompileCommands(a:buffer) + let l:build_dir = ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) endif " The extra arguments in the command are used to prevent .plist files from diff --git a/ale_linters/cpp/clangtidy.vim b/ale_linters/cpp/clangtidy.vim index 1d5fb77a..d683327f 100644 --- a/ale_linters/cpp/clangtidy.vim +++ b/ale_linters/cpp/clangtidy.vim @@ -29,7 +29,7 @@ function! s:GetBuildDirectory(buffer) abort return l:build_dir endif - return ale#c#FindCompileCommands(a:buffer) + return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) endfunction function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim index a663eaa3..242e389e 100644 --- a/ale_linters/cpp/gcc.vim +++ b/ale_linters/cpp/gcc.vim @@ -14,10 +14,10 @@ function! ale_linters#cpp#gcc#GetCommand(buffer, output) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer)) - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'cpp_gcc_options') . ' -' + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -' endfunction call ale#linter#Define('cpp', { diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index fbfe9509..19bb0d5e 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -2,6 +2,7 @@ " Description: Functions for integrating with C-family linters. call ale#Set('c_parse_makefile', 0) +call ale#Set('c_parse_compile_commands', 0) let s:sep = has('win32') ? '\' : '/' " Set just so tests can override it. @@ -26,11 +27,11 @@ function! ale#c#FindProjectRoot(buffer) abort return '' endfunction -function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort +function! ale#c#ParseCFlags(path_prefix, cflag_line) abort let l:cflags_list = [] let l:previous_options = [] - for l:option in a:cflags + for l:option in split(a:cflag_line, '-') call add(l:previous_options, l:option) " Check if cflag contained a '-' and should not have been splitted let l:option_list = split(l:option, '\zs') @@ -62,32 +63,122 @@ function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort endif endfor - return l:cflags_list + return join(l:cflags_list, ' ') endfunction -function! ale#c#ParseCFlags(buffer, stdout_make) abort +function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort if !g:ale_c_parse_makefile - return [] + return '' endif let l:buffer_filename = expand('#' . a:buffer . ':t') - let l:cflags = [] - for l:lines in split(a:stdout_make, '\\n') - if stridx(l:lines, l:buffer_filename) >= 0 - let l:cflags = split(l:lines, '-') + let l:cflag_line = '' + + " Find a line matching this buffer's filename in the make output. + for l:line in a:make_output + if stridx(l:line, l:buffer_filename) >= 0 + let l:cflag_line = l:line break endif endfor let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') - return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags) + let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h') + + return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line) +endfunction + +" Given a buffer number, find the build subdirectory with compile commands +" The subdirectory is returned without the trailing / +function! ale#c#FindCompileCommands(buffer) abort + " Look above the current source file to find compile_commands.json + let l:json_file = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') + + if !empty(l:json_file) + return l:json_file + endif + + " Search in build directories if we can't find it in the project. + for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) + for l:dirname in ale#Var(a:buffer, 'c_build_dir_names') + let l:c_build_dir = l:path . s:sep . l:dirname + let l:json_file = l:c_build_dir . s:sep . 'compile_commands.json' + + if filereadable(l:json_file) + return l:json_file + endif + endfor + endfor + + return '' +endfunction + +" Cache compile_commands.json data in a Dictionary, so we don't need to read +" the same files over and over again. The key in the dictionary will include +" the last modified time of the file. +if !exists('s:compile_commands_cache') + let s:compile_commands_cache = {} +endif + +function! s:GetListFromCompileCommandsFile(compile_commands_file) abort + if empty(a:compile_commands_file) + return [] + endif + + let l:time = getftime(a:compile_commands_file) + + if l:time < 0 + return [] + endif + + let l:key = a:compile_commands_file . ':' . l:time + + if has_key(s:compile_commands_cache, l:key) + return s:compile_commands_cache[l:key] + endif + + let l:data = [] + silent! let l:data = json_decode(join(readfile(a:compile_commands_file), '')) + + if !empty(l:data) + let s:compile_commands_cache[l:key] = l:data + + return l:data + endif + + return [] +endfunction + +function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort + for l:item in a:json_list + if bufnr(l:item.file) is a:buffer + return ale#c#ParseCFlags(a:dir, l:item.command) + endif + endfor + + return '' +endfunction + +function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort + let l:dir = ale#path#Dirname(a:compile_commands_file) + let l:json_list = s:GetListFromCompileCommandsFile(a:compile_commands_file) + + return ale#c#ParseCompileCommandsFlags(a:buffer, l:dir, l:json_list) endfunction function! ale#c#GetCFlags(buffer, output) abort let l:cflags = ' ' - if g:ale_c_parse_makefile && !empty(a:output) - let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' ' + if ale#Var(a:buffer, 'c_parse_makefile') && !empty(a:output) + let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output) + endif + + if ale#Var(a:buffer, 'c_parse_compile_commands') + let l:json_file = ale#c#FindCompileCommands(a:buffer) + + if !empty(l:json_file) + let l:cflags = ale#c#FlagsFromCompileCommands(a:buffer, l:json_file) + endif endif if l:cflags is# ' ' @@ -98,8 +189,9 @@ function! ale#c#GetCFlags(buffer, output) abort endfunction function! ale#c#GetMakeCommand(buffer) abort - if g:ale_c_parse_makefile + if ale#Var(a:buffer, 'c_parse_makefile') let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + if !empty(l:makefile_path) return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' endif @@ -161,19 +253,3 @@ let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ \ 'build', \ 'bin', \]) - -" Given a buffer number, find the build subdirectory with compile commands -" The subdirectory is returned without the trailing / -function! ale#c#FindCompileCommands(buffer) abort - for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) - for l:dirname in ale#Var(a:buffer, 'c_build_dir_names') - let l:c_build_dir = l:path . s:sep . l:dirname - - if filereadable(l:c_build_dir . '/compile_commands.json') - return l:c_build_dir - endif - endfor - endfor - - return '' -endfunction diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 45da3709..2d8a6ac7 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -105,6 +105,21 @@ function! ale#path#GetAbsPath(base_directory, filename) abort return ale#path#Simplify(a:base_directory . l:sep . a:filename) endfunction +" Given a path, return the directory name for that path, with no trailing +" slashes. If the argument is empty(), return an empty string. +function! ale#path#Dirname(path) abort + if empty(a:path) + return '' + endif + + " For /foo/bar/ we need :h:h to get /foo + if a:path[-1:] is# '/' + return fnamemodify(a:path, ':h:h') + endif + + return fnamemodify(a:path, ':h') +endfunction + " Given a buffer number and a relative or absolute path, return 1 if the " two paths represent the same file on disk. function! ale#path#IsBufferPath(buffer, complex_filename) abort diff --git a/doc/ale-c.txt b/doc/ale-c.txt index cf837e5e..282dcf94 100644 --- a/doc/ale-c.txt +++ b/doc/ale-c.txt @@ -25,13 +25,28 @@ g:ale_c_build_dir *g:ale_c_build_dir* Type: |String| Default: `''` - A path to the directory containing the `compile_commands.json` file to use - with c-family linters. Usually setting this option to a non-empty string - will override the |g:ale_c_build_dir_names| option to impose a compilation - database (it can be useful if multiple builds are in multiple build - subdirectories in the project tree). - This feature is also most useful for the clang tools linters, wrapped - around LibTooling (namely clang-tidy here) + For programs that can read `compile_commands.json` files, this option can be + set to the directory containing the file for the project. ALE will try to + determine the location of `compile_commands.json` automatically, but if your + file exists in some other directory, you can set this option so ALE will + know where it is. + + This directory will be searched instead of |g:ale_c_build_dir_names|. + + +g:ale_c_parse_compile_commands *g:ale_c_parse_compile_commands* + *b:ale_c_parse_compile_commands* + Type: |Number| + Default: `0` + + If set to `1`, ALE will parse `compile_commands.json` files to automatically + determine flags for C or C++ compilers. ALE will first search for the + nearest `compile_commands.json` file, and then look for + `compile_commands.json` files in the directories for + |g:ale_c_build_dir_names|. + + If |g:ale_c_parse_makefile| or |b:ale_c_parse_makefile| is set to `1`, the + output of `make -n` will be preferred over `compile_commands.json` files. g:ale_c_parse_makefile *g:ale_c_parse_makefile* diff --git a/doc/ale-cpp.txt b/doc/ale-cpp.txt index 8bd111e4..be7e840c 100644 --- a/doc/ale-cpp.txt +++ b/doc/ale-cpp.txt @@ -10,6 +10,7 @@ The following C options also apply to some C++ linters too. * |g:ale_c_build_dir_names| * |g:ale_c_build_dir| * |g:ale_c_parse_makefile| +* |g:ale_c_parse_compile_commands| =============================================================================== diff --git a/test/command_callback/test_c_clang_command_callbacks.vader b/test/command_callback/test_c_clang_command_callbacks.vader index 87f7fd06..7d2ff0bf 100644 --- a/test/command_callback/test_c_clang_command_callbacks.vader +++ b/test/command_callback/test_c_clang_command_callbacks.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_c_parse_makefile + let g:ale_c_parse_makefile = 0 + call ale#assert#SetUpLinterTest('c', 'clang') let b:command_tail = ' -S -x c -fsyntax-only -iquote' \ . ' ' . ale#Escape(getcwd()) diff --git a/test/command_callback/test_c_gcc_command_callbacks.vader b/test/command_callback/test_c_gcc_command_callbacks.vader index 45514051..1f51c3bc 100644 --- a/test/command_callback/test_c_gcc_command_callbacks.vader +++ b/test/command_callback/test_c_gcc_command_callbacks.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_c_parse_makefile + let g:ale_c_parse_makefile = 0 + call ale#assert#SetUpLinterTest('c', 'gcc') let b:command_tail = ' -S -x c -fsyntax-only -iquote' diff --git a/test/command_callback/test_cpp_clang_command_callbacks.vader b/test/command_callback/test_cpp_clang_command_callbacks.vader index 3a5ea945..e96fd8e7 100644 --- a/test/command_callback/test_cpp_clang_command_callbacks.vader +++ b/test/command_callback/test_cpp_clang_command_callbacks.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_c_parse_makefile + let g:ale_c_parse_makefile = 0 + call ale#assert#SetUpLinterTest('cpp', 'clang') let b:command_tail = ' -S -x c++ -fsyntax-only -iquote' \ . ' ' . ale#Escape(getcwd()) diff --git a/test/command_callback/test_cpp_gcc_command_callbacks.vader b/test/command_callback/test_cpp_gcc_command_callbacks.vader index f9fad8c8..0a86df4f 100644 --- a/test/command_callback/test_cpp_gcc_command_callbacks.vader +++ b/test/command_callback/test_cpp_gcc_command_callbacks.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_c_parse_makefile + let g:ale_c_parse_makefile = 0 + call ale#assert#SetUpLinterTest('cpp', 'gcc') let b:command_tail = ' -S -x c++ -fsyntax-only -iquote' \ . ' ' . ale#Escape(getcwd()) diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader new file mode 100644 index 00000000..8bd805df --- /dev/null +++ b/test/test_c_flag_parsing.vader @@ -0,0 +1,161 @@ +Before: + Save g:ale_c_parse_makefile + + call ale#test#SetDirectory('/testplugin/test') + + let g:ale_c_parse_makefile = 1 + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The CFlags parser should be able to parse include directives): + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c']) + +Execute(The CFlags parser should be able to parse macro directives): + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -DTEST=1', + \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=1 -c file.c']) + +Execute(The CFlags parser should be able to parse macro directives with spaces): + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -DTEST=$(( 2 * 4 ))', + \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c']) + +Execute(The CFlags parser should be able to parse shell directives with spaces): + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=`date +%s` -c file.c']) + +Execute(ParseCFlags should be able to parse flags with relative paths): + AssertEqual + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Isubdir ' + \ . '-I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should be able to parse -Dgoal): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Isubdir ' + \ . '-I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should ignore -T and other arguments): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should handle paths with spaces in double quotes): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should handle paths with spaces in single quotes): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should handle paths with minuses): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' + \ . ' -I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(ParseCFlags should handle -D with minuses): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -Dmacro-with-dash' + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-Dmacro-with-dash ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' + \ . ' -I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + +Execute(FlagsFromCompileCommands should tolerate empty values): + AssertEqual '', ale#c#FlagsFromCompileCommands(bufnr(''), '') + +Execute(ParseCompileCommandsFlags should tolerate empty values): + AssertEqual '', ale#c#ParseCompileCommandsFlags(bufnr(''), '', []) + +Execute(ParseCompileCommandsFlags should parse some basic flags): + noautocmd execute 'file! ' . fnameescape('/foo/bar/xmms2-mpris/src/xmms2-mpris.c') + + AssertEqual + \ '-I/usr/include/xmms2', + \ ale#c#ParseCompileCommandsFlags(bufnr(''), '/foo/bar/xmms2-mpris', [ + \ { + \ 'directory': '/foo/bar/xmms2-mpris', + \ 'command': '/usr/bin/cc -I/usr/include/xmms2 -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o -c /foo/bar/xmms2-mpris/src/xmms2-mpris.c', + \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c' + \ }, + \ ]) diff --git a/test/test_c_import_paths.vader b/test/test_c_import_paths.vader index 0490dec6..70b25070 100644 --- a/test/test_c_import_paths.vader +++ b/test/test_c_import_paths.vader @@ -2,6 +2,7 @@ Before: " Make sure the c.vim file is loaded first. call ale#c#FindProjectRoot(bufnr('')) + Save g:ale_c_parse_makefile Save g:ale_c_gcc_options Save g:ale_c_clang_options Save g:ale_cpp_gcc_options @@ -19,6 +20,7 @@ Before: call ale#test#SetDirectory('/testplugin/test') + let g:ale_c_parse_makefile = 0 let g:ale_c_gcc_options = '' let g:ale_c_clang_options = '' let g:ale_cpp_gcc_options = '' diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader deleted file mode 100644 index 7c2fc21e..00000000 --- a/test/test_c_parse_makefile.vader +++ /dev/null @@ -1,184 +0,0 @@ -Before: - Save g:ale_c_parse_makefile - Save g:ale_c_gcc_options - Save g:ale_c_clang_options - Save g:ale_cpp_gcc_options - Save g:ale_cpp_clang_options - - call ale#test#SetDirectory('/testplugin/test') - - let g:ale_c_parse_makefile=1 - let g:ale_c_gcc_options = '' - let g:ale_c_clang_options = '' - let g:ale_cpp_gcc_options = '' - let g:ale_cpp_clang_options = '' - -After: - Restore - - call ale#test#RestoreDirectory() - call ale#linter#Reset() - -Execute(The CFlags parser should be able to parse include directives): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))] - \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -c file.c') - -Execute(The CFlags parser should be able to parse macro directives): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ '-DTEST=1'] - \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=1 -c file.c') - -Execute(The CFlags parser should be able to parse macro directives with spaces): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ '-DTEST=$(( 2 * 4 ))'] - \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c') - -Execute(The CFlags parser should be able to parse shell directives with spaces): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=`date +%s` -c file.c') - -Execute(The CFlagsToList parser should be able to parse multiple cflags): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Isubdir -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #2): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Isubdir ' . - \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #3): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Isubdir ' . - \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #4): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #5): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #6): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #7): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-I''dir with spaces''' . ' -Idir-with-dash' . - \ ' -I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) - -Execute(The CFlagsToList parser should be able to parse multiple cflags #8): - runtime! ale_linters/c/gcc.vim - - call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') - - AssertEqual - \ ['-Dgoal=9', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), - \ '-Dmacro-with-dash', - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')), - \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ '-DTEST=`date +%s`'] - \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-Dmacro-with-dash ' . - \ '-I''dir with spaces''' . ' -Idir-with-dash' . - \ ' -I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c', '-')) diff --git a/test/test_path_dirname.vader b/test/test_path_dirname.vader new file mode 100644 index 00000000..79b78d29 --- /dev/null +++ b/test/test_path_dirname.vader @@ -0,0 +1,8 @@ +Execute(ale#path#Dirname should return empty strings should be returned for empty values): + AssertEqual '', ale#path#Dirname('') + AssertEqual '', ale#path#Dirname(0) + AssertEqual '', ale#path#Dirname(v:null) + +Execute(ale#path#Dirname should return the dirname of paths): + AssertEqual '/foo', ale#path#Dirname('/foo/bar') + AssertEqual '/foo', ale#path#Dirname('/foo/bar/')