diff options
82 files changed, 1098 insertions, 184 deletions
@@ -124,7 +124,7 @@ formatting. | FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) | | Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) | | GLSL | [glslang](https://github.com/KhronosGroup/glslang), [glslls](https://github.com/svenstaro/glsl-language-server) | -| Go | [gofmt](https://golang.org/cmd/gofmt/), [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), [go vet](https://golang.org/cmd/vet/) !!, [golint](https://godoc.org/github.com/golang/lint), [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype) !!, [gometalinter](https://github.com/alecthomas/gometalinter) !!, [go build](https://golang.org/cmd/go/) !!, [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) !!, [staticcheck](https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck) !!, [golangserver](https://github.com/sourcegraph/go-langserver), [golangci-lint](https://github.com/golangci/golangci-lint) !! | +| Go | [gofmt](https://golang.org/cmd/gofmt/), [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), [go mod](https://golang.org/cmd/go/) !!, [go vet](https://golang.org/cmd/vet/) !!, [golint](https://godoc.org/github.com/golang/lint), [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype) !!, [gometalinter](https://github.com/alecthomas/gometalinter) !!, [go build](https://golang.org/cmd/go/) !!, [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) !!, [staticcheck](https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck) !!, [golangserver](https://github.com/sourcegraph/go-langserver), [golangci-lint](https://github.com/golangci/golangci-lint) !! | | GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) | | Hack | [hack](http://hacklang.org/), [hackfmt](https://github.com/facebook/hhvm/tree/master/hphp/hack/hackfmt), [hhast](https://github.com/hhvm/hhast) (disabled by default; see `:help ale-integration-hack`) | | Haml | [haml-lint](https://github.com/brigade/haml-lint) | @@ -155,21 +155,21 @@ formatting. | OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [ocamlformat](https://github.com/ocaml-ppx/ocamlformat) | | Pawn | [uncrustify](https://github.com/uncrustify/uncrustify) | | Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic), [perltidy](https://metacpan.org/pod/distribution/Perl-Tidy/bin/perltidy) | -| PHP | [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer), [php-cs-fixer](http://cs.sensiolabs.org/) | +| PHP | [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer), [php-cs-fixer](http://cs.sensiolabs.org/), [psalm](https://getpsalm.org) !! | | PO | [alex](https://github.com/wooorm/alex) !!, [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html), [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | | Pod | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | | Pony | [ponyc](https://github.com/ponylang/ponyc) | | proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) | | Pug | [pug-lint](https://github.com/pugjs/pug-lint) | | Puppet | [languageserver](https://github.com/lingua-pupuli/puppet-editor-services), [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | -| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](http://github.com/landscapeio/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture) !!, [yapf](https://github.com/google/yapf) | +| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](https://github.com/PyCQA/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture) !!, [yapf](https://github.com/google/yapf) | | QML | [qmlfmt](https://github.com/jesperhh/qmlfmt), [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint) | | R | [lintr](https://github.com/jimhester/lintr) | | ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-reasonml-ols` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [refmt](https://github.com/reasonml/reason-cli) | | reStructuredText | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [rstcheck](https://github.com/myint/rstcheck), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) | | Re:VIEW | [redpen](http://redpen.cc/) | | RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) (disabled by default; see `:help ale-integration-spec`) | -| Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org), [rufo](https://github.com/ruby-formatter/rufo), [solargraph]([solargraph](https://solargraph.org) | +| Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org), [rufo](https://github.com/ruby-formatter/rufo), [solargraph](https://solargraph.org) | | Rust | cargo !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.org/), [rustfmt](https://github.com/rust-lang-nursery/rustfmt) | | SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) | | SCSS | [prettier](https://github.com/prettier/prettier), [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint) | diff --git a/ale_linters/go/gobuild.vim b/ale_linters/go/gobuild.vim index a44449f1..cef1ff88 100644 --- a/ale_linters/go/gobuild.vim +++ b/ale_linters/go/gobuild.vim @@ -3,6 +3,7 @@ " Description: go build for Go files " inspired by work from dzhou121 <dzhou121@gmail.com> +call ale#Set('go_go_executable', 'go') call ale#Set('go_gobuild_options', '') function! ale_linters#go#gobuild#GetCommand(buffer) abort @@ -10,7 +11,7 @@ function! ale_linters#go#gobuild#GetCommand(buffer) abort " Run go test in local directory with relative path return ale#path#BufferCdString(a:buffer) - \ . 'go test' + \ . ale#Var(a:buffer, 'go_go_executable') . ' test' \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' -c -o /dev/null ./' endfunction @@ -47,7 +48,7 @@ endfunction call ale#linter#Define('go', { \ 'name': 'gobuild', \ 'aliases': ['go build'], -\ 'executable': 'go', +\ 'executable_callback': ale#VarFunc('go_go_executable'), \ 'command_callback': 'ale_linters#go#gobuild#GetCommand', \ 'output_stream': 'stderr', \ 'callback': 'ale_linters#go#gobuild#Handler', diff --git a/ale_linters/go/golangci_lint.vim b/ale_linters/go/golangci_lint.vim index b44a6239..dd9a3c64 100644 --- a/ale_linters/go/golangci_lint.vim +++ b/ale_linters/go/golangci_lint.vim @@ -18,7 +18,7 @@ function! ale_linters#go#golangci_lint#GetCommand(buffer) abort return ale#path#BufferCdString(a:buffer) \ . '%e run ' - \ . ale#util#EscapePCRE(l:filename) + \ . ale#Escape(l:filename) \ . ' ' . l:options endfunction diff --git a/ale_linters/go/govet.vim b/ale_linters/go/govet.vim index 84c23236..3d0d2adf 100644 --- a/ale_linters/go/govet.vim +++ b/ale_linters/go/govet.vim @@ -4,20 +4,23 @@ " Author: John Eikenberry <jae@zhar.net> " Description: updated to work with go1.10 +call ale#Set('go_go_executable', 'go') call ale#Set('go_govet_options', '') function! ale_linters#go#govet#GetCommand(buffer) abort let l:options = ale#Var(a:buffer, 'go_govet_options') - return ale#path#BufferCdString(a:buffer) . ' go vet .' + return ale#path#BufferCdString(a:buffer) . ' ' + \ . ale#Var(a:buffer, 'go_go_executable') . ' vet ' \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' .' endfunction call ale#linter#Define('go', { \ 'name': 'govet', \ 'aliases': ['go vet'], \ 'output_stream': 'stderr', -\ 'executable': 'go', +\ 'executable_callback': ale#VarFunc('go_go_executable'), \ 'command_callback': 'ale_linters#go#govet#GetCommand', \ 'callback': 'ale#handlers#go#Handler', \ 'lint_file': 1, diff --git a/ale_linters/haskell/hlint.vim b/ale_linters/haskell/hlint.vim index be40d92c..3ee864bf 100644 --- a/ale_linters/haskell/hlint.vim +++ b/ale_linters/haskell/hlint.vim @@ -1,6 +1,9 @@ " Author: jparoz <jesse.paroz@gmail.com> " Description: hlint for Haskell files +call ale#Set('haskell_hlint_executable', 'hlint') +call ale#Set('haskell_hlint_options', get(g:, 'hlint_options', '')) + function! ale_linters#haskell#hlint#Handle(buffer, lines) abort let l:output = [] @@ -26,9 +29,17 @@ function! ale_linters#haskell#hlint#Handle(buffer, lines) abort return l:output endfunction +function! ale_linters#haskell#hlint#GetCommand(buffer) abort + let l:hlintopts = '--color=never --json' + + return '%e' + \ . ' ' . ale#Var(a:buffer, 'haskell_hlint_options') + \ . ' ' . l:hlintopts . ' -' +endfunction + call ale#linter#Define('haskell', { \ 'name': 'hlint', -\ 'executable': 'hlint', -\ 'command': 'hlint --color=never --json -', +\ 'executable_callback': ale#VarFunc('haskell_hlint_executable'), +\ 'command_callback': 'ale_linters#haskell#hlint#GetCommand', \ 'callback': 'ale_linters#haskell#hlint#Handle', \}) diff --git a/ale_linters/julia/languageserver.vim b/ale_linters/julia/languageserver.vim index b6bf8e73..cd2000de 100644 --- a/ale_linters/julia/languageserver.vim +++ b/ale_linters/julia/languageserver.vim @@ -6,7 +6,7 @@ call ale#Set('julia_executable', 'julia') function! ale_linters#julia#languageserver#GetCommand(buffer) abort let l:julia_executable = ale#Var(a:buffer, 'julia_executable') - let l:cmd_string = 'using LanguageServer; server = LanguageServer.LanguageServerInstance(STDIN, STDOUT, false); server.runlinter = true; run(server);' + let l:cmd_string = 'using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);' return ale#Escape(l:julia_executable) . ' --startup-file=no --history-file=no -e ' . ale#Escape(l:cmd_string) endfunction diff --git a/ale_linters/php/psalm.vim b/ale_linters/php/psalm.vim new file mode 100644 index 00000000..cd20ab81 --- /dev/null +++ b/ale_linters/php/psalm.vim @@ -0,0 +1,28 @@ +" Author: richard marmorstein <https://github.com/twitchard> +" Description: plugin for Psalm, static analyzer for PHP + +call ale#Set('php_psalm_executable', 'psalm') + +function! ale_linters#php#psalm#Handle(buffer, lines) abort + " Matches patterns like the following: + let l:pattern = '^.*:\(\d\+\):\(\d\+\):\(\w\+\) - \(.*\)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'text': l:match[4], + \ 'type': l:match[3][:0] is# 'e' ? 'E' : 'W', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('php', { +\ 'name': 'psalm', +\ 'command': '%e --diff --output-format=emacs %s', +\ 'executable_callback': ale#VarFunc('php_psalm_executable'), +\ 'callback': 'ale_linters#php#psalm#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim index 358f51a4..9dcdacc6 100644 --- a/ale_linters/python/flake8.vim +++ b/ale_linters/python/flake8.vim @@ -5,12 +5,18 @@ call ale#Set('python_flake8_executable', 'flake8') call ale#Set('python_flake8_options', '') call ale#Set('python_flake8_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#Set('python_flake8_change_directory', 1) +call ale#Set('python_flake8_auto_pipenv', 0) function! s:UsingModule(buffer) abort return ale#Var(a:buffer, 'python_flake8_options') =~# ' *-m flake8' endfunction function! ale_linters#python#flake8#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_flake8_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + if !s:UsingModule(a:buffer) return ale#python#FindExecutable(a:buffer, 'python_flake8', ['flake8']) endif diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim index b38ccdeb..0c90a3c7 100644 --- a/ale_linters/python/mypy.vim +++ b/ale_linters/python/mypy.vim @@ -5,8 +5,14 @@ call ale#Set('python_mypy_executable', 'mypy') call ale#Set('python_mypy_ignore_invalid_syntax', 0) call ale#Set('python_mypy_options', '') call ale#Set('python_mypy_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_mypy_auto_pipenv', 0) function! ale_linters#python#mypy#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_mypy_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_mypy', ['mypy']) endfunction diff --git a/ale_linters/python/prospector.vim b/ale_linters/python/prospector.vim index fff37147..b01cec87 100644 --- a/ale_linters/python/prospector.vim +++ b/ale_linters/python/prospector.vim @@ -1,6 +1,8 @@ " Author: chocoelho <carlospecter@gmail.com> " Description: prospector linter python files +call ale#Set('python_prospector_auto_pipenv', 0) + let g:ale_python_prospector_executable = \ get(g:, 'ale_python_prospector_executable', 'prospector') @@ -10,6 +12,11 @@ let g:ale_python_prospector_options = let g:ale_python_prospector_use_global = get(g:, 'ale_python_prospector_use_global', get(g:, 'ale_use_global_executables', 0)) function! ale_linters#python#prospector#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_prospector_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_prospector', ['prospector']) endfunction diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim index de96363f..f0269585 100644 --- a/ale_linters/python/pycodestyle.vim +++ b/ale_linters/python/pycodestyle.vim @@ -4,8 +4,14 @@ call ale#Set('python_pycodestyle_executable', 'pycodestyle') call ale#Set('python_pycodestyle_options', '') call ale#Set('python_pycodestyle_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pycodestyle_auto_pipenv', 0) function! ale_linters#python#pycodestyle#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pycodestyle_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_pycodestyle', ['pycodestyle']) endfunction diff --git a/ale_linters/python/pyflakes.vim b/ale_linters/python/pyflakes.vim index 86ff8773..091408d5 100644 --- a/ale_linters/python/pyflakes.vim +++ b/ale_linters/python/pyflakes.vim @@ -3,8 +3,14 @@ call ale#Set('python_pyflakes_executable', 'pyflakes') call ale#Set('python_pyflakes_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pyflakes_auto_pipenv', 0) function! ale_linters#python#pyflakes#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pyflakes_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_pyflakes', ['pyflakes']) endfunction diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim index 9239f835..01c3cb37 100644 --- a/ale_linters/python/pylint.vim +++ b/ale_linters/python/pylint.vim @@ -5,8 +5,14 @@ call ale#Set('python_pylint_executable', 'pylint') call ale#Set('python_pylint_options', '') call ale#Set('python_pylint_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#Set('python_pylint_change_directory', 1) +call ale#Set('python_pylint_auto_pipenv', 0) function! ale_linters#python#pylint#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pylint_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_pylint', ['pylint']) endfunction diff --git a/ale_linters/python/pyls.vim b/ale_linters/python/pyls.vim index ae71f022..83fe8066 100644 --- a/ale_linters/python/pyls.vim +++ b/ale_linters/python/pyls.vim @@ -3,8 +3,14 @@ call ale#Set('python_pyls_executable', 'pyls') call ale#Set('python_pyls_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pyls_auto_pipenv', 0) function! ale_linters#python#pyls#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pyls_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_pyls', ['pyls']) endfunction diff --git a/ale_linters/python/pyre.vim b/ale_linters/python/pyre.vim index 5efef409..adc185f2 100644 --- a/ale_linters/python/pyre.vim +++ b/ale_linters/python/pyre.vim @@ -3,8 +3,14 @@ call ale#Set('python_pyre_executable', 'pyre') call ale#Set('python_pyre_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pyre_auto_pipenv', 0) function! ale_linters#python#pyre#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pyre_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + return ale#python#FindExecutable(a:buffer, 'python_pyre', ['pyre']) endfunction diff --git a/ale_linters/ruby/brakeman.vim b/ale_linters/ruby/brakeman.vim index 85cfc184..122e0b5b 100644 --- a/ale_linters/ruby/brakeman.vim +++ b/ale_linters/ruby/brakeman.vim @@ -1,8 +1,9 @@ " Author: Eddie Lebow https://github.com/elebow " Description: Brakeman, a static analyzer for Rails security -let g:ale_ruby_brakeman_options = -\ get(g:, 'ale_ruby_brakeman_options', '') +call ale#Set('ruby_brakeman_options', '') +call ale#Set('ruby_brakeman_executable', 'brakeman') +call ale#Set('ruby_brakeman_options', '') function! ale_linters#ruby#brakeman#Handle(buffer, lines) abort let l:output = [] @@ -33,14 +34,17 @@ function! ale_linters#ruby#brakeman#GetCommand(buffer) abort return '' endif - return 'brakeman -f json -q ' + let l:executable = ale#Var(a:buffer, 'ruby_brakeman_executable') + + return ale#handlers#ruby#EscapeExecutable(l:executable, 'brakeman') + \ . ' -f json -q ' \ . ale#Var(a:buffer, 'ruby_brakeman_options') \ . ' -p ' . ale#Escape(l:rails_root) endfunction call ale#linter#Define('ruby', { \ 'name': 'brakeman', -\ 'executable': 'brakeman', +\ 'executable_callback': ale#VarFunc('ruby_brakeman_executable'), \ 'command_callback': 'ale_linters#ruby#brakeman#GetCommand', \ 'callback': 'ale_linters#ruby#brakeman#Handle', \ 'lint_file': 1, diff --git a/ale_linters/ruby/rails_best_practices.vim b/ale_linters/ruby/rails_best_practices.vim index 4ba1f3fe..20cadca8 100644 --- a/ale_linters/ruby/rails_best_practices.vim +++ b/ale_linters/ruby/rails_best_practices.vim @@ -22,26 +22,18 @@ function! ale_linters#ruby#rails_best_practices#Handle(buffer, lines) abort return l:output endfunction -function! ale_linters#ruby#rails_best_practices#GetExecutable(buffer) abort - return ale#Var(a:buffer, 'ruby_rails_best_practices_executable') -endfunction - function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort - let l:executable = ale_linters#ruby#rails_best_practices#GetExecutable(a:buffer) - let l:exec_args = l:executable =~? 'bundle$' - \ ? ' exec rails_best_practices' - \ : '' - let l:rails_root = ale#ruby#FindRailsRoot(a:buffer) if l:rails_root is? '' return '' endif + let l:executable = ale#Var(a:buffer, 'ruby_rails_best_practices_executable') let l:output_file = ale#Has('win32') ? '%t ' : '/dev/stdout ' let l:cat_file = ale#Has('win32') ? '; type %t' : '' - return ale#Escape(l:executable) . l:exec_args + return ale#handlers#ruby#EscapeExecutable(l:executable, 'rails_best_practices') \ . ' --silent -f json --output-file ' . l:output_file \ . ale#Var(a:buffer, 'ruby_rails_best_practices_options') \ . ale#Escape(l:rails_root) @@ -50,7 +42,7 @@ endfunction call ale#linter#Define('ruby', { \ 'name': 'rails_best_practices', -\ 'executable_callback': 'ale_linters#ruby#rails_best_practices#GetExecutable', +\ 'executable_callback': ale#VarFunc('ruby_rails_best_practices_executable'), \ 'command_callback': 'ale_linters#ruby#rails_best_practices#GetCommand', \ 'callback': 'ale_linters#ruby#rails_best_practices#Handle', \ 'lint_file': 1, diff --git a/ale_linters/ruby/reek.vim b/ale_linters/ruby/reek.vim index aa5d8d70..eefc4ecf 100644 --- a/ale_linters/ruby/reek.vim +++ b/ale_linters/ruby/reek.vim @@ -3,6 +3,8 @@ call ale#Set('ruby_reek_show_context', 0) call ale#Set('ruby_reek_show_wiki_link', 0) +call ale#Set('ruby_reek_options', '') +call ale#Set('ruby_reek_executable', 'reek') function! ale_linters#ruby#reek#VersionCheck(buffer) abort " If we have previously stored the version number in a cache, then @@ -12,18 +14,23 @@ function! ale_linters#ruby#reek#VersionCheck(buffer) abort return '' endif - return 'reek --version' + let l:executable = ale#Var(a:buffer, 'ruby_reek_executable') + + return ale#handlers#ruby#EscapeExecutable(l:executable, 'reek') + \ . ' --version' endfunction function! ale_linters#ruby#reek#GetCommand(buffer, version_output) abort let l:version = ale#semver#GetVersion('reek', a:version_output) + let l:executable = ale#Var(a:buffer, 'ruby_reek_executable') " Tell reek what the filename is if the version of reek is new enough. let l:display_name_args = ale#semver#GTE(l:version, [5, 0, 0]) \ ? ' --stdin-filename %s' \ : '' - return 'reek -f json --no-progress --no-color' + return ale#handlers#ruby#EscapeExecutable(l:executable, 'reek') + \ . ' -f json --no-progress --no-color' \ . l:display_name_args endfunction @@ -62,7 +69,7 @@ endfunction call ale#linter#Define('ruby', { \ 'name': 'reek', -\ 'executable': 'reek', +\ 'executable_callback': ale#VarFunc('ruby_reek_executable'), \ 'command_chain': [ \ {'callback': 'ale_linters#ruby#reek#VersionCheck'}, \ {'callback': 'ale_linters#ruby#reek#GetCommand'}, diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim index 777f457a..45218394 100644 --- a/ale_linters/ruby/rubocop.vim +++ b/ale_linters/ruby/rubocop.vim @@ -1,13 +1,13 @@ " Author: ynonp - https://github.com/ynonp, Eddie Lebow https://github.com/elebow " Description: RuboCop, a code style analyzer for Ruby files +call ale#Set('ruby_rubocop_executable', 'rubocop') +call ale#Set('ruby_rubocop_options', '') + function! ale_linters#ruby#rubocop#GetCommand(buffer) abort - let l:executable = ale#handlers#rubocop#GetExecutable(a:buffer) - let l:exec_args = l:executable =~? 'bundle$' - \ ? ' exec rubocop' - \ : '' + let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable') - return ale#Escape(l:executable) . l:exec_args + return ale#handlers#ruby#EscapeExecutable(l:executable, 'rubocop') \ . ' --format json --force-exclusion ' \ . ale#Var(a:buffer, 'ruby_rubocop_options') \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p')) @@ -55,7 +55,7 @@ endfunction call ale#linter#Define('ruby', { \ 'name': 'rubocop', -\ 'executable_callback': 'ale#handlers#rubocop#GetExecutable', +\ 'executable_callback': ale#VarFunc('ruby_rubocop_executable'), \ 'command_callback': 'ale_linters#ruby#rubocop#GetCommand', \ 'callback': 'ale_linters#ruby#rubocop#Handle', \}) diff --git a/ale_linters/ruby/solargraph.vim b/ale_linters/ruby/solargraph.vim index 2aad3af6..7ca0399f 100644 --- a/ale_linters/ruby/solargraph.vim +++ b/ale_linters/ruby/solargraph.vim @@ -1,20 +1,20 @@ " Author: Horacio Sanson - https://github.com/hsanson " Description: Solargraph Language Server https://solargraph.org/ +" +" Author: Devon Meunier <devon.meunier@gmail.com> +" Description: updated to use stdio -call ale#Set('ruby_solargraph_host', '127.0.0.1') -call ale#Set('ruby_solargraph_port', '7658') +call ale#Set('ruby_solargraph_executable', 'solargraph') -function! ale_linters#ruby#solargraph#GetAddress(buffer) abort - let l:host = ale#Var(a:buffer, 'ruby_solargraph_host') - let l:port = ale#Var(a:buffer, 'ruby_solargraph_port') - - return l:host . ':' . l:port +function! ale_linters#ruby#solargraph#GetCommand(buffer) abort + return '%e' . ale#Pad('stdio') endfunction call ale#linter#Define('ruby', { \ 'name': 'solargraph', -\ 'lsp': 'socket', -\ 'address_callback': 'ale_linters#ruby#solargraph#GetAddress', +\ 'lsp': 'stdio', \ 'language': 'ruby', -\ 'project_root_callback': 'ale#ruby#FindProjectRoot' +\ 'executable_callback': ale#VarFunc('ruby_solargraph_executable'), +\ 'command_callback': 'ale_linters#ruby#solargraph#GetCommand', +\ 'project_root_callback': 'ale#ruby#FindProjectRoot', \}) diff --git a/ale_linters/thrift/thrift.vim b/ale_linters/thrift/thrift.vim index 396a2355..36a8656e 100644 --- a/ale_linters/thrift/thrift.vim +++ b/ale_linters/thrift/thrift.vim @@ -2,7 +2,7 @@ call ale#Set('thrift_thrift_executable', 'thrift') call ale#Set('thrift_thrift_generators', ['cpp']) -call ale#Set('thrift_thrift_includes', []) +call ale#Set('thrift_thrift_includes', ['.']) call ale#Set('thrift_thrift_options', '-strict') function! ale_linters#thrift#thrift#GetCommand(buffer) abort diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index e7b43d88..ce59ae31 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -55,7 +55,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort " Check if cflag contained a '-' and should not have been splitted let l:option_list = split(l:option, '\zs') - if l:option_list[-1] isnot# ' ' + if len(l:option_list) > 0 && l:option_list[-1] isnot# ' ' continue endif diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index e96774c3..9dd913f5 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -98,14 +98,15 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort return '' endfunction -function! ale#completion#Filter(buffer, suggestions, prefix) abort +function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words') + let l:triggers = s:GetFiletypeValue(s:trigger_character_map, a:filetype) " For completing... " foo. " ^ " We need to include all of the given suggestions. - if a:prefix is# '.' + if index(l:triggers, a:prefix) >= 0 let l:filtered_suggestions = a:suggestions else let l:filtered_suggestions = [] @@ -359,17 +360,23 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:kind = 'v' endif + let l:doc = get(l:item, 'documentation', '') + + if type(l:doc) is v:t_dict && has_key(l:doc, 'value') + let l:doc = l:doc.value + endif + call add(l:results, { \ 'word': l:word, \ 'kind': l:kind, \ 'icase': 1, \ 'menu': get(l:item, 'detail', ''), - \ 'info': get(l:item, 'documentation', ''), + \ 'info': (type(l:doc) is v:t_string ? l:doc : ''), \}) endfor if has_key(l:info, 'prefix') - return ale#completion#Filter(l:buffer, l:results, l:info.prefix) + return ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix) endif return l:results @@ -390,6 +397,7 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort if l:command is# 'completions' let l:names = ale#completion#Filter( \ l:buffer, + \ &filetype, \ ale#completion#ParseTSServerCompletions(a:response), \ b:ale_completion_info.prefix, \)[: g:ale_completion_max_suggestions - 1] diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index 3fa4e5ba..c3b48ca3 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -1,4 +1,6 @@ +scriptencoding utf-8 " Author: w0rp <devw0rp@gmail.com> +" Author: João Paulo S. de Souza <joao.paulo.silvasouza@hotmail.com> " Description: Echoes lint message for the current line, if any " Controls the milliseconds delay before echoing a message. @@ -37,12 +39,11 @@ function! ale#cursor#TruncatedEcho(original_message) abort endtry endfunction -function! s:FindItemAtCursor() abort - let l:buf = bufnr('') - let l:info = get(g:ale_buffer_info, l:buf, {}) +function! s:FindItemAtCursor(buffer) abort + let l:info = get(g:ale_buffer_info, a:buffer, {}) let l:loclist = get(l:info, 'loclist', []) let l:pos = getcurpos() - let l:index = ale#util#BinarySearch(l:loclist, l:buf, l:pos[1], l:pos[2]) + let l:index = ale#util#BinarySearch(l:loclist, a:buffer, l:pos[1], l:pos[2]) let l:loc = l:index >= 0 ? l:loclist[l:index] : {} return [l:info, l:loc] @@ -56,7 +57,9 @@ function! s:StopCursorTimer() abort endfunction function! ale#cursor#EchoCursorWarning(...) abort - if !g:ale_echo_cursor + let l:buffer = bufnr('') + + if !g:ale_echo_cursor && !g:ale_cursor_detail return endif @@ -65,28 +68,39 @@ function! ale#cursor#EchoCursorWarning(...) abort return endif - if ale#ShouldDoNothing(bufnr('')) + if ale#ShouldDoNothing(l:buffer) return endif - let l:buffer = bufnr('') - let [l:info, l:loc] = s:FindItemAtCursor() + let [l:info, l:loc] = s:FindItemAtCursor(l:buffer) + + if g:ale_echo_cursor + if !empty(l:loc) + let l:format = ale#Var(l:buffer, 'echo_msg_format') + let l:msg = ale#GetLocItemMessage(l:loc, l:format) + call ale#cursor#TruncatedEcho(l:msg) + let l:info.echoed = 1 + elseif get(l:info, 'echoed') + " We'll only clear the echoed message when moving off errors once, + " so we don't continually clear the echo line. + execute 'echo' + let l:info.echoed = 0 + endif + endif - if !empty(l:loc) - let l:format = ale#Var(l:buffer, 'echo_msg_format') - let l:msg = ale#GetLocItemMessage(l:loc, l:format) - call ale#cursor#TruncatedEcho(l:msg) - let l:info.echoed = 1 - elseif get(l:info, 'echoed') - " We'll only clear the echoed message when moving off errors once, - " so we don't continually clear the echo line. - execute 'echo' - let l:info.echoed = 0 + if g:ale_cursor_detail + if !empty(l:loc) + call s:ShowCursorDetailForItem(l:loc, {'stay_here': 1}) + else + call ale#preview#CloseIfTypeMatches('ale-preview') + endif endif endfunction function! ale#cursor#EchoCursorWarningWithDelay() abort - if !g:ale_echo_cursor + let l:buffer = bufnr('') + + if !g:ale_echo_cursor && !g:ale_cursor_detail return endif @@ -104,7 +118,7 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort " we should echo something. Otherwise we can end up doing processing " the echo message far too frequently. if l:pos != s:last_pos - let l:delay = ale#Var(bufnr(''), 'echo_delay') + let l:delay = ale#Var(l:buffer, 'echo_delay') let s:last_pos = l:pos let s:cursor_timer = timer_start( @@ -114,24 +128,37 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort endif endfunction +function! s:ShowCursorDetailForItem(loc, options) abort + let l:stay_here = get(a:options, 'stay_here', 0) + + let s:last_detailed_line = line('.') + let l:message = get(a:loc, 'detail', a:loc.text) + let l:lines = split(l:message, "\n") + call ale#preview#Show(l:lines, {'stay_here': l:stay_here}) + + " Clear the echo message if we manually displayed details. + if !l:stay_here + execute 'echo' + endif +endfunction + function! ale#cursor#ShowCursorDetail() abort + let l:buffer = bufnr('') + " Only echo the warnings in normal mode, otherwise we will get problems. if mode() isnot# 'n' return endif - if ale#ShouldDoNothing(bufnr('')) + if ale#ShouldDoNothing(l:buffer) return endif call s:StopCursorTimer() - let [l:info, l:loc] = s:FindItemAtCursor() + let [l:info, l:loc] = s:FindItemAtCursor(l:buffer) if !empty(l:loc) - let l:message = get(l:loc, 'detail', l:loc.text) - - call ale#preview#Show(split(l:message, "\n")) - execute 'echo' + call s:ShowCursorDetailForItem(l:loc, {'stay_here': 0}) endif endfunction diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index b6c2d3c9..05db0e49 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -18,6 +18,22 @@ if !has_key(s:, 'executable_cache_map') let s:executable_cache_map = {} endif + +function! ale#engine#CleanupEveryBuffer() abort + for l:key in keys(g:ale_buffer_info) + " The key could be a filename or a buffer number, so try and + " convert it to a number. We need a number for the other + " functions. + let l:buffer = str2nr(l:key) + + if l:buffer > 0 + " Stop all jobs and clear the results for everything, and delete + " all of the data we stored for the buffer. + call ale#engine#Cleanup(l:buffer) + endif + endfor +endfunction + function! ale#engine#ResetExecutableCache() abort let s:executable_cache_map = {} endfunction diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index 300aefcc..e48ad488 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -131,13 +131,17 @@ function! ale#events#Init() abort autocmd InsertLeave * call ale#Queue(0) endif - if g:ale_echo_cursor + if g:ale_echo_cursor || g:ale_cursor_detail autocmd CursorMoved,CursorHold * if exists('*ale#engine#Cleanup') | call ale#cursor#EchoCursorWarningWithDelay() | endif " Look for a warning to echo as soon as we leave Insert mode. " The script's position variable used when moving the cursor will " not be changed here. autocmd InsertLeave * if exists('*ale#engine#Cleanup') | call ale#cursor#EchoCursorWarning() | endif endif + + if g:ale_close_preview_on_insert + autocmd InsertEnter * if exists('*ale#preview#CloseIfTypeMatches') | call ale#preview#CloseIfTypeMatches('ale-preview') | endif + endif endif augroup END endfunction diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index 1ae1a4a1..03652ecf 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -30,7 +30,14 @@ function! ale#fix#ApplyQueuedFixes() abort call winrestview(l:save) endif - call setline(1, l:data.output) + " If the file is in DOS mode, we have to remove carriage returns from + " the ends of lines before calling setline(), or we will see them + " twice. + let l:lines_to_set = getbufvar(l:buffer, '&fileformat') is# 'dos' + \ ? map(copy(l:data.output), 'substitute(v:val, ''\r\+$'', '''', '''')') + \ : l:data.output + + call setline(1, l:lines_to_set) if l:data.should_save if empty(&buftype) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index eb12f22d..76cce87f 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -145,6 +145,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['go'], \ 'description': 'Fix Go files imports with goimports.', \ }, +\ 'gomod': { +\ 'function': 'ale#fixers#gomod#Fix', +\ 'suggested_filetypes': ['gomod'], +\ 'description': 'Fix Go module files with go mod edit -fmt.', +\ }, \ 'tslint': { \ 'function': 'ale#fixers#tslint#Fix', \ 'suggested_filetypes': ['typescript'], diff --git a/autoload/ale/fixers/eslint.vim b/autoload/ale/fixers/eslint.vim index 36f47510..ea5b2a63 100644 --- a/autoload/ale/fixers/eslint.vim +++ b/autoload/ale/fixers/eslint.vim @@ -25,7 +25,7 @@ endfunction function! ale#fixers#eslint#ProcessEslintDOutput(buffer, output) abort " If the output is an error message, don't use it. for l:line in a:output[:10] - if l:line =~# '^Error:' + if l:line =~# '\v^Error:|^Could not connect' return [] endif endfor diff --git a/autoload/ale/fixers/generic_python.vim b/autoload/ale/fixers/generic_python.vim index 124146be..d55a23c3 100644 --- a/autoload/ale/fixers/generic_python.vim +++ b/autoload/ale/fixers/generic_python.vim @@ -6,13 +6,28 @@ function! ale#fixers#generic_python#AddLinesBeforeControlStatements(buffer, line let l:new_lines = [] let l:last_indent_size = 0 let l:last_line_is_blank = 0 + let l:in_docstring = 0 for l:line in a:lines let l:indent_size = len(matchstr(l:line, '^ *')) + if !l:in_docstring + " Make sure it is not just a single line docstring and then verify + " it's starting a new docstring + if match(l:line, '\v^ *("""|'''''').*("""|'''''')') == -1 + \&& match(l:line, '\v^ *("""|'''''')') >= 0 + let l:in_docstring = 1 + endif + else + if match(l:line, '\v^ *.*("""|'''''')') >= 0 + let l:in_docstring = 0 + endif + endif + if !l:last_line_is_blank + \&& !l:in_docstring \&& l:indent_size <= l:last_indent_size - \&& match(l:line, '\v^ *(return|if|for|while|break|continue)') >= 0 + \&& match(l:line, '\v^ *(return|if|for|while|break|continue)(\(| |$)') >= 0 call add(l:new_lines, '') endif diff --git a/autoload/ale/fixers/gomod.vim b/autoload/ale/fixers/gomod.vim new file mode 100644 index 00000000..68895f9b --- /dev/null +++ b/autoload/ale/fixers/gomod.vim @@ -0,0 +1,10 @@ +call ale#Set('go_go_executable', 'go') + +function! ale#fixers#gomod#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'go_go_executable') + + return { + \ 'command': ale#Escape(l:executable) . ' mod edit -fmt %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim index 0a39ef62..a4613817 100644 --- a/autoload/ale/fixers/rubocop.vim +++ b/autoload/ale/fixers/rubocop.vim @@ -1,12 +1,12 @@ +call ale#Set('ruby_rubocop_options', '') +call ale#Set('ruby_rubocop_executable', 'rubocop') + function! ale#fixers#rubocop#GetCommand(buffer) abort - let l:executable = ale#handlers#rubocop#GetExecutable(a:buffer) - let l:exec_args = l:executable =~? 'bundle$' - \ ? ' exec rubocop' - \ : '' + let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable') let l:config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml') let l:options = ale#Var(a:buffer, 'ruby_rubocop_options') - return ale#Escape(l:executable) . l:exec_args + return ale#handlers#ruby#EscapeExecutable(l:executable, 'rubocop') \ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '') \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' --auto-correct %t' diff --git a/autoload/ale/fixers/shfmt.vim b/autoload/ale/fixers/shfmt.vim index fc55f425..06e8da57 100644 --- a/autoload/ale/fixers/shfmt.vim +++ b/autoload/ale/fixers/shfmt.vim @@ -5,12 +5,27 @@ scriptencoding utf-8 call ale#Set('sh_shfmt_executable', 'shfmt') call ale#Set('sh_shfmt_options', '') +function! s:DefaultOption(buffer) abort + if getbufvar(a:buffer, '&expandtab') == 0 + " Tab is used by default + return '' + endif + + let l:tabsize = getbufvar(a:buffer, '&shiftwidth') + + if l:tabsize == 0 + let l:tabsize = getbufvar(a:buffer, '&tabstop') + endif + + return ' -i ' . l:tabsize +endfunction + function! ale#fixers#shfmt#Fix(buffer) abort let l:executable = ale#Var(a:buffer, 'sh_shfmt_executable') let l:options = ale#Var(a:buffer, 'sh_shfmt_options') return { \ 'command': ale#Escape(l:executable) - \ . (empty(l:options) ? '' : ' ' . l:options) + \ . (empty(l:options) ? s:DefaultOption(a:buffer) : ' ' . l:options) \} endfunction diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim index bc10ec21..eda033e4 100644 --- a/autoload/ale/handlers/eslint.vim +++ b/autoload/ale/handlers/eslint.vim @@ -99,6 +99,13 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort \}] endif + if a:lines == ['Could not connect'] + return [{ + \ 'lnum': 1, + \ 'text': 'Could not connect to eslint_d. Try updating eslint_d or killing it.', + \}] + endif + " Matches patterns line the following: " " /path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle] diff --git a/autoload/ale/handlers/rubocop.vim b/autoload/ale/handlers/rubocop.vim deleted file mode 100644 index f6367cf5..00000000 --- a/autoload/ale/handlers/rubocop.vim +++ /dev/null @@ -1,6 +0,0 @@ -call ale#Set('ruby_rubocop_options', '') -call ale#Set('ruby_rubocop_executable', 'rubocop') - -function! ale#handlers#rubocop#GetExecutable(buffer) abort - return ale#Var(a:buffer, 'ruby_rubocop_executable') -endfunction diff --git a/autoload/ale/handlers/ruby.vim b/autoload/ale/handlers/ruby.vim index 110fe156..c28b8b75 100644 --- a/autoload/ale/handlers/ruby.vim +++ b/autoload/ale/handlers/ruby.vim @@ -37,3 +37,10 @@ function! ale#handlers#ruby#HandleSyntaxErrors(buffer, lines) abort return s:HandleSyntaxError(a:buffer, a:lines) endfunction +function! ale#handlers#ruby#EscapeExecutable(executable, bundle_exec) abort + let l:exec_args = a:executable =~? 'bundle' + \ ? ' exec ' . a:bundle_exec + \ : '' + + return ale#Escape(a:executable) . l:exec_args +endfunction diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index 8fbad12f..a11c76bc 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -55,16 +55,29 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort endif let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response) + let l:no_changes = 0 " tsserver sends syntax and semantic errors in separate messages, so we " have to collect the messages separately for each buffer and join them " back together again. if a:error_type is# 'syntax' + if len(l:thislist) is 0 && len(get(l:info, 'syntax_loclist', [])) is 0 + let l:no_changes = 1 + endif + let l:info.syntax_loclist = l:thislist else + if len(l:thislist) is 0 && len(get(l:info, 'semantic_loclist', [])) is 0 + let l:no_changes = 1 + endif + let l:info.semantic_loclist = l:thislist endif + if l:no_changes + return + endif + let l:loclist = get(l:info, 'semantic_loclist', []) \ + get(l:info, 'syntax_loclist', []) @@ -99,9 +112,10 @@ endfunction function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort let l:method = get(a:response, 'method', '') - let l:linter_name = get(s:lsp_linter_map, a:conn_id, '') if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error') + let l:linter_name = get(s:lsp_linter_map, a:conn_id, '') + call s:HandleLSPErrorMessage(l:linter_name, a:response) elseif l:method is# 'textDocument/publishDiagnostics' call s:HandleLSPDiagnostics(a:conn_id, a:response) diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim index aefbb691..180a37d0 100644 --- a/autoload/ale/preview.vim +++ b/autoload/ale/preview.vim @@ -15,13 +15,13 @@ function! ale#preview#Show(lines, ...) abort setlocal modifiable setlocal noreadonly setlocal nobuflisted - let &l:filetype = get(l:options, 'filetype', 'ale-preview') setlocal buftype=nofile setlocal bufhidden=wipe :%d call setline(1, a:lines) setlocal nomodifiable setlocal readonly + let &l:filetype = get(l:options, 'filetype', 'ale-preview') if get(l:options, 'stay_here') wincmd p diff --git a/autoload/ale/python.vim b/autoload/ale/python.vim index 1f963431..8d6bf1f0 100644 --- a/autoload/ale/python.vim +++ b/autoload/ale/python.vim @@ -1,6 +1,8 @@ " Author: w0rp <devw0rp@gmail.com> " Description: Functions for integrating with Python linters. +call ale#Set('python_auto_pipenv', '0') + let s:sep = has('win32') ? '\' : '/' " bin is used for Unix virtualenv directories, and Scripts is for Windows. let s:bin_dir = has('unix') ? 'bin' : 'Scripts' @@ -107,3 +109,8 @@ function! ale#python#FindExecutable(buffer, base_var_name, path_list) abort return ale#Var(a:buffer, a:base_var_name . '_executable') endfunction + +" Detects whether a pipenv environment is present. +function! ale#python#PipenvPresent(buffer) abort + return findfile('Pipfile.lock', expand('#' . a:buffer . ':p:h') . ';') isnot# '' +endfunction diff --git a/autoload/ale/toggle.vim b/autoload/ale/toggle.vim index 1d052b4f..8e642b3f 100644 --- a/autoload/ale/toggle.vim +++ b/autoload/ale/toggle.vim @@ -15,21 +15,6 @@ function! s:DisablePostamble() abort endif endfunction -function! s:CleanupEveryBuffer() abort - for l:key in keys(g:ale_buffer_info) - " The key could be a filename or a buffer number, so try and - " convert it to a number. We need a number for the other - " functions. - let l:buffer = str2nr(l:key) - - if l:buffer > 0 - " Stop all jobs and clear the results for everything, and delete - " all of the data we stored for the buffer. - call ale#engine#Cleanup(l:buffer) - endif - endfor -endfunction - function! ale#toggle#Toggle() abort let g:ale_enabled = !get(g:, 'ale_enabled') @@ -40,7 +25,7 @@ function! ale#toggle#Toggle() abort call ale#balloon#Enable() endif else - call s:CleanupEveryBuffer() + call ale#engine#CleanupEveryBuffer() call s:DisablePostamble() if exists('*ale#balloon#Disable') @@ -64,7 +49,7 @@ function! ale#toggle#Disable() abort endfunction function! ale#toggle#Reset() abort - call s:CleanupEveryBuffer() + call ale#engine#CleanupEveryBuffer() call ale#highlight#UpdateHighlights() endfunction diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index 58a2065e..e0491653 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -405,7 +405,7 @@ endfunction " the buffer. function! ale#util#Writefile(buffer, lines, filename) abort let l:corrected_lines = getbufvar(a:buffer, '&fileformat') is# 'dos' - \ ? map(copy(a:lines), 'v:val . "\r"') + \ ? map(copy(a:lines), 'substitute(v:val, ''\r*$'', ''\r'', '''')') \ : a:lines call writefile(l:corrected_lines, a:filename) " no-custom-checks diff --git a/doc/ale-go.txt b/doc/ale-go.txt index 1d55763e..71b248ee 100644 --- a/doc/ale-go.txt +++ b/doc/ale-go.txt @@ -20,6 +20,15 @@ the benefit of running a number of linters, more than ALE would by default, while ensuring it doesn't run any linters known to be slow or resource intensive. +g:ale_go_go_executable *g:ale_go_go_options* + *b:ale_go_go_options* + + Type: |String| + Default: `'go'` + + The executable that will be run for the `gobuild` and `govet` linters, and + the gomod` fixer. + =============================================================================== gobuild *ale-go-gobuild* diff --git a/doc/ale-haskell.txt b/doc/ale-haskell.txt index c1f1e889..a59fa4e2 100644 --- a/doc/ale-haskell.txt +++ b/doc/ale-haskell.txt @@ -77,6 +77,15 @@ g:ale_haskell_hlint_executable *g:ale_haskell_hlint_executable* This variable can be changed to use a different executable for hlint. + +g:ale_haskell_hlint_options g:ale_haskell_hlint_options + b:ale_haskell_hlint_options + Type: String + Default: '' + + This variable can be used to pass extra options to the underlying hlint + executable. + =============================================================================== stack-build *ale-haskell-stack-build* diff --git a/doc/ale-php.txt b/doc/ale-php.txt index f38c3f88..2eed838e 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -170,6 +170,16 @@ g:ale_php_phpstan_configuration *g:ale_php_phpstan_configuration* =============================================================================== +psalm *ale-php-psalm* + +g:ale_php_psalm_executable *g:ale_php_psalm_executable* + *b:ale_php_psalm_executable* + Type: |String| + Default: `'psalm'` + + This variable sets the executable used for psalm. + +=============================================================================== php-cs-fixer *ale-php-php-cs-fixer* g:ale_php_cs_fixer_executable *g:ale_php_cs_fixer_executable* diff --git a/doc/ale-python.txt b/doc/ale-python.txt index b5c469b1..0b8e1746 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -2,6 +2,14 @@ ALE Python Integration *ale-python-options* +g:ale_python_auto_pipenv *g:ale_python_auto_pipenv* + *b:ale_python_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + =============================================================================== ALE Python Project Root Behavior *ale-python-root* @@ -137,6 +145,15 @@ g:ale_python_flake8_use_global *g:ale_python_flake8_use_global* Both variables can be set with `b:` buffer variables instead. +g:ale_python_flake8_auto_pipenv *g:ale_python_flake8_auto_pipenv* + *b:ale_python_flake8_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== isort *ale-python-isort* @@ -211,6 +228,15 @@ g:ale_python_mypy_use_global *g:ale_python_mypy_use_global* See |ale-integrations-local-executables| +g:ale_python_mypy_auto_pipenv *g:ale_python_mypy_auto_pipenv* + *b:ale_python_mypy_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== prospector *ale-python-prospector* @@ -253,6 +279,15 @@ g:ale_python_prospector_use_global *g:ale_python_prospector_use_global* See |ale-integrations-local-executables| +g:ale_python_prospector_auto_pipenv *g:ale_python_prospector_auto_pipenv* + *b:ale_python_prospector_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== pycodestyle *ale-python-pycodestyle* @@ -284,6 +319,15 @@ g:ale_python_pycodestyle_use_global *g:ale_python_pycodestyle_use_global* See |ale-integrations-local-executables| +g:ale_python_pycodestyle_auto_pipenv *g:ale_python_pycodestyle_auto_pipenv* + *b:ale_python_pycodestyle_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== pyflakes *ale-python-pyflakes* @@ -298,6 +342,15 @@ g:ale_python_pyflakes_executable *g:ale_python_pyflakes_executable* Set this to `'pipenv'` to invoke `'pipenv` `run` `pyflakes'`. +g:ale_python_pyflakes_auto_pipenv *g:ale_python_pyflakes_auto_pipenv* + *b:ale_python_pyflakes_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== pylint *ale-python-pylint* @@ -350,6 +403,15 @@ g:ale_python_pylint_use_global *g:ale_python_pylint_use_global* See |ale-integrations-local-executables| +g:ale_python_pylint_auto_pipenv *g:ale_python_pylint_auto_pipenv* + *b:ale_python_pylint_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== pyls *ale-python-pyls* @@ -374,6 +436,15 @@ g:ale_python_pyls_use_global *g:ale_python_pyls_use_global* See |ale-integrations-local-executables| +g:ale_python_pyls_auto_pipenv *g:ale_python_pyls_auto_pipenv* + *b:ale_python_pyls_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== pyre *ale-python-pyre* @@ -398,6 +469,15 @@ g:ale_python_pyre_use_global *g:ale_python_pyre_use_global* See |ale-integrations-local-executables| +g:ale_python_pyre_auto_pipenv *g:ale_python_pyre_auto_pipenv* + *b:ale_python_pyre_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + =============================================================================== vulture *ale-python-vulture* diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt index ec7b07cf..f8a41999 100644 --- a/doc/ale-ruby.txt +++ b/doc/ale-ruby.txt @@ -5,6 +5,15 @@ ALE Ruby Integration *ale-ruby-options* =============================================================================== brakeman *ale-ruby-brakeman* +g:ale_ruby_brakeman_executable *g:ale_ruby_brakeman_executable* + *b:ale_ruby_brakeman_executable* + Type: String + Default: `'brakeman'` + + Override the invoked brakeman binary. Set this to `'bundle'` to invoke + `'bundle` `exec` brakeman'. + + g:ale_ruby_brakeman_options *g:ale_ruby_brakeman_options* *b:ale_ruby_brakeman_options* Type: |String| @@ -20,10 +29,11 @@ g:ale_ruby_rails_best_practices_executable *g:ale_ruby_rails_best_practices_executable* *b:ale_ruby_rails_best_practices_executable* Type: String - Default: 'rails_best_practices' + Default: `'rails_best_practices'` Override the invoked rails_best_practices binary. Set this to `'bundle'` to - invoke `'bundle` `exec` `rails_best_practices'`. + invoke `'bundle` `exec` rails_best_practices'. + g:ale_ruby_rails_best_practices_options *g:ale_ruby_rails_best_practices_options* @@ -37,6 +47,15 @@ g:ale_ruby_rails_best_practices_options =============================================================================== reek *ale-ruby-reek* +g:ale_ruby_reek_executable *g:ale_ruby_reek_executable* + *b:ale_ruby_reek_executable* + Type: String + Default: `'reek'` + + Override the invoked reek binary. Set this to `'bundle'` to invoke + `'bundle` `exec` reek'. + + g:ale_ruby_reek_show_context *g:ale_ruby_reek_show_context* *b:ale_ruby_reek_show_context* Type: |Number| @@ -63,8 +82,8 @@ g:ale_ruby_rubocop_executable *g:ale_ruby_rubocop_executable* Type: String Default: `'rubocop'` - Override the invoked rubocop binary. This is useful for running rubocop - from binstubs or a bundle. + Override the invoked rubocop binary. Set this to `'bundle'` to invoke + `'bundle` `exec` rubocop'. g:ale_ruby_rubocop_options *g:ale_ruby_rubocop_options* @@ -101,19 +120,13 @@ g:ale_ruby_rufo_executable *g:ale_ruby_rufo_executable* =============================================================================== solargraph *ale-ruby-solargraph* -g:ale_ruby_solargraph_host *g:ale_ruby_solargraph_host* - *b:ale_ruby_solargraph_host* +g:ale_ruby_solargraph_executable *g:ale_ruby_solargraph_executable* + *b:ale_ruby_solargraph_executable* Type: String - Default: `'127.0.0.1'` - - The host/ip where the solargraph language server is running. + Default: `'solargraph'` -g:ale_ruby_solargraph_port *g:ale_ruby_solargraph_port* - *b:ale_ruby_solargraph_port* - Type: String - Default: `'7658'` - - The listening port where the solargraph language server is running. + Override the invoked solargraph binary. This is useful for running solargraph + from binstubs or a bundle. =============================================================================== diff --git a/doc/ale-thrift.txt b/doc/ale-thrift.txt index ed858db8..bb2ec058 100644 --- a/doc/ale-thrift.txt +++ b/doc/ale-thrift.txt @@ -28,7 +28,7 @@ g:ale_thrift_thrift_generators *g:ale_thrift_thrift_generators* g:ale_thrift_thrift_includes *g:ale_thrift_thrift_includes* *b:ale_thrift_thrift_includes* Type: |List| of |String|s - Default: `[]` + Default: `['.']` This list contains paths that will be searched for thrift `include` directives. diff --git a/doc/ale.txt b/doc/ale.txt index ac8925f7..1cf2945f 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -206,6 +206,7 @@ CONTENTS *ale-contents* phpcs...............................|ale-php-phpcs| phpmd...............................|ale-php-phpmd| phpstan.............................|ale-php-phpstan| + psalm...............................|ale-php-psalm| php-cs-fixer........................|ale-php-php-cs-fixer| po....................................|ale-po-options| write-good..........................|ale-po-write-good| @@ -402,7 +403,7 @@ Notes: * FusionScript: `fusion-lint` * Git Commit Messages: `gitlint` * GLSL: glslang, `glslls` -* Go: `gofmt`, `goimports`, `go vet`!!, `golint`, `gotype`!!, `gometalinter`!!, `go build`!!, `gosimple`!!, `staticcheck`!!, `golangserver`, `golangci-lint`!! +* Go: `gofmt`, `goimports`, `go mod`!!, `go vet`!!, `golint`, `gotype`!!, `gometalinter`!!, `go build`!!, `gosimple`!!, `staticcheck`!!, `golangserver`, `golangci-lint`!! * GraphQL: `eslint`, `gqlint`, `prettier` * Hack: `hack`, `hackfmt`, `hhast` * Haml: `haml-lint` @@ -433,7 +434,7 @@ Notes: * OCaml: `merlin` (see |ale-ocaml-merlin|), `ols`, `ocamlformat` * Pawn: `uncrustify` * Perl: `perl -c`, `perl-critic`, `perltidy` -* PHP: `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf`, `php-cs-fixer` +* PHP: `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf`, `php-cs-fixer`, `psalm`!! * PO: `alex`!!, `msgfmt`, `proselint`, `write-good` * Pod: `alex`!!, `proselint`, `write-good` * Pony: `ponyc` @@ -527,12 +528,13 @@ circumstances. ALE will report problems with your code in the following ways, listed with their relevant options. -* By updating loclist. (On by default) - |g:ale_set_loclist| -* By updating quickfix. (Off by default) - |g:ale_set_quickfix| -* By setting error highlights. - |g:ale_set_highlights| -* By creating signs in the sign column. - |g:ale_set_signs| -* By echoing messages based on your cursor. - |g:ale_echo_cursor| -* By showing balloons for your mouse cursor - |g:ale_set_balloons| +* By updating loclist. (On by default) - |g:ale_set_loclist| +* By updating quickfix. (Off by default) - |g:ale_set_quickfix| +* By setting error highlights. - |g:ale_set_highlights| +* By creating signs in the sign column. - |g:ale_set_signs| +* By echoing messages based on your cursor. - |g:ale_echo_cursor| +* By displaying the preview based on your cursor. - |g:ale_cursor_detail| +* By showing balloons for your mouse cursor - |g:ale_set_balloons| Please consult the documentation for each option, which can reveal some other ways of tweaking the behaviour of each way of displaying problems. You can @@ -805,6 +807,20 @@ g:ale_change_sign_column_color *g:ale_change_sign_column_color* windows. +g:ale_close_preview_on_insert *g:ale_close_preview_on_insert* + + Type: |Number| + Default: `0` + + When this option is set to `1`, ALE's |preview-window| will be automatically + closed upon entering Insert Mode. This option can be used in combination + with |g:ale_cursor_detail| for automatically displaying the preview window + on problem lines, and automatically closing it again when editing text. + + This setting must be set to `1` before ALE is loaded for this behavior + to be enabled. See |ale-lint-settings-on-startup|. + + g:ale_command_wrapper *g:ale_command_wrapper* *b:ale_command_wrapper* Type: |String| @@ -893,6 +909,27 @@ g:ale_completion_max_suggestions *g:ale_completion_max_suggestions* Adjust this option as needed, depending on the complexity of your codebase and your available processing power. +g:ale_cursor_detail *g:ale_cursor_detail* + + Type: |Number| + Default: `0` + + When this option is set to `1`, ALE's |preview-window| will be automatically + opened when the cursor moves onto lines with problems. ALE will search for + problems using the same logic that |g:ale_echo_cursor| uses. The preview + window will be closed automatically when you move away from the line. + + Messages are only displayed after a short delay. See |g:ale_echo_delay|. + + The preview window is opened without stealing focus, which means your cursor + will stay in the same buffer as it currently is. + + The preview window can be closed automatically upon entering Insert mode + by setting |g:ale_close_preview_on_insert| to `1`. + + Either this setting or |g:ale_echo_cursor| must be set to `1` before ALE is + loaded for messages to be displayed. See |ale-lint-settings-on-startup|. + g:ale_echo_cursor *g:ale_echo_cursor* @@ -903,11 +940,14 @@ g:ale_echo_cursor *g:ale_echo_cursor* cursor is near a warning or error. ALE will attempt to find the warning or error at a column nearest to the cursor when the cursor is resting on a line which contains a warning or error. This option can be set to `0` to disable - this behaviour. - The format of the message can be customizable in |g:ale_echo_msg_format|. + this behavior. - You should set this setting once before ALE is loaded, and restart Vim if - you want to change your preferences. See |ale-lint-settings-on-startup|. + Messages are only displayed after a short delay. See |g:ale_echo_delay|. + + The format of the message can be customized with |g:ale_echo_msg_format|. + + Either this setting or |g:ale_cursor_detail| must be set to `1` before ALE + is loaded for messages to be displayed. See |ale-lint-settings-on-startup|. g:ale_echo_delay *g:ale_echo_delay* @@ -916,7 +956,7 @@ g:ale_echo_delay *g:ale_echo_delay* Default: `10` Given any integer, this option controls the number of milliseconds before - ALE will echo a message for a problem near the cursor. + ALE will echo or preview a message for a problem near the cursor. The value can be increased to decrease the amount of processing ALE will do for files displaying a large number of problems. @@ -1163,6 +1203,12 @@ g:ale_lint_on_text_changed *g:ale_lint_on_text_changed* ALE will check buffers after a short delay, with a timer which resets on each change. The delay can be configured by adjusting the |g:ale_lint_delay| variable. + *ale-linting-interrupts-mapping* + + Due to a bug in Vim, ALE can interrupt mappings with pending key presses, + per |timeoutlen|. If this happens, follow the advice for enabling + |g:ale_lint_on_insert_leave| below, and set this option to `'normal'`, or + disable it entirely. You should set this setting once before ALE is loaded, and restart Vim if you want to change your preferences. See |ale-lint-settings-on-startup|. @@ -1178,7 +1224,7 @@ g:ale_lint_on_insert_leave *g:ale_lint_on_insert_leave* ALE will not lint files when you escape insert mode with |CTRL-C| by default. You can make ALE lint files with this option when you use |CTRL-C| - with the following keybind. > + with the following mapping. > " Make using Ctrl+C do the same as Escape, to trigger autocmd commands inoremap <C-c> <Esc> diff --git a/plugin/ale.vim b/plugin/ale.vim index ffb5da8a..41da7c74 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -109,6 +109,13 @@ let g:ale_set_highlights = get(g:, 'ale_set_highlights', has('syntax')) " This flag can be set to 0 to disable echoing when the cursor moves. let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1) +" This flag can be set to 1 to automatically show errors in the preview window. +let g:ale_cursor_detail = get(g:, 'ale_cursor_detail', 0) + +" This flag can be set to 1 to automatically close the preview window upon +" entering Insert Mode. +let g:ale_close_preview_on_insert = get(g:, 'ale_close_preview_on_insert', 0) + " This flag can be set to 0 to disable balloon support. let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has('gui_running')) @@ -126,6 +133,9 @@ let g:ale_history_log_output = get(g:, 'ale_history_log_output', 1) " Enable automatic completion with LSP servers and tsserver let g:ale_completion_enabled = get(g:, 'ale_completion_enabled', 0) +" Enable automatic detection of pipenv for Python linters. +let g:ale_python_auto_pipenv = get(g:, 'ale_python_auto_pipenv', 0) + if g:ale_set_balloons call ale#balloon#Enable() endif @@ -217,4 +227,8 @@ augroup ALECleanupGroup " Clean up buffers automatically when they are unloaded. autocmd BufDelete * if exists('*ale#engine#Cleanup') | call ale#engine#Cleanup(str2nr(expand('<abuf>'))) | endif autocmd QuitPre * call ale#events#QuitEvent(str2nr(expand('<abuf>'))) + + if exists('##VimSuspend') + autocmd VimSuspend * if exists('*ale#engine#CleanupEveryBuffer') | call ale#engine#CleanupEveryBuffer() | endif + endif augroup END diff --git a/test/command_callback/test_brakeman_command_callback.vader b/test/command_callback/test_brakeman_command_callback.vader index 61be4caf..15dbbe1c 100644 --- a/test/command_callback/test_brakeman_command_callback.vader +++ b/test/command_callback/test_brakeman_command_callback.vader @@ -12,7 +12,8 @@ Execute(The brakeman command callback should detect absence of a valid Rails app Execute(The brakeman command callback should find a valid Rails app root): call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/db/test.rb') - AssertLinter 'brakeman', 'brakeman -f json -q -p ' + AssertLinter 'brakeman', ale#Escape('brakeman') + \ . ' -f json -q -p ' \ . ale#Escape(ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app')) Execute(The brakeman command callback should include configured options): @@ -20,5 +21,17 @@ Execute(The brakeman command callback should include configured options): let g:ale_ruby_brakeman_options = '--combobulate' - AssertLinter 'brakeman', 'brakeman -f json -q --combobulate -p ' + AssertLinter 'brakeman', ale#Escape('brakeman') + \ . ' -f json -q --combobulate -p ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app')) + +Execute(Setting bundle appends 'exec brakeman'): + call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/db/test.rb') + + let g:ale_ruby_brakeman_executable = 'bundle' + let g:ale_ruby_brakeman_options = '--combobulate' + + AssertLinter 'bundle', ale#Escape('bundle') + \ . ' exec brakeman' + \ . ' -f json -q --combobulate -p ' \ . ale#Escape(ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app')) diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader index f12b6747..ede511e0 100644 --- a/test/command_callback/test_flake8_command_callback.vader +++ b/test/command_callback/test_flake8_command_callback.vader @@ -1,5 +1,6 @@ Before: call ale#assert#SetUpLinterTest('python', 'flake8') + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' WithChainResults ['3.0.0'] @@ -152,8 +153,16 @@ Execute(Using `python -m flake8` should be supported for running flake8): Execute(Setting executable to 'pipenv' should append 'run flake8'): let g:ale_python_flake8_executable = 'path/to/pipenv' - " FIXME: pipenv should check the vresion with flake8. + " FIXME: pipenv should check the version with flake8. WithChainResults [] AssertLinter 'path/to/pipenv', \ ale#path#BufferCdString(bufnr('')) \ . ale#Escape('path/to/pipenv') . ' run flake8 --format=default -' + +Execute(Pipenv is detected when python_flake8_auto_pipenv is set): + let g:ale_python_flake8_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('pipenv') . ' run flake8 --format=default --stdin-display-name %s -' diff --git a/test/command_callback/test_gobuild_command_callback.vader b/test/command_callback/test_gobuild_command_callback.vader index f9673213..8acbec56 100644 --- a/test/command_callback/test_gobuild_command_callback.vader +++ b/test/command_callback/test_gobuild_command_callback.vader @@ -1,9 +1,12 @@ Before: + Save g:ale_go_go_executable + call ale#assert#SetUpLinterTest('go', 'gobuild') WithChainResults ['/foo/bar', '/foo/baz'] After: + Restore call ale#assert#TearDownLinterTest() Execute(The default commands should be correct): @@ -17,3 +20,12 @@ Execute(Extra options should be supported): AssertLinter 'go', \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' \ . 'go test --foo-bar -c -o /dev/null ./' + + let g:ale_go_gobuild_options = '' + +Execute(The executable should be configurable): + let g:ale_go_go_executable = 'foobar' + + AssertLinter 'foobar', + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'foobar test -c -o /dev/null ./' diff --git a/test/command_callback/test_golangci_lint_command_callback.vader b/test/command_callback/test_golangci_lint_command_callback.vader index 6cb73246..b3805f3a 100644 --- a/test/command_callback/test_golangci_lint_command_callback.vader +++ b/test/command_callback/test_golangci_lint_command_callback.vader @@ -9,7 +9,7 @@ Execute(The golangci-lint defaults should be correct): AssertLinter 'golangci-lint', \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' \ . ale#Escape('golangci-lint') - \ . ' run ' . ale#util#EscapePCRE(expand('%' . ':t')) + \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --enable-all' Execute(The golangci-lint callback should use a configured executable): @@ -18,7 +18,7 @@ Execute(The golangci-lint callback should use a configured executable): AssertLinter 'something else', \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' \ . ale#Escape('something else') - \ . ' run ' . ale#util#EscapePCRE(expand('%' . ':t')) + \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --enable-all' Execute(The golangci-lint callback should use configured options): @@ -27,7 +27,7 @@ Execute(The golangci-lint callback should use configured options): AssertLinter 'golangci-lint', \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' \ . ale#Escape('golangci-lint') - \ . ' run ' . ale#util#EscapePCRE(expand('%' . ':t')) + \ . ' run ' . ale#Escape(expand('%' . ':t')) \ . ' --foobar' Execute(The golangci-lint `lint_package` option should use the correct command): diff --git a/test/command_callback/test_govet_command_callback.vader b/test/command_callback/test_govet_command_callback.vader index 3718e0a7..ab93a5cb 100644 --- a/test/command_callback/test_govet_command_callback.vader +++ b/test/command_callback/test_govet_command_callback.vader @@ -1,12 +1,19 @@ Before: + Save g:ale_go_go_executable + Save g:ale_go_govet_options call ale#assert#SetUpLinterTest('go', 'govet') After: + Restore call ale#assert#TearDownLinterTest() Execute(The default command should be correct): - AssertLinter 'go', 'cd ' . ale#Escape(expand('%:p:h')) . ' && go vet .' + AssertLinter 'go', 'cd ' . ale#Escape(expand('%:p:h')) . ' && go vet .' Execute(Extra options should be supported): let g:ale_go_govet_options = '--foo-bar' - AssertLinter 'go', 'cd ' . ale#Escape(expand('%:p:h')) . ' && go vet . --foo-bar' + AssertLinter 'go', 'cd ' . ale#Escape(expand('%:p:h')) . ' && go vet --foo-bar .' + +Execute(The executable should be configurable): + let g:ale_go_go_executable = 'foobar' + AssertLinter 'foobar', 'cd ' . ale#Escape(expand('%:p:h')) . ' && foobar vet .' diff --git a/test/command_callback/test_haskell_hlint_command_callbacks.vader b/test/command_callback/test_haskell_hlint_command_callbacks.vader new file mode 100644 index 00000000..fb354ed4 --- /dev/null +++ b/test/command_callback/test_haskell_hlint_command_callbacks.vader @@ -0,0 +1,16 @@ +Before: + call ale#assert#SetUpLinterTest('haskell', 'hlint') + let b:base_opts = '--color=never --json -' + +After: + unlet! b:base_opts + call ale#assert#TearDownLinterTest() + +Execute(executable should be configurable): + AssertLinter 'hlint', ale#Escape('hlint') . ' ' . b:base_opts + let b:ale_haskell_hlint_executable = 'myHlint' + AssertLinter 'myHlint', ale#Escape('myHlint') . ' ' . b:base_opts + +Execute(should accept options): + let b:ale_haskell_hlint_options= '-h myhlintfile.yaml' + AssertLinter 'hlint', ale#Escape('hlint') . ' -h myhlintfile.yaml ' . b:base_opts diff --git a/test/command_callback/test_julia_languageserver_callbacks.vader b/test/command_callback/test_julia_languageserver_callbacks.vader index a1f3a1ae..3bc46e3d 100644 --- a/test/command_callback/test_julia_languageserver_callbacks.vader +++ b/test/command_callback/test_julia_languageserver_callbacks.vader @@ -1,14 +1,18 @@ Before: + Save g:ale_julia_executable + call ale#assert#SetUpLinterTest('julia', 'languageserver') After: + Restore + call ale#assert#TearDownLinterTest() Execute(The default executable path should be correct): AssertLinter 'julia', \ ale#Escape('julia') . \' --startup-file=no --history-file=no -e ' . - \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(STDIN, STDOUT, false); server.runlinter = true; run(server);') + \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);') Execute(The executable should be configurable): let g:ale_julia_executable = 'julia-new' @@ -16,7 +20,7 @@ Execute(The executable should be configurable): AssertLinter 'julia-new', \ ale#Escape('julia-new') . \' --startup-file=no --history-file=no -e ' . - \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(STDIN, STDOUT, false); server.runlinter = true; run(server);') + \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);') Execute(The project root should be detected correctly): AssertLSPProject '' diff --git a/test/command_callback/test_mypy_command_callback.vader b/test/command_callback/test_mypy_command_callback.vader index 988dfb1b..037d8176 100644 --- a/test/command_callback/test_mypy_command_callback.vader +++ b/test/command_callback/test_mypy_command_callback.vader @@ -69,3 +69,11 @@ Execute(Setting executable to 'pipenv' appends 'run mypy'): \ ale#path#BufferCdString(bufnr('')) \ . 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') + + AssertLinter 'pipenv', + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('pipenv') . ' run mypy --show-column-numbers --shadow-file %s %t %s' diff --git a/test/command_callback/test_prospector_command_callback.vader b/test/command_callback/test_prospector_command_callback.vader index 316b9883..0d692bde 100644 --- a/test/command_callback/test_prospector_command_callback.vader +++ b/test/command_callback/test_prospector_command_callback.vader @@ -10,3 +10,11 @@ Execute(Setting executable to 'pipenv' appends 'run prospector'): AssertLinter 'path/to/pipenv', \ ale#Escape('path/to/pipenv') . ' run prospector' \ . ' --messages-only --absolute-paths --zero-exit --output-format json %s' + +Execute(Pipenv is detected when python_prospector_auto_pipenv is set): + let g:ale_python_prospector_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run prospector' + \ . ' --messages-only --absolute-paths --zero-exit --output-format json %s' diff --git a/test/command_callback/test_psalm_command_callbacks.vader b/test/command_callback/test_psalm_command_callbacks.vader new file mode 100644 index 00000000..4c31b7b4 --- /dev/null +++ b/test/command_callback/test_psalm_command_callbacks.vader @@ -0,0 +1,12 @@ +Before: + call ale#assert#SetUpLinterTest('php', 'psalm') + +After: + call ale#assert#TearDownLinterTest() + +Execute(Custom executables should be used for the executable and command): + let g:ale_php_psalm_executable = 'psalm_test' + + AssertLinter 'psalm_test', + \ ale#Escape('psalm_test') . ' --diff --output-format=emacs %s' + diff --git a/test/command_callback/test_pycodestyle_command_callback.vader b/test/command_callback/test_pycodestyle_command_callback.vader index 851eede9..a3a338a9 100644 --- a/test/command_callback/test_pycodestyle_command_callback.vader +++ b/test/command_callback/test_pycodestyle_command_callback.vader @@ -24,3 +24,10 @@ Execute(Setting executable to 'pipenv' appends 'run pycodestyle'): AssertLinter 'path/to/pipenv', \ ale#Escape('path/to/pipenv') . ' run pycodestyle -' + +Execute(Pipenv is detected when python_pycodestyle_auto_pipenv is set): + let g:ale_python_pycodestyle_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run pycodestyle -' diff --git a/test/command_callback/test_pyflakes_command_callback.vader b/test/command_callback/test_pyflakes_command_callback.vader index efc925fe..92f83820 100644 --- a/test/command_callback/test_pyflakes_command_callback.vader +++ b/test/command_callback/test_pyflakes_command_callback.vader @@ -37,3 +37,10 @@ Execute(Setting executable to 'pipenv' appends 'run pyflakes'): AssertLinter 'path/to/pipenv', \ ale#Escape('path/to/pipenv') . ' run pyflakes %t', + +Execute(Pipenv is detected when python_pyflakes_auto_pipenv is set): + let g:ale_python_pyflakes_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run pyflakes %t' diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader index be2908f9..6b21b127 100644 --- a/test/command_callback/test_pylint_command_callback.vader +++ b/test/command_callback/test_pylint_command_callback.vader @@ -68,3 +68,12 @@ Execute(Setting executable to 'pipenv' appends 'run pylint'): \ ale#path#BufferCdString(bufnr('')) \ . 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') + + AssertLinter 'pipenv', + \ ale#path#BufferCdString(bufnr('')) + \ . ale#Escape('pipenv') . ' run pylint' + \ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s' diff --git a/test/command_callback/test_pyls_command_callback.vader b/test/command_callback/test_pyls_command_callback.vader index 53bf3000..531b5b3b 100644 --- a/test/command_callback/test_pyls_command_callback.vader +++ b/test/command_callback/test_pyls_command_callback.vader @@ -38,3 +38,10 @@ Execute(Setting executable to 'pipenv' appends 'run pyls'): let g:ale_python_pyls_executable = 'path/to/pipenv' AssertLinter 'path/to/pipenv', ale#Escape('path/to/pipenv') . ' run pyls' + +Execute(Pipenv is detected when python_pyls_auto_pipenv is set): + let g:ale_python_pyls_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run pyls' diff --git a/test/command_callback/test_pyre_command_callback.vader b/test/command_callback/test_pyre_command_callback.vader index 6ad19b56..ba57c117 100644 --- a/test/command_callback/test_pyre_command_callback.vader +++ b/test/command_callback/test_pyre_command_callback.vader @@ -37,3 +37,10 @@ Execute(Setting executable to 'pipenv' appends 'run pyre'): AssertLinter 'path/to/pipenv', \ ale#Escape('path/to/pipenv') . ' run pyre persistent' + +Execute(Pipenv is detected when python_pyre_auto_pipenv is set): + let g:ale_python_pyre_auto_pipenv = 1 + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run pyre persistent' diff --git a/test/command_callback/test_reek_command_callback.vader b/test/command_callback/test_reek_command_callback.vader index 059e5e36..963247aa 100644 --- a/test/command_callback/test_reek_command_callback.vader +++ b/test/command_callback/test_reek_command_callback.vader @@ -7,8 +7,8 @@ After: Execute(The reek callbacks should return the correct default values): WithChainResults ['reek 5.0.0'] AssertLinter 'reek', [ - \ 'reek --version', - \ 'reek -f json --no-progress --no-color --stdin-filename %s', + \ ale#Escape('reek') . ' --version', + \ ale#Escape('reek') . ' -f json --no-progress --no-color --stdin-filename %s', \] " Try with older versions. @@ -16,19 +16,35 @@ Execute(The reek callbacks should return the correct default values): WithChainResults ['reek 4.8.2'] AssertLinter 'reek', [ - \ 'reek --version', - \ 'reek -f json --no-progress --no-color', + \ ale#Escape('reek') . ' --version', + \ ale#Escape('reek') . ' -f json --no-progress --no-color', \] +Execute(Setting bundle appends 'exec reek'): + let g:ale_ruby_reek_executable = 'bundle' + + WithChainResults ['reek 5.0.0'] + AssertLinter 'bundle', ale#Escape('bundle') + \ . ' exec reek' + \ . ' -f json --no-progress --no-color --stdin-filename %s', + + " Try with older versions. + call ale#semver#ResetVersionCache() + + WithChainResults ['reek 4.8.2'] + AssertLinter 'bundle', ale#Escape('bundle') + \ . ' exec reek' + \ . ' -f json --no-progress --no-color' + Execute(The reek version check should be cached): WithChainResults ['reek 5.0.0'] AssertLinter 'reek', [ - \ 'reek --version', - \ 'reek -f json --no-progress --no-color --stdin-filename %s', + \ ale#Escape('reek') . ' --version', + \ ale#Escape('reek') . ' -f json --no-progress --no-color --stdin-filename %s', \] WithChainResults [] AssertLinter 'reek', [ \ '', - \ 'reek -f json --no-progress --no-color --stdin-filename %s', + \ ale#Escape('reek') . ' -f json --no-progress --no-color --stdin-filename %s', \] diff --git a/test/command_callback/test_ruby_solargraph.vader b/test/command_callback/test_ruby_solargraph.vader index b9dd46d8..b39f686e 100644 --- a/test/command_callback/test_ruby_solargraph.vader +++ b/test/command_callback/test_ruby_solargraph.vader @@ -7,30 +7,33 @@ Before: After: call ale#assert#TearDownLinterTest() +Execute(command callback should return default string): + AssertLinter 'solargraph', ale#Escape('solargraph') . ' stdio' + +Execute(command callback executable can be overridden): + let g:ale_ruby_solargraph_executable = 'foobar' + AssertLinter 'foobar', ale#Escape('foobar') . ' stdio' + Execute(should set solargraph for rails app): call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/app/models/thing.rb') AssertLSPLanguage 'ruby' AssertLSPOptions {} AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../ruby_fixtures/valid_rails_app') - AssertLSPAddress '127.0.0.1:7658' Execute(should set solargraph for ruby app1): call ale#test#SetFilename('../ruby_fixtures/valid_ruby_app1/lib/file.rb') AssertLSPLanguage 'ruby' AssertLSPOptions {} AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../ruby_fixtures/valid_ruby_app1') - AssertLSPAddress '127.0.0.1:7658' Execute(should set solargraph for ruby app2): call ale#test#SetFilename('../ruby_fixtures/valid_ruby_app2/lib/file.rb') AssertLSPLanguage 'ruby' AssertLSPOptions {} AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../ruby_fixtures/valid_ruby_app2') - AssertLSPAddress '127.0.0.1:7658' Execute(should set solargraph for ruby app3): call ale#test#SetFilename('../ruby_fixtures/valid_ruby_app3/lib/file.rb') AssertLSPLanguage 'ruby' AssertLSPOptions {} AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../ruby_fixtures/valid_ruby_app3') - AssertLSPAddress '127.0.0.1:7658' diff --git a/test/command_callback/test_thrift_command_callback.vader b/test/command_callback/test_thrift_command_callback.vader index ea217259..cbada818 100644 --- a/test/command_callback/test_thrift_command_callback.vader +++ b/test/command_callback/test_thrift_command_callback.vader @@ -23,22 +23,22 @@ After: call ale#assert#TearDownLinterTest() Execute(The default command should be correct): - AssertLinter 'thrift', ale#Escape('thrift') . ' --gen cpp -strict' . b:suffix + AssertLinter 'thrift', ale#Escape('thrift') . ' --gen cpp -I . -strict' . b:suffix Execute(The executable should be configurable): let b:ale_thrift_thrift_executable = 'foobar' - AssertLinter 'foobar', ale#Escape('foobar') . ' --gen cpp -strict' . b:suffix + AssertLinter 'foobar', ale#Escape('foobar') . ' --gen cpp -I . -strict' . b:suffix Execute(The list of generators should be configurable): let b:ale_thrift_thrift_generators = ['java', 'py:dynamic'] AssertLinter 'thrift', ale#Escape('thrift') - \ . ' --gen java --gen py:dynamic -strict' . b:suffix + \ . ' --gen java --gen py:dynamic -I . -strict' . b:suffix let b:ale_thrift_thrift_generators = [] - AssertLinter 'thrift', ale#Escape('thrift') . ' --gen cpp -strict' . b:suffix + AssertLinter 'thrift', ale#Escape('thrift') . ' --gen cpp -I . -strict' . b:suffix Execute(The list of include paths should be configurable): let b:ale_thrift_thrift_includes = ['included/path'] @@ -50,4 +50,4 @@ Execute(The string of compiler options should be configurable): let b:ale_thrift_thrift_options = '-strict --allow-64bit-consts' AssertLinter 'thrift', ale#Escape('thrift') - \ . ' --gen cpp -strict --allow-64bit-consts' . b:suffix + \ . ' --gen cpp -I . -strict --allow-64bit-consts' . b:suffix diff --git a/test/completion/test_completion_filtering.vader b/test/completion/test_completion_filtering.vader index ae91a952..ffb313ef 100644 --- a/test/completion/test_completion_filtering.vader +++ b/test/completion/test_completion_filtering.vader @@ -12,16 +12,17 @@ After: Execute(Prefix filtering should work for Lists of strings): AssertEqual \ ['FooBar', 'foo'], - \ ale#completion#Filter(bufnr(''), ['FooBar', 'FongBar', 'baz', 'foo'], 'foo') + \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo') AssertEqual \ ['FooBar', 'FongBar', 'baz', 'foo'], - \ ale#completion#Filter(bufnr(''), ['FooBar', 'FongBar', 'baz', 'foo'], '.') + \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.') Execute(Prefix filtering should work for completion items): AssertEqual \ [{'word': 'FooBar'}, {'word': 'foo'}], \ ale#completion#Filter( \ bufnr(''), + \ '', \ [ \ {'word': 'FooBar'}, \ {'word': 'FongBar'}, @@ -40,6 +41,7 @@ Execute(Prefix filtering should work for completion items): \ ], \ ale#completion#Filter( \ bufnr(''), + \ '', \ [ \ {'word': 'FooBar'}, \ {'word': 'FongBar'}, @@ -56,6 +58,7 @@ Execute(Excluding words from completion results should work): \ [{'word': 'Italian'}], \ ale#completion#Filter( \ bufnr(''), + \ '', \ [ \ {'word': 'Italian'}, \ {'word': 'it'}, @@ -67,6 +70,7 @@ Execute(Excluding words from completion results should work): \ [{'word': 'Deutsch'}], \ ale#completion#Filter( \ bufnr(''), + \ '', \ [ \ {'word': 'describe'}, \ {'word': 'Deutsch'}, @@ -78,6 +82,7 @@ Execute(Excluding words from completion results should work): \ [{'word': 'Deutsch'}], \ ale#completion#Filter( \ bufnr(''), + \ '', \ [ \ {'word': 'describe'}, \ {'word': 'Deutsch'}, @@ -90,19 +95,26 @@ Execute(Excluding words from completion results should work with lists of String AssertEqual \ ['Italian'], - \ ale#completion#Filter(bufnr(''), ['Italian', 'it'], 'it') + \ ale#completion#Filter(bufnr(''), '', ['Italian', 'it'], 'it') AssertEqual \ ['Deutsch'], - \ ale#completion#Filter(bufnr(''), ['describe', 'Deutsch'], 'de') + \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], 'de') AssertEqual \ ['Deutsch'], - \ ale#completion#Filter(bufnr(''), ['describe', 'Deutsch'], '.') + \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.') Execute(Filtering shouldn't modify the original list): let b:ale_completion_excluded_words = ['it', 'describe'] let b:suggestions = [{'word': 'describe'}] - AssertEqual [], ale#completion#Filter(bufnr(''), b:suggestions, '.') + AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, '.') AssertEqual b:suggestions, [{'word': 'describe'}] - AssertEqual [], ale#completion#Filter(bufnr(''), b:suggestions, 'de') + AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, 'de') AssertEqual b:suggestions, [{'word': 'describe'}] + +Execute(Filtering should respect filetype triggers): + let b:suggestions = [{'word': 'describe'}] + + AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.') + AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.') + AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::') diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader index d5a45b54..71e53ab6 100644 --- a/test/completion/test_lsp_completion_parsing.vader +++ b/test/completion/test_lsp_completion_parsing.vader @@ -447,3 +447,27 @@ Execute(Should handle missing keys): \ ] \ } \ }) + +Execute(Should handle documentation in the markdown format): + AssertEqual + \ [ + \ {'word': 'migrations', 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1}, + \ ], + \ ale#completion#ParseLSPCompletions({ + \ 'jsonrpc': '2.0', + \ 'id': 6, + \ 'result': { + \ 'isIncomplete': v:false, + \ 'items': [ + \ { + \ 'label': 'migrations', + \ 'kind': 3, + \ 'detail': 'xxx', + \ 'documentation': { + \ 'kind': 'markdown', + \ 'value': 'Markdown documentation', + \ }, + \ }, + \ ], + \ }, + \ }) diff --git a/test/fixers/test_eslint_fixer_callback.vader b/test/fixers/test_eslint_fixer_callback.vader index be33825c..774595e3 100644 --- a/test/fixers/test_eslint_fixer_callback.vader +++ b/test/fixers/test_eslint_fixer_callback.vader @@ -170,3 +170,10 @@ Execute(The eslint_d post-processor should handle error messages correctly): \ ale#fixers#eslint#ProcessEslintDOutput(bufnr(''), [ \ 'Error: No ESLint configuration found.', \ ]) + +Execute(The eslint_d post-processor should handle failing to connect properly): + AssertEqual + \ [], + \ ale#fixers#eslint#ProcessEslintDOutput(bufnr(''), [ + \ 'Could not connect', + \ ]) diff --git a/test/fixers/test_gomod_fixer_callback.vader b/test/fixers/test_gomod_fixer_callback.vader new file mode 100644 index 00000000..a378e961 --- /dev/null +++ b/test/fixers/test_gomod_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_go_go_executable + + " Use an invalid global executable, so we don't match it. + let g:ale_go_go_executable = 'xxxinvalid' + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The gomod callback should return the correct default values): + call ale#test#SetFilename('../go_files/go.mod') + setl ft=gomod + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' mod edit -fmt' + \ . ' %t', + \ }, + \ ale#fixers#gomod#Fix(bufnr('')) diff --git a/test/fixers/test_python_add_blank_lines_fixer.vader b/test/fixers/test_python_add_blank_lines_fixer.vader index 4a91aa10..7d042c8a 100644 --- a/test/fixers/test_python_add_blank_lines_fixer.vader +++ b/test/fixers/test_python_add_blank_lines_fixer.vader @@ -6,15 +6,22 @@ After: Given python(Some Python without blank lines): def foo(): + """ This is a simple test docstring """ return 1 def bar(): + '''This is another simple test docstring''' return 1 return 4 def bar(): + """ + This is a multi-line + docstring + """ + if x: pass for l in x: @@ -44,16 +51,25 @@ Execute(Blank lines should be added appropriately): Expect python(Newlines should be added): def foo(): + """ This is a simple test docstring """ + return 1 def bar(): + '''This is another simple test docstring''' + return 1 return 4 def bar(): + """ + This is a multi-line + docstring + """ + if x: pass @@ -109,3 +125,43 @@ Expect python(extra newlines shouldn't be added to the main block): if __name__ == '__main__': main() + + +Given python(A file with variables/docstring that start with a control statement): + def some(): + """ + This is a docstring that contains an + break control statement and also contains a + return something funny. + """ + + continue_some_var = True + forward_something = False + + if ( + continue_some_var and + forwarded_something + ): + return True + + +Execute(Fix the file): + let g:ale_fixers = {'python': ['add_blank_lines_for_python_control_statements']} + ALEFix + +Expect python(Extra new lines are not added to the file): + def some(): + """ + This is a docstring that contains an + break control statement and also contains a + return something funny. + """ + + continue_some_var = True + forward_something = False + + if ( + continue_some_var and + forwarded_something + ): + return True diff --git a/test/fixers/test_shfmt_fixer_callback.vader b/test/fixers/test_shfmt_fixer_callback.vader index 5dc6e863..99cb0987 100644 --- a/test/fixers/test_shfmt_fixer_callback.vader +++ b/test/fixers/test_shfmt_fixer_callback.vader @@ -1,17 +1,52 @@ Before: Save g:ale_sh_shfmt_executable Save g:ale_sh_shfmt_options + Save &l:expandtab + Save &l:shiftwidth + Save &l:tabstop After: Restore -Execute(The shfmt callback should return the correct default values): +Execute(The shfmt callback should return 'shfmt' as default command): + setlocal noexpandtab + Assert + \ ale#fixers#shfmt#Fix(bufnr('')).command =~# '^' . ale#Escape('shfmt'), + \ "Default command name is expected to be 'shfmt'" + +Execute(The shfmt callback should return the command with no option as default when noexpandtab is set): + let g:ale_sh_shfmt_executable = 'shfmt' + let g:ale_sh_shfmt_options = '' + setlocal noexpandtab AssertEqual \ { \ 'command': ale#Escape('shfmt'), \ }, \ ale#fixers#shfmt#Fix(bufnr('')) +Execute(The shfmt callback should return the command specifying indent width by looking shiftwidth as default): + let g:ale_sh_shfmt_executable = 'shfmt' + let g:ale_sh_shfmt_options = '' + setlocal expandtab + setlocal shiftwidth=4 + AssertEqual + \ { + \ 'command': ale#Escape('shfmt') . ' -i 4', + \ }, + \ ale#fixers#shfmt#Fix(bufnr('')) + +Execute(The shfmt callback should return the command specifying indent width by looking tabstop when shiftwidth is 0 as default): + let g:ale_sh_shfmt_executable = 'shfmt' + let g:ale_sh_shfmt_options = '' + setlocal expandtab + setlocal shiftwidth=0 + setlocal tabstop=8 + AssertEqual + \ { + \ 'command': ale#Escape('shfmt') . ' -i 8', + \ }, + \ ale#fixers#shfmt#Fix(bufnr('')) + Execute(The shfmt executable and options should be configurable): let g:ale_sh_shfmt_executable = 'foobar' let g:ale_sh_shfmt_options = '--some-option' diff --git a/test/go_files/go.mod b/test/go_files/go.mod new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/go_files/go.mod @@ -0,0 +1 @@ + diff --git a/test/handler/test_eslint_handler.vader b/test/handler/test_eslint_handler.vader index 2e8bfd2a..4a57927b 100644 --- a/test/handler/test_eslint_handler.vader +++ b/test/handler/test_eslint_handler.vader @@ -365,3 +365,15 @@ Execute(eslint should handle react errors correctly): \ ale#handlers#eslint#Handle(bufnr(''), [ \ '/path/editor-help.jsx:59:9: Property should be placed on the same line as the component declaration [Error/react/jsx-first-prop-new-line]', \ ]) + +Execute(Failing to connect to eslint_d should be handled correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'text': 'Could not connect to eslint_d. Try updating eslint_d or killing it.', + \ }, + \ ], + \ ale#handlers#eslint#Handle(bufnr(''), [ + \ 'Could not connect', + \ ]) diff --git a/test/handler/test_php_psalm_handler.vader b/test/handler/test_php_psalm_handler.vader new file mode 100644 index 00000000..fd62a467 --- /dev/null +++ b/test/handler/test_php_psalm_handler.vader @@ -0,0 +1,24 @@ +Before: + runtime ale_linters/php/psalm.vim + +After: + call ale#linter#Reset() + +Execute(The php static analyzer handler should parse errors from psalm): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'type': 'W', + \ 'text': 'somewarning', + \ }, + \ { + \ 'lnum': 11, + \ 'type': 'E', + \ 'text': 'someerror', + \ }, + \ ], + \ ale_linters#php#psalm#Handle(347, [ + \ "/file:1:3:warning - somewarning", + \ "/file:11:33:error - someerror", + \ ]) diff --git a/test/python_fixtures/pipenv/Pipfile.lock b/test/python_fixtures/pipenv/Pipfile.lock new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/python_fixtures/pipenv/Pipfile.lock diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index 8d5048f0..4bb894ba 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -175,10 +175,19 @@ Execute (g:ale_lint_on_filetype_changed = 1 should bind the FileType event): \ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''\v^FileType''') Execute (ALECleanupGroup should include the right commands): - AssertEqual [ - \ 'BufDelete * if exists(''*ale#engine#Cleanup'') | call ale#engine#Cleanup(str2nr(expand(''<abuf>''))) | endif', - \ 'QuitPre * call ale#events#QuitEvent(str2nr(expand(''<abuf>'')))', - \], CheckAutocmd('ALECleanupGroup') + if exists('##VimSuspend') + AssertEqual [ + \ 'BufDelete * if exists(''*ale#engine#Cleanup'') | call ale#engine#Cleanup(str2nr(expand(''<abuf>''))) | endif', + \ 'QuitPre * call ale#events#QuitEvent(str2nr(expand(''<abuf>'')))', + \ 'VimSuspend * if exists(''*ale#engine#CleanupEveryBuffer'') | call ale#engine#CleanupEveryBuffer() | endif', + \], CheckAutocmd('ALECleanupGroup') + else + AssertEqual [ + \ 'BufDelete * if exists(''*ale#engine#Cleanup'') | call ale#engine#Cleanup(str2nr(expand(''<abuf>''))) | endif', + \ 'QuitPre * call ale#events#QuitEvent(str2nr(expand(''<abuf>'')))', + \], CheckAutocmd('ALECleanupGroup') + endif + Execute(Enabling completion should set up autocmd events correctly): let g:ale_completion_enabled = 0 diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader index a4e00452..f7bfaa04 100644 --- a/test/test_c_flag_parsing.vader +++ b/test/test_c_flag_parsing.vader @@ -74,7 +74,7 @@ Execute(ParseCFlags should ignore -T and other arguments): \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir --sysroot=subdir ' \ . '-I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) diff --git a/test/test_python_pipenv.vader b/test/test_python_pipenv.vader new file mode 100644 index 00000000..91d957f8 --- /dev/null +++ b/test/test_python_pipenv.vader @@ -0,0 +1,13 @@ +Execute(ale#python#PipenvPresent is true when a pipenv environment is present): + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertEqual + \ ale#python#PipenvPresent(bufnr('%')), + \ 1 + +Execute(ale#python#PipenvPresent is false true when no pipenv environment is present): + call ale#test#SetFilename('/testplugin/test/python_fixtures/no_pipenv/whatever.py') + + AssertEqual + \ ale#python#PipenvPresent(bufnr('%')), + \ 0 diff --git a/test/test_redundant_tsserver_rendering_avoided.vader b/test/test_redundant_tsserver_rendering_avoided.vader new file mode 100644 index 00000000..41cbe5e0 --- /dev/null +++ b/test/test_redundant_tsserver_rendering_avoided.vader @@ -0,0 +1,136 @@ +Before: + Save g:ale_buffer_info + + function! CreateError(type, message) abort + let l:diagnostics = [] + + if !empty(a:message) + let l:diagnostics = [{ + \ 'start': {'line': 1, 'offset': 1}, + \ 'end': {'line': 1, 'offset':1}, + \ 'text': a:message, + \ 'code': 1005, + \}] + endif + + return { + \ 'seq': 0, + \ 'type': 'event', + \ 'event': a:type, + \ 'body': {'file': expand('%:p'), 'diagnostics': l:diagnostics}, + \} + endfunction + + function! CreateLoclist(message) abort + let l:list = [] + + if !empty(a:message) + let l:list = [{ + \ 'lnum': 1, + \ 'col': 1, + \ 'end_lnum': 1, + \ 'end_col': 1, + \ 'text': a:message, + \}] + endif + + return l:list + endfunction + + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('filename.ts') + + runtime autoload/ale/engine.vim + + let g:ale_buffer_info = {bufnr(''): {'loclist': []}} + let g:ale_handle_loclist_called = 0 + + function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort + let g:ale_handle_loclist_called = 1 + endfunction + +After: + Restore + + delfunction CreateError + delfunction CreateLoclist + + call ale#test#RestoreDirectory() + + runtime autoload/ale/engine.vim + +Execute(An initial empty list of syntax errors should be ignored): + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', '')) + + Assert !g:ale_handle_loclist_called + +Execute(An initial list of syntax errors should be handled): + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(Subsequent empty lists should be ignored): + let g:ale_buffer_info[bufnr('')].syntax_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', '')) + + Assert !g:ale_handle_loclist_called + +Execute(Empty then non-empty syntax errors should be handled): + let g:ale_buffer_info[bufnr('')].syntax_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then empty syntax errors should be handled): + let g:ale_buffer_info[bufnr('')].syntax_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', '')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then non-empty syntax errors should be handled): + let g:ale_buffer_info[bufnr('')].syntax_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('syntaxDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(An initial empty list of semantic errors should be ignored): + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', '')) + + Assert !g:ale_handle_loclist_called + +Execute(An initial list of semantic errors should be handled): + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(Subsequent empty lists should be ignored): + let g:ale_buffer_info[bufnr('')].semantic_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', '')) + + Assert !g:ale_handle_loclist_called + +Execute(Empty then non-empty semantic errors should be handled): + let g:ale_buffer_info[bufnr('')].semantic_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then empty semantic errors should be handled): + let g:ale_buffer_info[bufnr('')].semantic_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', '')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then non-empty semantic errors should be handled): + let g:ale_buffer_info[bufnr('')].semantic_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', 'x')) + + Assert g:ale_handle_loclist_called diff --git a/test/test_writefile_function.vader b/test/test_writefile_function.vader index 4e4aab53..8c8a6f17 100644 --- a/test/test_writefile_function.vader +++ b/test/test_writefile_function.vader @@ -28,7 +28,26 @@ Execute(Carriage returns should be included for ale#util#Writefile): AssertEqual \ ["first\r", "second\r", "third\r", ''], \ readfile('.newline-test', 'b') - \ + +Given(A file with extra carriage returns): + first
+ second
+ third
+ fourth + +Execute(Carriage returns should be de-depulicated): + call ale#test#SetFilename('.newline-test') + + setlocal buftype= + noautocmd :w + noautocmd :e! ++ff=dos + + call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test') + + AssertEqual + \ ["first\r", "second\r", "third\r", "fourth\r", ''], + \ readfile('.newline-test', 'b') + Given(A file with Unix line ending characters): first second |