summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--PULL_REQUEST_TEMPLATE.md4
-rw-r--r--README.md107
-rw-r--r--ale_linters/ansible/ansible_lint.vim6
-rw-r--r--ale_linters/coffee/coffeelint.vim2
-rw-r--r--ale_linters/d/dmd.vim2
-rw-r--r--ale_linters/dart/dartanalyzer.vim2
-rw-r--r--ale_linters/dockerfile/hadolint.vim8
-rw-r--r--ale_linters/elixir/credo.vim4
-rw-r--r--ale_linters/elixir/dogma.vim4
-rw-r--r--ale_linters/elm/make.vim10
-rw-r--r--ale_linters/erlang/erlc.vim2
-rw-r--r--ale_linters/erlang/syntaxerl.vim21
-rw-r--r--ale_linters/eruby/erubis.vim11
-rw-r--r--ale_linters/fortran/gcc.vim2
-rw-r--r--ale_linters/go/gometalinter.vim2
-rw-r--r--ale_linters/graphql/gqlint.vim9
-rw-r--r--ale_linters/haskell/ghc-mod.vim4
-rw-r--r--ale_linters/haskell/hlint.vim4
-rw-r--r--ale_linters/html/tidy.vim2
-rw-r--r--ale_linters/idris/idris.vim87
-rw-r--r--ale_linters/java/javac.vim9
-rw-r--r--ale_linters/javascript/flow.vim8
-rw-r--r--ale_linters/javascript/standard.vim16
-rw-r--r--ale_linters/kotlin/kotlinc.vim12
-rw-r--r--ale_linters/matlab/mlint.vim2
-rw-r--r--ale_linters/nim/nimcheck.vim4
-rw-r--r--ale_linters/perl/perl.vim2
-rw-r--r--ale_linters/perl/perlcritic.vim6
-rw-r--r--ale_linters/php/langserver.vim1
-rw-r--r--ale_linters/php/phpcs.vim2
-rw-r--r--ale_linters/php/phpstan.vim10
-rw-r--r--ale_linters/python/flake8.vim9
-rw-r--r--ale_linters/python/mypy.vim27
-rw-r--r--ale_linters/python/pycodestyle.vim42
-rw-r--r--ale_linters/python/pylint.vim6
-rw-r--r--ale_linters/ruby/brakeman.vim2
-rw-r--r--ale_linters/ruby/rails_best_practices.vim2
-rw-r--r--ale_linters/ruby/rubocop.vim6
-rw-r--r--ale_linters/rust/cargo.vim2
-rw-r--r--ale_linters/rust/rustc.vim2
-rw-r--r--ale_linters/scala/scalac.vim2
-rw-r--r--ale_linters/scss/scsslint.vim2
-rw-r--r--ale_linters/sh/shell.vim16
-rw-r--r--ale_linters/sh/shellcheck.vim30
-rw-r--r--ale_linters/sml/smlnj.vim2
-rw-r--r--ale_linters/tcl/nagelfar.vim2
-rw-r--r--ale_linters/thrift/thrift.vim91
-rw-r--r--ale_linters/typescript/tslint.vim39
-rw-r--r--ale_linters/verilog/iverilog.vim2
-rw-r--r--ale_linters/verilog/verilator.vim5
-rw-r--r--ale_linters/vim/vint.vim28
-rw-r--r--ale_linters/yaml/swaglint.vim2
-rw-r--r--ale_linters/yaml/yamllint.vim2
-rw-r--r--autoload/ale.vim80
-rw-r--r--autoload/ale/balloon.vim2
-rw-r--r--autoload/ale/c.vim2
-rw-r--r--autoload/ale/completion.vim75
-rw-r--r--autoload/ale/cursor.vim45
-rw-r--r--autoload/ale/debugging.vim64
-rw-r--r--autoload/ale/engine.vim180
-rw-r--r--autoload/ale/events.vim12
-rw-r--r--autoload/ale/fix.vim13
-rw-r--r--autoload/ale/fix/registry.vim15
-rw-r--r--autoload/ale/fixers/clangformat.vim22
-rw-r--r--autoload/ale/fixers/generic_python.vim35
-rw-r--r--autoload/ale/fixers/phpcbf.vim24
-rw-r--r--autoload/ale/fixers/prettier.vim28
-rw-r--r--autoload/ale/fixers/rubocop.vim2
-rw-r--r--autoload/ale/fixers/swiftformat.vim25
-rw-r--r--autoload/ale/handlers/cppcheck.vim2
-rw-r--r--autoload/ale/handlers/css.vim4
-rw-r--r--autoload/ale/handlers/eslint.vim9
-rw-r--r--autoload/ale/handlers/gcc.vim4
-rw-r--r--autoload/ale/handlers/haskell.vim6
-rw-r--r--autoload/ale/handlers/rust.vim2
-rw-r--r--autoload/ale/handlers/sh.vim20
-rw-r--r--autoload/ale/highlight.vim8
-rw-r--r--autoload/ale/history.vim32
-rw-r--r--autoload/ale/job.vim18
-rw-r--r--autoload/ale/linter.vim15
-rw-r--r--autoload/ale/list.vim111
-rw-r--r--autoload/ale/loclist_jumping.vim28
-rw-r--r--autoload/ale/lsp.vim14
-rw-r--r--autoload/ale/path.vim39
-rw-r--r--autoload/ale/pattern_options.vim2
-rw-r--r--autoload/ale/ruby.vim2
-rw-r--r--autoload/ale/sign.vim349
-rw-r--r--autoload/ale/statusline.vim13
-rw-r--r--autoload/ale/test.vim2
-rw-r--r--autoload/ale/util.vim166
-rwxr-xr-xcheck-supported-tools-tables68
-rwxr-xr-xcustom-checks8
-rw-r--r--doc/ale-awk.txt12
-rw-r--r--doc/ale-c.txt19
-rw-r--r--doc/ale-cpp.txt35
-rw-r--r--doc/ale-css.txt4
-rw-r--r--doc/ale-eruby.txt17
-rw-r--r--doc/ale-graphql.txt9
-rw-r--r--doc/ale-idris.txt23
-rw-r--r--doc/ale-java.txt6
-rw-r--r--doc/ale-javascript.txt16
-rw-r--r--doc/ale-php.txt68
-rw-r--r--doc/ale-python.txt29
-rw-r--r--doc/ale-rust.txt21
-rw-r--r--doc/ale-thrift.txt46
-rw-r--r--doc/ale-typescript.txt8
-rw-r--r--doc/ale-xml.txt12
-rw-r--r--doc/ale.txt589
-rw-r--r--plugin/ale.vim44
-rwxr-xr-xrun-tests44
-rw-r--r--test/command_callback/c_paths/dummy.c0
-rw-r--r--test/command_callback/php_paths/project-with-phpcbf/foo/test.php0
-rw-r--r--test/command_callback/php_paths/project-with-phpcbf/vendor/bin/phpcbf0
-rw-r--r--test/command_callback/php_paths/project-without-phpcbf/foo/test.php0
-rwxr-xr-xtest/command_callback/standard-test-files/with-bin/node_modules/.bin/standard0
-rwxr-xr-xtest/command_callback/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js0
-rw-r--r--test/command_callback/swift_paths/dummy.swift0
-rw-r--r--test/command_callback/test_erlang_syntaxerl_command_callback.vader58
-rw-r--r--test/command_callback/test_flake8_command_callback.vader16
-rw-r--r--test/command_callback/test_idris_command_callbacks.vader42
-rw-r--r--test/command_callback/test_phpstan_command_callbacks.vader29
-rw-r--r--test/command_callback/test_pycodestyle_command_callback.vader23
-rw-r--r--test/command_callback/test_shellcheck_command_callback.vader47
-rw-r--r--test/command_callback/test_standard_command_callback.vader98
-rw-r--r--test/command_callback/test_thrift_command_callback.vader61
-rw-r--r--test/command_callback/test_tslint_command_callback.vader38
-rw-r--r--test/fixers/long-line-project/setup.cfg2
-rw-r--r--test/fixers/test_break_up_long_lines_python_fixer.vader39
-rw-r--r--test/fixers/test_clangformat_fixer_callback.vader36
-rw-r--r--test/fixers/test_phpcbf_fixer_callback.vader56
-rw-r--r--test/fixers/test_prettier_fixer_callback.vader58
-rw-r--r--test/fixers/test_rubocop_fixer_callback.vader16
-rw-r--r--test/fixers/test_swiftformat_fixer_callback.vader38
-rw-r--r--test/handler/test_eslint_handler.vader28
-rw-r--r--test/handler/test_idris_handler.vader52
-rw-r--r--test/handler/test_mypy_handler.vader30
-rw-r--r--test/handler/test_phpstan_handler.vader (renamed from test/test_phpstan_executable_detection.vader)44
-rw-r--r--test/handler/test_pycodestyle_handler.vader48
-rw-r--r--test/handler/test_syntaxerl_handler.vader4
-rw-r--r--test/handler/test_thrift_handler.vader63
-rw-r--r--test/handler/test_tslint_handler.vader33
-rw-r--r--test/handler/test_vint_handler.vader29
-rw-r--r--test/prettier-test-files/testfile.js0
-rw-r--r--test/prettier-test-files/with_config/.prettierrc0
-rw-r--r--test/prettier-test-files/with_config/testfile.js0
-rw-r--r--test/sign/test_sign_parsing.vader18
-rw-r--r--test/sign/test_sign_placement.vader161
-rw-r--r--test/test_ale_fix.vader5
-rw-r--r--test/test_ale_info.vader95
-rw-r--r--test/test_ale_init_au_groups.vader60
-rw-r--r--test/test_ale_lint_command.vader6
-rw-r--r--test/test_ale_toggle.vader66
-rw-r--r--test/test_balloon_messages.vader3
-rw-r--r--test/test_completion.vader228
-rw-r--r--test/test_get_abspath.vader15
-rw-r--r--test/test_highlight_placement.vader2
-rw-r--r--test/test_history_saving.vader26
-rw-r--r--test/test_lint_error_delay.vader26
-rw-r--r--test/test_lint_file_linters.vader36
-rw-r--r--test/test_lint_on_filetype_changed.vader47
-rw-r--r--test/test_linting_updates_loclist.vader7
-rw-r--r--test/test_list_opening.vader67
-rw-r--r--test/test_list_titles.vader6
-rw-r--r--test/test_loclist_binary_search.vader59
-rw-r--r--test/test_loclist_corrections.vader106
-rw-r--r--test/test_loclist_jumping.vader30
-rw-r--r--test/test_loclist_sorting.vader46
-rw-r--r--test/test_quickfix_deduplication.vader50
-rw-r--r--test/test_results_not_cleared_when_opening_loclist.vader3
-rw-r--r--test/test_set_list_timers.vader38
-rw-r--r--test/test_setting_loclist_from_another_buffer.vader17
-rw-r--r--test/test_setting_problems_found_in_previous_buffers.vader98
-rw-r--r--test/test_shell_detection.vader83
-rw-r--r--test/test_statusline.vader92
-rw-r--r--test/test_writefile_function.vader48
-rw-r--r--test/vimrc3
177 files changed, 4679 insertions, 1091 deletions
diff --git a/.gitattributes b/.gitattributes
index 4da669b7..27cbcff5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,6 +6,7 @@
/Makefile export-ignore
/PULL_REQUEST_TEMPLATE.md export-ignore
/README.md export-ignore
+/check-supported-tools-tables export-ignore
/custom-checks export-ignore
/img export-ignore
/run-tests export-ignore
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index d51bdecd..94116532 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -2,7 +2,9 @@
READ THIS: Before creating a pull request, please consider the following first.
* The most important thing you can do is write tests. Code without tests
- probably doesn't work, and will almost certainly stop working later on.
+ probably doesn't work, and will almost certainly stop working later on. Pull
+ requests without tests probably won't be accepted, although there are some
+ exceptions.
* Read the Contributing guide linked above first.
* If you are adding a new linter, remember to update the README.md file and
doc/ale.txt first.
diff --git a/README.md b/README.md
index cf1e9928..67238dcd 100644
--- a/README.md
+++ b/README.md
@@ -15,13 +15,17 @@ back to a filesystem.
In other words, this plugin allows you to lint while you type.
-ALE also supports fixing problems with files by running commands in the
-background with a command `ALEFix`.
+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`.
## Table of Contents
1. [Supported Languages and Tools](#supported-languages)
2. [Usage](#usage)
+ 1. [Linting](#usage-linting)
+ 2. [Fixing](#usage-fixing)
+ 3. [Completion](#usage-completion)
3. [Installation](#installation)
1. [Installation with Vim package management](#standard-installation)
2. [Installation with Pathogen](#installation-with-pathogen)
@@ -51,65 +55,75 @@ tools will be run in combination, so they can be complementary.
Keep the table rows sorted alphabetically by the language name,
and the tools in the tools column sorted alphabetically by the tool
name. That seems to be the fairest way to arrange this table.
+
+Remember to also update doc/ale.txt, which has a similar list with different
+formatting.
-->
+**Notes:**
+
+* *^ No linters for text or Vim help filetypes are enabled by default.*
+* *!! These linters check only files on disk. See `:help ale-lint-file-linters`*
+
| Language | Tools |
| -------- | ----- |
| ASM | [gcc](https://gcc.gnu.org) |
| Ansible | [ansible-lint](https://github.com/willthames/ansible-lint) |
-| AsciiDoc | [proselint](http://proselint.com/)|
+| AsciiDoc | [proselint](http://proselint.com/) |
| Awk | [gawk](https://www.gnu.org/software/gawk/)|
-| Bash | [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/) |
-| Bourne Shell | [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/) |
-| C | [cppcheck](http://cppcheck.sourceforge.net), [gcc](https://gcc.gnu.org/), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/)|
-| 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/)|
+| 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)|
| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) |
| Chef | [foodcritic](http://www.foodcritic.io/) |
| 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/) |
+| Crystal | [crystal](https://crystal-lang.org/) !! |
| CSS | [csslint](http://csslint.net/), [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) |
+| Elixir | [credo](https://github.com/rrrene/credo), [dogma](https://github.com/lpil/dogma) !! |
| Elm | [elm-make](https://github.com/elm-lang/elm-make) |
-| Erb | [erb](https://github.com/jeremyevans/erubi) |
+| Erb | [erb](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) |
| Fortran | [gcc](https://gcc.gnu.org/) |
| FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) |
-| Go | [gofmt -e](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) |
+| 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) |
| 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) |
+| 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/) |
+| 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/), [standard](http://standardjs.com/), [prettier](https://github.com/prettier/prettier) (and `prettier-eslint`, `prettier-standard`), [xo](https://github.com/sindresorhus/xo)
+| 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, prettier-standard, [standard](http://standardjs.com/), [xo](https://github.com/sindresorhus/xo)
| JSON | [jsonlint](http://zaa.ch/jsonlint/) |
-| Kotlin | [kotlinc](https://kotlinlang.org), [ktlint](https://ktlint.github.io) see `:help ale-integration-kotlin` for configuration instructions
+| 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/) |
| Lua | [luacheck](https://github.com/mpeterv/luacheck) |
| Markdown | [mdl](https://github.com/mivok/markdownlint), [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) |
| MATLAB | [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html) |
-| Nim | [nim](https://nim-lang.org/docs/nimc.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/)|
| 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-integration-ocaml-merlin` for configuration instructions
| Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) |
-| PHP | [hack](http://hacklang.org/), [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan) |
+| 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/)|
| 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/), [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/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [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/)|
+| reStructuredText | [proselint](http://proselint.com/) |
| 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 | [rustc](https://www.rust-lang.org/), cargo (see `:help ale-integration-rust` for configuration instructions) |
+| 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/) |
| 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) |
| Scala | [scalac](http://scala-lang.org), [scalastyle](http://www.scalastyle.org) |
@@ -117,10 +131,11 @@ name. That seems to be the fairest way to arrange this table.
| SML | [smlnj](http://www.smlnj.org/) |
| Stylus | [stylelint](https://github.com/stylelint/stylelint) |
| SQL | [sqlint](https://github.com/purcell/sqlint) |
-| Swift | [swiftlint](https://swift.org/) |
-| Tcl | [nagelfar](http://nagelfar.sourceforge.net)|
+| 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) |
+| Thrift | [thrift](http://thrift.apache.org/) |
| TypeScript | [eslint](http://eslint.org/), [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) |
@@ -129,12 +144,14 @@ name. That seems to be the fairest way to arrange this table.
| XML | [xmllint](http://xmlsoft.org/xmllint.html/)|
| YAML | [swaglint](https://github.com/byCedric/swaglint), [yamllint](https://yamllint.readthedocs.io/) |
-* *^ No linters for text or Vim help filetypes are enabled by default.*
-
<a name="usage"></a>
## 2. Usage
+<a name="usage-linting"></a>
+
+### 2.i Linting
+
Once this plugin is installed, while editing your files in supported
languages and tools which have been correctly installed,
this plugin will send the contents of your text buffers to a variety of
@@ -147,8 +164,46 @@ documented in [the Vim help file](doc/ale.txt). For more information on the
options ALE offers, consult `:help ale-options` for global options and `:help
ale-linter-options` for options specified to particular linters.
+<a name="usage-fixing"></a>
+
+### 2.ii Fixing
+
ALE can fix files with the `ALEFix` command. Functions need to be configured
-for different filetypes with the `g:ale_fixers` variable. See `:help ale-fix`.
+for different filetypes with the `g:ale_fixers` variable. For example, the
+following code can be used to fix JavaScript code with ESLint:
+
+```vim
+" Put this in vimrc or a plugin file of your own.
+" After this is configured, :ALEFix will try and fix your JS code with ESLint.
+let g:ale_fixers = {
+\ 'javascript': ['eslint'],
+\}
+
+" Set this setting in vimrc if you want to fix files automatically on save.
+" This is off by default.
+let g:ale_fix_on_save = 1
+```
+
+The `:ALEFixSuggest` command will suggest some supported tools for fixing code,
+but fixers can be also implemented with functions, including lambda functions
+too. See `:help ale-fix` for detailed information.
+
+<a name="usage-completion"></a>
+
+### 2.iii Completion
+
+ALE offers some support for completion via hijacking of omnicompletion while you
+type. All of ALE's completion information must come from Language Server
+Protocol linters, or similar protocols. At the moment, completion is only
+supported for TypeScript code with `tsserver`, when `tsserver` is enabled. You
+can enable completion like so:
+
+```vim
+" Enable completion where available.
+let g:ale_completion_enabled = 1
+```
+
+See `:help ale-completion` for more information.
<a name="installation"></a>
@@ -189,7 +244,7 @@ mkdir -p ~/vimfiles/pack/git-plugins/start
git clone https://github.com/w0rp/ale.git ~/vimfiles/pack/git-plugins/start/ale
```
-#### Generating documentation
+#### Generating Vim help files
You can add the following line to your vimrc files to generate documentation
tags automatically, if you don't have something similar already, so you can use
diff --git a/ale_linters/ansible/ansible_lint.vim b/ale_linters/ansible/ansible_lint.vim
index 192e65b9..7d68cde3 100644
--- a/ale_linters/ansible/ansible_lint.vim
+++ b/ale_linters/ansible/ansible_lint.vim
@@ -21,8 +21,8 @@ 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 ==# 'EANSIBLE002')
- \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ if l:code is# 'EANSIBLE002'
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
" Skip warnings for trailing whitespace if the option is off.
continue
endif
@@ -32,7 +32,7 @@ function! ale_linters#ansible#ansible_lint#Handle(buffer, lines) abort
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:code . ': ' . l:match[5],
- \ 'type': l:code[:0] ==# 'E' ? 'E' : 'W',
+ \ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
\})
endif
endfor
diff --git a/ale_linters/coffee/coffeelint.vim b/ale_linters/coffee/coffeelint.vim
index 9db3399c..6d3df353 100644
--- a/ale_linters/coffee/coffeelint.vim
+++ b/ale_linters/coffee/coffeelint.vim
@@ -27,7 +27,7 @@ function! ale_linters#coffee#coffeelint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': str2nr(l:match[1]),
- \ 'type': l:match[3] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[3] is# 'error' ? 'E' : 'W',
\ 'text': l:match[4],
\})
endfor
diff --git a/ale_linters/d/dmd.vim b/ale_linters/d/dmd.vim
index bd3f9d55..b91238ae 100644
--- a/ale_linters/d/dmd.vim
+++ b/ale_linters/d/dmd.vim
@@ -60,7 +60,7 @@ function! ale_linters#d#dmd#Handle(buffer, lines) abort
call add(l:output, {
\ 'lnum': l:match[1],
\ 'col': l:match[2],
- \ 'type': l:match[3] ==# 'Warning' ? 'W' : 'E',
+ \ 'type': l:match[3] is# 'Warning' ? 'W' : 'E',
\ 'text': l:match[4],
\})
endfor
diff --git a/ale_linters/dart/dartanalyzer.vim b/ale_linters/dart/dartanalyzer.vim
index 8f440571..f7b82c4b 100644
--- a/ale_linters/dart/dartanalyzer.vim
+++ b/ale_linters/dart/dartanalyzer.vim
@@ -22,7 +22,7 @@ function! ale_linters#dart#dartanalyzer#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'type': l:match[1] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[1] is# 'error' ? 'E' : 'W',
\ 'text': l:match[6] . ': ' . l:match[2],
\ 'lnum': str2nr(l:match[4]),
\ 'col': str2nr(l:match[5]),
diff --git a/ale_linters/dockerfile/hadolint.vim b/ale_linters/dockerfile/hadolint.vim
index 4063bf1b..5550d698 100644
--- a/ale_linters/dockerfile/hadolint.vim
+++ b/ale_linters/dockerfile/hadolint.vim
@@ -14,7 +14,7 @@ function! ale_linters#dockerfile#hadolint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:lnum = 0
- if l:match[1] !=# ''
+ if l:match[1] isnot# ''
let l:lnum = l:match[1] + 0
endif
@@ -45,9 +45,9 @@ function! ale_linters#dockerfile#hadolint#GetExecutable(buffer) abort
let l:use_docker = ale#Var(a:buffer, 'dockerfile_hadolint_use_docker')
" check for mandatory directives
- if l:use_docker ==# 'never'
+ if l:use_docker is# 'never'
return 'hadolint'
- elseif l:use_docker ==# 'always'
+ elseif l:use_docker is# 'always'
return 'docker'
endif
@@ -62,7 +62,7 @@ endfunction
function! ale_linters#dockerfile#hadolint#GetCommand(buffer) abort
let l:command = ale_linters#dockerfile#hadolint#GetExecutable(a:buffer)
- if l:command ==# 'docker'
+ if l:command is# 'docker'
return 'docker run --rm -i ' . ale#Var(a:buffer, 'dockerfile_hadolint_docker_image')
endif
return 'hadolint -'
diff --git a/ale_linters/elixir/credo.vim b/ale_linters/elixir/credo.vim
index 46f75457..3699dd23 100644
--- a/ale_linters/elixir/credo.vim
+++ b/ale_linters/elixir/credo.vim
@@ -11,9 +11,9 @@ function! ale_linters#elixir#credo#Handle(buffer, lines) abort
let l:type = l:match[3]
let l:text = l:match[4]
- if l:type ==# 'C'
+ if l:type is# 'C'
let l:type = 'E'
- elseif l:type ==# 'R'
+ elseif l:type is# 'R'
let l:type = 'W'
endif
diff --git a/ale_linters/elixir/dogma.vim b/ale_linters/elixir/dogma.vim
index e3b24711..b4f32b04 100644
--- a/ale_linters/elixir/dogma.vim
+++ b/ale_linters/elixir/dogma.vim
@@ -11,9 +11,9 @@ function! ale_linters#elixir#dogma#Handle(buffer, lines) abort
let l:type = l:match[3]
let l:text = l:match[4]
- if l:type ==# 'C'
+ if l:type is# 'C'
let l:type = 'E'
- elseif l:type ==# 'R'
+ elseif l:type is# 'R'
let l:type = 'W'
endif
diff --git a/ale_linters/elm/make.vim b/ale_linters/elm/make.vim
index 0a570ff6..04563a4d 100644
--- a/ale_linters/elm/make.vim
+++ b/ale_linters/elm/make.vim
@@ -7,16 +7,16 @@ function! ale_linters#elm#make#Handle(buffer, lines) abort
let l:temp_dir = l:is_windows ? $TMP : $TMPDIR
let l:unparsed_lines = []
for l:line in a:lines
- if l:line[0] ==# '['
+ if l:line[0] is# '['
let l:errors = json_decode(l:line)
for l:error in l:errors
" Check if file is from the temp directory.
" Filters out any errors not related to the buffer.
if l:is_windows
- let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] ==? l:temp_dir
+ let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] is? l:temp_dir
else
- let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] ==# l:temp_dir
+ let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] is# l:temp_dir
endif
if l:file_is_buffer
@@ -25,13 +25,13 @@ function! ale_linters#elm#make#Handle(buffer, lines) abort
\ 'col': l:error.region.start.column,
\ 'end_lnum': l:error.region.end.line,
\ 'end_col': l:error.region.end.column,
- \ 'type': (l:error.type ==? 'error') ? 'E' : 'W',
+ \ 'type': (l:error.type is? 'error') ? 'E' : 'W',
\ 'text': l:error.overview,
\ 'detail': l:error.overview . "\n\n" . l:error.details
\})
endif
endfor
- elseif l:line !=# 'Successfully generated /dev/null'
+ elseif l:line isnot# 'Successfully generated /dev/null'
call add(l:unparsed_lines, l:line)
endif
endfor
diff --git a/ale_linters/erlang/erlc.vim b/ale_linters/erlang/erlc.vim
index 162c7ec3..559dc679 100644
--- a/ale_linters/erlang/erlc.vim
+++ b/ale_linters/erlang/erlc.vim
@@ -27,7 +27,7 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
let l:pattern_no_module_definition = '\v(no module definition)$'
let l:pattern_unused = '\v(.* is unused)$'
- let l:is_hrl = fnamemodify(bufname(a:buffer), ':e') ==# 'hrl'
+ let l:is_hrl = fnamemodify(bufname(a:buffer), ':e') is# 'hrl'
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
diff --git a/ale_linters/erlang/syntaxerl.vim b/ale_linters/erlang/syntaxerl.vim
index dd53d8e8..46ecdcb7 100644
--- a/ale_linters/erlang/syntaxerl.vim
+++ b/ale_linters/erlang/syntaxerl.vim
@@ -9,8 +9,15 @@ function! ale_linters#erlang#syntaxerl#GetExecutable(buffer) abort
endfunction
-function! ale_linters#erlang#syntaxerl#GetCommand(buffer) abort
- return ale_linters#erlang#syntaxerl#GetExecutable(a:buffer) . ' %t'
+function! ale_linters#erlang#syntaxerl#FeatureCheck(buffer) abort
+ return s:GetEscapedExecutable(a:buffer) . ' -h'
+endfunction
+
+
+function! ale_linters#erlang#syntaxerl#GetCommand(buffer, output) abort
+ let l:use_b_option = match(a:output, '\C\V-b, --base\>') > -1
+
+ return s:GetEscapedExecutable(a:buffer) . (l:use_b_option ? ' -b %s %t' : ' %t')
endfunction
@@ -30,9 +37,17 @@ function! ale_linters#erlang#syntaxerl#Handle(buffer, lines) abort
endfunction
+function! s:GetEscapedExecutable(buffer) abort
+ return ale#Escape(ale_linters#erlang#syntaxerl#GetExecutable(a:buffer))
+endfunction
+
+
call ale#linter#Define('erlang', {
\ 'name': 'syntaxerl',
\ 'executable_callback': 'ale_linters#erlang#syntaxerl#GetExecutable',
-\ 'command_callback': 'ale_linters#erlang#syntaxerl#GetCommand',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#erlang#syntaxerl#FeatureCheck'},
+\ {'callback': 'ale_linters#erlang#syntaxerl#GetCommand'},
+\ ],
\ 'callback': 'ale_linters#erlang#syntaxerl#Handle',
\})
diff --git a/ale_linters/eruby/erubis.vim b/ale_linters/eruby/erubis.vim
new file mode 100644
index 00000000..be9332df
--- /dev/null
+++ b/ale_linters/eruby/erubis.vim
@@ -0,0 +1,11 @@
+" Author: Jake Zimmerman <jake@zimmerman.io>
+" Description: eruby checker using `erubis`, instead of `erb`
+
+call ale#linter#Define('eruby', {
+\ 'name': 'erubis',
+\ 'executable': 'erubis',
+\ 'output_stream': 'stderr',
+\ 'command': 'erubis -x %t | ruby -c',
+\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
+\})
+
diff --git a/ale_linters/fortran/gcc.vim b/ale_linters/fortran/gcc.vim
index a59c6564..5f2ac018 100644
--- a/ale_linters/fortran/gcc.vim
+++ b/ale_linters/fortran/gcc.vim
@@ -44,7 +44,7 @@ function! ale_linters#fortran#gcc#Handle(buffer, lines) abort
" Now we have the text, we can set it and add the error.
let l:last_loclist_obj.text = l:match[2]
- let l:last_loclist_obj.type = l:match[1] ==# 'Warning' ? 'W' : 'E'
+ let l:last_loclist_obj.type = l:match[1] is# 'Warning' ? 'W' : 'E'
call add(l:output, l:last_loclist_obj)
else
let l:last_loclist_obj = {
diff --git a/ale_linters/go/gometalinter.vim b/ale_linters/go/gometalinter.vim
index 6d10871d..f1abfc83 100644
--- a/ale_linters/go/gometalinter.vim
+++ b/ale_linters/go/gometalinter.vim
@@ -32,7 +32,7 @@ function! ale_linters#go#gometalinter#Handler(buffer, lines) abort
call add(l:output, {
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
- \ 'type': tolower(l:match[4]) ==# 'warning' ? 'W' : 'E',
+ \ 'type': tolower(l:match[4]) is# 'warning' ? 'W' : 'E',
\ 'text': l:match[5],
\})
endfor
diff --git a/ale_linters/graphql/gqlint.vim b/ale_linters/graphql/gqlint.vim
new file mode 100644
index 00000000..882cc697
--- /dev/null
+++ b/ale_linters/graphql/gqlint.vim
@@ -0,0 +1,9 @@
+" Author: Michiel Westerbeek <happylinks@gmail.com>
+" Description: Linter for GraphQL Schemas
+
+call ale#linter#Define('graphql', {
+\ 'name': 'gqlint',
+\ 'executable': 'gqlint',
+\ 'command': 'gqlint --reporter=simple %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/haskell/ghc-mod.vim b/ale_linters/haskell/ghc-mod.vim
index d3d23649..1b15d8c2 100644
--- a/ale_linters/haskell/ghc-mod.vim
+++ b/ale_linters/haskell/ghc-mod.vim
@@ -4,13 +4,13 @@
call ale#linter#Define('haskell', {
\ 'name': 'ghc-mod',
\ 'executable': 'ghc-mod',
-\ 'command': 'ghc-mod check %t',
+\ 'command': 'ghc-mod --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc-mod',
\ 'executable': 'stack',
-\ 'command': 'stack exec ghc-mod check %t',
+\ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/hlint.vim b/ale_linters/haskell/hlint.vim
index 91e65123..be40d92c 100644
--- a/ale_linters/haskell/hlint.vim
+++ b/ale_linters/haskell/hlint.vim
@@ -5,9 +5,9 @@ function! ale_linters#haskell#hlint#Handle(buffer, lines) abort
let l:output = []
for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
- if l:error.severity ==# 'Error'
+ if l:error.severity is# 'Error'
let l:type = 'E'
- elseif l:error.severity ==# 'Suggestion'
+ elseif l:error.severity is# 'Suggestion'
let l:type = 'I'
else
let l:type = 'W'
diff --git a/ale_linters/html/tidy.vim b/ale_linters/html/tidy.vim
index c9fdbc7c..4a55d62f 100644
--- a/ale_linters/html/tidy.vim
+++ b/ale_linters/html/tidy.vim
@@ -46,7 +46,7 @@ function! ale_linters#html#tidy#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:col = l:match[2] + 0
- let l:type = l:match[3] ==# 'Error' ? 'E' : 'W'
+ let l:type = l:match[3] is# 'Error' ? 'E' : 'W'
let l:text = l:match[4]
call add(l:output, {
diff --git a/ale_linters/idris/idris.vim b/ale_linters/idris/idris.vim
new file mode 100644
index 00000000..115d04fc
--- /dev/null
+++ b/ale_linters/idris/idris.vim
@@ -0,0 +1,87 @@
+" Author: Scott Bonds <scott@ggr.com>
+" Description: default Idris compiler
+
+call ale#Set('idris_idris_executable', 'idris')
+call ale#Set('idris_idris_options', '--total --warnpartial --warnreach --warnipkg')
+
+function! ale_linters#idris#idris#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'idris_idris_executable')
+endfunction
+
+function! ale_linters#idris#idris#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'idris_idris_options')
+
+ return ale#Escape(ale_linters#idris#idris#GetExecutable(a:buffer))
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --check %s'
+endfunction
+
+function! ale_linters#idris#idris#Handle(buffer, lines) abort
+ " This was copied almost verbatim from ale#handlers#haskell#HandleGHCFormat
+
+ " Look for lines like the following:
+ " foo.idr:2:6:When checking right hand side of main with expected type
+ " bar.idr:11:11-13:
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)(-\d+)?:(.*)?$'
+ let l:output = []
+
+ let l:corrected_lines = []
+
+ for l:line in a:lines
+ if len(matchlist(l:line, l:pattern)) > 0
+ call add(l:corrected_lines, l:line)
+ elseif len(l:corrected_lines) > 0
+ if l:line is# ''
+ let l:corrected_lines[-1] .= ' ' " turn a blank line into a space
+ else
+ let l:corrected_lines[-1] .= l:line
+ endif
+ let l:corrected_lines[-1] = substitute(l:corrected_lines[-1], '\s\+', ' ', 'g')
+ endif
+ endfor
+
+ for l:line in l:corrected_lines
+ let l:match = matchlist(l:line, l:pattern)
+
+ if len(l:match) == 0
+ continue
+ endif
+
+ if !ale#path#IsBufferPath(a:buffer, l:match[1])
+ continue
+ endif
+
+ let l:errors = matchlist(l:match[5], '\v([wW]arning|[eE]rror) - ?(.*)')
+
+ if len(l:errors) > 0
+ let l:ghc_type = l:errors[1]
+ let l:text = l:errors[2]
+ else
+ let l:ghc_type = ''
+ let l:text = l:match[5][:0] is# ' ' ? l:match[5][1:] : l:match[5]
+ endif
+
+ if l:ghc_type is? 'Warning'
+ let l:type = 'W'
+ else
+ let l:type = 'E'
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('idris', {
+\ 'name': 'idris',
+\ 'executable_callback': 'ale_linters#idris#idris#GetExecutable',
+\ 'command_callback': 'ale_linters#idris#idris#GetCommand',
+\ 'callback': 'ale_linters#idris#idris#Handle',
+\})
+
diff --git a/ale_linters/java/javac.vim b/ale_linters/java/javac.vim
index 97530882..d83da182 100644
--- a/ale_linters/java/javac.vim
+++ b/ale_linters/java/javac.vim
@@ -14,6 +14,11 @@ function! ale_linters#java#javac#GetImportPaths(buffer) abort
\ . 'mvn dependency:build-classpath'
endif
+ let l:classpath_command = ale#gradle#BuildClasspathCommand(a:buffer)
+ if !empty(l:classpath_command)
+ return l:classpath_command
+ endif
+
return ''
endfunction
@@ -68,14 +73,14 @@ function! ale_linters#java#javac#Handle(buffer, lines) abort
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 ==# 'error: cannot find symbol'
+ if l:output[-1].text is# 'error: cannot find symbol'
let l:output[-1].text .= ': ' . l:match[2]
endif
else
call add(l:output, {
\ 'lnum': l:match[1] + 0,
\ 'text': l:match[2] . ':' . l:match[3],
- \ 'type': l:match[2] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[2] is# 'error' ? 'E' : 'W',
\})
endif
endfor
diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim
index f3e5deff..0dd64535 100644
--- a/ale_linters/javascript/flow.vim
+++ b/ale_linters/javascript/flow.vim
@@ -46,7 +46,7 @@ function! s:GetJSONLines(lines) abort
let l:start_index = 0
for l:line in a:lines
- if l:line[:0] ==# '{'
+ if l:line[:0] is# '{'
break
endif
@@ -77,13 +77,13 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort
" In certain cases, `l:message.loc.source` points to a different path
" than the buffer one, thus we skip this loc information too.
if has_key(l:message, 'loc')
- \&& l:line ==# 0
+ \&& l:line is# 0
\&& ale#path#IsBufferPath(a:buffer, l:message.loc.source)
let l:line = l:message.loc.start.line + 0
let l:col = l:message.loc.start.column + 0
endif
- if l:text ==# ''
+ if l:text is# ''
let l:text = l:message.descr . ':'
else
let l:text = l:text . ' ' . l:message.descr
@@ -98,7 +98,7 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort
\ 'lnum': l:line,
\ 'col': l:col,
\ 'text': l:text,
- \ 'type': l:error.level ==# 'error' ? 'E' : 'W',
+ \ 'type': l:error.level is# 'error' ? 'E' : 'W',
\})
endfor
diff --git a/ale_linters/javascript/standard.vim b/ale_linters/javascript/standard.vim
index 15e6d78f..fc534ebb 100644
--- a/ale_linters/javascript/standard.vim
+++ b/ale_linters/javascript/standard.vim
@@ -7,13 +7,25 @@ call ale#Set('javascript_standard_options', '')
function! ale_linters#javascript#standard#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_standard', [
+ \ 'node_modules/standard/bin/cmd.js',
\ 'node_modules/.bin/standard',
\])
endfunction
function! ale_linters#javascript#standard#GetCommand(buffer) abort
- return ale#Escape(ale_linters#javascript#standard#GetExecutable(a:buffer))
- \ . ' ' . ale#Var(a:buffer, 'javascript_standard_options')
+ let l:executable = ale_linters#javascript#standard#GetExecutable(a:buffer)
+
+ if ale#Has('win32') && l:executable =~? '\.js$'
+ " .js files have to be executed with Node on Windows.
+ let l:head = 'node ' . ale#Escape(l:executable)
+ else
+ let l:head = ale#Escape(l:executable)
+ endif
+
+ let l:options = ale#Var(a:buffer, 'javascript_standard_options')
+
+ return l:head
+ \ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --stdin %s'
endfunction
diff --git a/ale_linters/kotlin/kotlinc.vim b/ale_linters/kotlin/kotlinc.vim
index ebaf4456..00f94be5 100644
--- a/ale_linters/kotlin/kotlinc.vim
+++ b/ale_linters/kotlin/kotlinc.vim
@@ -13,7 +13,7 @@ let s:classpath_sep = has('unix') ? ':' : ';'
function! ale_linters#kotlin#kotlinc#GetImportPaths(buffer) abort
" exec maven/gradle only if classpath is not set
- if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') !=# ''
+ if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') isnot# ''
return ''
else
let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
@@ -70,7 +70,7 @@ function! ale_linters#kotlin#kotlinc#GetCommand(buffer, import_paths) abort
endif
" We only get here if not using module or the module file not readable
- if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') !=# ''
+ if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') isnot# ''
let l:kotlinc_opts .= ' -cp ' . ale#Var(a:buffer, 'kotlin_kotlinc_classpath')
else
" get classpath from maven/gradle
@@ -78,7 +78,7 @@ function! ale_linters#kotlin#kotlinc#GetCommand(buffer, import_paths) abort
endif
let l:fname = ''
- if ale#Var(a:buffer, 'kotlin_kotlinc_sourcepath') !=# ''
+ if ale#Var(a:buffer, 'kotlin_kotlinc_sourcepath') isnot# ''
let l:fname .= expand(ale#Var(a:buffer, 'kotlin_kotlinc_sourcepath'), 1) . ' '
else
" Find the src directory for files in this project.
@@ -121,10 +121,10 @@ function! ale_linters#kotlin#kotlinc#Handle(buffer, lines) abort
let l:curbuf_abspath = expand('#' . a:buffer . ':p')
" Skip if file is not loaded
- if l:buf_abspath !=# l:curbuf_abspath
+ if l:buf_abspath isnot# l:curbuf_abspath
continue
endif
- let l:type_marker_str = l:type ==# 'warning' ? 'W' : 'E'
+ let l:type_marker_str = l:type is# 'warning' ? 'W' : 'E'
call add(l:output, {
\ 'lnum': l:line,
@@ -145,7 +145,7 @@ function! ale_linters#kotlin#kotlinc#Handle(buffer, lines) abort
let l:type = l:match[1]
let l:text = l:match[2]
- let l:type_marker_str = l:type ==# 'warning' || l:type ==# 'info' ? 'W' : 'E'
+ let l:type_marker_str = l:type is# 'warning' || l:type is# 'info' ? 'W' : 'E'
call add(l:output, {
\ 'lnum': 1,
diff --git a/ale_linters/matlab/mlint.vim b/ale_linters/matlab/mlint.vim
index 851e398a..32766334 100644
--- a/ale_linters/matlab/mlint.vim
+++ b/ale_linters/matlab/mlint.vim
@@ -30,7 +30,7 @@ function! ale_linters#matlab#mlint#Handle(buffer, lines) abort
" Suppress erroneous waring about filename
" TODO: Enable this error when copying filename is supported
- if l:code ==# 'FNDEF'
+ if l:code is# 'FNDEF'
continue
endif
diff --git a/ale_linters/nim/nimcheck.vim b/ale_linters/nim/nimcheck.vim
index 19e67303..cdd8c564 100644
--- a/ale_linters/nim/nimcheck.vim
+++ b/ale_linters/nim/nimcheck.vim
@@ -12,7 +12,7 @@ function! ale_linters#nim#nimcheck#Handle(buffer, lines) abort
" module names.
let l:temp_buffer_filename = fnamemodify(l:match[1], ':p:t')
- if l:buffer_filename !=# '' && l:temp_buffer_filename !=# l:buffer_filename
+ if l:buffer_filename isnot# '' && l:temp_buffer_filename isnot# l:buffer_filename
continue
endif
@@ -26,7 +26,7 @@ function! ale_linters#nim#nimcheck#Handle(buffer, lines) abort
if len(l:textmatch) > 0
let l:errortype = l:textmatch[1]
- if l:errortype ==# 'Error'
+ if l:errortype is# 'Error'
let l:type = 'E'
endif
endif
diff --git a/ale_linters/perl/perl.vim b/ale_linters/perl/perl.vim
index 087d03eb..33288061 100644
--- a/ale_linters/perl/perl.vim
+++ b/ale_linters/perl/perl.vim
@@ -34,7 +34,7 @@ function! ale_linters#perl#perl#Handle(buffer, lines) abort
if ale#path#IsBufferPath(a:buffer, l:match[2])
\ && (
- \ l:text !=# 'BEGIN failed--compilation aborted'
+ \ l:text isnot# 'BEGIN failed--compilation aborted'
\ || empty(l:output)
\ || match(l:output[-1].text, s:begin_failed_skip_pattern) < 0
\ )
diff --git a/ale_linters/perl/perlcritic.vim b/ale_linters/perl/perlcritic.vim
index a9e8f117..df2f8b23 100644
--- a/ale_linters/perl/perlcritic.vim
+++ b/ale_linters/perl/perlcritic.vim
@@ -21,7 +21,7 @@ function! ale_linters#perl#perlcritic#GetProfile(buffer) abort
" first see if we've been overridden
let l:profile = ale#Var(a:buffer, 'perl_perlcritic_profile')
- if l:profile ==? ''
+ if l:profile is? ''
return ''
endif
@@ -41,10 +41,10 @@ function! ale_linters#perl#perlcritic#GetCommand(buffer) abort
let l:command = ale#Escape(ale_linters#perl#perlcritic#GetExecutable(a:buffer))
\ . " --verbose '". l:critic_verbosity . "' --nocolor"
- if l:profile !=? ''
+ if l:profile isnot? ''
let l:command .= ' --profile ' . ale#Escape(l:profile)
endif
- if l:options !=? ''
+ if l:options isnot? ''
let l:command .= ' ' . l:options
endif
diff --git a/ale_linters/php/langserver.vim b/ale_linters/php/langserver.vim
index d772e90d..be2d6ef1 100644
--- a/ale_linters/php/langserver.vim
+++ b/ale_linters/php/langserver.vim
@@ -2,7 +2,6 @@
" Description: PHP Language server integration for ALE
call ale#Set('php_langserver_executable', 'php-language-server.php')
-call ale#Set('php_langserver_config_path', '')
call ale#Set('php_langserver_use_global', 0)
function! ale_linters#php#langserver#GetExecutable(buffer) abort
diff --git a/ale_linters/php/phpcs.vim b/ale_linters/php/phpcs.vim
index a8eae4e6..ddaf171e 100644
--- a/ale_linters/php/phpcs.vim
+++ b/ale_linters/php/phpcs.vim
@@ -40,7 +40,7 @@ function! ale_linters#php#phpcs#Handle(buffer, lines) abort
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
- \ 'type': l:type ==# 'error' ? 'E' : 'W',
+ \ 'type': l:type is# 'error' ? 'E' : 'W',
\})
endfor
diff --git a/ale_linters/php/phpstan.vim b/ale_linters/php/phpstan.vim
index 69173ef4..b99e4f58 100644
--- a/ale_linters/php/phpstan.vim
+++ b/ale_linters/php/phpstan.vim
@@ -5,8 +5,14 @@
let g:ale_php_phpstan_executable = get(g:, 'ale_php_phpstan_executable', 'phpstan')
let g:ale_php_phpstan_level = get(g:, 'ale_php_phpstan_level', '4')
-function! ale_linters#php#phpstan#GetCommand(buffer) abort
+function! ale_linters#php#phpstan#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'php_phpstan_executable')
+endfunction
+
+function! ale_linters#php#phpstan#GetCommand(buffer) abort
+ let l:executable = ale_linters#php#phpstan#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable)
\ . ' analyze -l'
\ . ale#Var(a:buffer, 'php_phpstan_level')
\ . ' --errorFormat raw'
@@ -34,7 +40,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpstan',
-\ 'executable': 'phpstan',
+\ 'executable_callback': 'ale_linters#php#phpstan#GetExecutable',
\ 'command_callback': 'ale_linters#php#phpstan#GetCommand',
\ 'callback': 'ale_linters#php#phpstan#Handle',
\})
diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim
index bf7b3027..8aa4c4df 100644
--- a/ale_linters/python/flake8.vim
+++ b/ale_linters/python/flake8.vim
@@ -83,6 +83,7 @@ function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
return ale#Escape(ale_linters#python#flake8#GetExecutable(a:buffer))
\ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --format=default'
\ . l:display_name_args . ' -'
endfunction
@@ -115,7 +116,7 @@ function! ale_linters#python#flake8#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:code = l:match[3]
- if (l:code ==# 'W291' || l:code ==# 'W293')
+ if (l:code is# 'W291' || l:code is# 'W293')
\ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
" Skip warnings for trailing whitespace if the option is off.
continue
@@ -128,12 +129,12 @@ function! ale_linters#python#flake8#Handle(buffer, lines) abort
\ 'type': 'W',
\}
- if l:code[:0] ==# 'F' || l:code ==# 'E999'
+ if l:code[:0] is# 'F' || l:code is# 'E999'
let l:item.type = 'E'
- elseif l:code[:0] ==# 'E'
+ elseif l:code[:0] is# 'E'
let l:item.type = 'E'
let l:item.sub_type = 'style'
- elseif l:code[:0] ==# 'W'
+ 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 f71365a6..6884a9ac 100644
--- a/ale_linters/python/mypy.vim
+++ b/ale_linters/python/mypy.vim
@@ -10,14 +10,22 @@ function! ale_linters#python#mypy#GetExecutable(buffer) abort
return ale#python#FindExecutable(a:buffer, 'python_mypy', ['mypy'])
endfunction
-function! ale_linters#python#mypy#GetCommand(buffer) abort
+" The directory to change to before running mypy
+function! s:GetDir(buffer) abort
let l:project_root = ale#python#FindProjectRoot(a:buffer)
- let l:cd_command = !empty(l:project_root)
- \ ? ale#path#CdString(l:project_root)
- \ : ''
+
+ return !empty(l:project_root)
+ \ ? l:project_root
+ \ : expand('#' . a:buffer . ':p:h')
+endfunction
+
+function! ale_linters#python#mypy#GetCommand(buffer) abort
+ let l:dir = s:GetDir(a:buffer)
let l:executable = ale_linters#python#mypy#GetExecutable(a:buffer)
- return l:cd_command
+ " We have to always switch to an explicit directory for a command so
+ " we can know with certainty the base path for the 'filename' keys below.
+ return ale#path#CdString(l:dir)
\ . ale#Escape(l:executable)
\ . ' --show-column-numbers '
\ . ale#Var(a:buffer, 'python_mypy_options')
@@ -25,6 +33,7 @@ function! ale_linters#python#mypy#GetCommand(buffer) abort
endfunction
function! ale_linters#python#mypy#Handle(buffer, lines) abort
+ let l:dir = s:GetDir(a:buffer)
" Look for lines like the following:
"
" file.py:4: error: No library stub file for module 'django.db'
@@ -34,17 +43,13 @@ function! ale_linters#python#mypy#Handle(buffer, lines) abort
" file.py:4: note: (Stub files are from https://github.com/python/typeshed)
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?: (error|warning): (.+)$'
let l:output = []
- let l:buffer_filename = expand('#' . a:buffer . ':p')
for l:match in ale#util#GetMatches(a:lines, l:pattern)
- if l:buffer_filename[-len(l:match[1]):] !=# 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,
- \ 'type': l:match[4] =~# 'error' ? 'E' : 'W',
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
\ 'text': l:match[5],
\})
endfor
diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim
new file mode 100644
index 00000000..ad895999
--- /dev/null
+++ b/ale_linters/python/pycodestyle.vim
@@ -0,0 +1,42 @@
+" Author: Michael Thiesen <micthiesen@gmail.com>
+" Description: pycodestyle linting for python files
+
+call ale#Set('python_pycodestyle_executable', 'pycodestyle')
+call ale#Set('python_pycodestyle_options', '')
+call ale#Set('python_pycodestyle_use_global', 0)
+
+function! ale_linters#python#pycodestyle#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_pycodestyle', ['pycodestyle'])
+endfunction
+
+function! ale_linters#python#pycodestyle#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#python#pycodestyle#GetExecutable(a:buffer))
+ \ . ' '
+ \ . ale#Var(a:buffer, 'python_pycodestyle_options')
+ \ . ' -'
+endfunction
+
+function! ale_linters#python#pycodestyle#Handle(buffer, lines) abort
+ 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, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': l:match[6],
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'pycodestyle',
+\ 'executable_callback': 'ale_linters#python#pycodestyle#GetExecutable',
+\ 'command_callback': 'ale_linters#python#pycodestyle#GetCommand',
+\ 'callback': 'ale_linters#python#pycodestyle#Handle',
+\})
diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim
index 6f95776b..befc51a5 100644
--- a/ale_linters/python/pylint.vim
+++ b/ale_linters/python/pylint.vim
@@ -31,13 +31,13 @@ function! ale_linters#python#pylint#Handle(buffer, lines) abort
"let l:failed = append(0, l:match)
let l:code = l:match[3]
- if (l:code ==# 'C0303')
+ if (l:code is# 'C0303')
\ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
" Skip warnings for trailing whitespace if the option is off.
continue
endif
- if l:code ==# 'I0011'
+ if l:code is# 'I0011'
" Skip 'Locally disabling' message
continue
endif
@@ -46,7 +46,7 @@ function! ale_linters#python#pylint#Handle(buffer, lines) abort
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 1,
\ 'text': l:code . ': ' . l:match[5] . ' (' . l:match[4] . ')',
- \ 'type': l:code[:0] ==# 'E' ? 'E' : 'W',
+ \ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
\})
endfor
diff --git a/ale_linters/ruby/brakeman.vim b/ale_linters/ruby/brakeman.vim
index 0070c15c..790eb563 100644
--- a/ale_linters/ruby/brakeman.vim
+++ b/ale_linters/ruby/brakeman.vim
@@ -33,7 +33,7 @@ endfunction
function! ale_linters#ruby#brakeman#GetCommand(buffer) abort
let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
- if l:rails_root ==? ''
+ if l:rails_root is? ''
return ''
endif
diff --git a/ale_linters/ruby/rails_best_practices.vim b/ale_linters/ruby/rails_best_practices.vim
index 503a115c..107753c3 100644
--- a/ale_linters/ruby/rails_best_practices.vim
+++ b/ale_linters/ruby/rails_best_practices.vim
@@ -30,7 +30,7 @@ function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort
let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
- if l:rails_root ==? ''
+ if l:rails_root is? ''
return ''
endif
diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim
index 3d7b64d1..2a4388f2 100644
--- a/ale_linters/ruby/rubocop.vim
+++ b/ale_linters/ruby/rubocop.vim
@@ -43,9 +43,9 @@ function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort
endfunction
function! ale_linters#ruby#rubocop#GetType(severity) abort
- if a:severity ==? 'convention'
- \|| a:severity ==? 'warning'
- \|| a:severity ==? 'refactor'
+ if a:severity is? 'convention'
+ \|| a:severity is? 'warning'
+ \|| a:severity is? 'refactor'
return 'W'
endif
diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim
index 32c09a58..f19061ad 100644
--- a/ale_linters/rust/cargo.vim
+++ b/ale_linters/rust/cargo.vim
@@ -4,7 +4,7 @@
let g:ale_rust_cargo_use_check = get(g:, 'ale_rust_cargo_use_check', 0)
function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort
- if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') !=# ''
+ if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') isnot# ''
return 'cargo'
else
" if there is no Cargo.toml file, we don't use cargo even if it exists,
diff --git a/ale_linters/rust/rustc.vim b/ale_linters/rust/rustc.vim
index 73c99cd9..e792faa7 100644
--- a/ale_linters/rust/rustc.vim
+++ b/ale_linters/rust/rustc.vim
@@ -7,7 +7,7 @@ function! ale_linters#rust#rustc#RustcCommand(buffer_number) abort
" <project root>/target/release/deps/
let l:cargo_file = ale#path#FindNearestFile(a:buffer_number, 'Cargo.toml')
- if l:cargo_file !=# ''
+ 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'
diff --git a/ale_linters/scala/scalac.vim b/ale_linters/scala/scalac.vim
index 9262e625..584aee74 100644
--- a/ale_linters/scala/scalac.vim
+++ b/ale_linters/scala/scalac.vim
@@ -38,7 +38,7 @@ function! ale_linters#scala#scalac#Handle(buffer, lines) abort
endif
let l:text = l:match[3]
- let l:type = l:match[2] ==# 'error' ? 'E' : 'W'
+ let l:type = l:match[2] is# 'error' ? 'E' : 'W'
let l:col = 0
if l:ln + 1 < len(a:lines)
diff --git a/ale_linters/scss/scsslint.vim b/ale_linters/scss/scsslint.vim
index 2331ac16..7ce57241 100644
--- a/ale_linters/scss/scsslint.vim
+++ b/ale_linters/scss/scsslint.vim
@@ -19,7 +19,7 @@ function! ale_linters#scss#scsslint#Handle(buffer, lines) abort
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4],
- \ 'type': l:match[3] ==# 'E' ? 'E' : 'W',
+ \ 'type': l:match[3] is# 'E' ? 'E' : 'W',
\})
endfor
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index 1539e8b3..cf5e4e6c 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -11,24 +11,16 @@ endif
if !exists('g:ale_sh_shell_default_shell')
let g:ale_sh_shell_default_shell = fnamemodify($SHELL, ':t')
- if g:ale_sh_shell_default_shell ==# '' || g:ale_sh_shell_default_shell ==# 'fish'
+ if g:ale_sh_shell_default_shell is# '' || g:ale_sh_shell_default_shell is# 'fish'
let g:ale_sh_shell_default_shell = 'bash'
endif
endif
function! ale_linters#sh#shell#GetExecutable(buffer) abort
- let l:banglines = getbufline(a:buffer, 1)
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
- " Take the shell executable from the hashbang, if we can.
- if len(l:banglines) == 1 && l:banglines[0] =~# '^#!'
- " Remove options like -e, etc.
- let l:line = substitute(l:banglines[0], '--\?[a-zA-Z0-9]\+', '', 'g')
-
- for l:possible_shell in ['bash', 'tcsh', 'csh', 'zsh', 'sh']
- if l:line =~# l:possible_shell . '\s*$'
- return l:possible_shell
- endif
- endfor
+ if !empty(l:shell_type)
+ return l:shell_type
endif
return ale#Var(a:buffer, 'sh_shell_default_shell')
diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim
index 5353683d..3a2d33f2 100644
--- a/ale_linters/sh/shellcheck.vim
+++ b/ale_linters/sh/shellcheck.vim
@@ -19,25 +19,35 @@ function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'sh_shellcheck_executable')
endfunction
-function! s:GetDialectArgument() abort
- if exists('b:is_bash') && b:is_bash
- return '-s bash'
- elseif exists('b:is_sh') && b:is_sh
- return '-s sh'
- elseif exists('b:is_kornshell') && b:is_kornshell
- return '-s ksh'
+function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
+
+ if !empty(l:shell_type)
+ return l:shell_type
+ endif
+
+ " If there's no hashbang, try using Vim's buffer variables.
+ if get(b:, 'is_bash')
+ return 'bash'
+ elseif get(b:, 'is_sh')
+ return 'sh'
+ elseif get(b:, 'is_kornshell')
+ return 'ksh'
endif
return ''
endfunction
function! ale_linters#sh#shellcheck#GetCommand(buffer) abort
+ 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)
return ale_linters#sh#shellcheck#GetExecutable(a:buffer)
- \ . ' ' . ale#Var(a:buffer, 'sh_shellcheck_options')
- \ . ' ' . (!empty(l:exclude_option) ? '-e ' . l:exclude_option : '')
- \ . ' ' . s:GetDialectArgument() . ' -f gcc -'
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '')
+ \ . (!empty(l:dialect) ? ' -s ' . l:dialect : '')
+ \ . ' -f gcc -'
endfunction
call ale#linter#Define('sh', {
diff --git a/ale_linters/sml/smlnj.vim b/ale_linters/sml/smlnj.vim
index fda1d038..4acfc9e6 100644
--- a/ale_linters/sml/smlnj.vim
+++ b/ale_linters/sml/smlnj.vim
@@ -29,7 +29,7 @@ function! ale_linters#sml#smlnj#Handle(buffer, lines) abort
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'text': l:match[2] . ': ' . l:match[3],
- \ 'type': l:match[2] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[2] is# 'error' ? 'E' : 'W',
\})
continue
endif
diff --git a/ale_linters/tcl/nagelfar.vim b/ale_linters/tcl/nagelfar.vim
index 8b29f9ee..13b7a549 100644
--- a/ale_linters/tcl/nagelfar.vim
+++ b/ale_linters/tcl/nagelfar.vim
@@ -28,7 +28,7 @@ function! ale_linters#tcl#nagelfar#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': l:match[1] + 0,
- \ 'type': l:match[2] ==# 'N' ? 'W' : l:match[2],
+ \ 'type': l:match[2] is# 'N' ? 'W' : l:match[2],
\ 'text': l:match[3],
\})
endfor
diff --git a/ale_linters/thrift/thrift.vim b/ale_linters/thrift/thrift.vim
new file mode 100644
index 00000000..2f62570a
--- /dev/null
+++ b/ale_linters/thrift/thrift.vim
@@ -0,0 +1,91 @@
+" Author: Jon Parise <jon@indelible.org>
+
+call ale#Set('thrift_thrift_executable', 'thrift')
+call ale#Set('thrift_thrift_generators', ['cpp'])
+call ale#Set('thrift_thrift_includes', [])
+call ale#Set('thrift_thrift_options', '-strict')
+
+function! ale_linters#thrift#thrift#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'thrift_thrift_executable')
+endfunction
+
+function! ale_linters#thrift#thrift#GetCommand(buffer) abort
+ let l:generators = ale#Var(a:buffer, 'thrift_thrift_generators')
+ let l:includes = ale#Var(a:buffer, 'thrift_thrift_includes')
+
+ " The thrift compiler requires at least one generator. If none are set,
+ " fall back to our default value to avoid silently failing. We could also
+ " `throw` here, but that seems even less helpful.
+ if empty(l:generators)
+ let l:generators = ['cpp']
+ endif
+
+ let l:output_dir = tempname()
+ call mkdir(l:output_dir)
+ call ale#engine#ManageDirectory(a:buffer, l:output_dir)
+
+ return ale#Escape(ale_linters#thrift#thrift#GetExecutable(a:buffer))
+ \ . ' ' . join(map(copy(l:generators), "'--gen ' . v:val"))
+ \ . ' ' . join(map(copy(l:includes), "'-I ' . v:val"))
+ \ . ' ' . ale#Var(a:buffer, 'thrift_thrift_options')
+ \ . ' -out ' . ale#Escape(l:output_dir)
+ \ . ' %t'
+endfunction
+
+function! ale_linters#thrift#thrift#Handle(buffer, lines) abort
+ " Matches lines like the following:
+ "
+ " [SEVERITY:/path/filename.thrift:31] Message text
+ " [ERROR:/path/filename.thrift:31] (last token was ';')
+ let l:pattern = '\v^\[(\u+):(.*):(\d+)\] (.*)$'
+
+ let l:index = 0
+ let l:output = []
+
+ " Roll our own output-matching loop instead of using ale#util#GetMatches
+ " because we need to support error messages that span multiple lines.
+ while l:index < len(a:lines)
+ let l:line = a:lines[l:index]
+
+ let l:match = matchlist(l:line, l:pattern)
+ if empty(l:match)
+ let l:index += 1
+ continue
+ endif
+
+ let l:severity = l:match[1]
+ if l:severity is# 'WARNING'
+ let l:type = 'W'
+ else
+ let l:type = 'E'
+ endif
+
+ " If our text looks like "(last token was ';')", the *next* line
+ " should contain a more descriptive error message.
+ let l:text = l:match[4]
+ if l:text =~# '\(last token was .*\)'
+ let l:index += 1
+ let l:text = get(a:lines, l:index, 'Unknown error ' . l:text)
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[3] + 0,
+ \ 'col': 0,
+ \ 'type': l:type,
+ \ 'text': l:text,
+ \})
+
+ let l:index += 1
+ endwhile
+
+ return l:output
+endfunction
+
+call ale#linter#Define('thrift', {
+\ 'name': 'thrift',
+\ 'executable': 'thrift',
+\ 'output_stream': 'both',
+\ 'executable_callback': 'ale_linters#thrift#thrift#GetExecutable',
+\ 'command_callback': 'ale_linters#thrift#thrift#GetCommand',
+\ 'callback': 'ale_linters#thrift#thrift#Handle',
+\})
diff --git a/ale_linters/typescript/tslint.vim b/ale_linters/typescript/tslint.vim
index 7cd1cb4b..18f9e08c 100644
--- a/ale_linters/typescript/tslint.vim
+++ b/ale_linters/typescript/tslint.vim
@@ -1,8 +1,9 @@
-" Author: Prashanth Chandra https://github.com/prashcr
+" Author: Prashanth Chandra <https://github.com/prashcr>, Jonathan Clem <https://jclem.net>
" Description: tslint for TypeScript files
call ale#Set('typescript_tslint_executable', 'tslint')
call ale#Set('typescript_tslint_config_path', '')
+call ale#Set('typescript_tslint_rules_dir', '')
call ale#Set('typescript_tslint_use_global', 0)
function! ale_linters#typescript#tslint#GetExecutable(buffer) abort
@@ -12,44 +13,52 @@ function! ale_linters#typescript#tslint#GetExecutable(buffer) abort
endfunction
function! ale_linters#typescript#tslint#Handle(buffer, lines) abort
+ let l:dir = expand('#' . a:buffer . ':p:h')
let l:output = []
for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
- if ale#path#IsBufferPath(a:buffer, l:error.name)
- call add(l:output, {
- \ 'type': (get(l:error, 'ruleSeverity', '') ==# 'WARNING' ? 'W' : 'E'),
- \ '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,
- \})
- endif
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:error.name),
+ \ 'type': (get(l:error, 'ruleSeverity', '') is# 'WARNING' ? 'W' : 'E'),
+ \ 'text': has_key(l:error, 'ruleName')
+ \ ? l:error.ruleName . ': ' . l:error.failure
+ \ : 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,
+ \})
endfor
return l:output
endfunction
-function! ale_linters#typescript#tslint#BuildLintCommand(buffer) abort
+function! ale_linters#typescript#tslint#GetCommand(buffer) abort
let l:tslint_config_path = ale#path#ResolveLocalPath(
\ a:buffer,
\ 'tslint.json',
\ ale#Var(a:buffer, 'typescript_tslint_config_path')
\)
-
let l:tslint_config_option = !empty(l:tslint_config_path)
\ ? ' -c ' . ale#Escape(l:tslint_config_path)
\ : ''
- return ale_linters#typescript#tslint#GetExecutable(a:buffer)
+ let l:tslint_rules_dir = ale#Var(a:buffer, 'typescript_tslint_rules_dir')
+ let l:tslint_rules_option = !empty(l:tslint_rules_dir)
+ \ ? ' -r ' . ale#Escape(l:tslint_rules_dir)
+ \ : ''
+
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale_linters#typescript#tslint#GetExecutable(a:buffer)
\ . ' --format json'
\ . l:tslint_config_option
+ \ . l:tslint_rules_option
\ . ' %t'
endfunction
call ale#linter#Define('typescript', {
\ 'name': 'tslint',
\ 'executable_callback': 'ale_linters#typescript#tslint#GetExecutable',
-\ 'command_callback': 'ale_linters#typescript#tslint#BuildLintCommand',
+\ 'command_callback': 'ale_linters#typescript#tslint#GetCommand',
\ 'callback': 'ale_linters#typescript#tslint#Handle',
\})
diff --git a/ale_linters/verilog/iverilog.vim b/ale_linters/verilog/iverilog.vim
index 0f4cd7b3..18769d56 100644
--- a/ale_linters/verilog/iverilog.vim
+++ b/ale_linters/verilog/iverilog.vim
@@ -14,7 +14,7 @@ function! ale_linters#verilog#iverilog#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:type = l:match[2] =~# 'error' ? 'E' : 'W'
- let l:text = l:match[2] ==# 'syntax error' ? 'syntax error' : l:match[4]
+ let l:text = l:match[2] is# 'syntax error' ? 'syntax error' : l:match[4]
call add(l:output, {
\ 'lnum': l:line,
diff --git a/ale_linters/verilog/verilator.vim b/ale_linters/verilog/verilator.vim
index aa5e7047..6053da09 100644
--- a/ale_linters/verilog/verilator.vim
+++ b/ale_linters/verilog/verilator.vim
@@ -11,7 +11,8 @@ function! ale_linters#verilog#verilator#GetCommand(buffer) abort
" Create a special filename, so we can detect it in the handler.
call ale#engine#ManageFile(a:buffer, l:filename)
- call writefile(getbufline(a:buffer, 1, '$'), l:filename)
+ let l:lines = getbufline(a:buffer, 1, '$')
+ call ale#util#Writefile(a:buffer, l:lines, l:filename)
return 'verilator --lint-only -Wall -Wno-DECLFILENAME '
\ . ale#Var(a:buffer, 'verilog_verilator_options') .' '
@@ -32,7 +33,7 @@ function! ale_linters#verilog#verilator#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[3] + 0
- let l:type = l:match[1] ==# 'Error' ? 'E' : 'W'
+ let l:type = l:match[1] is# 'Error' ? 'E' : 'W'
let l:text = l:match[4]
let l:file = l:match[2]
diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim
index 18ae2e4c..adf2b4ab 100644
--- a/ale_linters/vim/vint.vim
+++ b/ale_linters/vim/vint.vim
@@ -36,6 +36,32 @@ function! ale_linters#vim#vint#GetCommand(buffer, version_output) abort
\ . ' %t'
endfunction
+let s:word_regex_list = [
+\ '\v^Undefined variable: ([^ ]+)',
+\ '\v^Make the scope explicit like ...([^ ]+). ',
+\ '\v^.*start with a capital or contain a colon: ([^ ]+)',
+\ '\v.*instead of .(\=[=~]).',
+\]
+
+function! ale_linters#vim#vint#Handle(buffer, lines) abort
+ let l:loclist = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines)
+
+ for l:item in l:loclist
+ let l:match = []
+
+ for l:regex in s:word_regex_list
+ let l:match = matchlist(l:item.text, l:regex)
+
+ if !empty(l:match)
+ let l:item.end_col = l:item.col + len(l:match[1]) - 1
+ break
+ endif
+ endfor
+ endfor
+
+ return l:loclist
+endfunction
+
call ale#linter#Define('vim', {
\ 'name': 'vint',
\ 'executable': 'vint',
@@ -43,5 +69,5 @@ call ale#linter#Define('vim', {
\ {'callback': 'ale_linters#vim#vint#VersionCommand', 'output_stream': 'stderr'},
\ {'callback': 'ale_linters#vim#vint#GetCommand', 'output_stream': 'stdout'},
\ ],
-\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'callback': 'ale_linters#vim#vint#Handle',
\})
diff --git a/ale_linters/yaml/swaglint.vim b/ale_linters/yaml/swaglint.vim
index 1368d7b7..454cad05 100644
--- a/ale_linters/yaml/swaglint.vim
+++ b/ale_linters/yaml/swaglint.vim
@@ -21,7 +21,7 @@ function! ale_linters#yaml#swaglint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:obj = {
- \ 'type': l:match[1] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[1] is# 'error' ? 'E' : 'W',
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[4],
diff --git a/ale_linters/yaml/yamllint.vim b/ale_linters/yaml/yamllint.vim
index a0eb2a0a..731f8012 100644
--- a/ale_linters/yaml/yamllint.vim
+++ b/ale_linters/yaml/yamllint.vim
@@ -33,7 +33,7 @@ function! ale_linters#yaml#yamllint#Handle(buffer, lines) abort
\ 'lnum': l:line,
\ 'col': l:col,
\ 'text': l:text,
- \ 'type': l:type ==# 'error' ? 'E' : 'W',
+ \ 'type': l:type is# 'error' ? 'E' : 'W',
\})
endfor
diff --git a/autoload/ale.vim b/autoload/ale.vim
index aba3fda1..9defbd82 100644
--- a/autoload/ale.vim
+++ b/autoload/ale.vim
@@ -5,6 +5,31 @@
let s:lint_timer = -1
let s:queued_buffer_number = -1
let s:should_lint_file_for_buffer = {}
+let s:error_delay_ms = 1000 * 60 * 2
+
+let s:timestamp_map = {}
+
+" Given a key for a script variable for tracking the time to wait until
+" a given function should be called, a funcref for a function to call, and
+" a List of arguments, call the function and return whatever value it returns.
+"
+" If the function throws an exception, then the function will not be called
+" for a while, and 0 will be returned instead.
+function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
+ let l:now = ale#util#ClockMilliseconds()
+
+ if l:now < get(s:timestamp_map, a:timestamp_key, -1)
+ return 0
+ endif
+
+ let s:timestamp_map[a:timestamp_key] = l:now + s:error_delay_ms
+
+ let l:return_value = call(a:func, a:arglist)
+
+ let s:timestamp_map[a:timestamp_key] = -1
+
+ return l:return_value
+endfunction
" Return 1 if a file is too large for ALE to handle.
function! ale#FileTooLarge() abort
@@ -33,24 +58,31 @@ function! ale#Queue(delay, ...) abort
" Default linting_flag to ''
let l:linting_flag = get(a:000, 0, '')
+ let l:buffer = get(a:000, 1, bufnr(''))
+
+ return ale#CallWithCooldown(
+ \ 'dont_queue_until',
+ \ function('s:ALEQueueImpl'),
+ \ [a:delay, l:linting_flag, l:buffer],
+ \)
+endfunction
- if l:linting_flag !=# '' && l:linting_flag !=# 'lint_file'
+function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
+ if a:linting_flag isnot# '' && a:linting_flag isnot# 'lint_file'
throw "linting_flag must be either '' or 'lint_file'"
endif
- let l:buffer = get(a:000, 1, bufnr(''))
-
- if type(l:buffer) != type(0)
+ if type(a:buffer) != type(0)
throw 'buffer_number must be a Number'
endif
- if ale#ShouldDoNothing(l:buffer)
+ if ale#ShouldDoNothing(a:buffer)
return
endif
" Remember that we want to check files for this buffer.
" We will remember this until we finally run the linters, via any event.
- if l:linting_flag ==# 'lint_file'
+ if a:linting_flag is# 'lint_file'
let s:should_lint_file_for_buffer[bufnr('%')] = 1
endif
@@ -59,24 +91,24 @@ function! ale#Queue(delay, ...) abort
let s:lint_timer = -1
endif
- let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype'))
+ let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
" Don't set up buffer data and so on if there are no linters to run.
if empty(l:linters)
" If we have some previous buffer data, then stop any jobs currently
" running and clear everything.
- if has_key(g:ale_buffer_info, l:buffer)
- call ale#engine#RunLinters(l:buffer, [], 1)
+ if has_key(g:ale_buffer_info, a:buffer)
+ call ale#engine#RunLinters(a:buffer, [], 1)
endif
return
endif
if a:delay > 0
- let s:queued_buffer_number = l:buffer
+ let s:queued_buffer_number = a:buffer
let s:lint_timer = timer_start(a:delay, function('ale#Lint'))
else
- call ale#Lint(-1, l:buffer)
+ call ale#Lint(-1, a:buffer)
endif
endfunction
@@ -92,22 +124,30 @@ function! ale#Lint(...) abort
let l:buffer = bufnr('')
endif
- if ale#ShouldDoNothing(l:buffer)
+ return ale#CallWithCooldown(
+ \ 'dont_lint_until',
+ \ function('s:ALELintImpl'),
+ \ [l:buffer],
+ \)
+endfunction
+
+function! s:ALELintImpl(buffer) abort
+ if ale#ShouldDoNothing(a:buffer)
return
endif
" Use the filetype from the buffer
- let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype'))
+ let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
let l:should_lint_file = 0
" Check if we previously requested checking the file.
- if has_key(s:should_lint_file_for_buffer, l:buffer)
- unlet s:should_lint_file_for_buffer[l:buffer]
+ if has_key(s:should_lint_file_for_buffer, a:buffer)
+ unlet s:should_lint_file_for_buffer[a:buffer]
" Lint files if they exist.
- let l:should_lint_file = filereadable(expand('#' . l:buffer . ':p'))
+ let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p'))
endif
- call ale#engine#RunLinters(l:buffer, l:linters, l:should_lint_file)
+ call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file)
endfunction
" Reset flags indicating that files should be checked for all buffers.
@@ -115,6 +155,10 @@ function! ale#ResetLintFileMarkers() abort
let s:should_lint_file_for_buffer = {}
endfunction
+function! ale#ResetErrorDelays() abort
+ let s:timestamp_map = {}
+endfunction
+
let g:ale_has_override = get(g:, 'ale_has_override', {})
" Call has(), but check a global Dictionary so we can force flags on or off
@@ -157,7 +201,7 @@ endfunction
" Escape a string suitably for each platform.
" shellescape does not work on Windows.
function! ale#Escape(str) abort
- if fnamemodify(&shell, ':t') ==? 'cmd.exe'
+ if fnamemodify(&shell, ':t') is? 'cmd.exe'
" If the string contains spaces, it will be surrounded by quotes.
" Otherwise, special characters will be escaped with carets (^).
return substitute(
diff --git a/autoload/ale/balloon.vim b/autoload/ale/balloon.vim
index 3d179a0d..41fa95fa 100644
--- a/autoload/ale/balloon.vim
+++ b/autoload/ale/balloon.vim
@@ -3,7 +3,7 @@
function! ale#balloon#MessageForPos(bufnr, lnum, col) abort
let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist
- let l:index = ale#util#BinarySearch(l:loclist, a:lnum, a:col)
+ let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col)
return l:index >= 0 ? l:loclist[l:index].text : ''
endfunction
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index 1703da78..b9f94399 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -9,7 +9,7 @@ function! ale#c#FindProjectRoot(buffer) abort
let l:path = fnamemodify(l:full_path, ':h')
" Correct .git path detection.
- if fnamemodify(l:path, ':t') ==# '.git'
+ if fnamemodify(l:path, ':t') is# '.git'
let l:path = fnamemodify(l:path, ':h')
endif
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index eac6e6fc..9f4e3c28 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -39,12 +39,12 @@ let s:omni_start_map = {
\ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$',
\}
-function! ale#completion#FilterSuggestionsByPrefix(suggestions, prefix) abort
+function! ale#completion#Filter(suggestions, prefix) abort
" For completing...
" foo.
" ^
" We need to include all of the given suggestions.
- if a:prefix ==# '.'
+ if a:prefix is# '.'
return a:suggestions
endif
@@ -53,20 +53,31 @@ function! ale#completion#FilterSuggestionsByPrefix(suggestions, prefix) abort
" Filter suggestions down to those starting with the prefix we used for
" finding suggestions in the first place.
"
- " Some completion tools will
- " include suggestions which don't even start with the characters we have
- " already typed.
- for l:suggestion in a:suggestions
+ " Some completion tools will include suggestions which don't even start
+ " with the characters we have already typed.
+ for l:item in a:suggestions
+ " A List of String values or a List of completion item Dictionaries
+ " is accepted here.
+ let l:word = type(l:item) == type('') ? l:item : l:item.word
+
" Add suggestions if the suggestion starts with a case-insensitive
" match for the prefix.
- if l:suggestion.word[: len(a:prefix) - 1] ==? a:prefix
- call add(l:filtered_suggestions, l:suggestion)
+ if l:word[: len(a:prefix) - 1] is? a:prefix
+ call add(l:filtered_suggestions, l:item)
endif
endfor
return l:filtered_suggestions
endfunction
+function! s:ReplaceCompleteopt() abort
+ if !exists('b:ale_old_completopt')
+ let b:ale_old_completopt = &l:completeopt
+ endif
+
+ let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
+endfunction
+
function! ale#completion#OmniFunc(findstart, base) abort
if a:findstart
let l:line = b:ale_completion_info.line
@@ -86,14 +97,11 @@ function! ale#completion#OmniFunc(findstart, base) abort
unlet b:ale_completion_response
unlet b:ale_completion_parser
- let l:prefix = b:ale_completion_info.prefix
-
- let b:ale_completion_result = ale#completion#FilterSuggestionsByPrefix(
- \ function(l:parser)(l:response),
- \ l:prefix
- \)[: g:ale_completion_max_suggestions]
+ let b:ale_completion_result = function(l:parser)(l:response)
endif
+ call s:ReplaceCompleteopt()
+
return get(b:, 'ale_completion_result', [])
endif
endfunction
@@ -111,7 +119,8 @@ function! ale#completion#Show(response, completion_parser) abort
let b:ale_completion_response = a:response
let b:ale_completion_parser = a:completion_parser
let &l:omnifunc = 'ale#completion#OmniFunc'
- call feedkeys("\<C-x>\<C-o>", 'n')
+ call s:ReplaceCompleteopt()
+ call ale#util#FeedKeys("\<C-x>\<C-o>", 'n')
endfunction
function! s:CompletionStillValid(request_id) abort
@@ -150,9 +159,9 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
call add(l:documentationParts, l:part.text)
endfor
- if l:suggestion.kind ==# 'clasName'
+ if l:suggestion.kind is# 'clasName'
let l:kind = 'f'
- elseif l:suggestion.kind ==# 'parameterName'
+ elseif l:suggestion.kind is# 'parameterName'
let l:kind = 'f'
else
let l:kind = 'v'
@@ -171,7 +180,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
return l:results
endfunction
-function! s:HandleTSServerLSPResponse(conn_id, response) abort
+function! ale#completion#HandleTSServerLSPResponse(conn_id, response) abort
if !s:CompletionStillValid(get(a:response, 'request_seq'))
return
endif
@@ -182,8 +191,11 @@ function! s:HandleTSServerLSPResponse(conn_id, response) abort
let l:command = get(a:response, 'command', '')
- if l:command ==# 'completions'
- let l:names = ale#completion#ParseTSServerCompletions(a:response)
+ if l:command is# 'completions'
+ let l:names = ale#completion#Filter(
+ \ ale#completion#ParseTSServerCompletions(a:response),
+ \ b:ale_completion_info.prefix,
+ \)[: g:ale_completion_max_suggestions - 1]
if !empty(l:names)
let b:ale_completion_info.request_id = ale#lsp#Send(
@@ -196,7 +208,7 @@ function! s:HandleTSServerLSPResponse(conn_id, response) abort
\ ),
\)
endif
- elseif l:command ==# 'completionEntryDetails'
+ elseif l:command is# 'completionEntryDetails'
call ale#completion#Show(
\ a:response,
\ 'ale#completion#ParseTSServerCompletionEntryDetails',
@@ -209,7 +221,7 @@ function! s:GetLSPCompletions(linter) abort
let l:lsp_details = ale#linter#StartLSP(
\ l:buffer,
\ a:linter,
- \ function('s:HandleTSServerLSPResponse'),
+ \ function('ale#completion#HandleTSServerLSPResponse'),
\)
if empty(l:lsp_details)
@@ -237,10 +249,6 @@ endfunction
function! ale#completion#GetCompletions() abort
let [l:line, l:column] = getcurpos()[1:2]
- if s:timer_pos != [l:line, l:column]
- return
- endif
-
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
if empty(l:prefix)
@@ -256,7 +264,7 @@ function! ale#completion#GetCompletions() abort
\}
for l:linter in ale#linter#Get(&filetype)
- if l:linter.lsp ==# 'tsserver'
+ if l:linter.lsp is# 'tsserver'
call s:GetLSPCompletions(l:linter)
endif
endfor
@@ -265,7 +273,13 @@ endfunction
function! s:TimerHandler(...) abort
let s:timer_id = -1
- call ale#completion#GetCompletions()
+ let [l:line, l:column] = getcurpos()[1:2]
+
+ " When running the timer callback, we have to be sure that the cursor
+ " hasn't moved from where it was when we requested completions by typing.
+ if s:timer_pos == [l:line, l:column]
+ call ale#completion#GetCompletions()
+ endif
endfunction
function! ale#completion#Queue() abort
@@ -292,6 +306,11 @@ function! ale#completion#Done() abort
let &l:omnifunc = b:ale_old_omnifunc
unlet b:ale_old_omnifunc
endif
+
+ if exists('b:ale_old_completopt')
+ let &l:completeopt = b:ale_old_completopt
+ unlet b:ale_old_completopt
+ endif
endfunction
function! s:Setup(enabled) abort
diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim
index 0c6a8634..340432f7 100644
--- a/autoload/ale/cursor.vim
+++ b/autoload/ale/cursor.vim
@@ -1,10 +1,22 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Echoes lint message for the current line, if any
+let s:cursor_timer = -1
+let s:last_pos = [0, 0, 0]
+let s:error_delay_ms = 1000 * 60 * 2
+
+if !exists('s:dont_queue_until')
+ let s:dont_queue_until = -1
+endif
+
+if !exists('s:dont_echo_until')
+ let s:dont_echo_until = -1
+endif
+
" 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 ==# 'E'
+ let l:type = a:type is# 'E'
\ ? g:ale_echo_msg_error_str
\ : g:ale_echo_msg_warning_str
@@ -22,12 +34,12 @@ function! s:EchoWithShortMess(setting, message) abort
try
" Turn shortmess on or off.
- if a:setting ==# 'on'
+ 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 ==# 'off'
+ elseif a:setting is# 'off'
setlocal shortmess-=T
" Regular echo is needed for printing newline characters.
echo a:message
@@ -50,10 +62,12 @@ function! ale#cursor#TruncatedEcho(message) abort
endfunction
function! s:FindItemAtCursor() abort
- let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
+ let l:buf = bufnr('')
+ let l:info = get(g:ale_buffer_info, l:buf, {})
+ let l:loclist = get(l:info, 'loclist', [])
let l:pos = getcurpos()
- let l:index = ale#util#BinarySearch(l:info.loclist, l:pos[1], l:pos[2])
- let l:loc = l:index >= 0 ? l:info.loclist[l:index] : {}
+ let l:index = ale#util#BinarySearch(l:loclist, l:buf, l:pos[1], l:pos[2])
+ let l:loc = l:index >= 0 ? l:loclist[l:index] : {}
return [l:info, l:loc]
endfunction
@@ -66,12 +80,16 @@ function! s:StopCursorTimer() abort
endfunction
function! ale#cursor#EchoCursorWarning(...) abort
+ return ale#CallWithCooldown('dont_echo_until', function('s:EchoImpl'), [])
+endfunction
+
+function! s:EchoImpl() abort
if ale#ShouldDoNothing(bufnr(''))
return
endif
" Only echo the warnings in normal mode, otherwise we will get problems.
- if mode() !=# 'n'
+ if mode() isnot# 'n'
return
endif
@@ -89,10 +107,15 @@ function! ale#cursor#EchoCursorWarning(...) abort
endif
endfunction
-let s:cursor_timer = -1
-let s:last_pos = [0, 0, 0]
-
function! ale#cursor#EchoCursorWarningWithDelay() abort
+ return ale#CallWithCooldown(
+ \ 'dont_echo_with_delay_until',
+ \ function('s:EchoWithDelayImpl'),
+ \ [],
+ \)
+endfunction
+
+function! s:EchoWithDelayImpl() abort
if ale#ShouldDoNothing(bufnr(''))
return
endif
@@ -117,7 +140,7 @@ function! ale#cursor#ShowCursorDetail() abort
endif
" Only echo the warnings in normal mode, otherwise we will get problems.
- if mode() !=# 'n'
+ if mode() isnot# 'n'
return
endif
diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim
index 8136fbee..7454bb1a 100644
--- a/autoload/ale/debugging.vim
+++ b/autoload/ale/debugging.vim
@@ -70,39 +70,53 @@ function! s:EchoGlobalVariables() abort
endfor
endfunction
-function! s:EchoCommandHistory() abort
- let l:buffer = bufnr('%')
+" Echo a command that was run.
+function! s:EchoCommand(item) abort
+ let l:status_message = a:item.status
- if !has_key(g:ale_buffer_info, l:buffer)
- return
+ " Include the exit code in output if we have it.
+ if a:item.status is# 'finished'
+ let l:status_message .= ' - exit code ' . a:item.exit_code
endif
- for l:item in g:ale_buffer_info[l:buffer].history
- let l:status_message = l:item.status
+ echom '(' . l:status_message . ') ' . string(a:item.command)
- " Include the exit code in output if we have it.
- if l:item.status ==# 'finished'
- let l:status_message .= ' - exit code ' . l:item.exit_code
- endif
+ if g:ale_history_log_output && has_key(a:item, 'output')
+ if empty(a:item.output)
+ echom ''
+ echom '<<<NO OUTPUT RETURNED>>>'
+ echom ''
+ else
+ echom ''
+ echom '<<<OUTPUT STARTS>>>'
- echom '(' . l:status_message . ') ' . string(l:item.command)
+ for l:line in a:item.output
+ echom l:line
+ endfor
- if g:ale_history_log_output && has_key(l:item, 'output')
- if empty(l:item.output)
- echom ''
- echom '<<<NO OUTPUT RETURNED>>>'
- echom ''
- else
- echom ''
- echom '<<<OUTPUT STARTS>>>'
+ echom '<<<OUTPUT ENDS>>>'
+ echom ''
+ endif
+ endif
+endfunction
- for l:line in l:item.output
- echom l:line
- endfor
+" Echo the results of an executable check.
+function! s:EchoExecutable(item) abort
+ echom printf(
+ \ '(executable check - %s) %s',
+ \ a:item.status ? 'success' : 'failure',
+ \ a:item.command,
+ \)
+endfunction
- echom '<<<OUTPUT ENDS>>>'
- echom ''
- endif
+function! s:EchoCommandHistory() abort
+ let l:buffer = bufnr('%')
+
+ for l:item in ale#history#Get(l:buffer)
+ if l:item.job_id is# 'executable'
+ call s:EchoExecutable(l:item)
+ else
+ call s:EchoCommand(l:item)
endif
endfor
endfunction
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index a5a5f524..74ae0d9c 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -16,22 +16,34 @@ if !has_key(s:, 'lsp_linter_map')
let s:lsp_linter_map = {}
endif
-let s:executable_cache_map = {}
+if !has_key(s:, 'executable_cache_map')
+ let s:executable_cache_map = {}
+endif
+
+function! ale#engine#ResetExecutableCache() abort
+ let s:executable_cache_map = {}
+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! s:IsExecutable(executable) abort
+function! ale#engine#IsExecutable(buffer, executable) abort
if has_key(s:executable_cache_map, a:executable)
return 1
endif
+ let l:result = 0
+
if executable(a:executable)
let s:executable_cache_map[a:executable] = 1
- return 1
+ let l:result = 1
endif
- return 0
+ if g:ale_history_enabled
+ call ale#history#Add(a:buffer, l:result, 'executable', a:executable)
+ endif
+
+ return l:result
endfunction
function! ale#engine#InitBufferInfo(buffer) abort
@@ -41,16 +53,18 @@ function! ale#engine#InitBufferInfo(buffer) abort
" loclist holds the loclist items after all jobs have completed.
" temporary_file_list holds temporary files to be cleaned up
" temporary_directory_list holds temporary directories to be cleaned up
- " history holds a list of previously run commands for this buffer
let g:ale_buffer_info[a:buffer] = {
\ 'job_list': [],
\ 'active_linter_list': [],
\ 'loclist': [],
\ 'temporary_file_list': [],
\ 'temporary_directory_list': [],
- \ 'history': [],
\}
+
+ return 1
endif
+
+ return 0
endfunction
" Return 1 if ALE is busy checking a given buffer
@@ -83,7 +97,7 @@ function! ale#engine#CreateDirectory(buffer) abort
endfunction
function! ale#engine#RemoveManagedFiles(buffer) abort
- let l:info = get(g:ale_buffer_info, a:buffer)
+ let l:info = get(g:ale_buffer_info, a:buffer, {})
" We can't delete anything in a sandbox, so wait until we escape from
" it to delete temporary files and directories.
@@ -128,14 +142,14 @@ function! s:HandleLoclist(linter_name, buffer, loclist) abort
" 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 !=# a:linter_name')
+ call filter(l:buffer_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 !=# a:linter_name')
+ 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)
@@ -169,8 +183,8 @@ function! s:HandleExit(job_id, exit_code) abort
" Remove this job from the list.
call ale#job#Stop(a:job_id)
call remove(s:job_info_map, a:job_id)
- call filter(g:ale_buffer_info[l:buffer].job_list, 'v:val !=# a:job_id')
- call filter(g:ale_buffer_info[l:buffer].active_linter_list, 'v:val !=# l:linter.name')
+ call filter(g:ale_buffer_info[l:buffer].job_list, 'v:val isnot# a:job_id')
+ call filter(g:ale_buffer_info[l:buffer].active_linter_list, 'v:val isnot# l:linter.name')
" Stop here if we land in the handle for a job completing if we're in
" a sandbox.
@@ -224,7 +238,7 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort
" tsserver sends syntax and semantic errors in separate messages, so we
" have to collect the messages separately for each buffer and join them
" back together again.
- if a:error_type ==# 'syntax'
+ if a:error_type is# 'syntax'
let l:info.syntax_loclist = l:thislist
else
let l:info.semantic_loclist = l:thislist
@@ -247,16 +261,16 @@ endfunction
function! ale#engine#HandleLSPResponse(conn_id, response) abort
let l:method = get(a:response, 'method', '')
- if get(a:response, 'jsonrpc', '') ==# '2.0' && has_key(a:response, 'error')
+ if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
" Uncomment this line to print LSP error messages.
" call s:HandleLSPErrorMessage(a:response.error.message)
- elseif l:method ==# 'textDocument/publishDiagnostics'
+ elseif l:method is# 'textDocument/publishDiagnostics'
call s:HandleLSPDiagnostics(a:conn_id, a:response)
- elseif get(a:response, 'type', '') ==# 'event'
- \&& get(a:response, 'event', '') ==# 'semanticDiag'
+ elseif get(a:response, 'type', '') is# 'event'
+ \&& get(a:response, 'event', '') is# 'semanticDiag'
call s:HandleTSServerDiagnostics(a:response, 'semantic')
- elseif get(a:response, 'type', '') ==# 'event'
- \&& get(a:response, 'event', '') ==# 'syntaxDiag'
+ elseif get(a:response, 'type', '') is# 'event'
+ \&& get(a:response, 'event', '') is# 'syntaxDiag'
call s:HandleTSServerDiagnostics(a:response, 'syntax')
endif
endfunction
@@ -268,10 +282,6 @@ function! ale#engine#SetResults(buffer, loclist) abort
" The List could be sorted again here by SetSigns.
if g:ale_set_signs
call ale#sign#SetSigns(a:buffer, a:loclist)
-
- if l:linting_is_done
- call ale#sign#RemoveDummySignIfNeeded(a:buffer)
- endif
endif
if g:ale_set_quickfix || g:ale_set_loclist
@@ -298,6 +308,9 @@ function! ale#engine#SetResults(buffer, loclist) abort
endif
if l:linting_is_done
+ " Reset the save event marker, used for opening windows, etc.
+ call setbufvar(a:buffer, 'ale_save_event_fired', 0)
+
" Automatically remove all managed temporary files and directories
" now that all jobs have completed.
call ale#engine#RemoveManagedFiles(a:buffer)
@@ -310,17 +323,17 @@ endfunction
function! s:RemapItemTypes(type_map, loclist) abort
for l:item in a:loclist
let l:key = l:item.type
- \ . (get(l:item, 'sub_type', '') ==# 'style' ? 'S' : '')
+ \ . (get(l:item, 'sub_type', '') is# 'style' ? 'S' : '')
let l:new_key = get(a:type_map, l:key, '')
- if l:new_key ==# 'E'
- \|| l:new_key ==# 'ES'
- \|| l:new_key ==# 'W'
- \|| l:new_key ==# 'WS'
- \|| l:new_key ==# 'I'
+ if l:new_key is# 'E'
+ \|| l:new_key is# 'ES'
+ \|| l:new_key is# 'W'
+ \|| l:new_key is# 'WS'
+ \|| l:new_key is# 'I'
let l:item.type = l:new_key[0]
- if l:new_key ==# 'ES' || l:new_key ==# 'WS'
+ if l:new_key is# 'ES' || l:new_key is# 'WS'
let l:item.sub_type = 'style'
elseif has_key(l:item, 'sub_type')
call remove(l:item, 'sub_type')
@@ -329,7 +342,11 @@ function! s:RemapItemTypes(type_map, loclist) abort
endfor
endfunction
+" Save the temporary directory so we can figure out if files are in it.
+let s:temp_dir = fnamemodify(tempname(), ':h')
+
function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
+ let l:bufnr_map = {}
let l:new_loclist = []
" Some errors have line numbers beyond the end of the file,
@@ -351,16 +368,42 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
" The linter_name will be set on the errors so it can be used in
" output, filtering, etc..
let l:item = {
+ \ 'bufnr': a:buffer,
\ 'text': l:old_item.text,
\ 'lnum': str2nr(l:old_item.lnum),
\ 'col': str2nr(get(l:old_item, 'col', 0)),
- \ 'bufnr': get(l:old_item, 'bufnr', a:buffer),
\ 'vcol': get(l:old_item, 'vcol', 0),
\ 'type': get(l:old_item, 'type', 'E'),
\ 'nr': get(l:old_item, 'nr', -1),
\ 'linter_name': a:linter_name,
\}
+ if has_key(l:old_item, 'filename')
+ \&& l:old_item.filename[:len(s:temp_dir) - 1] isnot# s:temp_dir
+ " Use the filename given.
+ " Temporary files are assumed to be for this buffer,
+ " and the filename is not included then, because it looks bad
+ " in the loclist window.
+ let l:filename = l:old_item.filename
+ let l:item.filename = l:filename
+
+ if has_key(l:old_item, 'bufnr')
+ " If a buffer number is also given, include that too.
+ " If Vim detects that he buffer number is valid, it will
+ " be used instead of the filename.
+ let l:item.bufnr = l:old_item.bufnr
+ elseif has_key(l:bufnr_map, l:filename)
+ " Get the buffer number from the map, which can be faster.
+ let l:item.bufnr = l:bufnr_map[l:filename]
+ else
+ " Look up the buffer number.
+ let l:item.bufnr = bufnr(l:filename)
+ let l:bufnr_map[l:filename] = l:item.bufnr
+ endif
+ elseif has_key(l:old_item, 'bufnr')
+ let l:item.bufnr = l:old_item.bufnr
+ endif
+
if has_key(l:old_item, 'detail')
let l:item.detail = l:old_item.detail
endif
@@ -381,8 +424,9 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
if l:item.lnum < 1
" When errors appear before line 1, put them at line 1.
let l:item.lnum = 1
- elseif l:item.lnum > l:last_line_number
+ elseif l:item.bufnr == a:buffer && l:item.lnum > l:last_line_number
" When errors go beyond the end of the file, put them at the end.
+ " This is only done for the current buffer.
let l:item.lnum = l:last_line_number
endif
@@ -417,7 +461,8 @@ function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
" Automatically delete the directory later.
call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
" Write the buffer out to a file.
- call writefile(getbufline(a:buffer, 1, '$'), a:temporary_file)
+ let l:lines = getbufline(a:buffer, 1, '$')
+ call ale#util#Writefile(a:buffer, l:lines, a:temporary_file)
return 1
endfunction
@@ -432,6 +477,7 @@ function! s:RunJob(options) abort
let l:output_stream = a:options.output_stream
let l:next_chain_index = a:options.next_chain_index
let l:read_buffer = a:options.read_buffer
+ let l:info = g:ale_buffer_info[l:buffer]
if empty(l:command)
return 0
@@ -461,9 +507,9 @@ function! s:RunJob(options) abort
\ 'exit_cb': function('s:HandleExit'),
\}
- if l:output_stream ==# 'stderr'
+ if l:output_stream is# 'stderr'
let l:job_options.err_cb = function('s:GatherOutput')
- elseif l:output_stream ==# 'both'
+ elseif l:output_stream is# 'both'
let l:job_options.out_cb = function('s:GatherOutput')
let l:job_options.err_cb = function('s:GatherOutput')
else
@@ -487,8 +533,11 @@ function! s:RunJob(options) abort
" Only proceed if the job is being run.
if l:job_id
" Add the job to the list of jobs, so we can track them.
- call add(g:ale_buffer_info[l:buffer].job_list, l:job_id)
- call add(g:ale_buffer_info[l:buffer].active_linter_list, l:linter.name)
+ call add(l:info.job_list, l:job_id)
+
+ if index(l:info.active_linter_list, l:linter.name) < 0
+ call add(l:info.active_linter_list, l:linter.name)
+ endif
let l:status = 'started'
" Store the ID for the job in the map to read back again.
@@ -502,8 +551,6 @@ function! s:RunJob(options) abort
if g:ale_history_enabled
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
- else
- let g:ale_buffer_info[l:buffer].history = []
endif
if get(g:, 'ale_run_synchronously') == 1
@@ -596,6 +643,7 @@ endfunction
function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
let l:new_job_list = []
+ let l:new_active_linter_list = []
for l:job_id in get(l:info, 'job_list', [])
let l:job_info = get(s:job_info_map, l:job_id, {})
@@ -606,15 +654,23 @@ function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
call remove(s:job_info_map, l:job_id)
else
call add(l:new_job_list, l:job_id)
+ " Linters with jobs still running are still active.
+ call add(l:new_active_linter_list, l:job_info.linter.name)
endif
endif
endfor
+ " Remove duplicates from the active linter list.
+ call uniq(sort(l:new_active_linter_list))
+
" Update the List, so it includes only the jobs we still need.
let l:info.job_list = l:new_job_list
+ " Update the active linter list, clearing out anything not running.
+ let l:info.active_linter_list = l:new_active_linter_list
endfunction
function! s:CheckWithLSP(buffer, linter) abort
+ let l:info = g:ale_buffer_info[a:buffer]
let l:lsp_details = ale#linter#StartLSP(
\ a:buffer,
\ a:linter,
@@ -631,13 +687,15 @@ function! s:CheckWithLSP(buffer, linter) abort
" Remember the linter this connection is for.
let s:lsp_linter_map[l:id] = a:linter.name
- let l:change_message = a:linter.lsp ==# 'tsserver'
+ let l:change_message = a:linter.lsp is# 'tsserver'
\ ? ale#lsp#tsserver_message#Geterr(a:buffer)
\ : ale#lsp#message#DidChange(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:change_message, l:root)
if l:request_id != 0
- call add(g:ale_buffer_info[a:buffer].active_linter_list, a:linter.name)
+ if index(l:info.active_linter_list, a:linter.name) < 0
+ call add(l:info.active_linter_list, a:linter.name)
+ endif
endif
return l:request_id != 0
@@ -654,10 +712,44 @@ function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
call filter(
\ get(g:ale_buffer_info[a:buffer], 'loclist', []),
- \ 'get(l:name_map, v:val.linter_name)',
+ \ 'get(l:name_map, get(v:val, ''linter_name''))',
\)
endfunction
+function! s:AddProblemsFromOtherBuffers(buffer, linters) abort
+ let l:filename = expand('#' . a:buffer . ':p')
+ let l:loclist = []
+ let l:name_map = {}
+
+ " Build a map of the active linters.
+ for l:linter in a:linters
+ let l:name_map[l:linter.name] = 1
+ endfor
+
+ " Find the items from other buffers, for the linters that are enabled.
+ for l:info in values(g:ale_buffer_info)
+ for l:item in l:info.loclist
+ if has_key(l:item, 'filename')
+ \&& l:item.filename is# l:filename
+ \&& has_key(l:name_map, l:item.linter_name)
+ " Copy the items and set the buffer numbers to this one.
+ let l:new_item = copy(l:item)
+ let l:new_item.bufnr = a:buffer
+ call add(l:loclist, l:new_item)
+ endif
+ endfor
+ endfor
+
+ if !empty(l:loclist)
+ call sort(l:loclist, function('ale#util#LocItemCompareWithText'))
+ call uniq(l:loclist, function('ale#util#LocItemCompareWithText'))
+
+ " Set the loclist variable, used by some parts of ALE.
+ let g:ale_buffer_info[a:buffer].loclist = l:loclist
+ call ale#engine#SetResults(a:buffer, l:loclist)
+ endif
+endfunction
+
" Run a linter for a buffer.
"
" Returns 1 if the linter was successfully run.
@@ -667,7 +759,7 @@ function! s:RunLinter(buffer, linter) abort
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
- if s:IsExecutable(l:executable)
+ if ale#engine#IsExecutable(a:buffer, l:executable)
return s:InvokeChain(a:buffer, a:linter, 0, [])
endif
endif
@@ -677,7 +769,7 @@ endfunction
function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" Initialise the buffer information if needed.
- call ale#engine#InitBufferInfo(a:buffer)
+ let l:new_buffer = ale#engine#InitBufferInfo(a:buffer)
call s:StopCurrentJobs(a:buffer, a:should_lint_file)
call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
@@ -702,6 +794,8 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" disabled, or ALE itself is disabled.
if l:can_clear_results
call ale#engine#SetResults(a:buffer, [])
+ elseif l:new_buffer
+ call s:AddProblemsFromOtherBuffers(a:buffer, a:linters)
endif
endfunction
diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim
index 4722afa9..a3b74677 100644
--- a/autoload/ale/events.vim
+++ b/autoload/ale/events.vim
@@ -1,6 +1,7 @@
" Author: w0rp <devw0rp@gmail.com>
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
if g:ale_fix_on_save
@@ -23,9 +24,20 @@ function! s:LintOnEnter(buffer) abort
endfunction
function! ale#events#EnterEvent(buffer) abort
+ let l:filetype = getbufvar(a:buffer, '&filetype')
+ call setbufvar(a:buffer, 'ale_original_filetype', l:filetype)
+
call s:LintOnEnter(a:buffer)
endfunction
+function! ale#events#FileTypeEvent(buffer, new_filetype) abort
+ let l:filetype = getbufvar(a:buffer, 'ale_original_filetype', '')
+
+ if a:new_filetype isnot# l:filetype
+ call ale#Queue(300, 'lint_file', a:buffer)
+ endif
+endfunction
+
function! ale#events#FileChangedEvent(buffer) abort
call setbufvar(a:buffer, 'ale_file_changed', 1)
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index 45855a50..80f46c27 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -43,7 +43,6 @@ function! ale#fix#ApplyQueuedFixes() abort
if empty(&buftype)
noautocmd :w!
else
- call writefile(l:data.output, 'fix_test_file')
set nomodified
endif
endif
@@ -155,7 +154,7 @@ function! s:CreateTemporaryFileForJob(buffer, temporary_file, input) abort
" Automatically delete the directory later.
call ale#fix#ManageDirectory(a:buffer, l:temporary_directory)
" Write the buffer out to a file.
- call writefile(a:input, a:temporary_file)
+ call ale#util#Writefile(a:buffer, a:input, a:temporary_file)
return 1
endfunction
@@ -187,9 +186,9 @@ function! s:RunJob(options) abort
if l:read_temporary_file
" TODO: Check that a temporary file is set here.
let l:job_info.file_to_read = l:temporary_file
- elseif l:output_stream ==# 'stderr'
+ elseif l:output_stream is# 'stderr'
let l:job_options.err_cb = function('s:GatherOutput')
- elseif l:output_stream ==# 'both'
+ elseif l:output_stream is# 'both'
let l:job_options.out_cb = function('s:GatherOutput')
let l:job_options.err_cb = function('s:GatherOutput')
else
@@ -321,7 +320,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
\ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'filename': expand('#' . a:buffer . ':p'),
\ 'done': 0,
- \ 'should_save': a:fixing_flag ==# 'save_file',
+ \ 'should_save': a:fixing_flag is# 'save_file',
\ 'temporary_directory_list': [],
\}
endfunction
@@ -336,14 +335,14 @@ function! ale#fix#Fix(...) abort
let l:fixing_flag = get(a:000, 0, '')
- if l:fixing_flag !=# '' && l:fixing_flag !=# 'save_file'
+ if l:fixing_flag isnot# '' && l:fixing_flag isnot# 'save_file'
throw "fixing_flag must be either '' or 'save_file'"
endif
let l:callback_list = s:GetCallbacks()
if empty(l:callback_list)
- if l:fixing_flag ==# ''
+ if l:fixing_flag is# ''
echoerr 'No fixers have been defined. Try :ALEFixSuggest'
endif
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index ce30c36e..b77ac031 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -72,6 +72,21 @@ let s:default_registry = {
\ 'suggested_filetypes': ['css', 'sass', 'scss', 'stylus'],
\ 'description': 'Fix stylesheet files using stylelint --fix.',
\ },
+\ 'swiftformat': {
+\ 'function': 'ale#fixers#swiftformat#Fix',
+\ 'suggested_filetypes': ['swift'],
+\ 'description': 'Apply SwiftFormat to a file.',
+\ },
+\ 'phpcbf': {
+\ 'function': 'ale#fixers#phpcbf#Fix',
+\ 'suggested_filetypes': ['php'],
+\ 'description': 'Fix PHP files with phpcbf.',
+\ },
+\ 'clang-format': {
+\ 'function': 'ale#fixers#clangformat#Fix',
+\ 'suggested_filetypes': ['c', 'cpp'],
+\ 'description': 'Fix C/C++ files with clang-format.',
+\ },
\}
" Reset the function registry to the default entries.
diff --git a/autoload/ale/fixers/clangformat.vim b/autoload/ale/fixers/clangformat.vim
new file mode 100644
index 00000000..b50b7047
--- /dev/null
+++ b/autoload/ale/fixers/clangformat.vim
@@ -0,0 +1,22 @@
+scriptencoding utf-8
+" Author: Peter Renström <renstrom.peter@gmail.com>
+" Description: Fixing C/C++ files with clang-format.
+
+call ale#Set('c_clangformat_executable', 'clang-format')
+call ale#Set('c_clangformat_use_global', 0)
+call ale#Set('c_clangformat_options', '')
+
+function! ale#fixers#clangformat#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'c_clangformat', [
+ \ 'clang-format',
+ \])
+endfunction
+
+function! ale#fixers#clangformat#Fix(buffer) abort
+ let l:options = ale#Var(a:buffer, 'c_clangformat_options')
+
+ return {
+ \ 'command': ale#Escape(ale#fixers#clangformat#GetExecutable(a:buffer))
+ \ . ' ' . l:options,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/generic_python.vim b/autoload/ale/fixers/generic_python.vim
index 8bdde505..124146be 100644
--- a/autoload/ale/fixers/generic_python.vim
+++ b/autoload/ale/fixers/generic_python.vim
@@ -23,3 +23,38 @@ function! ale#fixers#generic_python#AddLinesBeforeControlStatements(buffer, line
return l:new_lines
endfunction
+
+" This function breaks up long lines so that autopep8 or other tools can
+" fix the badly-indented code which is produced as a result.
+function! ale#fixers#generic_python#BreakUpLongLines(buffer, lines) abort
+ " Default to a maximum line length of 79
+ let l:max_line_length = 79
+ let l:conf = ale#path#FindNearestFile(a:buffer, 'setup.cfg')
+
+ " Read the maximum line length from setup.cfg
+ if !empty(l:conf)
+ for l:match in ale#util#GetMatches(
+ \ readfile(l:conf),
+ \ '\v^ *max-line-length *\= *(\d+)',
+ \)
+ let l:max_line_length = str2nr(l:match[1])
+ endfor
+ endif
+
+ let l:new_list = []
+
+ for l:line in a:lines
+ if len(l:line) > l:max_line_length && l:line !~# '# *noqa'
+ let l:line = substitute(l:line, '\v([(,])([^)])', '\1\n\2', 'g')
+ let l:line = substitute(l:line, '\v([^(])([)])', '\1,\n\2', 'g')
+
+ for l:split_line in split(l:line, "\n")
+ call add(l:new_list, l:split_line)
+ endfor
+ else
+ call add(l:new_list, l:line)
+ endif
+ endfor
+
+ return l:new_list
+endfunction
diff --git a/autoload/ale/fixers/phpcbf.vim b/autoload/ale/fixers/phpcbf.vim
new file mode 100644
index 00000000..9bff7412
--- /dev/null
+++ b/autoload/ale/fixers/phpcbf.vim
@@ -0,0 +1,24 @@
+" Author: notomo <notomo.motono@gmail.com>
+" Description: Fixing files with phpcbf.
+
+call ale#Set('php_phpcbf_standard', '')
+call ale#Set('php_phpcbf_executable', 'phpcbf')
+call ale#Set('php_phpcbf_use_global', 0)
+
+function! ale#fixers#phpcbf#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'php_phpcbf', [
+ \ 'vendor/bin/phpcbf',
+ \ 'phpcbf'
+ \])
+endfunction
+
+function! ale#fixers#phpcbf#Fix(buffer) abort
+ let l:executable = ale#fixers#phpcbf#GetExecutable(a:buffer)
+ let l:standard = ale#Var(a:buffer, 'php_phpcbf_standard')
+ let l:standard_option = !empty(l:standard)
+ \ ? '--standard=' . l:standard
+ \ : ''
+ return {
+ \ '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 ae370ac6..581536e6 100644
--- a/autoload/ale/fixers/prettier.vim
+++ b/autoload/ale/fixers/prettier.vim
@@ -1,13 +1,33 @@
" Author: tunnckoCore (Charlike Mike Reagent) <mameto2011@gmail.com>,
-" w0rp <devw0rp@gmail.com>
+" w0rp <devw0rp@gmail.com>, morhetz (Pavel Pertsev) <morhetz@gmail.com>
" Description: Integration of Prettier with ALE.
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',
+ \ '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',
\ 'node_modules/prettier-cli/index.js',
\ 'node_modules/.bin/prettier',
\])
@@ -15,11 +35,15 @@ 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)
return {
\ 'command': ale#Escape(ale#fixers#prettier#GetExecutable(a:buffer))
\ . ' %t'
- \ . ' ' . l:options
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . (l:use_config ? ' --config ' . ale#Escape(l:config) : '')
\ . ' --write',
\ 'read_temporary_file': 1,
\}
diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim
index 88dc1c43..35569b19 100644
--- a/autoload/ale/fixers/rubocop.vim
+++ b/autoload/ale/fixers/rubocop.vim
@@ -4,9 +4,11 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort
\ ? ' exec rubocop'
\ : ''
let l:config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
+ let l:options = ale#Var(a:buffer, 'ruby_rubocop_options')
return ale#Escape(l:executable) . l:exec_args
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
+ \ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --auto-correct %t'
endfunction
diff --git a/autoload/ale/fixers/swiftformat.vim b/autoload/ale/fixers/swiftformat.vim
new file mode 100644
index 00000000..dcc204b1
--- /dev/null
+++ b/autoload/ale/fixers/swiftformat.vim
@@ -0,0 +1,25 @@
+" Author: gfontenot (Gordon Fontenot) <gordon@fonten.io>
+" Description: Integration of SwiftFormat with ALE.
+
+call ale#Set('swift_swiftformat_executable', 'swiftformat')
+call ale#Set('swift_swiftformat_use_global', 0)
+call ale#Set('swift_swiftformat_options', '')
+
+function! ale#fixers#swiftformat#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'swift_swiftformat', [
+ \ 'Pods/SwiftFormat/CommandLineTool/swiftformat',
+ \ 'ios/Pods/SwiftFormat/CommandLineTool/swiftformat',
+ \ 'swiftformat',
+ \])
+endfunction
+
+function! ale#fixers#swiftformat#Fix(buffer) abort
+ let l:options = ale#Var(a:buffer, 'swift_swiftformat_options')
+
+ return {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(ale#fixers#swiftformat#GetExecutable(a:buffer))
+ \ . ' %t'
+ \ . ' ' . l:options,
+ \}
+endfunction
diff --git a/autoload/ale/handlers/cppcheck.vim b/autoload/ale/handlers/cppcheck.vim
index b365c794..dc56cd0b 100644
--- a/autoload/ale/handlers/cppcheck.vim
+++ b/autoload/ale/handlers/cppcheck.vim
@@ -11,7 +11,7 @@ function! ale#handlers#cppcheck#HandleCppCheckFormat(buffer, lines) abort
if ale#path#IsBufferPath(a:buffer, l:match[1])
call add(l:output, {
\ 'lnum': str2nr(l:match[2]),
- \ 'type': l:match[3] ==# 'error' ? 'E' : 'W',
+ \ 'type': l:match[3] is# 'error' ? 'E' : 'W',
\ 'text': l:match[4],
\})
endif
diff --git a/autoload/ale/handlers/css.vim b/autoload/ale/handlers/css.vim
index 71eaf2ce..4c1b81cb 100644
--- a/autoload/ale/handlers/css.vim
+++ b/autoload/ale/handlers/css.vim
@@ -29,7 +29,7 @@ function! ale#handlers#css#HandleCSSLintFormat(buffer, lines) abort
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
- \ 'type': l:type ==# 'Warning' ? 'W' : 'E',
+ \ 'type': l:type is# 'Warning' ? 'W' : 'E',
\})
endfor
@@ -61,7 +61,7 @@ function! ale#handlers#css#HandleStyleLintFormat(buffer, lines) abort
call add(l:output, {
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
- \ 'type': l:match[3] ==# '✖' ? 'E' : 'W',
+ \ 'type': l:match[3] is# '✖' ? 'E' : 'W',
\ 'text': l:match[4] . ' [' . l:match[5] . ']',
\})
endfor
diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim
index a7ff0ae2..6c5d75c0 100644
--- a/autoload/ale/handlers/eslint.vim
+++ b/autoload/ale/handlers/eslint.vim
@@ -4,6 +4,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)
function! ale#handlers#eslint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_eslint', [
@@ -82,6 +83,12 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort
let l:type = 'Error'
let l:text = l:match[3]
+ if ale#Var(a:buffer, 'javascript_eslint_suppress_eslintignore')
+ if l:text is# 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.'
+ continue
+ endif
+ endif
+
" Take the error type from the output if available.
if !empty(l:match[4])
let l:type = split(l:match[4], '/')[0]
@@ -92,7 +99,7 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
- \ 'type': l:type ==# 'Warning' ? 'W' : 'E',
+ \ 'type': l:type is# 'Warning' ? 'W' : 'E',
\}
for l:col_match in ale#util#GetMatches(l:text, s:col_end_patterns)
diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim
index aaac5281..ad5cab39 100644
--- a/autoload/ale/handlers/gcc.vim
+++ b/autoload/ale/handlers/gcc.vim
@@ -81,7 +81,7 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
let l:included_filename = ''
endif
elseif l:include_lnum > 0
- \&& (empty(l:included_filename) || l:included_filename ==# l:match[1])
+ \&& (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.
@@ -96,7 +96,7 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
let l:included_filename = ''
if s:IsHeaderFile(bufname(bufnr('')))
- \&& l:match[5][:len(s:pragma_error) - 1] ==# s:pragma_error
+ \&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error
continue
endif
diff --git a/autoload/ale/handlers/haskell.vim b/autoload/ale/handlers/haskell.vim
index 5d417c89..bac5f4ae 100644
--- a/autoload/ale/handlers/haskell.vim
+++ b/autoload/ale/handlers/haskell.vim
@@ -14,7 +14,7 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
for l:line in a:lines
if len(matchlist(l:line, l:pattern)) > 0
call add(l:corrected_lines, l:line)
- elseif l:line ==# ''
+ elseif l:line is# ''
call add(l:corrected_lines, l:line)
else
if len(l:corrected_lines) > 0
@@ -42,10 +42,10 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
let l:text = l:errors[2]
else
let l:ghc_type = ''
- let l:text = l:match[4][:0] ==# ' ' ? l:match[4][1:] : l:match[4]
+ let l:text = l:match[4][:0] is# ' ' ? l:match[4][1:] : l:match[4]
endif
- if l:ghc_type ==? 'Warning'
+ if l:ghc_type is? 'Warning'
let l:type = 'W'
else
let l:type = 'E'
diff --git a/autoload/ale/handlers/rust.vim b/autoload/ale/handlers/rust.vim
index 3f9083a5..12a5a16a 100644
--- a/autoload/ale/handlers/rust.vim
+++ b/autoload/ale/handlers/rust.vim
@@ -47,7 +47,7 @@ function! ale#handlers#rust#HandleRustErrorsForFile(buffer, full_filename, lines
for l:span in l:error.spans
if (
\ l:span.is_primary
- \ && (ale#path#IsBufferPath(a:buffer, l:span.file_name) || l:span.file_name ==# '<anon>')
+ \ && (ale#path#IsBufferPath(a:buffer, l:span.file_name) || l:span.file_name is# '<anon>')
\)
call add(l:output, {
\ 'lnum': l:span.line_start,
diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim
new file mode 100644
index 00000000..894879ee
--- /dev/null
+++ b/autoload/ale/handlers/sh.vim
@@ -0,0 +1,20 @@
+" Author: w0rp <devw0rp@gmail.com>
+
+" Get the shell type for a buffer, based on the hashbang line.
+function! ale#handlers#sh#GetShellType(buffer) abort
+ let l:bang_line = get(getbufline(a:buffer, 1), 0, '')
+
+ " Take the shell executable from the hashbang, if we can.
+ if l:bang_line[:1] is# '#!'
+ " 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']
+ if l:command =~# l:possible_shell . '\s*$'
+ return l:possible_shell
+ endif
+ endfor
+ endif
+
+ return ''
+endfunction
diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim
index a109a265..5c01e7a9 100644
--- a/autoload/ale/highlight.vim
+++ b/autoload/ale/highlight.vim
@@ -65,15 +65,15 @@ function! ale#highlight#UpdateHighlights() abort
call ale#highlight#RemoveHighlights()
for l:item in l:item_list
- if l:item.type ==# 'W'
- if get(l:item, 'sub_type', '') ==# 'style'
+ if l:item.type is# 'W'
+ if get(l:item, 'sub_type', '') is# 'style'
let l:group = 'ALEStyleWarning'
else
let l:group = 'ALEWarning'
endif
- elseif l:item.type ==# 'I'
+ elseif l:item.type is# 'I'
let l:group = 'ALEInfo'
- elseif get(l:item, 'sub_type', '') ==# 'style'
+ elseif get(l:item, 'sub_type', '') is# 'style'
let l:group = 'ALEStyleError'
else
let l:group = 'ALEError'
diff --git a/autoload/ale/history.vim b/autoload/ale/history.vim
index 0356c022..a6282ea5 100644
--- a/autoload/ale/history.vim
+++ b/autoload/ale/history.vim
@@ -1,15 +1,20 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Tools for managing command history
-"
+
+" Return a shallow copy of the command history for a given buffer number.
+function! ale#history#Get(buffer) abort
+ return copy(getbufvar(a:buffer, 'ale_history', []))
+endfunction
+
function! ale#history#Add(buffer, status, job_id, command) abort
if g:ale_max_buffer_history_size <= 0
" Don't save anything if the history isn't a positive number.
- let g:ale_buffer_info[a:buffer].history = []
+ call setbufvar(a:buffer, 'ale_history', [])
return
endif
- let l:history = g:ale_buffer_info[a:buffer].history
+ let l:history = getbufvar(a:buffer, 'ale_history', [])
" Remove the first item if we hit the max history size.
if len(l:history) >= g:ale_max_buffer_history_size
@@ -22,18 +27,13 @@ function! ale#history#Add(buffer, status, job_id, command) abort
\ 'command': a:command,
\})
- let g:ale_buffer_info[a:buffer].history = l:history
+ call setbufvar(a:buffer, 'ale_history', l:history)
endfunction
function! s:FindHistoryItem(buffer, job_id) abort
- " Stop immediately if there's nothing set up for the buffer.
- if !has_key(g:ale_buffer_info, a:buffer)
- return {}
- endif
-
" Search backwards to find a matching job ID. IDs might be recycled,
" so finding the last one should be good enough.
- for l:obj in reverse(g:ale_buffer_info[a:buffer].history[:])
+ for l:obj in reverse(ale#history#Get(a:buffer))
if l:obj.job_id == a:job_id
return l:obj
endif
@@ -46,18 +46,14 @@ endfunction
function! ale#history#SetExitCode(buffer, job_id, exit_code) abort
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
- if !empty(l:obj)
- " If we find a match, then set the code and status.
- let l:obj.exit_code = a:exit_code
- let l:obj.status = 'finished'
- endif
+ " If we find a match, then set the code and status.
+ let l:obj.exit_code = a:exit_code
+ let l:obj.status = 'finished'
endfunction
" Set the output for a command which finished.
function! ale#history#RememberOutput(buffer, job_id, output) abort
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
- if !empty(l:obj)
- let l:obj.output = a:output
- endif
+ let l:obj.output = a:output
endfunction
diff --git a/autoload/ale/job.vim b/autoload/ale/job.vim
index 93f28824..1d8b6760 100644
--- a/autoload/ale/job.vim
+++ b/autoload/ale/job.vim
@@ -34,7 +34,7 @@ function! ale#job#JoinNeovimOutput(job, last_line, data, mode, callback) abort
let l:new_last_line = a:last_line . a:data[0]
endif
- if a:mode ==# 'raw'
+ if a:mode is# 'raw'
if !empty(l:lines)
call a:callback(a:job, join(l:lines, "\n") . "\n")
endif
@@ -50,7 +50,7 @@ endfunction
function! s:NeoVimCallback(job, data, event) abort
let l:info = s:job_map[a:job]
- if a:event ==# 'stdout'
+ if a:event is# 'stdout'
let l:info.out_cb_line = ale#job#JoinNeovimOutput(
\ a:job,
\ l:info.out_cb_line,
@@ -58,7 +58,7 @@ function! s:NeoVimCallback(job, data, event) abort
\ l:info.mode,
\ ale#util#GetFunction(l:info.out_cb),
\)
- elseif a:event ==# 'stderr'
+ elseif a:event is# 'stderr'
let l:info.err_cb_line = ale#job#JoinNeovimOutput(
\ a:job,
\ l:info.err_cb_line,
@@ -117,7 +117,7 @@ function! s:VimCloseCallback(channel) abort
" job_status() can trigger the exit handler.
" The channel can close before the job has exited.
- if job_status(l:job) ==# 'dead'
+ if job_status(l:job) is# 'dead'
try
if !empty(l:info) && has_key(l:info, 'exit_cb')
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, l:info.exit_code)
@@ -142,7 +142,7 @@ function! s:VimExitCallback(job, exit_code) abort
let l:info.exit_code = a:exit_code
" The program can exit before the data has finished being read.
- if ch_status(job_getchannel(a:job)) ==# 'closed'
+ if ch_status(job_getchannel(a:job)) is# 'closed'
try
if !empty(l:info) && has_key(l:info, 'exit_cb')
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, a:exit_code)
@@ -161,7 +161,7 @@ function! ale#job#ParseVim8ProcessID(job_string) abort
endfunction
function! ale#job#ValidateArguments(command, options) abort
- if a:options.mode !=# 'nl' && a:options.mode !=# 'raw'
+ if a:options.mode isnot# 'nl' && a:options.mode isnot# 'raw'
throw 'Invalid mode: ' . a:options.mode
endif
endfunction
@@ -244,7 +244,7 @@ function! ale#job#Start(command, options) abort
let l:job_id = ale#job#ParseVim8ProcessID(string(l:job_info.job))
endif
- if l:job_id
+ if l:job_id > 0
" Store the job in the map for later only if we can get the ID.
let s:job_map[l:job_id] = l:job_info
endif
@@ -273,7 +273,7 @@ function! ale#job#IsRunning(job_id) abort
endtry
elseif has_key(s:job_map, a:job_id)
let l:job = s:job_map[a:job_id].job
- return job_status(l:job) ==# 'run'
+ return job_status(l:job) is# 'run'
endif
return 0
@@ -296,7 +296,7 @@ function! ale#job#Stop(job_id) abort
" We must close the channel for reading the buffer if it is open
" when stopping a job. Otherwise, we will get errors in the status line.
- if ch_status(job_getchannel(l:job)) ==# 'open'
+ if ch_status(job_getchannel(l:job)) is# 'open'
call ch_close_in(job_getchannel(l:job))
endif
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 8fc68510..2cd773f1 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -25,6 +25,7 @@ let s:default_ale_linters = {
\ 'csh': ['shell'],
\ 'go': ['gofmt', 'golint', 'go vet'],
\ 'help': [],
+\ 'python': ['flake8', 'mypy', 'pylint'],
\ 'rust': ['cargo'],
\ 'spec': [],
\ 'text': [],
@@ -59,9 +60,9 @@ function! ale#linter#PreProcess(linter) abort
throw '`name` must be defined to name the linter'
endif
- let l:needs_address = l:obj.lsp ==# 'socket'
- let l:needs_executable = l:obj.lsp !=# 'socket'
- let l:needs_command = l:obj.lsp !=# 'socket'
+ let l:needs_address = l:obj.lsp is# 'socket'
+ let l:needs_executable = l:obj.lsp isnot# 'socket'
+ let l:needs_command = l:obj.lsp isnot# 'socket'
let l:needs_lsp_details = !empty(l:obj.lsp)
if empty(l:obj.lsp)
@@ -311,7 +312,7 @@ function! ale#linter#Get(original_filetypes) abort
let l:all_linters = ale#linter#GetAll(l:filetype)
let l:filetype_linters = []
- if type(l:linter_names) == type('') && l:linter_names ==# 'all'
+ if type(l:linter_names) == type('') && l:linter_names is# 'all'
let l:filetype_linters = l:all_linters
elseif type(l:linter_names) == type([])
" Select only the linters we or the user has specified.
@@ -377,13 +378,13 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort
let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
- if empty(l:root) && a:linter.lsp !=# 'tsserver'
+ if empty(l:root) && a:linter.lsp isnot# 'tsserver'
" If there's no project root, then we can't check files with LSP,
" unless we are using tsserver, which doesn't use project roots.
return {}
endif
- if a:linter.lsp ==# 'socket'
+ if a:linter.lsp is# 'socket'
let l:address = ale#linter#GetAddress(a:buffer, a:linter)
let l:conn_id = ale#lsp#ConnectToAddress(
\ l:address,
@@ -425,7 +426,7 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort
endif
" The change message needs to be sent for tsserver before doing anything.
- if a:linter.lsp ==# 'tsserver'
+ if a:linter.lsp is# 'tsserver'
call ale#lsp#Send(l:conn_id, ale#lsp#tsserver_message#Change(a:buffer))
endif
diff --git a/autoload/ale/list.vim b/autoload/ale/list.vim
index 9fe35bf3..7b2bf2cb 100644
--- a/autoload/ale/list.vim
+++ b/autoload/ale/list.vim
@@ -1,24 +1,71 @@
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
" Description: Manages the loclist and quickfix lists
+if !exists('s:timer_args')
+ let s:timer_args = {}
+endif
+
" Return 1 if there is a buffer with buftype == 'quickfix' in bufffer list
function! ale#list#IsQuickfixOpen() abort
for l:buf in range(1, bufnr('$'))
- if getbufvar(l:buf, '&buftype') ==# 'quickfix'
+ if getbufvar(l:buf, '&buftype') is# 'quickfix'
return 1
endif
endfor
return 0
endfunction
-function! ale#list#SetLists(buffer, loclist) abort
+" Check if we should open the list, based on the save event being fired, and
+" that setting being on, or the setting just being set to `1`.
+function! s:ShouldOpen(buffer) abort
+ let l:val = ale#Var(a:buffer, 'open_list')
+ let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
+
+ return l:val is 1 || (l:val is# 'on_save' && l:saved)
+endfunction
+
+function! ale#list#GetCombinedList() abort
+ let l:list = []
+
+ for l:info in values(g:ale_buffer_info)
+ call extend(l:list, l:info.loclist)
+ endfor
+
+ call sort(l:list, function('ale#util#LocItemCompareWithText'))
+ call uniq(l:list, function('ale#util#LocItemCompareWithText'))
+
+ return l:list
+endfunction
+
+function! s:FixList(list) abort
+ let l:new_list = []
+
+ for l:item in a:list
+ 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)
+ endfor
+
+ return l:new_list
+endfunction
+
+function! s:SetListsImpl(timer_id, buffer, loclist) abort
let l:title = expand('#' . a:buffer . ':p')
if g:ale_set_quickfix
+ let l:quickfix_list = ale#list#GetCombinedList()
+
if has('nvim')
- call setqflist(a:loclist, ' ', l:title)
+ call setqflist(s:FixList(l:quickfix_list), ' ', l:title)
else
- call setqflist(a:loclist)
+ call setqflist(s:FixList(l:quickfix_list))
call setqflist([], 'r', {'title': l:title})
endif
elseif g:ale_set_loclist
@@ -28,16 +75,24 @@ function! ale#list#SetLists(buffer, loclist) abort
let l:win_id = exists('*bufwinid') ? bufwinid(str2nr(a:buffer)) : 0
if has('nvim')
- call setloclist(l:win_id, a:loclist, ' ', l:title)
+ call setloclist(l:win_id, s:FixList(a:loclist), ' ', l:title)
else
- call setloclist(l:win_id, a:loclist)
+ call setloclist(l:win_id, s:FixList(a:loclist))
call setloclist(l:win_id, [], 'r', {'title': l:title})
endif
endif
- " If we have errors in our list, open the list. Only if it isn't already open
- if (g:ale_open_list && !empty(a:loclist)) || g:ale_keep_list_window_open
+ let l:keep_open = ale#Var(a:buffer, 'keep_list_window_open')
+
+ " Open a window to show the problems if we need to.
+ "
+ " We'll check if the current buffer's List is not empty here, so the
+ " window will only be opened if the current buffer has problems.
+ if s:ShouldOpen(a:buffer) && (l:keep_open || !empty(a:loclist))
let l:winnr = winnr()
+ let l:mode = mode()
+ let l:reset_visual_selection = l:mode is? 'v' || l:mode is# "\<c-v>"
+ let l:reset_character_selection = l:mode is? 's' || l:mode is# "\<c-s>"
if g:ale_set_quickfix
if !ale#list#IsQuickfixOpen()
@@ -48,14 +103,36 @@ function! ale#list#SetLists(buffer, loclist) abort
endif
" If focus changed, restore it (jump to the last window).
- if l:winnr !=# winnr()
+ if l:winnr isnot# winnr()
wincmd p
endif
+
+ if l:reset_visual_selection || l:reset_character_selection
+ " If we were in a selection mode before, select the last selection.
+ normal! gv
+
+ if l:reset_character_selection
+ " Switch back to Select mode, if we were in that.
+ normal! "\<c-g>"
+ endif
+ endif
endif
endfunction
-function! ale#list#CloseWindowIfNeeded(buffer) abort
- if g:ale_keep_list_window_open || !g:ale_open_list
+function! ale#list#SetLists(buffer, loclist) abort
+ if get(g:, 'ale_set_lists_synchronously') == 1
+ call s:SetListsImpl(-1, a:buffer, a:loclist)
+ else
+ call ale#util#StartPartialTimer(
+ \ 0,
+ \ function('s:SetListsImpl'),
+ \ [a:buffer, a:loclist],
+ \)
+ endif
+endfunction
+
+function! s:CloseWindowIfNeededImpl(timer_id, buffer) abort
+ if ale#Var(a:buffer, 'keep_list_window_open') || !s:ShouldOpen(a:buffer)
return
endif
@@ -73,3 +150,15 @@ function! ale#list#CloseWindowIfNeeded(buffer) abort
catch /E444/
endtry
endfunction
+
+function! ale#list#CloseWindowIfNeeded(buffer) abort
+ if get(g:, 'ale_set_lists_synchronously') == 1
+ call s:CloseWindowIfNeededImpl(-1, a:buffer)
+ else
+ call ale#util#StartPartialTimer(
+ \ 0,
+ \ function('s:CloseWindowIfNeededImpl'),
+ \ [a:buffer],
+ \)
+ endif
+endfunction
diff --git a/autoload/ale/loclist_jumping.vim b/autoload/ale/loclist_jumping.vim
index 88ed4c97..7ed9e6ba 100644
--- a/autoload/ale/loclist_jumping.vim
+++ b/autoload/ale/loclist_jumping.vim
@@ -10,15 +10,16 @@
" List will be returned, otherwise a pair of [line_number, column_number] will
" be returned.
function! ale#loclist_jumping#FindNearest(direction, wrap) abort
+ let l:buffer = bufnr('')
let l:pos = getcurpos()
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
- " This list will have already been sorted.
- let l:loclist = l:info.loclist
- let l:search_item = {'lnum': l:pos[1], 'col': l:pos[2]}
+ " Copy the list and filter to only the items in this buffer.
+ let l:loclist = filter(copy(l:info.loclist), 'v:val.bufnr == l:buffer')
+ let l:search_item = {'bufnr': l:buffer, 'lnum': l:pos[1], 'col': l:pos[2]}
" When searching backwards, so we can find the next smallest match.
- if a:direction ==# 'before'
- let l:loclist = reverse(copy(l:loclist))
+ if a:direction is# 'before'
+ call reverse(l:loclist)
endif
" Look for items before or after the current position.
@@ -30,17 +31,21 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort
" cursor to a line without changing the column, in some cases.
let l:cmp_value = ale#util#LocItemCompare(
\ {
+ \ 'bufnr': l:buffer,
\ 'lnum': l:item.lnum,
- \ 'col': min([max([l:item.col, 1]), len(getline(l:item.lnum))]),
+ \ 'col': min([
+ \ max([l:item.col, 1]),
+ \ max([len(getline(l:item.lnum)), 1]),
+ \ ]),
\ },
\ l:search_item
\)
- if a:direction ==# 'before' && l:cmp_value < 0
+ if a:direction is# 'before' && l:cmp_value < 0
return [l:item.lnum, l:item.col]
endif
- if a:direction ==# 'after' && l:cmp_value > 0
+ if a:direction is# 'after' && l:cmp_value > 0
return [l:item.lnum, l:item.col]
endif
endfor
@@ -66,13 +71,16 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort
endfunction
function! ale#loclist_jumping#JumpToIndex(index) abort
- let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
- let l:loclist = l:info.loclist
+ let l:buffer = bufnr('')
+ let l:info = get(g:ale_buffer_info, l:buffer, {'loclist': []})
+ let l:loclist = filter(copy(l:info.loclist), 'v:val.bufnr == l:buffer')
+
if empty(l:loclist)
return
endif
let l:item = l:loclist[a:index]
+
if !empty(l:item)
call cursor([l:item.lnum, l:item.col])
endif
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index bb9a24f2..b5525c98 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -190,17 +190,17 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
return
endif
- if get(a:response, 'method', '') ==# ''
+ if get(a:response, 'method', '') is# ''
if has_key(get(a:response, 'result', {}), 'capabilities')
for [l:dir, l:project] in l:uninitialized_projects
call s:MarkProjectAsInitialized(a:conn, l:project)
endfor
endif
- elseif get(a:response, 'method', '') ==# 'textDocument/publishDiagnostics'
+ elseif get(a:response, 'method', '') is# 'textDocument/publishDiagnostics'
let l:filename = ale#path#FromURI(a:response.params.uri)
for [l:dir, l:project] in l:uninitialized_projects
- if l:filename[:len(l:dir) - 1] ==# l:dir
+ if l:filename[:len(l:dir) - 1] is# l:dir
call s:MarkProjectAsInitialized(a:conn, l:project)
endif
endfor
@@ -215,7 +215,7 @@ function! ale#lsp#HandleMessage(conn, message) abort
" Call our callbacks.
for l:response in l:response_list
- if get(l:response, 'method', '') ==# 'initialize'
+ if get(l:response, 'method', '') is# 'initialize'
call s:HandleInitializeResponse(a:conn, l:response)
else
call ale#lsp#HandleOtherInitializeResponses(a:conn, l:response)
@@ -296,7 +296,7 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
" Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection()
- if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) !=# 'open'
+ if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) isnot# 'open'
let l:conn.channnel = ch_open(a:address, {
\ 'mode': 'raw',
\ 'waittime': 0,
@@ -304,7 +304,7 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
\})
endif
- if ch_status(l:conn.channnel) ==# 'fail'
+ if ch_status(l:conn.channnel) is# 'fail'
return 0
endif
@@ -319,7 +319,7 @@ endfunction
function! s:SendMessageData(conn, data) abort
if has_key(a:conn, 'executable')
call ale#job#SendRaw(a:conn.id, a:data)
- elseif has_key(a:conn, 'channel') && ch_status(a:conn.channnel) ==# 'open'
+ elseif has_key(a:conn, 'channel') && ch_status(a:conn.channnel) is# 'open'
" Send the message to the server
call ch_sendraw(a:conn.channel, a:data)
else
diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim
index c68114ae..bc026cc2 100644
--- a/autoload/ale/path.vim
+++ b/autoload/ale/path.vim
@@ -65,7 +65,7 @@ endfunction
" Return 1 if a path is an absolute path.
function! ale#path#IsAbsolute(filename) abort
" Check for /foo and C:\foo, etc.
- return a:filename[:0] ==# '/' || a:filename[1:2] ==# ':\'
+ return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
endfunction
" Given a filename, return 1 if the file represents some temporary file
@@ -78,7 +78,7 @@ function! ale#path#IsTempName(filename) abort
\]
for l:prefix in l:prefix_list
- if a:filename[:len(l:prefix) - 1] ==# l:prefix
+ if a:filename[:len(l:prefix) - 1] is# l:prefix
return 1
endif
endfor
@@ -86,23 +86,36 @@ function! ale#path#IsTempName(filename) abort
return 0
endfunction
+" Given a base directory, which must not have a trailing slash, and a
+" filename, which may have an absolute path a path relative to the base
+" 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
+ endif
+
+ let l:sep = has('win32') ? '\' : '/'
+
+ return ale#path#Simplify(a:base_directory . l:sep . a:filename)
+endfunction
+
" Given a buffer number and a relative or absolute path, return 1 if the
" two paths represent the same file on disk.
function! ale#path#IsBufferPath(buffer, complex_filename) abort
" If the path is one of many different names for stdin, we have a match.
- if a:complex_filename ==# '-'
- \|| a:complex_filename ==# 'stdin'
- \|| a:complex_filename[:0] ==# '<'
+ if a:complex_filename is# '-'
+ \|| a:complex_filename is# 'stdin'
+ \|| a:complex_filename[:0] is# '<'
return 1
endif
let l:test_filename = ale#path#Simplify(a:complex_filename)
- if l:test_filename[:1] ==# './'
+ if l:test_filename[:1] is# './'
let l:test_filename = l:test_filename[2:]
endif
- if l:test_filename[:1] ==# '..'
+ if l:test_filename[:1] is# '..'
" Remove ../../ etc. from the front of the path.
let l:test_filename = substitute(l:test_filename, '\v^(\.\.[/\\])+', '/', '')
endif
@@ -114,8 +127,8 @@ function! ale#path#IsBufferPath(buffer, complex_filename) abort
let l:buffer_filename = expand('#' . a:buffer . ':p')
- return l:buffer_filename ==# l:test_filename
- \ || l:buffer_filename[-len(l:test_filename):] ==# l:test_filename
+ return l:buffer_filename is# l:test_filename
+ \ || l:buffer_filename[-len(l:test_filename):] is# l:test_filename
endfunction
" Given a path, return every component of the path, moving upwards.
@@ -133,7 +146,7 @@ function! ale#path#Upwards(path) abort
if ale#Has('win32') && a:path =~# '^[a-zA-z]:\'
" Add \ to C: for C:\, etc.
let l:path_list[-1] .= '\'
- elseif a:path[0] ==# '/'
+ elseif a:path[0] is# '/'
" If the path starts with /, even on Windows, add / and / to all paths.
call map(l:path_list, '''/'' . v:val')
call add(l:path_list, '/')
@@ -146,10 +159,10 @@ endfunction
" relatives paths will not be prefixed with the protocol.
" For Windows paths, the `:` in C:\ etc. will not be percent-encoded.
function! ale#path#ToURI(path) abort
- let l:has_drive_letter = a:path[1:2] ==# ':\'
+ let l:has_drive_letter = a:path[1:2] is# ':\'
return substitute(
- \ ((l:has_drive_letter || a:path[:0] ==# '/') ? 'file://' : '')
+ \ ((l:has_drive_letter || a:path[:0] is# '/') ? 'file://' : '')
\ . (l:has_drive_letter ? '/' . a:path[:2] : '')
\ . ale#uri#Encode(l:has_drive_letter ? a:path[3:] : a:path),
\ '\\',
@@ -160,7 +173,7 @@ endfunction
function! ale#path#FromURI(uri) abort
let l:i = len('file://')
- let l:encoded_path = a:uri[: l:i - 1] ==# 'file://' ? a:uri[l:i :] : a:uri
+ let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri
return ale#uri#Decode(l:encoded_path)
endfunction
diff --git a/autoload/ale/pattern_options.vim b/autoload/ale/pattern_options.vim
index 03c9146a..a603c980 100644
--- a/autoload/ale/pattern_options.vim
+++ b/autoload/ale/pattern_options.vim
@@ -13,7 +13,7 @@ function! ale#pattern_options#SetOptions() abort
endfor
for l:key in keys(l:options)
- if l:key[:0] ==# '&'
+ if l:key[:0] is# '&'
call setbufvar(bufnr(''), l:key, l:options[l:key])
else
let b:[l:key] = l:options[l:key]
diff --git a/autoload/ale/ruby.vim b/autoload/ale/ruby.vim
index 503ff1db..b981ded6 100644
--- a/autoload/ale/ruby.vim
+++ b/autoload/ale/ruby.vim
@@ -10,7 +10,7 @@ function! ale#ruby#FindRailsRoot(buffer) abort
\ ':h:h'
\)
- if l:dir !=# '.'
+ if l:dir isnot# '.'
\&& isdirectory(l:dir . '/app')
\&& isdirectory(l:dir . '/config')
\&& isdirectory(l:dir . '/db')
diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim
index 37105f9f..7ba83647 100644
--- a/autoload/ale/sign.vim
+++ b/autoload/ale/sign.vim
@@ -2,8 +2,6 @@ scriptencoding utf8
" Author: w0rp <devw0rp@gmail.com>
" Description: Draws error and warning signs into signcolumn
-let b:dummy_sign_set_map = {}
-
if !hlexists('ALEErrorSign')
highlight link ALEErrorSign error
endif
@@ -40,7 +38,7 @@ if !hlexists('ALESignColumnWithoutErrors')
if !empty(l:match)
execute 'highlight link ALESignColumnWithoutErrors ' . l:match[1]
- elseif l:highlight_syntax !=# 'cleared'
+ elseif l:highlight_syntax isnot# 'cleared'
execute 'highlight ALESignColumnWithoutErrors ' . l:highlight_syntax
endif
endfunction
@@ -62,6 +60,62 @@ execute 'sign define ALEInfoSign text=' . g:ale_sign_info
\ . ' texthl=ALEInfoSign linehl=ALEInfoLine'
sign define ALEDummySign
+let s:error_priority = 1
+let s:warning_priority = 2
+let s:info_priority = 3
+let s:style_error_priority = 4
+let s:style_warning_priority = 5
+
+function! ale#sign#GetSignName(sublist) abort
+ let l:priority = s:style_warning_priority
+
+ " Determine the highest priority item for the line.
+ for l:item in a:sublist
+ if l:item.type is# 'I'
+ let l:item_priority = s:info_priority
+ elseif l:item.type is# 'W'
+ if get(l:item, 'sub_type', '') is# 'style'
+ let l:item_priority = s:style_warning_priority
+ else
+ let l:item_priority = s:warning_priority
+ endif
+ else
+ if get(l:item, 'sub_type', '') is# 'style'
+ let l:item_priority = s:style_error_priority
+ else
+ let l:item_priority = s:error_priority
+ endif
+ endif
+
+ if l:item_priority < l:priority
+ let l:priority = l:item_priority
+ endif
+ endfor
+
+ if l:priority is# s:error_priority
+ return 'ALEErrorSign'
+ endif
+
+ if l:priority is# s:warning_priority
+ return 'ALEWarningSign'
+ endif
+
+ if l:priority is# s:style_error_priority
+ return 'ALEStyleErrorSign'
+ endif
+
+ if l:priority is# s:style_warning_priority
+ return 'ALEStyleWarningSign'
+ endif
+
+ if l:priority is# s:info_priority
+ return 'ALEInfoSign'
+ endif
+
+ " Use the error sign for invalid severities.
+ return 'ALEErrorSign'
+endfunction
+
" Read sign data for a buffer to a list of lines.
function! ale#sign#ReadSigns(buffer) abort
redir => l:output
@@ -71,7 +125,7 @@ function! ale#sign#ReadSigns(buffer) abort
return split(l:output, "\n")
endfunction
-" Given a list of lines for sign output, return a List of pairs [line, id]
+" Given a list of lines for sign output, return a List of [line, id, group]
function! ale#sign#ParseSigns(line_list) abort
" Matches output like :
" line=4 id=1 name=ALEErrorSign
@@ -81,20 +135,25 @@ function! ale#sign#ParseSigns(line_list) abort
" riga=1 id=1000001, nome=ALEWarningSign
let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=(ALE[a-zA-Z]+Sign)'
let l:result = []
+ let l:is_dummy_sign_set = 0
for l:line in a:line_list
let l:match = matchlist(l:line, l:pattern)
if len(l:match) > 0
- call add(l:result, [
- \ str2nr(l:match[1]),
- \ str2nr(l:match[2]),
- \ l:match[3],
- \])
+ if l:match[3] is# 'ALEDummySign'
+ let l:is_dummy_sign_set = 1
+ else
+ call add(l:result, [
+ \ str2nr(l:match[1]),
+ \ str2nr(l:match[2]),
+ \ l:match[3],
+ \])
+ endif
endif
endfor
- return l:result
+ return [l:is_dummy_sign_set, l:result]
endfunction
function! ale#sign#FindCurrentSigns(buffer) abort
@@ -104,11 +163,15 @@ function! ale#sign#FindCurrentSigns(buffer) abort
endfunction
" Given a loclist, group the List into with one List per line.
-function! s:GroupLoclistItems(loclist) abort
+function! s:GroupLoclistItems(buffer, loclist) abort
let l:grouped_items = []
let l:last_lnum = -1
for l:obj in a:loclist
+ if l:obj.bufnr != a:buffer
+ continue
+ endif
+
" Create a new sub-List when we hit a new line.
if l:obj.lnum != l:last_lnum
call add(l:grouped_items, [])
@@ -118,148 +181,143 @@ function! s:GroupLoclistItems(loclist) abort
let l:last_lnum = l:obj.lnum
endfor
- " Now we've gathered the items in groups, filter the groups down to
- " the groups containing at least one new item.
- let l:new_grouped_items = []
+ return l:grouped_items
+endfunction
- for l:group in l:grouped_items
- for l:obj in l:group
- if !has_key(l:obj, 'sign_id')
- call add(l:new_grouped_items, l:group)
- break
- endif
- endfor
- endfor
+function! ale#sign#SetSignColumnHighlight(has_problems) abort
+ highlight clear SignColumn
- return l:new_grouped_items
+ if a:has_problems
+ highlight link SignColumn ALESignColumnWithErrors
+ else
+ highlight link SignColumn ALESignColumnWithoutErrors
+ endif
endfunction
-function! s:IsDummySignSet(current_id_list) abort
- for [l:line, l:id, l:name] in a:current_id_list
- if l:id == g:ale_sign_offset
- return 1
- endif
+function! s:UpdateLineNumbers(buffer, current_sign_list, loclist) abort
+ let l:line_map = {}
+ let l:line_numbers_changed = 0
- if l:line > 1
- return 0
- endif
+ for [l:line, l:sign_id, l:name] in a:current_sign_list
+ let l:line_map[l:sign_id] = l:line
endfor
- return 0
-endfunction
-
-function! s:SetDummySignIfNeeded(buffer, current_sign_list, new_signs) abort
- let l:is_dummy_sign_set = s:IsDummySignSet(a:current_sign_list)
+ for l:item in a:loclist
+ if l:item.bufnr == a:buffer
+ let l:lnum = get(l:line_map, get(l:item, 'sign_id', 0), 0)
- " If we haven't already set a dummy sign, and we have some previous signs
- " or always want a dummy sign, then set one, to keep the sign column open.
- if !l:is_dummy_sign_set && (a:new_signs || g:ale_sign_column_always)
- silent! execute 'sign place ' . g:ale_sign_offset
- \ . ' line=1 name=ALEDummySign buffer='
- \ . a:buffer
+ if l:lnum && l:item.lnum != l:lnum
+ let l:item.lnum = l:lnum
+ let l:line_numbers_changed = 1
+ endif
+ endif
+ endfor
- let l:is_dummy_sign_set = 1
+ " When the line numbers change, sort the list again
+ if l:line_numbers_changed
+ call sort(a:loclist, 'ale#util#LocItemCompare')
endif
-
- return l:is_dummy_sign_set
endfunction
-function! ale#sign#GetSignType(sublist) abort
- let l:highest_level = 100
+function! s:BuildSignMap(current_sign_list, grouped_items) abort
+ let l:sign_map = {}
+ let l:sign_offset = g:ale_sign_offset
- for l:item in a:sublist
- let l:level = (l:item.type ==# 'I' ? 2 : l:item.type ==# 'W')
-
- if get(l:item, 'sub_type', '') ==# 'style'
- let l:level += 10
+ for [l:line, l:sign_id, l:name] in a:current_sign_list
+ let l:sign_map[l:line] = {
+ \ 'current_id': l:sign_id,
+ \ 'current_name': l:name,
+ \ 'new_id': 0,
+ \ 'new_name': '',
+ \ 'items': [],
+ \}
+
+ if l:sign_id > l:sign_offset
+ let l:sign_offset = l:sign_id
endif
+ endfor
- if l:level < l:highest_level
- let l:highest_level = l:level
+ for l:group in a:grouped_items
+ let l:line = l:group[0].lnum
+ let l:sign_info = get(l:sign_map, l:line, {
+ \ 'current_id': 0,
+ \ 'current_name': '',
+ \ 'new_id': 0,
+ \ 'new_name': '',
+ \ 'items': [],
+ \})
+
+ let l:sign_info.new_name = ale#sign#GetSignName(l:group)
+ let l:sign_info.items = l:group
+
+ if l:sign_info.current_name isnot# l:sign_info.new_name
+ let l:sign_info.new_id = l:sign_offset + 1
+ let l:sign_offset += 1
+ else
+ let l:sign_info.new_id = l:sign_info.current_id
endif
- endfor
- if l:highest_level == 10
- return 'ALEStyleErrorSign'
- elseif l:highest_level == 11
- return 'ALEStyleWarningSign'
- elseif l:highest_level == 2
- return 'ALEInfoSign'
- elseif l:highest_level == 1
- return 'ALEWarningSign'
- endif
+ let l:sign_map[l:line] = l:sign_info
+ endfor
- return 'ALEErrorSign'
+ return l:sign_map
endfunction
-function! ale#sign#SetSignColumnHighlight(has_problems) abort
- highlight clear SignColumn
-
- if a:has_problems
- highlight link SignColumn ALESignColumnWithErrors
- else
- highlight link SignColumn ALESignColumnWithoutErrors
- endif
-endfunction
+function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
+ let l:command_list = []
+ let l:is_dummy_sign_set = a:was_sign_set
-function! s:PlaceNewSigns(buffer, grouped_items, current_sign_offset) abort
- if g:ale_change_sign_column_color
- call ale#sign#SetSignColumnHighlight(!empty(a:grouped_items))
+ " Set the dummy sign if we need to.
+ " The dummy sign is needed to keep the sign column open while we add
+ " and remove signs.
+ if !l:is_dummy_sign_set && (!empty(a:sign_map) || g:ale_sign_column_always)
+ call add(l:command_list, 'sign place '
+ \ . g:ale_sign_offset
+ \ . ' line=1 name=ALEDummySign buffer='
+ \ . a:buffer
+ \)
+ let l:is_dummy_sign_set = 1
endif
- let l:offset = a:current_sign_offset > 0
- \ ? a:current_sign_offset
- \ : g:ale_sign_offset
-
- " Add the new signs,
- for l:index in range(0, len(a:grouped_items) - 1)
- let l:sign_id = l:offset + l:index + 1
- let l:sublist = a:grouped_items[l:index]
- let l:type = ale#sign#GetSignType(a:grouped_items[l:index])
-
- " Save the sign IDs we are setting back on our loclist objects.
- " These IDs will be used to preserve items which are set many times.
- for l:obj in l:sublist
- let l:obj.sign_id = l:sign_id
- endfor
-
- silent! execute 'sign place ' . l:sign_id
- \ . ' line=' . l:sublist[0].lnum
- \ . ' name=' . l:type
- \ . ' buffer=' . a:buffer
- endfor
-endfunction
-
-" Get items grouped by any current sign IDs they might have.
-function! s:GetItemsWithSignIDs(loclist) abort
- let l:items_by_sign_id = {}
-
- for l:item in a:loclist
- if has_key(l:item, 'sign_id')
- if !has_key(l:items_by_sign_id, l:item.sign_id)
- let l:items_by_sign_id[l:item.sign_id] = []
+ " Place new items first.
+ for [l:line_str, l:info] in items(a:sign_map)
+ if l:info.new_id
+ " Save the sign IDs we are setting back on our loclist objects.
+ " These IDs will be used to preserve items which are set many times.
+ for l:item in l:info.items
+ let l:item.sign_id = l:info.new_id
+ endfor
+
+ if l:info.new_id isnot l:info.current_id
+ call add(l:command_list, 'sign place '
+ \ . (l:info.new_id)
+ \ . ' line=' . l:line_str
+ \ . ' name=' . (l:info.new_name)
+ \ . ' buffer=' . a:buffer
+ \)
endif
-
- call add(l:items_by_sign_id[l:item.sign_id], l:item)
endif
endfor
- return l:items_by_sign_id
-endfunction
+ " Remove signs without new IDs.
+ for l:info in values(a:sign_map)
+ if l:info.current_id && l:info.current_id isnot l:info.new_id
+ call add(l:command_list, 'sign unplace '
+ \ . (l:info.current_id)
+ \ . ' buffer=' . a:buffer
+ \)
+ endif
+ endfor
-" Given some current signs and a loclist, look for items with sign IDs,
-" and change the line numbers for loclist items to match the signs.
-function! s:UpdateLineNumbers(current_sign_list, items_by_sign_id) abort
- " Do nothing if there's nothing to work with.
- if empty(a:items_by_sign_id)
- return
+ " Remove the dummy sign to close the sign column if we need to.
+ if l:is_dummy_sign_set && !g:ale_sign_column_always
+ call add(l:command_list, 'sign unplace '
+ \ . g:ale_sign_offset
+ \ . ' buffer=' . a:buffer
+ \)
endif
- for [l:line, l:sign_id, l:name] in a:current_sign_list
- for l:obj in get(a:items_by_sign_id, l:sign_id, [])
- let l:obj.lnum = l:line
- endfor
- endfor
+ return l:command_list
endfunction
" This function will set the signs which show up on the left.
@@ -271,42 +329,25 @@ function! ale#sign#SetSigns(buffer, loclist) abort
endif
" Find the current markers
- let l:current_sign_list = ale#sign#FindCurrentSigns(a:buffer)
- " Get a mapping from sign IDs to current loclist items which have them.
- let l:items_by_sign_id = s:GetItemsWithSignIDs(a:loclist)
+ let [l:is_dummy_sign_set, l:current_sign_list] =
+ \ ale#sign#FindCurrentSigns(a:buffer)
- " Use sign information to update the line numbers for the loclist items.
- call s:UpdateLineNumbers(l:current_sign_list, l:items_by_sign_id)
- " Sort items again, as the line numbers could have changed.
- call sort(a:loclist, 'ale#util#LocItemCompare')
+ " Update the line numbers for items from before which may have moved.
+ call s:UpdateLineNumbers(a:buffer, l:current_sign_list, a:loclist)
- let l:grouped_items = s:GroupLoclistItems(a:loclist)
+ " Group items after updating the line numbers.
+ let l:grouped_items = s:GroupLoclistItems(a:buffer, a:loclist)
- " Set the dummy sign if we need to.
- " This keeps the sign gutter open while we remove things, etc.
- let l:is_dummy_sign_set = s:SetDummySignIfNeeded(
+ " Build a map of current and new signs, with the lines as the keys.
+ let l:sign_map = s:BuildSignMap(l:current_sign_list, l:grouped_items)
+
+ let l:command_list = ale#sign#GetSignCommands(
\ a:buffer,
- \ l:current_sign_list,
- \ !empty(l:grouped_items),
+ \ l:is_dummy_sign_set,
+ \ l:sign_map,
\)
- " Now remove the previous signs. The dummy will hold the column open
- " while we add the new signs, if we had signs before.
- for [l:line, l:sign_id, l:name] in l:current_sign_list
- if l:sign_id != g:ale_sign_offset
- \&& !has_key(l:items_by_sign_id, l:sign_id)
- execute 'sign unplace ' . l:sign_id . ' buffer=' . a:buffer
- endif
+ for l:command in l:command_list
+ silent! execute l:command
endfor
-
- " Compute a sign ID offset so we don't hit the same sign IDs again.
- let l:current_sign_offset = max(map(keys(l:items_by_sign_id), 'str2nr(v:val)'))
-
- call s:PlaceNewSigns(a:buffer, l:grouped_items, l:current_sign_offset)
-endfunction
-
-function! ale#sign#RemoveDummySignIfNeeded(buffer) abort
- if !g:ale_sign_column_always
- execute 'sign unplace ' . g:ale_sign_offset . ' buffer=' . a:buffer
- endif
endfunction
diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim
index afc27b3b..a073f7a0 100644
--- a/autoload/ale/statusline.vim
+++ b/autoload/ale/statusline.vim
@@ -22,19 +22,20 @@ function! ale#statusline#Update(buffer, loclist) abort
return
endif
+ let l:loclist = filter(copy(a:loclist), 'v:val.bufnr == a:buffer')
let l:count = s:CreateCountDict()
- let l:count.total = len(a:loclist)
+ let l:count.total = len(l:loclist)
- for l:entry in a:loclist
- if l:entry.type ==# 'W'
- if get(l:entry, 'sub_type', '') ==# 'style'
+ for l:entry in l:loclist
+ if l:entry.type is# 'W'
+ if get(l:entry, 'sub_type', '') is# 'style'
let l:count.style_warning += 1
else
let l:count.warning += 1
endif
- elseif l:entry.type ==# 'I'
+ elseif l:entry.type is# 'I'
let l:count.info += 1
- elseif get(l:entry, 'sub_type', '') ==# 'style'
+ elseif get(l:entry, 'sub_type', '') is# 'style'
let l:count.style_error += 1
else
let l:count.error += 1
diff --git a/autoload/ale/test.vim b/autoload/ale/test.vim
index 204b7115..c0458053 100644
--- a/autoload/ale/test.vim
+++ b/autoload/ale/test.vim
@@ -12,7 +12,7 @@
"
" This function should be run in a Vader Before: block.
function! ale#test#SetDirectory(docker_path) abort
- if a:docker_path[:len('/testplugin/') - 1] !=# '/testplugin/'
+ if a:docker_path[:len('/testplugin/') - 1] isnot# '/testplugin/'
throw 'docker_path must start with /testplugin/!'
endif
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index f3146151..cf8d5bec 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -1,11 +1,23 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Contains miscellaneous functions
-" A null file for sending output to nothing.
-let g:ale#util#nul_file = '/dev/null'
+" A wrapper function for mode() so we can test calls for it.
+function! ale#util#Mode(...) abort
+ return call('mode', a:000)
+endfunction
+
+" A wrapper function for feedkeys so we can test calls for it.
+function! ale#util#FeedKeys(...) abort
+ return call('feedkeys', a:000)
+endfunction
-if has('win32')
- let g:ale#util#nul_file = 'nul'
+if !exists('g:ale#util#nul_file')
+ " A null file for sending output to nothing.
+ let g:ale#util#nul_file = '/dev/null'
+
+ if has('win32')
+ let g:ale#util#nul_file = 'nul'
+ endif
endif
" Return the number of lines for a given buffer.
@@ -21,57 +33,113 @@ function! ale#util#GetFunction(string_or_ref) abort
return a:string_or_ref
endfunction
+" Compare two loclist items for ALE, sorted by their buffers, filenames, and
+" line numbers and column numbers.
function! ale#util#LocItemCompare(left, right) abort
- if a:left['lnum'] < a:right['lnum']
+ if a:left.bufnr < a:right.bufnr
+ return -1
+ endif
+
+ if a:left.bufnr > a:right.bufnr
+ return 1
+ endif
+
+ if a:left.bufnr == -1
+ if a:left.filename < a:right.filename
+ return -1
+ endif
+
+ if a:left.filename > a:right.filename
+ return 1
+ endif
+ endif
+
+ if a:left.lnum < a:right.lnum
return -1
endif
- if a:left['lnum'] > a:right['lnum']
+ if a:left.lnum > a:right.lnum
return 1
endif
- if a:left['col'] < a:right['col']
+ if a:left.col < a:right.col
return -1
endif
- if a:left['col'] > a:right['col']
+ if a:left.col > a:right.col
return 1
endif
return 0
endfunction
-" This function will perform a binary search to find a message from the
-" loclist to echo when the cursor moves.
-function! ale#util#BinarySearch(loclist, line, column) abort
+" Compare two loclist items, including the text for the items.
+"
+" This function can be used for de-duplicating lists.
+function! ale#util#LocItemCompareWithText(left, right) abort
+ let l:cmp_value = ale#util#LocItemCompare(a:left, a:right)
+
+ if l:cmp_value
+ return l:cmp_value
+ endif
+
+ if a:left.text < a:right.text
+ return -1
+ endif
+
+ if a:left.text > a:right.text
+ return 1
+ endif
+
+ return 0
+endfunction
+
+" This function will perform a binary search and a small sequential search
+" on the list to find the last problem in the buffer and line which is
+" on or before the column. The index of the problem will be returned.
+"
+" -1 will be returned if nothing can be found.
+function! ale#util#BinarySearch(loclist, buffer, line, column) abort
let l:min = 0
let l:max = len(a:loclist) - 1
- let l:last_column_match = -1
while 1
if l:max < l:min
- return l:last_column_match
+ return -1
endif
let l:mid = (l:min + l:max) / 2
- let l:obj = a:loclist[l:mid]
+ let l:item = a:loclist[l:mid]
- " Binary search to get on the same line
- if a:loclist[l:mid]['lnum'] < a:line
+ " Binary search for equal buffers, equal lines, then near columns.
+ if l:item.bufnr < a:buffer
let l:min = l:mid + 1
- elseif a:loclist[l:mid]['lnum'] > a:line
+ elseif l:item.bufnr > a:buffer
+ let l:max = l:mid - 1
+ elseif l:item.lnum < a:line
+ let l:min = l:mid + 1
+ elseif l:item.lnum > a:line
let l:max = l:mid - 1
else
- let l:last_column_match = l:mid
-
- " Binary search to get the same column, or near it
- if a:loclist[l:mid]['col'] < a:column
- let l:min = l:mid + 1
- elseif a:loclist[l:mid]['col'] > a:column
- let l:max = l:mid - 1
- else
- return l:mid
- endif
+ " This part is a small sequential search.
+ let l:index = l:mid
+
+ " Search backwards to find the first problem on the line.
+ while l:index > 0
+ \&& a:loclist[l:index - 1].bufnr == a:buffer
+ \&& a:loclist[l:index - 1].lnum == a:line
+ let l:index -= 1
+ endwhile
+
+ " Find the last problem on or before this column.
+ while l:index < l:max
+ \&& a:loclist[l:index + 1].bufnr == a:buffer
+ \&& a:loclist[l:index + 1].lnum == a:line
+ \&& a:loclist[l:index + 1].col <= a:column
+ let l:index += 1
+ endwhile
+
+ return l:index
endif
endwhile
endfunction
@@ -136,7 +204,7 @@ function! s:LoadArgCount(function) abort
endif
let l:match = matchstr(split(l:output, "\n")[0], '\v\([^)]+\)')[1:-2]
- let l:arg_list = filter(split(l:match, ', '), 'v:val !=# ''...''')
+ let l:arg_list = filter(split(l:match, ', '), 'v:val isnot# ''...''')
return len(l:arg_list)
endfunction
@@ -187,3 +255,45 @@ function! ale#util#FuzzyJSONDecode(data, default) abort
return a:default
endtry
endfunction
+
+" Write a file, including carriage return characters for DOS files.
+"
+" The buffer number is required for determining the fileformat setting for
+" the buffer.
+function! ale#util#Writefile(buffer, lines, filename) abort
+ let l:corrected_lines = getbufvar(a:buffer, '&fileformat') is# 'dos'
+ \ ? map(copy(a:lines), 'v:val . "\r"')
+ \ : a:lines
+
+ call writefile(l:corrected_lines, a:filename) " no-custom-checks
+endfunction
+
+if !exists('s:patial_timers')
+ let s:partial_timers = {}
+endif
+
+function! s:ApplyPartialTimer(timer_id) abort
+ let [l:Callback, l:args] = remove(s:partial_timers, a:timer_id)
+ call call(l:Callback, [a:timer_id] + l:args)
+endfunction
+
+" Given a delay, a callback, a List of arguments, start a timer with
+" timer_start() and call the callback provided with [timer_id] + args.
+"
+" The timer must not be stopped with timer_stop().
+" Use ale#util#StopPartialTimer() instead, which can stop any timer, and will
+" clear any arguments saved for executing callbacks later.
+function! ale#util#StartPartialTimer(delay, callback, args) abort
+ let l:timer_id = timer_start(a:delay, function('s:ApplyPartialTimer'))
+ let s:partial_timers[l:timer_id] = [a:callback, a:args]
+
+ return l:timer_id
+endfunction
+
+function! ale#util#StopPartialTimer(timer_id) abort
+ call timer_stop(a:timer_id)
+
+ if has_key(s:partial_timers, a:timer_id)
+ call remove(s:partial_timers, a:timer_id)
+ endif
+endfunction
diff --git a/check-supported-tools-tables b/check-supported-tools-tables
new file mode 100755
index 00000000..842d431b
--- /dev/null
+++ b/check-supported-tools-tables
@@ -0,0 +1,68 @@
+#!/bin/bash -eu
+
+# This script compares the table of supported tools in both the README file
+# and the doc/ale.txt file, so we can complain if they don't match up.
+
+# Find the start and end lines for the help section.
+ale_help_start_line="$( \
+ grep -m1 -n '^[0-9][0-9]*\. *Supported Languages' doc/ale.txt \
+ | sed 's/\([0-9]*\).*/\1/' \
+)"
+ale_help_section_size="$( \
+ tail -n +"$ale_help_start_line" doc/ale.txt \
+ | grep -m1 -n '================' \
+ | sed 's/\([0-9]*\).*/\1/' \
+)"
+# -- shellcheck complains about expr, but it works better.
+# shellcheck disable=SC2003
+ale_help_end_line="$(expr "$ale_help_start_line" + "$ale_help_section_size")"
+
+# Find the start and end lines for the same section in the README.
+readme_start_line="$( \
+ grep -m1 -n '^.*[0-9][0-9]*\. *Supported Languages' README.md \
+ | sed 's/\([0-9]*\).*/\1/' \
+)"
+readme_section_size="$( \
+ tail -n +"$readme_start_line" README.md \
+ | grep -m1 -n '^##.*Usage' \
+ | sed 's/\([0-9]*\).*/\1/' \
+)"
+# shellcheck disable=SC2003
+readme_end_line="$(expr "$readme_start_line" + "$readme_section_size")"
+
+doc_file="$(mktemp)"
+readme_file="$(mktemp)"
+
+sed -n "$ale_help_start_line,$ale_help_end_line"p doc/ale.txt \
+ | grep '\* .*: ' \
+ | sed 's/^*//' \
+ | sed 's/[`!^]\|([^)]*)//g' \
+ | sed 's/ *\([,:]\)/\1/g' \
+ | sed 's/ */ /g' \
+ | sed 's/^ *\| *$//g' \
+ | sed 's/^/ /' \
+ > "$doc_file"
+
+sed -n "$readme_start_line,$readme_end_line"p README.md \
+ | grep '| .* |' \
+ | sed '/^| Language\|^| ---/d' \
+ | sed 's/^|//' \
+ | sed 's/ \?|/:/' \
+ | sed 's/[`!^|]\|([^)]*)//g' \
+ | sed 's/\[\|\]//g' \
+ | sed 's/see.*\(,\|$\)/\1/g' \
+ | sed 's/ *\([,:]\)/\1/g' \
+ | sed 's/ */ /g' \
+ | sed 's/^ *\| *$//g' \
+ | sed 's/^/ /' \
+ | sed 's/ *-n flag//g' \
+ > "$readme_file"
+
+exit_code=0
+
+diff -U0 "$readme_file" "$doc_file" || exit_code=$?
+
+rm "$doc_file"
+rm "$readme_file"
+
+exit "$exit_code"
diff --git a/custom-checks b/custom-checks
index 3bb60cda..aad16c9c 100755
--- a/custom-checks
+++ b/custom-checks
@@ -65,6 +65,10 @@ 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
@@ -80,5 +84,9 @@ check_errors 'let g:ale_\w\+_\w\+_args =' 'Name your option g:ale_<filetype>_<li
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 '==#' "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"
exit $RETURN_CODE
diff --git a/doc/ale-awk.txt b/doc/ale-awk.txt
index d3f23ac7..b9c5c34e 100644
--- a/doc/ale-awk.txt
+++ b/doc/ale-awk.txt
@@ -1,20 +1,20 @@
===============================================================================
-ALE Awk Integration *ale-awk-options*
+ALE Awk Integration *ale-awk-options*
===============================================================================
-gawk *ale-awk-gawk*
+gawk *ale-awk-gawk*
-g:ale_awk_gawk_executable *g:ale_awk_gawk_executable*
- *b:ale_awk_gawk_executable*
+g:ale_awk_gawk_executable *g:ale_awk_gawk_executable*
+ *b:ale_awk_gawk_executable*
Type: |String|
Default: `'gawk'`
This variable sets executable used for gawk.
-g:ale_awk_gawk_options *g:ale_awk_gawk_options*
- *b:ale_awk_gawk_options*
+g:ale_awk_gawk_options *g:ale_awk_gawk_options*
+ *b:ale_awk_gawk_options*
Type: |String|
Default: `''`
diff --git a/doc/ale-c.txt b/doc/ale-c.txt
index 29208bc8..0c4f8dc9 100644
--- a/doc/ale-c.txt
+++ b/doc/ale-c.txt
@@ -144,4 +144,23 @@ g:ale_c_gcc_options *g:ale_c_gcc_options*
===============================================================================
+clang-format *ale-c-clangformat*
+
+g:ale_c_clangformat_executable *g:ale_c_clangformat_executable*
+ *b:ale_c_clangformat_executable*
+ Type: |String|
+ Default: `'clang-format'`
+
+ This variable can be changed to use a different executable for clang-format.
+
+
+g:ale_c_clangformat_options *g:ale_c_clangformat_options*
+ *b:ale_c_clangformat_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be change to modify flags given to clang-format.
+
+
+===============================================================================
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 854e9b4e..685bb8dc 100644
--- a/doc/ale-cpp.txt
+++ b/doc/ale-cpp.txt
@@ -5,33 +5,9 @@ ALE C++ Integration *ale-cpp-options*
===============================================================================
Global Options
-g:ale_c_build_dir_names *g:ale_c_build_dir_names*
- *b:ale_c_build_dir_names*
+The |g:ale_c_build_dir_names| and |g:ale_c_build_dir| also apply to some C++
+linters too.
- Type: |List|
- Default: `['build', 'bin']`
-
- A list of directory names to be used when searching upwards from cpp
- files to discover compilation databases with. For directory named `'foo'`,
- ALE will search for `'foo/compile_commands.json'` in all directories on and above
- the directory containing the cpp file to find path to compilation database.
- This feature is useful for the clang tools wrapped around LibTooling (namely
- here, clang-tidy)
-
-
-g:ale_c_build_dir *g:ale_c_build_dir*
- *b:ale_c_build_dir*
-
- Type: |String|
- Default: `''`
-
- A path to the directory containing the `compile_commands.json` file to use
- with c-family linters. Usually setting this option to a non-empty string
- will override the |g:ale_c_build_dir_names| option to impose a compilation
- database (it can be useful if multiple builds are in multiple build
- subdirectories in the project tree).
- This feature is also most useful for the clang tools linters, wrapped
- aroung LibTooling (namely clang-tidy here)
===============================================================================
clang *ale-cpp-clang*
@@ -190,4 +166,11 @@ g:ale_cpp_gcc_options *g:ale_cpp_gcc_options*
===============================================================================
+clang-format *ale-cpp-clangformat*
+
+See |ale-c-clangformat| for information about the available options.
+Note that the C options are also used for C++.
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-css.txt b/doc/ale-css.txt
index effa52ad..b1ab8eb8 100644
--- a/doc/ale-css.txt
+++ b/doc/ale-css.txt
@@ -13,8 +13,8 @@ g:ale_css_stylelint_executable *g:ale_css_stylelint_executable*
See |ale-integrations-local-executables|
-g:ale_css_stylelint_options *g:ale_css_stylelint_options*
- *b:ale_css_stylelint_options*
+g:ale_css_stylelint_options *g:ale_css_stylelint_options*
+ *b:ale_css_stylelint_options*
Type: |String|
Default: `''`
diff --git a/doc/ale-eruby.txt b/doc/ale-eruby.txt
new file mode 100644
index 00000000..b9cd3cbf
--- /dev/null
+++ b/doc/ale-eruby.txt
@@ -0,0 +1,17 @@
+===============================================================================
+ALE Eruby Integration *ale-eruby-options*
+
+There are two linters for `eruby` files:
+
+- `erubylint`
+- `erubis`
+
+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.)
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-graphql.txt b/doc/ale-graphql.txt
new file mode 100644
index 00000000..5ceb5ca7
--- /dev/null
+++ b/doc/ale-graphql.txt
@@ -0,0 +1,9 @@
+===============================================================================
+ALE GraphQL Integration *ale-graphql-options*
+
+
+===============================================================================
+gqlint *ale-graphql-gqlint*
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-idris.txt b/doc/ale-idris.txt
new file mode 100644
index 00000000..c7500b0d
--- /dev/null
+++ b/doc/ale-idris.txt
@@ -0,0 +1,23 @@
+===============================================================================
+ALE Idris Integration *ale-idris-options*
+
+===============================================================================
+idris *ale-idris-idris*
+
+g:ale_idris_idris_executable *g:ale_idris_idris_executable*
+ *b:ale_idris_idris_executable*
+ Type: |String|
+ Default: `'idris'`
+
+ This variable can be changed to change the path to idris.
+
+
+g:ale_idris_idris_options *g:ale_idris_idris_options*
+ *b:ale_idris_idris_options*
+ Type: |String|
+ Default: `'--total --warnpartial --warnreach --warnipkg'`
+
+ This variable can be changed to modify flags given to idris.
+
+===============================================================================
+ 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 36d606de..13decb41 100644
--- a/doc/ale-java.txt
+++ b/doc/ale-java.txt
@@ -3,10 +3,10 @@ ALE Java Integration *ale-java-options*
===============================================================================
-checkstyle ale-java-checkstyle
+checkstyle *ale-java-checkstyle*
-g:ale_java_checkstyle_options g:ale_java_checkstyle_options
- b:ale_java_checkstyle_options
+g:ale_java_checkstyle_options *g:ale_java_checkstyle_options*
+ *b:ale_java_checkstyle_options*
Type: String
Default: '-c /google_checks.xml'
diff --git a/doc/ale-javascript.txt b/doc/ale-javascript.txt
index 4abc6298..95d2504a 100644
--- a/doc/ale-javascript.txt
+++ b/doc/ale-javascript.txt
@@ -50,6 +50,16 @@ g:ale_javascript_eslint_use_global *g:ale_javascript_eslint_use_global*
See |ale-integrations-local-executables|
+g:ale_javascript_eslint_suppress_eslintignore
+ *g:ale_javascript_eslint_suppress_eslintignore*
+ *b: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`.
+
+
===============================================================================
prettier *ale-javascript-prettier*
@@ -76,6 +86,12 @@ 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*
diff --git a/doc/ale-php.txt b/doc/ale-php.txt
index 4109673a..e2b0de6f 100644
--- a/doc/ale-php.txt
+++ b/doc/ale-php.txt
@@ -3,6 +3,38 @@ ALE PHP Integration *ale-php-options*
===============================================================================
+hack *ale-php-hack*
+
+There are no options for this linter.
+
+
+===============================================================================
+langserver *ale-php-langserver*
+
+g:ale_php_langserver_executable *g:ale_php_langserver_executable*
+ *b:ale_php_langserver_executable*
+ Type: |String|
+ Default: `'php-language-server.php'`
+
+ The variable can be set to configure the executable that will be used for
+ running the PHP language server. `vendor` directory executables will be
+ preferred instead of this setting if |g:ale_php_langserver_use_global| is `0`.
+
+ See: |ale-integrations-local-executables|
+
+
+g:ale_php_langserver_use_global *g:ale_php_langserver_use_global*
+ *b:ale_php_langserver_use_global*
+ Type: |Number|
+ Default: `0`
+
+ This variable can be set to `1` to force the language server to be run with
+ the executable set for |g:ale_php_langserver_executable|.
+
+ See: |ale-integrations-local-executables|
+
+
+===============================================================================
phpcs *ale-php-phpcs*
g:ale_php_phpcs_executable *g:ale_php_phpcs_executable*
@@ -31,7 +63,7 @@ g:ale_php_phpcs_use_global *g:ale_php_phpcs_use_global*
See |ale-integrations-local-executables|
-------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
phpmd *ale-php-phpmd*
g:ale_php_phpmd_ruleset *g:ale_php_phpmd_ruleset*
@@ -43,8 +75,8 @@ g:ale_php_phpmd_ruleset *g:ale_php_phpmd_ruleset*
the available phpmd rulesets
-------------------------------------------------------------------------------
-phpstan *ale-php-stan*
+-------------------------------------------------------------------------------
+phpstan *ale-php-phpstan*
g:ale_php_phpstan_executable *g:ale_php_phpstan_executable*
*b:ale_php_phpstan_executable*
@@ -62,5 +94,35 @@ g:ale_php_phpstan_level *g:ale_php_phpstan_level*
This variable controls the rule levels. 0 is the loosest and 4 is the
strictest.
+
+-------------------------------------------------------------------------------
+phpcbf *ale-php-phpcbf*
+
+g:ale_php_phpcbf_executable *g:ale_php_phpcbf_executable*
+ *b:ale_php_phpcbf_executable*
+ Type: |String|
+ Default: `'phpcbf'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_php_phpcbf_standard *g:ale_php_phpcbf_standard*
+ *b:ale_php_phpcbf_standard*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to specify the coding standard used by phpcbf. If no
+ coding standard is specified, phpcbf will default to fixing against the
+ PEAR coding standard, or the standard you have set as the default.
+
+
+g:ale_php_phpcbf_use_global *g:ale_php_phpcbf_use_global*
+ *b:ale_php_phpcbf_use_global*
+ Type: |Number|
+ Default: `0`
+
+ See |ale-integrations-local-executables|
+
+
===============================================================================
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 dabc573c..a8d033e2 100644
--- a/doc/ale-python.txt
+++ b/doc/ale-python.txt
@@ -123,6 +123,35 @@ g:ale_python_mypy_use_global *g:ale_python_mypy_use_global*
===============================================================================
+pycodestyle *ale-python-pycodestyle*
+
+
+g:ale_python_pycodestyle_executable *g:ale_python_pycodestyle_executable*
+ *b:ale_python_pycodestyle_executable*
+ Type: |String|
+ Default: `'pycodestyle'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_python_pycodestyle_options *g:ale_python_pycodestyle_options*
+ *b:ale_python_pycodestyle_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to add command-line arguments to the
+ pycodestyle invocation.
+
+
+g:ale_python_pycodestyle_use_global *g:ale_python_pycodestyle_use_global*
+ *b:ale_python_pycodestyle_use_global*
+ Type: |Number|
+ Default: `0`
+
+ See |ale-integrations-local-executables|
+
+
+===============================================================================
pylint *ale-python-pylint*
g:ale_python_pylint_executable *g:ale_python_pylint_executable*
diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt
index 1a20757e..d03ab073 100644
--- a/doc/ale-rust.txt
+++ b/doc/ale-rust.txt
@@ -8,7 +8,7 @@ Integration Information
Since Vim does not detect the rust file type out-of-the-box, you need the
runtime files for rust from here: https://github.com/rust-lang/rust.vim
- Note that there are two possible linters for rust files:
+ Note that there are three possible linters for Rust files:
1. rustc -- The Rust compiler is used to check the currently edited file.
So, if your project consists of multiple files, you will get some errors
@@ -18,6 +18,10 @@ Integration Information
checked. That means that all errors are properly shown, but cargo can
only operate on the files written on disk, so errors will not be reported
while you type.
+ 3. rls -- If you have `rls` installed, you might prefer using this linter
+ over cargo. rls implements the Language Server Protocol for incremental
+ compliation of Rust code, and can check Rust files while you type. `rls`
+ requires Rust files to contained in Cargo projects.
Only cargo is enabled by default. To switch to using rustc instead of cargo,
configure |g:ale_linters| appropriately: >
@@ -35,19 +39,30 @@ 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`
+ 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.
===============================================================================
+rls *ale-rust-rls*
+
+g:ale_rust_rls_executable *g:ale_rust_rls_executable*
+ *b:ale_rust_rls_executable*
+ Type: |String|
+ Default: `'rls'`
+
+ This variable can be modified to change the executable path for `rls`.
+
+
+===============================================================================
rustc *ale-rust-rustc*
g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes*
*b:ale_rust_ignore_error_codes*
Type: |List| of |String|s
- Default: []
+ Default: `[]`
This variable can contain error codes which will be ignored. For example, to
ignore most errors regarding failed imports, put this in your .vimrc
diff --git a/doc/ale-thrift.txt b/doc/ale-thrift.txt
new file mode 100644
index 00000000..ed858db8
--- /dev/null
+++ b/doc/ale-thrift.txt
@@ -0,0 +1,46 @@
+===============================================================================
+ALE Thrift Integration *ale-thrift-options*
+
+
+===============================================================================
+thrift *ale-thrift-thrift*
+
+The `thrift` linter works by compiling the buffer's contents and reporting any
+errors reported by the parser and the configured code generator(s).
+
+g:ale_thrift_thrift_executable *g:ale_thrift_thrift_executable*
+ *b:ale_thrift_thrift_executable*
+ Type: |String|
+ Default: `'thrift'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_thrift_thrift_generators *g:ale_thrift_thrift_generators*
+ *b:ale_thrift_thrift_generators*
+ Type: |List| of |String|s
+ Default: `['cpp']`
+
+ This list must contain one or more named code generators. Generator options
+ can be included as part of each string, e.g. `['py:dynamic']`.
+
+
+g:ale_thrift_thrift_includes *g:ale_thrift_thrift_includes*
+ *b:ale_thrift_thrift_includes*
+ Type: |List| of |String|s
+ Default: `[]`
+
+ This list contains paths that will be searched for thrift `include`
+ directives.
+
+
+g:ale_thrift_thrift_options *g:ale_thrift_thrift_options*
+ *b:ale_thrift_thrift_options*
+ Type: |String|
+ Default: `'-strict'`
+
+ This variable can be changed to customize the additional command-line
+ arguments that are passed to the thrift compiler.
+
+===============================================================================
+ 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 df479c5a..794240ec 100644
--- a/doc/ale-typescript.txt
+++ b/doc/ale-typescript.txt
@@ -30,6 +30,14 @@ g:ale_typescript_tslint_config_path *g:ale_typescript_tslint_config_path*
such path exists, this variable will be used instead.
+g:ale_typescript_tslint_rules_dir *g:ale_typescript_tslint_rules_dir*
+ *b:ale_typescript_tslint_rules_dir*
+ Type: |String|
+ Default: `''`
+
+ If this variable is set, ALE will use it as the rules directory for tslint.
+
+
g:ale_typescript_tslint_use_global *g:ale_typescript_tslint_use_global*
*b:ale_typescript_tslint_use_global*
Type: |Number|
diff --git a/doc/ale-xml.txt b/doc/ale-xml.txt
index ddbeb31d..6c8af6c7 100644
--- a/doc/ale-xml.txt
+++ b/doc/ale-xml.txt
@@ -1,20 +1,20 @@
===============================================================================
-ALE XML Integration *ale-xml-options*
+ALE XML Integration *ale-xml-options*
===============================================================================
-xmllint *ale-xml-xmllint*
+xmllint *ale-xml-xmllint*
-g:ale_xml_xmllint_executable *g:ale_xml_xmllint_executable*
- *b:ale_xml_xmllint_executable*
+g:ale_xml_xmllint_executable *g:ale_xml_xmllint_executable*
+ *b:ale_xml_xmllint_executable*
Type: |String|
Default: `'xmllint'`
This variable can be set to change the path to xmllint.
-g:ale_xml_xmllint_options *g:ale_xml_xmllint_options*
- *b:ale_xml_xmllint_options*
+g:ale_xml_xmllint_options *g:ale_xml_xmllint_options*
+ *b:ale_xml_xmllint_options*
Type: |String|
Default: `''`
diff --git a/doc/ale.txt b/doc/ale.txt
index 944431df..dfdb2693 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -8,10 +8,12 @@ CONTENTS *ale-contents*
1. Introduction.........................|ale-introduction|
2. Supported Languages & Tools..........|ale-support|
- 3. Global Options.......................|ale-options|
- 3.1 Highlights........................|ale-highlights|
+ 3. Linting..............................|ale-lint|
4. Fixing Problems......................|ale-fix|
- 5. Integration Documentation............|ale-integrations|
+ 5. Completion...........................|ale-completion|
+ 6. Global Options.......................|ale-options|
+ 6.1 Highlights........................|ale-highlights|
+ 7. Integration Documentation............|ale-integrations|
asm...................................|ale-asm-options|
gcc.................................|ale-asm-gcc|
c.....................................|ale-c-options|
@@ -19,6 +21,7 @@ CONTENTS *ale-contents*
clangtidy...........................|ale-c-clangtidy|
cppcheck............................|ale-c-cppcheck|
gcc.................................|ale-c-gcc|
+ clang-format........................|ale-c-clangformat|
chef..................................|ale-chef-options|
foodcritic..........................|ale-chef-foodcritic|
cpp...................................|ale-cpp-options|
@@ -28,6 +31,7 @@ CONTENTS *ale-contents*
cppcheck............................|ale-cpp-cppcheck|
cpplint.............................|ale-cpp-cpplint|
gcc.................................|ale-cpp-gcc|
+ clang-format........................|ale-cpp-clangformat|
css...................................|ale-css-options|
stylelint...........................|ale-css-stylelint|
cmake.................................|ale-cmake-options|
@@ -37,12 +41,15 @@ CONTENTS *ale-contents*
erlang................................|ale-erlang-options|
erlc................................|ale-erlang-erlc|
syntaxerl...........................|ale-erlang-syntaxerl|
+ eruby.................................|ale-eruby-options|
fortran...............................|ale-fortran-options|
gcc.................................|ale-fortran-gcc|
fusionscript..........................|ale-fuse-options|
fusion-lint.........................|ale-fuse-fusionlint|
go....................................|ale-go-options|
gometalinter........................|ale-go-gometalinter|
+ graphql...............................|ale-graphql-options|
+ gqlint..............................|ale-graphql-gqlint|
handlebars............................|ale-handlebars-options|
ember-template-lint.................|ale-handlebars-embertemplatelint|
haskell...............................|ale-haskell-options|
@@ -50,6 +57,8 @@ CONTENTS *ale-contents*
html..................................|ale-html-options|
htmlhint............................|ale-html-htmlhint|
tidy................................|ale-html-tidy|
+ idris.................................|ale-idris-options|
+ idris...............................|ale-idris-idris|
java..................................|ale-java-options|
checkstyle..........................|ale-java-checkstyle|
javac...............................|ale-java-javac|
@@ -76,9 +85,12 @@ CONTENTS *ale-contents*
perl................................|ale-perl-perl|
perlcritic..........................|ale-perl-perlcritic|
php...................................|ale-php-options|
+ hack................................|ale-php-hack|
+ langserver..........................|ale-php-langserver|
phpcs...............................|ale-php-phpcs|
phpmd...............................|ale-php-phpmd|
phpstan.............................|ale-php-phpstan|
+ phpcbf..............................|ale-php-phpcbf|
pug...................................|ale-pug-options|
puglint.............................|ale-pug-puglint|
python................................|ale-python-options|
@@ -86,6 +98,7 @@ CONTENTS *ale-contents*
flake8..............................|ale-python-flake8|
isort...............................|ale-python-isort|
mypy................................|ale-python-mypy|
+ pycodestyle.........................|ale-python-pycodestyle|
pylint..............................|ale-python-pylint|
yapf................................|ale-python-yapf|
ruby..................................|ale-ruby-options|
@@ -95,6 +108,7 @@ CONTENTS *ale-contents*
rubocop.............................|ale-ruby-rubocop|
rust..................................|ale-rust-options|
cargo...............................|ale-rust-cargo|
+ rls.................................|ale-rust-rls|
rustc...............................|ale-rust-rustc|
sass..................................|ale-sass-options|
stylelint...........................|ale-sass-stylelint|
@@ -114,6 +128,8 @@ CONTENTS *ale-contents*
tex...................................|ale-tex-options|
chktex..............................|ale-tex-chktex|
lacheck.............................|ale-tex-lacheck|
+ thrift................................|ale-thrift-options|
+ thrift..............................|ale-thrift-thrift|
typescript............................|ale-typescript-options|
eslint..............................|ale-typescript-eslint|
tslint..............................|ale-typescript-tslint|
@@ -128,10 +144,10 @@ CONTENTS *ale-contents*
yaml..................................|ale-yaml-options|
swaglint............................|ale-yaml-swaglint|
yamllint............................|ale-yaml-yamllint|
- 6. Commands/Keybinds....................|ale-commands|
- 7. API..................................|ale-api|
- 8. Special Thanks.......................|ale-special-thanks|
- 9. Contact..............................|ale-contact|
+ 8. Commands/Keybinds....................|ale-commands|
+ 9. API..................................|ale-api|
+ 10. Special Thanks......................|ale-special-thanks|
+ 11. Contact.............................|ale-contact|
===============================================================================
1. Introduction *ale-introduction*
@@ -162,79 +178,247 @@ for the current buffer.
The following languages and tools are supported.
-* ASM: 'gcc'
-* Ansible: 'ansible-lint'
-* Asciidoc: 'proselint'
-* Bash: 'shell' (-n flag), 'shellcheck'
-* Bourne Shell: 'shell' (-n flag), 'shellcheck'
-* C: 'cppcheck', 'gcc', 'clang'
-* C++ (filetype cpp): 'clang', 'clangtidy', 'cppcheck', 'cpplint', 'gcc'
-* C#: 'mcs'
-* Chef: 'foodcritic'
-* CMake: 'cmakelint'
-* CoffeeScript: 'coffee', 'coffelint'
-* Crystal: 'crystal'
-* CSS: 'csslint', 'stylelint'
-* Cython (pyrex filetype): 'cython'
-* D: 'dmd'
-* Dart: 'dartanalyzer'
-* Dockerfile: 'hadolint'
-* Elixir: 'credo', 'dogma'
-* Elm: 'elm-make'
-* Erlang: 'erlc'
-* Fortran: 'gcc'
-* Go: 'gofmt', 'go vet', 'golint', 'go build', 'gosimple', 'staticcheck'
-* FusionScript: 'fusion-lint'
-* Haml: 'hamllint'
-* Handlebars: 'ember-template-lint'
-* Haskell: 'ghc', 'stack-ghc', 'stack-build', 'ghc-mod', 'stack-ghc-mod', 'hlint', 'hdevtools'
-* HTML: 'HTMLHint', 'proselint', 'tidy'
-* Java: 'javac'
-* JavaScript: 'eslint', 'jscs', 'jshint', 'flow', 'prettier', 'prettier-eslint', 'xo'
-* JSON: 'jsonlint'
-* Kotlin: 'kotlinc'
-* LaTeX (tex): 'chktex', 'lacheck', 'proselint'
-* Lua: 'luacheck'
-* Markdown: 'mdl', 'proselint', 'vale'
-* MATLAB: 'mlint'
-* nim: 'nim check'
-* nix: 'nix-instantiate'
-* nroff: 'proselint'
-* Objective-C: 'clang'
-* Objective-C++: 'clang'
-* OCaml: 'merlin' (see |ale-linter-integration-ocaml-merlin|)
-* Perl: 'perl' (-c flag), 'perlcritic'
-* PHP: 'hack', 'php' (-l flag), 'phpcs', 'phpmd', 'phpstan'
-* Pod: 'proselint'
-* Pug: 'pug-lint'
-* Puppet: 'puppet', 'puppet-lint'
-* Python: 'autopep8', 'flake8', 'isort', 'mypy', 'pylint', 'yapf'
-* R: 'lintr'
-* ReasonML: 'merlin'
-* reStructuredText: 'proselint'
-* RPM spec: 'spec'
-* Rust: 'rustc' (see |ale-integration-rust|)
-* Ruby: 'reek', 'rubocop'
-* SASS: 'sasslint', 'stylelint'
-* SCSS: 'sasslint', 'scsslint', 'stylelint'
-* Scala: 'scalac', 'scalastyle'
-* Slim: 'slim-lint'
-* SML: 'smlnj'
-* Stylus: 'stylelint'
-* SQL: 'sqlint'
-* Swift: 'swiftlint'
-* Texinfo: 'proselint'
-* Text: 'proselint', 'vale'
-* TypeScript: 'eslint', 'tslint', 'tsserver', 'typecheck'
-* Verilog: 'iverilog', 'verilator'
-* Vim: 'vint'
-* Vim help: 'proselint'
-* XHTML: 'proselint'
-* XML: 'xmllint'
-* YAML: 'swaglint', 'yamllint'
+Notes:
+
+`^` No linters for text or Vim help filetypes are enabled by default.
+`!!` These linters check only files on disk. See |ale-lint-file-linters|
+
+* ASM: `gcc`
+* Ansible: `ansible-lint`
+* AsciiDoc: `proselint`
+* 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`
+* C#: `mcs`
+* Chef: `foodcritic`
+* CMake: `cmakelint`
+* CoffeeScript: `coffee`, `coffeelint`
+* Crystal: `crystal`!!
+* CSS: `csslint`, `stylelint`
+* Cython (pyrex filetype): `cython`
+* D: `dmd`
+* Dart: `dartanalyzer`
+* Dockerfile: `hadolint`
+* Elixir: `credo`, `dogma`!!
+* Elm: `elm-make`
+* Erb: `erb`, `erubis`
+* Erlang: `erlc`, `SyntaxErl`
+* Fortran: `gcc`
+* FusionScript: `fusion-lint`
+* Go: `gofmt`, `go vet`, `golint`, `gometalinter`!!, `go build`!!, `gosimple`, `staticcheck`
+* GraphQL: `gqlint`
+* Haml: `haml-lint`
+* Handlebars: `ember-template-lint`
+* Haskell: `ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`
+* HTML: `HTMLHint`, `proselint`, `tidy`
+* Idris: `idris`
+* Java: `checkstyle`, `javac`
+* JavaScript: `eslint`, `jscs`, `jshint`, `flow`, `prettier`, `prettier-eslint`, `prettier-standard`, `standard`, `xo`
+* JSON: `jsonlint`
+* Kotlin: `kotlinc`, `ktlint`
+* LaTeX (tex): `chktex`, `lacheck`, `proselint`
+* Lua: `luacheck`
+* Markdown: `mdl`, `proselint`, `vale`
+* MATLAB: `mlint`
+* Nim: `nim check`!!
+* nix: `nix-instantiate`
+* nroff: `proselint`
+* Objective-C: `clang`
+* Objective-C++: `clang`
+* OCaml: `merlin` (see |ale-ocaml-merlin|)
+* Perl: `perl -c`, `perl-critic`
+* PHP: `hack`, `langserver`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf`
+* Pod: `proselint`
+* Pug: `pug-lint`
+* Puppet: `puppet`, `puppet-lint`
+* Python: `autopep8`, `flake8`, `isort`, `mypy`, `pycodestyle`, `pylint`!!, `yapf`
+* R: `lintr`
+* ReasonML: `merlin`
+* reStructuredText: `proselint`
+* RPM spec: `rpmlint`
+* Ruby: `brakeman`, `rails_best_practices`!!, `reek`, `rubocop`, `ruby`
+* Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|)
+* SASS: `sass-lint`, `stylelint`
+* SCSS: `sass-lint`, `scss-lint`, `stylelint`
+* Scala: `scalac`, `scalastyle`
+* Slim: `slim-lint`
+* SML: `smlnj`
+* Stylus: `stylelint`
+* SQL: `sqlint`
+* Swift: `swiftlint`, `swiftformat`
+* Tcl: `nagelfar`!!
+* Texinfo: `proselint`
+* Text^: `proselint`, `vale`
+* Thrift: `thrift`
+* TypeScript: `eslint`, `tslint`, `tsserver`, `typecheck`
+* Verilog: `iverilog`, `verilator`
+* Vim: `vint`
+* Vim help^: `proselint`
+* XHTML: `proselint`
+* XML: `xmllint`
+* YAML: `swaglint`, `yamllint`
===============================================================================
-3. Global Options *ale-options*
+3. Linting *ale-lint*
+
+ALE's primary focus is on checking for problems with your code with various
+programs via some Vim code for integrating with those programs, referred to
+as 'linters.' ALE supports a wide array of programs for linting by default,
+but additional programs can be added easily by defining files in |runtimepath|
+with the filename pattern `ale_linters/<filetype>/<filename>.vim`. For more
+information on defining new linters, see the extensive documentation
+for |ale#linter#Define()|.
+
+Without any configuration, ALE will attempt to check all of the code for every
+file you open in Vim with all available tools by default. To see what ALE
+is doing, and what options have been set, try using the |:ALEInfo| command.
+
+Most of the linters ALE runs will check the Vim buffer you are editing instead
+of the file on disk. This allows you to check your code for errors before you
+have even saved your changes. ALE will check your code in the following
+circumstances, which can be configured with the associated options.
+
+* When you modify a buffer. - |g:ale_lint_on_text_changed|
+* When you open a new or modified buffer. - |g:ale_lint_on_enter|
+* When you save a buffer. - |g:ale_lint_on_save|
+* When the filetype changes for a buffer. - |g:ale_lint_on_filetype_changed|
+* If ALE is used to check ccode manually. - |:ALELint|
+
+In addition to the above options, ALE can also check buffers for errors when
+you leave insert mode with |g:ale_lint_on_insert_leave|, which is off by
+default. It is worth reading the documentation for every option.
+
+ *ale-lint-file-linters*
+
+Some programs must be run against files which have been saved to disk, and
+simply do not support reading temporary files or stdin, either of which are
+required for ALE to be able to check for errors as you type. The programs
+which behave this way are documented in the lists and tables of supported
+programs. ALE will only lint files with these programs in the following
+circumstances.
+
+* When you open a new or modified buffer. - |g:ale_lint_on_enter|
+* When you save a buffer. - |g:ale_lint_on_save|
+* When the filetype changes for a buffer. - |g:ale_lint_on_filetype_changed|
+* If ALE is used to check ccode manually. - |:ALELint|
+
+ALE will report problems with your code in the following ways, listed with
+their relevant options.
+
+* By updating loclist. (On by default) - |g:ale_set_loclist|
+* By updating quickfix. (Off by default) - |g:ale_set_quickfix|
+* By setting error highlights. - |g:ale_set_highlights|
+* By creating signs in the sign column. - |g:ale_set_signs|
+* By echoing messages based on your cursor. - |g:ale_echo_cursor|
+* By showing balloons for your mouse cursor - |g:ale_set_balloons|
+
+Please consult the documentation for each option, which can reveal some other
+ways of tweaking the behaviour of each way of displaying problems. You can
+disable or enable whichever options you prefer.
+
+Most settings can be configured for each buffer. (|b:| instead of |g:|),
+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.
+
+
+===============================================================================
+4. Fixing Problems *ale-fix*
+
+ALE can fix problems with files with the |ALEFix| command. When |ALEFix| is
+run, the variable |g:ale_fixers| will be read for getting a |List| of commands
+for filetypes, split on `.`, and the functions named in |g:ale_fixers| will be
+executed for fixing the errors.
+
+The |ALEFixSuggest| command can be used to suggest tools that be used to
+fix problems for the current buffer.
+
+The values for `g:ale_fixers` can be a list of |String|, |Funcref|, or
+|lambda| values. String values must either name a function, or a short name
+for a function set in the ALE fixer registry.
+
+Each function for fixing errors must accept either one argument `(buffer)` or
+two arguments `(buffer, lines)`, representing the buffer being fixed and the
+lines to fix. The functions must return either `0`, for changing nothing, a
+|List| for new lines to set, or a |Dictionary| for describing a command to be
+run in the background.
+
+Functions receiving a variable number of arguments will not receive the second
+argument `lines`. Functions should name two arguments if the `lines` argument
+is desired. This is required to avoid unnecessary copying of the lines of
+the buffers being checked.
+
+When a |Dictionary| is returned for an |ALEFix| callback, the following keys
+are supported for running the commands.
+
+ `command` A |String| for the command to run. This key is required.
+
+ When `%t` is included in a command string, a temporary
+ file will be created, containing the lines from the file
+ after previous adjustment have been done.
+
+ `read_temporary_file` When set to `1`, ALE will read the contents of the
+ temporary file created for `%t`. This option can be used
+ for commands which need to modify some file on disk in
+ order to fix files.
+
+ *ale-fix-configuration*
+
+Synchronous functions and asynchronous jobs will be run in a sequence for
+fixing files, and can be combined. For example:
+>
+ let g:ale_fixers = {
+ \ 'javascript': [
+ \ 'DoSomething',
+ \ 'eslint',
+ \ {buffer, lines -> filter(lines, 'v:val !=~ ''^\s*//''')},
+ \ ],
+ \}
+
+ ALEFix
+<
+The above example will call a function called `DoSomething` which could act
+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 convenience, a plug mapping is defined for |ALEFix|, so you can set up a
+keybind easily for fixing files. >
+
+ " Bind F8 to fixing problems with ALE
+ nmap <F8> <Plug>(ale_fix)
+<
+Files can be fixed automatically with the following options, which are all off
+by default.
+
+|g:ale_fix_on_save| - Fix files when they are saved.
+
+
+===============================================================================
+5. Completion *ale-completion*
+
+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.
+
+NOTE: At the moment, only `tsserver` for TypeScript code is supported for
+completion.
+
+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
+only suggest so many possible matches for completion. The maximum number of
+items can be controlled with |g:ale_completion_max_suggestions|.
+
+
+===============================================================================
+6. Global Options *ale-options*
g:airline#extensions#ale#enabled *g:airline#extensions#ale#enabled*
@@ -266,6 +450,44 @@ g:ale_change_sign_column_color *g:ale_change_sign_column_color*
windows.
+g:ale_completion_delay *g:ale_completion_delay*
+
+ Type: |Number|
+ Default: `100`
+
+ The number of milliseconds before ALE will send a request to a language
+ server for completions after you have finished typing.
+
+ See |ale-completion|
+
+
+g:ale_completion_enabled *g:ale_completion_enabled*
+
+ Type: |Number|
+ Default: `0`
+
+ When this option is set to `1`, completion support will be enabled.
+
+ See |ale-completion|
+
+
+g:ale_completion_max_suggestions *g:ale_completion_max_suggestions*
+
+ Type: |Number|
+ Default: `50`
+
+ The maximum number of items ALE will suggest in completion menus for
+ automatic completion.
+
+ Setting this number higher will require more processing time, and may
+ suggest too much noise. Setting this number lower will require less
+ processing time, but some suggestions will not be included, so you might not
+ be able to see the suggestions you want.
+
+ Adjust this option as needed, depending on the complexity of your codebase
+ and your available processing power.
+
+
g:ale_echo_cursor *g:ale_echo_cursor*
Type: |Number|
@@ -285,8 +507,8 @@ g:ale_echo_msg_error_str *g:ale_echo_msg_error_str*
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
+ Note |g:ale_echo_cursor| should be set to 1
+ Note |g:ale_echo_msg_format| should contain the `%severity%` handler
g:ale_echo_msg_format *g:ale_echo_msg_format*
@@ -298,7 +520,7 @@ g:ale_echo_msg_format *g:ale_echo_msg_format*
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
+ Note |g:ale_echo_cursor| should be setted to 1
g:ale_echo_msg_warning_str *g:ale_echo_msg_warning_str*
@@ -307,8 +529,8 @@ g:ale_echo_msg_warning_str *g:ale_echo_msg_warning_str*
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
+ Note |g:ale_echo_cursor| should be set to 1
+ Note |g:ale_echo_msg_format| should contain the `%severity%` handler
g:ale_emit_conflict_warnings *g:ale_emit_conflict_warnings*
@@ -396,7 +618,7 @@ g:ale_history_log_output *g:ale_history_log_output*
g:ale_keep_list_window_open *g:ale_keep_list_window_open*
-
+ *b:ale_keep_list_window_open*
Type: |Number|
Default: `0`
@@ -405,7 +627,7 @@ g:ale_keep_list_window_open *g:ale_keep_list_window_open*
the loclist or quicfix windows will be closed automatically when there
are no warnings or errors.
- See: |g:ale_open_list|
+ See |g:ale_open_list|
g:ale_list_window_size *g:ale_list_window_size*
@@ -445,6 +667,11 @@ g:ale_lint_on_enter *g:ale_lint_on_enter*
files have been changed outside of Vim. If a file is changed outside of
Vim, it will be checked when it is next opened.
+ A |BufWinLeave| event will be used to look for the |E924|, |E925|, or |E926|
+ errors after moving from a loclist or quickfix window to a new buffer. If
+ prompts for these errors are opened after moving to new buffers, then ALE
+ will automatically send the `<CR>` key needed to close the prompt.
+
g:ale_lint_on_filetype_changed *g:ale_lint_on_filetype_changed*
@@ -598,13 +825,17 @@ g:ale_maximum_file_size *g:ale_maximum_file_size*
g:ale_open_list *g:ale_open_list*
-
- Type: |Number|
+ *b:ale_open_list*
+ Type: |Number| or |String|
Default: `0`
- When set to `1`, this will cause ALE to automatically open a window for
- the loclist (|lopen|) or for the quickfix list instead if
- |g:ale_set_quickfix| is `1`. (|copen|)
+ When set to `1`, this will cause ALE to automatically open a window for the
+ loclist (|lopen|) or for the quickfix list instead if |g:ale_set_quickfix|
+ is `1`. (|copen|)
+
+ When set to `'on_save'`, ALE will only open the loclist after buffers have
+ been saved. The list will be opened some time after buffers are saved and
+ any linter for a buffer returns results.
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
@@ -689,9 +920,12 @@ g:ale_set_quickfix *g:ale_set_quickfix*
Type: |Number|
Default: `0`
- When this option is set to `1`, the |quickfix| list will be populated with any
- warnings and errors which are found by ALE, instead of the |loclist|. The
- loclist will never be populated when this option is on.
+ When this option is set to `1`, the |quickfix| list will be populated with
+ any problems which are found by ALE, instead of the |loclist|. The loclist
+ will never be populated when this option is on.
+
+ 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.
g:ale_set_signs *g:ale_set_signs*
@@ -856,7 +1090,7 @@ b:ale_warn_about_trailing_whitespace *b:ale_warn_about_trailing_whitespace*
-------------------------------------------------------------------------------
-3.1. Highlights *ale-highlights*
+6.1. Highlights *ale-highlights*
ALEError *ALEError*
@@ -950,79 +1184,7 @@ ALEWarningSign *ALEWarningSign*
===============================================================================
-4. Fixing Problems *ale-fix*
-
-ALE can fix problems with files with the |ALEFix| command. When |ALEFix| is
-run, the variable |g:ale_fixers| will be read for getting a |List| of commands
-for filetypes, split on `.`, and the functions named in |g:ale_fixers| will be
-executed for fixing the errors.
-
-The |ALEFixSuggest| command can be used to suggest tools that be used to
-fix problems for the current buffer.
-
-The values for `g:ale_fixers` can be a list of |String|, |Funcref|, or
-|lambda| values. String values must either name a function, or a short name
-for a function set in the ALE fixer registry.
-
-Each function for fixing errors must accept either one argument `(buffer)` or
-two arguments `(buffer, lines)`, representing the buffer being fixed and the
-lines to fix. The functions must return either `0`, for changing nothing, a
-|List| for new lines to set, or a |Dictionary| for describing a command to be
-run in the background.
-
-Functions receiving a variable number of arguments will not receive the second
-argument `lines`. Functions should name two arguments if the `lines` argument
-is desired. This is required to avoid unnecessary copying of the lines of
-the buffers being checked.
-
-When a |Dictionary| is returned for an |ALEFix| callback, the following keys
-are supported for running the commands.
-
- `command` A |String| for the command to run. This key is required.
-
- When `%t` is included in a command string, a temporary
- file will be created, containing the lines from the file
- after previous adjustment have been done.
-
- `read_temporary_file` When set to `1`, ALE will read the contents of the
- temporary file created for `%t`. This option can be used
- for commands which need to modify some file on disk in
- order to fix files.
-
- *ale-fix-configuration*
-
-Synchronous functions and asynchronous jobs will be run in a sequence for
-fixing files, and can be combined. For example:
->
- let g:ale_fixers = {
- \ 'javascript': [
- \ 'DoSomething',
- \ 'eslint',
- \ {buffer, lines -> filter(lines, 'v:val !=~ ''^\s*//''')},
- \ ],
- \}
-
- ALEFix
-<
-The above example will call a function called `DoSomething` which could act
-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 convenience, a plug mapping is defined for |ALEFix|, so you can set up a
-keybind easily for fixing files. >
-
- " Bind F8 to fixing problems with ALE
- nmap <F8> <Plug>(ale_fix)
-<
-Files can be fixed automatically with the following options, which are all off
-by default.
-
-|g:ale_fix_on_save| - Fix files when they are saved.
-
-
-===============================================================================
-5. Integration Documentation *ale-integrations*
+7. Integration Documentation *ale-integrations*
Linter and fixer options are documented in individual help files. See the
table of contents at |ale-contents|.
@@ -1055,7 +1217,7 @@ ALE will use to search for Python executables.
===============================================================================
-6. Commands/Keybinds *ale-commands*
+8. Commands/Keybinds *ale-commands*
ALEFix *ALEFix*
@@ -1071,6 +1233,7 @@ ALEFixSuggest *ALEFixSuggest*
See |ale-fix| for more information.
+ *:ALELint*
ALELint *ALELint*
Run ALE once for the current buffer. This command can be used to run ALE
@@ -1140,6 +1303,7 @@ ALEDetail *ALEDetail*
A plug mapping `<Plug>(ale_detail)` is defined for this command.
+ *:ALEInfo*
ALEInfo *ALEInfo*
ALEInfoToClipboard *ALEInfoToClipboard*
@@ -1158,9 +1322,9 @@ ALEInfoToClipboard *ALEInfoToClipboard*
===============================================================================
-7. API *ale-api*
+9. API *ale-api*
-ale#Queue(delay, [linting_flag]) *ale#Queue()*
+ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
Run linters for the current buffer, based on the filetype of the buffer,
with a given `delay`. A `delay` of `0` will run the linters immediately.
@@ -1171,6 +1335,15 @@ ale#Queue(delay, [linting_flag]) *ale#Queue()*
is `'lint_file'`, then linters where the `lint_file` option is set to `1` will be
run. Linters with `lint_file` set to `1` are not run by default.
+ An optional `buffer_number` argument can be given for specifying the buffer
+ to check. The active buffer (`bufnr('')`) will be checked by default.
+
+ *ale-cool-down*
+ If an exception is thrown when queuing/running ALE linters, ALE will enter
+ a cool down period where it will stop checking anything for a short period
+ of time. This is to prevent ALE from seriously annoying users if a linter
+ is broken, or when developing ALE itself.
+
ale#engine#CreateDirectory(buffer) *ale#engine#CreateDirectory()*
@@ -1262,7 +1435,10 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
search by in future before being passed on to the
|loclist|, etc.
- This argument is required.
+ This argument is required, unless the linter is an
+ LSP linter. In which case, this argument must not be
+ defined, as LSP linters handle diangostics
+ automatically. See |ale-lsp-linters|.
The keys for each item in the List will be handled in
the following manner:
@@ -1283,9 +1459,27 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
`end_lnum` - An optional end line number.
This key can set along with `end_col` for
highlighting multi-line problems.
- `bufnr` - The buffer number should match the buffer
- being checked, and this value will default to
- the buffer being checked.
+ `bufnr` - This key represents the buffer number the
+ problems are for. This value will default to
+ the buffer number being checked.
+
+ The `filename` key can be set instead of this key,
+ and then the eventual `bufnr` value in the final
+ list will either represent the number for an open
+ buffer or `-1` for a file not open in any buffer.
+ `filename` - An optional filename for the file the
+ problems are for. This should be an absolute path to
+ a file.
+
+ Problems for files which have not yet been opened
+ will be set in those files after they are opened
+ and have been checked at least once.
+
+ Temporary files in directories used for Vim
+ temporary files with `tempname()` will be asssumed
+ to be the buffer being checked, unless the `bufnr`
+ key is also set with a valid number for some other
+ buffer.
`vcol` - Defaults to `0`.
`type` - Defaults to `'E'`.
`nr` - Defaults to `-1`.
@@ -1393,10 +1587,47 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
be set automatically to `0`. The two options cannot
be used together.
+ *ale-lsp-linters*
+ `lsp` A |String| for defining LSP (Language Server Protocol)
+ linters.
+
+ This argument may be omitted or `''` when a linter
+ does not represent an LSP linter.
+
+ When this argument is set to `'stdio'`, then the
+ linter will be defined as an LSP linter which keeps a
+ process for a language server runnning, and
+ communicates with it directly via a |channel|.
+
+ When this argument is not empty, then the
+ `project_callback` and `language_callback` arguments
+ must also be defined.
+
+ LSP linters handle diagonstics automatically, so
+ the `callback` argument must not be defined.
+
+ `project_callback` A |String| or |Funcref| for a callback function
+ accepting a buffer number. A |String| should be
+ returned representing the path to the project for the
+ file being checked with the language server. If an
+ empty string is returned, the file will not be
+ checked at all.
+
+ This argument must only be set if the `lsp` argument
+ is also set to a non-empty string.
+
+ `language_callback` A |String| or |Funcref| for a callback function
+ accepting a buffer number. A |String| should be
+ returned representing the name of the language being
+ checked.
+
+ This argument must only be set if the `lsp` argument
+ is also set to a non-empty string.
+
`aliases` A |List| of aliases for the linter name.
- This option can be set with alternative names for
- for selecting the linter with |g:ale_linters|. This
+ This argument can be set with alternative names for
+ selecting the linter with |g:ale_linters|. This
setting can make it easier to guess the linter name
by offering a few alternatives.
@@ -1499,13 +1730,13 @@ ALELint *ALELint-autocmd*
echoing messges.
===============================================================================
-8. Special Thanks *ale-special-thanks*
+10. Special Thanks *ale-special-thanks*
Special thanks to Mark Grealish (https://www.bhalash.com/) for providing ALE's
snazzy looking ale glass logo. Cheers, Mark!
===============================================================================
-9. Contact *ale-contact*
+11. Contact *ale-contact*
If you like this plugin, and wish to get in touch, check out the GitHub
page for issues and more at https://github.com/w0rp/ale
@@ -1513,10 +1744,8 @@ page for issues and more at https://github.com/w0rp/ale
If you wish to contact the author of this plugin directly, please feel
free to send an email to devw0rp@gmail.com.
-
Please drink responsibly, or not at all, which is ironically the preference
of w0rp, who is teetotal.
-
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/plugin/ale.vim b/plugin/ale.vim
index 79e6836b..a9ab88a1 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -189,8 +189,8 @@ call ale#Set('type_map', {})
" Enable automatic completion with LSP servers and tsserver
call ale#Set('completion_enabled', 0)
-call ale#Set('completion_delay', 300)
-call ale#Set('completion_max_suggestions', 20)
+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.
@@ -206,11 +206,11 @@ function! ALEInitAuGroups() abort
augroup ALERunOnTextChangedGroup
autocmd!
if g:ale_enabled
- if l:text_changed ==? 'always' || l:text_changed ==# '1'
+ 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 ==? 'normal'
+ elseif l:text_changed is? 'normal'
autocmd TextChanged * call ale#Queue(g:ale_lint_delay)
- elseif l:text_changed ==? 'insert'
+ elseif l:text_changed is? 'insert'
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
endif
endif
@@ -218,27 +218,26 @@ function! ALEInitAuGroups() abort
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('<abuf>')))
+ endif
if g:ale_enabled && g:ale_lint_on_enter
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
" Track when the file is changed outside of Vim.
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
- " If the file has been changed, then check it again on enter.
- autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand('<abuf>')))
endif
augroup END
augroup ALERunOnFiletypeChangeGroup
autocmd!
if g:ale_enabled && g:ale_lint_on_filetype_changed
- " Set the filetype after a buffer is opened or read.
- autocmd BufEnter,BufRead * let b:ale_original_filetype = &filetype
" Only start linting if the FileType actually changes after
" opening a buffer. The FileType will fire when buffers are opened.
- autocmd FileType *
- \ if has_key(b:, 'ale_original_filetype')
- \ && b:ale_original_filetype !=# expand('<amatch>')
- \| call ale#Queue(300, 'lint_file')
- \| endif
+ autocmd FileType * call ale#events#FileTypeEvent(
+ \ str2nr(expand('<abuf>')),
+ \ expand('<amatch>')
+ \)
endif
augroup END
@@ -296,12 +295,17 @@ function! s:ALEToggle() abort
call ale#balloon#Enable()
endif
else
- " Make sure the buffer number is a number, not a string,
- " otherwise things can go wrong.
- for l:buffer in map(keys(g:ale_buffer_info), 'str2nr(v:val)')
- " 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)
+ 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.
diff --git a/run-tests b/run-tests
index b32ac703..5b49b3e9 100755
--- a/run-tests
+++ b/run-tests
@@ -55,6 +55,12 @@ while [ $# -ne 0 ]; do
run_custom_checks=0
shift
;;
+ --custom-checks-only)
+ run_vim_tests=0
+ run_neovim_tests=0
+ run_vint=0
+ shift
+ ;;
--)
shift
break
@@ -76,6 +82,9 @@ if [ $# -ne 0 ]; then
tests="$*"
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"
@@ -211,6 +220,41 @@ if ((run_custom_checks)); then
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
+
+ ./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
fi
exit $EXIT
diff --git a/test/command_callback/c_paths/dummy.c b/test/command_callback/c_paths/dummy.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/c_paths/dummy.c
diff --git a/test/command_callback/php_paths/project-with-phpcbf/foo/test.php b/test/command_callback/php_paths/project-with-phpcbf/foo/test.php
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php_paths/project-with-phpcbf/foo/test.php
diff --git a/test/command_callback/php_paths/project-with-phpcbf/vendor/bin/phpcbf b/test/command_callback/php_paths/project-with-phpcbf/vendor/bin/phpcbf
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php_paths/project-with-phpcbf/vendor/bin/phpcbf
diff --git a/test/command_callback/php_paths/project-without-phpcbf/foo/test.php b/test/command_callback/php_paths/project-without-phpcbf/foo/test.php
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php_paths/project-without-phpcbf/foo/test.php
diff --git a/test/command_callback/standard-test-files/with-bin/node_modules/.bin/standard b/test/command_callback/standard-test-files/with-bin/node_modules/.bin/standard
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/standard-test-files/with-bin/node_modules/.bin/standard
diff --git a/test/command_callback/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js b/test/command_callback/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js
diff --git a/test/command_callback/swift_paths/dummy.swift b/test/command_callback/swift_paths/dummy.swift
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/swift_paths/dummy.swift
diff --git a/test/command_callback/test_erlang_syntaxerl_command_callback.vader b/test/command_callback/test_erlang_syntaxerl_command_callback.vader
new file mode 100644
index 00000000..1df2be39
--- /dev/null
+++ b/test/command_callback/test_erlang_syntaxerl_command_callback.vader
@@ -0,0 +1,58 @@
+Before:
+ Save g:ale_erlang_syntaxerl_executable
+
+ unlet! g:ale_erlang_syntaxerl_executable
+ unlet! b:ale_erlang_syntaxerl_executable
+
+ runtime ale_linters/erlang/syntaxerl.vim
+
+
+After:
+ Restore
+
+ call ale#linter#Reset()
+
+
+Execute (The executable should be correct):
+ AssertEqual 'syntaxerl', ale_linters#erlang#syntaxerl#GetExecutable(bufnr(''))
+
+ let g:ale_erlang_syntaxerl_executable = '/some/other/syntaxerl'
+ AssertEqual '/some/other/syntaxerl', ale_linters#erlang#syntaxerl#GetExecutable(bufnr(''))
+
+ let b:ale_erlang_syntaxerl_executable = '/yet/another/syntaxerl'
+ AssertEqual '/yet/another/syntaxerl', ale_linters#erlang#syntaxerl#GetExecutable(bufnr(''))
+
+
+Execute (The executable should be presented in the feature check command):
+ let g:ale_erlang_syntaxerl_executable = '/some/other/syntaxerl'
+ AssertEqual "'/some/other/syntaxerl' -h", ale_linters#erlang#syntaxerl#FeatureCheck(bufnr(''))
+
+ let b:ale_erlang_syntaxerl_executable = '/yet/another/syntaxerl'
+ AssertEqual "'/yet/another/syntaxerl' -h", ale_linters#erlang#syntaxerl#FeatureCheck(bufnr(''))
+
+
+Execute (The executable should be presented in the command):
+ let g:ale_erlang_syntaxerl_executable = '/some/other/syntaxerl'
+ AssertEqual "'/some/other/syntaxerl' %t", ale_linters#erlang#syntaxerl#GetCommand(bufnr(''), [])
+
+ let b:ale_erlang_syntaxerl_executable = '/yet/another/syntaxerl'
+ AssertEqual "'/yet/another/syntaxerl' %t", ale_linters#erlang#syntaxerl#GetCommand(bufnr(''), [])
+
+
+Execute (The -b option should be used when available):
+ AssertEqual "'syntaxerl' %t", ale_linters#erlang#syntaxerl#GetCommand(bufnr(''), [
+ \ 'Syntax checker for Erlang (0.14.0)',
+ \ 'Usage: syntaxerl [-d | --debug] <FILENAME>',
+ \ ' syntaxerl <-h | --help>',
+ \ ' -d, --debug Enable debug output',
+ \ ' -h, --help Show this message',
+ \ ])
+
+ AssertEqual "'syntaxerl' -b %s %t", ale_linters#erlang#syntaxerl#GetCommand(bufnr(''), [
+ \ 'Syntax checker for Erlang (0.14.0)',
+ \ 'Usage: syntaxerl [-b | --base <FILENAME>] [-d | --debug] <FILENAME>',
+ \ ' syntaxerl <-h | --help>',
+ \ ' -b, --base Set original filename',
+ \ ' -d, --debug Enable debug output',
+ \ ' -h, --help Show this message',
+ \ ])
diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader
index 70527ad3..c36fe4f5 100644
--- a/test/command_callback/test_flake8_command_callback.vader
+++ b/test/command_callback/test_flake8_command_callback.vader
@@ -19,23 +19,23 @@ Execute(The flake8 callbacks should return the correct default values):
\ '''flake8'' --version',
\ ale_linters#python#flake8#VersionCheck(bufnr(''))
AssertEqual
- \ '''flake8'' --stdin-display-name %s -',
+ \ '''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()
AssertEqual
- \ '''flake8'' -',
+ \ '''flake8'' --format=default -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9'])
Execute(The flake8 command callback should let you set options):
let g:ale_python_flake8_options = '--some-option'
AssertEqual
- \ '''flake8'' --some-option --stdin-display-name %s -',
+ \ '''flake8'' --some-option --format=default --stdin-display-name %s -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.4'])
call ale_linters#python#flake8#ClearVersionCache()
AssertEqual
- \ '''flake8'' --some-option -',
+ \ '''flake8'' --some-option --format=default -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9'])
Execute(You should be able to set a custom executable and it should be escaped):
@@ -48,7 +48,7 @@ Execute(You should be able to set a custom executable and it should be escaped):
\ '''executable with spaces'' --version',
\ ale_linters#python#flake8#VersionCheck(bufnr(''))
AssertEqual
- \ '''executable with spaces'' --stdin-display-name %s -',
+ \ '''executable with spaces'' --format=default --stdin-display-name %s -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.0'])
Execute(The flake8 callbacks should detect virtualenv directories):
@@ -62,7 +62,7 @@ Execute(The flake8 callbacks should detect virtualenv directories):
\ ale_linters#python#flake8#VersionCheck(bufnr(''))
AssertEqual
\ '''' . g:dir . '/python_paths/with_virtualenv/env/bin/flake8'''
- \ . ' --stdin-display-name %s -',
+ \ . ' --format=default --stdin-display-name %s -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.0'])
Execute(The FindProjectRoot should detect the project root directory for namespace package via Manifest.in):
@@ -114,7 +114,7 @@ Execute(Using `python -m flake8` should be supported for running flake8):
\ '''python'' -m flake8 --version',
\ ale_linters#python#flake8#VersionCheck(bufnr(''))
AssertEqual
- \ '''python'' -m flake8 --some-option -',
+ \ '''python'' -m flake8 --some-option --format=default -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9'])
call ale_linters#python#flake8#ClearVersionCache()
@@ -129,5 +129,5 @@ Execute(Using `python -m flake8` should be supported for running flake8):
\ '''python'' -m flake8 --version',
\ ale_linters#python#flake8#VersionCheck(bufnr(''))
AssertEqual
- \ '''python'' -m flake8 --some-option -',
+ \ '''python'' -m flake8 --some-option --format=default -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9'])
diff --git a/test/command_callback/test_idris_command_callbacks.vader b/test/command_callback/test_idris_command_callbacks.vader
new file mode 100644
index 00000000..03a69f39
--- /dev/null
+++ b/test/command_callback/test_idris_command_callbacks.vader
@@ -0,0 +1,42 @@
+Before:
+ Save g:ale_idris_idris_executable
+ Save g:ale_idris_idris_options
+
+ unlet! g:ale_idris_idris_executable
+ unlet! b:ale_idris_idris_executable
+ unlet! g:ale_idris_idris_options
+ unlet! b:ale_idris_idris_options
+
+ runtime ale_linters/idris/idris.vim
+
+After:
+ Restore
+ unlet! b:command_tail
+ unlet! b:ale_idris_idris_executable
+ unlet! b:ale_idris_idris_options
+ call ale#linter#Reset()
+
+Execute(The executable should be configurable):
+ AssertEqual 'idris', ale_linters#idris#idris#GetExecutable(bufnr(''))
+
+ let b:ale_idris_idris_executable = 'foobar'
+
+ AssertEqual 'foobar', ale_linters#idris#idris#GetExecutable(bufnr(''))
+
+Execute(The executable should be used in the command):
+ AssertEqual
+ \ ale#Escape('idris') . ' --total --warnpartial --warnreach --warnipkg --check %s',
+ \ ale_linters#idris#idris#GetCommand(bufnr(''))
+
+ let b:ale_idris_idris_executable = 'foobar'
+
+ AssertEqual
+ \ ale#Escape('foobar') . ' --total --warnpartial --warnreach --warnipkg --check %s',
+ \ ale_linters#idris#idris#GetCommand(bufnr(''))
+
+Execute(The options should be configurable):
+ let b:ale_idris_idris_options = '--something'
+
+ AssertEqual
+ \ ale#Escape('idris') . ' --something --check %s',
+ \ ale_linters#idris#idris#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_phpstan_command_callbacks.vader b/test/command_callback/test_phpstan_command_callbacks.vader
new file mode 100644
index 00000000..7366df8b
--- /dev/null
+++ b/test/command_callback/test_phpstan_command_callbacks.vader
@@ -0,0 +1,29 @@
+Before:
+ Save g:ale_php_phpstan_executable
+ Save g:ale_php_phpstan_level
+
+ unlet! g:ale_php_phpstan_executable
+ unlet! g:ale_php_phpstan_level
+
+ runtime ale_linters/php/phpstan.vim
+
+After:
+ Restore
+
+ call ale#linter#Reset()
+
+Execute(Custom executables should be used for the executable and command):
+ let g:ale_php_phpstan_executable = 'phpstan_test'
+
+ AssertEqual 'phpstan_test', ale_linters#php#phpstan#GetExecutable(bufnr(''))
+ AssertEqual
+ \ ale#Escape('phpstan_test') . ' analyze -l4 --errorFormat raw %s',
+ \ ale_linters#php#phpstan#GetCommand(bufnr(''))
+
+Execute(project with level set to 3):
+ call ale#test#SetFilename('phpstan-test-files/foo/test.php')
+ let g:ale_php_phpstan_level = 3
+
+ AssertEqual
+ \ ale#Escape('phpstan') . ' analyze -l3 --errorFormat raw %s',
+ \ ale_linters#php#phpstan#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_pycodestyle_command_callback.vader b/test/command_callback/test_pycodestyle_command_callback.vader
new file mode 100644
index 00000000..a5163461
--- /dev/null
+++ b/test/command_callback/test_pycodestyle_command_callback.vader
@@ -0,0 +1,23 @@
+Before:
+ 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
+
+Execute(The pycodestyle command callback should return default string):
+ AssertEqual '''pycodestyle'' -',
+ \ ale_linters#python#pycodestyle#GetCommand(bufnr(''))
+
+Execute(The pycodestyle command callback should allow options):
+ let g:ale_python_pycodestyle_options = '--exclude=test*.py'
+ AssertEqual '''pycodestyle'' --exclude=test*.py -',
+ \ ale_linters#python#pycodestyle#GetCommand(bufnr(''))
+
+Execute(The pycodestyle executable should be configurable):
+ let g:ale_python_pycodestyle_executable = '~/.local/bin/pycodestyle'
+ AssertEqual '''~/.local/bin/pycodestyle'' -',
+ \ ale_linters#python#pycodestyle#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader
new file mode 100644
index 00000000..0d8fef66
--- /dev/null
+++ b/test/command_callback/test_shellcheck_command_callback.vader
@@ -0,0 +1,47 @@
+Before:
+ Save g:ale_sh_shellcheck_exclusions
+ Save g:ale_sh_shellcheck_executable
+ Save g:ale_sh_shellcheck_options
+
+ unlet! g:ale_sh_shellcheck_exclusions
+ unlet! g:ale_sh_shellcheck_executable
+ unlet! g:ale_sh_shellcheck_options
+
+ runtime ale_linters/sh/shellcheck.vim
+
+After:
+ Restore
+
+ unlet! b:ale_sh_shellcheck_exclusions
+ unlet! b:ale_sh_shellcheck_executable
+ unlet! b:ale_sh_shellcheck_options
+ unlet! b:is_bash
+
+ call ale#linter#Reset()
+
+Execute(The default shellcheck command should be correct):
+ AssertEqual
+ \ 'shellcheck -f gcc -',
+ \ 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(''))
+
+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(''))
+
+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(''))
diff --git a/test/command_callback/test_standard_command_callback.vader b/test/command_callback/test_standard_command_callback.vader
new file mode 100644
index 00000000..fa90175b
--- /dev/null
+++ b/test/command_callback/test_standard_command_callback.vader
@@ -0,0 +1,98 @@
+Before:
+ Save g:ale_javascript_standard_executable
+ Save g:ale_javascript_standard_use_global
+ Save g:ale_javascript_standard_options
+
+ unlet! b:executable
+ unlet! g:ale_javascript_standard_executable
+ unlet! b:ale_javascript_standard_executable
+ unlet! g:ale_javascript_standard_use_global
+ unlet! g:ale_javascript_standard_options
+
+ call ale#test#SetDirectory('/testplugin/test/command_callback')
+ call ale#test#SetFilename('testfile.js')
+
+ runtime ale_linters/javascript/standard.vim
+
+After:
+ Restore
+
+ unlet! b:executable
+
+ let g:ale_has_override = {}
+
+ call ale#test#SetFilename('test.txt')
+
+ call ale#test#RestoreDirectory()
+ call ale#linter#Reset()
+
+Execute(bin/cmd.js paths should be preferred):
+ call ale#test#SetFilename('standard-test-files/with-cmd/testfile.js')
+
+ let b:executable = g:dir
+ \ . '/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js'
+
+ AssertEqual
+ \ b:executable,
+ \ ale_linters#javascript#standard#GetExecutable(bufnr(''))
+
+ AssertEqual
+ \ ale#Escape(b:executable) . ' --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
+
+Execute(.bin directories should be used too):
+ call ale#test#SetFilename('standard-test-files/with-bin/testfile.js')
+
+ let b:executable = g:dir
+ \ . '/standard-test-files/with-bin/node_modules/.bin/standard'
+
+ AssertEqual
+ \ b:executable,
+ \ ale_linters#javascript#standard#GetExecutable(bufnr(''))
+
+ AssertEqual
+ \ ale#Escape(b:executable) . ' --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
+
+Execute(.js files should be executed with node on Windows):
+ let g:ale_has_override['win32'] = 1
+
+ call ale#test#SetFilename('standard-test-files/with-cmd/testfile.js')
+
+ let b:executable = g:dir
+ \ . '/standard-test-files/with-cmd/node_modules/standard/bin/cmd.js'
+
+ AssertEqual
+ \ b:executable,
+ \ ale_linters#javascript#standard#GetExecutable(bufnr(''))
+
+ AssertEqual
+ \ 'node ' . ale#Escape(b:executable) . ' --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
+
+Execute(The global executable should be used otherwise):
+ AssertEqual
+ \ 'standard',
+ \ ale_linters#javascript#standard#GetExecutable(bufnr(''))
+
+ AssertEqual
+ \ ale#Escape('standard') . ' --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
+
+Execute(The global executable should be configurable):
+ let b:ale_javascript_standard_executable = 'foobar'
+
+ AssertEqual
+ \ 'foobar',
+ \ ale_linters#javascript#standard#GetExecutable(bufnr(''))
+
+ AssertEqual
+ \ ale#Escape('foobar') . ' --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
+
+Execute(The options should be configurable):
+ let b:ale_javascript_standard_options = '--wat'
+
+ AssertEqual
+ \ ale#Escape('standard') . ' --wat --stdin %s',
+ \ ale_linters#javascript#standard#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_thrift_command_callback.vader b/test/command_callback/test_thrift_command_callback.vader
new file mode 100644
index 00000000..43487f42
--- /dev/null
+++ b/test/command_callback/test_thrift_command_callback.vader
@@ -0,0 +1,61 @@
+Before:
+ Save g:ale_thrift_thrift_executable
+ Save g:ale_thrift_thrift_generators
+ Save g:ale_thrift_thrift_includes
+ Save g:ale_thrift_thrift_options
+
+ unlet! b:ale_thrift_thrift_executable
+ unlet! b:ale_thrift_thrift_generators
+ unlet! b:ale_thrift_thrift_includes
+ unlet! b:ale_thrift_thrift_options
+
+ function! GetCommand(buffer) abort
+ call ale#engine#InitBufferInfo(a:buffer)
+ let l:result = ale_linters#thrift#thrift#GetCommand(a:buffer)
+ call ale#engine#Cleanup(a:buffer)
+ return l:result
+ endfunction
+
+ runtime ale_linters/thrift/thrift.vim
+
+After:
+ Restore
+ delfunction GetCommand
+ unlet! b:ale_thrift_thrift_executable
+ unlet! b:ale_thrift_thrift_generators
+ unlet! b:ale_thrift_thrift_includes
+ unlet! b:ale_thrift_thrift_options
+ call ale#linter#Reset()
+
+Execute(The executable should be configurable):
+ AssertEqual 'thrift', ale_linters#thrift#thrift#GetExecutable(bufnr(''))
+
+ let b:ale_thrift_thrift_executable = 'foobar'
+ AssertEqual 'foobar', ale_linters#thrift#thrift#GetExecutable(bufnr(''))
+
+Execute(The executable should be used in the command):
+ Assert GetCommand(bufnr('%')) =~# "^'thrift'"
+
+ let b:ale_thrift_thrift_executable = 'foobar'
+ Assert GetCommand(bufnr('%')) =~# "^'foobar'"
+
+Execute(The list of generators should be configurable):
+ Assert GetCommand(bufnr('%')) =~# '--gen cpp'
+
+ let b:ale_thrift_thrift_generators = ['java', 'py:dynamic']
+ Assert GetCommand(bufnr('%')) =~# '--gen java --gen py:dynamic'
+
+ let b:ale_thrift_thrift_generators = []
+ Assert GetCommand(bufnr('%')) =~# '--gen cpp'
+
+Execute(The list of include paths should be configurable):
+ Assert GetCommand(bufnr('%')) !~# '-I'
+
+ let b:ale_thrift_thrift_includes = ['included/path']
+ Assert GetCommand(bufnr('%')) =~# '-I included/path'
+
+Execute(The string of compiler options should be configurable):
+ Assert GetCommand(bufnr('%')) =~# '-strict'
+
+ let b:ale_thrift_thrift_options = '-strict --allow-64bit-consts'
+ Assert GetCommand(bufnr('%')) =~# '-strict --allow-64bit-consts'
diff --git a/test/command_callback/test_tslint_command_callback.vader b/test/command_callback/test_tslint_command_callback.vader
new file mode 100644
index 00000000..51567951
--- /dev/null
+++ b/test/command_callback/test_tslint_command_callback.vader
@@ -0,0 +1,38 @@
+Before:
+ Save g:ale_typescript_tslint_executable
+ Save g:ale_typescript_tslint_config_path
+ Save g:ale_typescript_tslint_rules_dir
+ Save g:ale_typescript_tslint_use_global
+
+ unlet! g:ale_typescript_tslint_executable
+ unlet! g:ale_typescript_tslint_config_path
+ unlet! g:ale_typescript_tslint_rules_dir
+ unlet! g:ale_typescript_tslint_use_global
+
+ runtime ale_linters/typescript/tslint.vim
+
+ call ale#test#SetDirectory('/testplugin/test/command_callback')
+
+After:
+ Restore
+
+ unlet! b:ale_typescript_tslint_rules_dir
+
+ call ale#test#RestoreDirectory()
+ call ale#linter#Reset()
+
+Execute(The default tslint command should be correct):
+ AssertEqual
+ \ 'cd ''' . expand('%:p:h') . ''' && '
+ \ . 'tslint --format json %t',
+ \ ale_linters#typescript#tslint#GetCommand(bufnr(''))
+
+Execute(The rules directory option should be included if set):
+ let b:ale_typescript_tslint_rules_dir = '/foo/bar'
+
+ AssertEqual
+ \ 'cd ''' . expand('%:p:h') . ''' && '
+ \ . 'tslint --format json'
+ \ . ' -r ' . ale#Escape('/foo/bar')
+ \ . ' %t',
+ \ ale_linters#typescript#tslint#GetCommand(bufnr(''))
diff --git a/test/fixers/long-line-project/setup.cfg b/test/fixers/long-line-project/setup.cfg
new file mode 100644
index 00000000..43d7a3a1
--- /dev/null
+++ b/test/fixers/long-line-project/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 90
diff --git a/test/fixers/test_break_up_long_lines_python_fixer.vader b/test/fixers/test_break_up_long_lines_python_fixer.vader
new file mode 100644
index 00000000..5fd991f0
--- /dev/null
+++ b/test/fixers/test_break_up_long_lines_python_fixer.vader
@@ -0,0 +1,39 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ call ale#test#RestoreDirectory()
+
+Execute(Long lines with basic function calls should be broken up correctly):
+ AssertEqual
+ \ [
+ \ 'def foo():',
+ \ ' some_variable = this_is_a_longer_function(',
+ \ 'first_argument,',
+ \ ' second_argument,',
+ \ ' third_with_function_call(',
+ \ 'foo,',
+ \ ' bar,',
+ \ '))',
+ \ ],
+ \ ale#fixers#generic_python#BreakUpLongLines(bufnr(''), [
+ \ 'def foo():',
+ \ ' some_variable = this_is_a_longer_function(first_argument, second_argument, third_with_function_call(foo, bar))',
+ \ ])
+
+Execute(Longer lines should be permitted if a configuration file allows it):
+ call ale#test#SetFilename('long-line-project/foo/bar.py')
+
+ AssertEqual
+ \ [
+ \ 'x = this_line_is_between_79_and_90_characters(first, second, third, fourth, fifth)',
+ \ 'y = this_line_is_longer_than_90_characters(',
+ \ 'much_longer_word,',
+ \ ' another_longer_word,',
+ \ ' a_third_long_word,',
+ \ ')'
+ \ ],
+ \ ale#fixers#generic_python#BreakUpLongLines(bufnr(''), [
+ \ 'x = this_line_is_between_79_and_90_characters(first, second, third, fourth, fifth)',
+ \ 'y = this_line_is_longer_than_90_characters(much_longer_word, another_longer_word, a_third_long_word)',
+ \ ])
diff --git a/test/fixers/test_clangformat_fixer_callback.vader b/test/fixers/test_clangformat_fixer_callback.vader
new file mode 100644
index 00000000..a55576bf
--- /dev/null
+++ b/test/fixers/test_clangformat_fixer_callback.vader
@@ -0,0 +1,36 @@
+Before:
+ Save g:ale_c_clangformat_executable
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_c_clangformat_executable = 'xxxinvalid'
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The clang-format callback should return the correct default values):
+ call ale#test#SetFilename('c_paths/dummy.c')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape(g:ale_c_clangformat_executable)
+ \ . ' '
+ \ },
+ \ ale#fixers#clangformat#Fix(bufnr(''))
+
+Execute(The clangformat callback should include any additional options):
+ call ale#test#SetFilename('c_paths/dummy.c')
+ let g:ale_c_clangformat_options = '--some-option'
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape(g:ale_c_clangformat_executable)
+ \ . ' --some-option',
+ \ },
+ \ ale#fixers#clangformat#Fix(bufnr(''))
diff --git a/test/fixers/test_phpcbf_fixer_callback.vader b/test/fixers/test_phpcbf_fixer_callback.vader
new file mode 100644
index 00000000..c2fe3a66
--- /dev/null
+++ b/test/fixers/test_phpcbf_fixer_callback.vader
@@ -0,0 +1,56 @@
+Before:
+ Save g:ale_php_phpcbf_executable
+ Save g:ale_php_phpcbf_standard
+ Save g:ale_php_phpcbf_use_global
+
+ let g:ale_php_phpcbf_executable = 'phpcbf_test'
+ let g:ale_php_phpcbf_standard = ''
+ let g:ale_php_phpcbf_use_global = 0
+
+ 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 phpcbf should use local by default):
+ call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php')
+
+ AssertEqual
+ \ g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf',
+ \ ale#fixers#phpcbf#GetExecutable(bufnr(''))
+
+Execute(use-global should override local detection):
+ let g:ale_php_phpcbf_use_global = 1
+ call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php')
+
+ AssertEqual
+ \ 'phpcbf_test',
+ \ ale#fixers#phpcbf#GetExecutable(bufnr(''))
+
+Execute(project without phpcbf should use global):
+ call ale#test#SetFilename('php_paths/project-without-phpcbf/foo/test.php')
+
+ AssertEqual
+ \ 'phpcbf_test',
+ \ ale#fixers#phpcbf#GetExecutable(bufnr(''))
+
+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(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):
+ let g:ale_php_phpcbf_standard = 'phpcbf_ruleset.xml'
+ call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php')
+
+ AssertEqual
+ \ {'command': ale#Escape(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_fixer_callback.vader b/test/fixers/test_prettier_fixer_callback.vader
new file mode 100644
index 00000000..1eb24dae
--- /dev/null
+++ b/test/fixers/test_prettier_fixer_callback.vader
@@ -0,0 +1,58 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ Save g:ale_javascript_prettier_executable
+ Save g:ale_javascript_prettier_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_javascript_prettier_executable = 'xxxinvalid'
+ let g:ale_javascript_prettier_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+After:
+ let g:ale_has_override = {}
+ call ale#test#RestoreDirectory()
+
+Execute(The prettier callback should return the correct default values):
+ call ale#test#SetFilename('../prettier-test-files/testfile.js')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' %t'
+ \ . ' --write',
+ \ },
+ \ ale#fixers#prettier#Fix(bufnr(''))
+
+Execute(The prettier callback should include configuration files when the option is set):
+ let g:ale_javascript_prettier_use_local_config = 1
+ 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'
+ \ . ' --config ' . ale#Escape(simplify(g:dir . '/../prettier-test-files/with_config/.prettierrc'))
+ \ . ' --write',
+ \ },
+ \ ale#fixers#prettier#Fix(bufnr(''))
+
+Execute(The prettier callback should include custom prettier options):
+ 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'
+ \ . ' --no-semi'
+ \ . ' --config ' . ale#Escape(simplify(g:dir . '/../prettier-test-files/with_config/.prettierrc'))
+ \ . ' --write',
+ \ },
+ \ ale#fixers#prettier#Fix(bufnr(''))
diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader
index e3383537..87d56d07 100644
--- a/test/fixers/test_rubocop_fixer_callback.vader
+++ b/test/fixers/test_rubocop_fixer_callback.vader
@@ -1,8 +1,10 @@
Before:
Save g:ale_ruby_rubocop_executable
+ Save g:ale_ruby_rubocop_options
" Use an invalid global executable, so we don't match it.
let g:ale_ruby_rubocop_executable = 'xxxinvalid'
+ let g:ale_ruby_rubocop_options = ''
call ale#test#SetDirectory('/testplugin/test/fixers')
silent cd ..
@@ -36,3 +38,17 @@ Execute(The rubocop callback should include configuration files):
\ . ' --auto-correct %t',
\ },
\ ale#fixers#rubocop#Fix(bufnr(''))
+
+Execute(The rubocop callback should include custom rubocop options):
+ let g:ale_ruby_rubocop_options = '--except Lint/Debugger'
+ call ale#test#SetFilename('ruby_paths/with_config/dummy.rb')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_ruby_rubocop_executable)
+ \ . ' --config ' . ale#Escape(g:dir . '/ruby_paths/with_config/.rubocop.yml')
+ \ . ' --except Lint/Debugger'
+ \ . ' --auto-correct %t',
+ \ },
+ \ ale#fixers#rubocop#Fix(bufnr(''))
diff --git a/test/fixers/test_swiftformat_fixer_callback.vader b/test/fixers/test_swiftformat_fixer_callback.vader
new file mode 100644
index 00000000..e3674ded
--- /dev/null
+++ b/test/fixers/test_swiftformat_fixer_callback.vader
@@ -0,0 +1,38 @@
+Before:
+ Save g:ale_swift_swiftformat_executable
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_swift_swiftformat_executable = 'xxxinvalid'
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The swiftformat callback should return the correct default values):
+ call ale#test#SetFilename('swift_paths/dummy.swift')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_swift_swiftformat_executable)
+ \ . ' %t ',
+ \ },
+ \ ale#fixers#swiftformat#Fix(bufnr(''))
+
+Execute(The swiftformat callback should include any additional options):
+ call ale#test#SetFilename('swift_paths/dummy.swift')
+ let g:ale_swift_swiftformat_options = '--some-option'
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_swift_swiftformat_executable)
+ \ . ' %t --some-option',
+ \ },
+ \ ale#fixers#swiftformat#Fix(bufnr(''))
diff --git a/test/handler/test_eslint_handler.vader b/test/handler/test_eslint_handler.vader
index 7ac26c72..943e177f 100644
--- a/test/handler/test_eslint_handler.vader
+++ b/test/handler/test_eslint_handler.vader
@@ -1,4 +1,11 @@
+Before:
+ Save g:ale_javascript_eslint_suppress_eslintignore
+
+ let g:ale_javascript_eslint_suppress_eslintignore = 0
+
After:
+ Restore
+
unlet! g:config_error_lines
Execute(The eslint handler should parse lines correctly):
@@ -206,3 +213,24 @@ Execute(The eslint hint about using typescript-eslint-parser):
\ ale#handlers#eslint#Handle(bufnr(''), [
\ 'foo.ts:451:2: Parsing error: Unexpected token ) [Error]',
\ ])
+
+Execute(eslint should warn about ignored files by default):
+ AssertEqual
+ \ [{
+ \ 'lnum': 0,
+ \ 'col': 0,
+ \ 'type': 'W',
+ \ 'text': 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]'
+ \ }],
+ \ ale#handlers#eslint#Handle(347, [
+ \ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]',
+ \ ])
+
+Execute(eslint should not warn about ignored files when explicitly disabled):
+ let g:ale_javascript_eslint_suppress_eslintignore = 1
+
+ AssertEqual
+ \ [],
+ \ ale#handlers#eslint#Handle(347, [
+ \ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]',
+ \ ])
diff --git a/test/handler/test_idris_handler.vader b/test/handler/test_idris_handler.vader
new file mode 100644
index 00000000..1c20be7b
--- /dev/null
+++ b/test/handler/test_idris_handler.vader
@@ -0,0 +1,52 @@
+Before:
+ runtime ale_linters/idris/idris.vim
+
+Execute(The idris handler should parse messages that reference a single column):
+ call ale#test#SetFilename('/tmp/foo.idr')
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 4,
+ \ 'col': 5,
+ \ 'type': 'E',
+ \ 'text': 'When checking right hand side of main with expected type IO () When checking an application of function Prelude.Monad.>>=: Type mismatch between IO () (Type of putStrLn _) and _ -> _ (Is putStrLn _ applied to too many arguments?) Specifically: Type mismatch between IO and \uv => _ -> uv'
+ \ }
+ \ ],
+ \ ale_linters#idris#idris#Handle(bufnr(''), [
+ \ '/tmp/foo.idr:4:5:',
+ \ 'When checking right hand side of main with expected type',
+ \ ' IO ()',
+ \ '',
+ \ 'When checking an application of function Prelude.Monad.>>=:',
+ \ ' Type mismatch between',
+ \ ' IO () (Type of putStrLn _)',
+ \ ' and',
+ \ ' _ -> _ (Is putStrLn _ applied to too many arguments?)',
+ \ '',
+ \ ' Specifically:',
+ \ ' Type mismatch between',
+ \ ' IO',
+ \ ' and',
+ \ ' \uv => _ -> uv',
+ \ ])
+
+Execute(The idris handler should parse messages that reference a column range):
+ call ale#test#SetFilename('/tmp/foo.idr')
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 11,
+ \ 'col': 11,
+ \ 'type': 'E',
+ \ 'text': 'When checking right hand side of Main.case block in main at /tmp/foo.idr:10:10 with expected type IO () Last statement in do block must be an expression'
+ \ }
+ \ ],
+ \ ale_linters#idris#idris#Handle(bufnr(''), [
+ \ '/tmp/foo.idr:11:11-13:',
+ \ 'When checking right hand side of Main.case block in main at /tmp/foo.idr:10:10 with expected type',
+ \ ' IO ()',
+ \ '',
+ \ 'Last statement in do block must be an expression',
+ \ ])
diff --git a/test/handler/test_mypy_handler.vader b/test/handler/test_mypy_handler.vader
index c949b1aa..d0cf91e2 100644
--- a/test/handler/test_mypy_handler.vader
+++ b/test/handler/test_mypy_handler.vader
@@ -11,16 +11,39 @@ Execute(The mypy handler should parse lines correctly):
AssertEqual
\ [
\ {
+ \ 'lnum': 768,
+ \ 'col': 38,
+ \ 'filename': 'foo/bar/foo/bar/baz.py',
+ \ 'type': 'E',
+ \ 'text': 'Cannot determine type of ''SOME_SYMBOL''',
+ \ },
+ \ {
+ \ 'lnum': 821,
+ \ 'col': 38,
+ \ 'filename': 'foo/bar/foo/bar/baz.py',
+ \ 'type': 'E',
+ \ 'text': 'Cannot determine type of ''SOME_SYMBOL''',
+ \ },
+ \ {
+ \ 'lnum': 38,
+ \ 'col': 44,
+ \ 'filename': 'foo/bar/foo/bar/other.py',
+ \ 'type': 'E',
+ \ 'text': 'Cannot determine type of ''ANOTHER_SYMBOL''',
+ \ },
+ \ {
\ 'lnum': 15,
\ 'col': 3,
- \ 'text': 'Argument 1 to "somefunc" has incompatible type "int"; expected "str"',
+ \ 'filename': 'foo/bar/foo/bar/__init__.py',
\ 'type': 'E',
+ \ 'text': 'Argument 1 to "somefunc" has incompatible type "int"; expected "str"'
\ },
\ {
\ 'lnum': 72,
\ 'col': 1,
- \ 'text': 'Some warning',
+ \ 'filename': 'foo/bar/foo/bar/__init__.py',
\ 'type': 'W',
+ \ 'text': 'Some warning',
\ },
\ ],
\ ale_linters#python#mypy#Handle(bufnr(''), [
@@ -47,8 +70,9 @@ Execute(The mypy handler should handle Windows names with spaces):
\ {
\ 'lnum': 4,
\ 'col': 0,
- \ 'text': "No library stub file for module 'django.db'",
+ \ 'filename': 'C:\something\with spaces.py',
\ 'type': 'E',
+ \ 'text': 'No library stub file for module ''django.db''',
\ },
\ ],
\ ale_linters#python#mypy#Handle(bufnr(''), [
diff --git a/test/test_phpstan_executable_detection.vader b/test/handler/test_phpstan_handler.vader
index 24ba8cd8..207a7758 100644
--- a/test/test_phpstan_executable_detection.vader
+++ b/test/handler/test_phpstan_handler.vader
@@ -1,9 +1,4 @@
Before:
- Save g:ale_php_phpstan_executable
- Save g:ale_php_phpstan_level
-
- let g:ale_php_phpstan_executable = 'phpstan_test'
-
call ale#test#SetDirectory('/testplugin/test')
runtime ale_linters/php/phpstan.vim
@@ -14,45 +9,14 @@ After:
call ale#test#RestoreDirectory()
call ale#linter#Reset()
-Execute(project with level set to 3):
- call ale#test#SetFilename('phpstan-test-files/foo/test.php')
- let g:ale_php_phpstan_level = 3
-
- AssertEqual
- \ 'phpstan_test analyze -l3 --errorFormat raw %s',
- \ ale_linters#php#phpstan#GetCommand(bufnr(''))
-
-Execute(project with default level):
- call ale#test#SetFilename('phpstan-test-files/foo/test.php')
-
- AssertEqual
- \ 'phpstan_test analyze -l4 --errorFormat raw %s',
- \ ale_linters#php#phpstan#GetCommand(bufnr(''))
-
-Execute(parse output without errors):
+Execute(Output without errors should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/foo/test.php')
AssertEqual
\ [],
\ ale_linters#php#phpstan#Handle(bufnr(''), [" 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%"])
-Execute(parse output with one error):
- call ale#test#SetFilename('phpstan-test-files/foo/test.php')
-
- AssertEqual
- \ [
- \ {
- \ 'lnum': 9,
- \ 'text': 'Access to an undefined property Test::$var.',
- \ 'type': 'W'
- \ }
- \ ],
- \ ale_linters#php#phpstan#Handle(bufnr(''), [
- \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%',
- \ 'phpstan-test-files/foo/test.php:9:Access to an undefined property Test::$var.',
- \])
-
-Execute(parse output with three errors):
+Execute(Output with some errors should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/foo/test.php')
AssertEqual
@@ -80,7 +44,7 @@ Execute(parse output with three errors):
\ 'phpstan-test-files/foo/test.php:192:Invalid command testCommand.',
\])
-Execute(parse output for windows filesystem):
+Execute(Output should be parsed correctly with Windows paths):
call ale#test#SetFilename('phpstan-test-files/foo/test.php')
AssertEqual
@@ -96,7 +60,7 @@ Execute(parse output for windows filesystem):
\ 'D:\phpstan-test-files\foo\test.php:9:Access to an undefined property Test::$var.',
\])
-Execute(parse output for not php file):
+Execute(Output for .inc files should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/test.inc')
AssertEqual
diff --git a/test/handler/test_pycodestyle_handler.vader b/test/handler/test_pycodestyle_handler.vader
new file mode 100644
index 00000000..cc83fc83
--- /dev/null
+++ b/test/handler/test_pycodestyle_handler.vader
@@ -0,0 +1,48 @@
+Before:
+ runtime ale_linters/python/pycodestyle.vim
+
+After:
+ call ale#linter#Reset()
+ silent file something_else.py
+
+Execute(The pycodestyle handler should parse output):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 69,
+ \ 'col': 11,
+ \ 'text': 'E401 multiple imports on one line',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 77,
+ \ 'col': 1,
+ \ 'text': 'E302 expected 2 blank lines, found 1',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 88,
+ \ 'col': 5,
+ \ 'text': 'E301 expected 1 blank line, found 0',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 222,
+ \ 'col': 34,
+ \ 'text': 'W602 deprecated form of raising exception',
+ \ 'type': 'W',
+ \ },
+ \ {
+ \ 'lnum': 544,
+ \ 'col': 21,
+ \ 'text': 'W601 .has_key() is deprecated, use ''in''',
+ \ 'type': 'W',
+ \ },
+ \ ],
+ \ ale_linters#python#pycodestyle#Handle(bufnr(''), [
+ \ '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''',
+ \ ])
diff --git a/test/handler/test_syntaxerl_handler.vader b/test/handler/test_syntaxerl_handler.vader
index 1308ec84..95f2bfef 100644
--- a/test/handler/test_syntaxerl_handler.vader
+++ b/test/handler/test_syntaxerl_handler.vader
@@ -4,7 +4,7 @@ Before:
After:
call ale#linter#Reset()
-Execute:
+Execute (Handle SyntaxErl output):
AssertEqual
\ [
\ {
@@ -18,7 +18,7 @@ Execute:
\ 'type': 'W',
\ },
\ ],
- \ ale_linters#erlang#syntaxerl#Handle(42, [
+ \ ale_linters#erlang#syntaxerl#Handle(bufnr(''), [
\ "/tmp/v2wDixk/1/module.erl:42: syntax error before: ','",
\ '/tmp/v2wDixk/2/module.erl:42: warning: function foo/0 is unused',
\ ])
diff --git a/test/handler/test_thrift_handler.vader b/test/handler/test_thrift_handler.vader
new file mode 100644
index 00000000..9bdb9378
--- /dev/null
+++ b/test/handler/test_thrift_handler.vader
@@ -0,0 +1,63 @@
+Before:
+ runtime ale_linters/thrift/thrift.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The thrift handler should handle basic warnings and errors):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 17,
+ \ 'col': 0,
+ \ 'type': 'W',
+ \ 'text': 'The "byte" type is a compatibility alias for "i8". Use i8" to emphasize the signedness of this type.',
+ \ },
+ \ {
+ \ 'lnum': 20,
+ \ 'col': 0,
+ \ 'type': 'W',
+ \ 'text': 'Could not find include file include.thrift',
+ \ },
+ \ {
+ \ 'lnum': 83,
+ \ 'col': 0,
+ \ 'type': 'E',
+ \ 'text': 'Enum FOO is already defined!',
+ \ },
+ \ ],
+ \ ale_linters#thrift#thrift#Handle(1, [
+ \ '[WARNING:/path/filename.thrift:17] The "byte" type is a compatibility alias for "i8". Use i8" to emphasize the signedness of this type.',
+ \ '[WARNING:/path/filename.thrift:20] Could not find include file include.thrift',
+ \ '[FAILURE:/path/filename.thrift:83] Enum FOO is already defined!',
+ \ ])
+
+Execute(The thrift handler should handle multiline errors):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 75,
+ \ 'col': 0,
+ \ 'type': 'E',
+ \ 'text': 'This integer is too big: "11111111114213213453243"',
+ \ },
+ \ {
+ \ 'lnum': 76,
+ \ 'col': 0,
+ \ 'type': 'E',
+ \ 'text': 'Implicit field keys are deprecated and not allowed with -strict',
+ \ },
+ \ {
+ \ 'lnum': 77,
+ \ 'col': 0,
+ \ 'type': 'E',
+ \ 'text': "Unknown error (last token was ';')",
+ \ },
+ \ ],
+ \ ale_linters#thrift#thrift#Handle(1, [
+ \ "[ERROR:/path/filename.thrift:75] (last token was '11111111114213213453243')",
+ \ 'This integer is too big: "11111111114213213453243"',
+ \ "[ERROR:/path/filename.thrift:76] (last token was ';')",
+ \ 'Implicit field keys are deprecated and not allowed with -strict',
+ \ "[ERROR:/path/filename.thrift:77] (last token was ';')",
+ \ ])
diff --git a/test/handler/test_tslint_handler.vader b/test/handler/test_tslint_handler.vader
index 704123dd..5c8679a4 100644
--- a/test/handler/test_tslint_handler.vader
+++ b/test/handler/test_tslint_handler.vader
@@ -15,28 +15,40 @@ Execute(The tslint handler should parse lines correctly):
\ {
\ 'lnum': 1,
\ 'col': 15,
+ \ 'filename': expand('%:p:h') . '/test.ts',
\ 'end_lnum': 1,
- \ 'end_col': 15,
- \ 'text': 'Missing semicolon',
\ 'type': 'E',
+ \ 'end_col': 15,
+ \ 'text': 'semicolon: Missing semicolon'
\ },
\ {
\ 'lnum': 2,
\ 'col': 8,
+ \ 'filename': expand('%:p:h') . '/test.ts',
\ 'end_lnum': 3,
+ \ 'type': 'W',
\ 'end_col': 12,
- \ 'text': 'Something else',
+ \ 'text': 'Something else'
+ \ },
+ \ {
+ \ 'lnum': 2,
+ \ 'col': 8,
+ \ 'filename': expand('%:p:h') . '/something-else.ts',
+ \ 'end_lnum': 3,
\ 'type': 'W',
+ \ 'end_col': 12,
+ \ 'text': 'something: Something else'
\ },
\ {
\ 'lnum': 31,
\ 'col': 9,
+ \ 'filename': expand('%:p:h') . '/test.ts',
\ 'end_lnum': 31,
- \ 'end_col': 20,
- \ 'text': 'Calls to console.log are not allowed.',
\ 'type': 'E',
+ \ 'end_col': 20,
+ \ 'text': 'no-console: Calls to console.log are not allowed.'
\ },
- \ ],
+ \ ] ,
\ ale_linters#typescript#tslint#Handle(bufnr(''), [json_encode([
\ {
\ 'endPosition': {
@@ -50,7 +62,7 @@ Execute(The tslint handler should parse lines correctly):
\ 'innerStart': 14,
\ 'innerText': ';'
\ },
- \ 'name': 'app/test.ts',
+ \ 'name': 'test.ts',
\ 'ruleName': 'semicolon',
\ 'ruleSeverity': 'ERROR',
\ 'startPosition': {
@@ -71,8 +83,7 @@ Execute(The tslint handler should parse lines correctly):
\ 'innerStart': 14,
\ 'innerText': ';'
\ },
- \ 'name': 'app/test.ts',
- \ 'ruleName': 'something',
+ \ 'name': 'test.ts',
\ 'ruleSeverity': 'WARNING',
\ 'startPosition': {
\ 'character': 7,
@@ -92,7 +103,7 @@ Execute(The tslint handler should parse lines correctly):
\ 'innerStart': 14,
\ 'innerText': ';'
\ },
- \ 'name': 'app/something-else.ts',
+ \ 'name': 'something-else.ts',
\ 'ruleName': 'something',
\ 'ruleSeverity': 'WARNING',
\ 'startPosition': {
@@ -108,7 +119,7 @@ Execute(The tslint handler should parse lines correctly):
\ "position": 14590
\ },
\ "failure": "Calls to console.log are not allowed.",
- \ 'name': 'app/test.ts',
+ \ 'name': 'test.ts',
\ "ruleName": "no-console",
\ "startPosition": {
\ "character": 8,
diff --git a/test/handler/test_vint_handler.vader b/test/handler/test_vint_handler.vader
index c5af85c4..8747979c 100644
--- a/test/handler/test_vint_handler.vader
+++ b/test/handler/test_vint_handler.vader
@@ -1,6 +1,10 @@
-Execute(The vint handler should parse error messages correctly):
- :file! gxc.vim
+Before:
+ runtime ale_linters/vim/vint.vim
+
+After:
+ call ale#linter#Reset()
+Execute(The vint handler should parse error messages correctly):
AssertEqual
\ [
\ {
@@ -12,25 +16,44 @@ Execute(The vint handler should parse error messages correctly):
\ {
\ 'lnum': 3,
\ 'col': 17,
+ \ 'end_col': 18,
\ 'text': 'Use robust operators ''==#'' or ''==?'' instead of ''=='' (see Google VimScript Style Guide (Matching))',
\ 'type': 'W',
\ },
\ {
\ 'lnum': 3,
\ 'col': 8,
+ \ 'end_col': 15,
\ 'text': 'Make the scope explicit like ''l:filename'' (see Anti-pattern of vimrc (Scope of identifier))',
\ 'type': 'W',
\ },
\ {
\ 'lnum': 7,
\ 'col': 8,
+ \ 'end_col': 15,
\ 'text': 'Undefined variable: filename (see :help E738)',
\ 'type': 'W',
\ },
+ \ {
+ \ 'lnum': 8,
+ \ 'col': 11,
+ \ 'end_col': 16,
+ \ 'text': 'E128: Function name must start with a capital or contain a colon: foobar (see ynkdir/vim-vimlparser)',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 9,
+ \ 'col': 12,
+ \ 'end_col': 13,
+ \ 'text': 'Use robust operators ''=~#'' or ''=~?'' instead of ''=~'' (see Google VimScript Style Guide (Matching))',
+ \ 'type': 'W',
+ \ },
\ ],
- \ ale#handlers#gcc#HandleGCCFormat(347, [
+ \ ale_linters#vim#vint#Handle(bufnr(''), [
\ 'gcc.vim:1:1: warning: Use scriptencoding when multibyte char exists (see :help :script encoding)',
\ 'gcc.vim:3:17: warning: Use robust operators `==#` or `==?` instead of `==` (see Google VimScript Style Guide (Matching))',
\ 'gcc.vim:3:8: style_problem: Make the scope explicit like `l:filename` (see Anti-pattern of vimrc (Scope of identifier))',
\ 'gcc.vim:7:8: warning: Undefined variable: filename (see :help E738)',
+ \ 'gcc.vim:8:11: error: E128: Function name must start with a capital or contain a colon: foobar (see ynkdir/vim-vimlparser)',
+ \ 'gcc.vim:9:12: warning: Use robust operators `=~#` or `=~?` instead of `=~` (see Google VimScript Style Guide (Matching))',
\ ])
diff --git a/test/prettier-test-files/testfile.js b/test/prettier-test-files/testfile.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/prettier-test-files/testfile.js
diff --git a/test/prettier-test-files/with_config/.prettierrc b/test/prettier-test-files/with_config/.prettierrc
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/prettier-test-files/with_config/.prettierrc
diff --git a/test/prettier-test-files/with_config/testfile.js b/test/prettier-test-files/with_config/testfile.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/prettier-test-files/with_config/testfile.js
diff --git a/test/sign/test_sign_parsing.vader b/test/sign/test_sign_parsing.vader
index ee112a1b..07848afb 100644
--- a/test/sign/test_sign_parsing.vader
+++ b/test/sign/test_sign_parsing.vader
@@ -1,6 +1,6 @@
Execute (Parsing English signs should work):
AssertEqual
- \ [[9, 1000001, 'ALEWarningSign']],
+ \ [0, [[9, 1000001, 'ALEWarningSign']]],
\ ale#sign#ParseSigns([
\ 'Signs for app.js:',
\ ' line=9 id=1000001 name=ALEWarningSign',
@@ -8,20 +8,28 @@ Execute (Parsing English signs should work):
Execute (Parsing Russian signs should work):
AssertEqual
- \ [[1, 1000001, 'ALEErrorSign']],
+ \ [0, [[1, 1000001, 'ALEErrorSign']]],
\ ale#sign#ParseSigns([' строка=1 id=1000001 имя=ALEErrorSign'])
Execute (Parsing Japanese signs should work):
AssertEqual
- \ [[1, 1000001, 'ALEWarningSign']],
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
\ ale#sign#ParseSigns([' 行=1 識別子=1000001 名前=ALEWarningSign'])
Execute (Parsing Spanish signs should work):
AssertEqual
- \ [[12, 1000001, 'ALEWarningSign']],
+ \ [0, [[12, 1000001, 'ALEWarningSign']]],
\ ale#sign#ParseSigns([' línea=12 id=1000001 nombre=ALEWarningSign'])
Execute (Parsing Italian signs should work):
AssertEqual
- \ [[1, 1000001, 'ALEWarningSign']],
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
\ ale#sign#ParseSigns([' riga=1 id=1000001, nome=ALEWarningSign'])
+ \
+Execute (The sign parser should indicate if the dummy sign is set):
+ AssertEqual
+ \ [1, [[1, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([
+ \ ' строка=1 id=1000001 имя=ALEErrorSign',
+ \ ' line=1 id=1000000 name=ALEDummySign',
+ \ ])
diff --git a/test/sign/test_sign_placement.vader b/test/sign/test_sign_placement.vader
index 77f9bb6d..abae765b 100644
--- a/test/sign/test_sign_placement.vader
+++ b/test/sign/test_sign_placement.vader
@@ -1,4 +1,8 @@
Before:
+ Save g:ale_set_signs
+
+ let g:ale_set_signs = 1
+
function! GenerateResults(buffer, output)
return [
\ {
@@ -65,41 +69,43 @@ Before:
\})
After:
+ Restore
+
unlet! g:loclist
delfunction GenerateResults
delfunction ParseSigns
call ale#linter#Reset()
sign unplace *
-Execute(ale#sign#GetSignType should return the right sign types):
- AssertEqual 'ALEErrorSign', ale#sign#GetSignType([{'type': 'E'}])
- AssertEqual 'ALEStyleErrorSign', ale#sign#GetSignType([{'type': 'E', 'sub_type': 'style'}])
- AssertEqual 'ALEWarningSign', ale#sign#GetSignType([{'type': 'W'}])
- AssertEqual 'ALEStyleWarningSign', ale#sign#GetSignType([{'type': 'W', 'sub_type': 'style'}])
- AssertEqual 'ALEInfoSign', ale#sign#GetSignType([{'type': 'I'}])
- AssertEqual 'ALEErrorSign', ale#sign#GetSignType([
+Execute(ale#sign#GetSignName should return the right sign names):
+ AssertEqual 'ALEErrorSign', ale#sign#GetSignName([{'type': 'E'}])
+ AssertEqual 'ALEStyleErrorSign', ale#sign#GetSignName([{'type': 'E', 'sub_type': 'style'}])
+ AssertEqual 'ALEWarningSign', ale#sign#GetSignName([{'type': 'W'}])
+ AssertEqual 'ALEStyleWarningSign', ale#sign#GetSignName([{'type': 'W', 'sub_type': 'style'}])
+ AssertEqual 'ALEInfoSign', ale#sign#GetSignName([{'type': 'I'}])
+ AssertEqual 'ALEErrorSign', ale#sign#GetSignName([
\ {'type': 'E'},
\ {'type': 'W'},
\ {'type': 'I'},
\ {'type': 'E', 'sub_type': 'style'},
\ {'type': 'W', 'sub_type': 'style'},
\])
- AssertEqual 'ALEWarningSign', ale#sign#GetSignType([
+ AssertEqual 'ALEWarningSign', ale#sign#GetSignName([
\ {'type': 'W'},
\ {'type': 'I'},
\ {'type': 'E', 'sub_type': 'style'},
\ {'type': 'W', 'sub_type': 'style'},
\])
- AssertEqual 'ALEInfoSign', ale#sign#GetSignType([
+ AssertEqual 'ALEInfoSign', ale#sign#GetSignName([
\ {'type': 'I'},
\ {'type': 'E', 'sub_type': 'style'},
\ {'type': 'W', 'sub_type': 'style'},
\])
- AssertEqual 'ALEStyleErrorSign', ale#sign#GetSignType([
+ AssertEqual 'ALEStyleErrorSign', ale#sign#GetSignName([
\ {'type': 'E', 'sub_type': 'style'},
\ {'type': 'W', 'sub_type': 'style'},
\])
- AssertEqual 'ALEStyleWarningSign', ale#sign#GetSignType([
+ AssertEqual 'ALEStyleWarningSign', ale#sign#GetSignName([
\ {'type': 'W', 'sub_type': 'style'},
\])
@@ -124,34 +130,32 @@ Execute(The current signs should be set for running a job):
\ ],
\ ParseSigns()
-
Execute(Loclist items with sign_id values should be kept):
- exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('%')
- exec 'sign place 1000348 line=15 name=ALEErrorSign buffer=' . bufnr('%')
- exec 'sign place 1000349 line=16 name=ALEWarningSign buffer=' . bufnr('%')
+ exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000348 line=15 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000349 line=16 name=ALEWarningSign buffer=' . bufnr('')
let g:loclist = [
- \ {'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000348},
- \ {'lnum': 2, 'col': 1, 'type': 'W', 'text': 'b', 'sign_id': 1000349},
- \ {'lnum': 3, 'col': 1, 'type': 'E', 'text': 'c', 'sign_id': 1000347},
- \ {'lnum': 4, 'col': 1, 'type': 'W', 'text': 'd'},
- \ {'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e'},
- \ {'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f'},
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000348},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'W', 'text': 'b', 'sign_id': 1000349},
+ \ {'bufnr': bufnr(''), 'lnum': 3, 'col': 1, 'type': 'E', 'text': 'c', 'sign_id': 1000347},
+ \ {'bufnr': bufnr(''), 'lnum': 4, 'col': 1, 'type': 'W', 'text': 'd'},
+ \ {'bufnr': bufnr(''), 'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e'},
+ \ {'bufnr': bufnr(''), 'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f'},
\]
- call ale#sign#SetSigns(bufnr('%'), g:loclist)
- call ale#sign#RemoveDummySignIfNeeded(bufnr('%'))
+ call ale#sign#SetSigns(bufnr(''), g:loclist)
" Sign IDs from before should be kept, and new signs should use
" IDs that haven't been used yet.
AssertEqual
\ [
- \ {'lnum': 3, 'col': 1, 'type': 'E', 'text': 'c', 'sign_id': 1000347},
- \ {'lnum': 4, 'col': 1, 'type': 'W', 'text': 'd', 'sign_id': 1000350},
- \ {'lnum': 15, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000351},
- \ {'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e', 'sign_id': 1000351},
- \ {'lnum': 16, 'col': 1, 'type': 'W', 'text': 'b', 'sign_id': 1000352},
- \ {'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f', 'sign_id': 1000352},
+ \ {'bufnr': bufnr(''), 'lnum': 3, 'col': 1, 'type': 'E', 'text': 'c', 'sign_id': 1000347},
+ \ {'bufnr': bufnr(''), 'lnum': 4, 'col': 1, 'type': 'W', 'text': 'd', 'sign_id': 1000350},
+ \ {'bufnr': bufnr(''), 'lnum': 15, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000348},
+ \ {'bufnr': bufnr(''), 'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e', 'sign_id': 1000348},
+ \ {'bufnr': bufnr(''), 'lnum': 16, 'col': 1, 'type': 'W', 'text': 'b', 'sign_id': 1000351},
+ \ {'bufnr': bufnr(''), 'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f', 'sign_id': 1000351},
\ ],
\ g:loclist
@@ -161,13 +165,104 @@ Execute(Loclist items with sign_id values should be kept):
AssertEqual
\ [
\ ['15', '1000348', 'ALEErrorSign'],
- \ ['15', '1000351', 'ALEErrorSign'],
- \ ['16', '1000349', 'ALEWarningSign'],
- \ ['16', '1000352', 'ALEErrorSign'],
+ \ ['16', '1000351', 'ALEErrorSign'],
\ ['3', '1000347', 'ALEErrorSign'],
\ ['4', '1000350', 'ALEWarningSign'],
\ ],
\ sort(ParseSigns())
-Execute(No excpetions should be thrown when setting signs for invalid buffers):
+Execute(Items for other buffers should be ignored):
+ let g:loclist = [
+ \ {'bufnr': bufnr('') - 1, 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a'},
+ \ {'bufnr': bufnr('') - 1, 'lnum': 2, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000347},
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'W', 'text': 'b'},
+ \ {'bufnr': bufnr(''), 'lnum': 3, 'col': 1, 'type': 'E', 'text': 'c'},
+ \ {'bufnr': bufnr(''), 'lnum': 4, 'col': 1, 'type': 'W', 'text': 'd'},
+ \ {'bufnr': bufnr(''), 'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e'},
+ \ {'bufnr': bufnr(''), 'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f'},
+ \ {'bufnr': bufnr('') + 1, 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a'},
+ \]
+
+ call ale#sign#SetSigns(bufnr(''), g:loclist)
+
+ AssertEqual
+ \ [
+ \ ['1', '1000001', 'ALEErrorSign'],
+ \ ['15', '1000005', 'ALEWarningSign'],
+ \ ['16', '1000006', 'ALEErrorSign'],
+ \ ['2', '1000002', 'ALEWarningSign'],
+ \ ['3', '1000003', 'ALEErrorSign'],
+ \ ['4', '1000004', 'ALEWarningSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+Execute(Signs should be downgraded correctly):
+ call ale#sign#SetSigns(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'W', 'text': 'x'},
+ \])
+
+ AssertEqual
+ \ [
+ \ ['1', '1000001', 'ALEErrorSign'],
+ \ ['2', '1000002', 'ALEWarningSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+ call ale#sign#SetSigns(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'W', 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'I', 'text': 'x'},
+ \])
+
+ AssertEqual
+ \ [
+ \ ['1', '1000003', 'ALEWarningSign'],
+ \ ['2', '1000004', 'ALEInfoSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+Execute(Signs should be upgraded correctly):
+ call ale#sign#SetSigns(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'W', 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'I', 'text': 'x'},
+ \])
+
+ AssertEqual
+ \ [
+ \ ['1', '1000001', 'ALEWarningSign'],
+ \ ['2', '1000002', 'ALEInfoSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+ call ale#sign#SetSigns(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1, 'type': 'W', 'text': 'x'},
+ \])
+
+ AssertEqual
+ \ [
+ \ ['1', '1000003', 'ALEErrorSign'],
+ \ ['2', '1000004', 'ALEWarningSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+Execute(It should be possible to clear signs with empty lists):
+ let g:loclist = [
+ \ {'bufnr': bufnr(''), 'lnum': 16, 'col': 2, 'type': 'E', 'text': 'f'},
+ \]
+
+ call ale#sign#SetSigns(bufnr(''), g:loclist)
+
+ AssertEqual
+ \ [
+ \ ['16', '1000001', 'ALEErrorSign'],
+ \ ],
+ \ sort(ParseSigns())
+
+ call ale#sign#SetSigns(bufnr(''), [])
+
+ AssertEqual [], ParseSigns()
+
+Execute(No exceptions should be thrown when setting signs for invalid buffers):
call ale#sign#SetSigns(123456789, [{'lnum': 15, 'col': 2, 'type': 'W', 'text': 'e'}])
diff --git a/test/test_ale_fix.vader b/test/test_ale_fix.vader
index bafeee2b..b5c16724 100644
--- a/test/test_ale_fix.vader
+++ b/test/test_ale_fix.vader
@@ -82,6 +82,8 @@ After:
call ale#fix#registry#ResetToDefaults()
call ale#linter#Reset()
+ setlocal buftype=nofile
+
if filereadable('fix_test_file')
call delete('fix_test_file')
endif
@@ -245,6 +247,9 @@ Execute(ALEFix should save files on the save event):
let g:ale_fixers.testft = ['AddDollars']
+ " We have to set the buftype to empty so the file will be written.
+ setlocal buftype=
+
call SetUpLinters()
call ale#events#SaveEvent(bufnr(''))
diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader
index a8913df4..8ab5ad54 100644
--- a/test/test_ale_info.vader
+++ b/test/test_ale_info.vader
@@ -1,14 +1,19 @@
Before:
Save g:ale_warn_about_trailing_whitespace
Save g:ale_linters
+ Save g:ale_fixers
+
+ unlet! b:ale_history
let g:ale_warn_about_trailing_whitespace = 1
let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout'}
let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout'}
+ call ale#engine#ResetExecutableCache()
call ale#linter#Reset()
let g:ale_linters = {}
+ let g:ale_fixers = {}
let g:ale_linter_aliases = {}
let g:ale_buffer_info = {}
let g:globals_lines = [
@@ -59,9 +64,10 @@ After:
let g:ale_buffer_info = {}
- unlet! g:testlinter1
- unlet! g:testlinter2
+ unlet! g:testlinter1
+ unlet! g:testlinter2
+ unlet! b:ale_history
unlet! b:ale_linters
unlet! g:output
unlet! g:globals_string
@@ -247,12 +253,10 @@ Execute (ALEInfo should output linter aliases):
Given testft.testft2 (Empty buffer with two filetypes):
Execute (ALEInfo should return command history):
- let g:ale_buffer_info[bufnr('%')] = {
- \ 'history': [
- \ {'status': 'started', 'job_id': 347, 'command': 'first command'},
- \ {'status': 'started', 'job_id': 347, 'command': ['/bin/bash', '\c', 'last command']},
- \ ],
- \}
+ let b:ale_history = [
+ \ {'status': 'started', 'job_id': 347, 'command': 'first command'},
+ \ {'status': 'started', 'job_id': 347, 'command': ['/bin/bash', '\c', 'last command']},
+ \]
call ale#linter#Define('testft', g:testlinter1)
call ale#linter#Define('testft2', g:testlinter2)
@@ -271,12 +275,10 @@ Execute (ALEInfo should return command history):
Given testft.testft2 (Empty buffer with two filetypes):
Execute (ALEInfo command history should print exit codes correctly):
- let g:ale_buffer_info[bufnr('%')] = {
- \ 'history': [
- \ {'status': 'finished', 'exit_code': 0, 'job_id': 347, 'command': 'first command'},
- \ {'status': 'finished', 'exit_code': 1, 'job_id': 347, 'command': ['/bin/bash', '\c', 'last command']},
- \ ],
- \}
+ let b:ale_history = [
+ \ {'status': 'finished', 'exit_code': 0, 'job_id': 347, 'command': 'first command'},
+ \ {'status': 'finished', 'exit_code': 1, 'job_id': 347, 'command': ['/bin/bash', '\c', 'last command']},
+ \]
call ale#linter#Define('testft', g:testlinter1)
call ale#linter#Define('testft2', g:testlinter2)
@@ -297,31 +299,29 @@ Given testft.testft2 (Empty buffer with two filetypes):
Execute (ALEInfo command history should print command output if logging is on):
let g:ale_history_log_output = 1
- let g:ale_buffer_info[bufnr('%')] = {
- \ 'history': [
- \ {
- \ 'status': 'finished',
- \ 'exit_code': 0,
- \ 'job_id': 347,
- \ 'command': 'first command',
- \ 'output': ['some', 'first command output'],
- \ },
- \ {
- \ 'status': 'finished',
- \ 'exit_code': 1,
- \ 'job_id': 347,
- \ 'command': ['/bin/bash', '\c', 'last command'],
- \ 'output': ['different second command output'],
- \ },
- \ {
- \ 'status': 'finished',
- \ 'exit_code': 0,
- \ 'job_id': 347,
- \ 'command': 'command with no output',
- \ 'output': [],
- \ },
- \ ],
- \}
+ let b:ale_history = [
+ \ {
+ \ 'status': 'finished',
+ \ 'exit_code': 0,
+ \ 'job_id': 347,
+ \ 'command': 'first command',
+ \ 'output': ['some', 'first command output'],
+ \ },
+ \ {
+ \ 'status': 'finished',
+ \ 'exit_code': 1,
+ \ 'job_id': 347,
+ \ 'command': ['/bin/bash', '\c', 'last command'],
+ \ 'output': ['different second command output'],
+ \ },
+ \ {
+ \ 'status': 'finished',
+ \ 'exit_code': 0,
+ \ 'job_id': 347,
+ \ 'command': 'command with no output',
+ \ 'output': [],
+ \ },
+ \]
call ale#linter#Define('testft', g:testlinter1)
call ale#linter#Define('testft2', g:testlinter2)
@@ -351,3 +351,20 @@ Execute (ALEInfo command history should print command output if logging is on):
\ '',
\ '<<<NO OUTPUT RETURNED>>>',
\])
+
+Execute (ALEInfo should include executable checks in the history):
+ call ale#linter#Define('testft', g:testlinter1)
+ call ale#engine#IsExecutable(bufnr(''), 'echo')
+ 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) echo',
+ \ '(executable check - failure) TheresNoWayThisIsExecutable',
+ \])
diff --git a/test/test_ale_init_au_groups.vader b/test/test_ale_init_au_groups.vader
index e036343b..2685f50b 100644
--- a/test/test_ale_init_au_groups.vader
+++ b/test/test_ale_init_au_groups.vader
@@ -21,8 +21,14 @@ Before:
let l:header = split(l:line)[1]
let l:header = get(l:event_name_corrections, l:header, l:header)
elseif !empty(l:header)
- call add(l:matches, join(split(l:header . l:line)))
- let l:header = ''
+ " There's an extra line for buffer events, and we should only look
+ " for the one matching the current buffer.
+ if l:line =~# '<buffer=' . bufnr('') . '>'
+ let l:header .= ' <buffer>'
+ else
+ call add(l:matches, join(split(l:header . l:line)))
+ let l:header = ''
+ endif
endif
endfor
@@ -40,11 +46,18 @@ Before:
Save g:ale_lint_on_save
Save g:ale_echo_cursor
Save g:ale_fix_on_save
+ Save g:ale_completion_enabled
After:
delfunction CheckAutocmd
Restore
+ if g:ale_completion_enabled
+ call ale#completion#Enable()
+ else
+ call ale#completion#Disable()
+ endif
+
call ALEInitAuGroups()
Execute (g:ale_lint_on_text_changed = 0 should bind no events):
@@ -107,10 +120,12 @@ Execute (g:ale_pattern_options_enabled = 1 should bind BufReadPost and BufEnter)
\ 'BufReadPost * call ale#pattern_options#SetOptions()',
\], CheckAutocmd('ALEPatternOptionsGroup')
-Execute (g:ale_lint_on_enter = 0 should bind no events):
+Execute (g:ale_lint_on_enter = 0 should bind only the BufEnter event):
let g:ale_lint_on_enter = 0
- AssertEqual [], CheckAutocmd('ALERunOnEnterGroup')
+ AssertEqual
+ \ ['BufEnter * call ale#events#EnterEvent(str2nr(expand(''<abuf>'')))'],
+ \ CheckAutocmd('ALERunOnEnterGroup')
Execute (g:ale_lint_on_enter = 1 should bind the required events):
let g:ale_lint_on_enter = 1
@@ -127,18 +142,17 @@ Execute (g:ale_lint_on_filetype_changed = 0 should bind no events):
AssertEqual [], CheckAutocmd('ALERunOnFiletypeChangeGroup')
-Execute (g:ale_lint_on_filetype_changed = 1 should bind FileType, and required buffer events):
+Execute (g:ale_lint_on_filetype_changed = 1 should bind the FileType event):
let g:ale_lint_on_filetype_changed = 1
- AssertEqual [
- \ 'BufEnter * let b:ale_original_filetype = &filetype',
- \ 'BufReadPost * let b:ale_original_filetype = &filetype',
- \ 'FileType * '
- \ . 'if has_key(b:, ''ale_original_filetype'') '
- \ . '&& b:ale_original_filetype !=# expand(''<amatch>'')'
- \ . '| call ale#Queue(300, ''lint_file'')'
- \ . '| endif',
- \], CheckAutocmd('ALERunOnFiletypeChangeGroup')
+ AssertEqual
+ \ [
+ \ 'FileType * call ale#events#FileTypeEvent( '
+ \ . 'str2nr(expand(''<abuf>'')), '
+ \ . 'expand(''<amatch>'')'
+ \ . ')',
+ \ ],
+ \ CheckAutocmd('ALERunOnFiletypeChangeGroup')
Execute (g:ale_lint_on_save = 0 should bind no events):
let g:ale_lint_on_save = 0
@@ -184,3 +198,21 @@ Execute (g:ale_echo_cursor = 1 should bind cursor events):
\ 'CursorMoved * call ale#cursor#EchoCursorWarningWithDelay()',
\ 'InsertLeave * call ale#cursor#EchoCursorWarning()',
\], CheckAutocmd('ALECursorGroup')
+
+Execute(Enabling completion should set up autocmd events correctly):
+ let g:ale_completion_enabled = 0
+ call ale#completion#Enable()
+
+ AssertEqual [
+ \ 'CompleteDone * call ale#completion#Done()',
+ \ 'TextChangedI * call ale#completion#Queue()',
+ \], CheckAutocmd('ALECompletionGroup')
+ AssertEqual 1, g:ale_completion_enabled
+
+Execute(Disabling completion should remove autocmd events correctly):
+ let g:ale_completion_enabled = 1
+ call ale#completion#Enable()
+ call ale#completion#Disable()
+
+ AssertEqual [], CheckAutocmd('ALECompletionGroup')
+ AssertEqual 0, g:ale_completion_enabled
diff --git a/test/test_ale_lint_command.vader b/test/test_ale_lint_command.vader
index 9e70017c..42554ec1 100644
--- a/test/test_ale_lint_command.vader
+++ b/test/test_ale_lint_command.vader
@@ -1,4 +1,8 @@
Before:
+ Save g:ale_buffer_info
+
+ let g:ale_buffer_info = {}
+
let g:expected_loclist = [{
\ 'bufnr': bufnr('%'),
\ 'lnum': 2,
@@ -38,6 +42,8 @@ Before:
\})
After:
+ Restore
+
unlet! g:expected_loclist
unlet! g:expected_groups
diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader
index 202d8a35..f5d8599f 100644
--- a/test/test_ale_toggle.vader
+++ b/test/test_ale_toggle.vader
@@ -1,4 +1,12 @@
Before:
+ Save g:ale_buffer_info
+ Save g:ale_set_signs
+ Save g:ale_set_lists_synchronously
+
+ let g:ale_set_signs = 1
+ let g:ale_set_lists_synchronously = 1
+
+ let g:ale_buffer_info = {}
let g:expected_loclist = [{
\ 'bufnr': bufnr('%'),
\ 'lnum': 2,
@@ -42,10 +50,11 @@ Before:
for l:line in split(l:output, "\n")
let l:match = matchlist(l:line, '^ALE[a-zA-Z]\+Group')
- " We don't care about checking for the completion or fixing groups here.
+ " We don't care about some groups here.
if !empty(l:match)
\&& l:match[0] !=# 'ALECompletionGroup'
\&& l:match[0] !=# 'ALEBufferFixGroup'
+ \&& l:match[0] !=# 'ALEPatternOptionsGroup'
call add(l:results, l:match[0])
endif
endfor
@@ -63,11 +72,14 @@ Before:
\ 'read_buffer': 0,
\})
+ sign unplace *
+
After:
+ Restore
+
unlet! g:expected_loclist
unlet! g:expected_groups
- let g:ale_buffer_info = {}
call ale#linter#Reset()
" Toggle ALE back on if we fail when it's disabled.
@@ -91,7 +103,7 @@ Execute(ALEToggle should reset everything and then run again):
" First check that everything is there...
AssertEqual g:expected_loclist, getloclist(0)
- AssertEqual [[2, 1000001, 'ALEErrorSign']], ale#sign#FindCurrentSigns(bufnr('%'))
+ 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}')
@@ -104,7 +116,7 @@ Execute(ALEToggle should reset everything and then run again):
" 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 [], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
+ AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual [], getmatches(), 'The highlights were not cleared'
AssertEqual ['ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups()
@@ -113,9 +125,53 @@ Execute(ALEToggle should reset everything and then run again):
call ale#engine#WaitForJobs(2000)
AssertEqual g:expected_loclist, getloclist(0)
- AssertEqual [[2, 1000001, 'ALEErrorSign']], ale#sign#FindCurrentSigns(bufnr('%'))
+ 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(ALEToggle should skip filename keys and preserve them):
+ AssertEqual 'foobar', &filetype
+
+ let g:ale_buffer_info['/foo/bar/baz.txt'] = {
+ \ 'job_list': [],
+ \ 'active_linter_list': [],
+ \ 'loclist': [],
+ \ 'temporary_file_list': [],
+ \ 'temporary_directory_list': [],
+ \ 'history': [],
+ \}
+
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ " Now Toggle ALE off.
+ ALEToggle
+
+ AssertEqual
+ \ {
+ \ 'job_list': [],
+ \ 'active_linter_list': [],
+ \ 'loclist': [],
+ \ 'temporary_file_list': [],
+ \ 'temporary_directory_list': [],
+ \ 'history': [],
+ \ },
+ \ get(g:ale_buffer_info, '/foo/bar/baz.txt', {})
+
+ " Toggle ALE on again.
+ ALEToggle
+ call ale#engine#WaitForJobs(2000)
+
+ AssertEqual
+ \ {
+ \ 'job_list': [],
+ \ 'active_linter_list': [],
+ \ 'loclist': [],
+ \ 'temporary_file_list': [],
+ \ 'temporary_directory_list': [],
+ \ 'history': [],
+ \ },
+ \ get(g:ale_buffer_info, '/foo/bar/baz.txt', {})
diff --git a/test/test_balloon_messages.vader b/test/test_balloon_messages.vader
index 50dc6af4..ec09fe29 100644
--- a/test/test_balloon_messages.vader
+++ b/test/test_balloon_messages.vader
@@ -3,16 +3,19 @@ Before:
let g:ale_buffer_info[347] = {'loclist': [
\ {
+ \ 'bufnr': 347,
\ 'lnum': 1,
\ 'col': 10,
\ 'text': 'Missing semicolon. (semi)',
\ },
\ {
+ \ 'bufnr': 347,
\ 'lnum': 2,
\ 'col': 10,
\ 'text': 'Infix operators must be spaced. (space-infix-ops)'
\ },
\ {
+ \ 'bufnr': 347,
\ 'lnum': 2,
\ 'col': 15,
\ 'text': 'Missing radix parameter (radix)'
diff --git a/test/test_completion.vader b/test/test_completion.vader
index 65cef465..811a2645 100644
--- a/test/test_completion.vader
+++ b/test/test_completion.vader
@@ -1,10 +1,34 @@
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
- if !g:ale_completion_enabled
+ 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
@@ -84,6 +108,89 @@ Execute(TypeScript completion details responses should be parsed correctly):
\ ],
\})
+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
@@ -100,3 +207,122 @@ Execute(Completion should be done after words in parens in TypeScript):
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 [["\<C-x>\<C-o>", '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_get_abspath.vader b/test/test_get_abspath.vader
new file mode 100644
index 00000000..2def3773
--- /dev/null
+++ b/test/test_get_abspath.vader
@@ -0,0 +1,15 @@
+Execute(Relative paths should be resolved correctly):
+ AssertEqual
+ \ '/foo/bar/baz/whatever.txt',
+ \ ale#path#GetAbsPath('/foo/bar/xyz', '../baz/whatever.txt')
+ AssertEqual
+ \ '/foo/bar/xyz/whatever.txt',
+ \ ale#path#GetAbsPath('/foo/bar/xyz', './whatever.txt')
+ AssertEqual
+ \ '/foo/bar/xyz/whatever.txt',
+ \ ale#path#GetAbsPath('/foo/bar/xyz', 'whatever.txt')
+
+Execute(Absolute paths should be resolved correctly):
+ AssertEqual
+ \ '/ding/dong',
+ \ ale#path#GetAbsPath('/foo/bar/xyz', '/ding/dong')
diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader
index 02f570b9..c1909c4f 100644
--- a/test/test_highlight_placement.vader
+++ b/test/test_highlight_placement.vader
@@ -162,6 +162,7 @@ Execute(Higlight end columns should set an appropriate size):
Execute(Higlight end columns should set an appropriate size):
call ale#highlight#SetHighlights(bufnr('%'), [
+ \ {'bufnr': bufnr('%') - 1, 'type': 'E', 'lnum': 1, 'col': 1},
\ {'bufnr': bufnr('%'), 'type': 'E', 'lnum': 1, 'col': 1},
\ {'bufnr': bufnr('%'), 'type': 'E', 'lnum': 2, 'col': 1},
\ {'bufnr': bufnr('%'), 'type': 'E', 'sub_type': 'style', 'lnum': 3, 'col': 1},
@@ -169,6 +170,7 @@ Execute(Higlight end columns should set an appropriate size):
\ {'bufnr': bufnr('%'), 'type': 'W', 'lnum': 5, 'col': 1},
\ {'bufnr': bufnr('%'), 'type': 'W', 'sub_type': 'style', 'lnum': 6, 'col': 1},
\ {'bufnr': bufnr('%'), 'type': 'I', 'lnum': 7, 'col': 1},
+ \ {'bufnr': bufnr('%') + 1, 'type': 'E', 'lnum': 1, 'col': 1},
\])
AssertEqual
diff --git a/test/test_history_saving.vader b/test/test_history_saving.vader
index 8207d153..3b8fb2aa 100644
--- a/test/test_history_saving.vader
+++ b/test/test_history_saving.vader
@@ -2,6 +2,8 @@ Before:
Save g:ale_max_buffer_history_size
Save g:ale_history_log_output
+ unlet! b:ale_history
+
" Temporarily set the shell to /bin/sh, if it isn't already set that way.
" This will make it so the test works when running it directly.
let g:current_shell = &shell
@@ -26,6 +28,9 @@ Before:
After:
Restore
+ " Clear the history we changed.
+ unlet! b:ale_history
+
" Reset the shell back to what it was before.
let &shell = g:current_shell
unlet g:current_shell
@@ -46,7 +51,7 @@ Execute(History should be set when commands are run):
call ale#Lint()
call ale#engine#WaitForJobs(2000)
- let g:history = g:ale_buffer_info[bufnr('%')].history
+ let g:history = ale#history#Get(bufnr(''))
AssertEqual 1, len(g:history)
AssertEqual sort(['status', 'exit_code', 'job_id', 'command']), sort(keys(g:history[0]))
@@ -64,7 +69,7 @@ Execute(History should be not set when disabled):
call ale#Lint()
call ale#engine#WaitForJobs(2000)
- AssertEqual 0, len(g:ale_buffer_info[bufnr('%')].history)
+ AssertEqual [], ale#history#Get(bufnr(''))
Execute(History should include command output if logging is enabled):
AssertEqual 'foobar', &filetype
@@ -74,35 +79,32 @@ Execute(History should include command output if logging is enabled):
call ale#Lint()
call ale#engine#WaitForJobs(2000)
- let g:history = g:ale_buffer_info[bufnr('%')].history
+ let g:history = ale#history#Get(bufnr(''))
AssertEqual 1, len(g:history)
AssertEqual ['command history test'], g:history[0].output
Execute(History items should be popped after going over the max):
- let g:ale_buffer_info[1] = {
- \ 'history': map(range(20), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}'),
- \}
+ let b:ale_history = map(range(20), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}')
- call ale#history#Add(1, 'started', 347, 'last command')
+ call ale#history#Add(bufnr(''), 'started', 347, 'last command')
AssertEqual
\ (
\ map(range(1, 19), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}')
\ + [{'status': 'started', 'job_id': 347, 'command': 'last command'}]
\ ),
- \ g:ale_buffer_info[1].history
+ \ ale#history#Get(bufnr(''))
Execute(Nothing should be added to history if the size is too low):
let g:ale_max_buffer_history_size = 0
- let g:ale_buffer_info[1] = {'history': []}
- call ale#history#Add(1, 'started', 347, 'last command')
+ call ale#history#Add(bufnr(''), 'started', 347, 'last command')
- AssertEqual [], g:ale_buffer_info[1].history
+ AssertEqual [], ale#history#Get(bufnr(''))
let g:ale_max_buffer_history_size = -2
call ale#history#Add(1, 'started', 347, 'last command')
- AssertEqual [], g:ale_buffer_info[1].history
+ AssertEqual [], ale#history#Get(bufnr(''))
diff --git a/test/test_lint_error_delay.vader b/test/test_lint_error_delay.vader
new file mode 100644
index 00000000..4c7f0947
--- /dev/null
+++ b/test/test_lint_error_delay.vader
@@ -0,0 +1,26 @@
+Before:
+ Save g:ale_filetype_blacklist
+
+ " Delete some variable which should be defined.
+ unlet! g:ale_filetype_blacklist
+
+After:
+ Restore
+
+ call ale#ResetErrorDelays()
+
+Execute(ALE should stop queuing for a while after exceptions are thrown):
+ AssertThrows call ale#Queue(100)
+ call ale#Queue(100)
+
+Execute(ALE should stop linting for a while after exceptions are thrown):
+ AssertThrows call ale#Lint()
+ call ale#Lint()
+
+Execute(ALE should stop queuing echo messages for a while after exceptions are thrown):
+ AssertThrows call ale#cursor#EchoCursorWarningWithDelay()
+ call ale#cursor#EchoCursorWarningWithDelay()
+
+Execute(ALE should stop echoing messages for a while after exceptions are thrown):
+ AssertThrows call ale#cursor#EchoCursorWarning()
+ call ale#cursor#EchoCursorWarning()
diff --git a/test/test_lint_file_linters.vader b/test/test_lint_file_linters.vader
index 4110c059..cb859790 100644
--- a/test/test_lint_file_linters.vader
+++ b/test/test_lint_file_linters.vader
@@ -79,8 +79,14 @@ Before:
call ale#test#SetFilename(g:filename)
After:
+ if !g:ale_run_synchronously
+ call ale#engine#WaitForJobs(2000)
+ endif
+
Restore
+ unlet! b:ale_save_event_fired
+ unlet! b:ale_enabled
unlet g:buffer_result
let g:ale_buffer_info = {}
call ale#linter#Reset()
@@ -251,3 +257,33 @@ Execute(The Save event should respect the buffer number):
\ 'type': 'E',
\ },
\], GetSimplerLoclist()
+
+Execute(The Save event should set b:ale_save_event_fired to 1):
+ let b:ale_enabled = 0
+ call ale#events#SaveEvent(bufnr(''))
+
+ " This flag needs to be set so windows can be opened, etc.
+ AssertEqual 1, b:ale_save_event_fired
+
+Execute(b:ale_save_event_fired should be set to 0 when results are set):
+ let b:ale_save_event_fired = 1
+
+ call ale#engine#SetResults(bufnr(''), [])
+
+ AssertEqual 0, b:ale_save_event_fired
+
+Execute(lint_file linters should stay running after checking without them):
+ let g:ale_run_synchronously = 0
+
+ " Run all linters, then just the buffer linters.
+ call ale#Queue(0, 'lint_file')
+ call ale#Queue(0)
+
+ " The lint_file linter should still be running.
+ AssertEqual
+ \ ['lint_file_linter', 'buffer_linter'],
+ \ g:ale_buffer_info[bufnr('')].active_linter_list
+ " We should have 1 job for each linter.
+ AssertEqual 2, len(g:ale_buffer_info[bufnr('')].job_list)
+
+ call ale#engine#WaitForJobs(2000)
diff --git a/test/test_lint_on_filetype_changed.vader b/test/test_lint_on_filetype_changed.vader
new file mode 100644
index 00000000..44446ef0
--- /dev/null
+++ b/test/test_lint_on_filetype_changed.vader
@@ -0,0 +1,47 @@
+Before:
+ Save &filetype
+
+ let g:queue_calls = []
+
+ function! ale#Queue(...)
+ call add(g:queue_calls, a:000)
+ endfunction
+
+After:
+ Restore
+
+ unlet! g:queue_calls
+
+ " Reload the ALE code to load the real function again.
+ runtime autoload/ale.vim
+
+ unlet! b:ale_original_filetype
+
+Execute(The original filetype should be set on BufEnter):
+ let &filetype = 'foobar'
+
+ call ale#events#EnterEvent(bufnr(''))
+
+ AssertEqual 'foobar', b:ale_original_filetype
+
+ let &filetype = 'bazboz'
+
+ call ale#events#EnterEvent(bufnr(''))
+
+ AssertEqual 'bazboz', b:ale_original_filetype
+
+Execute(Linting should not be queued when the filetype is the same):
+ let b:ale_original_filetype = 'foobar'
+ let g:queue_calls = []
+
+ call ale#events#FileTypeEvent(bufnr(''), 'foobar')
+
+ AssertEqual [], g:queue_calls
+
+Execute(Linting should be queued when the filetype changes):
+ let b:ale_original_filetype = 'foobar'
+ let g:queue_calls = []
+
+ call ale#events#FileTypeEvent(bufnr(''), 'bazboz')
+
+ AssertEqual [[300, 'lint_file', bufnr('')]], g:queue_calls
diff --git a/test/test_linting_updates_loclist.vader b/test/test_linting_updates_loclist.vader
index 19adfa1f..a1daf28d 100644
--- a/test/test_linting_updates_loclist.vader
+++ b/test/test_linting_updates_loclist.vader
@@ -1,4 +1,7 @@
Before:
+ Save g:ale_set_signs
+ let g:ale_set_signs = 1
+
let g:expected_data = [
\ {
\ 'lnum': 1,
@@ -49,7 +52,11 @@ Before:
\ 'read_buffer': 0,
\})
+ sign unplace *
+
After:
+ Restore
+
delfunction TestCallback
unlet! g:expected_data
diff --git a/test/test_list_opening.vader b/test/test_list_opening.vader
index a46f28e5..7d386d80 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_buffer_info
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
@@ -13,11 +14,12 @@ Before:
let g:ale_list_window_size = 10
let g:loclist = [
- \ {'lnum': 5, 'col': 5},
- \ {'lnum': 5, 'col': 4},
- \ {'lnum': 2, 'col': 10},
- \ {'lnum': 3, 'col': 2},
+ \ {'bufnr': bufnr(''), 'lnum': 5, 'col': 5, 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 5, 'col': 4, 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 10, 'text': 'x'},
+ \ {'bufnr': bufnr(''), 'lnum': 3, 'col': 2, 'text': 'x'},
\]
+ let g:ale_buffer_info = {bufnr(''): {'loclist': g:loclist}}
function GetQuickfixHeight() abort
for l:win in range(1, winnr('$'))
@@ -34,6 +36,10 @@ After:
unlet! g:loclist
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
" Close quickfix window after every execute block
@@ -117,6 +123,10 @@ Execute(The quickfix window should open for the quickfix list):
let g:ale_set_quickfix = 1
let g:ale_open_list = 1
+ let g:ale_buffer_info[bufnr('') + 1] = {
+ \ 'loclist': [{'bufnr': -1, 'filename': '/foo/bar', 'lnum': 5, 'col': 5, 'text': 'x'}],
+ \}
+
" It should not open for an empty list.
call ale#list#SetLists(bufnr('%'), [])
call ale#list#CloseWindowIfNeeded(bufnr(''))
@@ -127,10 +137,17 @@ Execute(The quickfix window should open for the quickfix list):
call ale#list#CloseWindowIfNeeded(bufnr(''))
Assert ale#list#IsQuickfixOpen(), 'The quickfix window was closed when the list was not empty'
- " Clear the list and it should close again.
+ " Clear this List. The window should stay open, as there are other items.
+ let g:ale_buffer_info[bufnr('')].loclist = []
+ call ale#list#SetLists(bufnr('%'), [])
+ call ale#list#CloseWindowIfNeeded(bufnr(''))
+ Assert ale#list#IsQuickfixOpen(), 'The quickfix window closed even though there are items in another buffer'
+
+ " Clear the other List now. Now the window should close.
+ call remove(g:ale_buffer_info, bufnr('') + 1)
call ale#list#SetLists(bufnr('%'), [])
call ale#list#CloseWindowIfNeeded(bufnr(''))
- Assert !ale#list#IsQuickfixOpen(), 'The quickfix window was not closed when the list was empty'
+ Assert !ale#list#IsQuickfixOpen(), 'The quickfix window was not closed'
Execute(The quickfix window should stay open for the quickfix list):
let g:ale_set_quickfix = 1
@@ -163,3 +180,41 @@ Execute(The quickfix window height should be correct for the quickfix list with
call ale#list#CloseWindowIfNeeded(bufnr(''))
AssertEqual 8, GetQuickfixHeight()
+
+Execute(The buffer ale_open_list option should be respected):
+ let b:ale_open_list = 1
+
+ call ale#list#SetLists(bufnr('%'), g:loclist)
+ Assert ale#list#IsQuickfixOpen()
+
+Execute(The buffer ale_keep_list_window_open option should be respected):
+ let b:ale_open_list = 1
+ let b:ale_keep_list_window_open = 1
+
+ call ale#list#SetLists(bufnr('%'), g:loclist)
+ call ale#list#CloseWindowIfNeeded(bufnr(''))
+ call ale#list#SetLists(bufnr('%'), [])
+ call ale#list#CloseWindowIfNeeded(bufnr(''))
+
+ Assert ale#list#IsQuickfixOpen()
+
+Execute(The ale_open_list='on_save' option should work):
+ let b:ale_open_list = 'on_save'
+
+ call ale#list#SetLists(bufnr('%'), g:loclist)
+ " The list shouldn't open yet, the event wasn't fired.
+ Assert !ale#list#IsQuickfixOpen()
+
+ let b:ale_save_event_fired = 1
+
+ call ale#list#SetLists(bufnr('%'), g:loclist)
+ " Now the list should have opened.
+ Assert ale#list#IsQuickfixOpen()
+
+Execute(The window shouldn't open on save when ale_open_list=0):
+ let b:ale_open_list = 0
+ let b:ale_save_event_fired = 1
+
+ call ale#list#SetLists(bufnr('%'), g:loclist)
+ " Now the list should have opened.
+ Assert !ale#list#IsQuickfixOpen()
diff --git a/test/test_list_titles.vader b/test/test_list_titles.vader
index fe28629d..74cb4bcc 100644
--- a/test/test_list_titles.vader
+++ b/test/test_list_titles.vader
@@ -1,7 +1,9 @@
Before:
Save g:ale_set_loclist
Save g:ale_set_quickfix
+ Save g:ale_buffer_info
+ let g:ale_buffer_info = {}
let g:ale_set_loclist = 0
let g:ale_set_quickfix = 0
@@ -43,6 +45,10 @@ Execute(The quickfix titles should be set appropriately):
let g:ale_set_quickfix = 1
+ let g:ale_buffer_info[bufnr('')] = {
+ \ 'loclist': [{'bufnr': bufnr(''), 'lnum': 5, 'col': 5, 'text': 'x', 'type': 'E'}],
+ \}
+
call ale#list#SetLists(bufnr(''), [
\ {'bufnr': bufnr(''), 'lnum': 5, 'col': 5, 'text': 'x', 'type': 'E'},
\])
diff --git a/test/test_loclist_binary_search.vader b/test/test_loclist_binary_search.vader
index e0b2c651..5558191c 100644
--- a/test/test_loclist_binary_search.vader
+++ b/test/test_loclist_binary_search.vader
@@ -1,26 +1,49 @@
Before:
let g:loclist = [
- \ {'lnum': 2, 'col': 10},
- \ {'lnum': 3, 'col': 2},
- \ {'lnum': 3, 'col': 10},
- \ {'lnum': 3, 'col': 12},
- \ {'lnum': 3, 'col': 25},
- \ {'lnum': 5, 'col': 4},
- \ {'lnum': 5, 'col': 5},
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 10},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 12},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 25},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5},
+ \ {'bufnr': 1, 'lnum': 9, 'col': 5},
+ \ {'bufnr': 1, 'lnum': 10, 'col': 1},
+ \ {'bufnr': 2, 'lnum': 7, 'col': 10},
+ \ {'bufnr': 2, 'lnum': 9, 'col': 2},
+ \ {'bufnr': 2, 'lnum': 10, 'col': 2},
+ \ {'bufnr': 2, 'lnum': 11, 'col': 2},
\]
-Execute (Exact column matches should be correct):
- AssertEqual 1, ale#util#BinarySearch(g:loclist, 3, 2)
+After:
+ unlet g:loclist
-Execute (Off lines, there should be no match):
- AssertEqual -1, ale#util#BinarySearch(g:loclist, 4, 2)
+Execute(Exact column matches should be correct):
+ AssertEqual 1, ale#util#BinarySearch(g:loclist, 1, 3, 2)
-Execute (Near column matches should be taken):
- AssertEqual 2, ale#util#BinarySearch(g:loclist, 3, 11)
- AssertEqual 4, ale#util#BinarySearch(g:loclist, 3, 13)
+Execute(Off lines, there should be no match):
+ AssertEqual -1, ale#util#BinarySearch(g:loclist, 1, 4, 2)
-Execute (Columns before should be taken when the cursor is far ahead):
- AssertEqual 4, ale#util#BinarySearch(g:loclist, 3, 300)
+Execute(Near column matches should be taken):
+ AssertEqual 2, ale#util#BinarySearch(g:loclist, 1, 3, 11)
+ AssertEqual 3, ale#util#BinarySearch(g:loclist, 1, 3, 13)
-After:
- unlet g:loclist
+Execute(Columns before should be taken when the cursor is far ahead):
+ AssertEqual 4, ale#util#BinarySearch(g:loclist, 1, 3, 300)
+
+Execute(The only problems on lines in later columns should be matched):
+ AssertEqual 7, ale#util#BinarySearch(g:loclist, 1, 9, 1)
+
+Execute(The only problems on lines in earlier columns should be matched):
+ AssertEqual 8, ale#util#BinarySearch(g:loclist, 1, 10, 30)
+
+Execute(Lines for other buffers should not be matched):
+ AssertEqual -1, ale#util#BinarySearch(g:loclist, 1, 7, 10)
+
+Execute(Searches for buffers later in the list should work):
+ AssertEqual 10, ale#util#BinarySearch(g:loclist, 2, 9, 10)
+
+Execute(Searches should work with just one item):
+ let g:loclist = [{'bufnr': 1, 'lnum': 3, 'col': 10}]
+
+ AssertEqual 0, ale#util#BinarySearch(g:loclist, 1, 3, 2)
diff --git a/test/test_loclist_corrections.vader b/test/test_loclist_corrections.vader
index e23109ed..e6844d8e 100644
--- a/test/test_loclist_corrections.vader
+++ b/test/test_loclist_corrections.vader
@@ -1,3 +1,7 @@
+After:
+ unlet! b:temp_name
+ unlet! b:other_bufnr
+
Given foo (Some file with lines to count):
foo12345678
bar12345678
@@ -202,7 +206,6 @@ Execute(FixLocList should pass on end_lnum values):
\ ],
\ )
-
Execute(FixLocList should allow subtypes to be set):
AssertEqual
\ [
@@ -223,3 +226,104 @@ Execute(FixLocList should allow subtypes to be set):
\ 'foobar',
\ [{'text': 'a', 'lnum': 11, 'sub_type': 'style'}],
\ )
+
+Execute(FixLocList should accept filenames):
+ let b:other_bufnr = bufnr('/foo/bar/baz', 1)
+
+ " Make sure we actually get another buffer number, or the test is invalid.
+ AssertNotEqual -1, b:other_bufnr
+
+ call ale#test#SetFilename('test.txt')
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 2,
+ \ 'col': 0,
+ \ 'bufnr': bufnr('%'),
+ \ 'filename': expand('%:p'),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 3,
+ \ 'col': 0,
+ \ 'bufnr': bufnr('%'),
+ \ 'filename': expand('%:p'),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 4,
+ \ 'col': 0,
+ \ 'bufnr': b:other_bufnr,
+ \ 'filename': '/foo/bar/baz',
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 5,
+ \ 'col': 0,
+ \ 'bufnr': b:other_bufnr,
+ \ 'filename': '/foo/bar/baz',
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \],
+ \ ale#engine#FixLocList(
+ \ bufnr('%'),
+ \ 'foobar',
+ \ [
+ \ {'text': 'a', 'lnum': 2, 'filename': expand('%:p')},
+ \ {'text': 'a', 'lnum': 3, 'filename': expand('%:p')},
+ \ {'text': 'a', 'lnum': 4, 'filename': '/foo/bar/baz'},
+ \ {'text': 'a', 'lnum': 5, 'filename': '/foo/bar/baz'},
+ \ ],
+ \ )
+
+Execute(FixLocList should interpret temporary filenames as being the current buffer):
+ let b:temp_name = tempname()
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 2,
+ \ 'col': 0,
+ \ 'bufnr': bufnr(''),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 3,
+ \ 'col': 0,
+ \ 'bufnr': bufnr(''),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'foobar',
+ \ },
+ \],
+ \ ale#engine#FixLocList(
+ \ bufnr(''),
+ \ 'foobar',
+ \ [
+ \ {'text': 'a', 'lnum': 2, 'filename': b:temp_name},
+ \ {'text': 'a', 'lnum': 3, 'filename': b:temp_name},
+ \ ],
+ \ )
diff --git a/test/test_loclist_jumping.vader b/test/test_loclist_jumping.vader
index 13eac5ce..5e18499e 100644
--- a/test/test_loclist_jumping.vader
+++ b/test/test_loclist_jumping.vader
@@ -1,14 +1,16 @@
Before:
let g:ale_buffer_info = {
- \ bufnr('%'): {
+ \ bufnr(''): {
\ 'loclist': [
- \ {'lnum': 1, 'col': 2},
- \ {'lnum': 1, 'col': 3},
- \ {'lnum': 2, 'col': 1},
- \ {'lnum': 2, 'col': 2},
- \ {'lnum': 2, 'col': 3},
- \ {'lnum': 2, 'col': 6},
- \ {'lnum': 2, 'col': 700},
+ \ {'bufnr': bufnr('') - 1, 'lnum': 3, 'col': 2},
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 2},
+ \ {'bufnr': bufnr(''), 'lnum': 1, 'col': 3},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 1},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 2},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 3},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 6},
+ \ {'bufnr': bufnr(''), 'lnum': 2, 'col': 700},
+ \ {'bufnr': bufnr('') + 1, 'lnum': 3, 'col': 2},
\ ],
\ },
\}
@@ -74,3 +76,15 @@ Execute(We shouldn't move when jumping to the first item where there are none):
let g:ale_buffer_info[bufnr('%')].loclist = []
AssertEqual [1, 6], TestJump(0, 0, [1, 6])
+
+Execute(We should be able to jump when the error line is blank):
+ " Add a blank line at the end.
+ call setline(1, getline('.', '$') + [''])
+ " Add a problem on the blank line.
+ call add(g:ale_buffer_info[bufnr('%')].loclist, {'bufnr': bufnr(''), 'lnum': 3, 'col': 1})
+
+ AssertEqual 0, len(getline(3))
+ AssertEqual [2, 8], TestJump('before', 0, [3, 1])
+ AssertEqual [2, 8], TestJump('before', 1, [3, 1])
+ AssertEqual [3, 1], TestJump('after', 0, [3, 1])
+ AssertEqual [1, 2], TestJump('after', 1, [3, 1])
diff --git a/test/test_loclist_sorting.vader b/test/test_loclist_sorting.vader
index 6e52be6f..157b2a25 100644
--- a/test/test_loclist_sorting.vader
+++ b/test/test_loclist_sorting.vader
@@ -1,21 +1,27 @@
-Before:
- let g:loclist = [
- \ {'lnum': 5, 'col': 5},
- \ {'lnum': 5, 'col': 4},
- \ {'lnum': 2, 'col': 10},
- \ {'lnum': 3, 'col': 2},
- \]
-
-Execute (Sort loclist with comparison function):
- call sort(g:loclist, 'ale#util#LocItemCompare')
-
-Then (loclist item should be sorted):
+Execute(loclist item should be sorted):
AssertEqual [
- \ {'lnum': 2, 'col': 10},
- \ {'lnum': 3, 'col': 2},
- \ {'lnum': 5, 'col': 4},
- \ {'lnum': 5, 'col': 5},
- \], g:loclist
-
-After:
- unlet g:loclist
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 4, 'col': 2},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 5, 'col': 2},
+ \ {'bufnr': -1, 'filename': 'c', 'lnum': 3, 'col': 2},
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 2},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 5},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5},
+ \ {'bufnr': 3, 'lnum': 1, 'col': 1},
+ \ ],
+ \ sort([
+ \ {'bufnr': 3, 'lnum': 1, 'col': 1},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 5, 'col': 2},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 4, 'col': 2},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4},
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 5},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 2},
+ \ {'bufnr': -1, 'filename': 'c', 'lnum': 3, 'col': 2},
+ \], 'ale#util#LocItemCompare')
diff --git a/test/test_quickfix_deduplication.vader b/test/test_quickfix_deduplication.vader
new file mode 100644
index 00000000..0dff3f2e
--- /dev/null
+++ b/test/test_quickfix_deduplication.vader
@@ -0,0 +1,50 @@
+Before:
+ Save g:ale_buffer_info
+
+After:
+ Restore
+
+Execute:
+ " Results from multiple buffers should be gathered together.
+ " Equal problems should be de-duplicated.
+ let g:ale_buffer_info = {
+ \ '1': {'loclist': [
+ \ {'bufnr': 2, 'lnum': 1, 'col': 2, 'text': 'foo'},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 5, 'text': 'bar'},
+ \ {'bufnr': -1, 'filename': 'c', 'lnum': 3, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4, 'text': 'x'},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5, 'text': 'foo'},
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5, 'text': 'x'},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 4, 'col': 2, 'text': 'x'},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 5, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 3, 'lnum': 1, 'col': 1, 'text': 'foo'},
+ \ ]},
+ \ '2': {'loclist': [
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5, 'text': 'x'},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 2, 'text': 'foo'},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4, 'text': 'x'},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 5, 'text': 'bar'},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5, 'text': 'another error'},
+ \ ]},
+ \}
+
+ AssertEqual
+ \ [
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 4, 'col': 2, 'text': 'x'},
+ \ {'bufnr': -1, 'filename': 'b', 'lnum': 5, 'col': 2, 'text': 'x'},
+ \ {'bufnr': -1, 'filename': 'c', 'lnum': 3, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 2, 'col': 10, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 3, 'col': 2, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 4, 'text': 'x'},
+ \ {'bufnr': 1, 'lnum': 5, 'col': 5, 'text': 'x'},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 2, 'text': 'foo'},
+ \ {'bufnr': 2, 'lnum': 1, 'col': 5, 'text': 'bar'},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5, 'text': 'another error'},
+ \ {'bufnr': 2, 'lnum': 5, 'col': 5, 'text': 'foo'},
+ \ {'bufnr': 3, 'lnum': 1, 'col': 1, 'text': 'foo'},
+ \ ],
+ \ ale#list#GetCombinedList()
diff --git a/test/test_results_not_cleared_when_opening_loclist.vader b/test/test_results_not_cleared_when_opening_loclist.vader
index 07d3d303..0c053b85 100644
--- a/test/test_results_not_cleared_when_opening_loclist.vader
+++ b/test/test_results_not_cleared_when_opening_loclist.vader
@@ -26,6 +26,9 @@ After:
delfunction TestCallback
let g:ale_buffer_info = {}
call ale#linter#Reset()
+ call setloclist(0, [])
+ call clearmatches()
+ sign unplace *
Given foobar (Some file):
abc
diff --git a/test/test_set_list_timers.vader b/test/test_set_list_timers.vader
new file mode 100644
index 00000000..90aacb55
--- /dev/null
+++ b/test/test_set_list_timers.vader
@@ -0,0 +1,38 @@
+Before:
+ Save g:ale_set_lists_synchronously
+ Save g:ale_open_list
+
+ let g:ale_set_lists_synchronously = 0
+
+After:
+ Restore
+
+ sleep 1ms
+ call setloclist(0, [])
+ lclose
+
+Execute(The SetLists function should work when run in a timer):
+ call ale#list#SetLists(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'lnum': 5, 'col': 5, 'text': 'x', 'type': 'E'},
+ \])
+ sleep 1ms
+ AssertEqual [{
+ \ 'lnum': 5,
+ \ 'bufnr': bufnr(''),
+ \ 'col': 5,
+ \ 'text': 'x',
+ \ 'valid': 1,
+ \ 'vcol': 0,
+ \ 'nr': 0,
+ \ 'type': 'E',
+ \ 'pattern': '',
+ \}], getloclist(0)
+
+Execute(The CloseWindowIfNeeded function should work when run in a timer):
+ let g:ale_open_list = 1
+ lopen
+
+ call ale#list#CloseWindowIfNeeded(bufnr(''))
+ sleep 1ms
+
+ Assert !ale#list#IsQuickfixOpen(), 'The window was not closed!'
diff --git a/test/test_setting_loclist_from_another_buffer.vader b/test/test_setting_loclist_from_another_buffer.vader
index 4b757c61..10a44cce 100644
--- a/test/test_setting_loclist_from_another_buffer.vader
+++ b/test/test_setting_loclist_from_another_buffer.vader
@@ -1,12 +1,25 @@
Before:
+ Save g:ale_buffer_info
+
+ let g:ale_buffer_info = {
+ \ bufnr(''): {
+ \ 'loclist': [{'bufnr': bufnr(''), 'lnum': 4, 'col': 1, 'text': 'foo'}]
+ \ },
+ \}
+
let g:original_buffer = bufnr('%')
- new
+ noautocmd new
After:
+ Restore
+
unlet! g:original_buffer
Execute(Errors should be set in the loclist for the original buffer, not the new one):
- call ale#list#SetLists(g:original_buffer, [{'lnum': 4, 'text': 'foo'}])
+ call ale#list#SetLists(
+ \ g:original_buffer,
+ \ g:ale_buffer_info[(g:original_buffer)].loclist,
+ \ )
AssertEqual [], getloclist(0)
AssertEqual 1, len(getloclist(bufwinid(g:original_buffer)))
diff --git a/test/test_setting_problems_found_in_previous_buffers.vader b/test/test_setting_problems_found_in_previous_buffers.vader
new file mode 100644
index 00000000..45dfa662
--- /dev/null
+++ b/test/test_setting_problems_found_in_previous_buffers.vader
@@ -0,0 +1,98 @@
+Before:
+ Save g:ale_buffer_info
+ Save &filetype
+ Save g:ale_set_lists_synchronously
+
+ let g:ale_set_lists_synchronously = 1
+
+ " Set up items in other buffers which should set in this one.
+ let g:ale_buffer_info = {}
+ call ale#engine#InitBufferInfo(bufnr('') + 1)
+ let g:ale_buffer_info[bufnr('') + 1].loclist =
+ \ ale#engine#FixLocList(bufnr('') + 1, 'linter_one', [
+ \ {'lnum': 1, 'filename': expand('%:p'), 'text': 'foo'},
+ \ {'lnum': 2, 'filename': expand('%:p'), 'text': 'bar'},
+ \ {'lnum': 2, 'text': 'ignore this one'},
+ \ ])
+ call ale#engine#InitBufferInfo(bufnr('') + 2)
+ let g:ale_buffer_info[bufnr('') + 2].loclist =
+ \ ale#engine#FixLocList(bufnr('') + 2, 'linter_one', [
+ \ {'lnum': 1, 'filename': expand('%:p'), 'text': 'foo'},
+ \ {'lnum': 3, 'filename': expand('%:p'), 'text': 'baz'},
+ \ {'lnum': 5, 'text': 'ignore this one'},
+ \ ])
+
+ call ale#linter#Define('foobar', {
+ \ 'name': 'linter_one',
+ \ 'callback': 'WhoCares',
+ \ 'executable': 'echo',
+ \ 'command': 'sleep 1000',
+ \ 'lint_file': 1,
+ \})
+
+After:
+ call ale#engine#Cleanup(bufnr(''))
+ Restore
+ call ale#linter#Reset()
+
+ " Items and markers, etc.
+ call setloclist(0, [])
+ call clearmatches()
+ sign unplace *
+
+Given foobar(A file with some lines):
+ foo
+ bar
+ baz
+
+Execute(Problems found from previously opened buffers should be set when linting for the first time):
+ call ale#engine#RunLinters(bufnr(''), ale#linter#Get(&filetype), 0)
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'bufnr': bufnr(''),
+ \ 'col': 0,
+ \ 'filename': expand('%:p'),
+ \ 'linter_name': 'linter_one',
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'vcol': 0,
+ \ 'text': 'foo',
+ \ 'sign_id': 1000001,
+ \ },
+ \ {
+ \ 'lnum': 2,
+ \ 'bufnr': bufnr(''),
+ \ 'col': 0,
+ \ 'filename': expand('%:p'),
+ \ 'linter_name': 'linter_one',
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'vcol': 0,
+ \ 'text': 'bar',
+ \ 'sign_id': 1000002,
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'bufnr': bufnr(''),
+ \ 'col': 0,
+ \ 'filename': expand('%:p'),
+ \ 'linter_name': 'linter_one',
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'vcol': 0,
+ \ 'text': 'baz',
+ \ 'sign_id': 1000003,
+ \ },
+ \ ],
+ \ g:ale_buffer_info[bufnr('')].loclist
+
+ AssertEqual
+ \ [
+ \ {'lnum': 1, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'foo'},
+ \ {'lnum': 2, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'bar'},
+ \ {'lnum': 3, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'baz'},
+ \ ],
+ \ getloclist(0)
diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader
new file mode 100644
index 00000000..37cf43ce
--- /dev/null
+++ b/test/test_shell_detection.vader
@@ -0,0 +1,83 @@
+Before:
+ runtime ale_linters/sh/shell.vim
+ runtime ale_linters/sh/shellcheck.vim
+
+After:
+ call ale#linter#Reset()
+
+ unlet! b:is_bash
+ unlet! b:is_sh
+ unlet! b:is_kornshell
+
+Given(A file with a Bash hashbang):
+ #!/bin/bash
+
+Execute(/bin/bash should be detected appropriately):
+ AssertEqual 'bash', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with /bin/sh):
+ #!/usr/bin/env sh -eu --foobar
+
+Execute(/bin/sh should be detected appropriately):
+ AssertEqual 'sh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with bash as an argument to env):
+ #!/usr/bin/env bash
+
+Execute(/usr/bin/env bash should be detected appropriately):
+ AssertEqual 'bash', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a tcsh hash bang and arguments):
+ #!/usr/bin/env tcsh -eu --foobar
+
+Execute(tcsh should be detected appropriately):
+ AssertEqual 'tcsh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'tcsh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'tcsh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a zsh hash bang and arguments):
+ #!/usr/bin/env zsh -eu --foobar
+
+Execute(zsh should be detected appropriately):
+ AssertEqual 'zsh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'zsh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'zsh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a csh hash bang and arguments):
+ #!/usr/bin/env csh -eu --foobar
+
+Execute(zsh should be detected appropriately):
+ AssertEqual 'csh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'csh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'csh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a sh hash bang and arguments):
+ #!/usr/bin/env sh -eu --foobar
+
+Execute(sh should be detected appropriately):
+ AssertEqual 'sh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file without a hashbang):
+
+Execute(The bash dialect should be used for shellcheck if b:is_bash is 1):
+ let b:is_bash = 1
+
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Execute(The sh dialect should be used for shellcheck if b:is_sh is 1):
+ let b:is_sh = 1
+
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+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(''))
diff --git a/test/test_statusline.vader b/test/test_statusline.vader
index bd0fd5dc..7978a509 100644
--- a/test/test_statusline.vader
+++ b/test/test_statusline.vader
@@ -1,9 +1,11 @@
Before:
- Save g:ale_statusline_format, g:ale_buffer_info
+ Save g:ale_statusline_format
+ Save g:ale_buffer_info
+
let g:ale_buffer_info = {}
" A function for conveniently creating expected count objects.
- function Counts(data) abort
+ function! Counts(data) abort
let l:res = {
\ '0': 0,
\ '1': 0,
@@ -28,10 +30,11 @@ Before:
After:
Restore
+
delfunction Counts
Execute (Count should be 0 when data is empty):
- AssertEqual Counts({}), ale#statusline#Count(bufnr('%'))
+ AssertEqual Counts({}), ale#statusline#Count(bufnr(''))
Execute (Count should read data from the cache):
let g:ale_buffer_info = {'44': {'count': Counts({'error': 1, 'warning': 2})}}
@@ -44,23 +47,33 @@ Execute (The count should be correct after an update):
Execute (Count should be match the loclist):
let g:ale_buffer_info = {
- \ bufnr('%'): {
+ \ bufnr(''): {
\ 'loclist': [
- \ {'type': 'E'},
- \ {'type': 'E', 'sub_type': 'style'},
- \ {'type': 'E', 'sub_type': 'style'},
- \ {'type': 'W'},
- \ {'type': 'W'},
- \ {'type': 'W'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'I'},
- \ {'type': 'I'},
- \ {'type': 'I'},
- \ {'type': 'I'},
- \ {'type': 'I'},
+ \ {'bufnr': bufnr('') - 1, 'type': 'E'},
+ \ {'bufnr': bufnr('') - 1, 'type': 'E', 'sub_type': 'style'},
+ \ {'bufnr': bufnr('') - 1, 'type': 'W'},
+ \ {'bufnr': bufnr('') - 1, 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr('') - 1, 'type': 'I'},
+ \ {'bufnr': bufnr(''), 'type': 'E'},
+ \ {'bufnr': bufnr(''), 'type': 'E', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'E', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'W'},
+ \ {'bufnr': bufnr(''), 'type': 'W'},
+ \ {'bufnr': bufnr(''), 'type': 'W'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
+ \ {'bufnr': bufnr('') + 1, 'type': 'E'},
+ \ {'bufnr': bufnr('') + 1, 'type': 'E', 'sub_type': 'style'},
+ \ {'bufnr': bufnr('') + 1, 'type': 'W'},
+ \ {'bufnr': bufnr('') + 1, 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr('') + 1, 'type': 'I'},
\ ],
\ },
\}
@@ -73,59 +86,56 @@ Execute (Count should be match the loclist):
\ '0': 3,
\ '1': 12,
\ 'total': 15,
- \}, ale#statusline#Count(bufnr('%'))
+ \}, ale#statusline#Count(bufnr(''))
Execute (Output should be empty for non-existant buffer):
AssertEqual Counts({}), ale#statusline#Count(9001)
Execute (Status() should return just errors for the old format):
let g:ale_statusline_format = ['%sE', '%sW', 'OKIE']
- let g:ale_buffer_info = {bufnr('%'): {}}
- call ale#statusline#Update(bufnr('%'), [
- \ {'type': 'E'},
- \ {'type': 'E', 'sub_type': 'style'},
+ let g:ale_buffer_info = {bufnr(''): {}}
+ call ale#statusline#Update(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'type': 'E'},
+ \ {'bufnr': bufnr(''), 'type': 'E', 'sub_type': 'style'},
\])
AssertEqual '2E', ale#statusline#Status()
Execute (Status() should return just warnings for the old format):
let g:ale_statusline_format = ['%sE', '%sW', 'OKIE']
- let g:ale_buffer_info = {bufnr('%'): {}}
- call ale#statusline#Update(bufnr('%'), [
- \ {'type': 'W'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'I'},
+ let g:ale_buffer_info = {bufnr(''): {}}
+ call ale#statusline#Update(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'type': 'W'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
\])
AssertEqual '3W', ale#statusline#Status()
Execute (Status() should return errors and warnings for the old format):
let g:ale_statusline_format = ['%sE', '%sW', 'OKIE']
- let g:ale_buffer_info = {bufnr('%'): {}}
- call ale#statusline#Update(bufnr('%'), [
- \ {'type': 'E'},
- \ {'type': 'E', 'sub_type': 'style'},
- \ {'type': 'W'},
- \ {'type': 'W', 'sub_type': 'style'},
- \ {'type': 'I'},
+ let g:ale_buffer_info = {bufnr(''): {}}
+ call ale#statusline#Update(bufnr(''), [
+ \ {'bufnr': bufnr(''), 'type': 'E'},
+ \ {'bufnr': bufnr(''), 'type': 'E', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'W'},
+ \ {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'},
+ \ {'bufnr': bufnr(''), 'type': 'I'},
\])
AssertEqual '2E 3W', ale#statusline#Status()
Execute (Status() should return the custom 'OK' string with the old format):
let g:ale_statusline_format = ['%sE', '%sW', 'OKIE']
- let g:ale_buffer_info = {bufnr('%'): {}}
- call ale#statusline#Update(bufnr('%'), [])
+ let g:ale_buffer_info = {bufnr(''): {}}
+ call ale#statusline#Update(bufnr(''), [])
AssertEqual 'OKIE', ale#statusline#Status()
Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
- unlet! g:ale_buffer_info
unlet! g:ale_statusline_format
call ale#statusline#Update(1, [])
Execute(ale#statusline#Count should return 0 counts when globals are undefined):
- unlet! g:ale_buffer_info
unlet! g:ale_statusline_format
AssertEqual Counts({}), ale#statusline#Count(1)
Execute(ale#statusline#Status should return 'OK' when globals are undefined):
- unlet! g:ale_buffer_info
unlet! g:ale_statusline_format
AssertEqual 'OK', ale#statusline#Status()
diff --git a/test/test_writefile_function.vader b/test/test_writefile_function.vader
new file mode 100644
index 00000000..4e4aab53
--- /dev/null
+++ b/test/test_writefile_function.vader
@@ -0,0 +1,48 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test')
+
+After:
+ noautocmd :e! ++ff=unix
+ setlocal buftype=nofile
+
+ if filereadable('.newline-test')
+ call delete('.newline-test')
+ endif
+
+ call ale#test#RestoreDirectory()
+
+Given(A file with Windows line ending characters):
+ first
+ second
+ third
+
+Execute(Carriage returns should be included for ale#util#Writefile):
+ call ale#test#SetFilename('.newline-test')
+
+ setlocal buftype=
+ noautocmd :w
+ noautocmd :e! ++ff=dos
+
+ call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test')
+
+ AssertEqual
+ \ ["first\r", "second\r", "third\r", ''],
+ \ readfile('.newline-test', 'b')
+ \
+Given(A file with Unix line ending characters):
+ first
+ second
+ third
+
+Execute(Unix file lines should be written as normal):
+ call ale#test#SetFilename('.newline-test')
+
+ setlocal buftype=
+ noautocmd :w
+ noautocmd :e! ++ff=unix
+
+ call ale#util#Writefile(bufnr(''), getline(1, '$'), '.newline-test')
+
+ AssertEqual
+ \ ['first', 'second', 'third', ''],
+ \ readfile('.newline-test', 'b')
diff --git a/test/vimrc b/test/vimrc
index 57af7e15..8dadb4f2 100644
--- a/test/vimrc
+++ b/test/vimrc
@@ -1,5 +1,8 @@
" vint: -ProhibitSetNoCompatible
+" Make most tests just set lists synchronously when run in Docker.
+let g:ale_set_lists_synchronously = 1
+
" Load builtin plugins
" We need this because run_vim.sh sets -i NONE
set runtimepath=/home/vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,/testplugin,/vader