diff --git a/.appveyor.yml b/.appveyor.yml index aca83191..e6f2a1ee 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,6 +19,9 @@ init: # Stop git from changing newlines - git config --global core.autocrlf input +# NOTE: If you change the Vim or Vader versions here, please also update the +# instructions for running tests on Windows in ale-development.txt + install: # Download and unpack Vim - ps: >- diff --git a/.gitignore b/.gitignore index ae9f65fb..7711fb99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ !.editorconfig *.obj +*.pyc # Ignore all hidden files everywhere. # Use `git add -f` to add hidden files. .* -__pycache__ -*.pyc /doc/tags /init.vim /test/ale-info-test-file +/vader_output +__pycache__ tags diff --git a/README.md b/README.md index ffc6dd19..0f1c613b 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ other content at [w0rp.com](https://w0rp.com). 17. [How can I configure my C or C++ project?](#faq-c-configuration) 18. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration) 19. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height) + 20. [How can I run linters or fixers via Docker or a VM?](#faq-vm) @@ -877,3 +878,14 @@ To set a default height for the error list, use the `g:ale_list_window_size` var " Show 5 lines of errors (default: 10) let g:ale_list_window_size = 5 ``` + + + +### 5.xx. How can I run linters or fixers via Docker or a VM? + +ALE supports running linters or fixers via Docker, virtual machines, or in +combination with any remote machine with a different file system, so long as the +tools are well-integrated with ALE, and ALE is properly configured to run the +correct commands and map filename paths between different file systems. See +`:help ale-lint-other-machines` for the full documentation on how to configure +ALE to support this. diff --git a/ale_linters/ada/gcc.vim b/ale_linters/ada/gcc.vim index 87496b81..5afc9ae3 100644 --- a/ale_linters/ada/gcc.vim +++ b/ale_linters/ada/gcc.vim @@ -18,7 +18,7 @@ function! ale_linters#ada#gcc#GetCommand(buffer) abort " -gnatc: Check syntax and semantics only (no code generation attempted) return '%e -x ada -c -gnatc' \ . ' -o ' . ale#Escape(l:out_file) - \ . ' -I ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ' -I %s:h' \ . ale#Pad(ale#Var(a:buffer, 'ada_gcc_options')) \ . ' %t' endfunction diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim index eecab6ef..cda38923 100644 --- a/ale_linters/asm/gcc.vim +++ b/ale_linters/asm/gcc.vim @@ -9,7 +9,7 @@ function! ale_linters#asm#gcc#GetCommand(buffer) abort " -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')) + \ . '-iquote %s:h' \ . ' ' . ale#Var(a:buffer, 'asm_gcc_options') . ' -' endfunction diff --git a/ale_linters/c/cc.vim b/ale_linters/c/cc.vim index 6d920ab0..5655fbf7 100644 --- a/ale_linters/c/cc.vim +++ b/ale_linters/c/cc.vim @@ -38,7 +38,7 @@ function! ale_linters#c#cc#GetCommand(buffer, output) abort " -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')) + \ . ' -iquote %s:h' \ . ale#Pad(l:cflags) \ . ale#Pad(l:ale_flags) . ' -' endfunction diff --git a/ale_linters/cpp/cc.vim b/ale_linters/cpp/cc.vim index eed3898f..ffb8f068 100644 --- a/ale_linters/cpp/cc.vim +++ b/ale_linters/cpp/cc.vim @@ -38,7 +38,7 @@ function! ale_linters#cpp#cc#GetCommand(buffer, output) abort " -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')) + \ . ' -iquote %s:h' \ . ale#Pad(l:cflags) \ . ale#Pad(l:ale_flags) . ' -' endfunction diff --git a/ale_linters/cuda/nvcc.vim b/ale_linters/cuda/nvcc.vim index f3af07b6..2734f6ec 100644 --- a/ale_linters/cuda/nvcc.vim +++ b/ale_linters/cuda/nvcc.vim @@ -5,9 +5,6 @@ call ale#Set('cuda_nvcc_executable', 'nvcc') call ale#Set('cuda_nvcc_options', '-std=c++11') function! ale_linters#cuda#nvcc#GetCommand(buffer) abort - " Unused: use ale#util#nul_file - " let l:output_file = ale#util#Tempname() . '.ii' - " call ale#command#ManageFile(a:buffer, l:output_file) return '%e -cuda' \ . ale#Pad(ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer))) \ . ale#Pad(ale#Var(a:buffer, 'cuda_nvcc_options')) diff --git a/ale_linters/eruby/ruumba.vim b/ale_linters/eruby/ruumba.vim index 2e84acf7..f415f1ab 100644 --- a/ale_linters/eruby/ruumba.vim +++ b/ale_linters/eruby/ruumba.vim @@ -11,7 +11,7 @@ function! ale_linters#eruby#ruumba#GetCommand(buffer) abort return ale#ruby#EscapeExecutable(l:executable, 'ruumba') \ . ' --format json --force-exclusion ' \ . ale#Var(a:buffer, 'eruby_ruumba_options') - \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p')) + \ . ' --stdin %s' endfunction function! ale_linters#eruby#ruumba#Handle(buffer, lines) abort diff --git a/ale_linters/go/gofmt.vim b/ale_linters/go/gofmt.vim index a233b422..b313f9ca 100644 --- a/ale_linters/go/gofmt.vim +++ b/ale_linters/go/gofmt.vim @@ -6,7 +6,6 @@ function! ale_linters#go#gofmt#GetCommand(buffer) abort \ . '%e -e %t' endfunction - call ale#linter#Define('go', { \ 'name': 'gofmt', \ 'output_stream': 'stderr', diff --git a/ale_linters/nasm/nasm.vim b/ale_linters/nasm/nasm.vim index 347abc1b..c4f53629 100644 --- a/ale_linters/nasm/nasm.vim +++ b/ale_linters/nasm/nasm.vim @@ -7,10 +7,9 @@ call ale#Set('nasm_nasm_options', '') function! ale_linters#nasm#nasm#GetCommand(buffer) abort " Note that NASM requires a trailing slash for the -I option. let l:separator = has('win32') ? '\' : '/' - let l:path = fnamemodify(bufname(a:buffer), ':p:h') . l:separator let l:output_null = has('win32') ? 'NUL' : '/dev/null' - return '%e -X gnu -I ' . ale#Escape(l:path) + return '%e -X gnu -I %s:h' . l:separator \ . ale#Pad(ale#Var(a:buffer, 'nasm_nasm_options')) \ . ' %s' \ . ' -o ' . l:output_null diff --git a/ale_linters/objc/clang.vim b/ale_linters/objc/clang.vim index 7873dccd..cafb97db 100644 --- a/ale_linters/objc/clang.vim +++ b/ale_linters/objc/clang.vim @@ -10,7 +10,7 @@ function! ale_linters#objc#clang#GetCommand(buffer) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return 'clang -S -x objective-c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . '-iquote %s:h' \ . ' ' . ale#Var(a:buffer, 'objc_clang_options') . ' -' endfunction diff --git a/ale_linters/objcpp/clang.vim b/ale_linters/objcpp/clang.vim index 4dbe55b3..35a40c6f 100644 --- a/ale_linters/objcpp/clang.vim +++ b/ale_linters/objcpp/clang.vim @@ -10,7 +10,7 @@ function! ale_linters#objcpp#clang#GetCommand(buffer) abort " -iquote with the directory the file is in makes #include work for " headers in the same directory. return 'clang++ -S -x objective-c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . '-iquote %s:h' \ . ' ' . ale#Var(a:buffer, 'objcpp_clang_options') . ' -' endfunction diff --git a/ale_linters/pyrex/cython.vim b/ale_linters/pyrex/cython.vim index 84382ba1..247c3060 100644 --- a/ale_linters/pyrex/cython.vim +++ b/ale_linters/pyrex/cython.vim @@ -6,9 +6,7 @@ call ale#Set('pyrex_cython_executable', 'cython') call ale#Set('pyrex_cython_options', '--warning-extra') function! ale_linters#pyrex#cython#GetCommand(buffer) abort - let l:local_dir = ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) - - return '%e --working ' . l:local_dir . ' --include-dir ' . l:local_dir + return '%e --working %s:h --include-dir %s:h' \ . ale#Pad(ale#Var(a:buffer, 'pyrex_cython_options')) \ . ' --output-file ' . g:ale#util#nul_file . ' %t' endfunction diff --git a/ale_linters/python/pydocstyle.vim b/ale_linters/python/pydocstyle.vim index 3901db4d..69ae3807 100644 --- a/ale_linters/python/pydocstyle.vim +++ b/ale_linters/python/pydocstyle.vim @@ -16,17 +16,15 @@ function! ale_linters#python#pydocstyle#GetExecutable(buffer) abort endfunction function! ale_linters#python#pydocstyle#GetCommand(buffer) abort - let l:dir = fnamemodify(bufname(a:buffer), ':p:h') let l:executable = ale_linters#python#pydocstyle#GetExecutable(a:buffer) - let l:exec_args = l:executable =~? 'pipenv$' \ ? ' run pydocstyle' \ : '' - return ale#path#CdString(l:dir) + return ale#path#BufferCdString(a:buffer) \ . ale#Escape(l:executable) . l:exec_args - \ . ' ' . ale#Var(a:buffer, 'python_pydocstyle_options') - \ . ' ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:t')) + \ . ale#Pad(ale#Var(a:buffer, 'python_pydocstyle_options')) + \ . ' %s:t' endfunction function! ale_linters#python#pydocstyle#Handle(buffer, lines) abort diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim index 410ed0ea..483806a6 100644 --- a/ale_linters/ruby/rubocop.vim +++ b/ale_linters/ruby/rubocop.vim @@ -10,7 +10,7 @@ function! ale_linters#ruby#rubocop#GetCommand(buffer) abort return ale#ruby#EscapeExecutable(l:executable, 'rubocop') \ . ' --format json --force-exclusion ' \ . ale#Var(a:buffer, 'ruby_rubocop_options') - \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p')) + \ . ' --stdin %s' endfunction function! ale_linters#ruby#rubocop#GetType(severity) abort diff --git a/ale_linters/ruby/standardrb.vim b/ale_linters/ruby/standardrb.vim index f751e803..6ccfd2d6 100644 --- a/ale_linters/ruby/standardrb.vim +++ b/ale_linters/ruby/standardrb.vim @@ -11,7 +11,7 @@ function! ale_linters#ruby#standardrb#GetCommand(buffer) abort return ale#ruby#EscapeExecutable(l:executable, 'standardrb') \ . ' --format json --force-exclusion ' \ . ale#Var(a:buffer, 'ruby_standardrb_options') - \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p')) + \ . ' --stdin %s' endfunction " standardrb is based on RuboCop so the callback is the same diff --git a/ale_linters/sql/sqllint.vim b/ale_linters/sql/sqllint.vim new file mode 100644 index 00000000..78396fe9 --- /dev/null +++ b/ale_linters/sql/sqllint.vim @@ -0,0 +1,33 @@ +" ale_linters/sql/sqllint.vim +" Author: Joe Reynolds +" Description: sql-lint for SQL files. +" sql-lint can be found at +" https://www.npmjs.com/package/sql-lint +" https://github.com/joereynolds/sql-lint + +function! ale_linters#sql#sqllint#Handle(buffer, lines) abort + " Matches patterns like the following: + " + " stdin:1 [ER_NO_DB_ERROR] No database selected + let l:pattern = '\v^[^:]+:(\d+) (.*)' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'type': l:match[3][0], + \ 'text': l:match[0], + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('sql', { +\ 'name': 'sqllint', +\ 'aliases': ['sql-lint'], +\ 'executable': 'sql-lint', +\ 'command': 'sql-lint', +\ 'callback': 'ale_linters#sql#sqllint#Handle', +\}) diff --git a/autoload/ale.vim b/autoload/ale.vim index 6251b47b..b75c9fc9 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -266,3 +266,23 @@ function! ale#GetLocItemMessage(item, format_string) abort return l:msg endfunction + +" Given a buffer and a linter or fixer name, return an Array of two-item +" Arrays describing how to map filenames to and from the local to foreign file +" systems. +function! ale#GetFilenameMappings(buffer, name) abort + let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings') + + if type(l:linter_mappings) is v:t_list + return l:linter_mappings + endif + + let l:name = a:name + + if !has_key(l:linter_mappings, l:name) + " Use * as a default setting for all tools. + let l:name = '*' + endif + + return get(l:linter_mappings, l:name, []) +endfunction diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 5cf4c690..d0a4fa06 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -8,6 +8,11 @@ let s:sep = has('win32') ? '\' : '/' " Set just so tests can override it. let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] +let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ +\ 'build', +\ 'bin', +\]) + function! s:CanParseMakefile(buffer) abort " Something somewhere seems to delete this setting in tests, so ensure we " always have a default value. @@ -84,10 +89,10 @@ function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort let l:path = join(split(l:option, '\zs')[1:], '') " Make path absolute - if stridx(l:path, s:sep) != 0 && stridx(l:path, '/') != 0 + if !ale#path#IsAbsolute(l:path) let l:rel_path = substitute(l:path, '"', '', 'g') let l:rel_path = substitute(l:rel_path, '''', '', 'g') - let l:path = a:path_prefix . s:sep . l:rel_path + let l:path = ale#path#GetAbsPath(a:path_prefix, l:rel_path) endif " Read the file and add all the arguments @@ -115,16 +120,27 @@ function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort return l:out_lines endfunction -function! ale#c#ParseCFlags(path_prefix, cflag_line) abort - let l:cflags_list = [] +" Quote C/C++ a compiler argument, if needed. +" +" Quoting arguments might cause issues with some systems/compilers, so we only +" quote them if we need to. +function! ale#c#QuoteArg(arg) abort + if a:arg !~# '\v[#$&*()\\|[\]{};''"<>/?! ^%]' + return a:arg + endif - let l:raw_split_lines = ale#c#ShellSplit(a:cflag_line) + return ale#Escape(a:arg) +endfunction + +function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort " Expand @file arguments now before parsing - let l:split_lines = ale#c#ExpandAtArgs(a:path_prefix, l:raw_split_lines) + let l:arguments = ale#c#ExpandAtArgs(a:path_prefix, a:raw_arguments) + " A list of [already_quoted, argument] + let l:items = [] let l:option_index = 0 - while l:option_index < len(l:split_lines) - let l:option = l:split_lines[l:option_index] + while l:option_index < len(l:arguments) + let l:option = l:arguments[l:option_index] let l:option_index = l:option_index + 1 " Include options, that may need relative path fix @@ -133,36 +149,38 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \ || stridx(l:option, '-isystem') == 0 \ || stridx(l:option, '-idirafter') == 0 \ || stridx(l:option, '-iframework') == 0 + \ || stridx(l:option, '-include') == 0 if stridx(l:option, '-I') == 0 && l:option isnot# '-I' let l:arg = join(split(l:option, '\zs')[2:], '') let l:option = '-I' else - let l:arg = l:split_lines[l:option_index] + let l:arg = l:arguments[l:option_index] let l:option_index = l:option_index + 1 endif " Fix relative paths if needed - if stridx(l:arg, s:sep) != 0 && stridx(l:arg, '/') != 0 + if !ale#path#IsAbsolute(l:arg) let l:rel_path = substitute(l:arg, '"', '', 'g') let l:rel_path = substitute(l:rel_path, '''', '', 'g') - let l:arg = ale#Escape(a:path_prefix . s:sep . l:rel_path) + let l:arg = ale#path#GetAbsPath(a:path_prefix, l:rel_path) endif - call add(l:cflags_list, l:option) - call add(l:cflags_list, l:arg) + call add(l:items, [1, l:option]) + call add(l:items, [1, ale#Escape(l:arg)]) " Options with arg that can be grouped with the option or separate elseif stridx(l:option, '-D') == 0 || stridx(l:option, '-B') == 0 - call add(l:cflags_list, l:option) - if l:option is# '-D' || l:option is# '-B' - call add(l:cflags_list, l:split_lines[l:option_index]) + call add(l:items, [1, l:option]) + call add(l:items, [0, l:arguments[l:option_index]]) let l:option_index = l:option_index + 1 + else + call add(l:items, [0, l:option]) endif " Options that have an argument (always separate) elseif l:option is# '-iprefix' || stridx(l:option, '-iwithprefix') == 0 \ || l:option is# '-isysroot' || l:option is# '-imultilib' - call add(l:cflags_list, l:option) - call add(l:cflags_list, l:split_lines[l:option_index]) + call add(l:items, [0, l:option]) + call add(l:items, [0, l:arguments[l:option_index]]) let l:option_index = l:option_index + 1 " Options without argument elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0) @@ -174,11 +192,19 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0 \ || stridx(l:option, '--sysroot=') == 0 || l:option is# '--no-sysroot-suffix' \ || stridx(l:option, '-m') == 0 - call add(l:cflags_list, l:option) + call add(l:items, [0, l:option]) endif endwhile - return join(l:cflags_list, ' ') + if a:should_quote + " Quote C arguments that haven't already been quoted above. + " If and only if we've been asked to quote them. + call map(l:items, 'v:val[0] ? v:val[1] : ale#c#QuoteArg(v:val[1])') + else + call map(l:items, 'v:val[1]') + endif + + return join(l:items, ' ') endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort @@ -200,7 +226,7 @@ function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h') - return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line) + return ale#c#ParseCFlags(l:makefile_dir, 0, ale#c#ShellSplit(l:cflag_line)) endfunction " Given a buffer number, find the project directory containing @@ -268,6 +294,10 @@ if !exists('s:compile_commands_cache') let s:compile_commands_cache = {} endif +function! ale#c#ResetCompileCommandsCache() abort + let s:compile_commands_cache = {} +endfunction + function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort let l:empty = [{}, {}] @@ -298,9 +328,20 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort let l:dir_lookup = {} for l:entry in (type(l:raw_data) is v:t_list ? l:raw_data : []) + let l:filename = ale#path#GetAbsPath(l:entry.directory, l:entry.file) + + " Store a key for lookups by the absolute path to the filename. + let l:file_lookup[l:filename] = get(l:file_lookup, l:filename, []) + [l:entry] + + " Store a key for fuzzy lookups by the absolute path to the directory. + let l:dirname = fnamemodify(l:filename, ':h') + let l:dir_lookup[l:dirname] = get(l:dir_lookup, l:dirname, []) + [l:entry] + + " Store a key for fuzzy lookups by just the basename of the file. let l:basename = tolower(fnamemodify(l:entry.file, ':t')) let l:file_lookup[l:basename] = get(l:file_lookup, l:basename, []) + [l:entry] + " Store a key for fuzzy lookups by just the basename of the directory. let l:dirbasename = tolower(fnamemodify(l:entry.directory, ':p:h:t')) let l:dir_lookup[l:dirbasename] = get(l:dir_lookup, l:dirbasename, []) + [l:entry] endfor @@ -315,28 +356,80 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort return l:empty endfunction -function! ale#c#GetCompileCommand(json_item) abort - if has_key(a:json_item, 'command') - return a:json_item.command - elseif has_key(a:json_item, 'arguments') - return join(a:json_item.arguments, ' ') +" Get [should_quote, arguments] from either 'command' or 'arguments' +" 'arguments' should be quoted later, the split 'command' strings should not. +function! s:GetArguments(json_item) abort + if has_key(a:json_item, 'arguments') + return [1, a:json_item.arguments] + elseif has_key(a:json_item, 'command') + return [0, ale#c#ShellSplit(a:json_item.command)] endif - return '' + return [0, []] endfunction function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort + let l:buffer_filename = ale#path#Simplify(expand('#' . a:buffer . ':p')) + let l:basename = tolower(fnamemodify(l:buffer_filename, ':t')) + " Look for any file in the same directory if we can't find an exact match. + let l:dir = fnamemodify(l:buffer_filename, ':h') + " Search for an exact file match first. - let l:basename = tolower(expand('#' . a:buffer . ':t')) - let l:file_list = get(a:file_lookup, l:basename, []) + let l:file_list = get(a:file_lookup, l:buffer_filename, []) + + " We may have to look for /foo/bar instead of C:\foo\bar + if empty(l:file_list) && has('win32') + let l:file_list = get( + \ a:file_lookup, + \ ale#path#RemoveDriveLetter(l:buffer_filename), + \ [] + \) + endif + + " Try the absolute path to the directory second. + let l:dir_list = get(a:dir_lookup, l:dir, []) + + if empty(l:dir_list) && has('win32') + let l:dir_list = get( + \ a:dir_lookup, + \ ale#path#RemoveDriveLetter(l:dir), + \ [] + \) + endif + + if empty(l:file_list) && empty(l:dir_list) + " If we can't find matches with the path to the file, try a + " case-insensitive match for any similarly-named file. + let l:file_list = get(a:file_lookup, l:basename, []) + + " If we can't find matches with the path to the directory, try a + " case-insensitive match for anything in similarly-named directory. + let l:dir_list = get(a:dir_lookup, tolower(fnamemodify(l:dir, ':t')), []) + endif + " A source file matching the header filename. let l:source_file = '' if empty(l:file_list) && l:basename =~? '\.h$\|\.hpp$' for l:suffix in ['.c', '.cpp'] - let l:key = fnamemodify(l:basename, ':r') . l:suffix + " Try to find a source file by an absolute path first. + let l:key = fnamemodify(l:buffer_filename, ':r') . l:suffix let l:file_list = get(a:file_lookup, l:key, []) + if empty(l:file_list) && has('win32') + let l:file_list = get( + \ a:file_lookup, + \ ale#path#RemoveDriveLetter(l:key), + \ [] + \) + endif + + if empty(l:file_list) + " Look fuzzy matches on the basename second. + let l:key = fnamemodify(l:basename, ':r') . l:suffix + let l:file_list = get(a:file_lookup, l:key, []) + endif + if !empty(l:file_list) let l:source_file = l:key break @@ -345,28 +438,31 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort endif for l:item in l:file_list + let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) + " Load the flags for this file, or for a source file matching the " header file. if ( - \ bufnr(l:item.file) is a:buffer + \ bufnr(l:filename) is a:buffer \ || ( \ !empty(l:source_file) - \ && l:item.file[-len(l:source_file):] is? l:source_file + \ && l:filename[-len(l:source_file):] is? l:source_file \ ) \) - return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) + let [l:should_quote, l:args] = s:GetArguments(l:item) + + return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args) endif endfor - " Look for any file in the same directory if we can't find an exact match. - let l:dir = ale#path#Simplify(expand('#' . a:buffer . ':p:h')) - - let l:dirbasename = tolower(expand('#' . a:buffer . ':p:h:t')) - let l:dir_list = get(a:dir_lookup, l:dirbasename, []) - for l:item in l:dir_list - if ale#path#Simplify(fnamemodify(l:item.file, ':h')) is? l:dir - return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) + let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) + + if ale#path#RemoveDriveLetter(fnamemodify(l:filename, ':h')) + \ is? ale#path#RemoveDriveLetter(l:dir) + let [l:should_quote, l:args] = s:GetArguments(l:item) + + return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args) endif endfor @@ -405,10 +501,10 @@ endfunction function! ale#c#GetMakeCommand(buffer) abort if s:CanParseMakefile(a:buffer) - let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + let l:path = ale#path#FindNearestFile(a:buffer, 'Makefile') - if !empty(l:makefile_path) - return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' + if !empty(l:path) + return ale#path#CdString(fnamemodify(l:path, ':h')) . 'make -n' endif endif @@ -477,8 +573,3 @@ function! ale#c#IncludeOptions(include_paths) abort return join(l:option_list) endfunction - -let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ -\ 'build', -\ 'bin', -\]) diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim index 1bbc4f4c..8f497169 100644 --- a/autoload/ale/command.vim +++ b/autoload/ale/command.vim @@ -133,11 +133,36 @@ function! ale#command#EscapeCommandPart(command_part) abort return substitute(a:command_part, '%', '%%', 'g') endfunction +" Format a filename, converting it with filename mappings, if non-empty, +" and escaping it for putting into a command string. +" +" The filename can be modified. +function! s:FormatFilename(filename, mappings, modifiers) abort + let l:filename = a:filename + + if !empty(a:mappings) + let l:filename = ale#filename_mapping#Map(l:filename, a:mappings) + endif + + if !empty(a:modifiers) + let l:filename = fnamemodify(l:filename, a:modifiers) + endif + + return ale#Escape(l:filename) +endfunction + " Given a command string, replace every... " %s -> with the current filename " %t -> with the name of an unused file in a temporary directory " %% -> with a literal % -function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed, input) abort +function! ale#command#FormatCommand( +\ buffer, +\ executable, +\ command, +\ pipe_file_if_needed, +\ input, +\ mappings, +\) abort let l:temporary_file = '' let l:command = a:command @@ -154,14 +179,24 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne " file. if l:command =~# '%s' let l:filename = fnamemodify(bufname(a:buffer), ':p') - let l:command = substitute(l:command, '%s', '\=ale#Escape(l:filename)', 'g') + let l:command = substitute( + \ l:command, + \ '\v\%s(%(:h|:t|:r|:e)*)', + \ '\=s:FormatFilename(l:filename, a:mappings, submatch(1))', + \ 'g' + \) endif if a:input isnot v:false && l:command =~# '%t' " Create a temporary filename, / " The file itself will not be created by this function. let l:temporary_file = s:TemporaryFilename(a:buffer) - let l:command = substitute(l:command, '%t', '\=ale#Escape(l:temporary_file)', 'g') + let l:command = substitute( + \ l:command, + \ '\v\%t(%(:h|:t|:r|:e)*)', + \ '\=s:FormatFilename(l:temporary_file, a:mappings, submatch(1))', + \ 'g' + \) endif " Finish formatting so %% becomes %. @@ -265,6 +300,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort \ a:command, \ get(l:options, 'read_buffer', 0), \ get(l:options, 'input', v:null), + \ get(l:options, 'filename_mappings', []), \) let l:command = ale#job#PrepareCommand(a:buffer, l:command) let l:job_options = { diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index a273d4e1..f6a0c350 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -540,7 +540,8 @@ function! ale#completion#ParseLSPCompletions(response) abort " Don't use LSP items with additional text edits when autoimport for " completions is turned off. - if has_key(l:item, 'additionalTextEdits') && !g:ale_completion_autoimport + if !empty(get(l:item, 'additionalTextEdits')) + \&& !g:ale_completion_autoimport continue endif @@ -562,31 +563,32 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:text_changes = [] for l:edit in l:item.additionalTextEdits - let l:range = l:edit.range call add(l:text_changes, { \ 'start': { - \ 'line': l:range.start.line + 1, - \ 'offset': l:range.start.character + 1, + \ 'line': l:edit.range.start.line + 1, + \ 'offset': l:edit.range.start.character + 1, \ }, \ 'end': { - \ 'line': l:range.end.line + 1, - \ 'offset': l:range.end.character + 1, + \ 'line': l:edit.range.end.line + 1, + \ 'offset': l:edit.range.end.character + 1, \ }, \ 'newText': l:edit.newText, \}) endfor - let l:changes = [{ - \ 'fileName': expand('#' . l:buffer . ':p'), - \ 'textChanges': l:text_changes, - \}] - \ - let l:result.user_data = json_encode({ - \ 'codeActions': [{ - \ 'description': 'completion', - \ 'changes': l:changes, - \ }], - \ }) + if !empty(l:text_changes) + let l:result.user_data = json_encode({ + \ 'codeActions': [{ + \ 'description': 'completion', + \ 'changes': [ + \ { + \ 'fileName': expand('#' . l:buffer . ':p'), + \ 'textChanges': l:text_changes, + \ } + \ ], + \ }], + \}) + endif endif call add(l:results, l:result) @@ -900,6 +902,8 @@ function! ale#completion#Done() abort endfunction augroup ALECompletionActions + autocmd! + autocmd CompleteDone * call ale#completion#HandleUserData(v:completed_item) augroup END diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index 8c331c5c..9ca6fb15 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -39,6 +39,8 @@ function! ale#cursor#TruncatedEcho(original_message) abort endif exec 'echomsg l:message' + catch /E481/ + " Do nothing if running from a visual selection. endtry " Reset the cursor position if we moved off the end of the line. diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index cfc1e5d7..ae0354b8 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -4,6 +4,7 @@ " Remapping of linter problems. let g:ale_type_map = get(g:, 'ale_type_map', {}) +let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {}) if !has_key(s:, 'executable_cache_map') let s:executable_cache_map = {} @@ -256,6 +257,13 @@ function! s:RemapItemTypes(type_map, loclist) abort endfunction function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort + let l:mappings = ale#GetFilenameMappings(a:buffer, a:linter_name) + + if !empty(l:mappings) + " We need to apply reverse filename mapping here. + let l:mappings = ale#filename_mapping#Invert(l:mappings) + endif + let l:bufnr_map = {} let l:new_loclist = [] @@ -296,13 +304,19 @@ function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) let l:item.code = l:old_item.code endif - if has_key(l:old_item, 'filename') - \&& !ale#path#IsTempName(l:old_item.filename) + let l:old_name = get(l:old_item, 'filename', '') + + " Map parsed from output to local filesystem files. + if !empty(l:old_name) && !empty(l:mappings) + let l:old_name = ale#filename_mapping#Map(l:old_name, l:mappings) + endif + + if !empty(l:old_name) && !ale#path#IsTempName(l:old_name) " Use the filename given. " Temporary files are assumed to be for this buffer, " and the filename is not included then, because it looks bad " in the loclist window. - let l:filename = l:old_item.filename + let l:filename = l:old_name let l:item.filename = l:filename if has_key(l:old_item, 'bufnr') @@ -403,7 +417,7 @@ function! s:RunJob(command, options) abort let l:buffer = a:options.buffer let l:linter = a:options.linter let l:output_stream = a:options.output_stream - let l:read_buffer = a:options.read_buffer + let l:read_buffer = a:options.read_buffer && !a:options.lint_file let l:info = g:ale_buffer_info[l:buffer] let l:Callback = function('s:HandleExit', [{ @@ -415,6 +429,7 @@ function! s:RunJob(command, options) abort \ 'executable': l:executable, \ 'read_buffer': l:read_buffer, \ 'log_output': 1, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:linter.name), \}) " Only proceed if the job is being run. @@ -493,10 +508,15 @@ function! s:AddProblemsFromOtherBuffers(buffer, linters) abort endif endfunction -function! s:RunIfExecutable(buffer, linter, executable) abort +function! s:RunIfExecutable(buffer, linter, lint_file, executable) abort if ale#command#IsDeferred(a:executable) let a:executable.result_callback = { - \ executable -> s:RunIfExecutable(a:buffer, a:linter, executable) + \ executable -> s:RunIfExecutable( + \ a:buffer, + \ a:linter, + \ a:lint_file, + \ executable + \ ) \} return 1 @@ -504,7 +524,7 @@ function! s:RunIfExecutable(buffer, linter, executable) abort if ale#engine#IsExecutable(a:buffer, a:executable) " Use different job types for file or linter jobs. - let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter' + let l:job_type = a:lint_file ? 'file_linter' : 'linter' call setbufvar(a:buffer, 'ale_job_type', l:job_type) let l:command = ale#linter#GetCommand(a:buffer, a:linter) @@ -514,6 +534,7 @@ function! s:RunIfExecutable(buffer, linter, executable) abort \ 'linter': a:linter, \ 'output_stream': get(a:linter, 'output_stream', 'stdout'), \ 'read_buffer': a:linter.read_buffer, + \ 'lint_file': a:lint_file, \} return s:RunJob(l:command, l:options) @@ -525,33 +546,62 @@ endfunction " Run a linter for a buffer. " " Returns 1 if the linter was successfully run. -function! s:RunLinter(buffer, linter) abort +function! s:RunLinter(buffer, linter, lint_file) abort if !empty(a:linter.lsp) return ale#lsp_linter#CheckWithLSP(a:buffer, a:linter) else let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) - return s:RunIfExecutable(a:buffer, a:linter, l:executable) + return s:RunIfExecutable(a:buffer, a:linter, a:lint_file, l:executable) endif return 0 endfunction -function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort - " Initialise the buffer information if needed. - let l:new_buffer = ale#engine#InitBufferInfo(a:buffer) - call s:StopCurrentJobs(a:buffer, a:should_lint_file) - call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters) +function! s:GetLintFileValues(slots, Callback) abort + let l:deferred_list = [] + let l:new_slots = [] - " We can only clear the results if we aren't checking the buffer. - let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer) + for [l:lint_file, l:linter] in a:slots + while ale#command#IsDeferred(l:lint_file) && has_key(l:lint_file, 'value') + " If we've already computed the return value, use it. + let l:lint_file = l:lint_file.value + endwhile - silent doautocmd User ALELintPre + if ale#command#IsDeferred(l:lint_file) + " If we are going to return the result later, wait for it. + call add(l:deferred_list, l:lint_file) + else + " If we have the value now, coerce it to 0 or 1. + let l:lint_file = l:lint_file is 1 + endif - for l:linter in a:linters + call add(l:new_slots, [l:lint_file, l:linter]) + endfor + + if !empty(l:deferred_list) + for l:deferred in l:deferred_list + let l:deferred.result_callback = + \ {-> s:GetLintFileValues(l:new_slots, a:Callback)} + endfor + else + call a:Callback(l:new_slots) + endif +endfunction + +function! s:RunLinters( +\ buffer, +\ slots, +\ should_lint_file, +\ new_buffer, +\ can_clear_results +\) abort + let l:can_clear_results = a:can_clear_results + + for [l:lint_file, l:linter] in a:slots " Only run lint_file linters if we should. - if !l:linter.lint_file || a:should_lint_file - if s:RunLinter(a:buffer, l:linter) + if !l:lint_file || a:should_lint_file + if s:RunLinter(a:buffer, l:linter, l:lint_file) " If a single linter ran, we shouldn't clear everything. let l:can_clear_results = 0 endif @@ -566,11 +616,49 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort " disabled, or ALE itself is disabled. if l:can_clear_results call ale#engine#SetResults(a:buffer, []) - elseif l:new_buffer - call s:AddProblemsFromOtherBuffers(a:buffer, a:linters) + elseif a:new_buffer + call s:AddProblemsFromOtherBuffers( + \ a:buffer, + \ map(copy(a:slots), 'v:val[1]') + \) endif endfunction +function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort + " Initialise the buffer information if needed. + let l:new_buffer = ale#engine#InitBufferInfo(a:buffer) + call s:StopCurrentJobs(a:buffer, a:should_lint_file) + call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters) + + " We can only clear the results if we aren't checking the buffer. + let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer) + + silent doautocmd User ALELintPre + + " Handle `lint_file` callbacks first. + let l:linter_slots = [] + + for l:linter in a:linters + let l:LintFile = l:linter.lint_file + + if type(l:LintFile) is v:t_func + let l:LintFile = l:LintFile(a:buffer) + endif + + call add(l:linter_slots, [l:LintFile, l:linter]) + endfor + + call s:GetLintFileValues(l:linter_slots, { + \ new_slots -> s:RunLinters( + \ a:buffer, + \ new_slots, + \ a:should_lint_file, + \ l:new_buffer, + \ l:can_clear_results, + \ ) + \}) +endfunction + " Clean up a buffer. " " This function will stop all current jobs for the buffer, diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index 731e36f2..3568c117 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -105,11 +105,11 @@ function! ale#events#Init() abort if g:ale_enabled if l:text_changed is? 'always' || l:text_changed is# '1' - autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay) + autocmd TextChanged,TextChangedI * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) elseif l:text_changed is? 'normal' - autocmd TextChanged * call ale#Queue(g:ale_lint_delay) + autocmd TextChanged * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) elseif l:text_changed is? 'insert' - autocmd TextChangedI * call ale#Queue(g:ale_lint_delay) + autocmd TextChangedI * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) endif if g:ale_lint_on_enter diff --git a/autoload/ale/filename_mapping.vim b/autoload/ale/filename_mapping.vim new file mode 100644 index 00000000..76d47acc --- /dev/null +++ b/autoload/ale/filename_mapping.vim @@ -0,0 +1,22 @@ +" Author: w0rp +" Description: Logic for handling mappings between files + +" Invert filesystem mappings so they can be mapped in reverse. +function! ale#filename_mapping#Invert(filename_mappings) abort + return map(copy(a:filename_mappings), '[v:val[1], v:val[0]]') +endfunction + +" Given a filename and some filename_mappings, map a filename. +function! ale#filename_mapping#Map(filename, filename_mappings) abort + let l:simplified_filename = ale#path#Simplify(a:filename) + + for [l:mapping_from, l:mapping_to] in a:filename_mappings + let l:mapping_from = ale#path#Simplify(l:mapping_from) + + if l:simplified_filename[:len(l:mapping_from) - 1] is# l:mapping_from + return l:mapping_to . l:simplified_filename[len(l:mapping_from):] + endif + endfor + + return a:filename +endfunction diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index b2a39444..3ad0a433 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -1,4 +1,8 @@ -call ale#Set('fix_on_save_ignore', {}) +" Author: w0rp +" Description: Functions for fixing code with programs, or other means. + +let g:ale_fix_on_save_ignore = get(g:, 'ale_fix_on_save_ignore', {}) +let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {}) " Apply fixes queued up for buffers which may be hidden. " Vim doesn't let you modify hidden buffers. @@ -110,7 +114,6 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort call s:RunFixer({ \ 'buffer': a:buffer, \ 'input': l:input, - \ 'output': l:output, \ 'callback_list': a:job_info.callback_list, \ 'callback_index': a:job_info.callback_index + 1, \}) @@ -125,6 +128,7 @@ function! s:RunJob(result, options) abort let l:buffer = a:options.buffer let l:input = a:options.input + let l:fixer_name = a:options.fixer_name if a:result is 0 || type(a:result) is v:t_list if type(a:result) is v:t_list @@ -150,7 +154,6 @@ function! s:RunJob(result, options) abort \ 'input': l:input, \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, - \ 'output': [], \}) return @@ -177,6 +180,7 @@ function! s:RunJob(result, options) abort \ 'read_buffer': l:read_buffer, \ 'input': l:input, \ 'log_output': 0, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name), \}) if empty(l:run_result) @@ -200,32 +204,22 @@ function! s:RunFixer(options) abort return endif - let l:ChainCallback = get(a:options, 'chain_callback', v:null) - - let l:Function = l:ChainCallback isnot v:null - \ ? ale#util#GetFunction(l:ChainCallback) - \ : a:options.callback_list[l:index] + let [l:fixer_name, l:Function] = a:options.callback_list[l:index] " Record new jobs started as fixer jobs. call setbufvar(l:buffer, 'ale_job_type', 'fixer') - if l:ChainCallback isnot v:null - " Chained commands accept (buffer, output, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 2 - \ ? call(l:Function, [l:buffer, a:options.output]) - \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)]) - else - " Regular fixer commands accept (buffer, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 1 - \ ? call(l:Function, [l:buffer]) - \ : call(l:Function, [l:buffer, copy(l:input)]) - endif + " Regular fixer commands accept (buffer, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 1 + \ ? call(l:Function, [l:buffer]) + \ : call(l:Function, [l:buffer, copy(l:input)]) call s:RunJob(l:result, { \ 'buffer': l:buffer, \ 'input': l:input, \ 'callback_list': a:options.callback_list, \ 'callback_index': l:index, + \ 'fixer_name': l:fixer_name, \}) endfunction @@ -293,16 +287,24 @@ function! s:GetCallbacks(buffer, fixing_flag, fixers) abort " Variables with capital characters are needed, or Vim will complain about " funcref variables. for l:Item in l:callback_list + " Try to capture the names of registered fixer names, so we can use + " them for filename mapping or other purposes later. + let l:fixer_name = v:null + if type(l:Item) is v:t_string let l:Func = ale#fix#registry#GetFunc(l:Item) if !empty(l:Func) + let l:fixer_name = l:Item let l:Item = l:Func endif endif try - call add(l:corrected_list, ale#util#GetFunction(l:Item)) + call add(l:corrected_list, [ + \ l:fixer_name, + \ ale#util#GetFunction(l:Item) + \]) catch /E475/ " Rethrow exceptions for failing to get a function so we can print " a friendly message about it. diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 94476ca9..d71668f2 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -160,11 +160,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['php'], \ 'description': 'Fix PHP files with php-cs-fixer.', \ }, -\ 'astyle': { +\ 'astyle': { \ 'function': 'ale#fixers#astyle#Fix', \ 'suggested_filetypes': ['c', 'cpp'], \ 'description': 'Fix C/C++ with astyle.', -\ }, +\ }, \ 'clangtidy': { \ 'function': 'ale#fixers#clangtidy#Fix', \ 'suggested_filetypes': ['c', 'cpp', 'objc'], @@ -375,6 +375,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['html', 'htmldjango'], \ 'description': 'Fix HTML files with html-beautify.', \ }, +\ 'dhall': { +\ 'function': 'ale#fixers#dhall#Fix', +\ 'suggested_filetypes': ['dhall'], +\ 'description': 'Fix Dhall files with dhall-format.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/dhall.vim b/autoload/ale/fixers/dhall.vim new file mode 100644 index 00000000..18f6006c --- /dev/null +++ b/autoload/ale/fixers/dhall.vim @@ -0,0 +1,23 @@ +" Author: Pat Brisbin +" Description: Integration of dhall-format with ALE. + +call ale#Set('dhall_format_executable', 'dhall') + +function! ale#fixers#dhall#GetExecutable(buffer) abort + let l:executable = ale#Var(a:buffer, 'dhall_format_executable') + + " Dhall is written in Haskell and commonly installed with Stack + return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'dhall') +endfunction + +function! ale#fixers#dhall#Fix(buffer) abort + let l:executable = ale#fixers#dhall#GetExecutable(a:buffer) + + return { + \ 'command': l:executable + \ . ' format' + \ . ' --inplace' + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/latexindent.vim b/autoload/ale/fixers/latexindent.vim index b0a0884a..54f1231e 100644 --- a/autoload/ale/fixers/latexindent.vim +++ b/autoload/ale/fixers/latexindent.vim @@ -10,9 +10,7 @@ function! ale#fixers#latexindent#Fix(buffer) abort return { \ 'command': ale#Escape(l:executable) - \ . ' -l -w' + \ . ' -l' \ . (empty(l:options) ? '' : ' ' . l:options) - \ . ' %t', - \ 'read_temporary_file': 1, \} endfunction diff --git a/autoload/ale/fixers/ocamlformat.vim b/autoload/ale/fixers/ocamlformat.vim index 9b7c3e12..b12d2eb9 100644 --- a/autoload/ale/fixers/ocamlformat.vim +++ b/autoload/ale/fixers/ocamlformat.vim @@ -5,14 +5,13 @@ call ale#Set('ocaml_ocamlformat_executable', 'ocamlformat') call ale#Set('ocaml_ocamlformat_options', '') function! ale#fixers#ocamlformat#Fix(buffer) abort - let l:filename = expand('#' . a:buffer . ':p') let l:executable = ale#Var(a:buffer, 'ocaml_ocamlformat_executable') let l:options = ale#Var(a:buffer, 'ocaml_ocamlformat_options') return { \ 'command': ale#Escape(l:executable) \ . (empty(l:options) ? '' : ' ' . l:options) - \ . ' --name=' . ale#Escape(l:filename) + \ . ' --name=%s' \ . ' -' \} endfunction diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim index 23120777..e0f4972e 100644 --- a/autoload/ale/fixers/prettier.vim +++ b/autoload/ale/fixers/prettier.vim @@ -34,6 +34,21 @@ function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort return a:output endfunction +function! ale#fixers#prettier#GetProjectRoot(buffer) abort + let l:config = ale#path#FindNearestFile(a:buffer, '.prettierignore') + + if !empty(l:config) + return fnamemodify(l:config, ':h') + endif + + " Fall back to the directory of the buffer + return fnamemodify(bufname(a:buffer), ':p:h') +endfunction + +function! ale#fixers#prettier#CdProjectRoot(buffer) abort + return ale#path#CdString(ale#fixers#prettier#GetProjectRoot(a:buffer)) +endfunction + function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort let l:executable = ale#fixers#prettier#GetExecutable(a:buffer) let l:options = ale#Var(a:buffer, 'javascript_prettier_options') @@ -97,7 +112,7 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort " 1.4.0 is the first version with --stdin-filepath if ale#semver#GTE(a:version, [1, 4, 0]) return { - \ 'command': ale#path#BufferCdString(a:buffer) + \ 'command': ale#fixers#prettier#CdProjectRoot(a:buffer) \ . ale#Escape(l:executable) \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' --stdin-filepath %s --stdin', diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim index d9615256..cdfb014a 100644 --- a/autoload/ale/fixers/rubocop.vim +++ b/autoload/ale/fixers/rubocop.vim @@ -29,8 +29,7 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort \ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '') \ . (!empty(l:options) ? ' ' . l:options : '') \ . (l:auto_correct_all ? ' --auto-correct-all' : ' --auto-correct') - \ . ' --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . a:buffer . ':p')) + \ . ' --force-exclusion --stdin %s' endfunction function! ale#fixers#rubocop#Fix(buffer) abort diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index fecfeed6..b483fc19 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -32,7 +32,7 @@ let s:default_ale_linter_aliases = { " " No linters are used for plaintext files by default. " -" Only cargo is enabled for Rust by default. +" Only cargo and rls are enabled for Rust by default. " rpmlint is disabled by default because it can result in code execution. " hhast is disabled by default because it executes code in the project root. " @@ -46,7 +46,7 @@ let s:default_ale_linters = { \ 'perl': ['perlcritic'], \ 'perl6': [], \ 'python': ['flake8', 'mypy', 'pylint', 'pyright'], -\ 'rust': ['cargo'], +\ 'rust': ['cargo', 'rls'], \ 'spec': [], \ 'text': [], \ 'vue': ['eslint', 'vls'], @@ -211,21 +211,17 @@ function! ale#linter#PreProcess(filetype, linter) abort " file on disk. let l:obj.lint_file = get(a:linter, 'lint_file', 0) - if !s:IsBoolean(l:obj.lint_file) - throw '`lint_file` must be `0` or `1`' + if !s:IsBoolean(l:obj.lint_file) && type(l:obj.lint_file) isnot v:t_func + throw '`lint_file` must be `0`, `1`, or a Function' endif " An option indicating that the buffer should be read. - let l:obj.read_buffer = get(a:linter, 'read_buffer', !l:obj.lint_file) + let l:obj.read_buffer = get(a:linter, 'read_buffer', 1) if !s:IsBoolean(l:obj.read_buffer) throw '`read_buffer` must be `0` or `1`' endif - if l:obj.lint_file && l:obj.read_buffer - throw 'Only one of `lint_file` or `read_buffer` can be `1`' - endif - let l:obj.aliases = get(a:linter, 'aliases', []) if type(l:obj.aliases) isnot v:t_list diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index f4a42bf4..db640654 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -265,7 +265,14 @@ function! s:StartLSP(options, address, executable, command) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) endif - let l:command = ale#command#FormatCommand(l:buffer, a:executable, a:command, 0, v:false)[1] + let l:command = ale#command#FormatCommand( + \ l:buffer, + \ a:executable, + \ a:command, + \ 0, + \ v:false, + \ [], + \)[1] let l:command = ale#job#PrepareCommand(l:buffer, l:command) let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command) endif diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 30550503..fed95ccd 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -24,6 +24,14 @@ function! ale#path#Simplify(path) abort return substitute(simplify(l:win_path), '^\\\+', '\', 'g') " no-custom-checks endfunction +" Simplify a path without a Windows drive letter. +" This function can be used for checking if paths are equal. +function! ale#path#RemoveDriveLetter(path) abort + return has('win32') && a:path[1:2] is# ':\' + \ ? ale#path#Simplify(a:path[2:]) + \ : ale#path#Simplify(a:path) +endfunction + " Given a buffer and a filename, find the nearest file by searching upwards " through the paths relative to the given buffer. function! ale#path#FindNearestFile(buffer, filename) abort @@ -74,15 +82,19 @@ endfunction function! ale#path#CdString(directory) abort if has('win32') return 'cd /d ' . ale#Escape(a:directory) . ' && ' - else - return 'cd ' . ale#Escape(a:directory) . ' && ' endif + + return 'cd ' . ale#Escape(a:directory) . ' && ' endfunction " Output 'cd && ' " This function can be used changing the directory for a linter command. function! ale#path#BufferCdString(buffer) abort - return ale#path#CdString(fnamemodify(bufname(a:buffer), ':p:h')) + if has('win32') + return 'cd /d %s:h && ' + endif + + return 'cd %s:h && ' endfunction " Return 1 if a path is an absolute path. @@ -95,7 +107,7 @@ function! ale#path#IsAbsolute(filename) abort return a:filename[:0] is# '/' || a:filename[1:2] is# ':\' endfunction -let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h')) +let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h')) " Given a filename, return 1 if the file represents some temporary file " created by Vim. diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim index faf45cb0..8b94aa7a 100644 --- a/autoload/ale/preview.vim +++ b/autoload/ale/preview.vim @@ -1,14 +1,22 @@ " Author: w0rp " Description: Preview windows for showing whatever information in. -if !has_key(s:, 'last_selection_list') - let s:last_selection_list = [] +if !has_key(s:, 'last__list') + let s:last_list = [] endif -if !has_key(s:, 'last_selection_open_in') - let s:last_selection_open_in = 'current-buffer' +if !has_key(s:, 'last_options') + let s:last_options = {} endif +function! ale#preview#SetLastSelection(item_list, options) abort + let s:last_list = a:item_list + let s:last_options = { + \ 'open_in': get(a:options, 'open_in', 'current-buffer'), + \ 'use_relative_paths': get(a:options, 'use_relative_paths', 0), + \} +endfunction + " Open a preview window and show some lines in it. " A second argument can be passed as a Dictionary with options. They are... " @@ -81,19 +89,14 @@ function! ale#preview#ShowSelection(item_list, ...) abort let b:ale_preview_item_list = a:item_list let b:ale_preview_item_open_in = get(l:options, 'open_in', 'current-buffer') - " Remove the last preview - let s:last_selection_list = b:ale_preview_item_list - let s:last_selection_open_in = b:ale_preview_item_open_in + " Remember preview state, so we can repeat it later. + call ale#preview#SetLastSelection(a:item_list, l:options) endfunction function! ale#preview#RepeatSelection() abort - if empty(s:last_selection_list) - return + if !empty(s:last_list) + call ale#preview#ShowSelection(s:last_list, s:last_options) endif - - call ale#preview#ShowSelection(s:last_selection_list, { - \ 'open_in': s:last_selection_open_in, - \}) endfunction function! s:Open(open_in) abort diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index 1f396377..05f11993 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -505,6 +505,13 @@ function! ale#util#SetBufferContents(buffer, lines) abort \ : a:lines let l:first_line_to_remove = len(l:new_lines) + 1 + " We'll temporarily make a buffer modifiable, to force edits. + let l:modifiable = getbufvar(a:buffer, '&modifiable') + + if !l:modifiable + call setbufvar(a:buffer, '&modifiable', 1) + endif + " Use a Vim API for setting lines in other buffers, if available. if l:has_bufline_api call setbufline(a:buffer, 1, l:new_lines) @@ -523,5 +530,9 @@ function! ale#util#SetBufferContents(buffer, lines) abort call setline(1, l:new_lines) endif + if !l:modifiable + call setbufvar(a:buffer, '&modifiable', 0) + endif + return l:new_lines endfunction diff --git a/doc/ale-development.txt b/doc/ale-development.txt index faa570c1..afd9798f 100644 --- a/doc/ale-development.txt +++ b/doc/ale-development.txt @@ -13,6 +13,7 @@ CONTENTS *ale-development-contents* 4. Testing ALE..........................|ale-development-tests| 4.1. Writing Linter Tests.............|ale-development-linter-tests| 4.2. Writing Fixer Tests..............|ale-development-fixer-tests| + 4.3. Running Tests in a Windows VM....|ale-development-windows-tests| =============================================================================== 1. Introduction *ale-development-introduction* @@ -170,6 +171,11 @@ will run all of the tests in Vader, Vint checks, and several Bash scripts for finding extra issues. Run `./run-tests --help` to see all of the options the script supports. Note that the script supports selecting particular test files. +Once you get used to dealing with Vim and NeoVim compatibility issues, you +probably want to use `./run-tests --fast -q` for running tests with only the +fastest available Vim version, and with success messages from tests +suppressed. + Generally write tests for any changes you make. The following types of tests are recommended for the following types of code. @@ -353,5 +359,81 @@ given the above setup are as follows. `AssertFixerNotExecuted` - Check that fixers will not be executed. +=============================================================================== +4.3 Running Tests in a Windows VM *ale-development-windows-tests* + +Tests are run for ALE in a build of Vim 8 for Windows via AppVeyor. These +tests can frequently break due to minor differences in paths and how escaping +is done for commands on Windows. If you are a Linux or Mac user, running these +tests locally can be difficult. Here is a process that will make that easier. + +First, you want to install a Windows image with VirtualBox. Install VirtualBox +and grab a VirtualBox image for Windows such as from here: +https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/ + +NOTE: If you need to enter a password for the virtual machine at any point, +the password is "Passw0rd!" without the double quotes. + +NOTE: If your trial period for Windows runs out, run the commands like the +wallpaper tells you to. + +Your virtual machine will need to have PowerShell installed. Before you go any +further, confirm that PowerShell is installed in your Windows virtual machine. + +Consult the VirtualBox documentation on how to install "Guest Additions." +You probably want to install "Guest Additions" for most things to work +properly. + +After you've loaded your virtual machine image, go into "Settings" for your +virtual machine, and "Shared Folders." Add a shared folder with the name +"ale", and set the "Folder Path" to the path to your ALE repository, for +example: "/home/w0rp/ale" + +Find out which drive letter "ale" has been mounted as in Windows. We'll use +"E:" as the drive letter, for example. Open the command prompt as an +administrator by typing in `cmd` in the start menu, right clicking on the +command prompt application, and clicking "Run as administrator." Click "Yes" +when prompted to ask if you're sure you want to run the command prompt. You +should type in the following command to mount the "ale" directory for testing, +where "E:" is replaced with your drive letter. > + + mklink /D C:\testplugin E: +< +Close the administrator Command Prompt, and try running the command +`type C:\testplugin\LICENSE` in a new Command Prompt which you are NOT running +as administrator. You should see the license for ALE in your terminal. After +you have confirmed that you have mounted ALE on your machine, search in the +Start Menu for "power shell," run PowerShell as an administrator, and issue +the following commands to install the correct Vim and Vader versions for +running tests. > + + Add-Type -A System.IO.Compression.FileSystem + + Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586w32.zip -OutFile C:\vim.zip + [IO.Compression.ZipFile]::ExtractToDirectory('C:\vim.zip', 'C:\vim') + rm C:\vim.zip + + Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586rt.zip -OutFile C:\rt.zip + [IO.Compression.ZipFile]::ExtractToDirectory('C:\rt.zip', 'C:\vim') + rm C:\rt.zip + + Invoke-WebRequest https://github.com/junegunn/vader.vim/archive/c6243dd81c98350df4dec608fa972df98fa2a3af.zip -OutFile C:\vader.zip + [IO.Compression.ZipFile]::ExtractToDirectory('C:\vader.zip', 'C:\') + mv C:\vader.vim-c6243dd81c98350df4dec608fa972df98fa2a3af C:\vader + rm C:\vader.zip +< +After you have finished installing everything, you can run all of the tests +in Windows by opening a Command Prompt NOT as an administrator by navigating +to the directory where you've mounted the ALE code, which must be named +`C:\testplugin`, and by running the `run-tests.bat` batch file. > + + cd C:\testplugin + run-tests +< +It will probably take several minutes for all of the tests to run. Be patient. +You can run a specific test by passing the filename as an argument to the +batch file, for example: `run-tests test/test_c_flag_parsing.vader` . This will +give you results much more quickly. + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt index 2c0222b7..f4b4e7b7 100644 --- a/doc/ale-rust.txt +++ b/doc/ale-rust.txt @@ -31,11 +31,11 @@ Integration Information 5. rustfmt -- If you have `rustfmt` installed, you can use it as a fixer to consistently reformat your Rust code. - Only cargo is enabled by default. To switch to using rustc instead of cargo, - configure |g:ale_linters| appropriately: > + Only cargo and rls are enabled by default. To switch to using rustc instead + of cargo, configure |g:ale_linters| appropriately: > " See the help text for the option for more information. - let g:ale_linters = {'rust': ['rustc']} + let g:ale_linters = {'rust': ['rustc', 'rls']} < Also note that rustc 1.12. or later is needed. @@ -60,6 +60,7 @@ g:ale_rust_analyzer_config *g:ale_rust_analyzer_config* Dictionary with configuration settings for rust-analyzer. + =============================================================================== cargo *ale-rust-cargo* @@ -252,23 +253,25 @@ g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes* > let g:ale_rust_ignore_error_codes = ['E0432', 'E0433'] + g:ale_rust_ignore_secondary_spans *g:ale_rust_ignore_secondary_spans* *b:ale_rust_ignore_secondary_spans* Type: Number Default: 0 - When set to 1, instructs the Rust error repporting to ignore secondary - spans. The problem with secondary spans is that they sometimes appear in - error messages before the main cause of the error, for example: > + When set to 1, instructs the Rust error reporting to ignore secondary spans. + The problem with secondary spans is that they sometimes appear in error + messages before the main cause of the error, for example: > 1 src/main.rs|98 col 5 error| this function takes 4 parameters but 5 - parameters were supplied: defined here + parameters were supplied: defined here 2 src/main.rs|430 col 32 error| this function takes 4 parameters but 5 - parameters were supplied: expected 4 parameters + parameters were supplied: expected 4 parameters < This is due to the sorting by line numbers. With this option set to 1, the 'defined here' span will not be presented. + =============================================================================== rustfmt *ale-rust-rustfmt* diff --git a/doc/ale-sql.txt b/doc/ale-sql.txt index 2807271b..398e24d3 100644 --- a/doc/ale-sql.txt +++ b/doc/ale-sql.txt @@ -3,7 +3,7 @@ ALE SQL Integration *ale-sql-options* =============================================================================== -pgformatter *ale-sql-pgformatter* +pgformatter *ale-sql-pgformatter* g:ale_sql_pgformatter_executable *g:ale_sql_pgformatter_executable* *b:ale_sql_pgformatter_executable* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index e1a91b20..8fe00a57 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -120,6 +120,8 @@ Notes: * `dartanalyzer`!! * `dartfmt`!! * `language_server` +* Dhall + * `dhall-format` * Dockerfile * `dockerfile_lint` * `hadolint` @@ -448,6 +450,7 @@ Notes: * `sqlfmt` * `sqlformat` * `sqlint` + * `sql-lint` * Stylus * `stylelint` * SugarSS diff --git a/doc/ale.txt b/doc/ale.txt index df55b544..f3b83df3 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -9,8 +9,9 @@ CONTENTS *ale-contents* 1. Introduction.........................|ale-introduction| 2. Supported Languages & Tools..........|ale-support| 3. Linting..............................|ale-lint| - 3.1 Adding Language Servers...........|ale-lint-language-servers| - 3.2 Other Sources.....................|ale-lint-other-sources| + 3.1 Linting On Other Machines.........|ale-lint-other-machines| + 3.2 Adding Language Servers...........|ale-lint-language-servers| + 3.3 Other Sources.....................|ale-lint-other-sources| 4. Fixing Problems......................|ale-fix| 5. Language Server Protocol Support.....|ale-lsp| 5.1 Completion........................|ale-completion| @@ -148,7 +149,61 @@ ALE offers several options for controlling which linters are run. ------------------------------------------------------------------------------- -3.1 Adding Language Servers *ale-lint-language-servers* +3.1 Linting On Other Machines *ale-lint-other-machines* + +ALE offers support for running linters or fixers on files you are editing +locally on other machines, so long as the other machine has access the file +you are editing. This could be a linter or fixer run inside of a Docker image, +running in a virtual machine, running on a remote server, etc. + +In order to run tools on other machines, you will need to configure your tools +to run via scripts that execute commands on those machines, such as by setting +the ALE `_executable` options for those tools to a path for a script to run, +or by using |g:ale_command_wrapper| to specify a script to wrap all commands +that are run by ALE, before they are executed. For tools that ALE runs where +ALE looks for locally installed executables first, you may need to set the +`_use_global` options for those tools to `1`, or you can set +|g:ale_use_global_executables| to `1` before ALE is loaded to only use global +executables for all tools. + +In order for ALE to properly lint or fix files which are running on another +file system, you must provide ALE with |List|s of strings for mapping paths to +and from your local file system and the remote file system, such as the file +system of your Docker container. See |g:ale_filename_mappings| for all of the +different ways these filename mappings can be configured. + +For example, you might configure `pylint` to run via Docker by creating a +script like so. > + + #!/usr/bin/env bash + + exec docker run --rm -v "$(pwd):/data" cytopia/pylint "$@" +< +With the above script in mind, you might configure ALE to lint your Python +project with `pylint` by providing the path to the script to execute, and +mappings which describe how to between the two file systems in your +`python.vim` |ftplugin| file, like so: > + + if expand('%:p') =~# '^/home/w0rp/git/test-pylint/' + let b:ale_linters = ['pylint'] + let b:ale_python_pylint_use_global = 1 + " This is the path to the script above. + let b:ale_python_pylint_executable = '/home/w0rp/git/test-pylint/pylint.sh' + " /data matches the path in Docker. + let b:ale_filename_mappings = { + \ 'pylint': [ + \ ['/home/w0rp/git/test-pylint', '/data'], + \ ], + \} + endif +< + +You might consider using a Vim plugin for loading Vim configuration files +specific to each project, if you have a lot of projects to manage. + + +------------------------------------------------------------------------------- +3.2 Adding Language Servers *ale-lint-language-servers* ALE comes with many default configurations for language servers, so they can be detected and run automatically. ALE can connect to other language servers @@ -189,7 +244,7 @@ address to connect to instead. > ------------------------------------------------------------------------------- -3.2 Other Sources *ale-lint-other-sources* +3.3 Other Sources *ale-lint-other-sources* Problems for a buffer can be taken from other sources and rendered by ALE. This allows ALE to be used in combination with other plugins which also want @@ -287,6 +342,8 @@ are supported for running the commands. file will be created, containing the lines from the file after previous adjustment have been done. + See |ale-command-format-strings| for formatting options. + `read_temporary_file` When set to `1`, ALE will read the contents of the temporary file created for `%t`. This option can be used for commands which need to modify some file on disk in @@ -356,6 +413,10 @@ by default. Fixers can be disabled on save with |g:ale_fix_on_save_ignore|. They will still be run when you manually run |ALEFix|. +Fixers can be run on another machines, just like linters, such as fixers run +from a Docker container, running in a virtual machine, running a remote +server, etc. See |ale-lint-other-machines|. + =============================================================================== 5. Language Server Protocol Support *ale-lsp* @@ -501,15 +562,9 @@ displayed. ------------------------------------------------------------------------------- 5.4 Find References *ale-find-references* -ALE supports finding references for symbols though any enabled LSP linters. -ALE will display a preview window showing the places where a symbol is -referenced in a codebase when a command is run. The following commands are -supported: - -|ALEFindReferences| - Find references for the word under the cursor. - -Options: - `-relative` Show file paths in the results relative to the working dir +ALE supports finding references for symbols though any enabled LSP linters +with the |ALEFindReferences| command. See the documentation for the command +for a full list of options. ------------------------------------------------------------------------------- 5.5 Hovering *ale-hover* @@ -552,13 +607,9 @@ Documentation for symbols at the cursor can be retrieved using the ------------------------------------------------------------------------------- 5.6 Symbol Search *ale-symbol-search* -ALE supports searching for workspace symbols via LSP linters. The following -commands are supported: - -|ALESymbolSearch| - Search for symbols in the workspace. - -Options: - `-relative` Show file paths in the results relative to the working dir +ALE supports searching for workspace symbols via LSP linters with the +|ALESymbolSearch| command. See the documentation for the command +for a full list of options. =============================================================================== 6. Global Options *ale-options* @@ -810,7 +861,7 @@ g:ale_default_navigation *g:ale_default_navigation* Default: `'buffer'` The default method for navigating away from the current buffer to another - buffer, such as for |ALEFindReferences:|, or |ALEGoToDefinition|. + buffer, such as for |ALEFindReferences|, or |ALEGoToDefinition|. g:ale_disable_lsp *g:ale_disable_lsp* @@ -1121,7 +1172,7 @@ g:ale_list_window_size *g:ale_list_window_size* g:ale_lint_delay *g:ale_lint_delay* - + *b:ale_lint_delay* Type: |Number| Default: `200` @@ -1129,6 +1180,9 @@ g:ale_lint_delay *g:ale_lint_delay* be run after text is changed. This option is only meaningful with the |g:ale_lint_on_text_changed| variable set to `always`, `insert`, or `normal`. + A buffer-local option, `b:ale_lint_delay`, can be set to change the delay + for different buffers, such as in |ftplugin| files. + g:ale_lint_on_enter *g:ale_lint_on_enter* @@ -1286,6 +1340,90 @@ g:ale_linter_aliases *g:ale_linter_aliases* < No linters will be loaded when the buffer's filetype is empty. + +g:ale_filename_mappings *g:ale_filename_mappings* + *b:ale_filename_mappings* + + Type: |Dictionary| or |List| + Default: `{}` + + Either a |Dictionary| mapping a linter or fixer name, as displayed in + |:ALEInfo|, to a |List| of two-item |List|s for filename mappings, or just a + |List| of two-item |List|s. When given some paths to files, the value of + this setting will be used to convert filenames on a local file system to + filenames on some remote file system, such as paths in a Docker image, + virtual machine, or network drive. + + For example: > + + let g:ale_filename_mappings = { + \ 'pylint': [ + \ ['/home/john/proj', '/data'], + \ ], + \} +< + With the above configuration, a filename such as `/home/john/proj/foo.py` + will be provided to the linter/fixer as `/data/foo.py`, and paths parsed + from linter results such as `/data/foo.py` will be converted back to + `/home/john/proj/foo.py`. + + You can use `*` as to apply a |List| of filename mappings to all other + linters or fixers not otherwise matched. > + + " Use one List of paths for pylint. + " Use another List of paths for everything else. + let g:ale_filename_mappings = { + \ 'pylint': [ + \ ['/home/john/proj', '/data'], + \ ], + \ '*': [ + \ ['/home/john/proj', '/other-data'], + \ ], + \} +< + If you just want every single linter or fixer to use the same filename + mapping, you can just use a |List|. > + + " Same as above, but for ALL linters and fixers. + let g:ale_filename_mappings = [ + \ ['/home/john/proj', '/data'], + \] +< + You can provide many such filename paths for multiple projects. Paths are + matched by checking if the start of a file path matches the given strings, + in a case-sensitive manner. Earlier entries in the |List| will be tried + before later entries when mapping to a given file system. + + Buffer-local options can be set to the same values to override the global + options, such as in |ftplugin| files. + + NOTE: Only fixers registered with a short name can support filename mapping + by their fixer names. See |ale-fix|. Filename mappings set for all tools by + using only a |List| for the setting will also be applied to fixers not in + the registry. + + NOTE: In order for this filename mapping to work correctly, linters and + fixers must exclusively determine paths to files to lint or fix via ALE + command formatting as per |ale-command-format-strings|, and paths parsed + from linter files must be provided in `filename` keys if a linter returns + results for more than one file at a time, as per |ale-loclist-format|. If + you discover a linter or fixer which does not behave properly, please report + it as an issue. + + If you are running a linter or fixer through Docker or another remote file + system, you may have to mount your temporary directory, which you can + discover with the following command: > + + :echo fnamemodify(tempname(), ':h:h') +< + You should provide a mapping from this temporary directory to whatever you + mount this directory to in Docker, or whatever remote file system you are + working with. + + You can inspect the filename mappings ALE will use with the + |ale#GetFilenameMappings()| function. + + g:ale_linters *g:ale_linters* *b:ale_linters* Type: |Dictionary| @@ -1306,7 +1444,7 @@ g:ale_linters *g:ale_linters* \ 'perl': ['perlcritic'], \ 'perl6': [], \ 'python': ['flake8', 'mypy', 'pylint', 'pyright'], - \ 'rust': ['cargo'], + \ 'rust': ['cargo', 'rls'], \ 'spec': [], \ 'text': [], \ 'vue': ['eslint', 'vls'], @@ -2767,13 +2905,20 @@ ALEFindReferences *ALEFindReferences* The default method used for navigating to a new location can be changed by modifying |g:ale_default_navigation|. + You can add `-relative` to the command to view results with relatives paths, + instead of absolute paths. + The selection can be opened again with the |ALERepeatSelection| command. You can jump back to the position you were at before going to a reference of something with jump motions like CTRL-O. See |jump-motions|. A plug mapping `(ale_find_references)` is defined for this command. + You can define additional plug mapping with any additional options you want + like so: > + nnoremap (my_mapping) :ALEFindReferences -relative +< ALEFix *ALEFix* @@ -2885,14 +3030,17 @@ ALESymbolSearch `` *ALESymbolSearch* The arguments provided to this command will be used as a search query for finding symbols in the workspace, such as functions, types, etc. + You can add `-relative` to the command to view results with relatives paths, + instead of absolute paths. + *:ALELint* ALELint *ALELint* Run ALE once for the current buffer. This command can be used to run ALE manually, instead of automatically, if desired. - This command will also run linters where `lint_file` is set to `1`, or in - other words linters which check the file instead of the Vim buffer. + This command will also run linters where `lint_file` is evaluates to `1`, + meaning linters which check the file instead of the Vim buffer. A plug mapping `(ale_lint)` is defined for this command. @@ -3074,6 +3222,15 @@ ale#Env(variable_name, value) *ale#Env()* 'set VAR="some value" && command' # On Windows +ale#GetFilenameMappings(buffer, name) *ale#GetFilenameMappings()* + + Given a `buffer` and the `name` of either a linter for fixer, return a + |List| of two-item |List|s that describe mapping to and from the local and + foreign file systems for running a particular linter or fixer. + + See |g:ale_filename_mappings| for details on filename mapping. + + ale#Has(feature) *ale#Has()* Return `1` if ALE supports a given feature, like |has()| for Vim features. @@ -3096,9 +3253,9 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()* The linters will always be run in the background. Calling this function again from the same buffer - An optional `linting_flag` argument can be given. If `linting_flag` - is `'lint_file'`, then linters where the `lint_file` option is set to `1` will be - run. Linters with `lint_file` set to `1` are not run by default. + An optional `linting_flag` argument can be given. If `linting_flag` is + `'lint_file'`, then linters where the `lint_file` option evaluates to `1` + will be run. Otherwise, those linters will not be run. An optional `buffer_number` argument can be given for specifying the buffer to check. The active buffer (`bufnr('')`) will be checked by default. @@ -3188,23 +3345,36 @@ ale#command#Run(buffer, command, callback, [options]) *ale#command#Run()* < The following `options` can be provided. - `output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or `'none`' for - selecting which output streams to read lines from. + `output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or + `'none`' for selecting which output streams to read + lines from. - The default is `'stdout'` + The default is `'stdout'` - `executable` - An executable for formatting into `%e` in the command. - If this option is not provided, formatting commands with - `%e` will not work. + `executable` - An executable for formatting into `%e` in the + command. If this option is not provided, formatting + commands with `%e` will not work. - `read_buffer` - If set to `1`, the buffer will be piped into the - command. + `read_buffer` - If set to `1`, the buffer will be piped into the + command. - The default is `0`. + The default is `0`. + + `input` - When creating temporary files with `%t` or piping + text into a command `input` can be set to a |List| of + text to use instead of the buffer's text. + + `filename_mappings` - A |List| of two-item |List|s describing filename + mappings to apply for formatted filenames in the + command string, as per |g:ale_filename_mappings|. + + If the call to this function is being used for a + linter or fixer, the mappings should be provided with + this option, and can be retrieved easily with + |ale#GetFilenameMappings()|. + + The default is `[]`. - `input` - When creating temporary files with `%t` or piping text - into a command `input` can be set to a |List| of text to - use instead of the buffer's text. ale#command#EscapeCommandPart(command_part) *ale#command#EscapeCommandPart()* @@ -3419,24 +3589,30 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* if a command manually reads from a temporary file instead, etc. + This option behaves as if it was set to `0` when the + `lint_file` option evaluates to `1`. + *ale-lint-file* - `lint_file` A |Number| (`0` or `1`) indicating whether a command - should read the file instead of the Vim buffer. This - option can be used for linters which must check the - file on disk, and which cannot check a Vim buffer - instead. + `lint_file` A |Number| (`0` or `1`), or a |Funcref| for a function + accepting a buffer number for computing either `0` or + `1`, indicating whether a command should read the file + instead of the Vim buffer. This option can be used + for linters which must check the file on disk, and + which cannot check a Vim buffer instead. - Linters set with this option will not be run as a - user types, per |g:ale_lint_on_text_changed|. Linters - will instead be run only when events occur against - the file on disk, including |g:ale_lint_on_enter| - and |g:ale_lint_on_save|. Linters with this option - set to `1` will also be run when linters are run - manually, per |ALELintPost-autocmd|. + The result can be computed with |ale#command#Run()|. - When this option is set to `1`, `read_buffer` will - be set automatically to `0`. The two options cannot - be used together. + Linters where the eventual value of this option + evaluates to `1` will not be run as a user types, per + |g:ale_lint_on_text_changed|. Linters will instead be + run only when events occur against the file on disk, + including |g:ale_lint_on_enter| and + |g:ale_lint_on_save|. Linters where this option + evaluates to `1` will also be run when the |ALELint| + command is run. + + When this option is evaluates to `1`, ALE will behave + as if `read_buffer` was set to `0`. *ale-lsp-linters* `lsp` A |String| for defining LSP (Language Server Protocol) @@ -3575,6 +3751,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* command, so literal character sequences `%s` and `%t` can be escaped by using `%%s` and `%%t` instead, etc. + Some |filename-modifiers| can be applied to `%s` and `%t`. Only `:h`, `:t`, + `:r`, and `:e` may be applied, other modifiers will be ignored. Filename + modifiers can be applied to the format markers by placing them after them. + + For example: > + 'command': '%s:h %s:e %s:h:t', +< + Given a path `/foo/baz/bar.txt`, the above command string will generate + something akin to `'/foo/baz' 'txt' 'baz'` + If a callback for a command generates part of a command string which might possibly contain `%%`, `%s`, `%t`, or `%e`, where the special formatting behavior is not desired, the |ale#command#EscapeCommandPart()| function can diff --git a/plugin/ale.vim b/plugin/ale.vim index 0651dd4b..6a947ea7 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -97,6 +97,10 @@ let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 0) " should be used instead. let g:ale_enabled = get(g:, 'ale_enabled', 1) +" A Dictionary mapping linter or fixer names to Arrays of two-item Arrays +" mapping filename paths from one system to another. +let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {}) + " These flags dictates if ale uses the quickfix or the loclist (loclist is the " default, quickfix overrides loclist). let g:ale_set_loclist = get(g:, 'ale_set_loclist', 1) diff --git a/supported-tools.md b/supported-tools.md index f15bd79a..86caf816 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -16,7 +16,7 @@ formatting. | Key | Definition | | ------------- | -------------------------------- | -| :floppy_disk: | Only checked when saved to disk | +| :floppy_disk: | May only run on files on disk | | :warning: | Disabled by default | --- @@ -129,6 +129,8 @@ formatting. * [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) :floppy_disk: * [dartfmt](https://github.com/dart-lang/sdk/tree/master/utils/dartfmt) * [language_server](https://github.com/natebosch/dart_language_server) +* Dhall + * [dhall-format](https://github.com/dhall-lang/dhall-lang) * Dockerfile * [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint) * [hadolint](https://github.com/hadolint/hadolint) @@ -457,6 +459,7 @@ formatting. * [sqlfmt](https://github.com/jackc/sqlfmt) * [sqlformat](https://github.com/andialbrecht/sqlparse) * [sqlint](https://github.com/purcell/sqlint) + * [sql-lint](https://github.com/joereynolds/sql-lint) * Stylus * [stylelint](https://github.com/stylelint/stylelint) * SugarSS diff --git a/test/command_callback/test_ada_gcc_command_callbacks.vader b/test/command_callback/test_ada_gcc_command_callbacks.vader index de6e355e..906b31a4 100644 --- a/test/command_callback/test_ada_gcc_command_callbacks.vader +++ b/test/command_callback/test_ada_gcc_command_callbacks.vader @@ -18,11 +18,10 @@ After: call ale#assert#TearDownLinterTest() Execute(The executable should be configurable): - AssertLinter 'gcc', \ ale#Escape('gcc') . ' -x ada -c -gnatc' \ . ' -o ' . b:out_file - \ . ' -I ' . ale#Escape(getcwd()) + \ . ' -I %s:h' \ . ' -gnatwa -gnatq %t' let b:ale_ada_gcc_executable = 'foo' @@ -30,15 +29,14 @@ Execute(The executable should be configurable): AssertLinter 'foo', \ ale#Escape('foo') . ' -x ada -c -gnatc' \ . ' -o ' . b:out_file - \ . ' -I ' . ale#Escape(getcwd()) + \ . ' -I %s:h' \ . ' -gnatwa -gnatq %t' Execute(The options should be configurable): - let g:ale_ada_gcc_options = '--foo --bar' AssertLinter 'gcc', \ ale#Escape('gcc') . ' -x ada -c -gnatc' \ . ' -o ' . b:out_file - \ . ' -I ' . ale#Escape(getcwd()) + \ . ' -I %s:h' \ . ' --foo --bar %t' diff --git a/test/command_callback/test_asm_gcc_command_callbacks.vader b/test/command_callback/test_asm_gcc_command_callbacks.vader index 42606ec0..5976b5f2 100644 --- a/test/command_callback/test_asm_gcc_command_callbacks.vader +++ b/test/command_callback/test_asm_gcc_command_callbacks.vader @@ -3,7 +3,7 @@ Before: call ale#test#SetFilename('test.cpp') let b:command_tail = ' -x assembler' \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') - \ . '-iquote ' . ale#Escape(g:dir) + \ . '-iquote %s:h' \ . ' -Wall -' After: diff --git a/test/command_callback/test_c_cc_command_callbacks.vader b/test/command_callback/test_c_cc_command_callbacks.vader index 9d71d941..c8c2de7d 100644 --- a/test/command_callback/test_c_cc_command_callbacks.vader +++ b/test/command_callback/test_c_cc_command_callbacks.vader @@ -23,7 +23,7 @@ Before: let b:command_tail = ' -S -x c' \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(getcwd()) + \ . ' -iquote %s:h' \ . ' -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 8384a659..3c2bd79b 100644 --- a/test/command_callback/test_c_import_paths.vader +++ b/test/command_callback/test_c_import_paths.vader @@ -43,7 +43,7 @@ Execute(The C cc linter should include 'include' directories for projects with a AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -55,7 +55,7 @@ Execute(The C cc linter should include 'include' directories for projects with a AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include')) \ . ' -' @@ -67,7 +67,7 @@ Execute(The C cc linter should include root directories for projects with .h fil AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -79,7 +79,7 @@ Execute(The C cc linter should include root directories for projects with .hpp f AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' @@ -101,7 +101,7 @@ Execute(The C++ cc linter should include 'include' directories for projects with AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include')) \ . ' -' @@ -113,7 +113,7 @@ Execute(The C++ cc linter should include 'include' directories for projects with AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include')) \ . ' -' @@ -125,7 +125,7 @@ Execute(The C++ cc linter should include root directories for projects with .h f AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project')) \ . ' -' @@ -137,7 +137,7 @@ Execute(The C++ cc linter should include root directories for projects with .hpp AssertLinter 'gcc', \ ale#Escape('gcc') \ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir')) + \ . ' -iquote %s:h' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project')) \ . ' -' diff --git a/test/command_callback/test_cpp_gcc_command_callbacks.vader b/test/command_callback/test_cpp_cc_command_callbacks.vader similarity index 97% rename from test/command_callback/test_cpp_gcc_command_callbacks.vader rename to test/command_callback/test_cpp_cc_command_callbacks.vader index 930cc68a..dec3a07c 100644 --- a/test/command_callback/test_cpp_gcc_command_callbacks.vader +++ b/test/command_callback/test_cpp_cc_command_callbacks.vader @@ -23,7 +23,7 @@ Before: let b:command_tail = ' -S -x c++' \ . ' -o ' . (has('win32') ? 'nul': '/dev/null') - \ . ' -iquote ' . ale#Escape(getcwd()) + \ . ' -iquote %s:h' \ . ' -std=c++14 -Wall -' After: diff --git a/test/command_callback/test_gobuild_command_callback.vader b/test/command_callback/test_gobuild_command_callback.vader index fdf23866..063f3f2f 100644 --- a/test/command_callback/test_gobuild_command_callback.vader +++ b/test/command_callback/test_gobuild_command_callback.vader @@ -11,14 +11,14 @@ After: Execute(The default commands should be correct): AssertLinter 'go', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'go test -c -o /dev/null ./' Execute(Go environment variables should be supported): let b:ale_go_go111module = 'on' AssertLinter 'go', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Env('GO111MODULE', 'on') \ . 'go test -c -o /dev/null ./' @@ -28,7 +28,7 @@ Execute(Extra options should be supported): let g:ale_go_gobuild_options = '--foo-bar' AssertLinter 'go', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'go test --foo-bar -c -o /dev/null ./' let g:ale_go_gobuild_options = '' @@ -37,5 +37,5 @@ Execute(The executable should be configurable): let g:ale_go_go_executable = 'foobar' AssertLinter 'foobar', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'foobar test -c -o /dev/null ./' diff --git a/test/command_callback/test_gofmt_command_callback.vader b/test/command_callback/test_gofmt_command_callback.vader index 4da1f6c8..88b2e6b0 100644 --- a/test/command_callback/test_gofmt_command_callback.vader +++ b/test/command_callback/test_gofmt_command_callback.vader @@ -1,5 +1,8 @@ Before: Save g:ale_go_go111module + Save b:ale_go_go111module + + let b:ale_go_go111module = '' call ale#assert#SetUpLinterTest('go', 'gofmt') call ale#test#SetFilename('../go_files/testfile2.go') diff --git a/test/command_callback/test_golangci_lint_command_callback.vader b/test/command_callback/test_golangci_lint_command_callback.vader index 7f1e2ac4..37fb1f7d 100644 --- a/test/command_callback/test_golangci_lint_command_callback.vader +++ b/test/command_callback/test_golangci_lint_command_callback.vader @@ -13,7 +13,7 @@ After: Execute(The golangci-lint defaults should be correct): AssertLinter 'golangci-lint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('golangci-lint') \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --enable-all' @@ -22,7 +22,7 @@ Execute(The golangci-lint callback should use a configured executable): let b:ale_go_golangci_lint_executable = 'something else' AssertLinter 'something else', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('something else') \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --enable-all' @@ -31,7 +31,7 @@ Execute(The golangci-lint callback should use configured options): let b:ale_go_golangci_lint_options = '--foobar' AssertLinter 'golangci-lint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('golangci-lint') \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --foobar' @@ -40,7 +40,7 @@ Execute(The golangci-lint callback should support environment variables): let b:ale_go_go111module = 'on' AssertLinter 'golangci-lint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Env('GO111MODULE', 'on') \ . ale#Escape('golangci-lint') \ . ' run ' . ale#Escape(expand('%' . ':t')) @@ -50,5 +50,5 @@ Execute(The golangci-lint `lint_package` option should use the correct command): let b:ale_go_golangci_lint_package = 1 AssertLinter 'golangci-lint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('golangci-lint') . ' run --enable-all' diff --git a/test/command_callback/test_gometalinter_command_callback.vader b/test/command_callback/test_gometalinter_command_callback.vader index d922efc6..567997d8 100644 --- a/test/command_callback/test_gometalinter_command_callback.vader +++ b/test/command_callback/test_gometalinter_command_callback.vader @@ -13,7 +13,7 @@ After: Execute(The gometalinter defaults should be correct): AssertLinter 'gometalinter', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('gometalinter') \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) \ . ' .' @@ -22,7 +22,7 @@ Execute(The gometalinter callback should use a configured executable): let b:ale_go_gometalinter_executable = 'something else' AssertLinter 'something else', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('something else') \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) \ . ' .' @@ -31,7 +31,7 @@ Execute(The gometalinter callback should use configured options): let b:ale_go_gometalinter_options = '--foobar' AssertLinter 'gometalinter', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('gometalinter') \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) \ . ' --foobar' . ' .' @@ -40,7 +40,7 @@ Execute(The gometalinter should use configured environment variables): let b:ale_go_go111module = 'off' AssertLinter 'gometalinter', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Env('GO111MODULE', 'off') \ . ale#Escape('gometalinter') \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) @@ -50,5 +50,5 @@ Execute(The gometalinter `lint_package` option should use the correct command): let b:ale_go_gometalinter_lint_package = 1 AssertLinter 'gometalinter', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('gometalinter') . ' .' diff --git a/test/command_callback/test_gosimple_command_callback.vader b/test/command_callback/test_gosimple_command_callback.vader index ee89eed8..b006f783 100644 --- a/test/command_callback/test_gosimple_command_callback.vader +++ b/test/command_callback/test_gosimple_command_callback.vader @@ -11,11 +11,12 @@ After: Execute(The default gosimple command should be correct): AssertLinter 'gosimple', - \ ale#path#CdString(expand('%:p:h')) . ' gosimple .' + \ ale#path#BufferCdString(bufnr('')) + \ . ' gosimple .' Execute(The gosimple command should support Go environment variables): let b:ale_go_go111module = 'on' AssertLinter 'gosimple', - \ ale#path#CdString(expand('%:p:h')) . ' ' - \ . ale#Env('GO111MODULE', 'on') . 'gosimple .' + \ ale#path#BufferCdString(bufnr('')) + \ . ' ' . ale#Env('GO111MODULE', 'on') . 'gosimple .' diff --git a/test/command_callback/test_gotype_command_callback.vader b/test/command_callback/test_gotype_command_callback.vader index 1334fcff..204197d9 100644 --- a/test/command_callback/test_gotype_command_callback.vader +++ b/test/command_callback/test_gotype_command_callback.vader @@ -11,7 +11,8 @@ After: Execute(The default gotype command should be correct): AssertLinter 'gotype', - \ ale#path#CdString(expand('%:p:h')) . ' gotype -e .' + \ ale#path#BufferCdString(bufnr('')) + \ . ' gotype -e .' Execute(The gotype callback should ignore test files): call ale#test#SetFilename('bla_test.go') @@ -22,6 +23,6 @@ Execute(The gotype callback should support Go environment variables): let b:ale_go_go111module = 'on' AssertLinter 'gotype', - \ ale#path#CdString(expand('%:p:h')) . ' ' - \ . ale#Env('GO111MODULE', 'on') + \ ale#path#BufferCdString(bufnr('')) + \ . ' ' . ale#Env('GO111MODULE', 'on') \ . 'gotype -e .' diff --git a/test/command_callback/test_govet_command_callback.vader b/test/command_callback/test_govet_command_callback.vader index 59022180..0e1ea092 100644 --- a/test/command_callback/test_govet_command_callback.vader +++ b/test/command_callback/test_govet_command_callback.vader @@ -13,22 +13,22 @@ After: call ale#assert#TearDownLinterTest() Execute(The default command should be correct): - AssertLinter 'go', ale#path#CdString(expand('%:p:h')) . ' go vet .' + AssertLinter 'go', ale#path#BufferCdString(bufnr('')) . ' go vet .' Execute(Extra options should be supported): let g:ale_go_govet_options = '--foo-bar' - AssertLinter 'go', ale#path#CdString(expand('%:p:h')) . ' go vet --foo-bar .' + AssertLinter 'go', ale#path#BufferCdString(bufnr('')) . ' go vet --foo-bar .' Execute(The executable should be configurable): let g:ale_go_go_executable = 'foobar' - AssertLinter 'foobar', ale#path#CdString(expand('%:p:h')) . ' foobar vet .' + AssertLinter 'foobar', ale#path#BufferCdString(bufnr('')) . ' foobar vet .' Execute(Go environment variables should be supported): let b:ale_go_go111module = 'on' AssertLinter 'go', - \ ale#path#CdString(expand('%:p:h')) . ' ' + \ ale#path#BufferCdString(bufnr('')) . ' ' \ . ale#Env('GO111MODULE', 'on') \ . 'go vet .' diff --git a/test/command_callback/test_graphql_gqlint_command_callbacks.vader b/test/command_callback/test_graphql_gqlint_command_callbacks.vader index 0f4e9770..e8ed0e5d 100644 --- a/test/command_callback/test_graphql_gqlint_command_callbacks.vader +++ b/test/command_callback/test_graphql_gqlint_command_callbacks.vader @@ -6,6 +6,6 @@ After: Execute(The linter should run from the directory of the file in the buffer): AssertLinter 'gqlint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'gqlint --reporter=simple' \ . ' %t' diff --git a/test/command_callback/test_javac_command_callback.vader b/test/command_callback/test_javac_command_callback.vader index d2eebf7a..ac898e5f 100644 --- a/test/command_callback/test_javac_command_callback.vader +++ b/test/command_callback/test_javac_command_callback.vader @@ -3,7 +3,7 @@ Before: call ale#test#SetFilename('dummy.java') let g:cp_sep = has('unix') ? ':' : ';' - let g:prefix = ale#path#CdString(expand('%:p:h')) + let g:prefix = ale#path#BufferCdString(bufnr('')) \ . ale#Escape('javac') . ' -Xlint' function! GetCommand(previous_output) abort @@ -51,7 +51,7 @@ Execute(The executable should be configurable): let g:ale_java_javac_executable = 'foobar' AssertLinter 'foobar', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('foobar') . ' -Xlint' \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t' @@ -197,7 +197,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones) let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {}) AssertEqual - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'), @@ -210,7 +211,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones) let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {}) AssertEqual - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'), @@ -223,7 +225,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones) let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {}) AssertEqual - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/') @@ -238,7 +241,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones) let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {}) AssertEqual - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'), @@ -253,7 +257,8 @@ Execute(The javac callback should detect source directories): call ale#engine#InitBufferInfo(bufnr('')) AssertLinter 'javac', - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape( \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/') \ ) @@ -272,7 +277,8 @@ Execute(The javac callback should combine detected source directories and classp \], {}) AssertEqual - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -cp ' . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep)) \ . ' -sourcepath ' . ale#Escape( \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/') @@ -294,7 +300,8 @@ Execute(The javac callback should include src/test/java for test paths): call ale#engine#InitBufferInfo(bufnr('')) AssertLinter 'javac', - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths/src/test/java/'), @@ -307,7 +314,8 @@ Execute(The javac callback should include src/main/jaxb when available): call ale#engine#InitBufferInfo(bufnr('')) AssertLinter 'javac', - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/java/'), \ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/jaxb/'), @@ -320,7 +328,8 @@ Execute(The javac callback should add -sourcepath even if src/java/main doesn't call ale#engine#InitBufferInfo(bufnr('')) AssertLinter 'javac', - \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint' + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('javac') . ' -Xlint' \ . ' -sourcepath ' . ale#Escape(join([ \ ale#path#Simplify(g:dir . '/java_paths_no_main/src/test/java/'), \ ], g:cp_sep)) diff --git a/test/command_callback/test_lintr_command_callback.vader b/test/command_callback/test_lintr_command_callback.vader index 187d3875..ac4b419b 100644 --- a/test/command_callback/test_lintr_command_callback.vader +++ b/test/command_callback/test_lintr_command_callback.vader @@ -6,7 +6,7 @@ After: Execute(The default lintr command should be correct): AssertLinter 'Rscript', - \ ale#path#CdString(getcwd()) + \ ale#path#BufferCdString(bufnr('')) \ . 'Rscript --vanilla -e ' \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' \ . 'lint(cache = FALSE, commandArgs(TRUE), ' @@ -17,7 +17,7 @@ Execute(The lintr options should be configurable): let b:ale_r_lintr_options = 'with_defaults(object_usage_linter = NULL)' AssertLinter 'Rscript', - \ ale#path#CdString(getcwd()) + \ ale#path#BufferCdString(bufnr('')) \ . 'Rscript --vanilla -e ' \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' \ . 'lint(cache = FALSE, commandArgs(TRUE), ' @@ -28,7 +28,7 @@ Execute(If the lint_package flag is set, lintr::lint_package should be called): let b:ale_r_lintr_lint_package = 1 AssertLinter 'Rscript', - \ ale#path#CdString(getcwd()) + \ ale#path#BufferCdString(bufnr('')) \ . 'Rscript --vanilla -e ' \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' \ . 'lint_package(cache = FALSE, ' diff --git a/test/command_callback/test_mypy_command_callback.vader b/test/command_callback/test_mypy_command_callback.vader index afa9f9af..b9b6ae70 100644 --- a/test/command_callback/test_mypy_command_callback.vader +++ b/test/command_callback/test_mypy_command_callback.vader @@ -75,14 +75,14 @@ Execute(Setting executable to 'pipenv' appends 'run mypy'): let g:ale_python_mypy_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('path/to/pipenv') . ' run mypy' \ . ' --show-column-numbers --shadow-file %s %t %s' Execute(Pipenv is detected when python_mypy_auto_pipenv is set): let g:ale_python_mypy_auto_pipenv = 1 - call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py') AssertLinter 'pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pipenv') . ' run mypy --show-column-numbers --shadow-file %s %t %s' diff --git a/test/command_callback/test_nasm_nasm_command_callbacks.vader b/test/command_callback/test_nasm_nasm_command_callbacks.vader index 8e077306..2bfe2b0d 100644 --- a/test/command_callback/test_nasm_nasm_command_callbacks.vader +++ b/test/command_callback/test_nasm_nasm_command_callbacks.vader @@ -2,9 +2,9 @@ Before: call ale#assert#SetUpLinterTest('nasm', 'nasm') let b:command_tail = - \ ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' %s -o ' . (has('win32') ? 'NUL' : '/dev/null') + \ ' -X gnu -I %s:h' . (has('win32') ? '\' : '/') . ' %s -o ' . (has('win32') ? 'NUL' : '/dev/null') let b:command_tail_opt = - \ ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' -w+orphan-labels %s -o ' . (has('win32') ? 'NUL' : '/dev/null') + \ ' -X gnu -I %s:h' . (has('win32') ? '\' : '/') . ' -w+orphan-labels %s -o ' . (has('win32') ? 'NUL' : '/dev/null') After: unlet! b:command_tail @@ -23,7 +23,8 @@ Execute(The options should be configurable): let b:ale_nasm_nasm_options = '-w-macro-params' AssertLinter 'nasm', ale#Escape('nasm') - \ . ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' -w-macro-params %s -o ' . (has('win32') ? 'NUL' : '/dev/null') + \ . ' -X gnu -I %s:h' . (has('win32') ? '\' : '/') + \ . ' -w-macro-params %s -o ' . (has('win32') ? 'NUL' : '/dev/null') Execute(The options should be used in command): let b:ale_nasm_nasm_options = '-w+orphan-labels' diff --git a/test/command_callback/test_pydocstyle_command_callback.vader b/test/command_callback/test_pydocstyle_command_callback.vader index 7e0df9ca..511443a6 100644 --- a/test/command_callback/test_pydocstyle_command_callback.vader +++ b/test/command_callback/test_pydocstyle_command_callback.vader @@ -1,5 +1,6 @@ Before: call ale#assert#SetUpLinterTest('python', 'pydocstyle') + call ale#test#SetFilename('test.py') After: call ale#assert#TearDownLinterTest() @@ -7,33 +8,33 @@ After: Execute(The pydocstyle command callback should return default string): AssertLinter 'pydocstyle', \ ale#path#BufferCdString(bufnr('')) - \ . ale#Escape('pydocstyle') . ' ' . ale#Escape('dummy.txt') + \ . ale#Escape('pydocstyle') . ' %s:t' Execute(The pydocstyle command callback should allow options): let g:ale_python_pydocstyle_options = '--verbose' AssertLinter 'pydocstyle', \ ale#path#BufferCdString(bufnr('')) - \ . ale#Escape('pydocstyle') . ' --verbose ' . ale#Escape('dummy.txt') + \ . ale#Escape('pydocstyle') . ' --verbose %s:t' Execute(The pydocstyle executable should be configurable): let g:ale_python_pydocstyle_executable = '~/.local/bin/pydocstyle' AssertLinter '~/.local/bin/pydocstyle', \ ale#path#BufferCdString(bufnr('')) - \ . ale#Escape('~/.local/bin/pydocstyle') . ' ' . ale#Escape('dummy.txt') + \ . ale#Escape('~/.local/bin/pydocstyle') . ' %s:t' Execute(Setting executable to 'pipenv' appends 'run pydocstyle'): let g:ale_python_pydocstyle_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', \ ale#path#BufferCdString(bufnr('')) - \ . ale#Escape('path/to/pipenv') . ' run pydocstyle ' . ale#Escape('dummy.txt') + \ . ale#Escape('path/to/pipenv') . ' run pydocstyle %s:t' Execute(Pipenv is detected when python_pydocstyle_auto_pipenv is set): let g:ale_python_pydocstyle_auto_pipenv = 1 - call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py') AssertLinter 'pipenv', \ ale#path#BufferCdString(bufnr('')) - \ . ale#Escape('pipenv') . ' run pydocstyle ' . ale#Escape('whatever.py') + \ . ale#Escape('pipenv') . ' run pydocstyle %s:t' diff --git a/test/command_callback/test_pylama_command_callback.vader b/test/command_callback/test_pylama_command_callback.vader index 417cb5c9..0aea9a93 100644 --- a/test/command_callback/test_pylama_command_callback.vader +++ b/test/command_callback/test_pylama_command_callback.vader @@ -14,7 +14,7 @@ After: Execute(The pylama command callback should return a default): AssertLinter 'pylama', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pylama') . b:command_tail Execute(The option for disabling changing directories should work): @@ -26,14 +26,14 @@ Execute(The pylama executable should be configurable, and escaped properly): let g:ale_python_pylama_executable = 'executable with spaces' AssertLinter 'executable with spaces', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('executable with spaces') . b:command_tail Execute(The pylama command callback should let you set options): let g:ale_python_pylama_options = '--some-option' AssertLinter 'pylama', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pylama') . ' --some-option' . b:command_tail Execute(The pylama command callback should switch directories to the detected project root): @@ -73,13 +73,13 @@ Execute(Setting executable to 'pipenv' appends 'run pylama'): let g:ale_python_pylama_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('path/to/pipenv') . ' run pylama' . b:command_tail Execute(Pipenv is detected when python_pylama_auto_pipenv is set): let g:ale_python_pylama_auto_pipenv = 1 - call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py') AssertLinter 'pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pipenv') . ' run pylama' . b:command_tail diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader index c41c8398..755dd292 100644 --- a/test/command_callback/test_pylint_command_callback.vader +++ b/test/command_callback/test_pylint_command_callback.vader @@ -1,4 +1,8 @@ Before: + Save g:ale_python_auto_pipenv + + let g:ale_python_auto_pipenv = 0 + call ale#assert#SetUpLinterTest('python', 'pylint') let b:bin_dir = has('win32') ? 'Scripts' : 'bin' @@ -13,7 +17,7 @@ After: Execute(The pylint callbacks should return the correct default values): AssertLinter 'pylint', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pylint') . ' ' . b:command_tail Execute(The option for disabling changing directories should work): @@ -25,14 +29,14 @@ Execute(The pylint executable should be configurable, and escaped properly): let g:ale_python_pylint_executable = 'executable with spaces' AssertLinter 'executable with spaces', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('executable with spaces') . ' ' . b:command_tail Execute(The pylint command callback should let you set options): let g:ale_python_pylint_options = '--some-option' AssertLinter 'pylint', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pylint') . ' --some-option' . b:command_tail Execute(The pylint callbacks shouldn't detect virtualenv directories where they don't exist): @@ -65,15 +69,15 @@ Execute(Setting executable to 'pipenv' appends 'run pylint'): let g:ale_python_pylint_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('path/to/pipenv') . ' run pylint' \ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s' Execute(Pipenv is detected when python_pylint_auto_pipenv is set): let g:ale_python_pylint_auto_pipenv = 1 - call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py') AssertLinter 'pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('pipenv') . ' run pylint' \ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s' diff --git a/test/command_callback/test_pyrex_cython_command_callback.vader b/test/command_callback/test_pyrex_cython_command_callback.vader index b9020f11..af86366a 100644 --- a/test/command_callback/test_pyrex_cython_command_callback.vader +++ b/test/command_callback/test_pyrex_cython_command_callback.vader @@ -6,8 +6,8 @@ After: Execute(The default cython command should be correct): AssertLinter 'cython', ale#Escape('cython') - \ . ' --working ' . ale#Escape(g:dir) - \ . ' --include-dir ' . ale#Escape(g:dir) + \ . ' --working %s:h' + \ . ' --include-dir %s:h' \ . ' --warning-extra' \ . ' --output-file ' . g:ale#util#nul_file . ' %t' @@ -15,8 +15,8 @@ Execute(The cython executable should be configurable): let b:ale_pyrex_cython_executable = 'cython_foobar' AssertLinter 'cython_foobar', ale#Escape('cython_foobar') - \ . ' --working ' . ale#Escape(g:dir) - \ . ' --include-dir ' . ale#Escape(g:dir) + \ . ' --working %s:h' + \ . ' --include-dir %s:h' \ . ' --warning-extra' \ . ' --output-file ' . g:ale#util#nul_file . ' %t' @@ -24,7 +24,7 @@ Execute(Additional cython options should be configurable): let b:ale_pyrex_cython_options = '--foobar' AssertLinter 'cython', ale#Escape('cython') - \ . ' --working ' . ale#Escape(g:dir) - \ . ' --include-dir ' . ale#Escape(g:dir) + \ . ' --working %s:h' + \ . ' --include-dir %s:h' \ . ' --foobar' \ . ' --output-file ' . g:ale#util#nul_file . ' %t' diff --git a/test/command_callback/test_rubocop_command_callback.vader b/test/command_callback/test_rubocop_command_callback.vader index 7f42a8c0..e7cc32e8 100644 --- a/test/command_callback/test_rubocop_command_callback.vader +++ b/test/command_callback/test_rubocop_command_callback.vader @@ -10,20 +10,17 @@ After: Execute(Executable should default to rubocop): AssertLinter 'rubocop', ale#Escape('rubocop') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Should be able to set a custom executable): let g:ale_ruby_rubocop_executable = 'bin/rubocop' AssertLinter 'bin/rubocop' , ale#Escape('bin/rubocop') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Setting bundle appends 'exec rubocop'): let g:ale_ruby_rubocop_executable = 'path to/bundle' AssertLinter 'path to/bundle', ale#Escape('path to/bundle') \ . ' exec rubocop' - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' diff --git a/test/command_callback/test_ruumba_command_callback.vader b/test/command_callback/test_ruumba_command_callback.vader index 244b264a..9fa48903 100644 --- a/test/command_callback/test_ruumba_command_callback.vader +++ b/test/command_callback/test_ruumba_command_callback.vader @@ -10,20 +10,17 @@ After: Execute(Executable should default to ruumba): AssertLinter 'ruumba', ale#Escape('ruumba') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Should be able to set a custom executable): let g:ale_eruby_ruumba_executable = 'bin/ruumba' AssertLinter 'bin/ruumba' , ale#Escape('bin/ruumba') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Setting bundle appends 'exec ruumba'): let g:ale_eruby_ruumba_executable = 'path to/bundle' AssertLinter 'path to/bundle', ale#Escape('path to/bundle') \ . ' exec ruumba' - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb')) + \ . ' --format json --force-exclusion --stdin %s' diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader index 1d5b056b..9fb5303a 100644 --- a/test/command_callback/test_shellcheck_command_callback.vader +++ b/test/command_callback/test_shellcheck_command_callback.vader @@ -2,7 +2,7 @@ Before: call ale#assert#SetUpLinterTest('sh', 'shellcheck') call ale#test#SetFilename('test.sh') - let b:prefix = ale#path#CdString(ale#path#Simplify(g:dir)) + let b:prefix = ale#path#BufferCdString(bufnr('')) let b:suffix = ' -f gcc -' After: diff --git a/test/command_callback/test_sqllint_command_callback.vader b/test/command_callback/test_sqllint_command_callback.vader new file mode 100644 index 00000000..eea9b4e0 --- /dev/null +++ b/test/command_callback/test_sqllint_command_callback.vader @@ -0,0 +1,12 @@ +Before: + " Load the linter and set up a series of commands, reset linter variables, + " clear caches, etc. + " + " Vader's 'Save' command will be called here for linter variables. + call ale#assert#SetUpLinterTest('sql', 'sqllint') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'sql-lint', ['sql-lint'] diff --git a/test/command_callback/test_standardrb_command_callback.vader b/test/command_callback/test_standardrb_command_callback.vader index 7bc1c976..108dd870 100644 --- a/test/command_callback/test_standardrb_command_callback.vader +++ b/test/command_callback/test_standardrb_command_callback.vader @@ -10,20 +10,17 @@ After: Execute(Executable should default to standardrb): AssertLinter 'standardrb', ale#Escape('standardrb') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Should be able to set a custom executable): let g:ale_ruby_standardrb_executable = 'bin/standardrb' AssertLinter 'bin/standardrb' , ale#Escape('bin/standardrb') - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' Execute(Setting bundle appends 'exec standardrb'): let g:ale_ruby_standardrb_executable = 'path to/bundle' AssertLinter 'path to/bundle', ale#Escape('path to/bundle') \ . ' exec standardrb' - \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')) + \ . ' --format json --force-exclusion --stdin %s' diff --git a/test/command_callback/test_staticcheck_command_callback.vader b/test/command_callback/test_staticcheck_command_callback.vader index ae0d3584..871a5510 100644 --- a/test/command_callback/test_staticcheck_command_callback.vader +++ b/test/command_callback/test_staticcheck_command_callback.vader @@ -11,7 +11,7 @@ After: Execute(The staticcheck callback should return the right defaults): AssertLinter 'staticcheck', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'staticcheck ' \ . ale#Escape(expand('%' . ':t')) @@ -19,7 +19,7 @@ Execute(The staticcheck callback should use configured options): let b:ale_go_staticcheck_options = '-test' AssertLinter 'staticcheck', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . 'staticcheck ' \ . '-test ' . ale#Escape(expand('%' . ':t')) @@ -27,13 +27,14 @@ Execute(The staticcheck `lint_package` option should use the correct command): let b:ale_go_staticcheck_lint_package = 1 AssertLinter 'staticcheck', - \ ale#path#CdString(expand('%:p:h')) . 'staticcheck .', + \ ale#path#BufferCdString(bufnr('')) + \ . 'staticcheck .', Execute(The staticcheck callback should use the `GO111MODULE` option if set): let b:ale_go_go111module = 'off' AssertLinter 'staticcheck', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Env('GO111MODULE', 'off') \ . 'staticcheck ' \ . ale#Escape(expand('%' . ':t')) @@ -42,6 +43,6 @@ Execute(The staticcheck callback should use the `GO111MODULE` option if set): let b:ale_go_staticcheck_lint_package = 1 AssertLinter 'staticcheck', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Env('GO111MODULE', 'off') \ . 'staticcheck .' diff --git a/test/command_callback/test_tslint_command_callback.vader b/test/command_callback/test_tslint_command_callback.vader index 229ccc96..cc5d2666 100644 --- a/test/command_callback/test_tslint_command_callback.vader +++ b/test/command_callback/test_tslint_command_callback.vader @@ -7,14 +7,14 @@ After: Execute(The default tslint command should be correct): AssertLinter 'tslint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('tslint') . ' --format json %t' Execute(The rules directory option should be included if set): let b:ale_typescript_tslint_rules_dir = '/foo/bar' AssertLinter 'tslint', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('tslint') . ' --format json' \ . ' -r ' . ale#Escape('/foo/bar') \ . ' %t' @@ -23,5 +23,5 @@ Execute(The executable should be configurable and escaped): let b:ale_typescript_tslint_executable = 'foo bar' AssertLinter 'foo bar', - \ ale#path#CdString(expand('%:p:h')) + \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('foo bar') . ' --format json %t' diff --git a/test/command_callback/test_vulture_command_callback.vader b/test/command_callback/test_vulture_command_callback.vader index d6c866b9..bacf8f12 100644 --- a/test/command_callback/test_vulture_command_callback.vader +++ b/test/command_callback/test_vulture_command_callback.vader @@ -12,7 +12,7 @@ After: Execute(The vulture command callback should lint file directory by default): AssertLinter 'vulture', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('vulture') . ' .' Execute(The vulture command callback should lint project root, when present): @@ -31,14 +31,14 @@ Execute(The vulture executable should be configurable, and escaped properly): let g:ale_python_vulture_executable = 'executable with spaces' AssertLinter 'executable with spaces', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('executable with spaces') . ' .' Execute(The vulture command callback should let you set options): let g:ale_python_vulture_options = '--some-option' AssertLinter 'vulture', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('vulture') . ' --some-option .' Execute(The vulture command callback should detect virtualenv directories and switch to the project root): @@ -64,5 +64,5 @@ Execute(Setting executable to 'pipenv' appends 'run vulture'): let g:ale_python_vulture_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', - \ ale#path#BufferCdString(bufnr('')) + \ ale#path#CdString(expand('#' . bufnr('') . ':p:h')) \ . ale#Escape('path/to/pipenv') . ' run vulture' . ' .' diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader index 8b8b41c7..b8e71320 100644 --- a/test/completion/test_lsp_completion_parsing.vader +++ b/test/completion/test_lsp_completion_parsing.vader @@ -609,6 +609,7 @@ Execute(Should handle completion messages with additionalTextEdits when ale_comp Execute(Should not handle completion messages with additionalTextEdits when ale_completion_autoimport is turned off): let g:ale_completion_autoimport = 0 + let b:ale_completion_info = {'line': 30} AssertEqual \ [], @@ -645,3 +646,36 @@ Execute(Should not handle completion messages with additionalTextEdits when ale_ \ ], \ }, \ }) + +Execute(Should still handle completion messages with empty additionalTextEdits with ale_completion_autoimport turned off): + let g:ale_completion_autoimport = 0 + + AssertEqual + \ [ + \ { + \ 'word': 'next_callback', + \ 'menu': 'PlayTimeCallback', + \ 'info': '', + \ 'kind': 'v', + \ 'icase': 1, + \ } + \ ], + \ ale#completion#ParseLSPCompletions({ + \ 'id': 226, + \ 'jsonrpc': '2.0', + \ 'result': { + \ 'isIncomplete': v:false, + \ 'items': [ + \ { + \ 'detail': 'PlayTimeCallback', + \ 'filterText': 'next_callback', + \ 'insertText': 'next_callback', + \ 'insertTextFormat': 1, + \ 'kind': 6, + \ 'label': ' next_callback', + \ 'sortText': '3ee19999next_callback', + \ 'additionalTextEdits': [], + \ }, + \ ], + \ }, + \ }) diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader index 53079d16..e8a9dd7a 100644 --- a/test/fix/test_ale_fix.vader +++ b/test/fix/test_ale_fix.vader @@ -6,6 +6,7 @@ Before: Save g:ale_lint_on_save Save g:ale_echo_cursor Save g:ale_command_wrapper + Save g:ale_filename_mappings silent! cd /testplugin/test/fix @@ -19,6 +20,7 @@ Before: let g:ale_fixers = { \ 'testft': [], \} + let g:ale_filename_mappings = {} let g:pre_success = 0 let g:post_success = 0 @@ -72,6 +74,10 @@ Before: return {'command': 'cat %t <(echo d)'} endfunction + function EchoFilename(buffer, lines) abort + return {'command': 'echo %s'} + endfunction + function RemoveLastLine(buffer, lines) abort return ['a', 'b'] endfunction @@ -155,6 +161,7 @@ After: delfunction CatLineDeferred delfunction ReplaceWithTempFile delfunction CatWithTempFile + delfunction EchoFilename delfunction RemoveLastLine delfunction RemoveLastLineOneArg delfunction TestCallback @@ -209,6 +216,25 @@ Expect(The first function should be used): ^b ^c +Execute(Should apply filename mpapings): + " The command echos %s, and we'll map the current path so we can check + " that ALEFix applies filename mappings, end-to-end. + let g:ale_filename_mappings = { + \ 'echo_filename': [ + \ [expand('%:p:h') . '/', '/some/fake/path/'], + \ ], + \} + + call ale#fix#registry#Add('echo_filename', 'EchoFilename', [], 'echo filename') + let g:ale_fixers.testft = ['echo_filename'] + ALEFix + call ale#test#FlushJobs() + " Remote trailing whitespace from the line. + call setline(1, substitute(getline(1), '[ \r]\+$', '', '')) + +Expect(The mapped filename should be printed): + /some/fake/path/test.txt + Execute(ALEFix should apply simple functions in a chain): let g:ale_fixers.testft = ['AddCarets', 'Capitalize'] ALEFix diff --git a/test/fixers/test_dhall_fixer_callback.vader b/test/fixers/test_dhall_fixer_callback.vader new file mode 100644 index 00000000..f27880b7 --- /dev/null +++ b/test/fixers/test_dhall_fixer_callback.vader @@ -0,0 +1,11 @@ +Before: + call ale#assert#SetUpFixerTest('dhall', 'dhall') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The default command should be correct): + AssertFixer + \ { 'read_temporary_file': 1, + \ 'command': ale#Escape('dhall') . ' format --inplace %t' + \ } diff --git a/test/fixers/test_isort_fixer_callback.vader b/test/fixers/test_isort_fixer_callback.vader index 50818621..7f389dcf 100644 --- a/test/fixers/test_isort_fixer_callback.vader +++ b/test/fixers/test_isort_fixer_callback.vader @@ -4,6 +4,7 @@ Before: " Use an invalid global executable, so we don't match it. let g:ale_python_isort_executable = 'xxxinvalid' + let g:ale_python_isort_options = '' call ale#test#SetDirectory('/testplugin/test/fixers') silent cd .. @@ -27,7 +28,7 @@ Execute(The isort callback should return the correct default values): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual \ { - \ 'command': ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir/foo')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort')) . ' -', \ }, \ ale#fixers#isort#Fix(bufnr('')) @@ -42,7 +43,7 @@ Execute(The isort callback should respect custom options): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual \ { - \ 'command': ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir/foo')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort')) \ . ' --multi-line=3 --trailing-comma -', \ }, diff --git a/test/fixers/test_latexindent_fixer_callback.vader b/test/fixers/test_latexindent_fixer_callback.vader index d0da94a1..a440ed65 100644 --- a/test/fixers/test_latexindent_fixer_callback.vader +++ b/test/fixers/test_latexindent_fixer_callback.vader @@ -18,10 +18,8 @@ Execute(The latexindent callback should return the correct default values): AssertEqual \ { - \ 'read_temporary_file': 1, \ 'command': ale#Escape('xxxinvalid') - \ . ' -l -w' - \ . ' %t', + \ . ' -l' \ }, \ ale#fixers#latexindent#Fix(bufnr('')) @@ -31,10 +29,8 @@ Execute(The latexindent callback should include custom gofmt options): AssertEqual \ { - \ 'read_temporary_file': 1, \ 'command': ale#Escape('xxxinvalid') - \ . ' -l -w' + \ . ' -l' \ . ' ' . g:ale_tex_latexindent_options - \ . ' %t', \ }, \ ale#fixers#latexindent#Fix(bufnr('')) diff --git a/test/fixers/test_ocamlformat_fixer_callback.vader b/test/fixers/test_ocamlformat_fixer_callback.vader index f0c36ed7..0ccdb070 100644 --- a/test/fixers/test_ocamlformat_fixer_callback.vader +++ b/test/fixers/test_ocamlformat_fixer_callback.vader @@ -19,8 +19,7 @@ Execute(The ocamlformat callback should return the correct default values): AssertEqual \ { \ 'command': ale#Escape('xxxinvalid') - \ . ' --name=' . ale#Escape(bufname(bufnr(''))) - \ . ' -', + \ . ' --name=%s -', \ }, \ ale#fixers#ocamlformat#Fix(bufnr('')) @@ -32,7 +31,6 @@ Execute(The ocamlformat callback should include custom ocamlformat options): \ { \ 'command': ale#Escape('xxxinvalid') \ . ' ' . g:ale_ocaml_ocamlformat_options - \ . ' --name=' . ale#Escape(bufname(bufnr(''))) - \ . ' -', + \ . ' --name=%s -', \ }, \ ale#fixers#ocamlformat#Fix(bufnr('')) diff --git a/test/fixers/test_prettier_eslint_fixer.callback.vader b/test/fixers/test_prettier_eslint_fixer.callback.vader index f7bb3c61..be8f04e3 100644 --- a/test/fixers/test_prettier_eslint_fixer.callback.vader +++ b/test/fixers/test_prettier_eslint_fixer.callback.vader @@ -73,7 +73,7 @@ Execute(The new --stdin-filepath option should be used when the version is new e GivenCommandOutput ['4.4.0'] AssertFixer \ { - \ 'command': ale#path#CdString(expand('%:p:h')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . ale#Escape('prettier-eslint') \ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js')) \ . ' --stdin-filepath %s --stdin', @@ -83,7 +83,7 @@ Execute(The version number should be cached): GivenCommandOutput ['4.4.0'] AssertFixer \ { - \ 'command': ale#path#CdString(expand('%:p:h')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . ale#Escape('prettier-eslint') \ . ' --stdin-filepath %s --stdin', \ } @@ -91,7 +91,7 @@ Execute(The version number should be cached): GivenCommandOutput [] AssertFixer \ { - \ 'command': ale#path#CdString(expand('%:p:h')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . ale#Escape('prettier-eslint') \ . ' --stdin-filepath %s --stdin', \ } diff --git a/test/fixers/test_prettier_fixer_callback.vader b/test/fixers/test_prettier_fixer_callback.vader index 1087a160..fdd97df3 100644 --- a/test/fixers/test_prettier_fixer_callback.vader +++ b/test/fixers/test_prettier_fixer_callback.vader @@ -297,6 +297,17 @@ Execute(Should set --parser for experimental language, Handlebars): \ . ' --stdin-filepath %s --stdin', \ } +Execute(Changes to directory where .prettierignore is found): + call ale#test#SetFilename('../prettier-test-files/with_prettierignore/src/testfile.js') + + GivenCommandOutput ['1.6.0'] + AssertFixer + \ { + \ 'command': ale#path#CdString(expand('%:p:h:h')) + \ . ale#Escape(g:ale_javascript_prettier_executable) + \ . ' --stdin-filepath %s --stdin', + \ } + Execute(The prettier_d post-processor should permit regular JavaScript content): AssertEqual \ [ diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader index 305881e0..84579d31 100644 --- a/test/fixers/test_rubocop_fixer_callback.vader +++ b/test/fixers/test_rubocop_fixer_callback.vader @@ -23,8 +23,7 @@ Execute(The rubocop callback should return the correct default values): \ { \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --auto-correct --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . bufnr('') . ':p')), + \ . ' --auto-correct --force-exclusion --stdin %s', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -36,8 +35,7 @@ Execute(The rubocop callback should include configuration files): \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) - \ . ' --auto-correct --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . bufnr('') . ':p')), + \ . ' --auto-correct --force-exclusion --stdin %s', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -51,8 +49,7 @@ Execute(The rubocop callback should include custom rubocop options): \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) \ . ' --except Lint/Debugger' - \ . ' --auto-correct --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . bufnr('') . ':p')), + \ . ' --auto-correct --force-exclusion --stdin %s', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -65,8 +62,7 @@ Execute(The rubocop callback should use auto-correct-all option when set): \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) - \ . ' --auto-correct-all --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . bufnr('') . ':p')), + \ . ' --auto-correct-all --force-exclusion --stdin %s' \ }, \ ale#fixers#rubocop#Fix(bufnr('')) diff --git a/test/fixers/test_stylelint_fixer_callback.vader b/test/fixers/test_stylelint_fixer_callback.vader index f677cdf7..8fbd8a51 100644 --- a/test/fixers/test_stylelint_fixer_callback.vader +++ b/test/fixers/test_stylelint_fixer_callback.vader @@ -1,4 +1,8 @@ Before: + Save g:ale_stylelint_options + + let g:ale_stylelint_options = '' + call ale#assert#SetUpFixerTest('css', 'stylelint') After: @@ -10,7 +14,7 @@ Execute(The stylelint callback should return the correct default values): AssertFixer \ { \ 'read_temporary_file': 1, - \ 'command': ale#path#CdString(expand('%:p:h')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . (has('win32') ? 'node.exe ' : '') \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) \ . ' %t' @@ -24,7 +28,7 @@ Execute(The stylelint callback should include custom stylelint options): AssertFixer \ { \ 'read_temporary_file': 1, - \ 'command': ale#path#CdString(expand('%:p:h')) + \ 'command': ale#path#BufferCdString(bufnr('')) \ . (has('win32') ? 'node.exe ' : '') \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) \ . ' %t' diff --git a/test/handler/test_sqllint_handler.vader b/test/handler/test_sqllint_handler.vader new file mode 100644 index 00000000..2f2283c8 --- /dev/null +++ b/test/handler/test_sqllint_handler.vader @@ -0,0 +1,23 @@ +Before: + " Load the file which defines the linter. + runtime ale_linters/sql/sqllint.vim + +After: + " Unload all linters again. + call ale#linter#Reset() + +Execute (The output should be correct): + + " Test that the right loclist items are parsed from the handler. + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 0, + \ 'type': '', + \ 'text': 'stdin:1 [ER_NO_DB_ERROR] No database selected' + \ }, + \ ], + \ ale_linters#sql#sqllint#Handle(bufnr(''), [ + \ 'stdin:1 [ER_NO_DB_ERROR] No database selected' + \ ]) diff --git a/test/prettier-test-files/with_prettierignore/.prettierignore b/test/prettier-test-files/with_prettierignore/.prettierignore new file mode 100644 index 00000000..e69de29b diff --git a/test/prettier-test-files/with_prettierignore/src/testfile.js b/test/prettier-test-files/with_prettierignore/src/testfile.js new file mode 100644 index 00000000..e69de29b diff --git a/test/smoke_test.vader b/test/smoke_test.vader index 53e08a8d..0b126cc6 100644 --- a/test/smoke_test.vader +++ b/test/smoke_test.vader @@ -1,8 +1,10 @@ Before: + Save g:ale_enabled Save g:ale_set_lists_synchronously Save g:ale_buffer_info Save &shell + let g:ale_enabled = 1 let g:ale_buffer_info = {} let g:ale_set_lists_synchronously = 1 diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index a69333d4..2f0a893f 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -92,8 +92,8 @@ Execute (All events should be set up when everything is on): \ 'FileType * call ale#events#FileTypeEvent( str2nr(expand('''')), expand(''''))', \ 'InsertLeave * if ale#Var(str2nr(expand('''')), ''lint_on_insert_leave'') | call ale#Queue(0) | endif', \ 'InsertLeave if exists(''*ale#engine#Cleanup'') | call ale#cursor#EchoCursorWarning() | endif', - \ 'TextChanged * call ale#Queue(g:ale_lint_delay)', - \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)', + \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', + \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', \ ], \ CheckAutocmd('ALEEvents') @@ -145,8 +145,8 @@ Execute (g:ale_lint_on_text_changed = 1 bind both events): AssertEqual \ [ - \ 'TextChanged * call ale#Queue(g:ale_lint_delay)', - \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)', + \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', + \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', \ ], \ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''') @@ -155,8 +155,8 @@ Execute (g:ale_lint_on_text_changed = 'always' should bind both events): AssertEqual \ [ - \ 'TextChanged * call ale#Queue(g:ale_lint_delay)', - \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)', + \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', + \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', \ ], \ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''') @@ -165,7 +165,7 @@ Execute (g:ale_lint_on_text_changed = 'normal' should bind only TextChanged): AssertEqual \ [ - \ 'TextChanged * call ale#Queue(g:ale_lint_delay)', + \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', \ ], \ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''') @@ -174,7 +174,7 @@ Execute (g:ale_lint_on_text_changed = 'insert' should bind only TextChangedI): AssertEqual \ [ - \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)', + \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))', \ ], \ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''') diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader index e9830db8..076be6a1 100644 --- a/test/test_c_flag_parsing.vader +++ b/test/test_c_flag_parsing.vader @@ -5,7 +5,15 @@ Before: let g:ale_c_parse_makefile = 1 + function SplitAndParse(path_prefix, command) abort + let l:args = ale#c#ShellSplit(a:command) + + return ale#c#ParseCFlags(a:path_prefix, 0, l:args) + endfunction + After: + delfunction SplitAndParse + Restore call ale#test#RestoreDirectory() @@ -18,7 +26,7 @@ Execute(The CFlags parser should be able to parse include directives): \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c']) AssertEqual - \ '-isystem ' . '/usr/include/dir', + \ '-isystem ' . ale#Escape('/usr/include/dir'), \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -isystem /usr/include/dir -c file.c']) Execute(ParseCFlags should ignore -c and -o): @@ -57,48 +65,21 @@ Execute(ParseCFlags should be able to parse flags with relative paths): \ '-I' . ' ' . 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/kernel/include')) \ . ' -DTEST=`date +%s`', - \ ale#c#ParseCFlags( + \ SplitAndParse( \ 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' - \ . ' ' . '-I' . ' ' . 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/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' - \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) - \ . ' ' . '--sysroot=subdir' - \ . ' ' . '-I' . ' ' . ale#Escape(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 --sysroot=subdir ' - \ . '-I'. ale#path#Simplify('kernel/include') - \ . ' -DTEST=`date +%s` -c file.c' - \ ) - -Execute(ParseCFlags should handle paths with spaces in double quotes): +Execute(We should handle paths with spaces in double quotes): AssertEqual \ '-Dgoal=9' \ . ' ' . '-I' . ' ' . 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/dir with spaces')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', - \ ale#c#ParseCFlags( + \ SplitAndParse( \ 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') @@ -112,7 +93,7 @@ Execute(ParseCFlags should handle paths with spaces in single quotes): \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', - \ ale#c#ParseCFlags( + \ SplitAndParse( \ 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') @@ -127,7 +108,7 @@ Execute(ParseCFlags should handle paths with minuses): \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', - \ ale#c#ParseCFlags( + \ SplitAndParse( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-I''dir with spaces''' . ' -Idir-with-dash' @@ -135,7 +116,7 @@ Execute(ParseCFlags should handle paths with minuses): \ . ' -DTEST=`date +%s` -c file.c' \ ) -Execute(ParseCFlags should handle -D with minuses): +Execute(We should handle -D with minuses): AssertEqual \ '-Dgoal=9' \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) @@ -144,7 +125,7 @@ Execute(ParseCFlags should handle -D with minuses): \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', - \ ale#c#ParseCFlags( + \ SplitAndParse( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-Dmacro-with-dash ' @@ -153,7 +134,7 @@ Execute(ParseCFlags should handle -D with minuses): \ . ' -DTEST=`date +%s` -c file.c' \ ) -Execute(ParseCFlags should handle flags at the end of the line): +Execute(We should handle flags at the end of the line): AssertEqual \ '-Dgoal=9' \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) @@ -161,7 +142,7 @@ Execute(ParseCFlags should handle flags at the end of the line): \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ ale#c#ParseCFlags( + \ SplitAndParse( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-Dmacro-with-dash ' @@ -178,50 +159,170 @@ Execute(ParseCompileCommandsFlags should tolerate empty values): Execute(ParseCompileCommandsFlags should parse some basic flags): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) + " We should read the absolute path filename entry, not the other ones. AssertEqual - \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), - \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [ + \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')), + \ ale#c#ParseCompileCommandsFlags( + \ bufnr(''), \ { - \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), - \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') - \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), - \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'): [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ }, + \ ], + \ "xmms2-mpris.c": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ }, + \ ], \ }, - \ ] }, {}) + \ { + \ ale#path#Simplify('/foo/bar/xmms2-mpris/src'): [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris/src'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': 'other.c', + \ }, + \ ], + \ "src": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), + \ }, + \ ], + \ }, + \ ) + +Execute(ParseCompileCommandsFlags should fall back to files with the same name): + silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) + + " We should prefer the basename file flags, not the base dirname flags. + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')), + \ ale#c#ParseCompileCommandsFlags( + \ bufnr(''), + \ { + \ "xmms2-mpris.c": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ }, + \ ], + \ }, + \ { + \ "src": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), + \ }, + \ ], + \ }, + \ ) + +Execute(ParseCompileCommandsFlags should parse flags for exact directory matches): + silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) + + " We should ues the exact directory flags, not the file basename flags. + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')), + \ ale#c#ParseCompileCommandsFlags( + \ bufnr(''), + \ { + \ "xmms2-mpris.c": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ }, + \ ], + \ }, + \ { + \ ale#path#Simplify('/foo/bar/xmms2-mpris/src'): [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris/src'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': 'other.c', + \ }, + \ ], + \ "src": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), + \ }, + \ ], + \ }, + \ ) + +Execute(ParseCompileCommandsFlags should fall back to files in the same directory): + silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) + + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')), + \ ale#c#ParseCompileCommandsFlags( + \ bufnr(''), + \ {}, + \ { + \ "src": [ + \ { + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') + \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), + \ }, + \ ], + \ }, + \ ) Execute(ParseCompileCommandsFlags should tolerate items without commands): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) AssertEqual \ '', - \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [ + \ ale#c#ParseCompileCommandsFlags( + \ bufnr(''), \ { - \ 'directory': '/foo/bar/xmms2-mpris', - \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', + \ "xmms2-mpris.c": [ + \ { + \ 'directory': '/foo/bar/xmms2-mpris', + \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', + \ }, + \ ], \ }, - \ ] }, {}) - -Execute(ParseCompileCommandsFlags should fall back to files in the same directory): - silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) - - AssertEqual - \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), - \ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [ - \ { - \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), - \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') - \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), - \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), - \ }, - \ ] }) + \ {}, + \ ) Execute(ParseCompileCommandsFlags should take commands from matching .c files for .h files): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I /usr/include/xmms2', + \ '-I ' . ale#Escape('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -235,15 +336,14 @@ Execute(ParseCompileCommandsFlags should take commands from matching .c files fo \ }, \ ], \ }, - \ { - \ }, + \ {}, \ ) Execute(ParseCompileCommandsFlags should take commands from matching .cpp files for .hpp files): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.hpp')) AssertEqual - \ '-I /usr/include/xmms2', + \ '-I ' . ale#Escape('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -265,7 +365,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I /usr/include/xmms2', + \ '-I ' . ale#Escape('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -305,43 +405,69 @@ Execute(ParseCompileCommandsFlags should not take commands from .c files for .h \ }, \ ) -Execute(ParseCFlags should not merge flags): +Execute(ShellSplit should not merge flags): AssertEqual - \ '-Dgoal=9' - \ . ' ' . '-I' . ' ' . 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/dir with spaces')) - \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) - \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), - \ ale#c#ParseCFlags( - \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ [ + \ 'gcc', + \ '-Dgoal=9', + \ '-Tlinkerfile.ld', + \ 'blabla', + \ '-Isubdir', + \ 'subdir/somedep1.o', + \ 'subdir/somedep2.o', + \ '-I''dir with spaces''', + \ '-Idir-with-dash', + \ 'subdir/somedep3.o', + \ 'subdir/somedep4.o', + \ '-I' . ale#path#Simplify('kernel/include'), + \ 'subdir/somedep5.o', + \ 'subdir/somedep6.o', + \ ], + \ ale#c#ShellSplit( \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . 'subdir/somedep1.o ' . 'subdir/somedep2.o ' \ . '-I''dir with spaces''' . ' -Idir-with-dash ' \ . 'subdir/somedep3.o ' . 'subdir/somedep4.o ' \ . ' -I'. ale#path#Simplify('kernel/include') . ' ' - \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o ' + \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o' \ ) -Execute(ParseCFlags should handle parenthesis and quotes): +Execute(ShellSplit should handle parenthesis and quotes): AssertEqual - \ '-Dgoal=9 -Dtest1="('' '')" -Dtest2=''(` `)'' -Dtest3=`(" ")`', - \ ale#c#ParseCFlags( - \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ [ + \ 'gcc', + \ '-Dgoal=9', + \ '-Tlinkerfile.ld', + \ 'blabla', + \ '-Dtest1="('' '')"', + \ 'file1.o', + \ '-Dtest2=''(` `)''', + \ 'file2.o', + \ '-Dtest3=`(" ")`', + \ 'file3.o', + \ ] , + \ ale#c#ShellSplit( \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla ' \ . '-Dtest1="('' '')" file1.o ' \ . '-Dtest2=''(` `)'' file2.o ' - \ . '-Dtest3=`(" ")` file3.o ' + \ . '-Dtest3=`(" ")` file3.o' \ ) -Execute(CFlags we want to pass): +Execute(We should include several important flags): AssertEqual - \ '-I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/inc')) - \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/include')) - \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incquote')) - \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incsystem')) + \ '-I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/inc')) + \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incquote')) + \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incsystem')) \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) - \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incframework')) - \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' + \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework')) + \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar')) + \ . ' -Dmacro="value"' + \ . ' -DGoal=9' + \ . ' -D macro2' + \ . ' -D macro3="value"' + \ . ' -Bbdir' + \ . ' -B bdir2' \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' @@ -349,32 +475,175 @@ Execute(CFlags we want to pass): \ . ' -iplugindir=dir -march=native -w', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc' - \ . ' -Iinc -I include -iquote incquote -isystem incsystem -idirafter incafter -iframework incframework' - \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' - \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' - \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' - \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' - \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' - \ . ' -iplugindir=dir -march=native -w' + \ 0, + \ [ + \ 'gcc', + \ '-Iinc', + \ '-I', + \ 'include', + \ '-iquote', + \ 'incquote', + \ '-isystem', + \ 'incsystem', + \ '-idirafter', + \ 'incafter', + \ '-iframework', + \ 'incframework', + \ '-include', + \ '''foo bar''', + \ '-Dmacro="value"', + \ '-DGoal=9', + \ '-D', + \ 'macro2', + \ '-D', + \ 'macro3="value"', + \ '-Bbdir', + \ '-B', + \ 'bdir2', + \ '-iprefix', + \ 'prefix', + \ '-iwithprefix', + \ 'prefix2', + \ '-iwithprefixbefore', + \ 'prefix3', + \ '-isysroot', + \ 'sysroot', + \ '--sysroot=test', + \ '--no-sysroot-suffix', + \ '-imultilib', + \ 'multidir', + \ '-Wsome-warning', + \ '-std=c89', + \ '-pedantic', + \ '-pedantic-errors', + \ '-ansi', + \ '-foption', + \ '-O2', + \ '-C', + \ '-CC', + \ '-trigraphs', + \ '-nostdinc', + \ '-nostdinc++', + \ '-iplugindir=dir', + \ '-march=native', + \ '-w', + \ ], \ ) -Execute(CFlags we dont want to pass): +Execute(We should quote the flags we need to quote): + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/inc')) + \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incquote')) + \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incsystem')) + \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) + \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework')) + \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar')) + \ . ' ' . ale#Escape('-Dmacro="value"') + \ . ' -DGoal=9' + \ . ' -D macro2' + \ . ' -D ' . ale#Escape('macro3="value"') + \ . ' -Bbdir' + \ . ' -B bdir2' + \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' + \ . ' -isysroot sysroot --sysroot=test' + \ . ' ' . ale#Escape('--sysroot="quoted"') + \ . ' ' . ale#Escape('--sysroot=foo bar') + \ . ' --no-sysroot-suffix -imultilib multidir' + \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' + \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' + \ . ' -iplugindir=dir -march=native -w', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 1, + \ [ + \ 'gcc', + \ '-Iinc', + \ '-I', + \ 'include', + \ '-iquote', + \ 'incquote', + \ '-isystem', + \ 'incsystem', + \ '-idirafter', + \ 'incafter', + \ '-iframework', + \ 'incframework', + \ '-include', + \ '''foo bar''', + \ '-Dmacro="value"', + \ '-DGoal=9', + \ '-D', + \ 'macro2', + \ '-D', + \ 'macro3="value"', + \ '-Bbdir', + \ '-B', + \ 'bdir2', + \ '-iprefix', + \ 'prefix', + \ '-iwithprefix', + \ 'prefix2', + \ '-iwithprefixbefore', + \ 'prefix3', + \ '-isysroot', + \ 'sysroot', + \ '--sysroot=test', + \ '--sysroot="quoted"', + \ '--sysroot=foo bar', + \ '--no-sysroot-suffix', + \ '-imultilib', + \ 'multidir', + \ '-Wsome-warning', + \ '-std=c89', + \ '-pedantic', + \ '-pedantic-errors', + \ '-ansi', + \ '-foption', + \ '-O2', + \ '-C', + \ '-CC', + \ '-trigraphs', + \ '-nostdinc', + \ '-nostdinc++', + \ '-iplugindir=dir', + \ '-march=native', + \ '-w', + \ ], + \ ) + +Execute(We should exclude other flags that cause problems): AssertEqual \ '', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Wl,option -Wa,option -Wp,option filename.c somelib.a ' - \ . '-fdump-file=name -fdiagnostics-arg -fno-show-column -fstack-usage' + \ 0, + \ [ + \ 'gcc', + \ '-Wl,option', + \ '-Wa,option', + \ '-Wp,option', + \ '-c', + \ 'filename.c', + \ 'somelib.a', + \ '-fdump-file=name', + \ '-fdiagnostics-arg', + \ '-fno-show-column', + \ '-fstack-usage', + \ '-Tlinkerfile.ld', + \ ], \ ) -Execute(Expanding @file in CFlags): +Execute(We should expand @file in CFlags): AssertEqual \ '-DARGS1 -DARGS2 -O2', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc' - \ . ' -g' - \ . ' @./args' - \ . ' -O2', + \ 0, + \ [ + \ 'gcc', + \ '-g', + \ '@./args', + \ '-O2', + \ ], \ ) diff --git a/test/test_computed_lint_file_values.vader b/test/test_computed_lint_file_values.vader new file mode 100644 index 00000000..399e96fe --- /dev/null +++ b/test/test_computed_lint_file_values.vader @@ -0,0 +1,134 @@ +Before: + Save g:ale_enabled + Save g:ale_run_synchronously + Save g:ale_set_lists_synchronously + Save g:ale_buffer_info + + let g:ale_enabled = 1 + let g:ale_buffer_info = {} + let g:ale_run_synchronously = 1 + let g:ale_set_lists_synchronously = 1 + + function! TestCallback(buffer, output) + " Windows adds extra spaces to the text from echo. + return [{ + \ 'lnum': 2, + \ 'col': 3, + \ 'text': 'testlinter1', + \}] + endfunction + function! TestCallback2(buffer, output) + " Windows adds extra spaces to the text from echo. + return [{ + \ 'lnum': 1, + \ 'col': 3, + \ 'text': 'testlinter2', + \}] + endfunction + function! TestCallback3(buffer, output) + " Windows adds extra spaces to the text from echo. + return [{ + \ 'lnum': 3, + \ 'col': 3, + \ 'text': 'testlinter3', + \}] + endfunction + + " These two linters computer their lint_file values after running commands. + call ale#linter#Define('foobar', { + \ 'name': 'testlinter1', + \ 'callback': 'TestCallback', + \ 'executable': has('win32') ? 'cmd' : 'echo', + \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', + \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> 1})}, + \}) + call ale#linter#Define('foobar', { + \ 'name': 'testlinter2', + \ 'callback': 'TestCallback2', + \ 'executable': has('win32') ? 'cmd' : 'echo', + \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', + \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 1})})}, + \}) + " This one directly computes the result. + call ale#linter#Define('foobar', { + \ 'name': 'testlinter3', + \ 'callback': 'TestCallback3', + \ 'executable': has('win32') ? 'cmd' : 'echo', + \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', + \ 'lint_file': {b -> 1}, + \}) + + let g:filename = tempname() + call writefile([], g:filename) + call ale#test#SetFilename(g:filename) + +After: + delfunction TestCallback + + call ale#engine#Cleanup(bufnr('')) + Restore + call ale#linter#Reset() + + " Items and markers, etc. + call setloclist(0, []) + call clearmatches() + call ale#sign#Clear() + + if filereadable(g:filename) + call delete(g:filename) + endif + + unlet g:filename + +Given foobar(A file with some lines): + foo + bar + baz + +Execute(lint_file results where the result is eventually computed should be run): + call ale#Queue(0, 'lint_file') + call ale#test#FlushJobs() + + AssertEqual + \ [ + \ { + \ 'bufnr': bufnr('%'), + \ 'lnum': 1, + \ 'vcol': 0, + \ 'col': 3, + \ 'text': 'testlinter2', + \ 'type': 'E', + \ 'nr': -1, + \ 'pattern': '', + \ 'valid': 1, + \ }, + \ { + \ 'bufnr': bufnr('%'), + \ 'lnum': 2, + \ 'vcol': 0, + \ 'col': 3, + \ 'text': 'testlinter1', + \ 'type': 'E', + \ 'nr': -1, + \ 'pattern': '', + \ 'valid': 1, + \ }, + \ { + \ 'bufnr': bufnr('%'), + \ 'lnum': 3, + \ 'vcol': 0, + \ 'col': 3, + \ 'text': 'testlinter3', + \ 'type': 'E', + \ 'nr': -1, + \ 'pattern': '', + \ 'valid': 1, + \ }, + \ ], + \ ale#test#GetLoclistWithoutModule() + +Execute(Linters where lint_file eventually evaluates to 1 shouldn't be run if we don't want to run them): + call ale#Queue(0, '') + call ale#test#FlushJobs() + + AssertEqual [], ale#test#GetLoclistWithoutModule() diff --git a/test/test_deferred_command_string.vader b/test/test_deferred_command_string.vader index 026be6fe..173b6bb2 100644 --- a/test/test_deferred_command_string.vader +++ b/test/test_deferred_command_string.vader @@ -12,7 +12,7 @@ Before: call ale#linter#Define('foobar', { \ 'name': 'lint_file_linter', \ 'callback': 'LintFileCallback', - \ 'executable': 'echo', + \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 'foo'})})}, \ 'read_buffer': 0, \}) @@ -28,7 +28,7 @@ After: Given foobar (Some imaginary filetype): Execute(It should be possible to compute an executable to check based on the result of commands): - AssertLinter 'echo', 'foo' + AssertLinter has('win32') ? 'cmd' : 'echo', 'foo' ALELint call ale#test#FlushJobs() @@ -40,7 +40,7 @@ Execute(It should be possible to compute an executable to check based on the res Execute(It handle the deferred command failing): let g:ale_emulate_job_failure = 1 - AssertLinter 'echo', 0 + AssertLinter has('win32') ? 'cmd' : 'echo', 0 ALELint call ale#test#FlushJobs() diff --git a/test/test_filename_mapping.vader b/test/test_filename_mapping.vader new file mode 100644 index 00000000..e9af539a --- /dev/null +++ b/test/test_filename_mapping.vader @@ -0,0 +1,62 @@ +Before: + Save g:ale_filename_mappings + Save b:ale_filename_mappings + + let g:ale_filename_mappings = {} + unlet! b:ale_filename_mappings + +After: + Restore + +Execute(ale#GetFilenameMappings should return the correct mappings for given linters/fixers): + let g:ale_filename_mappings = {'a': [['foo', 'bar']], 'b': [['baz', 'foo']]} + + AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a') + AssertEqual [['baz', 'foo']], ale#GetFilenameMappings(bufnr(''), 'b') + AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c') + + let b:ale_filename_mappings = {'b': [['abc', 'xyz']]} + + AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'a') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b') + AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c') + +Execute(ale#GetFilenameMappings should return Lists set for use with all tools): + let g:ale_filename_mappings = [['foo', 'bar']] + + AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a') + AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), '') + AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), v:null) + + let b:ale_filename_mappings = [['abc', 'xyz']] + + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'a') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null) + +Execute(ale#GetFilenameMappings should let you use * as a fallback): + let g:ale_filename_mappings = {'a': [['foo', 'bar']], '*': [['abc', 'xyz']]} + + AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '') + AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null) + +Execute(ale#filename_mapping#Invert should invert filename mappings): + AssertEqual + \ [['b', 'a'], ['y', 'x']], + \ ale#filename_mapping#Invert([['a', 'b'], ['x', 'y']]) + \ +Execute(ale#filename_mapping#Map return the filename as-is if there are no mappings): + AssertEqual + \ '/foo//bar', + \ ale#filename_mapping#Map('/foo//bar', [['/bar', '/data/']]) + +Execute(ale#filename_mapping#Map should map filenames): + AssertEqual + \ '/data/bar', + \ ale#filename_mapping#Map('/foo//bar', [ + \ ['/data/', '/baz/'], + \ ['/foo/', '/data/'], + \ ['/foo/', '/xyz/'], + \ ]) diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader index 842cc394..e9980536 100644 --- a/test/test_filetype_linter_defaults.vader +++ b/test/test_filetype_linter_defaults.vader @@ -39,7 +39,7 @@ Execute(The defaults for the python filetype should be correct): AssertEqual [], GetLinterNames('python') Execute(The defaults for the rust filetype should be correct): - AssertEqual ['cargo'], GetLinterNames('rust') + AssertEqual ['cargo', 'rls'], GetLinterNames('rust') let g:ale_linters_explicit = 1 diff --git a/test/test_find_references.vader b/test/test_find_references.vader index 9949362a..ca05f631 100644 --- a/test/test_find_references.vader +++ b/test/test_find_references.vader @@ -63,6 +63,8 @@ Before: let g:preview_called = 1 let g:item_list = a:item_list let g:options = a:options + + call ale#preview#SetLastSelection(a:item_list, a:options) endfunction After: @@ -110,7 +112,16 @@ Given typescript(Some typescript file): bazxyzxyzxyz Execute(Results should be shown for tsserver responses): - call ale#references#SetMap({3: {}}) + " We should remember these options when we repeat the selection. + call ale#references#SetMap( + \ { + \ 3: { + \ 'ignorethis': 'x', + \ 'open_in': 'tab', + \ 'use_relative_paths': 1, + \ } + \ } + \) call ale#references#HandleTSServerResponse(1, { \ 'command': 'references', \ 'request_seq': 3, @@ -158,8 +169,7 @@ Execute(Results should be shown for tsserver responses): AssertEqual {}, ale#references#GetMap() " We should be able to repeat selections with ALERepeatSelection - let g:ale_item_list = [] - + let g:item_list = [] ALERepeatSelection AssertEqual @@ -170,6 +180,12 @@ Execute(Results should be shown for tsserver responses): \ ], \ g:item_list AssertEqual {}, ale#references#GetMap() + AssertEqual + \ { + \ 'open_in': 'tab', + \ 'use_relative_paths': 1, + \ }, + \ g:options Execute(The preview window should not be opened for empty tsserver responses): call ale#references#SetMap({3: {}}) diff --git a/test/test_format_command.vader b/test/test_format_command.vader index 15435326..9d730fce 100644 --- a/test/test_format_command.vader +++ b/test/test_format_command.vader @@ -25,12 +25,12 @@ After: Execute(FormatCommand should do nothing to basic command strings): AssertEqual \ ['', 'awesome-linter do something', 0], - \ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null, []) Execute(FormatCommand should handle %%, and ignore other percents): AssertEqual \ ['', '% %%d %%f %x %', 0], - \ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null, []) Execute(FormatCommand should convert %s to the current filename): AssertEqual @@ -39,10 +39,10 @@ Execute(FormatCommand should convert %s to the current filename): \ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p')), \ 0, \ ], - \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null, []) Execute(FormatCommand should convert %t to a new temporary filename): - let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null) + let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null, []) call CheckTempFile(g:result[0]) @@ -56,21 +56,21 @@ Execute(FormatCommand should convert %t to a new temporary filename): AssertEqual g:match[1], g:match[2] Execute(FormatCommand should not convert %t to a new temporary filename when the input is given as v:false): - let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false) + let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false, []) AssertEqual ['', 'foo %t bar %t', 0], g:result Execute(FormatCommand should signal that files are created when temporary files are needed): AssertEqual \ 1, - \ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null)[2] + \ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null, [])[2] AssertEqual \ 0, - \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null)[2] + \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null, [])[2] Execute(FormatCommand should let you combine %s and %t): - let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null) + let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null, []) call CheckTempFile(g:result[0]) @@ -87,30 +87,30 @@ Execute(FormatCommand should replace %e with the escaped executable): if has('win32') AssertEqual \ ['', 'foo foo', 0], - \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, []) AssertEqual \ ['', '"foo bar"', 0], - \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, []) AssertEqual \ ['', '%e %e', 0], - \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, []) else AssertEqual \ ['', '''foo'' ''foo''', 0], - \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, []) AssertEqual \ ['', '''foo bar''', 0], - \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, []) AssertEqual \ ['', '%e %e', 0], - \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null) + \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, []) endif Execute(EscapeCommandPart should escape all percent signs): AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%') Execute(EscapeCommandPart should pipe in temporary files appropriately): - let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null) + let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null, []) call CheckTempFile(g:result[0]) @@ -118,10 +118,57 @@ Execute(EscapeCommandPart should pipe in temporary files appropriately): Assert !empty(g:match), 'No match found! Result was: ' . g:result[1] AssertEqual ale#Escape(g:result[0]), g:match[1] - let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null) + let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null, []) call CheckTempFile(g:result[0]) let g:match = matchlist(g:result[1], '\v^foo bar (.*)$') Assert !empty(g:match), 'No match found! Result was: ' . g:result[1] AssertEqual ale#Escape(g:result[0]), g:match[1] + +Execute(FormatCommand should apply filename modifiers to the current file): + AssertEqual + \ ale#Escape(expand('%:p:h')) + \ . ' ' . ale#Escape('dummy.txt') + \ . ' ' . ale#Escape(expand('%:p:h:t')) + \ . ' ' . ale#Escape('txt') + \ . ' ' . ale#Escape(expand('%:p:r')), + \ ale#command#FormatCommand(bufnr(''), '', '%s:h %s:t %s:h:t %s:e %s:r', 0, v:null, [])[1] + +Execute(FormatCommand should apply filename modifiers to the temporary file): + let g:result = ale#command#FormatCommand(bufnr(''), '', '%t:h %t:t %t:h:t %t:e %t:r', 0, v:null, []) + + AssertEqual + \ ale#Escape(fnamemodify(g:result[0], ':h')) + \ . ' ' . ale#Escape('dummy.txt') + \ . ' ' . ale#Escape(fnamemodify(g:result[0], ':h:t')) + \ . ' ' . ale#Escape('txt') + \ . ' ' . ale#Escape(fnamemodify(g:result[0], ':r')), + \ g:result[1] + +Execute(FormatCommand should apply filename mappings the current file): + let g:result = ale#command#FormatCommand(bufnr('%'), '', '%s', 0, v:null, [ + \ [expand('%:p:h'), '/foo/bar'], + \]) + + Assert g:result[1] =~# '/foo/bar' + +Execute(FormatCommand should apply filename mappings to temporary files): + let g:result = ale#command#FormatCommand(bufnr('%'), '', '%t', 0, v:null, [ + \ [fnamemodify(tempname(), ':h:h'), '/foo/bar'] + \]) + + Assert g:result[1] =~# '/foo/bar' + +Execute(FormatCommand should apply filename modifiers to mapped filenames): + let g:result = ale#command#FormatCommand(bufnr('%'), '', '%s:h', 0, v:null, [ + \ [expand('%:p:h'), '/foo/bar'], + \]) + + AssertEqual ale#Escape('/foo/bar'), g:result[1] + + let g:result = ale#command#FormatCommand(bufnr('%'), '', '%t:h:h:h', 0, v:null, [ + \ [fnamemodify(tempname(), ':h:h'), '/foo/bar'] + \]) + + AssertEqual ale#Escape('/foo/bar'), g:result[1] diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader index 2c85299b..d000b158 100644 --- a/test/test_linter_defintion_processing.vader +++ b/test/test_linter_defintion_processing.vader @@ -174,7 +174,7 @@ Execute(PreProcess should process the lint_file option correctly): \} AssertThrows call ale#linter#PreProcess('testft', g:linter) - AssertEqual '`lint_file` must be `0` or `1`', g:vader_exception + AssertEqual '`lint_file` must be `0`, `1`, or a Function', g:vader_exception let g:linter.lint_file = 0 @@ -185,14 +185,17 @@ Execute(PreProcess should process the lint_file option correctly): let g:linter.lint_file = 1 AssertEqual 1, ale#linter#PreProcess('testft', g:linter).lint_file - " The default for read_buffer should change to 0 when lint_file is 1. - AssertEqual 0, ale#linter#PreProcess('testft', g:linter).read_buffer + " The default for read_buffer should still be 1 + AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer let g:linter.read_buffer = 1 - " We shouldn't be able to set both options to 1 at the same time. - AssertThrows call ale#linter#PreProcess('testft', g:linter) - AssertEqual 'Only one of `lint_file` or `read_buffer` can be `1`', g:vader_exception + " We should be able to set `read_buffer` and `lint_file` at the same time. + AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer + + let g:linter.lint_file = function('type') + + Assert type(ale#linter#PreProcess('testft', g:linter).lint_file) is v:t_func Execute(PreProcess should set a default value for lint_file): let g:linter = { diff --git a/test/test_loclist_corrections.vader b/test/test_loclist_corrections.vader index 343620a5..d53b1411 100644 --- a/test/test_loclist_corrections.vader +++ b/test/test_loclist_corrections.vader @@ -1,7 +1,50 @@ +Before: + Save g:ale_filename_mappings + + let g:ale_filename_mappings = {} + After: unlet! b:temp_name unlet! b:other_bufnr + Restore + + +Execute(FixLocList should map filenames): + " Paths converted back into temporary filenames shouldn't be included. + let g:ale_filename_mappings = { + \ 'linter2': [['/xxx/', '/data/']], + \ 'linter1': [ + \ ['/bar/', '/data/special/'], + \ ['/foo/', '/data/'], + \ [ + \ ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h')) . '/', + \ '/x-tmp/', + \ ], + \ ], + \} + + AssertEqual + \ [ + \ '/foo/file.txt', + \ v:null, + \ '/bar/file.txt', + \ ], + \ map( + \ ale#engine#FixLocList( + \ bufnr('%'), + \ 'linter1', + \ 0, + \ [ + \ {'text': 'x', 'lnum': 1, 'filename': '/data/file.txt'}, + \ {'text': 'x', 'lnum': 1, 'filename': '/x-tmp/file.txt'}, + \ {'text': 'x', 'lnum': 1, 'filename': '/data/special/file.txt'}, + \ ], + \ ), + \ 'get(v:val, ''filename'', v:null)', + \ ) + + Given foo (Some file with lines to count): foo12345678 bar12345678 @@ -37,7 +80,7 @@ Execute(FixLocList should set all the default values correctly): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -58,7 +101,7 @@ Execute(FixLocList should use the values we supply): \ 'nr': 42, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -87,7 +130,7 @@ Execute(FixLocList should set items with lines beyond the end to the last line): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -108,7 +151,7 @@ Execute(FixLocList should move line 0 to line 1): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -130,7 +173,7 @@ Execute(FixLocList should convert line and column numbers correctly): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -164,7 +207,7 @@ Execute(FixLocList should pass on end_col values): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -202,7 +245,7 @@ Execute(FixLocList should pass on end_lnum values): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -227,7 +270,7 @@ Execute(FixLocList should allow subtypes to be set): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -289,7 +332,7 @@ Execute(FixLocList should accept filenames): \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -327,7 +370,7 @@ Execute(FixLocList should interpret temporary filenames as being the current buf \ 'nr': -1, \ 'linter_name': 'foobar', \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr(''), \ 'foobar', @@ -352,7 +395,7 @@ Execute(The error code should be passed on): \ 'linter_name': 'foobar', \ 'code': 'some-code' \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -385,7 +428,7 @@ Execute(FixLocList should mark problems as coming from other sources if requeste \ 'linter_name': 'foobar', \ 'from_other_source': 1, \ }, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', @@ -407,7 +450,7 @@ Execute(character positions should be converted to byte positions): \ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 13, 'end_lnum': 1, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'}, \ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 17, 'end_lnum': 2, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'}, \ {'lnum': 2, 'bufnr': bufnr(''), 'col': 17, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'}, - \], + \ ], \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', diff --git a/test/test_swiftlint_executable_detection.vader b/test/test_swiftlint_executable_detection.vader index dfd4930b..ac83ff8f 100644 --- a/test/test_swiftlint_executable_detection.vader +++ b/test/test_swiftlint_executable_detection.vader @@ -23,22 +23,22 @@ Execute(React Native apps using CocoaPods should take precedence over the defaul call ale#test#SetFilename('swiftlint-test-files/react-native/testfile.swift') AssertEqual - \ ale#path#Simplify(g:dir . '/swiftlint-test-files/react-native/ios/Pods/SwiftLint/swiftlint'), - \ ale_linters#swift#swiftlint#GetExecutable(bufnr('')) + \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/react-native/ios/Pods/SwiftLint/swiftlint')), + \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr(''))) Execute(CocoaPods installation should take precedence over the default executable): call ale#test#SetFilename('swiftlint-test-files/cocoapods/testfile.swift') AssertEqual - \ ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods/Pods/SwiftLint/swiftlint'), - \ ale_linters#swift#swiftlint#GetExecutable(bufnr('')) + \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods/Pods/SwiftLint/swiftlint')), + \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr(''))) Execute(Top level CocoaPods installation should take precedence over React Native installation): call ale#test#SetFilename('swiftlint-test-files/cocoapods-and-react-native/testfile.swift') AssertEqual - \ ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods-and-react-native/Pods/SwiftLint/swiftlint'), - \ ale_linters#swift#swiftlint#GetExecutable(bufnr('')) + \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods-and-react-native/Pods/SwiftLint/swiftlint')), + \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr(''))) Execute(use-global should override other versions): let g:ale_swift_swiftlint_use_global = 1 diff --git a/test/util/test_cd_string_commands.vader b/test/util/test_cd_string_commands.vader index f2102e48..85c5065f 100644 --- a/test/util/test_cd_string_commands.vader +++ b/test/util/test_cd_string_commands.vader @@ -16,5 +16,5 @@ Execute(BufferCdString should output the correct command string): call ale#test#SetFilename('foo.txt') AssertEqual - \ has('unix') ? 'cd ' . ale#Escape(g:dir) . ' && ' : 'cd /d ' . ale#Escape(g:dir) . ' && ', + \ has('unix') ? 'cd %s:h && ' : 'cd /d %s:h && ', \ ale#path#BufferCdString(bufnr(''))