diff options
44 files changed, 658 insertions, 185 deletions
diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim index 72b293c0..eecab6ef 100644 --- a/ale_linters/asm/gcc.vim +++ b/ale_linters/asm/gcc.vim @@ -5,7 +5,10 @@ call ale#Set('asm_gcc_executable', 'gcc') call ale#Set('asm_gcc_options', '-Wall') function! ale_linters#asm#gcc#GetCommand(buffer) abort - return '%e -x assembler -fsyntax-only ' + " `-o /dev/null` or `-o null` is needed to catch all errors, + " -fsyntax-only doesn't catch everything. + return '%e -x assembler' + \ . ' -o ' . g:ale#util#nul_file \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) \ . ' ' . ale#Var(a:buffer, 'asm_gcc_options') . ' -' endfunction diff --git a/ale_linters/c/clangd.vim b/ale_linters/c/clangd.vim index 918eadcc..79b600fa 100644 --- a/ale_linters/c/clangd.vim +++ b/ale_linters/c/clangd.vim @@ -4,12 +4,6 @@ call ale#Set('c_clangd_executable', 'clangd') call ale#Set('c_clangd_options', '') -function! ale_linters#c#clangd#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' -endfunction - function! ale_linters#c#clangd#GetCommand(buffer) abort return '%e' . ale#Pad(ale#Var(a:buffer, 'c_clangd_options')) endfunction @@ -19,5 +13,5 @@ call ale#linter#Define('c', { \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'c_clangd_executable')}, \ 'command': function('ale_linters#c#clangd#GetCommand'), -\ 'project_root': function('ale_linters#c#clangd#GetProjectRoot'), +\ 'project_root': function('ale#c#FindProjectRoot'), \}) diff --git a/ale_linters/c/cppcheck.vim b/ale_linters/c/cppcheck.vim index 851f9f11..b2ded90f 100644 --- a/ale_linters/c/cppcheck.vim +++ b/ale_linters/c/cppcheck.vim @@ -9,19 +9,16 @@ function! ale_linters#c#cppcheck#GetCommand(buffer) abort " " If we find it, we'll `cd` to where the compile_commands.json file is, " then use the file to set up import paths, etc. - let l:compile_commmands_path = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - let l:cd_command = !empty(l:compile_commmands_path) - \ ? ale#path#CdString(fnamemodify(l:compile_commmands_path, ':h')) - \ : '' - let l:compile_commands_option = !empty(l:compile_commmands_path) - \ ? '--project=compile_commands.json ' + let [l:dir, l:json_path] = ale#c#FindCompileCommands(a:buffer) + let l:cd_command = !empty(l:dir) ? ale#path#CdString(l:dir) : '' + let l:compile_commands_option = !empty(l:json_path) + \ ? '--project=' . ale#Escape(l:json_path[len(l:dir) + 1: ]) \ : '' return l:cd_command - \ . '%e -q --language=c ' - \ . l:compile_commands_option - \ . ale#Var(a:buffer, 'c_cppcheck_options') + \ . '%e -q --language=c' + \ . ale#Pad(l:compile_commands_option) + \ . ale#Pad(ale#Var(a:buffer, 'c_cppcheck_options')) \ . ' %t' endfunction diff --git a/ale_linters/c/cquery.vim b/ale_linters/c/cquery.vim index d2be9cd9..ff0f34af 100644 --- a/ale_linters/c/cquery.vim +++ b/ale_linters/c/cquery.vim @@ -5,13 +5,15 @@ call ale#Set('c_cquery_executable', 'cquery') call ale#Set('c_cquery_cache_directory', expand('~/.cache/cquery')) function! ale_linters#c#cquery#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') + " Try to find cquery configuration files first. + let l:config = ale#path#FindNearestFile(a:buffer, '.cquery') - if empty(l:project_root) - let l:project_root = ale#path#FindNearestFile(a:buffer, '.cquery') + if !empty(l:config) + return fnamemodify(l:config, ':h') endif - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' + " Fall back on default project root detection. + return ale#c#FindProjectRoot(a:buffer) endfunction function! ale_linters#c#cquery#GetInitializationOptions(buffer) abort diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index d965965d..1df1018e 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -9,7 +9,11 @@ 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 '%e -S -x c -fsyntax-only' + " + " `-o /dev/null` or `-o null` is needed to catch all errors, + " -fsyntax-only doesn't catch everything. + return '%e -S -x c' + \ . ' -o ' . g:ale#util#nul_file \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) \ . ale#Pad(l:cflags) \ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -' diff --git a/ale_linters/cpp/clangcheck.vim b/ale_linters/cpp/clangcheck.vim index b511a413..7d32a57c 100644 --- a/ale_linters/cpp/clangcheck.vim +++ b/ale_linters/cpp/clangcheck.vim @@ -12,7 +12,8 @@ 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#path#Dirname(ale#c#FindCompileCommands(a:buffer)) + let [l:root, l:json_file] = ale#c#FindCompileCommands(a:buffer) + let l:build_dir = ale#path#Dirname(l:json_file) endif " The extra arguments in the command are used to prevent .plist files from diff --git a/ale_linters/cpp/clangd.vim b/ale_linters/cpp/clangd.vim index 4a8ff4f6..fab605f4 100644 --- a/ale_linters/cpp/clangd.vim +++ b/ale_linters/cpp/clangd.vim @@ -4,12 +4,6 @@ call ale#Set('cpp_clangd_executable', 'clangd') call ale#Set('cpp_clangd_options', '') -function! ale_linters#cpp#clangd#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' -endfunction - function! ale_linters#cpp#clangd#GetCommand(buffer) abort return '%e' . ale#Pad(ale#Var(a:buffer, 'cpp_clangd_options')) endfunction @@ -19,5 +13,5 @@ call ale#linter#Define('cpp', { \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'cpp_clangd_executable')}, \ 'command': function('ale_linters#cpp#clangd#GetCommand'), -\ 'project_root': function('ale_linters#cpp#clangd#GetProjectRoot'), +\ 'project_root': function('ale#c#FindProjectRoot'), \}) diff --git a/ale_linters/cpp/cppcheck.vim b/ale_linters/cpp/cppcheck.vim index 5173d698..dae0774e 100644 --- a/ale_linters/cpp/cppcheck.vim +++ b/ale_linters/cpp/cppcheck.vim @@ -9,19 +9,16 @@ function! ale_linters#cpp#cppcheck#GetCommand(buffer) abort " " If we find it, we'll `cd` to where the compile_commands.json file is, " then use the file to set up import paths, etc. - let l:compile_commmands_path = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - let l:cd_command = !empty(l:compile_commmands_path) - \ ? ale#path#CdString(fnamemodify(l:compile_commmands_path, ':h')) - \ : '' - let l:compile_commands_option = !empty(l:compile_commmands_path) - \ ? '--project=compile_commands.json ' + let [l:dir, l:json_path] = ale#c#FindCompileCommands(a:buffer) + let l:cd_command = !empty(l:dir) ? ale#path#CdString(l:dir) : '' + let l:compile_commands_option = !empty(l:json_path) + \ ? '--project=' . ale#Escape(l:json_path[len(l:dir) + 1: ]) \ : '' return l:cd_command - \ . '%e -q --language=c++ ' - \ . l:compile_commands_option - \ . ale#Var(a:buffer, 'cpp_cppcheck_options') + \ . '%e -q --language=c++' + \ . ale#Pad(l:compile_commands_option) + \ . ale#Pad(ale#Var(a:buffer, 'cpp_cppcheck_options')) \ . ' %t' endfunction diff --git a/ale_linters/cpp/cquery.vim b/ale_linters/cpp/cquery.vim index 0dd9f6ad..2971cdcb 100644 --- a/ale_linters/cpp/cquery.vim +++ b/ale_linters/cpp/cquery.vim @@ -5,13 +5,15 @@ call ale#Set('cpp_cquery_executable', 'cquery') call ale#Set('cpp_cquery_cache_directory', expand('~/.cache/cquery')) function! ale_linters#cpp#cquery#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') + " Try to find cquery configuration files first. + let l:config = ale#path#FindNearestFile(a:buffer, '.cquery') - if empty(l:project_root) - let l:project_root = ale#path#FindNearestFile(a:buffer, '.cquery') + if !empty(l:config) + return fnamemodify(l:config, ':h') endif - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' + " Fall back on default project root detection. + return ale#c#FindProjectRoot(a:buffer) endfunction function! ale_linters#cpp#cquery#GetInitializationOptions(buffer) abort diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim index c427020b..108d6d70 100644 --- a/ale_linters/cpp/gcc.vim +++ b/ale_linters/cpp/gcc.vim @@ -9,7 +9,11 @@ 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 '%e -S -x c++ -fsyntax-only' + " + " `-o /dev/null` or `-o null` is needed to catch all errors, + " -fsyntax-only doesn't catch everything. + return '%e -S -x c++' + \ . ' -o ' . g:ale#util#nul_file \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) \ . ale#Pad(l:cflags) \ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -' diff --git a/ale_linters/erlang/dialyzer.vim b/ale_linters/erlang/dialyzer.vim new file mode 100644 index 00000000..7af64c4f --- /dev/null +++ b/ale_linters/erlang/dialyzer.vim @@ -0,0 +1,93 @@ +" Author: Autoine Gagne - https://github.com/AntoineGagne +" Description: Define a checker that runs dialyzer on Erlang files. + +let g:ale_erlang_dialyzer_executable = +\ get(g:, 'ale_erlang_dialyzer_executable', 'dialyzer') +let g:ale_erlang_dialyzer_plt_file = +\ get(g:, 'ale_erlang_dialyzer_plt_file', '') +let g:ale_erlang_dialyzer_rebar3_profile = +\ get(g:, 'ale_erlang_dialyzer_rebar3_profile', 'default') + +function! ale_linters#erlang#dialyzer#GetRebar3Profile(buffer) abort + return ale#Var(a:buffer, 'erlang_dialyzer_rebar3_profile') +endfunction + +function! ale_linters#erlang#dialyzer#FindPlt(buffer) abort + let l:plt_file = '' + let l:rebar3_profile = ale_linters#erlang#dialyzer#GetRebar3Profile(a:buffer) + let l:plt_file_directory = ale#path#FindNearestDirectory(a:buffer, '_build' . l:rebar3_profile) + + if !empty(l:plt_file_directory) + let l:plt_file = split(globpath(l:plt_file_directory, '/*_plt'), '\n') + endif + + if !empty(l:plt_file) + return l:plt_file[0] + endif + + if !empty($REBAR_PLT_DIR) + return expand('$REBAR_PLT_DIR/dialyzer/plt') + endif + + return expand('$HOME/.dialyzer_plt') +endfunction + +function! ale_linters#erlang#dialyzer#GetPlt(buffer) abort + let l:plt_file = ale#Var(a:buffer, 'erlang_dialyzer_plt_file') + + if !empty(l:plt_file) + return l:plt_file + endif + + return ale_linters#erlang#dialyzer#FindPlt(a:buffer) +endfunction + +function! ale_linters#erlang#dialyzer#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'erlang_dialyzer_executable') +endfunction + +function! ale_linters#erlang#dialyzer#GetCommand(buffer) abort + let l:command = ale#Escape(ale_linters#erlang#dialyzer#GetExecutable(a:buffer)) + \ . ' -n' + \ . ' --plt ' . ale#Escape(ale_linters#erlang#dialyzer#GetPlt(a:buffer)) + \ . ' -Wunmatched_returns' + \ . ' -Werror_handling' + \ . ' -Wrace_conditions' + \ . ' -Wunderspecs' + \ . ' %s' + + return l:command +endfunction + +function! ale_linters#erlang#dialyzer#Handle(buffer, lines) abort + " Match patterns like the following: + " + " erl_tidy_prv_fmt.erl:3: Callback info about the provider behaviour is not available + let l:pattern = '^\S\+:\(\d\+\): \(.\+\)$' + let l:output = [] + + for l:line in a:lines + let l:match = matchlist(l:line, l:pattern) + + if len(l:match) != 0 + let l:code = l:match[2] + + call add(l:output, { + \ 'lnum': str2nr(l:match[1]), + \ 'lcol': 0, + \ 'text': l:code, + \ 'type': 'W' + \}) + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('erlang', { +\ 'name': 'dialyzer', +\ 'executable': function('ale_linters#erlang#dialyzer#GetExecutable'), +\ 'command': function('ale_linters#erlang#dialyzer#GetCommand'), +\ 'callback': function('ale_linters#erlang#dialyzer#Handle'), +\ 'lint_file': 1 +\}) diff --git a/ale_linters/objc/clangd.vim b/ale_linters/objc/clangd.vim index ab52fec3..318d85b5 100644 --- a/ale_linters/objc/clangd.vim +++ b/ale_linters/objc/clangd.vim @@ -4,12 +4,6 @@ call ale#Set('objc_clangd_executable', 'clangd') call ale#Set('objc_clangd_options', '') -function! ale_linters#objc#clangd#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' -endfunction - function! ale_linters#objc#clangd#GetCommand(buffer) abort return '%e' . ale#Pad(ale#Var(a:buffer, 'objc_clangd_options')) endfunction @@ -19,5 +13,5 @@ call ale#linter#Define('objc', { \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'objc_clangd_executable')}, \ 'command': function('ale_linters#objc#clangd#GetCommand'), -\ 'project_root': function('ale_linters#objc#clangd#GetProjectRoot'), +\ 'project_root': function('ale#c#FindProjectRoot'), \}) diff --git a/ale_linters/objcpp/clangd.vim b/ale_linters/objcpp/clangd.vim index 3991d2ac..29455325 100644 --- a/ale_linters/objcpp/clangd.vim +++ b/ale_linters/objcpp/clangd.vim @@ -4,12 +4,6 @@ call ale#Set('objcpp_clangd_executable', 'clangd') call ale#Set('objcpp_clangd_options', '') -function! ale_linters#objcpp#clangd#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') - - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' -endfunction - function! ale_linters#objcpp#clangd#GetCommand(buffer) abort return '%e' . ale#Pad(ale#Var(a:buffer, 'objcpp_clangd_options')) endfunction @@ -19,5 +13,5 @@ call ale#linter#Define('objcpp', { \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'objcpp_clangd_executable')}, \ 'command': function('ale_linters#objcpp#clangd#GetCommand'), -\ 'project_root': function('ale_linters#objcpp#clangd#GetProjectRoot'), +\ 'project_root': function('ale#c#FindProjectRoot'), \}) diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index a9289e22..2d2083da 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -23,27 +23,9 @@ function! ale#c#GetBuildDirectory(buffer) abort return l:build_dir endif - return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) -endfunction - - -function! ale#c#FindProjectRoot(buffer) abort - for l:project_filename in g:__ale_c_project_filenames - let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename) + let [l:root, l:json_file] = ale#c#FindCompileCommands(a:buffer) - if !empty(l:full_path) - let l:path = fnamemodify(l:full_path, ':h') - - " Correct .git path detection. - if fnamemodify(l:path, ':t') is# '.git' - let l:path = fnamemodify(l:path, ':h') - endif - - return l:path - endif - endfor - - return '' + return ale#path#Dirname(l:json_file) endfunction function! ale#c#AreSpecialCharsBalanced(option) abort @@ -120,7 +102,7 @@ endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort if !g:ale_c_parse_makefile - return '' + return v:null endif let l:buffer_filename = expand('#' . a:buffer . ':t') @@ -140,14 +122,17 @@ function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort 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 / +" Given a buffer number, find the project directory containing +" compile_commands.json, and the path to the compile_commands.json file. +" +" If compile_commands.json cannot be found, two empty strings will be +" returned. 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 + return [fnamemodify(l:json_file, ':h'), l:json_file] endif " Search in build directories if we can't find it in the project. @@ -157,12 +142,42 @@ function! ale#c#FindCompileCommands(buffer) abort let l:json_file = l:c_build_dir . s:sep . 'compile_commands.json' if filereadable(l:json_file) - return l:json_file + return [l:path, l:json_file] endif endfor endfor - return '' + return ['', ''] +endfunction + +" Find the project root for C/C++ projects. +" +" The location of compile_commands.json will be used to find project roots. +" +" If compile_commands.json cannot be found, other common configuration files +" will be used to detect the project root. +function! ale#c#FindProjectRoot(buffer) abort + let [l:root, l:json_file] = ale#c#FindCompileCommands(a:buffer) + + " Fall back on detecting the project root based on other filenames. + if empty(l:root) + for l:project_filename in g:__ale_c_project_filenames + let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename) + + if !empty(l:full_path) + let l:path = fnamemodify(l:full_path, ':h') + + " Correct .git path detection. + if fnamemodify(l:path, ':t') is# '.git' + let l:path = fnamemodify(l:path, ':h') + endif + + return l:path + endif + endfor + endif + + return l:root endfunction " Cache compile_commands.json data in a Dictionary, so we don't need to read @@ -194,10 +209,14 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort let l:raw_data = [] silent! let l:raw_data = json_decode(join(readfile(a:compile_commands_file), '')) + if type(l:raw_data) isnot v:t_list + let l:raw_data = [] + endif + let l:file_lookup = {} let l:dir_lookup = {} - for l:entry in l:raw_data + for l:entry in (type(l:raw_data) is v:t_list ? l:raw_data : []) let l:basename = tolower(fnamemodify(l:entry.file, ':t')) let l:file_lookup[l:basename] = get(l:file_lookup, l:basename, []) + [l:entry] @@ -274,25 +293,25 @@ function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort endfunction function! ale#c#GetCFlags(buffer, output) abort - let l:cflags = ' ' + let l:cflags = v:null 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) + let [l:root, 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# ' ' + if l:cflags is v:null let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) endif - return l:cflags + return l:cflags isnot v:null ? l:cflags : '' endfunction function! ale#c#GetMakeCommand(buffer) abort diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index 49e650c7..68b70d6a 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -2,47 +2,59 @@ call ale#Set('fix_on_save_ignore', {}) " Apply fixes queued up for buffers which may be hidden. " Vim doesn't let you modify hidden buffers. -function! ale#fix#ApplyQueuedFixes() abort - let l:buffer = bufnr('') - let l:data = get(g:ale_fix_buffer_data, l:buffer, {'done': 0}) +function! ale#fix#ApplyQueuedFixes(buffer) abort + let l:data = get(g:ale_fix_buffer_data, a:buffer, {'done': 0}) - if !l:data.done + if !l:data.done || (!exists('*deletebufline') && a:buffer isnot bufnr('')) return endif - call remove(g:ale_fix_buffer_data, l:buffer) + call remove(g:ale_fix_buffer_data, a:buffer) if l:data.changes_made - let l:start_line = len(l:data.output) + 1 - let l:end_line = len(l:data.lines_before) - - if l:end_line >= l:start_line - let l:save = winsaveview() - silent execute l:start_line . ',' . l:end_line . 'd_' - call winrestview(l:save) - endif - " If the file is in DOS mode, we have to remove carriage returns from " the ends of lines before calling setline(), or we will see them " twice. - let l:lines_to_set = getbufvar(l:buffer, '&fileformat') is# 'dos' + let l:new_lines = getbufvar(a:buffer, '&fileformat') is# 'dos' \ ? map(copy(l:data.output), 'substitute(v:val, ''\r\+$'', '''', '''')') \ : l:data.output + let l:first_line_to_remove = len(l:new_lines) + 1 + + " Use a Vim API for setting lines in other buffers, if available. + if exists('*deletebufline') + call setbufline(a:buffer, 1, l:new_lines) + call deletebufline(a:buffer, l:first_line_to_remove, '$') + " Fall back on setting lines the old way, for the current buffer. + else + let l:old_line_length = len(l:data.lines_before) + + if l:old_line_length >= l:first_line_to_remove + let l:save = winsaveview() + silent execute + \ l:first_line_to_remove . ',' . l:old_line_length . 'd_' + call winrestview(l:save) + endif - call setline(1, l:lines_to_set) + call setline(1, l:new_lines) + endif if l:data.should_save - if empty(&buftype) - noautocmd :w! + if a:buffer is bufnr('') + if empty(&buftype) + noautocmd :w! + else + set nomodified + endif else - set nomodified + call writefile(l:new_lines, expand(a:buffer . ':p')) " no-custom-checks + call setbufvar(a:buffer, '&modified', 0) endif endif endif if l:data.should_save let l:should_lint = g:ale_fix_on_save - \ && ale#Var(l:buffer, 'lint_on_save') + \ && ale#Var(a:buffer, 'lint_on_save') else let l:should_lint = l:data.changes_made endif @@ -53,7 +65,7 @@ function! ale#fix#ApplyQueuedFixes() abort " fixing problems. if g:ale_enabled \&& l:should_lint - \&& !ale#events#QuitRecently(l:buffer) + \&& !ale#events#QuitRecently(a:buffer) call ale#Queue(0, l:data.should_save ? 'lint_file' : '') endif endfunction @@ -84,7 +96,7 @@ function! ale#fix#ApplyFixes(buffer, output) abort " We can only change the lines of a buffer which is currently open, " so try and apply the fixes to the current buffer. - call ale#fix#ApplyQueuedFixes() + call ale#fix#ApplyQueuedFixes(a:buffer) endfunction function! s:HandleExit(job_info, buffer, job_output, data) abort @@ -400,5 +412,4 @@ endfunction " Set up an autocmd command to try and apply buffer fixes when available. augroup ALEBufferFixGroup autocmd! - autocmd BufEnter * call ale#fix#ApplyQueuedFixes() -augroup END + autocmd BufEnter * call ale#fix#ApplyQueuedFixes(str2nr(expand('<abuf>'))) diff --git a/autoload/ale/handlers/ccls.vim b/autoload/ale/handlers/ccls.vim index 29dd6aed..1e2aa318 100644 --- a/autoload/ale/handlers/ccls.vim +++ b/autoload/ale/handlers/ccls.vim @@ -3,15 +3,17 @@ scriptencoding utf-8 " Description: Utilities for ccls function! ale#handlers#ccls#GetProjectRoot(buffer) abort - let l:project_root = ale#path#FindNearestFile(a:buffer, '.ccls-root') + " Try to find ccls configuration files first. + let l:config = ale#path#FindNearestFile(a:buffer, '.ccls-root') - if empty(l:project_root) - let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') + if empty(l:config) + let l:config = ale#path#FindNearestFile(a:buffer, '.ccls') endif - if empty(l:project_root) - let l:project_root = ale#path#FindNearestFile(a:buffer, '.ccls') + if !empty(l:config) + return fnamemodify(l:config, ':h') endif - return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : '' + " Fall back on default project root detection. + return ale#c#FindProjectRoot(a:buffer) endfunction diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim index 72d639da..ec16b977 100644 --- a/autoload/ale/handlers/gcc.vim +++ b/autoload/ale/handlers/gcc.vim @@ -11,6 +11,7 @@ let s:pragma_error = '#pragma once in main file' " <stdin>:10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' +let s:inline_pattern = '\v inlined from .* at \<stdin\>:(\d+):(\d+):$' function! s:IsHeaderFile(filename) abort return a:filename =~? '\v\.(h|hpp)$' @@ -25,6 +26,28 @@ function! s:RemoveUnicodeQuotes(text) abort return l:text endfunction +function! s:ParseInlinedFunctionProblems(buffer, lines) abort + let l:output = [] + let l:pos_match = [] + + for l:line in a:lines + let l:match = matchlist(l:line, s:pattern) + + if !empty(l:match) && !empty(l:pos_match) + call add(l:output, { + \ 'lnum': str2nr(l:pos_match[1]), + \ 'col': str2nr(l:pos_match[2]), + \ 'type': (l:match[4] is# 'error' || l:match[4] is# 'fatal error') ? 'E' : 'W', + \ 'text': s:RemoveUnicodeQuotes(l:match[5]), + \}) + endif + + let l:pos_match = matchlist(l:line, s:inline_pattern) + endfor + + return l:output +endfunction + " Report problems inside of header files just for gcc and clang function! s:ParseProblemsInHeaders(buffer, lines) abort let l:output = [] @@ -129,6 +152,7 @@ endfunction function! ale#handlers#gcc#HandleGCCFormatWithIncludes(buffer, lines) abort let l:output = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines) + call extend(l:output, s:ParseInlinedFunctionProblems(a:buffer, a:lines)) call extend(l:output, s:ParseProblemsInHeaders(a:buffer, a:lines)) return l:output diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 986e4c1b..017096cd 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -321,7 +321,69 @@ endfunction function! s:SendInitMessage(conn) abort let [l:init_id, l:init_data] = ale#lsp#CreateMessageData( - \ ale#lsp#message#Initialize(a:conn.root, a:conn.init_options), + \ ale#lsp#message#Initialize( + \ a:conn.root, + \ a:conn.init_options, + \ { + \ 'workspace': { + \ 'applyEdit': v:false, + \ 'didChangeConfiguration': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'symbol': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'workspaceFolders': v:false, + \ 'configuration': v:false, + \ }, + \ 'textDocument': { + \ 'synchronization': { + \ 'dynamicRegistration': v:false, + \ 'willSave': v:false, + \ 'willSaveWaitUntil': v:false, + \ 'didSave': v:true, + \ }, + \ 'completion': { + \ 'dynamicRegistration': v:false, + \ 'completionItem': { + \ 'snippetSupport': v:false, + \ 'commitCharactersSupport': v:false, + \ 'documentationFormat': ['plaintext'], + \ 'deprecatedSupport': v:false, + \ 'preselectSupport': v:false, + \ }, + \ 'contextSupport': v:false, + \ }, + \ 'hover': { + \ 'dynamicRegistration': v:false, + \ 'contentFormat': ['plaintext'], + \ }, + \ 'references': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'documentSymbol': { + \ 'dynamicRegistration': v:false, + \ 'hierarchicalDocumentSymbolSupport': v:false, + \ }, + \ 'definition': { + \ 'dynamicRegistration': v:false, + \ 'linkSupport': v:false, + \ }, + \ 'typeDefinition': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'publishDiagnostics': { + \ 'relatedInformation': v:true, + \ }, + \ 'codeAction': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'rename': { + \ 'dynamicRegistration': v:false, + \ }, + \ }, + \ }, + \ ), \) let a:conn.init_request_id = l:init_id call s:SendMessageData(a:conn, l:init_data) diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim index 4ad94c4b..b6b14a22 100644 --- a/autoload/ale/lsp/message.vim +++ b/autoload/ale/lsp/message.vim @@ -28,14 +28,13 @@ function! ale#lsp#message#GetNextVersionID() abort return l:id endfunction -function! ale#lsp#message#Initialize(root_path, initialization_options) abort - " TODO: Define needed capabilities. +function! ale#lsp#message#Initialize(root_path, options, capabilities) abort " NOTE: rootPath is deprecated in favour of rootUri return [0, 'initialize', { \ 'processId': getpid(), \ 'rootPath': a:root_path, - \ 'capabilities': {}, - \ 'initializationOptions': a:initialization_options, + \ 'capabilities': a:capabilities, + \ 'initializationOptions': a:options, \ 'rootUri': ale#path#ToURI(a:root_path), \}] endfunction diff --git a/doc/ale-erlang.txt b/doc/ale-erlang.txt index ad3c1e5a..59993a99 100644 --- a/doc/ale-erlang.txt +++ b/doc/ale-erlang.txt @@ -3,6 +3,35 @@ ALE Erlang Integration *ale-erlang-options* =============================================================================== +dialyzer *ale-erlang-dialyzer* + +g:ale_erlang_dialyzer_executable *g:ale_erlang_dialyzer_executable* + *b:ale_erlang_dialyzer_executable* + Type: |String| + Default: `'dialyzer'` + + This variable can be changed to specify the dialyzer executable. + + +g:ale_erlang_dialyzer_plt_file *g:ale_erlang_dialyzer_plt_file* + *b:ale_erlang_dialyzer_plt_file* + Type: |String| + + This variable can be changed to specify the path to the PLT file. By + default, it will search for the PLT file inside the `_build` directory. If + there isn't one, it will fallback to the path `$REBAR_PLT_DIR/dialyzer/plt`. + Otherwise, it will default to `$HOME/.dialyzer_plt`. + + +g:ale_erlang_dialyzer_rebar3_profile *g:ale_erlang_dialyzer_rebar3_profile* + *b:ale_erlang_dialyzer_rebar3_profile* + Type: |String| + Default: `'default'` + + This variable can be changed to specify the profile that is used to + run dialyzer with rebar3. + +------------------------------------------------------------------------------- erlc *ale-erlang-erlc* g:ale_erlang_erlc_options *g:ale_erlang_erlc_options* diff --git a/doc/ale.txt b/doc/ale.txt index 7e6ac443..ac3661fc 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1989,6 +1989,7 @@ documented in additional help files. elm-lsp...............................|ale-elm-elm-lsp| elm-make..............................|ale-elm-elm-make| erlang..................................|ale-erlang-options| + dialyzer..............................|ale-erlang-dialyzer| erlc..................................|ale-erlang-erlc| syntaxerl.............................|ale-erlang-syntaxerl| eruby...................................|ale-eruby-options| diff --git a/test/command_callback/cquery_paths/with_compile_commands_json/compile_commands.json b/test/command_callback/cppcheck_paths/with_build_dir/build/compile_commands.json index e69de29b..e69de29b 100644 --- a/test/command_callback/cquery_paths/with_compile_commands_json/compile_commands.json +++ b/test/command_callback/cppcheck_paths/with_build_dir/build/compile_commands.json diff --git a/test/test_c_projects/build/compile_commands.json b/test/command_callback/cquery_paths/build/compile_commands.json index e69de29b..e69de29b 100644 --- a/test/test_c_projects/build/compile_commands.json +++ b/test/command_callback/cquery_paths/build/compile_commands.json diff --git a/test/command_callback/test_asm_gcc_command_callbacks.vader b/test/command_callback/test_asm_gcc_command_callbacks.vader index 5ad31186..42606ec0 100644 --- a/test/command_callback/test_asm_gcc_command_callbacks.vader +++ b/test/command_callback/test_asm_gcc_command_callbacks.vader @@ -1,8 +1,9 @@ Before: call ale#assert#SetUpLinterTest('asm', 'gcc') call ale#test#SetFilename('test.cpp') - let b:command_tail = ' -x assembler -fsyntax-only -iquote' - \ . ' ' . ale#Escape(g:dir) + let b:command_tail = ' -x assembler' + \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') + \ . '-iquote ' . ale#Escape(g:dir) \ . ' -Wall -' After: diff --git a/test/command_callback/test_c_ccls_command_callbacks.vader b/test/command_callback/test_c_ccls_command_callbacks.vader index b8f3ab5b..43fdb366 100644 --- a/test/command_callback/test_c_ccls_command_callbacks.vader +++ b/test/command_callback/test_c_ccls_command_callbacks.vader @@ -8,6 +8,8 @@ After: call ale#assert#TearDownLinterTest() Execute(The project root should be detected correctly using compile_commands.json file): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.c') @@ -15,6 +17,8 @@ Execute(The project root should be detected correctly using compile_commands.jso AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') Execute(The project root should be detected correctly using .ccls file): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls/dummy.c') @@ -22,6 +26,8 @@ Execute(The project root should be detected correctly using .ccls file): AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_ccls') Execute(The project root should be detected correctly using .ccls-root file): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls-root/dummy.c') diff --git a/test/command_callback/test_c_clangd_command_callbacks.vader b/test/command_callback/test_c_clangd_command_callbacks.vader index c8c10b67..dc52097d 100644 --- a/test/command_callback/test_c_clangd_command_callbacks.vader +++ b/test/command_callback/test_c_clangd_command_callbacks.vader @@ -14,6 +14,8 @@ Execute(The default executable should be correct): AssertLinter 'clangd', ale#Escape('clangd') Execute(The project root should be detected correctly): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' call ale#test#SetFilename('clangd_paths/dummy.c') diff --git a/test/command_callback/test_c_cppcheck_command_callbacks.vader b/test/command_callback/test_c_cppcheck_command_callbacks.vader index 3ae4bdbe..8e11ef2d 100644 --- a/test/command_callback/test_c_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_c_cppcheck_command_callbacks.vader @@ -21,4 +21,16 @@ Execute(cppcheck for C++ should detect compile_commands.json files): AssertLinter 'cppcheck', \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') - \ . ' -q --language=c --project=compile_commands.json --enable=style %t' + \ . ' -q --language=c' + \ . ' --project=' . ale#Escape('compile_commands.json') + \ . ' --enable=style %t' + +Execute(cppcheck for C++ should detect compile_commands.json files in build directories): + call ale#test#SetFilename('cppcheck_paths/with_build_dir/foo.cpp') + + AssertLinter 'cppcheck', + \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/with_build_dir')) + \ . ale#Escape('cppcheck') + \ . ' -q --language=c' + \ . ' --project=' . ale#Escape(ale#path#Simplify('build/compile_commands.json')) + \ . ' --enable=style %t' diff --git a/test/command_callback/test_c_cquery_command_callbacks.vader b/test/command_callback/test_c_cquery_command_callbacks.vader index 13b7a567..01356bdf 100644 --- a/test/command_callback/test_c_cquery_command_callbacks.vader +++ b/test/command_callback/test_c_cquery_command_callbacks.vader @@ -5,13 +5,17 @@ After: call ale#assert#TearDownLinterTest() Execute(The project root should be detected correctly using compile_commands.json file): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' - call ale#test#SetFilename('cquery_paths/with_compile_commands_json/dummy.c') + call ale#test#SetFilename('cquery_paths/dummy.c') - AssertLSPProject ale#path#Simplify(g:dir . '/cquery_paths/with_compile_commands_json') + AssertLSPProject ale#path#Simplify(g:dir . '/cquery_paths') Execute(The project root should be detected correctly using .cquery file): + call ale#test#SetFilename(tempname() . '/dummy.c') + AssertLSPProject '' call ale#test#SetFilename('cquery_paths/with_cquery/dummy.c') diff --git a/test/command_callback/test_c_gcc_command_callbacks.vader b/test/command_callback/test_c_gcc_command_callbacks.vader index c5b316c8..2dbb8b7c 100644 --- a/test/command_callback/test_c_gcc_command_callbacks.vader +++ b/test/command_callback/test_c_gcc_command_callbacks.vader @@ -4,8 +4,9 @@ Before: call ale#assert#SetUpLinterTest('c', 'gcc') - let b:command_tail = ' -S -x c -fsyntax-only -iquote' - \ . ' ' . ale#Escape(getcwd()) + let b:command_tail = ' -S -x c' + \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(getcwd()) \ . ' -std=c11 -Wall -' After: diff --git a/test/command_callback/test_c_import_paths.vader b/test/command_callback/test_c_import_paths.vader index 0b5fac40..6c616d89 100644 --- a/test/command_callback/test_c_import_paths.vader +++ b/test/command_callback/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_compile_commands Save g:ale_c_parse_makefile Save g:__ale_c_project_filenames @@ -14,9 +15,12 @@ Before: \ 'v:val isnot# ''.git/HEAD''' \) + let g:ale_c_parse_compile_commands = 0 let g:ale_c_parse_makefile = 0 After: + Restore + unlet! g:original_project_filenames call ale#assert#TearDownLinterTest() @@ -28,7 +32,7 @@ Execute(The C GCC handler should include 'include' directories for projects with AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c -fsyntax-only' + \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -40,8 +44,8 @@ Execute(The C GCC handler should include 'include' directories for projects with AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) + \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include')) \ . ' -' @@ -52,8 +56,8 @@ Execute(The C GCC handler should include root directories for projects with .h f AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -64,8 +68,8 @@ Execute(The C GCC handler should include root directories for projects with .hpp AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' @@ -76,8 +80,8 @@ Execute(The C Clang handler should include 'include' directories for projects wi AssertLinter 'clang', \ ale#Escape('clang') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -88,8 +92,8 @@ Execute(The C Clang handler should include 'include' directories for projects wi AssertLinter 'clang', \ ale#Escape('clang') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -100,8 +104,8 @@ Execute(The C Clang handler should include root directories for projects with .h AssertLinter 'clang', \ ale#Escape('clang') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -112,8 +116,8 @@ Execute(The C Clang handler should include root directories for projects with .h AssertLinter 'clang', \ ale#Escape('clang') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' @@ -124,8 +128,8 @@ Execute(The C++ GCC handler should include 'include' directories for projects wi AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) + \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -136,8 +140,8 @@ Execute(The C++ GCC handler should include 'include' directories for projects wi AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) + \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include')) \ . ' -' @@ -148,8 +152,8 @@ Execute(The C++ GCC handler should include root directories for projects with .h AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -160,8 +164,8 @@ Execute(The C++ GCC handler should include root directories for projects with .h AssertLinter 'gcc', \ ale#Escape('gcc') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' @@ -172,8 +176,8 @@ Execute(The C++ Clang handler should include 'include' directories for projects AssertLinter 'clang++', \ ale#Escape('clang++') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -184,8 +188,8 @@ Execute(The C++ Clang handler should include 'include' directories for projects AssertLinter 'clang++', \ ale#Escape('clang++') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include')) \ . ' -' @@ -196,8 +200,8 @@ Execute(The C++ Clang handler should include root directories for projects with AssertLinter 'clang++', \ ale#Escape('clang++') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -208,8 +212,8 @@ Execute(The C++ Clang handler should include root directories for projects with AssertLinter 'clang++', \ ale#Escape('clang++') - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' diff --git a/test/command_callback/test_cpp_ccls_command_callbacks.vader b/test/command_callback/test_cpp_ccls_command_callbacks.vader index 38947acf..eece42bc 100644 --- a/test/command_callback/test_cpp_ccls_command_callbacks.vader +++ b/test/command_callback/test_cpp_ccls_command_callbacks.vader @@ -8,6 +8,8 @@ After: call ale#assert#TearDownLinterTest() Execute(The project root should be detected correctly using compile_commands.json file): + call ale#test#SetFilename(tempname() . '/dummy.cpp') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.cpp') @@ -15,6 +17,8 @@ Execute(The project root should be detected correctly using compile_commands.jso AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') Execute(The project root should be detected correctly using .ccls file): + call ale#test#SetFilename(tempname() . '/dummy.cpp') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls/dummy.cpp') @@ -22,6 +26,8 @@ Execute(The project root should be detected correctly using .ccls file): AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_ccls') Execute(The project root should be detected correctly using .ccls-root file): + call ale#test#SetFilename(tempname() . '/dummy.cpp') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls-root/dummy.cpp') diff --git a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader index 352c88d5..15c8c4b8 100644 --- a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader @@ -17,6 +17,18 @@ Execute(cppcheck for C++ should detect compile_commands.json files): call ale#test#SetFilename('cppcheck_paths/one/foo.cpp') AssertLinter 'cppcheck', - \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) + \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') - \ . ' -q --language=c++ --project=compile_commands.json --enable=style %t' + \ . ' -q --language=c++' + \ . ' --project=' . ale#Escape('compile_commands.json') + \ . ' --enable=style %t' + +Execute(cppcheck for C++ should detect compile_commands.json files in build directories): + call ale#test#SetFilename('cppcheck_paths/with_build_dir/foo.cpp') + + AssertLinter 'cppcheck', + \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/with_build_dir')) + \ . ale#Escape('cppcheck') + \ . ' -q --language=c++' + \ . ' --project=' . ale#Escape(ale#path#Simplify('build/compile_commands.json')) + \ . ' --enable=style %t' diff --git a/test/command_callback/test_cpp_cquery_command_callbacks.vader b/test/command_callback/test_cpp_cquery_command_callbacks.vader index 682c90d5..842f123e 100644 --- a/test/command_callback/test_cpp_cquery_command_callbacks.vader +++ b/test/command_callback/test_cpp_cquery_command_callbacks.vader @@ -8,13 +8,17 @@ After: call ale#assert#TearDownLinterTest() Execute(The project root should be detected correctly using compile_commands.json file): + call ale#test#SetFilename(tempname() . '/dummy.cpp') + AssertLSPProject '' - call ale#test#SetFilename('cquery_paths/with_compile_commands_json/dummy.cpp') + call ale#test#SetFilename('cquery_paths/dummy.cpp') - AssertLSPProject ale#path#Simplify(g:dir . '/cquery_paths/with_compile_commands_json') + AssertLSPProject ale#path#Simplify(g:dir . '/cquery_paths') Execute(The project root should be detected correctly using .cquery file): + call ale#test#SetFilename(tempname() . '/dummy.cpp') + AssertLSPProject '' call ale#test#SetFilename('cquery_paths/with_cquery/dummy.cpp') diff --git a/test/command_callback/test_cpp_gcc_command_callbacks.vader b/test/command_callback/test_cpp_gcc_command_callbacks.vader index 0a86df4f..cfa4ecc0 100644 --- a/test/command_callback/test_cpp_gcc_command_callbacks.vader +++ b/test/command_callback/test_cpp_gcc_command_callbacks.vader @@ -3,8 +3,9 @@ Before: 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()) + let b:command_tail = ' -S -x c++' + \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') + \ . ' -iquote ' . ale#Escape(getcwd()) \ . ' -std=c++14 -Wall -' After: diff --git a/test/command_callback/test_erlang_dialyzer_command_callback.vader b/test/command_callback/test_erlang_dialyzer_command_callback.vader new file mode 100644 index 00000000..5e21c053 --- /dev/null +++ b/test/command_callback/test_erlang_dialyzer_command_callback.vader @@ -0,0 +1,37 @@ +Before: + call ale#assert#SetUpLinterTest('erlang', 'dialyzer') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct.): + AssertLinter 'dialyzer', + \ ale#Escape('dialyzer') + \ . ' -n --plt ' . ale#Escape(expand('$HOME/.dialyzer_plt')) + \ . ' -Wunmatched_returns' + \ . ' -Werror_handling' + \ . ' -Wrace_conditions' + \ . ' -Wunderspecs' + \ . ' %s' + +Execute(The command should accept configured executable.): + let b:ale_erlang_dialyzer_executable = '/usr/bin/dialyzer' + AssertLinter '/usr/bin/dialyzer', + \ ale#Escape('/usr/bin/dialyzer') + \ . ' -n --plt ' . ale#Escape(expand('$HOME/.dialyzer_plt')) + \ . ' -Wunmatched_returns' + \ . ' -Werror_handling' + \ . ' -Wrace_conditions' + \ . ' -Wunderspecs' + \ . ' %s' + +Execute(The command should accept configured PLT file.): + let b:ale_erlang_dialyzer_plt_file = 'custom-plt' + AssertLinter 'dialyzer', + \ ale#Escape('dialyzer') + \ . ' -n --plt ' . ale#Escape(expand('custom-plt')) + \ . ' -Wunmatched_returns' + \ . ' -Werror_handling' + \ . ' -Wrace_conditions' + \ . ' -Wunderspecs' + \ . ' %s' diff --git a/test/command_callback/test_objc_ccls_command_callbacks.vader b/test/command_callback/test_objc_ccls_command_callbacks.vader index 9d0c7690..5aa69d6a 100644 --- a/test/command_callback/test_objc_ccls_command_callbacks.vader +++ b/test/command_callback/test_objc_ccls_command_callbacks.vader @@ -5,6 +5,8 @@ After: call ale#assert#TearDownLinterTest() Execute(The project root should be detected correctly using compile_commands.json file): + call ale#test#SetFilename(tempname() . '/dummy.m') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.m') @@ -12,6 +14,8 @@ Execute(The project root should be detected correctly using compile_commands.jso AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') Execute(The project root should be detected correctly using .ccls file): + call ale#test#SetFilename(tempname() . '/dummy.m') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls/dummy.m') @@ -19,6 +23,8 @@ Execute(The project root should be detected correctly using .ccls file): AssertLSPProject ale#path#Simplify(g:dir . '/ccls_paths/with_ccls') Execute(The project root should be detected correctly using .ccls-root file): + call ale#test#SetFilename(tempname() . '/dummy.m') + AssertLSPProject '' call ale#test#SetFilename('ccls_paths/with_ccls-root/dummy.m') diff --git a/test/handler/test_erlang_dialyzer_handler.vader b/test/handler/test_erlang_dialyzer_handler.vader new file mode 100644 index 00000000..afd5c597 --- /dev/null +++ b/test/handler/test_erlang_dialyzer_handler.vader @@ -0,0 +1,27 @@ +Before: + runtime ale_linters/erlang/dialyzer.vim + +After: + call ale#linter#Reset() + +Execute(The dialyzer handler should handle error messages.): + AssertEqual + \[ + \ { + \ 'lnum': 3, + \ 'lcol': 0, + \ 'text': 'Callback info about the provider behaviour is not available', + \ 'type': 'W' + \ } + \], + \ ale_linters#erlang#dialyzer#Handle(bufnr(''), ['erl_tidy_prv_fmt.erl:3: Callback info about the provider behaviour is not available']) + +Execute(The dialyzer handler should handle empty file.): + AssertEqual + \[], + \ ale_linters#erlang#dialyzer#Handle(bufnr(''), []) + +Execute(The dialyzer handler should handle empty lines.): + AssertEqual + \[], + \ ale_linters#erlang#dialyzer#Handle(bufnr(''), ['']) diff --git a/test/handler/test_gcc_handler.vader b/test/handler/test_gcc_handler.vader index 3daa9e60..b67483a6 100644 --- a/test/handler/test_gcc_handler.vader +++ b/test/handler/test_gcc_handler.vader @@ -217,3 +217,65 @@ Execute(The GCC handler should handle fatal error messages due to missing files) \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ '<stdin>:3:12: fatal error: foo.h: No such file or directory', \ ]) + +Execute(The GCC handler should handle errors for inlined header functions): + AssertEqual + \ [ + \ { + \ 'lnum': 50, + \ 'col': 4, + \ 'filename': '/usr/include/bits/fcntl2.h', + \ 'type': 'E', + \ 'text': 'call to ''__open_missing_mode'' declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments', + \ }, + \ { + \ 'lnum': 44, + \ 'col': 5, + \ 'filename': '/usr/include/bits/fcntl2.h', + \ 'type': 'E', + \ 'text': 'call to ''__open_too_many_args'' declared with attribute error: open can be called either with 2 or 3 arguments, not more', + \ }, + \ { + \ 'lnum': 7, + \ 'col': 10, + \ 'type': 'E', + \ 'text': 'call to ''__open_missing_mode'' declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments', + \ }, + \ { + \ 'lnum': 13, + \ 'col': 11, + \ 'type': 'E', + \ 'text': 'call to ''__open_too_many_args'' declared with attribute error: open can be called either with 2 or 3 arguments, not more', + \ }, + \ { + \ 'lnum': 1, + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': join([ + \ 'In file included from /usr/include/fcntl.h:328,', + \ ' from <stdin>:1:', + \ 'In function ‘open’,', + \ ' inlined from ‘main’ at <stdin>:7:10:', + \ '/usr/include/bits/fcntl2.h:50:4: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments', + \ ' __open_missing_mode ();', + \ ' ^~~~~~~~~~~~~~~~~~~~~~', + \ 'In function ‘open’,', + \ ' inlined from ‘main’ at <stdin>:13:11:', + \ '/usr/include/bits/fcntl2.h:44:5: error: call to ‘__open_too_many_args’ declared with attribute error: open can be called either with 2 or 3 arguments, not more', + \ ' __open_too_many_args ();', + \ ], "\n") + \ }, + \], + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ + \ 'In file included from /usr/include/fcntl.h:328,', + \ ' from <stdin>:1:', + \ 'In function ‘open’,', + \ ' inlined from ‘main’ at <stdin>:7:10:', + \ '/usr/include/bits/fcntl2.h:50:4: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments', + \ ' __open_missing_mode ();', + \ ' ^~~~~~~~~~~~~~~~~~~~~~', + \ 'In function ‘open’,', + \ ' inlined from ‘main’ at <stdin>:13:11:', + \ '/usr/include/bits/fcntl2.h:44:5: error: call to ‘__open_too_many_args’ declared with attribute error: open can be called either with 2 or 3 arguments, not more', + \ ' __open_too_many_args ();', + \ ' ^~~~~~~~~~~~~~~~~~~~~~~', + \ ]) diff --git a/test/lsp/test_lsp_client_messages.vader b/test/lsp/test_lsp_client_messages.vader index 2abdf6ca..90a20832 100644 --- a/test/lsp/test_lsp_client_messages.vader +++ b/test/lsp/test_lsp_client_messages.vader @@ -20,7 +20,7 @@ Execute(ale#lsp#message#Initialize() should return correct messages): \ 'rootUri': 'file:///foo/bar', \ } \ ], - \ ale#lsp#message#Initialize('/foo/bar', {'foo': 'bar'}) + \ ale#lsp#message#Initialize('/foo/bar', {'foo': 'bar'}, {}) Execute(ale#lsp#message#Initialized() should return correct messages): AssertEqual [1, 'initialized', {}], ale#lsp#message#Initialized() diff --git a/test/lsp/test_lsp_root_detection.vader b/test/lsp/test_lsp_root_detection.vader index b7827248..3de4d825 100644 --- a/test/lsp/test_lsp_root_detection.vader +++ b/test/lsp/test_lsp_root_detection.vader @@ -56,7 +56,7 @@ Execute(The global variable is queried if the buffer-specific has no value): AssertLSPProject '/some/path' -Execute(The default hook value is acceptable): - call ale#test#SetFilename('other-file.c') +Execute(No path should be returned by default): + call ale#test#SetFilename(tempname() . '/other-file.c') AssertLSPProject '' diff --git a/test/lsp/test_lsp_startup.vader b/test/lsp/test_lsp_startup.vader index 028ec9b1..c29690bf 100644 --- a/test/lsp/test_lsp_startup.vader +++ b/test/lsp/test_lsp_startup.vader @@ -138,9 +138,67 @@ Before: \ 'params': { \ 'initializationOptions': {}, \ 'rootUri': ale#path#ToURI(a:root), - \ 'capabilities': {}, \ 'rootPath': a:root, \ 'processId': getpid(), + \ 'capabilities': { + \ 'workspace': { + \ 'applyEdit': v:false, + \ 'didChangeConfiguration': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'symbol': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'workspaceFolders': v:false, + \ 'configuration': v:false, + \ }, + \ 'textDocument': { + \ 'synchronization': { + \ 'dynamicRegistration': v:false, + \ 'willSave': v:false, + \ 'willSaveWaitUntil': v:false, + \ 'didSave': v:true, + \ }, + \ 'completion': { + \ 'dynamicRegistration': v:false, + \ 'completionItem': { + \ 'snippetSupport': v:false, + \ 'commitCharactersSupport': v:false, + \ 'documentationFormat': ['plaintext'], + \ 'deprecatedSupport': v:false, + \ 'preselectSupport': v:false, + \ }, + \ 'contextSupport': v:false, + \ }, + \ 'hover': { + \ 'dynamicRegistration': v:false, + \ 'contentFormat': ['plaintext'], + \ }, + \ 'references': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'documentSymbol': { + \ 'dynamicRegistration': v:false, + \ 'hierarchicalDocumentSymbolSupport': v:false, + \ }, + \ 'definition': { + \ 'dynamicRegistration': v:false, + \ 'linkSupport': v:false, + \ }, + \ 'typeDefinition': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'publishDiagnostics': { + \ 'relatedInformation': v:true, + \ }, + \ 'codeAction': { + \ 'dynamicRegistration': v:false, + \ }, + \ 'rename': { + \ 'dynamicRegistration': v:false, + \ }, + \ }, + \ }, \ }, \ }, \ ], diff --git a/test/test_c_projects/build/bad_folder_to_test_priority b/test/test_c_projects/build_compile_commands_project/build/bad_folder_to_test_priority index e69de29b..e69de29b 100644 --- a/test/test_c_projects/build/bad_folder_to_test_priority +++ b/test/test_c_projects/build_compile_commands_project/build/bad_folder_to_test_priority diff --git a/test/test_c_projects/build_compile_commands_project/build/compile_commands.json b/test/test_c_projects/build_compile_commands_project/build/compile_commands.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test_c_projects/build_compile_commands_project/build/compile_commands.json diff --git a/test/test_writefile_function.vader b/test/test_writefile_function.vader index 8c8a6f17..811d59e8 100644 --- a/test/test_writefile_function.vader +++ b/test/test_writefile_function.vader @@ -1,14 +1,18 @@ Before: call ale#test#SetDirectory('/testplugin/test') + let g:new_line_test_file = tempname() + After: noautocmd :e! ++ff=unix setlocal buftype=nofile - if filereadable('.newline-test') - call delete('.newline-test') + if filereadable(g:new_line_test_file) + call delete(g:new_line_test_file) endif + unlet! g:new_line_test_file + call ale#test#RestoreDirectory() Given(A file with Windows line ending characters): @@ -17,17 +21,17 @@ Given(A file with Windows line ending characters): third
Execute(Carriage returns should be included for ale#util#Writefile): - call ale#test#SetFilename('.newline-test') + call ale#test#SetFilename(g:new_line_test_file) setlocal buftype= noautocmd :w noautocmd :e! ++ff=dos - call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test') + call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file) AssertEqual \ ["first\r", "second\r", "third\r", ''], - \ readfile('.newline-test', 'b') + \ readfile(g:new_line_test_file, 'b') Given(A file with extra carriage returns): first
@@ -36,17 +40,17 @@ Given(A file with extra carriage returns): fourth Execute(Carriage returns should be de-depulicated): - call ale#test#SetFilename('.newline-test') + call ale#test#SetFilename(g:new_line_test_file) setlocal buftype= noautocmd :w noautocmd :e! ++ff=dos - call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test') + call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file) AssertEqual \ ["first\r", "second\r", "third\r", "fourth\r", ''], - \ readfile('.newline-test', 'b') + \ readfile(g:new_line_test_file, 'b') Given(A file with Unix line ending characters): first @@ -54,14 +58,14 @@ Given(A file with Unix line ending characters): third Execute(Unix file lines should be written as normal): - call ale#test#SetFilename('.newline-test') + call ale#test#SetFilename(g:new_line_test_file) setlocal buftype= noautocmd :w noautocmd :e! ++ff=unix - call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test') + call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file) AssertEqual \ ['first', 'second', 'third', ''], - \ readfile('.newline-test', 'b') + \ readfile(g:new_line_test_file, 'b') |