diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 45d5350..0276a65 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,8 +1,28 @@ + +## Information + +**VIM version** + +PASTE JUST THE FIRST TWO LINES OF `:version` HERE. + +Operating System: WHAT OS WERE YOU USING? + +### :ALEInfo + +PASTE OUTPUT OF `:ALEInfo` HERE. YOU CAN TRY `:ALEInfoToClipboard`. + +## What went wrong + +WRITE WHAT WENT WRONG HERE. + +## Reproducing the bug + +Steps for repeating the bug: + +1. Write a list of steps. +2. Otherwise nobody will fix the bug. diff --git a/LICENSE b/LICENSE index 650050f..739ccae 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017, w0rp +Copyright (c) 2016-2018, w0rp All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 89fd380..d5335e2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Asynchronous Lint Engine [![Travis CI Build Status](https://travis-ci.org/w0rp/ale.svg?branch=master)](https://travis-ci.org/w0rp/ale) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/w0rp/ale?svg=true)](https://ci.appveyor.com/project/w0rp/ale) +# Asynchronous Lint Engine [![Travis CI Build Status](https://travis-ci.org/w0rp/ale.svg?branch=master)](https://travis-ci.org/w0rp/ale) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/r0ef1xu8xjmik58d/branch/master?svg=true)](https://ci.appveyor.com/project/w0rp/ale) ![ALE Logo by Mark Grealish - https://www.bhalash.com/](img/logo.jpg?raw=true) @@ -17,8 +17,7 @@ back to a filesystem. In other words, this plugin allows you to lint while you type. In addition to linting support, ALE offers some support for fixing code with -formatting tools, and completion via Language Server Protocol servers, or -servers with similar enough protocols, like `tsserver`. +formatting tools, and some Language Server Protocol and `tsserver` features. ## Table of Contents @@ -27,6 +26,7 @@ servers with similar enough protocols, like `tsserver`. 1. [Linting](#usage-linting) 2. [Fixing](#usage-fixing) 3. [Completion](#usage-completion) + 4. [Go To Definition](#usage-go-to-definition) 3. [Installation](#installation) 1. [Installation with Vim package management](#standard-installation) 2. [Installation with Pathogen](#installation-with-pathogen) @@ -36,15 +36,18 @@ servers with similar enough protocols, like `tsserver`. 1. [How do I disable particular linters?](#faq-disable-linters) 2. [How can I keep the sign gutter open?](#faq-keep-signs) 3. [How can I change the signs ALE uses?](#faq-change-signs) - 4. [How can I show errors or warnings in my statusline?](#faq-statusline) - 5. [How can I show errors or warnings in my lightline?](#faq-lightline) - 6. [How can I change the format for echo messages?](#faq-echo-format) - 7. [How can I execute some code when ALE stops linting?](#faq-autocmd) - 8. [How can I navigate between errors quickly?](#faq-navigation) - 9. [How can I run linters only when I save files?](#faq-lint-on-save) - 10. [How can I use the quickfix list instead of the loclist?](#faq-quickfix) - 11. [How can I check JSX files with both stylelint and eslint?](#faq-jsx-stylelint-eslint) - 12. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad) + 4. [How can I change or disable the highlights ALE uses?](#faq-change-highlights) + 5. [How can I show errors or warnings in my statusline?](#faq-statusline) + 6. [How can I show errors or warnings in my lightline?](#faq-lightline) + 7. [How can I change the format for echo messages?](#faq-echo-format) + 8. [How can I execute some code when ALE starts or stops linting?](#faq-autocmd) + 9. [How can I navigate between errors quickly?](#faq-navigation) + 10. [How can I run linters only when I save files?](#faq-lint-on-save) + 11. [How can I use the quickfix list instead of the loclist?](#faq-quickfix) + 12. [How can I check JSX files with both stylelint and eslint?](#faq-jsx-stylelint-eslint) + 13. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad) + 14. [How can I configure my C or C++ project?](#faq-c-configuration) + 15. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration) @@ -71,83 +74,98 @@ formatting. | -------- | ----- | | ASM | [gcc](https://gcc.gnu.org) | | Ansible | [ansible-lint](https://github.com/willthames/ansible-lint) | -| AsciiDoc | [proselint](http://proselint.com/) | +| API Blueprint | [drafter](https://github.com/apiaryio/drafter) | +| AsciiDoc | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [write-good](https://github.com/btford/write-good) | | Awk | [gawk](https://www.gnu.org/software/gawk/)| -| Bash | shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/) | -| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/) | -| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [gcc](https://gcc.gnu.org/), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html)| -| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [gcc](https://gcc.gnu.org/), [clang-format](https://clang.llvm.org/docs/ClangFormat.html)| +| Bash | shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) | +| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) | +| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) | +| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) | | CUDA | [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) | | C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details, [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) !! see:`help ale-cs-mcsc` for details and configuration| | Chef | [foodcritic](http://www.foodcritic.io/) | +| Clojure | [joker](https://github.com/candid82/joker) | | CMake | [cmakelint](https://github.com/richq/cmake-lint) | | CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) | | Crystal | [crystal](https://crystal-lang.org/) !! | -| CSS | [csslint](http://csslint.net/), [stylelint](https://github.com/stylelint/stylelint), [prettier](https://github.com/prettier/prettier) | +| CSS | [csslint](http://csslint.net/), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) | | Cython (pyrex filetype) | [cython](http://cython.org/) | | D | [dmd](https://dlang.org/dmd-linux.html) | -| Dart | [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) | -| Dockerfile | [hadolint](https://github.com/lukasmartinelli/hadolint) | -| Elixir | [credo](https://github.com/rrrene/credo), [dogma](https://github.com/lpil/dogma) !! | +| Dafny | [dafny](https://rise4fun.com/Dafny) !! | +| Dart | [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) !!, [language_server](https://github.com/natebosch/dart_language_server) | +| Dockerfile | [hadolint](https://github.com/hadolint/hadolint) | +| Elixir | [credo](https://github.com/rrrene/credo), [dialyxir](https://github.com/jeremyjh/dialyxir), [dogma](https://github.com/lpil/dogma) !!| | Elm | [elm-format](https://github.com/avh4/elm-format), [elm-make](https://github.com/elm-lang/elm-make) | -| Erb | [erb](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) | +| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) | | Erlang | [erlc](http://erlang.org/doc/man/erlc.html), [SyntaxErl](https://github.com/ten0s/syntaxerl) | +| Fish | fish [-n flag](https://linux.die.net/man/1/fish) | Fortran | [gcc](https://gcc.gnu.org/) | +| Fountain | [proselint](http://proselint.com/) | | FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) | -| GLSL | [glslang](https://github.com/KhronosGroup/glslang) | -| Go | [gofmt](https://golang.org/cmd/gofmt/), [go vet](https://golang.org/cmd/vet/), [golint](https://godoc.org/github.com/golang/lint), [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) | -| GraphQL | [gqlint](https://github.com/happylinks/gqlint) | +| 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) !! | +| GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) | | Haml | [haml-lint](https://github.com/brigade/haml-lint) | | Handlebars | [ember-template-lint](https://github.com/rwjblue/ember-template-lint) | -| Haskell | [ghc](https://www.haskell.org/ghc/), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [stack-ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools) | -| HTML | [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/) | +| Haskell | [brittany](https://github.com/lspitzner/brittany), [ghc](https://www.haskell.org/ghc/), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [stack-ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools), [hfmt](https://github.com/danstiner/hfmt) | +| HTML | [alex](https://github.com/wooorm/alex) !!, [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/), [write-good](https://github.com/btford/write-good) | | Idris | [idris](http://www.idris-lang.org/) | -| Java | [checkstyle](http://checkstyle.sourceforge.net), [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html) | -| JavaScript | [eslint](http://eslint.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [flow](https://flowtype.org/), [prettier](https://github.com/prettier/prettier), prettier-eslint >= 4.2.0, prettier-standard, [standard](http://standardjs.com/), [xo](https://github.com/sindresorhus/xo) -| JSON | [jsonlint](http://zaa.ch/jsonlint/), [prettier](https://github.com/prettier/prettier) | +| Java | [checkstyle](http://checkstyle.sourceforge.net), [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html), [google-java-format](https://github.com/google/google-java-format) | +| JavaScript | [eslint](http://eslint.org/), [flow](https://flowtype.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [prettier](https://github.com/prettier/prettier), [prettier-eslint](https://github.com/prettier/prettier-eslint), [prettier-standard](https://github.com/sheerun/prettier-standard), [standard](http://standardjs.com/), [xo](https://github.com/sindresorhus/xo) +| JSON | [fixjson](https://github.com/rhysd/fixjson), [jsonlint](http://zaa.ch/jsonlint/), [jq](https://stedolan.github.io/jq/), [prettier](https://github.com/prettier/prettier) | | Kotlin | [kotlinc](https://kotlinlang.org) !!, [ktlint](https://ktlint.github.io) !! see `:help ale-integration-kotlin` for configuration instructions | -| LaTeX | [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck), [proselint](http://proselint.com/) | +| LaTeX | [alex](https://github.com/wooorm/alex) !!, [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) | +| Less | [lessc](https://www.npmjs.com/package/less), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) | | LLVM | [llc](https://llvm.org/docs/CommandGuide/llc.html) | -| Lua | [luacheck](https://github.com/mpeterv/luacheck) | -| Markdown | [mdl](https://github.com/mivok/markdownlint), [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale), [remark-lint](https://github.com/wooorm/remark-lint) !! | +| Lua | [luac](https://www.lua.org/manual/5.1/luac.html), [luacheck](https://github.com/mpeterv/luacheck) | +| Mail | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) | +| Make | [checkmake](https://github.com/mrtazz/checkmake) | +| Markdown | [alex](https://github.com/wooorm/alex) !!, [mdl](https://github.com/mivok/markdownlint), [prettier](https://github.com/prettier/prettier), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [remark-lint](https://github.com/wooorm/remark-lint) !!, [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) | | MATLAB | [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html) | | Nim | [nim check](https://nim-lang.org/docs/nimc.html) !! | | nix | [nix-instantiate](http://nixos.org/nix/manual/#sec-nix-instantiate) | -| nroff | [proselint](http://proselint.com/)| +| nroff | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)| | Objective-C | [clang](http://clang.llvm.org/) | | Objective-C++ | [clang](http://clang.llvm.org/) | -| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions | +| 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) | | Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) | -| PHP | [hack](http://hacklang.org/), [langserver](https://github.com/felixfbecker/php-language-server), [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) | -| Pod | [proselint](http://proselint.com/)| +| PHP | [hack](http://hacklang.org/), [hackfmt](https://github.com/facebook/flow/tree/master/hack/hackfmt), [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/) | +| 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 | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | -| Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | +| Python | [autopep8](https://github.com/hhatto/autopep8), [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), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | | R | [lintr](https://github.com/jimhester/lintr) | -| ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-reason-merlin` for configuration instructions | -| reStructuredText | [proselint](http://proselint.com/) | +| 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) | -| Rust | cargo !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.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 | [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint), [prettier](https://github.com/prettier/prettier) | +| 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) | | Scala | [scalac](http://scala-lang.org), [scalastyle](http://www.scalastyle.org) | | Slim | [slim-lint](https://github.com/sds/slim-lint) | | SML | [smlnj](http://www.smlnj.org/) | -| Solidity | [solium](https://github.com/duaraghav8/Solium) | +| Solidity | [solhint](https://github.com/protofire/solhint), [solium](https://github.com/duaraghav8/Solium) | | Stylus | [stylelint](https://github.com/stylelint/stylelint) | | SQL | [sqlint](https://github.com/purcell/sqlint) | | Swift | [swiftlint](https://github.com/realm/SwiftLint), [swiftformat](https://github.com/nicklockwood/SwiftFormat) | | Tcl | [nagelfar](http://nagelfar.sourceforge.net) !! | -| Texinfo | [proselint](http://proselint.com/)| -| Text^ | [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) | +| Terraform | [tflint](https://github.com/wata727/tflint) | +| Texinfo | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)| +| Text^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good), [redpen](http://redpen.cc/) | | Thrift | [thrift](http://thrift.apache.org/) | -| TypeScript | [eslint](http://eslint.org/), [tslint](https://github.com/palantir/tslint), tsserver, typecheck, [prettier](https://github.com/prettier/prettier) | +| TypeScript | [eslint](http://eslint.org/), [prettier](https://github.com/prettier/prettier), [tslint](https://github.com/palantir/tslint), tsserver, typecheck | | Verilog | [iverilog](https://github.com/steveicarus/iverilog), [verilator](http://www.veripool.org/projects/verilator/wiki/Intro) | | Vim | [vint](https://github.com/Kuniwak/vint) | -| Vim help^ | [proselint](http://proselint.com/)| -| XHTML | [proselint](http://proselint.com/)| -| XML | [xmllint](http://xmlsoft.org/xmllint.html)| +| Vim help^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | +| Vue | [prettier](https://github.com/prettier/prettier) | +| XHTML | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | +| XML | [xmllint](http://xmlsoft.org/xmllint.html) | | YAML | [swaglint](https://github.com/byCedric/swaglint), [yamllint](https://yamllint.readthedocs.io/) | @@ -211,6 +229,15 @@ let g:ale_completion_enabled = 1 See `:help ale-completion` for more information. + + +### 2.iv Go To Definition + +ALE supports jumping to the definition of words under your cursor with the +`:ALEGoToDefinition` command using any enabled LSP linters and `tsserver`. + +See `:help ale-go-to-definition` for more information. + ## 3. Installation @@ -370,9 +397,35 @@ highlight clear ALEErrorSign highlight clear ALEWarningSign ``` + + +### 5.iv. How can I change or disable the highlights ALE uses? + +ALE's highlights problems with highlight groups which link to `SpellBad`, +`SpellCap`, `error`, and `todo` groups by default. The characters that are +highlighted depend on the linters being used, and the information provided to +ALE. + +Highlighting can be disabled completely by setting `g:ale_set_highlights` to +`0`. + +```vim +" Set this in your vimrc file to disabling highlighting +let g:ale_set_highlights = 0 +``` + +You can control all of the highlights ALE uses, say if you are using a different +color scheme which produces ugly highlights. For example: + +```vim +highlight ALEWarning ctermbg=DarkMagenta +``` + +See `:help ale-highlights` for more information. + -### 5.iv. How can I show errors or warnings in my statusline? +### 5.v. How can I show errors or warnings in my statusline? [vim-airline](https://github.com/vim-airline/vim-airline) integrates with ALE for displaying error information in the status bar. If you want to see the @@ -412,68 +465,16 @@ See `:help ale#statusline#Count()` for more information. -### 5.v. How can I show errors or warnings in my lightline? +### 5.vi. How can I show errors or warnings in my lightline? [lightline](https://github.com/itchyny/lightline.vim) does not have built-in -support for ALE, nevertheless it's easy to do it yourself: +support for ALE, nevertheless there is a plugin that adds this functionality: [maximbaz/lightline-ale](https://github.com/maximbaz/lightline-ale). -```vim -" This is regular lightline configuration, we just added -" 'linter_warnings', 'linter_errors' and 'linter_ok' to -" the active right panel. Feel free to move it anywhere. -" `component_expand' and `component_type' are required. -" -" For more info on how this works, see lightline documentation. -let g:lightline = { - \ 'active': { - \ 'right': [ [ 'lineinfo' ], - \ [ 'percent' ], - \ [ 'linter_warnings', 'linter_errors', 'linter_ok' ], - \ [ 'fileformat', 'fileencoding', 'filetype' ] ] - \ }, - \ 'component_expand': { - \ 'linter_warnings': 'LightlineLinterWarnings', - \ 'linter_errors': 'LightlineLinterErrors', - \ 'linter_ok': 'LightlineLinterOK' - \ }, - \ 'component_type': { - \ 'linter_warnings': 'warning', - \ 'linter_errors': 'error', - \ 'linter_ok': 'ok' - \ }, - \ } - -autocmd User ALELint call lightline#update() - -" ale + lightline -function! LightlineLinterWarnings() abort - let l:counts = ale#statusline#Count(bufnr('')) - let l:all_errors = l:counts.error + l:counts.style_error - let l:all_non_errors = l:counts.total - l:all_errors - return l:counts.total == 0 ? '' : printf('%d --', all_non_errors) -endfunction - -function! LightlineLinterErrors() abort - let l:counts = ale#statusline#Count(bufnr('')) - let l:all_errors = l:counts.error + l:counts.style_error - let l:all_non_errors = l:counts.total - l:all_errors - return l:counts.total == 0 ? '' : printf('%d >>', all_errors) -endfunction - -function! LightlineLinterOK() abort - let l:counts = ale#statusline#Count(bufnr('')) - let l:all_errors = l:counts.error + l:counts.style_error - let l:all_non_errors = l:counts.total - l:all_errors - return l:counts.total == 0 ? '✓' : '' -endfunction -``` - -See `:help ale#statusline#Count()` and [lightline documentation](https://github.com/itchyny/lightline.vim#advanced-configuration) -for more information. +For more information, check out the sources of that plugin, `:help ale#statusline#Count()` and [lightline documentation](https://github.com/itchyny/lightline.vim#advanced-configuration). -### 5.vi. How can I change the format for echo messages? +### 5.vii. How can I change the format for echo messages? There are 3 global options that allow customizing the echoed message. @@ -498,22 +499,24 @@ Will give you: -### 5.vii. How can I execute some code when ALE stops linting? +### 5.viii. How can I execute some code when ALE starts or stops linting? ALE runs its own [autocmd](http://vimdoc.sourceforge.net/htmldoc/autocmd.html) -event whenever has a linter has been successfully executed and processed. This -autocmd event can be used to call arbitrary functions after ALE stops linting. +events whenever has a linter is started and has been successfully executed and +processed. These events can be used to call arbitrary functions before and after +ALE stops linting. ```vim augroup YourGroup autocmd! - autocmd User ALELint call YourFunction() + autocmd User ALELintPre call YourFunction() + autocmd User ALELintPost call YourFunction() augroup END ``` -### 5.viii. How can I navigate between errors quickly? +### 5.ix. How can I navigate between errors quickly? ALE offers some commands with `` keybinds for moving between warnings and errors quickly. You can map the keys Ctrl+j and Ctrl+k to moving between errors @@ -529,7 +532,7 @@ For more information, consult the online documentation with -### 5.ix. How can I run linters only when I save files? +### 5.x. How can I run linters only when I save files? ALE offers an option `g:ale_lint_on_save` for enabling running the linters when files are saved. This option is enabled by default. If you only @@ -549,7 +552,7 @@ files, you can set `g:ale_lint_on_save` to `0`. -### 5.x. How can I use the quickfix list instead of the loclist? +### 5.xi. How can I use the quickfix list instead of the loclist? The quickfix list can be enabled by turning the `g:ale_set_quickfix` option on. If you wish to also disable the loclist, you can disable @@ -574,9 +577,12 @@ let g:ale_open_list = 1 let g:ale_keep_list_window_open = 1 ``` +You can also set `let g:ale_list_vertical = 1` to open the windows vertically +instead of the default horizontally. + -### 5.xi. How can I check JSX files with both stylelint and eslint? +### 5.xii. How can I check JSX files with both stylelint and eslint? If you configure ALE options correctly in your vimrc file, and install the right tools, you can check JSX files with stylelint and eslint. @@ -609,7 +615,7 @@ no linter will be run twice for the same file. -### 5.xii. Will this plugin eat all of my laptop battery power? +### 5.xiii. Will this plugin eat all of my laptop battery power? ALE takes advantage of the power of various tools to check your code. This of course means that CPU time will be used to continuously check your code. If you @@ -631,3 +637,64 @@ still be an advantage. If you are still concerned, you can turn the automatic linting off altogether, including the option `g:ale_lint_on_enter`, and you can run ALE manually with `:ALELint`. + + + +### 5.xiv. How can I configure my C or C++ project? + +The structure of C and C++ projects varies wildly from project to project, with +many different build tools being used for building them, and many different +formats for project configuration files. ALE can run compilers easily, but +ALE cannot easily detect which compiler flags to use. + +Some tools and build configurations can generate +[compile_commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html) +files. The `cppcheck`, `clangcheck` and `clangtidy` linters can read these +files for automatically determining the appropriate compiler flags to use. + +For linting with compilers like `gcc` and `clang`, and with other tools, you +will need to tell ALE which compiler flags to use yourself. You can use +different options for different projects with the `g:ale_pattern_options` +setting. Consult the documentation for that setting for more information. +`b:ale_linters` can be used to select which tools you want to run, say if you +want to use only `gcc` for one project, and only `clang` for another. + +You may also configure buffer-local settings for linters with project-specific +vimrc files. [local_vimrc](https://github.com/LucHermitte/local_vimrc) can be +used for executing local vimrc files which can be shared in your project. + + + +### 5.xv. How can I configure ALE differently for different buffers? + +ALE offers various ways to configure which linters or fixers are run, and +other settings. For the majority of ALE's settings, they can either be +configured globally with a `g:` variable prefix, or for a specific buffer +with a `b:` variable prefix. For example, you can configure a Python ftplugin +file like so. + +```vim +" In ~/.vim/ftplugin/python.vim + +" Check Python files with flake8 and pylint. +let b:ale_linters = ['flake8', 'pylint'] +" Fix Python files with autopep8 and yapf. +let b:ale_fixers = ['autopep8', 'yapf'] +" Disable warnings about trailing whitespace for Python files. +let b:ale_warn_about_trailing_whitespace = 0 +``` + +For configuring files based on regular expression patterns matched against the +absolute path to a file, you can use `g:ale_pattern_options`. + +```vim +" Do not lint or fix minified files. +let g:ale_pattern_options = { +\ '\.min\.js$': {'ale_linters': [], 'ale_fixers': []}, +\ '\.min\.css$': {'ale_linters': [], 'ale_fixers': []}, +\} +" If you configure g:ale_pattern_options outside of vimrc, you need this. +let g:ale_pattern_options_enabled = 1 +``` + +Buffer-local variables for settings always override the global settings. diff --git a/ale_linters/ansible/ansible_lint.vim b/ale_linters/ansible/ansible_lint.vim index 7d68cde..0b3b39c 100644 --- a/ale_linters/ansible/ansible_lint.vim +++ b/ale_linters/ansible/ansible_lint.vim @@ -21,7 +21,7 @@ function! ale_linters#ansible#ansible_lint#Handle(buffer, lines) abort for l:match in ale#util#GetMatches(a:lines, l:pattern) let l:code = l:match[4] - if l:code is# 'EANSIBLE002' + if l:code is# 'EANSIBLE0002' \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace') " Skip warnings for trailing whitespace if the option is off. continue @@ -31,7 +31,8 @@ function! ale_linters#ansible#ansible_lint#Handle(buffer, lines) abort call add(l:output, { \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, - \ 'text': l:code . ': ' . l:match[5], + \ 'text': l:match[5], + \ 'code': l:code, \ 'type': l:code[:0] is# 'E' ? 'E' : 'W', \}) endif diff --git a/ale_linters/apiblueprint/drafter.vim b/ale_linters/apiblueprint/drafter.vim new file mode 100644 index 0000000..9cded35 --- /dev/null +++ b/ale_linters/apiblueprint/drafter.vim @@ -0,0 +1,36 @@ +" Author: nametake https://nametake.github.io +" Description: apiblueprint parser + +function! ale_linters#apiblueprint#drafter#HandleErrors(buffer, lines) abort + " Matches patterns line the following: + " + " warning: (3) unable to parse response signature, expected 'response [] [()]'; line 4, column 3k - line 4, column 22 + " warning: (10) message-body asset is expected to be a pre-formatted code block, separate it by a newline and indent every of its line by 12 spaces or 3 tabs; line 30, column 5 - line 30, column 9; line 31, column 9 - line 31, column 14; line 32, column 9 - line 32, column 14 + let l:pattern = '\(^.*\): (\d\+) \(.\{-\}\); line \(\d\+\), column \(\d\+\) - line \d\+, column \d\+\(.*; line \d\+, column \d\+ - line \(\d\+\), column \(\d\+\)\)\{-\}$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines[2:], l:pattern) + let l:item = { + \ 'type': l:match[1] is# 'warning' ? 'W' : 'E', + \ 'text': l:match[2], + \ 'lnum': l:match[3] + 0, + \ 'col': l:match[4] + 0, + \} + if l:match[5] isnot# '' + let l:item.end_lnum = l:match[6] + 0 + let l:item.end_col = l:match[7] + 0 + endif + call add(l:output, l:item) + endfor + + return l:output +endfunction + + +call ale#linter#Define('apiblueprint', { +\ 'name': 'drafter', +\ 'output_stream': 'stderr', +\ 'executable': 'drafter', +\ 'command': 'drafter --use-line-num --validate %t', +\ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors', +\}) diff --git a/ale_linters/asciidoc/alex.vim b/ale_linters/asciidoc/alex.vim new file mode 100644 index 0000000..79b04fc --- /dev/null +++ b/ale_linters/asciidoc/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for asciidoc files + +call ale#linter#Define('help', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/asciidoc/redpen.vim b/ale_linters/asciidoc/redpen.vim new file mode 100644 index 0000000..819e385 --- /dev/null +++ b/ale_linters/asciidoc/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('asciidoc', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f asciidoc -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/asciidoc/write-good.vim b/ale_linters/asciidoc/write-good.vim new file mode 100644 index 0000000..c986cc6 --- /dev/null +++ b/ale_linters/asciidoc/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for AsciiDoc files + +call ale#linter#Define('asciidoc', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim index 39b1f7c..4ac876f 100644 --- a/ale_linters/asm/gcc.vim +++ b/ale_linters/asm/gcc.vim @@ -1,10 +1,16 @@ " Author: Lucas Kolstad " Description: gcc linter for asm files -let g:ale_asm_gcc_options = get(g:, 'ale_asm_gcc_options', '-Wall') +call ale#Set('asm_gcc_executable', 'gcc') +call ale#Set('asm_gcc_options', '-Wall') + +function! ale_linters#asm#gcc#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'asm_gcc_executable') +endfunction function! ale_linters#asm#gcc#GetCommand(buffer) abort - return 'gcc -x assembler -fsyntax-only ' + return ale#Escape(ale_linters#asm#gcc#GetExecutable(a:buffer)) + \ . ' -x assembler -fsyntax-only ' \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) \ . ' ' . ale#Var(a:buffer, 'asm_gcc_options') . ' -' endfunction @@ -27,7 +33,7 @@ endfunction call ale#linter#Define('asm', { \ 'name': 'gcc', \ 'output_stream': 'stderr', -\ 'executable': 'gcc', +\ 'executable_callback': 'ale_linters#asm#gcc#GetExecutable', \ 'command_callback': 'ale_linters#asm#gcc#GetCommand', \ 'callback': 'ale_linters#asm#gcc#Handle', \}) diff --git a/ale_linters/c/flawfinder.vim b/ale_linters/c/flawfinder.vim new file mode 100644 index 0000000..27f269f --- /dev/null +++ b/ale_linters/c/flawfinder.vim @@ -0,0 +1,30 @@ +" Author: Christian Gibbons +" Description: flawfinder linter for c files + +call ale#Set('c_flawfinder_executable', 'flawfinder') +call ale#Set('c_flawfinder_options', '') +call ale#Set('c_flawfinder_minlevel', 1) + +function! ale_linters#c#flawfinder#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'c_flawfinder_executable') +endfunction + +function! ale_linters#c#flawfinder#GetCommand(buffer) abort + + " Set the minimum vulnerability level for flawfinder to bother with + let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'c_flawfinder_minlevel') + + return ale#Escape(ale_linters#c#flawfinder#GetExecutable(a:buffer)) + \ . ' -CDQS' + \ . ale#Var(a:buffer, 'c_flawfinder_options') + \ . l:minlevel + \ . ' %t' +endfunction + +call ale#linter#Define('c', { +\ 'name': 'flawfinder', +\ 'output_stream': 'stdout', +\ 'executable_callback': 'ale_linters#c#flawfinder#GetExecutable', +\ 'command_callback': 'ale_linters#c#flawfinder#GetCommand', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\}) diff --git a/ale_linters/chef/foodcritic.vim b/ale_linters/chef/foodcritic.vim index 079e304..2c28246 100644 --- a/ale_linters/chef/foodcritic.vim +++ b/ale_linters/chef/foodcritic.vim @@ -1,24 +1,37 @@ " Author: Edward Larkey " Author: Jose Junior +" Author: w0rp " Description: This file adds the foodcritic linter for Chef files. -" Support options! -let g:ale_chef_foodcritic_options = get(g:, 'ale_chef_foodcritic_options', '') -let g:ale_chef_foodcritic_executable = get(g:, 'ale_chef_foodcritic_executable', 'foodcritic') +call ale#Set('chef_foodcritic_executable', 'foodcritic') +call ale#Set('chef_foodcritic_options', '') + +function! ale_linters#chef#foodcritic#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'chef_foodcritic_executable') +endfunction + +function! ale_linters#chef#foodcritic#GetCommand(buffer) abort + let l:executable = ale_linters#chef#foodcritic#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'chef_foodcritic_options') + + return ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . escape(l:options, '~') : '') + \ . ' %s' +endfunction function! ale_linters#chef#foodcritic#Handle(buffer, lines) abort " Matches patterns line the following: " " FC002: Avoid string interpolation where not required: httpd.rb:13 - let l:pattern = '^\(.\+:\s.\+\):\s\(.\+\):\(\d\+\)$' + let l:pattern = '\v([^:]+): (.+): ([a-zA-Z]?:?[^:]+):(\d+)$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) - let l:text = l:match[1] - call add(l:output, { - \ 'lnum': l:match[3] + 0, - \ 'text': l:text, + \ 'code': l:match[1], + \ 'text': l:match[2], + \ 'filename': l:match[3], + \ 'lnum': l:match[4] + 0, \ 'type': 'W', \}) endfor @@ -26,17 +39,10 @@ function! ale_linters#chef#foodcritic#Handle(buffer, lines) abort return l:output endfunction -function! ale_linters#chef#foodcritic#GetCommand(buffer) abort - return printf('%s %s %%t', - \ ale#Var(a:buffer, 'chef_foodcritic_executable'), - \ escape(ale#Var(a:buffer, 'chef_foodcritic_options'), '~') - \) -endfunction - - call ale#linter#Define('chef', { \ 'name': 'foodcritic', -\ 'executable': 'foodcritic', +\ 'executable_callback': 'ale_linters#chef#foodcritic#GetExecutable', \ 'command_callback': 'ale_linters#chef#foodcritic#GetCommand', \ 'callback': 'ale_linters#chef#foodcritic#Handle', +\ 'lint_file': 1, \}) diff --git a/ale_linters/clojure/joker.vim b/ale_linters/clojure/joker.vim new file mode 100644 index 0000000..e78066f --- /dev/null +++ b/ale_linters/clojure/joker.vim @@ -0,0 +1,32 @@ +" Author: Nic West +" Description: linter for clojure using joker https://github.com/candid82/joker + +function! ale_linters#clojure#joker#HandleJokerFormat(buffer, lines) abort + " output format + " ::: : + let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+):? ((Read error|Parse error|Parse warning|Exception): ?(.+))$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:type = 'E' + if l:match[4] is? 'Parse warning' + let l:type = 'W' + endif + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'text': l:match[3], + \ 'type': l:type, + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('clojure', { +\ 'name': 'joker', +\ 'output_stream': 'stderr', +\ 'executable': 'joker', +\ 'command': 'joker --lint %t', +\ 'callback': 'ale_linters#clojure#joker#HandleJokerFormat', +\}) diff --git a/ale_linters/cpp/clangcheck.vim b/ale_linters/cpp/clangcheck.vim index 4b6169c..a109d5d 100644 --- a/ale_linters/cpp/clangcheck.vim +++ b/ale_linters/cpp/clangcheck.vim @@ -24,9 +24,9 @@ function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort " detected. return ale#Escape(ale_linters#cpp#clangcheck#GetExecutable(a:buffer)) \ . ' -analyze %s' + \ . (empty(l:build_dir) ? ' -extra-arg -Xclang -extra-arg -analyzer-output=text' : '') \ . (!empty(l:user_options) ? ' ' . l:user_options : '') \ . (!empty(l:build_dir) ? ' -p ' . ale#Escape(l:build_dir) : '') - \ . (empty(l:build_dir) ? ' -extra-arg -Xanalyzer -extra-arg -analyzer-output=text' : '') endfunction call ale#linter#Define('cpp', { diff --git a/ale_linters/cpp/flawfinder.vim b/ale_linters/cpp/flawfinder.vim new file mode 100644 index 0000000..a19f596 --- /dev/null +++ b/ale_linters/cpp/flawfinder.vim @@ -0,0 +1,30 @@ +" Author: Christian Gibbons +" Description: flawfinder linter for c++ files + +call ale#Set('cpp_flawfinder_executable', 'flawfinder') +call ale#Set('cpp_flawfinder_options', '') +call ale#Set('cpp_flawfinder_minlevel', 1) + +function! ale_linters#cpp#flawfinder#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'cpp_flawfinder_executable') +endfunction + +function! ale_linters#cpp#flawfinder#GetCommand(buffer) abort + + " Set the minimum vulnerability level for flawfinder to bother with + let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'cpp_flawfinder_minlevel') + + return ale#Escape(ale_linters#cpp#flawfinder#GetExecutable(a:buffer)) + \ . ' -CDQS' + \ . ale#Var(a:buffer, 'cpp_flawfinder_options') + \ . l:minlevel + \ . ' %t' +endfunction + +call ale#linter#Define('cpp', { +\ 'name': 'flawfinder', +\ 'output_stream': 'stdout', +\ 'executable_callback': 'ale_linters#cpp#flawfinder#GetExecutable', +\ 'command_callback': 'ale_linters#cpp#flawfinder#GetCommand', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\}) diff --git a/ale_linters/cs/mcs.vim b/ale_linters/cs/mcs.vim index 3d042f9..b5c4054 100644 --- a/ale_linters/cs/mcs.vim +++ b/ale_linters/cs/mcs.vim @@ -8,15 +8,16 @@ function! ale_linters#cs#mcs#Handle(buffer, lines) abort " Look for lines like the following. " " Tests.cs(12,29): error CSXXXX: ; expected - let l:pattern = '^.\+.cs(\(\d\+\),\(\d\+\)): \(.\+\): \(.\+\)' + let l:pattern = '^\v(.+\.cs)\((\d+),(\d+)\)\: ([^ ]+) ([^ ]+): (.+)$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) call add(l:output, { - \ 'lnum': l:match[1] + 0, - \ 'col': l:match[2] + 0, - \ 'text': l:match[3] . ': ' . l:match[4], - \ 'type': l:match[3] =~# '^error' ? 'E' : 'W', + \ 'lnum': l:match[2] + 0, + \ 'col': l:match[3] + 0, + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \ 'code': l:match[5], + \ 'text': l:match[6], \}) endfor diff --git a/ale_linters/cs/mcsc.vim b/ale_linters/cs/mcsc.vim index 38a0855..8a78d3b 100644 --- a/ale_linters/cs/mcsc.vim +++ b/ale_linters/cs/mcsc.vim @@ -1,55 +1,47 @@ -" general mcs options which are likely to stay constant across -" source trees like -pkg:dotnet -let g:ale_cs_mcsc_options = get(g:, 'ale_cs_mcsc_options', '') +call ale#Set('cs_mcsc_options', '') +call ale#Set('cs_mcsc_source', '') +call ale#Set('cs_mcsc_assembly_path', []) +call ale#Set('cs_mcsc_assemblies', []) -" path string pointing the linter to the base path of the -" source tree to check -let g:ale_cs_mcsc_source = get(g:, 'ale_cs_mcsc_source','.') +function! s:GetWorkingDirectory(buffer) abort + let l:working_directory = ale#Var(a:buffer, 'cs_mcsc_source') -" list of search paths for additional assemblies to consider -let g:ale_cs_mcsc_assembly_path = get(g:, 'ale_cs_mcsc_assembly_path',[]) + if !empty(l:working_directory) + return l:working_directory + endif + + return expand('#' . a:buffer . ':p:h') +endfunction -" list of assemblies to consider -let g:ale_cs_mcsc_assemblies = get(g:, 'ale_cs_mcsc_assemblies',[]) function! ale_linters#cs#mcsc#GetCommand(buffer) abort + " Pass assembly paths via the -lib: parameter. + let l:path_list = ale#Var(a:buffer, 'cs_mcsc_assembly_path') - " if list of assembly search paths is not empty convert it to - " appropriate -lib: parameter of mcs - let l:path = ale#Var(a:buffer, 'cs_mcsc_assembly_path') + let l:lib_option = !empty(l:path_list) + \ ? '-lib:' . join(map(copy(l:path_list), 'ale#Escape(v:val)'), ',') + \ : '' - if !empty(l:path) - let l:path = '-lib:"' . join(l:path, '","') .'"' - else - let l:path ='' - endif + " Pass paths to DLL files via the -r: parameter. + let l:assembly_list = ale#Var(a:buffer, 'cs_mcsc_assemblies') - " if list of assemblies to link is not empty convert it to the - " appropriate -r: parameter of mcs - let l:assemblies = ale#Var(a:buffer, 'cs_mcsc_assemblies') - - if !empty(l:assemblies) - let l:assemblies = '-r:"' . join(l:assemblies, '","') . '"' - else - let l:assemblies ='' - endif + let l:r_option = !empty(l:assembly_list) + \ ? '-r:' . join(map(copy(l:assembly_list), 'ale#Escape(v:val)'), ',') + \ : '' " register temporary module target file with ale let l:out = tempname() call ale#engine#ManageFile(a:buffer, l:out) - " assemble linter command string to be executed by ale - " implicitly set -unsafe mcs flag set compilation - " target to module (-t:module), direct mcs output to - " temporary file (-out) - " - return 'cd "' . ale#Var(a:buffer, 'cs_mcsc_source') . '";' + " The code is compiled as a module and the output is redirected to a + " temporary file. + return ale#path#CdString(s:GetWorkingDirectory(a:buffer)) \ . 'mcs -unsafe' \ . ' ' . ale#Var(a:buffer, 'cs_mcsc_options') - \ . ' ' . l:path - \ . ' ' . l:assemblies + \ . ' ' . l:lib_option + \ . ' ' . l:r_option \ . ' -out:' . l:out \ . ' -t:module' - \ . ' -recurse:"*.cs"' + \ . ' -recurse:' . ale#Escape('*.cs') endfunction function! ale_linters#cs#mcsc#Handle(buffer, lines) abort @@ -60,17 +52,19 @@ function! ale_linters#cs#mcsc#Handle(buffer, lines) abort " NOTE: pattern also captures file name as linter compiles all " files within the source tree rooted at the specified source " path and not just the file loaded in the buffer - let l:pattern = '^\(.\+\.cs\)(\(\d\+\),\(\d\+\)): \(.\+\): \(.\+\)' + let l:pattern = '^\v(.+\.cs)\((\d+),(\d+)\)\: ([^ ]+) ([^ ]+): (.+)$' let l:output = [] - let l:source = ale#Var(a:buffer, 'cs_mcsc_source') + + let l:dir = s:GetWorkingDirectory(a:buffer) for l:match in ale#util#GetMatches(a:lines, l:pattern) call add(l:output, { - \ 'filename': fnamemodify(l:source . '/' . l:match[1], ':p'), + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, - \ 'text': l:match[4] . ': ' . l:match[5], - \ 'type': l:match[4] =~# '^error' ? 'E' : 'W', + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \ 'code': l:match[5], + \ 'text': l:match[6], \}) endfor diff --git a/ale_linters/dafny/dafny.vim b/ale_linters/dafny/dafny.vim new file mode 100644 index 0000000..8bbf1b1 --- /dev/null +++ b/ale_linters/dafny/dafny.vim @@ -0,0 +1,25 @@ +" Author: Taylor Blau + +function! ale_linters#dafny#dafny#Handle(buffer, lines) abort + let l:pattern = '\v(.*)\((\d+),(\d+)\): (.*): (.*)' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'bufnr': a:buffer, + \ 'col': l:match[3] + 0, + \ 'lnum': l:match[2] + 0, + \ 'text': l:match[5], + \ 'type': l:match[4] =~# '^Error' ? 'E' : 'W' + \ }) + endfor + return l:output +endfunction + +call ale#linter#Define('dafny', { +\ 'name': 'dafny', +\ 'executable': 'dafny', +\ 'command': 'dafny %s /compile:0', +\ 'callback': 'ale_linters#dafny#dafny#Handle', +\ 'lint_file': 1, +\ }) diff --git a/ale_linters/dart/dartanalyzer.vim b/ale_linters/dart/dartanalyzer.vim index f7b82c4..ef33c9d 100644 --- a/ale_linters/dart/dartanalyzer.vim +++ b/ale_linters/dart/dartanalyzer.vim @@ -13,7 +13,7 @@ function! ale_linters#dart#dartanalyzer#GetCommand(buffer) abort return ale#Escape(l:executable) \ . (!empty(l:path) ? ' --packages ' . ale#Escape(l:path) : '') - \ . ' %t' + \ . ' %s' endfunction function! ale_linters#dart#dartanalyzer#Handle(buffer, lines) abort @@ -37,4 +37,5 @@ call ale#linter#Define('dart', { \ 'executable_callback': 'ale_linters#dart#dartanalyzer#GetExecutable', \ 'command_callback': 'ale_linters#dart#dartanalyzer#GetCommand', \ 'callback': 'ale_linters#dart#dartanalyzer#Handle', +\ 'lint_file': 1, \}) diff --git a/ale_linters/dart/language_server.vim b/ale_linters/dart/language_server.vim new file mode 100644 index 0000000..15c7701 --- /dev/null +++ b/ale_linters/dart/language_server.vim @@ -0,0 +1,30 @@ +" Author: aurieh +" Description: A language server for dart + +call ale#Set('dart_language_server_executable', 'dart_language_server') + +function! ale_linters#dart#language_server#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'dart_language_server_executable') +endfunction + +function! ale_linters#dart#language_server#GetLanguage(buffer) abort + return 'dart' +endfunction + +function! ale_linters#dart#language_server#GetProjectRoot(buffer) abort + " Note: pub only looks for pubspec.yaml, there's no point in adding + " support for pubspec.yml + let l:pubspec = ale#path#FindNearestFile(a:buffer, 'pubspec.yaml') + + return !empty(l:pubspec) ? fnamemodify(l:pubspec, ':h:h') : '' +endfunction + +call ale#linter#Define('dart', { +\ 'name': 'language_server', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale_linters#dart#language_server#GetExecutable', +\ 'command_callback': 'ale_linters#dart#language_server#GetExecutable', +\ 'language_callback': 'ale_linters#dart#language_server#GetLanguage', +\ 'project_root_callback': 'ale_linters#dart#language_server#GetProjectRoot', +\}) + diff --git a/ale_linters/dockerfile/hadolint.vim b/ale_linters/dockerfile/hadolint.vim index 5550d69..7772afb 100644 --- a/ale_linters/dockerfile/hadolint.vim +++ b/ale_linters/dockerfile/hadolint.vim @@ -2,31 +2,51 @@ " always, yes, never call ale#Set('dockerfile_hadolint_use_docker', 'never') -call ale#Set('dockerfile_hadolint_docker_image', 'lukasmartinelli/hadolint') +call ale#Set('dockerfile_hadolint_docker_image', 'hadolint/hadolint') function! ale_linters#dockerfile#hadolint#Handle(buffer, lines) abort " Matches patterns line the following: " - " stdin:19: F: Pipe chain should start with a raw value. - let l:pattern = '\v^/dev/stdin:?(\d+)? (\S+) (.+)$' + " /dev/stdin:19 DL3001 Pipe chain should start with a raw value. + " /dev/stdin:19:3 unexpected thing + let l:pattern = '\v^/dev/stdin:(\d+):?(\d+)? ((DL|SC)(\d+) )?(.+)$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) let l:lnum = 0 + let l:colnum = 0 if l:match[1] isnot# '' let l:lnum = l:match[1] + 0 endif + if l:match[2] isnot# '' + let l:colnum = l:match[2] + 0 + endif + let l:type = 'W' - let l:text = l:match[3] + let l:text = l:match[6] + let l:detail = l:match[6] + let l:domain = 'https://github.com/hadolint/hadolint/wiki/' + + if l:match[4] is# 'SC' + let l:domain = 'https://github.com/koalaman/shellcheck/wiki/' + endif + + if l:match[5] isnot# '' + let l:code = l:match[4] . l:match[5] + let l:link = ' ( ' . l:domain . l:code . ' )' + let l:detail = l:code . l:link . "\n\n" . l:detail + else + let l:type = 'E' + endif call add(l:output, { \ 'lnum': l:lnum, - \ 'col': 0, + \ 'col': l:colnum, \ 'type': l:type, \ 'text': l:text, - \ 'nr': l:match[2], + \ 'detail': l:detail \}) endfor diff --git a/ale_linters/elixir/credo.vim b/ale_linters/elixir/credo.vim index 3699dd2..af2ff48 100644 --- a/ale_linters/elixir/credo.vim +++ b/ale_linters/elixir/credo.vim @@ -32,6 +32,6 @@ endfunction call ale#linter#Define('elixir', { \ 'name': 'credo', \ 'executable': 'mix', -\ 'command': 'mix credo suggest --format=flycheck --read-from-stdin %s', +\ 'command': 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s', \ 'callback': 'ale_linters#elixir#credo#Handle', \}) diff --git a/ale_linters/elixir/dialyxir.vim b/ale_linters/elixir/dialyxir.vim new file mode 100644 index 0000000..5ef3a04 --- /dev/null +++ b/ale_linters/elixir/dialyxir.vim @@ -0,0 +1,34 @@ +" Author: Fran C. - https://github.com/franciscoj +" Description: Add dialyzer support for elixir through dialyxir +" https://github.com/jeremyjh/dialyxir + +function! ale_linters#elixir#dialyxir#Handle(buffer, lines) abort + " Matches patterns line the following: + " + " lib/filename.ex:19: Function fname/1 has no local return + let l:pattern = '\v(.+):(\d+): (.+)$' + let l:output = [] + let l:type = 'W' + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + if bufname(a:buffer) == l:match[1] + call add(l:output, { + \ 'bufnr': a:buffer, + \ 'lnum': l:match[2] + 0, + \ 'col': 0, + \ 'type': l:type, + \ 'text': l:match[3], + \}) + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('elixir', { +\ 'name': 'dialyxir', +\ 'executable': 'mix', +\ 'command': 'mix dialyzer', +\ 'callback': 'ale_linters#elixir#dialyxir#Handle', +\}) + diff --git a/ale_linters/elixir/dogma.vim b/ale_linters/elixir/dogma.vim index b4f32b0..71cf4f4 100644 --- a/ale_linters/elixir/dogma.vim +++ b/ale_linters/elixir/dogma.vim @@ -32,7 +32,7 @@ endfunction call ale#linter#Define('elixir', { \ 'name': 'dogma', \ 'executable': 'mix', -\ 'command': 'mix dogma %s --format=flycheck', +\ 'command': 'mix help dogma && mix dogma %s --format=flycheck', \ 'lint_file': 1, \ 'callback': 'ale_linters#elixir#dogma#Handle', \}) diff --git a/ale_linters/erlang/erlc.vim b/ale_linters/erlang/erlc.vim index 559dc67..bddb175 100644 --- a/ale_linters/erlang/erlc.vim +++ b/ale_linters/erlang/erlc.vim @@ -17,7 +17,7 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort " error.erl:4: variable 'B' is unbound " error.erl:3: Warning: function main/0 is unused " error.erl:4: Warning: variable 'A' is unused - let l:pattern = '\v^([^:]+):(\d+): (Warning: )?(.+)$' + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+): (Warning: )?(.+)$' " parse_transforms are a special case. The error message does not indicate a location: " error.erl: undefined parse transform 'some_parse_transform' diff --git a/ale_linters/eruby/erb.vim b/ale_linters/eruby/erb.vim new file mode 100644 index 0000000..61d9703 --- /dev/null +++ b/ale_linters/eruby/erb.vim @@ -0,0 +1,25 @@ +" Author: Matthias Guenther - https://wikimatze.de, Eddie Lebow https://github.com/elebow +" Description: ERB from the Ruby standard library, for eruby/erb files + +function! ale_linters#eruby#erb#GetCommand(buffer) abort + let l:rails_root = ale#ruby#FindRailsRoot(a:buffer) + + if empty(l:rails_root) + return 'erb -P -T - -x %t | ruby -c' + endif + + " Rails-flavored eRuby does not comply with the standard as understood by + " ERB, so we'll have to do some substitution. This does not reduce the + " effectiveness of the linter—the translated code is still evaluated. + return 'ruby -r erb -e ' . ale#Escape('puts ERB.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c' +endfunction + +call ale#linter#Define('eruby', { +\ 'name': 'erb', +\ 'aliases': ['erubylint'], +\ 'executable': 'erb', +\ 'output_stream': 'stderr', +\ 'command_callback': 'ale_linters#eruby#erb#GetCommand', +\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors', +\}) + diff --git a/ale_linters/eruby/erubi.vim b/ale_linters/eruby/erubi.vim new file mode 100644 index 0000000..6f2d3ac --- /dev/null +++ b/ale_linters/eruby/erubi.vim @@ -0,0 +1,35 @@ +" Author: Eddie Lebow https://github.com/elebow +" Description: eruby checker using `erubi` + +function! ale_linters#eruby#erubi#CheckErubi(buffer) abort + return 'ruby -r erubi/capture_end -e ' . ale#Escape('""') +endfunction + +function! ale_linters#eruby#erubi#GetCommand(buffer, check_erubi_output) abort + let l:rails_root = ale#ruby#FindRailsRoot(a:buffer) + + if (!empty(a:check_erubi_output)) + " The empty command in CheckErubi returns nothing if erubi runs and + " emits an error if erubi is not present + return '' + endif + + if empty(l:rails_root) + return 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read).src') . '< %t | ruby -c' + endif + + " Rails-flavored eRuby does not comply with the standard as understood by + " Erubi, so we'll have to do some substitution. This does not reduce the + " effectiveness of the linter---the translated code is still evaluated. + return 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c' +endfunction + +call ale#linter#Define('eruby', { +\ 'name': 'erubi', +\ 'executable': 'ruby', +\ 'command_chain': [ +\ {'callback': 'ale_linters#eruby#erubi#CheckErubi'}, +\ {'callback': 'ale_linters#eruby#erubi#GetCommand', 'output_stream': 'stderr'}, +\ ], +\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors', +\}) diff --git a/ale_linters/eruby/erubis.vim b/ale_linters/eruby/erubis.vim index be9332d..1ebd4a0 100644 --- a/ale_linters/eruby/erubis.vim +++ b/ale_linters/eruby/erubis.vim @@ -1,11 +1,23 @@ -" Author: Jake Zimmerman +" Author: Jake Zimmerman , Eddie Lebow https://github.com/elebow " Description: eruby checker using `erubis`, instead of `erb` +function! ale_linters#eruby#erubis#GetCommand(buffer) abort + let l:rails_root = ale#ruby#FindRailsRoot(a:buffer) + + if empty(l:rails_root) + return 'erubis -x %t | ruby -c' + endif + + " Rails-flavored eRuby does not comply with the standard as understood by + " Erubis, so we'll have to do some substitution. This does not reduce the + " effectiveness of the linter - the translated code is still evaluated. + return 'ruby -r erubis -e ' . ale#Escape('puts Erubis::Eruby.new($stdin.read.gsub(%{<%=},%{<%})).src') . '< %t | ruby -c' +endfunction + call ale#linter#Define('eruby', { \ 'name': 'erubis', \ 'executable': 'erubis', \ 'output_stream': 'stderr', -\ 'command': 'erubis -x %t | ruby -c', +\ 'command_callback': 'ale_linters#eruby#erubis#GetCommand', \ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors', \}) - diff --git a/ale_linters/eruby/erubylint.vim b/ale_linters/eruby/erubylint.vim deleted file mode 100644 index 2ff03c3..0000000 --- a/ale_linters/eruby/erubylint.vim +++ /dev/null @@ -1,11 +0,0 @@ -" Author: Matthias Guenther - https://wikimatze.de -" Description: erb-lint for eruby/erb files - -call ale#linter#Define('eruby', { -\ 'name': 'erubylint', -\ 'executable': 'erb', -\ 'output_stream': 'stderr', -\ 'command': 'erb -P -x %t | ruby -c', -\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors', -\}) - diff --git a/ale_linters/fish/fish.vim b/ale_linters/fish/fish.vim new file mode 100644 index 0000000..19158cb --- /dev/null +++ b/ale_linters/fish/fish.vim @@ -0,0 +1,36 @@ +" Author: Niraj Thapaliya - https://github.com/nthapaliya +" Description: Lints fish files using fish -n + +function! ale_linters#fish#fish#Handle(buffer, lines) abort + " Matches patterns such as: + " + " home/.config/fish/functions/foo.fish (line 1): Missing end to balance this function definition + " function foo + " ^ + " fish: Error while reading file .config/fish/functions/foo.fish + let l:pattern = '^.* (line \(\d\+\)): \(.*\)$' + let l:output = [] + + let l:i = 0 + while l:i < len(a:lines) + let l:match = matchlist(a:lines[l:i], l:pattern) + if len(l:match) && len(l:match[2]) + call add(l:output, { + \ 'col': len(a:lines[l:i + 2]), + \ 'lnum': str2nr(l:match[1]), + \ 'text': l:match[2], + \}) + endif + let l:i += 1 + endwhile + + return l:output +endfunction + +call ale#linter#Define('fish', { +\ 'name': 'fish', +\ 'output_stream': 'stderr', +\ 'executable': 'fish', +\ 'command': 'fish -n %t', +\ 'callback': 'ale_linters#fish#fish#Handle', +\}) diff --git a/ale_linters/fountain/proselint.vim b/ale_linters/fountain/proselint.vim new file mode 100644 index 0000000..353a2e5 --- /dev/null +++ b/ale_linters/fountain/proselint.vim @@ -0,0 +1,9 @@ +" Author: Jansen Mitchell https://github.com/JansenMitchell +" Description: proselint for Fountain files + +call ale#linter#Define('fountain', { +\ 'name': 'proselint', +\ 'executable': 'proselint', +\ 'command': 'proselint %t', +\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\}) diff --git a/ale_linters/gitcommit/gitlint.vim b/ale_linters/gitcommit/gitlint.vim new file mode 100644 index 0000000..49aeda7 --- /dev/null +++ b/ale_linters/gitcommit/gitlint.vim @@ -0,0 +1,52 @@ +" Author: Nick Yamane +" Description: gitlint for git commit message files + +let g:ale_gitcommit_gitlint_executable = +\ get(g:, 'ale_gitcommit_gitlint_executable', 'gitlint') +let g:ale_gitcommit_gitlint_options = get(g:, 'ale_gitcommit_gitlint_options', '') +let g:ale_gitcommit_gitlint_use_global = get(g:, 'ale_gitcommit_gitlint_use_global', 0) + + +function! ale_linters#gitcommit#gitlint#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'gitcommit_gitlint', ['gitlint']) +endfunction + +function! ale_linters#gitcommit#gitlint#GetCommand(buffer) abort + let l:options = ale#Var(a:buffer, 'gitcommit_gitlint_options') + let l:executable = ale_linters#gitcommit#gitlint#GetExecutable(a:buffer) + return ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' lint' +endfunction + + +function! ale_linters#gitcommit#gitlint#Handle(buffer, lines) abort + " Matches patterns line the following: + let l:pattern = '\v^(\d+): (\w+) (.*)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:code = l:match[2] + + let l:item = { + \ 'lnum': l:match[1] + 0, + \ 'text': l:match[3], + \ 'code': l:code, + \ 'type': 'E', + \} + + call add(l:output, l:item) + endfor + + return l:output +endfunction + + +call ale#linter#Define('gitcommit', { +\ 'name': 'gitlint', +\ 'output_stream': 'stderr', +\ 'executable_callback': 'ale_linters#gitcommit#gitlint#GetExecutable', +\ 'command_callback': 'ale_linters#gitcommit#gitlint#GetCommand', +\ 'callback': 'ale_linters#gitcommit#gitlint#Handle', +\}) + diff --git a/ale_linters/glsl/glslls.vim b/ale_linters/glsl/glslls.vim new file mode 100644 index 0000000..67ea379 --- /dev/null +++ b/ale_linters/glsl/glslls.vim @@ -0,0 +1,38 @@ +" Author: Sven-Hendrik Haase +" Description: A language server for glsl + +call ale#Set('glsl_glslls_executable', 'glslls') +call ale#Set('glsl_glslls_logfile', '') + +function! ale_linters#glsl#glslls#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'glsl_glslls_executable') +endfunction + +function! ale_linters#glsl#glslls#GetCommand(buffer) abort + let l:executable = ale_linters#glsl#glslls#GetExecutable(a:buffer) + let l:logfile = ale#Var(a:buffer, 'glsl_glslls_logfile') + let l:logfile_args = '' + if l:logfile isnot# '' + let l:logfile_args = ' --verbose -l ' . l:logfile + endif + return ale#Escape(l:executable) . l:logfile_args . ' --stdin' +endfunction + +function! ale_linters#glsl#glslls#GetLanguage(buffer) abort + return 'glsl' +endfunction + +function! ale_linters#glsl#glslls#GetProjectRoot(buffer) abort + let l:project_root = ale#c#FindProjectRoot(a:buffer) + + return !empty(l:project_root) ? fnamemodify(l:project_root, ':h:h') : '' +endfunction + +call ale#linter#Define('glsl', { +\ 'name': 'glslls', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale_linters#glsl#glslls#GetExecutable', +\ 'command_callback': 'ale_linters#glsl#glslls#GetCommand', +\ 'language_callback': 'ale_linters#glsl#glslls#GetLanguage', +\ 'project_root_callback': 'ale_linters#glsl#glslls#GetProjectRoot', +\}) diff --git a/ale_linters/go/gobuild.vim b/ale_linters/go/gobuild.vim index 143c2fd..068877a 100644 --- a/ale_linters/go/gobuild.vim +++ b/ale_linters/go/gobuild.vim @@ -1,8 +1,14 @@ -" Author: Joshua Rubin , Ben Reedy +" Author: Joshua Rubin , Ben Reedy , +" Jeff Willette " Description: go build for Go files - " inspired by work from dzhou121 +call ale#Set('go_gobuild_options', '') + +function! ale_linters#go#gobuild#ResetEnv() abort + unlet! s:go_env +endfunction + function! ale_linters#go#gobuild#GoEnv(buffer) abort if exists('s:go_env') return '' @@ -12,6 +18,8 @@ function! ale_linters#go#gobuild#GoEnv(buffer) abort endfunction function! ale_linters#go#gobuild#GetCommand(buffer, goenv_output) abort + let l:options = ale#Var(a:buffer, 'go_gobuild_options') + if !exists('s:go_env') let s:go_env = { \ 'GOPATH': a:goenv_output[0], @@ -19,10 +27,16 @@ function! ale_linters#go#gobuild#GetCommand(buffer, goenv_output) abort \} endif + let l:gopath_env_command = has('win32') + \ ? 'set GOPATH=' . ale#Escape(s:go_env.GOPATH) . ' && ' + \ : 'GOPATH=' . ale#Escape(s:go_env.GOPATH) . ' ' + " Run go test in local directory with relative path - return 'GOPATH=' . s:go_env.GOPATH - \ . ' cd ' . fnamemodify(bufname(a:buffer), ':.:h') - \ . ' && go test -c -o /dev/null ./' + return l:gopath_env_command + \ . ale#path#BufferCdString(a:buffer) + \ . 'go test' + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' -c -o /dev/null ./' endfunction function! ale_linters#go#gobuild#GetMatches(lines) abort @@ -39,15 +53,12 @@ function! ale_linters#go#gobuild#GetMatches(lines) abort endfunction function! ale_linters#go#gobuild#Handler(buffer, lines) abort + let l:dir = expand('#' . a:buffer . ':p:h') let l:output = [] for l:match in ale_linters#go#gobuild#GetMatches(a:lines) - " Omit errors from imported go packages - if !ale#path#IsBufferPath(a:buffer, l:match[1]) - continue - endif - call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, \ 'text': l:match[4], diff --git a/ale_linters/go/golint.vim b/ale_linters/go/golint.vim index cc807fe..d580fda 100644 --- a/ale_linters/go/golint.vim +++ b/ale_linters/go/golint.vim @@ -3,6 +3,7 @@ call ale#linter#Define('go', { \ 'name': 'golint', +\ 'output_stream': 'both', \ 'executable': 'golint', \ 'command': 'golint %t', \ 'callback': 'ale#handlers#unix#HandleAsWarning', diff --git a/ale_linters/go/gometalinter.vim b/ale_linters/go/gometalinter.vim index f1abfc8..375a8b0 100644 --- a/ale_linters/go/gometalinter.vim +++ b/ale_linters/go/gometalinter.vim @@ -1,8 +1,9 @@ -" Author: Ben Reedy +" Author: Ben Reedy , Jeff Willette " Description: Adds support for the gometalinter suite for Go files call ale#Set('go_gometalinter_options', '') call ale#Set('go_gometalinter_executable', 'gometalinter') +call ale#Set('go_gometalinter_lint_package', 0) function! ale_linters#go#gometalinter#GetExecutable(buffer) abort return ale#Var(a:buffer, 'go_gometalinter_executable') @@ -10,13 +11,22 @@ endfunction function! ale_linters#go#gometalinter#GetCommand(buffer) abort let l:executable = ale_linters#go#gometalinter#GetExecutable(a:buffer) - let l:filename = expand('#' . a:buffer) + let l:filename = expand('#' . a:buffer . ':t') let l:options = ale#Var(a:buffer, 'go_gometalinter_options') + let l:lint_package = ale#Var(a:buffer, 'go_gometalinter_lint_package') - return ale#Escape(l:executable) - \ . ' --include=' . ale#Escape('^' . ale#util#EscapePCRE(l:filename)) - \ . (!empty(l:options) ? ' ' . l:options : '') - \ . ' ' . ale#Escape(fnamemodify(l:filename, ':h')) + " BufferCdString is used so that we can be sure the paths output from gometalinter can + " be calculated to absolute paths in the Handler + if l:lint_package + return ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') . ' .' + endif + + return ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) + \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(l:filename)) + \ . (!empty(l:options) ? ' ' . l:options : '') . ' .' endfunction function! ale_linters#go#gometalinter#GetMatches(lines) abort @@ -26,10 +36,13 @@ function! ale_linters#go#gometalinter#GetMatches(lines) abort endfunction function! ale_linters#go#gometalinter#Handler(buffer, lines) abort + let l:dir = expand('#' . a:buffer . ':p:h') let l:output = [] for l:match in ale_linters#go#gometalinter#GetMatches(a:lines) + " l:match[1] will already be an absolute path, output from gometalinter call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, \ 'type': tolower(l:match[4]) is# 'warning' ? 'W' : 'E', diff --git a/ale_linters/go/gosimple.vim b/ale_linters/go/gosimple.vim index 9188e0d..8a4c01e 100644 --- a/ale_linters/go/gosimple.vim +++ b/ale_linters/go/gosimple.vim @@ -4,7 +4,8 @@ call ale#linter#Define('go', { \ 'name': 'gosimple', \ 'executable': 'gosimple', -\ 'command': 'gosimple %t', +\ 'command': 'gosimple %s', \ 'callback': 'ale#handlers#unix#HandleAsWarning', -\ 'output_stream': 'both' +\ 'output_stream': 'both', +\ 'lint_file': 1, \}) diff --git a/ale_linters/go/gotype.vim b/ale_linters/go/gotype.vim new file mode 100644 index 0000000..731f4c9 --- /dev/null +++ b/ale_linters/go/gotype.vim @@ -0,0 +1,23 @@ +" Author: Jelte Fennema +" Description: gotype for Go files + +call ale#linter#Define('go', { +\ 'name': 'gotype', +\ 'output_stream': 'stderr', +\ 'executable': 'gotype', +\ 'command_callback': 'ale_linters#go#gotype#GetCommand', +\ 'callback': 'ale#handlers#unix#HandleAsError', +\}) + +"\ 'command': +function! ale_linters#go#gotype#GetCommand(buffer) abort + let l:cur_file = expand('#' . a:buffer . ':p') + if l:cur_file =~# '_test\.go$' + return + endif + + let l:module_files = globpath(expand('#' . a:buffer . ':p:h'), '*.go', 0, 1) + let l:other_module_files = filter(l:module_files, 'v:val isnot# ' . ale#util#EscapeVim(l:cur_file) . ' && v:val !~# "_test\.go$"') + return 'gotype %t ' . join(map(l:other_module_files, 'ale#Escape(v:val)')) + +endfunction diff --git a/ale_linters/go/govet.vim b/ale_linters/go/govet.vim index f5bb47a..aae5969 100644 --- a/ale_linters/go/govet.vim +++ b/ale_linters/go/govet.vim @@ -1,10 +1,35 @@ " Author: neersighted " Description: go vet for Go files +" +" Author: John Eikenberry +" Description: updated to work with go1.10 + +function! ale_linters#go#govet#GetCommand(buffer) abort + return ale#path#BufferCdString(a:buffer) . ' go vet .' +endfunction + +function! ale_linters#go#govet#Handler(buffer, lines) abort + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$' + let l:output = [] + let l:dir = expand('#' . a:buffer . ':p:h') + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), + \ 'lnum': l:match[2] + 0, + \ 'col': l:match[3] + 0, + \ 'text': l:match[4], + \ 'type': 'E', + \}) + endfor + return l:output +endfunction call ale#linter#Define('go', { \ 'name': 'go vet', \ 'output_stream': 'stderr', \ 'executable': 'go', -\ 'command': 'go vet %t', -\ 'callback': 'ale#handlers#unix#HandleAsError', +\ 'command_callback': 'ale_linters#go#govet#GetCommand', +\ 'callback': 'ale_linters#go#govet#Handler', +\ 'lint_file': 1, \}) diff --git a/ale_linters/go/staticcheck.vim b/ale_linters/go/staticcheck.vim index cb4a5c7..ce9e6e3 100644 --- a/ale_linters/go/staticcheck.vim +++ b/ale_linters/go/staticcheck.vim @@ -1,10 +1,33 @@ " Author: Ben Reedy " Description: staticcheck for Go files +call ale#Set('go_staticcheck_options', '') +call ale#Set('go_staticcheck_lint_package', 0) + +function! ale_linters#go#staticcheck#GetCommand(buffer) abort + let l:filename = expand('#' . a:buffer . ':t') + let l:options = ale#Var(a:buffer, 'go_staticcheck_options') + let l:lint_package = ale#Var(a:buffer, 'go_staticcheck_lint_package') + + " BufferCdString is used so that we can be sure the paths output from + " staticcheck can be calculated to absolute paths in the Handler + if l:lint_package + return ale#path#BufferCdString(a:buffer) + \ . 'staticcheck' + \ . (!empty(l:options) ? ' ' . l:options : '') . ' .' + endif + + return ale#path#BufferCdString(a:buffer) + \ . 'staticcheck' + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' ' . ale#Escape(l:filename) +endfunction + call ale#linter#Define('go', { \ 'name': 'staticcheck', \ 'executable': 'staticcheck', -\ 'command': 'staticcheck %t', +\ 'command_callback': 'ale_linters#go#staticcheck#GetCommand', \ 'callback': 'ale#handlers#unix#HandleAsWarning', -\ 'output_stream': 'both' +\ 'output_stream': 'both', +\ 'lint_file': 1, \}) diff --git a/ale_linters/graphql/eslint.vim b/ale_linters/graphql/eslint.vim new file mode 100644 index 0000000..dfcbf9d --- /dev/null +++ b/ale_linters/graphql/eslint.vim @@ -0,0 +1,9 @@ +" Author: Benjie Gillam +" Description: eslint for GraphQL files + +call ale#linter#Define('graphql', { +\ 'name': 'eslint', +\ 'executable_callback': 'ale#handlers#eslint#GetExecutable', +\ 'command_callback': 'ale#handlers#eslint#GetCommand', +\ 'callback': 'ale#handlers#eslint#Handle', +\}) diff --git a/ale_linters/haml/hamllint.vim b/ale_linters/haml/hamllint.vim index b1a6aa5..d663359 100644 --- a/ale_linters/haml/hamllint.vim +++ b/ale_linters/haml/hamllint.vim @@ -1,6 +1,31 @@ -" Author: Patrick Lewis - https://github.com/patricklewis +" Author: Patrick Lewis - https://github.com/patricklewis, thenoseman - https://github.com/thenoseman " Description: haml-lint for Haml files +function! ale_linters#haml#hamllint#GetCommand(buffer) abort + let l:prefix = '' + + let l:rubocop_config_file_path = ale#path#FindNearestFile(a:buffer, '.rubocop.yml') + let l:hamllint_config_file_path = ale#path#FindNearestFile(a:buffer, '.haml-lint.yml') + + " Set HAML_LINT_RUBOCOP_CONF variable as it is needed for haml-lint to + " pick up the rubocop config. + " + " See https://github.com/brigade/haml-lint/blob/master/lib/haml_lint/linter/rubocop.rb#L89 + " HamlLint::Linter::RuboCop#rubocop_flags + if !empty(l:rubocop_config_file_path) + if ale#Has('win32') + let l:prefix = 'set HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path) . ' &&' + else + let l:prefix = 'HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path) + endif + endif + + return (!empty(l:prefix) ? l:prefix . ' ' : '') + \ . 'haml-lint' + \ . (!empty(l:hamllint_config_file_path) ? ' --config ' . ale#Escape(l:hamllint_config_file_path) : '') + \ . ' %t' +endfunction + function! ale_linters#haml#hamllint#Handle(buffer, lines) abort " Matches patterns like the following: " :51 [W] RuboCop: Use the new Ruby 1.9 hash syntax. @@ -21,6 +46,6 @@ endfunction call ale#linter#Define('haml', { \ 'name': 'hamllint', \ 'executable': 'haml-lint', -\ 'command': 'haml-lint %t', +\ 'command_callback': 'ale_linters#haml#hamllint#GetCommand', \ 'callback': 'ale_linters#haml#hamllint#Handle' \}) diff --git a/ale_linters/handlebars/embertemplatelint.vim b/ale_linters/handlebars/embertemplatelint.vim index 963ab56..68ea715 100644 --- a/ale_linters/handlebars/embertemplatelint.vim +++ b/ale_linters/handlebars/embertemplatelint.vim @@ -22,15 +22,13 @@ function! ale_linters#handlebars#embertemplatelint#Handle(buffer, lines) abort for l:error in get(values(l:json), 0, []) if has_key(l:error, 'fatal') call add(l:output, { - \ 'bufnr': a:buffer, - \ 'lnum': 1, - \ 'col': 1, + \ 'lnum': get(l:error, 'line', 1), + \ 'col': get(l:error, 'column', 1), \ 'text': l:error.message, \ 'type': l:error.severity == 1 ? 'W' : 'E', \}) else call add(l:output, { - \ 'bufnr': a:buffer, \ 'lnum': l:error.line, \ 'col': l:error.column, \ 'text': l:error.rule . ': ' . l:error.message, diff --git a/ale_linters/haskell/ghc.vim b/ale_linters/haskell/ghc.vim index fdf22f9..daf91c8 100644 --- a/ale_linters/haskell/ghc.vim +++ b/ale_linters/haskell/ghc.vim @@ -1,10 +1,18 @@ " Author: w0rp " Description: ghc for Haskell files +call ale#Set('haskell_ghc_options', '-fno-code -v0') + +function! ale_linters#haskell#ghc#GetCommand(buffer) abort + return 'ghc ' + \ . ale#Var(a:buffer, 'haskell_ghc_options') + \ . ' %t' +endfunction + call ale#linter#Define('haskell', { \ 'name': 'ghc', \ 'output_stream': 'stderr', \ 'executable': 'ghc', -\ 'command': 'ghc -fno-code -v0 %t', +\ 'command_callback': 'ale_linters#haskell#ghc#GetCommand', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \}) diff --git a/ale_linters/help/alex.vim b/ale_linters/help/alex.vim new file mode 100644 index 0000000..21b23b4 --- /dev/null +++ b/ale_linters/help/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for help files + +call ale#linter#Define('help', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/help/write-good.vim b/ale_linters/help/write-good.vim new file mode 100644 index 0000000..11254cd --- /dev/null +++ b/ale_linters/help/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for vim Help files + +call ale#linter#Define('help', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/html/alex.vim b/ale_linters/html/alex.vim new file mode 100644 index 0000000..5a1f61e --- /dev/null +++ b/ale_linters/html/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for HTML files + +call ale#linter#Define('html', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/html/htmlhint.vim b/ale_linters/html/htmlhint.vim index e142d22..88a83f1 100644 --- a/ale_linters/html/htmlhint.vim +++ b/ale_linters/html/htmlhint.vim @@ -1,7 +1,7 @@ " Author: KabbAmine , deathmaz <00maz1987@gmail.com>, diartyz " Description: HTMLHint for checking html files -call ale#Set('html_htmlhint_options', '--format=unix') +call ale#Set('html_htmlhint_options', '') call ale#Set('html_htmlhint_executable', 'htmlhint') call ale#Set('html_htmlhint_use_global', 0) @@ -12,9 +12,22 @@ function! ale_linters#html#htmlhint#GetExecutable(buffer) abort endfunction function! ale_linters#html#htmlhint#GetCommand(buffer) abort - return ale_linters#html#htmlhint#GetExecutable(a:buffer) - \ . ' ' . ale#Var(a:buffer, 'html_htmlhint_options') - \ . ' %t' + let l:options = ale#Var(a:buffer, 'html_htmlhint_options') + let l:config = l:options !~# '--config' + \ ? ale#path#FindNearestFile(a:buffer, '.htmlhintrc') + \ : '' + + if !empty(l:config) + let l:options .= ' --config ' . ale#Escape(l:config) + endif + + if !empty(l:options) + let l:options = substitute(l:options, '--format=unix', '', '') + endif + + return ale#Escape(ale_linters#html#htmlhint#GetExecutable(a:buffer)) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' --format=unix %t' endfunction call ale#linter#Define('html', { diff --git a/ale_linters/html/tidy.vim b/ale_linters/html/tidy.vim index 4a55d62..34152c6 100644 --- a/ale_linters/html/tidy.vim +++ b/ale_linters/html/tidy.vim @@ -25,8 +25,16 @@ function! ale_linters#html#tidy#GetCommand(buffer) abort \ 'utf-8': '-utf8', \ }, &fileencoding, '-utf8') + " On macOS, old tidy (released on 31 Oct 2006) is installed. It does not + " consider HTML5 so we should avoid it. + let l:executable = ale#Var(a:buffer, 'html_tidy_executable') + if has('mac') && l:executable is# 'tidy' && exists('*exepath') + \ && exepath(l:executable) is# '/usr/bin/tidy' + return '' + endif + return printf('%s %s %s -', - \ ale#Var(a:buffer, 'html_tidy_executable'), + \ l:executable, \ ale#Var(a:buffer, 'html_tidy_options'), \ l:file_encoding \) diff --git a/ale_linters/html/write-good.vim b/ale_linters/html/write-good.vim new file mode 100644 index 0000000..9fae882 --- /dev/null +++ b/ale_linters/html/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for nroff files + +call ale#linter#Define('html', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/java/checkstyle.vim b/ale_linters/java/checkstyle.vim index d3d4884..8155170 100644 --- a/ale_linters/java/checkstyle.vim +++ b/ale_linters/java/checkstyle.vim @@ -2,27 +2,17 @@ " Description: checkstyle for Java files function! ale_linters#java#checkstyle#Handle(buffer, lines) abort - let l:patterns = [ - \ '\v\[(WARN|ERROR)\] .*:(\d+):(\d+): (.*)', - \ '\v\[(WARN|ERROR)\] .*:(\d+): (.*)', - \] + let l:pattern = '\v\[(WARN|ERROR)\] [a-zA-Z]?:?[^:]+:(\d+):(\d+)?:? (.*) \[(.+)\]$' let l:output = [] - for l:match in ale#util#GetMatches(a:lines, l:patterns) - let l:args = { + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'type': l:match[1] is? 'WARN' ? 'W' : 'E', \ 'lnum': l:match[2] + 0, - \ 'type': l:match[1] =~? 'WARN' ? 'W' : 'E' - \ } - - let l:col = l:match[3] + 0 - if l:col > 0 - let l:args['col'] = l:col - let l:args['text'] = l:match[4] - else - let l:args['text'] = l:match[3] - endif - - call add(l:output, l:args) + \ 'col': l:match[3] + 0, + \ 'text': l:match[4], + \ 'code': l:match[5], + \}) endfor return l:output diff --git a/ale_linters/java/javac.vim b/ale_linters/java/javac.vim index d83da18..73e8414 100644 --- a/ale_linters/java/javac.vim +++ b/ale_linters/java/javac.vim @@ -41,15 +41,42 @@ function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort " Find the src directory, for files in this project. let l:src_dir = ale#path#FindNearestDirectory(a:buffer, 'src/main/java') + let l:sp_dirs = [] if !empty(l:src_dir) - let l:sp_option = '-sourcepath ' . ale#Escape(l:src_dir) + call add(l:sp_dirs, l:src_dir) + + " Automatically include the jaxb directory too, if it's there. + let l:jaxb_dir = fnamemodify(l:src_dir, ':h:h') + \ . (has('win32') ? '\jaxb\' : '/jaxb/') + + if isdirectory(l:jaxb_dir) + call add(l:sp_dirs, l:jaxb_dir) + endif + + " Automatically include the test directory, but only for test code. + if expand('#' . a:buffer . ':p') =~? '\vsrc[/\\]test[/\\]java' + let l:test_dir = fnamemodify(l:src_dir, ':h:h:h') + \ . (has('win32') ? '\test\java\' : '/test/java/') + + if isdirectory(l:test_dir) + call add(l:sp_dirs, l:test_dir) + endif + endif + endif + + if !empty(l:sp_dirs) + let l:sp_option = '-sourcepath ' + \ . ale#Escape(join(l:sp_dirs, s:classpath_sep)) endif " Create .class files in a temporary directory, which we will delete later. let l:class_file_directory = ale#engine#CreateDirectory(a:buffer) - return 'javac -Xlint' + " Always run javac from the directory the file is in, so we can resolve + " relative paths correctly. + return ale#path#BufferCdString(a:buffer) + \ . 'javac -Xlint' \ . ' ' . l:cp_option \ . ' ' . l:sp_option \ . ' -d ' . ale#Escape(l:class_file_directory) @@ -63,14 +90,15 @@ function! ale_linters#java#javac#Handle(buffer, lines) abort " Main.java:13: warning: [deprecation] donaught() in Testclass has been deprecated " Main.java:16: error: ';' expected - let l:pattern = '\v^.*:(\d+): (.+):(.+)$' + let l:directory = expand('#' . a:buffer . ':p:h') + let l:pattern = '\v^(.*):(\d+): (.+):(.+)$' let l:col_pattern = '\v^(\s*\^)$' let l:symbol_pattern = '\v^ +symbol: *(class|method) +([^ ]+)' let l:output = [] for l:match in ale#util#GetMatches(a:lines, [l:pattern, l:col_pattern, l:symbol_pattern]) if empty(l:match[2]) && empty(l:match[3]) - let l:output[-1].col = len(l:match[1]) + let l:output[-1].col = len(l:match[1]) elseif empty(l:match[3]) " Add symbols to 'cannot find symbol' errors. if l:output[-1].text is# 'error: cannot find symbol' @@ -78,9 +106,10 @@ function! ale_linters#java#javac#Handle(buffer, lines) abort endif else call add(l:output, { - \ 'lnum': l:match[1] + 0, - \ 'text': l:match[2] . ':' . l:match[3], - \ 'type': l:match[2] is# 'error' ? 'E' : 'W', + \ 'filename': ale#path#GetAbsPath(l:directory, l:match[1]), + \ 'lnum': l:match[2] + 0, + \ 'text': l:match[3] . ':' . l:match[4], + \ 'type': l:match[3] is# 'error' ? 'E' : 'W', \}) endif endfor diff --git a/ale_linters/javascript/eslint.vim b/ale_linters/javascript/eslint.vim index 785b8bb..23e1694 100644 --- a/ale_linters/javascript/eslint.vim +++ b/ale_linters/javascript/eslint.vim @@ -3,6 +3,7 @@ call ale#linter#Define('javascript', { \ 'name': 'eslint', +\ 'output_stream': 'both', \ 'executable_callback': 'ale#handlers#eslint#GetExecutable', \ 'command_callback': 'ale#handlers#eslint#GetCommand', \ 'callback': 'ale#handlers#eslint#Handle', diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim old mode 100644 new mode 100755 index 0dd6453..643ea19 --- a/ale_linters/javascript/flow.vim +++ b/ale_linters/javascript/flow.vim @@ -1,21 +1,12 @@ " Author: Zach Perrault -- @zperrault +" Author: Florian Beeres " Description: FlowType checking for JavaScript files call ale#Set('javascript_flow_executable', 'flow') +call ale#Set('javascript_flow_use_home_config', 0) call ale#Set('javascript_flow_use_global', 0) function! ale_linters#javascript#flow#GetExecutable(buffer) abort - return ale#node#FindExecutable(a:buffer, 'javascript_flow', [ - \ 'node_modules/.bin/flow', - \]) -endfunction - -function! ale_linters#javascript#flow#VersionCheck(buffer) abort - return ale#Escape(ale_linters#javascript#flow#GetExecutable(a:buffer)) - \ . ' --version' -endfunction - -function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort let l:flow_config = ale#path#FindNearestFile(a:buffer, '.flowconfig') if empty(l:flow_config) @@ -23,18 +14,43 @@ function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort return '' endif - let l:use_respect_pragma = 1 + " Don't run Flow with a configuration file from the home directory by + " default, which can eat all of your RAM. + if fnamemodify(l:flow_config, ':h') is? $HOME + \&& !ale#Var(a:buffer, 'javascript_flow_use_home_config') + return '' + endif + + return ale#node#FindExecutable(a:buffer, 'javascript_flow', [ + \ 'node_modules/.bin/flow', + \]) +endfunction + +function! ale_linters#javascript#flow#VersionCheck(buffer) abort + let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer) + + if empty(l:executable) + return '' + endif + + return ale#Escape(l:executable) . ' --version' +endfunction + +function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort + let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer) + + if empty(l:executable) + return '' + endif + + let l:version = ale#semver#GetVersion(l:executable, a:version_lines) " If we can parse the version number, then only use --respect-pragma " if the version is >= 0.36.0, which added the argument. - for l:match in ale#util#GetMatches(a:version_lines, '\v\d+\.\d+\.\d+$') - let l:use_respect_pragma = ale#semver#GreaterOrEqual( - \ ale#semver#Parse(l:match[0]), - \ [0, 36, 0] - \) - endfor + let l:use_respect_pragma = empty(l:version) + \ || ale#semver#GTE(l:version, [0, 36]) - return ale#Escape(ale_linters#javascript#flow#GetExecutable(a:buffer)) + return ale#Escape(l:executable) \ . ' check-contents' \ . (l:use_respect_pragma ? ' --respect-pragma': '') \ . ' --json --from ale %s' @@ -56,6 +72,44 @@ function! s:GetJSONLines(lines) abort return a:lines[l:start_index :] endfunction +function! s:ExtraErrorMsg(current, new) abort + let l:newMsg = '' + + if a:current is# '' + " extra messages appear to already have a : + let l:newMsg = a:new + else + let l:newMsg = a:current . ' ' . a:new + endif + + return l:newMsg +endfunction + + +function! s:GetDetails(error) abort + let l:detail = '' + + for l:extra_error in a:error.extra + + if has_key(l:extra_error, 'message') + for l:extra_message in l:extra_error.message + let l:detail = s:ExtraErrorMsg(l:detail, l:extra_message.descr) + endfor + endif + + if has_key(l:extra_error, 'children') + for l:child in l:extra_error.children + for l:child_message in l:child.message + let l:detail = l:detail . ' ' . l:child_message.descr + endfor + endfor + endif + + endfor + + return l:detail +endfunction + function! ale_linters#javascript#flow#Handle(buffer, lines) abort let l:str = join(s:GetJSONLines(a:lines), '') @@ -94,12 +148,19 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort let l:text = l:text . ' See also: ' . l:error.operation.descr endif - call add(l:output, { + let l:errorToAdd = { \ 'lnum': l:line, \ 'col': l:col, \ 'text': l:text, - \ 'type': l:error.level is# 'error' ? 'E' : 'W', - \}) + \ 'type': has_key(l:error, 'level') && l:error.level is# 'error' ? 'E' : 'W', + \} + + if has_key(l:error, 'extra') + let l:errorToAdd.detail = s:GetDetails(l:error) + endif + + call add(l:output, l:errorToAdd) + endfor return l:output diff --git a/ale_linters/javascript/jscs.vim b/ale_linters/javascript/jscs.vim index b3f826c..bcf3ee3 100644 --- a/ale_linters/javascript/jscs.vim +++ b/ale_linters/javascript/jscs.vim @@ -35,19 +35,23 @@ function! ale_linters#javascript#jscs#Handle(buffer, lines) abort " " foobar.js: line 2, col 1, Expected indentation of 1 characters " - let l:pattern = '^.*:\s\+line \(\d\+\),\s\+col\s\+\(\d\+\),\s\+\(.*\)$' + let l:pattern = '\v^.*:\s+line (\d+),\s+col\s+(\d+),\s+(.*)$' let l:output = [] - let l:m = ale#util#GetMatches(a:lines, [l:pattern]) - - for l:match in l:m - let l:text = l:match[3] + for l:match in ale#util#GetMatches(a:lines, l:pattern) let l:obj = { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, \ 'text': l:match[3] \} + let l:code_match = matchlist(l:match[3], '\v([^ :]+): (.+)$') + + if !empty(l:code_match) + let l:obj.code = l:code_match[1] + let l:obj.text = l:code_match[2] + endif + call add(l:output, l:obj) endfor diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim new file mode 100755 index 0000000..108679d --- /dev/null +++ b/ale_linters/less/lessc.vim @@ -0,0 +1,56 @@ +" Author: zanona , w0rp +" Description: This file adds support for checking Less code with lessc. + +call ale#Set('less_lessc_executable', 'lessc') +call ale#Set('less_lessc_options', '') +call ale#Set('less_lessc_use_global', 0) + +function! ale_linters#less#lessc#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'less_lessc', [ + \ 'node_modules/.bin/lessc', + \]) +endfunction + +function! ale_linters#less#lessc#GetCommand(buffer) abort + let l:executable = ale_linters#less#lessc#GetExecutable(a:buffer) + let l:dir = expand('#' . a:buffer . ':p:h') + let l:options = ale#Var(a:buffer, 'less_lessc_options') + + return ale#Escape(l:executable) + \ . ' --no-color --lint' + \ . ' --include-path=' . ale#Escape(l:dir) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' -' +endfunction + +function! ale_linters#less#lessc#Handle(buffer, lines) abort + let l:dir = expand('#' . a:buffer . ':p:h') + " Matches patterns like the following: + let l:pattern = '^\(\w\+\): \(.\{-}\) in \(.\{-}\) on line \(\d\+\), column \(\d\+\):$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:item = { + \ 'lnum': l:match[4] + 0, + \ 'col': l:match[5] + 0, + \ 'text': l:match[2], + \ 'type': 'E', + \} + + if l:match[3] isnot# '-' + let l:item.filename = ale#path#GetAbsPath(l:dir, l:match[3]) + endif + + call add(l:output, l:item) + endfor + + return l:output +endfunction + +call ale#linter#Define('less', { +\ 'name': 'lessc', +\ 'executable_callback': 'ale_linters#less#lessc#GetExecutable', +\ 'command_callback': 'ale_linters#less#lessc#GetCommand', +\ 'callback': 'ale_linters#less#lessc#Handle', +\ 'output_stream': 'stderr', +\}) diff --git a/ale_linters/less/stylelint.vim b/ale_linters/less/stylelint.vim new file mode 100644 index 0000000..690c8c9 --- /dev/null +++ b/ale_linters/less/stylelint.vim @@ -0,0 +1,27 @@ +" Author: diartyz , w0rp + +call ale#Set('less_stylelint_executable', 'stylelint') +call ale#Set('less_stylelint_options', '') +call ale#Set('less_stylelint_use_global', 0) + +function! ale_linters#less#stylelint#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'less_stylelint', [ + \ 'node_modules/.bin/stylelint', + \]) +endfunction + +function! ale_linters#less#stylelint#GetCommand(buffer) abort + let l:executable = ale_linters#less#stylelint#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'less_stylelint_options') + + return ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' --stdin-filename %s' +endfunction + +call ale#linter#Define('less', { +\ 'name': 'stylelint', +\ 'executable_callback': 'ale_linters#less#stylelint#GetExecutable', +\ 'command_callback': 'ale_linters#less#stylelint#GetCommand', +\ 'callback': 'ale#handlers#css#HandleStyleLintFormat', +\}) diff --git a/ale_linters/lua/luac.vim b/ale_linters/lua/luac.vim new file mode 100644 index 0000000..4a6bb40 --- /dev/null +++ b/ale_linters/lua/luac.vim @@ -0,0 +1,40 @@ +" Author: Jon Xie https://github.com/xiejiangzhi +" Description: luac linter for lua files + +call ale#Set('lua_luac_executable', 'luac') + +function! ale_linters#lua#luac#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'lua_luac_executable') +endfunction + +function! ale_linters#lua#luac#GetCommand(buffer) abort + let l:executable = ale_linters#lua#luac#GetExecutable(a:buffer) + return ale#Escape(l:executable) . ' -p - ' +endfunction + +function! ale_linters#lua#luac#Handle(buffer, lines) abort + " Matches patterns line the following: + " + " luac: stdin:5: '=' expected near ')' + " luac: stdin:8: ')' expected (to close '(' at line 6) near '123' + let l:pattern = '\v^.*:(\d+): (.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'type': 'E', + \ 'text': l:match[2], + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('lua', { +\ 'name': 'luac', +\ 'executable_callback': 'ale_linters#lua#luac#GetExecutable', +\ 'command_callback': 'ale_linters#lua#luac#GetCommand', +\ 'output_stream': 'stderr', +\ 'callback': 'ale_linters#lua#luac#Handle', +\}) diff --git a/ale_linters/lua/luacheck.vim b/ale_linters/lua/luacheck.vim index 9f9ca4c..725153c 100644 --- a/ale_linters/lua/luacheck.vim +++ b/ale_linters/lua/luacheck.vim @@ -35,8 +35,9 @@ function! ale_linters#lua#luacheck#Handle(buffer, lines) abort call add(l:output, { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, - \ 'text': l:match[3] . l:match[4] . ': ' . l:match[5], \ 'type': l:match[3], + \ 'code': l:match[3] . l:match[4], + \ 'text': l:match[5], \}) endfor diff --git a/ale_linters/mail/alex.vim b/ale_linters/mail/alex.vim new file mode 100644 index 0000000..b0651cc --- /dev/null +++ b/ale_linters/mail/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for HTML files + +call ale#linter#Define('mail', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/mail/proselint.vim b/ale_linters/mail/proselint.vim new file mode 100644 index 0000000..82c8d1f --- /dev/null +++ b/ale_linters/mail/proselint.vim @@ -0,0 +1,9 @@ +" Author: Daniel M. Capella https://github.com/polyzen +" Description: proselint for mail files + +call ale#linter#Define('mail', { +\ 'name': 'proselint', +\ 'executable': 'proselint', +\ 'command': 'proselint %t', +\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\}) diff --git a/ale_linters/mail/vale.vim b/ale_linters/mail/vale.vim new file mode 100644 index 0000000..e6dfd2e --- /dev/null +++ b/ale_linters/mail/vale.vim @@ -0,0 +1,9 @@ +" Author: chew-z https://github.com/chew-z +" Description: vale for Markdown files + +call ale#linter#Define('mail', { +\ 'name': 'vale', +\ 'executable': 'vale', +\ 'command': 'vale --output=JSON %t', +\ 'callback': 'ale#handlers#vale#Handle', +\}) diff --git a/ale_linters/make/checkmake.vim b/ale_linters/make/checkmake.vim new file mode 100644 index 0000000..63c35db --- /dev/null +++ b/ale_linters/make/checkmake.vim @@ -0,0 +1,24 @@ +" Author: aurieh - https://github.com/aurieh + +function! ale_linters#make#checkmake#Handle(buffer, lines) abort + let l:pattern = '\v^(\d+):(.+):(.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'bufnr': a:buffer, + \ 'lnum': l:match[1] + 0, + \ 'type': 'E', + \ 'code': l:match[2], + \ 'text': l:match[3], + \}) + endfor + return l:output +endfunction + +call ale#linter#Define('make', { +\ 'name': 'checkmake', +\ 'executable': 'checkmake', +\ 'command': 'checkmake %s --format="{{.LineNumber}}:{{.Rule}}:{{.Violation}}"', +\ 'callback': 'ale_linters#make#checkmake#Handle', +\}) diff --git a/ale_linters/markdown/alex.vim b/ale_linters/markdown/alex.vim new file mode 100644 index 0000000..2930614 --- /dev/null +++ b/ale_linters/markdown/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for markdown files + +call ale#linter#Define('markdown', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/markdown/mdl.vim b/ale_linters/markdown/mdl.vim index f239025..16b08cc 100644 --- a/ale_linters/markdown/mdl.vim +++ b/ale_linters/markdown/mdl.vim @@ -1,5 +1,20 @@ -" Author: Steve Dignam -" Description: Support for mdl, a markdown linter +" Author: Steve Dignam , Josh Leeb-du Toit +" Description: Support for mdl, a markdown linter. + +call ale#Set('markdown_mdl_executable', 'mdl') +call ale#Set('markdown_mdl_options', '') + +function! ale_linters#markdown#mdl#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'markdown_mdl_executable') +endfunction + +function! ale_linters#markdown#mdl#GetCommand(buffer) abort + let l:executable = ale_linters#markdown#mdl#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'markdown_mdl_options') + + return ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') +endfunction function! ale_linters#markdown#mdl#Handle(buffer, lines) abort " matches: '(stdin):173: MD004 Unordered list style' @@ -19,7 +34,7 @@ endfunction call ale#linter#Define('markdown', { \ 'name': 'mdl', -\ 'executable': 'mdl', -\ 'command': 'mdl', +\ 'executable_callback': 'ale_linters#markdown#mdl#GetExecutable', +\ 'command_callback': 'ale_linters#markdown#mdl#GetCommand', \ 'callback': 'ale_linters#markdown#mdl#Handle' \}) diff --git a/ale_linters/markdown/redpen.vim b/ale_linters/markdown/redpen.vim new file mode 100644 index 0000000..ff2cbaf --- /dev/null +++ b/ale_linters/markdown/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('markdown', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f markdown -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/markdown/vale.vim b/ale_linters/markdown/vale.vim index 43b3d34..838c4db 100644 --- a/ale_linters/markdown/vale.vim +++ b/ale_linters/markdown/vale.vim @@ -4,6 +4,6 @@ call ale#linter#Define('markdown', { \ 'name': 'vale', \ 'executable': 'vale', -\ 'command': 'vale --output=line %t', -\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\ 'command': 'vale --output=JSON %t', +\ 'callback': 'ale#handlers#vale#Handle', \}) diff --git a/ale_linters/markdown/write-good.vim b/ale_linters/markdown/write-good.vim new file mode 100644 index 0000000..21dbff1 --- /dev/null +++ b/ale_linters/markdown/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for Markdown files + +call ale#linter#Define('markdown', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/nim/nimcheck.vim b/ale_linters/nim/nimcheck.vim index cdd8c56..bff45f7 100644 --- a/ale_linters/nim/nimcheck.vim +++ b/ale_linters/nim/nimcheck.vim @@ -10,33 +10,40 @@ function! ale_linters#nim#nimcheck#Handle(buffer, lines) abort " Only show errors of the current buffer " NOTE: Checking filename only is OK because nim enforces unique " module names. - let l:temp_buffer_filename = fnamemodify(l:match[1], ':p:t') + if l:buffer_filename isnot# '' && l:temp_buffer_filename isnot# l:buffer_filename continue endif - let l:line = l:match[2] + 0 - let l:column = l:match[3] + 0 - let l:text = l:match[4] - let l:type = 'W' + let l:item = { + \ 'lnum': l:match[2] + 0, + \ 'col': l:match[3] + 0, + \ 'text': l:match[4], + \ 'type': 'W', + \} " Extract error type from message of type 'Error: Some error message' - let l:textmatch = matchlist(l:match[4], '^\(.\{-}\): .\+$') + let l:error_match = matchlist(l:item.text, '^\(.\{-}\): \(.\+\)$') - if len(l:textmatch) > 0 - let l:errortype = l:textmatch[1] - if l:errortype is# 'Error' - let l:type = 'E' + if !empty(l:error_match) + if l:error_match[1] is# 'Error' + let l:item.type = 'E' + let l:item.text = l:error_match[2] + elseif l:error_match[1] is# 'Warning' + \|| l:error_match[1] is# 'Hint' + let l:item.text = l:error_match[2] endif endif - call add(l:output, { - \ 'lnum': l:line, - \ 'col': l:column, - \ 'text': l:text, - \ 'type': l:type, - \}) + let l:code_match = matchlist(l:item.text, '\v^(.+) \[([^ \[]+)\]$') + + if !empty(l:code_match) + let l:item.text = l:code_match[1] + let l:item.code = l:code_match[2] + endif + + call add(l:output, l:item) endfor return l:output diff --git a/ale_linters/nroff/alex.vim b/ale_linters/nroff/alex.vim new file mode 100644 index 0000000..a10db2d --- /dev/null +++ b/ale_linters/nroff/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for nroff files + +call ale#linter#Define('nroff', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/nroff/write-good.vim b/ale_linters/nroff/write-good.vim new file mode 100644 index 0000000..d318fb2 --- /dev/null +++ b/ale_linters/nroff/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for nroff files + +call ale#linter#Define('nroff', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim new file mode 100644 index 0000000..c0255a6 --- /dev/null +++ b/ale_linters/ocaml/ols.vim @@ -0,0 +1,14 @@ +" Author: Michael Jungo +" Description: A language server for OCaml + +call ale#Set('ocaml_ols_executable', 'ocaml-language-server') +call ale#Set('ocaml_ols_use_global', 0) + +call ale#linter#Define('ocaml', { +\ 'name': 'ols', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale#handlers#ols#GetExecutable', +\ 'command_callback': 'ale#handlers#ols#GetCommand', +\ 'language_callback': 'ale#handlers#ols#GetLanguage', +\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot', +\}) diff --git a/ale_linters/perl/perl.vim b/ale_linters/perl/perl.vim index 3328806..1b9aa95 100644 --- a/ale_linters/perl/perl.vim +++ b/ale_linters/perl/perl.vim @@ -12,7 +12,7 @@ function! ale_linters#perl#perl#GetExecutable(buffer) abort endfunction function! ale_linters#perl#perl#GetCommand(buffer) abort - return ale_linters#perl#perl#GetExecutable(a:buffer) + return ale#Escape(ale_linters#perl#perl#GetExecutable(a:buffer)) \ . ' ' . ale#Var(a:buffer, 'perl_perl_options') \ . ' %t' endfunction @@ -27,12 +27,20 @@ function! ale_linters#perl#perl#Handle(buffer, lines) abort let l:output = [] let l:basename = expand('#' . a:buffer . ':t') + let l:type = 'E' + if a:lines[-1] =~# 'syntax OK' + let l:type = 'W' + endif + + let l:seen = {} + for l:match in ale#util#GetMatches(a:lines, l:pattern) let l:line = l:match[3] + let l:file = l:match[2] let l:text = l:match[1] - let l:type = 'E' - if ale#path#IsBufferPath(a:buffer, l:match[2]) + if ale#path#IsBufferPath(a:buffer, l:file) + \ && !has_key(l:seen,l:line) \ && ( \ l:text isnot# 'BEGIN failed--compilation aborted' \ || empty(l:output) @@ -43,6 +51,8 @@ function! ale_linters#perl#perl#Handle(buffer, lines) abort \ 'text': l:text, \ 'type': l:type, \}) + + let l:seen[l:line] = 1 endif endfor diff --git a/ale_linters/perl/perlcritic.vim b/ale_linters/perl/perlcritic.vim index df2f8b2..24f7eb8 100644 --- a/ale_linters/perl/perlcritic.vim +++ b/ale_linters/perl/perlcritic.vim @@ -61,6 +61,7 @@ function! ale_linters#perl#perlcritic#Handle(buffer, lines) abort \ 'lnum': l:match[1], \ 'col': l:match[2], \ 'text': l:match[3], + \ 'type': 'W' \}) endfor diff --git a/ale_linters/php/phan.vim b/ale_linters/php/phan.vim new file mode 100644 index 0000000..f3b3d48 --- /dev/null +++ b/ale_linters/php/phan.vim @@ -0,0 +1,36 @@ +" Author: diegoholiveira +" Description: static analyzer for PHP + +" Define the minimum severity +let g:ale_php_phan_minimum_severity = get(g:, 'ale_php_phan_minimum_severity', 0) + +function! ale_linters#php#phan#GetCommand(buffer) abort + return 'phan -y ' + \ . ale#Var(a:buffer, 'php_phan_minimum_severity') + \ . ' %s' +endfunction + +function! ale_linters#php#phan#Handle(buffer, lines) abort + " Matches against lines like the following: + " + " /path/to/some-filename.php:18 ERRORTYPE message + let l:pattern = '^.*:\(\d\+\)\s\(\w\+\)\s\(.\+\)$' + 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[3], + \ 'type': 'W', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('php', { +\ 'name': 'phan', +\ 'executable': 'phan', +\ 'command_callback': 'ale_linters#php#phan#GetCommand', +\ 'callback': 'ale_linters#php#phan#Handle', +\}) diff --git a/ale_linters/php/php.vim b/ale_linters/php/php.vim index 7158c95..6470383 100644 --- a/ale_linters/php/php.vim +++ b/ale_linters/php/php.vim @@ -4,12 +4,14 @@ function! ale_linters#php#php#Handle(buffer, lines) abort " Matches patterns like the following: " - " Parse error: syntax error, unexpected ';', expecting ']' in - on line 15 - let l:pattern = '\v^%(Fatal|Parse) error:\s+(.+unexpected ''(.+)%(expecting.+)@= - Parse error: syntax error, unexpected ';', expecting ']' in Standard input code on line 15 + let l:pattern = '\v^%(Fatal|Parse) error:\s+(.+unexpected ''(.+)%(expecting.+)@ +" Author: medains , David Sierra " Description: phpmd for PHP files +let g:ale_php_phpmd_executable = get(g:, 'ale_php_phpmd_executable', 'phpmd') + " Set to change the ruleset let g:ale_php_phpmd_ruleset = get(g:, 'ale_php_phpmd_ruleset', 'cleancode,codesize,controversial,design,naming,unusedcode') +function! ale_linters#php#phpmd#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'php_phpmd_executable') +endfunction + function! ale_linters#php#phpmd#GetCommand(buffer) abort - return 'phpmd %s text ' + let l:executable = ale_linters#php#phpmd#GetExecutable(a:buffer) + + return ale#Escape(l:executable) + \ . ' %s text ' \ . ale#Var(a:buffer, 'php_phpmd_ruleset') \ . ' --ignore-violations-on-exit %t' endfunction @@ -30,7 +39,7 @@ endfunction call ale#linter#Define('php', { \ 'name': 'phpmd', -\ 'executable': 'phpmd', +\ 'executable_callback': 'ale_linters#php#phpmd#GetExecutable', \ 'command_callback': 'ale_linters#php#phpmd#GetCommand', \ 'callback': 'ale_linters#php#phpmd#Handle', \}) diff --git a/ale_linters/po/alex.vim b/ale_linters/po/alex.vim new file mode 100644 index 0000000..411d835 --- /dev/null +++ b/ale_linters/po/alex.vim @@ -0,0 +1,11 @@ +" Author: Cian Butler https://github.com/butlerx +" Description: alex for PO files + +call ale#linter#Define('po', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/po/msgfmt.vim b/ale_linters/po/msgfmt.vim new file mode 100644 index 0000000..60c25d3 --- /dev/null +++ b/ale_linters/po/msgfmt.vim @@ -0,0 +1,10 @@ +" Author: Cian Butler https://github.com/butlerx +" Description: msgfmt for PO files + +call ale#linter#Define('po', { +\ 'name': 'msgfmt', +\ 'executable': 'msgfmt', +\ 'output_stream': 'stderr', +\ 'command': 'msgfmt --statistics %t', +\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\}) diff --git a/ale_linters/po/proselint.vim b/ale_linters/po/proselint.vim new file mode 100644 index 0000000..ce13250 --- /dev/null +++ b/ale_linters/po/proselint.vim @@ -0,0 +1,9 @@ +" Author: Cian Butler https://github.com/butlerx +" Description: proselint for PO files + +call ale#linter#Define('po', { +\ 'name': 'proselint', +\ 'executable': 'proselint', +\ 'command': 'proselint %t', +\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\}) diff --git a/ale_linters/po/write-good.vim b/ale_linters/po/write-good.vim new file mode 100644 index 0000000..5a01cb6 --- /dev/null +++ b/ale_linters/po/write-good.vim @@ -0,0 +1,9 @@ +" Author: Cian Butler https://github.com/butlerx +" Description: write-good for PO files + +call ale#linter#Define('po', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/pod/alex.vim b/ale_linters/pod/alex.vim new file mode 100644 index 0000000..5c09bef --- /dev/null +++ b/ale_linters/pod/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for pod files + +call ale#linter#Define('pod', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/pod/write-good.vim b/ale_linters/pod/write-good.vim new file mode 100644 index 0000000..14ed5c0 --- /dev/null +++ b/ale_linters/pod/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for Pod files + +call ale#linter#Define('pod', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/pony/ponyc.vim b/ale_linters/pony/ponyc.vim new file mode 100644 index 0000000..b332905 --- /dev/null +++ b/ale_linters/pony/ponyc.vim @@ -0,0 +1,21 @@ +" Description: ponyc linter for pony files + +call ale#Set('pony_ponyc_executable', 'ponyc') +call ale#Set('pony_ponyc_options', '--pass paint') + +function! ale_linters#pony#ponyc#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'pony_ponyc_executable') +endfunction + +function! ale_linters#pony#ponyc#GetCommand(buffer) abort + return ale#Escape(ale_linters#pony#ponyc#GetExecutable(a:buffer)) + \ . ' ' . ale#Var(a:buffer, 'pony_ponyc_options') +endfunction + +call ale#linter#Define('pony', { +\ 'name': 'ponyc', +\ 'output_stream': 'stderr', +\ 'executable_callback': 'ale_linters#pony#ponyc#GetExecutable', +\ 'command_callback': 'ale_linters#pony#ponyc#GetCommand', +\ 'callback': 'ale#handlers#pony#HandlePonycFormat', +\}) diff --git a/ale_linters/proto/protoc_gen_lint.vim b/ale_linters/proto/protoc_gen_lint.vim new file mode 100644 index 0000000..c8b5c33 --- /dev/null +++ b/ale_linters/proto/protoc_gen_lint.vim @@ -0,0 +1,27 @@ +" Author: Jeff Willette +" Description: run the protoc-gen-lint plugin for the protoc binary + +call ale#Set('proto_protoc_gen_lint_options', '') + +function! ale_linters#proto#protoc_gen_lint#GetCommand(buffer) abort + let l:dirname = expand('#' . a:buffer . ':p:h') + + let l:options = ['-I ' . ale#Escape(l:dirname)] + + if !empty(ale#Var(a:buffer, 'proto_protoc_gen_lint_options')) + let l:options += [ale#Var(a:buffer, 'proto_protoc_gen_lint_options')] + endif + + let l:options += ['--lint_out=. ' . '%s'] + + return 'protoc' . ' ' . join(l:options) +endfunction + +call ale#linter#Define('proto', { +\ 'name': 'protoc-gen-lint', +\ 'lint_file': 1, +\ 'output_stream': 'stderr', +\ 'executable': 'protoc', +\ 'command_callback': 'ale_linters#proto#protoc_gen_lint#GetCommand', +\ 'callback': 'ale#handlers#unix#HandleAsError', +\}) diff --git a/ale_linters/puppet/puppet.vim b/ale_linters/puppet/puppet.vim index 47e89d3..8beeb61 100644 --- a/ale_linters/puppet/puppet.vim +++ b/ale_linters/puppet/puppet.vim @@ -3,8 +3,9 @@ function! ale_linters#puppet#puppet#Handle(buffer, lines) abort " Matches patterns like the following: " Error: Could not parse for environment production: Syntax error at ':' at /root/puppetcode/modules/nginx/manifests/init.pp:43:12 + " Error: Could not parse for environment production: Syntax error at '='; expected '}' at /root/puppetcode/modules/pancakes/manifests/init.pp:5" - let l:pattern = '^Error: .*: \(.\+\) at .\+:\(\d\+\):\(\d\+\)$' + let l:pattern = '^Error: .*: \(.\+\) at .\+\.pp:\(\d\+\):\=\(\d*\)' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim index 8aa4c4d..e7bbcfb 100644 --- a/ale_linters/python/flake8.vim +++ b/ale_linters/python/flake8.vim @@ -10,10 +10,6 @@ let g:ale_python_flake8_options = \ get(g:, 'ale_python_flake8_options', s:default_options) let g:ale_python_flake8_use_global = get(g:, 'ale_python_flake8_use_global', 0) -" A map from Python executable paths to semver strings parsed for those -" executables, so we don't have to look up the version number constantly. -let s:version_cache = {} - function! s:UsingModule(buffer) abort return ale#Var(a:buffer, 'python_flake8_options') =~# ' *-m flake8' endfunction @@ -26,62 +22,35 @@ function! ale_linters#python#flake8#GetExecutable(buffer) abort return ale#Var(a:buffer, 'python_flake8_executable') endfunction -function! ale_linters#python#flake8#ClearVersionCache() abort - let s:version_cache = {} -endfunction - function! ale_linters#python#flake8#VersionCheck(buffer) abort let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) " If we have previously stored the version number in a cache, then " don't look it up again. - if has_key(s:version_cache, l:executable) + if ale#semver#HasVersion(l:executable) " Returning an empty string skips this command. return '' endif - let l:executable = ale#Escape(ale_linters#python#flake8#GetExecutable(a:buffer)) + let l:executable = ale#Escape(l:executable) let l:module_string = s:UsingModule(a:buffer) ? ' -m flake8' : '' return l:executable . l:module_string . ' --version' endfunction -" Get the flake8 version from the output, or the cache. -function! s:GetVersion(buffer, version_output) abort - let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) - let l:version = [] - - " Get the version from the cache. - if has_key(s:version_cache, l:executable) - return s:version_cache[l:executable] - endif - - if !empty(a:version_output) - " Parse the version string, and store it in the cache. - let l:version = ale#semver#Parse(a:version_output[0]) - let s:version_cache[l:executable] = l:version - endif - - return l:version -endfunction - -" flake8 versions 3 and up support the --stdin-display-name argument. -function! s:SupportsDisplayName(version) abort - return !empty(a:version) && ale#semver#GreaterOrEqual(a:version, [3, 0, 0]) -endfunction - function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort - let l:version = s:GetVersion(a:buffer, a:version_output) + let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) + let l:version = ale#semver#GetVersion(l:executable, a:version_output) " Only include the --stdin-display-name argument if we can parse the " flake8 version, and it is recent enough to support it. - let l:display_name_args = s:SupportsDisplayName(l:version) + let l:display_name_args = ale#semver#GTE(l:version, [3, 0, 0]) \ ? ' --stdin-display-name %s' \ : '' let l:options = ale#Var(a:buffer, 'python_flake8_options') - return ale#Escape(ale_linters#python#flake8#GetExecutable(a:buffer)) + return ale#Escape(l:executable) \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' --format=default' \ . l:display_name_args . ' -' @@ -122,18 +91,30 @@ function! ale_linters#python#flake8#Handle(buffer, lines) abort continue endif + if l:code is# 'W391' + \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines') + " Skip warnings for trailing blank lines if the option is off + continue + endif + let l:item = { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, - \ 'text': l:code . ': ' . l:match[4], + \ 'text': l:match[4], + \ 'code': l:code, \ 'type': 'W', \} - if l:code[:0] is# 'F' || l:code is# 'E999' - let l:item.type = 'E' + if l:code[:0] is# 'F' + if l:code isnot# 'F401' + let l:item.type = 'E' + endif elseif l:code[:0] is# 'E' let l:item.type = 'E' - let l:item.sub_type = 'style' + + if l:code isnot# 'E999' && l:code isnot# 'E112' + let l:item.sub_type = 'style' + endif elseif l:code[:0] is# 'W' let l:item.sub_type = 'style' endif diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim index 6884a9a..c1c9174 100644 --- a/ale_linters/python/mypy.vim +++ b/ale_linters/python/mypy.vim @@ -1,10 +1,10 @@ " Author: Keith Smiley , w0rp " Description: mypy support for optional python typechecking -let g:ale_python_mypy_executable = -\ get(g:, 'ale_python_mypy_executable', 'mypy') -let g:ale_python_mypy_options = get(g:, 'ale_python_mypy_options', '') -let g:ale_python_mypy_use_global = get(g:, 'ale_python_mypy_use_global', 0) +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', 0) function! ale_linters#python#mypy#GetExecutable(buffer) abort return ale#python#FindExecutable(a:buffer, 'python_mypy', ['mypy']) @@ -45,6 +45,12 @@ function! ale_linters#python#mypy#Handle(buffer, lines) abort let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) + " Skip invalid syntax errors if the option is on. + if l:match[5] is# 'invalid syntax' + \&& ale#Var(a:buffer, 'python_mypy_ignore_invalid_syntax') + continue + endif + call add(l:output, { \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), \ 'lnum': l:match[2] + 0, diff --git a/ale_linters/python/prospector.vim b/ale_linters/python/prospector.vim new file mode 100644 index 0000000..66af598 --- /dev/null +++ b/ale_linters/python/prospector.vim @@ -0,0 +1,82 @@ +" Author: chocoelho +" Description: prospector linter python files + +let g:ale_python_prospector_executable = +\ get(g:, 'ale_python_prospector_executable', 'prospector') + +let g:ale_python_prospector_options = +\ get(g:, 'ale_python_prospector_options', '') + +let g:ale_python_prospector_use_global = get(g:, 'ale_python_prospector_use_global', 0) + +function! ale_linters#python#prospector#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'python_prospector', ['prospector']) +endfunction + +function! ale_linters#python#prospector#GetCommand(buffer) abort + return ale#Escape(ale_linters#python#prospector#GetExecutable(a:buffer)) + \ . ' ' . ale#Var(a:buffer, 'python_prospector_options') + \ . ' --messages-only --absolute-paths --zero-exit --output-format json' + \ . ' %s' +endfunction + +function! ale_linters#python#prospector#Handle(buffer, lines) abort + let l:output = [] + + let l:prospector_error = json_decode(join(a:lines, '')) + + for l:error in l:prospector_error.messages + if (l:error.code is# 'W291' || l:error.code is# 'W293' || l:error.code is# 'trailing-whitespace') + \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace') + " Skip warnings for trailing whitespace if the option is off. + continue + endif + + if l:error.code is# 'W391' + \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines') + " Skip warnings for trailing blank lines if the option is off + continue + endif + + if l:error.source =~# '\v\[%(dodgy|mccabe|pep8|pep257|pyroma)\]$' + let l:sub_type = 'style' + else + let l:sub_type = '' + endif + + if l:error.source =~# '\v\[pylint\]$' + let l:type = l:error.code =~? '\m^[CRW]' ? 'W' : 'E' + elseif l:error.source =~# '\v\[%(frosted|pep8)\]$' + let l:type = l:error.code =~? '\m^W' ? 'W' : 'E' + elseif l:error.source =~# '\v\[%(dodgy|pyroma|vulture)\]$' + let l:type = 'W' + else + let l:type = 'E' + endif + + let l:item = { + \ 'lnum': l:error.location.line, + \ 'col': l:error.location.character + 1, + \ 'text': l:error.message, + \ 'code': printf('(%s) %s', l:error.source, l:error.code), + \ 'type': l:type, + \ 'sub_type': l:sub_type, + \} + + if l:sub_type is# '' + unlet l:item.sub_type + endif + + call add(l:output, l:item) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'prospector', +\ 'executable_callback': 'ale_linters#python#prospector#GetExecutable', +\ 'command_callback': 'ale_linters#python#prospector#GetCommand', +\ 'callback': 'ale_linters#python#prospector#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim index ad89599..19f05a5 100644 --- a/ale_linters/python/pycodestyle.vim +++ b/ale_linters/python/pycodestyle.vim @@ -17,18 +17,39 @@ function! ale_linters#python#pycodestyle#GetCommand(buffer) abort endfunction function! ale_linters#python#pycodestyle#Handle(buffer, lines) abort - let l:pattern = '\v^(\S*):(\d*):(\d*): ((([EW])\d+) .*)$' + let l:pattern = '\v^(\S*):(\d*):(\d*): ([EW]\d+) (.*)$' let l:output = [] " lines are formatted as follows: " file.py:21:26: W291 trailing whitespace for l:match in ale#util#GetMatches(a:lines, l:pattern) - call add(l:output, { + if(l:match[4] is# 'W291' || l:match[4] is# 'W293') + \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace') + " Skip warnings for trailing whitespace if the option is off. + continue + endif + + if l:match[4] is# 'W391' + \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines') + " Skip warnings for trailing blank lines if the option is off + continue + endif + + let l:item = { \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, - \ 'type': l:match[6], - \ 'text': l:match[4], - \}) + \ 'type': l:match[4][0], + \ 'sub_type': 'style', + \ 'text': l:match[5], + \ 'code': l:match[4], + \} + + " E999 and E112 are syntax errors. + if l:match[4] is# 'E999' || l:match[4] is# 'E112' + unlet l:item.sub_type + endif + + call add(l:output, l:item) endfor return l:output diff --git a/ale_linters/python/pyflakes.vim b/ale_linters/python/pyflakes.vim new file mode 100644 index 0000000..b4a0b5f --- /dev/null +++ b/ale_linters/python/pyflakes.vim @@ -0,0 +1,38 @@ +" Author: w0rp +" Description: pyflakes for python files + +call ale#Set('python_pyflakes_executable', 'pyflakes') +call ale#Set('python_pyflakes_use_global', 0) + +function! ale_linters#python#pyflakes#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'python_pyflakes', ['pyflakes']) +endfunction + +function! ale_linters#python#pyflakes#GetCommand(buffer) abort + let l:executable = ale_linters#python#pyflakes#GetExecutable(a:buffer) + + return ale#Escape(l:executable) . ' %t' +endfunction + +function! ale_linters#python#pyflakes#Handle(buffer, lines) abort + let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+)?:? (.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'text': l:match[3], + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pyflakes', +\ 'executable_callback': 'ale_linters#python#pyflakes#GetExecutable', +\ 'command_callback': 'ale_linters#python#pyflakes#GetCommand', +\ 'callback': 'ale_linters#python#pyflakes#Handle', +\ 'output_stream': 'both', +\}) diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim index befc51a..e3e6624 100644 --- a/ale_linters/python/pylint.vim +++ b/ale_linters/python/pylint.vim @@ -45,7 +45,8 @@ function! ale_linters#python#pylint#Handle(buffer, lines) abort call add(l:output, { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 1, - \ 'text': l:code . ': ' . l:match[5] . ' (' . l:match[4] . ')', + \ 'text': l:match[5], + \ 'code': l:match[4], \ 'type': l:code[:0] is# 'E' ? 'E' : 'W', \}) endfor diff --git a/ale_linters/python/pyls.vim b/ale_linters/python/pyls.vim new file mode 100644 index 0000000..9888853 --- /dev/null +++ b/ale_linters/python/pyls.vim @@ -0,0 +1,28 @@ +" Author: aurieh +" Description: A language server for Python + +call ale#Set('python_pyls_executable', 'pyls') +call ale#Set('python_pyls_use_global', 0) + +function! ale_linters#python#pyls#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'python_pyls', ['pyls']) +endfunction + +function! ale_linters#python#pyls#GetCommand(buffer) abort + let l:executable = ale_linters#python#pyls#GetExecutable(a:buffer) + + return ale#Escape(l:executable) +endfunction + +function! ale_linters#python#pyls#GetLanguage(buffer) abort + return 'python' +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pyls', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale_linters#python#pyls#GetExecutable', +\ 'command_callback': 'ale_linters#python#pyls#GetCommand', +\ 'language_callback': 'ale_linters#python#pyls#GetLanguage', +\ 'project_root_callback': 'ale#python#FindProjectRoot', +\}) diff --git a/ale_linters/r/lintr.vim b/ale_linters/r/lintr.vim index 86b591c..51e5c56 100644 --- a/ale_linters/r/lintr.vim +++ b/ale_linters/r/lintr.vim @@ -1,14 +1,29 @@ -" Author: Michel Lang , w0rp +" Author: Michel Lang , w0rp , +" Fenner Macrae " Description: This file adds support for checking R code with lintr. -let g:ale_r_lintr_options = -\ get(g:, 'ale_r_lintr_options', 'lintr::with_defaults()') +let g:ale_r_lintr_options = get(g:, 'ale_r_lintr_options', 'with_defaults()') " A reasonable alternative default: -" \ get(g:, 'ale_r_lintr_options', 'lintr::with_defaults(object_usage_linter = NULL)') +" get(g:, 'ale_r_lintr_options', 'with_defaults(object_usage_linter = NULL)') + + +let g:ale_r_lintr_lint_package = get(g:, 'ale_r_lintr_lint_package', 0) function! ale_linters#r#lintr#GetCommand(buffer) abort + if ale#Var(a:buffer, 'r_lintr_lint_package') + let l:lint_cmd = 'lint_package(cache = FALSE, linters = ' + \ . ale#Var(a:buffer, 'r_lintr_options') . ')' + else + let l:lint_cmd = 'lint(cache = FALSE, commandArgs(TRUE), ' + \ . ale#Var(a:buffer, 'r_lintr_options') . ')' + endif + + let l:cmd_string = 'suppressPackageStartupMessages(library(lintr));' + \ . l:lint_cmd + return ale#path#BufferCdString(a:buffer) - \ . 'Rscript -e ' . ale#Escape('lintr::lint(commandArgs(TRUE)[1], eval(parse(text = commandArgs(TRUE)[2])))') . ' %t' . ' ' . ale#Escape(ale#Var(a:buffer, 'r_lintr_options')) + \ . 'Rscript -e ' + \ . ale#Escape(l:cmd_string) . ' %t' endfunction call ale#linter#Define('r', { diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim new file mode 100644 index 0000000..b2cd5f7 --- /dev/null +++ b/ale_linters/reason/ols.vim @@ -0,0 +1,14 @@ +" Author: Michael Jungo +" Description: A language server for Reason + +call ale#Set('reason_ols_executable', 'ocaml-language-server') +call ale#Set('reason_ols_use_global', 0) + +call ale#linter#Define('reason', { +\ 'name': 'ols', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale#handlers#ols#GetExecutable', +\ 'command_callback': 'ale#handlers#ols#GetCommand', +\ 'language_callback': 'ale#handlers#ols#GetLanguage', +\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot', +\}) diff --git a/ale_linters/review/redpen.vim b/ale_linters/review/redpen.vim new file mode 100644 index 0000000..0006cab --- /dev/null +++ b/ale_linters/review/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('review', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f review -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/rst/alex.vim b/ale_linters/rst/alex.vim new file mode 100644 index 0000000..e637eae --- /dev/null +++ b/ale_linters/rst/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for rst files + +call ale#linter#Define('rst', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/rst/redpen.vim b/ale_linters/rst/redpen.vim new file mode 100644 index 0000000..ac966c5 --- /dev/null +++ b/ale_linters/rst/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('rst', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f rest -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/rst/rstcheck.vim b/ale_linters/rst/rstcheck.vim new file mode 100644 index 0000000..b660627 --- /dev/null +++ b/ale_linters/rst/rstcheck.vim @@ -0,0 +1,37 @@ +" Author: John Nduli https://github.com/jnduli +" Description: Rstcheck for reStructuredText files +" + +function! ale_linters#rst#rstcheck#Handle(buffer, lines) abort + " matches: 'bad_rst.rst:1: (SEVERE/4) Title overline & underline + " mismatch.' + let l:pattern = '\v^(.+):(\d*): \(([a-zA-Z]*)/\d*\) (.+)$' + let l:dir = expand('#' . a:buffer . ':p:h') + let l:output = [] + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), + \ 'lnum': l:match[2] + 0, + \ 'col': 0, + \ 'type': l:match[3] is# 'SEVERE' ? 'E' : 'W', + \ 'text': l:match[4], + \}) + endfor + + return l:output +endfunction + +function! ale_linters#rst#rstcheck#GetCommand(buffer) abort + return ale#path#BufferCdString(a:buffer) + \ . 'rstcheck' + \ . ' %t' +endfunction + + +call ale#linter#Define('rst', { +\ 'name': 'rstcheck', +\ 'executable': 'rstcheck', +\ 'command_callback': 'ale_linters#rst#rstcheck#GetCommand', +\ 'callback': 'ale_linters#rst#rstcheck#Handle', +\ 'output_stream': 'both', +\}) diff --git a/ale_linters/rst/vale.vim b/ale_linters/rst/vale.vim new file mode 100644 index 0000000..2e654dc --- /dev/null +++ b/ale_linters/rst/vale.vim @@ -0,0 +1,9 @@ +" Author: chew-z https://github.com/chew-z +" Description: vale for RST files + +call ale#linter#Define('rst', { +\ 'name': 'vale', +\ 'executable': 'vale', +\ 'command': 'vale --output=JSON %t', +\ 'callback': 'ale#handlers#vale#Handle', +\}) diff --git a/ale_linters/rst/write-good.vim b/ale_linters/rst/write-good.vim new file mode 100644 index 0000000..12137db --- /dev/null +++ b/ale_linters/rst/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for reStructuredText files + +call ale#linter#Define('rst', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/ruby/reek.vim b/ale_linters/ruby/reek.vim index 10bc9a8..a11b9cf 100644 --- a/ale_linters/ruby/reek.vim +++ b/ale_linters/ruby/reek.vim @@ -13,6 +13,7 @@ function! ale_linters#ruby#reek#Handle(buffer, lines) abort \ 'lnum': l:location, \ 'type': 'W', \ 'text': s:BuildText(a:buffer, l:error), + \ 'code': l:error.smell_type, \}) endfor endfor @@ -21,19 +22,19 @@ function! ale_linters#ruby#reek#Handle(buffer, lines) abort endfunction function! s:BuildText(buffer, error) abort - let l:text = a:error.smell_type . ':' + let l:parts = [] if ale#Var(a:buffer, 'ruby_reek_show_context') - let l:text .= ' ' . a:error.context + call add(l:parts, a:error.context) endif - let l:text .= ' ' . a:error.message + call add(l:parts, a:error.message) if ale#Var(a:buffer, 'ruby_reek_show_wiki_link') - let l:text .= ' [' . a:error.wiki_link . ']' + call add(l:parts, '[' . a:error.wiki_link . ']') endif - return l:text + return join(l:parts, ' ') endfunction call ale#linter#Define('ruby', { diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim index 2a4388f..777f457 100644 --- a/ale_linters/ruby/rubocop.vim +++ b/ale_linters/ruby/rubocop.vim @@ -34,7 +34,8 @@ function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort \ 'lnum': l:error['location']['line'] + 0, \ 'col': l:start_col, \ 'end_col': l:start_col + l:error['location']['length'] - 1, - \ 'text': printf('%s [%s]', l:error['message'], l:error['cop_name']), + \ 'code': l:error['cop_name'], + \ 'text': l:error['message'], \ 'type': ale_linters#ruby#rubocop#GetType(l:error['severity']), \}) endfor diff --git a/ale_linters/ruby/ruby.vim b/ale_linters/ruby/ruby.vim index a9f7b51..1aa8885 100644 --- a/ale_linters/ruby/ruby.vim +++ b/ale_linters/ruby/ruby.vim @@ -1,10 +1,22 @@ " Author: Brandon Roehl - https://github.com/BrandonRoehl " Description: Ruby MRI for Ruby files +call ale#Set('ruby_ruby_executable', 'ruby') + +function! ale_linters#ruby#ruby#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'ruby_ruby_executable') +endfunction + +function! ale_linters#ruby#ruby#GetCommand(buffer) abort + let l:executable = ale_linters#ruby#ruby#GetExecutable(a:buffer) + + return ale#Escape(l:executable) . ' -w -c -T1 %t' +endfunction + call ale#linter#Define('ruby', { \ 'name': 'ruby', -\ 'executable': 'ruby', +\ 'executable_callback': 'ale_linters#ruby#ruby#GetExecutable', +\ 'command_callback': 'ale_linters#ruby#ruby#GetCommand', \ 'output_stream': 'stderr', -\ 'command': 'ruby -w -c -T1 %t', \ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors', \}) diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim index f19061a..09f41bb 100644 --- a/ale_linters/rust/cargo.vim +++ b/ale_linters/rust/cargo.vim @@ -1,7 +1,11 @@ -" Author: Daniel Schemala +" Author: Daniel Schemala , +" Ivan Petkov " Description: rustc invoked by cargo for rust files -let g:ale_rust_cargo_use_check = get(g:, 'ale_rust_cargo_use_check', 0) +call ale#Set('rust_cargo_use_check', 1) +call ale#Set('rust_cargo_check_all_targets', 0) +call ale#Set('rust_cargo_default_feature_behavior', 'default') +call ale#Set('rust_cargo_include_features', '') function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') isnot# '' @@ -13,19 +17,52 @@ function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort endif endfunction -function! ale_linters#rust#cargo#GetCommand(buffer) abort - let l:command = ale#Var(a:buffer, 'rust_cargo_use_check') - \ ? 'check' - \ : 'build' +function! ale_linters#rust#cargo#VersionCheck(buffer) abort + return !ale#semver#HasVersion('cargo') + \ ? 'cargo --version' + \ : '' +endfunction - return 'cargo ' . l:command . ' --frozen --message-format=json -q' +function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort + let l:version = ale#semver#GetVersion('cargo', a:version_output) + + let l:use_check = ale#Var(a:buffer, 'rust_cargo_use_check') + \ && ale#semver#GTE(l:version, [0, 17, 0]) + let l:use_all_targets = l:use_check + \ && ale#Var(a:buffer, 'rust_cargo_check_all_targets') + \ && ale#semver#GTE(l:version, [0, 22, 0]) + + let l:include_features = ale#Var(a:buffer, 'rust_cargo_include_features') + if !empty(l:include_features) + let l:include_features = ' --features ' . ale#Escape(l:include_features) + endif + + let l:default_feature_behavior = ale#Var(a:buffer, 'rust_cargo_default_feature_behavior') + if l:default_feature_behavior is# 'all' + let l:include_features = '' + let l:default_feature = ' --all-features' + elseif l:default_feature_behavior is# 'none' + let l:default_feature = ' --no-default-features' + else + let l:default_feature = '' + endif + + return 'cargo ' + \ . (l:use_check ? 'check' : 'build') + \ . (l:use_all_targets ? ' --all-targets' : '') + \ . ' --frozen --message-format=json -q' + \ . l:default_feature + \ . l:include_features endfunction call ale#linter#Define('rust', { \ 'name': 'cargo', \ 'executable_callback': 'ale_linters#rust#cargo#GetCargoExecutable', -\ 'command_callback': 'ale_linters#rust#cargo#GetCommand', +\ 'command_chain': [ +\ {'callback': 'ale_linters#rust#cargo#VersionCheck'}, +\ {'callback': 'ale_linters#rust#cargo#GetCommand'}, +\ ], \ 'callback': 'ale#handlers#rust#HandleRustErrors', -\ 'output_stream': 'stdout', +\ 'output_stream': 'both', \ 'lint_file': 1, \}) diff --git a/ale_linters/rust/rls.vim b/ale_linters/rust/rls.vim index c49d268..832fe3e 100644 --- a/ale_linters/rust/rls.vim +++ b/ale_linters/rust/rls.vim @@ -2,6 +2,7 @@ " Description: A language server for Rust call ale#Set('rust_rls_executable', 'rls') +call ale#Set('rust_rls_toolchain', 'nightly') function! ale_linters#rust#rls#GetExecutable(buffer) abort return ale#Var(a:buffer, 'rust_rls_executable') @@ -9,8 +10,10 @@ endfunction function! ale_linters#rust#rls#GetCommand(buffer) abort let l:executable = ale_linters#rust#rls#GetExecutable(a:buffer) + let l:toolchain = ale#Var(a:buffer, 'rust_rls_toolchain') - return ale#Escape(l:executable) . ' +nightly' + return ale#Escape(l:executable) + \ . ' +' . ale#Escape(l:toolchain) endfunction function! ale_linters#rust#rls#GetLanguage(buffer) abort diff --git a/ale_linters/rust/rustc.vim b/ale_linters/rust/rustc.vim index e792faa..3cd401b 100644 --- a/ale_linters/rust/rustc.vim +++ b/ale_linters/rust/rustc.vim @@ -1,21 +1,27 @@ " Author: Daniel Schemala " Description: rustc for rust files -function! ale_linters#rust#rustc#RustcCommand(buffer_number) abort +call ale#Set('rust_rustc_options', '-Z no-trans') + +function! ale_linters#rust#rustc#RustcCommand(buffer) abort " Try to guess the library search path. If the project is managed by cargo, " it's usually /target/debug/deps/ or " /target/release/deps/ - let l:cargo_file = ale#path#FindNearestFile(a:buffer_number, 'Cargo.toml') + let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml') if l:cargo_file isnot# '' - let l:project_root = fnamemodify(l:cargo_file, ':h') - let l:dependencies = '-L ' . l:project_root . '/target/debug/deps -L ' . - \ l:project_root . '/target/release/deps' + let l:root = fnamemodify(l:cargo_file, ':h') + let l:dependencies = ' -L ' . ale#Escape(ale#path#GetAbsPath(l:root, 'target/debug/deps')) + \ . ' -L ' . ale#Escape(ale#path#GetAbsPath(l:root, 'target/release/deps')) else let l:dependencies = '' endif - return 'rustc --error-format=json -Z no-trans ' . l:dependencies . ' -' + let l:options = ale#Var(a:buffer, 'rust_rustc_options') + + return 'rustc --error-format=json' + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . l:dependencies . ' -' endfunction call ale#linter#Define('rust', { diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim index b47ba19..ac66892 100644 --- a/ale_linters/sh/shellcheck.vim +++ b/ale_linters/sh/shellcheck.vim @@ -12,47 +12,111 @@ let g:ale_sh_shellcheck_exclusions = let g:ale_sh_shellcheck_executable = \ get(g:, 'ale_sh_shellcheck_executable', 'shellcheck') +let g:ale_sh_shellcheck_use_global = +\ get(g:, 'ale_sh_shellcheck_use_global', 0) + let g:ale_sh_shellcheck_options = \ get(g:, 'ale_sh_shellcheck_options', '') function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort - return ale#Var(a:buffer, 'sh_shellcheck_executable') + return ale#node#FindExecutable(a:buffer, 'sh_shellcheck', [ + \ 'node_modules/.bin/shellcheck', + \]) endfunction function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort let l:shell_type = ale#handlers#sh#GetShellType(a:buffer) if !empty(l:shell_type) + " Use the dash dialect for /bin/ash, etc. + if l:shell_type is# 'ash' + return 'dash' + endif + return l:shell_type endif " If there's no hashbang, try using Vim's buffer variables. - if get(b:, 'is_bash') + if getbufvar(a:buffer, 'is_bash', 0) return 'bash' - elseif get(b:, 'is_sh') + elseif getbufvar(a:buffer, 'is_sh', 0) return 'sh' - elseif get(b:, 'is_kornshell') + elseif getbufvar(a:buffer, 'is_kornshell', 0) return 'ksh' endif return '' endfunction -function! ale_linters#sh#shellcheck#GetCommand(buffer) abort +function! ale_linters#sh#shellcheck#VersionCheck(buffer) abort + let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer) + + " Don't check the version again if we've already cached it. + return !ale#semver#HasVersion(l:executable) + \ ? ale#Escape(l:executable) . ' --version' + \ : '' +endfunction + +function! ale_linters#sh#shellcheck#GetCommand(buffer, version_output) abort + let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer) + let l:version = ale#semver#GetVersion(l:executable, a:version_output) + let l:options = ale#Var(a:buffer, 'sh_shellcheck_options') let l:exclude_option = ale#Var(a:buffer, 'sh_shellcheck_exclusions') let l:dialect = ale_linters#sh#shellcheck#GetDialectArgument(a:buffer) + let l:external_option = ale#semver#GTE(l:version, [0, 4, 0]) ? ' -x' : '' - return ale_linters#sh#shellcheck#GetExecutable(a:buffer) + return ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) \ . (!empty(l:dialect) ? ' -s ' . l:dialect : '') \ . (!empty(l:options) ? ' ' . l:options : '') \ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '') + \ . l:external_option \ . ' -f gcc -' endfunction +function! ale_linters#sh#shellcheck#Handle(buffer, lines) abort + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+) \[([^\]]+)\]$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + if l:match[4] is# 'error' + let l:type = 'E' + elseif l:match[4] is# 'note' + let l:type = 'I' + else + let l:type = 'W' + endif + + let l:item = { + \ 'lnum': str2nr(l:match[2]), + \ 'type': l:type, + \ 'text': l:match[5], + \ 'code': l:match[6], + \} + + if !empty(l:match[3]) + let l:item.col = str2nr(l:match[3]) + endif + + " If the filename is something like , or -, then + " this is an error for the file we checked. + if l:match[1] isnot# '-' && l:match[1][0] isnot# '<' + let l:item['filename'] = l:match[1] + endif + + call add(l:output, l:item) + endfor + + return l:output +endfunction + call ale#linter#Define('sh', { \ 'name': 'shellcheck', \ 'executable_callback': 'ale_linters#sh#shellcheck#GetExecutable', -\ 'command_callback': 'ale_linters#sh#shellcheck#GetCommand', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'command_chain': [ +\ {'callback': 'ale_linters#sh#shellcheck#VersionCheck'}, +\ {'callback': 'ale_linters#sh#shellcheck#GetCommand'}, +\ ], +\ 'callback': 'ale_linters#sh#shellcheck#Handle', \}) diff --git a/ale_linters/slim/slimlint.vim b/ale_linters/slim/slimlint.vim index bb62c73..00c6b26 100644 --- a/ale_linters/slim/slimlint.vim +++ b/ale_linters/slim/slimlint.vim @@ -28,11 +28,20 @@ function! ale_linters#slim#slimlint#Handle(buffer, lines) abort let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) - call add(l:output, { + let l:item = { \ 'lnum': l:match[1] + 0, \ 'type': l:match[2], \ 'text': l:match[3] - \}) + \} + + let l:code_match = matchlist(l:item.text, '\v^([^:]+): (.+)$') + + if !empty(l:code_match) + let l:item.code = l:code_match[1] + let l:item.text = l:code_match[2] + endif + + call add(l:output, l:item) endfor return l:output diff --git a/ale_linters/solidity/solhint.vim b/ale_linters/solidity/solhint.vim new file mode 100644 index 0000000..519fd49 --- /dev/null +++ b/ale_linters/solidity/solhint.vim @@ -0,0 +1,30 @@ +" Author: Franco Victorio - https://github.com/fvictorio +" Description: Report errors in Solidity code with solhint + +function! ale_linters#solidity#solhint#Handle(buffer, lines) abort + " Matches patterns like the following: + " /path/to/file/file.sol: line 1, col 10, Error - 'addOne' is defined but never used. (no-unused-vars) + + let l:pattern = '\v^[^:]+: line (\d+), col (\d+), (Error|Warning) - (.*) \((.*)\)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:isError = l:match[3] is? 'error' + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'text': l:match[4], + \ 'code': l:match[5], + \ 'type': l:isError ? 'E' : 'W', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('solidity', { +\ 'name': 'solhint', +\ 'executable': 'solhint', +\ 'command': 'solhint --formatter compact %t', +\ 'callback': 'ale_linters#solidity#solhint#Handle', +\}) diff --git a/ale_linters/swift/swiftlint.vim b/ale_linters/swift/swiftlint.vim index b7dcf93..697d246 100644 --- a/ale_linters/swift/swiftlint.vim +++ b/ale_linters/swift/swiftlint.vim @@ -1,9 +1,51 @@ " Author: David Mohundro " Description: swiftlint for swift files + +function! ale_linters#swift#swiftlint#Handle(buffer, lines) abort + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:item = { + \ 'lnum': str2nr(l:match[2]), + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \ 'text': l:match[5], + \} + + if l:match[4] is# 'error' + let l:item.type = 'E' + elseif l:match[4] is# 'note' + let l:item.type = 'I' + endif + + if !empty(l:match[3]) + let l:item.col = str2nr(l:match[3]) + endif + + " If the filename is something like , or -, then + " this is an error for the file we checked. + if l:match[1] isnot# '-' && l:match[1][0] isnot# '<' + let l:item['filename'] = l:match[1] + endif + + " Parse the code if it's there. + let l:code_match = matchlist(l:item.text, '\v^(.+) \(([^ (]+)\)$') + + if !empty(l:code_match) + let l:item.text = l:code_match[1] + let l:item.code = l:code_match[2] + endif + + call add(l:output, l:item) + endfor + + return l:output +endfunction + call ale#linter#Define('swift', { \ 'name': 'swiftlint', \ 'executable': 'swiftlint', \ 'command': 'swiftlint lint --use-stdin', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale_linters#swift#swiftlint#Handle', \}) diff --git a/ale_linters/terraform/tflint.vim b/ale_linters/terraform/tflint.vim new file mode 100644 index 0000000..93966ff --- /dev/null +++ b/ale_linters/terraform/tflint.vim @@ -0,0 +1,62 @@ +" Author: Nat Williams +" Description: tflint for Terraform files +" +" See: https://www.terraform.io/ +" https://github.com/wata727/tflint + +call ale#Set('terraform_tflint_options', '') +call ale#Set('terraform_tflint_executable', 'tflint') + +function! ale_linters#terraform#tflint#Handle(buffer, lines) abort + let l:output = [] + + for l:error in ale#util#FuzzyJSONDecode(a:lines, []) + if l:error.type is# 'ERROR' + let l:type = 'E' + elseif l:error.type is# 'NOTICE' + let l:type = 'I' + else + let l:type = 'W' + endif + + call add(l:output, { + \ 'lnum': l:error.line, + \ 'text': l:error.message, + \ 'type': l:type, + \ 'code': l:error.detector, + \}) + endfor + + return l:output +endfunction + +function! ale_linters#terraform#tflint#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'terraform_tflint_executable') +endfunction + +function! ale_linters#terraform#tflint#GetCommand(buffer) abort + let l:cmd = ale#Escape(ale#Var(a:buffer, 'terraform_tflint_executable')) + + let l:config_file = ale#path#FindNearestFile(a:buffer, '.tflint.hcl') + if !empty(l:config_file) + let l:cmd .= ' --config ' . ale#Escape(l:config_file) + endif + + let l:opts = ale#Var(a:buffer, 'terraform_tflint_options') + if !empty(l:opts) + let l:cmd .= ' ' . l:opts + endif + + let l:cmd .= ' -f json %t' + + return l:cmd +endfunction + +call ale#linter#Define('terraform', { +\ 'name': 'tflint', +\ 'executable_callback': 'ale_linters#terraform#tflint#GetExecutable', +\ 'command_callback': 'ale_linters#terraform#tflint#GetCommand', +\ 'callback': 'ale_linters#terraform#tflint#Handle', +\}) + +" vim:sw=4 diff --git a/ale_linters/tex/alex.vim b/ale_linters/tex/alex.vim new file mode 100644 index 0000000..78c530f --- /dev/null +++ b/ale_linters/tex/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for TeX files + +call ale#linter#Define('tex', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/tex/redpen.vim b/ale_linters/tex/redpen.vim new file mode 100644 index 0000000..952a600 --- /dev/null +++ b/ale_linters/tex/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('tex', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f latex -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/tex/vale.vim b/ale_linters/tex/vale.vim new file mode 100644 index 0000000..f64e72a --- /dev/null +++ b/ale_linters/tex/vale.vim @@ -0,0 +1,9 @@ +" Author: chew-z https://github.com/chew-z +" Description: vale for LaTeX files + +call ale#linter#Define('tex', { +\ 'name': 'vale', +\ 'executable': 'vale', +\ 'command': 'vale --output=JSON %t', +\ 'callback': 'ale#handlers#vale#Handle', +\}) diff --git a/ale_linters/tex/write-good.vim b/ale_linters/tex/write-good.vim new file mode 100644 index 0000000..dc59de2 --- /dev/null +++ b/ale_linters/tex/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for TeX files + +call ale#linter#Define('tex', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/texinfo/alex.vim b/ale_linters/texinfo/alex.vim new file mode 100644 index 0000000..4a88457 --- /dev/null +++ b/ale_linters/texinfo/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for texinfo files + +call ale#linter#Define('texinfo', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/texinfo/write-good.vim b/ale_linters/texinfo/write-good.vim new file mode 100644 index 0000000..8104c63 --- /dev/null +++ b/ale_linters/texinfo/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for Texinfo files + +call ale#linter#Define('texinfo', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/text/alex.vim b/ale_linters/text/alex.vim new file mode 100644 index 0000000..c696367 --- /dev/null +++ b/ale_linters/text/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for text files + +call ale#linter#Define('text', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/text/redpen.vim b/ale_linters/text/redpen.vim new file mode 100644 index 0000000..ec4433b --- /dev/null +++ b/ale_linters/text/redpen.vim @@ -0,0 +1,9 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +call ale#linter#Define('text', { +\ 'name': 'redpen', +\ 'executable': 'redpen', +\ 'command': 'redpen -f plain -r json %t', +\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput', +\}) diff --git a/ale_linters/text/vale.vim b/ale_linters/text/vale.vim index 60bd799..cf37c2f 100644 --- a/ale_linters/text/vale.vim +++ b/ale_linters/text/vale.vim @@ -4,6 +4,6 @@ call ale#linter#Define('text', { \ 'name': 'vale', \ 'executable': 'vale', -\ 'command': 'vale --output=line %t', -\ 'callback': 'ale#handlers#unix#HandleAsWarning', +\ 'command': 'vale --output=JSON %t', +\ 'callback': 'ale#handlers#vale#Handle', \}) diff --git a/ale_linters/text/write-good.vim b/ale_linters/text/write-good.vim new file mode 100644 index 0000000..ff76ce4 --- /dev/null +++ b/ale_linters/text/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for text files + +call ale#linter#Define('text', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/typescript/tslint.vim b/ale_linters/typescript/tslint.vim index c3852b8..f4b4816 100644 --- a/ale_linters/typescript/tslint.vim +++ b/ale_linters/typescript/tslint.vim @@ -24,17 +24,31 @@ function! ale_linters#typescript#tslint#Handle(buffer, lines) abort let l:output = [] for l:error in ale#util#FuzzyJSONDecode(a:lines, []) - call add(l:output, { - \ 'filename': ale#path#GetAbsPath(l:dir, l:error.name), + if get(l:error, 'ruleName', '') is# 'no-implicit-dependencies' + continue + endif + + let l:item = { \ 'type': (get(l:error, 'ruleSeverity', '') is# 'WARNING' ? 'W' : 'E'), - \ 'text': has_key(l:error, 'ruleName') - \ ? l:error.ruleName . ': ' . l:error.failure - \ : l:error.failure, + \ 'text': l:error.failure, \ 'lnum': l:error.startPosition.line + 1, \ 'col': l:error.startPosition.character + 1, \ 'end_lnum': l:error.endPosition.line + 1, \ 'end_col': l:error.endPosition.character + 1, - \}) + \} + + let l:filename = ale#path#GetAbsPath(l:dir, l:error.name) + + " Assume temporary files are this file. + if !ale#path#IsTempName(l:filename) + let l:item.filename = l:filename + endif + + if has_key(l:error, 'ruleName') + let l:item.code = l:error.ruleName + endif + + call add(l:output, l:item) endfor return l:output diff --git a/ale_linters/verilog/iverilog.vim b/ale_linters/verilog/iverilog.vim index 18769d5..c64a3be 100644 --- a/ale_linters/verilog/iverilog.vim +++ b/ale_linters/verilog/iverilog.vim @@ -1,6 +1,14 @@ " Author: Masahiro H https://github.com/mshr-h " Description: iverilog for verilog files +call ale#Set('verilog_iverilog_options', '') + +function! ale_linters#verilog#iverilog#GetCommand(buffer) abort + return 'iverilog -t null -Wall ' + \ . ale#Var(a:buffer, 'verilog_iverilog_options') + \ . ' %t' +endfunction + function! ale_linters#verilog#iverilog#Handle(buffer, lines) abort " Look for lines like the following. " @@ -30,6 +38,6 @@ call ale#linter#Define('verilog', { \ 'name': 'iverilog', \ 'output_stream': 'stderr', \ 'executable': 'iverilog', -\ 'command': 'iverilog -t null -Wall %t', +\ 'command_callback': 'ale_linters#verilog#iverilog#GetCommand', \ 'callback': 'ale_linters#verilog#iverilog#Handle', \}) diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim index adf2b4a..dfa00dc 100644 --- a/ale_linters/vim/vint.vim +++ b/ale_linters/vim/vint.vim @@ -6,25 +6,19 @@ let g:ale_vim_vint_show_style_issues = \ get(g:, 'ale_vim_vint_show_style_issues', 1) let s:enable_neovim = has('nvim') ? ' --enable-neovim ' : '' let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"' -let s:vint_version = [] function! ale_linters#vim#vint#VersionCommand(buffer) abort - if empty(s:vint_version) - " Check the Vint version if we haven't checked it already. - return 'vint --version' - endif - - return '' + " Check the Vint version if we haven't checked it already. + return !ale#semver#HasVersion('vint') + \ ? 'vint --version' + \ : '' endfunction function! ale_linters#vim#vint#GetCommand(buffer, version_output) abort - if empty(s:vint_version) && !empty(a:version_output) - " Parse the version out of the --version output. - let s:vint_version = ale#semver#Parse(join(a:version_output, "\n")) - endif + let l:version = ale#semver#GetVersion('vint', a:version_output) - let l:can_use_no_color_flag = empty(s:vint_version) - \ || ale#semver#GreaterOrEqual(s:vint_version, [0, 3, 7]) + let l:can_use_no_color_flag = empty(l:version) + \ || ale#semver#GTE(l:version, [0, 3, 7]) let l:warning_flag = ale#Var(a:buffer, 'vim_vint_show_style_issues') ? '-s' : '-w' diff --git a/ale_linters/xhtml/alex.vim b/ale_linters/xhtml/alex.vim new file mode 100644 index 0000000..60a9a7c --- /dev/null +++ b/ale_linters/xhtml/alex.vim @@ -0,0 +1,11 @@ +" Author: Johannes Wienke +" Description: alex for XHTML files + +call ale#linter#Define('xhtml', { +\ 'name': 'alex', +\ 'executable': 'alex', +\ 'command': 'alex %s -t', +\ 'output_stream': 'stderr', +\ 'callback': 'ale#handlers#alex#Handle', +\ 'lint_file': 1, +\}) diff --git a/ale_linters/xhtml/write-good.vim b/ale_linters/xhtml/write-good.vim new file mode 100644 index 0000000..83d1863 --- /dev/null +++ b/ale_linters/xhtml/write-good.vim @@ -0,0 +1,9 @@ +" Author: Sumner Evans +" Description: write-good for XHTML files + +call ale#linter#Define('xhtml', { +\ 'name': 'write-good', +\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', +\ 'command_callback': 'ale#handlers#writegood#GetCommand', +\ 'callback': 'ale#handlers#writegood#Handle', +\}) diff --git a/ale_linters/yaml/swaglint.vim b/ale_linters/yaml/swaglint.vim index 454cad0..75a496c 100644 --- a/ale_linters/yaml/swaglint.vim +++ b/ale_linters/yaml/swaglint.vim @@ -27,6 +27,14 @@ function! ale_linters#yaml#swaglint#Handle(buffer, lines) abort \ 'text': l:match[4], \} + " Parse the code if it's there. + let l:code_match = matchlist(l:obj.text, '\v^(.+) \(([^ (]+)\)$') + + if !empty(l:code_match) + let l:obj.text = l:code_match[1] + let l:obj.code = l:code_match[2] + endif + call add(l:output, l:obj) endfor diff --git a/autoload/ale.vim b/autoload/ale.vim index 6500b30..f6c06cf 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -61,6 +61,12 @@ function! ale#ShouldDoNothing(buffer) abort return 1 endif + let l:filename = fnamemodify(bufname(a:buffer), ':t') + + if l:filename is# '.' + return 1 + endif + " Do nothing if running in the sandbox if ale#util#InSandbox() return 1 @@ -251,3 +257,31 @@ function! ale#Escape(str) abort return shellescape (a:str) endfunction + +" Get the loclist item message according to a given format string. +" +" See `:help g:ale_loclist_msg_format` and `:help g:ale_echo_msg_format` +function! ale#GetLocItemMessage(item, format_string) abort + let l:msg = a:format_string + let l:severity = g:ale_echo_msg_warning_str + let l:code = get(a:item, 'code', '') + let l:type = get(a:item, 'type', 'E') + let l:linter_name = get(a:item, 'linter_name', '') + let l:code_repl = !empty(l:code) ? '\=submatch(1) . l:code . submatch(2)' : '' + + if l:type is# 'E' + let l:severity = g:ale_echo_msg_error_str + elseif l:type is# 'I' + let l:severity = g:ale_echo_msg_info_str + endif + + " Replace special markers with certain information. + " \=l:variable is used to avoid escaping issues. + let l:msg = substitute(l:msg, '\V%severity%', '\=l:severity', 'g') + let l:msg = substitute(l:msg, '\V%linter%', '\=l:linter_name', 'g') + let l:msg = substitute(l:msg, '\v\%([^\%]*)code([^\%]*)\%', l:code_repl, 'g') + " Replace %s with the text. + let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g') + + return l:msg +endfunction diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index b9f9439..f6ad7de 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -1,6 +1,8 @@ " Author: gagbo , w0rp " Description: Functions for integrating with C-family linters. +let s:sep = has('win32') ? '\' : '/' + function! ale#c#FindProjectRoot(buffer) abort for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename) @@ -47,7 +49,7 @@ function! ale#c#FindLocalHeaderPaths(buffer) abort " If we find an 'include' directory in the project root, then use that. if isdirectory(l:project_root . '/include') - return [ale#path#Simplify(l:project_root . '/include')] + return [ale#path#Simplify(l:project_root . s:sep . 'include')] endif return [] @@ -79,7 +81,7 @@ let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ function! ale#c#FindCompileCommands(buffer) abort for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) for l:dirname in ale#Var(a:buffer, 'c_build_dir_names') - let l:c_build_dir = l:path . '/' . l:dirname + let l:c_build_dir = l:path . s:sep . l:dirname if filereadable(l:c_build_dir . '/compile_commands.json') return l:c_build_dir diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 9f4e3c2..7ad7f9d 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -2,8 +2,45 @@ " Description: Completion support for LSP linters let s:timer_id = -1 +let s:last_done_pos = [] -function! s:GetRegex(map, filetype) abort +" CompletionItemKind values from the LSP protocol. +let s:LSP_COMPLETION_TEXT_KIND = 1 +let s:LSP_COMPLETION_METHOD_KIND = 2 +let s:LSP_COMPLETION_FUNCTION_KIND = 3 +let s:LSP_COMPLETION_CONSTRUCTOR_KIND = 4 +let s:LSP_COMPLETION_FIELD_KIND = 5 +let s:LSP_COMPLETION_VARIABLE_KIND = 6 +let s:LSP_COMPLETION_CLASS_KIND = 7 +let s:LSP_COMPLETION_INTERFACE_KIND = 8 +let s:LSP_COMPLETION_MODULE_KIND = 9 +let s:LSP_COMPLETION_PROPERTY_KIND = 10 +let s:LSP_COMPLETION_UNIT_KIND = 11 +let s:LSP_COMPLETION_VALUE_KIND = 12 +let s:LSP_COMPLETION_ENUM_KIND = 13 +let s:LSP_COMPLETION_KEYWORD_KIND = 14 +let s:LSP_COMPLETION_SNIPPET_KIND = 15 +let s:LSP_COMPLETION_COLOR_KIND = 16 +let s:LSP_COMPLETION_FILE_KIND = 17 +let s:LSP_COMPLETION_REFERENCE_KIND = 18 + +" Regular expressions for checking the characters in the line before where +" the insert cursor is. If one of these matches, we'll check for completions. +let s:should_complete_map = { +\ '': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$', +\} + +" Regular expressions for finding the start column to replace with completion. +let s:omni_start_map = { +\ '': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$', +\} + +" A map of exact characters for triggering LSP completions. +let s:trigger_character_map = { +\ '': ['.'], +\} + +function! s:GetFiletypeValue(map, filetype) abort for l:part in reverse(split(a:filetype, '\.')) let l:regex = get(a:map, l:part, []) @@ -12,19 +49,13 @@ function! s:GetRegex(map, filetype) abort endif endfor - return '' + " Use the default regex for other files. + return a:map[''] endfunction -" Regular expressions for checking the characters in the line before where -" the insert cursor is. If one of these matches, we'll check for completions. -let s:should_complete_map = { -\ 'javascript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$', -\ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$', -\} - " Check if we should look for completions for a language. function! ale#completion#GetPrefix(filetype, line, column) abort - let l:regex = s:GetRegex(s:should_complete_map, a:filetype) + let l:regex = s:GetFiletypeValue(s:should_complete_map, a:filetype) " The column we're using completions for is where we are inserting text, " like so: " abc @@ -33,11 +64,15 @@ function! ale#completion#GetPrefix(filetype, line, column) abort return matchstr(getline(a:line)[: a:column - 2], l:regex) endfunction -" Regular expressions for finding the start column to replace with completion. -let s:omni_start_map = { -\ 'javascript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$', -\ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$', -\} +function! ale#completion#GetTriggerCharacter(filetype, prefix) abort + let l:char_list = s:GetFiletypeValue(s:trigger_character_map, a:filetype) + + if index(l:char_list, a:prefix) >= 0 + return a:prefix + endif + + return '' +endfunction function! ale#completion#Filter(suggestions, prefix) abort " For completing... @@ -82,7 +117,7 @@ function! ale#completion#OmniFunc(findstart, base) abort if a:findstart let l:line = b:ale_completion_info.line let l:column = b:ale_completion_info.column - let l:regex = s:GetRegex(s:omni_start_map, &filetype) + let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype) let l:up_to_column = getline(l:line)[: l:column - 2] let l:match = matchstr(l:up_to_column, l:regex) @@ -180,7 +215,47 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort return l:results endfunction -function! ale#completion#HandleTSServerLSPResponse(conn_id, response) abort +function! ale#completion#ParseLSPCompletions(response) abort + let l:item_list = [] + + if type(get(a:response, 'result')) is type([]) + let l:item_list = a:response.result + elseif type(get(a:response, 'result')) is type({}) + \&& type(get(a:response.result, 'items')) is type([]) + let l:item_list = a:response.result.items + endif + + let l:results = [] + + for l:item in l:item_list + " See :help complete-items for Vim completion kinds + if l:item.kind is s:LSP_COMPLETION_METHOD_KIND + let l:kind = 'm' + elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND + let l:kind = 'm' + elseif l:item.kind is s:LSP_COMPLETION_FUNCTION_KIND + let l:kind = 'f' + elseif l:item.kind is s:LSP_COMPLETION_CLASS_KIND + let l:kind = 'f' + elseif l:item.kind is s:LSP_COMPLETION_INTERFACE_KIND + let l:kind = 'f' + else + let l:kind = 'v' + endif + + call add(l:results, { + \ 'word': l:item.label, + \ 'kind': l:kind, + \ 'icase': 1, + \ 'menu': l:item.detail, + \ 'info': l:item.documentation, + \}) + endfor + + return l:results +endfunction + +function! ale#completion#HandleTSServerResponse(conn_id, response) abort if !s:CompletionStillValid(get(a:response, 'request_seq')) return endif @@ -216,28 +291,59 @@ function! ale#completion#HandleTSServerLSPResponse(conn_id, response) abort endif endfunction + +function! ale#completion#HandleLSPResponse(conn_id, response) abort + if !s:CompletionStillValid(get(a:response, 'id')) + return + endif + + call ale#completion#Show( + \ a:response, + \ 'ale#completion#ParseLSPCompletions', + \) +endfunction + function! s:GetLSPCompletions(linter) abort let l:buffer = bufnr('') - let l:lsp_details = ale#linter#StartLSP( - \ l:buffer, - \ a:linter, - \ function('ale#completion#HandleTSServerLSPResponse'), - \) + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#completion#HandleTSServerResponse') + \ : function('ale#completion#HandleLSPResponse') + + let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback) if empty(l:lsp_details) return 0 endif let l:id = l:lsp_details.connection_id - let l:command = l:lsp_details.command let l:root = l:lsp_details.project_root - let l:message = ale#lsp#tsserver_message#Completions( - \ l:buffer, - \ b:ale_completion_info.line, - \ b:ale_completion_info.column, - \ b:ale_completion_info.prefix, - \) + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#Completions( + \ l:buffer, + \ b:ale_completion_info.line, + \ b:ale_completion_info.column, + \ b:ale_completion_info.prefix, + \) + else + " Send a message saying the buffer has changed first, otherwise + " completions won't know what text is nearby. + call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root) + + " For LSP completions, we need to clamp the column to the length of + " the line. python-language-server and perhaps others do not implement + " this correctly. + let l:message = ale#lsp#message#Completion( + \ l:buffer, + \ b:ale_completion_info.line, + \ min([ + \ b:ale_completion_info.line_length, + \ b:ale_completion_info.column, + \ ]), + \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix), + \) + endif + let l:request_id = ale#lsp#Send(l:id, l:message, l:root) if l:request_id @@ -247,6 +353,10 @@ function! s:GetLSPCompletions(linter) abort endfunction function! ale#completion#GetCompletions() abort + if !g:ale_completion_enabled + return + endif + let [l:line, l:column] = getcurpos()[1:2] let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column) @@ -255,8 +365,11 @@ function! ale#completion#GetCompletions() abort return endif + let l:line_length = len(getline('.')) + let b:ale_completion_info = { \ 'line': l:line, + \ 'line_length': l:line_length, \ 'column': l:column, \ 'prefix': l:prefix, \ 'conn_id': 0, @@ -264,8 +377,11 @@ function! ale#completion#GetCompletions() abort \} for l:linter in ale#linter#Get(&filetype) - if l:linter.lsp is# 'tsserver' - call s:GetLSPCompletions(l:linter) + if !empty(l:linter.lsp) + if l:linter.lsp is# 'tsserver' + \|| get(g:, 'ale_completion_experimental_lsp_support', 0) + call s:GetLSPCompletions(l:linter) + endif endif endfor endfunction @@ -282,18 +398,35 @@ function! s:TimerHandler(...) abort endif endfunction +" Stop any completion timer that is queued. This is useful for tests. +function! ale#completion#StopTimer() abort + if s:timer_id != -1 + call timer_stop(s:timer_id) + endif + + let s:timer_id = -1 +endfunction + function! ale#completion#Queue() abort + if !g:ale_completion_enabled + return + endif + let s:timer_pos = getcurpos()[1:2] + if s:timer_pos == s:last_done_pos + " Do not ask for completions if the cursor rests on the position we + " last completed on. + return + endif + " If we changed the text again while we're still waiting for a response, " then invalidate the requests before the timer ticks again. if exists('b:ale_completion_info') let b:ale_completion_info.request_id = 0 endif - if s:timer_id != -1 - call timer_stop(s:timer_id) - endif + call ale#completion#StopTimer() let s:timer_id = timer_start(g:ale_completion_delay, function('s:TimerHandler')) endfunction @@ -303,7 +436,10 @@ function! ale#completion#Done() abort " Reset settings when completion is done. if exists('b:ale_old_omnifunc') - let &l:omnifunc = b:ale_old_omnifunc + if b:ale_old_omnifunc isnot# 'pythoncomplete#Complete' + let &l:omnifunc = b:ale_old_omnifunc + endif + unlet b:ale_old_omnifunc endif @@ -311,6 +447,8 @@ function! ale#completion#Done() abort let &l:completeopt = b:ale_old_completopt unlet b:ale_old_completopt endif + + let s:last_done_pos = getcurpos()[1:2] endfunction function! s:Setup(enabled) abort diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index 6238b4a..50b1fb5 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -4,52 +4,32 @@ let s:cursor_timer = -1 let s:last_pos = [0, 0, 0] -" Return a formatted message according to g:ale_echo_msg_format variable -function! s:GetMessage(linter, type, text) abort - let l:msg = g:ale_echo_msg_format - let l:type = a:type is# 'E' - \ ? g:ale_echo_msg_error_str - \ : g:ale_echo_msg_warning_str - - " Replace handlers if they exist - for [l:k, l:v] in items({'linter': a:linter, 'severity': l:type}) - let l:msg = substitute(l:msg, '\V%' . l:k . '%', l:v, '') - endfor - - return printf(l:msg, a:text) -endfunction - -function! s:EchoWithShortMess(setting, message) abort - " We need to remember the setting for shormess and reset it again. - let l:shortmess_options = getbufvar('%', '&shortmess') - - try - " Turn shortmess on or off. - if a:setting is# 'on' - setlocal shortmess+=T - " echomsg is needed for the message to get truncated and appear in - " the message history. - exec "norm! :echomsg a:message\n" - elseif a:setting is# 'off' - setlocal shortmess-=T - " Regular echo is needed for printing newline characters. - echo a:message - else - throw 'Invalid setting: ' . string(a:setting) - endif - finally - call setbufvar('%', '&shortmess', l:shortmess_options) - endtry -endfunction - -function! ale#cursor#TruncatedEcho(message) abort - let l:message = a:message +function! ale#cursor#TruncatedEcho(original_message) abort + let l:message = a:original_message " Change tabs to spaces. let l:message = substitute(l:message, "\t", ' ', 'g') " Remove any newlines in the message. let l:message = substitute(l:message, "\n", '', 'g') - call s:EchoWithShortMess('on', l:message) + " We need to remember the setting for shortmess and reset it again. + let l:shortmess_options = &l:shortmess + + try + let l:cursor_position = getcurpos() + + " The message is truncated and saved to the history. + setlocal shortmess+=T + exec "norm! :echomsg l:message\n" + + " Reset the cursor position if we moved off the end of the line. + " Using :norm and :echomsg can move the cursor off the end of the + " line. + if l:cursor_position != getcurpos() + call setpos('.', l:cursor_position) + endif + finally + let &l:shortmess = l:shortmess_options + endtry endfunction function! s:FindItemAtCursor() abort @@ -75,6 +55,10 @@ function! ale#cursor#EchoCursorWarning(...) abort endfunction function! s:EchoImpl() abort + if !g:ale_echo_cursor + return + endif + " Only echo the warnings in normal mode, otherwise we will get problems. if mode() isnot# 'n' return @@ -84,21 +68,27 @@ function! s:EchoImpl() abort return endif + let l:buffer = bufnr('') let [l:info, l:loc] = s:FindItemAtCursor() if !empty(l:loc) - let l:msg = s:GetMessage(l:loc.linter_name, l:loc.type, l:loc.text) + 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. - echo + execute 'echo' let l:info.echoed = 0 endif endfunction function! ale#cursor#EchoCursorWarningWithDelay() abort + if !g:ale_echo_cursor + return + endif + " Only echo the warnings in normal mode, otherwise we will get problems. if mode() isnot# 'n' return @@ -140,9 +130,7 @@ function! ale#cursor#ShowCursorDetail() abort if !empty(l:loc) let l:message = get(l:loc, 'detail', l:loc.text) - call s:EchoWithShortMess('off', l:message) - - " Set the echo marker, so we can clear it by moving the cursor. - let l:info.echoed = 1 + call ale#preview#Show(split(l:message, "\n")) + execute 'echo' endif endfunction diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim index 7454bb1..cb93ec1 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -2,33 +2,63 @@ " Description: This file implements debugging information for ALE let s:global_variable_list = [ +\ 'ale_cache_executable_check_failures', +\ 'ale_change_sign_column_color', +\ 'ale_command_wrapper', +\ 'ale_completion_delay', +\ 'ale_completion_enabled', +\ 'ale_completion_max_suggestions', \ 'ale_echo_cursor', \ 'ale_echo_msg_error_str', \ 'ale_echo_msg_format', +\ 'ale_echo_msg_info_str', \ 'ale_echo_msg_warning_str', \ 'ale_enabled', \ 'ale_fix_on_save', \ 'ale_fixers', +\ 'ale_history_enabled', +\ 'ale_history_log_output', \ 'ale_keep_list_window_open', \ 'ale_lint_delay', \ 'ale_lint_on_enter', +\ 'ale_lint_on_filetype_changed', \ 'ale_lint_on_save', \ 'ale_lint_on_text_changed', +\ 'ale_lint_on_insert_leave', \ 'ale_linter_aliases', \ 'ale_linters', +\ 'ale_linters_explicit', +\ 'ale_list_window_size', +\ 'ale_list_vertical', +\ 'ale_loclist_msg_format', +\ 'ale_max_buffer_history_size', +\ 'ale_max_signs', +\ 'ale_maximum_file_size', \ 'ale_open_list', +\ 'ale_pattern_options', +\ 'ale_pattern_options_enabled', +\ 'ale_set_balloons', \ 'ale_set_highlights', \ 'ale_set_loclist', \ 'ale_set_quickfix', \ 'ale_set_signs', \ 'ale_sign_column_always', \ 'ale_sign_error', +\ 'ale_sign_info', \ 'ale_sign_offset', +\ 'ale_sign_style_error', +\ 'ale_sign_style_warning', \ 'ale_sign_warning', \ 'ale_statusline_format', +\ 'ale_type_map', +\ 'ale_warn_about_trailing_blank_lines', \ 'ale_warn_about_trailing_whitespace', \] +function! s:Echo(message) abort + execute 'echo a:message' +endfunction + function! s:GetLinterVariables(filetype, linter_names) abort let l:variable_list = [] let l:filetype_parts = split(a:filetype, '\.') @@ -52,20 +82,20 @@ endfunction function! s:EchoLinterVariables(variable_list) abort for l:key in a:variable_list - echom 'let g:' . l:key . ' = ' . string(g:[l:key]) + call s:Echo('let g:' . l:key . ' = ' . string(g:[l:key])) if has_key(b:, l:key) - echom 'let b:' . l:key . ' = ' . string(b:[l:key]) + call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key])) endif endfor endfunction function! s:EchoGlobalVariables() abort for l:key in s:global_variable_list - echom 'let g:' . l:key . ' = ' . string(get(g:, l:key, v:null)) + call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null))) if has_key(b:, l:key) - echom 'let b:' . l:key . ' = ' . string(b:[l:key]) + call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key])) endif endfor endfunction @@ -79,34 +109,34 @@ function! s:EchoCommand(item) abort let l:status_message .= ' - exit code ' . a:item.exit_code endif - echom '(' . l:status_message . ') ' . string(a:item.command) + call s:Echo('(' . l:status_message . ') ' . string(a:item.command)) if g:ale_history_log_output && has_key(a:item, 'output') if empty(a:item.output) - echom '' - echom '<<>>' - echom '' + call s:Echo('') + call s:Echo('<<>>') + call s:Echo('') else - echom '' - echom '<<>>' + call s:Echo('') + call s:Echo('<<>>') for l:line in a:item.output - echom l:line + call s:Echo(l:line) endfor - echom '<<>>' - echom '' + call s:Echo('<<>>') + call s:Echo('') endif endif endfunction " Echo the results of an executable check. function! s:EchoExecutable(item) abort - echom printf( + call s:Echo(printf( \ '(executable check - %s) %s', \ a:item.status ? 'success' : 'failure', \ a:item.command, - \) + \)) endfunction function! s:EchoCommandHistory() abort @@ -127,12 +157,12 @@ function! s:EchoLinterAliases(all_linters) abort for l:linter in a:all_linters if !empty(l:linter.aliases) if l:first - echom ' Linter Aliases:' + call s:Echo(' Linter Aliases:') endif let l:first = 0 - echom string(l:linter.name) . ' -> ' . string(l:linter.aliases) + call s:Echo(string(l:linter.name) . ' -> ' . string(l:linter.aliases)) endif endfor endfunction @@ -159,18 +189,18 @@ function! ale#debugging#Info() abort " This must be done after linters are loaded. let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names) - echom ' Current Filetype: ' . l:filetype - echom 'Available Linters: ' . string(l:all_names) + call s:Echo(' Current Filetype: ' . l:filetype) + call s:Echo('Available Linters: ' . string(l:all_names)) call s:EchoLinterAliases(l:all_linters) - echom ' Enabled Linters: ' . string(l:enabled_names) - echom ' Linter Variables:' - echom '' + call s:Echo(' Enabled Linters: ' . string(l:enabled_names)) + call s:Echo(' Linter Variables:') + call s:Echo('') call s:EchoLinterVariables(l:variable_list) - echom ' Global Variables:' - echom '' + call s:Echo(' Global Variables:') + call s:Echo('') call s:EchoGlobalVariables() - echom ' Command History:' - echom '' + call s:Echo(' Command History:') + call s:Echo('') call s:EchoCommandHistory() endfunction @@ -179,5 +209,5 @@ function! ale#debugging#InfoToClipboard() abort silent call ale#debugging#Info() redir END - echom 'ALEInfo copied to your clipboard' + call s:Echo('ALEInfo copied to your clipboard') endfunction diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim new file mode 100644 index 0000000..521cd0b --- /dev/null +++ b/autoload/ale/definition.vim @@ -0,0 +1,125 @@ +" Author: w0rp +" Description: Go to definition support for LSP linters. + +let s:go_to_definition_map = {} + +" Used to get the definition map in tests. +function! ale#definition#GetMap() abort + return deepcopy(s:go_to_definition_map) +endfunction + +" Used to set the definition map in tests. +function! ale#definition#SetMap(map) abort + let s:go_to_definition_map = a:map +endfunction + +" This function is used so we can check the execution of commands without +" running them. +function! ale#definition#Execute(expr) abort + execute a:expr +endfunction + +function! ale#definition#ClearLSPData() abort + let s:go_to_definition_map = {} +endfunction + +function! ale#definition#Open(options, filename, line, column) abort + if a:options.open_in_tab + call ale#definition#Execute('tabedit ' . fnameescape(a:filename)) + else + call ale#definition#Execute('edit ' . fnameescape(a:filename)) + endif + + call cursor(a:line, a:column) +endfunction + +function! ale#definition#HandleTSServerResponse(conn_id, response) abort + if get(a:response, 'command', '') is# 'definition' + \&& has_key(s:go_to_definition_map, a:response.request_seq) + let l:options = remove(s:go_to_definition_map, a:response.request_seq) + + if get(a:response, 'success', v:false) is v:true + let l:filename = a:response.body[0].file + let l:line = a:response.body[0].start.line + let l:column = a:response.body[0].start.offset + + call ale#definition#Open(l:options, l:filename, l:line, l:column) + endif + endif +endfunction + +function! ale#definition#HandleLSPResponse(conn_id, response) abort + if has_key(a:response, 'id') + \&& has_key(s:go_to_definition_map, a:response.id) + let l:options = remove(s:go_to_definition_map, a:response.id) + + " The result can be a Dictionary item, a List of the same, or null. + let l:result = get(a:response, 'result', v:null) + + if type(l:result) is type({}) + let l:result = [l:result] + elseif type(l:result) isnot type([]) + let l:result = [] + endif + + for l:item in l:result + let l:filename = ale#path#FromURI(l:item.uri) + let l:line = l:item.range.start.line + 1 + let l:column = l:item.range.start.character + + call ale#definition#Open(l:options, l:filename, l:line, l:column) + break + endfor + endif +endfunction + +function! s:GoToLSPDefinition(linter, options) abort + let l:buffer = bufnr('') + let [l:line, l:column] = getcurpos()[1:2] + + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#definition#HandleTSServerResponse') + \ : function('ale#definition#HandleLSPResponse') + + let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback) + + if empty(l:lsp_details) + return 0 + endif + + let l:id = l:lsp_details.connection_id + let l:root = l:lsp_details.project_root + + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#Definition( + \ l:buffer, + \ l:line, + \ l:column + \) + else + " Send a message saying the buffer has changed first, or the + " definition position probably won't make sense. + call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root) + + let l:column = min([l:column, len(getline(l:line))]) + + " For LSP completions, we need to clamp the column to the length of + " the line. python-language-server and perhaps others do not implement + " this correctly. + let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column) + endif + + let l:request_id = ale#lsp#Send(l:id, l:message, l:root) + + let s:go_to_definition_map[l:request_id] = { + \ 'open_in_tab': get(a:options, 'open_in_tab', 0), + \} +endfunction + +function! ale#definition#GoTo(options) abort + for l:linter in ale#linter#Get(&filetype) + if !empty(l:linter.lsp) + call s:GoToLSPDefinition(l:linter, a:options) + endif + endfor +endfunction diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 1b22df4..8916987 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -27,16 +27,25 @@ endfunction " Check if files are executable, and if they are, remember that they are " for subsequent calls. We'll keep checking until programs can be executed. function! ale#engine#IsExecutable(buffer, executable) abort - if has_key(s:executable_cache_map, a:executable) - return 1 + if empty(a:executable) + " Don't log the executable check if the executable string is empty. + return 0 endif - let l:result = 0 + " Check for a cached executable() check. + let l:result = get(s:executable_cache_map, a:executable, v:null) - if executable(a:executable) - let s:executable_cache_map[a:executable] = 1 + if l:result isnot v:null + return l:result + endif - let l:result = 1 + " Check if the file is executable, and convert -1 to 1. + let l:result = executable(a:executable) isnot 0 + + " Cache the executable check if we found it, or if the option to cache + " failing checks is on. + if l:result || g:ale_cache_executable_check_failures + let s:executable_cache_map[a:executable] = l:result endif if g:ale_history_enabled @@ -67,6 +76,13 @@ function! ale#engine#InitBufferInfo(buffer) abort return 0 endfunction +" Clear LSP linter data for the linting engine. +function! ale#engine#ClearLSPData() abort + let s:lsp_linter_map = {} +endfunction + +" This function is documented and part of the public API. +" " Return 1 if ALE is busy checking a given buffer function! ale#engine#IsCheckingBuffer(buffer) abort let l:info = get(g:ale_buffer_info, a:buffer, {}) @@ -133,36 +149,40 @@ function! s:GatherOutput(job_id, line) abort endif endfunction -function! s:HandleLoclist(linter_name, buffer, loclist) abort - let l:buffer_info = get(g:ale_buffer_info, a:buffer, {}) +function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort + let l:info = get(g:ale_buffer_info, a:buffer, {}) - if empty(l:buffer_info) + if empty(l:info) return endif " Remove this linter from the list of active linters. " This may have already been done when the job exits. - call filter(l:buffer_info.active_linter_list, 'v:val isnot# a:linter_name') + call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name') " Make some adjustments to the loclists to fix common problems, and also " to set default values for loclist items. let l:linter_loclist = ale#engine#FixLocList(a:buffer, a:linter_name, a:loclist) " Remove previous items for this linter. - call filter(g:ale_buffer_info[a:buffer].loclist, 'v:val.linter_name isnot# a:linter_name') - " Add the new items. - call extend(g:ale_buffer_info[a:buffer].loclist, l:linter_loclist) + call filter(l:info.loclist, 'v:val.linter_name isnot# a:linter_name') - " Sort the loclist again. - " We need a sorted list so we can run a binary search against it - " for efficient lookup of the messages in the cursor handler. - call sort(g:ale_buffer_info[a:buffer].loclist, 'ale#util#LocItemCompare') + " We don't need to add items or sort the list when this list is empty. + if !empty(l:linter_loclist) + " Add the new items. + call extend(l:info.loclist, l:linter_loclist) + + " Sort the loclist again. + " We need a sorted list so we can run a binary search against it + " for efficient lookup of the messages in the cursor handler. + call sort(l:info.loclist, 'ale#util#LocItemCompare') + endif if ale#ShouldDoNothing(a:buffer) return endif - call ale#engine#SetResults(a:buffer, g:ale_buffer_info[a:buffer].loclist) + call ale#engine#SetResults(a:buffer, l:info.loclist) endfunction function! s:HandleExit(job_id, exit_code) abort @@ -208,7 +228,7 @@ function! s:HandleExit(job_id, exit_code) abort let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output) - call s:HandleLoclist(l:linter.name, l:buffer, l:loclist) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist) endfunction function! s:HandleLSPDiagnostics(conn_id, response) abort @@ -222,7 +242,7 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort let l:loclist = ale#lsp#response#ReadDiagnostics(a:response) - call s:HandleLoclist(l:linter_name, l:buffer, l:loclist) + call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist) endfunction function! s:HandleTSServerDiagnostics(response, error_type) abort @@ -247,14 +267,14 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort let l:loclist = get(l:info, 'semantic_loclist', []) \ + get(l:info, 'syntax_loclist', []) - call s:HandleLoclist('tsserver', l:buffer, l:loclist) + call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist) endfunction function! s:HandleLSPErrorMessage(error_message) abort - echoerr 'Error from LSP:' + execute 'echoerr ''Error from LSP:''' for l:line in split(a:error_message, "\n") - echoerr l:line + execute 'echoerr l:line' endfor endfunction @@ -297,22 +317,30 @@ function! ale#engine#SetResults(buffer, loclist) abort call ale#highlight#SetHighlights(a:buffer, a:loclist) endif - if g:ale_echo_cursor - " Try and echo the warning now. - " This will only do something meaningful if we're in normal mode. - call ale#cursor#EchoCursorWarning() - endif - if l:linting_is_done + if g:ale_echo_cursor + " Try and echo the warning now. + " This will only do something meaningful if we're in normal mode. + call ale#cursor#EchoCursorWarning() + endif + " Reset the save event marker, used for opening windows, etc. call setbufvar(a:buffer, 'ale_save_event_fired', 0) + " Set a marker showing how many times a buffer has been checked. + call setbufvar( + \ a:buffer, + \ 'ale_linted', + \ getbufvar(a:buffer, 'ale_linted', 0) + 1 + \) " Automatically remove all managed temporary files and directories " now that all jobs have completed. call ale#engine#RemoveManagedFiles(a:buffer) " Call user autocommands. This allows users to hook into ALE's lint cycle. - silent doautocmd User ALELint + silent doautocmd User ALELintPost + " Old DEPRECATED name; call it for backwards compatibility. + silent doautocmd User ALELint endif endfunction @@ -374,6 +402,10 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort \ 'linter_name': a:linter_name, \} + if has_key(l:old_item, 'code') + let l:item.code = l:old_item.code + endif + if has_key(l:old_item, 'filename') \&& !ale#path#IsTempName(l:old_item.filename) " Use the filename given. @@ -497,7 +529,7 @@ function! s:RunJob(options) abort endif endif - let l:command = ale#job#PrepareCommand(l:command) + let l:command = ale#job#PrepareCommand(l:buffer, l:command) let l:job_options = { \ 'mode': 'nl', \ 'exit_cb': function('s:HandleExit'), @@ -688,6 +720,13 @@ function! s:CheckWithLSP(buffer, linter) abort \ : ale#lsp#message#DidChange(a:buffer) let l:request_id = ale#lsp#Send(l:id, l:change_message, l:root) + " If this was a file save event, also notify the server of that. + if a:linter.lsp isnot# 'tsserver' + \&& getbufvar(a:buffer, 'ale_save_event_fired', 0) + let l:save_message = ale#lsp#message#DidSave(a:buffer) + let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root) + endif + if l:request_id != 0 if index(l:info.active_linter_list, a:linter.name) < 0 call add(l:info.active_linter_list, a:linter.name) @@ -772,6 +811,8 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort " We can only clear the results if we aren't checking the buffer. let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer) + silent doautocmd User ALELintPre + for l:linter in a:linters " Only run lint_file linters if we should. if !l:linter.lint_file || a:should_lint_file diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index a7f6b37..c7d17ea 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -12,11 +12,13 @@ function! ale#events#QuitRecently(buffer) abort endfunction function! ale#events#SaveEvent(buffer) abort - call setbufvar(a:buffer, 'ale_save_event_fired', 1) - let l:should_lint = ale#Var(a:buffer, 'enabled') - \ && g:ale_lint_on_save + let l:should_lint = ale#Var(a:buffer, 'enabled') && g:ale_lint_on_save - if g:ale_fix_on_save + if l:should_lint + call setbufvar(a:buffer, 'ale_save_event_fired', 1) + endif + + if ale#Var(a:buffer, 'fix_on_save') let l:will_fix = ale#fix#Fix('save_file') let l:should_lint = l:should_lint && !l:will_fix endif diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index a9bb7d4..9111db3 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -35,7 +35,7 @@ function! ale#fix#ApplyQueuedFixes() abort if l:end_line >= l:start_line let l:save = winsaveview() - silent execute l:start_line . ',' . l:end_line . 'd' + silent execute l:start_line . ',' . l:end_line . 'd_' call winrestview(l:save) endif @@ -75,7 +75,7 @@ function! ale#fix#ApplyFixes(buffer, output) abort if l:data.lines_before != l:lines call remove(g:ale_fix_buffer_data, a:buffer) - echoerr 'The file was changed before fixing finished' + execute 'echoerr ''The file was changed before fixing finished''' return endif endif @@ -108,17 +108,38 @@ function! s:HandleExit(job_id, exit_code) abort let l:job_info.output = readfile(l:job_info.file_to_read) endif + let l:ChainCallback = get(l:job_info, 'chain_with', v:null) + let l:ProcessWith = get(l:job_info, 'process_with', v:null) + + " Post-process the output with a function if we have one. + if l:ProcessWith isnot v:null + let l:job_info.output = call( + \ ale#util#GetFunction(l:ProcessWith), + \ [l:buffer, l:job_info.output] + \) + endif + " Use the output of the job for changing the file if it isn't empty, " otherwise skip this job and use the input from before. - let l:input = !empty(l:job_info.output) - \ ? l:job_info.output - \ : l:job_info.input + " + " We'll use the input from before for chained commands. + if l:ChainCallback is v:null && !empty(split(join(l:job_info.output))) + let l:input = l:job_info.output + else + let l:input = l:job_info.input + endif + + let l:next_index = l:ChainCallback is v:null + \ ? l:job_info.callback_index + 1 + \ : l:job_info.callback_index call s:RunFixer({ \ 'buffer': l:buffer, \ 'input': l:input, + \ 'output': l:job_info.output, \ 'callback_list': l:job_info.callback_list, - \ 'callback_index': l:job_info.callback_index + 1, + \ 'callback_index': l:next_index, + \ 'chain_callback': l:ChainCallback, \}) endfunction @@ -172,11 +193,36 @@ function! s:RunJob(options) abort let l:input = a:options.input let l:output_stream = a:options.output_stream let l:read_temporary_file = a:options.read_temporary_file + let l:ChainWith = a:options.chain_with + let l:read_buffer = a:options.read_buffer - let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, 1) + if empty(l:command) + " If there's nothing further to chain the command with, stop here. + if l:ChainWith is v:null + return 0 + endif + + " If there's another chained callback to run, then run that. + call s:RunFixer({ + \ 'buffer': l:buffer, + \ 'input': l:input, + \ 'callback_index': a:options.callback_index, + \ 'callback_list': a:options.callback_list, + \ 'chain_callback': l:ChainWith, + \ 'output': [], + \}) + + return 1 + endif + + let [l:temporary_file, l:command] = ale#command#FormatCommand( + \ l:buffer, + \ l:command, + \ l:read_buffer, + \) call s:CreateTemporaryFileForJob(l:buffer, l:temporary_file, l:input) - let l:command = ale#job#PrepareCommand(l:command) + let l:command = ale#job#PrepareCommand(l:buffer, l:command) let l:job_options = { \ 'mode': 'nl', \ 'exit_cb': function('s:HandleExit'), @@ -186,8 +232,10 @@ function! s:RunJob(options) abort \ 'buffer': l:buffer, \ 'input': l:input, \ 'output': [], - \ 'callback_list': a:options.callback_list, + \ 'chain_with': l:ChainWith, \ 'callback_index': a:options.callback_index, + \ 'callback_list': a:options.callback_list, + \ 'process_with': a:options.process_with, \} if l:read_temporary_file @@ -250,13 +298,24 @@ function! s:RunFixer(options) abort let l:buffer = a:options.buffer let l:input = a:options.input let l:index = a:options.callback_index + let l:ChainCallback = get(a:options, 'chain_callback', v:null) while len(a:options.callback_list) > l:index - let l:Function = a:options.callback_list[l:index] + let l:Function = l:ChainCallback isnot v:null + \ ? ale#util#GetFunction(l:ChainCallback) + \ : a:options.callback_list[l:index] - let l:result = ale#util#FunctionArgCount(l:Function) == 1 - \ ? call(l:Function, [l:buffer]) - \ : call(l:Function, [l:buffer, copy(l:input)]) + if l:ChainCallback isnot v:null + " Chained commands accept (buffer, output, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 2 + \ ? call(l:Function, [l:buffer, a:options.output]) + \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)]) + else + " Chained commands accept (buffer, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 1 + \ ? call(l:Function, [l:buffer]) + \ : call(l:Function, [l:buffer, copy(l:input)]) + endif if type(l:result) == type(0) && l:result == 0 " When `0` is returned, skip this item. @@ -265,14 +324,21 @@ function! s:RunFixer(options) abort let l:input = l:result let l:index += 1 else + let l:ChainWith = get(l:result, 'chain_with', v:null) + " Default to piping the buffer for the last fixer in the chain. + let l:read_buffer = get(l:result, 'read_buffer', l:ChainWith is v:null) + let l:job_ran = s:RunJob({ \ 'buffer': l:buffer, \ 'command': l:result.command, \ 'input': l:input, \ 'output_stream': get(l:result, 'output_stream', 'stdout'), \ 'read_temporary_file': get(l:result, 'read_temporary_file', 0), + \ 'read_buffer': l:read_buffer, + \ 'chain_with': l:ChainWith, \ 'callback_list': a:options.callback_list, \ 'callback_index': l:index, + \ 'process_with': get(l:result, 'process_with', v:null), \}) if !l:job_ran @@ -289,18 +355,25 @@ function! s:RunFixer(options) abort endfunction function! s:GetCallbacks() abort - let l:fixers = ale#Var(bufnr(''), 'fixers') - let l:callback_list = [] + if type(get(b:, 'ale_fixers')) is type([]) + " Lists can be used for buffer-local variables only + let l:callback_list = b:ale_fixers + else + " buffer and global options can use dictionaries mapping filetypes to + " callbacks to run. + let l:fixers = ale#Var(bufnr(''), 'fixers') + let l:callback_list = [] - for l:sub_type in split(&filetype, '\.') - let l:sub_type_callacks = get(l:fixers, l:sub_type, []) + for l:sub_type in split(&filetype, '\.') + let l:sub_type_callacks = get(l:fixers, l:sub_type, []) - if type(l:sub_type_callacks) == type('') - call add(l:callback_list, l:sub_type_callacks) - else - call extend(l:callback_list, l:sub_type_callacks) - endif - endfor + if type(l:sub_type_callacks) == type('') + call add(l:callback_list, l:sub_type_callacks) + else + call extend(l:callback_list, l:sub_type_callacks) + endif + endfor + endif if empty(l:callback_list) return [] @@ -319,7 +392,13 @@ function! s:GetCallbacks() abort endif endif - call add(l:corrected_list, ale#util#GetFunction(l:Item)) + try + call add(l:corrected_list, ale#util#GetFunction(l:Item)) + catch /E475/ + " Rethrow exceptions for failing to get a function so we can print + " a friendly message about it. + throw 'BADNAME ' . v:exception + endtry endfor return l:corrected_list @@ -352,11 +431,22 @@ function! ale#fix#Fix(...) abort throw "fixing_flag must be either '' or 'save_file'" endif - let l:callback_list = s:GetCallbacks() + try + let l:callback_list = s:GetCallbacks() + catch /E700\|BADNAME/ + let l:function_name = join(split(split(v:exception, ':')[3])) + let l:echo_message = printf( + \ 'There is no fixer named `%s`. Check :ALEFixSuggest', + \ l:function_name, + \) + execute 'echom l:echo_message' + + return 0 + endtry if empty(l:callback_list) if l:fixing_flag is# '' - echoerr 'No fixers have been defined. Try :ALEFixSuggest' + execute 'echom ''No fixers have been defined. Try :ALEFixSuggest''' endif return 0 diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index d26c71a..3e407c5 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -21,12 +21,18 @@ let s:default_registry = { \ 'function': 'ale#fixers#prettier_standard#Fix', \ 'suggested_filetypes': ['javascript'], \ 'description': 'Apply prettier-standard to a file.', +\ 'aliases': ['prettier-standard'], \ }, \ 'eslint': { \ 'function': 'ale#fixers#eslint#Fix', \ 'suggested_filetypes': ['javascript', 'typescript'], \ 'description': 'Apply eslint --fix to a file.', \ }, +\ 'mix_format': { +\ 'function': 'ale#fixers#mix_format#Fix', +\ 'suggested_filetypes': ['elixir'], +\ 'description': 'Apply mix format to a file.', +\ }, \ 'format': { \ 'function': 'ale#fixers#format#Fix', \ 'suggested_filetypes': ['elm'], @@ -39,13 +45,19 @@ let s:default_registry = { \ }, \ 'prettier': { \ 'function': 'ale#fixers#prettier#Fix', -\ 'suggested_filetypes': ['javascript', 'typescript', 'json', 'css', 'scss', 'less'], +\ 'suggested_filetypes': ['javascript', 'typescript', 'json', 'css', 'scss', 'less', 'markdown', 'graphql', 'vue'], \ 'description': 'Apply prettier to a file.', \ }, \ 'prettier_eslint': { \ 'function': 'ale#fixers#prettier_eslint#Fix', \ 'suggested_filetypes': ['javascript'], \ 'description': 'Apply prettier-eslint to a file.', +\ 'aliases': ['prettier-eslint'], +\ }, +\ 'importjs': { +\ 'function': 'ale#fixers#importjs#Fix', +\ 'suggested_filetypes': ['javascript'], +\ 'description': 'automatic imports for javascript', \ }, \ 'puppetlint': { \ 'function': 'ale#fixers#puppetlint#Fix', @@ -92,6 +104,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['php'], \ 'description': 'Fix PHP files with phpcbf.', \ }, +\ 'php_cs_fixer': { +\ 'function': 'ale#fixers#php_cs_fixer#Fix', +\ 'suggested_filetypes': ['php'], +\ 'description': 'Fix PHP files with php-cs-fixer.', +\ }, \ 'clang-format': { \ 'function': 'ale#fixers#clangformat#Fix', \ 'suggested_filetypes': ['c', 'cpp'], @@ -102,16 +119,74 @@ let s:default_registry = { \ 'suggested_filetypes': ['go'], \ 'description': 'Fix Go files with go fmt.', \ }, +\ 'goimports': { +\ 'function': 'ale#fixers#goimports#Fix', +\ 'suggested_filetypes': ['go'], +\ 'description': 'Fix Go files imports with goimports.', +\ }, \ 'tslint': { \ 'function': 'ale#fixers#tslint#Fix', \ 'suggested_filetypes': ['typescript'], \ 'description': 'Fix typescript files with tslint --fix.', \ }, +\ 'rustfmt': { +\ 'function': 'ale#fixers#rustfmt#Fix', +\ 'suggested_filetypes': ['rust'], +\ 'description': 'Fix Rust files with Rustfmt.', +\ }, +\ 'hackfmt': { +\ 'function': 'ale#fixers#hackfmt#Fix', +\ 'suggested_filetypes': ['php'], +\ 'description': 'Fix Hack files with hackfmt.', +\ }, +\ 'hfmt': { +\ 'function': 'ale#fixers#hfmt#Fix', +\ 'suggested_filetypes': ['haskell'], +\ 'description': 'Fix Haskell files with hfmt.', +\ }, +\ 'brittany': { +\ 'function': 'ale#fixers#brittany#Fix', +\ 'suggested_filetypes': ['haskell'], +\ 'description': 'Fix Haskell files with brittany.', +\ }, +\ 'refmt': { +\ 'function': 'ale#fixers#refmt#Fix', +\ 'suggested_filetypes': ['reason'], +\ 'description': 'Fix ReasonML files with refmt.', +\ }, +\ 'shfmt': { +\ 'function': 'ale#fixers#shfmt#Fix', +\ 'suggested_filetypes': ['sh'], +\ 'description': 'Fix sh files with shfmt.', +\ }, +\ 'google_java_format': { +\ 'function': 'ale#fixers#google_java_format#Fix', +\ 'suggested_filetypes': ['java'], +\ 'description': 'Fix Java files with google-java-format.', +\ }, +\ 'fixjson': { +\ 'function': 'ale#fixers#fixjson#Fix', +\ 'suggested_filetypes': ['json'], +\ 'description': 'Fix JSON files with fixjson.', +\ }, +\ 'jq': { +\ 'function': 'ale#fixers#jq#Fix', +\ 'suggested_filetypes': ['json'], +\ 'description': 'Fix JSON files with jq.', +\ }, \} " Reset the function registry to the default entries. function! ale#fix#registry#ResetToDefaults() abort let s:entries = deepcopy(s:default_registry) + let s:aliases = {} + + " Set up aliases for fixers too. + for [l:key, l:entry] in items(s:entries) + for l:alias in get(l:entry, 'aliases', []) + let s:aliases[l:alias] = l:key + endfor + endfor endfunction " Set up entries now. @@ -120,10 +195,12 @@ call ale#fix#registry#ResetToDefaults() " Remove everything from the registry, useful for tests. function! ale#fix#registry#Clear() abort let s:entries = {} + let s:aliases = {} endfunction " Add a function for fixing problems to the registry. -function! ale#fix#registry#Add(name, func, filetypes, desc) abort +" (name, func, filetypes, desc, aliases) +function! ale#fix#registry#Add(name, func, filetypes, desc, ...) abort if type(a:name) != type('') throw '''name'' must be a String' endif @@ -146,16 +223,37 @@ function! ale#fix#registry#Add(name, func, filetypes, desc) abort throw '''desc'' must be a String' endif + let l:aliases = get(a:000, 0, []) + + if type(l:aliases) != type([]) + \|| !empty(filter(copy(l:aliases), 'type(v:val) != type('''')')) + throw '''aliases'' must be a List of String values' + endif + let s:entries[a:name] = { \ 'function': a:func, \ 'suggested_filetypes': a:filetypes, \ 'description': a:desc, \} + + " Set up aliases for the fixer. + if !empty(l:aliases) + let s:entries[a:name].aliases = l:aliases + + for l:alias in l:aliases + let s:aliases[l:alias] = a:name + endfor + endif endfunction " Get a function from the registry by its short name. function! ale#fix#registry#GetFunc(name) abort - return get(s:entries, a:name, {'function': ''}).function + " Use the exact name, or an alias. + let l:resolved_name = !has_key(s:entries, a:name) + \ ? get(s:aliases, a:name, a:name) + \ : a:name + + return get(s:entries, l:resolved_name, {'function': ''}).function endfunction function! s:ShouldSuggestForType(suggested_filetypes, type_list) abort @@ -168,6 +266,25 @@ function! s:ShouldSuggestForType(suggested_filetypes, type_list) abort return 0 endfunction +function! s:FormatEntry(key, entry) abort + let l:aliases_str = '' + + " Show aliases in :ALEFixSuggest if they are there. + if !empty(get(a:entry, 'aliases', [])) + let l:aliases_str = ', ' . join( + \ map(copy(a:entry.aliases), 'string(v:val)'), + \ ',' + \) + endif + + return printf( + \ '%s%s - %s', + \ string(a:key), + \ l:aliases_str, + \ a:entry.description, + \) +endfunction + " Suggest functions to use from the registry. function! ale#fix#registry#Suggest(filetype) abort let l:type_list = split(a:filetype, '\.') @@ -179,7 +296,7 @@ function! ale#fix#registry#Suggest(filetype) abort if s:ShouldSuggestForType(l:suggested_filetypes, l:type_list) call add( \ l:filetype_fixer_list, - \ printf('%s - %s', string(l:key), s:entries[l:key].description), + \ s:FormatEntry(l:key, s:entries[l:key]), \) endif endfor @@ -190,7 +307,7 @@ function! ale#fix#registry#Suggest(filetype) abort if empty(s:entries[l:key].suggested_filetypes) call add( \ l:generic_fixer_list, - \ printf('%s - %s', string(l:key), s:entries[l:key].description), + \ s:FormatEntry(l:key, s:entries[l:key]), \) endif endfor diff --git a/autoload/ale/fixers/autopep8.vim b/autoload/ale/fixers/autopep8.vim index e0e6205..e2dd7bf 100644 --- a/autoload/ale/fixers/autopep8.vim +++ b/autoload/ale/fixers/autopep8.vim @@ -12,7 +12,7 @@ function! ale#fixers#autopep8#Fix(buffer) abort \ ['autopep8'], \) - if !ale#python#IsExecutable(l:executable) + if !executable(l:executable) return 0 endif diff --git a/autoload/ale/fixers/brittany.vim b/autoload/ale/fixers/brittany.vim new file mode 100644 index 0000000..fed5eb8 --- /dev/null +++ b/autoload/ale/fixers/brittany.vim @@ -0,0 +1,15 @@ +" Author: eborden +" Description: Integration of brittany with ALE. + +call ale#Set('haskell_brittany_executable', 'brittany') + +function! ale#fixers#brittany#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'haskell_brittany_executable') + + return { + \ 'command': ale#Escape(l:executable) + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction + diff --git a/autoload/ale/fixers/eslint.vim b/autoload/ale/fixers/eslint.vim index ad9e9e0..36f4751 100644 --- a/autoload/ale/fixers/eslint.vim +++ b/autoload/ale/fixers/eslint.vim @@ -3,12 +3,64 @@ function! ale#fixers#eslint#Fix(buffer) abort let l:executable = ale#handlers#eslint#GetExecutable(a:buffer) + + let l:command = ale#semver#HasVersion(l:executable) + \ ? '' + \ : ale#node#Executable(a:buffer, l:executable) . ' --version' + + return { + \ 'command': l:command, + \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion', + \} +endfunction + +function! ale#fixers#eslint#ProcessFixDryRunOutput(buffer, output) abort + for l:item in ale#util#FuzzyJSONDecode(a:output, []) + return split(get(l:item, 'output', ''), "\n") + endfor + + return [] +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:' + return [] + endif + endfor + + return a:output +endfunction + +function! ale#fixers#eslint#ApplyFixForVersion(buffer, version_output) abort + let l:executable = ale#handlers#eslint#GetExecutable(a:buffer) + let l:version = ale#semver#GetVersion(l:executable, a:version_output) + let l:config = ale#handlers#eslint#FindConfig(a:buffer) if empty(l:config) return 0 endif + " Use --fix-to-stdout with eslint_d + if l:executable =~# 'eslint_d$' && ale#semver#GTE(l:version, [3, 19, 0]) + return { + \ 'command': ale#node#Executable(a:buffer, l:executable) + \ . ' --stdin-filename %s --stdin --fix-to-stdout', + \ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput', + \} + endif + + " 4.9.0 is the first version with --fix-dry-run + if ale#semver#GTE(l:version, [4, 9, 0]) + return { + \ 'command': ale#node#Executable(a:buffer, l:executable) + \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json', + \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput', + \} + endif + return { \ 'command': ale#node#Executable(a:buffer, l:executable) \ . ' -c ' . ale#Escape(l:config) diff --git a/autoload/ale/fixers/fixjson.vim b/autoload/ale/fixers/fixjson.vim new file mode 100644 index 0000000..43eb063 --- /dev/null +++ b/autoload/ale/fixers/fixjson.vim @@ -0,0 +1,27 @@ +" Author: rhysd +" Description: Integration of fixjson with ALE. + +call ale#Set('json_fixjson_executable', 'fixjson') +call ale#Set('json_fixjson_options', '') +call ale#Set('json_fixjson_use_global', 0) + +function! ale#fixers#fixjson#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'json_fixjson', [ + \ 'node_modules/.bin/fixjson', + \]) +endfunction + +function! ale#fixers#fixjson#Fix(buffer) abort + let l:executable = ale#Escape(ale#fixers#fixjson#GetExecutable(a:buffer)) + let l:filename = ale#Escape(bufname(a:buffer)) + let l:command = l:executable . ' --stdin-filename ' . l:filename + + let l:options = ale#Var(a:buffer, 'json_fixjson_options') + if l:options isnot# '' + let l:command .= ' ' . l:options + endif + + return { + \ 'command': l:command + \} +endfunction diff --git a/autoload/ale/fixers/goimports.vim b/autoload/ale/fixers/goimports.vim new file mode 100644 index 0000000..783d020 --- /dev/null +++ b/autoload/ale/fixers/goimports.vim @@ -0,0 +1,22 @@ +" Author: Jeff Willette +" Description: Integration of goimports with ALE. + +call ale#Set('go_goimports_executable', 'goimports') +call ale#Set('go_goimports_options', '') + +function! ale#fixers#goimports#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'go_goimports_executable') + let l:options = ale#Var(a:buffer, 'go_goimports_options') + + if !executable(l:executable) + return 0 + endif + + return { + \ 'command': ale#Escape(l:executable) + \ . ' -l -w -srcdir %s' + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/google_java_format.vim b/autoload/ale/fixers/google_java_format.vim new file mode 100644 index 0000000..92632e8 --- /dev/null +++ b/autoload/ale/fixers/google_java_format.vim @@ -0,0 +1,23 @@ +" Author: butlerx +" Description: Integration of Google-java-format with ALE. + +call ale#Set('google_java_format_executable', 'google-java-format') +call ale#Set('google_java_format_use_global', 0) +call ale#Set('google_java_format_options', '') + +function! ale#fixers#google_java_format#Fix(buffer) abort + let l:options = ale#Var(a:buffer, 'google_java_format_options') + let l:executable = ale#Var(a:buffer, 'google_java_format_executable') + + if !executable(l:executable) + return 0 + endif + + return { + \ 'command': ale#Escape(l:executable) + \ . ' ' . (empty(l:options) ? '' : ' ' . l:options) + \ . ' --replace' + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/hackfmt.vim b/autoload/ale/fixers/hackfmt.vim new file mode 100644 index 0000000..b5bf0dc --- /dev/null +++ b/autoload/ale/fixers/hackfmt.vim @@ -0,0 +1,18 @@ +" Author: Sam Howie +" Description: Integration of hackfmt with ALE. + +call ale#Set('php_hackfmt_executable', 'hackfmt') +call ale#Set('php_hackfmt_options', '') + +function! ale#fixers#hackfmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'php_hackfmt_executable') + let l:options = ale#Var(a:buffer, 'php_hackfmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . ' -i' + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/hfmt.vim b/autoload/ale/fixers/hfmt.vim new file mode 100644 index 0000000..ea061da --- /dev/null +++ b/autoload/ale/fixers/hfmt.vim @@ -0,0 +1,16 @@ +" Author: zack +" Description: Integration of hfmt with ALE. + +call ale#Set('haskell_hfmt_executable', 'hfmt') + +function! ale#fixers#hfmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'haskell_hfmt_executable') + + return { + \ 'command': ale#Escape(l:executable) + \ . ' -w' + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction + diff --git a/autoload/ale/fixers/importjs.vim b/autoload/ale/fixers/importjs.vim new file mode 100644 index 0000000..e8eedb1 --- /dev/null +++ b/autoload/ale/fixers/importjs.vim @@ -0,0 +1,24 @@ +" Author: Jeff Willette +" Description: Integration of importjs with ALE. + +call ale#Set('js_importjs_executable', 'importjs') + +function! ale#fixers#importjs#ProcessOutput(buffer, output) abort + let l:result = ale#util#FuzzyJSONDecode(a:output, []) + return split(get(l:result, 'fileContent', ''), "\n") +endfunction + +function! ale#fixers#importjs#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'js_importjs_executable') + + if !executable(l:executable) + return 0 + endif + + return { + \ 'command': ale#Escape(l:executable) + \ . ' fix' + \ . ' %s', + \ 'process_with': 'ale#fixers#importjs#ProcessOutput', + \} +endfunction diff --git a/autoload/ale/fixers/isort.vim b/autoload/ale/fixers/isort.vim index ddd9561..b631822 100644 --- a/autoload/ale/fixers/isort.vim +++ b/autoload/ale/fixers/isort.vim @@ -11,16 +11,12 @@ function! ale#fixers#isort#Fix(buffer) abort \ ['isort'], \) - if !ale#python#IsExecutable(l:executable) + if !executable(l:executable) return 0 endif - let l:config = ale#path#FindNearestFile(a:buffer, '.isort.cfg') - let l:config_options = !empty(l:config) - \ ? ' --settings-path ' . ale#Escape(l:config) - \ : '' - return { - \ 'command': ale#Escape(l:executable) . l:config_options . ' -', + \ 'command': ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) . ' -', \} endfunction diff --git a/autoload/ale/fixers/jq.vim b/autoload/ale/fixers/jq.vim new file mode 100644 index 0000000..b0a43fe --- /dev/null +++ b/autoload/ale/fixers/jq.vim @@ -0,0 +1,15 @@ +call ale#Set('json_jq_executable', 'jq') +call ale#Set('json_jq_options', '') + +function! ale#fixers#jq#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'json_jq_executable') +endfunction + +function! ale#fixers#jq#Fix(buffer) abort + let l:options = ale#Var(a:buffer, 'json_jq_options') + + return { + \ 'command': ale#Escape(ale#fixers#jq#GetExecutable(a:buffer)) + \ . ' . ' . l:options, + \} +endfunction diff --git a/autoload/ale/fixers/mix_format.vim b/autoload/ale/fixers/mix_format.vim new file mode 100644 index 0000000..0486640 --- /dev/null +++ b/autoload/ale/fixers/mix_format.vim @@ -0,0 +1,16 @@ +" Author: carakan +" Description: Fixing files with elixir formatter 'mix format'. + +call ale#Set('elixir_mix_executable', 'mix') + +function! ale#fixers#mix_format#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'elixir_mix_executable') +endfunction + +function! ale#fixers#mix_format#Fix(buffer) abort + return { + \ 'command': ale#Escape(ale#fixers#mix_format#GetExecutable(a:buffer)) + \ . ' format %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/php_cs_fixer.vim b/autoload/ale/fixers/php_cs_fixer.vim new file mode 100644 index 0000000..56aa915 --- /dev/null +++ b/autoload/ale/fixers/php_cs_fixer.vim @@ -0,0 +1,23 @@ +" Author: Julien Deniau +" Description: Fixing files with php-cs-fixer. + +call ale#Set('php_cs_fixer_executable', 'php-cs-fixer') +call ale#Set('php_cs_fixer_use_global', 0) + +function! ale#fixers#php_cs_fixer#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'php_cs_fixer', [ + \ 'vendor/bin/php-cs-fixer', + \ 'php-cs-fixer' + \]) +endfunction + +function! ale#fixers#php_cs_fixer#Fix(buffer) abort + let l:executable = ale#fixers#php_cs_fixer#GetExecutable(a:buffer) + return { + \ 'command': ale#Escape(l:executable) . ' fix %t', + \ 'read_temporary_file': 1, + \} +endfunction + + + diff --git a/autoload/ale/fixers/phpcbf.vim b/autoload/ale/fixers/phpcbf.vim index 9bff741..649e17d 100644 --- a/autoload/ale/fixers/phpcbf.vim +++ b/autoload/ale/fixers/phpcbf.vim @@ -19,6 +19,6 @@ function! ale#fixers#phpcbf#Fix(buffer) abort \ ? '--standard=' . l:standard \ : '' return { - \ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option + \ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ' -' \} endfunction diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim index d66e00f..d75299e 100644 --- a/autoload/ale/fixers/prettier.vim +++ b/autoload/ale/fixers/prettier.vim @@ -4,31 +4,8 @@ call ale#Set('javascript_prettier_executable', 'prettier') call ale#Set('javascript_prettier_use_global', 0) -call ale#Set('javascript_prettier_use_local_config', 0) call ale#Set('javascript_prettier_options', '') -function! s:FindConfig(buffer) abort - for l:filename in [ - \ '.prettierrc', - \ '.prettierrc.json', - \ '.prettierrc.yaml', - \ '.prettierrc.yml', - \ '.prettierrc.js', - \ 'prettier.config.js', - \ 'package.json', - \ ] - - let l:config = ale#path#FindNearestFile(a:buffer, l:filename) - - if !empty(l:config) - return l:config - endif - endfor - - return '' -endfunction - - function! ale#fixers#prettier#GetExecutable(buffer) abort return ale#node#FindExecutable(a:buffer, 'javascript_prettier', [ \ 'node_modules/.bin/prettier_d', @@ -38,32 +15,38 @@ function! ale#fixers#prettier#GetExecutable(buffer) abort endfunction function! ale#fixers#prettier#Fix(buffer) abort - let l:options = ale#Var(a:buffer, 'javascript_prettier_options') - let l:config = s:FindConfig(a:buffer) - let l:use_config = ale#Var(a:buffer, 'javascript_prettier_use_local_config') - \ && !empty(l:config) - let l:filetype = getbufvar(a:buffer, '&filetype') + let l:executable = ale#fixers#prettier#GetExecutable(a:buffer) - " Append the --parser flag depending on the current filetype (unless it's - " already set in g:javascript_prettier_options). - if match(l:options, '--parser') == -1 - if l:filetype is# 'typescript' - let l:parser = 'typescript' - elseif l:filetype =~# 'css\|scss\|less' - let l:parser = 'postcss' - elseif l:filetype is# 'json' - let l:parser = 'json' - else - let l:parser = 'babylon' - endif - let l:options = (!empty(l:options) ? l:options . ' ' : '') . '--parser ' . l:parser + let l:command = ale#semver#HasVersion(l:executable) + \ ? '' + \ : ale#Escape(l:executable) . ' --version' + + return { + \ 'command': l:command, + \ 'chain_with': 'ale#fixers#prettier#ApplyFixForVersion', + \} +endfunction + +function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort + let l:executable = ale#fixers#prettier#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'javascript_prettier_options') + + let l:version = ale#semver#GetVersion(l:executable, a:version_output) + + " 1.4.0 is the first version with --stdin-filepath + if ale#semver#GTE(l:version, [1, 4, 0]) + return { + \ 'command': ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' --stdin-filepath %s --stdin', + \} endif return { - \ 'command': ale#Escape(ale#fixers#prettier#GetExecutable(a:buffer)) + \ 'command': ale#Escape(l:executable) \ . ' %t' \ . (!empty(l:options) ? ' ' . l:options : '') - \ . (l:use_config ? ' --config ' . ale#Escape(l:config) : '') \ . ' --write', \ 'read_temporary_file': 1, \} diff --git a/autoload/ale/fixers/prettier_eslint.vim b/autoload/ale/fixers/prettier_eslint.vim index 524c52d..5dd9102 100644 --- a/autoload/ale/fixers/prettier_eslint.vim +++ b/autoload/ale/fixers/prettier_eslint.vim @@ -6,7 +6,6 @@ function! ale#fixers#prettier_eslint#SetOptionDefaults() abort call ale#Set('javascript_prettier_eslint_executable', 'prettier-eslint') call ale#Set('javascript_prettier_eslint_use_global', 0) call ale#Set('javascript_prettier_eslint_options', '') - call ale#Set('javascript_prettier_eslint_legacy', 0) endfunction call ale#fixers#prettier_eslint#SetOptionDefaults() @@ -19,16 +18,43 @@ function! ale#fixers#prettier_eslint#GetExecutable(buffer) abort endfunction function! ale#fixers#prettier_eslint#Fix(buffer) abort + let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer) + + let l:command = ale#semver#HasVersion(l:executable) + \ ? '' + \ : ale#Escape(l:executable) . ' --version' + + return { + \ 'command': l:command, + \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion', + \} +endfunction + +function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version_output) abort let l:options = ale#Var(a:buffer, 'javascript_prettier_eslint_options') let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer) - let l:config = !ale#Var(a:buffer, 'javascript_prettier_eslint_legacy') + let l:version = ale#semver#GetVersion(l:executable, a:version_output) + + " 4.2.0 is the first version with --eslint-config-path + let l:config = ale#semver#GTE(l:version, [4, 2, 0]) \ ? ale#handlers#eslint#FindConfig(a:buffer) \ : '' let l:eslint_config_option = !empty(l:config) \ ? ' --eslint-config-path ' . ale#Escape(l:config) \ : '' + " 4.4.0 is the first version with --stdin-filepath + if ale#semver#GTE(l:version, [4, 4, 0]) + return { + \ 'command': ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) + \ . l:eslint_config_option + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' --stdin-filepath %s --stdin', + \} + endif + return { \ 'command': ale#Escape(l:executable) \ . ' %t' diff --git a/autoload/ale/fixers/refmt.vim b/autoload/ale/fixers/refmt.vim new file mode 100644 index 0000000..514f950 --- /dev/null +++ b/autoload/ale/fixers/refmt.vim @@ -0,0 +1,18 @@ +" Author: Ahmed El Gabri <@ahmedelgabri> +" Description: Integration of refmt with ALE. + +call ale#Set('reasonml_refmt_executable', 'refmt') +call ale#Set('reasonml_refmt_options', '') + +function! ale#fixers#refmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'reasonml_refmt_executable') + let l:options = ale#Var(a:buffer, 'reasonml_refmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . ' --in-place' + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/rustfmt.vim b/autoload/ale/fixers/rustfmt.vim new file mode 100644 index 0000000..38882fb --- /dev/null +++ b/autoload/ale/fixers/rustfmt.vim @@ -0,0 +1,15 @@ +" Author: Kelly Fox +" Description: Integration of rustfmt with ALE. + +call ale#Set('rust_rustfmt_executable', 'rustfmt') +call ale#Set('rust_rustfmt_options', '') + +function! ale#fixers#rustfmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'rust_rustfmt_executable') + let l:options = ale#Var(a:buffer, 'rust_rustfmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options), + \} +endfunction diff --git a/autoload/ale/fixers/shfmt.vim b/autoload/ale/fixers/shfmt.vim new file mode 100644 index 0000000..882cf3a --- /dev/null +++ b/autoload/ale/fixers/shfmt.vim @@ -0,0 +1,17 @@ +scriptencoding utf-8 +" Author: Simon Bugert +" Description: Fix sh files with shfmt. + +call ale#Set('sh_shfmt_executable', 'shfmt') +call ale#Set('sh_shfmt_options', '') + +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) + \} + +endfunction diff --git a/autoload/ale/fixers/yapf.vim b/autoload/ale/fixers/yapf.vim index b15e481..ba7453b 100644 --- a/autoload/ale/fixers/yapf.vim +++ b/autoload/ale/fixers/yapf.vim @@ -11,7 +11,7 @@ function! ale#fixers#yapf#Fix(buffer) abort \ ['yapf'], \) - if !ale#python#IsExecutable(l:executable) + if !executable(l:executable) return 0 endif diff --git a/autoload/ale/handlers/alex.vim b/autoload/ale/handlers/alex.vim new file mode 100644 index 0000000..853d313 --- /dev/null +++ b/autoload/ale/handlers/alex.vim @@ -0,0 +1,22 @@ +" Author: Johannes Wienke +" Description: Error handling for errors in alex output format + +function! ale#handlers#alex#Handle(buffer, lines) abort + " Example output: + " 6:256-6:262 warning Be careful with “killed”, it’s profane in some cases killed retext-profanities + let l:pattern = '^ *\(\d\+\):\(\d\+\)-\(\d\+\):\(\d\+\) \+warning \+\(.\{-\}\) \+\(.\{-\}\) \+\(.\{-\}\)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'end_lnum': l:match[3] + 0, + \ 'end_col': l:match[4] - 1, + \ 'text': l:match[5] . ' (' . (l:match[7]) . ')', + \ 'type': 'W', + \}) + endfor + + return l:output +endfunction diff --git a/autoload/ale/handlers/cpplint.vim b/autoload/ale/handlers/cpplint.vim index 4607863..5c475a5 100644 --- a/autoload/ale/handlers/cpplint.vim +++ b/autoload/ale/handlers/cpplint.vim @@ -4,14 +4,15 @@ function! ale#handlers#cpplint#HandleCppLintFormat(buffer, lines) abort " Look for lines like the following. " test.cpp:5: Estra space after ( in function call [whitespace/parents] [4] - let l:pattern = '^.\{-}:\(\d\+\): \(.\+\)' + let l:pattern = '^.\{-}:\(\d\+\): *\(.\+\) *\[\(.*/.*\)\] ' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) call add(l:output, { \ 'lnum': l:match[1] + 0, \ 'col': 0, - \ 'text': l:match[2], + \ 'text': join(split(l:match[2])), + \ 'code': l:match[3], \ 'type': 'W', \}) endfor diff --git a/autoload/ale/handlers/css.vim b/autoload/ale/handlers/css.vim index 4c1b81c..de9eadc 100644 --- a/autoload/ale/handlers/css.vim +++ b/autoload/ale/handlers/css.vim @@ -14,23 +14,22 @@ function! ale#handlers#css#HandleCSSLintFormat(buffer, lines) abort let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) - let l:text = l:match[4] - let l:type = l:match[3] - - let l:group_match = matchlist(l:text, '\v^(.+) \((.+)\)$') - - " Put the error group at the front, so we can see what kind of error - " it is on small echo lines. - if !empty(l:group_match) - let l:text = '(' . l:group_match[2] . ') ' . l:group_match[1] - endif - - call add(l:output, { + let l:item = { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, - \ 'text': l:text, - \ 'type': l:type is# 'Warning' ? 'W' : 'E', - \}) + \ 'type': l:match[3] is# 'Warning' ? 'W' : 'E', + \ 'text': l:match[4], + \} + + let l:code_match = matchlist(l:match[4], '\v(.+) \(([^(]+)\)$') + + " Split up the error code and the text if we find one. + if !empty(l:code_match) + let l:item.text = l:code_match[1] + let l:item.code = l:code_match[2] + endif + + call add(l:output, l:item) endfor return l:output @@ -62,7 +61,8 @@ function! ale#handlers#css#HandleStyleLintFormat(buffer, lines) abort \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, \ 'type': l:match[3] is# '✖' ? 'E' : 'W', - \ 'text': l:match[4] . ' [' . l:match[5] . ']', + \ 'text': l:match[4], + \ 'code': l:match[5], \}) endfor diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim index 3397ab5..ff59016 100644 --- a/autoload/ale/handlers/eslint.vim +++ b/autoload/ale/handlers/eslint.vim @@ -7,6 +7,7 @@ call ale#Set('javascript_eslint_options', '') call ale#Set('javascript_eslint_executable', 'eslint') call ale#Set('javascript_eslint_use_global', 0) call ale#Set('javascript_eslint_suppress_eslintignore', 0) +call ale#Set('javascript_eslint_suppress_missing_config', 0) function! ale#handlers#eslint#FindConfig(buffer) abort for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) @@ -47,7 +48,7 @@ function! ale#handlers#eslint#GetCommand(buffer) abort endfunction let s:col_end_patterns = [ -\ '\vParsing error: Unexpected token (.+) ', +\ '\vParsing error: Unexpected token (.+) ?', \ '\v''(.+)'' is not defined.', \ '\v%(Unexpected|Redundant use of) [''`](.+)[''`]', \ '\vUnexpected (console) statement', @@ -64,7 +65,7 @@ function! s:AddHintsForTypeScriptParsingErrors(output) abort endfor endfunction -function! ale#handlers#eslint#Handle(buffer, lines) abort +function! s:CheckForBadConfig(buffer, lines) abort let l:config_error_pattern = '\v^ESLint couldn''t find a configuration file' \ . '|^Cannot read config file' \ . '|^.*Configuration for rule .* is invalid' @@ -73,15 +74,31 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort " Look for a message in the first few lines which indicates that " a configuration file couldn't be found. for l:line in a:lines[:10] - if len(matchlist(l:line, l:config_error_pattern)) > 0 - return [{ - \ 'lnum': 1, - \ 'text': 'eslint configuration error (type :ALEDetail for more information)', - \ 'detail': join(a:lines, "\n"), - \}] + let l:match = matchlist(l:line, l:config_error_pattern) + + if len(l:match) > 0 + " Don't show the missing config error if we've disabled it. + if ale#Var(a:buffer, 'javascript_eslint_suppress_missing_config') + \&& l:match[0] is# 'ESLint couldn''t find a configuration file' + return 0 + endif + + return 1 endif endfor + return 0 +endfunction + +function! ale#handlers#eslint#Handle(buffer, lines) abort + if s:CheckForBadConfig(a:buffer, a:lines) + return [{ + \ 'lnum': 1, + \ 'text': 'eslint configuration error (type :ALEDetail for more information)', + \ 'detail': join(a:lines, "\n"), + \}] + endif + " Matches patterns line the following: " " /path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle] @@ -94,7 +111,6 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort let l:output = [] for l:match in ale#util#GetMatches(a:lines, [l:pattern, l:parsing_pattern]) - let l:type = 'Error' let l:text = l:match[3] if ale#Var(a:buffer, 'javascript_eslint_suppress_eslintignore') @@ -103,19 +119,25 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort endif endif - " Take the error type from the output if available. - if !empty(l:match[4]) - let l:type = split(l:match[4], '/')[0] - let l:text .= ' [' . l:match[4] . ']' - endif - let l:obj = { \ 'lnum': l:match[1] + 0, \ 'col': l:match[2] + 0, \ 'text': l:text, - \ 'type': l:type is# 'Warning' ? 'W' : 'E', + \ 'type': 'E', \} + " Take the error type from the output if available. + let l:split_code = split(l:match[4], '/') + + if get(l:split_code, 0, '') is# 'Warning' + let l:obj.type = 'W' + endif + + " The code can be something like 'Error/foo/bar', or just 'Error' + if !empty(get(l:split_code, 1)) + let l:obj.code = join(l:split_code[1:], '/') + endif + for l:col_match in ale#util#GetMatches(l:text, s:col_end_patterns) let l:obj.end_col = l:obj.col + len(l:col_match[1]) - 1 endfor diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim index ad5cab3..7f2078a 100644 --- a/autoload/ale/handlers/gcc.vim +++ b/autoload/ale/handlers/gcc.vim @@ -5,17 +5,6 @@ scriptencoding utf-8 let s:pragma_error = '#pragma once in main file' -function! s:AddIncludedErrors(output, include_lnum, include_lines) abort - if a:include_lnum > 0 - call add(a:output, { - \ 'lnum': a:include_lnum, - \ 'type': 'E', - \ 'text': 'Problems were found in the header (See :ALEDetail)', - \ 'detail': join(a:include_lines, "\n"), - \}) - endif -endfunction - function! s:IsHeaderFile(filename) abort return a:filename =~? '\v\.(h|hpp)$' endfunction @@ -29,93 +18,52 @@ function! s:RemoveUnicodeQuotes(text) abort return l:text endfunction -function! ale#handlers#gcc#ParseGCCVersion(lines) abort - for l:line in a:lines - let l:match = matchstr(l:line, '\d\.\d\.\d') - - if !empty(l:match) - return ale#semver#Parse(l:match) - endif - endfor - - return [] -endfunction - function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort - let l:include_pattern = '\v^(In file included | *)from ([^:]*):(\d+)' - let l:include_lnum = 0 - let l:include_lines = [] - let l:included_filename = '' " Look for lines like the following. " " :8:5: warning: conversion lacks type at end of format [-Wformat=] " :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] - let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): ?(.+)$' let l:output = [] - for l:line in a:lines - let l:match = matchlist(l:line, l:pattern) - - if empty(l:match) - " Check for matches in includes. - " We will keep matching lines until we hit the last file, which - " is our file. - let l:include_match = matchlist(l:line, l:include_pattern) - - if empty(l:include_match) - " If this isn't another include header line, then we - " need to collect it. - call add(l:include_lines, l:line) - else - " GCC and clang return the lists of files in different orders, - " so we'll only grab the line number from lines which aren't - " header files. - if !s:IsHeaderFile(l:include_match[2]) - " Get the line number out of the parsed include line, - " and reset the other variables. - let l:include_lnum = str2nr(l:include_match[3]) - endif - - let l:include_lines = [] - let l:included_filename = '' - endif - elseif l:include_lnum > 0 - \&& (empty(l:included_filename) || l:included_filename is# l:match[1]) - " If we hit the first error after an include header, or the - " errors below have the same name as the first filename we see, - " then include these lines, and remember what that filename was. - let l:included_filename = l:match[1] - call add(l:include_lines, l:line) - else - " If we hit a regular error again, then add the previously - " collected lines as one error, and reset the include variables. - call s:AddIncludedErrors(l:output, l:include_lnum, l:include_lines) - let l:include_lnum = 0 - let l:include_lines = [] - let l:included_filename = '' - - if s:IsHeaderFile(bufname(bufnr(''))) - \&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error - continue - endif - - let l:item = { - \ 'lnum': str2nr(l:match[2]), - \ 'type': l:match[4] =~# 'error' ? 'E' : 'W', - \ 'text': s:RemoveUnicodeQuotes(l:match[5]), - \} - - if !empty(l:match[3]) - let l:item.col = str2nr(l:match[3]) - endif - - call add(l:output, l:item) + for l:match in ale#util#GetMatches(a:lines, l:pattern) + " Filter out the pragma errors + if s:IsHeaderFile(bufname(bufnr(''))) + \&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error + continue endif - endfor - " Add remaining include errors after we go beyond the last line. - call s:AddIncludedErrors(l:output, l:include_lnum, l:include_lines) + " If the 'error type' is a note, make it detail related to + " the previous error parsed in output + if l:match[4] is# 'note' + if !empty(l:output) + let l:output[-1]['detail'] = + \ get(l:output[-1], 'detail', '') + \ . s:RemoveUnicodeQuotes(l:match[0]) . "\n" + endif + + continue + endif + + let l:item = { + \ 'lnum': str2nr(l:match[2]), + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \ 'text': s:RemoveUnicodeQuotes(l:match[5]), + \} + + if !empty(l:match[3]) + let l:item.col = str2nr(l:match[3]) + endif + + " If the filename is something like , or -, then + " this is an error for the file we checked. + if l:match[1] isnot# '-' && l:match[1][0] isnot# '<' + let l:item['filename'] = l:match[1] + endif + + call add(l:output, l:item) + endfor return l:output endfunction diff --git a/autoload/ale/handlers/haskell.vim b/autoload/ale/handlers/haskell.vim index 9c8d058..8a0d001 100644 --- a/autoload/ale/handlers/haskell.vim +++ b/autoload/ale/handlers/haskell.vim @@ -19,25 +19,30 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort " in Haskell error messages with the basename for this file. let l:temp_filename_regex = s:temp_regex_prefix . l:basename - let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+):(.*)?$' + let l:pattern = '\v^\s*([a-zA-Z]?:?[^:]+):(\d+):(\d+):(.*)?$' let l:output = [] let l:corrected_lines = [] + " Group the lines into smaller lists. for l:line in a:lines if len(matchlist(l:line, l:pattern)) > 0 - call add(l:corrected_lines, l:line) + call add(l:corrected_lines, [l:line]) elseif l:line is# '' - call add(l:corrected_lines, l:line) - else - if len(l:corrected_lines) > 0 - let l:line = substitute(l:line, '\v^\s+', ' ', '') - let l:corrected_lines[-1] .= l:line - endif + call add(l:corrected_lines, [l:line]) + elseif len(l:corrected_lines) > 0 + call add(l:corrected_lines[-1], l:line) endif endfor - for l:line in l:corrected_lines + for l:line_list in l:corrected_lines + " Join the smaller lists into one large line to parse. + let l:line = l:line_list[0] + + for l:extra_line in l:line_list[1:] + let l:line .= substitute(l:extra_line, '\v^\s+', ' ', '') + endfor + let l:match = matchlist(l:line, l:pattern) if len(l:match) == 0 @@ -67,12 +72,19 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort " Replace temporary filenames in problem messages with the basename let l:text = substitute(l:text, l:temp_filename_regex, l:basename, 'g') - call add(l:output, { + let l:item = { \ 'lnum': l:match[2] + 0, \ 'col': l:match[3] + 0, \ 'text': l:text, \ 'type': l:type, - \}) + \} + + " Include extra lines as details if they are there. + if len(l:line_list) > 1 + let l:item.detail = join(l:line_list[1:], "\n") + endif + + call add(l:output, l:item) endfor return l:output diff --git a/autoload/ale/handlers/ols.vim b/autoload/ale/handlers/ols.vim new file mode 100644 index 0000000..1dda7f9 --- /dev/null +++ b/autoload/ale/handlers/ols.vim @@ -0,0 +1,25 @@ +" Author: Michael Jungo +" Description: Handlers for the OCaml language server + +function! ale#handlers#ols#GetExecutable(buffer) abort + let l:ols_setting = ale#handlers#ols#GetLanguage(a:buffer) . '_ols' + return ale#node#FindExecutable(a:buffer, l:ols_setting, [ + \ 'node_modules/.bin/ocaml-language-server', + \]) +endfunction + +function! ale#handlers#ols#GetCommand(buffer) abort + let l:executable = ale#handlers#ols#GetExecutable(a:buffer) + + return ale#node#Executable(a:buffer, l:executable) . ' --stdio' +endfunction + +function! ale#handlers#ols#GetLanguage(buffer) abort + return getbufvar(a:buffer, '&filetype') +endfunction + +function! ale#handlers#ols#GetProjectRoot(buffer) abort + let l:merlin_file = ale#path#FindNearestFile(a:buffer, '.merlin') + + return !empty(l:merlin_file) ? fnamemodify(l:merlin_file, ':h') : '' +endfunction diff --git a/autoload/ale/handlers/pony.vim b/autoload/ale/handlers/pony.vim new file mode 100644 index 0000000..0ac18e7 --- /dev/null +++ b/autoload/ale/handlers/pony.vim @@ -0,0 +1,34 @@ +scriptencoding utf-8 +" Description: This file defines a handler function which ought to work for +" any program which outputs errors in the format that ponyc uses. + +function! s:RemoveUnicodeQuotes(text) abort + let l:text = a:text + let l:text = substitute(l:text, '[`´‘’]', '''', 'g') + let l:text = substitute(l:text, '\v\\u2018([^\\]+)\\u2019', '''\1''', 'g') + let l:text = substitute(l:text, '[“”]', '"', 'g') + + return l:text +endfunction + +function! ale#handlers#pony#HandlePonycFormat(buffer, lines) abort + " Look for lines like the following. + " /home/code/pony/classes/Wombat.pony:22:30: can't lookup private fields from outside the type + + let l:pattern = '\v^([^:]+):(\d+):(\d+)?:? (.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:item = { + \ 'filename': l:match[1], + \ 'lnum': str2nr(l:match[2]), + \ 'col': str2nr(l:match[3]), + \ 'type': 'E', + \ 'text': s:RemoveUnicodeQuotes(l:match[4]), + \} + + call add(l:output, l:item) + endfor + + return l:output +endfunction diff --git a/autoload/ale/handlers/redpen.vim b/autoload/ale/handlers/redpen.vim new file mode 100644 index 0000000..2fb0568 --- /dev/null +++ b/autoload/ale/handlers/redpen.vim @@ -0,0 +1,32 @@ +" Author: rhysd https://rhysd.github.io +" Description: Redpen, a proofreading tool (http://redpen.cc) + +function! ale#handlers#redpen#HandleRedpenOutput(buffer, lines) abort + " Only one file was passed to redpen. So response array has only one + " element. + let l:res = json_decode(join(a:lines))[0] + let l:output = [] + for l:err in l:res.errors + let l:item = { + \ 'text': l:err.message, + \ 'type': 'W', + \ 'code': l:err.validator, + \} + if has_key(l:err, 'startPosition') + let l:item.lnum = l:err.startPosition.lineNum + let l:item.col = l:err.startPosition.offset + 1 + if has_key(l:err, 'endPosition') + let l:item.end_lnum = l:err.endPosition.lineNum + let l:item.end_col = l:err.endPosition.offset + endif + else + " Fallback to a whole sentence region when a region is not + " specified by the error. + let l:item.lnum = l:err.lineNum + let l:item.col = l:err.sentenceStartColumnNum + 1 + endif + call add(l:output, l:item) + endfor + return l:output +endfunction + diff --git a/autoload/ale/handlers/rust.vim b/autoload/ale/handlers/rust.vim index 395b915..537bc73 100644 --- a/autoload/ale/handlers/rust.vim +++ b/autoload/ale/handlers/rust.vim @@ -51,8 +51,8 @@ function! ale#handlers#rust#HandleRustErrors(buffer, lines) abort call add(l:output, { \ 'lnum': l:span.line_start, \ 'end_lnum': l:span.line_end, - \ 'col': l:span.byte_start, - \ 'end_col': l:span.byte_end, + \ 'col': l:span.column_start, + \ 'end_col': l:span.column_end, \ 'text': empty(l:span.label) ? l:error.message : printf('%s: %s', l:error.message, l:span.label), \ 'type': toupper(l:error.level[0]), \}) diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim index 894879e..e96dd3c 100644 --- a/autoload/ale/handlers/sh.vim +++ b/autoload/ale/handlers/sh.vim @@ -9,7 +9,7 @@ function! ale#handlers#sh#GetShellType(buffer) abort " Remove options like -e, etc. let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g') - for l:possible_shell in ['bash', 'tcsh', 'csh', 'zsh', 'sh'] + for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'sh'] if l:command =~# l:possible_shell . '\s*$' return l:possible_shell endif diff --git a/autoload/ale/handlers/vale.vim b/autoload/ale/handlers/vale.vim new file mode 100644 index 0000000..9dc0872 --- /dev/null +++ b/autoload/ale/handlers/vale.vim @@ -0,0 +1,38 @@ +" Author: Johannes Wienke +" Description: output handler for the vale JSON format + +function! ale#handlers#vale#GetType(severity) abort + if a:severity is? 'warning' + return 'W' + elseif a:severity is? 'suggestion' + return 'I' + endif + + return 'E' +endfunction + +function! ale#handlers#vale#Handle(buffer, lines) abort + try + let l:errors = json_decode(join(a:lines, '')) + catch + return [] + endtry + + if empty(l:errors) + return [] + endif + + let l:output = [] + for l:error in l:errors[keys(l:errors)[0]] + call add(l:output, { + \ 'lnum': l:error['Line'], + \ 'col': l:error['Span'][0], + \ 'end_col': l:error['Span'][1], + \ 'code': l:error['Check'], + \ 'text': l:error['Message'], + \ 'type': ale#handlers#vale#GetType(l:error['Severity']), + \}) + endfor + + return l:output +endfunction diff --git a/autoload/ale/handlers/writegood.vim b/autoload/ale/handlers/writegood.vim new file mode 100644 index 0000000..f9d452e --- /dev/null +++ b/autoload/ale/handlers/writegood.vim @@ -0,0 +1,61 @@ +" Author: Sumner Evans +" Description: Error handling for errors in the write-good format. + +function! ale#handlers#writegood#ResetOptions() abort + call ale#Set('writegood_options', '') + call ale#Set('writegood_executable', 'write-good') + call ale#Set('writegood_use_global', 0) +endfunction + +" Reset the options so the tests can test how they are set. +call ale#handlers#writegood#ResetOptions() + +function! ale#handlers#writegood#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'writegood', [ + \ 'node_modules/.bin/write-good', + \ 'node_modules/write-good/bin/write-good.js', + \]) +endfunction + +function! ale#handlers#writegood#GetCommand(buffer) abort + let l:executable = ale#handlers#writegood#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'writegood_options') + + return ale#node#Executable(a:buffer, l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' %t' +endfunction + +function! ale#handlers#writegood#Handle(buffer, lines) abort + " Look for lines like the following. + " + " "it is" is wordy or unneeded on line 20 at column 53 + " "easily" can weaken meaning on line 154 at column 29 + let l:marks_pattern = '\v^ *(\^+) *$' + let l:pattern = '\v^(".*"\s.*)\son\sline\s(\d+)\sat\scolumn\s(\d+)$' + let l:output = [] + let l:last_len = 0 + + for l:match in ale#util#GetMatches(a:lines, [l:marks_pattern, l:pattern]) + if empty(l:match[2]) + let l:last_len = len(l:match[1]) + else + let l:col = l:match[3] + 1 + + " Add the linter error. Note that we need to add 1 to the col because + " write-good reports the column corresponding to the space before the + " offending word or phrase. + call add(l:output, { + \ 'text': l:match[1], + \ 'lnum': l:match[2] + 0, + \ 'col': l:col, + \ 'end_col': l:last_len ? (l:col + l:last_len - 1) : l:col, + \ 'type': 'W', + \}) + + let l:last_len = 0 + endif + endfor + + return l:output +endfunction diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim index 5c01e7a..ae1f3e7 100644 --- a/autoload/ale/highlight.vim +++ b/autoload/ale/highlight.vim @@ -58,7 +58,7 @@ function! ale#highlight#RemoveHighlights() abort endfunction function! ale#highlight#UpdateHighlights() abort - let l:item_list = g:ale_enabled + let l:item_list = get(b:, 'ale_enabled', 1) && g:ale_enabled \ ? get(b:, 'ale_highlight_items', []) \ : [] @@ -91,6 +91,30 @@ function! ale#highlight#UpdateHighlights() abort \ 'matchaddpos(l:group, v:val)' \) endfor + + " If highlights are enabled and signs are not enabled, we should still + " offer line highlights by adding a separate set of highlights. + if !g:ale_set_signs + let l:available_groups = { + \ 'ALEWarningLine': hlexists('ALEWarningLine'), + \ 'ALEInfoLine': hlexists('ALEInfoLine'), + \ 'ALEErrorLine': hlexists('ALEErrorLine'), + \} + + for l:item in l:item_list + if l:item.type is# 'W' + let l:group = 'ALEWarningLine' + elseif l:item.type is# 'I' + let l:group = 'ALEInfoLine' + else + let l:group = 'ALEErrorLine' + endif + + if l:available_groups[l:group] + call matchaddpos(l:group, [l:item.lnum]) + endif + endfor + endif endfunction function! ale#highlight#BufferHidden(buffer) abort @@ -106,7 +130,7 @@ augroup ALEHighlightBufferGroup augroup END function! ale#highlight#SetHighlights(buffer, loclist) abort - let l:new_list = g:ale_enabled + let l:new_list = getbufvar(a:buffer, 'ale_enabled', 1) && g:ale_enabled \ ? filter(copy(a:loclist), 'v:val.bufnr == a:buffer && v:val.col > 0') \ : [] diff --git a/autoload/ale/job.vim b/autoload/ale/job.vim index 254f4ee..2909dab 100644 --- a/autoload/ale/job.vim +++ b/autoload/ale/job.vim @@ -25,24 +25,23 @@ endfunction " Note that jobs and IDs are the same thing on NeoVim. function! ale#job#JoinNeovimOutput(job, last_line, data, mode, callback) abort + if a:mode is# 'raw' + call a:callback(a:job, join(a:data, "\n")) + return '' + endif + let l:lines = a:data[:-2] if len(a:data) > 1 let l:lines[0] = a:last_line . l:lines[0] let l:new_last_line = a:data[-1] else - let l:new_last_line = a:last_line . a:data[0] + let l:new_last_line = a:last_line . get(a:data, 0, '') endif - if a:mode is# 'raw' - if !empty(l:lines) - call a:callback(a:job, join(l:lines, "\n") . "\n") - endif - else - for l:line in l:lines - call a:callback(a:job, l:line) - endfor - endif + for l:line in l:lines + call a:callback(a:job, l:line) + endfor return l:new_last_line endfunction @@ -166,23 +165,54 @@ function! ale#job#ValidateArguments(command, options) abort endif endfunction -function! ale#job#PrepareCommand(command) abort +function! s:PrepareWrappedCommand(original_wrapper, command) abort + let l:match = matchlist(a:command, '\v^(.*(\&\&|;)) *(.*)$') + let l:prefix = '' + let l:command = a:command + + if !empty(l:match) + let l:prefix = l:match[1] . ' ' + let l:command = l:match[3] + endif + + let l:format = a:original_wrapper + + if l:format =~# '%@' + let l:wrapped = substitute(l:format, '%@', ale#Escape(l:command), '') + else + if l:format !~# '%\*' + let l:format .= ' %*' + endif + + let l:wrapped = substitute(l:format, '%\*', l:command, '') + endif + + return l:prefix . l:wrapped +endfunction + +function! ale#job#PrepareCommand(buffer, command) abort + let l:wrapper = ale#Var(a:buffer, 'command_wrapper') + + let l:command = !empty(l:wrapper) + \ ? s:PrepareWrappedCommand(l:wrapper, a:command) + \ : a:command + " The command will be executed in a subshell. This fixes a number of " issues, including reading the PATH variables correctly, %PATHEXT% " expansion on Windows, etc. " " NeoVim handles this issue automatically if the command is a String, - " but we'll do this explicitly, so we use thes same exact command for both + " but we'll do this explicitly, so we use the same exact command for both " versions. - if ale#Has('win32') - return 'cmd /c ' . a:command + if has('win32') + return 'cmd /s/c "' . l:command . '"' endif if &shell =~? 'fish$' - return ['/bin/sh', '-c', a:command] + return ['/bin/sh', '-c', l:command] endif - return split(&shell) + split(&shellcmdflag) + [a:command] + return split(&shell) + split(&shellcmdflag) + [l:command] endfunction " Start a job with options which are agnostic to Vim and NeoVim. @@ -290,7 +320,7 @@ function! ale#job#Stop(job_id) abort " FIXME: NeoVim kills jobs on a timer, but will not kill any processes " which are child processes on Unix. Some work needs to be done to " kill child processes to stop long-running processes like pylint. - call jobstop(a:job_id) + silent! call jobstop(a:job_id) else let l:job = s:job_map[a:job_id].job diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 269b092..d059a12 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -1,3 +1,4 @@ +call ale#Set('wrap_command_as_one_argument', 0) " Author: w0rp " Description: Linter registration and lazy-loading " Retrieves linters as requested by the engine, loading them if needed. @@ -21,10 +22,13 @@ let s:default_ale_linter_aliases = { " " Only cargo is enabled for Rust by default. " rpmlint is disabled by default because it can result in code execution. +" +" NOTE: Update the g:ale_linters documentation when modifying this. let s:default_ale_linters = { \ 'csh': ['shell'], \ 'go': ['gofmt', 'golint', 'go vet'], \ 'help': [], +\ 'perl': ['perlcritic'], \ 'python': ['flake8', 'mypy', 'pylint'], \ 'rust': ['cargo'], \ 'spec': [], @@ -261,12 +265,19 @@ function! ale#linter#GetAll(filetypes) abort endfunction function! s:GetAliasedFiletype(original_filetype) abort + let l:buffer_aliases = get(b:, 'ale_linter_aliases', {}) + + " b:ale_linter_aliases can be set to a List. + if type(l:buffer_aliases) is type([]) + return l:buffer_aliases + endif + " Check for aliased filetypes first in a buffer variable, " then the global variable, " then in the default mapping, " otherwise use the original filetype. for l:dict in [ - \ get(b:, 'ale_linter_aliases', {}), + \ l:buffer_aliases, \ g:ale_linter_aliases, \ s:default_ale_linter_aliases, \] @@ -289,15 +300,38 @@ function! ale#linter#ResolveFiletype(original_filetype) abort endfunction function! s:GetLinterNames(original_filetype) abort - for l:dict in [ - \ get(b:, 'ale_linters', {}), - \ g:ale_linters, - \ s:default_ale_linters, - \] - if has_key(l:dict, a:original_filetype) - return l:dict[a:original_filetype] - endif - endfor + let l:buffer_ale_linters = get(b:, 'ale_linters', {}) + + " b:ale_linters can be set to 'all' + if l:buffer_ale_linters is# 'all' + return 'all' + endif + + " b:ale_linters can be set to a List. + if type(l:buffer_ale_linters) is type([]) + return l:buffer_ale_linters + endif + + " Try to get a buffer-local setting for the filetype + if has_key(l:buffer_ale_linters, a:original_filetype) + return l:buffer_ale_linters[a:original_filetype] + endif + + " Try to get a global setting for the filetype + if has_key(g:ale_linters, a:original_filetype) + return g:ale_linters[a:original_filetype] + endif + + " If the user has configured ALE to only enable linters explicitly, then + " don't enable any linters by default. + if g:ale_linters_explicit + return [] + endif + + " Try to get a default setting for the filetype + if has_key(s:default_ale_linters, a:original_filetype) + return s:default_ale_linters[a:original_filetype] + endif return 'all' endfunction @@ -399,6 +433,7 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort endif let l:command = ale#job#PrepareCommand( + \ a:buffer, \ ale#linter#GetCommand(a:buffer, a:linter), \) let l:conn_id = ale#lsp#StartProgram( diff --git a/autoload/ale/list.vim b/autoload/ale/list.vim index ecf088a..30b8f5c 100644 --- a/autoload/ale/list.vim +++ b/autoload/ale/list.vim @@ -37,17 +37,18 @@ function! ale#list#GetCombinedList() abort return l:list endfunction -function! s:FixList(list) abort +function! s:FixList(buffer, list) abort + let l:format = ale#Var(a:buffer, 'loclist_msg_format') let l:new_list = [] for l:item in a:list + let l:fixed_item = copy(l:item) + + let l:fixed_item.text = ale#GetLocItemMessage(l:item, l:format) + if l:item.bufnr == -1 " If the buffer number is invalid, remove it. - let l:fixed_item = copy(l:item) call remove(l:fixed_item, 'bufnr') - else - " Don't copy the Dictionary if we do not need to. - let l:fixed_item = l:item endif call add(l:new_list, l:fixed_item) @@ -67,22 +68,22 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort let l:quickfix_list = ale#list#GetCombinedList() if has('nvim') - call setqflist(s:FixList(l:quickfix_list), ' ', l:title) + call setqflist(s:FixList(a:buffer, l:quickfix_list), ' ', l:title) else - call setqflist(s:FixList(l:quickfix_list)) + call setqflist(s:FixList(a:buffer, l:quickfix_list)) call setqflist([], 'r', {'title': l:title}) endif elseif g:ale_set_loclist " If windows support is off, bufwinid() may not exist. " We'll set result in the current window, which might not be correct, - " but is better than nothing. - let l:win_id = s:BufWinId(a:buffer) + " but it's better than nothing. + let l:id = s:BufWinId(a:buffer) if has('nvim') - call setloclist(l:win_id, s:FixList(a:loclist), ' ', l:title) + call setloclist(l:id, s:FixList(a:buffer, a:loclist), ' ', l:title) else - call setloclist(l:win_id, s:FixList(a:loclist)) - call setloclist(l:win_id, [], 'r', {'title': l:title}) + call setloclist(l:id, s:FixList(a:buffer, a:loclist)) + call setloclist(l:id, [], 'r', {'title': l:title}) endif endif @@ -96,12 +97,17 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort let l:reset_visual_selection = l:mode is? 'v' || l:mode is# "\" let l:reset_character_selection = l:mode is? 's' || l:mode is# "\" + " open windows vertically instead of default horizontally + let l:open_type = '' + if ale#Var(a:buffer, 'list_vertical') == 1 + let l:open_type = 'vert ' + endif if g:ale_set_quickfix if !ale#list#IsQuickfixOpen() - silent! execute 'copen ' . str2nr(ale#Var(a:buffer, 'list_window_size')) + silent! execute l:open_type . 'copen ' . str2nr(ale#Var(a:buffer, 'list_window_size')) endif elseif g:ale_set_loclist - silent! execute 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size')) + silent! execute l:open_type . 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size')) endif " If focus changed, restore it (jump to the last window). diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index b6c890c..8db9348 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -83,9 +83,8 @@ function! ale#lsp#CreateMessageData(message) abort let l:is_notification = a:message[0] let l:obj = { - \ 'id': v:null, - \ 'jsonrpc': '2.0', \ 'method': a:message[1], + \ 'jsonrpc': '2.0', \} if !l:is_notification @@ -326,6 +325,20 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback) abort return 1 endfunction +" Stop all LSP connections, closing all jobs and channels, and removing any +" queued messages. +function! ale#lsp#StopAll() abort + for l:conn in s:connections + if has_key(l:conn, 'channel') + call ch_close(l:conn.channel) + else + call ale#job#Stop(l:conn.id) + endif + endfor + + let s:connections = [] +endfunction + function! s:SendMessageData(conn, data) abort if has_key(a:conn, 'executable') call ale#job#SendRaw(a:conn.id, a:data) diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim index 7910247..0b73cfc 100644 --- a/autoload/ale/lsp/message.vim +++ b/autoload/ale/lsp/message.vim @@ -53,7 +53,7 @@ function! ale#lsp#message#DidOpen(buffer, language_id) abort \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')), \ 'languageId': a:language_id, \ 'version': ale#lsp#message#GetNextVersionID(), - \ 'text': join(l:lines, "\n"), + \ 'text': join(l:lines, "\n") . "\n", \ }, \}] endfunction @@ -67,7 +67,7 @@ function! ale#lsp#message#DidChange(buffer) abort \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')), \ 'version': ale#lsp#message#GetNextVersionID(), \ }, - \ 'contentChanges': [{'text': join(l:lines, "\n")}] + \ 'contentChanges': [{'text': join(l:lines, "\n") . "\n"}] \}] endfunction @@ -86,3 +86,33 @@ function! ale#lsp#message#DidClose(buffer) abort \ }, \}] endfunction + +let s:COMPLETION_TRIGGER_INVOKED = 1 +let s:COMPLETION_TRIGGER_CHARACTER = 2 + +function! ale#lsp#message#Completion(buffer, line, column, trigger_character) abort + let l:message = [0, 'textDocument/completion', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')), + \ }, + \ 'position': {'line': a:line - 1, 'character': a:column}, + \}] + + if !empty(a:trigger_character) + let l:message[2].context = { + \ 'triggerKind': s:COMPLETION_TRIGGER_CHARACTER, + \ 'triggerCharacter': a:trigger_character, + \} + endif + + return l:message +endfunction + +function! ale#lsp#message#Definition(buffer, line, column) abort + return [0, 'textDocument/definition', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')), + \ }, + \ 'position': {'line': a:line - 1, 'character': a:column}, + \}] +endfunction diff --git a/autoload/ale/lsp/reset.vim b/autoload/ale/lsp/reset.vim new file mode 100644 index 0000000..c206ed0 --- /dev/null +++ b/autoload/ale/lsp/reset.vim @@ -0,0 +1,25 @@ +" Stop all LSPs and remove all of the data for them. +function! ale#lsp#reset#StopAllLSPs() abort + call ale#lsp#StopAll() + + if exists('*ale#definition#ClearLSPData') + " Clear the mapping for connections, etc. + call ale#definition#ClearLSPData() + endif + + if exists('*ale#engine#ClearLSPData') + " Clear the mapping for connections, etc. + call ale#engine#ClearLSPData() + + " Remove the problems for all of the LSP linters in every buffer. + for l:buffer_string in keys(g:ale_buffer_info) + let l:buffer = str2nr(l:buffer_string) + + for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype')) + if !empty(l:linter.lsp) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, []) + endif + endfor + endfor + endif +endfunction diff --git a/autoload/ale/lsp/response.vim b/autoload/ale/lsp/response.vim index 13219ef..5a43128 100644 --- a/autoload/ale/lsp/response.vim +++ b/autoload/ale/lsp/response.vim @@ -59,6 +59,14 @@ function! ale#lsp#response#ReadTSServerDiagnostics(response) abort let l:loclist_item.nr = l:diagnostic.code endif + if get(l:diagnostic, 'category') is# 'warning' + let l:loclist_item.type = 'W' + endif + + if get(l:diagnostic, 'category') is# 'suggestion' + let l:loclist_item.type = 'I' + endif + call add(l:loclist, l:loclist_item) endfor diff --git a/autoload/ale/lsp/tsserver_message.vim b/autoload/ale/lsp/tsserver_message.vim index ab18d74..b9bd7a0 100644 --- a/autoload/ale/lsp/tsserver_message.vim +++ b/autoload/ale/lsp/tsserver_message.vim @@ -28,7 +28,7 @@ function! ale#lsp#tsserver_message#Change(buffer) abort \ 'offset': 1, \ 'endLine': 1073741824, \ 'endOffset': 1, - \ 'insertString': join(l:lines, "\n"), + \ 'insertString': join(l:lines, "\n") . "\n", \}] endfunction @@ -53,3 +53,11 @@ function! ale#lsp#tsserver_message#CompletionEntryDetails(buffer, line, column, \ 'entryNames': a:entry_names, \}] endfunction + +function! ale#lsp#tsserver_message#Definition(buffer, line, column) abort + return [0, 'ts@definition', { + \ 'line': a:line, + \ 'offset': a:column, + \ 'file': expand('#' . a:buffer . ':p'), + \}] +endfunction diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 83f6e85..16dabf2 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -1,36 +1,27 @@ " Author: w0rp " Description: Functions for working with paths in the filesystem. -function! ale#path#Simplify(path) abort - " //foo is turned into /foo to stop Windows doing stupid things with - " search paths. - return substitute(simplify(a:path), '^//\+', '/', 'g') " no-custom-checks -endfunction - -" This function is mainly used for testing. -" Simplify() a path, and change forward slashes to back slashes on Windows. +" simplify a path, and fix annoying issues with paths on Windows. " -" If an additional 'add_drive' argument is given, the current drive letter -" will be prefixed to any absolute paths on Windows. -function! ale#path#Winify(path, ...) abort - let l:new_path = ale#path#Simplify(a:path) - - if has('win32') - let l:new_path = substitute(l:new_path, '/', '\\', 'g') - - " Add a drive letter to \foo\bar paths, if needed. - if a:0 && a:1 is# 'add_drive' && l:new_path[:0] is# '\' - let l:new_path = fnamemodify('.', ':p')[:1] . l:new_path - endif +" Forward slashes are changed to back slashes so path equality works better. +" +" Paths starting with more than one forward slash are changed to only one +" forward slash, to prevent the paths being treated as special MSYS paths. +function! ale#path#Simplify(path) abort + if has('unix') + return substitute(simplify(a:path), '^//\+', '/', 'g') " no-custom-checks endif - return l:new_path + let l:win_path = substitute(a:path, '/', '\\', 'g') + + return substitute(simplify(l:win_path), '^\\\+', '\', 'g') " no-custom-checks endfunction " Given a buffer and a filename, find the nearest file by searching upwards " through the paths relative to the given buffer. function! ale#path#FindNearestFile(buffer, filename) abort let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p') + let l:buffer_filename = fnameescape(l:buffer_filename) let l:relative_path = findfile(a:filename, l:buffer_filename . ';') @@ -45,6 +36,7 @@ endfunction " through the paths relative to the given buffer. function! ale#path#FindNearestDirectory(buffer, directory_name) abort let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p') + let l:buffer_filename = fnameescape(l:buffer_filename) let l:relative_path = finddir(a:directory_name, l:buffer_filename . ';') @@ -84,6 +76,10 @@ endfunction " Return 1 if a path is an absolute path. function! ale#path#IsAbsolute(filename) abort + if has('win32') && a:filename[:0] is# '\' + return 1 + endif + " Check for /foo and C:\foo, etc. return a:filename[:0] is# '/' || a:filename[1:2] is# ':\' endfunction @@ -101,7 +97,7 @@ endfunction " directory, return the absolute path to the file. function! ale#path#GetAbsPath(base_directory, filename) abort if ale#path#IsAbsolute(a:filename) - return a:filename + return ale#path#Simplify(a:filename) endif let l:sep = has('win32') ? '\' : '/' @@ -143,8 +139,8 @@ endfunction " Given a path, return every component of the path, moving upwards. function! ale#path#Upwards(path) abort - let l:pattern = ale#Has('win32') ? '\v/+|\\+' : '\v/+' - let l:sep = ale#Has('win32') ? '\' : '/' + let l:pattern = has('win32') ? '\v/+|\\+' : '\v/+' + let l:sep = has('win32') ? '\' : '/' let l:parts = split(ale#path#Simplify(a:path), l:pattern) let l:path_list = [] @@ -153,7 +149,7 @@ function! ale#path#Upwards(path) abort let l:parts = l:parts[:-2] endwhile - if ale#Has('win32') && a:path =~# '^[a-zA-z]:\' + if has('win32') && a:path =~# '^[a-zA-z]:\' " Add \ to C: for C:\, etc. let l:path_list[-1] .= '\' elseif a:path[0] is# '/' @@ -185,5 +181,12 @@ function! ale#path#FromURI(uri) abort let l:i = len('file://') let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri - return ale#uri#Decode(l:encoded_path) + let l:path = ale#uri#Decode(l:encoded_path) + + " If the path is like /C:/foo/bar, it should be C:\foo\bar instead. + if l:path =~# '^/[a-zA-Z]:' + let l:path = substitute(l:path[1:], '/', '\\', 'g') + endif + + return l:path endfunction diff --git a/autoload/ale/pattern_options.vim b/autoload/ale/pattern_options.vim index a603c98..e58b8cf 100644 --- a/autoload/ale/pattern_options.vim +++ b/autoload/ale/pattern_options.vim @@ -1,22 +1,44 @@ " Author: w0rp " Description: Set options in files based on regex patterns. -function! ale#pattern_options#SetOptions() abort - let l:filename = expand('%:p') " no-custom-checks - let l:options = {} +" These variables are used to cache the sorting of patterns below. +let s:last_pattern_options = {} +let s:sorted_items = [] - for l:pattern in keys(g:ale_pattern_options) +function! s:CmpPatterns(left_item, right_item) abort + if a:left_item[0] < a:right_item[0] + return -1 + endif + + if a:left_item[0] > a:right_item[0] + return 1 + endif + + return 0 +endfunction + +function! ale#pattern_options#SetOptions(buffer) abort + if !g:ale_pattern_options_enabled || empty(g:ale_pattern_options) + return + endif + + " The items will only be sorted whenever the patterns change. + if g:ale_pattern_options != s:last_pattern_options + let s:last_pattern_options = deepcopy(g:ale_pattern_options) + " The patterns are sorted, so they are applied consistently. + let s:sorted_items = sort( + \ items(g:ale_pattern_options), + \ function('s:CmpPatterns') + \) + endif + + let l:filename = expand('#' . a:buffer . ':p') + + for [l:pattern, l:options] in s:sorted_items if match(l:filename, l:pattern) >= 0 - let l:options = g:ale_pattern_options[l:pattern] - break - endif - endfor - - for l:key in keys(l:options) - if l:key[:0] is# '&' - call setbufvar(bufnr(''), l:key, l:options[l:key]) - else - let b:[l:key] = l:options[l:key] + for [l:key, l:value] in items(l:options) + call setbufvar(a:buffer, l:key, l:value) + endfor endif endfor endfunction diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim new file mode 100644 index 0000000..3b1c16a --- /dev/null +++ b/autoload/ale/preview.vim @@ -0,0 +1,18 @@ +" Author: w0rp +" Description: Preview windows for showing whatever information in. + +" Open a preview window and show some lines in it. +function! ale#preview#Show(lines) abort + silent pedit ALEPreviewWindow + wincmd P + setlocal modifiable + setlocal noreadonly + setlocal nobuflisted + setlocal filetype=ale-preview + setlocal buftype=nofile + setlocal bufhidden=wipe + :%d + call setline(1, a:lines) + setlocal nomodifiable + setlocal readonly +endfunction diff --git a/autoload/ale/python.vim b/autoload/ale/python.vim index d788b77..82dd9d7 100644 --- a/autoload/ale/python.vim +++ b/autoload/ale/python.vim @@ -10,6 +10,7 @@ let g:ale_virtualenv_dir_names = get(g:, 'ale_virtualenv_dir_names', [ \ 've-py3', \ 've', \ 'virtualenv', +\ 'venv', \]) function! ale#python#FindProjectRootIni(buffer) abort @@ -18,6 +19,9 @@ function! ale#python#FindProjectRootIni(buffer) abort \|| filereadable(l:path . '/setup.cfg') \|| filereadable(l:path . '/pytest.ini') \|| filereadable(l:path . '/tox.ini') + \|| filereadable(l:path . '/mypy.ini') + \|| filereadable(l:path . '/pycodestyle.cfg') + \|| filereadable(l:path . '/flake8.cfg') return l:path endif endfor @@ -29,7 +33,7 @@ endfunction " The root directory is defined as the first directory found while searching " upwards through paths, including the current directory, until a path " containing an init file (one from MANIFEST.in, setup.cfg, pytest.ini, -" tox.ini) is found. If it is not possible to find the project root directorty +" tox.ini) is found. If it is not possible to find the project root directory " via init file, then it will be defined as the first directory found " searching upwards through paths, including the current directory, until no " __init__.py files is found. @@ -74,12 +78,6 @@ function! ale#python#FindVirtualenv(buffer) abort return $VIRTUAL_ENV endfunction -" Run an executable check for Python scripts. -" On Windows, 1 will be returned if the file is merely readable. -function! ale#python#IsExecutable(path) abort - return has('win32') ? filereadable(a:path) : executable(a:path) -endfunction - " Given a buffer number and a command name, find the path to the executable. " First search on a virtualenv for Python, if nothing is found, try the global " command. Returns an empty string if cannot find the executable @@ -96,7 +94,7 @@ function! ale#python#FindExecutable(buffer, base_var_name, path_list) abort \ join([l:virtualenv, s:bin_dir, l:path], s:sep) \) - if ale#python#IsExecutable(l:ve_executable) + if executable(l:ve_executable) return l:ve_executable endif endfor diff --git a/autoload/ale/semver.vim b/autoload/ale/semver.vim index b153dd1..6b0fd34 100644 --- a/autoload/ale/semver.vim +++ b/autoload/ale/semver.vim @@ -1,27 +1,55 @@ -" Given some text, parse a semantic versioning string from the text -" into a triple of integeers [major, minor, patch]. +let s:version_cache = {} + +" Reset the version cache used for parsing the version. +function! ale#semver#ResetVersionCache() abort + let s:version_cache = {} +endfunction + +" Given an executable name and some lines of output, which can be empty, +" parse the version from the lines of output, or return the cached version +" triple [major, minor, patch] " -" If no match can be performed, then an empty List will be returned instead. -function! ale#semver#Parse(text) abort - let l:match = matchlist(a:text, '^ *\(\d\+\)\.\(\d\+\)\.\(\d\+\)') +" If the version cannot be found, an empty List will be returned instead. +function! ale#semver#GetVersion(executable, version_lines) abort + let l:version = get(s:version_cache, a:executable, []) - if empty(l:match) - return [] - endif + for l:line in a:version_lines + let l:match = matchlist(l:line, '\v(\d+)\.(\d+)\.(\d+)') - return [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0] + if !empty(l:match) + let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0] + let s:version_cache[a:executable] = l:version + + break + endif + endfor + + return l:version +endfunction + +" Return 1 if the semver version has been cached for a given executable. +function! ale#semver#HasVersion(executable) abort + return has_key(s:version_cache, a:executable) endfunction " Given two triples of integers [major, minor, patch], compare the triples -" and return 1 if the lhs is greater than or equal to the rhs. -function! ale#semver#GreaterOrEqual(lhs, rhs) abort +" and return 1 if the LHS is greater than or equal to the RHS. +" +" Pairs of [major, minor] can also be used for either argument. +" +" 0 will be returned if the LHS is an empty List. +function! ale#semver#GTE(lhs, rhs) abort + if empty(a:lhs) + return 0 + endif + if a:lhs[0] > a:rhs[0] return 1 elseif a:lhs[0] == a:rhs[0] if a:lhs[1] > a:rhs[1] return 1 elseif a:lhs[1] == a:rhs[1] - return a:lhs[2] >= a:rhs[2] + return get(a:lhs, 2) >= get(a:rhs, 2) endif endif diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim index a073f7a..3f53368 100644 --- a/autoload/ale/statusline.vim +++ b/autoload/ale/statusline.vim @@ -95,6 +95,11 @@ endfunction " This function is deprecated, and should not be used. Use the airline plugin " instead, or write your own status function with ale#statusline#Count() function! ale#statusline#Status() abort + if !get(g:, 'ale_deprecation_ale_statusline_status', 0) + execute 'echom ''ale#statusline#Status() is deprecated, use ale#statusline#Count() to write your own function.''' + let g:ale_deprecation_ale_statusline_status = 1 + endif + if !exists('g:ale_statusline_format') return 'OK' endif diff --git a/autoload/ale/test.vim b/autoload/ale/test.vim index 8fc4fe4..bea10c5 100644 --- a/autoload/ale/test.vim +++ b/autoload/ale/test.vim @@ -50,5 +50,5 @@ function! ale#test#SetFilename(path) abort \ ? a:path \ : l:dir . '/' . a:path - silent! noautocmd execute 'file ' . fnameescape(ale#path#Winify(l:full_path)) + silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path)) endfunction diff --git a/autoload/ale/toggle.vim b/autoload/ale/toggle.vim new file mode 100644 index 0000000..e9cc29b --- /dev/null +++ b/autoload/ale/toggle.vim @@ -0,0 +1,193 @@ +function! ale#toggle#InitAuGroups() abort + " This value used to be a Boolean as a Number, and is now a String. + let l:text_changed = '' . g:ale_lint_on_text_changed + + augroup ALEPatternOptionsGroup + autocmd! + autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand(''))) + augroup END + + augroup ALERunOnTextChangedGroup + autocmd! + if g:ale_enabled + if l:text_changed is? 'always' || l:text_changed is# '1' + autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay) + elseif l:text_changed is? 'normal' + autocmd TextChanged * call ale#Queue(g:ale_lint_delay) + elseif l:text_changed is? 'insert' + autocmd TextChangedI * call ale#Queue(g:ale_lint_delay) + endif + endif + augroup END + + augroup ALERunOnEnterGroup + autocmd! + if g:ale_enabled + " Handle everything that needs to happen when buffers are entered. + autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand(''))) + endif + if g:ale_enabled && g:ale_lint_on_enter + autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand(''))) + " Track when the file is changed outside of Vim. + autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''))) + endif + augroup END + + augroup ALERunOnFiletypeChangeGroup + autocmd! + if g:ale_enabled && g:ale_lint_on_filetype_changed + " Only start linting if the FileType actually changes after + " opening a buffer. The FileType will fire when buffers are opened. + autocmd FileType * call ale#events#FileTypeEvent( + \ str2nr(expand('')), + \ expand('') + \) + endif + augroup END + + augroup ALERunOnSaveGroup + autocmd! + autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand(''))) + augroup END + + augroup ALERunOnInsertLeave + autocmd! + if g:ale_enabled && g:ale_lint_on_insert_leave + autocmd InsertLeave * call ale#Queue(0) + endif + augroup END + + augroup ALECursorGroup + autocmd! + if g:ale_enabled && g:ale_echo_cursor + autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay() + " 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 * call ale#cursor#EchoCursorWarning() + endif + augroup END + + if !g:ale_enabled + augroup! ALERunOnTextChangedGroup + augroup! ALERunOnEnterGroup + augroup! ALERunOnInsertLeave + augroup! ALECursorGroup + endif +endfunction + +function! s:EnablePreamble() abort + " Set pattern options again, if enabled. + if g:ale_pattern_options_enabled + call ale#pattern_options#SetOptions(bufnr('')) + endif + + " Lint immediately, including running linters against the file. + call ale#Queue(0, 'lint_file') + + if g:ale_set_balloons + call ale#balloon#Enable() + endif +endfunction + +function! s:DisablePostamble() abort + " Remove highlights for the current buffer now. + if g:ale_set_highlights + call ale#highlight#UpdateHighlights() + endif + + if g:ale_set_balloons + call ale#balloon#Disable() + 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') + + if g:ale_enabled + call s:EnablePreamble() + else + call s:CleanupEveryBuffer() + call s:DisablePostamble() + endif + + call ale#toggle#InitAuGroups() +endfunction + +function! ale#toggle#Enable() abort + if !g:ale_enabled + " Set pattern options again, if enabled. + if g:ale_pattern_options_enabled + call ale#pattern_options#SetOptions(bufnr('')) + endif + + call ale#toggle#Toggle() + endif +endfunction + +function! ale#toggle#Disable() abort + if g:ale_enabled + call ale#toggle#Toggle() + endif +endfunction + +function! ale#toggle#Reset() abort + call s:CleanupEveryBuffer() + call ale#highlight#UpdateHighlights() +endfunction + +function! ale#toggle#ToggleBuffer(buffer) abort + " Get the new value for the toggle. + let l:enabled = !getbufvar(a:buffer, 'ale_enabled', 1) + + " Disabling ALE globally removes autocmd events, so we cannot enable + " linting locally when linting is disabled globally + if l:enabled && !g:ale_enabled + execute 'echom ''ALE cannot be enabled locally when disabled globally''' + return + endif + + call setbufvar(a:buffer, 'ale_enabled', l:enabled) + + if l:enabled + call s:EnablePreamble() + else + " Stop all jobs and clear the results for everything, and delete + " all of the data we stored for the buffer. + call ale#engine#Cleanup(a:buffer) + call s:DisablePostamble() + endif +endfunction + +function! ale#toggle#EnableBuffer(buffer) abort + " ALE is enabled by default for all buffers. + if !getbufvar(a:buffer, 'ale_enabled', 1) + call ale#toggle#ToggleBuffer(a:buffer) + endif +endfunction + +function! ale#toggle#DisableBuffer(buffer) abort + if getbufvar(a:buffer, 'ale_enabled', 1) + call ale#toggle#ToggleBuffer(a:buffer) + endif +endfunction + +function! ale#toggle#ResetBuffer(buffer) abort + call ale#engine#Cleanup(a:buffer) + call ale#highlight#UpdateHighlights() +endfunction diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index cf8d5be..b94a11b 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -236,6 +236,13 @@ function! ale#util#EscapePCRE(unsafe_string) abort return substitute(a:unsafe_string, '\([\-\[\]{}()*+?.^$|]\)', '\\\1', 'g') endfunction +" Escape a string so that it can be used as a literal string inside an evaled +" vim command. +function! ale#util#EscapeVim(unsafe_string) abort + return "'" . substitute(a:unsafe_string, "'", "''", 'g') . "'" +endfunction + + " Given a String or a List of String values, try and decode the string(s) " as a JSON value which can be decoded with json_decode. If the JSON string " is invalid, the default argument value will be returned instead. @@ -250,7 +257,14 @@ function! ale#util#FuzzyJSONDecode(data, default) abort let l:str = type(a:data) == type('') ? a:data : join(a:data, '') try - return json_decode(l:str) + let l:result = json_decode(l:str) + + " Vim 8 only uses the value v:none for decoding blank strings. + if !has('nvim') && l:result is v:none + return a:default + endif + + return l:result catch /E474/ return a:default endtry diff --git a/doc/ale-asciidoc.txt b/doc/ale-asciidoc.txt new file mode 100644 index 0000000..b6b64fd --- /dev/null +++ b/doc/ale-asciidoc.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE AsciiDoc Integration *ale-asciidoc-options* + + +=============================================================================== +write-good *ale-asciidoc-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-asm.txt b/doc/ale-asm.txt index 63b5441..a97c6d0 100644 --- a/doc/ale-asm.txt +++ b/doc/ale-asm.txt @@ -5,6 +5,14 @@ ALE ASM Integration *ale-asm-options* =============================================================================== gcc *ale-asm-gcc* +g:ale_asm_gcc_executable *g:ale_asm_gcc_executable* + *b:ale_asm_gcc_executable* + Type: |String| + Default: `'gcc'` + +This variable can be changed to use a different executable for gcc. + + g:ale_asm_gcc_options *g:ale_asm_gcc_options* *b:ale_asm_gcc_options* Type: |String| diff --git a/doc/ale-c.txt b/doc/ale-c.txt index fc2c45c..cf483fb 100644 --- a/doc/ale-c.txt +++ b/doc/ale-c.txt @@ -143,6 +143,33 @@ g:ale_c_cppcheck_options *g:ale_c_cppcheck_options* This variable can be changed to modify flags given to cppcheck. +=============================================================================== +flawfinder *ale-c-flawfinder* + +g:ale_c_flawfinder_executable *g:ale_c_flawfinder_executable* + *b:ale_c_flawfinder_executable* + Type: |String| + Default: `'flawfinder'` + + This variable can be changed to use a different executable for flawfinder. + + +g:ale_c_flawfinder_minlevel *g:ale_c_flawfinder_minlevel* + *b:ale_c_flawfinder_minlevel* + Type: |Number| + Default: `1` + + This variable can be changed to ignore risks under the given risk threshold. + + +g:ale_c_flawfinder_options *g:ale-c-flawfinder* + *b:ale-c-flawfinder* + Type: |String| + Default: `''` + + This variable can be used to pass extra options into the flawfinder command. + + =============================================================================== gcc *ale-c-gcc* diff --git a/doc/ale-clojure.txt b/doc/ale-clojure.txt new file mode 100644 index 0000000..a83e336 --- /dev/null +++ b/doc/ale-clojure.txt @@ -0,0 +1,21 @@ +=============================================================================== +ALE Clojure Integration *ale-clojure-options* + + +=============================================================================== +joker *ale-clojure-joker* + +Joker is a small Clojure interpreter and linter written in Go. + +https://github.com/candid82/joker + +Linting options are not configurable by ale, but instead are controlled by a +`.joker` file in same directory as the file (or current working directory if +linting stdin), a parent directory relative to the file, or the users home +directory. + +see https://github.com/candid82/joker#linter-mode for more information. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: + diff --git a/doc/ale-cpp.txt b/doc/ale-cpp.txt index cda5768..315f293 100644 --- a/doc/ale-cpp.txt +++ b/doc/ale-cpp.txt @@ -153,6 +153,33 @@ g:ale_cpp_cpplint_options *g:ale_cpp_cpplint_options* This variable can be changed to modify flags given to cpplint. +=============================================================================== +flawfinder *ale-cpp-flawfinder* + +g:ale_cpp_flawfinder_executable *g:ale_cpp_flawfinder_executable* + *b:ale_cpp_flawfinder_executable* + Type: |String| + Default: `'flawfinder'` + + This variable can be changed to use a different executable for flawfinder. + + +g:ale_cpp_flawfinder_minlevel *g:ale_cpp_flawfinder_minlevel* + *b:ale_cpp_flawfinder_minlevel* + Type: |Number| + Default: `1` + + This variable can be changed to ignore risks under the given risk threshold. + + +g:ale_cpp_flawfinder_options *g:ale-cpp-flawfinder* + *b:ale-cpp-flawfinder* + Type: |String| + Default: `''` + + This variable can be used to pass extra options into the flawfinder command. + + =============================================================================== gcc *ale-cpp-gcc* diff --git a/doc/ale-cs.txt b/doc/ale-cs.txt index eeb1abd..3a02df6 100644 --- a/doc/ale-cs.txt +++ b/doc/ale-cs.txt @@ -5,9 +5,9 @@ ALE C# Integration *ale-cs-options* =============================================================================== mcs *ale-cs-mcs* - The mcs linter checks the syntax of the '*.cs' file loaded in the current - buffer only. It uses the --parse option of the mcs compiler and implicitly - sets the -unsafe flag. + The `mcs` linter looks only for syntax errors while you type. See |ale-cs-mcsc| + for the separately configured linter for checking for semantic errors. + g:ale_cs_mcs_options *g:ale_cs_mcs_options* *b:ale_cs_mcs_options* @@ -17,7 +17,7 @@ g:ale_cs_mcs_options *g:ale_cs_mcs_options* This variable can be changed to pass additional flags given to mcs. - NOTE: The -unsafe flag is selected implicitly and thus does not need to be + NOTE: The -unsafe flag is selected implicitly and thus does not need to be explicitly included in the |g:ale_cs_mcs_options| or |b:ale_cs_mcs_options| parameter. @@ -25,49 +25,37 @@ g:ale_cs_mcs_options *g:ale_cs_mcs_options* =============================================================================== mcsc *ale-cs-mcsc* + The mcsc linter checks for semantic errors when files are opened or saved + See |ale-lint-file-linters| for more information on linters which do not + check for problems while you type. + The mcsc linter uses the mono mcs compiler to generate a temporary module target file (-t:module). The module includes including all '*.cs' files - contained in the directory tree rooted at the path defined by the + contained in the directory tree rooted at the path defined by the |g:ale_cs_mcsc_source| or |b:ale_cs_mcsc_source| variable. - variable and all sub directories. + variable and all sub directories. - The paths to search for additional assembly ('*.dll') files can be - specified using the |g:ale_cs_mcsc_assembly_path| or - |b:ale_cs_mcsc_assembly_path| variable. The additional assembly files ('*.dll') - can be included through the |g:ale_cs_mcsc_assemblies| or - |b:ale_cs_mcsc_assemblies| parameter. + The paths to search for additional assembly files can be specified using the + |g:ale_cs_mcsc_assembly_path| or |b:ale_cs_mcsc_assembly_path| variables. + + NOTE: ALE will not any errors in files apart from syntax errors if any one + of the source files contains a syntax error. Syntax errors must be fixed + first before other errors will be shown. - NOTE: mcs compiles sources in multiple phases. It stops compilation after - finding errors during the current phase. - For example assume a file named 'FileWithTypeError.cs' is edited and saved - which contains a Type error. In the same directory tree a file named - 'FileWithSyntaxError.cs' exists which contains a syntax error - (eg.: a missing '{'). - In that case mcs and thus mcsc linter will stop after the syntax check phase is - finished and report the syntax error in the file 'FileWithSyntaxError.cs'. The - Type error in the file 'FileWithTypeError.cs is not seen jet. - The only possibility to find the error in in 'FileWithTypeError.cs' is to fix - the syntax error in 'FileWithSyntaxError.cs' first. After saving mcs will - successfully pass the syntax check phase and advance to the next compilation - phase at which the Type error hidden in 'FileWithTypeError.cs' is found and - now can be indicated by ale. g:ale_cs_mcsc_options *g:ale_cs_mcsc_options* *b:ale_cs_mcsc_options* Type: |String| Default: `''` - This parameter can be used to define additional flags and parameters independent - of the source tree to be linted. The specified string is directly passed to - mcs compiler without any further change. + This option can be set to pass additional arguments to the `mcs` compiler. - For example, to add the dotnet package which is not added per default + For example, to add the dotnet package which is not added per default: > let g:ale_cs_mcs_options = '-pkg:dotnet' +< + NOTE: the `-unsafe` option is always passed to `mcs`. - NOTE: The mcs -unsafe option is included implicitly per default. Therefore it - is not necessary to specify it explicitly through the |g:ale_cs_mcsc_options| - or |b:ale_cs_mcsc_options| parameter. g:ale_cs_mcsc_source *g:ale_cs_mcsc_source* *b:ale_cs_mcsc_source* @@ -75,28 +63,40 @@ g:ale_cs_mcsc_source *g:ale_cs_mcsc_source* Default: `''` This variable defines the root path of the directory tree searched for the - '*.cs' files to be linted. If empty the current working directory is used. + '*.cs' files to be linted. If this option is empty, the source file's + directory will be used. NOTE: Currently it is not possible to specify sub directories and directory sub trees which shall not be searched for *.cs files. + g:ale_cs_mcsc_assembly_path *g:ale_cs_mcsc_assembly_path* *b:ale_cs_mcsc_assembly_path* Type: |List| Default: `[]` This variable defines a list of path strings to be searched for external - assembly ('*.dll') files. The list is passed to the mcs compiler using the - '-lib:' flag. + assembly files. The list is passed to the mcs compiler using the `-lib:` + flag. + g:ale_cs_mcsc_assemblies *g:ale_cs_mcsc_assemblies* *b:ale_cs_mcsc_assemblies* Type: |List| Default: `[]` - This variable defines a list of external assembly (*.dll) files required - by the mono mcs compiler to generate a valid module target. The list is - passed the mcs compiler using the '-r:' flag. + This variable defines a list of external assembly (*.dll) files required + by the mono mcs compiler to generate a valid module target. The list is + passed the mcs compiler using the `-r:` flag. + + For example: > + + " Compile C# programs with the Unity engine DLL file on Mac. + let g:ale_cs_mcsc_assemblies = [ + \ '/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEngine.dll', + \ 'path-to-unityproject/obj/Debug', + \] +< =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-dart.txt b/doc/ale-dart.txt index 37ba6fa..c6faa5c 100644 --- a/doc/ale-dart.txt +++ b/doc/ale-dart.txt @@ -20,6 +20,8 @@ If you have installed Dart on Linux, you can also try the following: > ... or similarly for wherever your Dart SDK lives. This should work without having to modify `$PATH`. +ALE can only check for problems with `dartanalyzer` with the file on disk. +See |ale-lint-file-linters| Options ------------------------------------------------------------------------------- diff --git a/doc/ale-dockerfile.txt b/doc/ale-dockerfile.txt index 288addb..805cc47 100644 --- a/doc/ale-dockerfile.txt +++ b/doc/ale-dockerfile.txt @@ -5,7 +5,7 @@ ALE Dockerfile Integration *ale-dockerfile-options* =============================================================================== hadolint *ale-dockerfile-hadolint* - hadolint can be found at: https://github.com/lukasmartinelli/hadolint + hadolint can be found at: https://github.com/hadolint/hadolint g:ale_dockerfile_hadolint_use_docker *g:ale_dockerfile_hadolint_use_docker* @@ -25,12 +25,12 @@ g:ale_dockerfile_hadolint_use_docker *g:ale_dockerfile_hadolint_use_docker* g:ale_dockerfile_hadolint_image *g:ale_dockerfile_hadolint_image* *b:ale_dockerfile_hadolint_image* Type: |String| - Default: `'lukasmartinelli/hadolint'` + Default: `'hadolint/hadolint'` This variable controls the docker image used to run hadolint. The default is hadolint's author's build, and can be found at: - https://hub.docker.com/r/lukasmartinelli/hadolint/ + https://hub.docker.com/r/hadolint/hadolint/ =============================================================================== diff --git a/doc/ale-elixir.txt b/doc/ale-elixir.txt new file mode 100644 index 0000000..b7d4922 --- /dev/null +++ b/doc/ale-elixir.txt @@ -0,0 +1,32 @@ +=============================================================================== +ALE Elixir Integration *ale-elixir-options* + + +=============================================================================== +mix *ale-elixir-mix* + +g:ale_elixir_mix_options *g:ale_elixir_mix_options* + *b:ale_elixir_mix_options* + Type: |String| + Default: `'mix'` + + + This variable can be changed to specify the mix executable. + +=============================================================================== +dialyxir *ale-elixir-dialyxir* + +Dialyzer, a DIscrepancy AnaLYZer for ERlang programs. +http://erlang.org/doc/man/dialyzer.html + +It can be used with elixir through dialyxir +https://github.com/jeremyjh/dialyxir + +Options for dialyzer are not configurable by ale, but they are instead +configured on your project's `mix.exs`. + +See https://github.com/jeremyjh/dialyxir#with-explaining-stuff for more +information. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-eruby.txt b/doc/ale-eruby.txt index b9cd3cb..a0f6f4f 100644 --- a/doc/ale-eruby.txt +++ b/doc/ale-eruby.txt @@ -1,17 +1,15 @@ =============================================================================== ALE Eruby Integration *ale-eruby-options* -There are two linters for `eruby` files: +There are three linters for `eruby` files: -- `erubylint` +- `erb` - `erubis` +- `erubi` -If you don't know which one your project uses, it's probably `erb`. -To selectively enable one or the other, see |g:ale_linters|. - -(Note that ALE already disables linters if the executable for that linter is -not found; thus, there's probably no need to disable one of these if you're -using the other one.) +`erb` is in the Ruby standard library and is mostly universal. `erubis` is the +default parser in Rails between 3.0 and 5.1. `erubi` is the default in Rails +5.1 and later. To selectively enable a subset, see |g:ale_linters|. =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-fish.txt b/doc/ale-fish.txt new file mode 100644 index 0000000..8450b38 --- /dev/null +++ b/doc/ale-fish.txt @@ -0,0 +1,14 @@ +=============================================================================== +ALE Fish Integration *ale-fish-options* + +Lints fish files using `fish -n`. + +Note that `fish -n` is not foolproof: it sometimes gives false positives or +errors that are difficult to parse without more context. This integration skips +displaying errors if an error message is not found. + +If ALE is not showing any errors but your file does not run as expected, run +`fish -n ` from the command line. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-fountain.txt b/doc/ale-fountain.txt new file mode 100644 index 0000000..ac0870c --- /dev/null +++ b/doc/ale-fountain.txt @@ -0,0 +1,6 @@ +=============================================================================== +ALE Fountain Integration *ale-fountain-options* + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-gitcommit.txt b/doc/ale-gitcommit.txt new file mode 100644 index 0000000..71813dd --- /dev/null +++ b/doc/ale-gitcommit.txt @@ -0,0 +1,42 @@ +=============================================================================== +ALE Git Commit Integration *ale-gitcommit-options* + + +=============================================================================== +gitlint *ale-gitcommit-gitlint* + +g:ale_gitcommit_gitlint_executable *g:ale_gitcommit_gitlint_executable* + *b:ale_gitcommit_gitlint_executable* + Type: |String| + Default: `'gitlint'` + + This variable can be changed to modify the executable used for gitlint. + + +g:ale_gitcommit_gitlint_options *g:ale_gitcommit_gitlint_options* + *b:ale_gitcommit_gitlint_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the gitlint + invocation. + + For example, to dinamically set the gitlint configuration file path, you + may want to set > + + let g:ale_gitcommit_gitlint_options = '-C /home/user/.config/gitlint.ini' +< + +g:ale_gitcommit_gitlint_use_global *g:ale_gitcommit_gitlint_use_global* + *b:ale_gitcommit_gitlint_use_global* + Type: |Number| + Default: `0` + + This variable controls whether or not ALE will search for gitlint in a + virtualenv directory first. If this variable is set to `1`, then ALE will + always use |g:ale_gitcommit_gitlint_executable| for the executable path. + + Both variables can be set with `b:` buffer variables instead. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-glsl.txt b/doc/ale-glsl.txt index fbadf03..257de75 100644 --- a/doc/ale-glsl.txt +++ b/doc/ale-glsl.txt @@ -32,5 +32,25 @@ g:ale_glsl_glslang_options *g:ale_glsl_glslang_options* This variable can be set to pass additional options to glslangValidator. +=============================================================================== +glslls *ale-glsl-glslls* + +g:ale_glsl_glslls_executable *g:ale_glsl_glslls_executable* + *b:ale_glsl_glslls_executable* + Type: |String| + Default: `'glslls'` + + This variable can be changed to change the path to glslls. + See |ale-integrations-local-executables| + +g:ale_glsl_glslls_logfile *g:ale_glsl_glslls_logfile* + *b:ale_glsl_glslls_logfile* + Type: |String| + Default: `''` + + Setting this variable to a writeable file path will enable logging to that + file. + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-go.txt b/doc/ale-go.txt index c5a6887..b80bd45 100644 --- a/doc/ale-go.txt +++ b/doc/ale-go.txt @@ -20,6 +20,19 @@ 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. + +=============================================================================== +gobuild *ale-go-gobuild* + +g:ale_go_gobuild_options *g:ale_go_gobuild_options* + *b:ale_go_gobuild_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the gobuild linter. + They are injected directly after "go test". + + =============================================================================== gofmt *ale-go-gofmt* @@ -30,6 +43,7 @@ g:ale_go_gofmt_options *g:ale_go_gofmt_options* This variable can be set to pass additional options to the gofmt fixer. + =============================================================================== gometalinter *ale-go-gometalinter* @@ -59,5 +73,35 @@ g:ale_go_gometalinter_options *g:ale_go_gometalinter_options* number of linters known to be slow or consume a lot of resources. +g:ale_go_gometalinter_package *g:ale_go_gometalinter_package* + *b:ale_go_gometalinter_package* + Type: |Number| + Default: `0` + + When set to `1`, the whole Go package will be checked instead of only the + current file. + + +=============================================================================== +staticcheck *ale-go-staticcheck* + +g:ale_go_staticcheck_options *g:ale_go_staticcheck_options* + *b:ale_go_staticcheck_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the staticcheck + linter. + + +g:ale_go_staticcheck_package *g:ale_go_staticcheck_package* + *b:ale_go_staticcheck_package* + Type: |Number| + Default: `0` + + When set to `1`, the whole Go package will be checked instead of only the + current file. + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-graphql.txt b/doc/ale-graphql.txt index 5ceb5ca..603694b 100644 --- a/doc/ale-graphql.txt +++ b/doc/ale-graphql.txt @@ -2,8 +2,21 @@ ALE GraphQL Integration *ale-graphql-options* +=============================================================================== +eslint *ale-graphql-eslint* + +The `eslint` linter for GraphQL uses the JavaScript options for `eslint`; see: +|ale-javascript-eslint|. + +You will need the GraphQL ESLint plugin installed for this to work. + =============================================================================== gqlint *ale-graphql-gqlint* +=============================================================================== +prettier *ale-graphql-prettier* + +See |ale-javascript-prettier| for information about the available options. + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-haskell.txt b/doc/ale-haskell.txt index bbf99fc..9fab39b 100644 --- a/doc/ale-haskell.txt +++ b/doc/ale-haskell.txt @@ -2,6 +2,26 @@ ALE Haskell Integration *ale-haskell-options* +=============================================================================== +brittany *ale-haskell-brittany* + +g:ale_haskell_brittany_executable *g:ale_haskell_brittany_executable* + *b:ale_haskell_brittany_executable* + Type: |String| + Default: `'brittany'` + + This variable can be changed to use a different executable for brittany. + +=============================================================================== +ghc *ale-haskell-ghc* + +g:ale_haskell_ghc_options *g:ale_haskell_ghc_options* + *b:ale_haskell_ghc_options* + Type: |String| + Default: `'-fno-code -v0'` + + This variable can be changed to modify flags given to ghc. + =============================================================================== hdevtools *ale-haskell-hdevtools* @@ -20,6 +40,16 @@ g:ale_haskell_hdevtools_options *g:ale_haskell_hdevtools_options* This variable can be changed to modify flags given to hdevtools. +=============================================================================== +hfmt *ale-haskell-hfmt* + +g:ale_haskell_hfmt_executable *g:ale_haskell_hfmt_executable* + *b:ale_haskell_hfmt_executable* + Type: |String| + Default: `'hfmt'` + + This variable can be changed to use a different executable for hfmt. + =============================================================================== stack-build *ale-haskell-stack-build* diff --git a/doc/ale-html.txt b/doc/ale-html.txt index e6f3398..416e932 100644 --- a/doc/ale-html.txt +++ b/doc/ale-html.txt @@ -16,7 +16,7 @@ g:ale_html_htmlhint_executable *g:ale_html_htmlhint_executable* g:ale_html_htmlhint_options *g:ale_html_htmlhint_options* *b:ale_html_htmlhint_options* Type: |String| - Default: `'--format=unix'` + Default: `''` This variable can be changed to modify flags given to HTMLHint. @@ -32,6 +32,21 @@ g:ale_html_htmlhint_use_global *g:ale_html_htmlhint_use_global* =============================================================================== tidy *ale-html-tidy* +`tidy` is a console application which corrects and cleans up HTML and XML +documents by fixing markup errors and upgrading legacy code to modern +standards. + +Note: +`/usr/bin/tidy` on macOS (installed by default) is too old. It was released +on 31 Oct 2006. It does not consider modern HTML specs (HTML5) and shows +outdated warnings. So |ale| ignores `/usr/bin/tidy` on macOS. + +To use `tidy` on macOS, please install the latest version with Homebrew: +> + $ brew install tidy-html5 +< +`/usr/local/bin/tidy` is installed. + g:ale_html_tidy_executable *g:ale_html_tidy_executable* *b:ale_html_tidy_executable* Type: |String| @@ -56,5 +71,11 @@ g:ale_html_tidy_options *g:ale_html_tidy_options* (mac), sjis (shiftjis), utf-16le, utf-16, utf-8 +=============================================================================== +write-good *ale-html-write-good* + +See |ale-write-good-options| + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-java.txt b/doc/ale-java.txt index 13decb4..0d2011f 100644 --- a/doc/ale-java.txt +++ b/doc/ale-java.txt @@ -33,5 +33,25 @@ g:ale_java_javac_options *g:ale_java_javac_options* This variable can be set to pass additional options to javac. +=============================================================================== +google-java-format *ale-java-google-java-format* + + +g:ale_java_google_java_format_executable + *g:ale_java_google_java_format_executable* + *b:ale_java_google_java_format_executable* + Type: |String| + Default: `'google-java-format'` + + See |ale-integrations-local-executables| + + +g:ale_java_google_java_format_options *g:ale_java_google_java_format_options* + *b:ale_java_google_java_format_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-javascript.txt b/doc/ale-javascript.txt index 8bf1a0d..f625fd7 100644 --- a/doc/ale-javascript.txt +++ b/doc/ale-javascript.txt @@ -56,8 +56,21 @@ g:ale_javascript_eslint_suppress_eslintignore Type: |Number| Default: `0` - This variable can be set to disable the warning that linting is disabled on - the current file due to being covered by `.eslintignore`. + This variable can be set to `1` to disable warnings for files being ignored + by eslint. + + +g:ale_javascript_eslint_suppress_missing_config + *g:ale_javascript_eslint_suppress_missing_config* + *b:ale_javascript_eslint_suppress_missing_config* + Type: |Number| + Default: `0` + + This variable can be set to `1` to disable errors for missing eslint + configuration files. + + When turning this option on, eslint will not report any problems when no + configuration files are found. =============================================================================== @@ -71,6 +84,17 @@ g:ale_javascript_flow_executable *g:ale_javascript_flow_executable* See |ale-integrations-local-executables| +g:ale_javascript_flow_use_home_config *g:ale_javascript_flow_use_home_config* + *b:ale_javascript_flow_use_home_config* + Type: |Number| + Default: `0` + + When set to `1`, ALE will allow Flow to be executed with configuration files + from your home directory. ALE will not run Flow with home directory + configuration files by default, as doing so can lead to Vim consuming all of + your RAM and CPU power. + + g:ale_javascript_flow_use_global *g:ale_javascript_flow_use_global* *b:ale_javascript_flow_use_global* Type: |Number| @@ -79,6 +103,15 @@ g:ale_javascript_flow_use_global *g:ale_javascript_flow_use_global* See |ale-integrations-local-executables| +=============================================================================== +importjs *ale-javascript-importjs* + +g:ale_javascript_importjs_executable *g:ale_javascript_importjs_executable* + *b:ale_javascript_importjs_executable* + Type: |String| + Default: `'importjs'` + + =============================================================================== jscs *ale-javascript-jscs* @@ -143,23 +176,10 @@ g:ale_javascript_prettier_use_global *g:ale_javascript_prettier_use_global* See |ale-integrations-local-executables| -g:ale_javascript_prettier_use_local_config - *g:ale_javascript_prettier_use_local_config* - *b:ale_javascript_prettier_use_local_config* - Type: |Number| - Default: `0` - - This variable can be set to use the local prettier configuration file. - =============================================================================== prettier-eslint *ale-javascript-prettier-eslint* -ALE supports `prettier-eslint` >= 4.2.0. Using lower version is not recommended -because it cannot be configured to use the ESLint configuration file for input -given via stdin. However ALE could be set up on your own risk with older -versions with |g:ale_javascript_prettier_eslint_legacy| - g:ale_javascript_prettier_eslint_executable *g:ale_javascript_prettier_eslint_executable* *b:ale_javascript_prettier_eslint_executable* @@ -186,14 +206,6 @@ g:ale_javascript_prettier_eslint_use_global See |ale-integrations-local-executables| -g:ale_javascript_prettier_eslint_legacy - *g:ale_javascript_prettier_eslint_legacy* - *b:ale_javascript_prettier_eslint_legacy* - Type: |Number| - Default: `0` - - Fallback option for `prettier-eslint` < 4.2.0 - =============================================================================== prettier-standard *ale-javascript-prettier-standard* diff --git a/doc/ale-json.txt b/doc/ale-json.txt index 1d052d5..1e97abc 100644 --- a/doc/ale-json.txt +++ b/doc/ale-json.txt @@ -2,12 +2,78 @@ ALE JSON Integration *ale-json-options* +=============================================================================== +fixjson *ale-json-fixjson* + +fixjson is a JSON file fixer/formatter for humans using (relaxed) JSON5. +It provides: + +- Pretty-prints JSON input +- Fixes various failures while humans writing JSON + - Fixes trailing commas objects or arrays + - Fixes missing commas for elements of objects or arrays + - Adds quotes to keys in objects + - Newlines in strings + - Hex numbers + - Fixes single quotes to double quotes + +You can install it using npm: +> + $ npm install -g fixjson +< +ALE provides fixjson integration as a fixer. See |ale-fix|. + +g:ale_json_fixjson_executable *g:ale_json_fixjson_executable* + *b:ale_json_fixjson_executable* + + Type: |String| + Default: `'fixjson'` + + The executable that will be run for fixjson. + +g:ale_json_fixjson_options *g:ale_json_fixjson_options* + *b:ale_json_fixjson_options* + + Type: |String| + Default: `''` + + This variable can add extra options to the command executed for running + fixjson. + +g:ale_json_fixjson_use_global *g:ale_json_fixjson_use_global* + *b:ale_json_fixjson_use_global* + + Type: |Number| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== jsonlint *ale-json-jsonlint* There are no options available. +=============================================================================== +jq *ale-json-jq* + +g:ale_json_jq_executable *g:ale_json_jq_executable* + *b:ale_json_jq_executable* + Type: |String| + Default: `'jq'` + + This option can be changed to change the path for `jq`. + + +g:ale_json_jq_options *g:ale_json_jq_options* + *b:ale_json_jq_options* + Type: |String| + Default: `''` + + This option can be changed to pass extra options to `jq`. + + =============================================================================== prettier *ale-json-prettier* diff --git a/doc/ale-latex.txt b/doc/ale-latex.txt new file mode 100644 index 0000000..87fbd4e --- /dev/null +++ b/doc/ale-latex.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE LaTeX Integration *ale-latex-options* + + +=============================================================================== +write-good *ale-latex-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-less.txt b/doc/ale-less.txt index a6b5998..05f56e2 100644 --- a/doc/ale-less.txt +++ b/doc/ale-less.txt @@ -2,6 +2,33 @@ ALE Less Integration *ale-less-options* +=============================================================================== +lessc *ale-less-lessc* + +g:ale_less_lessc_executable *g:ale_less_lessc_executable* + *b:ale_less_lessc_executable* + Type: |String| + Default: `'lessc'` + + See |ale-integrations-local-executables| + + +g:ale_less_lessc_options *g:ale_less_lessc_options* + *b:ale_less_lessc_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to lessc. + + +g:ale_less_lessc_use_global *g:ale_less_lessc_use_global* + *b:ale_less_lessc_use_global* + Type: |String| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== prettier *ale-less-prettier* @@ -9,4 +36,31 @@ See |ale-javascript-prettier| for information about the available options. =============================================================================== +stylelint *ale-less-stylelint* +g:ale_less_stylelint_executable *g:ale_less_stylelint_executable* + *b:ale_less_stylelint_executable* + Type: |String| + Default: `'stylelint'` + + See |ale-integrations-local-executables| + + +g:ale_less_stylelint_options *g:ale_less_stylelint_options* + *b:ale_less_stylelint_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to stylelint. + + +g:ale_less_stylelint_use_global *g:ale_less_stylelint_use_global* + *b:ale_less_stylelint_use_global* + Type: |String| + Default: `0` + + See |ale-integrations-local-executables| + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-lua.txt b/doc/ale-lua.txt index 74d6b94..f1286f8 100644 --- a/doc/ale-lua.txt +++ b/doc/ale-lua.txt @@ -1,6 +1,15 @@ =============================================================================== ALE Lua Integration *ale-lua-options* +=============================================================================== +luac *ale-lua-luac* + +g:ale_lua_luac_executable *g:ale_lua_luac_executable* + *b:ale_lua_luac_executable* + Type: |String| + Default: `'luac'` + + This variable can be changed to change the path to luac. =============================================================================== luacheck *ale-lua-luacheck* diff --git a/doc/ale-markdown.txt b/doc/ale-markdown.txt new file mode 100644 index 0000000..9a5290b --- /dev/null +++ b/doc/ale-markdown.txt @@ -0,0 +1,37 @@ +=============================================================================== +ALE Markdown Integration *ale-markdown-options* + + +=============================================================================== +mdl *ale-markdown-mdl* + +g:ale_markdown_mdl_executable *g:ale_markdown_mdl_executable* + *b:ale_markdown_mdl_executable* + Type: |String| + Default: `'mdl'` + + See |ale-integrations-local-executables| + + +g:ale_markdown_mdl_options *g:ale_markdown_mdl_options* + *b:ale_markdown_mdl_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to mdl. + + +=============================================================================== +prettier *ale-markdown-prettier* + +See |ale-javascript-prettier| for information about the available options. + + +=============================================================================== +write-good *ale-markdown-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-nroff.txt b/doc/ale-nroff.txt new file mode 100644 index 0000000..62ec789 --- /dev/null +++ b/doc/ale-nroff.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE nroff Integration *ale-nroff-options* + + +=============================================================================== +write-good *ale-nroff-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-ocaml.txt b/doc/ale-ocaml.txt index 093d911..cfa365a 100644 --- a/doc/ale-ocaml.txt +++ b/doc/ale-ocaml.txt @@ -10,6 +10,28 @@ merlin *ale-ocaml-merlin* detailed instructions (https://github.com/the-lambda-church/merlin/wiki/vim-from-scratch). +=============================================================================== +ols *ale-ocaml-ols* + + The `ocaml-language-server` is the engine that powers OCaml and ReasonML + editor support using the Language Server Protocol. See the installation + instructions: + https://github.com/freebroccolo/ocaml-language-server#installation + +g:ale_ocaml_ols_executable *g:ale_ocaml_ols_executable* + *b:ale_ocaml_ols_executable* + Type: |String| + Default: `'ocaml-language-server'` + + This variable can be set to change the executable path for `ols`. + +g:ale_ocaml_ols_use_global *g:ale_ocaml_ols_use_global* + *b:ale_ocaml_ols_use_global* + Type: |String| + Default: `0` + + This variable can be set to `1` to always use the globally installed + executable. See also |ale-integrations-local-executables|. =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-perl.txt b/doc/ale-perl.txt index 7611d30..0a4adff 100644 --- a/doc/ale-perl.txt +++ b/doc/ale-perl.txt @@ -1,6 +1,14 @@ =============================================================================== ALE Perl Integration *ale-perl-options* +ALE offers a few ways to check Perl code. Checking code with `perl` is +disabled by default, as `perl` code cannot be checked without executing it. +Specifically, we use the `-c` flag to see if `perl` code compiles. This does +not execute all of the code in a file, but it does run `BEGIN` and `CHECK` +blocks. See `perl --help` and https://stackoverflow.com/a/12908487/406224 + +See |g:ale_linters|. + =============================================================================== perl *ale-perl-perl* diff --git a/doc/ale-php.txt b/doc/ale-php.txt index bae6d7d..7edfe23 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -8,6 +8,17 @@ hack *ale-php-hack* There are no options for this linter. +=============================================================================== +hackfmt *ale-php-hackfmt* + +g:ale_php_hackfmt_options *g:ale_php_hackfmt_options* + *b:ale_php_hackfmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the hackfmt fixer. + + =============================================================================== langserver *ale-php-langserver* @@ -34,6 +45,21 @@ g:ale_php_langserver_use_global *g:ale_php_langserver_use_global* See: |ale-integrations-local-executables| +=============================================================================== +phan *ale-php-phan* + +WARNING: please do not use this linter if you have an configuration file +for your project because the phan will look into your entirely project and +ale will display in the current buffer warnings that may belong to other file. + +g:ale_php_phan_minimum_severity *g:ale_php_phan_minimum_severity* + *b:ale_php_phan_minimum_severity* + Type: |Number| + Default: `0` + + This variable defines the minimum severity level + + =============================================================================== phpcbf *ale-php-phpcbf* @@ -95,6 +121,14 @@ g:ale_php_phpcs_use_global *g:ale_php_phpcs_use_global* =============================================================================== phpmd *ale-php-phpmd* +g:ale_php_phpmd_executable *g:ale_php_phpmd_executable* + *b:ale_php_phpmd_executable* + Type: |String| + Default: `'phpmd'` + + This variable sets executable used for phpmd. + + g:ale_php_phpmd_ruleset *g:ale_php_phpmd_ruleset* *b:ale_php_phpmd_ruleset* Type: |String| @@ -132,5 +166,22 @@ g:ale_php_phpstan_configuration *g:ale_php_phpstan_configuration* This variable sets path to phpstan configuration file. +=============================================================================== +php-cs-fixer *ale-php-php-cs-fixer* + +g:ale_php_cs_fixer_executable *g:ale_php_cs_fixer_executable* + *b:ale_php_cs_fixer_executable* + Type: |String| + Default: `'php-cs-fixer'` + + This variable sets executable used for php-cs-fixer. + +g:ale_php_cs_fixer_use_global *g:ale_php_cs_fixer_use_global* + *b:ale_php_cs_fixer_use_global* + Type: |Boolean| + Default: `0` + + This variable force globally installed fixer. + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-po.txt b/doc/ale-po.txt new file mode 100644 index 0000000..1e03b7b --- /dev/null +++ b/doc/ale-po.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE PO Integration *ale-po-options* + + +=============================================================================== +write-good *ale-po-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-pod.txt b/doc/ale-pod.txt new file mode 100644 index 0000000..c7cc0bb --- /dev/null +++ b/doc/ale-pod.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE Pod Integration *ale-pod-options* + + +=============================================================================== +write-good *ale-pod-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-pony.txt b/doc/ale-pony.txt new file mode 100644 index 0000000..3b32168 --- /dev/null +++ b/doc/ale-pony.txt @@ -0,0 +1,25 @@ +=============================================================================== +ALE Pony Integration *ale-pony-options* + + +=============================================================================== +ponyc *ale-pony-ponyc* + +g:ale_pony_ponyc_executable *g:ale_pony_ponyc_executable* + *b:ale_pony_ponyc_executable* + Type: |String| + Default: `'ponyc'` + + See |ale-integrations-local-executables| + + +g:ale_pony_ponyc_options *g:ale_pony_ponyc_options* + *b:ale_pony_ponyc_options* + Type: |String| + Default: `'--pass paint'` + + This variable can be set to pass options to ponyc. + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-proto.txt b/doc/ale-proto.txt new file mode 100644 index 0000000..734e23d --- /dev/null +++ b/doc/ale-proto.txt @@ -0,0 +1,33 @@ +=============================================================================== +ALE Proto Integration *ale-proto-options* + + +=============================================================================== +Integration Information + +Linting of `.proto` files requires that the `protoc` binary is installed in the +system path and that the `protoc-gen-lint` plugin for the `protoc` binary is also +installed. + +To enable `.proto` file linting, update |g:ale_linters| as appropriate: +> + " Enable linter for .proto files + let g:ale_linters = {'proto': ['protoc-gen-lint']} +< +=============================================================================== +protoc-gen-lint *ale-proto-protoc-gen-lint* + + The linter is a plugin for the `protoc` binary. As long as the binary resides + in the system path, `protoc` will find it. + +g:ale_proto_protoc_gen_lint_options *g:ale_proto_protoc_gen_lint_options* + + Type: |String| + Default: `''` + + This variable can be changed to modify flags given to protoc. Note that the + directory of the linted file is always passed as an include path with '-I' + before any user-supplied options. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-python.txt b/doc/ale-python.txt index a8d033e..4d55e75 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -104,6 +104,16 @@ g:ale_python_mypy_executable *g:ale_python_mypy_executable* See |ale-integrations-local-executables| +g:ale_python_mypy_ignore_invalid_syntax + *g:ale_python_mypy_ignore_invalid_syntax* + *b:ale_python_mypy_ignore_invalid_syntax* + Type: |Number| + Default: `0` + + When set to `1`, syntax error messages for mypy will be ignored. This option + can be used when running other Python linters which check for syntax errors, + as mypy can take a while to finish executing. + g:ale_python_mypy_options *g:ale_python_mypy_options* *b:ale_python_mypy_options* @@ -122,6 +132,46 @@ g:ale_python_mypy_use_global *g:ale_python_mypy_use_global* See |ale-integrations-local-executables| +=============================================================================== +prospector *ale-python-prospector* + +g:ale_python_prospector_executable *g:ale_python_prospector_executable* + *b:ale_python_prospector_executable* + Type: |String| + Default: `'prospector'` + + See |ale-integrations-local-executables| + + +g:ale_python_prospector_options *g:ale_python_prospector_options* + *b:ale_python_prospector_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the prospector + invocation. + + For example, to dynamically switch between programs targeting Python 2 and + Python 3, you may want to set > + + let g:ale_python_prospector_executable = 'python3' + " or 'python' for Python 2 + let g:ale_python_prospector_options = '--rcfile /path/to/.prospector.yaml' + " The virtualenv detection needs to be disabled. + let g:ale_python_prospector_use_global = 0 + + after making sure it's installed for the appropriate Python versions (e.g. + `python3 -m pip install --user prospector`). + + +g:ale_python_prospector_use_global *g:ale_python_prospector_use_global* + *b:ale_python_prospector_use_global* + Type: |Number| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== pycodestyle *ale-python-pycodestyle* @@ -174,7 +224,7 @@ g:ale_python_pylint_options *g:ale_python_pylint_options* Python 3, you may want to set > let g:ale_python_pylint_executable = 'python3' " or 'python' for Python 2 - let g:ale_python_pylint_options = '-rcfile /path/to/pylint.rc' + let g:ale_python_pylint_options = '--rcfile /path/to/pylint.rc' " The virtualenv detection needs to be disabled. let g:ale_python_pylint_use_global = 0 @@ -190,6 +240,25 @@ g:ale_python_pylint_use_global *g:ale_python_pylint_use_global* See |ale-integrations-local-executables| +=============================================================================== +pyls *ale-python-pyls* + +g:ale_python_pyls_executable *g:ale_python_pyls_executable* + *b:ale_python_pyls_executable* + Type: |String| + Default: `'pyls'` + + See |ale-integrations-local-executables| + + +g:ale_python_pyls_use_global *g:ale_python_pyls_use_global* + *b:ale_python_pyls_use_global* + Type: |Number| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== yapf *ale-python-yapf* diff --git a/doc/ale-r.txt b/doc/ale-r.txt index 6372f80..f85f48f 100644 --- a/doc/ale-r.txt +++ b/doc/ale-r.txt @@ -16,5 +16,14 @@ g:ale_r_lintr_options *g:ale_r_lintr_options* options. Consult the lintr documentation for more information. +g:ale_r_lintr_lint_package *g:ale_r_lintr_lint_package* + *b:ale_r_lintr_lint_package* + Type: |Number| + Default: `0` + + When set to `1`, the file will be checked with `lintr::lint_package` instead + of `lintr::lint`. This prevents erroneous namespace warnings when linting + package files. + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-reasonml.txt b/doc/ale-reasonml.txt index 17a7b2e..36ddd75 100644 --- a/doc/ale-reasonml.txt +++ b/doc/ale-reasonml.txt @@ -10,6 +10,45 @@ merlin *ale-reasonml-merlin* detailed instructions (https://github.com/the-lambda-church/merlin/wiki/vim-from-scratch). +=============================================================================== +ols *ale-reasonml-ols* + + The `ocaml-language-server` is the engine that powers OCaml and ReasonML + editor support using the Language Server Protocol. See the installation + instructions: + https://github.com/freebroccolo/ocaml-language-server#installation + +g:ale_reason_ols_executable *g:ale_reason_ols_executable* + *b:ale_reason_ols_executable* + Type: |String| + Default: `'ocaml-language-server'` + + This variable can be set to change the executable path for `ols`. + +g:ale_reason_ols_use_global *g:ale_reason_ols_use_global* + *b:ale_reason_ols_use_global* + Type: |String| + Default: `0` + + This variable can be set to `1` to always use the globally installed + executable. See also |ale-integrations-local-executables|. + +=============================================================================== +refmt *ale-reasonml-refmt* + +g:ale_reasonml_refmt_executable *g:ale_reasonml_refmt_executable* + *b:ale_reasonml_refmt_executable* + Type: |String| + Default: `'refmt'` + + This variable can be set to pass the path of the refmt fixer. + +g:ale_reasonml_refmt_options *g:ale_reasonml_refmt_options* + *b:ale_reasonml_refmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the refmt fixer. =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-restructuredtext.txt b/doc/ale-restructuredtext.txt new file mode 100644 index 0000000..02fbc4a --- /dev/null +++ b/doc/ale-restructuredtext.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE reStructuredText Integration *ale-restructuredtext-options* + + +=============================================================================== +write-good *ale-restructuredtext-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt index c710a26..94181ed 100644 --- a/doc/ale-ruby.txt +++ b/doc/ale-ruby.txt @@ -58,10 +58,10 @@ g:ale_ruby_reek_show_wiki_link *g:ale_ruby_reek_show_wiki_link* =============================================================================== rubocop *ale-ruby-rubocop* -g:ale_ruby_rubocop_executable g:ale_ruby_rubocop_executable - b:ale_ruby_rubocop_executable +g:ale_ruby_rubocop_executable *g:ale_ruby_rubocop_executable* + *b:ale_ruby_rubocop_executable* Type: String - Default: 'rubocop' + Default: `'rubocop'` Override the invoked rubocop binary. This is useful for running rubocop from binstubs or a bundle. @@ -75,5 +75,16 @@ g:ale_ruby_rubocop_options *g:ale_ruby_rubocop_options* This variable can be change to modify flags given to rubocop. +=============================================================================== +ruby *ale-ruby-ruby* + +g:ale_ruby_ruby_executable *g:ale_ruby_ruby_executable* + *b:ale_ruby_ruby_executable* + Type: String + Default: `'ruby'` + + This variable can be changed to use a different executable for ruby. + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt index 52dc3d6..dad9ae6 100644 --- a/doc/ale-rust.txt +++ b/doc/ale-rust.txt @@ -22,6 +22,8 @@ Integration Information over cargo. rls implements the Language Server Protocol for incremental compilation of Rust code, and can check Rust files while you type. `rls` requires Rust files to contained in Cargo projects. + 4. rustfmt -- If you have `rustfmt` installed, you can use it as a fixer to + consistently reformat your Rust code. Only cargo is enabled by default. To switch to using rustc instead of cargo, configure |g:ale_linters| appropriately: > @@ -39,10 +41,52 @@ cargo *ale-rust-cargo* g:ale_rust_cargo_use_check *g:ale_rust_cargo_use_check* *b:ale_rust_cargo_use_check* Type: |Number| + Default: `1` + + When set to `1`, this option will cause ALE to use `cargo check` instead of + `cargo build` . `cargo check` is supported since version 1.16.0 of Rust. + + ALE will never use `cargo check` when the version of `cargo` is less than + 0.17.0. + + +g:ale_rust_cargo_check_all_targets *g:ale_rust_cargo_check_all_targets* + *b:ale_rust_cargo_check_all_targets* + Type: |Number| Default: `0` - When set to `1`, this option will cause ALE to use "cargo check" instead of - "cargo build". "cargo check" is supported since version 1.16.0 of Rust. + When set to `1`, ALE will set the `--all-targets` option when `cargo check` + is used. See |g:ale_rust_cargo_use_check|, + + +g:ale_rust_cargo_default_feature_behavior + *g:ale_rust_cargo_default_feature_behavior* + *b:ale_rust_cargo_default_feature_behavior* + Type: |String| + Default: `default` + + When set to `none`, ALE will set the `--no-default-features` option when + invoking `cargo`. Only the features specified in + |g:ale_rust_cargo_include_features| will be included when performing the + lint check. + + When set to `default`, ALE will instruct `cargo` to build all default + features specified in the project's `Cargo.toml` file, in addition to + including any additional features defined in + |g:ale_rust_cargo_include_features|. + + When set to `all`, ALE will set the `--all-features` option when + invoking `cargo`, which will include all features defined in the project's + `Cargo.toml` file when performing the lint check. + + +g:ale_rust_cargo_include_features *g:ale_rust_cargo_include_features* + *b:ale_rust_cargo_include_features* + Type: |String| + Default: `''` + + When defined, ALE will set the `--features` option when invoking `cargo` to + perform the lint check. See |g:ale_rust_cargo_default_feature_behavior|. =============================================================================== @@ -56,9 +100,33 @@ g:ale_rust_rls_executable *g:ale_rust_rls_executable* This variable can be modified to change the executable path for `rls`. +g:ale_rust_rls_toolchain *g:ale_rust_rls_toolchain* + *b:ale_rust_rls_toolchain* + Type: |String| + Default: `'nightly'` + + This option can be set to change the toolchain used for `rls`. Possible + values include `'nightly'`, `'beta'`, and `'stable'`. + + The `rls` server will only be started once per executable. + + =============================================================================== rustc *ale-rust-rustc* + +g:ale_rust_rustc_options *g:ale_rust_rustc_options* + *b:ale_rust_rustc_options* + Type: |String| + Default: `'-Z no-trans'` + + The variable can be used to change the options passed to `rustc`. + + `-Z no-trans` should only work with nightly builds of Rust. Be careful when + setting the options, as running `rustc` could execute code or generate + binary files. + + g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes* *b:ale_rust_ignore_error_codes* Type: |List| of |String|s @@ -70,5 +138,16 @@ g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes* let g:ale_rust_ignore_error_codes = ['E0432', 'E0433'] +=============================================================================== +rustfmt *ale-rust-rustfmt* + +g:ale_rust_rustfmt_options *g:ale_rust_rustfmt_options* + *b:ale_rust_rustfmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the rustfmt fixer. + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-sh.txt b/doc/ale-sh.txt index 6fbc9fe..941dc59 100644 --- a/doc/ale-sh.txt +++ b/doc/ale-sh.txt @@ -57,5 +57,16 @@ g:ale_sh_shellcheck_exclusions *g:ale_sh_shellcheck_exclusions* \ let b:ale_sh_shellcheck_exclusions = 'SC2034,SC2154,SC2164' < +=============================================================================== +shfmt *ale-sh-shfmt* + +g:ale_sh_shfmt_options *g:ale_sh_shfmt_options* + *b:ale_sh_shfmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the shfmt fixer. + + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-solidity.txt b/doc/ale-solidity.txt index 3dea123..4b74a27 100644 --- a/doc/ale-solidity.txt +++ b/doc/ale-solidity.txt @@ -2,6 +2,14 @@ ALE Solidity Integration *ale-solidity-options* +=============================================================================== +solhint *ale-solidity-solhint* + + Solhint should work out-of-the-box. You can further configure it using a + `.solihint.json` file. See https://github.com/protofire/solhint for more + information. + + =============================================================================== solium *ale-solidity-solium* diff --git a/doc/ale-terraform.txt b/doc/ale-terraform.txt new file mode 100644 index 0000000..ec86e9a --- /dev/null +++ b/doc/ale-terraform.txt @@ -0,0 +1,29 @@ +=============================================================================== +ALE Terraform Integration *ale-terraform-options* + + +=============================================================================== +tflint *ale-terraform-tflint* + +g:ale_terraform_tflint_executable *g:ale_terraform_tflint_executable* + *b:ale_terraform_tflint_executable* + + Type: |String| + Default: `'tflint'` + + This variable can be changed to use a different executable for tflint. + + +g:ale_terraform_tflint_options *g:ale_terraform_tflint_options* + *b:ale_terraform_tflint_options* + Type: |String| + Default: `'-f json'` + + This variable can be changed to pass different options to tflint. Ale does + expect json output from tflint, so if you change this, you'll probably want + to include '-f json' in your new value. + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: + diff --git a/doc/ale-texinfo.txt b/doc/ale-texinfo.txt new file mode 100644 index 0000000..f8ed342 --- /dev/null +++ b/doc/ale-texinfo.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE Texinfo Integration *ale-texinfo-options* + + +=============================================================================== +write-good *ale-texinfo-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-text.txt b/doc/ale-text.txt new file mode 100644 index 0000000..a4dfa5e --- /dev/null +++ b/doc/ale-text.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE Text Integration *ale-text-options* + + +=============================================================================== +write-good *ale-text-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-typescript.txt b/doc/ale-typescript.txt index d31ac71..d83a2df 100644 --- a/doc/ale-typescript.txt +++ b/doc/ale-typescript.txt @@ -5,7 +5,7 @@ ALE TypeScript Integration *ale-typescript-options* =============================================================================== eslint *ale-typescript-eslint* -Becauase of how TypeScript compiles code to JavaScript and how interrelated +Because of how TypeScript compiles code to JavaScript and how interrelated the two languages are, the `eslint` linter for TypeScript uses the JavaScript options for `eslint` too. See: |ale-javascript-eslint|. diff --git a/doc/ale-vim-help.txt b/doc/ale-vim-help.txt new file mode 100644 index 0000000..3cbe20d --- /dev/null +++ b/doc/ale-vim-help.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE Vim help Integration *ale-vim-help-options* + + +=============================================================================== +write-good *ale-vim-help-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-vue.txt b/doc/ale-vue.txt new file mode 100644 index 0000000..937b603 --- /dev/null +++ b/doc/ale-vue.txt @@ -0,0 +1,11 @@ +=============================================================================== +ALE Vue Integration *ale-vue-options* + + +=============================================================================== +prettier *ale-vue-prettier* + +See |ale-javascript-prettier| for information about the available options. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-xhtml.txt b/doc/ale-xhtml.txt new file mode 100644 index 0000000..3cc639e --- /dev/null +++ b/doc/ale-xhtml.txt @@ -0,0 +1,12 @@ +=============================================================================== +ALE XHTML Integration *ale-xhtml-options* + + +=============================================================================== +write-good *ale-xhtml-write-good* + +See |ale-write-good-options| + + +=============================================================================== +vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index fb0b5a7..2e98cd7 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -10,10 +10,15 @@ CONTENTS *ale-contents* 2. Supported Languages & Tools..........|ale-support| 3. Linting..............................|ale-lint| 4. Fixing Problems......................|ale-fix| - 5. Completion...........................|ale-completion| + 5. Language Server Protocol Support.....|ale-lsp| + 5.1 Completion........................|ale-completion| + 5.2 Go To Definition..................|ale-go-to-definition| 6. Global Options.......................|ale-options| 6.1 Highlights........................|ale-highlights| + 6.2 Options for write-good Linter.....|ale-write-good-options| 7. Integration Documentation............|ale-integrations| + asciidoc..............................|ale-asciidoc-options| + write-good..........................|ale-asciidoc-write-good| asm...................................|ale-asm-options| gcc.................................|ale-asm-gcc| awk...................................|ale-awk-options| @@ -23,9 +28,12 @@ CONTENTS *ale-contents* clang-format........................|ale-c-clangformat| clangtidy...........................|ale-c-clangtidy| cppcheck............................|ale-c-cppcheck| + flawfinder..........................|ale-c-flawfinder| gcc.................................|ale-c-gcc| chef..................................|ale-chef-options| foodcritic..........................|ale-chef-foodcritic| + clojure...............................|ale-clojure-options| + joker...............................|ale-clojure-joker| cmake.................................|ale-cmake-options| cmakelint...........................|ale-cmake-cmakelint| cpp...................................|ale-cpp-options| @@ -35,6 +43,7 @@ CONTENTS *ale-contents* clangtidy...........................|ale-cpp-clangtidy| cppcheck............................|ale-cpp-cppcheck| cpplint.............................|ale-cpp-cpplint| + flawfinder..........................|ale-cpp-flawfinder| gcc.................................|ale-cpp-gcc| c#....................................|ale-cs-options| mcs.................................|ale-cs-mcs| @@ -48,6 +57,9 @@ CONTENTS *ale-contents* dartanalyzer........................|ale-dart-dartanalyzer| dockerfile............................|ale-dockerfile-options| hadolint............................|ale-dockerfile-hadolint| + elixir................................|ale-elixir-options| + mix.................................|ale-elixir-mix| + dialyxir............................|ale-elixir-dialyxir| elm...................................|ale-elm-options| elm-format..........................|ale-elm-elm-format| elm-make............................|ale-elm-elm-make| @@ -55,33 +67,48 @@ CONTENTS *ale-contents* erlc................................|ale-erlang-erlc| syntaxerl...........................|ale-erlang-syntaxerl| eruby.................................|ale-eruby-options| + fish..................................|ale-fish-options| fortran...............................|ale-fortran-options| gcc.................................|ale-fortran-gcc| + fountain..............................|ale-fountain-options| fusionscript..........................|ale-fuse-options| fusion-lint.........................|ale-fuse-fusionlint| + git commit............................|ale-gitcommit-options| + gitlint.............................|ale-gitcommit-gitlint| glsl..................................|ale-glsl-options| glslang.............................|ale-glsl-glslang| + glslls..............................|ale-glsl-glslls| go....................................|ale-go-options| + gobuild.............................|ale-go-gobuild| gofmt...............................|ale-go-gofmt| gometalinter........................|ale-go-gometalinter| + staticcheck.........................|ale-go-staticcheck| graphql...............................|ale-graphql-options| + eslint..............................|ale-graphql-eslint| gqlint..............................|ale-graphql-gqlint| + prettier............................|ale-graphql-prettier| handlebars............................|ale-handlebars-options| ember-template-lint.................|ale-handlebars-embertemplatelint| haskell...............................|ale-haskell-options| + brittany............................|ale-haskell-brittany| + ghc.................................|ale-haskell-ghc| hdevtools...........................|ale-haskell-hdevtools| + hfmt................................|ale-haskell-hfmt| stack-build.........................|ale-haskell-stack-build| html..................................|ale-html-options| htmlhint............................|ale-html-htmlhint| tidy................................|ale-html-tidy| + write-good..........................|ale-html-write-good| idris.................................|ale-idris-options| idris...............................|ale-idris-idris| java..................................|ale-java-options| checkstyle..........................|ale-java-checkstyle| javac...............................|ale-java-javac| + google-java-format..................|ale-java-google-java-format| javascript............................|ale-javascript-options| eslint..............................|ale-javascript-eslint| flow................................|ale-javascript-flow| + importjs............................|ale-javascript-importjs| jscs................................|ale-javascript-jscs| jshint..............................|ale-javascript-jshint| prettier............................|ale-javascript-prettier| @@ -90,33 +117,58 @@ CONTENTS *ale-contents* standard............................|ale-javascript-standard| xo..................................|ale-javascript-xo| json..................................|ale-json-options| + fixjson.............................|ale-json-fixjson| jsonlint............................|ale-json-jsonlint| + jq..................................|ale-json-jq| prettier............................|ale-json-prettier| kotlin................................|ale-kotlin-options| kotlinc.............................|ale-kotlin-kotlinc| ktlint..............................|ale-kotlin-ktlint| + latex.................................|ale-latex-options| + write-good..........................|ale-latex-write-good| less..................................|ale-less-options| + lessc...............................|ale-less-lessc| prettier............................|ale-less-prettier| + stylelint...........................|ale-less-stylelint| llvm..................................|ale-llvm-options| llc.................................|ale-llvm-llc| lua...................................|ale-lua-options| + luac................................|ale-lua-luac| luacheck............................|ale-lua-luacheck| + markdown..............................|ale-markdown-options| + mdl.................................|ale-markdown-mdl| + prettier............................|ale-markdown-prettier| + write-good..........................|ale-markdown-write-good| + nroff.................................|ale-nroff-options| + write-good..........................|ale-nroff-write-good| objc..................................|ale-objc-options| clang...............................|ale-objc-clang| objcpp................................|ale-objcpp-options| clang...............................|ale-objcpp-clang| ocaml.................................|ale-ocaml-options| merlin..............................|ale-ocaml-merlin| + ols.................................|ale-ocaml-ols| perl..................................|ale-perl-options| perl................................|ale-perl-perl| perlcritic..........................|ale-perl-perlcritic| php...................................|ale-php-options| hack................................|ale-php-hack| + hackfmt.............................|ale-php-hackfmt| langserver..........................|ale-php-langserver| + phan................................|ale-php-phan| phpcbf..............................|ale-php-phpcbf| phpcs...............................|ale-php-phpcs| phpmd...............................|ale-php-phpmd| phpstan.............................|ale-php-phpstan| + php-cs-fixer........................|ale-php-php-cs-fixer| + po....................................|ale-po-options| + write-good..........................|ale-po-write-good| + pod...................................|ale-pod-options| + write-good..........................|ale-pod-write-good| + pony..................................|ale-pony-options| + ponyc...............................|ale-pony-ponyc| + proto.................................|ale-proto-options| + protoc-gen-lint.....................|ale-proto-protoc-gen-lint| pug...................................|ale-pug-options| puglint.............................|ale-pug-puglint| puppet................................|ale-puppet-options| @@ -126,22 +178,30 @@ CONTENTS *ale-contents* flake8..............................|ale-python-flake8| isort...............................|ale-python-isort| mypy................................|ale-python-mypy| + prospector..........................|ale-python-prospector| pycodestyle.........................|ale-python-pycodestyle| pylint..............................|ale-python-pylint| + pyls................................|ale-python-pyls| yapf................................|ale-python-yapf| r.....................................|ale-r-options| lintr...............................|ale-r-lintr| reasonml..............................|ale-reasonml-options| merlin..............................|ale-reasonml-merlin| + ols.................................|ale-reasonml-ols| + refmt...............................|ale-reasonml-refmt| + restructuredtext......................|ale-restructuredtext-options| + write-good..........................|ale-restructuredtext-write-good| ruby..................................|ale-ruby-options| brakeman............................|ale-ruby-brakeman| rails_best_practices................|ale-ruby-rails_best_practices| reek................................|ale-ruby-reek| rubocop.............................|ale-ruby-rubocop| + ruby................................|ale-ruby-ruby| rust..................................|ale-rust-options| cargo...............................|ale-rust-cargo| rls.................................|ale-rust-rls| rustc...............................|ale-rust-rustc| + rustfmt.............................|ale-rust-rustfmt| sass..................................|ale-sass-options| stylelint...........................|ale-sass-stylelint| scala.................................|ale-scala-options| @@ -152,9 +212,11 @@ CONTENTS *ale-contents* sh....................................|ale-sh-options| shell...............................|ale-sh-shell| shellcheck..........................|ale-sh-shellcheck| + shfmt...............................|ale-sh-shfmt| sml...................................|ale-sml-options| smlnj...............................|ale-sml-smlnj| solidity..............................|ale-solidity-options| + solhint.............................|ale-solidity-solhint| solium..............................|ale-solidity-solium| spec..................................|ale-spec-options| rpmlint.............................|ale-spec-rpmlint| @@ -162,9 +224,15 @@ CONTENTS *ale-contents* stylelint...........................|ale-stylus-stylelint| tcl...................................|ale-tcl-options| nagelfar............................|ale-tcl-nagelfar| + terraform.............................|ale-terraform-options| + tflint..............................|ale-terraform-tflint| tex...................................|ale-tex-options| chktex..............................|ale-tex-chktex| lacheck.............................|ale-tex-lacheck| + texinfo...............................|ale-texinfo-options| + write-good..........................|ale-texinfo-write-good| + text..................................|ale-text-options| + write-good..........................|ale-text-write-good| thrift................................|ale-thrift-options| thrift..............................|ale-thrift-thrift| typescript............................|ale-typescript-options| @@ -177,6 +245,12 @@ CONTENTS *ale-contents* verilator...........................|ale-verilog-verilator| vim...................................|ale-vim-options| vint................................|ale-vim-vint| + vim help..............................|ale-vim-help-options| + write-good..........................|ale-vim-help-write-good| + vue...................................|ale-vue-options| + prettier............................|ale-vue-prettier| + xhtml.................................|ale-xhtml-options| + write-good..........................|ale-xhtml-write-good| xml...................................|ale-xml-options| xmllint.............................|ale-xml-xmllint| yaml..................................|ale-yaml-options| @@ -223,82 +297,97 @@ Notes: * ASM: `gcc` * Ansible: `ansible-lint` -* AsciiDoc: `proselint` +* API Blueprint: `drafter` +* AsciiDoc: `alex`!!, `proselint`, `redpen`, `write-good` * Awk: `gawk` -* Bash: `shell` (-n flag), `shellcheck` -* Bourne Shell: `shell` (-n flag), `shellcheck` -* C: `cppcheck`, `cpplint`!!, `gcc`, `clang`, `clangtidy`!!, `clang-format` -* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `cppcheck`, `cpplint`!!, `gcc`, `clang-format` +* Bash: `shell` (-n flag), `shellcheck`, `shfmt` +* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt` +* C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc` +* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `flawfinder`, `gcc` * CUDA: `nvcc`!! * C#: `mcs`, `mcsc`!! * Chef: `foodcritic` +* Clojure: `joker` * CMake: `cmakelint` * CoffeeScript: `coffee`, `coffeelint` * Crystal: `crystal`!! -* CSS: `csslint`, `stylelint`, `prettier` +* CSS: `csslint`, `prettier`, `stylelint` * Cython (pyrex filetype): `cython` * D: `dmd` -* Dart: `dartanalyzer` +* Dafny: `dafny`!! +* Dart: `dartanalyzer`!!, `language_server` * Dockerfile: `hadolint` -* Elixir: `credo`, `dogma`!! +* Elixir: `credo`, `dialyxir`, `dogma`!! * Elm: `elm-format, elm-make` -* Erb: `erb`, `erubis` +* Erb: `erb`, `erubi`, `erubis` * Erlang: `erlc`, `SyntaxErl` +* Fish: `fish` (-n flag) * Fortran: `gcc` +* Fountain: `proselint` * FusionScript: `fusion-lint` -* GLSL: glslang -* Go: `gofmt`, `go vet`, `golint`, `gometalinter`!!, `go build`!!, `gosimple`, `staticcheck` -* GraphQL: `gqlint` +* Git Commit Messages: `gitlint` +* GLSL: glslang, `glslls` +* Go: `gofmt`, `goimports`, `go vet`!!, `golint`, `gotype`, `gometalinter`!!, `go build`!!, `gosimple`!!, `staticcheck`!! +* GraphQL: `eslint`, `gqlint`, `prettier` * Haml: `haml-lint` * Handlebars: `ember-template-lint` -* Haskell: `ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools` -* HTML: `HTMLHint`, `proselint`, `tidy` +* Haskell: `brittany`, `ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`, `hfmt` +* HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `write-good` * Idris: `idris` -* Java: `checkstyle`, `javac` -* JavaScript: `eslint`, `jscs`, `jshint`, `flow`, `prettier`, `prettier-eslint` >= 4.2.0, `prettier-standard`, `standard`, `xo` -* JSON: `jsonlint`, `prettier` +* Java: `checkstyle`, `javac`, `google-java-format` +* JavaScript: `eslint`, `flow`, `jscs`, `jshint`, `prettier`, `prettier-eslint`, `prettier-standard`, `standard`, `xo` +* JSON: `fixjson`, `jsonlint`, `jq`, `prettier` * Kotlin: `kotlinc`, `ktlint` -* LaTeX (tex): `chktex`, `lacheck`, `proselint` +* LaTeX (tex): `alex`!!, `chktex`, `lacheck`, `proselint`, `redpen`, `vale`, `write-good` +* Less: `lessc`, `prettier`, `stylelint` * LLVM: `llc` -* Lua: `luacheck` -* Markdown: `mdl`, `proselint`, `vale`, `remark-lint` +* Lua: `luac`, `luacheck` +* Mail: `alex`!!, `proselint`, `vale` +* Make: `checkmake` +* Markdown: `alex`!!, `mdl`, `prettier`, `proselint`, `redpen`, `remark-lint`, `vale`, `write-good` * MATLAB: `mlint` * Nim: `nim check`!! * nix: `nix-instantiate` -* nroff: `proselint` +* nroff: `alex`!!, `proselint`, `write-good` * Objective-C: `clang` * Objective-C++: `clang` -* OCaml: `merlin` (see |ale-ocaml-merlin|) +* OCaml: `merlin` (see |ale-ocaml-merlin|), `ols` * Perl: `perl -c`, `perl-critic` -* PHP: `hack`, `langserver`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf` -* Pod: `proselint` +* PHP: `hack`, `hackfmt`, `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf`, `php-cs-fixer` +* PO: `alex`!!, `msgfmt`, `proselint`, `write-good` +* Pod: `alex`!!, `proselint`, `write-good` +* Pony: `ponyc` +* proto: `protoc-gen-lint` * Pug: `pug-lint` * Puppet: `puppet`, `puppet-lint` -* Python: `autopep8`, `flake8`, `isort`, `mypy`, `pycodestyle`, `pylint`!!, `yapf` +* Python: `autopep8`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pylint`!!, `yapf` * R: `lintr` -* ReasonML: `merlin` -* reStructuredText: `proselint` +* ReasonML: `merlin`, `ols`, `refmt` +* reStructuredText: `alex`!!, `proselint`, `redpen`, `rstcheck`, `vale`, `write-good` +* Re:VIEW: `redpen` * RPM spec: `rpmlint` * Ruby: `brakeman`, `rails_best_practices`!!, `reek`, `rubocop`, `ruby` -* Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|) +* Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|), `rustfmt` * SASS: `sass-lint`, `stylelint` -* SCSS: `sass-lint`, `scss-lint`, `stylelint`, `prettier` +* SCSS: `prettier`, `sass-lint`, `scss-lint`, `stylelint` * Scala: `scalac`, `scalastyle` * Slim: `slim-lint` * SML: `smlnj` -* Solidity: `solium` +* Solidity: `solhint, solium` * Stylus: `stylelint` * SQL: `sqlint` * Swift: `swiftlint`, `swiftformat` * Tcl: `nagelfar`!! -* Texinfo: `proselint` -* Text^: `proselint`, `vale` +* Terraform: `tflint` +* Texinfo: `alex`!!, `proselint`, `write-good` +* Text^: `alex`!!, `proselint`, `vale`, `write-good`, `redpen` * Thrift: `thrift` -* TypeScript: `eslint`, `tslint`, `tsserver`, `typecheck`, `prettier` +* TypeScript: `eslint`, `prettier`, `tslint`, `tsserver`, `typecheck` * Verilog: `iverilog`, `verilator` * Vim: `vint` -* Vim help^: `proselint` -* XHTML: `proselint` +* Vim help^: `alex`!!, `proselint`, `write-good` +* Vue: `prettier` +* XHTML: `alex`!!, `proselint`, `write-good` * XML: `xmllint` * YAML: `swaglint`, `yamllint` @@ -365,7 +454,8 @@ including disabling ALE for certain buffers with |b:ale_enabled|. The |g:ale_pattern_options| setting can be used to configure files differently based on regular expressions for filenames. For configuring entire projects, the buffer-local options can be used with external plugins for reading Vim -project configuration files. +project configuration files. Buffer-local settings can also be used in +ftplugin files for different filetypes. =============================================================================== @@ -408,6 +498,47 @@ are supported for running the commands. for commands which need to modify some file on disk in order to fix files. + `process_with` An optional callback for post-processing. + + The callback must accept two arguments, + `(buffer, output)`, which can be used for converting + the output from a command into lines to replace the + buffer's contents with. + + A |List| of |String|s must be returned. + + `chain_with` An optional key for defining a callback to call next. + + The callback must accept two or three arguments, + `(buffer, output)` or `(buffer, output, input)` . + Functions receiving a variable number of arguments will + only receive the first two values. The `output` argument + will contain the lines of output from the command run. + The `input` argument is the List of lines for the + buffer, after applying any previous fixers. + + The callback must return the same values returned for + any fixer function. This allows fixer functions to be + chained recursively. + + When the command string returned for a fixer is an empty + string, the next command in the chain will still be run. + This allows commands to be skipped, like version checks + that are cached. An empty List will be passed to the + next callback in the chain for the `output`. + + `read_buffer` An optional key for disabling reading the buffer. + + When set to `0`, ALE will not pipe the buffer's data + into the command via stdin. This option is ignored and + the buffer is not read when `read_temporary_file` is + `1`. + + This option defaults to `0` when `chain_with` is defined + as anything other than `v:null`, and defaults to `1` + otherwise. This is so earlier commands in a chain + do not receive the buffer's data by default. + *ale-fix-configuration* Synchronous functions and asynchronous jobs will be run in a sequence for @@ -428,6 +559,18 @@ upon some lines immediately, then run `eslint` from the ALE registry, and then call a lambda function which will remove every single line comment from the file. +For buffer-local settings, such as in |g:ale_pattern_options| or in ftplugin +files, a |List| may be used for configuring the fixers instead. +> + " Same as the above, only a List can be used instead of a Dictionary. + let b:ale_fixers = [ + \ 'DoSomething', + \ 'eslint', + \ {buffer, lines -> filter(lines, 'v:val !=~ ''^\s*//''')}, + \] + + ALEFix +< For convenience, a plug mapping is defined for |ALEFix|, so you can set up a keybind easily for fixing files. > @@ -441,17 +584,30 @@ by default. =============================================================================== -5. Completion *ale-completion* +5. Language Server Protocol Support *ale-lsp* -ALE offers some limited support for automatic completion of code while you -type. Completion is only supported via Language Server Protocol servers which -ALE can connect to for linting, which can offer good built-in support for -suggesting completion information. ALE will only suggest symbols for -completion for LSP linters that are enabled. +ALE offers some support for integrating with Language Server Protocol (LSP) +servers. LSP linters can be used in combination with any other linter, and +will automatically connect to LSP servers when needed. ALE also supports +`tsserver` for TypeScript, which uses a different but very similar protocol. + +ALE supports the following LSP/tsserver features. + +1. Diagnostics/linting - Enabled via selecting linters as usual. +2. Completion (Only for tsserver) +3. Go to definition + + +------------------------------------------------------------------------------- +5.1 Completion *ale-completion* NOTE: At the moment, only `tsserver` for TypeScript code is supported for completion. +ALE offers limited support for automatic completion of code while you type. +Completion is only supported while a least one LSP linter is enabled. ALE +will only suggest symbols provided by the LSP servers. + Suggestions will be made while you type after completion is enabled. Completion can be enabled by setting |g:ale_completion_enabled| to `1`. The delay for completion can be configured with |g:ale_completion_delay|. ALE will @@ -459,6 +615,17 @@ only suggest so many possible matches for completion. The maximum number of items can be controlled with |g:ale_completion_max_suggestions|. +------------------------------------------------------------------------------- +5.2 Go To Definition *ale-go-to-definition* + +ALE supports jumping to the files and locations where symbols are defined +through any enabled LSP linters. The locations ALE will jump to depend on the +information returned by LSP servers. The following commands are supported: + +|ALEGoToDefinition| - Open the definition of the symbol under the cursor. +|ALEGoToDefinitionInTab| - The same, but for opening the file in a new tab. + + =============================================================================== 6. Global Options *ale-options* @@ -473,6 +640,19 @@ g:airline#extensions#ale#enabled *g:airline#extensions#ale#enabled* |airline#extensions#ale#warning_symbol|. +g:ale_cache_executable_check_failures *g:ale_cache_executable_check_failures* + + Type: |Number| + Default: `0` + + When set to `1`, ALE will cache failing executable checks for linters. By + default, only executable checks which succeed will be cached. + + When this option is set to `1`, Vim will have to be restarted after new + executables are installed for ALE to be able to run linters for those + executables. + + g:ale_change_sign_column_color *g:ale_change_sign_column_color* Type: |Number| @@ -492,6 +672,39 @@ g:ale_change_sign_column_color *g:ale_change_sign_column_color* windows. +g:ale_command_wrapper *g:ale_command_wrapper* + *b:ale_command_wrapper* + Type: |String| + Default: `''` + + An option for wrapping all commands that ALE runs, for linters, fixers, + and LSP commands. This option can be set globally, or for specific buffers. + + This option can be used to apply nice to all commands. For example: > + + " Prefix all commands with nice. + let g:ale_command_wrapper = 'nice -n5' +< + Use the |ALEInfo| command to view the commands that are run. All of the + arguments for commands will be put on the end of the wrapped command by + default. A `%*` marker can be used to spread the arguments in the wrapped + command. > + + " Has the same effect as the above. + let g:ale_command_wrapper = 'nice -n5 %*' +< + + For passing all of the arguments for a command as one argument to a wrapper, + `%@` can be used instead. > + + " Will result in say: /bin/bash -c 'other-wrapper -c "some command" -x' + let g:ale_command_wrapper = 'other-wrapper -c %@ -x' +< + For commands including `&&` or `;`, only the last command in the list will + be passed to the wrapper. `&&` is most commonly used in ALE to change the + working directory before running a command. + + g:ale_completion_delay *g:ale_completion_delay* Type: |Number| @@ -558,33 +771,58 @@ g:ale_echo_delay *g:ale_echo_delay* g:ale_echo_msg_error_str *g:ale_echo_msg_error_str* Type: |String| - Default: `Error` + Default: `'Error'` - The string used for error severity in the echoed message. - Note |g:ale_echo_cursor| should be set to 1 - Note |g:ale_echo_msg_format| should contain the `%severity%` handler + The string used for `%severity%` for errors. See |g:ale_echo_msg_format| g:ale_echo_msg_format *g:ale_echo_msg_format* +b:ale_echo_msg_format *b:ale_echo_msg_format* Type: |String| - Default: `%s` + Default: `'%code: %%s'` - This variable defines the format of the echoed message. The `%s` is the - error message itself, and it can contain the following handlers: - - `%linter%` for linter's name - - `%severity%` for the type of severity - Note |g:ale_echo_cursor| should be setted to 1 + This variable defines a message format for echoed messages. The following + sequences of characters will be replaced. + + `%s` - replaced with the text for the problem + `%...code...% `- replaced with the error code + `%linter%` - replaced with the name of the linter + `%severity%` - replaced withe severity of the problem + + The strings for `%severity%` can be configured with the following options. + + |g:ale_echo_msg_error_str| - Defaults to `'Error'` + |g:ale_echo_msg_info_str| - Defaults to `'Info'` + |g:ale_echo_msg_warning_str| - Defaults to `'Warning'` + + `%code%` is replaced with the error code, and replaced with an empty string + when there is no error code. Any extra characters between the percent signs + will be printed when an error code is present. For example, a message like + `(error code): message` will be printed for `'%(code): %%s'` and simply the + message will be printed when there is no code. + + |g:ale_echo_cursor| needs to be set to 1 for messages to be displayed. + + The echo message format can also be configured separately for each buffer, + so different formats can be used for differnt languages. (Say in ftplugin + files.) + + +g:ale_echo_msg_info_str *g:ale_echo_msg_info_str* + + Type: |String| + Default: `'Info'` + + The string used for `%severity%` for info. See |g:ale_echo_msg_format| g:ale_echo_msg_warning_str *g:ale_echo_msg_warning_str* Type: |String| - Default: `Warning` + Default: `'Warning'` - The string used for warning severity in the echoed message. - Note |g:ale_echo_cursor| should be set to 1 - Note |g:ale_echo_msg_format| should contain the `%severity%` handler + The string used for `%severity%` for warnings. See |g:ale_echo_msg_format| g:ale_emit_conflict_warnings *g:ale_emit_conflict_warnings* @@ -595,6 +833,11 @@ g:ale_emit_conflict_warnings *g:ale_emit_conflict_warnings* When set to `0`, ALE will not emit any warnings on startup about conflicting plugins. ALE will probably not work if other linting plugins are installed. + When this option is set to `1`, ALE will add its `after` directory to + |runtimepath| automatically, so the checks can be applied. Setting this + option to `0` before ALE is loaded will prevent ALE from modifying + |runtimepath|. + g:ale_enabled *g:ale_enabled* *b:ale_enabled* @@ -627,9 +870,12 @@ g:ale_fixers *g:ale_fixers* See |ale-fix| for more information. This variable can be overridden with variables in each buffer. + `b:ale_fixers` can be set to a |List| of callbacks instead, which can be + more convenient. g:ale_fix_on_save *g:ale_fix_on_save* +b:ale_fix_on_save *b:ale_fix_on_save* Type: |Number| Default: `0` @@ -640,6 +886,9 @@ g:ale_fix_on_save *g:ale_fix_on_save* after files are fixed, only when the buffer is open, or re-opened. Changes to the file will be saved to the file on disk. + Fixing files can be disabled or enabled for individual buffers by setting + `b:ale_fix_on_save` to `0` or `1`. + g:ale_history_enabled *g:ale_history_enabled* @@ -676,10 +925,10 @@ g:ale_keep_list_window_open *g:ale_keep_list_window_open* Type: |Number| Default: `0` - When set to `1`, this option will keep the loclist or quickfix windows - event after all warnings/errors have been removed for files. By default - the loclist or quicfix windows will be closed automatically when there - are no warnings or errors. + When set to `1`, this option will keep the loclist or quickfix windows event + after all warnings/errors have been removed for files. By default the + loclist or quickfix windows will be closed automatically when there are no + warnings or errors. See |g:ale_open_list| @@ -775,9 +1024,16 @@ g:ale_lint_on_insert_leave *g:ale_lint_on_insert_leave* Type: |Number| Default: `0` - This option will make ALE run the linters whenever leaving insert mode when - it is set to `1` in your vimrc file. + When set to `1` in your vimrc file, this option will cause ALE to run + linters when you leave insert mode. + 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. > + + " Make using Ctrl+C do the same as Escape, to trigger autocmd commands + inoremap +< g:ale_linter_aliases *g:ale_linter_aliases* *b:ale_linter_aliases* @@ -813,35 +1069,44 @@ g:ale_linter_aliases *g:ale_linter_aliases* `let g:ale_linter_aliases = {'html': ['html', 'javascript', 'css']}` Note that `html` itself was included as an alias. That is because aliases - will override the original linters for the aliased filetepe. + will override the original linters for the aliased filetype. Linter aliases can be configured in each buffer with buffer-local variables. ALE will first look for aliases for filetypes in the `b:ale_linter_aliases` variable, then `g:ale_linter_aliases`, and then a default Dictionary. + `b:ale_linter_aliases` can be set to a |List|, to tell ALE to load the + linters for specific filetypes for a given buffer. > + + let b:ale_linter_aliases = ['html', 'javascript', 'css'] +< + No linters will be loaded when the buffer's filetype is empty. g:ale_linters *g:ale_linters* *b:ale_linters* Type: |Dictionary| Default: `{}` - The |g:ale_linters| option sets a |Dictionary| mapping a filetype - to a |List| of linter programs to be run when checking particular filetypes. - Only the filetypes specified in the dictionary will be limited in terms - of which linters will be run. + The |g:ale_linters| option sets a |Dictionary| mapping a filetype to a + |List| of linter programs to be run when checking particular filetypes. This |Dictionary| will be merged with a default dictionary containing the following values: > { \ 'csh': ['shell'], + \ 'go': ['gofmt', 'golint', 'go vet'], + \ 'help': [], + \ 'perl': ['perlcritic'], + \ 'python': ['flake8', 'mypy', 'pylint'], \ 'rust': ['cargo'], + \ 'spec': [], \ 'text': [], \ 'zsh': ['shell'], \} < This option can be used to enable only a particular set of linters for a - file. For example, you can enable only 'eslint' for JavaScript files: > + file. For example, you can enable only `eslint` for JavaScript files: > let g:ale_linters = {'javascript': ['eslint']} < @@ -850,14 +1115,64 @@ g:ale_linters *g:ale_linters* let g:ale_linters = {'javascript': []} < - All linters available for a given filetype can be enabled by using the - string `'all'`: > + All linters will be run for unspecified filetypes. All available linters can + be enabled explicitly for a given filetype by passing the string `'all'`, + instead of a List. > let g:ale_linters = {'c': 'all'} < Linters can be configured in each buffer with buffer-local variables. ALE will first look for linters for filetypes in the `b:ale_linters` variable, - then `g:ale_linters`, and then a default Dictionary. + then `g:ale_linters`, and then the default Dictionary mentioned above. + + `b:ale_linters` can be set to a List, or the string `'all'`. When linters + for two different filetypes share the same name, the first linter loaded + will be used. Any ambiguity can be resolved by using a Dictionary specifying + which linter to run for which filetype instead. > + + " Use ESLint for the buffer if the filetype includes 'javascript'. + let b:ale_linters = {'javascript': ['eslint'], 'html': ['tidy']} + " Use a List for the same setting. This will work in most cases. + let b:ale_linters = ['eslint', 'tidy'] + " Disable all linters for the buffer. + let b:ale_linters = [] + " Explicitly enable all available linters for the filetype. + let b:ale_linters = 'all' +< + ALE can be configured to disable all linters unless otherwise specified with + `g:ale_enabled` or `b:ale_enabled` with the option |g:ale_linters_explicit|. + + +g:ale_linters_explicit *g:ale_linters_explicit* + + Type: |Number| + Default: `0` + + When set to `1`, only the linters from |g:ale_linters| and |b:ale_linters| + will be enabled. The default behavior for ALE is to enable as many linters + as possible, unless otherwise specified. + + +g:ale_list_vertical *g:ale_list_vertical* + *b:ale_list_vertical* + Type: |Number| + Default: `0` + + When set to `1`, this will cause ALE to open any windows (loclist or + quickfix) vertically instead of horizontally (|vert| |lopen|) or (|vert| + |copen|) + + +g:ale_loclist_msg_format *g:ale_loclist_msg_format* +b:ale_loclist_msg_format *b:ale_loclist_msg_format* + + Type: |String| + Default: `g:ale_echo_msg_format` + + This option is the same as |g:ale_echo_msg_format|, but for formatting the + message used for the loclist and the quickfix list. + + The strings for configuring `%severity%` are also used for this option. g:ale_max_buffer_history_size *g:ale_max_buffer_history_size* @@ -914,10 +1229,21 @@ g:ale_open_list *g:ale_open_list* The window will be kept open until all warnings or errors are cleared, including those not set by ALE, unless |g:ale_keep_list_window_open| is set - to `1`, in which case the window will be kept open until closed manually. + to `1`, in which case the window will be kept open when no problems are + found. The window size can be configured with |g:ale_list_window_size|. + Windows can be opened vertically with |g:ale_list_vertical|. + + If you want to close the loclist window automatically when the buffer is + closed, you can set up the following |autocmd| command: > + + augroup CloseLoclistWindowGroup + autocmd! + autocmd QuitPre * if empty(&buftype) | lclose | endif + augroup END +< g:ale_pattern_options *g:ale_pattern_options* @@ -928,21 +1254,25 @@ g:ale_pattern_options *g:ale_pattern_options* buffer variables. This option can be set to automatically configure different settings for different files. For example: > + " Use just ESLint for linting and fixing files which end in '.foo.js' let g:ale_pattern_options = { \ '\.foo\.js$': { - \ 'ale_linters': {'javascript': ['eslint']}, + \ 'ale_linters': ['eslint'], + \ 'ale_fixers: ['eslint'], \ }, \} < - The above example will match any filename ending in `.foo.js`, and use - only `eslint` for checking those files by setting `b:ale_linters`. + See |b:ale_linters| and |b:ale_fixers| for information for those options. Filenames are matched with |match()|, and patterns depend on the |magic| setting, unless prefixed with the special escape sequences like `'\v'`, etc. - The patterns can match any part of a filename. The absolute path of the filename will be used for matching, taken from `expand('%:p')`. + The options for every match for the filename will be applied, with the + pattern keys sorted in alphabetical order. Options for `'zebra'` will + override the options for `'alpha'` for a filename `alpha-zebra`. + g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* @@ -950,8 +1280,9 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* Default: `!empty(g:ale_pattern_options)` This option can be used for turning the behaviour of setting - |g:ale_pattern_options| on or off. By default, setting a single key - for |g:ale_pattern_options| will turn this option on. + |g:ale_pattern_options| on or off. By default, setting a single key for + |g:ale_pattern_options| will turn this option on, as long as the setting is + configured before ALE is loaded. g:ale_set_balloons *g:ale_set_balloons* @@ -979,6 +1310,17 @@ g:ale_set_highlights *g:ale_set_highlights* |ALEStyleError| - Items with `'type': 'E'` and `'sub_type': 'style'` |ALEStyleWarning| - Items with `'type': 'W'` and `'sub_type': 'style'` + When |g:ale_set_signs| is set to `0`, the following highlights for entire + lines will be set. + + |ALEErrorLine| - All items with `'type': 'E'` + |ALEWarningLine| - All items with `'type': 'W'` + |ALEInfoLine| - All items with `'type': 'I'` + + Vim can only highlight the characters up to the last column in a buffer for + match highlights, whereas the line highlights when signs are enabled will + run to the edge of the screen. + g:ale_set_loclist *g:ale_set_loclist* @@ -1002,6 +1344,11 @@ g:ale_set_quickfix *g:ale_set_quickfix* Problems from every buffer ALE has checked will be included in the quickfix list, which can be checked with |:copen|. Problems will be de-duplicated. + This feature should not be used in combination with tools for searching for + matches and commands like |:cfdo|, as ALE will replace the quickfix list + pretty frequently. If you wish to use such tools, you should populate the + loclist instead. + g:ale_set_signs *g:ale_set_signs* @@ -1138,7 +1485,7 @@ g:ale_virtualenv_dir_names *g:ale_virtualenv_dir_names* b:ale_virtualenv_dir_names *b:ale_virtualenv_dir_names* Type: |List| - Default: `['.env', 'env', 've-py3', 've', 'virtualenv']` + Default: `['.env', 'env', 've-py3', 've', 'virtualenv', 'venv']` A list of directory names to be used when searching upwards from Python files to discover virtulenv directories with. @@ -1148,6 +1495,18 @@ b:ale_virtualenv_dir_names *b:ale_virtualenv_dir_names* directory containing the Python file to find virtualenv paths. +g:ale_warn_about_trailing_blank_lines *g:ale_warn_about_trailing_blank_lines* +b:ale_warn_about_trailing_blank_lines *b:ale_warn_about_trailing_blank_lines* + + Type: |Number| + Default: `1` + + When this option is set to `1`, warnings about trailing blank lines will be + shown. + + This option behaves similarly to |g:ale_warn_about_trailing_whitespace|. + + g:ale_warn_about_trailing_whitespace *g:ale_warn_about_trailing_whitespace* b:ale_warn_about_trailing_whitespace *b:ale_warn_about_trailing_whitespace* @@ -1155,10 +1514,9 @@ b:ale_warn_about_trailing_whitespace *b:ale_warn_about_trailing_whitespace* Default: `1` When this option is set to `1`, warnings relating to trailing whitespace on - lines will be shown in signs, the loclist, and echo messages, etc. If these - errors are found to be too irritating while edits are being made, and you - have configured Vim to automatically remove trailing whitespace, then you - can disable these warnings for some linters by setting this option to `0`. + lines will be shown. If warnings are too irritating while editing buffers, + and you have configured Vim to automatically remove trailing whitespace, + you can disable these warnings by setting this option to `0`. Not all linters may respect this option. If a linter does not, please file a bug report, and it may be possible to add such support. @@ -1196,7 +1554,10 @@ ALEErrorLine *ALEErrorLine* Default: Undefined - The highlight for lines where error signs appear. See |g:ale_set_signs|. + The highlight for an entire line where errors appear. Only the first + line for a problem will be highlighted. + + See |g:ale_set_signs| and |g:ale_set_highlights|. ALEErrorSign *ALEErrorSign* @@ -1224,7 +1585,10 @@ ALEInfoLine *ALEInfoLine* Default: Undefined - The highlight for lines where info signs appear. See |g:ale_set_signs|. + The highlight for entire lines where info messages appear. Only the first + line for a problem will be highlighted. + + See |g:ale_set_signs| and |g:ale_set_highlights|. ALEStyleError *ALEStyleError* @@ -1266,7 +1630,10 @@ ALEWarningLine *ALEWarningLine* Default: Undefined - The highlight for lines where warning signs appear. See |g:ale_set_signs|. + The highlight for entire lines where warnings appear. Only the first line + for a problem will be highlighted. + + See |g:ale_set_signs| and |g:ale_set_highlights|. ALEWarningSign *ALEWarningSign* @@ -1276,6 +1643,36 @@ ALEWarningSign *ALEWarningSign* The highlight used for warning signs. See |g:ale_set_signs|. +------------------------------------------------------------------------------- +6.2. Options for write-good *ale-write-good-options* + +The options for the write-good linter are global because it does not make +sense to have them specified on a per-language basis. + +g:ale_writegood_executable *g:ale_writegood_executable* + *b:ale_writegood_executable* + Type: |String| + Default: `'writegood'` + + See |ale-integrations-local-executables| + + +g:ale_writegood_options *g:ale_writegood_options* + *b:ale_writegood_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to writegood. + + +g:ale_writegood_use_global *g:ale_writegood_use_global* + *b:ale_writegood_use_global* + Type: |Number| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== 7. Integration Documentation *ale-integrations* @@ -1326,6 +1723,22 @@ ALEFixSuggest *ALEFixSuggest* See |ale-fix| for more information. +ALEGoToDefinition *ALEGoToDefinition* + + Jump to the definition of a symbol under the cursor using the enabled LSP + linters for the buffer. ALE will jump to a definition if an LSP server + provides a location to jump to. Otherwise, ALE will do nothing. + + A plug mapping `(ale_go_to_definition)` is defined for this command. + + +ALEGoToDefinitionInTab *ALEGoToDefinitionInTab* + + The same as |ALEGoToDefinition|, but opens results in a new tab. + + A plug mapping `(ale_go_to_definition_in_tab)` is defined for this + command. + *:ALELint* ALELint *ALELint* @@ -1379,19 +1792,45 @@ ALELast *ALELast* ALEToggle *ALEToggle* ALEEnable *ALEEnable* ALEDisable *ALEDisable* +ALEToggleBuffer *ALEToggleBuffer* +ALEEnableBuffer *ALEEnableBuffer* +ALEDisableBuffer *ALEDisableBuffer* - Enable or disable ALE, including all of its autocmd events, loclist items, - quickfix items, signs, current jobs, etc. Executing any of those commands - will change the |g:ale_enabled| variable. + `ALEToggle`, `ALEEnable`, and `ALEDisable` enable or disable ALE linting, + including all of its autocmd events, loclist items, quickfix items, signs, + current jobs, etc., globally. Executing any of these commands will change + the |g:ale_enabled| variable. - For convenience, a plug mapping `(ale_toggle)` is defined for the - |ALEToggle| command. + ALE can be disabled or enabled for only a single buffer with + `ALEToggleBuffer`, `ALEEnableBuffer`, and `ALEDisableBuffer`. Disabling ALE + for a buffer will not remove autocmd events, but will prevent ALE from + checking for problems and reporting problems for whatever buffer the + `ALEDisableBuffer` or `ALEToggleBuffer` command is executed from. These + commands can be used for temporarily disabling ALE for a buffer. These + commands will modify the |b:ale_enabled| variable. + ALE linting cannot be enabled for a single buffer when it is disabled + globally, as disabling ALE globally removes the autocmd events needed to + perform linting with. + The following plug mappings are defined, for conveniently defining keybinds: + + |ALEToggle| - `(ale_toggle)` + |ALEEnable| - `(ale_enable)` + |ALEDisable| - `(ale_disable)` + |ALEToggleBuffer| - `(ale_toggle_buffer)` + |ALEEnableBuffer| - `(ale_enable_buffer)` + |ALEDisableBuffer| - `(ale_disable_buffer)` + + For removing problems reported by ALE, but leaving ALE enabled, see + |ALEReset| and |ALEResetBuffer|. + + *:ALEDetail* ALEDetail *ALEDetail* - Show the full linter message for the current line. This will only have an - effect on lines that contain a linter message. + Show the full linter message for the current line in the preview window. + This will only have an effect on lines that contain a linter message. The + preview window can be easily closed with the `q` key. A plug mapping `(ale_detail)` is defined for this command. @@ -1414,6 +1853,33 @@ ALEInfoToClipboard *ALEInfoToClipboard* your clipboard. This might not work on every machine. +ALEReset *ALEReset* +ALEResetBuffer *ALEResetBuffer* + + `ALEReset` will remove all problems reported by ALE for all buffers. + `ALEResetBuffer` will remove all problems reported for a single buffer. + + Either command will leave ALE linting enabled, so ALE will report problems + when linting is performed again. See |ale-lint| for more information. + + The following plug mappings are defined, for conveniently defining keybinds: + + |ALEReset| - `(ale_reset)` + |ALEResetBuffer| - `(ale_reset_buffer)` + + ALE can be disabled globally or for a buffer with |ALEDisable| or + |ALEDisableBuffer|. + + +ALEStopAllLSPs *ALEStopAllLSPs* + + `ALEStopAllLSPs` will close and stop all channels and jobs for all LSP-like + clients, including tsserver, remove all of the data stored for them, and + delete all of the problems found for them, updating every linted buffer. + + This command can be used when LSP clients mess up and need to be restarted. + + =============================================================================== 9. API *ale-api* @@ -1459,9 +1925,18 @@ ale#engine#EscapeCommandPart(command_part) *ale#engine#EscapeCommandPart()* ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()* - Given a buffer number, this function will rerurn the list of warnings and - errors reported by ALE for a given buffer in the format accepted by - |setqflist()|. + Given a buffer number, this function will return the list of problems + reported by ALE for a given buffer in the format accepted by |setqflist()|. + + A reference to the buffer's list of problems will be returned. The list must + be copied before applying |map()| or |filter()|. + + +ale#engine#IsCheckingBuffer(buffer) *ale#engine#IsCheckingBuffer()* + + Given a buffer number, returns `1` when ALE is busy checking that buffer. + + This function can be used for status lines, tab names, etc. ale#engine#ManageFile(buffer, filename) *ale#engine#ManageFile()* @@ -1493,7 +1968,8 @@ ale#engine#ManageDirectory(buffer, directory) *ale#engine#ManageDirectory()* files. -ale#fix#registry#Add(name, func, filetypes, desc) *ale#fix#registry#Add()* +ale#fix#registry#Add(name, func, filetypes, desc, [aliases]) + *ale#fix#registry#Add()* Given a |String| `name` for a name to add to the registry, a |String| `func` for a function name, a |List| `filetypes` for a list of filetypes to @@ -1503,6 +1979,11 @@ ale#fix#registry#Add(name, func, filetypes, desc) *ale#fix#registry#Add()* The `name` can then be used for |g:ale_fixers| in place of the function name, and suggested for fixing files. + An optional |List| of |String|s for aliases can be passed as the `aliases` + argument. These aliases can also be used for looking up a fixer function. + ALE will search for fixers in the registry first by `name`, then by their + `aliases`. + ale#linter#Define(filetype, linter) *ale#linter#Define()* @@ -1674,7 +2155,7 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* the file on disk, including |g:ale_lint_on_enter| and |g:ale_lint_on_save|. Linters with this option set to `1` will also be run when linters are run - manually, per |ALELint-autocmd|. + manually, per |ALELintPost-autocmd|. When this option is set to `1`, `read_buffer` will be set automatically to `0`. The two options cannot @@ -1808,20 +2289,47 @@ ale#statusline#Count(buffer) *ale#statusline#Count()* `total` -> The total number of problems. -ALELint *ALELint-autocmd* +b:ale_linted *b:ale_linted* - This |User| autocommand is triggered by ALE every time it completes a lint - cycle. It can be used to update statuslines, send notifications, or - complete any other operation that needs to be done after linting has been - performed. + `b:ale_linted` is set to the number of times a buffer has been checked by + ALE after all linters for one lint cycle have finished checking a buffer. + This variable may not be defined until ALE first checks a buffer, so it + should be accessed with |get()| or |getbufvar()|. For example: > - For example, you can echo a message when linting is complete like so: - > - autocmd User ALELint unsilent echom 'ALE run!' + " Print a message indicating how many times ALE has checked this buffer. + echo 'ALE has checked this buffer ' . get(b:, 'ale_linted') . ' time(s).' + " Print 'checked' using getbufvar() if a buffer has been checked. + echo getbufvar(bufnr(''), 'ale_linted', 0) > 0 ? 'checked' : 'not checked' < + +ALELintPre *ALELintPre-autocmd* +ALELintPost *ALELintPost-autocmd* + + These |User| autocommands are triggered before and after every lint cycle. + They can be used to update statuslines, send notifications, etc. The autocmd commands are run with |:silent|, so |:unsilent| is required for echoing messges. + For example to change the color of the statusline while the linter is + running: +> + augroup ALEProgress + autocmd! + autocmd User ALELintPre hi Statusline ctermfg=darkgrey + autocmd User ALELintPOST hi Statusline ctermfg=NONE + augroup end +< + Or to display the progress in the statusline: +> + let s:ale_running = 0 + let l:stl .= '%{s:ale_running ? "[linting]" : ""}' + augroup ALEProgress + autocmd! + autocmd User ALELintPre let s:ale_running = 1 | redrawstatus + autocmd User ALELintPost let s:ale_running = 0 | redrawstatus + augroup end +< + =============================================================================== 10. Special Thanks *ale-special-thanks* diff --git a/ftplugin/ale-preview.vim b/ftplugin/ale-preview.vim new file mode 100644 index 0000000..ffbffbd --- /dev/null +++ b/ftplugin/ale-preview.vim @@ -0,0 +1,2 @@ +" Close the ALEPreviewWindow window with the q key. +noremap q :q! diff --git a/plugin/ale.vim b/plugin/ale.vim index a0d9b27..1aa3582 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -24,16 +24,28 @@ endif if !s:has_features " Only output a warning if editing some special files. if index(['', 'gitcommit'], &filetype) == -1 - echoerr 'ALE requires NeoVim >= 0.1.5 or Vim 8 with +timers +job +channel' - echoerr 'Please update your editor appropriately.' + execute 'echoerr ''ALE requires NeoVim >= 0.1.5 or Vim 8 with +timers +job +channel''' + execute 'echoerr ''Please update your editor appropriately.''' endif " Stop here, as it won't work. finish endif -" Add the after directory to the runtimepath -let &runtimepath .= ',' . expand(':p:h:h') . '/after' +if has('nvim') && !has('nvim-0.2.0') && !get(g:, 'ale_use_deprecated_neovim') + execute 'echom ''ALE support for NeoVim versions below 0.2.0 is deprecated.''' + execute 'echom ''Use `let g:ale_use_deprecated_neovim = 1` to silence this warning for now.''' +endif + +" This flag can be set to 0 to disable emitting conflict warnings. +let g:ale_emit_conflict_warnings = get(g:, 'ale_emit_conflict_warnings', 1) + +if g:ale_emit_conflict_warnings +\&& match(&runtimepath, '[/\\]ale[/\\]after') < 0 + " Add the after directory to the runtimepath + " This is only done if the after directory isn't already in runtimepath + let &runtimepath .= ',' . expand(':p:h:h') . '/after' +endif " Set this flag so that other plugins can use it, like airline. let g:loaded_ale = 1 @@ -44,9 +56,6 @@ if has('unix') && empty($TMPDIR) let $TMPDIR = '/tmp' endif -" This flag can be set to 0 to disable emitting conflict warnings. -let g:ale_emit_conflict_warnings = get(g:, 'ale_emit_conflict_warnings', 1) - " This global variable is used internally by ALE for tracking information for " each buffer which linters are being run against. let g:ale_buffer_info = {} @@ -64,7 +73,9 @@ let g:ale_filetype_blacklist = [ \] " This Dictionary configures which linters are enabled for which languages. -let g:ale_linters = get(g:, 'ale_linters', {}) +call ale#Set('linters', {}) +" This option can be changed to only enable explicitly selected linters. +call ale#Set('linters_explicit', 0) " This Dictionary configures which functions will be used for fixing problems. let g:ale_fixers = get(g:, 'ale_fixers', {}) @@ -112,6 +123,9 @@ let g:ale_open_list = get(g:, 'ale_open_list', 0) " This flag dictates if ale keeps open loclist even if there is no error in loclist let g:ale_keep_list_window_open = get(g:, 'ale_keep_list_window_open', 0) +" This flag dictates that quickfix windows should be opened vertically +let g:ale_list_vertical = get(g:, 'ale_list_vertical', 0) + " The window size to set for the quickfix and loclist windows call ale#Set('list_window_size', 10) @@ -144,13 +158,14 @@ let g:ale_sign_offset = get(g:, 'ale_sign_offset', 1000000) " This flag can be set to 1 to keep sign gutter always open let g:ale_sign_column_always = get(g:, 'ale_sign_column_always', 0) -" String format for the echoed message -" A %s is mandatory -" It can contain 2 handlers: %linter%, %severity% -let g:ale_echo_msg_format = get(g:, 'ale_echo_msg_format', '%s') +" A string format for the echoed message +call ale#Set('echo_msg_format', '%code: %%s') +" The same for the loclist. +call ale#Set('loclist_msg_format', g:ale_echo_msg_format) " Strings used for severity in the echoed message let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error') +let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info') let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning') " This flag can be set to 0 to disable echoing when the cursor moves. @@ -168,8 +183,9 @@ let g:ale_statusline_format = get(g:, 'ale_statusline_format', \) " This flag can be set to 0 to disable warnings for trailing whitespace -let g:ale_warn_about_trailing_whitespace = -\ get(g:, 'ale_warn_about_trailing_whitespace', 1) +call ale#Set('warn_about_trailing_whitespace', 1) +" This flag can be set to 0 to disable warnings for trailing blank lines +call ale#Set('warn_about_trailing_blank_lines', 1) " A flag for controlling the maximum size of the command history to store. let g:ale_max_buffer_history_size = get(g:, 'ale_max_buffer_history_size', 20) @@ -180,6 +196,10 @@ let g:ale_history_enabled = get(g:, 'ale_history_enabled', 1) " A flag for storing the full output of commands in the history. let g:ale_history_log_output = get(g:, 'ale_history_log_output', 1) +" A flag for caching failed executable checks. +" This is off by default, because it will cause problems. +call ale#Set('cache_executable_check_failures', 0) + " A dictionary mapping regular expression patterns to arbitrary buffer " variables to be set. Useful for configuration ALE based on filename " patterns. @@ -197,136 +217,8 @@ call ale#Set('completion_enabled', 0) call ale#Set('completion_delay', 100) call ale#Set('completion_max_suggestions', 50) -function! ALEInitAuGroups() abort - " This value used to be a Boolean as a Number, and is now a String. - let l:text_changed = '' . g:ale_lint_on_text_changed - - augroup ALEPatternOptionsGroup - autocmd! - if g:ale_enabled && g:ale_pattern_options_enabled - autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions() - endif - augroup END - - augroup ALERunOnTextChangedGroup - autocmd! - if g:ale_enabled - if l:text_changed is? 'always' || l:text_changed is# '1' - autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay) - elseif l:text_changed is? 'normal' - autocmd TextChanged * call ale#Queue(g:ale_lint_delay) - elseif l:text_changed is? 'insert' - autocmd TextChangedI * call ale#Queue(g:ale_lint_delay) - endif - endif - augroup END - - augroup ALERunOnEnterGroup - autocmd! - if g:ale_enabled - " Handle everything that needs to happen when buffers are entered. - autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand(''))) - endif - if g:ale_enabled && g:ale_lint_on_enter - autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand(''))) - " Track when the file is changed outside of Vim. - autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''))) - endif - augroup END - - augroup ALERunOnFiletypeChangeGroup - autocmd! - if g:ale_enabled && g:ale_lint_on_filetype_changed - " Only start linting if the FileType actually changes after - " opening a buffer. The FileType will fire when buffers are opened. - autocmd FileType * call ale#events#FileTypeEvent( - \ str2nr(expand('')), - \ expand('') - \) - endif - augroup END - - augroup ALERunOnSaveGroup - autocmd! - if (g:ale_enabled && g:ale_lint_on_save) || g:ale_fix_on_save - autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand(''))) - endif - augroup END - - augroup ALERunOnInsertLeave - autocmd! - if g:ale_enabled && g:ale_lint_on_insert_leave - autocmd InsertLeave * call ale#Queue(0) - endif - augroup END - - augroup ALECursorGroup - autocmd! - if g:ale_enabled && g:ale_echo_cursor - autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay() - " 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 * call ale#cursor#EchoCursorWarning() - endif - augroup END - - if !g:ale_enabled - if !g:ale_fix_on_save - augroup! ALERunOnSaveGroup - endif - - augroup! ALEPatternOptionsGroup - augroup! ALERunOnTextChangedGroup - augroup! ALERunOnEnterGroup - augroup! ALERunOnInsertLeave - augroup! ALECursorGroup - endif -endfunction - -function! s:ALEToggle() abort - let g:ale_enabled = !get(g:, 'ale_enabled') - - if g:ale_enabled - " Set pattern options again, if enabled. - if g:ale_pattern_options_enabled - call ale#pattern_options#SetOptions() - endif - - " Lint immediately, including running linters against the file. - call ale#Queue(0, 'lint_file') - - if g:ale_set_balloons - call ale#balloon#Enable() - endif - else - 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 - - " Remove highlights for the current buffer now. - if g:ale_set_highlights - call ale#highlight#UpdateHighlights() - endif - - if g:ale_set_balloons - call ale#balloon#Disable() - endif - endif - - call ALEInitAuGroups() -endfunction - -call ALEInitAuGroups() +" A setting for wrapping commands. +call ale#Set('command_wrapper', '') if g:ale_set_balloons call ale#balloon#Enable() @@ -348,9 +240,17 @@ command! -bar ALELast :call ale#loclist_jumping#JumpToIndex(-1) command! -bar ALEDetail :call ale#cursor#ShowCursorDetail() " Define commands for turning ALE on or off. -command! -bar ALEToggle :call s:ALEToggle() -command! -bar ALEEnable :if !g:ale_enabled | ALEToggle | endif -command! -bar ALEDisable :if g:ale_enabled | ALEToggle | endif +command! -bar ALEToggle :call ale#toggle#Toggle() +command! -bar ALEEnable :call ale#toggle#Enable() +command! -bar ALEDisable :call ale#toggle#Disable() +command! -bar ALEReset :call ale#toggle#Reset() +" Commands for turning ALE on or off for a buffer. +command! -bar ALEToggleBuffer :call ale#toggle#ToggleBuffer(bufnr('')) +command! -bar ALEEnableBuffer :call ale#toggle#EnableBuffer(bufnr('')) +command! -bar ALEDisableBuffer :call ale#toggle#DisableBuffer(bufnr('')) +command! -bar ALEResetBuffer :call ale#toggle#ResetBuffer(bufnr('')) +" A command to stop all LSP-like clients, including tsserver. +command! -bar ALEStopAllLSPs :call ale#lsp#reset#StopAllLSPs() " A command for linting manually. command! -bar ALELint :call ale#Queue(0, 'lint_file') @@ -365,6 +265,10 @@ command! -bar ALEFix :call ale#fix#Fix() " Suggest registered functions to use for fixing problems. command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype) +" Go to definition for tsserver and LSP +command! -bar ALEGoToDefinition :call ale#definition#GoTo({}) +command! -bar ALEGoToDefinitionInTab :call ale#definition#GoTo({'open_in_tab': 1}) + " mappings for commands nnoremap (ale_previous) :ALEPrevious nnoremap (ale_previous_wrap) :ALEPreviousWrap @@ -373,25 +277,47 @@ nnoremap (ale_next_wrap) :ALENextWrap nnoremap (ale_first) :ALEFirst nnoremap (ale_last) :ALELast nnoremap (ale_toggle) :ALEToggle +nnoremap (ale_enable) :ALEEnable +nnoremap (ale_disable) :ALEDisable +nnoremap (ale_reset) :ALEReset +nnoremap (ale_toggle_buffer) :ALEToggleBuffer +nnoremap (ale_enable_buffer) :ALEEnableBuffer +nnoremap (ale_disable_buffer) :ALEDisableBuffer +nnoremap (ale_reset_buffer) :ALEResetBuffer nnoremap (ale_lint) :ALELint nnoremap (ale_detail) :ALEDetail nnoremap (ale_fix) :ALEFix +nnoremap (ale_go_to_definition) :ALEGoToDefinition +nnoremap (ale_go_to_definition_in_tab) :ALEGoToDefinitionInTab + +" Set up autocmd groups now. +call ale#toggle#InitAuGroups() " Housekeeping augroup ALECleanupGroup autocmd! " Clean up buffers automatically when they are unloaded. - autocmd BufUnload * call ale#engine#Cleanup(str2nr(expand(''))) + autocmd BufDelete * call ale#engine#Cleanup(str2nr(expand(''))) autocmd QuitPre * call ale#events#QuitEvent(str2nr(expand(''))) augroup END " Backwards Compatibility function! ALELint(delay) abort + if !get(g:, 'ale_deprecation_ale_lint', 0) + execute 'echom ''ALELint() is deprecated, use ale#Queue() instead.''' + let g:ale_deprecation_ale_lint = 1 + endif + call ale#Queue(a:delay) endfunction function! ALEGetStatusLine() abort + if !get(g:, 'ale_deprecation_ale_get_status_line', 0) + execute 'echom ''ALEGetStatusLine() is deprecated.''' + let g:ale_deprecation_ale_get_status_line = 1 + endif + return ale#statusline#Status() endfunction diff --git a/run-tests b/run-tests index 07ff3a8..6004911 100755 --- a/run-tests +++ b/run-tests @@ -9,17 +9,13 @@ # --neovim-only Run tests only for NeoVim # --vim-only Run tests only for Vim -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' -CURRENT_IMAGE_ID=d5a1b5915b09 -IMAGE=w0rp/ale -DOCKER_FLAGS=(--rm -v "$PWD:/testplugin" -v "$PWD/test:/home" -w /testplugin "$IMAGE") -EXIT=0 +current_image_id=d5a1b5915b09 +image=w0rp/ale tests='test/*.vader test/*/*.vader test/*/*/*.vader test/*/*/*.vader' -verbose=0 -quiet=0 +# These flags are forwarded to the script for running Vader tests. +verbose_flag='' +quiet_flag='' run_neovim_tests=1 run_vim_tests=1 run_vint=1 @@ -28,11 +24,11 @@ run_custom_checks=1 while [ $# -ne 0 ]; do case $1 in -v) - verbose=1 + verbose_flag='-v' shift ;; -q) - quiet=1 + quiet_flag='-q' shift ;; --neovim-only) @@ -51,6 +47,12 @@ while [ $# -ne 0 ]; do run_vint=0 shift ;; + --vint-only) + run_vim_tests=0 + run_neovim_tests=0 + run_custom_checks=0 + shift + ;; --no-custom-checks) run_custom_checks=0 shift @@ -85,183 +87,53 @@ fi # Delete .swp files in the test directory, which cause Vim 8 to hang. find test -name '*.swp' -delete -docker images -q w0rp/ale | grep "^$CURRENT_IMAGE_ID" > /dev/null \ - || docker pull "$IMAGE" +docker images -q w0rp/ale | grep "^$current_image_id" > /dev/null \ + || docker pull "$image" -function filter-vader-output() { - # When verbose mode is off, suppress output until Vader starts. - local start_output="$verbose" - local filtered_data='' +output_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir') - while read -r; do - if ((!start_output)); then - if [[ "$REPLY" = *'Starting Vader:'* ]]; then - start_output=1 - else - continue - fi - fi +trap '{ rm -rf "$output_dir"; }' EXIT - if ((quiet)); then - if [[ "$REPLY" = *'Starting Vader:'* ]]; then - filtered_data="$REPLY" - elif [[ "$REPLY" = *'Success/Total'* ]]; then - success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)" - total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)" +file_number=0 +pid_list='' - if [ "$success" -lt "$total" ]; then - echo "$filtered_data" - echo "$REPLY" - fi - - filtered_data='' - else - filtered_data="$filtered_data"$'\n'"$REPLY" - fi - else - echo "$REPLY" - fi - done -} - -function color-vader-output() { - while read -r; do - if [[ "$REPLY" = *'[EXECUTE] (X)'* ]]; then - echo -en "$RED" - elif [[ "$REPLY" = *'[EXECUTE]'* ]] || [[ "$REPLY" = *'[ GIVEN]'* ]]; then - echo -en "$NC" - fi - - if [[ "$REPLY" = *'Success/Total'* ]]; then - success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)" - total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)" - - if [ "$success" -lt "$total" ]; then - echo -en "$RED" - else - echo -en "$GREEN" - fi - - echo "$REPLY" - echo -en "$NC" - else - echo "$REPLY" - fi - done - - echo -en "$NC" -} - -if ((run_neovim_tests)); then - for vim in $(docker run --rm "$IMAGE" ls /vim-build/bin | grep '^neovim' ); do - echo - echo '========================================' - echo "Running tests for $vim" - echo '========================================' - echo - - set -o pipefail - docker run -it -e VADER_OUTPUT_FILE=/dev/stderr "${DOCKER_FLAGS[@]}" \ - "/vim-build/bin/$vim" -u test/vimrc \ - --headless "+Vader! $tests" | filter-vader-output | color-vader-output || EXIT=$? - set +o pipefail - done - - echo -fi - -if ((run_vim_tests)); then - for vim in $(docker run --rm "$IMAGE" ls /vim-build/bin | grep '^vim' ); do - echo - echo '========================================' - echo "Running tests for $vim" - echo '========================================' - echo - - set -o pipefail - docker run -a stderr -e VADER_OUTPUT_FILE=/dev/stderr "${DOCKER_FLAGS[@]}" \ - "/vim-build/bin/$vim" -u test/vimrc \ - "+Vader! $tests" 2>&1 | filter-vader-output | color-vader-output || EXIT=$? - set +o pipefail - done - - echo -fi +for vim in $(docker run --rm "$image" ls /vim-build/bin | grep '^neovim\|^vim' ); do + if ((run_vim_tests)) || [[ $vim =~ ^neovim ]] && ((run_neovim_tests)); then + echo "Starting Vim: $vim..." + file_number=$((file_number+1)) + test/script/run-vader-tests $quiet_flag $verbose_flag "$vim" "$tests" \ + > "$output_dir/$file_number" 2>&1 & + pid_list="$pid_list $!" + fi +done if ((run_vint)); then - echo '========================================' - echo 'Running Vint to lint our code' - echo '========================================' - echo 'Vint warnings/errors follow:' - echo - - set -o pipefail - docker run -a stdout "${DOCKER_FLAGS[@]}" vint -s . || EXIT=$? - set +o pipefail - echo + echo "Starting Vint..." + file_number=$((file_number+1)) + test/script/run-vint > "$output_dir/$file_number" 2>&1 & + pid_list="$pid_list $!" fi if ((run_custom_checks)); then - echo '========================================' - echo 'Running custom checks' - echo '========================================' - echo 'Custom warnings/errors follow:' - echo - - set -o pipefail - docker run -a stdout "${DOCKER_FLAGS[@]}" test/script/custom-checks . || EXIT=$? - set +o pipefail - echo - - echo '========================================' - echo 'Checking for duplicate tags' - echo '========================================' - echo 'Duplicate tags follow:' - echo - - grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d || EXIT=$? - - echo '========================================' - echo 'Checking for invalid tag references' - echo '========================================' - echo 'Invalid tag references tags follow:' - echo - - tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+' - - # Grep for tags and references, and complain if we find a reference without - # a tag for the reference. Only our tags will be included. - diff -u \ - <(grep --exclude=tags -roh "\*$tag_regex\*" doc | sort -u | sed 's/*//g') \ - <(grep --exclude=tags -roh "|$tag_regex|" doc | sort -u | sed 's/|//g') \ - | grep '^+[^+]' && EXIT=1 - - echo '========================================' - echo 'diff README.md and doc/ale.txt tables' - echo '========================================' - echo 'Differences follow:' - echo - - test/script/check-supported-tools-tables || EXIT=$? - - echo '========================================' - echo 'Look for badly aligned doc tags' - echo '========================================' - echo 'Badly aligned tags follow:' - echo - - # Documentation tags need to be aligned to the right margin, so look for - # tags which aren't at the right margin. - grep ' \*[^*]\+\*$' doc/ -r \ - | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \ - | grep . && EXIT=1 - - echo '========================================' - echo 'Look for table of contents issues' - echo '========================================' - echo - - test/script/check-toc || EXIT=$? + echo "Starting Custom checks..." + file_number=$((file_number+1)) + test/script/custom-checks &> "$output_dir/$file_number" 2>&1 & + pid_list="$pid_list $!" fi -exit $EXIT +echo + +failed=0 +index=0 + +for pid in $pid_list; do + index=$((index+1)) + + if ! wait "$pid"; then + failed=1 + fi + + cat "$output_dir/$index" +done + +exit $failed diff --git a/syntax/ale-fix-suggest.vim b/syntax/ale-fix-suggest.vim index be3d45e..b112f5b 100644 --- a/syntax/ale-fix-suggest.vim +++ b/syntax/ale-fix-suggest.vim @@ -3,7 +3,7 @@ if exists('b:current_syntax') endif syn match aleFixerComment /^.*$/ -syn match aleFixerName /^'[^']*'/ +syn match aleFixerName /\(^\|, \)'[^']*'/ syn match aleFixerHelp /^See :help ale-fix-configuration/ hi def link aleFixerComment Comment diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/autopep8 b/test/command_callback/cargo_paths/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/autopep8 rename to test/command_callback/cargo_paths/Cargo.toml diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/flake8 b/test/command_callback/htmlhint_paths/node_modules/.bin/htmlhint similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/flake8 rename to test/command_callback/htmlhint_paths/node_modules/.bin/htmlhint diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/isort b/test/command_callback/htmlhint_paths/with_config/.htmlhintrc old mode 100755 new mode 100644 similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/isort rename to test/command_callback/htmlhint_paths/with_config/.htmlhintrc diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/mypy b/test/command_callback/java_paths/src/test/java/com/something/dummy old mode 100755 new mode 100644 similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/mypy rename to test/command_callback/java_paths/src/test/java/com/something/dummy diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylint b/test/command_callback/java_paths_with_jaxb/src/main/java/com/something/dummy old mode 100755 new mode 100644 similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/pylint rename to test/command_callback/java_paths_with_jaxb/src/main/java/com/something/dummy diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/yapf b/test/command_callback/java_paths_with_jaxb/src/main/jaxb/com/something/dummy old mode 100755 new mode 100644 similarity index 100% rename from test/command_callback/python_paths/with_virtualenv/env/Scripts/yapf rename to test/command_callback/java_paths_with_jaxb/src/main/jaxb/com/something/dummy diff --git a/test/command_callback/lessc_paths/node_modules/.bin/lessc b/test/command_callback/lessc_paths/node_modules/.bin/lessc new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/ols_paths/.merlin b/test/command_callback/ols_paths/.merlin new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/ols_paths/node_modules/.bin/ocaml-language-server b/test/command_callback/ols_paths/node_modules/.bin/ocaml-language-server new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/php_paths/project-with-php-cs-fixer/test.php b/test/command_callback/php_paths/project-with-php-cs-fixer/test.php new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/php_paths/project-with-php-cs-fixer/vendor/bin/php-cs-fixer b/test/command_callback/php_paths/project-with-php-cs-fixer/vendor/bin/php-cs-fixer new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/php_paths/project-without-php-cs-fixer/test.php b/test/command_callback/php_paths/project-without-php-cs-fixer/test.php new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/no_virtualenv/subdir/foo/COMMIT_EDITMSG b/test/command_callback/python_paths/no_virtualenv/subdir/foo/COMMIT_EDITMSG new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/autopep8.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autopep8.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/flake8.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/flake8.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/gitlint.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/gitlint.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/isort.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/isort.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/mypy.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/mypy.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/pyflakes.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/pyflakes.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylint.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylint.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/pyls.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/pyls.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/yapf.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yapf.exe new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/gitlint b/test/command_callback/python_paths/with_virtualenv/env/bin/gitlint new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/pyflakes b/test/command_callback/python_paths/with_virtualenv/env/bin/pyflakes new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/pyls b/test/command_callback/python_paths/with_virtualenv/env/bin/pyls new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/python_paths/with_virtualenv/subdir/foo/COMMIT_EDITMSG b/test/command_callback/python_paths/with_virtualenv/subdir/foo/COMMIT_EDITMSG new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/stylelint_paths/node_modules/.bin/stylelint b/test/command_callback/stylelint_paths/node_modules/.bin/stylelint new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/test_asm_gcc_command_callbacks.vader b/test/command_callback/test_asm_gcc_command_callbacks.vader new file mode 100644 index 0000000..ce8b906 --- /dev/null +++ b/test/command_callback/test_asm_gcc_command_callbacks.vader @@ -0,0 +1,39 @@ +Before: + Save g:ale_asm_gcc_executable + Save g:ale_asm_gcc_options + + unlet! g:ale_asm_gcc_executable + unlet! b:ale_asm_gcc_executable + unlet! g:ale_asm_gcc_options + unlet! b:ale_asm_gcc_options + + runtime ale_linters/asm/gcc.vim + + let b:command_tail = ' -x assembler -fsyntax-only -iquote' + \ . ' ' . ale#Escape(getcwd()) + \ . ' -Wall -' + +After: + Restore + unlet! b:command_tail + unlet! b:ale_asm_gcc_executable + unlet! b:ale_asm_gcc_options + call ale#linter#Reset() + +Execute(The executable should be configurable): + AssertEqual 'gcc', ale_linters#asm#gcc#GetExecutable(bufnr('')) + + let b:ale_asm_gcc_executable = 'foobar' + + AssertEqual 'foobar', ale_linters#asm#gcc#GetExecutable(bufnr('')) + +Execute(The executable should be used in the command): + AssertEqual + \ ale#Escape('gcc') . b:command_tail, + \ ale_linters#asm#gcc#GetCommand(bufnr('')) + + let b:ale_asm_gcc_executable = 'foobar' + + AssertEqual + \ ale#Escape('foobar') . b:command_tail, + \ ale_linters#asm#gcc#GetCommand(bufnr('')) diff --git a/test/command_callback/test_brakeman_command_callback.vader b/test/command_callback/test_brakeman_command_callback.vader index f98801b..1772c9d 100644 --- a/test/command_callback/test_brakeman_command_callback.vader +++ b/test/command_callback/test_brakeman_command_callback.vader @@ -25,7 +25,7 @@ Execute(The brakeman command callback should find a valid Rails app root): AssertEqual \ 'brakeman -f json -q -p ' - \ . ale#Escape(ale#path#Winify(g:dir . '/../ruby_fixtures/valid_rails_app')), + \ . ale#Escape(ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app')), \ ale_linters#ruby#brakeman#GetCommand(bufnr('')) Execute(The brakeman command callback should include configured options): @@ -35,5 +35,5 @@ Execute(The brakeman command callback should include configured options): AssertEqual \ 'brakeman -f json -q --combobulate -p ' - \ . ale#Escape(ale#path#Winify(g:dir . '/../ruby_fixtures/valid_rails_app')), + \ . ale#Escape(ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app')), \ ale_linters#ruby#brakeman#GetCommand(bufnr('')) diff --git a/test/command_callback/test_c_cppcheck_command_callbacks.vader b/test/command_callback/test_c_cppcheck_command_callbacks.vader index e6fe1b7..1643e3e 100644 --- a/test/command_callback/test_c_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_c_cppcheck_command_callbacks.vader @@ -43,7 +43,7 @@ Execute(cppcheck for C++ should detect compile_commands.json files): call ale#test#SetFilename('cppcheck_paths/one/foo.cpp') AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/cppcheck_paths/one')) . ' && ' + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) . ' && ' \ . ale#Escape('cppcheck') \ . ' -q --language=c --project=compile_commands.json --enable=style %t', \ ale_linters#c#cppcheck#GetCommand(bufnr('')) diff --git a/test/command_callback/test_c_flawfinder_command_callbacks.vader b/test/command_callback/test_c_flawfinder_command_callbacks.vader new file mode 100644 index 0000000..38a602d --- /dev/null +++ b/test/command_callback/test_c_flawfinder_command_callbacks.vader @@ -0,0 +1,51 @@ +Before: + Save g:ale_c_flawfinder_executable + Save g:ale_c_flawfinder_options + Save g:ale_c_flawfinder_minlevel + + unlet! g:ale_c_flawfinder_executable + unlet! b:ale_c_flawfinder_executable + unlet! g:ale_c_flawfinder_options + unlet! b:ale_c_flawfinder_options + unlet! g:ale_c_flawfinder_minlevel + unlet! b:ale_c_flawfinder_minlevel + + runtime ale_linters/c/flawfinder.vim + +After: + unlet! b:ale_c_flawfinder_executable + unlet! b:ale_c_flawfinder_options + unlet! b:ale_c_flawfinder_minlevel + + Restore + call ale#linter#Reset() + +Execute(The flawfinder command should be correct): + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --minlevel=1 %t', + \ ale_linters#c#flawfinder#GetCommand(bufnr('')) + +Execute(The minlevel of flawfinder should be configurable): + let b:ale_c_flawfinder_minlevel = 8 + + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --minlevel=8 %t', + \ ale_linters#c#flawfinder#GetCommand(bufnr('')) + +Execute(Additional flawfinder options should be configurable): + let b:ale_c_flawfinder_options = ' --foobar' + + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --foobar --minlevel=1 %t', + \ ale_linters#c#flawfinder#GetCommand(bufnr('')) + +Execute(The flawfinder exectable should be configurable): + let b:ale_c_flawfinder_executable = 'foo/bar' + + AssertEqual + \ ale#Escape('foo/bar') + \ . ' -CDQS --minlevel=1 %t', + \ ale_linters#c#flawfinder#GetCommand(bufnr('')) diff --git a/test/command_callback/test_cargo_command_callbacks.vader b/test/command_callback/test_cargo_command_callbacks.vader new file mode 100644 index 0000000..9c06f27 --- /dev/null +++ b/test/command_callback/test_cargo_command_callbacks.vader @@ -0,0 +1,164 @@ +Before: + Save g:ale_rust_cargo_use_check + Save g:ale_rust_cargo_check_all_targets + Save g:ale_rust_cargo_default_feature_behavior + Save g:ale_rust_cargo_include_features + + unlet! g:ale_rust_cargo_use_check + unlet! g:ale_cargo_check_all_targets + unlet! g:ale_rust_cargo_default_feature_behavior + unlet! g:ale_rust_cargo_include_features + + runtime ale_linters/rust/cargo.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + + let g:suffix = ' --frozen --message-format=json -q' + +After: + Restore + + unlet! g:suffix + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + call ale#semver#ResetVersionCache() + +Execute(An empty string should be returned for the cargo executable when there's no Cargo.toml file): + AssertEqual + \ '', + \ ale_linters#rust#cargo#GetCargoExecutable(bufnr('')) + +Execute(The executable should be returned when there is a Cargo.toml file): + call ale#test#SetFilename('cargo_paths/test.rs') + + AssertEqual + \ 'cargo', + \ ale_linters#rust#cargo#GetCargoExecutable(bufnr('')) + +Execute(The VersionCheck function should return the --version command): + AssertEqual + \ 'cargo --version', + \ ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(The default command should be correct): + AssertEqual + \ 'cargo build' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + +Execute(`cargo check` should be used when the version is new enough): + AssertEqual + \ 'cargo check' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.17.0 (3423351a5 2017-10-06)', + \ ]) + + " We should cache the version check + AssertEqual + \ 'cargo check' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(`cargo build` should be used when cargo is too old): + AssertEqual + \ 'cargo build' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.16.0 (3423351a5 2017-10-06)', + \ ]) + + " We should cache the version check + AssertEqual + \ 'cargo build' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(`cargo build` should be used when g:ale_rust_cargo_use_check is set to 0): + let g:ale_rust_cargo_use_check = 0 + + AssertEqual + \ 'cargo build' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.24.0 (3423351a5 2017-10-06)', + \ ]) + + " We should cache the version check + AssertEqual + \ 'cargo build' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(`cargo check` should be used when the version is new enough): + AssertEqual + \ 'cargo check' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) + + " We should cache the version check + AssertEqual + \ 'cargo check' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(--all-targets should be used when g:ale_rust_cargo_check_all_targets is set to 1): + let g:ale_rust_cargo_check_all_targets = 1 + + AssertEqual + \ 'cargo check --all-targets' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) + + " We should cache the version check + AssertEqual + \ 'cargo check --all-targets' . g:suffix, + \ ale_linters#rust#cargo#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr('')) + +Execute(--no-default-features should be used when g:ale_rust_cargo_default_feature_behavior is none): + let g:ale_rust_cargo_default_feature_behavior = 'none' + + AssertEqual + \ 'cargo check' . g:suffix . ' --no-default-features', + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) + +Execute(g:ale_rust_cargo_include_features added when g:ale_rust_cargo_default_feature_behavior is none): + let g:ale_rust_cargo_default_feature_behavior = 'none' + let g:ale_rust_cargo_include_features = 'foo bar' + + AssertEqual + \ 'cargo check' . g:suffix . ' --no-default-features --features ' . + \ (fnamemodify(&shell, ':t') is? 'cmd.exe' ? '"foo bar"' : "'foo bar'"), + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) + +Execute(g:ale_rust_cargo_include_features added and escaped): + let g:ale_rust_cargo_default_feature_behavior = 'default' + let g:ale_rust_cargo_include_features = "foo bar baz" + + AssertEqual + \ 'cargo check' . g:suffix . ' --features ' . + \ (fnamemodify(&shell, ':t') is? 'cmd.exe' ? '"foo bar baz"' : "'foo bar baz'"), + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) + +Execute(--all-features should be used when g:ale_rust_cargo_default_feature_behavior is all): + let g:ale_rust_cargo_default_feature_behavior = 'all' + + " When all features are enabled we should ignore extra features to add + " since it won't do anything + let g:ale_rust_cargo_include_features = 'foo bar' + + AssertEqual + \ 'cargo check' . g:suffix . ' --all-features', + \ ale_linters#rust#cargo#GetCommand(bufnr(''), [ + \ 'cargo 0.22.0 (3423351a5 2017-10-06)', + \ ]) diff --git a/test/command_callback/test_cpp_clangcheck_command_callbacks.vader b/test/command_callback/test_cpp_clangcheck_command_callbacks.vader index 34b87fc..f708c52 100644 --- a/test/command_callback/test_cpp_clangcheck_command_callbacks.vader +++ b/test/command_callback/test_cpp_clangcheck_command_callbacks.vader @@ -28,7 +28,7 @@ Execute(The executable should be used in the command): AssertEqual \ ale#Escape('clang-check') \ . ' -analyze %s' - \ . ' -extra-arg -Xanalyzer -extra-arg -analyzer-output=text', + \ . ' -extra-arg -Xclang -extra-arg -analyzer-output=text', \ ale_linters#cpp#clangcheck#GetCommand(bufnr('')) let b:ale_cpp_clangcheck_executable = 'foobar' @@ -38,7 +38,7 @@ Execute(The executable should be used in the command): AssertEqual \ ale#Escape('foobar') \ . ' -analyze %s' - \ . ' -extra-arg -Xanalyzer -extra-arg -analyzer-output=text', + \ . ' -extra-arg -Xclang -extra-arg -analyzer-output=text', \ ale_linters#cpp#clangcheck#GetCommand(bufnr('')) Execute(The options should be configurable): @@ -46,8 +46,9 @@ Execute(The options should be configurable): AssertEqual \ ale#Escape('clang-check') - \ . ' -analyze %s --something' - \ . ' -extra-arg -Xanalyzer -extra-arg -analyzer-output=text', + \ . ' -analyze %s' + \ . ' -extra-arg -Xclang -extra-arg -analyzer-output=text' + \ . ' --something', \ ale_linters#cpp#clangcheck#GetCommand(bufnr('')) Execute(The build directory should be used when set): diff --git a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader index f7b37d4..2c9d729 100644 --- a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader @@ -43,7 +43,7 @@ Execute(cppcheck for C++ should detect compile_commands.json files): call ale#test#SetFilename('cppcheck_paths/one/foo.cpp') AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/cppcheck_paths/one')) . ' && ' + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) . ' && ' \ . ale#Escape('cppcheck') \ . ' -q --language=c++ --project=compile_commands.json --enable=style %t', \ ale_linters#cpp#cppcheck#GetCommand(bufnr('')) diff --git a/test/command_callback/test_cpp_flawfinder_command_callbacks.vader b/test/command_callback/test_cpp_flawfinder_command_callbacks.vader new file mode 100644 index 0000000..8769ec9 --- /dev/null +++ b/test/command_callback/test_cpp_flawfinder_command_callbacks.vader @@ -0,0 +1,51 @@ +Before: + Save g:ale_cpp_flawfinder_executable + Save g:ale_cpp_flawfinder_options + Save g:ale_cpp_flawfinder_minlevel + + unlet! g:ale_cpp_flawfinder_executable + unlet! b:ale_cpp_flawfinder_executable + unlet! g:ale_cpp_flawfinder_options + unlet! b:ale_cpp_flawfinder_options + unlet! g:ale_cpp_flawfinder_minlevel + unlet! b:ale_cpp_flawfinder_minlevel + + runtime ale_linters/cpp/flawfinder.vim + +After: + unlet! b:ale_cpp_flawfinder_executable + unlet! b:ale_cpp_flawfinder_options + unlet! b:ale_cpp_flawfinder_minlevel + + Restore + call ale#linter#Reset() + +Execute(The flawfinder command should be correct): + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --minlevel=1 %t', + \ ale_linters#cpp#flawfinder#GetCommand(bufnr('')) + +Execute(The minlevel of flawfinder should be configurable): + let b:ale_cpp_flawfinder_minlevel = 8 + + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --minlevel=8 %t', + \ ale_linters#cpp#flawfinder#GetCommand(bufnr('')) + +Execute(Additional flawfinder options should be configurable): + let b:ale_cpp_flawfinder_options = ' --foobar' + + AssertEqual + \ ale#Escape('flawfinder') + \ . ' -CDQS --foobar --minlevel=1 %t', + \ ale_linters#cpp#flawfinder#GetCommand(bufnr('')) + +Execute(The flawfinder exectable should be configurable): + let b:ale_cpp_flawfinder_executable = 'foo/bar' + + AssertEqual + \ ale#Escape('foo/bar') + \ . ' -CDQS --minlevel=1 %t', + \ ale_linters#cpp#flawfinder#GetCommand(bufnr('')) diff --git a/test/command_callback/test_cs_mcsc_command_callbacks.vader b/test/command_callback/test_cs_mcsc_command_callbacks.vader index 441cef5..cb52c96 100644 --- a/test/command_callback/test_cs_mcsc_command_callbacks.vader +++ b/test/command_callback/test_cs_mcsc_command_callbacks.vader @@ -12,6 +12,8 @@ Before: unlet! g:ale_cs_mcsc_assembly_path unlet! g:ale_cs_mcsc_assemblies + let g:prefix = ' -out:TEMP -t:module -recurse:' . ale#Escape('*.cs') + function! GetCommand() let l:command = ale_linters#cs#mcsc#GetCommand(bufnr('')) let l:command = join(split(l:command)) @@ -28,52 +30,64 @@ After: unlet! g:ale_cs_mcsc_source unlet! g:ale_cs_mcsc_assembly_path unlet! g:ale_cs_mcsc_assemblies + unlet! g:ale_prefix delfunction GetCommand call ale#linter#Reset() -Execute(Check for proper default command): +Execute(The mcsc linter should return the correct default command): AssertEqual - \ 'cd ".";mcs -unsafe -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe' . g:prefix, \ GetCommand() Execute(The options should be be used in the command): let g:ale_cs_mcsc_options = '-pkg:dotnet' AssertEqual - \ 'cd ".";mcs -unsafe ' . g:ale_cs_mcsc_options . ' -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe -pkg:dotnet' . g:prefix, \ GetCommand() Execute(The souce path should be be used in the command): - let g:ale_cs_mcsc_source='../foo/bar' + let g:ale_cs_mcsc_source = '../foo/bar' AssertEqual - \ 'cd "' . g:ale_cs_mcsc_source . '";mcs -unsafe -out:TEMP -t:module -recurse:"*.cs"', + \ 'cd ' . ale#Escape('../foo/bar') . ' && ' + \ . 'mcs -unsafe' . g:prefix, \ GetCommand() Execute(The list of search pathes for assemblies should be be used in the command if not empty): let g:ale_cs_mcsc_assembly_path = ['/usr/lib/mono', '../foo/bar'] AssertEqual - \ 'cd ".";mcs -unsafe -lib:"' . join(g:ale_cs_mcsc_assembly_path,'","') . '" -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe' + \ . ' -lib:' . ale#Escape('/usr/lib/mono') . ',' . ale#Escape('../foo/bar') + \ . g:prefix, \ GetCommand() let g:ale_cs_mcsc_assembly_path = [] AssertEqual - \ 'cd ".";mcs -unsafe -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe' . g:prefix, \ GetCommand() Execute(The list of assemblies should be be used in the command if not empty): let g:ale_cs_mcsc_assemblies = ['foo.dll', 'bar.dll'] AssertEqual - \ 'cd ".";mcs -unsafe -r:"' . join(g:ale_cs_mcsc_assemblies,'","') . '" -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe' + \ . ' -r:' . ale#Escape('foo.dll') . ',' . ale#Escape('bar.dll') + \ . g:prefix, \ GetCommand() let g:ale_cs_mcsc_assemblies = [] AssertEqual - \ 'cd ".";mcs -unsafe -out:TEMP -t:module -recurse:"*.cs"', + \ ale#path#BufferCdString(bufnr('')) + \ . 'mcs -unsafe' . g:prefix, \ GetCommand() diff --git a/test/command_callback/test_dartanalyzer_command_callback.vader b/test/command_callback/test_dartanalyzer_command_callback.vader index dbd8290..c7b5139 100644 --- a/test/command_callback/test_dartanalyzer_command_callback.vader +++ b/test/command_callback/test_dartanalyzer_command_callback.vader @@ -17,7 +17,7 @@ Execute(The default command and executable should be correct): \ 'dartanalyzer', \ ale_linters#dart#dartanalyzer#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape('dartanalyzer') . ' %t', + \ ale#Escape('dartanalyzer') . ' %s', \ ale_linters#dart#dartanalyzer#GetCommand(bufnr('')) Execute(The executable should be configurable): @@ -27,7 +27,7 @@ Execute(The executable should be configurable): \ '/usr/lib/dart/bin/dartanalyzer', \ ale_linters#dart#dartanalyzer#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape('/usr/lib/dart/bin/dartanalyzer') . ' %t', + \ ale#Escape('/usr/lib/dart/bin/dartanalyzer') . ' %s', \ ale_linters#dart#dartanalyzer#GetCommand(bufnr('')) Execute(The .packages file should be set if detected): @@ -35,6 +35,6 @@ Execute(The .packages file should be set if detected): AssertEqual \ ale#Escape('dartanalyzer') - \ . ' --packages ' . ale#Escape(ale#path#Winify(g:dir . '/dart_paths/.packages')) - \ . ' %t', + \ . ' --packages ' . ale#Escape(ale#path#Simplify(g:dir . '/dart_paths/.packages')) + \ . ' %s', \ ale_linters#dart#dartanalyzer#GetCommand(bufnr('')) diff --git a/test/command_callback/test_erb_command_callback.vader b/test/command_callback/test_erb_command_callback.vader new file mode 100644 index 0000000..481f64f --- /dev/null +++ b/test/command_callback/test_erb_command_callback.vader @@ -0,0 +1,21 @@ +Before: + runtime ale_linters/eruby/erb.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(Executable should not contain any filter code by default): + call ale#test#SetFilename('../ruby_fixtures/not_a_rails_app/file.rb') + + AssertEqual + \ 'erb -P -T - -x %t | ruby -c', + \ ale_linters#eruby#erb#GetCommand(bufnr('')) + +Execute(Executable should filter invalid eRuby when inside a Rails project): + call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/app/views/my_great_view.html.erb') + + AssertEqual + \ 'ruby -r erb -e ' . ale#Escape('puts ERB.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c', + \ ale_linters#eruby#erb#GetCommand(bufnr('')) diff --git a/test/command_callback/test_erubi_command_callback.vader b/test/command_callback/test_erubi_command_callback.vader new file mode 100644 index 0000000..1953d76 --- /dev/null +++ b/test/command_callback/test_erubi_command_callback.vader @@ -0,0 +1,31 @@ +Before: + runtime ale_linters/eruby/erubi.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(Executable should not contain any filter code by default): + call ale#test#SetFilename('../ruby_fixtures/not_a_rails_app/file.rb') + + AssertEqual + \ 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read).src') . '< %t | ruby -c', + \ ale_linters#eruby#erubi#GetCommand(bufnr(''), []) + +Execute(Executable should filter invalid eRuby when inside a Rails project): + call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/app/views/my_great_view.html.erb') + + AssertEqual + \ 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c', + \ ale_linters#eruby#erubi#GetCommand(bufnr(''), []) + +Execute(Command should be blank if the first command in the chain return output): + let output_lines = [ + \ "/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- erubi/capture_end (LoadError)", + \ " from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'", + \] + + AssertEqual + \ '', + \ ale_linters#eruby#erubi#GetCommand(bufnr(''), output_lines) diff --git a/test/command_callback/test_erubis_command_callback.vader b/test/command_callback/test_erubis_command_callback.vader new file mode 100644 index 0000000..574dd68 --- /dev/null +++ b/test/command_callback/test_erubis_command_callback.vader @@ -0,0 +1,21 @@ +Before: + runtime ale_linters/eruby/erubis.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(Executable should not contain any filter code by default): + call ale#test#SetFilename('../ruby_fixtures/not_a_rails_app/file.rb') + + AssertEqual + \ 'erubis -x %t | ruby -c', + \ ale_linters#eruby#erubis#GetCommand(bufnr('')) + +Execute(Executable should filter invalid eRuby when inside a Rails project): + call ale#test#SetFilename('../ruby_fixtures/valid_rails_app/app/views/my_great_view.html.erb') + + AssertEqual + \ 'ruby -r erubis -e ' . ale#Escape('puts Erubis::Eruby.new($stdin.read.gsub(%{<%=},%{<%})).src') . '< %t | ruby -c', + \ ale_linters#eruby#erubis#GetCommand(bufnr('')) diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader index a510f4c..1784b81 100644 --- a/test/command_callback/test_flake8_command_callback.vader +++ b/test/command_callback/test_flake8_command_callback.vader @@ -23,7 +23,7 @@ After: call ale#test#RestoreDirectory() call ale#linter#Reset() - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() Execute(The flake8 callbacks should return the correct default values): AssertEqual @@ -35,8 +35,9 @@ Execute(The flake8 callbacks should return the correct default values): AssertEqual \ ale#Escape('flake8') . ' --format=default --stdin-display-name %s -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.0']) + " Try with older versions. - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() AssertEqual \ ale#Escape('flake8') . ' --format=default -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9']) @@ -49,7 +50,9 @@ Execute(The flake8 command callback should let you set options): \ . ' --some-option --format=default' \ . ' --stdin-display-name %s -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.4']) - call ale_linters#python#flake8#ClearVersionCache() + + call ale#semver#ResetVersionCache() + AssertEqual \ ale#Escape('flake8') \ . ' --some-option --format=default -', @@ -73,7 +76,7 @@ Execute(You should be able to set a custom executable and it should be escaped): Execute(The flake8 callbacks should detect virtualenv directories): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') - let b:executable = ale#path#Winify( + let b:executable = ale#path#Simplify( \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/flake8' \) @@ -92,35 +95,35 @@ Execute(The FindProjectRoot should detect the project root directory for namespa silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_manifest/namespace/foo/bar.py') AssertEqual - \ ale#path#Winify(g:dir . '/python_paths/namespace_package_manifest'), + \ ale#path#Simplify(g:dir . '/python_paths/namespace_package_manifest'), \ ale#python#FindProjectRoot(bufnr('')) Execute(The FindProjectRoot should detect the project root directory for namespace package via setup.cf): silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_setup/namespace/foo/bar.py') AssertEqual - \ ale#path#Winify(g:dir . '/python_paths/namespace_package_setup'), + \ ale#path#Simplify(g:dir . '/python_paths/namespace_package_setup'), \ ale#python#FindProjectRoot(bufnr('')) Execute(The FindProjectRoot should detect the project root directory for namespace package via pytest.ini): silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_pytest/namespace/foo/bar.py') AssertEqual - \ ale#path#Winify(g:dir . '/python_paths/namespace_package_pytest'), + \ ale#path#Simplify(g:dir . '/python_paths/namespace_package_pytest'), \ ale#python#FindProjectRoot(bufnr('')) Execute(The FindProjectRoot should detect the project root directory for namespace package via tox.ini): silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_tox/namespace/foo/bar.py') AssertEqual - \ ale#path#Winify(g:dir . '/python_paths/namespace_package_tox'), + \ ale#path#Simplify(g:dir . '/python_paths/namespace_package_tox'), \ ale#python#FindProjectRoot(bufnr('')) Execute(The FindProjectRoot should detect the project root directory for non-namespace package): silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/bar.py') AssertEqual - \ ale#path#Winify(g:dir . '/python_paths/no_virtualenv/subdir'), + \ ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir'), \ ale#python#FindProjectRoot(bufnr('')) " Some users currently run flake8 this way, so we should support it. @@ -140,7 +143,7 @@ Execute(Using `python -m flake8` should be supported for running flake8): \ ale#Escape('python') . ' -m flake8 --some-option --format=default -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9']) - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() " Leading spaces shouldn't matter let g:ale_python_flake8_options = ' -m flake8 --some-option' @@ -155,7 +158,7 @@ Execute(Using `python -m flake8` should be supported for running flake8): \ ale#Escape('python') . ' -m flake8 --some-option --format=default -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9']) -Execute(Using `python2 -m flake8` should be use with the old args option): +Execute(Using `python2 -m flake8` should be supported with the old args option): let g:ale_python_flake8_executable = 'python2' let g:ale_python_flake8_args = '-m flake8' let g:ale_python_flake8_use_global = 0 diff --git a/test/command_callback/test_foodcritic_command_callback.vader b/test/command_callback/test_foodcritic_command_callback.vader new file mode 100644 index 0000000..e3ad8a7 --- /dev/null +++ b/test/command_callback/test_foodcritic_command_callback.vader @@ -0,0 +1,44 @@ +Before: + Save g:ale_chef_foodcritic_executable + Save g:ale_chef_foodcritic_options + + unlet! g:ale_chef_foodcritic_executable + unlet! g:ale_chef_foodcritic_options + + call ale#test#SetDirectory('/testplugin/test') + + runtime ale_linters/chef/foodcritic.vim + +After: + Restore + + unlet! b:ale_chef_foodcritic_executable + unlet! b:ale_chef_foodcritic_options + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual + \ 'foodcritic', + \ ale_linters#chef#foodcritic#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('foodcritic') . ' %s', + \ ale_linters#chef#foodcritic#GetCommand(bufnr('')) + +Execute(Extra options should be included with escapeed tildes (~)): + let b:ale_chef_foodcritic_options = '-t ~F011' + + AssertEqual + \ ale#Escape('foodcritic') . ' -t \~F011 %s', + \ ale_linters#chef#foodcritic#GetCommand(bufnr('')) + +Execute(The executable should be configurable): + let b:ale_chef_foodcritic_executable = 'foobar' + + AssertEqual + \ 'foobar', + \ ale_linters#chef#foodcritic#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('foobar') . ' %s', + \ ale_linters#chef#foodcritic#GetCommand(bufnr('')) diff --git a/test/command_callback/test_gitlint_command_callback.vader b/test/command_callback/test_gitlint_command_callback.vader new file mode 100644 index 0000000..6ff95ea --- /dev/null +++ b/test/command_callback/test_gitlint_command_callback.vader @@ -0,0 +1,84 @@ +Before: + Save g:ale_gitcommit_gitlint_executable + Save g:ale_gitcommit_gitlint_options + Save g:ale_gitcommit_gitlint_use_global + + unlet! g:ale_gitcommit_gitlint_executable + unlet! g:ale_gitcommit_gitlint_options + unlet! g:ale_gitcommit_gitlint_use_global + + runtime ale_linters/gitcommit/gitlint.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + let b:command_tail = ' lint' + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + unlet! b:bin_dir + unlet! b:executable + +Execute(The gitlint callbacks should return the correct default values): + AssertEqual + \ 'gitlint', + \ ale_linters#gitcommit#gitlint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('gitlint') . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) + +Execute(The gitlint executable should be configurable, and escaped properly): + let g:ale_gitcommit_gitlint_executable = 'executable with spaces' + + AssertEqual + \ 'executable with spaces', + \ ale_linters#gitcommit#gitlint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('executable with spaces') . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) + +Execute(The gitlint command callback should let you set options): + let g:ale_gitcommit_gitlint_options = '--some-option' + + AssertEqual + \ ale#Escape('gitlint') . ' --some-option' . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) + +Execute(The gitlint callbacks shouldn't detect virtualenv directories where they don't exist): + silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/COMMIT_EDITMSG') + + AssertEqual + \ 'gitlint', + \ ale_linters#gitcommit#gitlint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('gitlint') . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) + +Execute(The gitlint callbacks should detect virtualenv directories): + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/COMMIT_EDITMSG') + + let b:executable = ale#path#Simplify( + \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/gitlint' + \) + + AssertEqual + \ b:executable, + \ ale_linters#gitcommit#gitlint#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape(b:executable) . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) + +Execute(You should able able to use the global gitlint instead): + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/COMMIT_EDITMSG') + let g:ale_gitcommit_gitlint_use_global = 1 + + AssertEqual + \ 'gitlint', + \ ale_linters#gitcommit#gitlint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('gitlint') . b:command_tail, + \ ale_linters#gitcommit#gitlint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_glslang_command_callback.vader b/test/command_callback/test_glslang_command_callback.vader index 9d40683..1b1722a 100644 --- a/test/command_callback/test_glslang_command_callback.vader +++ b/test/command_callback/test_glslang_command_callback.vader @@ -10,8 +10,6 @@ Before: After: Restore - unlet! g:ale_cuda_nvcc_executable - unlet! g:ale_cuda_nvcc_options call ale#linter#Reset() Execute(Executable should default to glslangValidator): diff --git a/test/command_callback/test_glslls_command_callback.vader b/test/command_callback/test_glslls_command_callback.vader new file mode 100644 index 0000000..e64c235 --- /dev/null +++ b/test/command_callback/test_glslls_command_callback.vader @@ -0,0 +1,37 @@ +Before: + Save g:ale_glsl_glslls_executable + Save g:ale_glsl_glslls_logfile + + unlet! g:ale_glsl_glslls_executable + unlet! g:ale_glsl_glslls_logfile + + runtime ale_linters/glsl/glslls.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + call ale#linter#Reset() + +Execute(Executable should default to 'glslls'): + AssertEqual + \ 'glslls', + \ ale_linters#glsl#glslls#GetExecutable(bufnr('')) + +Execute(Executable should be configurable): + let g:ale_glsl_glslls_executable = 'foobar' + AssertEqual + \ 'foobar', + \ ale_linters#glsl#glslls#GetExecutable(bufnr('')) + +Execute(Command should use executable): + let command1 = ale_linters#glsl#glslls#GetCommand(bufnr('')) + AssertEqual command1, ale#Escape('glslls') . ' --stdin' + + let g:ale_glsl_glslls_executable = 'foobar' + let command2 = ale_linters#glsl#glslls#GetCommand(bufnr('')) + AssertEqual command2, ale#Escape('foobar') . ' --stdin' + +Execute(Setting logfile should work): + let g:ale_glsl_glslls_logfile = '/tmp/test.log' + let command = ale_linters#glsl#glslls#GetCommand(bufnr('')) + AssertEqual command, ale#Escape('glslls') . ' --verbose -l /tmp/test.log --stdin' diff --git a/test/command_callback/test_gobuild_command_callback.vader b/test/command_callback/test_gobuild_command_callback.vader new file mode 100644 index 0000000..240f29c --- /dev/null +++ b/test/command_callback/test_gobuild_command_callback.vader @@ -0,0 +1,52 @@ +Before: + Save g:ale_go_gobuild_options + + unlet! g:ale_go_gobuild_options + + let g:env_prefix = has('win32') + \ ? 'set GOPATH=' . ale#Escape('/foo/bar') . ' && ' + \ : 'GOPATH=' . ale#Escape('/foo/bar') . ' ' + + runtime ale_linters/go/gobuild.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + + call ale_linters#go#gobuild#ResetEnv() + +After: + Restore + + unlet! g:env_prefix + + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(The default gobuild command should be correct): + AssertEqual + \ ale_linters#go#gobuild#GetCommand(bufnr(''), ['/foo/bar', '/foo/baz']), + \ g:env_prefix . 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'go test -c -o /dev/null ./' + +Execute(The command for getting GOPATH should be correct): + AssertEqual ale_linters#go#gobuild#GoEnv(bufnr('')), 'go env GOPATH GOROOT' + + call ale_linters#go#gobuild#GetCommand(bufnr(''), ['/foo/bar', '/foo/baz']) + + " We shouldn't run `go env` many times after we've got it. + AssertEqual ale_linters#go#gobuild#GoEnv(bufnr('')), '' + +Execute(The GOPATH output should be used after it has been read once): + call ale_linters#go#gobuild#GetCommand(bufnr(''), ['/foo/bar', '/foo/baz']) + + AssertEqual + \ ale_linters#go#gobuild#GetCommand(bufnr(''), []), + \ g:env_prefix . 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'go test -c -o /dev/null ./' + +Execute(Extra options should be supported): + let g:ale_go_gobuild_options = '--foo-bar' + + AssertEqual + \ ale_linters#go#gobuild#GetCommand(bufnr(''), ['/foo/bar', '/foo/baz']), + \ g:env_prefix . 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'go test --foo-bar -c -o /dev/null ./' diff --git a/test/command_callback/test_gometalinter_command_callback.vader b/test/command_callback/test_gometalinter_command_callback.vader index 912396c..93a541c 100644 --- a/test/command_callback/test_gometalinter_command_callback.vader +++ b/test/command_callback/test_gometalinter_command_callback.vader @@ -1,9 +1,11 @@ Before: Save b:ale_go_gometalinter_executable Save b:ale_go_gometalinter_options + Save b:ale_go_gometalinter_lint_package let b:ale_go_gometalinter_executable = 'gometalinter' let b:ale_go_gometalinter_options = '' + let b:ale_go_gometalinter_lint_package = 0 runtime ale_linters/go/gometalinter.vim @@ -21,9 +23,10 @@ Execute(The gometalinter callback should return the right defaults): \ 'gometalinter', \ ale_linters#go#gometalinter#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape('gometalinter') - \ . ' --include=' . ale#Escape('^' . ale#util#EscapePCRE(expand('%'))) - \ . ' ' . ale#Escape(getcwd()), + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('gometalinter') + \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) + \ . ' .', \ ale_linters#go#gometalinter#GetCommand(bufnr('')) Execute(The gometalinter callback should use a configured executable): @@ -33,17 +36,26 @@ Execute(The gometalinter callback should use a configured executable): \ 'something else', \ ale_linters#go#gometalinter#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape('something else') - \ . ' --include=' . ale#Escape('^' . ale#util#EscapePCRE(expand('%'))) - \ . ' ' . ale#Escape(getcwd()), + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('something else') + \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) + \ . ' .', \ ale_linters#go#gometalinter#GetCommand(bufnr('')) Execute(The gometalinter callback should use configured options): let b:ale_go_gometalinter_options = '--foobar' AssertEqual - \ ale#Escape('gometalinter') - \ . ' --include=' . ale#Escape('^' . ale#util#EscapePCRE(expand('%'))) - \ . ' --foobar' - \ . ' ' . ale#Escape(getcwd()), + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('gometalinter') + \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t'))) + \ . ' --foobar' . ' .', + \ ale_linters#go#gometalinter#GetCommand(bufnr('')) + +Execute(The gometalinter `lint_package` option should use the correct command): + let b:ale_go_gometalinter_lint_package = 1 + + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('gometalinter') . ' .', \ ale_linters#go#gometalinter#GetCommand(bufnr('')) diff --git a/test/command_callback/test_gotype_command_callback.vader b/test/command_callback/test_gotype_command_callback.vader new file mode 100644 index 0000000..f95e842 --- /dev/null +++ b/test/command_callback/test_gotype_command_callback.vader @@ -0,0 +1,19 @@ +Before: + runtime ale_linters/go/gotype.vim + call ale#test#SetFilename('../go_files/testfile2.go') + +After: + call ale#linter#Reset() + + +Execute(The gotype callback should include other files from the directory but exclude the file itself): + let dir = expand('#' . bufnr('') . ':p:h') + AssertEqual + \ "gotype %t ". ale#Escape(ale#path#Simplify(dir . "/testfile.go")), + \ ale_linters#go#gotype#GetCommand(bufnr('')) + +Execute(The gotype callback should ignore test files): + call ale#test#SetFilename('bla_test.go') + AssertEqual + \ 0, + \ ale_linters#go#gotype#GetCommand(bufnr('')) diff --git a/test/command_callback/test_govet_command_callback.vader b/test/command_callback/test_govet_command_callback.vader new file mode 100644 index 0000000..a9b2960 --- /dev/null +++ b/test/command_callback/test_govet_command_callback.vader @@ -0,0 +1,16 @@ +Before: + runtime ale_linters/go/govet.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(The default command should be correct): + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ' go vet .', + \ ale_linters#go#govet#GetCommand(bufnr('')) diff --git a/test/command_callback/test_haml_hamllint_command_callback.vader b/test/command_callback/test_haml_hamllint_command_callback.vader new file mode 100644 index 0000000..0d9b1e0 --- /dev/null +++ b/test/command_callback/test_haml_hamllint_command_callback.vader @@ -0,0 +1,72 @@ +Before: + runtime ale_linters/haml/hamllint.vim + + let g:default_command = 'haml-lint %t' + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + unlet! g:default_command + unlet! b:conf + + call ale#linter#Reset() + call ale#test#RestoreDirectory() + +Execute(The default command should be correct): + AssertEqual g:default_command, ale_linters#haml#hamllint#GetCommand(bufnr('')) + +Execute(The command should have the .rubocop.yml prepended as an env var if one exists): + call ale#test#SetFilename('../hamllint-test-files/rubocop-yml/subdir/file.haml') + let b:conf = ale#path#Simplify(g:dir . '/../hamllint-test-files/rubocop-yml/.rubocop.yml') + + if has('win32') + " Windows uses 'set var=... && command' + AssertEqual + \ 'set HAML_LINT_RUBOCOP_CONF=' + \ . ale#Escape(b:conf) + \ . ' && ' . g:default_command, + \ ale_linters#haml#hamllint#GetCommand(bufnr('')) + else + " Unix uses 'var=... command' + AssertEqual + \ 'HAML_LINT_RUBOCOP_CONF=' + \ . ale#Escape(b:conf) + \ . ' ' . g:default_command, + \ ale_linters#haml#hamllint#GetCommand(bufnr('')) + endif + +Execute(The command should have the nearest .haml-lint.yml set as --config if it exists): + call ale#test#SetFilename('../hamllint-test-files/haml-lint-yml/subdir/file.haml') + let b:conf = ale#path#Simplify(g:dir . '/../hamllint-test-files/haml-lint-yml/.haml-lint.yml') + + AssertEqual + \ 'haml-lint --config ' + \ . ale#Escape(b:conf) + \ . ' %t', + \ ale_linters#haml#hamllint#GetCommand(bufnr('')) + +Execute(The command should include a .rubocop.yml and a .haml-lint if both are found): + call ale#test#SetFilename('../hamllint-test-files/haml-lint-and-rubocop/subdir/file.haml') + let b:conf_hamllint = ale#path#Simplify(g:dir . '/../hamllint-test-files/haml-lint-and-rubocop/.haml-lint.yml') + let b:conf_rubocop = ale#path#Simplify(g:dir . '/../hamllint-test-files/haml-lint-and-rubocop/.rubocop.yml') + + if has('win32') + " Windows uses 'set var=... && command' + AssertEqual + \ 'set HAML_LINT_RUBOCOP_CONF=' + \ . ale#Escape(b:conf_rubocop) + \ . ' && haml-lint --config ' + \ . ale#Escape(b:conf_hamllint) + \ . ' %t', + \ ale_linters#haml#hamllint#GetCommand(bufnr('')) + else + " Unix uses 'var=... command' + AssertEqual + \ 'HAML_LINT_RUBOCOP_CONF=' + \ . ale#Escape(b:conf_rubocop) + \ . ' haml-lint --config ' + \ . ale#Escape(b:conf_hamllint) + \ . ' %t', + \ ale_linters#haml#hamllint#GetCommand(bufnr('')) + endif diff --git a/test/command_callback/test_haskell_ghc_command_callbacks.vader b/test/command_callback/test_haskell_ghc_command_callbacks.vader new file mode 100644 index 0000000..edaf2b9 --- /dev/null +++ b/test/command_callback/test_haskell_ghc_command_callbacks.vader @@ -0,0 +1,23 @@ +Before: + Save g:ale_haskell_ghc_options + + unlet! g:ale_haskell_ghc_options + unlet! b:ale_haskell_ghc_options + + runtime ale_linters/haskell/ghc.vim + +After: + Restore + unlet! b:ale_haskell_ghc_options + call ale#linter#Reset() + +Execute(The options should be used in the command): + AssertEqual + \ 'ghc -fno-code -v0 %t', + \ ale_linters#haskell#ghc#GetCommand(bufnr('')) + + let b:ale_haskell_ghc_options = 'foobar' + + AssertEqual + \ 'ghc foobar %t', + \ ale_linters#haskell#ghc#GetCommand(bufnr('')) diff --git a/test/command_callback/test_htmlhint_command_callback.vader b/test/command_callback/test_htmlhint_command_callback.vader new file mode 100644 index 0000000..5bb21a6 --- /dev/null +++ b/test/command_callback/test_htmlhint_command_callback.vader @@ -0,0 +1,71 @@ +Before: + Save g:ale_html_htmlhint_options + Save g:ale_html_htmlhint_executable + Save g:ale_html_htmlhint_use_global + + unlet! g:ale_html_htmlhint_options + unlet! g:ale_html_htmlhint_executable + unlet! g:ale_html_htmlhint_use_global + + runtime ale_linters/html/htmlhint.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('htmlhint_paths/test.html') + + let g:node_executable = ale#path#Simplify( + \ g:dir + \ . '/htmlhint_paths/node_modules/.bin/htmlhint' + \) + let g:config_path = ale#path#Simplify( + \ g:dir + \ . '/htmlhint_paths/with_config/.htmlhintrc' + \) + +After: + Restore + + unlet! g:node_executable + unlet! g:config_path + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual + \ ale#Escape(g:node_executable) . ' --format=unix %t', + \ ale_linters#html#htmlhint#GetCommand(bufnr('')) + +Execute(The global executable should be uesd if the option is set): + let g:ale_html_htmlhint_executable = 'foo' + let g:ale_html_htmlhint_use_global = 1 + + AssertEqual + \ ale#Escape('foo') . ' --format=unix %t', + \ ale_linters#html#htmlhint#GetCommand(bufnr('')) + +" This is so old configurations which might include this still work. +Execute(--format=unix should be removed from the options if added): + let g:ale_html_htmlhint_options = '--format=unix' + + AssertEqual + \ ale#Escape(g:node_executable) . ' --format=unix %t', + \ ale_linters#html#htmlhint#GetCommand(bufnr('')) + +Execute(The configuration file should be automatically detected): + call ale#test#SetFilename('htmlhint_paths/with_config/test.html') + + AssertEqual + \ ale#Escape(g:node_executable) + \ . ' --config ' . ale#Escape(g:config_path) + \ . ' --format=unix %t', + \ ale_linters#html#htmlhint#GetCommand(bufnr('')) + +" This is so old configurations which might include the config will work. +Execute(The configuration file should be configurable through the options variable): + call ale#test#SetFilename('htmlhint_paths/with_config/test.html') + let g:ale_html_htmlhint_options = '--config=/foo/bar/.htmlhintrc' + + AssertEqual + \ ale#Escape(g:node_executable) + \ . ' --config=/foo/bar/.htmlhintrc' + \ . ' --format=unix %t', + \ ale_linters#html#htmlhint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_iverilog_command_callback.vader b/test/command_callback/test_iverilog_command_callback.vader new file mode 100644 index 0000000..2c63317 --- /dev/null +++ b/test/command_callback/test_iverilog_command_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_verilog_iverilog_options + + unlet! g:ale_verilog_iverilog_options + + runtime ale_linters/verilog/iverilog.vim + +After: + Restore + + call ale#linter#Reset() + +Execute(The default iverilog command should be correct): + AssertEqual + \ 'iverilog -t null -Wall %t', + \ ale_linters#verilog#iverilog#GetCommand(bufnr('')) + +Execute(iverilog options should be configurable): + " Additional args for the linter + let g:ale_verilog_iverilog_options = '-y.' + + AssertEqual + \ 'iverilog -t null -Wall -y. %t', + \ ale_linters#verilog#iverilog#GetCommand(bufnr('')) diff --git a/test/command_callback/test_javac_command_callback.vader b/test/command_callback/test_javac_command_callback.vader index 8033e4f..7823d03 100644 --- a/test/command_callback/test_javac_command_callback.vader +++ b/test/command_callback/test_javac_command_callback.vader @@ -28,12 +28,15 @@ Before: call ale#test#SetFilename('dummy.java') + let g:prefix = 'cd ' . ale#Escape(expand('%:p:h')) . ' && javac -Xlint' + After: call ale#test#RestoreDirectory() Restore unlet! g:cp_sep + unlet! g:prefix delfunction GetCommand @@ -43,20 +46,21 @@ After: call ale#engine#Cleanup(bufnr('')) Execute(The javac callback should return the correct default value): - AssertEqual 'javac -Xlint -d TEMP %t', GetCommand([]) + AssertEqual g:prefix . ' -d TEMP %t', GetCommand([]) Execute(The javac callback should use g:ale_java_javac_classpath correctly): let g:ale_java_javac_classpath = 'foo.jar' AssertEqual - \ 'javac -Xlint' + \ g:prefix \ . ' -cp ' . ale#Escape('foo.jar') \ . ' -d TEMP %t', \ GetCommand([]) Execute(The javac callback should include discovered classpaths): AssertEqual - \ 'javac -Xlint -cp ' + \ g:prefix + \ . ' -cp ' \ . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep)) \ . ' -d TEMP %t', \ GetCommand([ @@ -70,7 +74,8 @@ Execute(The javac callback should combine discovered classpaths and manual ones) let g:ale_java_javac_classpath = 'configured.jar' AssertEqual - \ 'javac -Xlint -cp ' + \ g:prefix + \ . ' -cp ' \ . ale#Escape(join( \ [ \ '/foo/bar.jar', @@ -90,7 +95,8 @@ Execute(The javac callback should combine discovered classpaths and manual ones) let g:ale_java_javac_classpath = 'configured.jar' . g:cp_sep . 'configured2.jar' AssertEqual - \ 'javac -Xlint -cp ' + \ g:prefix + \ . ' -cp ' \ . ale#Escape(join( \ [ \ '/foo/bar.jar', @@ -110,13 +116,13 @@ Execute(The javac callback should combine discovered classpaths and manual ones) Execute(The javac callback should detect source directories): call ale#engine#Cleanup(bufnr('')) - :e! java_paths/src/main/java/com/something/dummy + noautocmd e! java_paths/src/main/java/com/something/dummy call ale#engine#InitBufferInfo(bufnr('')) AssertEqual - \ 'javac -Xlint' + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && javac -Xlint' \ . ' -sourcepath ' . ale#Escape( - \ ale#path#Winify(g:dir . '/java_paths/src/main/java/') + \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/') \ ) \ . ' -d TEMP %t', \ GetCommand([]) @@ -127,10 +133,10 @@ Execute(The javac callback should combine detected source directories and classp call ale#engine#InitBufferInfo(bufnr('')) AssertEqual - \ 'javac -Xlint' + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && javac -Xlint' \ . ' -cp ' . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep)) \ . ' -sourcepath ' . ale#Escape( - \ ale#path#Winify(g:dir . '/java_paths/src/main/java/') + \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/') \ ) \ . ' -d TEMP %t', \ GetCommand([ @@ -146,6 +152,36 @@ Execute(The javac callback should use g:ale_java_javac_options correctly): let b:command = ale_linters#java#javac#GetCommand(bufnr(''), []) AssertEqual - \ 'javac -Xlint' + \ g:prefix \ . ' -d TEMP --anything --else %t', \ GetCommand([]) + +Execute(The javac callback should include src/test/java for test paths): + call ale#engine#Cleanup(bufnr('')) + " The test path is only included for test files. + " Regular Java files shouldn't import from tests. + noautocmd e! java_paths/src/test/java/com/something/dummy + call ale#engine#InitBufferInfo(bufnr('')) + + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && javac -Xlint' + \ . ' -sourcepath ' . ale#Escape(join([ + \ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'), + \ ale#path#Simplify(g:dir . '/java_paths/src/test/java/'), + \ ], g:cp_sep)) + \ . ' -d TEMP %t', + \ GetCommand([]) + +Execute(The javac callback should include src/main/jaxb when available): + call ale#engine#Cleanup(bufnr('')) + noautocmd e! java_paths_with_jaxb/src/main/java/com/something/dummy + call ale#engine#InitBufferInfo(bufnr('')) + + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && javac -Xlint' + \ . ' -sourcepath ' . ale#Escape(join([ + \ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/java/'), + \ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/jaxb/'), + \ ], g:cp_sep)) + \ . ' -d TEMP %t', + \ GetCommand([]) diff --git a/test/command_callback/test_less_stylelint_command_callback.vader b/test/command_callback/test_less_stylelint_command_callback.vader new file mode 100644 index 0000000..a5912ec --- /dev/null +++ b/test/command_callback/test_less_stylelint_command_callback.vader @@ -0,0 +1,60 @@ +Before: + Save g:ale_less_stylelint_executable + Save g:ale_less_stylelint_use_global + Save g:ale_less_stylelint_options + + unlet! b:executable + + unlet! g:ale_less_stylelint_executable + unlet! g:ale_less_stylelint_use_global + unlet! g:ale_less_stylelint_options + + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('testfile.less') + + runtime ale_linters/less/stylelint.vim + +After: + Restore + + unlet! b:executable + unlet! b:ale_less_stylelint_executable + unlet! b:ale_less_stylelint_use_global + unlet! b:ale_less_stylelint_options + + call ale#test#SetFilename('test.txt') + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(node_modules directories should be discovered): + call ale#test#SetFilename('stylelint_paths/nested/testfile.less') + + let b:executable = ale#path#Simplify( + \ g:dir + \ . '/stylelint_paths/node_modules/.bin/stylelint' + \) + + AssertEqual b:executable, ale_linters#less#stylelint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape(b:executable) . ' --stdin-filename %s', + \ ale_linters#less#stylelint#GetCommand(bufnr('')) + +Execute(The global override should work): + let b:ale_less_stylelint_executable = 'foobar' + let b:ale_less_stylelint_use_global = 1 + + call ale#test#SetFilename('stylelint_paths/nested/testfile.less') + + AssertEqual 'foobar', ale_linters#less#stylelint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('foobar') . ' --stdin-filename %s', + \ ale_linters#less#stylelint#GetCommand(bufnr('')) + +Execute(Extra options should be configurable): + let b:ale_less_stylelint_options = '--whatever' + + AssertEqual 'stylelint', ale_linters#less#stylelint#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('stylelint') . ' --whatever --stdin-filename %s', + \ ale_linters#less#stylelint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_lessc_command_callback.vader b/test/command_callback/test_lessc_command_callback.vader new file mode 100644 index 0000000..ec2899d --- /dev/null +++ b/test/command_callback/test_lessc_command_callback.vader @@ -0,0 +1,82 @@ +Before: + Save g:ale_less_lessc_executable + Save g:ale_less_lessc_use_global + Save g:ale_less_lessc_options + + unlet! b:executable + + unlet! g:ale_less_lessc_executable + unlet! g:ale_less_lessc_use_global + unlet! g:ale_less_lessc_options + + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('testfile.less') + + runtime ale_linters/less/lessc.vim + +After: + Restore + + unlet! b:executable + unlet! b:ale_less_lessc_executable + unlet! b:ale_less_lessc_use_global + unlet! b:ale_less_lessc_options + + call ale#test#SetFilename('test.txt') + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(node_modules directories should be discovered): + call ale#test#SetFilename('lessc_paths/nested/testfile.less') + + let b:executable = ale#path#Simplify( + \ g:dir + \ . '/lessc_paths/node_modules/.bin/lessc' + \) + + AssertEqual + \ b:executable, + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape(b:executable) + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Simplify(g:dir . '/lessc_paths/nested')) + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) + +Execute(The global override should work): + let b:ale_less_lessc_executable = 'foobar' + let b:ale_less_lessc_use_global = 1 + + call ale#test#SetFilename('lessc_paths/nested/testfile.less') + + AssertEqual + \ 'foobar', + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('foobar') + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Simplify(g:dir . '/lessc_paths/nested')) + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) + +Execute(Extra options should be configurable): + let b:ale_less_lessc_options = '--whatever' + + AssertEqual + \ 'lessc', + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('lessc') + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Simplify(g:dir)) + \ . ' --whatever' + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) diff --git a/test/command_callback/test_lintr_command_callback.vader b/test/command_callback/test_lintr_command_callback.vader index 3199b49..e655328 100644 --- a/test/command_callback/test_lintr_command_callback.vader +++ b/test/command_callback/test_lintr_command_callback.vader @@ -17,18 +17,32 @@ Execute(The default lintr command should be correct): AssertEqual \ 'cd ' . ale#Escape(getcwd()) . ' && ' \ . 'Rscript -e ' - \ . ale#Escape('lintr::lint(commandArgs(TRUE)[1], eval(parse(text = commandArgs(TRUE)[2])))') - \ . ' %t ' - \ . ale#Escape('lintr::with_defaults()'), + \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' + \ . 'lint(cache = FALSE, commandArgs(TRUE), ' + \ . 'with_defaults())') + \ . ' %t', \ ale_linters#r#lintr#GetCommand(bufnr('')) Execute(The lintr options should be configurable): - let b:ale_r_lintr_options = 'lintr::with_defaults(object_usage_linter = NULL)' + let b:ale_r_lintr_options = 'with_defaults(object_usage_linter = NULL)' AssertEqual \ 'cd ' . ale#Escape(getcwd()) . ' && ' \ . 'Rscript -e ' - \ . ale#Escape('lintr::lint(commandArgs(TRUE)[1], eval(parse(text = commandArgs(TRUE)[2])))') - \ . ' %t ' - \ . ale#Escape('lintr::with_defaults(object_usage_linter = NULL)'), + \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' + \ . 'lint(cache = FALSE, commandArgs(TRUE), ' + \ . 'with_defaults(object_usage_linter = NULL))') + \ . ' %t', + \ ale_linters#r#lintr#GetCommand(bufnr('')) + +Execute(If the lint_package flag is set, lintr::lint_package should be called): + let b:ale_r_lintr_lint_package = 1 + + AssertEqual + \ 'cd ' . ale#Escape(getcwd()) . ' && ' + \ . 'Rscript -e ' + \ . ale#Escape('suppressPackageStartupMessages(library(lintr));' + \ . 'lint_package(cache = FALSE, ' + \ . 'linters = with_defaults())') + \ . ' %t', \ ale_linters#r#lintr#GetCommand(bufnr('')) diff --git a/test/command_callback/test_luac_command_callback.vader b/test/command_callback/test_luac_command_callback.vader new file mode 100644 index 0000000..f9eb4d3 --- /dev/null +++ b/test/command_callback/test_luac_command_callback.vader @@ -0,0 +1,16 @@ +Before: + runtime ale_linters/lua/luac.vim + +After: + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual ale#Escape('luac') . ' -p -', + \ join(split(ale_linters#lua#luac#GetCommand(1))) + +Execute(The luac executable should be configurable): + let g:ale_lua_luac_executable = 'luac.sh' + + AssertEqual 'luac.sh', ale_linters#lua#luac#GetExecutable(1) + AssertEqual ale#Escape('luac.sh') . ' -p -', + \ join(split(ale_linters#lua#luac#GetCommand(1))) diff --git a/test/command_callback/test_markdown_mdl_command_callback.vader b/test/command_callback/test_markdown_mdl_command_callback.vader new file mode 100644 index 0000000..3a68a4b --- /dev/null +++ b/test/command_callback/test_markdown_mdl_command_callback.vader @@ -0,0 +1,28 @@ +Before: + Save g:ale_markdown_mdl_executable + Save g:ale_markdown_mdl_options + + unlet! g:ale_markdown_mdl_executable + unlet! g:ale_markdown_mdl_options + + runtime ale_linters/markdown/mdl.vim + +After: + Restore + + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual ale_linters#markdown#mdl#GetExecutable(bufnr('')), 'mdl' + AssertEqual + \ ale_linters#markdown#mdl#GetCommand(bufnr('')), + \ ale#Escape('mdl') + +Execute(The executable and options should be configurable): + let g:ale_markdown_mdl_executable = 'foo bar' + let g:ale_markdown_mdl_options = '--wat' + + AssertEqual ale_linters#markdown#mdl#GetExecutable(bufnr('')), 'foo bar' + AssertEqual + \ ale_linters#markdown#mdl#GetCommand(bufnr('')), + \ ale#Escape('foo bar') . ' --wat' diff --git a/test/command_callback/test_mypy_command_callback.vader b/test/command_callback/test_mypy_command_callback.vader index 4ccc008..6a0add5 100644 --- a/test/command_callback/test_mypy_command_callback.vader +++ b/test/command_callback/test_mypy_command_callback.vader @@ -61,7 +61,7 @@ Execute(The mypy command should switch directories to the detected project root) \ 'mypy', \ ale_linters#python#mypy#GetExecutable(bufnr('')) AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/python_paths/no_virtualenv/subdir')) + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir')) \ . ' && ' . ale#Escape('mypy') \ . ' --show-column-numbers ' \ . '--shadow-file %s %t %s', @@ -70,13 +70,13 @@ Execute(The mypy command should switch directories to the detected project root) Execute(The mypy callbacks should detect virtualenv directories and switch to the project root): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') - let b:executable = ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/mypy') + let b:executable = ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/mypy') AssertEqual \ b:executable, \ ale_linters#python#mypy#GetExecutable(bufnr('')) AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/subdir')) + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir')) \ . ' && ' . ale#Escape(b:executable) \ . ' --show-column-numbers ' \ . '--shadow-file %s %t %s', @@ -90,7 +90,7 @@ Execute(You should able able to use the global mypy instead): \ 'mypy', \ ale_linters#python#mypy#GetExecutable(bufnr('')) AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/subdir')) + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir')) \ . ' && ' . ale#Escape('mypy') \ . ' --show-column-numbers ' \ . '--shadow-file %s %t %s', diff --git a/test/command_callback/test_ocaml_ols_callbacks.vader b/test/command_callback/test_ocaml_ols_callbacks.vader new file mode 100644 index 0000000..d10898f --- /dev/null +++ b/test/command_callback/test_ocaml_ols_callbacks.vader @@ -0,0 +1,54 @@ +Before: + Save &filetype + Save g:ale_ocaml_ols_executable + Save g:ale_ocaml_ols_use_global + + let &filetype = 'ocaml' + unlet! g:ale_ocaml_ols_executable + unlet! g:ale_ocaml_ols_use_global + + runtime ale_linters/ocaml/ols.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The language string should be correct): + AssertEqual 'ocaml', ale#handlers#ols#GetLanguage(bufnr('')) + +Execute(The default executable should be correct): + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('ocaml-language-server') . ' --stdio', + \ ale#handlers#ols#GetCommand(bufnr('')) + +Execute(The project root should be detected correctly): + AssertEqual '', ale#handlers#ols#GetProjectRoot(bufnr('')) + + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual + \ ale#path#Simplify(g:dir . '/ols_paths'), + \ ale#handlers#ols#GetProjectRoot(bufnr('')) + +Execute(The local executable should be used when available): + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual + \ ale#path#Simplify(g:dir . '/ols_paths/node_modules/.bin/ocaml-language-server'), + \ ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The gloabl executable should always be used when use_global is set): + let g:ale_ocaml_ols_use_global = 1 + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The executable should be configurable): + let g:ale_ocaml_ols_executable = 'foobar' + + AssertEqual 'foobar', ale#handlers#ols#GetExecutable(bufnr('')) diff --git a/test/command_callback/test_perl_command_callback.vader b/test/command_callback/test_perl_command_callback.vader new file mode 100644 index 0000000..ba85e53 --- /dev/null +++ b/test/command_callback/test_perl_command_callback.vader @@ -0,0 +1,37 @@ +Before: + Save g:ale_perl_perl_executable + Save g:ale_perl_perl_options + + unlet! g:ale_perl_perl_executable + unlet! g:ale_perl_perl_options + + runtime ale_linters/perl/perl.vim + +After: + Restore + + unlet! b:ale_perl_perl_executable + unlet! b:ale_perl_perl_options + + call ale#linter#Reset() + +Execute(The default Perl command callback should be correct): + AssertEqual + \ 'perl', + \ ale_linters#perl#perl#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('perl') . ' -c -Mwarnings -Ilib %t', + \ ale_linters#perl#perl#GetCommand(bufnr('')) + +Execute(Overriding the executable and command should work): + let b:ale_perl_perl_executable = 'foobar' + let b:ale_perl_perl_options = '-w' + + AssertEqual + \ 'foobar', + \ ale_linters#perl#perl#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('foobar') . ' -w %t', + \ ale_linters#perl#perl#GetCommand(bufnr('')) diff --git a/test/command_callback/test_perlcritic_command_callback.vader b/test/command_callback/test_perlcritic_command_callback.vader index 8f339d3..6507868 100644 --- a/test/command_callback/test_perlcritic_command_callback.vader +++ b/test/command_callback/test_perlcritic_command_callback.vader @@ -43,7 +43,7 @@ Execute(The command should be correct with g:ale_perl_perlcritic_showrules on): Execute(The command search for the profile file when set): let b:ale_perl_perlcritic_profile = 'README.md' - let b:readme_path = ale#path#Winify(expand('%:p:h:h:h') . '/README.md') + let b:readme_path = ale#path#Simplify(expand('%:p:h:h:h') . '/README.md') AssertEqual \ ale#Escape('perlcritic') . ' --verbose ''%l:%c %m\n'' --nocolor' diff --git a/test/command_callback/test_php_langserver_callbacks.vader b/test/command_callback/test_php_langserver_callbacks.vader index 0c7e69e..0dc3063 100644 --- a/test/command_callback/test_php_langserver_callbacks.vader +++ b/test/command_callback/test_php_langserver_callbacks.vader @@ -33,10 +33,10 @@ Execute(Vendor executables should be detected): call ale#test#SetFilename('php-langserver-project/test.php') AssertEqual - \ ale#path#Winify(g:dir . '/php-langserver-project/vendor/bin/php-language-server.php'), + \ ale#path#Simplify(g:dir . '/php-langserver-project/vendor/bin/php-language-server.php'), \ ale_linters#php#langserver#GetExecutable(bufnr('')) AssertEqual - \ 'php ' . ale#Escape(ale#path#Winify( + \ 'php ' . ale#Escape(ale#path#Simplify( \ g:dir \ . '/php-langserver-project/vendor/bin/php-language-server.php' \ )), diff --git a/test/command_callback/test_phpmd_command_callbacks.vader b/test/command_callback/test_phpmd_command_callbacks.vader new file mode 100644 index 0000000..928b977 --- /dev/null +++ b/test/command_callback/test_phpmd_command_callbacks.vader @@ -0,0 +1,20 @@ +Before: + Save g:ale_php_phpmd_executable + + unlet! g:ale_php_phpmd_executable + + runtime ale_linters/php/phpmd.vim + +After: + Restore + + call ale#linter#Reset() + +Execute(Custom executables should be used for the executable and command): + let g:ale_php_phpmd_executable = 'phpmd_test' + + AssertEqual 'phpmd_test', ale_linters#php#phpmd#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('phpmd_test') + \ . ' %s text cleancode,codesize,controversial,design,naming,unusedcode --ignore-violations-on-exit %t', + \ ale_linters#php#phpmd#GetCommand(bufnr('')) diff --git a/test/command_callback/test_pony_ponyc_command_callbacks.vader b/test/command_callback/test_pony_ponyc_command_callbacks.vader new file mode 100644 index 0000000..7acbfa9 --- /dev/null +++ b/test/command_callback/test_pony_ponyc_command_callbacks.vader @@ -0,0 +1,23 @@ +Before: + Save g:ale_pony_ponyc_options + + unlet! g:ale_pony_ponyc_options + unlet! b:ale_pony_ponyc_options + + runtime ale_linters/pony/ponyc.vim + +After: + Restore + unlet! b:ale_pony_ponyc_options + call ale#linter#Reset() + +Execute(The options should be used in the command): + AssertEqual + \ ale#Escape('ponyc') . ' --pass paint', + \ ale_linters#pony#ponyc#GetCommand(bufnr('')) + + let b:ale_pony_ponyc_options = 'foobar' + + AssertEqual + \ ale#Escape('ponyc') . ' foobar', + \ ale_linters#pony#ponyc#GetCommand(bufnr('')) diff --git a/test/command_callback/test_proto_command_callback.vader b/test/command_callback/test_proto_command_callback.vader new file mode 100644 index 0000000..76050c6 --- /dev/null +++ b/test/command_callback/test_proto_command_callback.vader @@ -0,0 +1,21 @@ +Before: + call ale#test#SetFilename('test.proto') + +After: + Restore + + unlet! b:ale_proto_protoc_gen_lint_options + + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual + \ 'protoc' . ' -I ' . ale#Escape(getcwd()) . ' --lint_out=. ' . '%s', + \ ale_linters#proto#protoc_gen_lint#GetCommand(bufnr('')) + +Execute(The callback should include any additional options): + let b:ale_proto_protoc_gen_lint_options = '--some-option' + + AssertEqual + \ 'protoc' . ' -I ' . ale#Escape(getcwd()) . ' --some-option --lint_out=. ' . '%s', + \ ale_linters#proto#protoc_gen_lint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_puglint_command_callback.vader b/test/command_callback/test_puglint_command_callback.vader index 1194658..f9b4a85 100644 --- a/test/command_callback/test_puglint_command_callback.vader +++ b/test/command_callback/test_puglint_command_callback.vader @@ -21,12 +21,12 @@ Execute(puglint should detect local executables and package.json): call ale#test#SetFilename('puglint_project/test.pug') AssertEqual - \ ale#path#Winify(g:dir . '/puglint_project/node_modules/.bin/pug-lint'), + \ ale#path#Simplify(g:dir . '/puglint_project/node_modules/.bin/pug-lint'), \ ale_linters#pug#puglint#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape(ale#path#Winify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/puglint_project/package.json')) + \ ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/package.json')) \ . ' -r inline %t', \ ale_linters#pug#puglint#GetCommand(bufnr('')) @@ -39,7 +39,7 @@ Execute(puglint should use global executables if configured): AssertEqual \ ale#Escape('pug-lint') - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/puglint_project/package.json')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/package.json')) \ . ' -r inline %t', \ ale_linters#pug#puglint#GetCommand(bufnr('')) @@ -47,8 +47,8 @@ Execute(puglint should detect .pug-lintrc): call ale#test#SetFilename('puglint_project/puglint_rc_dir/subdir/test.pug') AssertEqual - \ ale#Escape(ale#path#Winify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/puglint_project/puglint_rc_dir/.pug-lintrc')) + \ ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/puglint_rc_dir/.pug-lintrc')) \ . ' -r inline %t', \ ale_linters#pug#puglint#GetCommand(bufnr('')) @@ -56,8 +56,8 @@ Execute(puglint should detect .pug-lintrc.js): call ale#test#SetFilename('puglint_project/puglint_rc_js_dir/subdir/test.pug') AssertEqual - \ ale#Escape(ale#path#Winify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/puglint_project/puglint_rc_js_dir/.pug-lintrc.js')) + \ ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/puglint_rc_js_dir/.pug-lintrc.js')) \ . ' -r inline %t', \ ale_linters#pug#puglint#GetCommand(bufnr('')) @@ -65,7 +65,7 @@ Execute(puglint should detect .pug-lintrc.json): call ale#test#SetFilename('puglint_project/puglint_rc_json_dir/subdir/test.pug') AssertEqual - \ ale#Escape(ale#path#Winify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/puglint_project/puglint_rc_json_dir/.pug-lintrc.json')) + \ ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/node_modules/.bin/pug-lint')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/puglint_project/puglint_rc_json_dir/.pug-lintrc.json')) \ . ' -r inline %t', \ ale_linters#pug#puglint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_pycodestyle_command_callback.vader b/test/command_callback/test_pycodestyle_command_callback.vader index 58aefa2..5b309e1 100644 --- a/test/command_callback/test_pycodestyle_command_callback.vader +++ b/test/command_callback/test_pycodestyle_command_callback.vader @@ -1,13 +1,15 @@ Before: + Save g:ale_python_pycodestyle_executable + Save g:ale_python_pycodestyle_options + Save g:ale_python_pycodestyle_use_global + runtime ale_linters/python/pycodestyle.vim - Save g:ale_python_pycodestyle_executable, - \ g:ale_python_pycodestyle_options, - \ g:ale_python_pycodestyle_use_global After: - call ale#linter#Reset() Restore + call ale#linter#Reset() + Execute(The pycodestyle command callback should return default string): AssertEqual ale#Escape('pycodestyle') . ' -', \ ale_linters#python#pycodestyle#GetCommand(bufnr('')) diff --git a/test/command_callback/test_pyflakes_command_callback.vader b/test/command_callback/test_pyflakes_command_callback.vader new file mode 100644 index 0000000..e8486ca --- /dev/null +++ b/test/command_callback/test_pyflakes_command_callback.vader @@ -0,0 +1,49 @@ +Before: + Save g:ale_python_pyflakes_executable + Save g:ale_python_pyflakes_use_global + + unlet! g:ale_python_pyflakes_executable + unlet! g:ale_python_pyflakes_use_global + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + + call ale#test#SetDirectory('/testplugin/test/command_callback') + + runtime ale_linters/python/pyflakes.vim + +After: + Restore + + unlet! b:bin_dir + unlet! b:executable + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The pyflakes command callback should return default string): + AssertEqual ale#Escape('pyflakes') . ' %t', + \ ale_linters#python#pyflakes#GetCommand(bufnr('')) + +Execute(The pyflakes executable should be configurable): + let g:ale_python_pyflakes_executable = '~/.local/bin/pyflakes' + + AssertEqual ale#Escape('~/.local/bin/pyflakes') . ' %t', + \ ale_linters#python#pyflakes#GetCommand(bufnr('')) + +Execute(The pyflakes executable should be run from the virtualenv path): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + let b:executable = ale#path#Simplify( + \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/pyflakes' + \) + + AssertEqual ale#Escape(b:executable) . ' %t', + \ ale_linters#python#pyflakes#GetCommand(bufnr('')) + +Execute(You should be able to override the pyflakes virtualenv lookup): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + let g:ale_python_pyflakes_use_global = 1 + + AssertEqual ale#Escape('pyflakes') . ' %t', + \ ale_linters#python#pyflakes#GetCommand(bufnr('')) diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader index 447409b..1ff8e35 100644 --- a/test/command_callback/test_pylint_command_callback.vader +++ b/test/command_callback/test_pylint_command_callback.vader @@ -61,7 +61,7 @@ Execute(The pylint callbacks shouldn't detect virtualenv directories where they Execute(The pylint callbacks should detect virtualenv directories): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') - let b:executable = ale#path#Winify( + let b:executable = ale#path#Simplify( \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/pylint' \) diff --git a/test/command_callback/test_pyls_command_callback.vader b/test/command_callback/test_pyls_command_callback.vader new file mode 100644 index 0000000..06ea718 --- /dev/null +++ b/test/command_callback/test_pyls_command_callback.vader @@ -0,0 +1,49 @@ +Before: + Save g:ale_python_pyls_executable + Save g:ale_python_pyls_use_global + + unlet! g:ale_python_pyls_executable + unlet! g:ale_python_pyls_use_global + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + + call ale#test#SetDirectory('/testplugin/test/command_callback') + + runtime ale_linters/python/pyls.vim + +After: + Restore + + unlet! b:bin_dir + unlet! b:executable + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The pyls command callback should return default string): + AssertEqual ale#Escape('pyls'), + \ ale_linters#python#pyls#GetCommand(bufnr('')) + +Execute(The pyls executable should be configurable): + let g:ale_python_pyls_executable = '~/.local/bin/pyls' + + AssertEqual ale#Escape('~/.local/bin/pyls'), + \ ale_linters#python#pyls#GetCommand(bufnr('')) + +Execute(The pyls executable should be run from the virtualenv path): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + let b:executable = ale#path#Simplify( + \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/pyls' + \) + + AssertEqual ale#Escape(b:executable), + \ ale_linters#python#pyls#GetCommand(bufnr('')) + +Execute(You should be able to override the pyls virtualenv lookup): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + let g:ale_python_pyls_use_global = 1 + + AssertEqual ale#Escape('pyls'), + \ ale_linters#python#pyls#GetCommand(bufnr('')) diff --git a/test/command_callback/test_rails_best_practices_command_callback.vader b/test/command_callback/test_rails_best_practices_command_callback.vader index 7305f4a..b4d2e82 100644 --- a/test/command_callback/test_rails_best_practices_command_callback.vader +++ b/test/command_callback/test_rails_best_practices_command_callback.vader @@ -9,7 +9,7 @@ Before: let b:args = '--silent -f json' \ . ' --output-file ' . (has('win32') ? '%t' : '/dev/stdout') - let b:app_path = ale#path#Winify(g:dir . '/../ruby_fixtures/valid_rails_app') + let b:app_path = ale#path#Simplify(g:dir . '/../ruby_fixtures/valid_rails_app') let b:suffix = has('win32') ? '; type %t' : '' After: diff --git a/test/command_callback/test_reason_ols_callbacks.vader b/test/command_callback/test_reason_ols_callbacks.vader new file mode 100644 index 0000000..5fb39af --- /dev/null +++ b/test/command_callback/test_reason_ols_callbacks.vader @@ -0,0 +1,54 @@ +Before: + Save &filetype + Save g:ale_reason_ols_executable + Save g:ale_reason_ols_use_global + + let &filetype = 'reason' + unlet! g:ale_reason_ols_executable + unlet! g:ale_reason_ols_use_global + + runtime ale_linters/reason/ols.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The language string should be correct): + AssertEqual 'reason', ale#handlers#ols#GetLanguage(bufnr('')) + +Execute(The default executable should be correct): + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('ocaml-language-server') . ' --stdio', + \ ale#handlers#ols#GetCommand(bufnr('')) + +Execute(The project root should be detected correctly): + AssertEqual '', ale#handlers#ols#GetProjectRoot(bufnr('')) + + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual + \ ale#path#Simplify(g:dir . '/ols_paths'), + \ ale#handlers#ols#GetProjectRoot(bufnr('')) + +Execute(The local executable should be used when available): + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual + \ ale#path#Simplify(g:dir . '/ols_paths/node_modules/.bin/ocaml-language-server'), + \ ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The gloabl executable should always be used when use_global is set): + let g:ale_reason_ols_use_global = 1 + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The executable should be configurable): + let g:ale_reason_ols_executable = 'foobar' + + AssertEqual 'foobar', ale#handlers#ols#GetExecutable(bufnr('')) diff --git a/test/command_callback/test_rubocop_command_callback.vader b/test/command_callback/test_rubocop_command_callback.vader index fddf714..f0aa194 100644 --- a/test/command_callback/test_rubocop_command_callback.vader +++ b/test/command_callback/test_rubocop_command_callback.vader @@ -17,7 +17,7 @@ Execute(Executable should default to rubocop): AssertEqual \ ale#Escape('rubocop') \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Winify(g:dir . '/dummy.rb')), + \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')), \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) Execute(Should be able to set a custom executable): @@ -26,7 +26,7 @@ Execute(Should be able to set a custom executable): AssertEqual \ ale#Escape('bin/rubocop') \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Winify(g:dir . '/dummy.rb')), + \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')), \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) Execute(Setting bundle appends 'exec rubocop'): @@ -35,5 +35,5 @@ Execute(Setting bundle appends 'exec rubocop'): AssertEqual \ ale#Escape('path to/bundle') . ' exec rubocop' \ . ' --format json --force-exclusion --stdin ' - \ . ale#Escape(ale#path#Winify(g:dir . '/dummy.rb')), + \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb')), \ ale_linters#ruby#rubocop#GetCommand(bufnr('')) diff --git a/test/command_callback/test_ruby_command_callback.vader b/test/command_callback/test_ruby_command_callback.vader new file mode 100644 index 0000000..3813d56 --- /dev/null +++ b/test/command_callback/test_ruby_command_callback.vader @@ -0,0 +1,25 @@ +Before: + Save g:ale_ruby_ruby_executable + + unlet! g:ale_ruby_ruby_executable + + runtime ale_linters/ruby/ruby.vim + +After: + Restore + + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual 'ruby', ale_linters#ruby#ruby#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('ruby') . ' -w -c -T1 %t', + \ ale_linters#ruby#ruby#GetCommand(bufnr('')) + +Execute(The executable should be configurable): + let g:ale_ruby_ruby_executable = 'foobar' + + AssertEqual 'foobar', ale_linters#ruby#ruby#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('foobar') . ' -w -c -T1 %t', + \ ale_linters#ruby#ruby#GetCommand(bufnr('')) diff --git a/test/command_callback/test_rust_rls_callbacks.vader b/test/command_callback/test_rust_rls_callbacks.vader index b01f8f0..693d6e9 100644 --- a/test/command_callback/test_rust_rls_callbacks.vader +++ b/test/command_callback/test_rust_rls_callbacks.vader @@ -1,7 +1,9 @@ Before: Save g:ale_rust_rls_executable + Save g:ale_rust_rls_toolchain unlet! g:ale_rust_rls_executable + unlet! g:ale_rust_rls_toolchain runtime ale_linters/rust/rls.vim @@ -16,7 +18,14 @@ After: Execute(The default executable path should be correct): AssertEqual 'rls', ale_linters#rust#rls#GetExecutable(bufnr('')) AssertEqual - \ ale#Escape('rls') . ' +nightly', + \ ale#Escape('rls') . ' +' . ale#Escape('nightly'), + \ ale_linters#rust#rls#GetCommand(bufnr('')) + +Execute(The toolchain should be configurable): + let g:ale_rust_rls_toolchain = 'stable' + + AssertEqual + \ ale#Escape('rls') . ' +' . ale#Escape('stable'), \ ale_linters#rust#rls#GetCommand(bufnr('')) Execute(The language string should be correct): @@ -28,5 +37,5 @@ Execute(The project root should be detected correctly): call ale#test#SetFilename('rust-rls-project/test.rs') AssertEqual - \ ale#path#Winify(g:dir . '/rust-rls-project'), + \ ale#path#Simplify(g:dir . '/rust-rls-project'), \ ale_linters#rust#rls#GetProjectRoot(bufnr('')) diff --git a/test/command_callback/test_rustc_command_callback.vader b/test/command_callback/test_rustc_command_callback.vader new file mode 100644 index 0000000..fe46c9a --- /dev/null +++ b/test/command_callback/test_rustc_command_callback.vader @@ -0,0 +1,37 @@ +Before: + Save g:ale_rust_rustc_options + + unlet! g:ale_rust_rustc_options + + runtime ale_linters/rust/rustc.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + unlet! b:ale_rust_rustc_options + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The default command should be correct): + AssertEqual + \ 'rustc --error-format=json -Z no-trans -', + \ ale_linters#rust#rustc#RustcCommand(bufnr('')) + +Execute(The options should be configurable): + let b:ale_rust_rustc_options = '--foo' + + AssertEqual + \ 'rustc --error-format=json --foo -', + \ ale_linters#rust#rustc#RustcCommand(bufnr('')) + +Execute(Some default paths should be included when the project is a Cargo project): + call ale#test#SetFilename('cargo_paths/test.rs') + + AssertEqual + \ 'rustc --error-format=json -Z no-trans' + \ . ' -L ' . ale#Escape(ale#path#GetAbsPath(g:dir, 'cargo_paths/target/debug/deps')) + \ . ' -L ' . ale#Escape(ale#path#GetAbsPath(g:dir, 'cargo_paths/target/release/deps')) + \ . ' -', + \ ale_linters#rust#rustc#RustcCommand(bufnr('')) diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader index 8e22905..68694b6 100644 --- a/test/command_callback/test_shellcheck_command_callback.vader +++ b/test/command_callback/test_shellcheck_command_callback.vader @@ -9,6 +9,12 @@ Before: runtime ale_linters/sh/shellcheck.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('test.sh') + + let b:prefix = 'cd ' . ale#Escape(ale#path#Simplify(g:dir)) . ' && ' + let b:suffix = ' -f gcc -' + After: Restore @@ -16,35 +22,38 @@ After: unlet! b:ale_sh_shellcheck_executable unlet! b:ale_sh_shellcheck_options unlet! b:is_bash + unlet! b:prefix + + call ale#test#RestoreDirectory() call ale#linter#Reset() Execute(The default shellcheck command should be correct): AssertEqual - \ 'shellcheck -f gcc -', - \ ale_linters#sh#shellcheck#GetCommand(bufnr('')) + \ b:prefix . ale#Escape('shellcheck') . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) Execute(The shellcheck command should accept options): let b:ale_sh_shellcheck_options = '--foobar' AssertEqual - \ 'shellcheck --foobar -f gcc -', - \ ale_linters#sh#shellcheck#GetCommand(bufnr('')) + \ b:prefix . ale#Escape('shellcheck') . ' --foobar' . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) Execute(The shellcheck command should accept options and exclusions): let b:ale_sh_shellcheck_options = '--foobar' let b:ale_sh_shellcheck_exclusions = 'foo,bar' AssertEqual - \ 'shellcheck --foobar -e foo,bar -f gcc -', - \ ale_linters#sh#shellcheck#GetCommand(bufnr('')) + \ b:prefix . ale#Escape('shellcheck') . ' --foobar -e foo,bar' . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) Execute(The shellcheck command should include the dialect): let b:is_bash = 1 AssertEqual - \ 'shellcheck -s bash -f gcc -', - \ ale_linters#sh#shellcheck#GetCommand(bufnr('')) + \ b:prefix . ale#Escape('shellcheck') . ' -s bash' . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) Execute(The shellcheck command should include the dialect before options and exclusions): let b:is_bash = 1 @@ -52,5 +61,69 @@ Execute(The shellcheck command should include the dialect before options and exc let b:ale_sh_shellcheck_exclusions = 'foo,bar' AssertEqual - \ 'shellcheck -s bash --foobar -e foo,bar -f gcc -', - \ ale_linters#sh#shellcheck#GetCommand(bufnr('')) + \ b:prefix + \ . ale#Escape('shellcheck') + \ . ' -s bash --foobar -e foo,bar' + \ . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) + +Execute(The VersionCheck function should return the --version command): + AssertEqual + \ ale#Escape('shellcheck') . ' --version', + \ ale_linters#sh#shellcheck#VersionCheck(bufnr('')) + + let g:ale_sh_shellcheck_executable = 'foobar' + + AssertEqual + \ ale#Escape('foobar') . ' --version', + \ ale_linters#sh#shellcheck#VersionCheck(bufnr('')) + +Execute(The -x option should be added when the version is new enough): + AssertEqual + \ b:prefix . ale#Escape('shellcheck') . ' -x' . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), [ + \ 'ShellCheck - shell script analysis tool', + \ 'version: 0.4.4', + \ 'license: GNU General Public License, version 3', + \ 'website: http://www.shellcheck.net', + \ ]) + + " We should cache the version check + AssertEqual + \ b:prefix . ale#Escape('shellcheck') . ' -x' . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) + + AssertEqual '', ale_linters#sh#shellcheck#VersionCheck(bufnr('')) + +Execute(The version check shouldn't be run again for new versions): + call ale_linters#sh#shellcheck#GetCommand(bufnr(''), [ + \ 'ShellCheck - shell script analysis tool', + \ 'version: 0.4.4', + \ 'license: GNU General Public License, version 3', + \ 'website: http://www.shellcheck.net', + \]) + +Execute(The -x option should not be added when the version is too old): + AssertEqual + \ b:prefix . ale#Escape('shellcheck') . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), [ + \ 'ShellCheck - shell script analysis tool', + \ 'version: 0.3.9', + \ 'license: GNU General Public License, version 3', + \ 'website: http://www.shellcheck.net', + \ ]) + + " We should cache the version check + AssertEqual + \ b:prefix . ale#Escape('shellcheck') . b:suffix, + \ ale_linters#sh#shellcheck#GetCommand(bufnr(''), []) + +Execute(The version check shouldn't be run again for old versions): + call ale_linters#sh#shellcheck#GetCommand(bufnr(''), [ + \ 'ShellCheck - shell script analysis tool', + \ 'version: 0.3.9', + \ 'license: GNU General Public License, version 3', + \ 'website: http://www.shellcheck.net', + \]) + + AssertEqual '', ale_linters#sh#shellcheck#VersionCheck(bufnr('')) diff --git a/test/command_callback/test_slimlint_command_callback.vader b/test/command_callback/test_slimlint_command_callback.vader index d4dad4c..38588a1 100644 --- a/test/command_callback/test_slimlint_command_callback.vader +++ b/test/command_callback/test_slimlint_command_callback.vader @@ -20,7 +20,7 @@ Execute(The default command should be correct): Execute(The command should have the .rubocop.yml prepended as an env var if one exists): call ale#test#SetFilename('../slimlint-test-files/subdir/file.slim') - let b:conf = ale#path#Winify(g:dir . '/../slimlint-test-files/.rubocop.yml') + let b:conf = ale#path#Simplify(g:dir . '/../slimlint-test-files/.rubocop.yml') if has('win32') " Windows uses 'set var=... && command' diff --git a/test/command_callback/test_standard_command_callback.vader b/test/command_callback/test_standard_command_callback.vader index 279109e..3dee285 100644 --- a/test/command_callback/test_standard_command_callback.vader +++ b/test/command_callback/test_standard_command_callback.vader @@ -27,7 +27,7 @@ After: Execute(bin/cmd.js paths should be preferred): call ale#test#SetFilename('standard-test-files/with-cmd/testfile.js') - let b:executable = ale#path#Winify( + let b:executable = ale#path#Simplify( \ g:dir \ . '/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js' \) @@ -45,7 +45,7 @@ Execute(bin/cmd.js paths should be preferred): Execute(.bin directories should be used too): call ale#test#SetFilename('standard-test-files/with-bin/testfile.js') - let b:executable = ale#path#Winify( + let b:executable = ale#path#Simplify( \ g:dir \ . '/standard-test-files/with-bin/node_modules/.bin/standard' \) diff --git a/test/command_callback/test_staticcheck_command_callback.vader b/test/command_callback/test_staticcheck_command_callback.vader new file mode 100644 index 0000000..e9628eb --- /dev/null +++ b/test/command_callback/test_staticcheck_command_callback.vader @@ -0,0 +1,41 @@ +Before: + Save b:ale_go_staticcheck_options + Save b:ale_go_staticcheck_lint_package + + let b:ale_go_staticcheck_options = '' + let b:ale_go_staticcheck_lint_package = 0 + + runtime ale_linters/go/staticcheck.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('test.go') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The staticcheck callback should return the right defaults): + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'staticcheck ' + \ . ale#Escape(expand('%' . ':t')), + \ ale_linters#go#staticcheck#GetCommand(bufnr('')) + +Execute(The staticcheck callback should use configured options): + let b:ale_go_staticcheck_options = '-test' + + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'staticcheck ' + \ . '-test ' . ale#Escape(expand('%' . ':t')), + \ ale_linters#go#staticcheck#GetCommand(bufnr('')) + +Execute(The staticcheck `lint_package` option should use the correct command): + let b:ale_go_staticcheck_lint_package = 1 + + AssertEqual + \ 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . 'staticcheck .', + \ ale_linters#go#staticcheck#GetCommand(bufnr('')) diff --git a/test/command_callback/test_swaglint_command_callback.vader b/test/command_callback/test_swaglint_command_callback.vader index 379aa0c..51a1009 100644 --- a/test/command_callback/test_swaglint_command_callback.vader +++ b/test/command_callback/test_swaglint_command_callback.vader @@ -1,12 +1,14 @@ Before: runtime ale_linters/yaml/swaglint.vim + call ale#test#SetDirectory('/testplugin/test/command_callback') After: - call ale#linter#Reset() let g:ale_yaml_swaglint_executable = 'swaglint' let g:ale_yaml_swaglint_use_global = 0 + call ale#linter#Reset() + Execute(The yaml swaglint command callback should return the correct default string): AssertEqual 'swaglint', \ ale_linters#yaml#swaglint#GetExecutable(bufnr('')) @@ -32,10 +34,10 @@ Execute(The yaml swaglint command callback should allow a local installation to call ale#test#SetFilename('swaglint_paths/docs/swagger.yaml') AssertEqual - \ ale#path#Winify(g:dir . '/swaglint_paths/node_modules/.bin/swaglint'), + \ ale#path#Simplify(g:dir . '/swaglint_paths/node_modules/.bin/swaglint'), \ ale_linters#yaml#swaglint#GetExecutable(bufnr('')) AssertEqual - \ ale#path#Winify(g:dir . '/swaglint_paths/node_modules/.bin/swaglint') + \ ale#path#Simplify(g:dir . '/swaglint_paths/node_modules/.bin/swaglint') \ . ' -r compact --stdin', \ ale_linters#yaml#swaglint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_terraform_tflint_command_callback.vader b/test/command_callback/test_terraform_tflint_command_callback.vader new file mode 100644 index 0000000..a4ae56b --- /dev/null +++ b/test/command_callback/test_terraform_tflint_command_callback.vader @@ -0,0 +1,30 @@ +Before: + Save g:ale_terraform_tflint_executable + Save g:ale_terraform_tflint_options + + runtime ale_linters/terraform/tflint.vim + +After: + Restore + + call ale#linter#Reset() + +Execute(The default executable should be configurable): + AssertEqual 'tflint', ale_linters#terraform#tflint#GetExecutable(bufnr('')) + + let g:ale_terraform_tflint_executable = 'asdf' + + AssertEqual 'asdf', ale_linters#terraform#tflint#GetExecutable(bufnr('')) + +Execute(The default command should be good): + let g:ale_terraform_tflint_executable = 'tflint' + AssertEqual + \ ale#Escape('tflint') . ' -f json %t', + \ ale_linters#terraform#tflint#GetCommand(bufnr('')) + +Execute(Overriding things should work): + let g:ale_terraform_tflint_executable = 'fnord' + let g:ale_terraform_tflint_options = '--whatever' + AssertEqual + \ ale#Escape('fnord') . ' --whatever -f json %t', + \ ale_linters#terraform#tflint#GetCommand(bufnr('')) diff --git a/test/command_callback/test_thrift_command_callback.vader b/test/command_callback/test_thrift_command_callback.vader index 6700891..7d4e436 100644 --- a/test/command_callback/test_thrift_command_callback.vader +++ b/test/command_callback/test_thrift_command_callback.vader @@ -28,6 +28,7 @@ Before: After: Restore + delfunction GetCommand unlet! b:ale_thrift_thrift_executable unlet! b:ale_thrift_thrift_generators diff --git a/test/command_callback/test_write_good_command_callback.vader b/test/command_callback/test_write_good_command_callback.vader new file mode 100644 index 0000000..8d9e9a0 --- /dev/null +++ b/test/command_callback/test_write_good_command_callback.vader @@ -0,0 +1,65 @@ +Before: + Save g:ale_writegood_options + Save g:ale_writegood_executable + Save g:ale_writegood_use_global + + unlet! g:ale_writegood_options + unlet! g:ale_writegood_executable + unlet! g:ale_writegood_use_global + + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('testfile.txt') + + call ale#handlers#writegood#ResetOptions() + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The global executable should be used when the local one cannot be found): + AssertEqual 'write-good', ale#handlers#writegood#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('write-good') . ' %t', + \ ale#handlers#writegood#GetCommand(bufnr('')) + +Execute(The options should be used in the command): + let g:ale_writegood_options = '--foo --bar' + + AssertEqual + \ ale#Escape('write-good') . ' --foo --bar %t', + \ ale#handlers#writegood#GetCommand(bufnr('')) + +Execute(Should use the node_modules/.bin executable, if available): + call ale#test#SetFilename('write-good-node-modules/test.txt') + + AssertEqual + \ ale#path#Simplify(g:dir . '/write-good-node-modules/node_modules/.bin/write-good'), + \ ale#handlers#writegood#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape(ale#path#Simplify(g:dir . '/write-good-node-modules/node_modules/.bin/write-good')) + \ . ' %t', + \ ale#handlers#writegood#GetCommand(bufnr('')) + +Execute(Should use the node_modules/write-good executable, if available): + call ale#test#SetFilename('write-good-node-modules-2/test.txt') + + AssertEqual + \ ale#path#Simplify(g:dir . '/write-good-node-modules-2/node_modules/write-good/bin/write-good.js'), + \ ale#handlers#writegood#GetExecutable(bufnr('')) + AssertEqual + \ (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/write-good-node-modules-2/node_modules/write-good/bin/write-good.js')) + \ . ' %t', + \ ale#handlers#writegood#GetCommand(bufnr('')) + +Execute(Should let users configure a global executable and override local paths): + call ale#test#SetFilename('write-good-node-modules-2/test.txt') + + let g:ale_writegood_executable = 'foo-bar' + let g:ale_writegood_use_global = 1 + + AssertEqual 'foo-bar', ale#handlers#writegood#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('foo-bar') . ' %t', + \ ale#handlers#writegood#GetCommand(bufnr('')) diff --git a/test/command_callback/test_xmllint_command_callback.vader b/test/command_callback/test_xmllint_command_callback.vader index 3cffde8..12ca15d 100644 --- a/test/command_callback/test_xmllint_command_callback.vader +++ b/test/command_callback/test_xmllint_command_callback.vader @@ -2,10 +2,11 @@ Before: runtime ale_linters/xml/xmllint.vim After: - call ale#linter#Reset() let g:ale_xml_xmllint_options = '' let g:ale_xml_xmllint_executable = 'xmllint' + call ale#linter#Reset() + Execute(The xml xmllint command callback should return the correct default string): AssertEqual ale#Escape('xmllint') . ' --noout -', \ join(split(ale_linters#xml#xmllint#GetCommand(1))) @@ -22,4 +23,3 @@ Execute(The xmllint executable should be configurable): AssertEqual '~/.local/bin/xmllint', ale_linters#xml#xmllint#GetExecutable(1) AssertEqual ale#Escape('~/.local/bin/xmllint') . ' --noout -', \ join(split(ale_linters#xml#xmllint#GetCommand(1))) - diff --git a/test/command_callback/write-good-node-modules-2/node_modules/write-good/bin/write-good.js b/test/command_callback/write-good-node-modules-2/node_modules/write-good/bin/write-good.js new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/write-good-node-modules/node_modules/.bin/write-good b/test/command_callback/write-good-node-modules/node_modules/.bin/write-good new file mode 100644 index 0000000..e69de29 diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader new file mode 100644 index 0000000..49d485f --- /dev/null +++ b/test/completion/test_completion_events.vader @@ -0,0 +1,172 @@ +Before: + Save g:ale_completion_enabled + Save g:ale_completion_delay + Save g:ale_completion_max_suggestions + Save g:ale_completion_experimental_lsp_support + Save &l:omnifunc + Save &l:completeopt + + unlet! g:ale_completion_experimental_lsp_support + + let g:ale_completion_enabled = 1 + let g:get_completions_called = 0 + let g:feedkeys_calls = [] + + runtime autoload/ale/util.vim + + function! ale#util#FeedKeys(string, mode) abort + call add(g:feedkeys_calls, [a:string, a:mode]) + endfunction + + function! CheckCompletionCalled(expect_success) abort + let g:get_completions_called = 0 + + " We just want to check if the function is called. + function! ale#completion#GetCompletions() + let g:get_completions_called = 1 + endfunction + + let g:ale_completion_delay = 0 + call ale#completion#Queue() + sleep 1m + + AssertEqual a:expect_success, g:get_completions_called + endfunction + +After: + Restore + + unlet! g:get_completions_called + unlet! b:ale_old_omnifunc + unlet! b:ale_old_completopt + unlet! b:ale_completion_info + unlet! b:ale_completion_response + unlet! b:ale_completion_parser + unlet! b:ale_complete_done_time + unlet! g:ale_completion_experimental_lsp_support + + delfunction CheckCompletionCalled + + " Stop any timers we left behind. + " This stops the tests from failing randomly. + call ale#completion#StopTimer() + + runtime autoload/ale/completion.vim + runtime autoload/ale/util.vim + +Execute(ale#completion#GetCompletions should be called when the cursor position stays the same): + call CheckCompletionCalled(1) + +Given typescript(): + let abc = y. + let foo = ab + let foo = (ab) + +Execute(ale#completion#GetCompletions should not be called when the cursor position changes): + call setpos('.', [bufnr(''), 1, 2, 0]) + + " We just want to check if the function is called. + function! ale#completion#GetCompletions() + let g:get_completions_called = 1 + endfunction + + let g:ale_completion_delay = 0 + call ale#completion#Queue() + + " Change the cursor position before the callback is triggered. + call setpos('.', [bufnr(''), 2, 2, 0]) + + sleep 1m + + Assert !g:get_completions_called + +Execute(Completion should not be done shortly after the CompleteDone function): + call CheckCompletionCalled(1) + call ale#completion#Done() + call CheckCompletionCalled(0) + +Execute(ale#completion#Show() should remember the omnifunc setting and replace it): + let &l:omnifunc = 'FooBar' + + call ale#completion#Show('Response', 'Parser') + + AssertEqual 'FooBar', b:ale_old_omnifunc + AssertEqual 'ale#completion#OmniFunc', &l:omnifunc + +Execute(ale#completion#Show() should remember the completeopt setting and replace it): + let &l:completeopt = 'menu' + + call ale#completion#Show('Response', 'Parser') + + AssertEqual 'menu', b:ale_old_completopt + AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt + +Execute(ale#completion#OmniFunc() should also remember the completeopt setting and replace it): + let &l:completeopt = 'menu' + + call ale#completion#OmniFunc(0, '') + + AssertEqual 'menu', b:ale_old_completopt + AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt + +Execute(ale#completion#Show() should make the correct feedkeys() call): + call ale#completion#Show('Response', 'Parser') + + AssertEqual [["\\", 'n']], g:feedkeys_calls + +Execute(ale#completion#Show() should set up the response and parser): + call ale#completion#Show('Response', 'Parser') + + AssertEqual 'Response', b:ale_completion_response + AssertEqual 'Parser', b:ale_completion_parser + +Execute(ale#completion#Done() should restore old omnifunc values): + let b:ale_old_omnifunc = 'FooBar' + + call ale#completion#Done() + + " We reset the old omnifunc setting and remove the buffer variable. + AssertEqual 'FooBar', &l:omnifunc + Assert !has_key(b:, 'ale_old_omnifunc') + +Execute(ale#completion#Done() should restore the old completeopt setting): + let b:ale_old_completopt = 'menu' + let &l:completeopt = 'menu,menuone,preview,noselect,noinsert' + + call ale#completion#Done() + + AssertEqual 'menu', &l:completeopt + Assert !has_key(b:, 'ale_old_completopt') + +Execute(ale#completion#Done() should leave settings alone when none were remembered): + let &l:omnifunc = 'BazBoz' + let &l:completeopt = 'menu' + + call ale#completion#Done() + + AssertEqual 'BazBoz', &l:omnifunc + AssertEqual 'menu', &l:completeopt + +Execute(The completion request_id should be reset when queuing again): + let b:ale_completion_info = {'request_id': 123} + + let g:ale_completion_delay = 0 + call ale#completion#Queue() + sleep 1m + + AssertEqual 0, b:ale_completion_info.request_id + +Execute(b:ale_completion_info should be set up correctly when requesting completions): + call setpos('.', [bufnr(''), 3, 14, 0]) + call ale#completion#GetCompletions() + + AssertEqual + \ { + \ 'request_id': 0, + \ 'conn_id': 0, + \ 'column': 14, + \ 'line_length': 14, + \ 'line': 3, + \ 'prefix': 'ab', + \ }, + \ b:ale_completion_info diff --git a/test/completion/test_completion_filtering.vader b/test/completion/test_completion_filtering.vader new file mode 100644 index 0000000..3e461ae --- /dev/null +++ b/test/completion/test_completion_filtering.vader @@ -0,0 +1,36 @@ +Execute(Prefix filtering should work for Lists of strings): + AssertEqual + \ ['FooBar', 'foo'], + \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], 'foo') + AssertEqual + \ ['FooBar', 'FongBar', 'baz', 'foo'], + \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], '.') + +Execute(Prefix filtering should work for completion items): + AssertEqual + \ [{'word': 'FooBar'}, {'word': 'foo'}], + \ ale#completion#Filter( + \ [ + \ {'word': 'FooBar'}, + \ {'word': 'FongBar'}, + \ {'word': 'baz'}, + \ {'word': 'foo'}, + \ ], + \ 'foo' + \ ) + AssertEqual + \ [ + \ {'word': 'FooBar'}, + \ {'word': 'FongBar'}, + \ {'word': 'baz'}, + \ {'word': 'foo'}, + \ ], + \ ale#completion#Filter( + \ [ + \ {'word': 'FooBar'}, + \ {'word': 'FongBar'}, + \ {'word': 'baz'}, + \ {'word': 'foo'}, + \ ], + \ '.' + \ ) diff --git a/test/completion/test_completion_prefixes.vader b/test/completion/test_completion_prefixes.vader new file mode 100644 index 0000000..8ac2932 --- /dev/null +++ b/test/completion/test_completion_prefixes.vader @@ -0,0 +1,19 @@ +Given typescript(): + let abc = y. + let foo = ab + let foo = (ab) + +Execute(Completion should be done after dots in TypeScript): + AssertEqual '.', ale#completion#GetPrefix(&filetype, 1, 13) + +Execute(Completion should be done after words in TypeScript): + AssertEqual 'ab', ale#completion#GetPrefix(&filetype, 2, 13) + +Execute(Completion should be done after words in parens in TypeScript): + AssertEqual 'ab', ale#completion#GetPrefix(&filetype, 3, 14) + +Execute(Completion should not be done after parens in TypeScript): + AssertEqual '', ale#completion#GetPrefix(&filetype, 3, 15) + +Execute(Completion prefixes should work for other filetypes): + AssertEqual 'ab', ale#completion#GetPrefix('xxxyyyzzz', 3, 14) diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader new file mode 100644 index 0000000..f21acfb --- /dev/null +++ b/test/completion/test_lsp_completion_messages.vader @@ -0,0 +1,182 @@ +Before: + Save g:ale_completion_delay + Save g:ale_completion_max_suggestions + Save g:ale_completion_info + Save g:ale_completion_experimental_lsp_support + Save &l:omnifunc + Save &l:completeopt + + unlet! g:ale_completion_experimental_lsp_support + + let g:ale_completion_enabled = 1 + + call ale#test#SetDirectory('/testplugin/test/completion') + call ale#test#SetFilename('dummy.txt') + + runtime autoload/ale/lsp.vim + + let g:message_list = [] + let g:Callback = '' + + function! ale#linter#StartLSP(buffer, linter, callback) abort + let g:Callback = a:callback + + return { + \ 'connection_id': 347, + \ 'project_root': '/foo/bar', + \} + endfunction + + " Replace the Send function for LSP, so we can monitor calls to it. + function! ale#lsp#Send(conn_id, message, ...) abort + call add(g:message_list, a:message) + endfunction + +After: + Restore + + unlet! g:message_list + unlet! g:Callback + unlet! b:ale_old_omnifunc + unlet! b:ale_old_completopt + unlet! b:ale_completion_info + unlet! b:ale_completion_response + unlet! b:ale_completion_parser + unlet! b:ale_complete_done_time + unlet! b:ale_linters + unlet! g:ale_completion_experimental_lsp_support + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + " Stop any timers we left behind. + " This stops the tests from failing randomly. + call ale#completion#StopTimer() + + runtime autoload/ale/completion.vim + runtime autoload/ale/lsp.vim + +Given typescript(Some typescript file): + foo + somelongerline + bazxyzxyzxyz + +Execute(The right message should be sent for the initial tsserver request): + runtime ale_linters/typescript/tsserver.vim + let b:ale_linters = ['tsserver'] + " The cursor position needs to match what was saved before. + call setpos('.', [bufnr(''), 1, 3, 0]) + + call ale#completion#GetCompletions() + + " We should send the right callback. + AssertEqual + \ 'function(''ale#completion#HandleTSServerResponse'')', + \ string(g:Callback) + " We should send the right message. + AssertEqual + \ [[0, 'ts@completions', {'file': expand('%:p'), 'line': 1, 'offset': 3, 'prefix': 'fo'}]], + \ g:message_list + " We should set up the completion info correctly. + AssertEqual + \ { + \ 'line_length': 3, + \ 'conn_id': 0, + \ 'column': 3, + \ 'request_id': 0, + \ 'line': 1, + \ 'prefix': 'fo', + \ }, + \ get(b:, 'ale_completion_info', {}) + +Execute(The right message sent to the tsserver LSP when the first completion message is received): + " The cursor position needs to match what was saved before. + call setpos('.', [bufnr(''), 1, 1, 0]) + let b:ale_completion_info = { + \ 'conn_id': 123, + \ 'prefix': 'f', + \ 'request_id': 4, + \ 'line': 1, + \ 'column': 1, + \} + " We should only show up to this many suggestions. + let g:ale_completion_max_suggestions = 3 + + " Handle the response for completions. + call ale#completion#HandleTSServerResponse(123, { + \ 'request_seq': 4, + \ 'command': 'completions', + \ 'body': [ + \ {'name': 'Baz'}, + \ {'name': 'dingDong'}, + \ {'name': 'Foo'}, + \ {'name': 'FooBar'}, + \ {'name': 'frazzle'}, + \ {'name': 'FFS'}, + \ ], + \}) + + " The entry details messages should have been sent. + AssertEqual + \ [[ + \ 0, + \ 'ts@completionEntryDetails', + \ { + \ 'file': expand('%:p'), + \ 'entryNames': ['Foo', 'FooBar', 'frazzle'], + \ 'offset': 1, + \ 'line': 1, + \ }, + \ ]], + \ g:message_list + +Given python(Some Python file): + foo + somelongerline + bazxyzxyzxyz + +Execute(The right message should be sent for the initial LSP request): + let g:ale_completion_experimental_lsp_support = 1 + + runtime ale_linters/python/pyls.vim + let b:ale_linters = ['pyls'] + " The cursor position needs to match what was saved before. + call setpos('.', [bufnr(''), 1, 5, 0]) + + call ale#completion#GetCompletions() + + " We should send the right callback. + AssertEqual + \ 'function(''ale#completion#HandleLSPResponse'')', + \ string(g:Callback) + " We should send the right message. + " The character index needs to be at most the index of the last character on + " the line, or integration with pyls will be broken. + " + " We need to send the message for changing the document first. + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}] + \ }], + \ [0, 'textDocument/completion', { + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, + \ 'position': {'line': 0, 'character': 3}, + \ }], + \ ], + \ g:message_list + " We should set up the completion info correctly. + AssertEqual + \ { + \ 'line_length': 3, + \ 'conn_id': 0, + \ 'column': 3, + \ 'request_id': 0, + \ 'line': 1, + \ 'prefix': 'fo', + \ }, + \ get(b:, 'ale_completion_info', {}) diff --git a/test/completion/test_tsserver_completion_parsing.vader b/test/completion/test_tsserver_completion_parsing.vader new file mode 100644 index 0000000..b663ef4 --- /dev/null +++ b/test/completion/test_tsserver_completion_parsing.vader @@ -0,0 +1,75 @@ +Execute(TypeScript completions responses should be parsed correctly): + AssertEqual [], + \ ale#completion#ParseTSServerCompletions({ + \ 'body': [], + \}) + AssertEqual ['foo', 'bar', 'baz'], + \ ale#completion#ParseTSServerCompletions({ + \ 'body': [ + \ {'name': 'foo'}, + \ {'name': 'bar'}, + \ {'name': 'baz'}, + \ ], + \}) + +Execute(TypeScript completion details responses should be parsed correctly): + AssertEqual + \ [ + \ { + \ 'word': 'abc', + \ 'menu': '(property) Foo.abc: number', + \ 'info': '', + \ 'kind': 'f', + \ 'icase': 1, + \ }, + \ { + \ 'word': 'def', + \ 'menu': '(property) Foo.def: number', + \ 'info': 'foo bar baz', + \ 'kind': 'f', + \ 'icase': 1, + \ }, + \ ], + \ ale#completion#ParseTSServerCompletionEntryDetails({ + \ 'body': [ + \ { + \ 'name': 'abc', + \ 'kind': 'parameterName', + \ 'displayParts': [ + \ {'text': '('}, + \ {'text': 'property'}, + \ {'text': ')'}, + \ {'text': ' '}, + \ {'text': 'Foo'}, + \ {'text': '.'}, + \ {'text': 'abc'}, + \ {'text': ':'}, + \ {'text': ' '}, + \ {'text': 'number'}, + \ ], + \ }, + \ { + \ 'name': 'def', + \ 'kind': 'parameterName', + \ 'displayParts': [ + \ {'text': '('}, + \ {'text': 'property'}, + \ {'text': ')'}, + \ {'text': ' '}, + \ {'text': 'Foo'}, + \ {'text': '.'}, + \ {'text': 'def'}, + \ {'text': ':'}, + \ {'text': ' '}, + \ {'text': 'number'}, + \ ], + \ 'documentation': [ + \ {'text': 'foo'}, + \ {'text': ' '}, + \ {'text': 'bar'}, + \ {'text': ' '}, + \ {'text': 'baz'}, + \ ], + \ }, + \ ], + \}) diff --git a/test/elixir-test-files/testfile.ex b/test/elixir-test-files/testfile.ex new file mode 100644 index 0000000..e69de29 diff --git a/test/test_ale_fix.vader b/test/fix/test_ale_fix.vader similarity index 58% rename from test/test_ale_fix.vader rename to test/fix/test_ale_fix.vader index 9968c4a..5b66c92 100644 --- a/test/test_ale_fix.vader +++ b/test/fix/test_ale_fix.vader @@ -6,7 +6,7 @@ Before: Save g:ale_lint_on_save Save g:ale_echo_cursor - silent! cd /testplugin/test + silent! cd /testplugin/test/fix let g:ale_enabled = 0 let g:ale_echo_cursor = 0 @@ -62,6 +62,65 @@ Before: return [{'lnum': 1, 'col': 1, 'text': 'xxx'}] endfunction + function! FirstChainCallback(buffer) + return {'command': 'echo echoline', 'chain_with': 'SecondChainCallback'} + endfunction + + function! FirstChainCallbackSkipped(buffer) + let l:ChainWith = 'SecondChainCallback' + + " Test with lambdas where support is available. + if has('lambda') + let l:ChainWith = {buffer, output -> SecondChainCallback(buffer, output)} + endif + + return {'command': '', 'chain_with': l:ChainWith} + endfunction + + function! FirstChainCallbackSecondSkipped(buffer) + return {'command': 'echo skipit', 'chain_with': 'SecondChainCallback'} + endfunction + + function! SecondChainCallback(buffer, output) + let l:previous_line = empty(a:output) + \ ? 'emptydefault' + \ : join(split(a:output[0])) + + if l:previous_line is# 'skipit' + return {'command': '', 'chain_with': 'ThirdChainCallback'} + endif + + return { + \ 'command': 'echo ' . l:previous_line, + \ 'chain_with': 'ThirdChainCallback', + \} + endfunction + + function! ThirdChainCallback(buffer, output, input) + let l:previous_line = empty(a:output) + \ ? 'thirddefault' + \ : join(split(a:output[0])) + + return a:input + [l:previous_line] + endfunction + + function! ChainWhereLastIsSkipped(buffer) + return {'command': 'echo echoline', 'chain_with': 'ChainEndSkipped'} + endfunction + + function! ChainEndSkipped(buffer, output) + return {'command': ''} + endfunction + + " echo will output a single blank line, and we should ingore it. + function! IgnoredEmptyOutput(buffer, output) + return {'command': has('win32') ? 'echo(' : 'echo'} + endfunction + + function! EchoLineNoPipe(buffer, output) + return {'command': 'echo new line', 'read_buffer': 0} + endfunction + function! SetUpLinters() call ale#linter#Define('testft', { \ 'name': 'testlinter', @@ -71,12 +130,47 @@ Before: \}) endfunction + function GetLastMessage() + redir => l:output + silent mess + redir END + + let l:lines = split(l:output, "\n") + + return empty(l:lines) ? '' : l:lines[-1] + endfunction + + function! FixWithJSONPostProcessing(buffer) abort + let l:ProcessWith = 'JSONPostProcessor' + + " Test with lambdas where support is available. + if has('lambda') + let l:ProcessWith = {buffer, output -> JSONPostProcessor(buffer, output)} + endif + + " Escaping needs to be handled specially for CMD on Windows. + let l:json_string = has('win32') + \ ? '{"output":["x","y","z"]}' + \ : ale#Escape('{"output": ["x", "y", "z"]}') + + return { + \ 'command': 'echo ' . l:json_string, + \ 'read_buffer': 0, + \ 'process_with': l:ProcessWith, + \} + endfunction + + function! JSONPostProcessor(buffer, output) abort + return json_decode(a:output[0]).output + endfunction + After: Restore unlet! g:ale_run_synchronously unlet! g:ale_set_lists_synchronously unlet! g:ale_emulate_job_failure unlet! b:ale_fixers + unlet! b:ale_fix_on_save delfunction AddCarets delfunction AddDollars delfunction DoNothing @@ -86,7 +180,19 @@ After: delfunction RemoveLastLine delfunction RemoveLastLineOneArg delfunction TestCallback + delfunction FirstChainCallback + delfunction FirstChainCallbackSkipped + delfunction FirstChainCallbackSecondSkipped + delfunction SecondChainCallback + delfunction ThirdChainCallback + delfunction ChainWhereLastIsSkipped + delfunction ChainEndSkipped delfunction SetUpLinters + delfunction GetLastMessage + delfunction IgnoredEmptyOutput + delfunction EchoLineNoPipe + delfunction FixWithJSONPostProcessing + delfunction JSONPostProcessor call ale#test#RestoreDirectory() @@ -103,14 +209,17 @@ After: let g:ale_fix_buffer_data = {} + " Clear the messages between tests. + echomsg '' + Given testft (A file with three lines): a b c Execute(ALEFix should complain when there are no functions to call): - AssertThrows ALEFix - AssertEqual 'Vim(echoerr):No fixers have been defined. Try :ALEFixSuggest', g:vader_exception + ALEFix + AssertEqual 'No fixers have been defined. Try :ALEFixSuggest', GetLastMessage() Execute(ALEFix should apply simple functions): let g:ale_fixers.testft = ['AddCarets'] @@ -265,6 +374,15 @@ Expect(There should be only two lines): a b +Execute(ALEFix should allow Lists to be used for buffer-local fixer settings): + let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + let b:ale_fixers = ['RemoveLastLine'] + ALEFix + +Expect(There should be only two lines): + a + b + Given testft (A file with three lines): a b @@ -397,7 +515,7 @@ Execute(ale#fix#InitBufferData() should set up the correct data): \ bufnr(''): { \ 'temporary_directory_list': [], \ 'vars': b:, - \ 'filename': ale#path#Winify(getcwd() . '/fix_test_file'), + \ 'filename': ale#path#Simplify(getcwd() . '/fix_test_file'), \ 'done': 0, \ 'lines_before': ['a', 'b', 'c'], \ 'should_save': 1, @@ -412,6 +530,29 @@ Expect(There should be only two lines): a b +Execute(b:ale_fix_on_save = 1 should override g:ale_fix_on_save = 0): + let g:ale_fix_on_save = 0 + let b:ale_fix_on_save = 1 + + let g:ale_fixers.testft = ['RemoveLastLineOneArg'] + call ale#events#SaveEvent(bufnr('')) + +Expect(There should be only two lines): + a + b + +Execute(b:ale_fix_on_save = 0 should override g:ale_fix_on_save = 1): + let g:ale_fix_on_save = 1 + let b:ale_fix_on_save = 0 + + let g:ale_fixers.testft = ['RemoveLastLineOneArg'] + call ale#events#SaveEvent(bufnr('')) + +Expect(The lines should be the same): + a + b + c + Execute(ALEFix functions returning jobs should be able to accept one argument): if has('win32') " Just skip this test on Windows, we can't run it. @@ -426,3 +567,100 @@ Expect(An extra line should be added): b c d + +Execute(ALE should print a message telling you something isn't a valid fixer when you type some nonsense): + let g:ale_fixers.testft = ['CatLine', 'invalidname'] + ALEFix + + AssertEqual 'There is no fixer named `invalidname`. Check :ALEFixSuggest', GetLastMessage() + +Execute(ALE should complain about invalid fixers with minuses in the name): + let g:ale_fixers.testft = ['foo-bar'] + ALEFix + + AssertEqual 'There is no fixer named `foo-bar`. Check :ALEFixSuggest', GetLastMessage() + +Execute(ALE should tolerate valid fixers with minuses in the name): + let g:ale_fixers.testft = ['prettier-standard'] + ALEFix + +Execute(Test fixing with chained callbacks): + let g:ale_fixers.testft = ['FirstChainCallback'] + ALEFix + + " The buffer shouldn't be piped in for earlier commands in the chain. + AssertEqual + \ [ + \ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')), + \ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')), + \ ], + \ map(ale#history#Get(bufnr(''))[-2:-1], 'string(v:val.command)') + +Expect(The echoed line should be added): + a + b + c + echoline + +Execute(Test fixing with chained callback where the first command is skipped): + let g:ale_fixers.testft = ['FirstChainCallbackSkipped'] + ALEFix + +Expect(The default line should be added): + a + b + c + emptydefault + +Execute(Test fixing with chained callback where the second command is skipped): + let g:ale_fixers.testft = ['FirstChainCallbackSecondSkipped'] + ALEFix + +Expect(The default line should be added): + a + b + c + thirddefault + +Execute(Test fixing with chained callback where the final callback is skipped): + let g:ale_fixers.testft = ['ChainWhereLastIsSkipped'] + ALEFix + +Expect(The lines should be the same): + a + b + c + +Execute(Empty output should be ignored): + let g:ale_fixers.testft = ['IgnoredEmptyOutput'] + ALEFix + +Expect(The lines should be the same): + a + b + c + +Execute(A temporary file shouldn't be piped into the command when disabled): + let g:ale_fixers.testft = ['EchoLineNoPipe'] + ALEFix + + AssertEqual + \ string(ale#job#PrepareCommand(bufnr(''), 'echo new line')), + \ string(ale#history#Get(bufnr(''))[-1].command) + + " Remove trailing whitespace for Windows. + if has('win32') + %s/[[:space:]]*$//g + endif + +Expect(The new line should be used): + new line + +Execute(Post-processing should work): + let g:ale_fixers.testft = ['FixWithJSONPostProcessing'] + ALEFix + +Expect(The lines in the JSON should be used): + x + y + z diff --git a/test/fix/test_ale_fix_aliases.vader b/test/fix/test_ale_fix_aliases.vader new file mode 100644 index 0000000..d3c47b3 --- /dev/null +++ b/test/fix/test_ale_fix_aliases.vader @@ -0,0 +1,5 @@ +Execute(prettier-eslint should be aliased): + AssertEqual 'ale#fixers#prettier_eslint#Fix', ale#fix#registry#GetFunc('prettier-eslint') + +Execute(prettier-standard should be aliased): + AssertEqual 'ale#fixers#prettier_standard#Fix', ale#fix#registry#GetFunc('prettier-standard') diff --git a/test/test_ale_fix_suggest.vader b/test/fix/test_ale_fix_suggest.vader similarity index 97% rename from test/test_ale_fix_suggest.vader rename to test/fix/test_ale_fix_suggest.vader index 97227b4..1100aee 100644 --- a/test/test_ale_fix_suggest.vader +++ b/test/fix/test_ale_fix_suggest.vader @@ -80,7 +80,7 @@ Execute(ALEFixSuggest output should be correct for only filetype handlers): Execute(ALEFixSuggest should suggest filetype and generic handlers): let &filetype = 'testft2.testft' - call ale#fix#registry#Add('zed', 'XYZ', ['testft2'], 'Zedify things.') + call ale#fix#registry#Add('zed', 'XYZ', ['testft2'], 'Zedify things.', ['foobar']) call ale#fix#registry#Add('alpha', 'XYZ', ['testft'], 'Alpha things.') call ale#fix#registry#Add('generic', 'XYZ', [], 'Generic things.') @@ -89,7 +89,7 @@ Execute(ALEFixSuggest should suggest filetype and generic handlers): \ 'Try the following fixers appropriate for the filetype:', \ '', \ '''alpha'' - Alpha things.', - \ '''zed'' - Zedify things.', + \ '''zed'', ''foobar'' - Zedify things.', \ '', \ 'Try the following generic fixers:', \ '', diff --git a/test/fixers/test_autopep8_fixer_callback.vader b/test/fixers/test_autopep8_fixer_callback.vader index 600fb19..5678aaf 100644 --- a/test/fixers/test_autopep8_fixer_callback.vader +++ b/test/fixers/test_autopep8_fixer_callback.vader @@ -27,7 +27,7 @@ Execute(The autopep8 callback should return the correct default values): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autopep8')) . ' -'}, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autopep8')) . ' -'}, \ ale#fixers#autopep8#Fix(bufnr('')) Execute(The autopep8 callback should include options): @@ -35,5 +35,5 @@ Execute(The autopep8 callback should include options): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autopep8')) . ' --some-option -' }, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autopep8')) . ' --some-option -' }, \ ale#fixers#autopep8#Fix(bufnr('')) diff --git a/test/fixers/test_brittany_fixer_callback.vader b/test/fixers/test_brittany_fixer_callback.vader new file mode 100644 index 0000000..a0182b5 --- /dev/null +++ b/test/fixers/test_brittany_fixer_callback.vader @@ -0,0 +1,23 @@ +Before: + Save g:ale_haskell_brittany_executable + + " Use an invalid global executable, so we don't match it. + let g:ale_haskell_brittany_executable = 'xxxinvalid' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The brittany callback should return the correct default values): + call ale#test#SetFilename('../haskell_files/testfile.hs') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' %t', + \ }, + \ ale#fixers#brittany#Fix(bufnr('')) diff --git a/test/fixers/test_elm_format_fixer_callback.vader b/test/fixers/test_elm_format_fixer_callback.vader index 8552c5d..d613aa8 100644 --- a/test/fixers/test_elm_format_fixer_callback.vader +++ b/test/fixers/test_elm_format_fixer_callback.vader @@ -15,7 +15,7 @@ Execute(The elm-format command should have default params): \ { \ 'read_temporary_file': 1, \ 'command': - \ ale#Escape(ale#path#Winify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) + \ ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) \ . ' %t --yes', \ }, \ ale#fixers#format#Fix(bufnr('')) @@ -55,7 +55,7 @@ Execute(The elm-format command should manage empty options): \ { \ 'read_temporary_file': 1, \ 'command': - \ ale#Escape(ale#path#Winify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) + \ ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) \ . ' %t', \ }, \ ale#fixers#format#Fix(bufnr('')) @@ -68,7 +68,7 @@ Execute(The elm-format command should manage custom options): \ { \ 'read_temporary_file': 1, \ 'command': - \ ale#Escape(ale#path#Winify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) + \ ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/node_modules/.bin/elm-format')) \ . ' %t --param1 --param2', \ }, \ ale#fixers#format#Fix(bufnr('')) diff --git a/test/fixers/test_eslint_fixer_callback.vader b/test/fixers/test_eslint_fixer_callback.vader index d4783fc..be33825 100644 --- a/test/fixers/test_eslint_fixer_callback.vader +++ b/test/fixers/test_eslint_fixer_callback.vader @@ -3,19 +3,21 @@ Before: After: call ale#test#RestoreDirectory() + call ale#semver#ResetVersionCache() Execute(The executable path should be correct): call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js') + " eslint_d output with an older eslint version is used here. AssertEqual \ { \ 'read_temporary_file': 1, \ 'command': (has('win32') ? 'node.exe ' : '') - \ . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js')) \ . ' --fix %t', \ }, - \ ale#fixers#eslint#Fix(bufnr('')) + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['v4.4.1 (eslint_d v5.1.0)']) Execute(The lower priority configuration file in a nested directory should be preferred): call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-config/testfile.js') @@ -24,11 +26,11 @@ Execute(The lower priority configuration file in a nested directory should be pr \ { \ 'read_temporary_file': 1, \ 'command': (has('win32') ? 'node.exe ' : '') - \ . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/subdir-with-config/.eslintrc')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/subdir-with-config/.eslintrc')) \ . ' --fix %t', \ }, - \ ale#fixers#eslint#Fix(bufnr('')) + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), []) Execute(package.json should be used as a last resort): call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-package-json/testfile.js') @@ -37,11 +39,11 @@ Execute(package.json should be used as a last resort): \ { \ 'read_temporary_file': 1, \ 'command': (has('win32') ? 'node.exe ' : '') - \ . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js')) \ . ' --fix %t', \ }, - \ ale#fixers#eslint#Fix(bufnr('')) + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), []) call ale#test#SetFilename('../eslint-test-files/package.json') @@ -49,8 +51,122 @@ Execute(package.json should be used as a last resort): \ { \ 'read_temporary_file': 1, \ 'command': - \ ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/node_modules/.bin/eslint')) - \ . ' -c ' . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/package.json')) + \ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/node_modules/.bin/eslint')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/package.json')) \ . ' --fix %t', \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), []) + +Execute(The version check should be correct): + call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js') + + AssertEqual + \ { + \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion', + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' --version' + \ }, \ ale#fixers#eslint#Fix(bufnr('')) + +Execute(--fix-dry-run should be used for 4.9.0 and up): + call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js') + + AssertEqual + \ { + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json', + \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput', + \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0']) + +Execute(--fix-to-stdout should be used for eslint_d): + call ale#test#SetFilename('../eslint-test-files/app-with-eslint-d/testfile.js') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': + \ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d')) + \ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/package.json')) + \ . ' --fix %t', + \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['']) + + " The option should be used when eslint_d is new enough. + " We look at the ESLint version instead of the eslint_d version. + AssertEqual + \ { + \ 'command': + \ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d')) + \ . ' --stdin-filename %s --stdin --fix-to-stdout', + \ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput', + \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['v3.19.0 (eslint_d v4.2.0)']) + + " The option should be used for new versions too. + AssertEqual + \ { + \ 'command': + \ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d')) + \ . ' --stdin-filename %s --stdin --fix-to-stdout', + \ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput', + \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0']) + +Execute(The version number should be cached): + call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-config/testfile.js') + + " Call the second callback with the version output. + call ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0']) + + " The version command should be skipped. + AssertEqual + \ { + \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion', + \ 'command': '', + \ }, + \ ale#fixers#eslint#Fix(bufnr('')) + + " Call it again without the version output. We should use the newer command. + AssertEqual + \ { + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json', + \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput', + \ }, + \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), []) + +Execute(The --fix-dry-run post-processor should handle JSON output correctly): + AssertEqual + \ [], + \ ale#fixers#eslint#ProcessFixDryRunOutput(bufnr(''), []) + AssertEqual + \ [], + \ ale#fixers#eslint#ProcessFixDryRunOutput(bufnr(''), ['']) + AssertEqual + \ [], + \ ale#fixers#eslint#ProcessFixDryRunOutput(bufnr(''), ['[{}]']) + AssertEqual + \ ['foo', 'bar'], + \ ale#fixers#eslint#ProcessFixDryRunOutput(bufnr(''), ['[{"output": "foo\nbar"}]']) + +Execute(The eslint_d post-processor should permit regular JavaScript content): + AssertEqual + \ [ + \ 'const x = ''Error: foo''', + \ 'const y = 3', + \ ], + \ ale#fixers#eslint#ProcessEslintDOutput(bufnr(''), [ + \ 'const x = ''Error: foo''', + \ 'const y = 3', + \ ]) + +Execute(The eslint_d post-processor should handle error messages correctly): + AssertEqual + \ [], + \ ale#fixers#eslint#ProcessEslintDOutput(bufnr(''), [ + \ 'Error: No ESLint configuration found.', + \ ]) diff --git a/test/fixers/test_fixjson_fixer_callback.vader b/test/fixers/test_fixjson_fixer_callback.vader new file mode 100644 index 0000000..1a3bdcf --- /dev/null +++ b/test/fixers/test_fixjson_fixer_callback.vader @@ -0,0 +1,50 @@ +Before: + Save g:ale_json_fixjson_executable + Save g:ale_json_fixjson_options + + let g:ale_json_fixjson_executable = '/path/to/fixjson' + let g:ale_json_fixjson_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + +Execute(The fixjson callback should return the correct default command): + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/fixjson') + \ . ' --stdin-filename ' + \ . ale#Escape(bufname(bufnr(''))) + \ }, + \ ale#fixers#fixjson#Fix(bufnr('')) + +Execute(The fixjson callback should set the buffer name as file name): + call ale#test#SetFilename('../json_files/testfile.json') + + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/fixjson') + \ . ' --stdin-filename ' + \ . ale#Escape(bufname(bufnr(''))) + \ }, + \ ale#fixers#fixjson#Fix(bufnr('')) + + AssertNotEqual + \ stridx( + \ ale#fixers#fixjson#Fix(bufnr('')).command, + \ 'testfile.json', + \ ), + \ -1 + +Execute(The fixjson callback should include additional options): + let g:ale_json_fixjson_options = '-i 2' + + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/fixjson') + \ . ' --stdin-filename ' + \ . ale#Escape(bufname(bufnr(''))) + \ . ' -i 2' + \ }, + \ ale#fixers#fixjson#Fix(bufnr('')) diff --git a/test/fixers/test_goimports_fixer_callback.vader b/test/fixers/test_goimports_fixer_callback.vader new file mode 100644 index 0000000..cec0635 --- /dev/null +++ b/test/fixers/test_goimports_fixer_callback.vader @@ -0,0 +1,41 @@ +Before: + Save g:ale_go_goimports_executable + Save g:ale_go_goimports_options + + " Use an invalid global executable, so we don't match it. + let g:ale_go_goimports_executable = 'xxxinvalid' + let g:ale_go_goimports_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + call ale#test#SetFilename('../go_files/testfile.go') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The goimports callback should return 0 when the executable isn't executable): + AssertEqual + \ 0, + \ ale#fixers#goimports#Fix(bufnr('')) + +Execute(The goimports callback should the command when the executable test passes): + let g:ale_go_goimports_executable = has('win32') ? 'cmd' : 'echo' + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape(g:ale_go_goimports_executable) . ' -l -w -srcdir %s %t' + \ }, + \ ale#fixers#goimports#Fix(bufnr('')) + +Execute(The goimports callback should include extra options): + let g:ale_go_goimports_executable = has('win32') ? 'cmd' : 'echo' + let g:ale_go_goimports_options = '--xxx' + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape(g:ale_go_goimports_executable) . ' -l -w -srcdir %s --xxx %t' + \ }, + \ ale#fixers#goimports#Fix(bufnr('')) diff --git a/test/fixers/test_goofle_java_format_fixer_callback.vader b/test/fixers/test_goofle_java_format_fixer_callback.vader new file mode 100644 index 0000000..d64e278 --- /dev/null +++ b/test/fixers/test_goofle_java_format_fixer_callback.vader @@ -0,0 +1,27 @@ +Before: + Save g:ale_google_java_format_executable + + " Use an invalid global executable, so we don't match it. + let g:ale_google_java_format_executable = 'xxxinvalid' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The google-java-format callback should return 0 when the executable isn't executable): + AssertEqual + \ 0, + \ ale#fixers#google_java_format#Fix(bufnr('')) + +Execute(The google-java-format callback should run the command when the executable test passes): + let g:ale_google_java_format_executable = has('win32') ? 'cmd' : 'echo' + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape(ale_google_java_format_executable) . ' --replace %t' + \ }, + \ ale#fixers#google_java_format#Fix(bufnr('')) diff --git a/test/fixers/test_hackfmt_fixer_callback.vader b/test/fixers/test_hackfmt_fixer_callback.vader new file mode 100644 index 0000000..ed78fc8 --- /dev/null +++ b/test/fixers/test_hackfmt_fixer_callback.vader @@ -0,0 +1,37 @@ +Before: + Save g:ale_php_hackfmt_executable + Save g:ale_php_hackfmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_php_hackfmt_executable = 'xxxinvalid' + let g:ale_php_hackfmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The hackfmt callback should return the correct default values): + call ale#test#SetFilename('../hack_files/testfile.php') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i %t', + \ }, + \ ale#fixers#hackfmt#Fix(bufnr('')) + +Execute(The hackfmt callback should include custom hackfmt options): + let g:ale_php_hackfmt_options = "--some-option" + call ale#test#SetFilename('../hack_files/testfile.php') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i --some-option %t', + \ }, + \ ale#fixers#hackfmt#Fix(bufnr('')) diff --git a/test/fixers/test_hfmt_fixer_callback.vader b/test/fixers/test_hfmt_fixer_callback.vader new file mode 100644 index 0000000..69cd03f --- /dev/null +++ b/test/fixers/test_hfmt_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_haskell_hfmt_executable + + " Use an invalid global executable, so we don't match it. + let g:ale_haskell_hfmt_executable = 'xxxinvalid' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The hfmt callback should return the correct default values): + call ale#test#SetFilename('../haskell_files/testfile.hs') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -w' + \ . ' %t', + \ }, + \ ale#fixers#hfmt#Fix(bufnr('')) diff --git a/test/fixers/test_importjs_fixer_callback.vader b/test/fixers/test_importjs_fixer_callback.vader new file mode 100644 index 0000000..c3e57f8 --- /dev/null +++ b/test/fixers/test_importjs_fixer_callback.vader @@ -0,0 +1,35 @@ +Before: + Save g:ale_js_importjs_executable + + " Use an invalid global executable, so we don't match it. + let g:ale_js_importjs_executable = 'xxxinvalid' + + call ale#test#SetDirectory('/testplugin/test/fixers') + call ale#test#SetFilename('../javascript_files/test.js') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The importjs callback should return 0 when the executable isn't executable): + AssertEqual + \ 0, + \ ale#fixers#importjs#Fix(bufnr('')) + +Execute(The importjs callback should run the command when the executable test passes): + let g:ale_js_importjs_executable = has('win32') ? 'cmd' : 'echo' + + AssertEqual + \ { + \ 'process_with': 'ale#fixers#importjs#ProcessOutput', + \ 'command': ale#Escape(g:ale_js_importjs_executable) . ' fix %s' + \ }, + \ ale#fixers#importjs#Fix(bufnr('')) + +Execute(The ProcessOutput callback should return the expected output): + let g:testOutput = '{"messages":[],"fileContent":"one\ntwo","unresolvedImports":{}}' + + AssertEqual + \ ['one', 'two'], + \ ale#fixers#importjs#ProcessOutput(bufnr(''), g:testOutput) diff --git a/test/fixers/test_isort_fixer_callback.vader b/test/fixers/test_isort_fixer_callback.vader index ea4426d..7c2b515 100644 --- a/test/fixers/test_isort_fixer_callback.vader +++ b/test/fixers/test_isort_fixer_callback.vader @@ -25,5 +25,8 @@ Execute(The isort callback should return the correct default values): silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort')) . ' -' }, + \ { + \ 'command': 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir/foo')) . ' && ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort')) . ' -', + \ }, \ ale#fixers#isort#Fix(bufnr('')) diff --git a/test/fixers/test_jq_fixer_callback.vader b/test/fixers/test_jq_fixer_callback.vader new file mode 100644 index 0000000..2e32bf8 --- /dev/null +++ b/test/fixers/test_jq_fixer_callback.vader @@ -0,0 +1,14 @@ +Before: + Save g:ale_json_jq_executable + Save g:ale_json_jq_options + +After: + Restore + +Execute(The jq fixer should use the options you set): + let g:ale_json_jq_executable = 'foo' + let g:ale_json_jq_options = '--bar' + + AssertEqual + \ {'command': ale#Escape('foo') . ' . --bar'}, + \ ale#fixers#jq#Fix(bufnr('')) diff --git a/test/fixers/test_mix_format_fixer_callback.vader b/test/fixers/test_mix_format_fixer_callback.vader new file mode 100644 index 0000000..c6c97c5 --- /dev/null +++ b/test/fixers/test_mix_format_fixer_callback.vader @@ -0,0 +1,20 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/fixers') + Save g:ale_elixir_mix_executable + + let g:ale_elixir_mix_executable = 'xxxinvalid' + +After: + call ale#test#RestoreDirectory() + +Execute(The mix_format callback should return the correct default values): + call ale#test#SetFilename('../elixir-test-files/testfile.ex') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' format %t', + \ }, + \ ale#fixers#mix_format#Fix(bufnr('')) + diff --git a/test/fixers/test_php_cs_fixer.vader b/test/fixers/test_php_cs_fixer.vader new file mode 100644 index 0000000..b657967 --- /dev/null +++ b/test/fixers/test_php_cs_fixer.vader @@ -0,0 +1,46 @@ +Before: + Save g:ale_php_cs_fixer_executable + let g:ale_php_cs_fixer_executable = 'php-cs-fixer' + + call ale#test#SetDirectory('/testplugin/test/fixers') + silent cd .. + silent cd command_callback + let g:dir = getcwd() + +After: + Restore + + call ale#test#RestoreDirectory() + + +Execute(project with php-cs-fixer should use local by default): + call ale#test#SetFilename('php_paths/project-with-php-cs-fixer/test.php') + + AssertEqual + \ ale#path#Simplify(g:dir . '/php_paths/project-with-php-cs-fixer/vendor/bin/php-cs-fixer'), + \ ale#fixers#php_cs_fixer#GetExecutable(bufnr('')) + +Execute(use-global should override local detection): + let g:ale_php_cs_fixer_use_global = 1 + call ale#test#SetFilename('php_paths/project-with-php-cs-fixer/test.php') + + AssertEqual + \ 'php-cs-fixer', + \ ale#fixers#php_cs_fixer#GetExecutable(bufnr('')) + +Execute(project without php-cs-fixer should use global): + call ale#test#SetFilename('php_paths/project-without-php-cs-fixer/test.php') + + AssertEqual + \ 'php-cs-fixer', + \ ale#fixers#php_cs_fixer#GetExecutable(bufnr('')) + + + + +Execute(The php-cs-fixer callback should return the correct default values): + call ale#test#SetFilename('php_paths/project-without-php-cs-fixer/foo/test.php') + + AssertEqual + \ {'read_temporary_file': 1, 'command': ale#Escape('php-cs-fixer') . ' fix %t' }, + \ ale#fixers#php_cs_fixer#Fix(bufnr('')) diff --git a/test/fixers/test_phpcbf_fixer_callback.vader b/test/fixers/test_phpcbf_fixer_callback.vader index cf02a0b..1663c89 100644 --- a/test/fixers/test_phpcbf_fixer_callback.vader +++ b/test/fixers/test_phpcbf_fixer_callback.vader @@ -21,7 +21,7 @@ Execute(project with phpcbf should use local by default): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf'), + \ ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf'), \ ale#fixers#phpcbf#GetExecutable(bufnr('')) Execute(use-global should override local detection): @@ -43,7 +43,7 @@ Execute(The phpcbf callback should return the correct default values): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' }, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s -' }, \ ale#fixers#phpcbf#Fix(bufnr('')) Execute(The phpcbf callback should include the phpcbf_standard option): @@ -51,7 +51,7 @@ Execute(The phpcbf callback should include the phpcbf_standard option): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml'}, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'}, \ ale#fixers#phpcbf#Fix(bufnr('')) Before: @@ -77,7 +77,7 @@ Execute(project with phpcbf should use local by default): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf'), + \ ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf'), \ ale#fixers#phpcbf#GetExecutable(bufnr('')) Execute(use-global should override local detection): @@ -99,7 +99,7 @@ Execute(The phpcbf callback should return the correct default values): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' }, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s -' }, \ ale#fixers#phpcbf#Fix(bufnr('')) Execute(The phpcbf callback should include the phpcbf_standard option): @@ -107,6 +107,6 @@ Execute(The phpcbf callback should include the phpcbf_standard option): call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml'}, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'}, \ ale#fixers#phpcbf#Fix(bufnr('')) diff --git a/test/fixers/test_prettier_eslint_fixer.callback.vader b/test/fixers/test_prettier_eslint_fixer.callback.vader index b48a708..ef0b35d 100644 --- a/test/fixers/test_prettier_eslint_fixer.callback.vader +++ b/test/fixers/test_prettier_eslint_fixer.callback.vader @@ -4,12 +4,10 @@ Before: Save g:ale_javascript_prettier_eslint_executable Save g:ale_javascript_prettier_eslint_use_global Save g:ale_javascript_prettier_eslint_options - Save g:ale_javascript_prettier_eslint_legacy unlet! g:ale_javascript_prettier_eslint_executable unlet! g:ale_javascript_prettier_eslint_use_global unlet! g:ale_javascript_prettier_eslint_options - unlet! g:ale_javascript_prettier_eslint_legacy call ale#fixers#prettier_eslint#SetOptionDefaults() @@ -19,9 +17,9 @@ After: unlet! b:ale_javascript_prettier_eslint_executable unlet! b:ale_javascript_prettier_eslint_use_global unlet! b:ale_javascript_prettier_eslint_options - unlet! b:ale_javascript_prettier_eslint_legacy call ale#test#RestoreDirectory() + call ale#semver#ResetVersionCache() Execute(The default command should be correct): AssertEqual @@ -32,7 +30,7 @@ Execute(The default command should be correct): \ . ' %t' \ . ' --write' \ }, - \ ale#fixers#prettier_eslint#Fix(bufnr('')) + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), []) Execute(Additional options should be used when set): let b:ale_javascript_prettier_eslint_options = '--foobar' @@ -45,9 +43,9 @@ Execute(Additional options should be used when set): \ . ' %t' \ . ' --foobar --write' \ }, - \ ale#fixers#prettier_eslint#Fix(bufnr('')) + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), []) -Execute(Configuration files should be detected): +Execute(--eslint-config-path should be set for 4.2.0 and up): call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js') AssertEqual @@ -56,14 +54,13 @@ Execute(Configuration files should be detected): \ 'command': \ ale#Escape('prettier-eslint') \ . ' %t' - \ . ' --eslint-config-path ' . ale#Escape(ale#path#Winify(g:dir . '/eslint-test-files/react-app/.eslintrc.js')) + \ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js')) \ . ' --write' \ }, - \ ale#fixers#prettier_eslint#Fix(bufnr('')) + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.2.0']) -Execute(Configuration files should be disabled if the legacy option is on): +Execute(--eslint-config-path shouldn't be used for older versions): call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js') - let b:ale_javascript_prettier_eslint_legacy = 1 AssertEqual \ { @@ -73,4 +70,44 @@ Execute(Configuration files should be disabled if the legacy option is on): \ . ' %t' \ . ' --write' \ }, + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), []) + +Execute(The version check should be correct): + AssertEqual + \ { + \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion', + \ 'command': ale#Escape('prettier-eslint') . ' --version', + \ }, \ ale#fixers#prettier_eslint#Fix(bufnr('')) + +Execute(The new --stdin-filepath option should be used when the version is new enough): + call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js') + + AssertEqual + \ { + \ 'command': 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('prettier-eslint') + \ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js')) + \ . ' --stdin-filepath %s --stdin', + \ }, + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.4.0']) + +Execute(The version number should be cached): + call ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.4.0']) + + " The version command should be skipped. + AssertEqual + \ { + \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion', + \ 'command': '', + \ }, + \ ale#fixers#prettier_eslint#Fix(bufnr('')) + + " The newer command should be used. + AssertEqual + \ { + \ 'command': 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape('prettier-eslint') + \ . ' --stdin-filepath %s --stdin', + \ }, + \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), []) diff --git a/test/fixers/test_prettier_fixer_callback.vader b/test/fixers/test_prettier_fixer_callback.vader index 471a863..c4f36f5 100644 --- a/test/fixers/test_prettier_fixer_callback.vader +++ b/test/fixers/test_prettier_fixer_callback.vader @@ -14,7 +14,9 @@ Before: After: let g:ale_has_override = {} + call ale#test#RestoreDirectory() + call ale#semver#ResetVersionCache() Execute(The prettier callback should return the correct default values): call ale#test#SetFilename('../prettier-test-files/testfile.js') @@ -24,12 +26,11 @@ Execute(The prettier callback should return the correct default values): \ 'read_temporary_file': 1, \ 'command': ale#Escape(g:ale_javascript_prettier_executable) \ . ' %t' - \ . ' --parser babylon' \ . ' --write', \ }, - \ ale#fixers#prettier#Fix(bufnr('')) + \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), []) -Execute(The prettier callback should include configuration files when the option is set): +Execute(The --config option should not be set automatically): let g:ale_javascript_prettier_use_local_config = 1 call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js') @@ -38,11 +39,9 @@ Execute(The prettier callback should include configuration files when the option \ 'read_temporary_file': 1, \ 'command': ale#Escape(g:ale_javascript_prettier_executable) \ . ' %t' - \ . ' --parser babylon' - \ . ' --config ' . ale#Escape(ale#path#Winify(g:dir . '/../prettier-test-files/with_config/.prettierrc')) \ . ' --write', \ }, - \ ale#fixers#prettier#Fix(bufnr('')) + \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), []) Execute(The prettier callback should include custom prettier options): let g:ale_javascript_prettier_options = '--no-semi' @@ -53,78 +52,46 @@ Execute(The prettier callback should include custom prettier options): \ 'read_temporary_file': 1, \ 'command': ale#Escape(g:ale_javascript_prettier_executable) \ . ' %t' - \ . ' --no-semi --parser babylon' - \ . ' --config ' . ale#Escape(ale#path#Winify(g:dir . '/../prettier-test-files/with_config/.prettierrc')) + \ . ' --no-semi' \ . ' --write', \ }, - \ ale#fixers#prettier#Fix(bufnr('')) + \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), []) -Execute(Append '--parser typescript' for filetype=typescript): - set filetype=typescript - call ale#test#SetFilename('../prettier-test-files/testfile.ts') +Execute(The version check should be correct): + call ale#test#SetFilename('../prettier-test-files/testfile.js') AssertEqual \ { - \ 'read_temporary_file': 1, + \ 'chain_with': 'ale#fixers#prettier#ApplyFixForVersion', \ 'command': ale#Escape(g:ale_javascript_prettier_executable) - \ . ' %t' - \ . ' --parser typescript' - \ . ' --write', + \ . ' --version', \ }, \ ale#fixers#prettier#Fix(bufnr('')) -Execute(Append '--parser json' for filetype=json): - set filetype=json - call ale#test#SetFilename('../prettier-test-files/testfile.json') +Execute(--stdin-filepath should be used when prettier is new enough): + let g:ale_javascript_prettier_options = '--no-semi' + call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js') AssertEqual \ { - \ 'read_temporary_file': 1, - \ 'command': ale#Escape(g:ale_javascript_prettier_executable) - \ . ' %t' - \ . ' --parser json' - \ . ' --write', + \ 'command': 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape(g:ale_javascript_prettier_executable) + \ . ' --no-semi' + \ . ' --stdin-filepath %s --stdin', \ }, - \ ale#fixers#prettier#Fix(bufnr('')) + \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0']) -Execute(Append '--parser postcss' for filetype=scss): - set filetype=scss - call ale#test#SetFilename('../prettier-test-files/testfile.scss') +Execute(The version number should be cached): + call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js') + " Call the second callback with the version output. + call ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0']) + + " Call it again without the version output. We should use the newer command. AssertEqual \ { - \ 'read_temporary_file': 1, - \ 'command': ale#Escape(g:ale_javascript_prettier_executable) - \ . ' %t' - \ . ' --parser postcss' - \ . ' --write', + \ 'command': 'cd ' . ale#Escape(expand('%:p:h')) . ' && ' + \ . ale#Escape(g:ale_javascript_prettier_executable) + \ . ' --stdin-filepath %s --stdin', \ }, - \ ale#fixers#prettier#Fix(bufnr('')) - -Execute(Append '--parser postcss' for filetype=css): - set filetype=css - call ale#test#SetFilename('../prettier-test-files/testfile.css') - - AssertEqual - \ { - \ 'read_temporary_file': 1, - \ 'command': ale#Escape(g:ale_javascript_prettier_executable) - \ . ' %t' - \ . ' --parser postcss' - \ . ' --write', - \ }, - \ ale#fixers#prettier#Fix(bufnr('')) - -Execute(Append '--parser postcss' for filetype=less): - set filetype=less - call ale#test#SetFilename('../prettier-test-files/testfile.less') - - AssertEqual - \ { - \ 'read_temporary_file': 1, - \ 'command': ale#Escape(g:ale_javascript_prettier_executable) - \ . ' %t' - \ . ' --parser postcss' - \ . ' --write', - \ }, - \ ale#fixers#prettier#Fix(bufnr('')) + \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), []) diff --git a/test/fixers/test_refmt_fixer_callback.vader b/test/fixers/test_refmt_fixer_callback.vader new file mode 100644 index 0000000..9ec331e --- /dev/null +++ b/test/fixers/test_refmt_fixer_callback.vader @@ -0,0 +1,41 @@ +Before: + Save g:ale_reasonml_refmt_executable + Save g:ale_reasonml_refmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_reasonml_refmt_executable = 'xxxinvalid' + let g:ale_reasonml_refmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The refmt callback should return the correct default values): + call ale#test#SetFilename('../reasonml_files/testfile.re') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' --in-place' + \ . ' %t', + \ }, + \ ale#fixers#refmt#Fix(bufnr('')) + +Execute(The refmt callback should include custom refmt options): + let g:ale_reasonml_refmt_options = "-w 80" + call ale#test#SetFilename('../reasonml_files/testfile.re') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' ' . g:ale_reasonml_refmt_options + \ . ' --in-place' + \ . ' %t', + \ }, + \ ale#fixers#refmt#Fix(bufnr('')) + diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader index ff2ca96..045256f 100644 --- a/test/fixers/test_rubocop_fixer_callback.vader +++ b/test/fixers/test_rubocop_fixer_callback.vader @@ -34,7 +34,7 @@ Execute(The rubocop callback should include configuration files): \ { \ 'read_temporary_file': 1, \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --config ' . ale#Escape(ale#path#Winify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) + \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) \ . ' --auto-correct %t', \ }, \ ale#fixers#rubocop#Fix(bufnr('')) @@ -47,7 +47,7 @@ Execute(The rubocop callback should include custom rubocop options): \ { \ 'read_temporary_file': 1, \ 'command': ale#Escape(g:ale_ruby_rubocop_executable) - \ . ' --config ' . ale#Escape(ale#path#Winify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) + \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml')) \ . ' --except Lint/Debugger' \ . ' --auto-correct %t', \ }, diff --git a/test/fixers/test_rustfmt_fixer_callback.vader b/test/fixers/test_rustfmt_fixer_callback.vader new file mode 100644 index 0000000..95c78de --- /dev/null +++ b/test/fixers/test_rustfmt_fixer_callback.vader @@ -0,0 +1,34 @@ +Before: + Save g:ale_rust_rustfmt_executable + Save g:ale_rust_rustfmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_rust_rustfmt_executable = 'xxxinvalid' + let g:ale_rust_rustfmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The rustfmt callback should return the correct default values): + call ale#test#SetFilename('../rust_files/testfile.rs') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid'), + \ }, + \ ale#fixers#rustfmt#Fix(bufnr('')) + +Execute(The rustfmt callback should include custom rustfmt options): + let g:ale_rust_rustfmt_options = "--skip-children" + call ale#test#SetFilename('../rust_files/testfile.rs') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' ' . g:ale_rust_rustfmt_options, + \ }, + \ ale#fixers#rustfmt#Fix(bufnr('')) diff --git a/test/fixers/test_shfmt_fixer_callback.vader b/test/fixers/test_shfmt_fixer_callback.vader new file mode 100644 index 0000000..5dc6e86 --- /dev/null +++ b/test/fixers/test_shfmt_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_sh_shfmt_executable + Save g:ale_sh_shfmt_options + +After: + Restore + +Execute(The shfmt callback should return the correct default values): + AssertEqual + \ { + \ 'command': ale#Escape('shfmt'), + \ }, + \ 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' + + AssertEqual + \ { + \ 'command': ale#Escape('foobar') + \ . ' --some-option', + \ }, + \ ale#fixers#shfmt#Fix(bufnr('')) diff --git a/test/fixers/test_standard_fixer_callback.vader b/test/fixers/test_standard_fixer_callback.vader index 34c752d..38f2d54 100644 --- a/test/fixers/test_standard_fixer_callback.vader +++ b/test/fixers/test_standard_fixer_callback.vader @@ -11,7 +11,7 @@ Execute(The executable path should be correct): \ { \ 'read_temporary_file': 1, \ 'command': (has('win32') ? 'node.exe ' : '') - \ . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/node_modules/standard/bin/cmd.js')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/standard/bin/cmd.js')) \ . ' --fix %t', \ }, \ ale#fixers#standard#Fix(bufnr('')) diff --git a/test/fixers/test_stylelint_fixer_callback.vader b/test/fixers/test_stylelint_fixer_callback.vader index a0fc6ff..90a9dc1 100644 --- a/test/fixers/test_stylelint_fixer_callback.vader +++ b/test/fixers/test_stylelint_fixer_callback.vader @@ -11,7 +11,7 @@ Execute(The executable path should be correct): \ { \ 'read_temporary_file': 1, \ 'command': (has('win32') ? 'node.exe ' : '') - \ . ale#Escape(ale#path#Winify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) \ . ' --fix %t', \ }, \ ale#fixers#stylelint#Fix(bufnr('')) diff --git a/test/fixers/test_yapf_fixer_callback.vader b/test/fixers/test_yapf_fixer_callback.vader index e607556..cfc508c 100644 --- a/test/fixers/test_yapf_fixer_callback.vader +++ b/test/fixers/test_yapf_fixer_callback.vader @@ -26,7 +26,7 @@ Execute(The yapf callback should return the correct default values): call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') AssertEqual - \ {'command': ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yapf'))}, + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yapf'))}, \ ale#fixers#yapf#Fix(bufnr('')) \ Execute(The yapf should include the .style.yapf file if present): @@ -35,8 +35,8 @@ Execute(The yapf should include the .style.yapf file if present): AssertEqual \ { \ 'command': - \ ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yapf')) + \ ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yapf')) \ . ' --no-local-style' - \ . ' --style ' . ale#Escape(ale#path#Winify(g:dir . '/python_paths/with_virtualenv/dir_with_yapf_config/.style.yapf')), + \ . ' --style ' . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/dir_with_yapf_config/.style.yapf')), \ }, \ ale#fixers#yapf#Fix(bufnr('')) diff --git a/test/go_files/testfile2.go b/test/go_files/testfile2.go new file mode 100644 index 0000000..e69de29 diff --git a/test/hack_files/testfile.php b/test/hack_files/testfile.php new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/haml-lint-and-rubocop/.haml-lint.yml b/test/hamllint-test-files/haml-lint-and-rubocop/.haml-lint.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/haml-lint-and-rubocop/.rubocop.yml b/test/hamllint-test-files/haml-lint-and-rubocop/.rubocop.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/haml-lint-and-rubocop/subdir/file.haml b/test/hamllint-test-files/haml-lint-and-rubocop/subdir/file.haml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/haml-lint-yml/.haml-lint.yml b/test/hamllint-test-files/haml-lint-yml/.haml-lint.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/haml-lint-yml/subdir/file.haml b/test/hamllint-test-files/haml-lint-yml/subdir/file.haml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/rubocop-yml/.rubocop.yml b/test/hamllint-test-files/rubocop-yml/.rubocop.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/hamllint-test-files/rubocop-yml/subdir/file.haml b/test/hamllint-test-files/rubocop-yml/subdir/file.haml new file mode 100644 index 0000000..e69de29 diff --git a/test/handler/test_alex_handler.vader b/test/handler/test_alex_handler.vader new file mode 100644 index 0000000..eb241f8 --- /dev/null +++ b/test/handler/test_alex_handler.vader @@ -0,0 +1,54 @@ +Execute(The alex handler should handle the example from the alex README): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 5, + \ 'end_lnum': 1, + \ 'end_col': 13, + \ 'type': 'W', + \ 'text': '`boogeyman` may be insensitive, use `boogey` instead (retext-equality)', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 42, + \ 'end_lnum': 1, + \ 'end_col': 47, + \ 'type': 'W', + \ 'text': '`master` / `slaves` may be insensitive, use `primary` / `replica` instead (retext-equality)', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 69, + \ 'end_lnum': 1, + \ 'end_col': 74, + \ 'type': 'W', + \ 'text': 'Don’t use “slaves”, it’s profane (retext-profanities)', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 52, + \ 'end_lnum': 2, + \ 'end_col': 53, + \ 'type': 'W', + \ 'text': '`he` may be insensitive, use `they`, `it` instead (retext-equality)', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 61, + \ 'end_lnum': 2, + \ 'end_col': 67, + \ 'type': 'W', + \ 'text': '`cripple` may be insensitive, use `person with a limp` instead (retext-equality)', + \ }, + \ ], + \ ale#handlers#alex#Handle(bufnr(''), [ + \ 'example.md', + \ ' 1:5-1:14 warning `boogeyman` may be insensitive, use `boogey` instead boogeyman-boogeywoman retext-equality', + \ ' 1:42-1:48 warning `master` / `slaves` may be insensitive, use `primary` / `replica` instead master-slave retext-equality', + \ ' 1:69-1:75 warning Don’t use “slaves”, it’s profane slaves retext-profanities', + \ ' 2:52-2:54 warning `he` may be insensitive, use `they`, `it` instead he-she retext-equality', + \ ' 2:61-2:68 warning `cripple` may be insensitive, use `person with a limp` instead cripple retext-equality', + \ '', + \ '⚠ 5 warnings', + \ ]) diff --git a/test/handler/test_ansible_lint_handler.vader b/test/handler/test_ansible_lint_handler.vader index 3a86429..796277e 100644 --- a/test/handler/test_ansible_lint_handler.vader +++ b/test/handler/test_ansible_lint_handler.vader @@ -1,9 +1,13 @@ Before: - runtime ale_linters/ansible/ansible_lint.vim - call ale#test#SetFilename('main.yml') + runtime ale_linters/ansible/ansible_lint.vim + call ale#test#SetFilename('main.yml') + + let b:ale_warn_about_trailing_whitespace = 1 After: - call ale#linter#Reset() + unlet! b:ale_warn_about_trailing_whitespace + + call ale#linter#Reset() Execute(The ansible-lint handler should handle basic errors): AssertEqual @@ -12,13 +16,24 @@ Execute(The ansible-lint handler should handle basic errors): \ 'lnum': 35, \ 'col': 0, \ 'type': 'E', - \ 'text': 'EANSIBLE0002: Trailing whitespace', + \ 'text': 'Trailing whitespace', + \ 'code': 'EANSIBLE0002', \ }, \ ], \ ale_linters#ansible#ansible_lint#Handle(bufnr(''), [ \ fnamemodify(tempname(), ':h') . '/main.yml:35: [EANSIBLE0002] Trailing whitespace', \ ]) +Execute(The ansible-lint handler should supress trailing whitespace output when the option is used): + let b:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#ansible#ansible_lint#Handle(bufnr(''), [ + \ fnamemodify(tempname(), ':h') . '/main.yml:35: [EANSIBLE0002] Trailing whitespace', + \ ]) + Execute (The ansible-lint handler should handle names with spaces): AssertEqual \ [ @@ -26,7 +41,8 @@ Execute (The ansible-lint handler should handle names with spaces): \ 'lnum': 6, \ 'col': 6, \ 'type': 'E', - \ 'text': 'E111: indentation is not a multiple of four', + \ 'text': 'indentation is not a multiple of four', + \ 'code': 'E111', \ }, \ ], \ ale_linters#ansible#ansible_lint#Handle(bufnr(''), [ diff --git a/test/handler/test_asm_handler.vader b/test/handler/test_asm_handler.vader index 2868628..4ab9999 100644 --- a/test/handler/test_asm_handler.vader +++ b/test/handler/test_asm_handler.vader @@ -1,6 +1,11 @@ -Execute(The asm GCC handler should parse lines from GCC 6.3.1 correctly): +Before: runtime ale_linters/asm/gcc.vim +After: + call ale#linter#Reset() + +Execute(The asm GCC handler should parse lines from GCC 6.3.1 correctly): + AssertEqual \ [ \ { @@ -19,6 +24,3 @@ Execute(The asm GCC handler should parse lines from GCC 6.3.1 correctly): \ "{standard_input}:38: Error: too many memory references for `mov'", \ "{standard input}:42: Error: incorrect register `%ax' used with `l' suffix", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_brakeman_handler.vader b/test/handler/test_brakeman_handler.vader index 5a39879..02eb31b 100644 --- a/test/handler/test_brakeman_handler.vader +++ b/test/handler/test_brakeman_handler.vader @@ -34,7 +34,7 @@ Execute(The brakeman handler should parse JSON correctly): \ '"fingerprint": "1234",', \ '"check_name": "SQL",', \ '"message": "Possible SQL injection",', - \ '"file": "' . substitute(ale#path#Winify('app/models/thing.rb'), '\\', '\\\\', 'g') . '",', + \ '"file": "' . substitute(ale#path#Simplify('app/models/thing.rb'), '\\', '\\\\', 'g') . '",', \ '"line": 84,', \ '"link": "http://brakemanscanner.org/docs/warning_types/sql_injection/",', \ '"code": "Thing.connection.execute(params[:data])",', @@ -53,7 +53,7 @@ Execute(The brakeman handler should parse JSON correctly): \ '"fingerprint": "1235",', \ '"check_name": "ModelAttrAccessible",', \ '"message": "Potentially dangerous attribute available for mass assignment",', - \ '"file": "' . substitute(ale#path#Winify('app/models/thing.rb'), '\\', '\\\\', 'g') . '",', + \ '"file": "' . substitute(ale#path#Simplify('app/models/thing.rb'), '\\', '\\\\', 'g') . '",', \ '"line": null,', \ '"link": "http://brakemanscanner.org/docs/warning_types/mass_assignment/",', \ '"code": ":name",', diff --git a/test/handler/test_checkmake_handler.vader b/test/handler/test_checkmake_handler.vader new file mode 100644 index 0000000..e2e1842 --- /dev/null +++ b/test/handler/test_checkmake_handler.vader @@ -0,0 +1,23 @@ +Before: + runtime ale_linters/make/checkmake.vim + +After: + call ale#linter#Reset() + +Execute(Parsing checkmake errors should work): + silent file Makefile + + AssertEqual + \ [ + \ { + \ 'bufnr': 42, + \ 'lnum': 1, + \ 'type': 'E', + \ 'code': 'woops', + \ 'text': 'an error has occurred', + \ } + \ ], + \ ale_linters#make#checkmake#Handle(42, [ + \ 'This shouldnt match', + \ '1:woops:an error has occurred', + \ ]) diff --git a/test/handler/test_checkstyle_handler.vader b/test/handler/test_checkstyle_handler.vader index 0384451..2f1f0f8 100644 --- a/test/handler/test_checkstyle_handler.vader +++ b/test/handler/test_checkstyle_handler.vader @@ -9,17 +9,20 @@ Execute(The checkstyle handler should parse lines correctly): \ [ \ { \ 'lnum': 101, - \ 'text': "'method def rcurly' has incorrect indentation level 4, expected level should be 2. [Indentation]", + \ 'col': 0, + \ 'text': '''method def rcurly'' has incorrect indentation level 4, expected level should be 2.', + \ 'code': 'Indentation', \ 'type': 'W', \ }, \ { \ 'lnum': 63, \ 'col': 3, - \ 'text': "Missing a Javadoc comment. [JavadocMethod]", + \ 'text': 'Missing a Javadoc comment.', + \ 'code': 'JavadocMethod', \ 'type': 'W', \ }, \ ], \ ale_linters#java#checkstyle#Handle(666, [ - \ "[WARN] whatever:101: 'method def rcurly' has incorrect indentation level 4, expected level should be 2. [Indentation]", - \ "[WARN] whatever:63:3: Missing a Javadoc comment. [JavadocMethod]", + \ '[WARN] whatever:101: ''method def rcurly'' has incorrect indentation level 4, expected level should be 2. [Indentation]', + \ '[WARN] whatever:63:3: Missing a Javadoc comment. [JavadocMethod]', \ ]) diff --git a/test/handler/test_clang_handler.vader b/test/handler/test_clang_handler.vader index d28b9eb..278737a 100644 --- a/test/handler/test_clang_handler.vader +++ b/test/handler/test_clang_handler.vader @@ -2,15 +2,11 @@ Execute(clang errors from included files should be parsed correctly): AssertEqual \ [ \ { - \ 'lnum': 3, + \ 'lnum': 1, + \ 'col': 1, + \ 'filename': './b.h', \ 'type': 'E', - \ 'text': 'Problems were found in the header (See :ALEDetail)', - \ 'detail': join([ - \ './b.h:1:1: error: expected identifier or ''(''', - \ '{{{', - \ '^', - \ '1 error generated.', - \ ], "\n"), + \ 'text': 'expected identifier or ''(''', \ }, \ ], \ ale#handlers#gcc#HandleGCCFormat(347, [ diff --git a/test/handler/test_clojure_joker_handler.vader b/test/handler/test_clojure_joker_handler.vader new file mode 100644 index 0000000..460c62e --- /dev/null +++ b/test/handler/test_clojure_joker_handler.vader @@ -0,0 +1,75 @@ +Before: + runtime ale_linters/clojure/joker.vim + +After: + call ale#linter#Reset() + +Execute(the clojure joker handler should be able to handle errors): + AssertEqual + \ [ + \ { + \ 'lnum': 123, + \ 'col': 44, + \ 'type': 'E', + \ 'text': 'Read error: Unexpected )', + \ }, + \ ], + \ ale_linters#clojure#joker#HandleJokerFormat(0, [ + \ 'test.clj:123:44: Read error: Unexpected )', + \ ]) + +Execute(the clojure joker handler should be able to handle warnings): + AssertEqual + \ [ + \ { + \ 'lnum': 654, + \ 'col': 321, + \ 'type': 'W', + \ 'text': 'Parse warning: let form with empty body', + \ } + \ ], + \ ale_linters#clojure#joker#HandleJokerFormat(0, [ + \ 'test.clj:654:321: Parse warning: let form with empty body' + \ ]) + +Execute(the clojure joker handler should be able to handle exceptions): + AssertEqual + \ [ + \ { + \ 'lnum': 123, + \ 'col': 321, + \ 'type': 'E', + \ 'text': 'Exception: something horrible happen', + \ } + \ ], + \ ale_linters#clojure#joker#HandleJokerFormat(0, [ + \ 'test.clj:123:321: Exception: something horrible happen' + \ ]) + +Execute(the clojure joker handler should be able to handle errors from stdin): + AssertEqual + \ [ + \ { + \ 'lnum': 16, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Read error: Unexpected )', + \ }, + \ ], + \ ale_linters#clojure#joker#HandleJokerFormat(0, [ + \ ':16:1: Read error: Unexpected )', + \ ]) + +Execute(the clojure joker handler should be able to handle windows files): + AssertEqual + \ [ + \ { + \ 'lnum': 123, + \ 'col': 44, + \ 'type': 'E', + \ 'text': 'Read error: Unexpected )', + \ } + \ ], + \ ale_linters#clojure#joker#HandleJokerFormat(0, [ + \ 'C:\my\operating\system\is\silly\core.clj:123:44: Read error: Unexpected )', + \ ]) diff --git a/test/handler/test_coffeelint_handler.vader b/test/handler/test_coffeelint_handler.vader index 4426e44..a061f3a 100644 --- a/test/handler/test_coffeelint_handler.vader +++ b/test/handler/test_coffeelint_handler.vader @@ -1,6 +1,11 @@ -Execute(The coffeelint handler should parse lines correctly): +Before: runtime ale_linters/coffee/coffeelint.vim +After: + call ale#linter#Reset() + +Execute(The coffeelint handler should parse lines correctly): + AssertEqual \ [ \ { @@ -13,6 +18,3 @@ Execute(The coffeelint handler should parse lines correctly): \ "path,lineNumber,lineNumberEnd,level,message", \ "stdin,125,,error,Line exceeds maximum allowed length Length is 122, max is 120.", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_common_handlers.vader b/test/handler/test_common_handlers.vader index 65026d8..ee29da3 100644 --- a/test/handler/test_common_handlers.vader +++ b/test/handler/test_common_handlers.vader @@ -5,13 +5,15 @@ Execute(HandleCSSLintFormat should handle CSS errors): \ 'lnum': 2, \ 'col': 1, \ 'type': 'E', - \ 'text': '(errors) Expected RBRACE at line 2, col 1.', + \ 'text': 'Expected RBRACE at line 2, col 1.', + \ 'code': 'errors', \ }, \ { \ 'lnum': 2, \ 'col': 5, \ 'type': 'W', - \ 'text': '(known-properties) Expected ... but found ''wat''.', + \ 'text': 'Expected ... but found ''wat''.', + \ 'code': 'known-properties', \ }, \ ], \ ale#handlers#css#HandleCSSLintFormat(42, [ diff --git a/test/handler/test_cpplint_handler.vader b/test/handler/test_cpplint_handler.vader index 6df84cc..d8d7b8b 100644 --- a/test/handler/test_cpplint_handler.vader +++ b/test/handler/test_cpplint_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/cpp/cpplint.vim +After: + call ale#linter#Reset() + Execute(cpplint warnings from included files should be parsed correctly): AssertEqual @@ -8,20 +11,19 @@ Execute(cpplint warnings from included files should be parsed correctly): \ { \ 'lnum': 5, \ 'col': 0, - \ 'text': ' Estra space after ( in function call [whitespace/parents] [4]', + \ 'text': 'Extra space after ( in function call', + \ 'code': 'whitespace/parents', \ 'type': 'W', \ }, \ { \ 'lnum': 120, \ 'col': 0, - \ 'text': ' At least two spaces is best between code and comments [whitespace/comments] [2]', + \ 'text': 'At least two spaces is best between code and comments', + \ 'code': 'whitespace/comments', \ 'type': 'W', \ }, \ ], \ ale#handlers#cpplint#HandleCppLintFormat(347, [ - \ 'test.cpp:5: Estra space after ( in function call [whitespace/parents] [4]', + \ 'test.cpp:5: Extra space after ( in function call [whitespace/parents] [4]', \ 'keymap_keys.hpp:120: At least two spaces is best between code and comments [whitespace/comments] [2]', \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_credo_handler.vader b/test/handler/test_credo_handler.vader index 73f98ba..5eb0e96 100644 --- a/test/handler/test_credo_handler.vader +++ b/test/handler/test_credo_handler.vader @@ -1,6 +1,10 @@ -Execute(The credo handler should parse lines correctly): +Before: runtime ale_linters/elixir/credo.vim +After: + call ale#linter#Reset() + +Execute(The credo handler should parse lines correctly): AssertEqual \ [ \ { @@ -23,7 +27,3 @@ Execute(The credo handler should parse lines correctly): \ 'lib/filename.ex:1:4: C: There is no whitespace around parentheses/brackets most of the time, but here there is.', \ 'lib/phoenix/channel.ex:26: R: If/else blocks should not have a negated condition in `if`.', \ ]) - -After: - call ale#linter#Reset() - diff --git a/test/handler/test_crystal_handler.vader b/test/handler/test_crystal_handler.vader index 984b976..a7b7f3a 100644 --- a/test/handler/test_crystal_handler.vader +++ b/test/handler/test_crystal_handler.vader @@ -1,5 +1,10 @@ -Execute(The crystal handler should parse lines correctly and add the column if it can): +Before: runtime ale_linters/crystal/crystal.vim + +After: + call ale#linter#Reset() + +Execute(The crystal handler should parse lines correctly and add the column if it can): AssertEqual \ [ \ { @@ -11,6 +16,3 @@ Execute(The crystal handler should parse lines correctly and add the column if i \ ale_linters#crystal#crystal#Handle(255, [ \ '[{"file":"/tmp/test.cr","line":2,"column":1,"size":null,"message":"unexpected token: EOF"}]' \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_dafny_handler.vader b/test/handler/test_dafny_handler.vader new file mode 100644 index 0000000..674f691 --- /dev/null +++ b/test/handler/test_dafny_handler.vader @@ -0,0 +1,28 @@ +Before: + runtime ale_linters/dafny/dafny.vim + +After: + call ale#linter#Reset() + +Execute(The Dafny handler should parse output correctly): + AssertEqual + \ [ + \ { + \ 'bufnr': 0, + \ 'col': 45, + \ 'lnum': 123, + \ 'text': 'A precondition for this call might not hold.', + \ 'type': 'E' + \ }, + \ { + \ 'bufnr': 0, + \ 'col': 90, + \ 'lnum': 678, + \ 'text': 'This is the precondition that might not hold.', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#dafny#dafny#Handle(0, [ + \ 'File.dfy(123,45): Error BP5002: A precondition for this call might not hold.', + \ 'File.dfy(678,90): Related location: This is the precondition that might not hold.' + \ ]) diff --git a/test/handler/test_dogma_handler.vader b/test/handler/test_dogma_handler.vader index ee9795e..ead6d09 100644 --- a/test/handler/test_dogma_handler.vader +++ b/test/handler/test_dogma_handler.vader @@ -1,6 +1,11 @@ -Execute(The dogma handler should parse lines correctly): +Before: runtime ale_linters/elixir/dogma.vim +After: + call ale#linter#Reset() + +Execute(The dogma handler should parse lines correctly): + AssertEqual \ [ \ { @@ -23,6 +28,3 @@ Execute(The dogma handler should parse lines correctly): \ 'lib/filename.ex:18:5: C: Some error', \ 'lib/filename.ex:19:7: R: Some warning', \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_drafter_handler.vader b/test/handler/test_drafter_handler.vader new file mode 100644 index 0000000..1524dde --- /dev/null +++ b/test/handler/test_drafter_handler.vader @@ -0,0 +1,37 @@ +Before: + runtime! ale_linters/apiblueprint/drafter.vim + +After: + call ale#linter#Reset() + +Execute(drafter handler should handle errors output): + AssertEqual + \ [ + \ { + \ 'lnum': 25, + \ 'col': 3, + \ 'text': "unable to parse response signature, expected 'response [] [()]'", + \ 'type': "W", + \ }, + \ { + \ 'lnum': 25, + \ 'col': 3, + \ 'text': "missing response HTTP status code, assuming 'Response 200'", + \ 'type': "W", + \ }, + \ { + \ 'lnum': 30, + \ 'col': 7, + \ 'end_lnum': 32, + \ 'end_col': 7, + \ 'text': "message-body asset is expected to be a pre-formatted code block, separate it by a newline and indent every of its line by 12 spaces or 3 tabs", + \ 'type': "W", + \ }, + \ ], + \ ale_linters#apiblueprint#drafter#HandleErrors(bufnr(''), [ + \ "", + \ "OK.", + \ "warning: (3) unable to parse response signature, expected 'response [] [()]'; line 25, column 3 - line 25, column 29", + \ "warning: (6) missing response HTTP status code, assuming 'Response 200'; line 25, column 3 - line 25, column 29", + \ "warning: (10) message-body asset is expected to be a pre-formatted code block, separate it by a newline and indent every of its line by 12 spaces or 3 tabs; line 30, column 7 - line 30, column 11; line 31, column 6 - line 31, column 7; line 32, column 6 - line 32, column 7" + \ ]) diff --git a/test/handler/test_embertemplatelint_handler.vader b/test/handler/test_embertemplatelint_handler.vader index 8e132d3..97ca439 100644 --- a/test/handler/test_embertemplatelint_handler.vader +++ b/test/handler/test_embertemplatelint_handler.vader @@ -1,8 +1,10 @@ " Author: Adrian Zalewski - Before: runtime ale_linters/handlebars/embertemplatelint.vim +After: + call ale#linter#Reset() + Execute(The ember-template-lint handler should parse lines correctly): let input_lines = split('{ \ "/ember-project/app/templates/application.hbs": [ @@ -30,14 +32,12 @@ Execute(The ember-template-lint handler should parse lines correctly): AssertEqual \ [ \ { - \ 'bufnr': 347, \ 'lnum': 1, \ 'col': 10, \ 'text': 'bare-strings: Non-translated string used', \ 'type': 'E', \ }, \ { - \ 'bufnr': 347, \ 'lnum': 3, \ 'col': 6, \ 'text': 'invalid-interactive: Interaction added to non-interactive element', @@ -53,8 +53,8 @@ Execute(The ember-template-lint handler should handle template parsing error cor \ "fatal": true, \ "moduleId": "app/templates/application", \ "message": "Parse error on line 5 ...", - \ "line": 1, - \ "column": 1, + \ "line": 5, + \ "column": 3, \ "source": "Error: Parse error on line 5 ...", \ "severity": 2 \ } @@ -64,9 +64,8 @@ Execute(The ember-template-lint handler should handle template parsing error cor AssertEqual \ [ \ { - \ 'bufnr': 347, - \ 'lnum': 1, - \ 'col': 1, + \ 'lnum': 5, + \ 'col': 3, \ 'text': 'Parse error on line 5 ...', \ 'type': 'E', \ }, @@ -80,6 +79,3 @@ Execute(The ember-template-lint handler should handle no lint errors/warnings): AssertEqual \ [], \ ale_linters#handlebars#embertemplatelint#Handle(347, ['{}']) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_eslint_handler.vader b/test/handler/test_eslint_handler.vader index 943e177..2e8bfd2 100644 --- a/test/handler/test_eslint_handler.vader +++ b/test/handler/test_eslint_handler.vader @@ -1,11 +1,15 @@ Before: Save g:ale_javascript_eslint_suppress_eslintignore + Save g:ale_javascript_eslint_suppress_missing_config let g:ale_javascript_eslint_suppress_eslintignore = 0 + let g:ale_javascript_eslint_suppress_missing_config = 0 After: Restore + unlet! b:ale_javascript_eslint_suppress_eslintignore + unlet! b:ale_javascript_eslint_suppress_missing_config unlet! g:config_error_lines Execute(The eslint handler should parse lines correctly): @@ -14,13 +18,15 @@ Execute(The eslint handler should parse lines correctly): \ { \ 'lnum': 47, \ 'col': 14, - \ 'text': 'Missing trailing comma. [Warning/comma-dangle]', + \ 'text': 'Missing trailing comma.', + \ 'code': 'comma-dangle', \ 'type': 'W', \ }, \ { \ 'lnum': 56, \ 'col': 41, - \ 'text': 'Missing semicolon. [Error/semi]', + \ 'text': 'Missing semicolon.', + \ 'code': 'semi', \ 'type': 'E', \ }, \ { @@ -30,7 +36,7 @@ Execute(The eslint handler should parse lines correctly): \ 'type': 'E', \ }, \ ], - \ ale#handlers#eslint#Handle(347, [ + \ ale#handlers#eslint#Handle(bufnr(''), [ \ 'This line should be ignored completely', \ '/path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle]', \ '/path/to/some-filename.js:56:41: Missing semicolon. [Error/semi]', @@ -58,7 +64,26 @@ Execute(The eslint handler should print a message about a missing configuration \ 'text': 'eslint configuration error (type :ALEDetail for more information)', \ 'detail': join(g:config_error_lines, "\n"), \ }], - \ ale#handlers#eslint#Handle(347, g:config_error_lines[:]) + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) + +Execute(The eslint handler should allow the missing config error to be suppressed): + let b:ale_javascript_eslint_suppress_missing_config = 1 + let g:config_error_lines = [ + \ '', + \ 'Oops! Something went wrong! :(', + \ '', + \ 'ESLint couldn''t find a configuration file. To set up a configuration file for this project, please run:', + \ ' eslint --init', + \ '', + \ 'ESLint looked for configuration files in /some/path/or/other and its ancestors.', + \ '', + \ 'If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://gitter.im/eslint/eslint', + \ '', + \ ] + + AssertEqual + \ [], + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) Execute(The eslint handler should print a message for config parsing errors): let g:config_error_lines = [ @@ -86,7 +111,36 @@ Execute(The eslint handler should print a message for config parsing errors): \ 'text': 'eslint configuration error (type :ALEDetail for more information)', \ 'detail': join(g:config_error_lines, "\n"), \ }], - \ ale#handlers#eslint#Handle(347, g:config_error_lines[:]) + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) + +Execute(Suppressing missing configs shouldn't suppress parsing errors): + let b:ale_javascript_eslint_suppress_missing_config = 1 + let g:config_error_lines = [ + \ 'Cannot read config file: /some/path/or/other/.eslintrc.js', + \ 'Error: Unexpected token <<', + \ '/some/path/or/other/.eslintrc.js:1', + \ '(function (exports, require, module, __filename, __dirname) { <<<>>>', + \ ' ^^', + \ 'SyntaxError: Unexpected token <<', + \ ' at Object.exports.runInThisContext (vm.js:76:16)', + \ ' at Module._compile (module.js:528:28)', + \ ' at Object.Module._extensions..js (module.js:565:10)', + \ ' at Module.load (module.js:473:32)', + \ ' at tryModuleLoad (module.js:432:12)', + \ ' at Function.Module._load (module.js:424:3)', + \ ' at Module.require (module.js:483:17)', + \ ' at require (internal/module.js:20:19)', + \ ' at module.exports (/usr/local/lib/node_modules/eslint/node_modules/require-uncached/index.js:14:12)', + \ ' at loadJSConfigFile (/usr/local/lib/node_modules/eslint/lib/config/config-file.js:160:16)', + \] + + AssertEqual + \ [{ + \ 'lnum': 1, + \ 'text': 'eslint configuration error (type :ALEDetail for more information)', + \ 'detail': join(g:config_error_lines, "\n"), + \ }], + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) Execute(The eslint handler should print a message for invalid configuration settings): let g:config_error_lines = [ @@ -116,7 +170,38 @@ Execute(The eslint handler should print a message for invalid configuration sett \ 'text': 'eslint configuration error (type :ALEDetail for more information)', \ 'detail': join(g:config_error_lines, "\n"), \ }], - \ ale#handlers#eslint#Handle(347, g:config_error_lines[:]) + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) + +Execute(Suppressing missing configs shouldn't suppress invalid config errors): + let b:ale_javascript_eslint_suppress_missing_config = 1 + let g:config_error_lines = [ + \ '/home/w0rp/git/wazoku/wazoku-spotlight/.eslintrc.js:', + \ ' Configuration for rule "indent" is invalid:', + \ ' Value "off" is the wrong type.', + \ '', + \ 'Error: /home/w0rp/git/wazoku/wazoku-spotlight/.eslintrc.js:', + \ ' Configuration for rule "indent" is invalid:', + \ ' Value "off" is the wrong type.', + \ '', + \ ' at validateRuleOptions (/usr/local/lib/node_modules/eslint/lib/config/config-validator.js:115:15)', + \ ' at /usr/local/lib/node_modules/eslint/lib/config/config-validator.js:162:13', + \ ' at Array.forEach (native)', + \ ' at Object.validate (/usr/local/lib/node_modules/eslint/lib/config/config-validator.js:161:35)', + \ ' at Object.load (/usr/local/lib/node_modules/eslint/lib/config/config-file.js:522:19)', + \ ' at loadConfig (/usr/local/lib/node_modules/eslint/lib/config.js:63:33)', + \ ' at getLocalConfig (/usr/local/lib/node_modules/eslint/lib/config.js:130:29)', + \ ' at Config.getConfig (/usr/local/lib/node_modules/eslint/lib/config.js:256:22)', + \ ' at processText (/usr/local/lib/node_modules/eslint/lib/cli-engine.js:224:33)', + \ ' at CLIEngine.executeOnText (/usr/local/lib/node_modules/eslint/lib/cli-engine.js:756:26)', + \] + + AssertEqual + \ [{ + \ 'lnum': 1, + \ 'text': 'eslint configuration error (type :ALEDetail for more information)', + \ 'detail': join(g:config_error_lines, "\n"), + \ }], + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) Execute(The eslint handler should print a message when import is not used in a module): let g:config_error_lines = [ @@ -140,7 +225,33 @@ Execute(The eslint handler should print a message when import is not used in a m \ 'text': 'eslint configuration error (type :ALEDetail for more information)', \ 'detail': join(g:config_error_lines, "\n"), \ }], - \ ale#handlers#eslint#Handle(347, g:config_error_lines[:]) + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) + +Execute(Suppressing missing configs shouldn't suppress module import errors): + let b:ale_javascript_eslint_suppress_missing_config = 1 + let g:config_error_lines = [ + \ 'ImportDeclaration should appear when the mode is ES6 and in the module context.', + \ 'AssertionError: ImportDeclaration should appear when the mode is ES6 and in the module context.', + \ ' at Referencer.ImportDeclaration (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint-scope/lib/referencer.js:597:9)', + \ ' at Referencer.Visitor.visit (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/esrecurse/esrecurse.js:122:34)', + \ ' at Referencer.Visitor.visitChildren (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/esrecurse/esrecurse.js:101:38)', + \ ' at Referencer.Program (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint-scope/lib/referencer.js:449:14)', + \ ' at Referencer.Visitor.visit (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/esrecurse/esrecurse.js:122:34)', + \ ' at Object.analyze (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint-scope/lib/index.js:138:16)', + \ ' at EventEmitter.module.exports.api.verify (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint/lib/eslint.js:887:40)', + \ ' at processText (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint/lib/cli-engine.js:278:31)', + \ ' at CLIEngine.executeOnText (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint/lib/cli-engine.js:734:26)', + \ ' at Object.execute (/home/w0rp/git/wazoku/wazoku-spotlight/spotlight/static/node_modules/eslint/lib/cli.js:171:42) ', + \] + + AssertEqual + \ [{ + \ 'lnum': 1, + \ 'text': 'eslint configuration error (type :ALEDetail for more information)', + \ 'detail': join(g:config_error_lines, "\n"), + \ }], + \ ale#handlers#eslint#Handle(bufnr(''), g:config_error_lines[:]) + Execute(The eslint handler should output end_col values where appropriate): AssertEqual @@ -149,46 +260,51 @@ Execute(The eslint handler should output end_col values where appropriate): \ 'lnum': 4, \ 'col': 3, \ 'end_col': 15, - \ 'text': 'Parsing error: Unexpected token ''some string'' [Error]', + \ 'text': 'Parsing error: Unexpected token ''some string''', \ 'type': 'E', \ }, \ { \ 'lnum': 70, \ 'col': 3, \ 'end_col': 5, - \ 'text': '''foo'' is not defined. [Error/no-undef]', + \ 'text': '''foo'' is not defined.', + \ 'code': 'no-undef', \ 'type': 'E', \ }, \ { \ 'lnum': 71, \ 'col': 2, \ 'end_col': 6, - \ 'text': 'Unexpected `await` inside a loop. [Error/no-await-in-loop]', + \ 'text': 'Unexpected `await` inside a loop.', + \ 'code': 'no-await-in-loop', \ 'type': 'E', \ }, \ { \ 'lnum': 72, \ 'col': 6, \ 'end_col': 10, - \ 'text': 'Redundant use of `await` on a return value. [Error/no-return-await]', + \ 'text': 'Redundant use of `await` on a return value.', + \ 'code': 'no-return-await', \ 'type': 'E', \ }, \ { \ 'lnum': 73, \ 'col': 4, \ 'end_col': 10, - \ 'text': 'Unexpected console statement [Error/no-console]', + \ 'text': 'Unexpected console statement', + \ 'code': 'no-console', \ 'type': 'E', \ }, \ { \ 'lnum': 74, \ 'col': 4, \ 'end_col': 11, - \ 'text': 'Unexpected ''debugger'' statement. [Error/no-debugger]', + \ 'text': 'Unexpected ''debugger'' statement.', + \ 'code': 'no-debugger', \ 'type': 'E', \ }, \ ], - \ ale#handlers#eslint#Handle(347, [ + \ ale#handlers#eslint#Handle(bufnr(''), [ \ 'app.js:4:3: Parsing error: Unexpected token ''some string'' [Error]', \ 'app.js:70:3: ''foo'' is not defined. [Error/no-undef]', \ 'app.js:71:2: Unexpected `await` inside a loop. [Error/no-await-in-loop]', @@ -206,7 +322,7 @@ Execute(The eslint hint about using typescript-eslint-parser): \ 'lnum': 451, \ 'col': 2, \ 'end_col': 2, - \ 'text': 'Parsing error (You may need configure typescript-eslint-parser): Unexpected token ) [Error]', + \ 'text': 'Parsing error (You may need configure typescript-eslint-parser): Unexpected token )', \ 'type': 'E', \ }, \ ], @@ -220,9 +336,9 @@ Execute(eslint should warn about ignored files by default): \ 'lnum': 0, \ 'col': 0, \ 'type': 'W', - \ 'text': 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]' + \ 'text': 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.' \ }], - \ ale#handlers#eslint#Handle(347, [ + \ ale#handlers#eslint#Handle(bufnr(''), [ \ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]', \ ]) @@ -231,6 +347,21 @@ Execute(eslint should not warn about ignored files when explicitly disabled): AssertEqual \ [], - \ ale#handlers#eslint#Handle(347, [ + \ ale#handlers#eslint#Handle(bufnr(''), [ \ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]', \ ]) + +Execute(eslint should handle react errors correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 59, + \ 'col': 9, + \ 'type': 'E', + \ 'text': 'Property should be placed on the same line as the component declaration', + \ 'code': 'react/jsx-first-prop-new-line', + \ }, + \ ], + \ 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]', + \ ]) diff --git a/test/handler/test_fish_handler.vader b/test/handler/test_fish_handler.vader new file mode 100644 index 0000000..567952e --- /dev/null +++ b/test/handler/test_fish_handler.vader @@ -0,0 +1,39 @@ +Before: + runtime ale_linters/fish/fish.vim + +After: + call ale#linter#Reset() + +Execute(The fish handler should handle basic warnings and syntax errors): + AssertEqual + \ [ + \ { + \ 'lnum': 20, + \ 'col': 23, + \ 'text': "Unsupported use of '||'. In fish, please use 'COMMAND; or COMMAND'.", + \ }, + \ { + \ 'lnum': 26, + \ 'col': 7, + \ 'text': "Illegal command name '(prompt_pwd)'", + \ }, + \ { + \ 'lnum': 36, + \ 'col': 1, + \ 'text': "'end' outside of a block", + \ }, + \ ], + \ ale_linters#fish#fish#Handle(1, [ + \ "fish_prompt.fish (line 20): Unsupported use of '||'. In fish, please use 'COMMAND; or COMMAND'.", + \ 'if set -q SSH_CLIENT || set -q SSH_TTY', + \ ' ^', + \ "fish_prompt.fish (line 26): Illegal command name '(prompt_pwd)'", + \ ' (prompt_pwd) \', + \ ' ^', + \ "fish_prompt.fish (line 36): 'end' outside of a block", + \ 'end', + \ '^', + \ 'config.fish (line 45):', + \ "abbr --add p 'cd ~/Projects'", + \ '^', + \ ]) diff --git a/test/handler/test_flake8_handler.vader b/test/handler/test_flake8_handler.vader index 0d6d65f..efacdfb 100644 --- a/test/handler/test_flake8_handler.vader +++ b/test/handler/test_flake8_handler.vader @@ -1,8 +1,19 @@ Before: - runtime ale_linters/python/flake8.vim + Save g:ale_warn_about_trailing_blank_lines + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_blank_lines = 1 + let g:ale_warn_about_trailing_whitespace = 1 + + runtime ale_linters/python/flake8.vim After: - call ale#linter#Reset() + Restore + + unlet! b:ale_warn_about_trailing_blank_lines + unlet! b:ale_warn_about_trailing_whitespace + + call ale#linter#Reset() Execute(The flake8 handler should handle basic warnings and syntax errors): AssertEqual @@ -11,21 +22,24 @@ Execute(The flake8 handler should handle basic warnings and syntax errors): \ 'lnum': 6, \ 'col': 6, \ 'type': 'E', - \ 'text': 'E111: indentation is not a multiple of four', + \ 'text': 'indentation is not a multiple of four', + \ 'code': 'E111', \ 'sub_type': 'style', \ }, \ { \ 'lnum': 7, \ 'col': 6, \ 'type': 'W', - \ 'text': 'W123: some warning', + \ 'text': 'some warning', + \ 'code': 'W123', \ 'sub_type': 'style', \ }, \ { \ 'lnum': 8, \ 'col': 3, \ 'type': 'E', - \ 'text': 'E999: SyntaxError: invalid syntax', + \ 'text': 'SyntaxError: invalid syntax', + \ 'code': 'E999', \ }, \ ], \ ale_linters#python#flake8#Handle(1, [ @@ -34,7 +48,7 @@ Execute(The flake8 handler should handle basic warnings and syntax errors): \ 'stdin:8:3: E999 SyntaxError: invalid syntax', \ ]) -Execute(The flake8 handler should set end column indexes should be set for certain errors): +Execute(The flake8 handler should set end column indexes for certain errors): AssertEqual \ [ \ { @@ -42,35 +56,40 @@ Execute(The flake8 handler should set end column indexes should be set for certa \ 'col': 1, \ 'type': 'E', \ 'end_col': 3, - \ 'text': 'F821: undefined name ''foo''', + \ 'text': 'undefined name ''foo''', + \ 'code': 'F821', \ }, \ { \ 'lnum': 28, \ 'col': 5, \ 'type': 'E', \ 'end_col': 9, - \ 'text': 'F405: hello may be undefined, or defined from star imports: x', + \ 'text': 'hello may be undefined, or defined from star imports: x', + \ 'code': 'F405', \ }, \ { \ 'lnum': 104, \ 'col': 5, \ 'type': 'E', \ 'end_col': 12, - \ 'text': 'F999: ''continue'' not properly in loop', + \ 'text': '''continue'' not properly in loop', + \ 'code': 'F999', \ }, \ { \ 'lnum': 106, \ 'col': 5, \ 'type': 'E', \ 'end_col': 9, - \ 'text': 'F999: ''break'' outside loop', + \ 'text': '''break'' outside loop', + \ 'code': 'F999', \ }, \ { \ 'lnum': 109, \ 'col': 5, \ 'type': 'E', \ 'end_col': 8, - \ 'text': 'F841: local variable ''test'' is assigned to but never used', + \ 'text': 'local variable ''test'' is assigned to but never used', + \ 'code': 'F841', \ }, \ ], \ ale_linters#python#flake8#Handle(1, [ @@ -118,17 +137,110 @@ Execute(The flake8 handler should handle stack traces): \ 'ImportError: No module named parser', \ ]) -Execute (The flake8 handler should handle names with spaces): +Execute(The flake8 handler should handle names with spaces): AssertEqual \ [ \ { \ 'lnum': 6, \ 'col': 6, \ 'type': 'E', - \ 'text': 'E111: indentation is not a multiple of four', + \ 'text': 'indentation is not a multiple of four', + \ 'code': 'E111', \ 'sub_type': 'style', \ }, \ ], \ ale_linters#python#flake8#Handle(42, [ \ 'C:\something\with spaces.py:6:6: E111 indentation is not a multiple of four', \ ]) + +Execute(Warnings about trailing whitespace should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W291', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W293', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Disabling trailing whitespace warnings should work): + let b:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Warnings about trailing blank lines should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W391', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'blank line at end of file', + \ }, + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(Disabling trailing blank line warnings should work): + let b:ale_warn_about_trailing_blank_lines = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(F401 should be a warning): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'F401', + \ 'type': 'W', + \ 'text': 'module imported but unused', + \ }, + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: F401 module imported but unused', + \ ]) + +Execute(E112 should be a syntax error): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'E112', + \ 'type': 'E', + \ 'text': 'expected an indented block', + \ }, + \ ], + \ ale_linters#python#flake8#Handle(bufnr(''), [ + \ 'foo.py:6:1: E112 expected an indented block', + \ ]) diff --git a/test/handler/test_flow_handler.vader b/test/handler/test_flow_handler.vader index 47efc30..3a575a0 100644 --- a/test/handler/test_flow_handler.vader +++ b/test/handler/test_flow_handler.vader @@ -243,7 +243,7 @@ Execute(The flow handler should fetch the correct location for the currently ope \ 'lnum': 6, \ 'col': 3, \ 'type': 'E', - \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`' + \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`', \ } \] @@ -347,7 +347,159 @@ Execute(The flow handler should handle relative paths): \ 'lnum': 6, \ 'col': 3, \ 'type': 'E', - \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`' + \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`', + \ } + \] + + AssertEqual g:expected, g:actual + +Execute(The flow handler should handle extra errors): + silent! noautocmd file /Users/rav/Projects/vim-ale-flow/index.js + + let g:flow_output = { + \ "flowVersion": "0.54.0", + \ "errors": [{ + \ "extra": [{ + \ "message": [{ + \ "context": v:null, + \ "descr": "Property \`setVector\` is incompatible:", + \ "type": "Blame ", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }], + \ "children": [{ + \ "message": [{ + \ "context": "setVector = \{2\}", + \ "descr": "number ", + \ "type": "Blame ", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile ", + \ "start": { + \ "line": 90, + \ "column": 30, + \ "offset": 2296 + \ }, + \ "end": { + \ "line": 90, + \ "column": 30, + \ "offset": 2297 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 90, + \ "endline": 90, + \ "start": 30, + \ "end": 30 + \ }, { + \ "context": v:null, + \ "descr": "This type is incompatible with ", + \ "type": "Comment ", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }, { + \ "context": "setVector: VectorType => void,", + \ "descr": "function type ", + \ "type": "Blame ", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile", + \ "start": { + \ "line": 9, + \ "column": 14, + \ "offset": 252 + \ }, + \ "end": { + \ "line": 9, + \ "column": 31, + \ "offset": 270 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 9, + \ "endline": 9, + \ "start": 14, + \ "end": 31 + \ }] + \ }] + \ }], + \ "kind": "infer", + \ "level": "error", + \ "suppressions": [], + \ "message": [{ + \ "context": " < New ", + \ "descr": "props of React element `New`", + \ "type": "Blame", + \ "loc": { + \ "source": "vim-ale-flow/foo.js", + \ "type": "SourceFile", + \ "start": { + \ "line": 89, + \ "column": 17, + \ "offset": 2262 + \ }, + \ "end": { + \ "line": 94, + \ "column": 18, + \ "offset": 2488 + \ } + \ }, + \ "path": "", + \ "line": 89, + \ "endline": 94, + \ "start": 17, + \ "end": 18 + \ }, { + \ "context": v:null, + \ "descr": "This type is incompatible with", + \ "type": "Comment", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }, { + \ "context": "class New extends React.Component < NewProps,NewState > {", + \ "descr": "object type", + \ "type": "Blame", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile", + \ "start": { + \ "line": 20, + \ "column": 35, + \ "offset": 489 + \ }, + \ "end": { + \ "line": 20, + \ "column": 42, + \ "offset": 497 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 20, + \ "endline": 20, + \ "start": 35, + \ "end": 42 + \ }] + \ }], + \ "passed": v:false + \} + + let g:actual = ale_linters#javascript#flow#Handle(bufnr(''), [json_encode(g:flow_output)]) + let g:expected = [ + \ { + \ 'lnum': 20, + \ 'col': 35, + \ 'type': 'E', + \ 'text': 'props of React element `New`: This type is incompatible with object type', + \ 'detail': 'Property `setVector` is incompatible: number This type is incompatible with function type ', \ } \] diff --git a/test/handler/test_foodcritic_handler.vader b/test/handler/test_foodcritic_handler.vader new file mode 100644 index 0000000..67cb6ca --- /dev/null +++ b/test/handler/test_foodcritic_handler.vader @@ -0,0 +1,44 @@ +Before: + runtime ale_linters/chef/foodcritic.vim + +After: + call ale#linter#Reset() + +Execute(Basic warnings should be handled): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'code': 'CINK001', + \ 'type': 'W', + \ 'text': 'Missing CHANGELOG in markdown format', + \ 'filename': '/foo/bar/CHANGELOG.md', + \ }, + \ { + \ 'lnum': 1, + \ 'code': 'FC011', + \ 'type': 'W', + \ 'text': 'Missing README in markdown format', + \ 'filename': '/foo/bar/README.md', + \ }, + \ { + \ 'lnum': 1, + \ 'code': 'FC031', + \ 'type': 'W', + \ 'text': 'Cookbook without metadata.rb file', + \ 'filename': '/foo/bar/metadata.rb', + \ }, + \ { + \ 'lnum': 1, + \ 'code': 'FC071', + \ 'type': 'W', + \ 'text': 'Missing LICENSE file', + \ 'filename': '/foo/bar/LICENSE', + \ }, + \ ], + \ ale_linters#chef#foodcritic#Handle(bufnr(''), [ + \ 'CINK001: Missing CHANGELOG in markdown format: /foo/bar/CHANGELOG.md:1', + \ 'FC011: Missing README in markdown format: /foo/bar/README.md:1', + \ 'FC031: Cookbook without metadata.rb file: /foo/bar/metadata.rb:1', + \ 'FC071: Missing LICENSE file: /foo/bar/LICENSE:1', + \ ]) diff --git a/test/handler/test_fortran_handler.vader b/test/handler/test_fortran_handler.vader index acd83e3..c55a4c6 100644 --- a/test/handler/test_fortran_handler.vader +++ b/test/handler/test_fortran_handler.vader @@ -1,6 +1,10 @@ -Execute(The fortran handler should parse lines from GCC 4.1.2 correctly): +Before: runtime ale_linters/fortran/gcc.vim +After: + call ale#linter#Reset() + +Execute(The fortran handler should parse lines from GCC 4.1.2 correctly): AssertEqual \ [ \ { @@ -31,13 +35,8 @@ Execute(The fortran handler should parse lines from GCC 4.1.2 correctly): \ "Error: Symbol ‘a’ at (1) has no IMPLICIT type", \ ]) -After: - call ale#linter#Reset() - Execute(The fortran handler should parse lines from GCC 4.9.3 correctly): - runtime ale_linters/fortran/gcc.vim - AssertEqual \ [ \ { @@ -68,14 +67,7 @@ Execute(The fortran handler should parse lines from GCC 4.9.3 correctly): \ "Error: Symbol ‘b’ at (1) has no IMPLICIT type", \ ]) -After: - call ale#linter#Reset() - - - Execute(The fortran handler should parse lines from GCC 6.3.1 correctly): - runtime ale_linters/fortran/gcc.vim - AssertEqual \ [ \ { @@ -101,6 +93,3 @@ Execute(The fortran handler should parse lines from GCC 6.3.1 correctly): \ "", \ "Error: Symbol ‘b’ at (1) has no IMPLICIT type", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_gcc_handler.vader b/test/handler/test_gcc_handler.vader index 2f60390..79f1789 100644 --- a/test/handler/test_gcc_handler.vader +++ b/test/handler/test_gcc_handler.vader @@ -1,15 +1,21 @@ +Execute(The GCC handler should ignore other lines of output): + AssertEqual + \ [], + \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ 'foo', + \ 'bar', + \ 'baz', + \ ]) + Execute(GCC errors from included files should be parsed correctly): AssertEqual \ [ \ { - \ 'lnum': 3, + \ 'lnum': 1, + \ 'col': 1, + \ 'filename': 'broken.h', \ 'type': 'E', - \ 'text': 'Problems were found in the header (See :ALEDetail)', - \ 'detail': join([ - \ 'broken.h:1:1: error: expected identifier or ''('' before ''{'' token', - \ ' {{{', - \ ' ^', - \ ], "\n"), + \ 'text': 'expected identifier or ''('' before ''{'' token', \ }, \ ], \ ale#handlers#gcc#HandleGCCFormat(347, [ @@ -22,14 +28,11 @@ Execute(GCC errors from included files should be parsed correctly): AssertEqual \ [ \ { - \ 'lnum': 3, + \ 'lnum': 1, + \ 'col': 1, + \ 'filename': 'b.h', \ 'type': 'E', - \ 'text': 'Problems were found in the header (See :ALEDetail)', - \ 'detail': join([ - \ 'b.h:1:1: error: expected identifier or ''('' before ''{'' token', - \ ' {{{', - \ ' ^', - \ ], "\n"), + \ 'text': 'expected identifier or ''('' before ''{'' token', \ }, \ ], \ ale#handlers#gcc#HandleGCCFormat(347, [ @@ -43,17 +46,18 @@ Execute(GCC errors from included files should be parsed correctly): AssertEqual \ [ \ { - \ 'lnum': 3, + \ 'lnum': 1, + \ 'col': 1, + \ 'filename': 'b.h', \ 'type': 'E', - \ 'text': 'Problems were found in the header (See :ALEDetail)', - \ 'detail': join([ - \ 'b.h:1:1: error: unknown type name ‘bad_type’', - \ ' bad_type x;', - \ ' ^', - \ 'b.h:2:1: error: unknown type name ‘other_bad_type’', - \ ' other_bad_type y;', - \ ' ^', - \ ], "\n"), + \ 'text': 'unknown type name ''bad_type''', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'filename': 'b.h', + \ 'type': 'E', + \ 'text': 'unknown type name ''other_bad_type''', \ }, \ ], \ ale#handlers#gcc#HandleGCCFormat(347, [ @@ -67,17 +71,6 @@ Execute(GCC errors from included files should be parsed correctly): \ ' ^', \ ]) -Execute(GCC versions should be parsed correctly): - AssertEqual [4, 9, 1], ale#handlers#gcc#ParseGCCVersion([ - \ 'g++ (GCC) 4.9.1 20140922 (Red Hat 4.9.1-10)', - \]) - AssertEqual [4, 8, 4], ale#handlers#gcc#ParseGCCVersion([ - \ 'gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4', - \ 'Copyright (C) 2013 Free Software Foundation, Inc.', - \ 'This is free software; see the source for copying conditions. There is NO', - \ 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.', - \]) - Execute(The GCC handler shouldn't complain about #pragma once for headers): silent file! test.h @@ -133,3 +126,25 @@ Execute(The GCC handler should handle syntax errors): \ ':4: error: ''cat'' was not declared in this scope', \ ':12: error: expected `;'' before ''o''', \ ]) + +Execute(The GCC handler should handle notes with no previous message): + AssertEqual + \ [], + \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ':1:1: note: x', + \ ':1:1: note: x', + \ ]) + +Execute(The GCC handler should interpret - as being the current file): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 12, + \ 'type': 'E', + \ 'text': 'Some error', + \ }, + \ ], + \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ '-:6:12: error: Some error', + \ ]) diff --git a/test/handler/test_ghc_handler.vader b/test/handler/test_ghc_handler.vader index b76046c..b040a23 100644 --- a/test/handler/test_ghc_handler.vader +++ b/test/handler/test_ghc_handler.vader @@ -8,6 +8,10 @@ Execute(The ghc handler should handle hdevtools output): \ 'type': 'W', \ 'col': 62, \ 'text': '• Couldnt match type ‘a -> T.Text’ with ‘T.Text’ Expected type: [T.Text]', + \ 'detail': join([ + \ '• Couldnt match type ‘a -> T.Text’ with ‘T.Text’', + \ ' Expected type: [T.Text]', + \ ], "\n"), \ }, \ ], \ ale#handlers#haskell#HandleGHCFormat(bufnr(''), [ @@ -26,21 +30,29 @@ Execute(The ghc handler should handle ghc 8 output): \ 'type': 'E', \ 'col': 1, \ 'text': 'Failed to load interface for ‘GitHub.Data’ Use -v to see a list of the files searched for.', + \ 'detail': join([ + \ ' Failed to load interface for ‘GitHub.Data’', + \ ' Use -v to see a list of the files searched for.', + \ ], "\n"), \ }, \ { \ 'lnum': 7, \ 'type': 'W', \ 'col': 1, \ 'text': 'Failed to load interface for ‘GitHub.Endpoints.PullRequests’ Use -v to see a list of the files searched for.', + \ 'detail': join([ + \ ' Failed to load interface for ‘GitHub.Endpoints.PullRequests’', + \ ' Use -v to see a list of the files searched for.', + \ ], "\n"), \ }, \ ], \ ale#handlers#haskell#HandleGHCFormat(bufnr(''), [ \ '', - \ ale#path#Winify('src/Appoint/Lib.hs') . ':6:1: error:', + \ ale#path#Simplify('src/Appoint/Lib.hs') . ':6:1: error:', \ ' Failed to load interface for ‘GitHub.Data’', \ ' Use -v to see a list of the files searched for.', \ '', - \ ale#path#Winify('src/Appoint/Lib.hs') . ':7:1: warning:', + \ ale#path#Simplify('src/Appoint/Lib.hs') . ':7:1: warning:', \ ' Failed to load interface for ‘GitHub.Endpoints.PullRequests’', \ ' Use -v to see a list of the files searched for.', \ ]) @@ -55,24 +67,66 @@ Execute(The ghc handler should handle ghc 7 output): \ 'type': 'E', \ 'col': 1, \ 'text': 'parse error (possibly incorrect indentation or mismatched brackets)', + \ 'detail': join([ + \ ' parse error (possibly incorrect indentation or mismatched brackets)', + \ ], "\n"), \ }, \ { \ 'lnum': 84, \ 'col': 1, \ 'type': 'W', - \ 'text': 'Top-level binding with no type signature:^@ myLayout :: Choose Tall (Choose (Mirror Tall) Full) a', + \ 'text': 'Top-level binding with no type signature: myLayout :: Choose Tall (Choose (Mirror Tall) Full) a', + \ 'detail': join([ + \ ' Top-level binding with no type signature:', + \ ' myLayout :: Choose Tall (Choose (Mirror Tall) Full) a', + \ ], "\n"), \ }, \ { \ 'lnum': 94, \ 'col': 5, \ 'type': 'E', \ 'text': 'Some other error', + \ 'detail': join([ + \ ' Some other error', + \ ], "\n"), \ }, \ ], \ ale#handlers#haskell#HandleGHCFormat(bufnr(''), [ - \ ale#path#Winify('src/Main.hs') . ':168:1:', + \ ale#path#Simplify('src/Main.hs') . ':168:1:', \ ' parse error (possibly incorrect indentation or mismatched brackets)', - \ ale#path#Winify('src/Main.hs') . ':84:1:Warning: Top-level binding with no type signature:^@ myLayout :: Choose Tall (Choose (Mirror Tall) Full) a', - \ ale#path#Winify('src/Main.hs') . ':94:5:Error:', + \ ale#path#Simplify('src/Main.hs') . ':84:1:Warning:', + \ ' Top-level binding with no type signature:', + \ ' myLayout :: Choose Tall (Choose (Mirror Tall) Full) a', + \ ale#path#Simplify('src/Main.hs') . ':94:5:Error:', \ ' Some other error', \ ]) + +Execute(The ghc handler should handle stack 1.5.1 output): + call ale#test#SetFilename('src/Main.hs') + + AssertEqual + \ [ + \ { + \ 'lnum': 160, + \ 'col': 14, + \ 'type': 'E', + \ 'text': '• Expecting one fewer arguments to ‘Exp’ Expected kind ‘k0 -> *’, but ‘Exp’ has kind ‘*’ • In the type ‘Exp a’ | 160 | pattern F :: Exp a | ^^^^^', + \ 'detail': join([ + \ ' • Expecting one fewer arguments to ‘Exp’', + \ ' Expected kind ‘k0 -> *’, but ‘Exp’ has kind ‘*’', + \ ' • In the type ‘Exp a’', + \ ' |', + \ ' 160 | pattern F :: Exp a', + \ ' | ^^^^^', + \ ], "\n"), + \ }, + \ ], + \ ale#handlers#haskell#HandleGHCFormat(bufnr(''), [ + \ ' ' . ale#path#Simplify('src/Main.hs') . ':160:14: error:', + \ ' • Expecting one fewer arguments to ‘Exp’', + \ ' Expected kind ‘k0 -> *’, but ‘Exp’ has kind ‘*’', + \ ' • In the type ‘Exp a’', + \ ' |', + \ ' 160 | pattern F :: Exp a', + \ ' | ^^^^^', + \ ]) diff --git a/test/handler/test_gitlint_handler.vader b/test/handler/test_gitlint_handler.vader new file mode 100644 index 0000000..73ee988 --- /dev/null +++ b/test/handler/test_gitlint_handler.vader @@ -0,0 +1,41 @@ +Before: + runtime ale_linters/gitcommit/gitlint.vim + +After: + call ale#linter#Reset() + +Execute(The gitlint handler should handle basic warnings and syntax errors): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'type': 'E', + \ 'text': 'Body message is missing', + \ 'code': 'B6', + \ }, + \ { + \ 'lnum': 2, + \ 'type': 'E', + \ 'text': 'Second line is not empty: "to send to upstream"', + \ 'code': 'B4', + \ }, + \ { + \ 'lnum': 3, + \ 'type': 'E', + \ 'text': 'Body message is too short (19<20): "to send to upstream"', + \ 'code': 'B5', + \ }, + \ { + \ 'lnum': 8, + \ 'type': 'E', + \ 'text': 'Title exceeds max length (92>72): "some very long commit subject line where the author can''t wait to explain what he just fixed"', + \ 'code': 'T1', + \ }, + \ ], + \ ale_linters#gitcommit#gitlint#Handle(1, [ + \ '1: B6 Body message is missing', + \ '2: B4 Second line is not empty: "to send to upstream"', + \ '3: B5 Body message is too short (19<20): "to send to upstream"', + \ '8: T1 Title exceeds max length (92>72): "some very long commit subject line where the author can''t wait to explain what he just fixed"' + \ ]) + diff --git a/test/handler/test_gobuild_handler.vader b/test/handler/test_gobuild_handler.vader index ce2119c..17608c3 100644 --- a/test/handler/test_gobuild_handler.vader +++ b/test/handler/test_gobuild_handler.vader @@ -28,7 +28,7 @@ Execute (The gobuild handler should handle names with spaces): \ ]), 'v:val[1:4]') Execute (The gobuild handler should handle relative paths correctly): - silent file! /foo/bar/baz.go + call ale#test#SetFilename('app/test.go') AssertEqual \ [ @@ -37,8 +37,9 @@ Execute (The gobuild handler should handle relative paths correctly): \ 'col': 0, \ 'text': 'missing argument for Printf("%s"): format reads arg 2, have only 1 args', \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.go'), \ }, \ ], \ ale_linters#go#gobuild#Handler(bufnr(''), [ - \ 'baz.go:27: missing argument for Printf("%s"): format reads arg 2, have only 1 args', + \ 'test.go:27: missing argument for Printf("%s"): format reads arg 2, have only 1 args', \ ]) diff --git a/test/handler/test_gometalinter_handler.vader b/test/handler/test_gometalinter_handler.vader index 603ba22..1aade8a 100644 --- a/test/handler/test_gometalinter_handler.vader +++ b/test/handler/test_gometalinter_handler.vader @@ -29,8 +29,10 @@ Execute (The gometalinter handler should handle names with spaces): \ 'C:\something\file with spaces.go:37:5:error: expected ''package'', found ''IDENT'' gibberish (golint)', \ ]), 'v:val[1:5]') -Execute (The gometalinter handler should handle relative paths correctly): - silent file /foo/bar/baz.go +Execute (The gometalinter handler should handle paths correctly): + call ale#test#SetFilename('app/test.go') + + let file = ale#path#GetAbsPath(expand('%:p:h'), 'test.go') AssertEqual \ [ @@ -39,15 +41,17 @@ Execute (The gometalinter handler should handle relative paths correctly): \ 'col': 3, \ 'text': 'expected ''package'', found ''IDENT'' gibberish (staticcheck)', \ 'type': 'W', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.go'), \ }, \ { \ 'lnum': 37, \ 'col': 5, \ 'text': 'expected ''package'', found ''IDENT'' gibberish (golint)', \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.go'), \ }, \ ], \ ale_linters#go#gometalinter#Handler(bufnr(''), [ - \ 'baz.go:12:3:warning: expected ''package'', found ''IDENT'' gibberish (staticcheck)', - \ 'baz.go:37:5:error: expected ''package'', found ''IDENT'' gibberish (golint)', + \ file . ':12:3:warning: expected ''package'', found ''IDENT'' gibberish (staticcheck)', + \ file . ':37:5:error: expected ''package'', found ''IDENT'' gibberish (golint)', \ ]) diff --git a/test/handler/test_govet_handler.vader b/test/handler/test_govet_handler.vader new file mode 100644 index 0000000..b4bfdc9 --- /dev/null +++ b/test/handler/test_govet_handler.vader @@ -0,0 +1,28 @@ +Before: + runtime ale_linters/go/govet.vim + +After: + call ale#linter#Reset() + +Execute(The govet handler should return the correct filenames): + AssertEqual + \ [ + \ { + \ 'lnum': 27, + \ 'col': 0, + \ 'text': 'some error', + \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.go'), + \ }, + \ { + \ 'lnum': 27, + \ 'col': 5, + \ 'text': 'some error with a column', + \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/other.go'), + \ }, + \ ], + \ ale_linters#go#govet#Handler(bufnr(''), [ + \ 'test.go:27: some error', + \ 'other.go:27:5: some error with a column', + \ ]) diff --git a/test/handler/test_javac_handler.vader b/test/handler/test_javac_handler.vader index 2cf3207..ff4e163 100644 --- a/test/handler/test_javac_handler.vader +++ b/test/handler/test_javac_handler.vader @@ -1,42 +1,51 @@ Before: runtime ale_linters/java/javac.vim + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('dummy.java') + After: + call ale#test#RestoreDirectory() call ale#linter#Reset() Execute(The javac handler should handle cannot find symbol errors): AssertEqual \ [ \ { + \ 'filename': ale#path#Simplify('/tmp/vLPr4Q5/33/foo.java'), \ 'lnum': 1, \ 'text': 'error: some error', \ 'type': 'E', \ }, \ { + \ 'filename': ale#path#Simplify('/tmp/vLPr4Q5/33/foo.java'), \ 'lnum': 2, \ 'col': 5, \ 'text': 'error: cannot find symbol: BadName', \ 'type': 'E', \ }, \ { + \ 'filename': ale#path#Simplify('/tmp/vLPr4Q5/33/foo.java'), \ 'lnum': 34, \ 'col': 5, \ 'text': 'error: cannot find symbol: BadName2', \ 'type': 'E', \ }, \ { + \ 'filename': ale#path#Simplify('/tmp/vLPr4Q5/33/foo.java'), \ 'lnum': 37, \ 'text': 'warning: some warning', \ 'type': 'W', \ }, \ { + \ 'filename': ale#path#Simplify('/tmp/vLPr4Q5/33/foo.java'), \ 'lnum': 42, \ 'col': 11, \ 'text': 'error: cannot find symbol: bar()', \ 'type': 'E', \ }, \ ], - \ ale_linters#java#javac#Handle(347, [ + \ ale_linters#java#javac#Handle(bufnr(''), [ \ '/tmp/vLPr4Q5/33/foo.java:1: error: some error', \ '/tmp/vLPr4Q5/33/foo.java:2: error: cannot find symbol', \ ' BadName foo() {', @@ -49,9 +58,30 @@ Execute(The javac handler should handle cannot find symbol errors): \ ' symbol: class BadName2', \ ' location: class Bar', \ '/tmp/vLPr4Q5/33/foo.java:37: warning: some warning', - \ '/tmp/vLPr4Q5/264/foo.java:42: error: cannot find symbol', + \ '/tmp/vLPr4Q5/33/foo.java:42: error: cannot find symbol', \ ' this.bar();', \ ' ^', \ ' symbol: method bar()', \ '5 errors', \ ]) + +Execute(The javac handler should resolve files from different directories): + AssertEqual + \ [ + \ { + \ 'filename': ale#path#Simplify(g:dir . '/Foo.java'), + \ 'lnum': 1, + \ 'text': 'error: some error', + \ 'type': 'E', + \ }, + \ { + \ 'filename': ale#path#Simplify(g:dir . '/Bar.java'), + \ 'lnum': 1, + \ 'text': 'error: some error', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#java#javac#Handle(bufnr(''), [ + \ './Foo.java:1: error: some error', + \ './Bar.java:1: error: some error', + \ ]) diff --git a/test/handler/test_jscs_handler.vader b/test/handler/test_jscs_handler.vader index 6247307..5566116 100644 --- a/test/handler/test_jscs_handler.vader +++ b/test/handler/test_jscs_handler.vader @@ -10,21 +10,30 @@ Execute(jscs should parse lines correctly): \ { \ 'lnum': 1, \ 'col': 7, - \ 'text': 'disallowVar: Variable declarations should use `let` or `const` not `var`', + \ 'text': 'Variable declarations should use `let` or `const` not `var`', + \ 'code': 'disallowVar', \ }, \ { \ 'lnum': 3, \ 'col': 21, - \ 'text': 'disallowTrailingWhitespace: Illegal trailing whitespace', + \ 'text': 'Illegal trailing whitespace', + \ 'code': 'disallowTrailingWhitespace', \ }, \ { \ 'lnum': 5, \ 'col': 9, - \ 'text': 'disallowUnusedVariables: Variable `hello` is not used', + \ 'text': 'Variable `hello` is not used', + \ 'code': 'disallowUnusedVariables', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'text': 'Expected indentation of 1 characters', \ }, \ ], \ ale_linters#javascript#jscs#Handle(347, [ \ 'foobar.js: line 1, col 7, disallowVar: Variable declarations should use `let` or `const` not `var`', \ 'foobar.js: line 3, col 21, disallowTrailingWhitespace: Illegal trailing whitespace', \ 'foobar.js: line 5, col 9, disallowUnusedVariables: Variable `hello` is not used', + \ 'foobar.js: line 2, col 1, Expected indentation of 1 characters', \ ]) diff --git a/test/handler/test_lessc_handler.vader b/test/handler/test_lessc_handler.vader new file mode 100644 index 0000000..31de559 --- /dev/null +++ b/test/handler/test_lessc_handler.vader @@ -0,0 +1,69 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/handler') + call ale#test#SetFilename('testfile.less') + + runtime ale_linters/less/lessc.vim + +After: + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The lessc handler should handle errors for the current file correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in - on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \]) + +Execute(The lessc handler should handle errors for other files in the same directory correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Simplify(g:dir . '/imported.less') + \ }, + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Simplify(g:dir . '/imported.less') + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in imported.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \ 'ParseError: Unrecognised input. Possibly missing something in ./imported.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \]) + +Execute(The lessc handler should handle errors for files in directories above correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Simplify(g:dir . '/../imported2.less') + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in ../imported2.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \]) diff --git a/test/handler/test_llc_handler.vader b/test/handler/test_llc_handler.vader index edea233..bbe686f 100644 --- a/test/handler/test_llc_handler.vader +++ b/test/handler/test_llc_handler.vader @@ -1,6 +1,9 @@ Before: runtime! ale_linters/llvm/llc.vim +After: + call ale#linter#Reset() + Execute(llc handler should parse errors output for STDIN): AssertEqual \ [ @@ -53,4 +56,3 @@ Execute(llc handler should parse errors output for some file): \ 'call void @foo(i64 %0)', \ ' ^', \ ]) - diff --git a/test/handler/test_luac_handler.vader b/test/handler/test_luac_handler.vader new file mode 100644 index 0000000..3a2e769 --- /dev/null +++ b/test/handler/test_luac_handler.vader @@ -0,0 +1,36 @@ +Before: + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_whitespace = 1 + + runtime ale_linters/lua/luac.vim + +After: + Restore + call ale#linter#Reset() + +Execute(The luac handler should parse lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'text': 'line contains trailing whitespace', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 3, + \ 'text': 'unexpected symbol near ''-''', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 5, + \ 'text': '''='' expected near '')''', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#lua#luac#Handle(347, [ + \ 'luac /file/path/here.lua:1: line contains trailing whitespace', + \ 'luac /file/path/here.lua:3: unexpected symbol near ''-''', + \ 'luac /file/path/here.lua:5: ''='' expected near '')''', + \ ]) + diff --git a/test/handler/test_lua_handler.vader b/test/handler/test_luacheck_handler.vader similarity index 80% rename from test/handler/test_lua_handler.vader rename to test/handler/test_luacheck_handler.vader index 712c7c5..7cebb01 100644 --- a/test/handler/test_lua_handler.vader +++ b/test/handler/test_luacheck_handler.vader @@ -1,31 +1,36 @@ Before: Save g:ale_warn_about_trailing_whitespace + let g:ale_warn_about_trailing_whitespace = 1 + + runtime ale_linters/lua/luacheck.vim + After: Restore call ale#linter#Reset() Execute(The luacheck handler should parse lines correctly): - runtime ale_linters/lua/luacheck.vim - AssertEqual \ [ \ { \ 'lnum': 1, \ 'col': 8, - \ 'text': 'W612: line contains trailing whitespace', + \ 'text': 'line contains trailing whitespace', + \ 'code': 'W612', \ 'type': 'W', \ }, \ { \ 'lnum': 3, \ 'col': 5, - \ 'text': 'W213: unused loop variable ''k''', + \ 'text': 'unused loop variable ''k''', + \ 'code': 'W213', \ 'type': 'W', \ }, \ { \ 'lnum': 3, \ 'col': 19, - \ 'text': 'W113: accessing undefined variable ''x''', + \ 'text': 'accessing undefined variable ''x''', + \ 'code': 'W113', \ 'type': 'W', \ }, \ ], @@ -36,8 +41,6 @@ Execute(The luacheck handler should parse lines correctly): \ ]) Execute(The luacheck handler should respect the warn_about_trailing_whitespace option): - runtime ale_linters/lua/luacheck.vim - let g:ale_warn_about_trailing_whitespace = 0 AssertEqual @@ -45,7 +48,8 @@ Execute(The luacheck handler should respect the warn_about_trailing_whitespace o \ { \ 'lnum': 5, \ 'col': 43, - \ 'text': 'W212: unused argument ''g''', + \ 'text': 'unused argument ''g''', + \ 'code': 'W212', \ 'type': 'W', \ } \ ], diff --git a/test/handler/test_mcs_handler.vader b/test/handler/test_mcs_handler.vader index 75a764a..3defc32 100644 --- a/test/handler/test_mcs_handler.vader +++ b/test/handler/test_mcs_handler.vader @@ -10,19 +10,22 @@ Execute(The mcs handler should handle cannot find symbol errors): \ { \ 'lnum': 12, \ 'col' : 29, - \ 'text': 'error CS1001: ; expected', + \ 'text': '; expected', + \ 'code': 'CS1001', \ 'type': 'E', \ }, \ { \ 'lnum': 101, \ 'col': 0, - \ 'text': 'error CS1028: Unexpected processor directive (no #if for this #endif)', + \ 'text': 'Unexpected processor directive (no #if for this #endif)', + \ 'code': 'CS1028', \ 'type': 'E', \ }, \ { \ 'lnum': 10, \ 'col': 12, - \ 'text': 'warning CS0123: some warning', + \ 'text': 'some warning', + \ 'code': 'CS0123', \ 'type': 'W', \ }, \ ], diff --git a/test/handler/test_mcsc_handler.vader b/test/handler/test_mcsc_handler.vader index 5f4c133..8ae4735 100644 --- a/test/handler/test_mcsc_handler.vader +++ b/test/handler/test_mcsc_handler.vader @@ -3,40 +3,65 @@ Before: unlet! g:ale_cs_mcsc_source + call ale#test#SetDirectory('/testplugin/test/handler') + call ale#test#SetFilename('Test.cs') + runtime ale_linters/cs/mcsc.vim After: unlet! g:ale_cs_mcsc_source + + call ale#test#RestoreDirectory() call ale#linter#Reset() +Execute(The mcs handler should work with the default of the buffer's directory): + AssertEqual + \ [ + \ { + \ 'lnum': 12, + \ 'col' : 29, + \ 'text': '; expected', + \ 'code': 'CS1001', + \ 'type': 'E', + \ 'filename': ale#path#Simplify(g:dir . '/Test.cs'), + \ }, + \ ], + \ ale_linters#cs#mcsc#Handle(bufnr(''), [ + \ 'Test.cs(12,29): error CS1001: ; expected', + \ 'Compilation failed: 2 error(s), 1 warnings', + \ ]) + Execute(The mcs handler should handle cannot find symbol errors): - let g:ale_cs_mcsc_source='/home/foo/project/bar' + let g:ale_cs_mcsc_source = '/home/foo/project/bar' AssertEqual \ [ \ { \ 'lnum': 12, \ 'col' : 29, - \ 'text': 'error CS1001: ; expected', + \ 'text': '; expected', + \ 'code': 'CS1001', \ 'type': 'E', - \ 'filename': ale#path#Winify('/home/foo/project/bar/Test.cs', 'add_drive'), + \ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'), \ }, \ { \ 'lnum': 101, \ 'col': 0, - \ 'text': 'error CS1028: Unexpected processor directive (no #if for this #endif)', + \ 'text': 'Unexpected processor directive (no #if for this #endif)', + \ 'code': 'CS1028', \ 'type': 'E', - \ 'filename': ale#path#Winify('/home/foo/project/bar/Test.cs', 'add_drive'), + \ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'), \ }, \ { \ 'lnum': 10, \ 'col': 12, - \ 'text': 'warning CS0123: some warning', + \ 'text': 'some warning', + \ 'code': 'CS0123', \ 'type': 'W', - \ 'filename': ale#path#Winify('/home/foo/project/bar/Test.cs', 'add_drive'), + \ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'), \ }, \ ], - \ ale_linters#cs#mcsc#Handle(347, [ + \ ale_linters#cs#mcsc#Handle(bufnr(''), [ \ 'Test.cs(12,29): error CS1001: ; expected', \ 'Test.cs(101,0): error CS1028: Unexpected processor directive (no #if for this #endif)', \ 'Test.cs(10,12): warning CS0123: some warning', diff --git a/test/handler/test_mypy_handler.vader b/test/handler/test_mypy_handler.vader index a3e224f..f3d4cbf 100644 --- a/test/handler/test_mypy_handler.vader +++ b/test/handler/test_mypy_handler.vader @@ -1,9 +1,15 @@ Before: + Save g:ale_python_mypy_ignore_invalid_syntax + + unlet! g:ale_python_mypy_ignore_invalid_syntax + runtime ale_linters/python/mypy.vim call ale#test#SetDirectory('/testplugin/test/handler') After: + Restore + call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -15,35 +21,35 @@ Execute(The mypy handler should parse lines correctly): \ { \ 'lnum': 768, \ 'col': 38, - \ 'filename': ale#path#Winify(g:dir . '/baz.py'), + \ 'filename': ale#path#Simplify(g:dir . '/baz.py'), \ 'type': 'E', \ 'text': 'Cannot determine type of ''SOME_SYMBOL''', \ }, \ { \ 'lnum': 821, \ 'col': 38, - \ 'filename': ale#path#Winify(g:dir . '/baz.py'), + \ 'filename': ale#path#Simplify(g:dir . '/baz.py'), \ 'type': 'E', \ 'text': 'Cannot determine type of ''SOME_SYMBOL''', \ }, \ { \ 'lnum': 38, \ 'col': 44, - \ 'filename': ale#path#Winify(g:dir . '/other.py'), + \ 'filename': ale#path#Simplify(g:dir . '/other.py'), \ 'type': 'E', \ 'text': 'Cannot determine type of ''ANOTHER_SYMBOL''', \ }, \ { \ 'lnum': 15, \ 'col': 3, - \ 'filename': ale#path#Winify(g:dir . '/__init__.py'), + \ 'filename': ale#path#Simplify(g:dir . '/__init__.py'), \ 'type': 'E', \ 'text': 'Argument 1 to "somefunc" has incompatible type "int"; expected "str"' \ }, \ { \ 'lnum': 72, \ 'col': 1, - \ 'filename': ale#path#Winify(g:dir . '/__init__.py'), + \ 'filename': ale#path#Simplify(g:dir . '/__init__.py'), \ 'type': 'W', \ 'text': 'Some warning', \ }, @@ -80,3 +86,27 @@ Execute(The mypy handler should handle Windows names with spaces): \ ale_linters#python#mypy#Handle(bufnr(''), [ \ 'C:\something\with spaces.py:4: error: No library stub file for module ''django.db''', \ ]) + +Execute(The mypy syntax errors shouldn't be ignored by default): + AssertEqual + \ [ + \ { + \ 'lnum': 4, + \ 'col': 0, + \ 'filename': ale#path#Simplify(g:dir . '/foo.py'), + \ 'type': 'E', + \ 'text': 'invalid syntax', + \ }, + \ ], + \ ale_linters#python#mypy#Handle(bufnr(''), [ + \ 'foo.py:4: error: invalid syntax', + \ ]) + +Execute(The mypy syntax errors should be ignored when the option is on): + let g:ale_python_mypy_ignore_invalid_syntax = 1 + + AssertEqual + \ [], + \ ale_linters#python#mypy#Handle(bufnr(''), [ + \ 'foo.py:4: error: invalid syntax', + \ ]) diff --git a/test/handler/test_nagelfar_handler.vader b/test/handler/test_nagelfar_handler.vader index 2a31f19..ceaee19 100644 --- a/test/handler/test_nagelfar_handler.vader +++ b/test/handler/test_nagelfar_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/tcl/nagelfar.vim +After: + call ale#linter#Reset() + Execute(The nagelfar handler should parse lines correctly): AssertEqual \ [ diff --git a/test/handler/test_nim_handler.vader b/test/handler/test_nim_handler.vader index c9a1b71..e484000 100644 --- a/test/handler/test_nim_handler.vader +++ b/test/handler/test_nim_handler.vader @@ -1,5 +1,10 @@ -Execute(Parsing nim errors should work): +Before: runtime ale_linters/nim/nimcheck.vim + +After: + call ale#linter#Reset() + +Execute(Parsing nim errors should work): silent file foobar.nim AssertEqual @@ -7,25 +12,27 @@ Execute(Parsing nim errors should work): \ { \ 'lnum': 8, \ 'col': 8, - \ 'text': 'Warning: use {.base.} for base methods; baseless methods are deprecated [UseBase]', + \ 'text': 'use {.base.} for base methods; baseless methods are deprecated', + \ 'code': 'UseBase', \ 'type': 'W', \ }, \ { \ 'lnum': 12, \ 'col': 2, - \ 'text': 'Error: identifier expected, but found ''a.barfoo''', + \ 'text': 'identifier expected, but found ''a.barfoo''', \ 'type': 'E', \ }, \ { \ 'lnum': 2, \ 'col': 5, - \ 'text': 'Hint: ''NotUsed'' is declared but not used [XDeclaredButNotUsed]', + \ 'text': '''NotUsed'' is declared but not used', + \ 'code': 'XDeclaredButNotUsed', \ 'type': 'W', \ }, \ { \ 'lnum': 12, \ 'col': 2, - \ 'text': 'Error: with : character', + \ 'text': 'with : character', \ 'type': 'E', \ }, \ ], diff --git a/test/handler/test_nix_handler.vader b/test/handler/test_nix_handler.vader index 1555e59..398e1ac 100644 --- a/test/handler/test_nix_handler.vader +++ b/test/handler/test_nix_handler.vader @@ -1,6 +1,10 @@ -Execute(The nix handler should parse nix-instantiate error messages correctly): +Before: runtime ale_linters/nix/nix.vim +After: + call ale#linter#Reset() + +Execute(The nix handler should parse nix-instantiate error messages correctly): AssertEqual \ [ \ { @@ -22,6 +26,3 @@ Execute(The nix handler should parse nix-instantiate error messages correctly): \ 'error: syntax error, unexpected IN, at /path/to/filename.nix:23:14', \ 'error: syntax error, unexpected ''='', expecting '';'', at /path/to/filename.nix:3:12', \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_perl_handler.vader b/test/handler/test_perl_handler.vader index 9e1c520..c5791d7 100644 --- a/test/handler/test_perl_handler.vader +++ b/test/handler/test_perl_handler.vader @@ -15,9 +15,9 @@ Execute(The Perl linter should ignore errors from other files): \ {'lnum': '2', 'type': 'E', 'text': 'Compilation failed in require'}, \ ], \ ale_linters#perl#perl#Handle(bufnr(''), [ - \ 'syntax error at ' . ale#path#Winify(g:dir . '/foo.pm') . ' line 4, near "aklsdfjmy "', - \ 'Compilation failed in require at ' . ale#path#Winify(g:dir . '/bar.pl') . ' line 2.', - \ 'BEGIN failed--compilation aborted at ' . ale#path#Winify(g:dir . '/bar.pl') . ' line 2.', + \ 'syntax error at ' . ale#path#Simplify(g:dir . '/foo.pm') . ' line 4, near "aklsdfjmy "', + \ 'Compilation failed in require at ' . ale#path#Simplify(g:dir . '/bar.pl') . ' line 2.', + \ 'BEGIN failed--compilation aborted at ' . ale#path#Simplify(g:dir . '/bar.pl') . ' line 2.', \ ]) Execute(The Perl linter should complain about failing to locate modules): @@ -47,3 +47,42 @@ Execute(The Perl linter should complain about failing to locate modules): \ 'Unable to build `ro` accessor for slot `path` in `App::CPANFileUpdate` because the slot cannot be found. at /extlib/Method/Traits.pm line 189.', \ 'BEGIN failed--compilation aborted at - line 10.', \ ]) + +Execute(The Perl linter should not report warnings as errors): + AssertEqual + \ [ + \ {'lnum': '5', 'type': 'W', 'text': '"my" variable $foo masks earlier declaration in same scope'}, + \ ], + \ ale_linters#perl#perl#Handle(bufnr(''), [ + \ '"my" variable $foo masks earlier declaration in same scope at - line 5.', + \ 't.pl syntax OK', + \ ]) + +Execute(The Perl linter does not default to reporting generic error): + AssertEqual + \ [ + \ {'lnum': '8', 'type': 'E', 'text': 'Missing right curly or square bracket'}, + \ ], + \ ale_linters#perl#perl#Handle(bufnr(''), [ + \ 'Missing right curly or square bracket at - line 8, at end of line', + \ 'syntax error at - line 8, at EOF', + \ 'Execution of t.pl aborted due to compilation errors.', + \ ]) + +" The first "error" is actually a warning, but the current implementation +" doesn't have a good way of teasing out the warnings from amongst the +" errors. If we're able to do this in future, then we'll want to switch +" the first "E" to a "W". + +Execute(The Perl linter reports errors even when mixed with warnings): + AssertEqual + \ [ + \ {'lnum': '5', 'type': 'E', 'text': '"my" variable $foo masks earlier declaration in same scope'}, + \ {'lnum': '8', 'type': 'E', 'text': 'Missing right curly or square bracket'}, + \ ], + \ ale_linters#perl#perl#Handle(bufnr(''), [ + \ '"my" variable $foo masks earlier declaration in same scope at - line 5.', + \ 'Missing right curly or square bracket at - line 8, at end of line', + \ 'syntax error at - line 8, at EOF', + \ 'Execution of t.pl aborted due to compilation errors.', + \ ]) diff --git a/test/handler/test_perlcritic_handler.vader b/test/handler/test_perlcritic_handler.vader new file mode 100644 index 0000000..f00b07d --- /dev/null +++ b/test/handler/test_perlcritic_handler.vader @@ -0,0 +1,20 @@ +Before: + runtime ale_linters/perl/perlcritic.vim + +After: + call ale#linter#Reset() + +Execute(The Perl::Critic handler should create all issues as warnings): + AssertEqual + \ [ + \ { + \ 'lnum': '21', + \ 'col': '17', + \ 'text': 'Regular expression without "/m" flag', + \ 'type': 'W', + \ } + \ ], + \ ale_linters#perl#perlcritic#Handle(99, [ + \ '21:17 Regular expression without "/m" flag' + \ ]) + diff --git a/test/handler/test_php_handler.vader b/test/handler/test_php_handler.vader index 0d4d427..6fe9b32 100644 --- a/test/handler/test_php_handler.vader +++ b/test/handler/test_php_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/php/php.vim +After: + call ale#linter#Reset() + Given (Some invalid lines of PHP): [foo;] class Foo { / } @@ -51,6 +54,18 @@ Execute (The php handler should ignore lines starting with 'PHP Parse error'): \ "PHP Parse error: syntax error, This line should be ignored completely in - on line 1", \ ]) +Execute (The php handler should handle lines containing 'Standard input code'): + AssertEqual + \ [ + \ { + \ 'lnum': 47, + \ 'col': 0, + \ 'text': "Invalid numeric literal", + \ }, + \ ], + \ ale_linters#php#php#Handle(347, [ + \ "Parse error: Invalid numeric literal in Standard input code on line 47", + \ ]) Execute (The php handler should parse lines without column indication): AssertEqual \ [ @@ -76,6 +91,3 @@ Execute (The php handler should parse lines without column indication): \ "Parse error: syntax error, unexpected end of file in - on line 21", \ "Parse error: Invalid numeric literal in - on line 47", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_php_phan_handler.vader b/test/handler/test_php_phan_handler.vader new file mode 100644 index 0000000..2374792 --- /dev/null +++ b/test/handler/test_php_phan_handler.vader @@ -0,0 +1,24 @@ +Before: + runtime ale_linters/php/phan.vim + +After: + call ale#linter#Reset() + +Execute(The php static analyzer handler should parse errors from phan): + AssertEqual + \ [ + \ { + \ 'lnum': 25, + \ 'type': 'W', + \ 'text': 'Return type of getValidator is undeclared type \Respect\Validation\Validator', + \ }, + \ { + \ 'lnum': 66, + \ 'type': 'W', + \ 'text': 'Call to method string from undeclared class \Respect\Validation\Validator', + \ }, + \ ], + \ ale_linters#php#phan#Handle(347, [ + \ "example.php:25 PhanUndeclaredTypeReturnType Return type of getValidator is undeclared type \\Respect\\Validation\\Validator", + \ "example.php:66 PhanUndeclaredClassMethod Call to method string from undeclared class \\Respect\\Validation\\Validator", + \ ]) diff --git a/test/handler/test_php_phpmd_handler.vader b/test/handler/test_php_phpmd_handler.vader index be36f3d..f161d73 100644 --- a/test/handler/test_php_phpmd_handler.vader +++ b/test/handler/test_php_phpmd_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/php/phpmd.vim +After: + call ale#linter#Reset() + Execute(The php static analyzer handler should parse errors from phpmd): AssertEqual \ [ @@ -19,6 +22,3 @@ Execute(The php static analyzer handler should parse errors from phpmd): \ "example.php:22 Avoid unused local variables such as '$response'.", \ "example.php:14 The method test uses an else expression. Else is never necessary and you can simplify the code to work without else.", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_pony_handler.vader b/test/handler/test_pony_handler.vader new file mode 100644 index 0000000..25a8254 --- /dev/null +++ b/test/handler/test_pony_handler.vader @@ -0,0 +1,21 @@ +Execute(The pony handler should handle ponyc output): + call ale#test#SetFilename('foo.pony') + + AssertEqual + \ [ + \ { + \ 'filename': '/home/projects/Wombat.pony', + \ 'lnum': 22, + \ 'type': 'E', + \ 'col': 30, + \ 'text': 'can''t lookup private fields from outside the type', + \ }, + \ ], + \ ale#handlers#pony#HandlePonycFormat(bufnr(''), [ + \ 'Building builtin -> /usr/lib/pony/0.21.3/packages/builtin', + \ 'Building . -> /home/projects', + \ 'Error:', + \ '/home/projects/Wombat.pony:22:30: can''t lookup private fields from outside the type', + \ ' env.out.print(defaultWombat._hunger_level)', + \ ' ^', + \ ]) diff --git a/test/handler/test_prospector_handler.vader b/test/handler/test_prospector_handler.vader new file mode 100644 index 0000000..7962f6c --- /dev/null +++ b/test/handler/test_prospector_handler.vader @@ -0,0 +1,158 @@ +Before: + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_whitespace = 1 + + runtime ale_linters/python/prospector.vim + +After: + Restore + + call ale#linter#Reset() + + silent file something_else.py + +Execute(Basic prospector errors should be handle): + AssertEqual + \ [ + \ { + \ 'lnum': 20, + \ 'col': 1, + \ 'text': 'Trailing whitespace', + \ 'code': '(pylint) trailing-whitespace', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'text': 'Missing module docstring', + \ 'code': '(pylint) missing-docstring', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'text': 'Missing function docstring', + \ 'code': '(pylint) missing-docstring', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 3, + \ 'col': 5, + \ 'text': '''break'' not properly in loop', + \ 'code': '(pylint) not-in-loop', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 4, + \ 'col': 5, + \ 'text': 'Unreachable code', + \ 'code': '(pylint) unreachable', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 7, + \ 'col': 33, + \ 'text': 'No exception type(s) specified', + \ 'code': '(pylint) bare-except', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#python#prospector#Handle(bufnr(''), [ + \ '{', + \ ' "messages": [', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "trailing-whitespace",', + \ ' "message": "Trailing whitespace",', + \ ' "location": {', + \ ' "character": 0,', + \ ' "line": 20', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "missing-docstring",', + \ ' "message": "Missing module docstring",', + \ ' "location": {', + \ ' "character": 0,', + \ ' "line": 1', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "missing-docstring",', + \ ' "message": "Missing function docstring",', + \ ' "location": {', + \ ' "character": 0,', + \ ' "line": 2', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "not-in-loop",', + \ ' "message": "''break'' not properly in loop",', + \ ' "location": {', + \ ' "character": 4,', + \ ' "line": 3', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "unreachable",', + \ ' "message": "Unreachable code",', + \ ' "location": {', + \ ' "character": 4,', + \ ' "line": 4', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "bare-except",', + \ ' "message": "No exception type(s) specified",', + \ ' "location": {', + \ ' "character": 32,', + \ ' "line": 7', + \ ' }', + \ ' }', + \ ' ]', + \ '}', + \ ]) + +Execute(Ignoring trailing whitespace messages should work): + let g:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'text': 'Missing module docstring', + \ 'code': '(pylint) missing-docstring', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#python#prospector#Handle(bufnr(''), [ + \ '{', + \ ' "messages": [', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "trailing-whitespace",', + \ ' "message": "Trailing whitespace",', + \ ' "location": {', + \ ' "character": 0,', + \ ' "line": 4', + \ ' }', + \ ' },', + \ ' {', + \ ' "source": "pylint",', + \ ' "code": "missing-docstring",', + \ ' "message": "Missing module docstring",', + \ ' "location": {', + \ ' "character": 0,', + \ ' "line": 1', + \ ' }', + \ ' }', + \ ' ]', + \ '}', + \ ]) diff --git a/test/handler/test_puppet_handler.vader b/test/handler/test_puppet_handler.vader new file mode 100644 index 0000000..0d274fd --- /dev/null +++ b/test/handler/test_puppet_handler.vader @@ -0,0 +1,45 @@ +Before: + runtime ale_linters/puppet/puppet.vim + +After: + call ale#linter#Reset() + +Execute(The puppet handler should parse lines correctly when no column is supplied): + " Line Error + AssertEqual + \ [ + \ { + \ 'lnum': 5, + \ 'col': 0, + \ 'text': "Syntax error at '='; expected '}'" + \ }, + \ { + \ 'lnum': 3, + \ 'col': 0, + \ 'text': "Syntax error at '='; expected '}'" + \ }, + \ ], + \ ale_linters#puppet#puppet#Handle(255, [ + \ "Error: Could not parse for environment production: Syntax error at '='; expected '}' at /root/puppetcode/modules/pancakes/manifests/init.pp:5", + \ "Error: Could not parse for environment production: Syntax error at '='; expected '}' at C:/puppet/modules/pancakes/manifests/init.pp:3", + \ ]) + +Execute(The puppet handler should parse lines and column correctly): + " Line Error + AssertEqual + \ [ + \ { + \ 'lnum': 43, + \ 'col': 12, + \ 'text': "Syntax error at ':'" + \ }, + \ { + \ 'lnum': 54, + \ 'col': 9, + \ 'text': "Syntax error at ':'" + \ } + \ ], + \ ale_linters#puppet#puppet#Handle(255, [ + \ "Error: Could not parse for environment production: Syntax error at ':' at /root/puppetcode/modules/nginx/manifests/init.pp:43:12", + \ "Error: Could not parse for environment production: Syntax error at ':' at C:/puppet/modules/nginx/manifests/init.pp:54:9", + \ ]) diff --git a/test/handler/test_pycodestyle_handler.vader b/test/handler/test_pycodestyle_handler.vader index cc83fc8..3664455 100644 --- a/test/handler/test_pycodestyle_handler.vader +++ b/test/handler/test_pycodestyle_handler.vader @@ -1,7 +1,18 @@ Before: + Save g:ale_warn_about_trailing_blank_lines + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_blank_lines = 1 + let g:ale_warn_about_trailing_whitespace = 1 + runtime ale_linters/python/pycodestyle.vim After: + Restore + + unlet! b:ale_warn_about_trailing_blank_lines + unlet! b:ale_warn_about_trailing_whitespace + call ale#linter#Reset() silent file something_else.py @@ -9,40 +20,135 @@ Execute(The pycodestyle handler should parse output): AssertEqual \ [ \ { + \ 'lnum': 8, + \ 'col': 3, + \ 'type': 'E', + \ 'text': 'SyntaxError: invalid syntax', + \ 'code': 'E999', + \ }, + \ { \ 'lnum': 69, \ 'col': 11, - \ 'text': 'E401 multiple imports on one line', + \ 'text': 'multiple imports on one line', + \ 'code': 'E401', \ 'type': 'E', + \ 'sub_type': 'style', \ }, \ { \ 'lnum': 77, \ 'col': 1, - \ 'text': 'E302 expected 2 blank lines, found 1', + \ 'text': 'expected 2 blank lines, found 1', + \ 'code': 'E302', \ 'type': 'E', + \ 'sub_type': 'style', \ }, \ { \ 'lnum': 88, \ 'col': 5, - \ 'text': 'E301 expected 1 blank line, found 0', + \ 'text': 'expected 1 blank line, found 0', + \ 'code': 'E301', \ 'type': 'E', + \ 'sub_type': 'style', \ }, \ { \ 'lnum': 222, \ 'col': 34, - \ 'text': 'W602 deprecated form of raising exception', + \ 'text': 'deprecated form of raising exception', + \ 'code': 'W602', \ 'type': 'W', + \ 'sub_type': 'style', \ }, \ { \ 'lnum': 544, \ 'col': 21, - \ 'text': 'W601 .has_key() is deprecated, use ''in''', + \ 'text': '.has_key() is deprecated, use ''in''', + \ 'code': 'W601', \ 'type': 'W', + \ 'sub_type': 'style', \ }, \ ], \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'stdin:8:3: E999 SyntaxError: invalid syntax', \ 'stdin:69:11: E401 multiple imports on one line', \ 'stdin:77:1: E302 expected 2 blank lines, found 1', \ 'stdin:88:5: E301 expected 1 blank line, found 0', \ 'stdin:222:34: W602 deprecated form of raising exception', \ 'example.py:544:21: W601 .has_key() is deprecated, use ''in''', \ ]) + +Execute(Warnings about trailing whitespace should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W291', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W293', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Disabling trailing whitespace warnings should work): + let b:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Warnings about trailing blank lines should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'W391', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'blank line at end of file', + \ }, + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(Disabling trailing blank line warnings should work): + let b:ale_warn_about_trailing_blank_lines = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(E112 should be a syntax error): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'code': 'E112', + \ 'type': 'E', + \ 'text': 'expected an indented block', + \ }, + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'foo.py:6:1: E112 expected an indented block', + \ ]) diff --git a/test/handler/test_pyflakes_handler.vader b/test/handler/test_pyflakes_handler.vader new file mode 100644 index 0000000..ab4fab4 --- /dev/null +++ b/test/handler/test_pyflakes_handler.vader @@ -0,0 +1,24 @@ +Before: + runtime ale_linters/python/pyflakes.vim + +After: + call ale#linter#Reset() + +Execute(The pyflakes handler should handle basic errors): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 0, + \ 'text': 'undefined name ''foo''', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 7, + \ 'text': 'invalid syntax', + \ }, + \ ], + \ ale_linters#python#pyflakes#Handle(bufnr(''), [ + \ 'test.py:1: undefined name ''foo''', + \ 'test.py:1:7: invalid syntax', + \ ]) diff --git a/test/handler/test_pylint_handler.vader b/test/handler/test_pylint_handler.vader index 2314e9b..aff4084 100644 --- a/test/handler/test_pylint_handler.vader +++ b/test/handler/test_pylint_handler.vader @@ -1,47 +1,60 @@ Before: + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_whitespace = 1 + runtime ale_linters/python/pylint.vim After: + Restore + call ale#linter#Reset() + silent file something_else.py -Execute(pylint handler parsing, translating columns to 1-based index): +Execute(Basic pylint errors should be handle): AssertEqual \ [ \ { \ 'lnum': 4, \ 'col': 1, - \ 'text': 'C0303: Trailing whitespace (trailing-whitespace)', + \ 'text': 'Trailing whitespace', + \ 'code': 'trailing-whitespace', \ 'type': 'W', \ }, \ { \ 'lnum': 1, \ 'col': 1, - \ 'text': 'C0111: Missing module docstring (missing-docstring)', + \ 'text': 'Missing module docstring', + \ 'code': 'missing-docstring', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 1, - \ 'text': 'C0111: Missing function docstring (missing-docstring)', + \ 'text': 'Missing function docstring', + \ 'code': 'missing-docstring', \ 'type': 'W', \ }, \ { \ 'lnum': 3, \ 'col': 5, - \ 'text': 'E0103: ''break'' not properly in loop (not-in-loop)', + \ 'text': '''break'' not properly in loop', + \ 'code': 'not-in-loop', \ 'type': 'E', \ }, \ { \ 'lnum': 4, \ 'col': 5, - \ 'text': 'W0101: Unreachable code (unreachable)', + \ 'text': 'Unreachable code', + \ 'code': 'unreachable', \ 'type': 'W', \ }, \ { \ 'lnum': 7, \ 'col': 33, - \ 'text': 'W0702: No exception type(s) specified (bare-except)', + \ 'text': 'No exception type(s) specified', + \ 'code': 'bare-except', \ 'type': 'W', \ }, \ ], @@ -58,3 +71,26 @@ Execute(pylint handler parsing, translating columns to 1-based index): \ '------------------------------------------------------------------', \ 'Your code has been rated at 0.00/10 (previous run: 2.50/10, -2.50)', \ ]) + +Execute(Ignoring trailing whitespace messages should work): + let g:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'text': 'Missing module docstring', + \ 'code': 'missing-docstring', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#python#pylint#Handle(bufnr(''), [ + \ 'No config file found, using default configuration', + \ '************* Module test', + \ 'test.py:4:0: C0303 (trailing-whitespace) Trailing whitespace', + \ 'test.py:1:0: C0111 (missing-docstring) Missing module docstring', + \ '', + \ '------------------------------------------------------------------', + \ 'Your code has been rated at 0.00/10 (previous run: 2.50/10, -2.50)', + \ ]) diff --git a/test/handler/test_redpen_handler.vader b/test/handler/test_redpen_handler.vader new file mode 100644 index 0000000..f28d692 --- /dev/null +++ b/test/handler/test_redpen_handler.vader @@ -0,0 +1,69 @@ +Before: + runtime! ale_linters/markdown/redpen.vim + +After: + call ale#linter#Reset() + +Execute(redpen handler should handle errors output): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 10, + \ 'end_lnum': 1, + \ 'end_col': 15, + \ 'text': 'Found possibly misspelled word "plugin".', + \ 'type': 'W', + \ 'code': 'Spelling', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'text': 'Found possibly misspelled word "NeoVim".', + \ 'type': 'W', + \ 'code': 'Spelling', + \ }, + \ ], + \ ale#handlers#redpen#HandleRedpenOutput(bufnr(''), [ + \ '[', + \ ' {', + \ ' "document": "test.md",', + \ ' "errors": [', + \ ' {', + \ ' "sentence": "ALE is a plugin for providing linting in NeoVim and Vim 8 while you edit your text files.",', + \ ' "endPosition": {', + \ ' "offset": 15,', + \ ' "lineNum": 1', + \ ' },', + \ ' "validator": "Spelling",', + \ ' "lineNum": 1,', + \ ' "sentenceStartColumnNum": 0,', + \ ' "message": "Found possibly misspelled word \"plugin\".",', + \ ' "startPosition": {', + \ ' "offset": 9,', + \ ' "lineNum": 1', + \ ' }', + \ ' },', + \ ' {', + \ ' "sentence": "ALE is a plugin for providing linting in NeoVim and Vim 8 while you edit your text files.",', + \ ' "validator": "Spelling",', + \ ' "lineNum": 1,', + \ ' "sentenceStartColumnNum": 0,', + \ ' "message": "Found possibly misspelled word \"NeoVim\"."', + \ ' }', + \ ' ]', + \ ' }', + \ ']', + \ ]) + +Execute(redpen handler should no error output): + AssertEqual + \ [], + \ ale#handlers#redpen#HandleRedpenOutput(bufnr(''), [ + \ '[', + \ ' {', + \ ' "document": "test.md",', + \ ' "errors": []', + \ ' }', + \ ']', + \ ]) diff --git a/test/handler/test_reek_handler.vader b/test/handler/test_reek_handler.vader index 6861428..db0a111 100644 --- a/test/handler/test_reek_handler.vader +++ b/test/handler/test_reek_handler.vader @@ -12,17 +12,20 @@ Execute(The reek handler should parse JSON correctly, with only context enabled) \ [ \ { \ 'lnum': 12, - \ 'text': 'Rule1: Context#method violates rule number one', + \ 'text': 'Context#method violates rule number one', + \ 'code': 'Rule1', \ 'type': 'W', \ }, \ { \ 'lnum': 34, - \ 'text': 'Rule2: Context#method violates rule number two', + \ 'text': 'Context#method violates rule number two', + \ 'code': 'Rule2', \ 'type': 'W', \ }, \ { \ 'lnum': 56, - \ 'text': 'Rule2: Context#method violates rule number two', + \ 'text': 'Context#method violates rule number two', + \ 'code': 'Rule2', \ 'type': 'W', \ }, \ ], @@ -38,7 +41,8 @@ Execute(The reek handler should parse JSON correctly, with no context or wiki li \ [ \ { \ 'lnum': 12, - \ 'text': 'Rule1: violates rule number one', + \ 'text': 'violates rule number one', + \ 'code': 'Rule1', \ 'type': 'W', \ }, \ ], @@ -54,7 +58,8 @@ Execute(The reek handler should parse JSON correctly, with both context and wiki \ [ \ { \ 'lnum': 12, - \ 'text': 'Rule1: Context#method violates rule number one [https://example.com/Rule1.md]', + \ 'text': 'Context#method violates rule number one [https://example.com/Rule1.md]', + \ 'code': 'Rule1', \ 'type': 'W', \ }, \ ], diff --git a/test/handler/test_remark_lint_handler.vader b/test/handler/test_remark_lint_handler.vader index f63e0c5..f61da19 100644 --- a/test/handler/test_remark_lint_handler.vader +++ b/test/handler/test_remark_lint_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/markdown/remark_lint.vim +After: + call ale#linter#Reset() + Execute(Warning and error messages should be handled correctly): AssertEqual \ [ diff --git a/test/handler/test_rpmlint_handler.vader b/test/handler/test_rpmlint_handler.vader index 45f5071..2ea9e5c 100644 --- a/test/handler/test_rpmlint_handler.vader +++ b/test/handler/test_rpmlint_handler.vader @@ -1,6 +1,10 @@ -Execute(The rpmlint handler should parse error messages correctly): +Before: runtime ale_linters/spec/rpmlint.vim +After: + call ale#linter#Reset() + +Execute(The rpmlint handler should parse error messages correctly): AssertEqual \ [ \ { diff --git a/test/handler/test_rstcheck_lint_handler.vader b/test/handler/test_rstcheck_lint_handler.vader new file mode 100644 index 0000000..3b4ac03 --- /dev/null +++ b/test/handler/test_rstcheck_lint_handler.vader @@ -0,0 +1,36 @@ +Before: + runtime ale_linters/rstcheck/rstcheck.vim + +After: + call ale#linter#Reset() + +Execute(Warning and error messages should be handled correctly): + AssertEqual + \ [ + \ { + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/bad_python.rst'), + \ 'lnum': 7, + \ 'col': 0, + \ 'type': 'W', + \ 'text': '(python) unexpected EOF while parsing', + \ }, + \ { + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/bad_cpp.rst'), + \ 'lnum': 9, + \ 'col': 0, + \ 'type': 'W', + \ 'text': '(cpp) error: ''x'' was not declared in this scope', + \ }, + \ { + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/bad_rst.rst'), + \ 'lnum': 1, + \ 'col': 0, + \ 'type': 'E', + \ 'text': 'Title overline & underline mismatch.', + \ }, + \ ], + \ ale_linters#rst#rstcheck#Handle(1, [ + \ 'bad_python.rst:7: (ERROR/3) (python) unexpected EOF while parsing', + \ 'bad_cpp.rst:9: (ERROR/3) (cpp) error: ''x'' was not declared in this scope', + \ 'bad_rst.rst:1: (SEVERE/4) Title overline & underline mismatch.', + \]) diff --git a/test/handler/test_rubocop_handler.vader b/test/handler/test_rubocop_handler.vader index 4d3bbe2..ef0137d 100644 --- a/test/handler/test_rubocop_handler.vader +++ b/test/handler/test_rubocop_handler.vader @@ -12,28 +12,32 @@ Execute(The rubocop handler should parse lines correctly): \ 'lnum': 83, \ 'col': 29, \ 'end_col': 35, - \ 'text': 'Prefer single-quoted strings... [Style/SomeCop]', + \ 'text': 'Prefer single-quoted strings...', + \ 'code': 'Style/SomeCop', \ 'type': 'W', \ }, \ { \ 'lnum': 12, \ 'col': 2, \ 'end_col': 2, - \ 'text': 'Some error [Style/SomeOtherCop]', + \ 'text': 'Some error', + \ 'code': 'Style/SomeOtherCop', \ 'type': 'E', \ }, \ { \ 'lnum': 10, \ 'col': 5, \ 'end_col': 12, - \ 'text': 'Regular warning [Style/WarningCop]', + \ 'text': 'Regular warning', + \ 'code': 'Style/WarningCop', \ 'type': 'W', \ }, \ { \ 'lnum': 11, \ 'col': 1, \ 'end_col': 1, - \ 'text': 'Another error [Style/SpaceBeforeBlockBraces]', + \ 'text': 'Another error', + \ 'code': 'Style/SpaceBeforeBlockBraces', \ 'type': 'E', \ }, \ ], diff --git a/test/handler/test_ruby_handler.vader b/test/handler/test_ruby_handler.vader index ba67650..824d8c5 100644 --- a/test/handler/test_ruby_handler.vader +++ b/test/handler/test_ruby_handler.vader @@ -1,5 +1,10 @@ -Execute(The ruby handler should parse lines correctly and add the column if it can): +Before: runtime ale_linters/ruby/ruby.vim + +After: + call ale#linter#Reset() + +Execute(The ruby handler should parse lines correctly and add the column if it can): " Point Error " Warning " Line Error @@ -31,6 +36,3 @@ Execute(The ruby handler should parse lines correctly and add the column if it c \ "test.rb:9: warning: statement not reached", \ "test.rb:12: syntax error, unexpected end-of-input, expecting keyword_end", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_rust_handler.vader b/test/handler/test_rust_handler.vader index a148103..e3ab3e8 100644 --- a/test/handler/test_rust_handler.vader +++ b/test/handler/test_rust_handler.vader @@ -7,16 +7,16 @@ Execute(The Rust handler should handle rustc output): \ 'lnum': 15, \ 'end_lnum': 15, \ 'type': 'E', - \ 'col': 418, - \ 'end_col': 421, + \ 'col': 5, + \ 'end_col': 8, \ 'text': 'expected one of `.`, `;`, `?`, `}`, or an operator, found `for`', \ }, \ { \ 'lnum': 13, \ 'end_lnum': 13, \ 'type': 'E', - \ 'col': 407, - \ 'end_col': 410, + \ 'col': 7, + \ 'end_col': 10, \ 'text': 'no method named `wat` found for type `std::string::String` in the current scope', \ }, \ ], @@ -83,16 +83,16 @@ Execute(The Rust handler should handle cargo output): \ 'lnum': 15, \ 'end_lnum': 15, \ 'type': 'E', - \ 'col': 11505, - \ 'end_col': 11508, + \ 'col': 5, + \ 'end_col': 8, \ 'text': 'expected one of `.`, `;`, `?`, `}`, or an operator, found `for`', \ }, \ { \ 'lnum': 13, \ 'end_lnum': 13, \ 'type': 'E', - \ 'col': 11494, - \ 'end_col': 11497, + \ 'col': 7, + \ 'end_col': 10, \ 'text': 'no method named `wat` found for type `std::string::String` in the current scope', \ }, \ ], @@ -110,7 +110,7 @@ Execute(The Rust handler should handle cargo output): \ 'byte_start': 11505, \ 'column_end': 8, \ 'column_start': 5, - \ 'file_name': ale#path#Winify('src/playpen.rs'), + \ 'file_name': ale#path#Simplify('src/playpen.rs'), \ 'is_primary': v:true, \ 'label': v:null, \ 'line_end': 15, @@ -130,7 +130,7 @@ Execute(The Rust handler should handle cargo output): \ 'byte_start': 11494, \ 'column_end': 10, \ 'column_start': 7, - \ 'file_name': ale#path#Winify('src/playpen.rs'), + \ 'file_name': ale#path#Simplify('src/playpen.rs'), \ 'is_primary': v:true, \ 'label': v:null, \ 'line_end': 13, @@ -157,8 +157,8 @@ Execute(The Rust handler should should errors from expansion spans): \ 'lnum': 4, \ 'end_lnum': 4, \ 'type': 'E', - \ 'col': 52, - \ 'end_col': 54, + \ 'col': 21, + \ 'end_col': 23, \ 'text': 'mismatched types: expected bool, found integral variable', \ }, \ ], @@ -174,7 +174,7 @@ Execute(The Rust handler should should errors from expansion spans): \ 'byte_start': 1, \ 'column_end': 1, \ 'column_start': 1, - \ 'file_name': ale#path#Winify('src/other.rs'), + \ 'file_name': ale#path#Simplify('src/other.rs'), \ 'is_primary': v:true, \ 'label': 'some other error', \ 'line_end': 4, @@ -185,7 +185,7 @@ Execute(The Rust handler should should errors from expansion spans): \ 'byte_start': 52, \ 'column_end': 23, \ 'column_start': 21, - \ 'file_name': ale#path#Winify('src/playpen.rs'), + \ 'file_name': ale#path#Simplify('src/playpen.rs'), \ 'is_primary': v:true, \ 'label': 'expected bool, found integral variable', \ 'line_end': 4, @@ -207,8 +207,8 @@ Execute(The Rust handler should show detailed errors): \ 'lnum': 4, \ 'end_lnum': 4, \ 'type': 'E', - \ 'col': 52, - \ 'end_col': 54, + \ 'col': 21, + \ 'end_col': 23, \ 'text': 'mismatched types: expected bool, found integral variable', \ }, \ ], @@ -227,7 +227,7 @@ Execute(The Rust handler should show detailed errors): \ 'column_end': 23, \ 'column_start': 21, \ 'expansion': v:null, - \ 'file_name': ale#path#Winify('src/playpen.rs'), + \ 'file_name': ale#path#Simplify('src/playpen.rs'), \ 'is_primary': v:true, \ 'label': 'expected bool, found integral variable', \ 'line_end': 4, diff --git a/test/handler/test_scalac_handler.vader b/test/handler/test_scalac_handler.vader index a4c7363..fd222f6 100644 --- a/test/handler/test_scalac_handler.vader +++ b/test/handler/test_scalac_handler.vader @@ -1,8 +1,8 @@ Before: - runtime ale_linters/scala/scalac.vim + runtime ale_linters/scala/scalac.vim After: - call ale#linter#Reset() + call ale#linter#Reset() Given scala(An empty Scala file): diff --git a/test/handler/test_shell_handler.vader b/test/handler/test_shell_handler.vader index ecfbf02..2465f17 100644 --- a/test/handler/test_shell_handler.vader +++ b/test/handler/test_shell_handler.vader @@ -1,9 +1,10 @@ +Before: + runtime ale_linters/sh/shell.vim + After: call ale#linter#Reset() Execute(The shell handler should parse lines correctly): - runtime ale_linters/sh/shell.vim - AssertEqual \ [ \ { diff --git a/test/handler/test_shellcheck_handler.vader b/test/handler/test_shellcheck_handler.vader new file mode 100644 index 0000000..bfb73ff --- /dev/null +++ b/test/handler/test_shellcheck_handler.vader @@ -0,0 +1,43 @@ +Before: + runtime ale_linters/shell/shellcheck.vim + +After: + call ale#linter#Reset() + +Execute(The shellcheck handler should handle basic errors or warnings): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'W', + \ 'text': 'In POSIX sh, ''let'' is not supported.', + \ 'code': 'SC2039', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 3, + \ 'type': 'E', + \ 'text': 'Don''t put spaces around the = in assignments.', + \ 'code': 'SC1068', + \ }, + \ ], + \ ale_linters#sh#shellcheck#Handle(bufnr(''), [ + \ '-:2:1: warning: In POSIX sh, ''let'' is not supported. [SC2039]', + \ '-:2:3: error: Don''t put spaces around the = in assignments. [SC1068]', + \ ]) + +Execute(The shellcheck handler should handle notes): + AssertEqual + \ [ + \ { + \ 'lnum': 3, + \ 'col': 3, + \ 'type': 'I', + \ 'text': 'Double quote to prevent globbing and word splitting.', + \ 'code': 'SC2086', + \ }, + \ ], + \ ale_linters#sh#shellcheck#Handle(bufnr(''), [ + \ '-:3:3: note: Double quote to prevent globbing and word splitting. [SC2086]', + \ ]) diff --git a/test/handler/test_slim_handler.vader b/test/handler/test_slim_handler.vader index 21c1ec9..bfd29f3 100644 --- a/test/handler/test_slim_handler.vader +++ b/test/handler/test_slim_handler.vader @@ -1,18 +1,24 @@ " Author: Markus Doits +Before: + runtime ale_linters/slim/slimlint.vim + +After: + call ale#linter#Reset() Execute(The slim handler should parse lines correctly): - runtime ale_linters/slim/slimlint.vim AssertEqual \ [ \ { \ 'lnum': 1, - \ 'text': 'RedundantDiv: `div` is redundant when class attribute shortcut is present', + \ 'text': '`div` is redundant when class attribute shortcut is present', + \ 'code': 'RedundantDiv', \ 'type': 'W', \ }, \ { \ 'lnum': 2, - \ 'text': 'LineLength: Line is too long. [136/80]', + \ 'text': 'Line is too long. [136/80]', + \ 'code': 'LineLength', \ 'type': 'W', \ }, \ { @@ -26,6 +32,3 @@ Execute(The slim handler should parse lines correctly): \ 'inv.slim:2 [W] LineLength: Line is too long. [136/80]', \ 'inv.slim:3 [E] Invalid syntax', \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_sml_handler.vader b/test/handler/test_sml_handler.vader index f711cc9..90e7c2c 100644 --- a/test/handler/test_sml_handler.vader +++ b/test/handler/test_sml_handler.vader @@ -85,4 +85,3 @@ Execute (Testing a warning): \ "val f = fn : int -> int", \ "-", \]) - diff --git a/test/handler/test_solhint_handler.vader b/test/handler/test_solhint_handler.vader new file mode 100644 index 0000000..a3ed5d5 --- /dev/null +++ b/test/handler/test_solhint_handler.vader @@ -0,0 +1,60 @@ +Before: + runtime ale_linters/solidity/solhint.vim + +After: + call ale#linter#Reset() + +Execute(The vint handler should parse error messages correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 17, + \ 'text': 'Compiler version must be fixed', + \ 'code': 'compiler-fixed', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 4, + \ 'col': 8, + \ 'text': 'Use double quotes for string literals', + \ 'code': 'quotes', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 5, + \ 'col': 8, + \ 'text': 'Use double quotes for string literals', + \ 'code': 'quotes', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 13, + \ 'col': 3, + \ 'text': 'Expected indentation of 4 spaces but found 2', + \ 'code': 'indent', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 14, + \ 'col': 3, + \ 'text': 'Expected indentation of 4 spaces but found 2', + \ 'code': 'indent', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 47, + \ 'col': 3, + \ 'text': 'Function order is incorrect, public function can not go after internal function.', + \ 'code': 'func-order', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#solidity#solhint#Handle(bufnr(''), [ + \ 'contracts/Bounty.sol: line 1, col 17, Warning - Compiler version must be fixed (compiler-fixed)', + \ 'contracts/Bounty.sol: line 4, col 8, Error - Use double quotes for string literals (quotes)', + \ 'contracts/Bounty.sol: line 5, col 8, Error - Use double quotes for string literals (quotes)', + \ 'contracts/Bounty.sol: line 13, col 3, Error - Expected indentation of 4 spaces but found 2 (indent)', + \ 'contracts/Bounty.sol: line 14, col 3, Error - Expected indentation of 4 spaces but found 2 (indent)', + \ 'contracts/Bounty.sol: line 47, col 3, Error - Function order is incorrect, public function can not go after internal function. (func-order)', + \ ]) diff --git a/test/handler/test_sqlint_handler.vader b/test/handler/test_sqlint_handler.vader index 62d2ea7..5567ca4 100644 --- a/test/handler/test_sqlint_handler.vader +++ b/test/handler/test_sqlint_handler.vader @@ -1,6 +1,10 @@ -Execute(The sqlint handler should parse lines correctly): +Before: runtime! ale_linters/sql/sqlint.vim +After: + call ale#linter#Reset() + +Execute(The sqlint handler should parse lines correctly): AssertEqual \ [ \ { @@ -28,6 +32,3 @@ Execute(The sqlint handler should parse lines correctly): \ 'stdin:47:11:ERROR unterminated quoted string at or near "''', \ 'stdin:50:12:WARNING some warning at end of input', \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_stylelint_handler.vader b/test/handler/test_stylelint_handler.vader index 69de1ee..5cb3460 100644 --- a/test/handler/test_stylelint_handler.vader +++ b/test/handler/test_stylelint_handler.vader @@ -10,13 +10,15 @@ Execute (stylelint errors should be handled correctly): \ 'lnum': 108, \ 'col': 10, \ 'type': 'E', - \ 'text': 'Unexpected leading zero [number-leading-zero]', + \ 'text': 'Unexpected leading zero', + \ 'code': 'number-leading-zero', \ }, \ { \ 'lnum': 116, \ 'col': 20, \ 'type': 'E', - \ 'text': 'Expected a trailing semicolon [declaration-block-trailing-semicolon]', + \ 'text': 'Expected a trailing semicolon', + \ 'code': 'declaration-block-trailing-semicolon', \ }, \ ], \ ale#handlers#css#HandleStyleLintFormat(42, [ diff --git a/test/handler/test_swaglint_handler.vader b/test/handler/test_swaglint_handler.vader index e2c2730..7ab1043 100644 --- a/test/handler/test_swaglint_handler.vader +++ b/test/handler/test_swaglint_handler.vader @@ -1,49 +1,59 @@ Before: runtime ale_linters/yaml/swaglint.vim +After: + call ale#linter#Reset() + Execute(The swaglint handler should parse lines correctly): AssertEqual \ [ \ { \ 'lnum': 1, \ 'col': 1, - \ 'text': 'Missing required property: info (sway_object_missing_required_property)', + \ 'text': 'Missing required property: info', + \ 'code': 'sway_object_missing_required_property', \ 'type': 'E', \ }, \ { \ 'lnum': 6, \ 'col': 9, - \ 'text': 'Not a valid response definition (sway_one_of_missing)', + \ 'text': 'Not a valid response definition', + \ 'code': 'sway_one_of_missing', \ 'type': 'E', \ }, \ { \ 'lnum': 7, \ 'col': 11, - \ 'text': 'Missing required property: description (sway_object_missing_required_property)', + \ 'text': 'Missing required property: description', + \ 'code': 'sway_object_missing_required_property', \ 'type': 'E', \ }, \ { \ 'lnum': 7, \ 'col': 11, - \ 'text': 'Missing required property: $ref (sway_object_missing_required_property)', + \ 'text': 'Missing required property: $ref', + \ 'code': 'sway_object_missing_required_property', \ 'type': 'E', \ }, \ { \ 'lnum': 1, \ 'col': 10, - \ 'text': 'Expected type string but found type integer (sway_invalid_type)', + \ 'text': 'Expected type string but found type integer', + \ 'code': 'sway_invalid_type', \ 'type': 'E', \ }, \ { \ 'lnum': 1, \ 'col': 10, - \ 'text': 'No enum match for: 2 (sway_enum_mismatch)', + \ 'text': 'No enum match for: 2', + \ 'code': 'sway_enum_mismatch', \ 'type': 'E', \ }, \ { \ 'lnum': 14, \ 'col': 3, - \ 'text': 'Definition is not used: #/definitions/Foo (sway_unused_definition)', + \ 'text': 'Definition is not used: #/definitions/Foo', + \ 'code': 'sway_unused_definition', \ 'type': 'W', \ }, \ ], diff --git a/test/handler/test_swiftlint_handler.vader b/test/handler/test_swiftlint_handler.vader index b77b442..725ff97 100644 --- a/test/handler/test_swiftlint_handler.vader +++ b/test/handler/test_swiftlint_handler.vader @@ -1,21 +1,29 @@ +Before: + runtime ale_linters/swift/swiftlint.vim + +After: + call ale#linter#Reset() + Execute(The swiftint handler should parse error messages correctly): AssertEqual \ [ \ { \ 'lnum': 1, \ 'col': 7, - \ 'text': 'Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used. (operator_usage_whitespace)', + \ 'text': 'Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used.', + \ 'code': 'operator_usage_whitespace', \ 'type': 'W', \ }, \ { \ 'lnum': 1, \ 'col': 11, - \ 'text': 'Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used. (operator_usage_whitespace)', + \ 'text': 'Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used.', + \ 'code': 'operator_usage_whitespace', \ 'type': 'W', \ }, \ \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale_linters#swift#swiftlint#Handle(bufnr(''), [ \ 'This line should be ignored', \ ':1:7: warning: Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used. (operator_usage_whitespace)', \ ':1:11: warning: Operator Usage Whitespace Violation: Operators should be surrounded by a single whitespace when they are being used. (operator_usage_whitespace)', diff --git a/test/handler/test_tflint_handler.vader b/test/handler/test_tflint_handler.vader new file mode 100644 index 0000000..099d092 --- /dev/null +++ b/test/handler/test_tflint_handler.vader @@ -0,0 +1,31 @@ +Before: + runtime! ale_linters/terraform/tflint.vim + +After: + call ale#linter#Reset() + +Execute(The tflint handler should parse items correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 12, + \ 'text': 'be warned, traveller', + \ 'code': 'aws_db_instance_readable_password', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 9, + \ 'text': 'error message', + \ 'code': 'aws_elasticache_cluster_invalid_type', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 5, + \ 'text': 'just so ya know', + \ 'code': 'aws_instance_not_specified_iam_profile', + \ 'type': 'I', + \ }, + \ ], + \ ale_linters#terraform#tflint#Handle(123, [ + \ '[ { "detector": "aws_db_instance_readable_password", "type": "WARNING", "message": "be warned, traveller", "line": 12, "file": "github.com/wata727/example-module/aws_db_instance.tf", "link": "https://github.com/wata727/tflint/blob/master/docs/aws_db_instance_readable_password.md" }, { "detector": "aws_elasticache_cluster_invalid_type", "type": "ERROR", "message": "error message", "line": 9, "file": "github.com/wata727/example-module/aws_elasticache_cluster.tf", "link": "https://github.com/wata727/tflint/blob/master/docs/aws_elasticache_cluster_invalid_type.md" }, { "detector": "aws_instance_not_specified_iam_profile", "type": "NOTICE", "message": "just so ya know", "line": 5, "file": "github.com/wata727/example-module/aws_instance.tf", "link": "https://github.com/wata727/tflint/blob/master/docs/aws_instance_not_specified_iam_profile.md" } ]' + \ ]) diff --git a/test/handler/test_tslint_handler.vader b/test/handler/test_tslint_handler.vader index 2ed3357..32036ed 100644 --- a/test/handler/test_tslint_handler.vader +++ b/test/handler/test_tslint_handler.vader @@ -12,6 +12,9 @@ After: Restore unlet! b:ale_typescript_tslint_ignore_empty_files + unlet! b:relative_to_root + unlet! b:tempname_suffix + unlet! b:relative_tempname call ale#test#RestoreDirectory() call ale#linter#Reset() @@ -24,38 +27,41 @@ Execute(The tslint handler should parse lines correctly): \ { \ 'lnum': 1, \ 'col': 15, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/test.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.ts'), \ 'end_lnum': 1, \ 'type': 'E', \ 'end_col': 15, - \ 'text': 'semicolon: Missing semicolon' + \ 'text': 'Missing semicolon', + \ 'code': 'semicolon', \ }, \ { \ 'lnum': 2, \ 'col': 8, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/test.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.ts'), \ 'end_lnum': 3, \ 'type': 'W', \ 'end_col': 12, - \ 'text': 'Something else' + \ 'text': 'Something else', \ }, \ { \ 'lnum': 2, \ 'col': 8, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/something-else.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/something-else.ts'), \ 'end_lnum': 3, \ 'type': 'W', \ 'end_col': 12, - \ 'text': 'something: Something else' + \ 'text': 'Something else', + \ 'code': 'something', \ }, \ { \ 'lnum': 31, \ 'col': 9, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/test.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.ts'), \ 'end_lnum': 31, \ 'type': 'E', \ 'end_col': 20, - \ 'text': 'no-console: Calls to console.log are not allowed.' + \ 'text': 'Calls to console.log are not allowed.', + \ 'code': 'no-console', \ }, \ ] , \ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([ @@ -151,11 +157,12 @@ Execute(The tslint handler report errors for empty files by default): \ { \ 'lnum': 2, \ 'col': 1, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/test.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.ts'), \ 'end_lnum': 2, \ 'type': 'E', \ 'end_col': 1, - \ 'text': 'no-consecutive-blank-lines: Consecutive blank lines are forbidden', + \ 'text': 'Consecutive blank lines are forbidden', + \ 'code': 'no-consecutive-blank-lines', \ }, \ ], \ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([{ @@ -224,11 +231,12 @@ Execute(The tslint handler should report errors when the ignore option is on, bu \ { \ 'lnum': 2, \ 'col': 1, - \ 'filename': ale#path#Winify(expand('%:p:h') . '/test.ts'), + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/test.ts'), \ 'end_lnum': 2, \ 'type': 'E', \ 'end_col': 1, - \ 'text': 'no-consecutive-blank-lines: Consecutive blank lines are forbidden', + \ 'text': 'Consecutive blank lines are forbidden', + \ 'code': 'no-consecutive-blank-lines', \ }, \ ], \ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([{ @@ -252,3 +260,56 @@ Execute(The tslint handler should report errors when the ignore option is on, bu \ 'position': 1 \ } \ }])]) + +Execute(The tslint handler should not report no-implicit-dependencies errors): + call ale#test#SetFilename('app/test.ts') + + AssertEqual + \ [ + \ ], + \ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([{ + \ 'endPosition': { + \ 'character': 0, + \ 'line': 1, + \ 'position': 1 + \ }, + \ 'failure': 'this is ignored', + \ 'name': 'test.ts', + \ 'ruleName': 'no-implicit-dependencies', + \ 'ruleSeverity': 'ERROR', + \ 'startPosition': { + \ 'character': 0, + \ 'line': 1, + \ 'position': 1 + \ }, + \ }])]) + +Execute(The tslint handler should set filename keys for temporary files): + " The temporay filename below is hacked into being a relative path so we can + " test that we resolve the temporary filename first. + let b:relative_to_root = substitute(expand('%:p'), '\v[^/\\]*([/\\])[^/\\]*', '../', 'g') + let b:tempname_suffix = substitute(tempname(), '^\v([A-Z]:)?[/\\]', '', '') + let b:relative_tempname = substitute(b:relative_to_root . b:tempname_suffix, '\\', '/', 'g') + + AssertEqual + \ [ + \ {'lnum': 47, 'col': 1, 'code': 'curly', 'end_lnum': 47, 'type': 'E', 'end_col': 26, 'text': 'if statements must be braced'}, + \ ], + \ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([ + \ { + \ 'endPosition': { + \ 'character':25, + \ 'line':46, + \ 'position':1383, + \ }, + \ 'failure': 'if statements must be braced', + \ 'name': b:relative_tempname, + \ 'ruleName': 'curly', + \ 'ruleSeverity':'ERROR', + \ 'startPosition': { + \ 'character':0, + \ 'line':46, + \ 'position':1358, + \ } + \ }, + \ ])]) diff --git a/test/handler/test_typecheck_handler.vader b/test/handler/test_typecheck_handler.vader index cf93798..fda55d6 100644 --- a/test/handler/test_typecheck_handler.vader +++ b/test/handler/test_typecheck_handler.vader @@ -1,6 +1,10 @@ -Execute(The typecheck handler should parse lines correctly): +Before: runtime ale_linters/typescript/typecheck.vim +After: + call ale#linter#Reset() + +Execute(The typecheck handler should parse lines correctly): AssertEqual \ [ \ { @@ -18,6 +22,3 @@ Execute(The typecheck handler should parse lines correctly): \ "somets.ts[16, 7]: Type 'A' is not assignable to type 'B'", \ "somets.ts[7, 41]: Property 'a' does not exist on type 'A'", \ ]) - -After: - call ale#linter#Reset() diff --git a/test/handler/test_vale_handler.vader b/test/handler/test_vale_handler.vader new file mode 100644 index 0000000..37badb4 --- /dev/null +++ b/test/handler/test_vale_handler.vader @@ -0,0 +1,88 @@ +Execute(The vale handler should handle broken JSON): + AssertEqual + \ [], + \ ale#handlers#vale#Handle(bufnr(''), ["{asdf"]) + +Execute(The vale handler should handle am empty string response): + AssertEqual + \ [], + \ ale#handlers#vale#Handle(bufnr(''), []) + +Execute(The vale handler should handle an empty result): + AssertEqual + \ [], + \ ale#handlers#vale#Handle(bufnr(''), ["{}"]) + +Execute(The vale handler should handle a normal example): + AssertEqual + \ [ + \ { + \ 'lnum': 5, + \ 'col': 195, + \ 'end_col': 201, + \ 'type': 'W', + \ 'text': "Consider removing 'usually'", + \ 'code': 'vale.Hedging', + \ }, + \ { + \ 'lnum': 7, + \ 'col': 1, + \ 'end_col': 27, + \ 'type': 'E', + \ 'text': "'Documentation' is repeated!", + \ 'code': 'vale.Repetition', + \ }, + \ { + \ 'lnum': 7, + \ 'col': 1, + \ 'end_col': 27, + \ 'type': 'I', + \ 'text': "'Documentation' is repeated!", + \ 'code': 'vale.Repetition', + \ }, + \ ], + \ ale#handlers#vale#Handle(bufnr(''), [ + \ '{', + \ ' "/home/languitar/src/autosuspend/README.md": [', + \ ' {', + \ ' "Check": "vale.Hedging",', + \ ' "Description": "",', + \ ' "Line": 5,', + \ ' "Link": "",', + \ " \"Message\": \"Consider removing 'usually'\",", + \ ' "Severity": "warning",', + \ ' "Span": [', + \ ' 195,', + \ ' 201', + \ ' ],', + \ ' "Hide": false', + \ ' },', + \ ' {', + \ ' "Check": "vale.Repetition",', + \ ' "Description": "",', + \ ' "Line": 7,', + \ ' "Link": "",', + \ " \"Message\": \"'Documentation' is repeated!\",", + \ ' "Severity": "error",', + \ ' "Span": [', + \ ' 1,', + \ ' 27', + \ ' ],', + \ ' "Hide": false', + \ ' },', + \ ' {', + \ ' "Check": "vale.Repetition",', + \ ' "Description": "",', + \ ' "Line": 7,', + \ ' "Link": "",', + \ " \"Message\": \"'Documentation' is repeated!\",", + \ ' "Severity": "suggestion",', + \ ' "Span": [', + \ ' 1,', + \ ' 27', + \ ' ],', + \ ' "Hide": false', + \ ' }', + \ ' ]', + \ '}', + \ ]) diff --git a/test/handler/test_vint_handler.vader b/test/handler/test_vint_handler.vader index 8747979..c542b4e 100644 --- a/test/handler/test_vint_handler.vader +++ b/test/handler/test_vint_handler.vader @@ -10,12 +10,14 @@ Execute(The vint handler should parse error messages correctly): \ { \ 'lnum': 1, \ 'col': 1, + \ 'filename': 'gcc.vim', \ 'text': 'Use scriptencoding when multibyte char exists (see :help :script encoding)', \ 'type': 'W', \ }, \ { \ 'lnum': 3, \ 'col': 17, + \ 'filename': 'gcc.vim', \ 'end_col': 18, \ 'text': 'Use robust operators ''==#'' or ''==?'' instead of ''=='' (see Google VimScript Style Guide (Matching))', \ 'type': 'W', @@ -23,6 +25,7 @@ Execute(The vint handler should parse error messages correctly): \ { \ 'lnum': 3, \ 'col': 8, + \ 'filename': 'gcc.vim', \ 'end_col': 15, \ 'text': 'Make the scope explicit like ''l:filename'' (see Anti-pattern of vimrc (Scope of identifier))', \ 'type': 'W', @@ -30,6 +33,7 @@ Execute(The vint handler should parse error messages correctly): \ { \ 'lnum': 7, \ 'col': 8, + \ 'filename': 'gcc.vim', \ 'end_col': 15, \ 'text': 'Undefined variable: filename (see :help E738)', \ 'type': 'W', @@ -37,6 +41,7 @@ Execute(The vint handler should parse error messages correctly): \ { \ 'lnum': 8, \ 'col': 11, + \ 'filename': 'gcc.vim', \ 'end_col': 16, \ 'text': 'E128: Function name must start with a capital or contain a colon: foobar (see ynkdir/vim-vimlparser)', \ 'type': 'E', @@ -44,6 +49,7 @@ Execute(The vint handler should parse error messages correctly): \ { \ 'lnum': 9, \ 'col': 12, + \ 'filename': 'gcc.vim', \ 'end_col': 13, \ 'text': 'Use robust operators ''=~#'' or ''=~?'' instead of ''=~'' (see Google VimScript Style Guide (Matching))', \ 'type': 'W', diff --git a/test/handler/test_write_good_handler.vader b/test/handler/test_write_good_handler.vader new file mode 100644 index 0000000..8bf4b22 --- /dev/null +++ b/test/handler/test_write_good_handler.vader @@ -0,0 +1,37 @@ +Execute(The write-good handler should handle the example from the write-good README): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'end_col': 2, + \ 'type': 'W', + \ 'text': '"So" adds no meaning', + \ }, + \ { + \ 'lnum': 1, + \ 'col': 12, + \ 'end_col': 21, + \ 'type': 'W', + \ 'text': '"was stolen" may be passive voice', + \ }, + \ { + \ 'lnum': 6, + \ 'col': 2, + \ 'end_col': 2, + \ 'type': 'W', + \ 'text': '"foo bar" bla', + \ }, + \ ], + \ ale#handlers#writegood#Handle(bufnr(''), [ + \ 'In /tmp/vBYivbZ/6/test.md', + \ '=============', + \ 'So the cat was stolen.', + \ '^^', + \ '"So" adds no meaning on line 1 at column 0', + \ '-------------', + \ 'So the cat was stolen.', + \ ' ^^^^^^^^^^', + \ '"was stolen" may be passive voice on line 1 at column 11', + \ '"foo bar" bla on line 6 at column 1', + \ ]) diff --git a/test/handler/test_xmllint_handler.vader b/test/handler/test_xmllint_handler.vader index 4a377ab..a17d74a 100644 --- a/test/handler/test_xmllint_handler.vader +++ b/test/handler/test_xmllint_handler.vader @@ -1,6 +1,9 @@ Before: runtime ale_linters/xml/xmllint.vim +After: + call ale#linter#Reset() + Execute(The xmllint handler should parse error messages correctly): AssertEqual \ [ @@ -25,6 +28,3 @@ Execute(The xmllint handler should parse error messages correctly): \ 'blahblah>', \ '^' \ ]) - -After: - call ale#linter#Reset() diff --git a/test/javascript_files/test.js b/test/javascript_files/test.js new file mode 100644 index 0000000..e69de29 diff --git a/test/json_files/testfile.json b/test/json_files/testfile.json new file mode 100644 index 0000000..fe317eb --- /dev/null +++ b/test/json_files/testfile.json @@ -0,0 +1 @@ +{"answer":42} diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader new file mode 100644 index 0000000..042a3ce --- /dev/null +++ b/test/lsp/test_did_save_event.vader @@ -0,0 +1,108 @@ +Before: + Save g:ale_lint_on_save + Save g:ale_enabled + Save g:ale_linters + Save g:ale_run_synchronously + + call ale#test#SetDirectory('/testplugin/test/completion') + call ale#test#SetFilename('dummy.txt') + + runtime autoload/ale/lsp.vim + + let g:ale_lint_on_save = 1 + let b:ale_enabled = 1 + let g:ale_lsp_next_message_id = 1 + let g:ale_run_synchronously = 1 + let g:message_list = [] + let g:Callback = '' + + function! LanguageCallback() abort + return 'foobar' + endfunction + + function! ProjectRootCallback() abort + return expand('.') + endfunction + + call ale#linter#Define('foobar', { + \ 'name': 'dummy_linter', + \ 'lsp': 'stdio', + \ 'command': 'cat - > /dev/null', + \ 'executable': has('win32') ? 'cmd' : 'echo', + \ 'language_callback': 'LanguageCallback', + \ 'project_root_callback': 'ProjectRootCallback', + \ }) + let g:ale_linters = {'foobar': ['dummy_linter']} + + function! ale#linter#StartLSP(buffer, linter, callback) abort + let g:Callback = a:callback + + return { + \ 'connection_id': 347, + \ 'project_root': '/foo/bar', + \} + endfunction + + " Replace the Send function for LSP, so we can monitor calls to it. + function! ale#lsp#Send(conn_id, message, ...) abort + call add(g:message_list, a:message) + endfunction + +After: + Restore + + unlet! b:ale_enabled + unlet! b:ale_linters + unlet! g:Callback + unlet! g:message_list + + delfunction LanguageCallback + delfunction ProjectRootCallback + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + " Stop any timers we left behind. + " This stops the tests from failing randomly. + call ale#completion#StopTimer() + + runtime autoload/ale/completion.vim + runtime autoload/ale/lsp.vim + +Given foobar (Some imaginary filetype): + + +Execute(Server should be notified on save): + call ale#events#SaveEvent(bufnr('')) + + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}], + \ }], + \ [1, 'textDocument/didSave', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ }, + \ }], + \ ], + \ g:message_list + +Execute(Server should be notified on change): + call ale#events#FileChangedEvent(bufnr('')) + + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}], + \ }], + \ ], + \ g:message_list diff --git a/test/lsp/test_lsp_client_messages.vader b/test/lsp/test_lsp_client_messages.vader index 7ec905c..053da80 100644 --- a/test/lsp/test_lsp_client_messages.vader +++ b/test/lsp/test_lsp_client_messages.vader @@ -45,7 +45,7 @@ Execute(ale#lsp#message#DidOpen() should return correct messages): \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'), \ 'languageId': 'typescript', \ 'version': 12, - \ 'text': "foo()\nbar()\nbaz()", + \ 'text': "foo()\nbar()\nbaz()\n", \ }, \ } \ ], @@ -63,7 +63,7 @@ Execute(ale#lsp#message#DidChange() should return correct messages): \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'), \ 'version': 34, \ }, - \ 'contentChanges': [{'text': "foo()\nbar()\nbaz()"}], + \ 'contentChanges': [{'text': "foo()\nbar()\nbaz()\n"}], \ } \ ], \ ale#lsp#message#DidChange(bufnr('')) @@ -101,13 +101,56 @@ Execute(ale#lsp#message#DidClose() should return correct messages): \ ], \ ale#lsp#message#DidClose(bufnr('')) +Execute(ale#lsp#message#Completion() should return correct messages): + AssertEqual + \ [ + \ 0, + \ 'textDocument/completion', + \ { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'), + \ }, + \ 'position': {'line': 11, 'character': 34}, + \ } + \ ], + \ ale#lsp#message#Completion(bufnr(''), 12, 34, '') + +Execute(ale#lsp#message#Completion() should return correct messages with a trigger charaacter): + AssertEqual + \ [ + \ 0, + \ 'textDocument/completion', + \ { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'), + \ }, + \ 'position': {'line': 11, 'character': 34}, + \ 'context': {'triggerKind': 2, 'triggerCharacter': '.'}, + \ } + \ ], + \ ale#lsp#message#Completion(bufnr(''), 12, 34, '.') + \ +Execute(ale#lsp#message#Definition() should return correct messages): + AssertEqual + \ [ + \ 0, + \ 'textDocument/definition', + \ { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'), + \ }, + \ 'position': {'line': 11, 'character': 34}, + \ } + \ ], + \ ale#lsp#message#Definition(bufnr(''), 12, 34) + Execute(ale#lsp#tsserver_message#Open() should return correct messages): AssertEqual \ [ \ 1, \ 'ts@open', \ { - \ 'file': ale#path#Winify(g:dir . '/foo/bar.ts'), + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), \ } \ ], \ ale#lsp#tsserver_message#Open(bufnr('')) @@ -118,7 +161,7 @@ Execute(ale#lsp#tsserver_message#Close() should return correct messages): \ 1, \ 'ts@close', \ { - \ 'file': ale#path#Winify(g:dir . '/foo/bar.ts'), + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), \ } \ ], \ ale#lsp#tsserver_message#Close(bufnr('')) @@ -129,12 +172,12 @@ Execute(ale#lsp#tsserver_message#Change() should return correct messages): \ 1, \ 'ts@change', \ { - \ 'file': ale#path#Winify(g:dir . '/foo/bar.ts'), + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), \ 'line': 1, \ 'offset': 1, \ 'endLine': 1073741824, \ 'endOffset': 1, - \ 'insertString': "foo()\nbar()\nbaz()", + \ 'insertString': "foo()\nbar()\nbaz()\n", \ } \ ], \ ale#lsp#tsserver_message#Change(bufnr('')) @@ -145,7 +188,7 @@ Execute(ale#lsp#tsserver_message#Geterr() should return correct messages): \ 1, \ 'ts@geterr', \ { - \ 'files': [ale#path#Winify(g:dir . '/foo/bar.ts')], + \ 'files': [ale#path#Simplify(g:dir . '/foo/bar.ts')], \ } \ ], \ ale#lsp#tsserver_message#Geterr(bufnr('')) @@ -156,7 +199,7 @@ Execute(ale#lsp#tsserver_message#Completions() should return correct messages): \ 0, \ 'ts@completions', \ { - \ 'file': ale#path#Winify(g:dir . '/foo/bar.ts'), + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), \ 'line': 347, \ 'offset': 12, \ 'prefix': 'abc', @@ -170,10 +213,23 @@ Execute(ale#lsp#tsserver_message#CompletionEntryDetails() should return correct \ 0, \ 'ts@completionEntryDetails', \ { - \ 'file': ale#path#Winify(g:dir . '/foo/bar.ts'), + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), \ 'line': 347, \ 'offset': 12, \ 'entryNames': ['foo', 'bar'], \ } \ ], \ ale#lsp#tsserver_message#CompletionEntryDetails(bufnr(''), 347, 12, ['foo', 'bar']) + +Execute(ale#lsp#tsserver_message#Definition() should return correct messages): + AssertEqual + \ [ + \ 0, + \ 'ts@definition', + \ { + \ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'), + \ 'line': 347, + \ 'offset': 12, + \ } + \ ], + \ ale#lsp#tsserver_message#Definition(bufnr(''), 347, 12) diff --git a/test/lsp/test_lsp_connections.vader b/test/lsp/test_lsp_connections.vader index 5549b1f..8651d80 100644 --- a/test/lsp/test_lsp_connections.vader +++ b/test/lsp/test_lsp_connections.vader @@ -26,7 +26,7 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 1, \ "Content-Length: 79\r\n\r\n" - \ . '{"id": 1, "jsonrpc": "2.0", "method": "someMethod", "params": {"foo": "barÜ"}}', + \ . '{"method": "someMethod", "jsonrpc": "2.0", "id": 1, "params": {"foo": "barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) " Check again to ensure that we use the next ID. @@ -34,7 +34,7 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 2, \ "Content-Length: 79\r\n\r\n" - \ . '{"id": 2, "jsonrpc": "2.0", "method": "someMethod", "params": {"foo": "barÜ"}}', + \ . '{"method": "someMethod", "jsonrpc": "2.0", "id": 2, "params": {"foo": "barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) else @@ -42,14 +42,14 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 1, \ "Content-Length: 71\r\n\r\n" - \ . '{"id":1,"jsonrpc":"2.0","method":"someMethod","params":{"foo":"barÜ"}}', + \ . '{"method":"someMethod","jsonrpc":"2.0","id":1,"params":{"foo":"barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) AssertEqual \ [ \ 2, \ "Content-Length: 71\r\n\r\n" - \ . '{"id":2,"jsonrpc":"2.0","method":"someMethod","params":{"foo":"barÜ"}}', + \ . '{"method":"someMethod","jsonrpc":"2.0","id":2,"params":{"foo":"barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) endif @@ -60,7 +60,7 @@ Execute(ale#lsp#CreateMessageData() should create messages without params): \ [ \ 1, \ "Content-Length: 56\r\n\r\n" - \ . '{"id": 1, "jsonrpc": "2.0", "method": "someOtherMethod"}', + \ . '{"method": "someOtherMethod", "jsonrpc": "2.0", "id": 1}', \ ], \ ale#lsp#CreateMessageData([0, 'someOtherMethod']) else @@ -68,7 +68,7 @@ Execute(ale#lsp#CreateMessageData() should create messages without params): \ [ \ 1, \ "Content-Length: 51\r\n\r\n" - \ . '{"id":1,"jsonrpc":"2.0","method":"someOtherMethod"}', + \ . '{"method":"someOtherMethod","jsonrpc":"2.0","id":1}', \ ], \ ale#lsp#CreateMessageData([0, 'someOtherMethod']) endif @@ -78,30 +78,30 @@ Execute(ale#lsp#CreateMessageData() should create notifications): AssertEqual \ [ \ 0, - \ "Content-Length: 60\r\n\r\n" - \ . '{"id": null, "jsonrpc": "2.0", "method": "someNotification"}', + \ "Content-Length: 48\r\n\r\n" + \ . '{"method": "someNotification", "jsonrpc": "2.0"}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification']) AssertEqual \ [ \ 0, - \ "Content-Length: 86\r\n\r\n" - \ . '{"id": null, "jsonrpc": "2.0", "method": "someNotification", "params": {"foo": "bar"}}', + \ "Content-Length: 74\r\n\r\n" + \ . '{"method": "someNotification", "jsonrpc": "2.0", "params": {"foo": "bar"}}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification', {'foo': 'bar'}]) else AssertEqual \ [ \ 0, - \ "Content-Length: 55\r\n\r\n" - \ . '{"id":null,"jsonrpc":"2.0","method":"someNotification"}', + \ "Content-Length: 45\r\n\r\n" + \ . '{"method":"someNotification","jsonrpc":"2.0"}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification']) AssertEqual \ [ \ 0, - \ "Content-Length: 78\r\n\r\n" - \ . '{"id":null,"jsonrpc":"2.0","method":"someNotification","params":{"foo":"bar"}}', + \ "Content-Length: 68\r\n\r\n" + \ . '{"method":"someNotification","jsonrpc":"2.0","params":{"foo":"bar"}}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification', {'foo': 'bar'}]) endif diff --git a/test/lsp/test_read_lsp_diagnostics.vader b/test/lsp/test_read_lsp_diagnostics.vader index 3e63741..444272a 100644 --- a/test/lsp/test_read_lsp_diagnostics.vader +++ b/test/lsp/test_read_lsp_diagnostics.vader @@ -121,7 +121,8 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages): \ ]}}) Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle tsserver responses): - AssertEqual [ + AssertEqual + \ [ \ { \ 'type': 'E', \ 'nr': 2365, @@ -131,5 +132,35 @@ Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle tsserver respon \ 'end_lnum': 1, \ 'end_col': 17, \ }, - \], + \ ], \ ale#lsp#response#ReadTSServerDiagnostics({"seq":0,"type":"event","event":"semanticDiag","body":{"file":"/bar/foo.ts","diagnostics":[{"start":{"line":1,"offset":11},"end":{"line":1,"offset":17},"text":"Operator ''+'' cannot be applied to types ''3'' and ''{}''.","code":2365}]}}) + +Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle warnings from tsserver): + AssertEqual + \ [ + \ { + \ 'lnum': 27, + \ 'col': 3, + \ 'nr': 2515, + \ 'end_lnum': 27, + \ 'type': 'W', + \ 'end_col': 14, + \ 'text': 'Calls to ''console.log'' are not allowed. (no-console)', + \ } + \ ], + \ ale#lsp#response#ReadTSServerDiagnostics({"seq":0,"type":"event","event":"semanticDiag","body":{"file":"","diagnostics":[{"start":{"line":27,"offset":3},"end":{"line":27,"offset":14},"text":"Calls to 'console.log' are not allowed. (no-console)","code":2515,"category":"warning","source":"tslint"}]}}) + +Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle suggestions from tsserver): + AssertEqual + \ [ + \ { + \ 'lnum': 27, + \ 'col': 3, + \ 'nr': 2515, + \ 'end_lnum': 27, + \ 'type': 'I', + \ 'end_col': 14, + \ 'text': 'Some info', + \ } + \ ], + \ ale#lsp#response#ReadTSServerDiagnostics({"seq":0,"type":"event","event":"semanticDiag","body":{"file":"","diagnostics":[{"start":{"line":27,"offset":3},"end":{"line":27,"offset":14},"text":"Some info","code":2515,"category":"suggestion","source":"tslint"}]}}) diff --git a/test/lsp/test_reset_lsp.vader b/test/lsp/test_reset_lsp.vader new file mode 100644 index 0000000..2bec13d --- /dev/null +++ b/test/lsp/test_reset_lsp.vader @@ -0,0 +1,90 @@ +Before: + Save g:ale_enabled + Save g:ale_set_signs + Save g:ale_set_quickfix + Save g:ale_set_loclist + Save g:ale_set_highlights + Save g:ale_echo_cursor + + let g:ale_enabled = 0 + let g:ale_set_signs = 0 + let g:ale_set_quickfix = 0 + let g:ale_set_loclist = 0 + let g:ale_set_highlights = 0 + let g:ale_echo_cursor = 0 + + function EmptyString() abort + return '' + endfunction + + call ale#engine#InitBufferInfo(bufnr('')) + + call ale#linter#Define('testft', { + \ 'name': 'lsplinter', + \ 'lsp': 'tsserver', + \ 'executable_callback': 'EmptyString', + \ 'command_callback': 'EmptyString', + \ 'project_root_callback': 'EmptyString', + \ 'language_callback': 'EmptyString', + \}) + + call ale#linter#Define('testft', { + \ 'name': 'otherlinter', + \ 'callback': 'TestCallback', + \ 'executable': has('win32') ? 'cmd': 'true', + \ 'command': 'true', + \ 'read_buffer': 0, + \}) + +After: + Restore + + unlet! b:ale_save_event_fired + + delfunction EmptyString + call ale#linter#Reset() + +Given testft(Some file with an imaginary filetype): +Execute(ALEStopAllLSPs should clear the loclist): + let g:ale_buffer_info[bufnr('')].loclist = [ + \ { + \ 'text': 'a', + \ 'lnum': 10, + \ 'col': 0, + \ 'bufnr': bufnr(''), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'lsplinter', + \ }, + \ { + \ 'text': 'a', + \ 'lnum': 10, + \ 'col': 0, + \ 'bufnr': bufnr(''), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'otherlinter', + \ }, + \] + let g:ale_buffer_info[bufnr('')].active_linter_list = ['lsplinter', 'otherlinter'] + + ALEStopAllLSPs + + " The loclist should be updated. + AssertEqual g:ale_buffer_info[bufnr('')].loclist, [ + \ { + \ 'text': 'a', + \ 'lnum': 10, + \ 'col': 0, + \ 'bufnr': bufnr(''), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'otherlinter', + \ }, + \] + + " The LSP linter should be removed from the active linter list. + AssertEqual g:ale_buffer_info[bufnr('')].active_linter_list, ['otherlinter'] diff --git a/test/reasonml_files/testfile.re b/test/reasonml_files/testfile.re new file mode 100644 index 0000000..e69de29 diff --git a/test/ruby_fixtures/valid_rails_app/app/views/my_great_view.html.erb b/test/ruby_fixtures/valid_rails_app/app/views/my_great_view.html.erb new file mode 100644 index 0000000..e69de29 diff --git a/test/rust_files/testfile.rs b/test/rust_files/testfile.rs new file mode 100644 index 0000000..e69de29 diff --git a/test/script/check-supported-tools-tables b/test/script/check-supported-tools-tables index 1d0fec5..32cebb2 100755 --- a/test/script/check-supported-tools-tables +++ b/test/script/check-supported-tools-tables @@ -30,30 +30,30 @@ readme_section_size="$( \ # shellcheck disable=SC2003 readme_end_line="$(expr "$readme_start_line" + "$readme_section_size")" -doc_file="$(mktemp)" -readme_file="$(mktemp)" +doc_file="$(mktemp -t doc.XXXXXXXX)" +readme_file="$(mktemp -t readme.XXXXXXXX)" sed -n "$ale_help_start_line,$ale_help_end_line"p doc/ale.txt \ | grep '\* .*: ' \ | sed 's/^*//' \ - | sed 's/[`!^]\|([^)]*)//g' \ + | sed 's/[`!^]//g;s/([^)]*)//g' \ | sed 's/ *\([,:]\)/\1/g' \ | sed 's/ */ /g' \ - | sed 's/^ *\| *$//g' \ + | sed 's/^ *//;s/ *$//' \ | sed 's/^/ /' \ > "$doc_file" sed -n "$readme_start_line,$readme_end_line"p README.md \ | grep '| .* |' \ - | sed '/^| Language\|^| ---/d' \ + | sed '/^| Language/d;/^| ---/d' \ | sed 's/^|//' \ - | sed 's/ \?|/:/' \ - | sed 's/[`!^|]\|([^)]*)//g' \ - | sed 's/\[\|\]//g' \ - | sed 's/see[^,]*\(,\|$\)/\1/g' \ + | sed 's/ \{0,1\}|/:/' \ + | sed 's/[`!^|]//g;s/([^)]*)//g' \ + | sed 's/\[//g;s/\]//g' \ + | sed 's/see[^,]*//g' \ | sed 's/ *\([,:]\)/\1/g' \ | sed 's/ */ /g' \ - | sed 's/^ *\| *$//g' \ + | sed 's/^ *//;s/ *$//' \ | sed 's/^/ /' \ | sed 's/ *-n flag//g' \ > "$readme_file" diff --git a/test/script/check-toc b/test/script/check-toc index c4512b0..cc2d2b9 100755 --- a/test/script/check-toc +++ b/test/script/check-toc @@ -23,18 +23,19 @@ tagged_toc_file="$(mktemp -t ale.txt.XXXXXXXX)" sorted_toc_file="$(mktemp -t sorted-ale.txt.XXXXXXXX)" sed -n "$toc_start_line,$toc_end_line"p doc/ale.txt \ - | sed 's/^ \( *[^.]\+\)\.\+|\(.\+\)|/\1, \2/' \ + | sed 's/^ \( *[^.][^.]*\)\.\.*|\(..*\)|/\1, \2/' \ > "$toc_file" # Get all of the doc files in a natural sorted order. -doc_files="$(/bin/ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ')" +doc_files="$(/bin/ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" # shellcheck disable=SC2086 -grep -h 'ale-.*-options\|^[a-z].*\*ale-.*\*$' $doc_files \ +grep -h '\*ale-.*-options\|^[a-z].*\*ale-.*\*$' $doc_files \ | sed 's/^/ /' \ | sed 's/ALE Shell Integration/ALE sh Integration/' \ - | sed 's/ ALE \(.*\) Integration/\L\1/' \ - | sed 's/ *\*\(.\+\)\*$/, \1/' \ + | sed 's/ ALE \(.*\) Integration/\1/' \ + | sed 's/ *\*\(..*\)\*$/, \1/' \ + | tr '[:upper:]' '[:lower:]' \ | sed 's/objective-c/objc/' \ | sed 's/c++/cpp/' \ > "$heading_file" @@ -62,7 +63,7 @@ while read -r; do done < "$toc_file" # Sort the sections and sub-sections and remove the tags. -sort -h "$tagged_toc_file" | sed 's/[0-9]\+ //' > "$sorted_toc_file" +sort -sn "$tagged_toc_file" | sed 's/[0-9][0-9]* //' > "$sorted_toc_file" echo 'Check for bad ToC sorting:' echo diff --git a/test/script/custom-checks b/test/script/custom-checks index a1a734d..791053d 100755 --- a/test/script/custom-checks +++ b/test/script/custom-checks @@ -1,94 +1,68 @@ #!/bin/bash -eu -# This Bash script implements custom sanity checks for scripts beyond what -# Vint covers, which are easy to check with regex. +exit_code=0 +image=w0rp/ale +docker_flags=(--rm -v "$PWD:/testplugin" -v "$PWD/test:/home" -w /testplugin "$image") -# A flag for automatically fixing some errors. -FIX_ERRORS=0 -RETURN_CODE=0 +echo '========================================' +echo 'Running custom linting rules' +echo '========================================' +echo 'Custom warnings/errors follow:' +echo -function print_help() { - echo "Usage: ./custom-checks [--fix] [DIRECTORY]" 1>&2 - echo 1>&2 - echo " -h, --help Print this help text" 1>&2 - echo " --fix Automatically fix some errors" 1>&2 - exit 1 -} +set -o pipefail +docker run -a stdout "${docker_flags[@]}" test/script/custom-linting-rules . || exit_code=$? +set +o pipefail +echo -while [ $# -ne 0 ]; do - case $1 in - -h) ;& --help) - print_help - ;; - --fix) - FIX_ERRORS=1 - shift - ;; - --) - shift - break - ;; - -?*) - echo "Invalid argument: $1" 1>&2 - exit 1 - ;; - *) - break - ;; - esac -done +echo '========================================' +echo 'Checking for duplicate tags' +echo '========================================' +echo 'Duplicate tags follow:' +echo -if [ $# -eq 0 ] || [ -z "$1" ]; then - print_help -fi +grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d || exit_code=$? -shopt -s globstar +echo '========================================' +echo 'Checking for invalid tag references' +echo '========================================' +echo 'Invalid tag references tags follow:' +echo -directories=("$@") +tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+' -check_errors() { - regex="$1" - message="$2" +# Grep for tags and references, and complain if we find a reference without +# a tag for the reference. Only our tags will be included. +diff -u \ + <(grep --exclude=tags -roh "\*$tag_regex\*" doc | sort -u | sed 's/*//g') \ + <(grep --exclude=tags -roh "|$tag_regex|" doc | sort -u | sed 's/|//g') \ + | grep '^+[^+]' && exit_code=1 - for directory in "${directories[@]}"; do - while IFS= read -r match; do - RETURN_CODE=1 - echo "$match $message" - done < <(grep -n "$regex" "$directory"/**/*.vim \ - | grep -v 'no-custom-checks' \ - | grep -o '^[^:]\+:[0-9]\+' \ - | sed 's:^\./::') - done -} +echo '========================================' +echo 'diff README.md and doc/ale.txt tables' +echo '========================================' +echo 'Differences follow:' +echo -if (( FIX_ERRORS )); then - for directory in "${directories[@]}"; do - sed -i "s/^\(function.*)\) *$/\1 abort/" "$directory"/**/*.vim - sed -i "s/shellescape(/ale#Escape(/" "$directory"/**/*.vim - sed -i 's/==#/is#/g' "$directory"/**/*.vim - sed -i 's/==?/is?/g' "$directory"/**/*.vim - sed -i 's/!=#/isnot#/g' "$directory"/**/*.vim - sed -i 's/!=?/isnot?/g' "$directory"/**/*.vim - done -fi +test/script/check-supported-tools-tables || exit_code=$? -check_errors \ - '^function.*) *$' \ - 'Function without abort keyword (See :help except-compat)' -check_errors '^function[^!]' 'function without !' -check_errors ' \+$' 'Trailing whitespace' -check_errors '^ * end\?i\? *$' 'Write endif, not en, end, or endi' -check_errors '^ [^ ]' 'Use four spaces, not two spaces' -check_errors $'\t' 'Use four spaces, not tabs' -# This check should prevent people from using a particular inconsistent name. -check_errors 'let g:ale_\w\+_\w\+_args =' 'Name your option g:ale___options instead' -check_errors 'shellescape(' 'Use ale#Escape instead of shellescape' -check_errors 'simplify(' 'Use ale#path#Simplify instead of simplify' -check_errors "expand(['\"]%" "Use expand('#' . a:buffer . '...') instead. You might get a filename for the wrong buffer." -check_errors 'getcwd()' "Do not use getcwd(), as it could run from the wrong buffer. Use expand('#' . a:buffer . ':p:h') instead." -check_errors '==#' "Use 'is#' instead of '==#'. 0 ==# 'foobar' is true" -check_errors '==?' "Use 'is?' instead of '==?'. 0 ==? 'foobar' is true" -check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false" -check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false" +echo '========================================' +echo 'Look for badly aligned doc tags' +echo '========================================' +echo 'Badly aligned tags follow:' +echo -exit $RETURN_CODE +# Documentation tags need to be aligned to the right margin, so look for +# tags which aren't at the right margin. +grep ' \*[^*]\+\*$' doc/ -r \ + | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \ + | grep . && exit_code=1 + +echo '========================================' +echo 'Look for table of contents issues' +echo '========================================' +echo + +test/script/check-toc || exit_code=$? + +exit $exit_code diff --git a/test/script/custom-linting-rules b/test/script/custom-linting-rules new file mode 100755 index 0000000..ef6d792 --- /dev/null +++ b/test/script/custom-linting-rules @@ -0,0 +1,95 @@ +#!/bin/bash -eu + +# This Bash script implements custom sanity checks for scripts beyond what +# Vint covers, which are easy to check with regex. + +# A flag for automatically fixing some errors. +FIX_ERRORS=0 +RETURN_CODE=0 + +function print_help() { + echo "Usage: test/script/custom-linting-rules [--fix] [DIRECTORY]" 1>&2 + echo 1>&2 + echo " -h, --help Print this help text" 1>&2 + echo " --fix Automatically fix some errors" 1>&2 + exit 1 +} + +while [ $# -ne 0 ]; do + case $1 in + -h) ;& --help) + print_help + ;; + --fix) + FIX_ERRORS=1 + shift + ;; + --) + shift + break + ;; + -?*) + echo "Invalid argument: $1" 1>&2 + exit 1 + ;; + *) + break + ;; + esac +done + +if [ $# -eq 0 ] || [ -z "$1" ]; then + print_help +fi + +shopt -s globstar + +directories=("$@") + +check_errors() { + regex="$1" + message="$2" + + for directory in "${directories[@]}"; do + while IFS= read -r match; do + RETURN_CODE=1 + echo "$match $message" + done < <(grep -n "$regex" "$directory"/**/*.vim \ + | grep -v 'no-custom-checks' \ + | grep -o '^[^:]\+:[0-9]\+' \ + | sed 's:^\./::') + done +} + +if (( FIX_ERRORS )); then + for directory in "${directories[@]}"; do + sed -i "s/^\(function.*)\) *$/\1 abort/" "$directory"/**/*.vim + sed -i "s/shellescape(/ale#Escape(/" "$directory"/**/*.vim + sed -i 's/==#/is#/g' "$directory"/**/*.vim + sed -i 's/==?/is?/g' "$directory"/**/*.vim + sed -i 's/!=#/isnot#/g' "$directory"/**/*.vim + sed -i 's/!=?/isnot?/g' "$directory"/**/*.vim + done +fi + +check_errors \ + '^function.*) *$' \ + 'Function without abort keyword (See :help except-compat)' +check_errors '^function[^!]' 'function without !' +check_errors ' \+$' 'Trailing whitespace' +check_errors '^ * end\?i\? *$' 'Write endif, not en, end, or endi' +check_errors '^ [^ ]' 'Use four spaces, not two spaces' +check_errors $'\t' 'Use four spaces, not tabs' +# This check should prevent people from using a particular inconsistent name. +check_errors 'let g:ale_\w\+_\w\+_args =' 'Name your option g:ale___options instead' +check_errors 'shellescape(' 'Use ale#Escape instead of shellescape' +check_errors 'simplify(' 'Use ale#path#Simplify instead of simplify' +check_errors "expand(['\"]%" "Use expand('#' . a:buffer . '...') instead. You might get a filename for the wrong buffer." +check_errors 'getcwd()' "Do not use getcwd(), as it could run from the wrong buffer. Use expand('#' . a:buffer . ':p:h') instead." +check_errors '==#' "Use 'is#' instead of '==#'. 0 ==# 'foobar' is true" +check_errors '==?' "Use 'is?' instead of '==?'. 0 ==? 'foobar' is true" +check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false" +check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false" +check_errors '^ *:\?echo' "Stray echo line. Use \`execute echo\` if you want to echo something" + +exit $RETURN_CODE diff --git a/test/script/run-vader-tests b/test/script/run-vader-tests new file mode 100755 index 0000000..a10b8ba --- /dev/null +++ b/test/script/run-vader-tests @@ -0,0 +1,115 @@ +#!/bin/bash -eu + +image=w0rp/ale +docker_flags=(--rm -v "$PWD:/testplugin" -v "$PWD/test:/home" -w /testplugin "$image") +red='\033[0;31m' +green='\033[0;32m' +nc='\033[0m' +verbose=0 +quiet=0 +exit_code=0 + +while [ $# -ne 0 ]; do + case $1 in + -v) + verbose=1 + shift + ;; + -q) + quiet=1 + shift + ;; + --) + shift + break + ;; + -?*) + echo "Invalid argument: $1" 1>&2 + exit 1 + ;; + *) + break + ;; + esac +done + +vim="$1" +tests="$2" + +function filter-vader-output() { + # When verbose mode is off, suppress output until Vader starts. + local start_output="$verbose" + local filtered_data='' + + while read -r; do + if ((!start_output)); then + if [[ "$REPLY" = *'Starting Vader:'* ]]; then + start_output=1 + else + continue + fi + fi + + if ((quiet)); then + if [[ "$REPLY" = *'Starting Vader:'* ]]; then + filtered_data="$REPLY" + elif [[ "$REPLY" = *'Success/Total'* ]]; then + success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)" + total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)" + + if [ "$success" -lt "$total" ]; then + echo "$filtered_data" + echo "$REPLY" + fi + + filtered_data='' + else + filtered_data="$filtered_data"$'\n'"$REPLY" + fi + else + echo "$REPLY" + fi + done +} + +function color-vader-output() { + while read -r; do + if [[ "$REPLY" = *'[EXECUTE] (X)'* ]]; then + echo -en "$red" + elif [[ "$REPLY" = *'[EXECUTE]'* ]] || [[ "$REPLY" = *'[ GIVEN]'* ]]; then + echo -en "$nc" + fi + + if [[ "$REPLY" = *'Success/Total'* ]]; then + success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)" + total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)" + + if [ "$success" -lt "$total" ]; then + echo -en "$red" + else + echo -en "$green" + fi + + echo "$REPLY" + echo -en "$nc" + else + echo "$REPLY" + fi + done + + echo -en "$nc" +} + +echo +echo '========================================' +echo "Running tests for $vim" +echo '========================================' +echo + +set -o pipefail +docker run -a stderr -e VADER_OUTPUT_FILE=/dev/stderr "${docker_flags[@]}" \ + "/vim-build/bin/$vim" -u test/vimrc \ + "+Vader! $tests" 2>&1 | filter-vader-output | color-vader-output || exit_code=$? +set +o pipefail + +exit "$exit_code" diff --git a/test/script/run-vint b/test/script/run-vint new file mode 100755 index 0000000..e114030 --- /dev/null +++ b/test/script/run-vint @@ -0,0 +1,18 @@ +#!/bin/bash -eu + +exit_code=0 +image=w0rp/ale +docker_flags=(--rm -v "$PWD:/testplugin" -v "$PWD/test:/home" -w /testplugin "$image") + +echo '========================================' +echo 'Running Vint to lint our code' +echo '========================================' +echo 'Vint warnings/errors follow:' +echo + +set -o pipefail +docker run -a stdout "${docker_flags[@]}" vint -s . || exit_code=$? +set +o pipefail +echo + +exit $exit_code diff --git a/test/sign/test_linting_sets_signs.vader b/test/sign/test_linting_sets_signs.vader index c2cc0db..c23b400 100644 --- a/test/sign/test_linting_sets_signs.vader +++ b/test/sign/test_linting_sets_signs.vader @@ -20,7 +20,7 @@ Before: let l:actual_sign_list = [] for l:line in split(l:output, "\n") - let l:match = matchlist(l:line, 'line=\(\d\+\).*name=\(ALE[a-zA-Z]\+\)') + let l:match = matchlist(l:line, '\v^.*\=(\d+).*\=\d+.*\=(ALE[a-zA-Z]+Sign)') if len(l:match) > 0 call add(l:actual_sign_list, [l:match[1], l:match[2]]) @@ -34,7 +34,7 @@ Before: \ 'name': 'testlinter', \ 'callback': 'TestCallback', \ 'executable': has('win32') ? 'cmd' : 'echo', - \ 'command': 'echo foo bar', + \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', \}) diff --git a/test/smoke_test.vader b/test/smoke_test.vader index 7635cbd..f6d0be5 100644 --- a/test/smoke_test.vader +++ b/test/smoke_test.vader @@ -1,6 +1,7 @@ Before: Save g:ale_set_lists_synchronously Save g:ale_buffer_info + Save &shell let g:ale_buffer_info = {} let g:ale_set_lists_synchronously = 1 @@ -59,6 +60,67 @@ Execute(Linters should run with the default options): \ 'valid': 1, \ }], getloclist(0) +Execute(Linters should run in PowerShell too): + if has('win32') + set shell=powershell + + AssertEqual 'foobar', &filetype + + " Replace the callback to handle two lines. + function! TestCallback(buffer, output) + " Windows adds extra spaces to the text from echo. + return [ + \ { + \ 'lnum': 1, + \ 'col': 3, + \ 'text': substitute(a:output[0], ' *$', '', ''), + \ }, + \ { + \ 'lnum': 2, + \ 'col': 3, + \ 'text': substitute(a:output[1], ' *$', '', ''), + \ }, + \] + endfunction + + " Recreate the command string to use &&, which PowerShell does not support. + call ale#linter#Reset() + call ale#linter#Define('foobar', { + \ 'name': 'testlinter', + \ 'callback': 'TestCallback', + \ 'executable': 'cmd', + \ 'command': 'echo foo && echo bar', + \}) + + call ale#Lint() + call ale#engine#WaitForJobs(2000) + + AssertEqual [ + \ { + \ 'bufnr': bufnr('%'), + \ 'lnum': 1, + \ 'vcol': 0, + \ 'col': 3, + \ 'text': 'foo', + \ 'type': 'E', + \ 'nr': -1, + \ 'pattern': '', + \ 'valid': 1, + \ }, + \ { + \ 'bufnr': bufnr('%'), + \ 'lnum': 2, + \ 'vcol': 0, + \ 'col': 3, + \ 'text': 'bar', + \ 'type': 'E', + \ 'nr': -1, + \ 'pattern': '', + \ 'valid': 1, + \ }, + \], getloclist(0) + endif + Execute(Previous errors should be removed when linters change): call ale#Lint() call ale#engine#WaitForJobs(2000) diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader index ceb65af..05c045b 100644 --- a/test/test_ale_info.vader +++ b/test/test_ale_info.vader @@ -1,10 +1,30 @@ Before: - Save g:ale_warn_about_trailing_whitespace - Save g:ale_linters + Save g:ale_buffer_info + Save g:ale_cache_executable_check_failures + Save g:ale_completion_enabled Save g:ale_fixers + Save g:ale_history_log_output + Save g:ale_lint_on_insert_leave + Save g:ale_lint_on_text_changed + Save g:ale_linters + Save g:ale_maximum_file_size + Save g:ale_pattern_options + Save g:ale_pattern_options_enabled + Save g:ale_set_balloons + Save g:ale_warn_about_trailing_whitespace unlet! b:ale_history + let g:ale_buffer_info = {} + let g:ale_cache_executable_check_failures = 0 + let g:ale_completion_enabled = 0 + let g:ale_history_log_output = 1 + let g:ale_lint_on_insert_leave = 0 + let g:ale_lint_on_text_changed = 'always' + let g:ale_maximum_file_size = 0 + let g:ale_pattern_options = {} + let g:ale_pattern_options_enabled = 0 + let g:ale_set_balloons = 0 let g:ale_warn_about_trailing_whitespace = 1 let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout'} @@ -19,30 +39,56 @@ Before: let g:globals_lines = [ \ ' Global Variables:', \ '', + \ 'let g:ale_cache_executable_check_failures = 0', + \ 'let g:ale_change_sign_column_color = 0', + \ 'let g:ale_command_wrapper = ''''', + \ 'let g:ale_completion_delay = 100', + \ 'let g:ale_completion_enabled = 0', + \ 'let g:ale_completion_max_suggestions = 50', \ 'let g:ale_echo_cursor = 1', \ 'let g:ale_echo_msg_error_str = ''Error''', - \ 'let g:ale_echo_msg_format = ''%s''', + \ 'let g:ale_echo_msg_format = ''%code: %%s''', + \ 'let g:ale_echo_msg_info_str = ''Info''', \ 'let g:ale_echo_msg_warning_str = ''Warning''', \ 'let g:ale_enabled = 1', \ 'let g:ale_fix_on_save = 0', \ 'let g:ale_fixers = {}', + \ 'let g:ale_history_enabled = 1', + \ 'let g:ale_history_log_output = 1', \ 'let g:ale_keep_list_window_open = 0', \ 'let g:ale_lint_delay = 200', \ 'let g:ale_lint_on_enter = 1', + \ 'let g:ale_lint_on_filetype_changed = 1', \ 'let g:ale_lint_on_save = 1', \ 'let g:ale_lint_on_text_changed = ''always''', + \ 'let g:ale_lint_on_insert_leave = 0', \ 'let g:ale_linter_aliases = {}', \ 'let g:ale_linters = {}', + \ 'let g:ale_linters_explicit = 0', + \ 'let g:ale_list_window_size = 10', + \ 'let g:ale_list_vertical = 0', + \ 'let g:ale_loclist_msg_format = ''%code: %%s''', + \ 'let g:ale_max_buffer_history_size = 20', + \ 'let g:ale_max_signs = -1', + \ 'let g:ale_maximum_file_size = 0', \ 'let g:ale_open_list = 0', + \ 'let g:ale_pattern_options = {}', + \ 'let g:ale_pattern_options_enabled = 0', + \ 'let g:ale_set_balloons = 0', \ 'let g:ale_set_highlights = 1', \ 'let g:ale_set_loclist = 1', \ 'let g:ale_set_quickfix = 0', \ 'let g:ale_set_signs = 1', \ 'let g:ale_sign_column_always = 0', \ 'let g:ale_sign_error = ''>>''', + \ 'let g:ale_sign_info = ''--''', \ 'let g:ale_sign_offset = 1000000', + \ 'let g:ale_sign_style_error = ''>>''', + \ 'let g:ale_sign_style_warning = ''--''', \ 'let g:ale_sign_warning = ''--''', \ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']', + \ 'let g:ale_type_map = {}', + \ 'let g:ale_warn_about_trailing_blank_lines = 1', \ 'let g:ale_warn_about_trailing_whitespace = 1', \] let g:command_header = [ @@ -62,8 +108,6 @@ Before: After: Restore - let g:ale_buffer_info = {} - unlet! g:testlinter1 unlet! g:testlinter2 @@ -72,8 +116,6 @@ After: unlet! g:output unlet! g:globals_string unlet! g:command_header - let g:ale_buffer_info = {} - let g:ale_history_log_output = 0 unlet! g:ale_testft_testlinter1_foo unlet! g:ale_testft_testlinter1_bar unlet! g:ale_testft2_testlinter2_foo @@ -355,6 +397,32 @@ Execute (ALEInfo command history should print command output if logging is on): Execute (ALEInfo should include executable checks in the history): call ale#linter#Define('testft', g:testlinter1) call ale#engine#IsExecutable(bufnr(''), has('win32') ? 'cmd' : 'echo') + call ale#engine#IsExecutable(bufnr(''), has('win32') ? 'cmd' : 'echo') + call ale#engine#IsExecutable(bufnr(''), 'TheresNoWayThisIsExecutable') + call ale#engine#IsExecutable(bufnr(''), 'TheresNoWayThisIsExecutable') + + call CheckInfo([ + \ ' Current Filetype: testft.testft2', + \ 'Available Linters: [''testlinter1'']', + \ ' Enabled Linters: [''testlinter1'']', + \ ' Linter Variables:', + \ '', + \] + g:globals_lines + g:command_header + [ + \ '', + \ '(executable check - success) ' . (has('win32') ? 'cmd' : 'echo'), + \ '(executable check - failure) TheresNoWayThisIsExecutable', + \ '(executable check - failure) TheresNoWayThisIsExecutable', + \]) + +Execute (The option for caching failing executable checks should work): + let g:ale_cache_executable_check_failures = 1 + let g:globals_lines[2] = 'let g:ale_cache_executable_check_failures = 1' + + call ale#linter#Define('testft', g:testlinter1) + + call ale#engine#IsExecutable(bufnr(''), has('win32') ? 'cmd' : 'echo') + call ale#engine#IsExecutable(bufnr(''), has('win32') ? 'cmd' : 'echo') + call ale#engine#IsExecutable(bufnr(''), 'TheresNoWayThisIsExecutable') call ale#engine#IsExecutable(bufnr(''), 'TheresNoWayThisIsExecutable') call CheckInfo([ diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader index f3dbf10..d56f8c2 100644 --- a/test/test_ale_toggle.vader +++ b/test/test_ale_toggle.vader @@ -2,9 +2,17 @@ Before: Save g:ale_buffer_info Save g:ale_set_signs Save g:ale_set_lists_synchronously + Save g:ale_run_synchronously + Save g:ale_pattern_options + Save g:ale_pattern_options_enabled let g:ale_set_signs = 1 let g:ale_set_lists_synchronously = 1 + let g:ale_run_synchronously = 1 + let g:ale_pattern_options = {} + let g:ale_pattern_options_enabled = 1 + + unlet! b:ale_enabled let g:ale_buffer_info = {} let g:expected_loclist = [{ @@ -79,6 +87,8 @@ After: unlet! g:expected_loclist unlet! g:expected_groups + unlet! b:ale_enabled + unlet! g:output call ale#linter#Reset() @@ -90,12 +100,19 @@ After: delfunction ToggleTestCallback delfunction ParseAuGroups + call setloclist(0, []) + sign unplace * + call clearmatches() + Given foobar (Some imaginary filetype): foo bar baz Execute(ALEToggle should reset everything and then run again): + " Run this test asynchrously. + let g:ale_run_synchronously = 0 + AssertEqual 'foobar', &filetype call ale#Lint() @@ -118,7 +135,13 @@ Execute(ALEToggle should reset everything and then run again): AssertEqual [], getloclist(0), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' AssertEqual [], getmatches(), 'The highlights were not cleared' - AssertEqual ['ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups() + AssertEqual + \ [ + \ 'ALECleanupGroup', + \ 'ALEHighlightBufferGroup', + \ 'ALERunOnSaveGroup', + \ ], + \ ParseAuGroups() " Toggle ALE on, everything should be set up and run again. ALEToggle @@ -133,6 +156,9 @@ Execute(ALEToggle should reset everything and then run again): AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist Execute(ALEToggle should skip filename keys and preserve them): + " Run this test asynchrously. + let g:ale_run_synchronously = 0 + AssertEqual 'foobar', &filetype let g:ale_buffer_info['/foo/bar/baz.txt'] = { @@ -175,3 +201,146 @@ Execute(ALEToggle should skip filename keys and preserve them): \ 'history': [], \ }, \ get(g:ale_buffer_info, '/foo/bar/baz.txt', {}) + +Execute(ALEDisable should reset everything and stay disabled): + call ale#Lint() + + AssertEqual g:expected_loclist, getloclist(0) + + ALEDisable + + AssertEqual [], getloclist(0) + AssertEqual 0, g:ale_enabled + + ALEDisable + + AssertEqual [], getloclist(0) + AssertEqual 0, g:ale_enabled + +Execute(ALEEnable should enable ALE and lint again): + let g:ale_enabled = 0 + + ALEEnable + + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual 1, g:ale_enabled + +Execute(ALEReset should reset everything for a buffer): + AssertEqual 'foobar', &filetype + + call ale#Lint() + + " First check that everything is there... + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist + + " Now Toggle ALE off. + ALEReset + + " Everything should be cleared. + Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' + AssertEqual [], getloclist(0), 'The loclist was not cleared' + AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' + AssertEqual [], getmatches(), 'The highlights were not cleared' + + AssertEqual 1, g:ale_enabled + +Execute(ALEToggleBuffer should reset everything and then run again): + " Run this test asynchrously. + let g:ale_run_synchronously = 0 + + AssertEqual 'foobar', &filetype + + call ale#Lint() + call ale#engine#WaitForJobs(2000) + + " First check that everything is there... + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist + + " Now Toggle ALE off. + ALEToggleBuffer + + " Everything should be cleared. + Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' + AssertEqual [], getloclist(0), 'The loclist was not cleared' + AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' + AssertEqual [], getmatches(), 'The highlights were not cleared' + + " Toggle ALE on, everything should be set up and run again. + ALEToggleBuffer + call ale#engine#WaitForJobs(2000) + + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + AssertEqual g:expected_groups, ParseAuGroups() + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist + +Execute(ALEDisableBuffer should reset everything and stay disabled): + call ale#Lint() + + AssertEqual g:expected_loclist, getloclist(0) + + ALEDisableBuffer + + AssertEqual [], getloclist(0) + AssertEqual 0, b:ale_enabled + +Execute(ALEEnableBuffer should enable ALE and lint again): + let b:ale_enabled = 0 + + ALEEnableBuffer + + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual 1, b:ale_enabled + +Execute(ALEEnableBuffer should complain when ALE is disabled globally): + let g:ale_enabled = 0 + let b:ale_enabled = 0 + + redir => g:output + ALEEnableBuffer + redir END + + AssertEqual [], getloclist(0) + AssertEqual 0, b:ale_enabled + AssertEqual 0, g:ale_enabled + AssertEqual + \ 'ALE cannot be enabled locally when disabled globally', + \ join(split(g:output)) + +Execute(ALEResetBuffer should reset everything for a buffer): + AssertEqual 'foobar', &filetype + + call ale#Lint() + + " First check that everything is there... + AssertEqual g:expected_loclist, getloclist(0) + AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist + + " Now Toggle ALE off. + ALEResetBuffer + + " Everything should be cleared. + Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' + AssertEqual [], getloclist(0), 'The loclist was not cleared' + AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' + AssertEqual [], getmatches(), 'The highlights were not cleared' + + AssertEqual 1, g:ale_enabled + AssertEqual 1, get(b:, 'ale_enabled', 1) diff --git a/test/test_alelint_autocmd.vader b/test/test_alelint_autocmd.vader index 4503005..d51694f 100644 --- a/test/test_alelint_autocmd.vader +++ b/test/test_alelint_autocmd.vader @@ -1,18 +1,38 @@ Before: - let g:success = 0 + let g:pre_success = 0 + let g:post_success = 0 let g:ale_run_synchronously = 1 + unlet! b:ale_linted + After: let g:ale_run_synchronously = 0 let g:ale_buffer_info = {} - augroup! VaderTest -Execute (Run a lint cycle, and check that a variable is set in the autocmd): + try + augroup! VaderTest + catch + endtry + +Execute(Run a lint cycle, and check that a variable is set in the autocmd): augroup VaderTest autocmd! - autocmd User ALELint let g:success = 1 + autocmd User ALELintPre let g:pre_success = 1 + autocmd User ALELintPost let g:post_success = 1 augroup end call ale#Lint() - AssertEqual g:success, 1 + AssertEqual g:pre_success, 1 + AssertEqual g:post_success, 1 + +Execute(b:ale_linted should be increased after each lint cycle): + AssertEqual get(b:, 'ale_linted'), 0 + + call ale#Lint() + + AssertEqual get(b:, 'ale_linted'), 1 + + call ale#Lint() + + AssertEqual get(b:, 'ale_linted'), 2 diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index 17e3b16..c03e8fb 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -1,6 +1,7 @@ Before: function! CheckAutocmd(group) - call ALEInitAuGroups() + call ale#toggle#InitAuGroups() + redir => l:output execute 'silent! autocmd ' . a:group redir END @@ -58,7 +59,7 @@ After: call ale#completion#Disable() endif - call ALEInitAuGroups() + call ale#toggle#InitAuGroups() Execute (g:ale_lint_on_text_changed = 0 should bind no events): let g:ale_lint_on_text_changed = 0 @@ -107,17 +108,28 @@ Execute (g:ale_lint_on_insert_leave = 0 should bind no events): AssertEqual [], CheckAutocmd('ALERunOnInsertLeave') -Execute (g:ale_pattern_options_enabled = 0 should bind no events): - let g:ale_pattern_options_enabled = 0 - - AssertEqual [], CheckAutocmd('ALEPatternOptionsGroup') - Execute (g:ale_pattern_options_enabled = 1 should bind BufReadPost and BufEnter): let g:ale_pattern_options_enabled = 1 AssertEqual [ - \ 'BufEnter * call ale#pattern_options#SetOptions()', - \ 'BufReadPost * call ale#pattern_options#SetOptions()', + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \], CheckAutocmd('ALEPatternOptionsGroup') + +Execute (g:ale_pattern_options_enabled = 0 should still bind events): + let g:ale_pattern_options_enabled = 0 + + AssertEqual [ + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \], CheckAutocmd('ALEPatternOptionsGroup') + +Execute (g:ale_enabled = 0 should still bind pattern events): + let g:ale_enabled = 0 + + AssertEqual [ + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', \], CheckAutocmd('ALEPatternOptionsGroup') Execute (g:ale_lint_on_enter = 0 should bind only the BufEnter event): @@ -154,32 +166,10 @@ Execute (g:ale_lint_on_filetype_changed = 1 should bind the FileType event): \ ], \ CheckAutocmd('ALERunOnFiletypeChangeGroup') -Execute (g:ale_lint_on_save = 0 should bind no events): - let g:ale_lint_on_save = 0 - let g:ale_fix_on_save = 0 - - AssertEqual [], CheckAutocmd('ALERunOnSaveGroup') - -Execute (g:ale_lint_on_save = 1 should bind no events): - let g:ale_lint_on_save = 1 - let g:ale_fix_on_save = 0 - - AssertEqual [ - \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', - \], CheckAutocmd('ALERunOnSaveGroup') - -Execute (g:ale_lint_on_save = 0 and g:ale_fix_on_save = 1 should bind events): - let g:ale_lint_on_save = 0 - let g:ale_fix_on_save = 1 - - AssertEqual [ - \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', - \], CheckAutocmd('ALERunOnSaveGroup') - -Execute (g:ale_fix_on_save = 1 should bind events even when ALE is disabled): +Execute (The SaveEvent should always be bound): let g:ale_enabled = 0 let g:ale_lint_on_save = 0 - let g:ale_fix_on_save = 1 + let g:ale_fix_on_save = 0 AssertEqual [ \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', @@ -201,7 +191,7 @@ Execute (g:ale_echo_cursor = 1 should bind cursor events): Execute (ALECleanupGroup should include the right commands): AssertEqual [ - \ 'BufUnload * call ale#engine#Cleanup(str2nr(expand('''')))', + \ 'BufDelete * call ale#engine#Cleanup(str2nr(expand('''')))', \ 'QuitPre * call ale#events#QuitEvent(str2nr(expand('''')))', \], CheckAutocmd('ALECleanupGroup') diff --git a/test/test_c_import_paths.vader b/test/test_c_import_paths.vader index af185ea..6080779 100644 --- a/test/test_c_import_paths.vader +++ b/test/test_c_import_paths.vader @@ -39,8 +39,8 @@ Execute(The C GCC handler should include 'include' directories for projects with AssertEqual \ ale#Escape('gcc') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' \ , ale_linters#c#gcc#GetCommand(bufnr('')) @@ -52,8 +52,8 @@ Execute(The C GCC handler should include 'include' directories for projects with AssertEqual \ ale#Escape('gcc') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/include')) . ' ' \ . ' -' \ , ale_linters#c#gcc#GetCommand(bufnr('')) @@ -65,8 +65,8 @@ Execute(The C GCC handler should include root directories for projects with .h f AssertEqual \ ale#Escape('gcc') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' \ , ale_linters#c#gcc#GetCommand(bufnr('')) @@ -78,8 +78,8 @@ Execute(The C GCC handler should include root directories for projects with .hpp AssertEqual \ ale#Escape('gcc') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' \ , ale_linters#c#gcc#GetCommand(bufnr('')) @@ -91,8 +91,8 @@ Execute(The C Clang handler should include 'include' directories for projects wi AssertEqual \ ale#Escape('clang') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' \ , ale_linters#c#clang#GetCommand(bufnr('')) @@ -104,8 +104,8 @@ Execute(The C Clang handler should include 'include' directories for projects wi AssertEqual \ ale#Escape('clang') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' \ , ale_linters#c#clang#GetCommand(bufnr('')) @@ -117,8 +117,8 @@ Execute(The C Clang handler should include root directories for projects with .h AssertEqual \ ale#Escape('clang') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' \ , ale_linters#c#clang#GetCommand(bufnr('')) @@ -130,8 +130,8 @@ Execute(The C Clang handler should include root directories for projects with .h AssertEqual \ ale#Escape('clang') \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' \ , ale_linters#c#clang#GetCommand(bufnr('')) @@ -143,8 +143,8 @@ Execute(The C++ GCC handler should include 'include' directories for projects wi AssertEqual \ ale#Escape('gcc') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' \ , ale_linters#cpp#gcc#GetCommand(bufnr('')) @@ -156,8 +156,8 @@ Execute(The C++ GCC handler should include 'include' directories for projects wi AssertEqual \ ale#Escape('gcc') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/include')) . ' ' \ . ' -' \ , ale_linters#cpp#gcc#GetCommand(bufnr('')) @@ -169,8 +169,8 @@ Execute(The C++ GCC handler should include root directories for projects with .h AssertEqual \ ale#Escape('gcc') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' \ , ale_linters#cpp#gcc#GetCommand(bufnr('')) @@ -182,8 +182,8 @@ Execute(The C++ GCC handler should include root directories for projects with .h AssertEqual \ ale#Escape('gcc') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' \ , ale_linters#cpp#gcc#GetCommand(bufnr('')) @@ -195,8 +195,8 @@ Execute(The C++ Clang handler should include 'include' directories for projects AssertEqual \ ale#Escape('clang++') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/makefile_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' \ , ale_linters#cpp#clang#GetCommand(bufnr('')) @@ -208,8 +208,8 @@ Execute(The C++ Clang handler should include 'include' directories for projects AssertEqual \ ale#Escape('clang++') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/configure_project') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/include')) . ' ' \ . ' -' \ , ale_linters#cpp#clang#GetCommand(bufnr('')) @@ -221,8 +221,8 @@ Execute(The C++ Clang handler should include root directories for projects with AssertEqual \ ale#Escape('clang++') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/h_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' \ , ale_linters#cpp#clang#GetCommand(bufnr('')) @@ -234,8 +234,8 @@ Execute(The C++ Clang handler should include root directories for projects with AssertEqual \ ale#Escape('clang++') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' \ , ale_linters#cpp#clang#GetCommand(bufnr('')) @@ -255,8 +255,8 @@ Execute(The C++ Clang handler shoud use the include directory based on the .git AssertEqual \ ale#Escape('clang++') \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/git_and_nested_makefiles/src')) . ' ' - \ . ' -I' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/git_and_nested_makefiles') . '/include') . ' ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/git_and_nested_makefiles/src')) . ' ' + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/git_and_nested_makefiles/include')) . ' ' \ . ' -' \ , ale_linters#cpp#clang#GetCommand(bufnr('')) @@ -268,7 +268,7 @@ Execute(The C++ ClangTidy handler should include json folders for projects with AssertEqual \ ale#Escape('clang-tidy') \ . ' -checks=' . ale#Escape('*') . ' %s ' - \ . '-p ' . ale#Escape(ale#path#Winify(g:dir . '/test_c_projects/json_project') . '/build') + \ . '-p ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/json_project/build')) \ , ale_linters#cpp#clangtidy#GetCommand(bufnr('')) Execute(Move .git/HEAD back): diff --git a/test/test_cleanup.vader b/test/test_cleanup.vader index 23e5bcf..232874a 100644 --- a/test/test_cleanup.vader +++ b/test/test_cleanup.vader @@ -1,15 +1,14 @@ -Before: - let g:buffer = bufnr('%') - - let g:ale_buffer_info = { - \ g:buffer : {'temporary_file_list': [], 'temporary_directory_list': []}, - \ 10347: {'temporary_file_list': [], 'temporary_directory_list': []}, - \} - After: - unlet! g:buffer - let g:ale_buffer_info = {} + unlet! g:buffer + let g:ale_buffer_info = {} -Execute('ALE globals should be cleared when the buffer is closed.'): - :q! - AssertEqual {10347: {'temporary_file_list': [], 'temporary_directory_list': []}}, g:ale_buffer_info +Execute('ALE globals should be cleared when the buffer is deleted): + new + + let g:ale_buffer_info = { + \ bufnr(''): {'temporary_file_list': [], 'temporary_directory_list': []}, + \ 10347: {'temporary_file_list': [], 'temporary_directory_list': []}, + \} + + bdelete + AssertEqual {10347: {'temporary_file_list': [], 'temporary_directory_list': []}}, g:ale_buffer_info diff --git a/test/test_completion.vader b/test/test_completion.vader deleted file mode 100644 index 811a264..0000000 --- a/test/test_completion.vader +++ /dev/null @@ -1,328 +0,0 @@ -Before: - Save g:ale_completion_enabled - Save g:ale_completion_delay - Save g:ale_completion_max_suggestions - Save &l:omnifunc - Save &l:completeopt - - let g:test_vars = { - \ 'feedkeys_calls': [], - \} - - function! ale#util#FeedKeys(string, mode) abort - call add(g:test_vars.feedkeys_calls, [a:string, a:mode]) - endfunction - -After: - Restore - - unlet! g:test_vars - unlet! b:ale_old_omnifunc - unlet! b:ale_old_completopt - unlet! b:ale_completion_info - unlet! b:ale_completion_response - unlet! b:ale_completion_parser - - runtime autoload/ale/completion.vim - runtime autoload/ale/lsp.vim - - if g:ale_completion_enabled - call ale#completion#Enable() - else - call ale#completion#Disable() - endif - -Execute(TypeScript completions responses should be parsed correctly): - AssertEqual [], - \ ale#completion#ParseTSServerCompletions({ - \ 'body': [], - \}) - AssertEqual ['foo', 'bar', 'baz'], - \ ale#completion#ParseTSServerCompletions({ - \ 'body': [ - \ {'name': 'foo'}, - \ {'name': 'bar'}, - \ {'name': 'baz'}, - \ ], - \}) - -Execute(TypeScript completion details responses should be parsed correctly): - AssertEqual - \ [ - \ { - \ 'word': 'abc', - \ 'menu': '(property) Foo.abc: number', - \ 'info': '', - \ 'kind': 'f', - \ 'icase': 1, - \ }, - \ { - \ 'word': 'def', - \ 'menu': '(property) Foo.def: number', - \ 'info': 'foo bar baz', - \ 'kind': 'f', - \ 'icase': 1, - \ }, - \ ], - \ ale#completion#ParseTSServerCompletionEntryDetails({ - \ 'body': [ - \ { - \ 'name': 'abc', - \ 'kind': 'parameterName', - \ 'displayParts': [ - \ {'text': '('}, - \ {'text': 'property'}, - \ {'text': ')'}, - \ {'text': ' '}, - \ {'text': 'Foo'}, - \ {'text': '.'}, - \ {'text': 'abc'}, - \ {'text': ':'}, - \ {'text': ' '}, - \ {'text': 'number'}, - \ ], - \ }, - \ { - \ 'name': 'def', - \ 'kind': 'parameterName', - \ 'displayParts': [ - \ {'text': '('}, - \ {'text': 'property'}, - \ {'text': ')'}, - \ {'text': ' '}, - \ {'text': 'Foo'}, - \ {'text': '.'}, - \ {'text': 'def'}, - \ {'text': ':'}, - \ {'text': ' '}, - \ {'text': 'number'}, - \ ], - \ 'documentation': [ - \ {'text': 'foo'}, - \ {'text': ' '}, - \ {'text': 'bar'}, - \ {'text': ' '}, - \ {'text': 'baz'}, - \ ], - \ }, - \ ], - \}) - -Execute(Prefix filtering should work for Lists of strings): - AssertEqual - \ ['FooBar', 'foo'], - \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], 'foo') - AssertEqual - \ ['FooBar', 'FongBar', 'baz', 'foo'], - \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], '.') - -Execute(Prefix filtering should work for completion items): - AssertEqual - \ [{'word': 'FooBar'}, {'word': 'foo'}], - \ ale#completion#Filter( - \ [ - \ {'word': 'FooBar'}, - \ {'word': 'FongBar'}, - \ {'word': 'baz'}, - \ {'word': 'foo'}, - \ ], - \ 'foo' - \ ) - AssertEqual - \ [ - \ {'word': 'FooBar'}, - \ {'word': 'FongBar'}, - \ {'word': 'baz'}, - \ {'word': 'foo'}, - \ ], - \ ale#completion#Filter( - \ [ - \ {'word': 'FooBar'}, - \ {'word': 'FongBar'}, - \ {'word': 'baz'}, - \ {'word': 'foo'}, - \ ], - \ '.' - \ ) - -Execute(The right message sent to the tsserver LSP when the first completion message is received): - " The cursor position needs to match what was saved before. - call setpos('.', [bufnr(''), 1, 1, 0]) - let b:ale_completion_info = { - \ 'conn_id': 123, - \ 'prefix': 'f', - \ 'request_id': 4, - \ 'line': 1, - \ 'column': 1, - \} - " We should only show up to this many suggestions. - let g:ale_completion_max_suggestions = 3 - - " Replace the Send function for LSP, so we can monitor calls to it. - function! ale#lsp#Send(conn_id, message) abort - let g:test_vars.message = a:message - endfunction - - " Handle the response for completions. - call ale#completion#HandleTSServerLSPResponse(123, { - \ 'request_seq': 4, - \ 'command': 'completions', - \ 'body': [ - \ {'name': 'Baz'}, - \ {'name': 'dingDong'}, - \ {'name': 'Foo'}, - \ {'name': 'FooBar'}, - \ {'name': 'frazzle'}, - \ {'name': 'FFS'}, - \ ], - \}) - - " The entry details messages should have been sent. - AssertEqual - \ [ - \ 0, - \ 'ts@completionEntryDetails', - \ { - \ 'file': expand('%:p'), - \ 'entryNames': ['Foo', 'FooBar', 'frazzle'], - \ 'offset': 1, - \ 'line': 1, - \ }, - \ ], - \ g:test_vars.message - -Given typescript(): - let abc = y. - let foo = ab - let foo = (ab) - -Execute(Completion should be done after dots in TypeScript): - AssertEqual '.', ale#completion#GetPrefix(&filetype, 1, 13) - -Execute(Completion should be done after words in TypeScript): - AssertEqual 'ab', ale#completion#GetPrefix(&filetype, 2, 13) - -Execute(Completion should be done after words in parens in TypeScript): - AssertEqual 'ab', ale#completion#GetPrefix(&filetype, 3, 14) - -Execute(Completion should not be done after parens in TypeScript): - AssertEqual '', ale#completion#GetPrefix(&filetype, 3, 15) - -Execute(ale#completion#Show() should remember the omnifunc setting and replace it): - let &l:omnifunc = 'FooBar' - - call ale#completion#Show('Response', 'Parser') - - AssertEqual 'FooBar', b:ale_old_omnifunc - AssertEqual 'ale#completion#OmniFunc', &l:omnifunc - -Execute(ale#completion#Show() should remember the completeopt setting and replace it): - let &l:completeopt = 'menu' - - call ale#completion#Show('Response', 'Parser') - - AssertEqual 'menu', b:ale_old_completopt - AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt - -Execute(ale#completion#OmniFunc() should also remember the completeopt setting and replace it): - let &l:completeopt = 'menu' - - call ale#completion#OmniFunc(0, '') - - AssertEqual 'menu', b:ale_old_completopt - AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt - -Execute(ale#completion#Show() should make the correct feedkeys() call): - call ale#completion#Show('Response', 'Parser') - - AssertEqual [["\\", 'n']], g:test_vars.feedkeys_calls - -Execute(ale#completion#Show() should set up the response and parser): - call ale#completion#Show('Response', 'Parser') - - AssertEqual 'Response', b:ale_completion_response - AssertEqual 'Parser', b:ale_completion_parser - -Execute(ale#completion#Done() should restore old omnifunc values): - let b:ale_old_omnifunc = 'FooBar' - - call ale#completion#Done() - - " We reset the old omnifunc setting and remove the buffer variable. - AssertEqual 'FooBar', &l:omnifunc - Assert !has_key(b:, 'ale_old_omnifunc') - -Execute(ale#completion#Done() should restore the old completeopt setting): - let b:ale_old_completopt = 'menu' - let &l:completeopt = 'menu,menuone,preview,noselect,noinsert' - - call ale#completion#Done() - - AssertEqual 'menu', &l:completeopt - Assert !has_key(b:, 'ale_old_completopt') - -Execute(ale#completion#Done() should leave settings alone when none were remembered): - let &l:omnifunc = 'BazBoz' - let &l:completeopt = 'menu' - - call ale#completion#Done() - - AssertEqual 'BazBoz', &l:omnifunc - AssertEqual 'menu', &l:completeopt - -Execute(The completion request_id should be reset when queuing again): - let b:ale_completion_info = {'request_id': 123} - - let g:ale_completion_delay = 0 - call ale#completion#Queue() - sleep 1m - - AssertEqual 0, b:ale_completion_info.request_id - -Execute(b:ale_completion_info should be set up correctly when requesting completions): - call setpos('.', [bufnr(''), 3, 14, 0]) - call ale#completion#GetCompletions() - - AssertEqual - \ { - \ 'request_id': 0, - \ 'conn_id': 0, - \ 'column': 14, - \ 'line': 3, - \ 'prefix': 'ab', - \ }, - \ b:ale_completion_info - -Execute(ale#completion#GetCompletions should be called when the cursor position stays the same): - let g:test_vars.get_completions_called = 0 - - " We just want to check if the function is called. - function! ale#completion#GetCompletions() - let g:test_vars.get_completions_called = 1 - endfunction - - let g:ale_completion_delay = 0 - call ale#completion#Queue() - sleep 1m - - Assert g:test_vars.get_completions_called - -Execute(ale#completion#GetCompletions should not be called when the cursor position changes): - call setpos('.', [bufnr(''), 1, 2, 0]) - - let g:test_vars.get_completions_called = 0 - - " We just want to check if the function is called. - function! ale#completion#GetCompletions() - let g:test_vars.get_completions_called = 1 - endfunction - - let g:ale_completion_delay = 0 - call ale#completion#Queue() - - " Change the cursor position before the callback is triggered. - call setpos('.', [bufnr(''), 2, 2, 0]) - - sleep 1m - - Assert !g:test_vars.get_completions_called diff --git a/test/test_csslint_config_detection.vader b/test/test_csslint_config_detection.vader index d84a00f..47e80d0 100644 --- a/test/test_csslint_config_detection.vader +++ b/test/test_csslint_config_detection.vader @@ -13,7 +13,7 @@ Execute(--config should be set when the .csslintrc file is found): AssertEqual \ ( \ 'csslint --format=compact ' - \ . '--config=' . ale#Escape(ale#path#Winify(g:dir . '/csslint-test-files/some-app/.csslintrc')) + \ . '--config=' . ale#Escape(ale#path#Simplify(g:dir . '/csslint-test-files/some-app/.csslintrc')) \ . ' %t' \ ), \ ale_linters#css#csslint#GetCommand(bufnr('')) diff --git a/test/test_cursor_warnings.vader b/test/test_cursor_warnings.vader index 586cc13..1959221 100644 --- a/test/test_cursor_warnings.vader +++ b/test/test_cursor_warnings.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_echo_msg_format + Save g:ale_echo_cursor + let g:ale_buffer_info = { \ bufnr('%'): { \ 'loclist': [ @@ -10,8 +13,19 @@ Before: \ 'linter_name': 'eslint', \ 'nr': -1, \ 'type': 'E', - \ 'text': 'Missing semicolon. (semi)', - \ 'detail': "Every statement should end with a semicolon\nsecond line" + \ 'code': 'semi', + \ 'text': 'Missing semicolon.', + \ 'detail': "Every statement should end with a semicolon\nsecond line", + \ }, + \ { + \ 'lnum': 1, + \ 'col': 14, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'I', + \ 'text': 'Some information', \ }, \ { \ 'lnum': 2, @@ -21,7 +35,8 @@ Before: \ 'linter_name': 'eslint', \ 'nr': -1, \ 'type': 'W', - \ 'text': 'Infix operators must be spaced. (space-infix-ops)' + \ 'code': 'space-infix-ops', + \ 'text': 'Infix operators must be spaced.', \ }, \ { \ 'lnum': 2, @@ -31,7 +46,8 @@ Before: \ 'linter_name': 'eslint', \ 'nr': -1, \ 'type': 'E', - \ 'text': 'Missing radix parameter (radix)' + \ 'code': 'radix', + \ 'text': 'Missing radix parameter', \ }, \ { \ 'lnum': 3, @@ -41,7 +57,7 @@ Before: \ 'linter_name': 'eslint', \ 'nr': -1, \ 'type': 'E', - \ 'text': 'lowercase error' + \ 'text': 'lowercase error', \ }, \ ], \ }, @@ -51,6 +67,7 @@ Before: let g:ale_set_loclist = 0 let g:ale_set_signs = 0 let g:ale_set_highlights = 0 + let g:ale_echo_cursor = 1 function GetLastMessage() redir => l:output @@ -63,6 +80,8 @@ Before: endfunction After: + Restore + call cursor(1, 1) let g:ale_set_loclist = 1 @@ -72,6 +91,7 @@ After: let g:ale_buffer_info = {} unlet! g:output + unlet! b:ale_loclist_msg_format delfunction GetLastMessage @@ -80,8 +100,13 @@ After: " carried over between test cases. echomsg '' + " Close the preview window if it's open. + if &filetype is# 'ale-preview' + noautocmd :q! + endif + Given javascript(A Javscript file with warnings/errors): - var x = 3 + var x = 3 + 12345678 var x = 5*2 + parseInt("10"); // comment @@ -89,19 +114,19 @@ Execute(Messages should be shown for the correct lines): call cursor(1, 1) call ale#cursor#EchoCursorWarning() - AssertEqual 'Missing semicolon. (semi)', GetLastMessage() + AssertEqual 'semi: Missing semicolon.', GetLastMessage() Execute(Messages should be shown for earlier columns): call cursor(2, 1) call ale#cursor#EchoCursorWarning() - AssertEqual 'Infix operators must be spaced. (space-infix-ops)', GetLastMessage() + AssertEqual 'space-infix-ops: Infix operators must be spaced.', GetLastMessage() Execute(Messages should be shown for later columns): call cursor(2, 16) call ale#cursor#EchoCursorWarning() - AssertEqual 'Missing radix parameter (radix)', GetLastMessage() + AssertEqual 'radix: Missing radix parameter', GetLastMessage() Execute(The message at the cursor should be shown when linting ends): call cursor(1, 1) @@ -110,34 +135,101 @@ Execute(The message at the cursor should be shown when linting ends): \ g:ale_buffer_info[bufnr('%')].loclist, \) - AssertEqual 'Missing semicolon. (semi)', GetLastMessage() + AssertEqual 'semi: Missing semicolon.', GetLastMessage() Execute(The message at the cursor should be shown on InsertLeave): call cursor(2, 9) doautocmd InsertLeave - AssertEqual 'Infix operators must be spaced. (space-infix-ops)', GetLastMessage() + AssertEqual 'space-infix-ops: Infix operators must be spaced.', GetLastMessage() Execute(ALEDetail should print 'detail' attributes): call cursor(1, 1) - redir => g:output - ALEDetail - redir END + ALEDetail - AssertEqual "\nEvery statement should end with a semicolon\nsecond line", g:output + AssertEqual + \ ['Every statement should end with a semicolon', 'second line'], + \ getline(1, '$') Execute(ALEDetail should print regular 'text' attributes): call cursor(2, 10) - redir => g:output - ALEDetail - redir END + ALEDetail - AssertEqual "\nInfix operators must be spaced. (space-infix-ops)", g:output + " ALEDetail opens a window, so check the text in it. + AssertEqual + \ ['Infix operators must be spaced.'], + \ getline(1, '$') Execute(ALEDetail should not capitlise cursor messages): call cursor(3, 1) call ale#cursor#EchoCursorWarning() AssertEqual 'lowercase error', GetLastMessage() + +Execute(The linter name should be formatted into the message correctly): + let g:ale_echo_msg_format = '%linter%: %s' + + call cursor(2, 9) + call ale#cursor#EchoCursorWarning() + + AssertEqual + \ 'eslint: Infix operators must be spaced.', + \ GetLastMessage() + +Execute(The severity should be formatted into the message correctly): + let g:ale_echo_msg_format = '%severity%: %s' + + call cursor(2, 9) + call ale#cursor#EchoCursorWarning() + + AssertEqual + \ 'Warning: Infix operators must be spaced.', + \ GetLastMessage() + + call cursor(1, 10) + call ale#cursor#EchoCursorWarning() + + AssertEqual 'Error: Missing semicolon.', GetLastMessage() + + call cursor(1, 14) + call ale#cursor#EchoCursorWarning() + + AssertEqual 'Info: Some information', GetLastMessage() + +Execute(The %code% and %ifcode% should show the code and some text): + let g:ale_echo_msg_format = '%(code) %%s' + + call cursor(2, 9) + call ale#cursor#EchoCursorWarning() + + AssertEqual + \ '(space-infix-ops) Infix operators must be spaced.', + \ GetLastMessage() + +Execute(The %code% and %ifcode% should be removed when there's no code): + let g:ale_echo_msg_format = '%(code) %%s' + + call cursor(1, 14) + call ale#cursor#EchoCursorWarning() + + AssertEqual 'Some information', GetLastMessage() + +Execute(The buffer message format option should take precedence): + let g:ale_echo_msg_format = '%(code) %%s' + let b:ale_echo_msg_format = 'FOO %s' + + call cursor(1, 14) + call ale#cursor#EchoCursorWarning() + + AssertEqual 'FOO Some information', GetLastMessage() + +Execute(The cursor message shouldn't be echoed if the option is off): + let g:ale_echo_cursor = 0 + echom 'foo' + + call cursor(1, 1) + call ale#cursor#EchoCursorWarning() + + AssertEqual 'foo', GetLastMessage() diff --git a/test/test_dockerfile_hadolint_linter.vader b/test/test_dockerfile_hadolint_linter.vader index 7262c5b..3edbb2b 100644 --- a/test/test_dockerfile_hadolint_linter.vader +++ b/test/test_dockerfile_hadolint_linter.vader @@ -55,7 +55,7 @@ Execute(command is correct when using docker): let b:ale_dockerfile_hadolint_use_docker = 'always' AssertEqual - \ "docker run --rm -i lukasmartinelli/hadolint", + \ "docker run --rm -i hadolint/hadolint", \ ale_linters#dockerfile#hadolint#GetCommand(bufnr('')) @@ -66,4 +66,25 @@ Execute(command is correct when not docker): \ "hadolint -", \ ale_linters#dockerfile#hadolint#GetCommand(bufnr('')) +Execute(test warnings from hadolint): + AssertEqual + \ [{'lnum': 10, 'col': 0, 'type': 'W', 'text': 'Using latest is prone to errors', 'detail': "DL3007 ( https://github.com/hadolint/hadolint/wiki/DL3007 )\n\nUsing latest is prone to errors"}], + \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ + \ '/dev/stdin:10 DL3007 Using latest is prone to errors', + \ ]) + +Execute(test warnings from shellcheck): + AssertEqual + \ [{'lnum': 3, 'col': 0, 'type': 'W', 'text': 'bar is referenced but not assigned.', 'detail': "SC2154 ( https://github.com/koalaman/shellcheck/wiki/SC2154 )\n\nbar is referenced but not assigned."}], + \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ + \ '/dev/stdin:3 SC2154 bar is referenced but not assigned.', + \ ]) + +Execute(test errors from dockerfile parser): + AssertEqual + \ [{'lnum': 3, 'col': 4, 'type': 'E', 'text': 'unexpected "A" expecting at least one space after ''RUN''', 'detail': 'unexpected "A" expecting at least one space after ''RUN'''}], + \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ + \ "/dev/stdin:3:4 unexpected \"A\" expecting at least one space after 'RUN'", + \ ]) + " fin... diff --git a/test/test_elm_executable_detection.vader b/test/test_elm_executable_detection.vader index cca8a6e..4227cbf 100644 --- a/test/test_elm_executable_detection.vader +++ b/test/test_elm_executable_detection.vader @@ -12,7 +12,7 @@ Execute(should get valid executable with default params): call ale#test#SetFilename('elm-test-files/app/testfile.elm') AssertEqual - \ ale#path#Winify(g:dir . '/elm-test-files/app/node_modules/.bin/elm-make'), + \ ale#path#Simplify(g:dir . '/elm-test-files/app/node_modules/.bin/elm-make'), \ ale_linters#elm#make#GetExecutable(bufnr('')) Execute(should get valid executable with 'use_global' params): diff --git a/test/test_eslint_executable_detection.vader b/test/test_eslint_executable_detection.vader index ee79242..c1438ed 100644 --- a/test/test_eslint_executable_detection.vader +++ b/test/test_eslint_executable_detection.vader @@ -17,7 +17,7 @@ Execute(create-react-app directories should be detected correctly): call ale#test#SetFilename('eslint-test-files/react-app/subdir/testfile.js') AssertEqual - \ ale#path#Winify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'), + \ ale#path#Simplify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'), \ ale#handlers#eslint#GetExecutable(bufnr('')) Execute(use-global should override create-react-app detection): @@ -33,7 +33,7 @@ Execute(other app directories should be detected correctly): call ale#test#SetFilename('eslint-test-files/other-app/subdir/testfile.js') AssertEqual - \ ale#path#Winify(g:dir . '/eslint-test-files/node_modules/.bin/eslint'), + \ ale#path#Simplify(g:dir . '/eslint-test-files/node_modules/.bin/eslint'), \ ale#handlers#eslint#GetExecutable(bufnr('')) Execute(use-global should override other app directories): @@ -49,7 +49,7 @@ Execute(eslint_d should be detected correctly): call ale#test#SetFilename('eslint-test-files/app-with-eslint-d/testfile.js') AssertEqual - \ ale#path#Winify(g:dir . '/eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d'), + \ ale#path#Simplify(g:dir . '/eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d'), \ ale#handlers#eslint#GetExecutable(bufnr('')) Execute(eslint.js executables should be run with node on Windows): @@ -59,6 +59,6 @@ Execute(eslint.js executables should be run with node on Windows): " We have to execute the file with node. AssertEqual \ ale#Escape('node.exe') . ' ' - \ . ale#Escape(ale#path#Winify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js')) \ . ' -f unix --stdin --stdin-filename %s', \ ale#handlers#eslint#GetCommand(bufnr('')) diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader new file mode 100644 index 0000000..ea4a05f --- /dev/null +++ b/test/test_filetype_linter_defaults.vader @@ -0,0 +1,68 @@ +Before: + Save g:ale_linters + Save g:ale_linters_explicit + + let g:ale_linters_explicit = 0 + let g:ale_linters = {} + + function! GetLinterNames(filetype) abort + return map(ale#linter#Get(a:filetype), 'v:val.name') + endfunction + +After: + Restore + + call ale#linter#Reset() + +Execute(The defaults for the csh filetype should be correct): + AssertEqual ['shell'], GetLinterNames('csh') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('csh') + +Execute(The defaults for the go filetype should be correct): + AssertEqual ['gofmt', 'golint', 'go vet'], GetLinterNames('go') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('go') + +Execute(The defaults for the help filetype should be correct): + AssertEqual [], GetLinterNames('help') + +Execute(The defaults for the python filetype should be correct): + AssertEqual ['flake8', 'mypy', 'pylint'], GetLinterNames('python') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('python') + +Execute(The defaults for the rust filetype should be correct): + AssertEqual ['cargo'], GetLinterNames('rust') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('rust') + +Execute(The defaults for the spec filetype should be correct): + AssertEqual [], GetLinterNames('spec') + +Execute(The defaults for the text filetype should be correct): + AssertEqual [], GetLinterNames('text') + +Execute(The defaults for the zsh filetype should be correct): + AssertEqual ['shell'], GetLinterNames('zsh') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('zsh') + +Execute(The defaults for the verilog filetype should be correct): + " This filetype isn't configured with default, so we can test loading all + " available linters with this. + AssertEqual ['iverilog', 'verilator'], GetLinterNames('verilog') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('verilog') diff --git a/test/test_find_nearest_directory.vader b/test/test_find_nearest_directory.vader index 1442c8f..2529950 100644 --- a/test/test_find_nearest_directory.vader +++ b/test/test_find_nearest_directory.vader @@ -8,7 +8,7 @@ Execute(We should be able to find a directory some directory down): call ale#test#SetFilename('top/middle/bottom/dummy.txt') AssertEqual - \ ale#path#Winify(expand('%:p:h:h:h:h') . '/top/ale-special-directory-name-dont-use-this-please/'), + \ ale#path#Simplify(expand('%:p:h:h:h:h') . '/top/ale-special-directory-name-dont-use-this-please/'), \ ale#path#FindNearestDirectory(bufnr('%'), 'ale-special-directory-name-dont-use-this-please') Execute(We shouldn't find anything for files which don't match): diff --git a/test/test_flow_command.vader b/test/test_flow_command.vader index 32ceb57..49546e9 100644 --- a/test/test_flow_command.vader +++ b/test/test_flow_command.vader @@ -5,6 +5,7 @@ Before: After: call ale#test#RestoreDirectory() call ale#linter#Reset() + call ale#semver#ResetVersionCache() Execute(flow should return a command to run if a .flowconfig file exists): call ale#test#SetFilename('flow/a/sub/dummy') diff --git a/test/test_foodcritic_command_callback.vader b/test/test_foodcritic_command_callback.vader deleted file mode 100644 index a5b02e4..0000000 --- a/test/test_foodcritic_command_callback.vader +++ /dev/null @@ -1,18 +0,0 @@ -Before: - let g:ale_chef_foodcritic_options = '-t ~F011' - let g:ale_chef_foodcritic_executable = 'foodcritic' - - call ale#test#SetDirectory('/testplugin/test') - runtime ale_linters/chef/foodcritic.vim - -After: - let g:ale_chef_foodcritic_options = '' - let g:ale_chef_foodcritic_executable = '' - - call ale#test#RestoreDirectory() - call ale#linter#Reset() - -Execute(command line should be assembled correctly): - AssertEqual - \ 'foodcritic -t \~F011 %t', - \ ale_linters#chef#foodcritic#GetCommand(bufnr('')) diff --git a/test/test_fuzzy_json_decode.vader b/test/test_fuzzy_json_decode.vader index 4ac0ca1..4b1c608 100644 --- a/test/test_fuzzy_json_decode.vader +++ b/test/test_fuzzy_json_decode.vader @@ -6,6 +6,14 @@ Execute(FuzzyJSONDecode should return the default for empty Strings): AssertEqual [], ale#util#FuzzyJSONDecode('', []) AssertEqual {}, ale#util#FuzzyJSONDecode('', {}) +Execute(FuzzyJSONDecode should return the default value for ['']): + AssertEqual [], ale#util#FuzzyJSONDecode([''], []) + AssertEqual {}, ale#util#FuzzyJSONDecode([''], {}) + +Execute(FuzzyJSONDecode should return the default value for only whitespace lines): + AssertEqual [], ale#util#FuzzyJSONDecode(['', "\n"], []) + AssertEqual {}, ale#util#FuzzyJSONDecode(['', "\n"], {}) + Execute(FuzzyJSONDecode should return the default for Lists with invalid JSON): AssertEqual [], ale#util#FuzzyJSONDecode(['x'], []) AssertEqual {}, ale#util#FuzzyJSONDecode(['x'], {}) diff --git a/test/test_get_abspath.vader b/test/test_get_abspath.vader index 5f81380..7e1b593 100644 --- a/test/test_get_abspath.vader +++ b/test/test_get_abspath.vader @@ -1,15 +1,29 @@ Execute(Relative paths should be resolved correctly): AssertEqual - \ '/foo/bar/baz/whatever.txt', + \ has('win32') ? '\foo\bar\baz\whatever.txt' : '/foo/bar/baz/whatever.txt', \ ale#path#GetAbsPath('/foo/bar/xyz', '../baz/whatever.txt') AssertEqual - \ has('win32') ? '/foo/bar/xyz\whatever.txt' : '/foo/bar/xyz/whatever.txt', + \ has('win32') ? '\foo\bar\xyz\whatever.txt' : '/foo/bar/xyz/whatever.txt', \ ale#path#GetAbsPath('/foo/bar/xyz', './whatever.txt') AssertEqual - \ has('win32') ? '/foo/bar/xyz\whatever.txt' : '/foo/bar/xyz/whatever.txt', + \ has('win32') ? '\foo\bar\xyz\whatever.txt' : '/foo/bar/xyz/whatever.txt', \ ale#path#GetAbsPath('/foo/bar/xyz', 'whatever.txt') + if has('win32') + AssertEqual + \ 'C:\foo\bar\baz\whatever.txt', + \ ale#path#GetAbsPath('C:\foo\bar\baz\xyz', '../whatever.txt') + endif + Execute(Absolute paths should be resolved correctly): AssertEqual - \ '/ding/dong', + \ has('win32') ? '\ding\dong' : '/ding/dong', \ ale#path#GetAbsPath('/foo/bar/xyz', '/ding/dong') + + AssertEqual + \ has('win32') ? '\ding\dong' : '/ding/dong', + \ ale#path#GetAbsPath('/foo/bar/xyz', '//ding/dong') + + if has('win32') + AssertEqual '\ding', ale#path#GetAbsPath('/foo/bar/xyz', '\\ding') + endif diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader new file mode 100644 index 0000000..b77a75a --- /dev/null +++ b/test/test_go_to_definition.vader @@ -0,0 +1,285 @@ +Before: + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('dummy.txt') + + let g:old_filename = expand('%:p') + let g:Callback = 0 + let g:message_list = [] + let g:expr_list = [] + + runtime autoload/ale/definition.vim + runtime autoload/ale/linter.vim + runtime autoload/ale/lsp.vim + + function! ale#linter#StartLSP(buffer, linter, callback) abort + let g:Callback = a:callback + + return { + \ 'connection_id': 347, + \ 'project_root': '/foo/bar', + \} + endfunction + + function! ale#lsp#Send(conn_id, message, root) abort + call add(g:message_list, a:message) + + return 42 + endfunction + + function! ale#definition#Execute(expr) abort + call add(g:expr_list, a:expr) + endfunction + +After: + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + unlet! g:old_filename + unlet! g:Callback + unlet! g:message_list + unlet! g:expr_list + unlet! b:ale_linters + + runtime autoload/ale/definition.vim + runtime autoload/ale/linter.vim + runtime autoload/ale/lsp.vim + +Execute(Other messages for the tsserver handler should be ignored): + call ale#definition#HandleTSServerResponse(1, {'command': 'foo'}) + +Execute(Failed definition responses should be handled correctly): + call ale#definition#SetMap({3: {'open_in_tab': 0}}) + call ale#definition#HandleTSServerResponse( + \ 1, + \ {'command': 'definition', 'request_seq': 3} + \) + AssertEqual {}, ale#definition#GetMap() + +Given typescript(Some typescript file): + foo + somelongerline + bazxyzxyzxyz + +Execute(Other files should be jumped to for definition responses): + call ale#definition#SetMap({3: {'open_in_tab': 0}}) + call ale#definition#HandleTSServerResponse( + \ 1, + \ { + \ 'command': 'definition', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': [ + \ { + \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), + \ 'start': {'line': 3, 'offset': 7}, + \ }, + \ ], + \ } + \) + + AssertEqual + \ [ + \ 'edit ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ ], + \ g:expr_list + AssertEqual [3, 7], getpos('.')[1:2] + AssertEqual {}, ale#definition#GetMap() + +Execute(Other files should be jumped to for definition responses in tabs too): + call ale#definition#SetMap({3: {'open_in_tab': 1}}) + call ale#definition#HandleTSServerResponse( + \ 1, + \ { + \ 'command': 'definition', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': [ + \ { + \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'), + \ 'start': {'line': 3, 'offset': 7}, + \ }, + \ ], + \ } + \) + + AssertEqual + \ [ + \ 'tabedit ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ ], + \ g:expr_list + AssertEqual [3, 7], getpos('.')[1:2] + AssertEqual {}, ale#definition#GetMap() + +Execute(tsserver completion requests should be sent): + runtime ale_linters/typescript/tsserver.vim + call setpos('.', [bufnr(''), 2, 5, 0]) + + ALEGoToDefinition + + AssertEqual + \ 'function(''ale#definition#HandleTSServerResponse'')', + \ string(g:Callback) + AssertEqual + \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]], + \ g:message_list + AssertEqual {'42': {'open_in_tab': 0}}, ale#definition#GetMap() + +Execute(tsserver tab completion requests should be sent): + runtime ale_linters/typescript/tsserver.vim + call setpos('.', [bufnr(''), 2, 5, 0]) + + ALEGoToDefinitionInTab + + AssertEqual + \ 'function(''ale#definition#HandleTSServerResponse'')', + \ string(g:Callback) + AssertEqual + \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]], + \ g:message_list + AssertEqual {'42': {'open_in_tab': 1}}, ale#definition#GetMap() + +Given python(Some Python file): + foo + somelongerline + bazxyzxyzxyz + +Execute(Other files should be jumped to for LSP definition responses): + call ale#definition#SetMap({3: {'open_in_tab': 0}}) + call ale#definition#HandleLSPResponse( + \ 1, + \ { + \ 'id': 3, + \ 'result': { + \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ 'range': { + \ 'start': {'line': 2, 'character': 7}, + \ }, + \ }, + \ } + \) + + AssertEqual + \ [ + \ 'edit ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ ], + \ g:expr_list + AssertEqual [3, 7], getpos('.')[1:2] + AssertEqual {}, ale#definition#GetMap() + +Execute(Other files should be jumped to in tabs for LSP definition responses): + call ale#definition#SetMap({3: {'open_in_tab': 1}}) + call ale#definition#HandleLSPResponse( + \ 1, + \ { + \ 'id': 3, + \ 'result': { + \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ 'range': { + \ 'start': {'line': 2, 'character': 7}, + \ }, + \ }, + \ } + \) + + AssertEqual + \ [ + \ 'tabedit ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ ], + \ g:expr_list + AssertEqual [3, 7], getpos('.')[1:2] + AssertEqual {}, ale#definition#GetMap() + +Execute(Definition responses with lists should be handled): + call ale#definition#SetMap({3: {'open_in_tab': 0}}) + call ale#definition#HandleLSPResponse( + \ 1, + \ { + \ 'id': 3, + \ 'result': [ + \ { + \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ 'range': { + \ 'start': {'line': 2, 'character': 7}, + \ }, + \ }, + \ { + \ 'uri': ale#path#ToURI(ale#path#Simplify(g:dir . '/other_file')), + \ 'range': { + \ 'start': {'line': 20, 'character': 3}, + \ }, + \ }, + \ ], + \ } + \) + + AssertEqual + \ [ + \ 'edit ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')), + \ ], + \ g:expr_list + AssertEqual [3, 7], getpos('.')[1:2] + AssertEqual {}, ale#definition#GetMap() + +Execute(Definition responses with null response should be handled): + call ale#definition#SetMap({3: {'open_in_tab': 0}}) + call ale#definition#HandleLSPResponse(1, {'id': 3, 'result': v:null}) + + AssertEqual [], g:expr_list + +Execute(LSP completion requests should be sent): + runtime ale_linters/python/pyls.vim + let b:ale_linters = ['pyls'] + call setpos('.', [bufnr(''), 1, 5, 0]) + + ALEGoToDefinition + + AssertEqual + \ 'function(''ale#definition#HandleLSPResponse'')', + \ string(g:Callback) + + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}] + \ }], + \ [0, 'textDocument/definition', { + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, + \ 'position': {'line': 0, 'character': 3}, + \ }], + \ ], + \ g:message_list + + AssertEqual {'42': {'open_in_tab': 0}}, ale#definition#GetMap() + +Execute(LSP tab completion requests should be sent): + runtime ale_linters/python/pyls.vim + let b:ale_linters = ['pyls'] + call setpos('.', [bufnr(''), 1, 5, 0]) + + ALEGoToDefinitionInTab + + AssertEqual + \ 'function(''ale#definition#HandleLSPResponse'')', + \ string(g:Callback) + + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}] + \ }], + \ [0, 'textDocument/definition', { + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, + \ 'position': {'line': 0, 'character': 3}, + \ }], + \ ], + \ g:message_list + + AssertEqual {'42': {'open_in_tab': 1}}, ale#definition#GetMap() diff --git a/test/test_gradle_build_classpath_command.vader b/test/test_gradle_build_classpath_command.vader index c31dc69..3c1eceb 100644 --- a/test/test_gradle_build_classpath_command.vader +++ b/test/test_gradle_build_classpath_command.vader @@ -10,7 +10,7 @@ Before: let g:command_tail = ' -I ' . ale#Escape(ale#gradle#GetInitPath()) \ . ' -q printClasspath' - let g:gradle_init_path = ale#path#Winify(g:dir . '../../autoload/ale/gradle/init.gradle') + let g:gradle_init_path = ale#path#Simplify(g:dir . '../../autoload/ale/gradle/init.gradle') After: Restore @@ -25,18 +25,18 @@ Execute(Should return 'gradlew' command if project includes gradle wapper): call ale#test#SetFilename('gradle-test-files/wrapped-project/src/main/kotlin/dummy.kt') AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/gradle-test-files/wrapped-project')) - \ . ' && ' . ale#Escape(ale#path#Winify(g:dir . '/gradle-test-files/wrapped-project/gradlew')) + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/gradle-test-files/wrapped-project')) + \ . ' && ' . ale#Escape(ale#path#Simplify(g:dir . '/gradle-test-files/wrapped-project/gradlew')) \ . g:command_tail, \ ale#gradle#BuildClasspathCommand(bufnr('')) Execute(Should return 'gradle' command if project does not include gradle wapper): call ale#test#SetFilename('gradle-test-files/unwrapped-project/src/main/kotlin/dummy.kt') let $PATH .= (has('win32') ? ';' : ':') - \ . ale#path#Winify(g:dir . '/gradle-test-files') + \ . ale#path#Simplify(g:dir . '/gradle-test-files') AssertEqual - \ 'cd ' . ale#Escape(ale#path#Winify(g:dir . '/gradle-test-files/unwrapped-project')) + \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/gradle-test-files/unwrapped-project')) \ . ' && ' . ale#Escape('gradle') \ . g:command_tail, \ ale#gradle#BuildClasspathCommand(bufnr('')) diff --git a/test/test_gradle_find_executable.vader b/test/test_gradle_find_executable.vader index 054c21a..5daa490 100644 --- a/test/test_gradle_find_executable.vader +++ b/test/test_gradle_find_executable.vader @@ -18,12 +18,12 @@ Execute(Should return 'gradlew' if found in parent directory): call ale#test#SetFilename('gradle-test-files/wrapped-project/src/main/kotlin/dummy.kt') AssertEqual - \ ale#path#Winify(g:dir . '/gradle-test-files/wrapped-project/gradlew'), + \ ale#path#Simplify(g:dir . '/gradle-test-files/wrapped-project/gradlew'), \ ale#gradle#FindExecutable(bufnr('')) Execute(Should return 'gradle' if 'gradlew' not found in parent directory): call ale#test#SetFilename('gradle-test-files/unwrapped-project/src/main/kotlin/dummy.kt') - let $PATH .= (has('win32') ? ';': ':') . ale#path#Winify(g:dir . '/gradle-test-files') + let $PATH .= (has('win32') ? ';': ':') . ale#path#Simplify(g:dir . '/gradle-test-files') AssertEqual \ 'gradle', diff --git a/test/test_gradle_find_project_root.vader b/test/test_gradle_find_project_root.vader index 87af110..8305bba 100644 --- a/test/test_gradle_find_project_root.vader +++ b/test/test_gradle_find_project_root.vader @@ -10,21 +10,21 @@ Execute(Should return directory for 'gradlew' if found in parent directory): call ale#test#SetFilename('gradle-test-files/wrapped-project/src/main/kotlin/dummy.kt') AssertEqual - \ ale#path#Winify(g:dir . '/gradle-test-files/wrapped-project'), + \ ale#path#Simplify(g:dir . '/gradle-test-files/wrapped-project'), \ ale#gradle#FindProjectRoot(bufnr('')) Execute(Should return directory for 'settings.gradle' if found in parent directory): call ale#test#SetFilename('gradle-test-files/settings-gradle-project/src/main/kotlin/dummy.kt') AssertEqual - \ ale#path#Winify(g:dir . '/gradle-test-files/settings-gradle-project'), + \ ale#path#Simplify(g:dir . '/gradle-test-files/settings-gradle-project'), \ ale#gradle#FindProjectRoot(bufnr('')) Execute(Should return directory for 'build.gradle' if found in parent directory): call ale#test#SetFilename('gradle-test-files/build-gradle-project/src/main/kotlin/dummy.kt') AssertEqual - \ ale#path#Winify(g:dir . '/gradle-test-files/build-gradle-project'), + \ ale#path#Simplify(g:dir . '/gradle-test-files/build-gradle-project'), \ ale#gradle#FindProjectRoot(bufnr('')) Execute(Should return empty string if gradle files are not found in parent directory): diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader index de8decc..725faff 100644 --- a/test/test_highlight_placement.vader +++ b/test/test_highlight_placement.vader @@ -1,4 +1,7 @@ Before: + Save g:ale_enabled + Save g:ale_set_signs + function! GenerateResults(buffer, output) return [ \ { @@ -37,13 +40,16 @@ Before: call ale#linter#Define('testft', { \ 'name': 'x', \ 'executable': has('win32') ? 'cmd': 'echo', - \ 'command': 'echo', + \ 'command': has('win32') ? 'echo' : '/bin/sh -c ''echo''', \ 'callback': 'GenerateResults', \}) highlight link SomeOtherGroup SpellBad After: + Restore + unlet! g:items + unlet! b:ale_enabled delfunction GenerateResults call ale#linter#Reset() @@ -206,7 +212,7 @@ Execute(Highlighting should support errors spanning many lines): \ }, \ ], \ GetMatchesWithoutIDs() - \ + Execute(Highlights should always be cleared when the buffer highlight list is empty): " Add our highlights and something else. call matchaddpos('ALEError', [[1, 1, 1]]) @@ -232,3 +238,44 @@ Execute(Highlights should always be cleared when the buffer highlight list is em \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, \ ], \ GetMatchesWithoutIDs() + +Execute(Highlights should be cleared when ALE is disabled): + let g:ale_enabled = 1 + call ale#highlight#SetHighlights(bufnr(''), [ + \ {'bufnr': bufnr(''), 'type': 'E', 'lnum': 1, 'col': 1, 'end_lnum': 10, 'end_col': 3}, + \]) + + let g:ale_enabled = 0 + call ale#highlight#UpdateHighlights() + + AssertEqual [], GetMatchesWithoutIDs() + + let g:ale_enabled = 1 + call ale#highlight#SetHighlights(bufnr(''), [ + \ {'bufnr': bufnr(''), 'type': 'E', 'lnum': 1, 'col': 1, 'end_lnum': 10, 'end_col': 3}, + \]) + + let b:ale_enabled = 0 + call ale#highlight#UpdateHighlights() + + AssertEqual [], GetMatchesWithoutIDs() + +Execute(Line highlights should be set when signs are disabled): + let g:ale_set_signs = 0 + + call ale#highlight#SetHighlights(bufnr(''), [ + \ {'bufnr': bufnr(''), 'type': 'E', 'lnum': 1, 'col': 1}, + \ {'bufnr': bufnr(''), 'type': 'W', 'lnum': 2, 'col': 1}, + \ {'bufnr': bufnr(''), 'type': 'I', 'lnum': 3, 'col': 1}, + \]) + + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, + \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, + \ {'group': 'ALEErrorLine', 'priority': 10, 'pos1': [1]}, + \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2]}, + \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3]}, + \ ], + \ GetMatchesWithoutIDs() diff --git a/test/test_history_saving.vader b/test/test_history_saving.vader index 020ceb5..7dabcd9 100644 --- a/test/test_history_saving.vader +++ b/test/test_history_saving.vader @@ -76,7 +76,7 @@ Execute(History should be set when commands are run): AssertEqual sort(['status', 'exit_code', 'job_id', 'command']), sort(keys(g:history[0])) if has('win32') - AssertEqual 'cmd /c echo command history test', g:history[0].command + AssertEqual 'cmd /s/c "echo command history test"', g:history[0].command else AssertEqual ['/bin/sh', '-c', '/bin/sh -c ''echo command history test'''], g:history[0].command endif @@ -151,7 +151,7 @@ Execute(The history should be updated when fixers are run): AssertEqual ['finished'], map(copy(b:ale_history), 'v:val.status') if has('win32') - AssertEqual 'cmd /c echo foo ', split(b:ale_history[0].command, '<')[0] + AssertEqual 'cmd /s/c "echo foo ', split(b:ale_history[0].command, '<')[0] else AssertEqual '/bin/sh -c echo foo ', split(join(b:ale_history[0].command), '<')[0] endif diff --git a/test/test_line_join.vader b/test/test_line_join.vader index 0426429..25cefbc 100644 --- a/test/test_line_join.vader +++ b/test/test_line_join.vader @@ -17,6 +17,12 @@ After: delfunction LineCallback delfunction RawCallback +Execute (ALE should handle empty Lists for the lines): + let g:last_line = ale#job#JoinNeovimOutput(1, '', [], 'nl', function('LineCallback')) + + AssertEqual [], g:lines + AssertEqual '', g:last_line + Execute (ALE should pass on full lines for NeoVim): let g:last_line = ale#job#JoinNeovimOutput(1, '', ['x', 'y', ''], 'nl', function('LineCallback')) @@ -62,8 +68,8 @@ Execute (ALE should pass on full lines for NeoVim for raw data): Execute (ALE should pass on a single long line): let g:last_line = ale#job#JoinNeovimOutput(1, '', ['x'], 'raw', function('RawCallback')) - AssertEqual '', g:data - AssertEqual 'x', g:last_line + AssertEqual 'x', g:data + AssertEqual '', g:last_line Execute (ALE should handle just a single line of output): let g:last_line = ale#job#JoinNeovimOutput(1, '', ['x', ''], 'raw', function('RawCallback')) @@ -71,20 +77,8 @@ Execute (ALE should handle just a single line of output): AssertEqual "x\n", g:data AssertEqual '', g:last_line -Execute (ALE should join two incomplete pieces of large lines together): - let g:last_line = ale#job#JoinNeovimOutput(1, 'x', ['y'], 'raw', function('RawCallback')) +Execute (ALE should pass on two lines and one incomplete one): + let g:last_line = ale#job#JoinNeovimOutput(1, '', ['y', 'z', 'a'], 'raw', function('RawCallback')) - AssertEqual '', g:data - AssertEqual 'xy', g:last_line - -Execute (ALE join incomplete lines, and set new ones): - let g:last_line = ale#job#JoinNeovimOutput(1, 'x', ['y', 'z', 'a'], 'raw', function('RawCallback')) - - AssertEqual "xy\nz\n", g:data - AssertEqual 'a', g:last_line - -Execute (ALE join incomplete lines, and set new ones, with two elements): - let g:last_line = ale#job#JoinNeovimOutput(1, 'x', ['y', 'z'], 'raw', function('RawCallback')) - - AssertEqual "xy\n", g:data - AssertEqual 'z', g:last_line + AssertEqual "y\nz\na", g:data + AssertEqual '', g:last_line diff --git a/test/test_lint_file_linters.vader b/test/test_lint_file_linters.vader index bea8c3f..2e992e1 100644 --- a/test/test_lint_file_linters.vader +++ b/test/test_lint_file_linters.vader @@ -1,4 +1,6 @@ Before: + Save g:ale_fix_on_save + Save g:ale_enabled Save g:ale_run_synchronously Save g:ale_set_lists_synchronously Save g:ale_buffer_info @@ -7,6 +9,7 @@ Before: let g:ale_buffer_info = {} let g:ale_run_synchronously = 1 let g:ale_set_lists_synchronously = 1 + let b:ale_save_event_fired = 0 call ale#ResetLintFileMarkers() let g:buffer_result = [ @@ -261,7 +264,10 @@ Execute(The Save event should respect the buffer number): \], GetSimplerLoclist() Execute(The Save event should set b:ale_save_event_fired to 1): - let b:ale_enabled = 0 + let g:ale_lint_on_save = 1 + let b:ale_enabled = 1 + + call ale#linter#Reset() call ale#events#SaveEvent(bufnr('')) " This flag needs to be set so windows can be opened, etc. @@ -289,3 +295,10 @@ Execute(lint_file linters should stay running after checking without them): AssertEqual 2, len(g:ale_buffer_info[bufnr('')].job_list) call ale#engine#WaitForJobs(2000) + +Execute(The save event should not lint the buffer when ALE is disabled): + let g:ale_enabled = 0 + call ale#events#SaveEvent(bufnr('')) + + AssertEqual [], GetSimplerLoclist() + AssertEqual 0, b:ale_save_event_fired diff --git a/test/test_linter_retrieval.vader b/test/test_linter_retrieval.vader index 1a1e258..5d1ee45 100644 --- a/test/test_linter_retrieval.vader +++ b/test/test_linter_retrieval.vader @@ -1,5 +1,6 @@ Before: - Save g:ale_linters, g:ale_linter_aliases + Save g:ale_linters + Save g:ale_linter_aliases let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout', 'read_buffer': 1, 'lint_file': 0, 'aliases': [], 'lsp': '', 'add_newline': 0} let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout', 'read_buffer': 0, 'lint_file': 1, 'aliases': [], 'lsp': '', 'add_newline': 0} @@ -42,6 +43,30 @@ Execute (You should be able to select linters with a buffer option): AssertEqual [g:testlinter1], ale#linter#Get('testft') +Execute (b:ale_linters should work when set to a List): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1', 'testlinter2']} + let b:ale_linters = ['testlinter1'] + + AssertEqual [g:testlinter1], ale#linter#Get('testft') + +Execute (b:ale_linters should disable all linters when set to an empty List): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1', 'testlinter2']} + let b:ale_linters = [] + + AssertEqual [], ale#linter#Get('testft') + +Execute (b:ale_linters should enable all available linters when set to 'all'): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1']} + let b:ale_linters = 'all' + + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft') + Execute (Buffer settings shouldn't completely replace global settings): call ale#linter#Define('testft', g:testlinter1) call ale#linter#Define('testft', g:testlinter2) @@ -102,6 +127,21 @@ Execute (The local alias option shouldn't completely replace the global one): " global Dictionary. let b:ale_linter_aliases = {'testft3': ['testft1']} +Execute (Lists should be accepted for local aliases): + call ale#linter#Define('testft1', g:testlinter1) + call ale#linter#Define('testft2', g:testlinter2) + let g:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} + " We should load the testft2 linters for this buffer, with no duplicates. + let b:ale_linter_aliases = ['testft2'] + + AssertEqual [g:testlinter2], ale#linter#Get('anything.else') + +Execute (Buffer-local overrides for aliases should be used): + call ale#linter#Define('testft1', g:testlinter1) + call ale#linter#Define('testft2', g:testlinter2) + let g:ale_linter_aliases = {'testft1': ['testft2']} + let b:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1') Execute (Linters should be loaded from disk appropriately): diff --git a/test/test_list_formatting.vader b/test/test_list_formatting.vader new file mode 100644 index 0000000..0c52f10 --- /dev/null +++ b/test/test_list_formatting.vader @@ -0,0 +1,188 @@ +Before: + Save g:ale_set_loclist + Save g:ale_set_quickfix + Save g:ale_loclist_msg_format + Save g:ale_open_list + Save g:ale_buffer_info + Save g:ale_set_lists_synchronously + + let g:ale_set_lists_synchronously = 1 + let g:ale_loclist_msg_format = '%code: %%s' + let g:ale_open_list = 0 + let g:loclist = [] + let g:ale_buffer_info = {bufnr(''): {'loclist': g:loclist}} + + function! AddItem(data) abort + let l:item = { + \ 'bufnr': bufnr(''), + \ 'lnum': 1, + \ 'col': 1, + \ 'type': 'E', + \ 'linter_name': 'some_linter', + \} + + call add(g:loclist, extend(l:item, a:data)) + endfunction + +After: + Restore + + unlet! g:loclist + unlet! b:ale_loclist_msg_format + + delfunction AddItem + + call setloclist(0, []) + call setqflist([]) + +Execute(Formatting with codes should work for the loclist): + call AddItem({'text': 'nocode'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'nocode', + \ }, + \ ], + \ getloclist(0) + + call remove(g:loclist, 0) + call AddItem({'text': 'withcode', 'code': 'E123'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'E123: withcode', + \ }, + \ ], + \ getloclist(0) + +Execute(Formatting with codes should work for the quickfix list): + let g:ale_set_loclist = 0 + let g:ale_set_quickfix = 1 + + call AddItem({'text': 'nocode'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'nocode', + \ }, + \ ], + \ getqflist() + + call remove(g:loclist, 0) + call AddItem({'text': 'withcode', 'code': 'E123'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'E123: withcode', + \ }, + \ ], + \ getqflist() + +Execute(Formatting with the linter name should work for the loclist): + let g:ale_loclist_msg_format = '(%linter%) %s' + + call AddItem({'text': 'whatever'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': '(some_linter) whatever', + \ }, + \ ], + \ getloclist(0) + +Execute(Formatting with the linter name should work for the quickfix list): + let g:ale_loclist_msg_format = '(%linter%) %s' + let g:ale_set_loclist = 0 + let g:ale_set_quickfix = 1 + + call AddItem({'text': 'whatever'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': '(some_linter) whatever', + \ }, + \ ], + \ getqflist() + +Execute(The buffer loclist format option should take precedence): + let g:ale_loclist_msg_format = '(%linter%) %s' + let b:ale_loclist_msg_format = 'FOO %s' + + call AddItem({'text': 'whatever'}) + call ale#list#SetLists(bufnr(''), g:loclist) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 1, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': 0, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'FOO whatever', + \ }, + \ ], + \ getloclist(0) diff --git a/test/test_list_opening.vader b/test/test_list_opening.vader index 63b30ef..a24e8de 100644 --- a/test/test_list_opening.vader +++ b/test/test_list_opening.vader @@ -5,6 +5,7 @@ Before: Save g:ale_open_list Save g:ale_keep_list_window_open Save g:ale_list_window_size + Save g:ale_list_vertical Save g:ale_buffer_info Save g:ale_set_lists_synchronously @@ -13,6 +14,7 @@ Before: let g:ale_open_list = 0 let g:ale_keep_list_window_open = 0 let g:ale_list_window_size = 10 + let g:ale_list_vertical = 0 let g:ale_set_lists_synchronously = 1 let g:loclist = [ @@ -33,16 +35,29 @@ Before: return 0 endfunction + " If the window is vertical, window size should match column size/width + function GetQuickfixIsVertical(cols) abort + for l:win in range(1, winnr('$')) + if getwinvar(l:win, '&buftype') is# 'quickfix' + return winwidth(l:win) == a:cols + endif + endfor + + return 0 + endfunction + After: Restore unlet! g:loclist + unlet! b:ale_list_vertical unlet! b:ale_list_window_size unlet! b:ale_open_list unlet! b:ale_keep_list_window_open unlet! b:ale_save_event_fired delfunction GetQuickfixHeight + delfunction GetQuickfixIsVertical " Close quickfix window after every execute block lcl @@ -98,6 +113,24 @@ Execute(The quickfix window height should be correct for the loclist with buffer AssertEqual 8, GetQuickfixHeight() +Execute(The quickfix window should be vertical for the loclist with appropriate variables): + let g:ale_open_list = 1 + let b:ale_list_window_size = 8 + let b:ale_list_vertical = 1 + + call ale#list#SetLists(bufnr('%'), g:loclist) + + AssertEqual 1, GetQuickfixIsVertical(b:ale_list_window_size) + +Execute(The quickfix window should be horizontal for the loclist with appropriate variables): + let g:ale_open_list = 1 + let b:ale_list_window_size = 8 + let b:ale_list_vertical = 0 + + call ale#list#SetLists(bufnr('%'), g:loclist) + + AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size) + Execute(The quickfix window should stay open for just the loclist): let g:ale_open_list = 1 let g:ale_keep_list_window_open = 1 @@ -167,6 +200,24 @@ Execute(The quickfix window height should be correct for the quickfix list with AssertEqual 8, GetQuickfixHeight() +Execute(The quickfix window should be vertical for the quickfix with appropriate variables): + let g:ale_open_list = 1 + let b:ale_list_window_size = 8 + let b:ale_list_vertical = 1 + + call ale#list#SetLists(bufnr('%'), g:loclist) + + AssertEqual 1, GetQuickfixIsVertical(b:ale_list_window_size) + +Execute(The quickfix window should be horizontal for the quickfix with appropriate variables): + let g:ale_open_list = 1 + let b:ale_list_window_size = 8 + let b:ale_list_vertical = 0 + + call ale#list#SetLists(bufnr('%'), g:loclist) + + AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size) + Execute(The buffer ale_open_list option should be respected): let b:ale_open_list = 1 diff --git a/test/test_list_titles.vader b/test/test_list_titles.vader index e729541..d521906 100644 --- a/test/test_list_titles.vader +++ b/test/test_list_titles.vader @@ -42,7 +42,7 @@ Execute(The loclist titles should be set appropriately): if !has('nvim') AssertEqual - \ {'title': ale#path#Winify(getcwd() . '/foo')}, + \ {'title': ale#path#Simplify(getcwd() . '/foo')}, \ getloclist(0, {'title': ''}) endif @@ -72,6 +72,6 @@ Execute(The quickfix titles should be set appropriately): if !has('nvim') AssertEqual - \ {'title': ale#path#Winify(getcwd() . '/foo')}, + \ {'title': ale#path#Simplify(getcwd() . '/foo')}, \ getqflist({'title': ''}) endif diff --git a/test/test_loclist_corrections.vader b/test/test_loclist_corrections.vader index e6844d8..6224d60 100644 --- a/test/test_loclist_corrections.vader +++ b/test/test_loclist_corrections.vader @@ -327,3 +327,24 @@ Execute(FixLocList should interpret temporary filenames as being the current buf \ {'text': 'a', 'lnum': 3, 'filename': b:temp_name}, \ ], \ ) + +Execute(The error code should be passed on): + AssertEqual + \ [ + \ { + \ 'text': 'a', + \ 'lnum': 10, + \ 'col': 0, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'foobar', + \ 'code': 'some-code' + \ }, + \], + \ ale#engine#FixLocList( + \ bufnr('%'), + \ 'foobar', + \ [{'text': 'a', 'lnum': 11, 'code': 'some-code'}], + \ ) diff --git a/test/test_nearest_file_search.vader b/test/test_nearest_file_search.vader index 63e82da..10d2cb3 100644 --- a/test/test_nearest_file_search.vader +++ b/test/test_nearest_file_search.vader @@ -8,7 +8,7 @@ Execute(We should be able to find a configuration file further up): call ale#test#SetFilename('top/middle/bottom/dummy.txt') AssertEqual - \ ale#path#Winify(expand('%:p:h:h:h:h') . '/top/example.ini'), + \ ale#path#Simplify(expand('%:p:h:h:h:h') . '/top/example.ini'), \ ale#path#FindNearestFile(bufnr('%'), 'example.ini') Execute(We shouldn't find anything for files which don't match): diff --git a/test/test_path_equality.vader b/test/test_path_equality.vader index 314a2d9..4ec9bd6 100644 --- a/test/test_path_equality.vader +++ b/test/test_path_equality.vader @@ -1,6 +1,6 @@ Before: function! CheckPath(path) abort - return ale#path#IsBufferPath(bufnr(''), ale#path#Winify(a:path)) + return ale#path#IsBufferPath(bufnr(''), ale#path#Simplify(a:path)) endfunction After: diff --git a/test/test_path_upwards.vader b/test/test_path_upwards.vader index 8b81a10..cd461a2 100644 --- a/test/test_path_upwards.vader +++ b/test/test_path_upwards.vader @@ -1,52 +1,48 @@ -After: - let g:ale_has_override = {} +Execute(ale#path#Upwards should return the correct path components): + if has('unix') + " Absolute paths should include / on the end. + AssertEqual + \ ['/foo/bar/baz', '/foo/bar', '/foo', '/'], + \ ale#path#Upwards('/foo/bar/baz') + AssertEqual + \ ['/foo/bar/baz', '/foo/bar', '/foo', '/'], + \ ale#path#Upwards('/foo/bar/baz///') + " Relative paths do not. + AssertEqual + \ ['foo/bar/baz', 'foo/bar', 'foo'], + \ ale#path#Upwards('foo/bar/baz') + AssertEqual + \ ['foo2/bar', 'foo2'], + \ ale#path#Upwards('foo//..////foo2////bar') + " Expect an empty List for empty strings. + AssertEqual [], ale#path#Upwards('') + endif -Execute(ale#path#Upwards should return the correct path components for Unix): - let g:ale_has_override = {'win32': 0} - - " Absolute paths should include / on the end. - AssertEqual - \ ['/foo/bar/baz', '/foo/bar', '/foo', '/'], - \ ale#path#Upwards('/foo/bar/baz') - AssertEqual - \ ['/foo/bar/baz', '/foo/bar', '/foo', '/'], - \ ale#path#Upwards('/foo/bar/baz///') - " Relative paths do not. - AssertEqual - \ ['foo/bar/baz', 'foo/bar', 'foo'], - \ ale#path#Upwards('foo/bar/baz') - AssertEqual - \ ['foo2/bar', 'foo2'], - \ ale#path#Upwards('foo//..////foo2////bar') - " Expect an empty List for empty strings. - AssertEqual [], ale#path#Upwards('') - -Execute(ale#path#Upwards should return the correct path components for Windows): - let g:ale_has_override = {'win32': 1} - - AssertEqual - \ ['C:\foo\bar\baz', 'C:\foo\bar', 'C:\foo', 'C:\'], - \ ale#path#Upwards('C:\foo\bar\baz') - AssertEqual - \ ['C:\foo\bar\baz', 'C:\foo\bar', 'C:\foo', 'C:\'], - \ ale#path#Upwards('C:\foo\bar\baz\\\') - AssertEqual - \ ['/foo\bar\baz', '/foo\bar', '/foo', '/'], - \ ale#path#Upwards('/foo/bar/baz') - AssertEqual - \ ['foo\bar\baz', 'foo\bar', 'foo'], - \ ale#path#Upwards('foo/bar/baz') - AssertEqual - \ ['foo\bar\baz', 'foo\bar', 'foo'], - \ ale#path#Upwards('foo\bar\baz') - " simplify() is used internally, and should sort out \ paths when actually - " running Windows, which we can't test here. - AssertEqual - \ ['foo2\bar', 'foo2'], - \ ale#path#Upwards('foo//..///foo2////bar') - " Expect an empty List for empty strings. - AssertEqual [], ale#path#Upwards('') - " Paths starting with // return / - AssertEqual - \ ['/foo2\bar', '/foo2', '/'], - \ ale#path#Upwards('//foo//..///foo2////bar') + if has('win32') + AssertEqual + \ ['C:\foo\bar\baz', 'C:\foo\bar', 'C:\foo', 'C:\'], + \ ale#path#Upwards('C:\foo\bar\baz') + AssertEqual + \ ['C:\foo\bar\baz', 'C:\foo\bar', 'C:\foo', 'C:\'], + \ ale#path#Upwards('C:\foo\bar\baz\\\') + AssertEqual + \ ['/foo\bar\baz', '/foo\bar', '/foo', '/'], + \ ale#path#Upwards('/foo/bar/baz') + AssertEqual + \ ['foo\bar\baz', 'foo\bar', 'foo'], + \ ale#path#Upwards('foo/bar/baz') + AssertEqual + \ ['foo\bar\baz', 'foo\bar', 'foo'], + \ ale#path#Upwards('foo\bar\baz') + " simplify() is used internally, and should sort out \ paths when actually + " running Windows, which we can't test here. + AssertEqual + \ ['foo2\bar', 'foo2'], + \ ale#path#Upwards('foo//..///foo2////bar') + " Expect an empty List for empty strings. + AssertEqual [], ale#path#Upwards('') + " Paths starting with // return / + AssertEqual + \ ['/foo2\bar', '/foo2', '/'], + \ ale#path#Upwards('//foo//..///foo2////bar') + endif diff --git a/test/test_path_uri.vader b/test/test_path_uri.vader index dbceac3..a3e68d9 100644 --- a/test/test_path_uri.vader +++ b/test/test_path_uri.vader @@ -2,6 +2,9 @@ Execute(ale#path#ToURI should work for Windows paths): AssertEqual 'file:///C:/foo/bar/baz.tst', ale#path#ToURI('C:\foo\bar\baz.tst') AssertEqual 'foo/bar/baz.tst', ale#path#ToURI('foo\bar\baz.tst') +Execute(ale#path#FromURI should work for Windows paths): + AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:///C:/foo/bar/baz.tst') + Execute(ale#path#ToURI should work for Unix paths): AssertEqual 'file:///foo/bar/baz.tst', ale#path#ToURI('/foo/bar/baz.tst') AssertEqual 'foo/bar/baz.tst', ale#path#ToURI('foo/bar/baz.tst') diff --git a/test/test_pattern_options.vader b/test/test_pattern_options.vader index 164e5aa..0e26eaa 100644 --- a/test/test_pattern_options.vader +++ b/test/test_pattern_options.vader @@ -3,30 +3,90 @@ Before: Save g:ale_pattern_options_enabled Save &filetype + let g:ale_pattern_options_enabled = 1 + let g:ale_pattern_options = {} + + let b:ale_enabled = 0 + let b:some_option = 0 + + call ale#test#SetDirectory('/testplugin/test') + After: Restore unlet! b:ale_enabled unlet! b:some_option + call ale#test#RestoreDirectory() + +Execute(The pattern options function should work when there are no patterns): + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + Execute(Buffer variables should be set when filename patterns match): - let g:ale_pattern_options = {'baz.*\.js': { - \ 'ale_enabled': 1, - \ 'some_option': 347, - \ '&filetype': 'pattern_option_set_filetype', - \}} + let g:ale_pattern_options = { + \ 'baz.*\.js': { + \ 'ale_enabled': 1, + \ 'some_option': 347, + \ '&filetype': 'pattern_option_set_filetype', + \ }, + \} - silent! file foobar.js + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) - call ale#pattern_options#SetOptions() + AssertEqual 0, b:ale_enabled + AssertEqual 0, b:some_option - Assert !exists('b:ale_enabled') - Assert !exists('b:some_option') - - silent! file bazboz.js - - call ale#pattern_options#SetOptions() + call ale#test#SetFilename('bazboz.js') + call ale#pattern_options#SetOptions(bufnr('')) AssertEqual 1, b:ale_enabled AssertEqual 347, b:some_option AssertEqual 'pattern_option_set_filetype', &filetype + +Execute(Multiple pattern matches should be applied): + let g:ale_pattern_options = { + \ 'foo': { + \ 'some_option': 666, + \ }, + \ 'bar': { + \ 'ale_enabled': 1, + \ 'some_option': 123, + \ }, + \ 'notmatched': { + \ 'some_option': 489, + \ 'ale_enabled': 0, + \ }, + \} + + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 1, b:ale_enabled + AssertEqual 666, b:some_option + +Execute(Patterns should not be applied when the setting is disabled): + let g:ale_pattern_options_enabled = 0 + let g:ale_pattern_options = {'foo': {'some_option': 666}} + + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 0, b:some_option + +" This test is important for making sure we update the sorted items. +Execute(Patterns should be applied after the Dictionary changes): + call ale#test#SetFilename('foobar.js') + + let g:ale_pattern_options = {} + + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 0, b:some_option + + let g:ale_pattern_options['foo'] = {'some_option': 666} + + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 666, b:some_option diff --git a/test/test_phpcs_executable_detection.vader b/test/test_phpcs_executable_detection.vader index f51ba9f..020bfac 100644 --- a/test/test_phpcs_executable_detection.vader +++ b/test/test_phpcs_executable_detection.vader @@ -19,7 +19,7 @@ Execute(project with phpcs should use local by default): call ale#test#SetFilename('phpcs-test-files/project-with-phpcs/foo/test.php') AssertEqual - \ ale#path#Winify(g:dir . '/phpcs-test-files/project-with-phpcs/vendor/bin/phpcs'), + \ ale#path#Simplify(g:dir . '/phpcs-test-files/project-with-phpcs/vendor/bin/phpcs'), \ ale_linters#php#phpcs#GetExecutable(bufnr('')) Execute(use-global should override local detection): diff --git a/test/test_phpcs_include_code.vader b/test/test_phpcs_include_code.vader new file mode 100644 index 0000000..1cff191 --- /dev/null +++ b/test/test_phpcs_include_code.vader @@ -0,0 +1,7 @@ +Execute(errors should include code): + AssertEqual + \ [{'lnum': 18, 'col': 3, 'type': 'E', 'text': 'Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)'}], + \ ale_linters#php#phpcs#Handle(bufnr(''), [ + \ '/path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)', + \ ]) + diff --git a/test/test_prepare_command.vader b/test/test_prepare_command.vader index ebb9998..ed9272a 100644 --- a/test/test_prepare_command.vader +++ b/test/test_prepare_command.vader @@ -4,35 +4,36 @@ Before: After: Restore - let g:ale_has_override = {} Execute(sh should be used when the shell is fish): - " Set something else, so we will replace that too. - let &shellcmdflag = '-f' - let g:ale_has_override = {'win32': 0} + if !has('win32') + " Set something else, so we will replace that too. + let &shellcmdflag = '-f' + let &shell = 'fish' - let &shell = 'fish' + AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar') - AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar') + let &shell = '/usr/bin/fish' - let &shell = '/usr/bin/fish' + AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar') - AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar') + let &shell = '/usr/local/bin/fish' - let &shell = '/usr/local/bin/fish' - - AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar') + AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar') + endif Execute(Other shells should be used when set): - let &shell = '/bin/bash' - let &shellcmdflag = '-c' - let g:ale_has_override = {'win32': 0} + if !has('win32') + let &shell = '/bin/bash' + let &shellcmdflag = '-c' - AssertEqual ['/bin/bash', '-c', 'foobar'], ale#job#PrepareCommand('foobar') + AssertEqual ['/bin/bash', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar') + endif -Execute(cmd /c as a string should be used on Windows): - let &shell = 'who cares' - let &shellcmdflag = 'whatever' - let g:ale_has_override = {'win32': 1} +Execute(cmd /s/c as a string should be used on Windows): + if has('win32') + let &shell = 'who cares' + let &shellcmdflag = 'whatever' - AssertEqual 'cmd /c foobar', ale#job#PrepareCommand('foobar') + AssertEqual 'cmd /s/c "foobar"', ale#job#PrepareCommand(bufnr(''), 'foobar') + endif diff --git a/test/test_resolve_local_path.vader b/test/test_resolve_local_path.vader index 125ae2f..3f0fb20 100644 --- a/test/test_resolve_local_path.vader +++ b/test/test_resolve_local_path.vader @@ -8,7 +8,7 @@ Execute(We should be able to find the local version of a file): call ale#test#SetFilename('top/middle/bottom/dummy.txt') AssertEqual - \ ale#path#Winify(expand('%:p:h:h:h:h') . '/top/example.ini'), + \ ale#path#Simplify(expand('%:p:h:h:h:h') . '/top/example.ini'), \ ale#path#ResolveLocalPath(bufnr('%'), 'example.ini', '/global/config.ini') Execute(We shouldn't find anything for files which don't match): diff --git a/test/test_semver_utils.vader b/test/test_semver_utils.vader index 9730b74..30e9e81 100644 --- a/test/test_semver_utils.vader +++ b/test/test_semver_utils.vader @@ -1,16 +1,50 @@ -Execute(ParseSemver should return the correct results): - " We should be able to parse the semver string from flake8 - AssertEqual [3, 0, 4], ale#semver#Parse('3.0.4 (mccabe: 0.5.2, pyflakes: 1.2.3, pycodestyle: 2.0.0) CPython 2.7.12 on Linux') +After: + call ale#semver#ResetVersionCache() -Execute(GreaterOrEqual should compare triples correctly): - Assert ale#semver#GreaterOrEqual([3, 0, 4], [3, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 0, 0], [3, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 0, 0], [2, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 1, 0], [3, 1, 0]) - Assert ale#semver#GreaterOrEqual([3, 2, 0], [3, 1, 0]) - Assert ale#semver#GreaterOrEqual([3, 2, 2], [3, 1, 6]) - Assert ale#semver#GreaterOrEqual([3, 2, 5], [3, 2, 5]) - Assert ale#semver#GreaterOrEqual([3, 2, 6], [3, 2, 5]) - Assert !ale#semver#GreaterOrEqual([2, 9, 1], [3, 0, 0]) - Assert !ale#semver#GreaterOrEqual([3, 2, 3], [3, 3, 3]) - Assert !ale#semver#GreaterOrEqual([3, 3, 2], [3, 3, 3]) +Execute(GetVersion should return the version from the lines of output): + " We should be able to parse the semver string from flake8 + AssertEqual [3, 0, 4], ale#semver#GetVersion('dummy', [ + \ '3.0.4 (mccabe: 0.5.2, pyflakes: 1.2.3, pycodestyle: 2.0.0) CPython 2.7.12 on Linux', + \ '1.2.3', + \]) + +Execute(GetVersion should return an empty list when no vesrion can be found): + AssertEqual [], ale#semver#GetVersion('dummy', ['x']) + AssertEqual [], ale#semver#GetVersion('dummy', []) + +Execute(GetVersion should cache the version): + AssertEqual [], ale#semver#GetVersion('dummy', []) + AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', ['Version 3.4.7']) + AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', []) + +Execute(HasVersion should return 1 when the version has been cached): + call ale#semver#GetVersion('dummy', []) + AssertEqual 0, ale#semver#HasVersion('dummy') + call ale#semver#GetVersion('dummy', ['3.4.7']) + AssertEqual 1, ale#semver#HasVersion('dummy') + +Execute(GTE should compare triples correctly): + Assert ale#semver#GTE([3, 0, 4], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 0], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 0], [2, 0, 0]) + Assert ale#semver#GTE([3, 1, 0], [3, 1, 0]) + Assert ale#semver#GTE([3, 2, 0], [3, 1, 0]) + Assert ale#semver#GTE([3, 2, 2], [3, 1, 6]) + Assert ale#semver#GTE([3, 2, 5], [3, 2, 5]) + Assert ale#semver#GTE([3, 2, 6], [3, 2, 5]) + Assert !ale#semver#GTE([2, 9, 1], [3, 0, 0]) + Assert !ale#semver#GTE([3, 2, 3], [3, 3, 3]) + Assert !ale#semver#GTE([3, 3, 2], [3, 3, 3]) + +Execute(GTE should compare pairs correctly): + Assert ale#semver#GTE([3, 0], [3, 0, 0]) + Assert ale#semver#GTE([3, 0], [3, 0]) + Assert ale#semver#GTE([3, 1], [3, 0]) + Assert ale#semver#GTE([3, 1], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 1], [3, 0]) + Assert !ale#semver#GTE([3, 0], [3, 0, 1]) + Assert !ale#semver#GTE([3, 0], [3, 1]) + Assert !ale#semver#GTE([2, 9, 11], [3, 0]) + +Execute(GTE should permit the LHS to be an empty List): + Assert !ale#semver#GTE([], [0, 0, 0]) diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader index 37cf43c..adb8d70 100644 --- a/test/test_shell_detection.vader +++ b/test/test_shell_detection.vader @@ -81,3 +81,23 @@ Execute(The ksh dialect should be used for shellcheck if b:is_kornshell is 1): let b:is_kornshell = 1 AssertEqual 'ksh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with /bin/ash): + #!/bin/ash + +Execute(The ash dialect should be used for the shell and the base function): + AssertEqual 'ash', ale#handlers#sh#GetShellType(bufnr('')) + AssertEqual 'ash', ale_linters#sh#shell#GetExecutable(bufnr('')) + +Execute(dash should be used for shellcheck, which has no ash dialect): + AssertEqual 'dash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with /bin/dash): + #!/bin/dash + +Execute(The dash dialect should be used for the shell and the base function): + AssertEqual 'dash', ale#handlers#sh#GetShellType(bufnr('')) + AssertEqual 'dash', ale_linters#sh#shell#GetExecutable(bufnr('')) + +Execute(dash should be used for shellcheck): + AssertEqual 'dash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr('')) diff --git a/test/test_should_do_nothing_conditions.vader b/test/test_should_do_nothing_conditions.vader index 3afa11a..23ebd92 100644 --- a/test/test_should_do_nothing_conditions.vader +++ b/test/test_should_do_nothing_conditions.vader @@ -1,6 +1,8 @@ Before: Save &l:statusline + call ale#test#SetDirectory('/testplugin/test') + let b:funky_command_created = 0 " We will test for the existence of this command, so create one if needed. @@ -10,6 +12,8 @@ Before: endif After: + call ale#test#RestoreDirectory() + if b:funky_command_created delcommand CtrlPFunky let b:funky_command_created = 0 @@ -25,3 +29,13 @@ Execute(ALE shouldn't do much of anything for ctrlp-funky buffers): let &l:statusline = '%#CtrlPMode2# prt %*%#CtrlPMode1# line %* ={%#CtrlPMode1# funky %*}= <-> %=%<%#CtrlPMode2# %{getcwd()} %*' Assert ale#ShouldDoNothing(bufnr('')) + +Execute(ALE shouldn't try to check buffers with '.' as the filename): + AssertEqual + \ 0, + \ ale#ShouldDoNothing(bufnr('')), + \ 'ShouldDoNothing() was 1 for some other reason' + + silent! noautocmd file . + + Assert ale#ShouldDoNothing(bufnr('')) diff --git a/test/test_sml_command.vader b/test/test_sml_command.vader index 2db2552..d26f650 100644 --- a/test/test_sml_command.vader +++ b/test/test_sml_command.vader @@ -9,14 +9,14 @@ Execute(smlnj finds CM file if it exists): call ale#test#SetFilename('smlnj/cm/foo.sml') AssertEqual - \ ale#path#Winify(g:dir . '/smlnj/cm/sources.cm'), + \ ale#path#Simplify(g:dir . '/smlnj/cm/sources.cm'), \ ale#handlers#sml#GetCmFile(bufnr('%')) Execute(smlnj finds CM file by searching upwards): call ale#test#SetFilename('smlnj/cm/path/to/bar.sml') AssertEqual - \ ale#path#Winify(g:dir . '/smlnj/cm/sources.cm'), + \ ale#path#Simplify(g:dir . '/smlnj/cm/sources.cm'), \ ale#handlers#sml#GetCmFile(bufnr('%')) Execute(smlnj returns '' when no CM file found): diff --git a/test/test_tflint_config_detection.vader b/test/test_tflint_config_detection.vader new file mode 100644 index 0000000..3500869 --- /dev/null +++ b/test/test_tflint_config_detection.vader @@ -0,0 +1,18 @@ +Before: + call ale#test#SetDirectory('/testplugin/test') + runtime ale_linters/terraform/tflint.vim + +After: + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(adjacent config file should be found): + call ale#test#SetFilename('tflint-test-files/foo/bar.tf') + AssertEqual + \ ( + \ ale#Escape('tflint') + \ . ' --config ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/tflint-test-files/foo/.tflint.hcl')) + \ . ' -f json %t' + \ ), + \ ale_linters#terraform#tflint#GetCommand(bufnr('')) diff --git a/test/test_wrap_comand.vader b/test/test_wrap_comand.vader new file mode 100644 index 0000000..7ddb06a --- /dev/null +++ b/test/test_wrap_comand.vader @@ -0,0 +1,48 @@ +Before: + Save g:ale_command_wrapper + + let g:ale_command_wrapper = '' + + function! TestCommand(expected_part, input) abort + let l:expected = has('win32') + \ ? 'cmd /s/c "' . a:expected_part . '"' + \ : split(&shell) + split(&shellcmdflag) + [a:expected_part] + + AssertEqual l:expected, ale#job#PrepareCommand(bufnr(''), a:input) + endfunction + +After: + Restore + + unlet! b:ale_command_wrapper + + delfunction TestCommand + +Execute(The command wrapper should work with a nice command): + let b:ale_command_wrapper = 'nice -n 5' + + call TestCommand('nice -n 5 foo bar', 'foo bar') + +Execute(The command wrapper should work with a nice command with an explicit marker): + let b:ale_command_wrapper = 'nice -n 5 %*' + + call TestCommand('nice -n 5 foo bar', 'foo bar') + +Execute(Wrappers with spread arguments in the middle should be suppported): + let b:ale_command_wrapper = 'wrap %* --' + + call TestCommand('wrap foo bar --', 'foo bar') + +Execute(Wrappers with the command as one argument should be supported): + let b:ale_command_wrapper = 'wrap -c %@ -x' + + call TestCommand('wrap -c ' . ale#Escape('foo bar') . ' -x', 'foo bar') + +Execute(&& and ; should be moved to the front): + let b:ale_command_wrapper = 'wrap -c %@ -x' + + call TestCommand('foo && bar; wrap -c ' . ale#Escape('baz') . ' -x', 'foo && bar;baz') + + let b:ale_command_wrapper = 'nice -n 5' + + call TestCommand('foo && bar; nice -n 5 baz -z', 'foo && bar;baz -z') diff --git a/test/tflint-test-files/foo/.tflint.hcl b/test/tflint-test-files/foo/.tflint.hcl new file mode 100644 index 0000000..e69de29 diff --git a/test/tflint-test-files/foo/bar.tf b/test/tflint-test-files/foo/bar.tf new file mode 100644 index 0000000..e69de29