Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bartek thindil Jasicki 2020-08-29 10:42:25 +02:00
commit 0de847a8e1
100 changed files with 1897 additions and 545 deletions

View file

@ -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: >-

5
.gitignore vendored
View file

@ -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

View file

@ -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)
<a name="supported-languages"></a>
@ -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
```
<a name="faq-vm"></a>
### 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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'))

View file

@ -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

View file

@ -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',

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,33 @@
" ale_linters/sql/sqllint.vim
" Author: Joe Reynolds <joereynolds952@gmail.co>
" 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',
\})

View file

@ -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

View file

@ -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,27 +356,79 @@ 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: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']
" 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
@ -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',
\])

View file

@ -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, <temp_dir>/<original_basename>
" 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 = {

View file

@ -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,
\}]
\
if !empty(l:text_changes)
let l:result.user_data = json_encode({
\ 'codeActions': [{
\ 'description': 'completion',
\ 'changes': l:changes,
\ '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

View file

@ -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.

View file

@ -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 <nomodeline> 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 <nomodeline> 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,

View file

@ -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('<abuf>')), '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('<abuf>')), '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('<abuf>')), 'lint_delay'))
endif
if g:ale_lint_on_enter

View file

@ -0,0 +1,22 @@
" Author: w0rp <devw0rp@gmail.com>
" 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

View file

@ -1,4 +1,8 @@
call ale#Set('fix_on_save_ignore', {})
" Author: w0rp <devw0rp@gmail.com>
" 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
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.

View file

@ -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.

View file

@ -0,0 +1,23 @@
" Author: Pat Brisbin <pbrisbin@gmail.com>
" 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

View file

@ -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

View file

@ -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

View file

@ -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',

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <buffer_filename_directory> && '
" 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.

View file

@ -1,14 +1,22 @@
" Author: w0rp <devw0rp@gmail.com>
" 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

View file

@ -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

View file

@ -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:

View file

@ -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,14 +253,15 @@ 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
@ -269,6 +271,7 @@ g:ale_rust_ignore_secondary_spans *g:ale_rust_ignore_secondary_spans*
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*

View file

@ -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

View file

@ -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 `<Plug>(ale_find_references)` is defined for this command.
You can define additional plug mapping with any additional options you want
like so: >
nnoremap <silent> <Plug>(my_mapping) :ALEFindReferences -relative<Return>
<
ALEFix *ALEFix*
@ -2885,14 +3030,17 @@ ALESymbolSearch `<query>` *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 `<Plug>(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'`
`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.
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.
`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 `[]`.
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

View file

@ -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)

View file

@ -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

View file

@ -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'

View file

@ -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:

View file

@ -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:

View file

@ -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'))
\ . ' -'

View file

@ -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:

View file

@ -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 ./'

View file

@ -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')

View file

@ -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'

View file

@ -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') . ' .'

View file

@ -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 .'

View file

@ -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 .'

View file

@ -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 .'

View file

@ -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'

View file

@ -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))

View file

@ -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, '

View file

@ -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'

View file

@ -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'

View file

@ -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'

View file

@ -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

View file

@ -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'

View file

@ -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'

View file

@ -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'

View file

@ -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'

View file

@ -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:

View file

@ -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']

View file

@ -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'

View file

@ -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 .'

View file

@ -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'

View file

@ -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' . ' .'

View file

@ -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': [],
\ },
\ ],
\ },
\ })

View file

@ -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

View file

@ -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'
\ }

View file

@ -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 -',
\ },

View file

@ -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(''))

View file

@ -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(''))

View file

@ -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',
\ }

View file

@ -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
\ [

View file

@ -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(''))

View file

@ -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'

View file

@ -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'
\ ])

View file

@ -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

View file

@ -92,8 +92,8 @@ Execute (All events should be set up when everything is on):
\ 'FileType * call ale#events#FileTypeEvent( str2nr(expand(''<abuf>'')), expand(''<amatch>''))',
\ 'InsertLeave * if ale#Var(str2nr(expand(''<abuf>'')), ''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(''<abuf>'')), ''lint_delay''))',
\ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand(''<abuf>'')), ''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(''<abuf>'')), ''lint_delay''))',
\ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand(''<abuf>'')), ''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(''<abuf>'')), ''lint_delay''))',
\ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand(''<abuf>'')), ''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(''<abuf>'')), ''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(''<abuf>'')), ''lint_delay''))',
\ ],
\ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''')

View file

@ -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,9 +159,13 @@ 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(''),
\ {
\ 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')
@ -188,26 +173,122 @@ Execute(ParseCompileCommandsFlags should parse some basic flags):
\ . ' -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 tolerate items without commands):
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
\ '',
\ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [
\ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')),
\ ale#c#ParseCompileCommandsFlags(
\ bufnr(''),
\ {
\ 'directory': '/foo/bar/xmms2-mpris',
\ 'file': '/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/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#path#Simplify('/usr/include/xmms2'),
\ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [
\ '-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')
@ -215,13 +296,33 @@ Execute(ParseCompileCommandsFlags should fall back to files in the same director
\ . ' -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": [
\ {
\ 'directory': '/foo/bar/xmms2-mpris',
\ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.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',
\ ],
\ )

View file

@ -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()

View file

@ -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()

View file

@ -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/'],
\ ])

View file

@ -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

View file

@ -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: {}})

View file

@ -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]

View file

@ -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 = {

View file

@ -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',

View file

@ -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

View file

@ -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(''))