summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md4
-rw-r--r--.gitignore4
-rw-r--r--Dockerfile2
-rw-r--r--README.md185
-rw-r--r--ale_linters/ada/gcc.vim6
-rw-r--r--ale_linters/ansible/ansible_lint.vim2
-rw-r--r--ale_linters/asciidoc/alex.vim9
-rw-r--r--ale_linters/asciidoc/textlint.vim9
-rw-r--r--ale_linters/asm/gcc.vim4
-rw-r--r--ale_linters/awk/gawk.vim4
-rw-r--r--ale_linters/bib/bibclean.vim53
-rw-r--r--ale_linters/c/ccls.vim6
-rw-r--r--ale_linters/c/clang.vim7
-rw-r--r--ale_linters/c/clangd.vim6
-rw-r--r--ale_linters/c/clangtidy.vim4
-rw-r--r--ale_linters/c/cppcheck.vim4
-rw-r--r--ale_linters/c/cquery.vim6
-rw-r--r--ale_linters/c/flawfinder.vim16
-rw-r--r--ale_linters/c/gcc.vim7
-rw-r--r--ale_linters/chef/cookstyle.vim54
-rw-r--r--ale_linters/chef/foodcritic.vim4
-rw-r--r--ale_linters/clojure/clj_kondo.vim34
-rw-r--r--ale_linters/clojure/joker.vim2
-rw-r--r--ale_linters/cmake/cmakelint.vim4
-rw-r--r--ale_linters/coffee/coffee.vim4
-rw-r--r--ale_linters/coffee/coffeelint.vim4
-rw-r--r--ale_linters/cpp/ccls.vim6
-rw-r--r--ale_linters/cpp/clang.vim7
-rw-r--r--ale_linters/cpp/clangcheck.vim4
-rw-r--r--ale_linters/cpp/clangd.vim6
-rw-r--r--ale_linters/cpp/clangtidy.vim4
-rw-r--r--ale_linters/cpp/clazy.vim4
-rw-r--r--ale_linters/cpp/cppcheck.vim4
-rw-r--r--ale_linters/cpp/cpplint.vim4
-rw-r--r--ale_linters/cpp/cquery.vim6
-rw-r--r--ale_linters/cpp/flawfinder.vim16
-rw-r--r--ale_linters/cpp/gcc.vim7
-rw-r--r--ale_linters/crystal/ameba.vim57
-rw-r--r--ale_linters/crystal/crystal.vim2
-rw-r--r--ale_linters/cs/mcs.vim2
-rw-r--r--ale_linters/cs/mcsc.vim4
-rw-r--r--ale_linters/css/csslint.vim2
-rw-r--r--ale_linters/css/fecs.vim9
-rw-r--r--ale_linters/css/stylelint.vim6
-rw-r--r--ale_linters/cucumber/cucumber.vim2
-rw-r--r--ale_linters/cuda/nvcc.vim6
-rw-r--r--ale_linters/cypher/cypher_lint.vim26
-rw-r--r--ale_linters/d/dls.vim6
-rw-r--r--ale_linters/d/dmd.vim21
-rw-r--r--ale_linters/dart/dartanalyzer.vim4
-rw-r--r--ale_linters/dart/language_server.vim4
-rw-r--r--ale_linters/dockerfile/dockerfile_lint.vim4
-rw-r--r--ale_linters/dockerfile/hadolint.vim4
-rw-r--r--ale_linters/elixir/credo.vim15
-rw-r--r--ale_linters/elixir/dialyxir.vim2
-rw-r--r--ale_linters/elixir/dogma.vim2
-rw-r--r--ale_linters/elixir/elixir_ls.vim10
-rw-r--r--ale_linters/elixir/mix.vim4
-rw-r--r--ale_linters/elm/elm_lsp.vim22
-rw-r--r--ale_linters/elm/make.vim86
-rw-r--r--ale_linters/erlang/erlc.vim4
-rw-r--r--ale_linters/erlang/syntaxerl.vim19
-rw-r--r--ale_linters/eruby/erb.vim2
-rw-r--r--ale_linters/eruby/erubi.vim17
-rw-r--r--ale_linters/eruby/erubis.vim2
-rw-r--r--ale_linters/eruby/ruumba.vim4
-rw-r--r--ale_linters/fortran/gcc.vim4
-rw-r--r--ale_linters/fortran/language_server.vim4
-rw-r--r--ale_linters/fuse/fusionlint.vim4
-rw-r--r--ale_linters/gitcommit/gitlint.vim4
-rw-r--r--ale_linters/glsl/glslang.vim4
-rw-r--r--ale_linters/glsl/glslls.vim6
-rw-r--r--ale_linters/go/bingo.vim29
-rw-r--r--ale_linters/go/gobuild.vim4
-rw-r--r--ale_linters/go/golangci_lint.vim4
-rw-r--r--ale_linters/go/golint.vim4
-rw-r--r--ale_linters/go/gometalinter.vim4
-rw-r--r--ale_linters/go/gopls.vim30
-rw-r--r--ale_linters/go/gosimple.vim2
-rw-r--r--ale_linters/go/gotype.vim4
-rw-r--r--ale_linters/go/govet.vim4
-rw-r--r--ale_linters/go/langserver.vim10
-rw-r--r--ale_linters/go/staticcheck.vim2
-rw-r--r--ale_linters/graphql/eslint.vim4
-rw-r--r--ale_linters/graphql/gqlint.vim8
-rw-r--r--ale_linters/hack/hack.vim4
-rw-r--r--ale_linters/hack/hhast.vim8
-rw-r--r--ale_linters/haml/hamllint.vim6
-rw-r--r--ale_linters/handlebars/embertemplatelint.vim4
-rw-r--r--ale_linters/haskell/cabal_ghc.vim5
-rw-r--r--ale_linters/haskell/ghc.vim2
-rw-r--r--ale_linters/haskell/ghc_mod.vim4
-rw-r--r--ale_linters/haskell/hdevtools.vim4
-rw-r--r--ale_linters/haskell/hie.vim32
-rw-r--r--ale_linters/haskell/hlint.vim4
-rw-r--r--ale_linters/haskell/stack_build.vim4
-rw-r--r--ale_linters/haskell/stack_ghc.vim14
-rw-r--r--ale_linters/help/alex.vim9
-rw-r--r--ale_linters/html/alex.vim9
-rw-r--r--ale_linters/html/fecs.vim9
-rw-r--r--ale_linters/html/htmlhint.vim6
-rw-r--r--ale_linters/html/stylelint.vim4
-rw-r--r--ale_linters/html/tidy.vim4
-rw-r--r--ale_linters/idris/idris.vim12
-rw-r--r--ale_linters/ispc/ispc.vim4
-rw-r--r--ale_linters/java/checkstyle.vim2
-rw-r--r--ale_linters/java/eclipselsp.vim137
-rw-r--r--ale_linters/java/javac.vim45
-rw-r--r--ale_linters/java/javalsp.vim10
-rw-r--r--ale_linters/java/pmd.vim2
-rw-r--r--ale_linters/javascript/eslint.vim4
-rw-r--r--ale_linters/javascript/fecs.vim10
-rwxr-xr-xale_linters/javascript/flow.vim46
-rw-r--r--ale_linters/javascript/flow_ls.vim6
-rw-r--r--ale_linters/javascript/jscs.vim6
-rw-r--r--ale_linters/javascript/jshint.vim6
-rw-r--r--ale_linters/javascript/standard.vim4
-rw-r--r--ale_linters/javascript/tsserver.vim6
-rw-r--r--ale_linters/javascript/xo.vim4
-rw-r--r--ale_linters/json/jsonlint.vim23
-rw-r--r--ale_linters/julia/languageserver.vim6
-rw-r--r--ale_linters/kotlin/kotlinc.vim43
-rw-r--r--ale_linters/kotlin/ktlint.vim48
-rw-r--r--ale_linters/kotlin/languageserver.vim4
-rwxr-xr-xale_linters/less/lessc.vim6
-rw-r--r--ale_linters/less/stylelint.vim6
-rw-r--r--ale_linters/llvm/llc.vim4
-rw-r--r--ale_linters/lua/luac.vim2
-rw-r--r--ale_linters/lua/luacheck.vim4
-rw-r--r--ale_linters/mail/alex.vim11
-rw-r--r--ale_linters/mail/languagetool.vim5
-rw-r--r--ale_linters/markdown/alex.vim9
-rw-r--r--ale_linters/markdown/languagetool.vim5
-rw-r--r--ale_linters/markdown/markdownlint.vim14
-rw-r--r--ale_linters/markdown/mdl.vim4
-rw-r--r--ale_linters/markdown/remark_lint.vim6
-rw-r--r--ale_linters/markdown/textlint.vim4
-rw-r--r--ale_linters/matlab/mlint.vim2
-rw-r--r--ale_linters/mercury/mmc.vim4
-rw-r--r--ale_linters/nasm/nasm.vim4
-rw-r--r--ale_linters/nim/nimcheck.vim2
-rw-r--r--ale_linters/nroff/alex.vim9
-rw-r--r--ale_linters/objc/ccls.vim6
-rw-r--r--ale_linters/objc/clang.vim2
-rw-r--r--ale_linters/objc/clangd.vim6
-rw-r--r--ale_linters/objcpp/clang.vim2
-rw-r--r--ale_linters/objcpp/clangd.vim6
-rw-r--r--ale_linters/ocaml/ols.vim6
-rw-r--r--ale_linters/perl/perl.vim6
-rw-r--r--ale_linters/perl/perlcritic.vim4
-rw-r--r--ale_linters/perl6/perl6.vim22
-rw-r--r--ale_linters/php/langserver.vim12
-rw-r--r--ale_linters/php/phan.vim4
-rw-r--r--ale_linters/php/php.vim2
-rw-r--r--ale_linters/php/phpcs.vim6
-rw-r--r--ale_linters/php/phpmd.vim4
-rw-r--r--ale_linters/php/phpstan.vim55
-rw-r--r--ale_linters/php/psalm.vim6
-rw-r--r--ale_linters/po/alex.vim9
-rw-r--r--ale_linters/pod/alex.vim9
-rw-r--r--ale_linters/pony/ponyc.vim4
-rwxr-xr-xale_linters/powershell/powershell.vim91
-rw-r--r--ale_linters/powershell/psscriptanalyzer.vim76
-rw-r--r--ale_linters/prolog/swipl.vim8
-rw-r--r--ale_linters/proto/protoc_gen_lint.vim2
-rw-r--r--ale_linters/pug/puglint.vim6
-rw-r--r--ale_linters/puppet/languageserver.vim4
-rw-r--r--ale_linters/puppet/puppet.vim4
-rw-r--r--ale_linters/puppet/puppetlint.vim4
-rw-r--r--ale_linters/pyrex/cython.vim4
-rw-r--r--ale_linters/python/bandit.vim68
-rw-r--r--ale_linters/python/flake8.vim49
-rw-r--r--ale_linters/python/mypy.vim4
-rw-r--r--ale_linters/python/prospector.vim4
-rw-r--r--ale_linters/python/pycodestyle.vim4
-rw-r--r--ale_linters/python/pydocstyle.vim7
-rw-r--r--ale_linters/python/pyflakes.vim4
-rw-r--r--ale_linters/python/pylama.vim92
-rw-r--r--ale_linters/python/pylint.vim20
-rw-r--r--ale_linters/python/pyls.vim8
-rw-r--r--ale_linters/python/pyre.vim6
-rw-r--r--ale_linters/python/vulture.vim19
-rw-r--r--ale_linters/qml/qmlfmt.vim2
-rw-r--r--ale_linters/r/lintr.vim2
-rw-r--r--ale_linters/racket/raco.vim33
-rw-r--r--ale_linters/reason/ols.vim6
-rw-r--r--ale_linters/rst/alex.vim9
-rw-r--r--ale_linters/rst/rstcheck.vim2
-rw-r--r--ale_linters/rst/textlint.vim9
-rw-r--r--ale_linters/ruby/brakeman.vim4
-rw-r--r--ale_linters/ruby/rails_best_practices.vim8
-rw-r--r--ale_linters/ruby/reek.vim31
-rw-r--r--ale_linters/ruby/rubocop.vim36
-rw-r--r--ale_linters/ruby/ruby.vim2
-rw-r--r--ale_linters/ruby/solargraph.vim8
-rw-r--r--ale_linters/ruby/standardrb.vim23
-rw-r--r--ale_linters/rust/cargo.vim30
-rw-r--r--ale_linters/rust/rls.vim10
-rw-r--r--ale_linters/rust/rustc.vim2
-rw-r--r--ale_linters/sass/sasslint.vim4
-rw-r--r--ale_linters/sass/stylelint.vim4
-rw-r--r--ale_linters/scala/fsc.vim2
-rw-r--r--ale_linters/scala/sbtserver.vim4
-rw-r--r--ale_linters/scala/scalac.vim2
-rw-r--r--ale_linters/scala/scalastyle.vim2
-rw-r--r--ale_linters/scss/sasslint.vim4
-rw-r--r--ale_linters/scss/stylelint.vim6
-rw-r--r--ale_linters/sh/language_server.vim6
-rw-r--r--ale_linters/sh/shell.vim4
-rw-r--r--ale_linters/sh/shellcheck.vim47
-rw-r--r--ale_linters/slim/slimlint.vim12
-rw-r--r--ale_linters/sml/smlnj.vim2
-rw-r--r--ale_linters/sml/smlnj_cm.vim4
-rw-r--r--ale_linters/spec/rpmlint.vim4
-rw-r--r--ale_linters/stylus/stylelint.vim6
-rw-r--r--ale_linters/sugarss/stylelint.vim21
-rw-r--r--ale_linters/swift/sourcekitlsp.vim13
-rw-r--r--ale_linters/swift/swiftlint.vim22
-rw-r--r--ale_linters/tcl/nagelfar.vim4
-rw-r--r--ale_linters/terraform/tflint.vim4
-rw-r--r--ale_linters/tex/alex.vim9
-rw-r--r--ale_linters/tex/chktex.vim2
-rw-r--r--ale_linters/tex/lacheck.vim16
-rw-r--r--ale_linters/tex/textlint.vim9
-rw-r--r--ale_linters/texinfo/alex.vim9
-rw-r--r--ale_linters/text/alex.vim9
-rw-r--r--ale_linters/text/languagetool.vim4
-rw-r--r--ale_linters/text/textlint.vim4
-rw-r--r--ale_linters/thrift/thrift.vim7
-rw-r--r--ale_linters/typescript/eslint.vim4
-rw-r--r--ale_linters/typescript/tslint.vim4
-rw-r--r--ale_linters/typescript/tsserver.vim6
-rw-r--r--ale_linters/typescript/xo.vim23
-rw-r--r--ale_linters/verilog/iverilog.vim2
-rw-r--r--ale_linters/verilog/verilator.vim4
-rw-r--r--ale_linters/verilog/vlog.vim36
-rw-r--r--ale_linters/verilog/xvlog.vim35
-rw-r--r--ale_linters/vhdl/ghdl.vim37
-rw-r--r--ale_linters/vhdl/vcom.vim38
-rw-r--r--ale_linters/vhdl/xvhdl.vim37
-rw-r--r--ale_linters/vim/ale_custom_linting_rules.vim6
-rw-r--r--ale_linters/vim/vint.vim36
-rw-r--r--ale_linters/vue/vls.vim6
-rw-r--r--ale_linters/xhtml/alex.vim9
-rw-r--r--ale_linters/xml/xmllint.vim28
-rw-r--r--ale_linters/yaml/swaglint.vim4
-rw-r--r--ale_linters/yaml/yamllint.vim4
-rw-r--r--ale_linters/yang/yang_lsp.vim4
-rw-r--r--autoload/ale.vim29
-rw-r--r--autoload/ale/args.vim43
-rw-r--r--autoload/ale/assert.vim204
-rw-r--r--autoload/ale/c.vim186
-rw-r--r--autoload/ale/command.vim327
-rw-r--r--autoload/ale/completion.vim244
-rw-r--r--autoload/ale/cursor.vim8
-rw-r--r--autoload/ale/debugging.vim1
-rw-r--r--autoload/ale/definition.vim81
-rw-r--r--autoload/ale/engine.vim560
-rw-r--r--autoload/ale/engine/ignore.vim6
-rw-r--r--autoload/ale/filetypes.vim2
-rw-r--r--autoload/ale/fix.vim361
-rw-r--r--autoload/ale/fix/registry.vim55
-rw-r--r--autoload/ale/fixers/black.vim29
-rw-r--r--autoload/ale/fixers/cmakeformat.vim18
-rw-r--r--autoload/ale/fixers/eslint.vim24
-rw-r--r--autoload/ale/fixers/fecs.vim17
-rw-r--r--autoload/ale/fixers/floskell.vim20
-rw-r--r--autoload/ale/fixers/jq.vim20
-rw-r--r--autoload/ale/fixers/ktlint.vim9
-rw-r--r--autoload/ale/fixers/latexindent.vim18
-rw-r--r--autoload/ale/fixers/ocp_indent.vim18
-rw-r--r--autoload/ale/fixers/prettier.vim31
-rw-r--r--autoload/ale/fixers/prettier_eslint.vim34
-rw-r--r--autoload/ale/fixers/qmlfmt.vim6
-rw-r--r--autoload/ale/fixers/standard.vim2
-rw-r--r--autoload/ale/fixers/standardrb.vim23
-rw-r--r--autoload/ale/fixers/stylelint.vim9
-rw-r--r--autoload/ale/fixers/styler.vim16
-rw-r--r--autoload/ale/fixers/textlint.vim15
-rw-r--r--autoload/ale/go.vim12
-rw-r--r--autoload/ale/handlers/alex.vim34
-rw-r--r--autoload/ale/handlers/eslint.vim2
-rw-r--r--autoload/ale/handlers/fecs.vim52
-rw-r--r--autoload/ale/handlers/flawfinder.vim3
-rw-r--r--autoload/ale/handlers/haskell.vim8
-rw-r--r--autoload/ale/handlers/ktlint.vim45
-rw-r--r--autoload/ale/handlers/languagetool.vim74
-rw-r--r--autoload/ale/handlers/markdownlint.vim8
-rw-r--r--autoload/ale/handlers/redpen.vim4
-rw-r--r--autoload/ale/handlers/rust.vim2
-rw-r--r--autoload/ale/handlers/sh.vim2
-rw-r--r--autoload/ale/handlers/sml.vim32
-rw-r--r--autoload/ale/handlers/tsserver.vim8
-rw-r--r--autoload/ale/handlers/writegood.vim4
-rw-r--r--autoload/ale/highlight.vim140
-rw-r--r--autoload/ale/hover.vim70
-rw-r--r--autoload/ale/job.vim59
-rw-r--r--autoload/ale/linter.vim96
-rw-r--r--autoload/ale/list.vim2
-rw-r--r--autoload/ale/loclist_jumping.vim100
-rw-r--r--autoload/ale/lsp.vim116
-rw-r--r--autoload/ale/lsp/message.vim25
-rw-r--r--autoload/ale/lsp/response.vim20
-rw-r--r--autoload/ale/lsp_linter.vim280
-rw-r--r--autoload/ale/node.vim7
-rw-r--r--autoload/ale/path.vim12
-rw-r--r--autoload/ale/powershell.vim32
-rw-r--r--autoload/ale/preview.vim14
-rw-r--r--autoload/ale/python.vim43
-rw-r--r--autoload/ale/references.vim70
-rw-r--r--autoload/ale/ruby.vim36
-rw-r--r--autoload/ale/semver.vim49
-rw-r--r--autoload/ale/sign.vim17
-rw-r--r--autoload/ale/statusline.vim75
-rw-r--r--autoload/ale/swift.vim13
-rw-r--r--autoload/ale/symbol.vim37
-rw-r--r--autoload/ale/test.vim116
-rw-r--r--autoload/ale/toggle.vim4
-rw-r--r--autoload/ale/util.vim24
-rw-r--r--autoload/ale/virtualtext.vim5
-rw-r--r--doc/ale-asciidoc.txt6
-rw-r--r--doc/ale-chef.txt20
-rw-r--r--doc/ale-clojure.txt7
-rw-r--r--doc/ale-cmake.txt18
-rw-r--r--doc/ale-css.txt8
-rw-r--r--doc/ale-cuda.txt7
-rw-r--r--doc/ale-development.txt51
-rw-r--r--doc/ale-elixir.txt12
-rw-r--r--doc/ale-elm.txt18
-rw-r--r--doc/ale-go.txt151
-rw-r--r--doc/ale-haskell.txt21
-rw-r--r--doc/ale-html.txt8
-rw-r--r--doc/ale-java.txt57
-rw-r--r--doc/ale-javascript.txt27
-rw-r--r--doc/ale-json.txt16
-rw-r--r--doc/ale-kotlin.txt10
-rw-r--r--doc/ale-latex.txt6
-rw-r--r--doc/ale-ocaml.txt28
-rw-r--r--doc/ale-php.txt8
-rw-r--r--doc/ale-powershell.txt77
-rw-r--r--doc/ale-python.txt165
-rw-r--r--doc/ale-r.txt16
-rw-r--r--doc/ale-restructuredtext.txt14
-rw-r--r--doc/ale-ruby.txt22
-rw-r--r--doc/ale-rust.txt22
-rw-r--r--doc/ale-sass.txt2
-rw-r--r--doc/ale-sh.txt24
-rw-r--r--doc/ale-sugarss.txt31
-rw-r--r--doc/ale-supported-languages-and-tools.txt484
-rw-r--r--doc/ale-swift.txt21
-rw-r--r--doc/ale-tex.txt21
-rw-r--r--doc/ale-verilog.txt61
-rw-r--r--doc/ale-vhdl.txt92
-rw-r--r--doc/ale.txt1605
-rw-r--r--plugin/ale.vim50
-rw-r--r--rplugin/python3/deoplete/sources/ale.py50
-rw-r--r--supported-tools.md493
-rw-r--r--test/command_callback/alex-node-modules-2/node_modules/alex/cli.js (renamed from test/elm-test-files/app/filetest.elm)0
-rw-r--r--test/command_callback/alex-node-modules/node_modules/.bin/alex (renamed from test/elm-test-files/app/node_modules/.bin/elm)0
-rwxr-xr-xtest/command_callback/fecs_paths/fecs0
-rwxr-xr-xtest/command_callback/fecs_paths/fecs.exe0
-rw-r--r--test/command_callback/java_paths_no_main/src/test/java/com/something/dummy0
-rw-r--r--test/command_callback/php-langserver-project/with-composer/composer.json0
-rwxr-xr-xtest/command_callback/php-langserver-project/with-composer/vendor/bin/php-language-server.php0
-rwxr-xr-xtest/command_callback/php-langserver-project/with-git/vendor/bin/php-language-server.php0
-rw-r--r--test/command_callback/python_paths/with_bandit/.bandit0
-rw-r--r--test/command_callback/python_paths/with_bandit/namespace/foo/__init__.py0
-rw-r--r--test/command_callback/python_paths/with_bandit/namespace/foo/bar.py0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/Scripts/pylama.exe0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/Scripts/vulture.exe0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/bin/pylama0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/bin/vulture0
-rw-r--r--test/command_callback/ruby_paths/with_config/.standard.yml0
-rw-r--r--test/command_callback/test_alex_command_callback.vader34
-rw-r--r--test/command_callback/test_ameba_command_callback.vader20
-rw-r--r--test/command_callback/test_asciidoc_textlint_command_callbacks.vader65
-rw-r--r--test/command_callback/test_bandit_command_callback.vader71
-rw-r--r--test/command_callback/test_bingo_command_callback.vader46
-rw-r--r--test/command_callback/test_c_clang_command_callbacks.vader4
-rw-r--r--test/command_callback/test_c_gcc_command_callbacks.vader4
-rw-r--r--test/command_callback/test_cargo_command_callbacks.vader106
-rw-r--r--test/command_callback/test_cookstyle_command_callback.vader19
-rw-r--r--test/command_callback/test_cypher_cypher_lint_command_callback.vader8
-rw-r--r--test/command_callback/test_eclipselsp_command_callback.vader87
-rw-r--r--test/command_callback/test_elixir_credo.vader28
-rw-r--r--test/command_callback/test_elixir_ls_command_callbacks.vader23
-rw-r--r--test/command_callback/test_elm_lsp_command_callbacks.vader29
-rw-r--r--test/command_callback/test_elm_make_command_callback.vader45
-rw-r--r--test/command_callback/test_erlang_syntaxerl_command_callback.vader4
-rw-r--r--test/command_callback/test_erubi_command_callback.vader2
-rw-r--r--test/command_callback/test_fecs_command_callback.vader8
-rw-r--r--test/command_callback/test_flake8_command_callback.vader19
-rw-r--r--test/command_callback/test_ghdl_command_callbacks.vader19
-rw-r--r--test/command_callback/test_gobuild_command_callback.vader2
-rw-r--r--test/command_callback/test_gopls_command_callback.vader46
-rw-r--r--test/command_callback/test_gotype_command_callback.vader2
-rw-r--r--test/command_callback/test_graphql_gqlint_command_callbacks.vader11
-rw-r--r--test/command_callback/test_haskell_cabal_ghc_command_callbacks.vader4
-rw-r--r--test/command_callback/test_haskell_stack_ghc_command_callback.vader5
-rw-r--r--test/command_callback/test_javac_command_callback.vader54
-rw-r--r--test/command_callback/test_javalsp_command_callback.vader4
-rw-r--r--test/command_callback/test_javascript_tsserver_command_callback.vader8
-rw-r--r--test/command_callback/test_kotlinc_command_callback.vader9
-rw-r--r--test/command_callback/test_languagetool_command_callback.vader15
-rw-r--r--test/command_callback/test_php_langserver_callbacks.vader15
-rw-r--r--test/command_callback/test_phpstan_command_callbacks.vader44
-rw-r--r--test/command_callback/test_psalm_command_callbacks.vader7
-rw-r--r--test/command_callback/test_pylama_command_callback.vader85
-rw-r--r--test/command_callback/test_pylint_command_callback.vader6
-rw-r--r--test/command_callback/test_pyls_command_callback.vader5
-rw-r--r--test/command_callback/test_racket_raco_command_callback.vader10
-rw-r--r--test/command_callback/test_reek_command_callback.vader13
-rw-r--r--test/command_callback/test_rst_textlint_command_callbacks.vader65
-rw-r--r--test/command_callback/test_rust_rls_callbacks.vader7
-rw-r--r--test/command_callback/test_shellcheck_command_callback.vader27
-rw-r--r--test/command_callback/test_standardrb_command_callback.vader29
-rw-r--r--test/command_callback/test_sugarss_stylelint_command_callback.vader31
-rw-r--r--test/command_callback/test_swift_sourcekitlsp_command_callbacks.vader21
-rw-r--r--test/command_callback/test_tex_lacheck_command_callback.vader13
-rw-r--r--test/command_callback/test_tex_textlint_command_callbacks.vader65
-rw-r--r--test/command_callback/test_vcom_command_callbacks.vader19
-rw-r--r--test/command_callback/test_vlog_command_callbacks.vader19
-rw-r--r--test/command_callback/test_vulture_command_callback.vader68
-rw-r--r--test/command_callback/test_xo_command_callback.vader20
-rw-r--r--test/command_callback/test_xvhdl_command_callbacks.vader19
-rw-r--r--test/command_callback/test_xvlog_command_callbacks.vader19
-rw-r--r--test/command_callback/tex_paths/sample1.tex0
-rw-r--r--test/command_callback/tex_paths/sample2.tex0
-rw-r--r--test/command_callback/tsserver_paths/src/file1.ts0
-rw-r--r--test/command_callback/tsserver_paths/src/level-1/file2.ts0
-rw-r--r--test/command_callback/tsserver_paths/src/level-1/level-2/file3.ts0
-rw-r--r--test/command_callback/tsserver_paths/src/level-1/tsconfig.json0
-rw-r--r--test/command_callback/tsserver_paths/tsconfig.json0
-rwxr-xr-xtest/compile_database_perf/test.sh29
-rw-r--r--test/completion/test_completion_events.vader106
-rw-r--r--test/completion/test_completion_filtering.vader6
-rw-r--r--test/completion/test_lsp_completion_messages.vader42
-rw-r--r--test/completion/test_lsp_completion_parsing.vader31
-rw-r--r--test/elm-test-files/newapp-notests/elm.json0
-rw-r--r--test/elm-test-files/newapp-notests/node_modules/.bin/elm0
-rw-r--r--test/elm-test-files/newapp-notests/tests/TestMain.elm0
-rw-r--r--test/elm-test-files/newapp/elm.json0
-rw-r--r--test/elm-test-files/newapp/node_modules/.bin/elm0
-rw-r--r--test/elm-test-files/newapp/node_modules/.bin/elm-test0
-rw-r--r--test/elm-test-files/newapp/src/Main.elm0
-rw-r--r--test/elm-test-files/newapp/tests/TestSuite.elm0
-rw-r--r--test/elm-test-files/oldapp/elm-package.json0
-rw-r--r--test/elm-test-files/oldapp/node_modules/.bin/elm0
-rw-r--r--test/elm-test-files/oldapp/node_modules/.bin/elm-test0
-rw-r--r--test/elm-test-files/oldapp/src/Main.elm0
-rw-r--r--test/elm-test-files/oldapp/tests/TestSuite.elm0
-rw-r--r--test/fix/test_ale_fix.vader115
-rw-r--r--test/fix/test_ale_fix_ignore.vader110
-rw-r--r--test/fixers/test_black_fixer_callback.vader18
-rw-r--r--test/fixers/test_cmakeformat_fixer_callback.vader40
-rw-r--r--test/fixers/test_eslint_fixer_callback.vader98
-rw-r--r--test/fixers/test_fecs_fixer_callback.vader26
-rw-r--r--test/fixers/test_floskell_fixer_callback.vader23
-rw-r--r--test/fixers/test_ktlint_fixer_callback.vader44
-rw-r--r--test/fixers/test_latexindent_fixer_callback.vader40
-rw-r--r--test/fixers/test_ocp_indent_fixer_callback.vader34
-rw-r--r--test/fixers/test_prettier_eslint_fixer.callback.vader83
-rw-r--r--test/fixers/test_prettier_fixer_callback.vader162
-rw-r--r--test/fixers/test_standard_fixer_callback.vader14
-rw-r--r--test/fixers/test_standardrb_fixer_callback.vader54
-rw-r--r--test/fixers/test_styler_fixer_callback.vader21
-rw-r--r--test/fixers/test_textlint_fixer_callback.vader42
-rw-r--r--test/handler/test_ameba_handler.vader44
-rw-r--r--test/handler/test_bandit_handler.vader42
-rw-r--r--test/handler/test_clojure_clj_kondo_handler.vader75
-rw-r--r--test/handler/test_cookstyle_handler.vader22
-rw-r--r--test/handler/test_cypher_lint_handler.vader21
-rw-r--r--test/handler/test_eslint_handler.vader17
-rw-r--r--test/handler/test_fecs_handler.vader35
-rw-r--r--test/handler/test_flake8_handler.vader18
-rw-r--r--test/handler/test_flow_handler.vader3
-rw-r--r--test/handler/test_ghdl_handler.vader26
-rw-r--r--test/handler/test_ktlint_handler.vader21
-rw-r--r--test/handler/test_lacheck_handler.vader36
-rw-r--r--test/handler/test_languagetool_handler.vader62
-rw-r--r--test/handler/test_perl_handler.vader16
-rw-r--r--test/handler/test_phpstan_handler.vader15
-rwxr-xr-xtest/handler/test_powershell_handler.vader62
-rw-r--r--test/handler/test_psscriptanalyzer_handler.vader42
-rw-r--r--test/handler/test_pydocstyle_handler.vader6
-rw-r--r--test/handler/test_pylama_handler.vader193
-rw-r--r--test/handler/test_raco_handler.vader26
-rw-r--r--test/handler/test_redpen_handler.vader7
-rw-r--r--test/handler/test_rubocop_handler.vader12
-rw-r--r--test/handler/test_rust_handler.vader18
-rw-r--r--test/handler/test_vcom_handler.vader36
-rw-r--r--test/handler/test_vlog_handler.vader24
-rw-r--r--test/handler/test_vulture_handler.vader2
-rw-r--r--test/handler/test_xvhdl_handler.vader24
-rw-r--r--test/handler/test_xvlog_handler.vader18
-rw-r--r--test/jsonlint-test-files/app-without-jsonlint/src/app.json0
-rw-r--r--test/jsonlint-test-files/app/node_modules/.bin/jsonlint0
-rw-r--r--test/jsonlint-test-files/app/src/app.json0
-rw-r--r--test/jsonlint-test-files/node_modules/jsonlint/lib/cli.js0
-rw-r--r--test/kotlin_files/testfile.kt0
-rw-r--r--test/lsp/test_did_save_event.vader12
-rw-r--r--test/lsp/test_lsp_client_messages.vader28
-rw-r--r--test/lsp/test_lsp_command_formatting.vader2
-rw-r--r--test/lsp/test_lsp_root_detection.vader62
-rw-r--r--test/lsp/test_lsp_startup.vader366
-rw-r--r--test/lsp/test_other_initialize_message_handling.vader21
-rw-r--r--test/lsp/test_read_lsp_diagnostics.vader38
-rw-r--r--test/lsp/test_reset_lsp.vader20
-rw-r--r--test/lsp/test_update_config.vader4
-rw-r--r--test/markdown_files/testfile.md0
-rw-r--r--test/python/test_deoplete_source.py130
-rwxr-xr-xtest/script/block-padding-checker32
-rwxr-xr-xtest/script/check-supported-tools-tables92
-rwxr-xr-xtest/script/check-toc17
-rwxr-xr-xtest/script/custom-checks10
-rwxr-xr-xtest/script/custom-linting-rules1
-rw-r--r--test/sign/test_linting_sets_signs.vader3
-rw-r--r--test/sign/test_sign_placement.vader2
-rw-r--r--test/smoke_test.vader8
-rw-r--r--test/swift-test-files/non-swift-package-project/src/folder/dummy.swift0
-rw-r--r--test/swift-test-files/swift-package-project/Package.swift0
-rw-r--r--test/swift-test-files/swift-package-project/src/folder/dummy.swift0
-rw-r--r--test/test_ale_has.vader7
-rw-r--r--test/test_ale_info.vader1
-rw-r--r--test/test_ale_lint_command.vader4
-rw-r--r--test/test_ale_toggle.vader19
-rw-r--r--test/test_c_flag_parsing.vader233
-rw-r--r--test/test_command_chain.vader3
-rw-r--r--test/test_deferred_command_string.vader50
-rw-r--r--test/test_deferred_executable_string.vader46
-rw-r--r--test/test_engine_invocation.vader39
-rw-r--r--test/test_engine_lsp_response_handling.vader44
-rw-r--r--test/test_errors_removed_after_filetype_changed.vader9
-rw-r--r--test/test_eslint_executable_detection.vader19
-rw-r--r--test/test_filetype_linter_defaults.vader2
-rw-r--r--test/test_find_references.vader97
-rw-r--r--test/test_flow_command.vader37
-rw-r--r--test/test_format_command.vader67
-rw-r--r--test/test_format_temporary_file_creation.vader3
-rw-r--r--test/test_go_to_definition.vader225
-rw-r--r--test/test_highlight_placement.vader3
-rw-r--r--test/test_history_saving.vader68
-rw-r--r--test/test_hover.vader21
-rw-r--r--test/test_ignoring_linters.vader47
-rw-r--r--test/test_jsonlint_executable_detection.vader45
-rw-r--r--test/test_lint_file_linters.vader20
-rw-r--r--test/test_lint_on_enter_when_file_changed.vader2
-rw-r--r--test/test_linter_defintion_processing.vader213
-rw-r--r--test/test_linter_retrieval.vader8
-rw-r--r--test/test_linting_blacklist.vader2
-rw-r--r--test/test_linting_updates_loclist.vader1
-rw-r--r--test/test_loclist_jumping.vader78
-rw-r--r--test/test_no_linting_on_write_quit.vader8
-rw-r--r--test/test_nvim_api_highlight.vader160
-rw-r--r--test/test_parse_command_args.vader52
-rw-r--r--test/test_path_uri.vader47
-rw-r--r--test/test_prepare_command.vader19
-rw-r--r--test/test_python_traceback.vader79
-rw-r--r--test/test_redundant_tsserver_rendering_avoided.vader2
-rw-r--r--test/test_sandbox_execution.vader2
-rw-r--r--test/test_semver_utils.vader23
-rw-r--r--test/test_shell_detection.vader18
-rw-r--r--test/test_should_do_nothing_conditions.vader9
-rw-r--r--test/test_statusline.vader95
-rw-r--r--test/test_swift_find_project_root.vader18
-rw-r--r--test/test_swiftlint_executable_detection.vader1
-rw-r--r--test/test_symbol_search.vader40
-rw-r--r--test/test_temporary_file_management.vader50
-rw-r--r--test/tex_files/testfile.tex0
-rw-r--r--test/vimrc2
570 files changed, 13315 insertions, 4034 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index a73f67ba..785da9b8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -2,7 +2,7 @@
Before creating a pull request, do the following.
* Read the Contributing guide linked above first.
-* Read the documentation that comes with ALE with `:help ale-development`.
+* Read the documentation that comes with ALE with `:help ale-dev`.
Have fun!
-->
@@ -10,4 +10,4 @@ Have fun!
Where are the tests? Have you added tests? Have you updated the tests? Read the
comment above and the documentation referenced in it first. Write tests!
-Seriously, read `:help ale-development` and write tests.
+Seriously, read `:help ale-dev` and write tests.
diff --git a/.gitignore b/.gitignore
index edb6293e..ae9f65fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
!.editorconfig
*.obj
+# Ignore all hidden files everywhere.
+# Use `git add -f` to add hidden files.
.*
+__pycache__
+*.pyc
/doc/tags
/init.vim
/test/ale-info-test-file
diff --git a/Dockerfile b/Dockerfile
index 6111f9ba..58ab0771 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM tweekmonster/vim-testbed:latest
RUN install_vim -tag v8.0.0027 -build \
- -tag v8.1.0204 -build \
+ -tag v8.1.0519 -build \
-tag neovim:v0.2.0 -build \
-tag neovim:v0.3.0 -build
diff --git a/README.md b/README.md
index 5fcca8c7..a35a62ab 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,9 @@
![ALE Logo by Mark Grealish - https://www.bhalash.com/](img/logo.jpg?raw=true)
-ALE (Asynchronous Lint Engine) is a plugin for providing linting in NeoVim
-0.2.0+ and Vim 8 while you edit your text files, and acts as a Vim
-[Language Server Protocol](https://langserver.org/) client.
+ALE (Asynchronous Lint Engine) is a plugin providing linting (syntax checking
+and semantic errors) in NeoVim 0.2.0+ and Vim 8 while you edit your text files,
+and acts as a Vim [Language Server Protocol](https://langserver.org/) client.
<img src="img/example.gif?raw=true" alt="A linting example with the darkspectrum color scheme in GVim." title="A linting example with the darkspectrum color scheme in GVim.">
@@ -26,7 +26,7 @@ features, including:
* Diagnostics (via Language Server Protocol linters)
* Go To Definition (`:ALEGoToDefinition`)
-* Completion (`let g:ale_completion_enabled = 1` before ALE is loaded)
+* Completion (Built in completion support, or with Deoplete)
* Finding references (`:ALEFindReferences`)
* Hover information (`:ALEHover`)
* Symbol search (`:ALESymbolSearch`)
@@ -73,141 +73,15 @@ other content at [w0rp.com](https://w0rp.com).
15. [How can I configure my C or C++ project?](#faq-c-configuration)
16. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
17. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height)
+ 18. [How can I see what ALE has configured for the current file?](#faq-get-info)
<a name="supported-languages"></a>
## 1. Supported Languages and Tools
-This plugin supports the following languages and tools. All available
-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 |
-| -------- | ----- |
-| Ada | [gcc](https://gcc.gnu.org) |
-| ASM | [gcc](https://gcc.gnu.org) |
-| Ansible | [ansible-lint](https://github.com/willthames/ansible-lint) |
-| API Blueprint | [drafter](https://github.com/apiaryio/drafter) |
-| AsciiDoc | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [write-good](https://github.com/btford/write-good), [vale](https://github.com/ValeLint/vale) |
-| Awk | [gawk](https://www.gnu.org/software/gawk/)|
-| Bash | [language-server](https://github.com/mads-hartmann/bash-language-server), shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
-| BibTeX | [bibclean](http://ftp.math.utah.edu/pub/bibclean/) |
-| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
-| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [clang](http://clang.llvm.org/), [clangd](https://clang.llvm.org/extra/clangd.html), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [cquery](https://github.com/cquery-project/cquery), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/), [uncrustify](https://github.com/uncrustify/uncrustify), [ccls](https://github.com/MaskRay/ccls) |
-| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangd](https://clang.llvm.org/extra/clangd.html), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [clazy](https://github.com/KDE/clazy) !!, [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [cquery](https://github.com/cquery-project/cquery), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/), [uncrustify](https://github.com/uncrustify/uncrustify), [ccls](https://github.com/MaskRay/ccls) |
-| CUDA | [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) |
-| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details, [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) !! see:`help ale-cs-mcsc` for details and configuration, [uncrustify](https://github.com/uncrustify/uncrustify) |
-| Chef | [foodcritic](http://www.foodcritic.io/) |
-| Clojure | [joker](https://github.com/candid82/joker) |
-| CloudFormation | [cfn-python-lint](https://github.com/awslabs/cfn-python-lint) |
-| CMake | [cmakelint](https://github.com/richq/cmake-lint) |
-| CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) |
-| Crystal | [crystal](https://crystal-lang.org/) !! |
-| CSS | [csslint](http://csslint.net/), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
-| Cucumber | [cucumber](https://cucumber.io/) |
-| Cython (pyrex filetype) | [cython](http://cython.org/) |
-| D | [dls](https://github.com/d-language-server/dls), [dmd](https://dlang.org/dmd-linux.html), [uncrustify](https://github.com/uncrustify/uncrustify) |
-| Dafny | [dafny](https://rise4fun.com/Dafny) !! |
-| Dart | [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) !!, [language_server](https://github.com/natebosch/dart_language_server), [dartfmt](https://github.com/dart-lang/sdk/tree/master/utils/dartfmt) |
-| Dockerfile | [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint), [hadolint](https://github.com/hadolint/hadolint) |
-| Elixir | [credo](https://github.com/rrrene/credo), [dialyxir](https://github.com/jeremyjh/dialyxir), [dogma](https://github.com/lpil/dogma), [mix](https://hexdocs.pm/mix/Mix.html) !!, [elixir-ls](https://github.com/JakeBecker/elixir-ls) |
-| Elm | [elm-format](https://github.com/avh4/elm-format), [elm-make](https://github.com/elm-lang/elm-make) |
-| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis), [ruumba](https://github.com/ericqweinstein/ruumba) |
-| Erlang | [erlc](http://erlang.org/doc/man/erlc.html), [SyntaxErl](https://github.com/ten0s/syntaxerl) |
-| Fish | fish [-n flag](https://linux.die.net/man/1/fish)
-| Fortran | [gcc](https://gcc.gnu.org/), [language_server](https://github.com/hansec/fortran-language-server) |
-| Fountain | [proselint](http://proselint.com/) |
-| FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) |
-| Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) |
-| GLSL | [glslang](https://github.com/KhronosGroup/glslang), [glslls](https://github.com/svenstaro/glsl-language-server) |
-| Go | [gofmt](https://golang.org/cmd/gofmt/), [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), [go mod](https://golang.org/cmd/go/) !!, [go vet](https://golang.org/cmd/vet/) !!, [golint](https://godoc.org/github.com/golang/lint), [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype) !!, [gometalinter](https://github.com/alecthomas/gometalinter) !!, [go build](https://golang.org/cmd/go/) !!, [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) !!, [staticcheck](https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck) !!, [golangserver](https://github.com/sourcegraph/go-langserver), [golangci-lint](https://github.com/golangci/golangci-lint) !! |
-| GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) |
-| Hack | [hack](http://hacklang.org/), [hackfmt](https://github.com/facebook/hhvm/tree/master/hphp/hack/hackfmt), [hhast](https://github.com/hhvm/hhast) (disabled by default; see `:help ale-integration-hack`) |
-| Haml | [haml-lint](https://github.com/brigade/haml-lint) |
-| Handlebars | [ember-template-lint](https://github.com/rwjblue/ember-template-lint) |
-| Haskell | [brittany](https://github.com/lspitzner/brittany), [ghc](https://www.haskell.org/ghc/), [cabal-ghc](https://www.haskell.org/cabal/), [stylish-haskell](https://github.com/jaspervdj/stylish-haskell), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools), [hfmt](https://github.com/danstiner/hfmt), [hie](https://github.com/haskell/haskell-ide-engine) |
-| HCL | [terraform-fmt](https://github.com/hashicorp/terraform) |
-| HTML | [alex](https://github.com/wooorm/alex) !!, [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/), [prettier](https://github.com/prettier/prettier), [write-good](https://github.com/btford/write-good) |
-| Idris | [idris](http://www.idris-lang.org/) |
-| ISPC | [ispc](https://ispc.github.io/) !! |
-| Java | [checkstyle](http://checkstyle.sourceforge.net), [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html), [google-java-format](https://github.com/google/google-java-format), [PMD](https://pmd.github.io/), [javalsp](https://github.com/georgewfraser/vscode-javac), [uncrustify](https://github.com/uncrustify/uncrustify) |
-| JavaScript | [eslint](http://eslint.org/), [flow](https://flowtype.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [prettier](https://github.com/prettier/prettier), [prettier-eslint](https://github.com/prettier/prettier-eslint-cli), [prettier-standard](https://github.com/sheerun/prettier-standard), [standard](http://standardjs.com/), [xo](https://github.com/sindresorhus/xo)
-| JSON | [fixjson](https://github.com/rhysd/fixjson), [jsonlint](http://zaa.ch/jsonlint/), [jq](https://stedolan.github.io/jq/), [prettier](https://github.com/prettier/prettier) |
-| Julia | [languageserver](https://github.com/JuliaEditorSupport/LanguageServer.jl) |
-| Kotlin | [kotlinc](https://kotlinlang.org) !!, [ktlint](https://ktlint.github.io) !!, [languageserver](https://github.com/fwcd/KotlinLanguageServer) see `:help ale-integration-kotlin` for configuration instructions |
-| LaTeX | [alex](https://github.com/wooorm/alex) !!, [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
-| Less | [lessc](https://www.npmjs.com/package/less), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
-| LLVM | [llc](https://llvm.org/docs/CommandGuide/llc.html) |
-| Lua | [luac](https://www.lua.org/manual/5.1/luac.html), [luacheck](https://github.com/mpeterv/luacheck) |
-| Mail | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) |
-| Make | [checkmake](https://github.com/mrtazz/checkmake) |
-| Markdown | [alex](https://github.com/wooorm/alex) !!, [markdownlint](https://github.com/DavidAnson/markdownlint) !!, [mdl](https://github.com/mivok/markdownlint), [prettier](https://github.com/prettier/prettier), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [remark-lint](https://github.com/wooorm/remark-lint), [textlint](https://textlint.github.io/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
-| MATLAB | [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html) |
-| Mercury | [mmc](http://mercurylang.org) !! |
-| NASM | [nasm](https://www.nasm.us/) !! |
-| Nim | [nim check](https://nim-lang.org/docs/nimc.html) !! |
-| nix | [nix-instantiate](http://nixos.org/nix/manual/#sec-nix-instantiate) |
-| nroff | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)|
-| Objective-C | [clang](http://clang.llvm.org/), [clangd](https://clang.llvm.org/extra/clangd.html), [uncrustify](https://github.com/uncrustify/uncrustify), [ccls](https://github.com/MaskRay/ccls) |
-| Objective-C++ | [clang](http://clang.llvm.org/), [clangd](https://clang.llvm.org/extra/clangd.html), [uncrustify](https://github.com/uncrustify/uncrustify) |
-| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [ocamlformat](https://github.com/ocaml-ppx/ocamlformat) |
-| Pawn | [uncrustify](https://github.com/uncrustify/uncrustify) |
-| Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic), [perltidy](https://metacpan.org/pod/distribution/Perl-Tidy/bin/perltidy) |
-| Perl6 | [perl6 -c](https://perl6.org) |
-| PHP | [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer), [php-cs-fixer](http://cs.sensiolabs.org/), [psalm](https://getpsalm.org) !! |
-| PO | [alex](https://github.com/wooorm/alex) !!, [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html), [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
-| Pod | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
-| Pony | [ponyc](https://github.com/ponylang/ponyc) |
-| Prolog | [swipl](https://github.com/SWI-Prolog/swipl-devel) |
-| proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) |
-| Pug | [pug-lint](https://github.com/pugjs/pug-lint) |
-| Puppet | [languageserver](https://github.com/lingua-pupuli/puppet-editor-services), [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) |
-| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](https://github.com/PyCQA/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pydocstyle](https://www.pydocstyle.org/), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture) !!, [yapf](https://github.com/google/yapf) |
-| QML | [qmlfmt](https://github.com/jesperhh/qmlfmt), [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint) |
-| R | [lintr](https://github.com/jimhester/lintr) |
-| ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-reasonml-ols` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [refmt](https://github.com/reasonml/reason-cli) |
-| reStructuredText | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [rstcheck](https://github.com/myint/rstcheck), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
-| Re:VIEW | [redpen](http://redpen.cc/) |
-| RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) (disabled by default; see `:help ale-integration-spec`) |
-| Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org), [rufo](https://github.com/ruby-formatter/rufo), [solargraph](https://solargraph.org) |
-| Rust | [cargo](https://github.com/rust-lang/cargo) !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.org/), [rustfmt](https://github.com/rust-lang-nursery/rustfmt) |
-| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) |
-| SCSS | [prettier](https://github.com/prettier/prettier), [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint) |
-| Scala | [fsc](https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/tools/fsc.html), [sbtserver](https://www.scala-sbt.org/1.x/docs/sbt-server.html), [scalac](http://scala-lang.org), [scalafmt](https://scalameta.org/scalafmt/), [scalastyle](http://www.scalastyle.org)|
-| Slim | [slim-lint](https://github.com/sds/slim-lint) |
-| SML | [smlnj](http://www.smlnj.org/) |
-| Solidity | [solhint](https://github.com/protofire/solhint), [solium](https://github.com/duaraghav8/Solium) |
-| Stylus | [stylelint](https://github.com/stylelint/stylelint) |
-| SQL | [sqlint](https://github.com/purcell/sqlint), [sqlfmt](https://github.com/jackc/sqlfmt) |
-| Swift | [swiftlint](https://github.com/realm/SwiftLint), [swiftformat](https://github.com/nicklockwood/SwiftFormat) |
-| Tcl | [nagelfar](http://nagelfar.sourceforge.net) !! |
-| Terraform | [fmt](https://github.com/hashicorp/terraform), [tflint](https://github.com/wata727/tflint) |
-| Texinfo | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)|
-| Text^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [textlint](https://textlint.github.io/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
-| Thrift | [thrift](http://thrift.apache.org/) |
-| TypeScript | [eslint](http://eslint.org/), [prettier](https://github.com/prettier/prettier), [tslint](https://github.com/palantir/tslint), [tsserver](https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29), typecheck |
-| VALA | [uncrustify](https://github.com/uncrustify/uncrustify) |
-| Verilog | [iverilog](https://github.com/steveicarus/iverilog), [verilator](http://www.veripool.org/projects/verilator/wiki/Intro) |
-| Vim | [vint](https://github.com/Kuniwak/vint) |
-| Vim help^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
-| Vue | [prettier](https://github.com/prettier/prettier), [vls](https://github.com/vuejs/vetur/tree/master/server) |
-| XHTML | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
-| XML | [xmllint](http://xmlsoft.org/xmllint.html) |
-| YAML | [prettier](https://github.com/prettier/prettier), [swaglint](https://github.com/byCedric/swaglint), [yamllint](https://yamllint.readthedocs.io/) |
-| YANG | [yang-lsp](https://github.com/theia-ide/yang-lsp) |
+ALE supports a wide variety of languages and tools. See the
+[full list](supported-tools.md) in the
+[Supported Languages and Tools](supported-tools.md) page.
<a name="usage"></a>
@@ -227,7 +101,7 @@ new buffers or as you make edits to your files.
The behaviour of linting can be configured with a variety of options,
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.
+ale-integration-options` for options specified to particular linters.
<a name="usage-fixing"></a>
@@ -285,6 +159,18 @@ 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 from `tsserver` for TypeScript.
+ALE integrates with [Deoplete](https://github.com/Shougo/deoplete.nvim) as a
+completion source, named `'ale'`. You can configure Deoplete to only use ALE as
+the source of completion information, or mix it with other sources.
+
+```vim
+" Use ALE and also some plugin 'foobar' as completion sources for all code.
+let g:deoplete#sources = {'_': ['ale', 'foobar']}
+```
+
+ALE also offers its own automatic completion support, which does not require any
+other plugins, and can be enabled by changing a setting before ALE is loaded.
+
```vim
" Enable completion where available.
" This setting must be set before ALE is loaded.
@@ -575,8 +461,16 @@ let g:airline#extensions#ale#enabled = 1
```
If you don't want to use vim-airline, you can implement your own statusline
-function without adding any other plugins. ALE provides a function for counting
-the number of problems for this purpose, named `ale#statusline#Count`.
+function without adding any other plugins. ALE provides some functions to
+assist in this endeavour, including:
+
+* `ale#statusline#Count`: Which returns the number of problems found by ALE
+ for a specified buffer.
+* `ale#statusline#FirstProblem`: Which returns a dictionary containing the
+ full loclist details of the first problem of a specified type found by ALE
+ in a buffer. (e.g. The first style warning in the current buffer.)
+ This can be useful for displaying more detailed information such as the
+ line number of the first problem in a file.
Say you want to display all errors as one figure, and all non-errors as another
figure. You can do the following:
@@ -598,7 +492,8 @@ endfunction
set statusline=%{LinterStatus()}
```
-See `:help ale#statusline#Count()` for more information.
+See `:help ale#statusline#Count()` or `:help ale#statusline#FirstProblem()`
+for more information.
<a name="faq-lightline"></a>
@@ -620,7 +515,7 @@ There are 3 global options that allow customizing the echoed message.
* `%...code...%` is an optional error code, and most characters can be
written between the `%` characters.
* `%linter%` is the linter name
- * `%severity` is the severity type
+ * `%severity%` is the severity type
- `g:ale_echo_msg_error_str` is the string used for error severity.
- `g:ale_echo_msg_warning_str` is the string used for warning severity.
@@ -760,7 +655,7 @@ Or if you want, you can configure the linters from your vimrc file.
```vim
" In ~/.vim/vimrc, or somewhere similar.
-let g:ale_linter_aliases = {'jsx': ['css, 'javascript']}
+let g:ale_linter_aliases = {'jsx': ['css', 'javascript']}
let g:ale_linters = {'jsx': ['stylelint', 'eslint']}
```
@@ -897,3 +792,13 @@ To set a default height for the error list, use the `g:ale_list_window_size` var
" Show 5 lines of errors (default: 10)
let g:ale_list_window_size = 5
```
+
+<a name="faq-get-info"></a>
+
+### 5.xviii. How can I see what ALE has configured for the current file?
+
+Run the following to see what is currently configured:
+
+```vim
+:ALEInfo
+```
diff --git a/ale_linters/ada/gcc.vim b/ale_linters/ada/gcc.vim
index d6f973ae..87496b81 100644
--- a/ale_linters/ada/gcc.vim
+++ b/ale_linters/ada/gcc.vim
@@ -12,7 +12,7 @@ function! ale_linters#ada#gcc#GetCommand(buffer) abort
" the .ali file may be created even if no code generation is attempted.
" The output file name must match the source file name (except for the
" extension), so here we cannot use the null file as output.
- let l:tmp_dir = fnamemodify(ale#engine#CreateDirectory(a:buffer), ':p')
+ let l:tmp_dir = fnamemodify(ale#command#CreateDirectory(a:buffer), ':p')
let l:out_file = l:tmp_dir . fnamemodify(bufname(a:buffer), ':t:r') . '.o'
" -gnatc: Check syntax and semantics only (no code generation attempted)
@@ -48,7 +48,7 @@ endfunction
call ale#linter#Define('ada', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('ada_gcc_executable'),
-\ 'command_callback': 'ale_linters#ada#gcc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'ada_gcc_executable')},
+\ 'command': function('ale_linters#ada#gcc#GetCommand'),
\ 'callback': 'ale_linters#ada#gcc#Handle',
\})
diff --git a/ale_linters/ansible/ansible_lint.vim b/ale_linters/ansible/ansible_lint.vim
index 99fff6c3..c4affa31 100644
--- a/ale_linters/ansible/ansible_lint.vim
+++ b/ale_linters/ansible/ansible_lint.vim
@@ -50,7 +50,7 @@ endfunction
call ale#linter#Define('ansible', {
\ 'name': 'ansible_lint',
\ 'aliases': ['ansible', 'ansible-lint'],
-\ 'executable_callback': 'ale_linters#ansible#ansible_lint#GetExecutable',
+\ 'executable': function('ale_linters#ansible#ansible_lint#GetExecutable'),
\ 'command': '%e -p %t',
\ 'callback': 'ale_linters#ansible#ansible_lint#Handle',
\})
diff --git a/ale_linters/asciidoc/alex.vim b/ale_linters/asciidoc/alex.vim
index 79b04fc3..97976b2c 100644
--- a/ale_linters/asciidoc/alex.vim
+++ b/ale_linters/asciidoc/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for asciidoc files
-call ale#linter#Define('help', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('asciidoc', '--text')
diff --git a/ale_linters/asciidoc/textlint.vim b/ale_linters/asciidoc/textlint.vim
new file mode 100644
index 00000000..308a3a29
--- /dev/null
+++ b/ale_linters/asciidoc/textlint.vim
@@ -0,0 +1,9 @@
+" Author: TANIGUCHI Masaya <ta2gch@gmail.com>
+" Description: textlint for AsciiDoc files
+
+call ale#linter#Define('asciidoc', {
+\ 'name': 'textlint',
+\ 'executable': function('ale#handlers#textlint#GetExecutable'),
+\ 'command': function('ale#handlers#textlint#GetCommand'),
+\ 'callback': 'ale#handlers#textlint#HandleTextlintOutput',
+\})
diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim
index fdd0ee83..72b293c0 100644
--- a/ale_linters/asm/gcc.vim
+++ b/ale_linters/asm/gcc.vim
@@ -28,7 +28,7 @@ endfunction
call ale#linter#Define('asm', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('asm_gcc_executable'),
-\ 'command_callback': 'ale_linters#asm#gcc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'asm_gcc_executable')},
+\ 'command': function('ale_linters#asm#gcc#GetCommand'),
\ 'callback': 'ale_linters#asm#gcc#Handle',
\})
diff --git a/ale_linters/awk/gawk.vim b/ale_linters/awk/gawk.vim
index eb92e45e..f795c57d 100644
--- a/ale_linters/awk/gawk.vim
+++ b/ale_linters/awk/gawk.vim
@@ -15,8 +15,8 @@ endfunction
call ale#linter#Define('awk', {
\ 'name': 'gawk',
-\ 'executable_callback': ale#VarFunc('awk_gawk_executable'),
-\ 'command_callback': 'ale_linters#awk#gawk#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'awk_gawk_executable')},
+\ 'command': function('ale_linters#awk#gawk#GetCommand'),
\ 'callback': 'ale#handlers#gawk#HandleGawkFormat',
\ 'output_stream': 'both'
\})
diff --git a/ale_linters/bib/bibclean.vim b/ale_linters/bib/bibclean.vim
index 6750f22f..9056a9c3 100644
--- a/ale_linters/bib/bibclean.vim
+++ b/ale_linters/bib/bibclean.vim
@@ -11,9 +11,9 @@ endfunction
function! ale_linters#bib#bibclean#get_type(str) abort
if a:str is# '??'
- return 'E'
+ return 'E'
else
- return 'W'
+ return 'W'
endif
endfunction
@@ -36,30 +36,31 @@ function! ale_linters#bib#bibclean#Handle(buffer, lines) abort
let l:msg = ''
for l:line in a:lines
- if empty(l:msg)
- let l:mlist = ale_linters#bib#bibclean#match_msg(l:line)
+ if empty(l:msg)
+ let l:mlist = ale_linters#bib#bibclean#match_msg(l:line)
- if !empty(l:mlist)
- let l:msg = l:mlist[3]
- let l:type = ale_linters#bib#bibclean#get_type(l:mlist[1])
- endif
- else
- if l:type is# 'E'
- let l:mlist = ale_linters#bib#bibclean#match_entry(l:line)
- else
- let l:mlist = ale_linters#bib#bibclean#match_value(l:line)
- endif
+ if !empty(l:mlist)
+ let l:msg = l:mlist[3]
+ let l:type = ale_linters#bib#bibclean#get_type(l:mlist[1])
+ endif
+ else
+ if l:type is# 'E'
+ let l:mlist = ale_linters#bib#bibclean#match_entry(l:line)
+ else
+ let l:mlist = ale_linters#bib#bibclean#match_value(l:line)
+ endif
- if !empty(l:mlist)
- call add(l:output, {
- \ 'lnum': l:mlist[1],
- \ 'col': l:mlist[2],
- \ 'text': l:msg,
- \ 'type': l:type
- \})
- let l:msg = ''
- endif
- endif
+ if !empty(l:mlist)
+ call add(l:output, {
+ \ 'lnum': l:mlist[1],
+ \ 'col': l:mlist[2],
+ \ 'text': l:msg,
+ \ 'type': l:type
+ \})
+
+ let l:msg = ''
+ endif
+ endif
endfor
return l:output
@@ -67,8 +68,8 @@ endfunction
call ale#linter#Define('bib', {
\ 'name': 'bibclean',
-\ 'executable_callback': ale#VarFunc('bib_bibclean_executable'),
-\ 'command_callback': 'ale_linters#bib#bibclean#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'bib_bibclean_executable')},
+\ 'command': function('ale_linters#bib#bibclean#GetCommand'),
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#bib#bibclean#Handle',
\})
diff --git a/ale_linters/c/ccls.vim b/ale_linters/c/ccls.vim
index 5dc2339f..9e3dafe9 100644
--- a/ale_linters/c/ccls.vim
+++ b/ale_linters/c/ccls.vim
@@ -7,8 +7,8 @@ call ale#Set('c_ccls_init_options', {})
call ale#linter#Define('c', {
\ 'name': 'ccls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('c_ccls_executable'),
+\ 'executable': {b -> ale#Var(b, 'c_ccls_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale#handlers#ccls#GetProjectRoot',
-\ 'initialization_options_callback':ale#VarFunc('c_ccls_init_options'),
+\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
+\ 'initialization_options': {b -> ale#Var(b, 'c_ccls_init_options')},
\})
diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim
index f1bd675b..681101fc 100644
--- a/ale_linters/c/clang.vim
+++ b/ale_linters/c/clang.vim
@@ -18,10 +18,7 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('c_clang_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#c#clang#GetCommand'}
-\ ],
+\ 'executable': {b -> ale#Var(b, 'c_clang_executable')},
+\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#c#clang#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/c/clangd.vim b/ale_linters/c/clangd.vim
index 6cad601a..918eadcc 100644
--- a/ale_linters/c/clangd.vim
+++ b/ale_linters/c/clangd.vim
@@ -17,7 +17,7 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('c_clangd_executable'),
-\ 'command_callback': 'ale_linters#c#clangd#GetCommand',
-\ 'project_root_callback': 'ale_linters#c#clangd#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'c_clangd_executable')},
+\ 'command': function('ale_linters#c#clangd#GetCommand'),
+\ 'project_root': function('ale_linters#c#clangd#GetProjectRoot'),
\})
diff --git a/ale_linters/c/clangtidy.vim b/ale_linters/c/clangtidy.vim
index 4f334655..6484f8af 100644
--- a/ale_linters/c/clangtidy.vim
+++ b/ale_linters/c/clangtidy.vim
@@ -35,8 +35,8 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'clangtidy',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('c_clangtidy_executable'),
-\ 'command_callback': 'ale_linters#c#clangtidy#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'c_clangtidy_executable')},
+\ 'command': function('ale_linters#c#clangtidy#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/c/cppcheck.vim b/ale_linters/c/cppcheck.vim
index 5e8c7936..851f9f11 100644
--- a/ale_linters/c/cppcheck.vim
+++ b/ale_linters/c/cppcheck.vim
@@ -28,7 +28,7 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
-\ 'executable_callback': ale#VarFunc('c_cppcheck_executable'),
-\ 'command_callback': 'ale_linters#c#cppcheck#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'c_cppcheck_executable')},
+\ 'command': function('ale_linters#c#cppcheck#GetCommand'),
\ 'callback': 'ale#handlers#cppcheck#HandleCppCheckFormat',
\})
diff --git a/ale_linters/c/cquery.vim b/ale_linters/c/cquery.vim
index a20782a2..d2be9cd9 100644
--- a/ale_linters/c/cquery.vim
+++ b/ale_linters/c/cquery.vim
@@ -21,8 +21,8 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'cquery',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('c_cquery_executable'),
+\ 'executable': {b -> ale#Var(b, 'c_cquery_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale_linters#c#cquery#GetProjectRoot',
-\ 'initialization_options_callback': 'ale_linters#c#cquery#GetInitializationOptions',
+\ 'project_root': function('ale_linters#c#cquery#GetProjectRoot'),
+\ 'initialization_options': function('ale_linters#c#cquery#GetInitializationOptions'),
\})
diff --git a/ale_linters/c/flawfinder.vim b/ale_linters/c/flawfinder.vim
index 7e1f6769..53c36716 100644
--- a/ale_linters/c/flawfinder.vim
+++ b/ale_linters/c/flawfinder.vim
@@ -7,19 +7,19 @@ call ale#Set('c_flawfinder_minlevel', 1)
call ale#Set('c_flawfinder_error_severity', 6)
function! ale_linters#c#flawfinder#GetCommand(buffer) abort
- " Set the minimum vulnerability level for flawfinder to bother with
- let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'c_flawfinder_minlevel')
+ " Set the minimum vulnerability level for flawfinder to bother with
+ let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'c_flawfinder_minlevel')
- return '%e -CDQS'
- \ . ale#Pad(ale#Var(a:buffer, 'c_flawfinder_options'))
- \ . l:minlevel
- \ . ' %t'
+ return '%e -CDQS'
+ \ . ale#Pad(ale#Var(a:buffer, 'c_flawfinder_options'))
+ \ . l:minlevel
+ \ . ' %t'
endfunction
call ale#linter#Define('c', {
\ 'name': 'flawfinder',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('c_flawfinder_executable'),
-\ 'command_callback': 'ale_linters#c#flawfinder#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'c_flawfinder_executable')},
+\ 'command': function('ale_linters#c#flawfinder#GetCommand'),
\ 'callback': 'ale#handlers#flawfinder#HandleFlawfinderFormat',
\})
diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim
index 60ecb712..d965965d 100644
--- a/ale_linters/c/gcc.vim
+++ b/ale_linters/c/gcc.vim
@@ -18,10 +18,7 @@ endfunction
call ale#linter#Define('c', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('c_gcc_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#c#gcc#GetCommand'}
-\ ],
+\ 'executable': {b -> ale#Var(b, 'c_gcc_executable')},
+\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#c#gcc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/chef/cookstyle.vim b/ale_linters/chef/cookstyle.vim
new file mode 100644
index 00000000..50bae2aa
--- /dev/null
+++ b/ale_linters/chef/cookstyle.vim
@@ -0,0 +1,54 @@
+" Author: Raphael Hoegger - https://github.com/pfuender
+" Description: Cookstyle (RuboCop based), a code style analyzer for Ruby files
+
+call ale#Set('chef_cookstyle_executable', 'cookstyle')
+call ale#Set('chef_cookstyle_options', '')
+
+function! ale_linters#chef#cookstyle#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'chef_cookstyle_options')
+
+ return '%e' . ale#Pad(escape(l:options, '~')) . ' --force-exclusion --format json --stdin ' . ' %s'
+endfunction
+
+function! ale_linters#chef#cookstyle#Handle(buffer, lines) abort
+ if len(a:lines) == 0
+ return []
+ endif
+
+ let l:errors = ale#util#FuzzyJSONDecode(a:lines[0], {})
+
+ if !has_key(l:errors, 'summary')
+ \|| l:errors['summary']['offense_count'] == 0
+ \|| empty(l:errors['files'])
+ return []
+ endif
+
+ let l:output = []
+
+ for l:error in l:errors['files'][0]['offenses']
+ let l:start_col = str2nr(l:error['location']['start_column'])
+ let l:end_col = str2nr(l:error['location']['last_column'])
+
+ if !l:end_col
+ let l:end_col = l:start_col + 1
+ endif
+
+ call add(l:output, {
+ \ 'lnum': str2nr(l:error['location']['line']),
+ \ 'col': l:start_col,
+ \ 'end_col': l:end_col,
+ \ 'code': l:error['cop_name'],
+ \ 'text': l:error['message'],
+ \ 'type': l:error['severity'] is? 'convention' ? 'W' : 'E',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('chef', {
+\ 'name': 'cookstyle',
+\ 'executable': {b -> ale#Var(b, 'chef_cookstyle_executable')},
+\ 'command': function('ale_linters#chef#cookstyle#GetCommand'),
+\ 'callback': 'ale_linters#chef#cookstyle#Handle',
+\})
diff --git a/ale_linters/chef/foodcritic.vim b/ale_linters/chef/foodcritic.vim
index c86336d6..48beba75 100644
--- a/ale_linters/chef/foodcritic.vim
+++ b/ale_linters/chef/foodcritic.vim
@@ -34,8 +34,8 @@ endfunction
call ale#linter#Define('chef', {
\ 'name': 'foodcritic',
-\ 'executable_callback': ale#VarFunc('chef_foodcritic_executable'),
-\ 'command_callback': 'ale_linters#chef#foodcritic#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'chef_foodcritic_executable')},
+\ 'command': function('ale_linters#chef#foodcritic#GetCommand'),
\ 'callback': 'ale_linters#chef#foodcritic#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/clojure/clj_kondo.vim b/ale_linters/clojure/clj_kondo.vim
new file mode 100644
index 00000000..5dd11c12
--- /dev/null
+++ b/ale_linters/clojure/clj_kondo.vim
@@ -0,0 +1,34 @@
+" Author: Masashi Iizuka <liquidz.uo@gmail.com>
+" Description: linter for clojure using clj-kondo https://github.com/borkdude/clj-kondo
+
+function! ale_linters#clojure#clj_kondo#HandleCljKondoFormat(buffer, lines) abort
+ " output format
+ " <filename>:<line>:<column>: <issue type>: <message>
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+):? ((Exception|error|warning): ?(.+))$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:type = 'E'
+
+ if l:match[4] is? 'warning'
+ let l:type = 'W'
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[3],
+ \ 'type': l:type,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('clojure', {
+\ 'name': 'clj-kondo',
+\ 'output_stream': 'stdout',
+\ 'executable': 'clj-kondo',
+\ 'command': 'clj-kondo --lint %t',
+\ 'callback': 'ale_linters#clojure#clj_kondo#HandleCljKondoFormat',
+\})
diff --git a/ale_linters/clojure/joker.vim b/ale_linters/clojure/joker.vim
index 2f61148b..1f17cd31 100644
--- a/ale_linters/clojure/joker.vim
+++ b/ale_linters/clojure/joker.vim
@@ -11,7 +11,7 @@ function! ale_linters#clojure#joker#HandleJokerFormat(buffer, lines) abort
let l:type = 'E'
if l:match[4] is? 'Parse warning'
- let l:type = 'W'
+ let l:type = 'W'
endif
call add(l:output, {
diff --git a/ale_linters/cmake/cmakelint.vim b/ale_linters/cmake/cmakelint.vim
index 78676518..d955a265 100644
--- a/ale_linters/cmake/cmakelint.vim
+++ b/ale_linters/cmake/cmakelint.vim
@@ -18,7 +18,7 @@ endfunction
call ale#linter#Define('cmake', {
\ 'name': 'cmakelint',
-\ 'executable_callback': 'ale_linters#cmake#cmakelint#Executable',
-\ 'command_callback': 'ale_linters#cmake#cmakelint#Command',
+\ 'executable': function('ale_linters#cmake#cmakelint#Executable'),
+\ 'command': function('ale_linters#cmake#cmakelint#Command'),
\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/coffee/coffee.vim b/ale_linters/coffee/coffee.vim
index f2539281..8e891639 100644
--- a/ale_linters/coffee/coffee.vim
+++ b/ale_linters/coffee/coffee.vim
@@ -16,8 +16,8 @@ endfunction
call ale#linter#Define('coffee', {
\ 'name': 'coffee',
-\ 'executable_callback': 'ale_linters#coffee#coffee#GetExecutable',
-\ 'command_callback': 'ale_linters#coffee#coffee#GetCommand',
+\ 'executable': function('ale_linters#coffee#coffee#GetExecutable'),
+\ 'command': function('ale_linters#coffee#coffee#GetCommand'),
\ 'output_stream': 'stderr',
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/coffee/coffeelint.vim b/ale_linters/coffee/coffeelint.vim
index 6d3df353..b7c85fa7 100644
--- a/ale_linters/coffee/coffeelint.vim
+++ b/ale_linters/coffee/coffeelint.vim
@@ -37,7 +37,7 @@ endfunction
call ale#linter#Define('coffee', {
\ 'name': 'coffeelint',
-\ 'executable_callback': 'ale_linters#coffee#coffeelint#GetExecutable',
-\ 'command_callback': 'ale_linters#coffee#coffeelint#GetCommand',
+\ 'executable': function('ale_linters#coffee#coffeelint#GetExecutable'),
+\ 'command': function('ale_linters#coffee#coffeelint#GetCommand'),
\ 'callback': 'ale_linters#coffee#coffeelint#Handle',
\})
diff --git a/ale_linters/cpp/ccls.vim b/ale_linters/cpp/ccls.vim
index 501fd685..b265ff70 100644
--- a/ale_linters/cpp/ccls.vim
+++ b/ale_linters/cpp/ccls.vim
@@ -7,8 +7,8 @@ call ale#Set('cpp_ccls_init_options', {})
call ale#linter#Define('cpp', {
\ 'name': 'ccls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('cpp_ccls_executable'),
+\ 'executable': {b -> ale#Var(b, 'cpp_ccls_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale#handlers#ccls#GetProjectRoot',
-\ 'initialization_options_callback': ale#VarFunc('cpp_ccls_init_options'),
+\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
+\ 'initialization_options': {b -> ale#Var(b, 'cpp_ccls_init_options')},
\})
diff --git a/ale_linters/cpp/clang.vim b/ale_linters/cpp/clang.vim
index 649c5993..e48291eb 100644
--- a/ale_linters/cpp/clang.vim
+++ b/ale_linters/cpp/clang.vim
@@ -18,10 +18,7 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cpp_clang_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#cpp#clang#GetCommand'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'cpp_clang_executable')},
+\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#cpp#clang#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/cpp/clangcheck.vim b/ale_linters/cpp/clangcheck.vim
index c66d6702..b511a413 100644
--- a/ale_linters/cpp/clangcheck.vim
+++ b/ale_linters/cpp/clangcheck.vim
@@ -27,8 +27,8 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clangcheck',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cpp_clangcheck_executable'),
-\ 'command_callback': 'ale_linters#cpp#clangcheck#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_clangcheck_executable')},
+\ 'command': function('ale_linters#cpp#clangcheck#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/cpp/clangd.vim b/ale_linters/cpp/clangd.vim
index 9139f054..4a8ff4f6 100644
--- a/ale_linters/cpp/clangd.vim
+++ b/ale_linters/cpp/clangd.vim
@@ -17,7 +17,7 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('cpp_clangd_executable'),
-\ 'command_callback': 'ale_linters#cpp#clangd#GetCommand',
-\ 'project_root_callback': 'ale_linters#cpp#clangd#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'cpp_clangd_executable')},
+\ 'command': function('ale_linters#cpp#clangd#GetCommand'),
+\ 'project_root': function('ale_linters#cpp#clangd#GetProjectRoot'),
\})
diff --git a/ale_linters/cpp/clangtidy.vim b/ale_linters/cpp/clangtidy.vim
index 9c3da8db..841b795f 100644
--- a/ale_linters/cpp/clangtidy.vim
+++ b/ale_linters/cpp/clangtidy.vim
@@ -29,8 +29,8 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clangtidy',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('cpp_clangtidy_executable'),
-\ 'command_callback': 'ale_linters#cpp#clangtidy#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_clangtidy_executable')},
+\ 'command': function('ale_linters#cpp#clangtidy#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/cpp/clazy.vim b/ale_linters/cpp/clazy.vim
index cbbd0ccf..9b29ac9a 100644
--- a/ale_linters/cpp/clazy.vim
+++ b/ale_linters/cpp/clazy.vim
@@ -25,8 +25,8 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clazy',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cpp_clazy_executable'),
-\ 'command_callback': 'ale_linters#cpp#clazy#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_clazy_executable')},
+\ 'command': function('ale_linters#cpp#clazy#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/cpp/cppcheck.vim b/ale_linters/cpp/cppcheck.vim
index 229d6133..5173d698 100644
--- a/ale_linters/cpp/cppcheck.vim
+++ b/ale_linters/cpp/cppcheck.vim
@@ -28,7 +28,7 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
-\ 'executable_callback': ale#VarFunc('cpp_cppcheck_executable'),
-\ 'command_callback': 'ale_linters#cpp#cppcheck#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_cppcheck_executable')},
+\ 'command': function('ale_linters#cpp#cppcheck#GetCommand'),
\ 'callback': 'ale#handlers#cppcheck#HandleCppCheckFormat',
\})
diff --git a/ale_linters/cpp/cpplint.vim b/ale_linters/cpp/cpplint.vim
index d135fa79..f1f6ce7f 100644
--- a/ale_linters/cpp/cpplint.vim
+++ b/ale_linters/cpp/cpplint.vim
@@ -13,8 +13,8 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'cpplint',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cpp_cpplint_executable'),
-\ 'command_callback': 'ale_linters#cpp#cpplint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_cpplint_executable')},
+\ 'command': function('ale_linters#cpp#cpplint#GetCommand'),
\ 'callback': 'ale#handlers#cpplint#HandleCppLintFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/cpp/cquery.vim b/ale_linters/cpp/cquery.vim
index b1c81989..0dd9f6ad 100644
--- a/ale_linters/cpp/cquery.vim
+++ b/ale_linters/cpp/cquery.vim
@@ -21,8 +21,8 @@ endfunction
call ale#linter#Define('cpp', {
\ 'name': 'cquery',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('cpp_cquery_executable'),
+\ 'executable': {b -> ale#Var(b, 'cpp_cquery_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale_linters#cpp#cquery#GetProjectRoot',
-\ 'initialization_options_callback': 'ale_linters#cpp#cquery#GetInitializationOptions',
+\ 'project_root': function('ale_linters#cpp#cquery#GetProjectRoot'),
+\ 'initialization_options': function('ale_linters#cpp#cquery#GetInitializationOptions'),
\})
diff --git a/ale_linters/cpp/flawfinder.vim b/ale_linters/cpp/flawfinder.vim
index 4f669bff..5bfdea22 100644
--- a/ale_linters/cpp/flawfinder.vim
+++ b/ale_linters/cpp/flawfinder.vim
@@ -7,19 +7,19 @@ call ale#Set('cpp_flawfinder_minlevel', 1)
call ale#Set('c_flawfinder_error_severity', 6)
function! ale_linters#cpp#flawfinder#GetCommand(buffer) abort
- " Set the minimum vulnerability level for flawfinder to bother with
- let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'cpp_flawfinder_minlevel')
+ " Set the minimum vulnerability level for flawfinder to bother with
+ let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'cpp_flawfinder_minlevel')
- return '%e -CDQS'
- \ . ale#Var(a:buffer, 'cpp_flawfinder_options')
- \ . l:minlevel
- \ . ' %t'
+ return '%e -CDQS'
+ \ . ale#Var(a:buffer, 'cpp_flawfinder_options')
+ \ . l:minlevel
+ \ . ' %t'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'flawfinder',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('cpp_flawfinder_executable'),
-\ 'command_callback': 'ale_linters#cpp#flawfinder#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cpp_flawfinder_executable')},
+\ 'command': function('ale_linters#cpp#flawfinder#GetCommand'),
\ 'callback': 'ale#handlers#flawfinder#HandleFlawfinderFormat',
\})
diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim
index 9935b0bb..c427020b 100644
--- a/ale_linters/cpp/gcc.vim
+++ b/ale_linters/cpp/gcc.vim
@@ -19,10 +19,7 @@ call ale#linter#Define('cpp', {
\ 'name': 'gcc',
\ 'aliases': ['g++'],
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cpp_gcc_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#cpp#gcc#GetCommand'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'cpp_gcc_executable')},
+\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#cpp#gcc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/crystal/ameba.vim b/ale_linters/crystal/ameba.vim
new file mode 100644
index 00000000..5dfc7f45
--- /dev/null
+++ b/ale_linters/crystal/ameba.vim
@@ -0,0 +1,57 @@
+" Author: Harrison Bachrach - https://github.com/HarrisonB
+" Description: Ameba, a linter for crystal files
+
+call ale#Set('crystal_ameba_executable', 'bin/ameba')
+
+function! ale_linters#crystal#ameba#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'crystal_ameba_executable')
+
+ return ale#Escape(l:executable)
+ \ . ' --format json '
+ \ . ale#Escape(expand('#' . a:buffer . ':p'))
+endfunction
+
+" Handle output from ameba
+function! ale_linters#crystal#ameba#HandleAmebaOutput(buffer, lines) abort
+ if len(a:lines) == 0
+ return []
+ endif
+
+ let l:errors = ale#util#FuzzyJSONDecode(a:lines[0], {})
+
+ if !has_key(l:errors, 'summary')
+ \|| l:errors['summary']['issues_count'] == 0
+ \|| empty(l:errors['sources'])
+ return []
+ endif
+
+ let l:output = []
+
+ for l:error in l:errors['sources'][0]['issues']
+ let l:start_col = str2nr(l:error['location']['column'])
+ let l:end_col = str2nr(l:error['end_location']['column'])
+
+ if !l:end_col
+ let l:end_col = l:start_col + 1
+ endif
+
+ call add(l:output, {
+ \ 'lnum': str2nr(l:error['location']['line']),
+ \ 'col': l:start_col,
+ \ 'end_col': l:end_col,
+ \ 'code': l:error['rule_name'],
+ \ 'text': l:error['message'],
+ \ 'type': 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('crystal', {
+\ 'name': 'ameba',
+\ 'executable': {b -> ale#Var(b, 'crystal_ameba_executable')},
+\ 'command': function('ale_linters#crystal#ameba#GetCommand'),
+\ 'callback': 'ale_linters#crystal#ameba#HandleAmebaOutput',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/crystal/crystal.vim b/ale_linters/crystal/crystal.vim
index 81579d63..3c2fefb7 100644
--- a/ale_linters/crystal/crystal.vim
+++ b/ale_linters/crystal/crystal.vim
@@ -26,6 +26,6 @@ call ale#linter#Define('crystal', {
\ 'executable': 'crystal',
\ 'output_stream': 'both',
\ 'lint_file': 1,
-\ 'command_callback': 'ale_linters#crystal#crystal#GetCommand',
+\ 'command': function('ale_linters#crystal#crystal#GetCommand'),
\ 'callback': 'ale_linters#crystal#crystal#Handle',
\})
diff --git a/ale_linters/cs/mcs.vim b/ale_linters/cs/mcs.vim
index 8738026d..1b373e73 100644
--- a/ale_linters/cs/mcs.vim
+++ b/ale_linters/cs/mcs.vim
@@ -32,6 +32,6 @@ call ale#linter#Define('cs',{
\ 'name': 'mcs',
\ 'output_stream': 'stderr',
\ 'executable': 'mcs',
-\ 'command_callback': 'ale_linters#cs#mcs#GetCommand',
+\ 'command': function('ale_linters#cs#mcs#GetCommand'),
\ 'callback': 'ale_linters#cs#mcs#Handle',
\})
diff --git a/ale_linters/cs/mcsc.vim b/ale_linters/cs/mcsc.vim
index 1561661e..dd067eba 100644
--- a/ale_linters/cs/mcsc.vim
+++ b/ale_linters/cs/mcsc.vim
@@ -30,7 +30,7 @@ function! ale_linters#cs#mcsc#GetCommand(buffer) abort
" register temporary module target file with ale
" register temporary module target file with ALE.
- let l:out = ale#engine#CreateFile(a:buffer)
+ let l:out = ale#command#CreateFile(a:buffer)
" The code is compiled as a module and the output is redirected to a
" temporary file.
@@ -75,7 +75,7 @@ call ale#linter#Define('cs',{
\ 'name': 'mcsc',
\ 'output_stream': 'stderr',
\ 'executable': 'mcs',
-\ 'command_callback': 'ale_linters#cs#mcsc#GetCommand',
+\ 'command': function('ale_linters#cs#mcsc#GetCommand'),
\ 'callback': 'ale_linters#cs#mcsc#Handle',
\ 'lint_file': 1
\})
diff --git a/ale_linters/css/csslint.vim b/ale_linters/css/csslint.vim
index 98b7fdd4..50c21ce2 100644
--- a/ale_linters/css/csslint.vim
+++ b/ale_linters/css/csslint.vim
@@ -13,6 +13,6 @@ endfunction
call ale#linter#Define('css', {
\ 'name': 'csslint',
\ 'executable': 'csslint',
-\ 'command_callback': 'ale_linters#css#csslint#GetCommand',
+\ 'command': function('ale_linters#css#csslint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/css/fecs.vim b/ale_linters/css/fecs.vim
new file mode 100644
index 00000000..511847c6
--- /dev/null
+++ b/ale_linters/css/fecs.vim
@@ -0,0 +1,9 @@
+" Author: harttle <yangjvn@126.com>
+" Description: fecs for CSS files
+
+call ale#linter#Define('css', {
+\ 'name': 'fecs',
+\ 'executable': function('ale#handlers#fecs#GetExecutable'),
+\ 'command': function('ale#handlers#fecs#GetCommand'),
+\ 'callback': 'ale#handlers#fecs#Handle',
+\})
diff --git a/ale_linters/css/stylelint.vim b/ale_linters/css/stylelint.vim
index 6f8bef68..38cb0e0b 100644
--- a/ale_linters/css/stylelint.vim
+++ b/ale_linters/css/stylelint.vim
@@ -11,9 +11,9 @@ endfunction
call ale#linter#Define('css', {
\ 'name': 'stylelint',
-\ 'executable_callback': ale#node#FindExecutableFunc('css_stylelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'css_stylelint', [
\ 'node_modules/.bin/stylelint',
-\ ]),
-\ 'command_callback': 'ale_linters#css#stylelint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#css#stylelint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/cucumber/cucumber.vim b/ale_linters/cucumber/cucumber.vim
index e8ae09ff..0cfd815e 100644
--- a/ale_linters/cucumber/cucumber.vim
+++ b/ale_linters/cucumber/cucumber.vim
@@ -41,6 +41,6 @@ endfunction
call ale#linter#Define('cucumber', {
\ 'name': 'cucumber',
\ 'executable': 'cucumber',
-\ 'command_callback': 'ale_linters#cucumber#cucumber#GetCommand',
+\ 'command': function('ale_linters#cucumber#cucumber#GetCommand'),
\ 'callback': 'ale_linters#cucumber#cucumber#Handle'
\})
diff --git a/ale_linters/cuda/nvcc.vim b/ale_linters/cuda/nvcc.vim
index f4442cb8..f3af07b6 100644
--- a/ale_linters/cuda/nvcc.vim
+++ b/ale_linters/cuda/nvcc.vim
@@ -7,7 +7,7 @@ call ale#Set('cuda_nvcc_options', '-std=c++11')
function! ale_linters#cuda#nvcc#GetCommand(buffer) abort
" Unused: use ale#util#nul_file
" let l:output_file = ale#util#Tempname() . '.ii'
- " call ale#engine#ManageFile(a:buffer, l:output_file)
+ " call ale#command#ManageFile(a:buffer, l:output_file)
return '%e -cuda'
\ . ale#Pad(ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)))
\ . ale#Pad(ale#Var(a:buffer, 'cuda_nvcc_options'))
@@ -42,8 +42,8 @@ endfunction
call ale#linter#Define('cuda', {
\ 'name': 'nvcc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('cuda_nvcc_executable'),
-\ 'command_callback': 'ale_linters#cuda#nvcc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'cuda_nvcc_executable')},
+\ 'command': function('ale_linters#cuda#nvcc#GetCommand'),
\ 'callback': 'ale_linters#cuda#nvcc#HandleNVCCFormat',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/cypher/cypher_lint.vim b/ale_linters/cypher/cypher_lint.vim
new file mode 100644
index 00000000..408ddd6e
--- /dev/null
+++ b/ale_linters/cypher/cypher_lint.vim
@@ -0,0 +1,26 @@
+" Author: Francisco Lopes <francisco@oblita.com>
+" Description: Linting for Neo4j's Cypher
+
+function! ale_linters#cypher#cypher_lint#Handle(buffer, lines) abort
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+): (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \ 'type': 'E',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('cypher', {
+\ 'name': 'cypher_lint',
+\ 'executable': 'cypher-lint',
+\ 'command': 'cypher-lint',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale_linters#cypher#cypher_lint#Handle',
+\})
diff --git a/ale_linters/d/dls.vim b/ale_linters/d/dls.vim
index 7210d21e..78d1c152 100644
--- a/ale_linters/d/dls.vim
+++ b/ale_linters/d/dls.vim
@@ -16,7 +16,7 @@ endfunction
call ale#linter#Define('d', {
\ 'name': 'dls',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#d#dls#GetExecutable',
-\ 'command_callback': 'ale_linters#d#dls#GetExecutable',
-\ 'project_root_callback': 'ale_linters#d#dls#FindProjectRoot',
+\ 'executable': function('ale_linters#d#dls#GetExecutable'),
+\ 'command': function('ale_linters#d#dls#GetExecutable'),
+\ 'project_root': function('ale_linters#d#dls#FindProjectRoot'),
\})
diff --git a/ale_linters/d/dmd.vim b/ale_linters/d/dmd.vim
index c816d592..14461ae6 100644
--- a/ale_linters/d/dmd.vim
+++ b/ale_linters/d/dmd.vim
@@ -1,7 +1,7 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: "dmd for D files"
-function! ale_linters#d#dmd#DUBCommand(buffer) abort
+function! ale_linters#d#dmd#GetDUBCommand(buffer) abort
" If we can't run dub, then skip this command.
if !executable('dub')
" Returning an empty string skips to the DMD command.
@@ -21,7 +21,18 @@ function! ale_linters#d#dmd#DUBCommand(buffer) abort
\ . ' && dub describe --import-paths'
endfunction
-function! ale_linters#d#dmd#DMDCommand(buffer, dub_output) abort
+function! ale_linters#d#dmd#RunDUBCommand(buffer) abort
+ let l:command = ale_linters#d#dmd#GetDUBCommand(a:buffer)
+
+ if empty(l:command)
+ " If we can't run DUB, just run DMD.
+ return ale_linters#d#dmd#DMDCommand(a:buffer, [], {})
+ endif
+
+ return ale#command#Run(a:buffer, l:command, function('ale_linters#d#dmd#DMDCommand'))
+endfunction
+
+function! ale_linters#d#dmd#DMDCommand(buffer, dub_output, meta) abort
let l:import_list = []
" Build a list of import paths generated from DUB, if available.
@@ -57,9 +68,7 @@ endfunction
call ale#linter#Define('d', {
\ 'name': 'dmd',
\ 'executable': 'dmd',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#d#dmd#DUBCommand', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#d#dmd#DMDCommand', 'output_stream': 'stderr'},
-\ ],
+\ 'command': function('ale_linters#d#dmd#RunDUBCommand'),
\ 'callback': 'ale_linters#d#dmd#Handle',
+\ 'output_stream': 'stderr',
\})
diff --git a/ale_linters/dart/dartanalyzer.vim b/ale_linters/dart/dartanalyzer.vim
index 26817df5..0a4d9742 100644
--- a/ale_linters/dart/dartanalyzer.vim
+++ b/ale_linters/dart/dartanalyzer.vim
@@ -29,8 +29,8 @@ endfunction
call ale#linter#Define('dart', {
\ 'name': 'dartanalyzer',
-\ 'executable_callback': ale#VarFunc('dart_dartanalyzer_executable'),
-\ 'command_callback': 'ale_linters#dart#dartanalyzer#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'dart_dartanalyzer_executable')},
+\ 'command': function('ale_linters#dart#dartanalyzer#GetCommand'),
\ 'callback': 'ale_linters#dart#dartanalyzer#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/dart/language_server.vim b/ale_linters/dart/language_server.vim
index 8e0c139b..d0e639c8 100644
--- a/ale_linters/dart/language_server.vim
+++ b/ale_linters/dart/language_server.vim
@@ -14,7 +14,7 @@ endfunction
call ale#linter#Define('dart', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('dart_language_server_executable'),
+\ 'executable': {b -> ale#Var(b, 'dart_language_server_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale_linters#dart#language_server#GetProjectRoot',
+\ 'project_root': function('ale_linters#dart#language_server#GetProjectRoot'),
\})
diff --git a/ale_linters/dockerfile/dockerfile_lint.vim b/ale_linters/dockerfile/dockerfile_lint.vim
index a5a846f2..95768b12 100644
--- a/ale_linters/dockerfile/dockerfile_lint.vim
+++ b/ale_linters/dockerfile/dockerfile_lint.vim
@@ -55,7 +55,7 @@ endfunction
call ale#linter#Define('dockerfile', {
\ 'name': 'dockerfile_lint',
-\ 'executable_callback': ale#VarFunc('dockerfile_dockerfile_lint_executable'),
-\ 'command_callback': 'ale_linters#dockerfile#dockerfile_lint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'dockerfile_dockerfile_lint_executable')},
+\ 'command': function('ale_linters#dockerfile#dockerfile_lint#GetCommand'),
\ 'callback': 'ale_linters#dockerfile#dockerfile_lint#Handle',
\})
diff --git a/ale_linters/dockerfile/hadolint.vim b/ale_linters/dockerfile/hadolint.vim
index dc0f5b9e..e57cd76d 100644
--- a/ale_linters/dockerfile/hadolint.vim
+++ b/ale_linters/dockerfile/hadolint.vim
@@ -93,7 +93,7 @@ endfunction
call ale#linter#Define('dockerfile', {
\ 'name': 'hadolint',
-\ 'executable_callback': 'ale_linters#dockerfile#hadolint#GetExecutable',
-\ 'command_callback': 'ale_linters#dockerfile#hadolint#GetCommand',
+\ 'executable': function('ale_linters#dockerfile#hadolint#GetExecutable'),
+\ 'command': function('ale_linters#dockerfile#hadolint#GetCommand'),
\ 'callback': 'ale_linters#dockerfile#hadolint#Handle',
\})
diff --git a/ale_linters/elixir/credo.vim b/ale_linters/elixir/credo.vim
index d778471c..317ecab3 100644
--- a/ale_linters/elixir/credo.vim
+++ b/ale_linters/elixir/credo.vim
@@ -37,16 +37,27 @@ function! ale_linters#elixir#credo#Handle(buffer, lines) abort
return l:output
endfunction
+function! ale_linters#elixir#credo#GetMode() abort
+ if get(g:, 'ale_elixir_credo_strict', 0)
+ return '--strict'
+ else
+ return 'suggest'
+ endif
+endfunction
+
function! ale_linters#elixir#credo#GetCommand(buffer) abort
let l:project_root = ale#handlers#elixir#FindMixProjectRoot(a:buffer)
+ let l:mode = ale_linters#elixir#credo#GetMode()
return ale#path#CdString(l:project_root)
- \ . ' mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
+ \ . 'mix help credo && '
+ \ . 'mix credo ' . ale_linters#elixir#credo#GetMode()
+ \ . ' --format=flycheck --read-from-stdin %s'
endfunction
call ale#linter#Define('elixir', {
\ 'name': 'credo',
\ 'executable': 'mix',
-\ 'command_callback': 'ale_linters#elixir#credo#GetCommand',
+\ 'command': function('ale_linters#elixir#credo#GetCommand'),
\ 'callback': 'ale_linters#elixir#credo#Handle',
\})
diff --git a/ale_linters/elixir/dialyxir.vim b/ale_linters/elixir/dialyxir.vim
index d28d3c70..c7da7757 100644
--- a/ale_linters/elixir/dialyxir.vim
+++ b/ale_linters/elixir/dialyxir.vim
@@ -35,7 +35,7 @@ endfunction
call ale#linter#Define('elixir', {
\ 'name': 'dialyxir',
\ 'executable': 'mix',
-\ 'command_callback': 'ale_linters#elixir#dialyxir#GetCommand',
+\ 'command': function('ale_linters#elixir#dialyxir#GetCommand'),
\ 'callback': 'ale_linters#elixir#dialyxir#Handle',
\})
diff --git a/ale_linters/elixir/dogma.vim b/ale_linters/elixir/dogma.vim
index dcfb6f28..1c721158 100644
--- a/ale_linters/elixir/dogma.vim
+++ b/ale_linters/elixir/dogma.vim
@@ -39,7 +39,7 @@ endfunction
call ale#linter#Define('elixir', {
\ 'name': 'dogma',
\ 'executable': 'mix',
-\ 'command_callback': 'ale_linters#elixir#dogma#GetCommand',
+\ 'command': function('ale_linters#elixir#dogma#GetCommand'),
\ 'lint_file': 1,
\ 'callback': 'ale_linters#elixir#dogma#Handle',
\})
diff --git a/ale_linters/elixir/elixir_ls.vim b/ale_linters/elixir/elixir_ls.vim
index 3b299ec6..d5517de5 100644
--- a/ale_linters/elixir/elixir_ls.vim
+++ b/ale_linters/elixir/elixir_ls.vim
@@ -6,7 +6,7 @@ call ale#Set('elixir_elixir_ls_config', {})
function! ale_linters#elixir#elixir_ls#GetExecutable(buffer) abort
let l:dir = ale#path#Simplify(ale#Var(a:buffer, 'elixir_elixir_ls_release'))
- let l:cmd = ale#Has('win32') ? '\language_server.bat' : '/language_server.sh'
+ let l:cmd = has('win32') ? '\language_server.bat' : '/language_server.sh'
return l:dir . l:cmd
endfunction
@@ -14,8 +14,8 @@ endfunction
call ale#linter#Define('elixir', {
\ 'name': 'elixir-ls',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#elixir#elixir_ls#GetExecutable',
-\ 'command_callback': 'ale_linters#elixir#elixir_ls#GetExecutable',
-\ 'project_root_callback': 'ale#handlers#elixir#FindMixUmbrellaRoot',
-\ 'lsp_config_callback': ale#VarFunc('elixir_elixir_ls_config'),
+\ 'executable': function('ale_linters#elixir#elixir_ls#GetExecutable'),
+\ 'command': function('ale_linters#elixir#elixir_ls#GetExecutable'),
+\ 'project_root': function('ale#handlers#elixir#FindMixUmbrellaRoot'),
+\ 'lsp_config': {b -> ale#Var(b, 'elixir_elixir_ls_config')},
\})
diff --git a/ale_linters/elixir/mix.vim b/ale_linters/elixir/mix.vim
index dc3c1818..abf5d0aa 100644
--- a/ale_linters/elixir/mix.vim
+++ b/ale_linters/elixir/mix.vim
@@ -32,7 +32,7 @@ endfunction
function! ale_linters#elixir#mix#GetCommand(buffer) abort
let l:project_root = ale#handlers#elixir#FindMixProjectRoot(a:buffer)
- let l:temp_dir = ale#engine#CreateDirectory(a:buffer)
+ let l:temp_dir = ale#command#CreateDirectory(a:buffer)
let l:mix_build_path = has('win32')
\ ? 'set MIX_BUILD_PATH=' . ale#Escape(l:temp_dir) . ' &&'
@@ -46,7 +46,7 @@ endfunction
call ale#linter#Define('elixir', {
\ 'name': 'mix',
\ 'executable': 'mix',
-\ 'command_callback': 'ale_linters#elixir#mix#GetCommand',
+\ 'command': function('ale_linters#elixir#mix#GetCommand'),
\ 'callback': 'ale_linters#elixir#mix#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/elm/elm_lsp.vim b/ale_linters/elm/elm_lsp.vim
new file mode 100644
index 00000000..2259286f
--- /dev/null
+++ b/ale_linters/elm/elm_lsp.vim
@@ -0,0 +1,22 @@
+" Author: antew - https://github.com/antew
+" Description: LSP integration for elm, currently supports diagnostics (linting)
+
+call ale#Set('elm_lsp_executable', 'elm-lsp')
+call ale#Set('elm_lsp_use_global', get(g:, 'ale_use_global_executables', 0))
+
+function! elm_lsp#GetRootDir(buffer) abort
+ let l:elm_json = ale#path#FindNearestFile(a:buffer, 'elm.json')
+
+ return !empty(l:elm_json) ? fnamemodify(l:elm_json, ':p:h') : ''
+endfunction
+
+call ale#linter#Define('elm', {
+\ 'name': 'elm_lsp',
+\ 'lsp': 'stdio',
+\ 'executable': {b -> ale#node#FindExecutable(b, 'elm_lsp', [
+\ 'node_modules/.bin/elm-lsp',
+\ ])},
+\ 'command': '%e --stdio',
+\ 'project_root': function('elm_lsp#GetRootDir'),
+\ 'language': 'elm'
+\})
diff --git a/ale_linters/elm/make.vim b/ale_linters/elm/make.vim
index ddea983f..6b93257f 100644
--- a/ale_linters/elm/make.vim
+++ b/ale_linters/elm/make.vim
@@ -137,9 +137,7 @@ function! ale_linters#elm#make#ParseMessageItem(item) abort
endif
endfunction
-" Return the command to execute the linter in the projects directory.
-" If it doesn't, then this will fail when imports are needed.
-function! ale_linters#elm#make#GetCommand(buffer) abort
+function! ale_linters#elm#make#GetPackageFile(buffer) abort
let l:elm_json = ale#path#FindNearestFile(a:buffer, 'elm.json')
if empty(l:elm_json)
@@ -147,26 +145,96 @@ function! ale_linters#elm#make#GetCommand(buffer) abort
let l:elm_json = ale#path#FindNearestFile(a:buffer, 'elm-package.json')
endif
+ return l:elm_json
+endfunction
+
+function! ale_linters#elm#make#IsVersionGte19(buffer) abort
+ let l:elm_json = ale_linters#elm#make#GetPackageFile(a:buffer)
+
+ if l:elm_json =~# '-package'
+ return 0
+ else
+ return 1
+ endif
+endfunction
+
+function! ale_linters#elm#make#GetRootDir(buffer) abort
+ let l:elm_json = ale_linters#elm#make#GetPackageFile(a:buffer)
+
if empty(l:elm_json)
+ return ''
+ else
+ return fnamemodify(l:elm_json, ':p:h')
+ endif
+endfunction
+
+function! ale_linters#elm#make#IsTest(buffer) abort
+ let l:root_dir = ale_linters#elm#make#GetRootDir(a:buffer)
+
+ if empty(l:root_dir)
+ return 0
+ endif
+
+ let l:tests_dir = join([l:root_dir, 'tests', ''], has('win32') ? '\' : '/')
+
+ let l:buffer_path = fnamemodify(bufname(a:buffer), ':p')
+
+ if stridx(l:buffer_path, l:tests_dir) == 0
+ return 1
+ else
+ return 0
+ endif
+endfunction
+
+" Return the command to execute the linter in the projects directory.
+" If it doesn't, then this will fail when imports are needed.
+function! ale_linters#elm#make#GetCommand(buffer) abort
+ let l:executable = ale_linters#elm#make#GetExecutable(a:buffer)
+ let l:root_dir = ale_linters#elm#make#GetRootDir(a:buffer)
+ let l:is_v19 = ale_linters#elm#make#IsVersionGte19(a:buffer)
+ let l:is_using_elm_test = l:executable =~# 'elm-test$'
+
+ if empty(l:root_dir)
let l:dir_set_cmd = ''
else
- let l:root_dir = fnamemodify(l:elm_json, ':p:h')
let l:dir_set_cmd = 'cd ' . ale#Escape(l:root_dir) . ' && '
endif
+ " elm-test needs to know the path of elm-make if elm isn't installed globally.
+ " https://github.com/rtfeldman/node-test-runner/blob/57728f10668f2d2ab3179e7e3208bcfa9a1f19aa/README.md#--compiler
+ if l:is_v19 && l:is_using_elm_test
+ let l:elm_make_executable = ale#node#FindExecutable(a:buffer, 'elm_make', ['node_modules/.bin/elm'])
+ let l:elm_test_compiler_flag = ' --compiler ' . l:elm_make_executable . ' '
+ else
+ let l:elm_test_compiler_flag = ' '
+ endif
+
" The elm compiler, at the time of this writing, uses '/dev/null' as
" a sort of flag to tell the compiler not to generate an output file,
" which is why this is hard coded here.
" Source: https://github.com/elm-lang/elm-compiler/blob/19d5a769b30ec0b2fc4475985abb4cd94cd1d6c3/builder/src/Generate/Output.hs#L253
- return l:dir_set_cmd . '%e make --report=json --output=/dev/null %t'
+ return l:dir_set_cmd . '%e make --report=json --output=/dev/null' . l:elm_test_compiler_flag . '%t'
+endfunction
+
+function! ale_linters#elm#make#GetExecutable(buffer) abort
+ let l:is_test = ale_linters#elm#make#IsTest(a:buffer)
+ let l:is_v19 = ale_linters#elm#make#IsVersionGte19(a:buffer)
+
+ if l:is_test && l:is_v19
+ return ale#node#FindExecutable(
+ \ a:buffer,
+ \ 'elm_make',
+ \ ['node_modules/.bin/elm-test', 'node_modules/.bin/elm']
+ \)
+ else
+ return ale#node#FindExecutable(a:buffer, 'elm_make', ['node_modules/.bin/elm'])
+ endif
endfunction
call ale#linter#Define('elm', {
\ 'name': 'make',
-\ 'executable_callback': ale#node#FindExecutableFunc('elm_make', [
-\ 'node_modules/.bin/elm',
-\ ]),
+\ 'executable': function('ale_linters#elm#make#GetExecutable'),
\ 'output_stream': 'both',
-\ 'command_callback': 'ale_linters#elm#make#GetCommand',
+\ 'command': function('ale_linters#elm#make#GetCommand'),
\ 'callback': 'ale_linters#elm#make#Handle'
\})
diff --git a/ale_linters/erlang/erlc.vim b/ale_linters/erlang/erlc.vim
index 0bdb4dea..a83bacc3 100644
--- a/ale_linters/erlang/erlc.vim
+++ b/ale_linters/erlang/erlc.vim
@@ -4,7 +4,7 @@ let g:ale_erlang_erlc_options = get(g:, 'ale_erlang_erlc_options', '')
function! ale_linters#erlang#erlc#GetCommand(buffer) abort
let l:output_file = ale#util#Tempname()
- call ale#engine#ManageFile(a:buffer, l:output_file)
+ call ale#command#ManageFile(a:buffer, l:output_file)
return 'erlc -o ' . ale#Escape(l:output_file)
\ . ' ' . ale#Var(a:buffer, 'erlang_erlc_options')
@@ -91,6 +91,6 @@ endfunction
call ale#linter#Define('erlang', {
\ 'name': 'erlc',
\ 'executable': 'erlc',
-\ 'command_callback': 'ale_linters#erlang#erlc#GetCommand',
+\ 'command': function('ale_linters#erlang#erlc#GetCommand'),
\ 'callback': 'ale_linters#erlang#erlc#Handle',
\})
diff --git a/ale_linters/erlang/syntaxerl.vim b/ale_linters/erlang/syntaxerl.vim
index 5b679743..5d555a8d 100644
--- a/ale_linters/erlang/syntaxerl.vim
+++ b/ale_linters/erlang/syntaxerl.vim
@@ -3,7 +3,17 @@
call ale#Set('erlang_syntaxerl_executable', 'syntaxerl')
-function! ale_linters#erlang#syntaxerl#GetCommand(buffer, output) abort
+function! ale_linters#erlang#syntaxerl#RunHelpCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'erlang_syntaxerl_executable')
+
+ return ale#command#Run(
+ \ a:buffer,
+ \ ale#Escape(l:executable) . ' -h',
+ \ function('ale_linters#erlang#syntaxerl#GetCommand'),
+ \)
+endfunction
+
+function! ale_linters#erlang#syntaxerl#GetCommand(buffer, output, meta) abort
let l:use_b_option = match(a:output, '\C\V-b, --base\>') > -1
return '%e' . (l:use_b_option ? ' -b %s %t' : ' %t')
@@ -26,10 +36,7 @@ endfunction
call ale#linter#Define('erlang', {
\ 'name': 'syntaxerl',
-\ 'executable_callback': ale#VarFunc('erlang_syntaxerl_executable'),
-\ 'command_chain': [
-\ {'callback': {-> '%e -h'}},
-\ {'callback': 'ale_linters#erlang#syntaxerl#GetCommand'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'erlang_syntaxerl_executable')},
+\ 'command': {b -> ale_linters#erlang#syntaxerl#RunHelpCommand(b)},
\ 'callback': 'ale_linters#erlang#syntaxerl#Handle',
\})
diff --git a/ale_linters/eruby/erb.vim b/ale_linters/eruby/erb.vim
index 61d97032..f3438320 100644
--- a/ale_linters/eruby/erb.vim
+++ b/ale_linters/eruby/erb.vim
@@ -19,7 +19,7 @@ call ale#linter#Define('eruby', {
\ 'aliases': ['erubylint'],
\ 'executable': 'erb',
\ 'output_stream': 'stderr',
-\ 'command_callback': 'ale_linters#eruby#erb#GetCommand',
+\ 'command': function('ale_linters#eruby#erb#GetCommand'),
\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
\})
diff --git a/ale_linters/eruby/erubi.vim b/ale_linters/eruby/erubi.vim
index 6f2d3ac6..ddca3f61 100644
--- a/ale_linters/eruby/erubi.vim
+++ b/ale_linters/eruby/erubi.vim
@@ -1,14 +1,10 @@
" Author: Eddie Lebow https://github.com/elebow
" Description: eruby checker using `erubi`
-function! ale_linters#eruby#erubi#CheckErubi(buffer) abort
- return 'ruby -r erubi/capture_end -e ' . ale#Escape('""')
-endfunction
-
-function! ale_linters#eruby#erubi#GetCommand(buffer, check_erubi_output) abort
+function! ale_linters#eruby#erubi#GetCommand(buffer, output, meta) abort
let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
- if (!empty(a:check_erubi_output))
+ if !empty(a:output)
" The empty command in CheckErubi returns nothing if erubi runs and
" emits an error if erubi is not present
return ''
@@ -27,9 +23,10 @@ endfunction
call ale#linter#Define('eruby', {
\ 'name': 'erubi',
\ 'executable': 'ruby',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#eruby#erubi#CheckErubi'},
-\ {'callback': 'ale_linters#eruby#erubi#GetCommand', 'output_stream': 'stderr'},
-\ ],
+\ 'command': {buffer -> ale#command#Run(
+\ buffer,
+\ 'ruby -r erubi/capture_end -e ' . ale#Escape('""'),
+\ function('ale_linters#eruby#erubi#GetCommand'),
+\ )},
\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
\})
diff --git a/ale_linters/eruby/erubis.vim b/ale_linters/eruby/erubis.vim
index 1ebd4a05..755c5803 100644
--- a/ale_linters/eruby/erubis.vim
+++ b/ale_linters/eruby/erubis.vim
@@ -18,6 +18,6 @@ call ale#linter#Define('eruby', {
\ 'name': 'erubis',
\ 'executable': 'erubis',
\ 'output_stream': 'stderr',
-\ 'command_callback': 'ale_linters#eruby#erubis#GetCommand',
+\ 'command': function('ale_linters#eruby#erubis#GetCommand'),
\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
\})
diff --git a/ale_linters/eruby/ruumba.vim b/ale_linters/eruby/ruumba.vim
index 24f112e4..e68bb51d 100644
--- a/ale_linters/eruby/ruumba.vim
+++ b/ale_linters/eruby/ruumba.vim
@@ -56,7 +56,7 @@ endfunction
call ale#linter#Define('eruby', {
\ 'name': 'ruumba',
-\ 'executable_callback': ale#VarFunc('eruby_ruumba_executable'),
-\ 'command_callback': 'ale_linters#eruby#ruumba#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'eruby_ruumba_executable')},
+\ 'command': function('ale_linters#eruby#ruumba#GetCommand'),
\ 'callback': 'ale_linters#eruby#ruumba#Handle',
\})
diff --git a/ale_linters/fortran/gcc.vim b/ale_linters/fortran/gcc.vim
index f1595789..6e97d6fd 100644
--- a/ale_linters/fortran/gcc.vim
+++ b/ale_linters/fortran/gcc.vim
@@ -66,7 +66,7 @@ endfunction
call ale#linter#Define('fortran', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('fortran_gcc_executable'),
-\ 'command_callback': 'ale_linters#fortran#gcc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'fortran_gcc_executable')},
+\ 'command': function('ale_linters#fortran#gcc#GetCommand'),
\ 'callback': 'ale_linters#fortran#gcc#Handle',
\})
diff --git a/ale_linters/fortran/language_server.vim b/ale_linters/fortran/language_server.vim
index 4e5f5dc4..00aa0577 100644
--- a/ale_linters/fortran/language_server.vim
+++ b/ale_linters/fortran/language_server.vim
@@ -13,7 +13,7 @@ endfunction
call ale#linter#Define('fortran', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('fortran_language_server_executable'),
+\ 'executable': {b -> ale#Var(b, 'fortran_language_server_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale_linters#fortran#language_server#GetProjectRoot',
+\ 'project_root': function('ale_linters#fortran#language_server#GetProjectRoot'),
\})
diff --git a/ale_linters/fuse/fusionlint.vim b/ale_linters/fuse/fusionlint.vim
index ab8f143b..ffb25d33 100644
--- a/ale_linters/fuse/fusionlint.vim
+++ b/ale_linters/fuse/fusionlint.vim
@@ -27,7 +27,7 @@ endfunction
call ale#linter#Define('fuse', {
\ 'name': 'fusionlint',
-\ 'executable_callback': ale#VarFunc('fuse_fusionlint_executable'),
-\ 'command_callback': 'ale_linters#fuse#fusionlint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'fuse_fusionlint_executable')},
+\ 'command': function('ale_linters#fuse#fusionlint#GetCommand'),
\ 'callback': 'ale_linters#fuse#fusionlint#Handle',
\})
diff --git a/ale_linters/gitcommit/gitlint.vim b/ale_linters/gitcommit/gitlint.vim
index a9c4822d..4b9cec63 100644
--- a/ale_linters/gitcommit/gitlint.vim
+++ b/ale_linters/gitcommit/gitlint.vim
@@ -45,7 +45,7 @@ endfunction
call ale#linter#Define('gitcommit', {
\ 'name': 'gitlint',
\ 'output_stream': 'stderr',
-\ 'executable_callback': 'ale_linters#gitcommit#gitlint#GetExecutable',
-\ 'command_callback': 'ale_linters#gitcommit#gitlint#GetCommand',
+\ 'executable': function('ale_linters#gitcommit#gitlint#GetExecutable'),
+\ 'command': function('ale_linters#gitcommit#gitlint#GetCommand'),
\ 'callback': 'ale_linters#gitcommit#gitlint#Handle',
\})
diff --git a/ale_linters/glsl/glslang.vim b/ale_linters/glsl/glslang.vim
index d55a5e7c..bbddce90 100644
--- a/ale_linters/glsl/glslang.vim
+++ b/ale_linters/glsl/glslang.vim
@@ -34,7 +34,7 @@ endfunction
call ale#linter#Define('glsl', {
\ 'name': 'glslang',
-\ 'executable_callback': ale#VarFunc('glsl_glslang_executable'),
-\ 'command_callback': 'ale_linters#glsl#glslang#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'glsl_glslang_executable')},
+\ 'command': function('ale_linters#glsl#glslang#GetCommand'),
\ 'callback': 'ale_linters#glsl#glslang#Handle',
\})
diff --git a/ale_linters/glsl/glslls.vim b/ale_linters/glsl/glslls.vim
index 8c6d9bd9..b62844c7 100644
--- a/ale_linters/glsl/glslls.vim
+++ b/ale_linters/glsl/glslls.vim
@@ -24,7 +24,7 @@ endfunction
call ale#linter#Define('glsl', {
\ 'name': 'glslls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('glsl_glslls_executable'),
-\ 'command_callback': 'ale_linters#glsl#glslls#GetCommand',
-\ 'project_root_callback': 'ale_linters#glsl#glslls#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'glsl_glslls_executable')},
+\ 'command': function('ale_linters#glsl#glslls#GetCommand'),
+\ 'project_root': function('ale_linters#glsl#glslls#GetProjectRoot'),
\})
diff --git a/ale_linters/go/bingo.vim b/ale_linters/go/bingo.vim
new file mode 100644
index 00000000..e446bdcc
--- /dev/null
+++ b/ale_linters/go/bingo.vim
@@ -0,0 +1,29 @@
+" Author: Jerko Steiner <https://github.com/jeremija>
+" Description: https://github.com/saibing/bingo
+
+call ale#Set('go_bingo_executable', 'bingo')
+call ale#Set('go_bingo_options', '--mode stdio')
+
+function! ale_linters#go#bingo#GetCommand(buffer) abort
+ return '%e' . ale#Pad(ale#Var(a:buffer, 'go_bingo_options'))
+endfunction
+
+function! ale_linters#go#bingo#FindProjectRoot(buffer) abort
+ let l:project_root = ale#path#FindNearestFile(a:buffer, 'go.mod')
+ let l:mods = ':h'
+
+ if empty(l:project_root)
+ let l:project_root = ale#path#FindNearestDirectory(a:buffer, '.git')
+ let l:mods = ':h:h'
+ endif
+
+ return !empty(l:project_root) ? fnamemodify(l:project_root, l:mods) : ''
+endfunction
+
+call ale#linter#Define('go', {
+\ 'name': 'bingo',
+\ 'lsp': 'stdio',
+\ 'executable': {b -> ale#Var(b, 'go_bingo_executable')},
+\ 'command': function('ale_linters#go#bingo#GetCommand'),
+\ 'project_root': function('ale_linters#go#bingo#FindProjectRoot'),
+\})
diff --git a/ale_linters/go/gobuild.vim b/ale_linters/go/gobuild.vim
index cef1ff88..374ded35 100644
--- a/ale_linters/go/gobuild.vim
+++ b/ale_linters/go/gobuild.vim
@@ -48,8 +48,8 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'gobuild',
\ 'aliases': ['go build'],
-\ 'executable_callback': ale#VarFunc('go_go_executable'),
-\ 'command_callback': 'ale_linters#go#gobuild#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'go_go_executable')},
+\ 'command': function('ale_linters#go#gobuild#GetCommand'),
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#go#gobuild#Handler',
\ 'lint_file': 1,
diff --git a/ale_linters/go/golangci_lint.vim b/ale_linters/go/golangci_lint.vim
index dd9a3c64..357f7949 100644
--- a/ale_linters/go/golangci_lint.vim
+++ b/ale_linters/go/golangci_lint.vim
@@ -49,8 +49,8 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'golangci-lint',
-\ 'executable_callback': ale#VarFunc('go_golangci_lint_executable'),
-\ 'command_callback': 'ale_linters#go#golangci_lint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'go_golangci_lint_executable')},
+\ 'command': function('ale_linters#go#golangci_lint#GetCommand'),
\ 'callback': 'ale_linters#go#golangci_lint#Handler',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/golint.vim b/ale_linters/go/golint.vim
index 4bf14fcc..765e1477 100644
--- a/ale_linters/go/golint.vim
+++ b/ale_linters/go/golint.vim
@@ -15,7 +15,7 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'golint',
\ 'output_stream': 'both',
-\ 'executable_callback': ale#VarFunc('go_golint_executable'),
-\ 'command_callback': 'ale_linters#go#golint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'go_golint_executable')},
+\ 'command': function('ale_linters#go#golint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/go/gometalinter.vim b/ale_linters/go/gometalinter.vim
index d005e1d2..19d70a81 100644
--- a/ale_linters/go/gometalinter.vim
+++ b/ale_linters/go/gometalinter.vim
@@ -50,8 +50,8 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'gometalinter',
-\ 'executable_callback': ale#VarFunc('go_gometalinter_executable'),
-\ 'command_callback': 'ale_linters#go#gometalinter#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'go_gometalinter_executable')},
+\ 'command': function('ale_linters#go#gometalinter#GetCommand'),
\ 'callback': 'ale_linters#go#gometalinter#Handler',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/gopls.vim b/ale_linters/go/gopls.vim
new file mode 100644
index 00000000..c411dc2b
--- /dev/null
+++ b/ale_linters/go/gopls.vim
@@ -0,0 +1,30 @@
+" Author: w0rp <devw0rp@gmail.com>
+" Author: Jerko Steiner <https://github.com/jeremija>
+" Description: https://github.com/saibing/gopls
+
+call ale#Set('go_gopls_executable', 'gopls')
+call ale#Set('go_gopls_options', '--mode stdio')
+
+function! ale_linters#go#gopls#GetCommand(buffer) abort
+ return '%e' . ale#Pad(ale#Var(a:buffer, 'go_gopls_options'))
+endfunction
+
+function! ale_linters#go#gopls#FindProjectRoot(buffer) abort
+ let l:project_root = ale#path#FindNearestFile(a:buffer, 'go.mod')
+ let l:mods = ':h'
+
+ if empty(l:project_root)
+ let l:project_root = ale#path#FindNearestDirectory(a:buffer, '.git')
+ let l:mods = ':h:h'
+ endif
+
+ return !empty(l:project_root) ? fnamemodify(l:project_root, l:mods) : ''
+endfunction
+
+call ale#linter#Define('go', {
+\ 'name': 'gopls',
+\ 'lsp': 'stdio',
+\ 'executable': {b -> ale#Var(b, 'go_gopls_executable')},
+\ 'command': function('ale_linters#go#gopls#GetCommand'),
+\ 'project_root': function('ale_linters#go#gopls#FindProjectRoot'),
+\})
diff --git a/ale_linters/go/gosimple.vim b/ale_linters/go/gosimple.vim
index dbdc3fcf..281a0e53 100644
--- a/ale_linters/go/gosimple.vim
+++ b/ale_linters/go/gosimple.vim
@@ -8,7 +8,7 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'gosimple',
\ 'executable': 'gosimple',
-\ 'command_callback': 'ale_linters#go#gosimple#GetCommand',
+\ 'command': function('ale_linters#go#gosimple#GetCommand'),
\ 'callback': 'ale#handlers#go#Handler',
\ 'output_stream': 'both',
\ 'lint_file': 1,
diff --git a/ale_linters/go/gotype.vim b/ale_linters/go/gotype.vim
index a678e0f3..d5d563aa 100644
--- a/ale_linters/go/gotype.vim
+++ b/ale_linters/go/gotype.vim
@@ -6,14 +6,14 @@ function! ale_linters#go#gotype#GetCommand(buffer) abort
return ''
endif
- return ale#path#BufferCdString(a:buffer) . ' gotype .'
+ return ale#path#BufferCdString(a:buffer) . ' gotype -e .'
endfunction
call ale#linter#Define('go', {
\ 'name': 'gotype',
\ 'output_stream': 'stderr',
\ 'executable': 'gotype',
-\ 'command_callback': 'ale_linters#go#gotype#GetCommand',
+\ 'command': function('ale_linters#go#gotype#GetCommand'),
\ 'callback': 'ale#handlers#go#Handler',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/govet.vim b/ale_linters/go/govet.vim
index 3d0d2adf..bb81d5d0 100644
--- a/ale_linters/go/govet.vim
+++ b/ale_linters/go/govet.vim
@@ -20,8 +20,8 @@ call ale#linter#Define('go', {
\ 'name': 'govet',
\ 'aliases': ['go vet'],
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('go_go_executable'),
-\ 'command_callback': 'ale_linters#go#govet#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'go_go_executable')},
+\ 'command': function('ale_linters#go#govet#GetCommand'),
\ 'callback': 'ale#handlers#go#Handler',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/langserver.vim b/ale_linters/go/langserver.vim
index df956483..776186c7 100644
--- a/ale_linters/go/langserver.vim
+++ b/ale_linters/go/langserver.vim
@@ -10,8 +10,8 @@ function! ale_linters#go#langserver#GetCommand(buffer) abort
let l:options = substitute(l:options, '-gocodecompletion', '', 'g')
let l:options = filter(split(l:options, ' '), 'empty(v:val) != 1')
- if(ale#Var(a:buffer, 'completion_enabled') == 1)
- call add(l:options, '-gocodecompletion')
+ if ale#Var(a:buffer, 'completion_enabled')
+ call add(l:options, '-gocodecompletion')
endif
let l:options = uniq(sort(l:options))
@@ -22,7 +22,7 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'golangserver',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('go_langserver_executable'),
-\ 'command_callback': 'ale_linters#go#langserver#GetCommand',
-\ 'project_root_callback': 'ale#go#FindProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'go_langserver_executable')},
+\ 'command': function('ale_linters#go#langserver#GetCommand'),
+\ 'project_root': function('ale#go#FindProjectRoot'),
\})
diff --git a/ale_linters/go/staticcheck.vim b/ale_linters/go/staticcheck.vim
index a3464015..26fe0193 100644
--- a/ale_linters/go/staticcheck.vim
+++ b/ale_linters/go/staticcheck.vim
@@ -26,7 +26,7 @@ endfunction
call ale#linter#Define('go', {
\ 'name': 'staticcheck',
\ 'executable': 'staticcheck',
-\ 'command_callback': 'ale_linters#go#staticcheck#GetCommand',
+\ 'command': function('ale_linters#go#staticcheck#GetCommand'),
\ 'callback': 'ale#handlers#go#Handler',
\ 'output_stream': 'both',
\ 'lint_file': 1,
diff --git a/ale_linters/graphql/eslint.vim b/ale_linters/graphql/eslint.vim
index dfcbf9d9..654b8c17 100644
--- a/ale_linters/graphql/eslint.vim
+++ b/ale_linters/graphql/eslint.vim
@@ -3,7 +3,7 @@
call ale#linter#Define('graphql', {
\ 'name': 'eslint',
-\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
-\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'executable': function('ale#handlers#eslint#GetExecutable'),
+\ 'command': function('ale#handlers#eslint#GetCommand'),
\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/graphql/gqlint.vim b/ale_linters/graphql/gqlint.vim
index 882cc697..d5029de1 100644
--- a/ale_linters/graphql/gqlint.vim
+++ b/ale_linters/graphql/gqlint.vim
@@ -1,9 +1,15 @@
" Author: Michiel Westerbeek <happylinks@gmail.com>
" Description: Linter for GraphQL Schemas
+function! ale_linters#graphql#gqlint#GetCommand(buffer) abort
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'gqlint'
+ \ . ' --reporter=simple %t'
+endfunction
+
call ale#linter#Define('graphql', {
\ 'name': 'gqlint',
\ 'executable': 'gqlint',
-\ 'command': 'gqlint --reporter=simple %t',
+\ 'command': function('ale_linters#graphql#gqlint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/hack/hack.vim b/ale_linters/hack/hack.vim
index aea428cc..822b5c87 100644
--- a/ale_linters/hack/hack.vim
+++ b/ale_linters/hack/hack.vim
@@ -16,7 +16,7 @@ endfunction
call ale#linter#Define('hack', {
\ 'name': 'hack',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#hack#hack#GetExecutable',
+\ 'executable': function('ale_linters#hack#hack#GetExecutable'),
\ 'command': '%e lsp --from vim-ale',
-\ 'project_root_callback': 'ale_linters#hack#hack#GetProjectRoot',
+\ 'project_root': function('ale_linters#hack#hack#GetProjectRoot'),
\})
diff --git a/ale_linters/hack/hhast.vim b/ale_linters/hack/hhast.vim
index 710b7b25..5e6d4dec 100644
--- a/ale_linters/hack/hhast.vim
+++ b/ale_linters/hack/hhast.vim
@@ -9,7 +9,7 @@ function! ale_linters#hack#hhast#GetProjectRoot(buffer) abort
let l:hhconfig = ale#path#FindNearestFile(a:buffer, '.hhconfig')
if empty(l:hhconfig)
- return ''
+ return ''
endif
let l:root = fnamemodify(l:hhconfig, ':h')
@@ -33,8 +33,8 @@ endfunction
call ale#linter#Define('hack', {
\ 'name': 'hhast',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#hack#hhast#GetExecutable',
+\ 'executable': function('ale_linters#hack#hhast#GetExecutable'),
\ 'command': '%e --mode lsp --from vim-ale',
-\ 'project_root_callback': 'ale_linters#hack#hhast#GetProjectRoot',
-\ 'initialization_options_callback': 'ale_linters#hack#hhast#GetInitializationOptions',
+\ 'project_root': function('ale_linters#hack#hhast#GetProjectRoot'),
+\ 'initialization_options': function('ale_linters#hack#hhast#GetInitializationOptions'),
\})
diff --git a/ale_linters/haml/hamllint.vim b/ale_linters/haml/hamllint.vim
index 6598f81a..9fcd999f 100644
--- a/ale_linters/haml/hamllint.vim
+++ b/ale_linters/haml/hamllint.vim
@@ -19,7 +19,7 @@ function! ale_linters#haml#hamllint#GetCommand(buffer) abort
" See https://github.com/brigade/haml-lint/blob/master/lib/haml_lint/linter/rubocop.rb#L89
" HamlLint::Linter::RuboCop#rubocop_flags
if !empty(l:rubocop_config_file_path)
- if ale#Has('win32')
+ if has('win32')
let l:prefix = 'set HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path) . ' &&'
else
let l:prefix = 'HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path)
@@ -51,7 +51,7 @@ endfunction
call ale#linter#Define('haml', {
\ 'name': 'hamllint',
-\ 'executable_callback': 'ale_linters#haml#hamllint#GetExecutable',
-\ 'command_callback': 'ale_linters#haml#hamllint#GetCommand',
+\ 'executable': function('ale_linters#haml#hamllint#GetExecutable'),
+\ 'command': function('ale_linters#haml#hamllint#GetCommand'),
\ 'callback': 'ale_linters#haml#hamllint#Handle'
\})
diff --git a/ale_linters/handlebars/embertemplatelint.vim b/ale_linters/handlebars/embertemplatelint.vim
index 4fc0f20d..74bd6a99 100644
--- a/ale_linters/handlebars/embertemplatelint.vim
+++ b/ale_linters/handlebars/embertemplatelint.vim
@@ -31,9 +31,9 @@ endfunction
call ale#linter#Define('handlebars', {
\ 'name': 'ember-template-lint',
-\ 'executable_callback': ale#node#FindExecutableFunc('handlebars_embertemplatelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'handlebars_embertemplatelint', [
\ 'node_modules/.bin/ember-template-lint',
-\ ]),
+\ ])},
\ 'command': '%e --json %t',
\ 'callback': 'ale_linters#handlebars#embertemplatelint#Handle',
\})
diff --git a/ale_linters/haskell/cabal_ghc.vim b/ale_linters/haskell/cabal_ghc.vim
index 003adf5d..f3f248f5 100644
--- a/ale_linters/haskell/cabal_ghc.vim
+++ b/ale_linters/haskell/cabal_ghc.vim
@@ -4,7 +4,8 @@
call ale#Set('haskell_cabal_ghc_options', '-fno-code -v0')
function! ale_linters#haskell#cabal_ghc#GetCommand(buffer) abort
- return 'cabal exec -- ghc '
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'cabal exec -- ghc '
\ . ale#Var(a:buffer, 'haskell_cabal_ghc_options')
\ . ' %t'
endfunction
@@ -14,6 +15,6 @@ call ale#linter#Define('haskell', {
\ 'aliases': ['cabal-ghc'],
\ 'output_stream': 'stderr',
\ 'executable': 'cabal',
-\ 'command_callback': 'ale_linters#haskell#cabal_ghc#GetCommand',
+\ 'command': function('ale_linters#haskell#cabal_ghc#GetCommand'),
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/ghc.vim b/ale_linters/haskell/ghc.vim
index daf91c8f..9c3906b2 100644
--- a/ale_linters/haskell/ghc.vim
+++ b/ale_linters/haskell/ghc.vim
@@ -13,6 +13,6 @@ call ale#linter#Define('haskell', {
\ 'name': 'ghc',
\ 'output_stream': 'stderr',
\ 'executable': 'ghc',
-\ 'command_callback': 'ale_linters#haskell#ghc#GetCommand',
+\ 'command': function('ale_linters#haskell#ghc#GetCommand'),
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/ghc_mod.vim b/ale_linters/haskell/ghc_mod.vim
index 9762be7a..30e96b40 100644
--- a/ale_linters/haskell/ghc_mod.vim
+++ b/ale_linters/haskell/ghc_mod.vim
@@ -13,7 +13,7 @@ endfunction
call ale#linter#Define('haskell', {
\ 'name': 'ghc_mod',
\ 'aliases': ['ghc-mod'],
-\ 'executable_callback': ale#VarFunc('haskell_ghc_mod_executable'),
-\ 'command_callback': 'ale_linters#haskell#ghc_mod#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'haskell_ghc_mod_executable')},
+\ 'command': function('ale_linters#haskell#ghc_mod#GetCommand'),
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/hdevtools.vim b/ale_linters/haskell/hdevtools.vim
index cc5ce56f..3e55e4f0 100644
--- a/ale_linters/haskell/hdevtools.vim
+++ b/ale_linters/haskell/hdevtools.vim
@@ -14,7 +14,7 @@ endfunction
call ale#linter#Define('haskell', {
\ 'name': 'hdevtools',
-\ 'executable_callback': ale#VarFunc('haskell_hdevtools_executable'),
-\ 'command_callback': 'ale_linters#haskell#hdevtools#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'haskell_hdevtools_executable')},
+\ 'command': function('ale_linters#haskell#hdevtools#GetCommand'),
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/hie.vim b/ale_linters/haskell/hie.vim
index 3ff1180a..c4b5f1df 100644
--- a/ale_linters/haskell/hie.vim
+++ b/ale_linters/haskell/hie.vim
@@ -9,39 +9,33 @@ function! ale_linters#haskell#hie#GetProjectRoot(buffer) abort
" If it's empty, search for the cabal file
if empty(l:project_file)
- let l:cabal_file = fnamemodify(bufname(a:buffer), ':p:h')
- let l:paths = ''
-
- while empty(matchstr(l:cabal_file, '^\(\/\|\(\w:\\\)\)$'))
- let l:cabal_file = fnamemodify(l:cabal_file, ':h')
- let l:paths = l:paths . l:cabal_file . ','
- endwhile
-
+ " Search all of the paths except for the root filesystem path.
+ let l:paths = join(
+ \ ale#path#Upwards(expand('#' . a:buffer . ':p:h'))[:-2],
+ \ ','
+ \)
let l:project_file = globpath(l:paths, '*.cabal')
endif
- " Either extract the project directory or take the current working
- " directory
- if !empty(l:project_file)
- let l:project_file = fnamemodify(l:project_file, ':h')
- else
- let l:project_file = expand('#' . a:buffer . ':p:h')
+ " If we still can't find one, use the current file.
+ if empty(l:project_file)
+ let l:project_file = expand('#' . a:buffer . ':p')
endif
- return l:project_file
+ return fnamemodify(l:project_file, ':h')
endfunction
function! ale_linters#haskell#hie#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'haskell_hie_executable')
return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'hie')
-\ . ' --lsp'
+ \ . ' --lsp'
endfunction
call ale#linter#Define('haskell', {
\ 'name': 'hie',
\ 'lsp': 'stdio',
-\ 'command_callback': 'ale_linters#haskell#hie#GetCommand',
-\ 'executable_callback': ale#VarFunc('haskell_hie_executable'),
-\ 'project_root_callback': 'ale_linters#haskell#hie#GetProjectRoot',
+\ 'command': function('ale_linters#haskell#hie#GetCommand'),
+\ 'executable': {b -> ale#Var(b, 'haskell_hie_executable')},
+\ 'project_root': function('ale_linters#haskell#hie#GetProjectRoot'),
\})
diff --git a/ale_linters/haskell/hlint.vim b/ale_linters/haskell/hlint.vim
index 0cc7437f..1425251a 100644
--- a/ale_linters/haskell/hlint.vim
+++ b/ale_linters/haskell/hlint.vim
@@ -40,7 +40,7 @@ endfunction
call ale#linter#Define('haskell', {
\ 'name': 'hlint',
-\ 'executable_callback': ale#VarFunc('haskell_hlint_executable'),
-\ 'command_callback': 'ale_linters#haskell#hlint#GetCommand' ,
+\ 'executable': {b -> ale#Var(b, 'haskell_hlint_executable')},
+\ 'command': function('ale_linters#haskell#hlint#GetCommand') ,
\ 'callback': 'ale_linters#haskell#hlint#Handle',
\})
diff --git a/ale_linters/haskell/stack_build.vim b/ale_linters/haskell/stack_build.vim
index 95a54587..8f2d9fd9 100644
--- a/ale_linters/haskell/stack_build.vim
+++ b/ale_linters/haskell/stack_build.vim
@@ -16,8 +16,8 @@ call ale#linter#Define('haskell', {
\ 'name': 'stack_build',
\ 'aliases': ['stack-build'],
\ 'output_stream': 'stderr',
-\ 'executable_callback': 'ale#handlers#haskell#GetStackExecutable',
-\ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand',
+\ 'executable': function('ale#handlers#haskell#GetStackExecutable'),
+\ 'command': function('ale_linters#haskell#stack_build#GetCommand'),
\ 'lint_file': 1,
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/stack_ghc.vim b/ale_linters/haskell/stack_ghc.vim
index 8f42b96c..c345fe43 100644
--- a/ale_linters/haskell/stack_ghc.vim
+++ b/ale_linters/haskell/stack_ghc.vim
@@ -1,11 +1,21 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: ghc for Haskell files, using Stack
+call ale#Set('haskell_stack_ghc_options', '-fno-code -v0')
+
+function! ale_linters#haskell#stack_ghc#GetCommand(buffer) abort
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale#handlers#haskell#GetStackExecutable(a:buffer)
+ \ . ' ghc -- '
+ \ . ale#Var(a:buffer, 'haskell_stack_ghc_options')
+ \ . ' %t'
+endfunction
+
call ale#linter#Define('haskell', {
\ 'name': 'stack_ghc',
\ 'aliases': ['stack-ghc'],
\ 'output_stream': 'stderr',
-\ 'executable_callback': 'ale#handlers#haskell#GetStackExecutable',
-\ 'command': 'stack ghc -- -fno-code -v0 %t',
+\ 'executable': function('ale#handlers#haskell#GetStackExecutable'),
+\ 'command': function('ale_linters#haskell#stack_ghc#GetCommand'),
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/help/alex.vim b/ale_linters/help/alex.vim
index 21b23b4f..9be00a82 100644
--- a/ale_linters/help/alex.vim
+++ b/ale_linters/help/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for help files
-call ale#linter#Define('help', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('help', '--text')
diff --git a/ale_linters/html/alex.vim b/ale_linters/html/alex.vim
index 5a1f61e9..97756753 100644
--- a/ale_linters/html/alex.vim
+++ b/ale_linters/html/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for HTML files
-call ale#linter#Define('html', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('html', '--html')
diff --git a/ale_linters/html/fecs.vim b/ale_linters/html/fecs.vim
new file mode 100644
index 00000000..15e00e12
--- /dev/null
+++ b/ale_linters/html/fecs.vim
@@ -0,0 +1,9 @@
+" Author: harttle <yangjvn@126.com>
+" Description: fecs for HTMl files
+
+call ale#linter#Define('html', {
+\ 'name': 'fecs',
+\ 'executable': function('ale#handlers#fecs#GetExecutable'),
+\ 'command': function('ale#handlers#fecs#GetCommand'),
+\ 'callback': 'ale#handlers#fecs#Handle',
+\})
diff --git a/ale_linters/html/htmlhint.vim b/ale_linters/html/htmlhint.vim
index 234c1176..3e01f51a 100644
--- a/ale_linters/html/htmlhint.vim
+++ b/ale_linters/html/htmlhint.vim
@@ -24,9 +24,9 @@ endfunction
call ale#linter#Define('html', {
\ 'name': 'htmlhint',
-\ 'executable_callback': ale#node#FindExecutableFunc('html_htmlhint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'html_htmlhint', [
\ 'node_modules/.bin/htmlhint',
-\ ]),
-\ 'command_callback': 'ale_linters#html#htmlhint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#html#htmlhint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/html/stylelint.vim b/ale_linters/html/stylelint.vim
index 908c4b02..ae8955f3 100644
--- a/ale_linters/html/stylelint.vim
+++ b/ale_linters/html/stylelint.vim
@@ -21,7 +21,7 @@ endfunction
call ale#linter#Define('html', {
\ 'name': 'stylelint',
-\ 'executable_callback': 'ale_linters#html#stylelint#GetExecutable',
-\ 'command_callback': 'ale_linters#html#stylelint#GetCommand',
+\ 'executable': function('ale_linters#html#stylelint#GetExecutable'),
+\ 'command': function('ale_linters#html#stylelint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/html/tidy.vim b/ale_linters/html/tidy.vim
index 4ec29091..1e476d40 100644
--- a/ale_linters/html/tidy.vim
+++ b/ale_linters/html/tidy.vim
@@ -63,8 +63,8 @@ endfunction
call ale#linter#Define('html', {
\ 'name': 'tidy',
-\ 'executable_callback': ale#VarFunc('html_tidy_executable'),
+\ 'executable': {b -> ale#Var(b, 'html_tidy_executable')},
\ 'output_stream': 'stderr',
-\ 'command_callback': 'ale_linters#html#tidy#GetCommand',
+\ 'command': function('ale_linters#html#tidy#GetCommand'),
\ 'callback': 'ale_linters#html#tidy#Handle',
\ })
diff --git a/ale_linters/idris/idris.vim b/ale_linters/idris/idris.vim
index feac0f10..879e92f2 100644
--- a/ale_linters/idris/idris.vim
+++ b/ale_linters/idris/idris.vim
@@ -49,11 +49,11 @@ function! ale_linters#idris#idris#Handle(buffer, lines) abort
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]
+ 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]
+ 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'
@@ -75,7 +75,7 @@ endfunction
call ale#linter#Define('idris', {
\ 'name': 'idris',
-\ 'executable_callback': ale#VarFunc('idris_idris_executable'),
-\ 'command_callback': 'ale_linters#idris#idris#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'idris_idris_executable')},
+\ 'command': function('ale_linters#idris#idris#GetCommand'),
\ 'callback': 'ale_linters#idris#idris#Handle',
\})
diff --git a/ale_linters/ispc/ispc.vim b/ale_linters/ispc/ispc.vim
index de7ceafa..eb365117 100644
--- a/ale_linters/ispc/ispc.vim
+++ b/ale_linters/ispc/ispc.vim
@@ -38,8 +38,8 @@ endfunction
call ale#linter#Define('ispc', {
\ 'name': 'ispc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('ispc_ispc_executable'),
-\ 'command_callback': 'ale_linters#ispc#ispc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'ispc_ispc_executable')},
+\ 'command': function('ale_linters#ispc#ispc#GetCommand'),
\ 'callback': 'ale_linters#ispc#ispc#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/java/checkstyle.vim b/ale_linters/java/checkstyle.vim
index c07b65d0..3159cd55 100644
--- a/ale_linters/java/checkstyle.vim
+++ b/ale_linters/java/checkstyle.vim
@@ -44,7 +44,7 @@ endif
call ale#linter#Define('java', {
\ 'name': 'checkstyle',
\ 'executable': 'checkstyle',
-\ 'command_callback': 'ale_linters#java#checkstyle#GetCommand',
+\ 'command': function('ale_linters#java#checkstyle#GetCommand'),
\ 'callback': 'ale_linters#java#checkstyle#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/java/eclipselsp.vim b/ale_linters/java/eclipselsp.vim
new file mode 100644
index 00000000..d0ea9d6c
--- /dev/null
+++ b/ale_linters/java/eclipselsp.vim
@@ -0,0 +1,137 @@
+" Author: Horacio Sanson <https://github.com/hsanson>
+" Description: Support for the Eclipse language server https://github.com/eclipse/eclipse.jdt.ls
+
+let s:version_cache = {}
+
+call ale#Set('java_eclipselsp_path', ale#path#Simplify($HOME . '/eclipse.jdt.ls'))
+call ale#Set('java_eclipselsp_executable', 'java')
+
+function! ale_linters#java#eclipselsp#Executable(buffer) abort
+ return ale#Var(a:buffer, 'java_eclipselsp_executable')
+endfunction
+
+function! ale_linters#java#eclipselsp#TargetPath(buffer) abort
+ return ale#Var(a:buffer, 'java_eclipselsp_path')
+endfunction
+
+function! ale_linters#java#eclipselsp#JarPath(buffer) abort
+ let l:path = ale_linters#java#eclipselsp#TargetPath(a:buffer)
+
+ " Search jar file within repository path when manually built using mvn
+ let l:repo_path = l:path . '/org.eclipse.jdt.ls.product/target/repository'
+ let l:files = globpath(l:repo_path, '**/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
+
+ if len(l:files) == 1
+ return l:files[0]
+ endif
+
+ " Search jar file within VSCode extensions folder.
+ let l:files = globpath(l:path, '**/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
+
+ if len(l:files) == 1
+ return l:files[0]
+ endif
+
+ return ''
+endfunction
+
+function! ale_linters#java#eclipselsp#ConfigurationPath(buffer) abort
+ let l:path = fnamemodify(ale_linters#java#eclipselsp#JarPath(a:buffer), ':p:h:h')
+
+ if has('win32')
+ let l:path = l:path . '/config_win'
+ elseif has('macunix')
+ let l:path = l:path . '/config_mac'
+ else
+ let l:path = l:path . '/config_linux'
+ endif
+
+ return ale#path#Simplify(l:path)
+endfunction
+
+function! ale_linters#java#eclipselsp#VersionCheck(version_lines) abort
+ return s:GetVersion('', a:version_lines)
+endfunction
+
+function! s:GetVersion(executable, version_lines) abort
+ let l:version = []
+
+ for l:line in a:version_lines
+ let l:match = matchlist(l:line, '\(\d\+\)\.\(\d\+\)\.\(\d\+\)')
+
+ if !empty(l:match)
+ let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0]
+ let s:version_cache[a:executable] = l:version
+ break
+ endif
+ endfor
+
+ return l:version
+endfunction
+
+function! ale_linters#java#eclipselsp#CommandWithVersion(buffer, version_lines, meta) abort
+ let l:executable = ale_linters#java#eclipselsp#Executable(a:buffer)
+ let l:version = s:GetVersion(l:executable, a:version_lines)
+
+ return ale_linters#java#eclipselsp#Command(a:buffer, l:version)
+endfunction
+
+function! ale_linters#java#eclipselsp#Command(buffer, version) abort
+ let l:path = ale#Var(a:buffer, 'java_eclipselsp_path')
+
+ let l:executable = ale_linters#java#eclipselsp#Executable(a:buffer)
+
+ let l:cmd = [ ale#Escape(l:executable),
+ \ '-Declipse.application=org.eclipse.jdt.ls.core.id1',
+ \ '-Dosgi.bundles.defaultStartLevel=4',
+ \ '-Declipse.product=org.eclipse.jdt.ls.core.product',
+ \ '-Dlog.level=ALL',
+ \ '-noverify',
+ \ '-Xmx1G',
+ \ '-jar',
+ \ ale_linters#java#eclipselsp#JarPath(a:buffer),
+ \ '-configuration',
+ \ ale_linters#java#eclipselsp#ConfigurationPath(a:buffer),
+ \ '-data',
+ \ ale#java#FindProjectRoot(a:buffer)
+ \ ]
+
+ if ale#semver#GTE(a:version, [1, 9])
+ call add(l:cmd, '--add-modules=ALL-SYSTEM')
+ call add(l:cmd, '--add-opens java.base/java.util=ALL-UNNAMED')
+ call add(l:cmd, '--add-opens java.base/java.lang=ALL-UNNAMED')
+ endif
+
+ return join(l:cmd, ' ')
+endfunction
+
+function! ale_linters#java#eclipselsp#RunWithVersionCheck(buffer) abort
+ let l:executable = ale_linters#java#eclipselsp#Executable(a:buffer)
+
+ if empty(l:executable)
+ return ''
+ endif
+
+ let l:cache = s:version_cache
+
+ if has_key(s:version_cache, l:executable)
+ return ale_linters#java#eclipselsp#Command(a:buffer, s:version_cache[l:executable])
+ endif
+
+ let l:command = ale#Escape(l:executable) . ' -version'
+
+ return ale#command#Run(
+ \ a:buffer,
+ \ l:command,
+ \ function('ale_linters#java#eclipselsp#CommandWithVersion')
+ \)
+endfunction
+
+call ale#linter#Define('java', {
+\ 'name': 'eclipselsp',
+\ 'lsp': 'stdio',
+\ 'executable': function('ale_linters#java#eclipselsp#Executable'),
+\ 'command': function('ale_linters#java#eclipselsp#RunWithVersionCheck'),
+\ 'language': 'java',
+\ 'project_root': function('ale#java#FindProjectRoot'),
+\})
diff --git a/ale_linters/java/javac.vim b/ale_linters/java/javac.vim
index 659f7d04..3883783b 100644
--- a/ale_linters/java/javac.vim
+++ b/ale_linters/java/javac.vim
@@ -7,21 +7,29 @@ call ale#Set('java_javac_executable', 'javac')
call ale#Set('java_javac_options', '')
call ale#Set('java_javac_classpath', '')
-function! ale_linters#java#javac#GetImportPaths(buffer) abort
+function! ale_linters#java#javac#RunWithImportPaths(buffer) abort
+ let l:command = ''
let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
if !empty(l:pom_path) && executable('mvn')
- return ale#path#CdString(fnamemodify(l:pom_path, ':h'))
+ let l:command = ale#path#CdString(fnamemodify(l:pom_path, ':h'))
\ . 'mvn dependency:build-classpath'
endif
- let l:classpath_command = ale#gradle#BuildClasspathCommand(a:buffer)
+ " Try to use Gradle if Maven isn't available.
+ if empty(l:command)
+ let l:command = ale#gradle#BuildClasspathCommand(a:buffer)
+ endif
- if !empty(l:classpath_command)
- return l:classpath_command
+ if empty(l:command)
+ return ale_linters#java#javac#GetCommand(a:buffer, [], {})
endif
- return ''
+ return ale#command#Run(
+ \ a:buffer,
+ \ l:command,
+ \ function('ale_linters#java#javac#GetCommand')
+ \)
endfunction
function! s:BuildClassPathOption(buffer, import_paths) abort
@@ -37,7 +45,7 @@ function! s:BuildClassPathOption(buffer, import_paths) abort
\ : ''
endfunction
-function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort
+function! ale_linters#java#javac#GetCommand(buffer, import_paths, meta) abort
let l:cp_option = s:BuildClassPathOption(a:buffer, a:import_paths)
let l:sp_option = ''
@@ -55,15 +63,14 @@ function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort
if isdirectory(l:jaxb_dir)
call add(l:sp_dirs, l:jaxb_dir)
endif
+ endif
- " Automatically include the test directory, but only for test code.
- if expand('#' . a:buffer . ':p') =~? '\vsrc[/\\]test[/\\]java'
- let l:test_dir = fnamemodify(l:src_dir, ':h:h:h')
- \ . (has('win32') ? '\test\java\' : '/test/java/')
+ " Automatically include the test directory, but only for test code.
+ if expand('#' . a:buffer . ':p') =~? '\vsrc[/\\]test[/\\]java'
+ let l:test_dir = ale#path#FindNearestDirectory(a:buffer, 'src/test/java')
- if isdirectory(l:test_dir)
- call add(l:sp_dirs, l:test_dir)
- endif
+ if isdirectory(l:test_dir)
+ call add(l:sp_dirs, l:test_dir)
endif
endif
@@ -73,7 +80,7 @@ function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort
endif
" Create .class files in a temporary directory, which we will delete later.
- let l:class_file_directory = ale#engine#CreateDirectory(a:buffer)
+ let l:class_file_directory = ale#command#CreateDirectory(a:buffer)
" Always run javac from the directory the file is in, so we can resolve
" relative paths correctly.
@@ -120,10 +127,8 @@ endfunction
call ale#linter#Define('java', {
\ 'name': 'javac',
-\ 'executable_callback': ale#VarFunc('java_javac_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale_linters#java#javac#GetImportPaths', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#java#javac#GetCommand', 'output_stream': 'stderr'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'java_javac_executable')},
+\ 'command': function('ale_linters#java#javac#RunWithImportPaths'),
+\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#java#javac#Handle',
\})
diff --git a/ale_linters/java/javalsp.vim b/ale_linters/java/javalsp.vim
index 1436a52c..a327363d 100644
--- a/ale_linters/java/javalsp.vim
+++ b/ale_linters/java/javalsp.vim
@@ -1,7 +1,6 @@
" Author: Horacio Sanson <https://github.com/hsanson>
" Description: Support for the Java language server https://github.com/georgewfraser/vscode-javac
-call ale#Set('java_javalsp_jar', 'javacs.jar')
call ale#Set('java_javalsp_executable', 'java')
function! ale_linters#java#javalsp#Executable(buffer) abort
@@ -9,17 +8,16 @@ function! ale_linters#java#javalsp#Executable(buffer) abort
endfunction
function! ale_linters#java#javalsp#Command(buffer) abort
- let l:jar = ale#Var(a:buffer, 'java_javalsp_jar')
let l:executable = ale_linters#java#javalsp#Executable(a:buffer)
- return ale#Escape(l:executable) . ' -cp ' . l:jar . ' -Xverify:none org.javacs.Main'
+ return ale#Escape(l:executable) . ' -Xverify:none -m javacs/org.javacs.Main'
endfunction
call ale#linter#Define('java', {
\ 'name': 'javalsp',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#java#javalsp#Executable',
-\ 'command_callback': 'ale_linters#java#javalsp#Command',
+\ 'executable': function('ale_linters#java#javalsp#Executable'),
+\ 'command': function('ale_linters#java#javalsp#Command'),
\ 'language': 'java',
-\ 'project_root_callback': 'ale#java#FindProjectRoot',
+\ 'project_root': function('ale#java#FindProjectRoot'),
\})
diff --git a/ale_linters/java/pmd.vim b/ale_linters/java/pmd.vim
index b530ad09..a1f4c93c 100644
--- a/ale_linters/java/pmd.vim
+++ b/ale_linters/java/pmd.vim
@@ -31,6 +31,6 @@ endif
call ale#linter#Define('java', {
\ 'name': 'pmd',
\ 'executable': 'pmd',
-\ 'command_callback': 'ale_linters#java#pmd#GetCommand',
+\ 'command': function('ale_linters#java#pmd#GetCommand'),
\ 'callback': 'ale_linters#java#pmd#Handle',
\})
diff --git a/ale_linters/javascript/eslint.vim b/ale_linters/javascript/eslint.vim
index 23e16949..8aeac2d8 100644
--- a/ale_linters/javascript/eslint.vim
+++ b/ale_linters/javascript/eslint.vim
@@ -4,7 +4,7 @@
call ale#linter#Define('javascript', {
\ 'name': 'eslint',
\ 'output_stream': 'both',
-\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
-\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'executable': function('ale#handlers#eslint#GetExecutable'),
+\ 'command': function('ale#handlers#eslint#GetCommand'),
\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/javascript/fecs.vim b/ale_linters/javascript/fecs.vim
new file mode 100644
index 00000000..e47c0a0b
--- /dev/null
+++ b/ale_linters/javascript/fecs.vim
@@ -0,0 +1,10 @@
+" Author: harttle <yangjvn@126.com>
+" Description: fecs for JavaScript files
+
+call ale#linter#Define('javascript', {
+\ 'name': 'fecs',
+\ 'executable': function('ale#handlers#fecs#GetExecutable'),
+\ 'command': function('ale#handlers#fecs#GetCommand'),
+\ 'read_buffer': 0,
+\ 'callback': 'ale#handlers#fecs#Handle',
+\})
diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim
index cdb289c7..3135e2e9 100755
--- a/ale_linters/javascript/flow.vim
+++ b/ale_linters/javascript/flow.vim
@@ -27,34 +27,16 @@ function! ale_linters#javascript#flow#GetExecutable(buffer) abort
\])
endfunction
-function! ale_linters#javascript#flow#VersionCheck(buffer) abort
- let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer)
-
- if empty(l:executable)
- return ''
- endif
-
- return ale#Escape(l:executable) . ' --version'
-endfunction
-
-function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort
- let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer)
-
- if empty(l:executable)
- return ''
- endif
-
- let l:version = ale#semver#GetVersion(l:executable, a:version_lines)
-
+function! ale_linters#javascript#flow#GetCommand(buffer, version) abort
" If we can parse the version number, then only use --respect-pragma
" if the version is >= 0.36.0, which added the argument.
let l:use_respect_pragma = ale#Var(a:buffer, 'javascript_flow_use_respect_pragma')
- \ && (empty(l:version) || ale#semver#GTE(l:version, [0, 36]))
+ \ && (empty(a:version) || ale#semver#GTE(a:version, [0, 36]))
- return ale#Escape(l:executable)
- \ . ' check-contents'
+ return '%e check-contents'
\ . (l:use_respect_pragma ? ' --respect-pragma': '')
- \ . ' --json --from ale %s'
+ \ . ' --json --from ale %s < %t'
+ \ . (!has('win32') ? '; echo' : '')
endfunction
" Filter lines of flow output until we find the first line where the JSON
@@ -86,7 +68,6 @@ function! s:ExtraErrorMsg(current, new) abort
return l:newMsg
endfunction
-
function! s:GetDetails(error) abort
let l:detail = ''
@@ -155,7 +136,8 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort
\}
if has_key(l:error, 'extra')
- let l:errorToAdd.detail = s:GetDetails(l:error)
+ let l:errorToAdd.detail = l:errorToAdd.text
+ \ . "\n" . s:GetDetails(l:error)
endif
call add(l:output, l:errorToAdd)
@@ -166,11 +148,13 @@ endfunction
call ale#linter#Define('javascript', {
\ 'name': 'flow',
-\ 'executable_callback': 'ale_linters#javascript#flow#GetExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#javascript#flow#VersionCheck'},
-\ {'callback': 'ale_linters#javascript#flow#GetCommand'},
-\ ],
+\ 'executable': function('ale_linters#javascript#flow#GetExecutable'),
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale_linters#javascript#flow#GetExecutable(buffer),
+\ '%e --version',
+\ function('ale_linters#javascript#flow#GetCommand'),
+\ )},
\ 'callback': 'ale_linters#javascript#flow#Handle',
-\ 'add_newline': !has('win32'),
+\ 'read_buffer': 0,
\})
diff --git a/ale_linters/javascript/flow_ls.vim b/ale_linters/javascript/flow_ls.vim
index 75377183..accaaa73 100644
--- a/ale_linters/javascript/flow_ls.vim
+++ b/ale_linters/javascript/flow_ls.vim
@@ -19,10 +19,10 @@ endfunction
call ale#linter#Define('javascript', {
\ 'name': 'flow-language-server',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#node#FindExecutableFunc('javascript_flow_ls', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'javascript_flow_ls', [
\ 'node_modules/.bin/flow',
-\ ]),
+\ ])},
\ 'command': '%e lsp --from ale-lsp',
-\ 'project_root_callback': 'ale_linters#javascript#flow_ls#FindProjectRoot',
+\ 'project_root': function('ale_linters#javascript#flow_ls#FindProjectRoot'),
\ 'language': 'javascript',
\})
diff --git a/ale_linters/javascript/jscs.vim b/ale_linters/javascript/jscs.vim
index a38766a6..8905b3a1 100644
--- a/ale_linters/javascript/jscs.vim
+++ b/ale_linters/javascript/jscs.vim
@@ -53,9 +53,9 @@ endfunction
call ale#linter#Define('javascript', {
\ 'name': 'jscs',
-\ 'executable_callback': ale#node#FindExecutableFunc('javascript_jscs', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'javascript_jscs', [
\ 'node_modules/.bin/jscs',
-\ ]),
-\ 'command_callback': 'ale_linters#javascript#jscs#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#javascript#jscs#GetCommand'),
\ 'callback': 'ale_linters#javascript#jscs#Handle',
\})
diff --git a/ale_linters/javascript/jshint.vim b/ale_linters/javascript/jshint.vim
index cb7f66fc..d80a2250 100644
--- a/ale_linters/javascript/jshint.vim
+++ b/ale_linters/javascript/jshint.vim
@@ -25,9 +25,9 @@ endfunction
call ale#linter#Define('javascript', {
\ 'name': 'jshint',
-\ 'executable_callback': ale#node#FindExecutableFunc('javascript_jshint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'javascript_jshint', [
\ 'node_modules/.bin/jshint',
-\ ]),
-\ 'command_callback': 'ale_linters#javascript#jshint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#javascript#jshint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/javascript/standard.vim b/ale_linters/javascript/standard.vim
index f16b837a..4cd2c303 100644
--- a/ale_linters/javascript/standard.vim
+++ b/ale_linters/javascript/standard.vim
@@ -24,7 +24,7 @@ endfunction
" standard uses eslint and the output format is the same
call ale#linter#Define('javascript', {
\ 'name': 'standard',
-\ 'executable_callback': 'ale_linters#javascript#standard#GetExecutable',
-\ 'command_callback': 'ale_linters#javascript#standard#GetCommand',
+\ 'executable': function('ale_linters#javascript#standard#GetExecutable'),
+\ 'command': function('ale_linters#javascript#standard#GetCommand'),
\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/javascript/tsserver.vim b/ale_linters/javascript/tsserver.vim
index 6cf08dd6..68c252c5 100644
--- a/ale_linters/javascript/tsserver.vim
+++ b/ale_linters/javascript/tsserver.vim
@@ -8,10 +8,10 @@ call ale#Set('javascript_tsserver_use_global', get(g:, 'ale_use_global_executabl
call ale#linter#Define('javascript', {
\ 'name': 'tsserver',
\ 'lsp': 'tsserver',
-\ 'executable_callback': ale#node#FindExecutableFunc('javascript_tsserver', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'javascript_tsserver', [
\ 'node_modules/.bin/tsserver',
-\ ]),
+\ ])},
\ 'command': '%e',
-\ 'project_root_callback': {-> ''},
+\ 'project_root': function('ale#handlers#tsserver#GetProjectRoot'),
\ 'language': '',
\})
diff --git a/ale_linters/javascript/xo.vim b/ale_linters/javascript/xo.vim
index bc8657ed..4ba39101 100644
--- a/ale_linters/javascript/xo.vim
+++ b/ale_linters/javascript/xo.vim
@@ -20,7 +20,7 @@ endfunction
" xo uses eslint and the output format is the same
call ale#linter#Define('javascript', {
\ 'name': 'xo',
-\ 'executable_callback': 'ale_linters#javascript#xo#GetExecutable',
-\ 'command_callback': 'ale_linters#javascript#xo#GetCommand',
+\ 'executable': function('ale_linters#javascript#xo#GetExecutable'),
+\ 'command': function('ale_linters#javascript#xo#GetCommand'),
\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/json/jsonlint.vim b/ale_linters/json/jsonlint.vim
index f01553d6..f677b488 100644
--- a/ale_linters/json/jsonlint.vim
+++ b/ale_linters/json/jsonlint.vim
@@ -1,4 +1,21 @@
-" Author: KabbAmine <amine.kabb@gmail.com>
+" Author: KabbAmine <amine.kabb@gmail.com>, David Sierra <https://github.com/davidsierradz>
+
+call ale#Set('json_jsonlint_executable', 'jsonlint')
+call ale#Set('json_jsonlint_use_global', get(g:, 'ale_use_global_executables', 0))
+
+function! ale_linters#json#jsonlint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'json_jsonlint', [
+ \ 'node_modules/.bin/jsonlint',
+ \ 'node_modules/jsonlint/lib/cli.js',
+ \])
+endfunction
+
+function! ale_linters#json#jsonlint#GetCommand(buffer) abort
+ let l:executable = ale_linters#json#jsonlint#GetExecutable(a:buffer)
+
+ return ale#node#Executable(a:buffer, l:executable)
+ \ . ' --compact -'
+endfunction
function! ale_linters#json#jsonlint#Handle(buffer, lines) abort
" Matches patterns like the following:
@@ -19,8 +36,8 @@ endfunction
call ale#linter#Define('json', {
\ 'name': 'jsonlint',
-\ 'executable': 'jsonlint',
+\ 'executable': function('ale_linters#json#jsonlint#GetExecutable'),
\ 'output_stream': 'stderr',
-\ 'command': 'jsonlint --compact -',
+\ 'command': function('ale_linters#json#jsonlint#GetCommand'),
\ 'callback': 'ale_linters#json#jsonlint#Handle',
\})
diff --git a/ale_linters/julia/languageserver.vim b/ale_linters/julia/languageserver.vim
index cd2000de..564bec39 100644
--- a/ale_linters/julia/languageserver.vim
+++ b/ale_linters/julia/languageserver.vim
@@ -14,8 +14,8 @@ endfunction
call ale#linter#Define('julia', {
\ 'name': 'languageserver',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('julia_executable'),
-\ 'command_callback': 'ale_linters#julia#languageserver#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'julia_executable')},
+\ 'command': function('ale_linters#julia#languageserver#GetCommand'),
\ 'language': 'julia',
-\ 'project_root_callback': 'ale#julia#FindProjectRoot',
+\ 'project_root': function('ale#julia#FindProjectRoot'),
\})
diff --git a/ale_linters/kotlin/kotlinc.vim b/ale_linters/kotlin/kotlinc.vim
index 4a993986..3c6854fa 100644
--- a/ale_linters/kotlin/kotlinc.vim
+++ b/ale_linters/kotlin/kotlinc.vim
@@ -11,26 +11,35 @@ let g:ale_kotlin_kotlinc_module_filename = get(g:, 'ale_kotlin_kotlinc_module_fi
let s:classpath_sep = has('unix') ? ':' : ';'
-function! ale_linters#kotlin#kotlinc#GetImportPaths(buffer) abort
+function! ale_linters#kotlin#kotlinc#RunWithImportPaths(buffer) abort
+ let l:command = ''
+
" exec maven/gradle only if classpath is not set
if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') isnot# ''
- return ''
- else
- let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
+ return ale_linters#kotlin#kotlinc#GetCommand(a:buffer, [], {})
+ endif
- if !empty(l:pom_path) && executable('mvn')
- return ale#path#CdString(fnamemodify(l:pom_path, ':h'))
- \ . 'mvn dependency:build-classpath'
- endif
+ let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
- let l:classpath_command = ale#gradle#BuildClasspathCommand(a:buffer)
+ if !empty(l:pom_path) && executable('mvn')
+ let l:command = ale#path#CdString(fnamemodify(l:pom_path, ':h'))
+ \ . 'mvn dependency:build-classpath'
+ endif
- if !empty(l:classpath_command)
- return l:classpath_command
- endif
+ " Try to use Gradle if Maven isn't available.
+ if empty(l:command)
+ let l:command = ale#gradle#BuildClasspathCommand(a:buffer)
+ endif
- return ''
+ if empty(l:command)
+ return ale_linters#kotlin#kotlinc#GetCommand(a:buffer, [], {})
endif
+
+ return ale#command#Run(
+ \ a:buffer,
+ \ l:command,
+ \ function('ale_linters#kotlin#kotlinc#GetCommand')
+ \)
endfunction
function! s:BuildClassPathOption(buffer, import_paths) abort
@@ -46,7 +55,7 @@ function! s:BuildClassPathOption(buffer, import_paths) abort
\ : ''
endfunction
-function! ale_linters#kotlin#kotlinc#GetCommand(buffer, import_paths) abort
+function! ale_linters#kotlin#kotlinc#GetCommand(buffer, import_paths, meta) abort
let l:kotlinc_opts = ale#Var(a:buffer, 'kotlin_kotlinc_options')
let l:command = 'kotlinc '
@@ -165,11 +174,7 @@ endfunction
call ale#linter#Define('kotlin', {
\ 'name': 'kotlinc',
\ 'executable': 'kotlinc',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#kotlin#kotlinc#GetImportPaths', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#kotlin#kotlinc#GetCommand', 'output_stream': 'stderr'},
-\ ],
+\ 'command': function('ale_linters#kotlin#kotlinc#RunWithImportPaths'),
\ 'callback': 'ale_linters#kotlin#kotlinc#Handle',
\ 'lint_file': 1,
\})
-
diff --git a/ale_linters/kotlin/ktlint.vim b/ale_linters/kotlin/ktlint.vim
index f474e845..f0384005 100644
--- a/ale_linters/kotlin/ktlint.vim
+++ b/ale_linters/kotlin/ktlint.vim
@@ -1,54 +1,10 @@
" Author: Francis Agyapong <francisagyapong2@gmail.com>
" Description: Lint kotlin files using ktlint
-call ale#Set('kotlin_ktlint_executable', 'ktlint')
-call ale#Set('kotlin_ktlint_rulesets', [])
-call ale#Set('kotlin_ktlint_format', 0)
-
-
-function! ale_linters#kotlin#ktlint#GetCommand(buffer) abort
- let l:executable = ale#Var(a:buffer, 'kotlin_ktlint_executable')
- let l:file_path = expand('#' . a:buffer . ':p')
- let l:options = ''
-
- " Formmatted content written to original file, not sure how to handle
- " if ale#Var(a:buffer, 'kotlin_ktlint_format')
- " let l:options = l:options . ' --format'
- " endif
-
- for l:ruleset in ale#Var(a:buffer, 'kotlin_ktlint_rulesets')
- let l:options = l:options . ' --ruleset ' . l:ruleset
- endfor
-
- return l:executable . ' ' . l:options . ' ' . l:file_path
-endfunction
-
-function! ale_linters#kotlin#ktlint#Handle(buffer, lines) abort
- let l:message_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(.*\)'
- let l:output = []
-
- for l:match in ale#util#GetMatches(a:lines, l:message_pattern)
- let l:line = l:match[2] + 0
- let l:column = l:match[3] + 0
- let l:text = l:match[4]
-
- let l:type = l:text =~? 'not a valid kotlin file' ? 'E' : 'W'
-
- call add(l:output, {
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type
- \})
- endfor
-
- return l:output
-endfunction
-
call ale#linter#Define('kotlin', {
\ 'name': 'ktlint',
\ 'executable': 'ktlint',
-\ 'command_callback': 'ale_linters#kotlin#ktlint#GetCommand',
-\ 'callback': 'ale_linters#kotlin#ktlint#Handle',
+\ 'command': function('ale#handlers#ktlint#GetCommand'),
+\ 'callback': 'ale#handlers#ktlint#Handle',
\ 'lint_file': 1
\})
diff --git a/ale_linters/kotlin/languageserver.vim b/ale_linters/kotlin/languageserver.vim
index aea817ba..af78c0e0 100644
--- a/ale_linters/kotlin/languageserver.vim
+++ b/ale_linters/kotlin/languageserver.vim
@@ -22,8 +22,8 @@ endfunction
call ale#linter#Define('kotlin', {
\ 'name': 'languageserver',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('kotlin_languageserver_executable'),
+\ 'executable': {b -> ale#Var(b, 'kotlin_languageserver_executable')},
\ 'command': '%e',
\ 'language': 'kotlin',
-\ 'project_root_callback': 'ale_linters#kotlin#languageserver#GetProjectRoot',
+\ 'project_root': function('ale_linters#kotlin#languageserver#GetProjectRoot'),
\})
diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim
index 37600649..4ec8b00e 100755
--- a/ale_linters/less/lessc.vim
+++ b/ale_linters/less/lessc.vim
@@ -38,10 +38,10 @@ endfunction
call ale#linter#Define('less', {
\ 'name': 'lessc',
-\ 'executable_callback': ale#node#FindExecutableFunc('less_lessc', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'less_lessc', [
\ 'node_modules/.bin/lessc',
-\ ]),
-\ 'command_callback': 'ale_linters#less#lessc#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#less#lessc#GetCommand'),
\ 'callback': 'ale_linters#less#lessc#Handle',
\ 'output_stream': 'stderr',
\})
diff --git a/ale_linters/less/stylelint.vim b/ale_linters/less/stylelint.vim
index 479808c2..efb036c2 100644
--- a/ale_linters/less/stylelint.vim
+++ b/ale_linters/less/stylelint.vim
@@ -12,9 +12,9 @@ endfunction
call ale#linter#Define('less', {
\ 'name': 'stylelint',
-\ 'executable_callback': ale#node#FindExecutableFunc('less_stylelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'less_stylelint', [
\ 'node_modules/.bin/stylelint',
-\ ]),
-\ 'command_callback': 'ale_linters#less#stylelint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#less#stylelint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/llvm/llc.vim b/ale_linters/llvm/llc.vim
index 044f8c44..594be063 100644
--- a/ale_linters/llvm/llc.vim
+++ b/ale_linters/llvm/llc.vim
@@ -17,8 +17,8 @@ endfunction
call ale#linter#Define('llvm', {
\ 'name': 'llc',
-\ 'executable_callback': ale#VarFunc('llvm_llc_executable'),
+\ 'executable': {b -> ale#Var(b, 'llvm_llc_executable')},
\ 'output_stream': 'stderr',
-\ 'command_callback': {-> '%e -filetype=null -o=' . g:ale#util#nul_file},
+\ 'command': {-> '%e -filetype=null -o=' . g:ale#util#nul_file},
\ 'callback': 'ale_linters#llvm#llc#HandleErrors',
\})
diff --git a/ale_linters/lua/luac.vim b/ale_linters/lua/luac.vim
index bca2cd8d..41674a43 100644
--- a/ale_linters/lua/luac.vim
+++ b/ale_linters/lua/luac.vim
@@ -24,7 +24,7 @@ endfunction
call ale#linter#Define('lua', {
\ 'name': 'luac',
-\ 'executable_callback': ale#VarFunc('lua_luac_executable'),
+\ 'executable': {b -> ale#Var(b, 'lua_luac_executable')},
\ 'command': '%e -p -',
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#lua#luac#Handle',
diff --git a/ale_linters/lua/luacheck.vim b/ale_linters/lua/luacheck.vim
index 669103b8..34be2b5a 100644
--- a/ale_linters/lua/luacheck.vim
+++ b/ale_linters/lua/luacheck.vim
@@ -38,7 +38,7 @@ endfunction
call ale#linter#Define('lua', {
\ 'name': 'luacheck',
-\ 'executable_callback': ale#VarFunc('lua_luacheck_executable'),
-\ 'command_callback': 'ale_linters#lua#luacheck#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'lua_luacheck_executable')},
+\ 'command': function('ale_linters#lua#luacheck#GetCommand'),
\ 'callback': 'ale_linters#lua#luacheck#Handle',
\})
diff --git a/ale_linters/mail/alex.vim b/ale_linters/mail/alex.vim
index b0651ccd..0fceea7b 100644
--- a/ale_linters/mail/alex.vim
+++ b/ale_linters/mail/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
-" Description: alex for HTML files
+" Description: alex for mail files
-call ale#linter#Define('mail', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('mail', '--text')
diff --git a/ale_linters/mail/languagetool.vim b/ale_linters/mail/languagetool.vim
new file mode 100644
index 00000000..330fb8ec
--- /dev/null
+++ b/ale_linters/mail/languagetool.vim
@@ -0,0 +1,5 @@
+" Author: Vincent (wahrwolf [ät] wolfpit.net)
+" Description: languagetool for mails
+
+
+call ale#handlers#languagetool#DefineLinter('mail')
diff --git a/ale_linters/markdown/alex.vim b/ale_linters/markdown/alex.vim
index 29306141..63769b5e 100644
--- a/ale_linters/markdown/alex.vim
+++ b/ale_linters/markdown/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for markdown files
-call ale#linter#Define('markdown', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('markdown', '')
diff --git a/ale_linters/markdown/languagetool.vim b/ale_linters/markdown/languagetool.vim
new file mode 100644
index 00000000..d6bca22e
--- /dev/null
+++ b/ale_linters/markdown/languagetool.vim
@@ -0,0 +1,5 @@
+" Author: Vincent (wahrwolf [ät] wolfpit.net)
+" Description: languagetool for markdown files
+
+
+call ale#handlers#languagetool#DefineLinter('markdown')
diff --git a/ale_linters/markdown/markdownlint.vim b/ale_linters/markdown/markdownlint.vim
index 5c8af650..e935cbfe 100644
--- a/ale_linters/markdown/markdownlint.vim
+++ b/ale_linters/markdown/markdownlint.vim
@@ -2,10 +2,10 @@
" Description: Adds support for markdownlint
call ale#linter#Define('markdown', {
- \ 'name': 'markdownlint',
- \ 'executable': 'markdownlint',
- \ 'lint_file': 1,
- \ 'output_stream': 'both',
- \ 'command': 'markdownlint %s',
- \ 'callback': 'ale#handlers#markdownlint#Handle'
-\ })
+\ 'name': 'markdownlint',
+\ 'executable': 'markdownlint',
+\ 'lint_file': 1,
+\ 'output_stream': 'both',
+\ 'command': 'markdownlint %s',
+\ 'callback': 'ale#handlers#markdownlint#Handle'
+\})
diff --git a/ale_linters/markdown/mdl.vim b/ale_linters/markdown/mdl.vim
index 0953144b..305f5359 100644
--- a/ale_linters/markdown/mdl.vim
+++ b/ale_linters/markdown/mdl.vim
@@ -38,7 +38,7 @@ endfunction
call ale#linter#Define('markdown', {
\ 'name': 'mdl',
-\ 'executable_callback': 'ale_linters#markdown#mdl#GetExecutable',
-\ 'command_callback': 'ale_linters#markdown#mdl#GetCommand',
+\ 'executable': function('ale_linters#markdown#mdl#GetExecutable'),
+\ 'command': function('ale_linters#markdown#mdl#GetCommand'),
\ 'callback': 'ale_linters#markdown#mdl#Handle'
\})
diff --git a/ale_linters/markdown/remark_lint.vim b/ale_linters/markdown/remark_lint.vim
index 4f8d48fa..ed87d1ad 100644
--- a/ale_linters/markdown/remark_lint.vim
+++ b/ale_linters/markdown/remark_lint.vim
@@ -39,10 +39,10 @@ endfunction
call ale#linter#Define('markdown', {
\ 'name': 'remark_lint',
\ 'aliases': ['remark-lint'],
-\ 'executable_callback': ale#node#FindExecutableFunc('markdown_remark_lint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'markdown_remark_lint', [
\ 'node_modules/.bin/remark',
-\ ]),
-\ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#markdown#remark_lint#GetCommand'),
\ 'callback': 'ale_linters#markdown#remark_lint#Handle',
\ 'output_stream': 'stderr',
\})
diff --git a/ale_linters/markdown/textlint.vim b/ale_linters/markdown/textlint.vim
index 26458506..613c8411 100644
--- a/ale_linters/markdown/textlint.vim
+++ b/ale_linters/markdown/textlint.vim
@@ -3,7 +3,7 @@
call ale#linter#Define('markdown', {
\ 'name': 'textlint',
-\ 'executable_callback': 'ale#handlers#textlint#GetExecutable',
-\ 'command_callback': 'ale#handlers#textlint#GetCommand',
+\ 'executable': function('ale#handlers#textlint#GetExecutable'),
+\ 'command': function('ale#handlers#textlint#GetCommand'),
\ 'callback': 'ale#handlers#textlint#HandleTextlintOutput',
\})
diff --git a/ale_linters/matlab/mlint.vim b/ale_linters/matlab/mlint.vim
index 3435045e..f58f8b6d 100644
--- a/ale_linters/matlab/mlint.vim
+++ b/ale_linters/matlab/mlint.vim
@@ -37,7 +37,7 @@ endfunction
call ale#linter#Define('matlab', {
\ 'name': 'mlint',
-\ 'executable_callback': ale#VarFunc('matlab_mlint_executable'),
+\ 'executable': {b -> ale#Var(b, 'matlab_mlint_executable')},
\ 'command': '%e -id %t',
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#matlab#mlint#Handle',
diff --git a/ale_linters/mercury/mmc.vim b/ale_linters/mercury/mmc.vim
index 76d357f0..8a9ccc0e 100644
--- a/ale_linters/mercury/mmc.vim
+++ b/ale_linters/mercury/mmc.vim
@@ -33,8 +33,8 @@ endfunction
call ale#linter#Define('mercury', {
\ 'name': 'mmc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('mercury_mmc_executable'),
-\ 'command_callback': 'ale_linters#mercury#mmc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'mercury_mmc_executable')},
+\ 'command': function('ale_linters#mercury#mmc#GetCommand'),
\ 'callback': 'ale_linters#mercury#mmc#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/nasm/nasm.vim b/ale_linters/nasm/nasm.vim
index cb2119a6..347abc1b 100644
--- a/ale_linters/nasm/nasm.vim
+++ b/ale_linters/nasm/nasm.vim
@@ -36,7 +36,7 @@ call ale#linter#Define('nasm', {
\ 'name': 'nasm',
\ 'output_stream': 'stderr',
\ 'lint_file': 1,
-\ 'executable_callback': ale#VarFunc('nasm_nasm_executable'),
-\ 'command_callback': 'ale_linters#nasm#nasm#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'nasm_nasm_executable')},
+\ 'command': function('ale_linters#nasm#nasm#GetCommand'),
\ 'callback': 'ale_linters#nasm#nasm#Handle',
\})
diff --git a/ale_linters/nim/nimcheck.vim b/ale_linters/nim/nimcheck.vim
index bff45f7d..b5796dcd 100644
--- a/ale_linters/nim/nimcheck.vim
+++ b/ale_linters/nim/nimcheck.vim
@@ -59,7 +59,7 @@ call ale#linter#Define('nim', {
\ 'name': 'nimcheck',
\ 'executable': 'nim',
\ 'output_stream': 'both',
-\ 'command_callback': 'ale_linters#nim#nimcheck#GetCommand',
+\ 'command': function('ale_linters#nim#nimcheck#GetCommand'),
\ 'callback': 'ale_linters#nim#nimcheck#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/nroff/alex.vim b/ale_linters/nroff/alex.vim
index a10db2dd..3f06af26 100644
--- a/ale_linters/nroff/alex.vim
+++ b/ale_linters/nroff/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for nroff files
-call ale#linter#Define('nroff', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('nroff', '--text')
diff --git a/ale_linters/objc/ccls.vim b/ale_linters/objc/ccls.vim
index 0aa6a5e5..51ecf056 100644
--- a/ale_linters/objc/ccls.vim
+++ b/ale_linters/objc/ccls.vim
@@ -7,8 +7,8 @@ call ale#Set('objc_ccls_init_options', {})
call ale#linter#Define('objc', {
\ 'name': 'ccls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('objc_ccls_executable'),
+\ 'executable': {b -> ale#Var(b, 'objc_ccls_executable')},
\ 'command': '%e',
-\ 'project_root_callback': 'ale#handlers#ccls#GetProjectRoot',
-\ 'initialization_options_callback': ale#VarFunc('objc_ccls_init_options'),
+\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
+\ 'initialization_options': {b -> ale#Var(b, 'objc_ccls_init_options')},
\})
diff --git a/ale_linters/objc/clang.vim b/ale_linters/objc/clang.vim
index 4e80ac5c..7873dccd 100644
--- a/ale_linters/objc/clang.vim
+++ b/ale_linters/objc/clang.vim
@@ -18,6 +18,6 @@ call ale#linter#Define('objc', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': 'clang',
-\ 'command_callback': 'ale_linters#objc#clang#GetCommand',
+\ 'command': function('ale_linters#objc#clang#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/objc/clangd.vim b/ale_linters/objc/clangd.vim
index f090e6ce..ab52fec3 100644
--- a/ale_linters/objc/clangd.vim
+++ b/ale_linters/objc/clangd.vim
@@ -17,7 +17,7 @@ endfunction
call ale#linter#Define('objc', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('objc_clangd_executable'),
-\ 'command_callback': 'ale_linters#objc#clangd#GetCommand',
-\ 'project_root_callback': 'ale_linters#objc#clangd#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'objc_clangd_executable')},
+\ 'command': function('ale_linters#objc#clangd#GetCommand'),
+\ 'project_root': function('ale_linters#objc#clangd#GetProjectRoot'),
\})
diff --git a/ale_linters/objcpp/clang.vim b/ale_linters/objcpp/clang.vim
index d1474f17..4dbe55b3 100644
--- a/ale_linters/objcpp/clang.vim
+++ b/ale_linters/objcpp/clang.vim
@@ -18,6 +18,6 @@ call ale#linter#Define('objcpp', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': 'clang++',
-\ 'command_callback': 'ale_linters#objcpp#clang#GetCommand',
+\ 'command': function('ale_linters#objcpp#clang#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})
diff --git a/ale_linters/objcpp/clangd.vim b/ale_linters/objcpp/clangd.vim
index a09753be..3991d2ac 100644
--- a/ale_linters/objcpp/clangd.vim
+++ b/ale_linters/objcpp/clangd.vim
@@ -17,7 +17,7 @@ endfunction
call ale#linter#Define('objcpp', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('objcpp_clangd_executable'),
-\ 'command_callback': 'ale_linters#objcpp#clangd#GetCommand',
-\ 'project_root_callback': 'ale_linters#objcpp#clangd#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'objcpp_clangd_executable')},
+\ 'command': function('ale_linters#objcpp#clangd#GetCommand'),
+\ 'project_root': function('ale_linters#objcpp#clangd#GetProjectRoot'),
\})
diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim
index 077862fc..d8208c52 100644
--- a/ale_linters/ocaml/ols.vim
+++ b/ale_linters/ocaml/ols.vim
@@ -7,8 +7,8 @@ call ale#Set('ocaml_ols_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#linter#Define('ocaml', {
\ 'name': 'ols',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale#handlers#ols#GetExecutable',
-\ 'command_callback': 'ale#handlers#ols#GetCommand',
+\ 'executable': function('ale#handlers#ols#GetExecutable'),
+\ 'command': function('ale#handlers#ols#GetCommand'),
\ 'language_callback': 'ale#handlers#ols#GetLanguage',
-\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot',
+\ 'project_root': function('ale#handlers#ols#GetProjectRoot'),
\})
diff --git a/ale_linters/perl/perl.vim b/ale_linters/perl/perl.vim
index 1cb20fa7..0f06528a 100644
--- a/ale_linters/perl/perl.vim
+++ b/ale_linters/perl/perl.vim
@@ -18,7 +18,7 @@ function! ale_linters#perl#perl#Handle(buffer, lines) abort
return []
endif
- let l:pattern = '\(.\+\) at \(.\+\) line \(\d\+\)'
+ let l:pattern = '\(..\{-}\) at \(..\{-}\) line \(\d\+\)'
let l:output = []
let l:basename = expand('#' . a:buffer . ':t')
@@ -57,8 +57,8 @@ endfunction
call ale#linter#Define('perl', {
\ 'name': 'perl',
-\ 'executable_callback': ale#VarFunc('perl_perl_executable'),
+\ 'executable': {b -> ale#Var(b, 'perl_perl_executable')},
\ 'output_stream': 'both',
-\ 'command_callback': 'ale_linters#perl#perl#GetCommand',
+\ 'command': function('ale_linters#perl#perl#GetCommand'),
\ 'callback': 'ale_linters#perl#perl#Handle',
\})
diff --git a/ale_linters/perl/perlcritic.vim b/ale_linters/perl/perlcritic.vim
index 8619a404..f3154c09 100644
--- a/ale_linters/perl/perlcritic.vim
+++ b/ale_linters/perl/perlcritic.vim
@@ -55,7 +55,7 @@ endfunction
call ale#linter#Define('perl', {
\ 'name': 'perlcritic',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('perl_perlcritic_executable'),
-\ 'command_callback': 'ale_linters#perl#perlcritic#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'perl_perlcritic_executable')},
+\ 'command': function('ale_linters#perl#perlcritic#GetCommand'),
\ 'callback': 'ale_linters#perl#perlcritic#Handle',
\})
diff --git a/ale_linters/perl6/perl6.vim b/ale_linters/perl6/perl6.vim
index b33a0c51..68ef4769 100644
--- a/ale_linters/perl6/perl6.vim
+++ b/ale_linters/perl6/perl6.vim
@@ -36,8 +36,8 @@ function! ale_linters#perl6#perl6#ExtractError(dict, item, type, buffer) abort
endif
if has_key(a:dict[a:item], 'line') && !empty(a:dict[a:item]['line'])
- let l:line = a:dict[a:item]['line']
- let l:counter -= 1
+ let l:line = a:dict[a:item]['line']
+ let l:counter -= 1
endif
if has_key(a:dict[a:item], 'column') && !empty(a:dict[a:item]['column'])
@@ -61,7 +61,7 @@ function! ale_linters#perl6#perl6#ExtractError(dict, item, type, buffer) abort
" Currently, filenames and line numbers are not always given in the error output
if l:counter < 2
- \&& ( ale#path#IsBufferPath(a:buffer, l:file) || l:file is# '' )
+ \&& ( ale#path#IsBufferPath(a:buffer, l:file) || l:file is# '' )
return {
\ 'lnum': '' . l:line,
\ 'text': l:text,
@@ -83,7 +83,7 @@ function! ale_linters#perl6#perl6#Handle(buffer, lines) abort
endif
if a:lines[0] is# 'Syntax OK'
- return l:output
+ return l:output
endif
try
@@ -101,8 +101,8 @@ function! ale_linters#perl6#perl6#Handle(buffer, lines) abort
if type(l:json) is v:t_dict
for l:key in keys(l:json)
- if has_key(l:json[l:key], 'sorrows') &&
- \ has_key(l:json[l:key], 'worries')
+ if has_key(l:json[l:key], 'sorrows')
+ \&& has_key(l:json[l:key], 'worries')
if !empty(l:json[l:key]['sorrows'])
for l:dictionary in get(l:json[l:key], 'sorrows')
for l:item in keys(l:dictionary)
@@ -115,7 +115,7 @@ function! ale_linters#perl6#perl6#Handle(buffer, lines) abort
\ )
if l:result isnot# ''
- call add(l:output, l:result)
+ call add(l:output, l:result)
endif
endfor
endfor
@@ -133,7 +133,7 @@ function! ale_linters#perl6#perl6#Handle(buffer, lines) abort
\ )
if l:result isnot# ''
- call add(l:output, l:result)
+ call add(l:output, l:result)
endif
endfor
endfor
@@ -147,7 +147,7 @@ function! ale_linters#perl6#perl6#Handle(buffer, lines) abort
\ )
if l:result isnot# ''
- call add(l:output, l:result)
+ call add(l:output, l:result)
endif
endif
endfor
@@ -158,9 +158,9 @@ endfunction
call ale#linter#Define('perl6', {
\ 'name': 'perl6',
-\ 'executable_callback': 'ale_linters#perl6#perl6#GetExecutable',
+\ 'executable': function('ale_linters#perl6#perl6#GetExecutable'),
\ 'output_stream': 'both',
-\ 'command_callback': 'ale_linters#perl6#perl6#GetCommand',
+\ 'command': function('ale_linters#perl6#perl6#GetCommand'),
\ 'callback': 'ale_linters#perl6#perl6#Handle',
\})
diff --git a/ale_linters/php/langserver.vim b/ale_linters/php/langserver.vim
index ca91db4c..fdd1bf2b 100644
--- a/ale_linters/php/langserver.vim
+++ b/ale_linters/php/langserver.vim
@@ -5,6 +5,12 @@ call ale#Set('php_langserver_executable', 'php-language-server.php')
call ale#Set('php_langserver_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#php#langserver#GetProjectRoot(buffer) abort
+ let l:composer_path = ale#path#FindNearestFile(a:buffer, 'composer.json')
+
+ if (!empty(l:composer_path))
+ return fnamemodify(l:composer_path, ':h')
+ endif
+
let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : ''
@@ -13,9 +19,9 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'langserver',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#node#FindExecutableFunc('php_langserver', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'php_langserver', [
\ 'vendor/bin/php-language-server.php',
-\ ]),
+\ ])},
\ 'command': 'php %e',
-\ 'project_root_callback': 'ale_linters#php#langserver#GetProjectRoot',
+\ 'project_root': function('ale_linters#php#langserver#GetProjectRoot'),
\})
diff --git a/ale_linters/php/phan.vim b/ale_linters/php/phan.vim
index c6f16356..53cb1ea9 100644
--- a/ale_linters/php/phan.vim
+++ b/ale_linters/php/phan.vim
@@ -67,7 +67,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phan',
-\ 'executable_callback': 'ale_linters#php#phan#GetExecutable',
-\ 'command_callback': 'ale_linters#php#phan#GetCommand',
+\ 'executable': function('ale_linters#php#phan#GetExecutable'),
+\ 'command': function('ale_linters#php#phan#GetCommand'),
\ 'callback': 'ale_linters#php#phan#Handle',
\})
diff --git a/ale_linters/php/php.vim b/ale_linters/php/php.vim
index 5d87196c..51a109b9 100644
--- a/ale_linters/php/php.vim
+++ b/ale_linters/php/php.vim
@@ -32,7 +32,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'php',
-\ 'executable_callback': ale#VarFunc('php_php_executable'),
+\ 'executable': {b -> ale#Var(b, 'php_php_executable')},
\ 'output_stream': 'stdout',
\ 'command': '%e -l -d error_reporting=E_ALL -d display_errors=1 -d log_errors=0 --',
\ 'callback': 'ale_linters#php#php#Handle',
diff --git a/ale_linters/php/phpcs.vim b/ale_linters/php/phpcs.vim
index 408c2652..1c92bbb2 100644
--- a/ale_linters/php/phpcs.vim
+++ b/ale_linters/php/phpcs.vim
@@ -44,10 +44,10 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpcs',
-\ 'executable_callback': ale#node#FindExecutableFunc('php_phpcs', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'php_phpcs', [
\ 'vendor/bin/phpcs',
\ 'phpcs'
-\ ]),
-\ 'command_callback': 'ale_linters#php#phpcs#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#php#phpcs#GetCommand'),
\ 'callback': 'ale_linters#php#phpcs#Handle',
\})
diff --git a/ale_linters/php/phpmd.vim b/ale_linters/php/phpmd.vim
index 65f1cc3c..9b1d1e44 100644
--- a/ale_linters/php/phpmd.vim
+++ b/ale_linters/php/phpmd.vim
@@ -32,7 +32,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpmd',
-\ 'executable_callback': ale#VarFunc('php_phpmd_executable'),
-\ 'command_callback': 'ale_linters#php#phpmd#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'php_phpmd_executable')},
+\ 'command': function('ale_linters#php#phpmd#GetCommand'),
\ 'callback': 'ale_linters#php#phpmd#Handle',
\})
diff --git a/ale_linters/php/phpstan.vim b/ale_linters/php/phpstan.vim
index 1c831e1b..b621f855 100644
--- a/ale_linters/php/phpstan.vim
+++ b/ale_linters/php/phpstan.vim
@@ -3,44 +3,35 @@
" Set to change the ruleset
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')
+let g:ale_php_phpstan_level = get(g:, 'ale_php_phpstan_level', '')
let g:ale_php_phpstan_configuration = get(g:, 'ale_php_phpstan_configuration', '')
-function! ale_linters#php#phpstan#GetExecutable(buffer) abort
- return ale#Var(a:buffer, 'php_phpstan_executable')
-endfunction
+function! ale_linters#php#phpstan#GetCommand(buffer, version) abort
+ let l:configuration = ale#Var(a:buffer, 'php_phpstan_configuration')
+ let l:configuration_option = !empty(l:configuration)
+ \ ? ' -c ' . ale#Escape(l:configuration)
+ \ : ''
-function! ale_linters#php#phpstan#VersionCheck(buffer) abort
- let l:executable = ale_linters#php#phpstan#GetExecutable(a:buffer)
+ let l:level = ale#Var(a:buffer, 'php_phpstan_level')
+ let l:config_file_exists = ale#path#FindNearestFile(a:buffer, 'phpstan.neon')
- " If we have previously stored the version number in a cache, then
- " don't look it up again.
- if ale#semver#HasVersion(l:executable)
- " Returning an empty string skips this command.
- return ''
+ if empty(l:level) && empty(l:config_file_exists)
+ " if no configuration file is found, then use 4 as a default level
+ let l:level = '4'
endif
- let l:executable = ale#Escape(l:executable)
-
- return l:executable . ' --version'
-endfunction
-
-function! ale_linters#php#phpstan#GetCommand(buffer, version_output) abort
- let l:configuration = ale#Var(a:buffer, 'php_phpstan_configuration')
- let l:configuration_option = !empty(l:configuration)
- \ ? ' -c ' . l:configuration
+ let l:level_option = !empty(l:level)
+ \ ? ' -l ' . ale#Escape(l:level)
\ : ''
- let l:executable = ale_linters#php#phpstan#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
- let l:error_format = ale#semver#GTE(l:version, [0, 10, 3])
+ let l:error_format = ale#semver#GTE(a:version, [0, 10, 3])
\ ? ' --error-format raw'
\ : ' --errorFormat raw'
- return '%e analyze -l'
- \ . ale#Var(a:buffer, 'php_phpstan_level')
+ return '%e analyze --no-progress'
\ . l:error_format
\ . l:configuration_option
+ \ . l:level_option
\ . ' %s'
endfunction
@@ -56,7 +47,7 @@ function! ale_linters#php#phpstan#Handle(buffer, lines) abort
call add(l:output, {
\ 'lnum': l:match[2] + 0,
\ 'text': l:match[3],
- \ 'type': 'W',
+ \ 'type': 'E',
\})
endfor
@@ -65,10 +56,12 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpstan',
-\ 'executable_callback': 'ale_linters#php#phpstan#GetExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#php#phpstan#VersionCheck'},
-\ {'callback': 'ale_linters#php#phpstan#GetCommand'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'php_phpstan_executable')},
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale#Var(buffer, 'php_phpstan_executable'),
+\ '%e --version',
+\ function('ale_linters#php#phpstan#GetCommand'),
+\ )},
\ 'callback': 'ale_linters#php#phpstan#Handle',
\})
diff --git a/ale_linters/php/psalm.vim b/ale_linters/php/psalm.vim
index dce59178..3cdb026a 100644
--- a/ale_linters/php/psalm.vim
+++ b/ale_linters/php/psalm.vim
@@ -13,9 +13,9 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'psalm',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#node#FindExecutableFunc('psalm_langserver', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'psalm_langserver', [
\ 'vendor/bin/psalm-language-server',
-\ ]),
+\ ])},
\ 'command': '%e',
-\ 'project_root_callback': 'ale_linters#php#psalm#GetProjectRoot',
+\ 'project_root': function('ale_linters#php#psalm#GetProjectRoot'),
\})
diff --git a/ale_linters/po/alex.vim b/ale_linters/po/alex.vim
index 411d835b..05c67f15 100644
--- a/ale_linters/po/alex.vim
+++ b/ale_linters/po/alex.vim
@@ -1,11 +1,4 @@
" Author: Cian Butler https://github.com/butlerx
" Description: alex for PO files
-call ale#linter#Define('po', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('po', '--text')
diff --git a/ale_linters/pod/alex.vim b/ale_linters/pod/alex.vim
index 5c09befb..c89f8330 100644
--- a/ale_linters/pod/alex.vim
+++ b/ale_linters/pod/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for pod files
-call ale#linter#Define('pod', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('pod', '--text')
diff --git a/ale_linters/pony/ponyc.vim b/ale_linters/pony/ponyc.vim
index 19e7e828..6d4594f9 100644
--- a/ale_linters/pony/ponyc.vim
+++ b/ale_linters/pony/ponyc.vim
@@ -10,7 +10,7 @@ endfunction
call ale#linter#Define('pony', {
\ 'name': 'ponyc',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('pony_ponyc_executable'),
-\ 'command_callback': 'ale_linters#pony#ponyc#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'pony_ponyc_executable')},
+\ 'command': function('ale_linters#pony#ponyc#GetCommand'),
\ 'callback': 'ale#handlers#pony#HandlePonycFormat',
\})
diff --git a/ale_linters/powershell/powershell.vim b/ale_linters/powershell/powershell.vim
new file mode 100755
index 00000000..51ded71d
--- /dev/null
+++ b/ale_linters/powershell/powershell.vim
@@ -0,0 +1,91 @@
+" Author: Jesse Harris - https://github.com/zigford
+" Description: This file adds support for powershell scripts synatax errors
+
+call ale#Set('powershell_powershell_executable', 'pwsh')
+
+function! ale_linters#powershell#powershell#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'powershell_powershell_executable')
+endfunction
+
+" Some powershell magic to show syntax errors without executing the script
+" thanks to keith hill:
+" https://rkeithhill.wordpress.com/2007/10/30/powershell-quicktip-preparsing-scripts-to-check-for-syntax-errors/
+function! ale_linters#powershell#powershell#GetCommand(buffer) abort
+ let l:script = ['Param($Script);
+ \ trap {$_;continue} & {
+ \ $Contents = Get-Content -Path $Script;
+ \ $Contents = [string]::Join([Environment]::NewLine, $Contents);
+ \ [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Contents);
+ \ };']
+
+ return ale#powershell#RunPowerShell(
+ \ a:buffer, 'powershell_powershell', l:script)
+endfunction
+
+" Parse powershell error output using regex into a list of dicts
+function! ale_linters#powershell#powershell#Handle(buffer, lines) abort
+ let l:output = []
+ " Our 3 patterns we need to scrape the data for the dicts
+ let l:patterns = [
+ \ '\v^At line:(\d+) char:(\d+)',
+ \ '\v^(At|\+| )@!.*',
+ \ '\vFullyQualifiedErrorId : (\w+)',
+ \]
+
+ let l:matchcount = 0
+
+ for l:match in ale#util#GetMatches(a:lines, l:patterns)
+ " We want to work with 3 matches per syntax error
+ let l:matchcount = l:matchcount + 1
+
+ if l:matchcount == 1 || str2nr(l:match[1])
+ " First match consists of 2 capture groups, and
+ " can capture the line and col
+ if exists('l:item')
+ " We may be here because the last syntax
+ " didn't emit a code, and so only had 2
+ " matches
+ call add(l:output, l:item)
+ let l:matchcount = 1
+ endif
+
+ let l:item = {
+ \ 'lnum': str2nr(l:match[1]),
+ \ 'col': str2nr(l:match[2]),
+ \ 'type': 'E',
+ \}
+ elseif l:matchcount == 2
+ " Second match[0] grabs the full line in order
+ " to handles the text
+ let l:item['text'] = l:match[0]
+ else
+ " Final match handles the code, however
+ " powershell only emits 1 code for all errors
+ " so, we get the final code on the last error
+ " and loop over the previously added items to
+ " append the code we now know
+ call add(l:output, l:item)
+ unlet l:item
+
+ if len(l:match[1]) > 0
+ for l:i in l:output
+ let l:i['code'] = l:match[1]
+ endfor
+ endif
+
+ " Reset the matchcount so we can begin gathering
+ " matches for the next syntax error
+ let l:matchcount = 0
+ endif
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('powershell', {
+\ 'name': 'powershell',
+\ 'executable_callback': 'ale_linters#powershell#powershell#GetExecutable',
+\ 'command_callback': 'ale_linters#powershell#powershell#GetCommand',
+\ 'output_stream': 'stdout',
+\ 'callback': 'ale_linters#powershell#powershell#Handle',
+\})
diff --git a/ale_linters/powershell/psscriptanalyzer.vim b/ale_linters/powershell/psscriptanalyzer.vim
new file mode 100644
index 00000000..4794d9d8
--- /dev/null
+++ b/ale_linters/powershell/psscriptanalyzer.vim
@@ -0,0 +1,76 @@
+" Author: Jesse Harris - https://github.com/zigford
+" Description: This file adds support for lintng powershell scripts
+" using the PSScriptAnalyzer module.
+
+" let g:ale_powershell_psscriptanalyzer_exclusions =
+" \ 'PSAvoidUsingWriteHost,PSAvoidGlobalVars'
+call ale#Set('powershell_psscriptanalyzer_exclusions', '')
+call ale#Set('powershell_psscriptanalyzer_executable', 'pwsh')
+call ale#Set('powershell_psscriptanalyzer_module',
+\ 'psscriptanalyzer')
+
+function! ale_linters#powershell#psscriptanalyzer#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'powershell_psscriptanalyzer_executable')
+endfunction
+
+" Run Invoke-ScriptAnalyzer and output each linting message as 4 seperate lines
+" for each parsing
+function! ale_linters#powershell#psscriptanalyzer#GetCommand(buffer) abort
+ let l:exclude_option = ale#Var(
+ \ a:buffer, 'powershell_psscriptanalyzer_exclusions')
+ let l:module = ale#Var(
+ \ a:buffer, 'powershell_psscriptanalyzer_module')
+ let l:script = ['Param($Script);
+ \ Invoke-ScriptAnalyzer "$Script" '
+ \ . (!empty(l:exclude_option) ? '-Exclude ' . l:exclude_option : '')
+ \ . '| ForEach-Object {
+ \ $_.Line;
+ \ $_.Severity;
+ \ $_.Message;
+ \ $_.RuleName}']
+
+ return ale#powershell#RunPowerShell(
+ \ a:buffer,
+ \ 'powershell_psscriptanalyzer',
+ \ l:script)
+endfunction
+
+" add every 4 lines to an item(Dict) and every item to a list
+" return the list
+function! ale_linters#powershell#psscriptanalyzer#Handle(buffer, lines) abort
+ let l:output = []
+ let l:lcount = 0
+
+ for l:line in a:lines
+ if l:lcount is# 0
+ " the very first line
+ let l:item = {'lnum': str2nr(l:line)}
+ elseif l:lcount is# 1
+ if l:line is# 'Error'
+ let l:item['type'] = 'E'
+ elseif l:line is# 'Information'
+ let l:item['type'] = 'I'
+ else
+ let l:item['type'] = 'W'
+ endif
+ elseif l:lcount is# 2
+ let l:item['text'] = l:line
+ elseif l:lcount is# 3
+ let l:item['code'] = l:line
+ call add(l:output, l:item)
+ let l:lcount = -1
+ endif
+
+ let l:lcount = l:lcount + 1
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('powershell', {
+\ 'name': 'psscriptanalyzer',
+\ 'executable': function('ale_linters#powershell#psscriptanalyzer#GetExecutable'),
+\ 'command': function('ale_linters#powershell#psscriptanalyzer#GetCommand'),
+\ 'output_stream': 'stdout',
+\ 'callback': 'ale_linters#powershell#psscriptanalyzer#Handle',
+\})
diff --git a/ale_linters/prolog/swipl.vim b/ale_linters/prolog/swipl.vim
index 401e52b6..5c601c40 100644
--- a/ale_linters/prolog/swipl.vim
+++ b/ale_linters/prolog/swipl.vim
@@ -87,14 +87,14 @@ endfunction
" Skip sandbox error which is caused by directives
" because what we want is syntactic or semantic check.
function! s:Ignore(item) abort
- return a:item.type is# 'E' &&
- \ a:item.text =~# '\vNo permission to (call|directive|assert) sandboxed'
+ return a:item.type is# 'E'
+ \ && a:item.text =~# '\vNo permission to (call|directive|assert) sandboxed'
endfunction
call ale#linter#Define('prolog', {
\ 'name': 'swipl',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('prolog_swipl_executable'),
-\ 'command_callback': 'ale_linters#prolog#swipl#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'prolog_swipl_executable')},
+\ 'command': function('ale_linters#prolog#swipl#GetCommand'),
\ 'callback': 'ale_linters#prolog#swipl#Handle',
\})
diff --git a/ale_linters/proto/protoc_gen_lint.vim b/ale_linters/proto/protoc_gen_lint.vim
index c8b5c331..c3d10935 100644
--- a/ale_linters/proto/protoc_gen_lint.vim
+++ b/ale_linters/proto/protoc_gen_lint.vim
@@ -22,6 +22,6 @@ call ale#linter#Define('proto', {
\ 'lint_file': 1,
\ 'output_stream': 'stderr',
\ 'executable': 'protoc',
-\ 'command_callback': 'ale_linters#proto#protoc_gen_lint#GetCommand',
+\ 'command': function('ale_linters#proto#protoc_gen_lint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/pug/puglint.vim b/ale_linters/pug/puglint.vim
index 63208986..c4e0e233 100644
--- a/ale_linters/pug/puglint.vim
+++ b/ale_linters/pug/puglint.vim
@@ -33,10 +33,10 @@ endfunction
call ale#linter#Define('pug', {
\ 'name': 'puglint',
-\ 'executable_callback': ale#node#FindExecutableFunc('pug_puglint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'pug_puglint', [
\ 'node_modules/.bin/pug-lint',
-\ ]),
+\ ])},
\ 'output_stream': 'stderr',
-\ 'command_callback': 'ale_linters#pug#puglint#GetCommand',
+\ 'command': function('ale_linters#pug#puglint#GetCommand'),
\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/puppet/languageserver.vim b/ale_linters/puppet/languageserver.vim
index a3060e65..2078695f 100644
--- a/ale_linters/puppet/languageserver.vim
+++ b/ale_linters/puppet/languageserver.vim
@@ -30,8 +30,8 @@ endfunction
call ale#linter#Define('puppet', {
\ 'name': 'languageserver',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('puppet_languageserver_executable'),
+\ 'executable': {b -> ale#Var(b, 'puppet_languageserver_executable')},
\ 'command': '%e --stdio',
\ 'language': 'puppet',
-\ 'project_root_callback': 'ale_linters#puppet#languageserver#GetProjectRoot',
+\ 'project_root': function('ale_linters#puppet#languageserver#GetProjectRoot'),
\})
diff --git a/ale_linters/puppet/puppet.vim b/ale_linters/puppet/puppet.vim
index 0e37bdbd..ae648615 100644
--- a/ale_linters/puppet/puppet.vim
+++ b/ale_linters/puppet/puppet.vim
@@ -30,8 +30,8 @@ endfunction
call ale#linter#Define('puppet', {
\ 'name': 'puppet',
-\ 'executable_callback': ale#VarFunc('puppet_puppet_executable'),
+\ 'executable': {b -> ale#Var(b, 'puppet_puppet_executable')},
\ 'output_stream': 'stderr',
-\ 'command_callback': 'ale_linters#puppet#puppet#GetCommand',
+\ 'command': function('ale_linters#puppet#puppet#GetCommand'),
\ 'callback': 'ale_linters#puppet#puppet#Handle',
\})
diff --git a/ale_linters/puppet/puppetlint.vim b/ale_linters/puppet/puppetlint.vim
index c9c16f5e..985d6a4d 100644
--- a/ale_linters/puppet/puppetlint.vim
+++ b/ale_linters/puppet/puppetlint.vim
@@ -12,7 +12,7 @@ endfunction
call ale#linter#Define('puppet', {
\ 'name': 'puppetlint',
-\ 'executable_callback': ale#VarFunc('puppet_puppetlint_executable'),
-\ 'command_callback': 'ale_linters#puppet#puppetlint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'puppet_puppetlint_executable')},
+\ 'command': function('ale_linters#puppet#puppetlint#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/pyrex/cython.vim b/ale_linters/pyrex/cython.vim
index d260698c..84382ba1 100644
--- a/ale_linters/pyrex/cython.vim
+++ b/ale_linters/pyrex/cython.vim
@@ -32,7 +32,7 @@ endfunction
call ale#linter#Define('pyrex', {
\ 'name': 'cython',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('pyrex_cython_executable'),
-\ 'command_callback': 'ale_linters#pyrex#cython#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'pyrex_cython_executable')},
+\ 'command': function('ale_linters#pyrex#cython#GetCommand'),
\ 'callback': 'ale_linters#pyrex#cython#Handle',
\})
diff --git a/ale_linters/python/bandit.vim b/ale_linters/python/bandit.vim
new file mode 100644
index 00000000..554f5000
--- /dev/null
+++ b/ale_linters/python/bandit.vim
@@ -0,0 +1,68 @@
+" Author: Martino Pilia <martino.pilia@gmail.com>
+" Description: bandit linting for python files
+
+call ale#Set('python_bandit_executable', 'bandit')
+call ale#Set('python_bandit_options', '')
+call ale#Set('python_bandit_use_config', 1)
+call ale#Set('python_bandit_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('python_bandit_auto_pipenv', 0)
+
+function! ale_linters#python#bandit#GetExecutable(buffer) abort
+ if (
+ \ ale#Var(a:buffer, 'python_auto_pipenv')
+ \ || ale#Var(a:buffer, 'python_bandit_auto_pipenv')
+ \) && ale#python#PipenvPresent(a:buffer)
+ return 'pipenv'
+ endif
+
+ return ale#python#FindExecutable(a:buffer, 'python_bandit', ['bandit'])
+endfunction
+
+function! ale_linters#python#bandit#GetCommand(buffer) abort
+ let l:executable = ale_linters#python#bandit#GetExecutable(a:buffer)
+ let l:flags = ' --format custom'
+ \ . ' --msg-template "{line}:{test_id}:{severity}:{msg}" '
+
+ if ale#Var(a:buffer, 'python_bandit_use_config')
+ let l:config_path = ale#path#FindNearestFile(a:buffer, '.bandit')
+
+ if !empty(l:config_path)
+ let l:flags = ' --ini ' . ale#Escape(l:config_path) . l:flags
+ endif
+ endif
+
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run bandit'
+ \ : ''
+
+ return ale#Escape(l:executable) . l:exec_args
+ \ . l:flags
+ \ . ale#Pad(ale#Var(a:buffer, 'python_bandit_options'))
+ \ . ' -'
+endfunction
+
+function! ale_linters#python#bandit#Handle(buffer, lines) abort
+ " Custom format defined in GetCommand via --msg-template
+ let l:pattern = '\v^([0-9]+):(B[0-9]+):([A-Z]+):(.*)$'
+ let l:severity = {'LOW': 'I', 'MEDIUM': 'W', 'HIGH': 'E'}
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': str2nr(l:match[1]),
+ \ 'code': l:match[2],
+ \ 'type': l:severity[l:match[3]],
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'bandit',
+\ 'executable': function('ale_linters#python#bandit#GetExecutable'),
+\ 'command': function('ale_linters#python#bandit#GetCommand'),
+\ 'callback': 'ale_linters#python#bandit#Handle',
+\})
diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim
index 14b67d77..e2e7b743 100644
--- a/ale_linters/python/flake8.vim
+++ b/ale_linters/python/flake8.vim
@@ -24,28 +24,25 @@ function! ale_linters#python#flake8#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'python_flake8_executable')
endfunction
-function! ale_linters#python#flake8#VersionCheck(buffer) abort
+function! ale_linters#python#flake8#RunWithVersionCheck(buffer) abort
let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer)
- " If we have previously stored the version number in a cache, then
- " don't look it up again.
- if ale#semver#HasVersion(l:executable)
- " Returning an empty string skips this command.
- return ''
- endif
-
- let l:executable = ale#Escape(l:executable)
let l:module_string = s:UsingModule(a:buffer) ? ' -m flake8' : ''
-
- return l:executable . l:module_string . ' --version'
+ let l:command = ale#Escape(l:executable) . l:module_string . ' --version'
+
+ return ale#semver#RunWithVersionCheck(
+ \ a:buffer,
+ \ l:executable,
+ \ l:command,
+ \ function('ale_linters#python#flake8#GetCommand'),
+ \)
endfunction
-function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
+function! ale_linters#python#flake8#GetCommand(buffer, version) abort
let l:cd_string = ale#Var(a:buffer, 'python_flake8_change_directory')
\ ? ale#path#BufferCdString(a:buffer)
\ : ''
let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run flake8'
@@ -53,7 +50,7 @@ function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
" Only include the --stdin-display-name argument if we can parse the
" flake8 version, and it is recent enough to support it.
- let l:display_name_args = ale#semver#GTE(l:version, [3, 0, 0])
+ let l:display_name_args = ale#semver#GTE(a:version, [3, 0, 0])
\ ? ' --stdin-display-name %s'
\ : ''
@@ -74,23 +71,18 @@ let s:end_col_pattern_map = {
\}
function! ale_linters#python#flake8#Handle(buffer, lines) abort
- for l:line in a:lines[:10]
- if match(l:line, '^Traceback') >= 0
- return [{
- \ 'lnum': 1,
- \ 'text': 'An exception was thrown. See :ALEDetail',
- \ 'detail': join(a:lines, "\n"),
- \}]
- endif
- endfor
+ let l:output = ale#python#HandleTraceback(a:lines, 10)
+
+ if !empty(l:output)
+ return l:output
+ endif
" Matches patterns line the following:
"
" Matches patterns line the following:
"
" stdin:6:6: E111 indentation is not a multiple of four
- let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):?(\d+)?: ([[:alnum:]]+) (.*)$'
- let l:output = []
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):?(\d+)?: ([[:alnum:]]+):? (.*)$'
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:code = l:match[3]
@@ -148,10 +140,7 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'flake8',
-\ 'executable_callback': 'ale_linters#python#flake8#GetExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#python#flake8#VersionCheck'},
-\ {'callback': 'ale_linters#python#flake8#GetCommand', 'output_stream': 'both'},
-\ ],
+\ 'executable': function('ale_linters#python#flake8#GetExecutable'),
+\ 'command': function('ale_linters#python#flake8#RunWithVersionCheck'),
\ 'callback': 'ale_linters#python#flake8#Handle',
\})
diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim
index 0c90a3c7..c4c6507f 100644
--- a/ale_linters/python/mypy.vim
+++ b/ale_linters/python/mypy.vim
@@ -75,7 +75,7 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'mypy',
-\ 'executable_callback': 'ale_linters#python#mypy#GetExecutable',
-\ 'command_callback': 'ale_linters#python#mypy#GetCommand',
+\ 'executable': function('ale_linters#python#mypy#GetExecutable'),
+\ 'command': function('ale_linters#python#mypy#GetCommand'),
\ 'callback': 'ale_linters#python#mypy#Handle',
\})
diff --git a/ale_linters/python/prospector.vim b/ale_linters/python/prospector.vim
index b01cec87..ee47012f 100644
--- a/ale_linters/python/prospector.vim
+++ b/ale_linters/python/prospector.vim
@@ -93,8 +93,8 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'prospector',
-\ 'executable_callback': 'ale_linters#python#prospector#GetExecutable',
-\ 'command_callback': 'ale_linters#python#prospector#GetCommand',
+\ 'executable': function('ale_linters#python#prospector#GetExecutable'),
+\ 'command': function('ale_linters#python#prospector#GetCommand'),
\ 'callback': 'ale_linters#python#prospector#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim
index f0269585..fb521bc1 100644
--- a/ale_linters/python/pycodestyle.vim
+++ b/ale_linters/python/pycodestyle.vim
@@ -69,7 +69,7 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pycodestyle',
-\ 'executable_callback': 'ale_linters#python#pycodestyle#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pycodestyle#GetCommand',
+\ 'executable': function('ale_linters#python#pycodestyle#GetExecutable'),
+\ 'command': function('ale_linters#python#pycodestyle#GetCommand'),
\ 'callback': 'ale_linters#python#pycodestyle#Handle',
\})
diff --git a/ale_linters/python/pydocstyle.vim b/ale_linters/python/pydocstyle.vim
index ebf92bf1..3901db4d 100644
--- a/ale_linters/python/pydocstyle.vim
+++ b/ale_linters/python/pydocstyle.vim
@@ -33,8 +33,7 @@ function! ale_linters#python#pydocstyle#Handle(buffer, lines) abort
" Matches patterns like the following:
" mydir/myfile.py:33 in public function `myfunction`:
" DXXX: Error description
- let l:fname = ale#Escape(fnamemodify(bufname(a:buffer), ':p:t'))
- let l:line1_pattern = '\v^' . l:fname . ':\s*(\d+)\s+.*$'
+ let l:line1_pattern = '\v^.*:\s*(\d+)\s+.*$'
let l:line2_pattern = '\v^.*([a-zA-Z]\d+):\s*(.*)$'
let l:output = []
@@ -68,7 +67,7 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pydocstyle',
-\ 'executable_callback': 'ale_linters#python#pydocstyle#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pydocstyle#GetCommand',
+\ 'executable': function('ale_linters#python#pydocstyle#GetExecutable'),
+\ 'command': function('ale_linters#python#pydocstyle#GetCommand'),
\ 'callback': 'ale_linters#python#pydocstyle#Handle',
\})
diff --git a/ale_linters/python/pyflakes.vim b/ale_linters/python/pyflakes.vim
index 091408d5..b5127022 100644
--- a/ale_linters/python/pyflakes.vim
+++ b/ale_linters/python/pyflakes.vim
@@ -43,8 +43,8 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pyflakes',
-\ 'executable_callback': 'ale_linters#python#pyflakes#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pyflakes#GetCommand',
+\ 'executable': function('ale_linters#python#pyflakes#GetExecutable'),
+\ 'command': function('ale_linters#python#pyflakes#GetCommand'),
\ 'callback': 'ale_linters#python#pyflakes#Handle',
\ 'output_stream': 'both',
\})
diff --git a/ale_linters/python/pylama.vim b/ale_linters/python/pylama.vim
new file mode 100644
index 00000000..38dd2836
--- /dev/null
+++ b/ale_linters/python/pylama.vim
@@ -0,0 +1,92 @@
+" Author: Kevin Locke <kevin@kevinlocke.name>
+" Description: pylama for python files
+
+call ale#Set('python_pylama_executable', 'pylama')
+call ale#Set('python_pylama_options', '')
+call ale#Set('python_pylama_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('python_pylama_auto_pipenv', 0)
+call ale#Set('python_pylama_change_directory', 1)
+
+function! ale_linters#python#pylama#GetExecutable(buffer) abort
+ if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pylama_auto_pipenv'))
+ \ && ale#python#PipenvPresent(a:buffer)
+ return 'pipenv'
+ endif
+
+ return ale#python#FindExecutable(a:buffer, 'python_pylama', ['pylama'])
+endfunction
+
+function! ale_linters#python#pylama#GetCommand(buffer) abort
+ let l:cd_string = ''
+
+ if ale#Var(a:buffer, 'python_pylama_change_directory')
+ " Pylama loads its configuration from the current directory only, and
+ " applies file masks using paths relative to the current directory.
+ " Run from project root, if found, otherwise buffer dir.
+ let l:project_root = ale#python#FindProjectRoot(a:buffer)
+ let l:cd_string = l:project_root isnot# ''
+ \ ? ale#path#CdString(l:project_root)
+ \ : ale#path#BufferCdString(a:buffer)
+ endif
+
+ let l:executable = ale_linters#python#pylama#GetExecutable(a:buffer)
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run pylama'
+ \ : ''
+
+ " Note: Using %t to lint changes would be preferable, but many pylama
+ " checks use surrounding paths (e.g. C0103 module name, E0402 relative
+ " import beyond top, etc.). Neither is ideal.
+ return l:cd_string
+ \ . ale#Escape(l:executable) . l:exec_args
+ \ . ale#Pad(ale#Var(a:buffer, 'python_pylama_options'))
+ \ . ' %s'
+endfunction
+
+function! ale_linters#python#pylama#Handle(buffer, lines) abort
+ if empty(a:lines)
+ return []
+ endif
+
+ let l:output = ale#python#HandleTraceback(a:lines, 1)
+ let l:pattern = '\v^.{-}:([0-9]+):([0-9]+): +%(([A-Z][0-9]+):? +)?(.*)$'
+
+ " First letter of error code is a pylint-compatible message type
+ " http://pylint.pycqa.org/en/latest/user_guide/output.html#source-code-analysis-section
+ " D is for Documentation (pydocstyle)
+ let l:pylint_type_to_ale_type = {
+ \ 'I': 'I',
+ \ 'R': 'W',
+ \ 'C': 'W',
+ \ 'W': 'W',
+ \ 'E': 'E',
+ \ 'F': 'E',
+ \ 'D': 'W',
+ \}
+ let l:pylint_type_to_ale_sub_type = {
+ \ 'R': 'style',
+ \ 'C': 'style',
+ \ 'D': 'style',
+ \}
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': str2nr(l:match[1]),
+ \ 'col': str2nr(l:match[2]),
+ \ 'code': l:match[3],
+ \ 'type': get(l:pylint_type_to_ale_type, l:match[3][0], 'W'),
+ \ 'sub_type': get(l:pylint_type_to_ale_sub_type, l:match[3][0], ''),
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'pylama',
+\ 'executable': function('ale_linters#python#pylama#GetExecutable'),
+\ 'command': function('ale_linters#python#pylama#GetCommand'),
+\ 'callback': 'ale_linters#python#pylama#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim
index 01c3cb37..57e82691 100644
--- a/ale_linters/python/pylint.vim
+++ b/ale_linters/python/pylint.vim
@@ -17,9 +17,17 @@ function! ale_linters#python#pylint#GetExecutable(buffer) abort
endfunction
function! ale_linters#python#pylint#GetCommand(buffer) abort
- let l:cd_string = ale#Var(a:buffer, 'python_pylint_change_directory')
- \ ? ale#path#BufferCdString(a:buffer)
- \ : ''
+ let l:cd_string = ''
+
+ if ale#Var(a:buffer, 'python_pylint_change_directory')
+ " pylint only checks for pylintrc in the packages above its current
+ " directory before falling back to user and global pylintrc.
+ " Run from project root, if found, otherwise buffer dir.
+ let l:project_root = ale#python#FindProjectRoot(a:buffer)
+ let l:cd_string = l:project_root isnot# ''
+ \ ? ale#path#CdString(l:project_root)
+ \ : ale#path#BufferCdString(a:buffer)
+ endif
let l:executable = ale_linters#python#pylint#GetExecutable(a:buffer)
@@ -53,7 +61,7 @@ function! ale_linters#python#pylint#Handle(buffer, lines) abort
if l:code is# 'I0011'
" Skip 'Locally disabling' message
- continue
+ continue
endif
call add(l:output, {
@@ -70,8 +78,8 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pylint',
-\ 'executable_callback': 'ale_linters#python#pylint#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pylint#GetCommand',
+\ 'executable': function('ale_linters#python#pylint#GetExecutable'),
+\ 'command': function('ale_linters#python#pylint#GetCommand'),
\ 'callback': 'ale_linters#python#pylint#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/python/pyls.vim b/ale_linters/python/pyls.vim
index 83fe8066..c7f91430 100644
--- a/ale_linters/python/pyls.vim
+++ b/ale_linters/python/pyls.vim
@@ -4,6 +4,7 @@
call ale#Set('python_pyls_executable', 'pyls')
call ale#Set('python_pyls_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_pyls_auto_pipenv', 0)
+call ale#Set('python_pyls_config', {})
function! ale_linters#python#pyls#GetExecutable(buffer) abort
if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pyls_auto_pipenv'))
@@ -27,8 +28,9 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pyls',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#python#pyls#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pyls#GetCommand',
-\ 'project_root_callback': 'ale#python#FindProjectRoot',
+\ 'executable': function('ale_linters#python#pyls#GetExecutable'),
+\ 'command': function('ale_linters#python#pyls#GetCommand'),
+\ 'project_root': function('ale#python#FindProjectRoot'),
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
+\ 'lsp_config': {b -> ale#Var(b, 'python_pyls_config')},
\})
diff --git a/ale_linters/python/pyre.vim b/ale_linters/python/pyre.vim
index adc185f2..4edd80f7 100644
--- a/ale_linters/python/pyre.vim
+++ b/ale_linters/python/pyre.vim
@@ -27,8 +27,8 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pyre',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#python#pyre#GetExecutable',
-\ 'command_callback': 'ale_linters#python#pyre#GetCommand',
-\ 'project_root_callback': 'ale#python#FindProjectRoot',
+\ 'executable': function('ale_linters#python#pyre#GetExecutable'),
+\ 'command': function('ale_linters#python#pyre#GetCommand'),
+\ 'project_root': function('ale#python#FindProjectRoot'),
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\})
diff --git a/ale_linters/python/vulture.vim b/ale_linters/python/vulture.vim
index 80828013..d328d262 100644
--- a/ale_linters/python/vulture.vim
+++ b/ale_linters/python/vulture.vim
@@ -46,19 +46,14 @@ endfunction
function! ale_linters#python#vulture#Handle(buffer, lines) abort
- for l:line in a:lines[:10]
- if match(l:line, '^Traceback') >= 0
- return [{
- \ 'lnum': 1,
- \ 'text': 'An exception was thrown. See :ALEDetail',
- \ 'detail': join(a:lines, "\n"),
- \}]
- endif
- endfor
+ let l:output = ale#python#HandleTraceback(a:lines, 10)
+
+ if !empty(l:output)
+ return l:output
+ endif
" Matches patterns line the following:
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+): (.*)$'
- let l:output = []
let l:dir = s:GetDir(a:buffer)
for l:match in ale#util#GetMatches(a:lines, l:pattern)
@@ -78,8 +73,8 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'vulture',
-\ 'executable_callback': 'ale_linters#python#vulture#GetExecutable',
-\ 'command_callback': 'ale_linters#python#vulture#GetCommand',
+\ 'executable': function('ale_linters#python#vulture#GetExecutable'),
+\ 'command': function('ale_linters#python#vulture#GetCommand'),
\ 'callback': 'ale_linters#python#vulture#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/qml/qmlfmt.vim b/ale_linters/qml/qmlfmt.vim
index 12f3e97b..11cc9413 100644
--- a/ale_linters/qml/qmlfmt.vim
+++ b/ale_linters/qml/qmlfmt.vim
@@ -19,7 +19,7 @@ endfunction
call ale#linter#Define('qml', {
\ 'name': 'qmlfmt',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('qml_qmlfmt_executable'),
+\ 'executable': {b -> ale#Var(b, 'qml_qmlfmt_executable')},
\ 'command': '%e -e',
\ 'callback': 'ale_linters#qml#qmlfmt#Handle',
\})
diff --git a/ale_linters/r/lintr.vim b/ale_linters/r/lintr.vim
index 8f74c9b8..3164c06f 100644
--- a/ale_linters/r/lintr.vim
+++ b/ale_linters/r/lintr.vim
@@ -29,7 +29,7 @@ endfunction
call ale#linter#Define('r', {
\ 'name': 'lintr',
\ 'executable': 'Rscript',
-\ 'command_callback': 'ale_linters#r#lintr#GetCommand',
+\ 'command': function('ale_linters#r#lintr#GetCommand'),
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ 'output_stream': 'both',
\})
diff --git a/ale_linters/racket/raco.vim b/ale_linters/racket/raco.vim
new file mode 100644
index 00000000..e5ee4fb4
--- /dev/null
+++ b/ale_linters/racket/raco.vim
@@ -0,0 +1,33 @@
+" Author: aqui18 <https://github.com/aqui18>
+" Description: This file adds support for checking Racket code with raco.
+" This is the same form of syntax-checking used by DrRacket as well. The
+" downside is that it will only catch the first error, but none of the
+" subsequent ones. This is due to how evaluation in Racket works.
+
+function! ale_linters#racket#raco#Handle(buffer, lines) abort
+ " Matches patterns
+ " <file>:<line>:<column> <message>
+ " eg:
+ " info.rkt:4:0: infotab-module: not a well-formed definition
+ let l:pattern = '^\(\s\)\@!\(.\+\):\(\d\+\):\(\d\+\): \(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[3] + 0,
+ \ 'col': l:match[4] + 0,
+ \ 'type': 'E',
+ \ 'text': l:match[5],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('racket', {
+\ 'name': 'raco',
+\ 'executable': 'raco',
+\ 'output_stream': 'stderr',
+\ 'command': 'raco expand %s',
+\ 'callback': 'ale_linters#racket#raco#Handle',
+\})
diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim
index 4e5bd395..66137e1b 100644
--- a/ale_linters/reason/ols.vim
+++ b/ale_linters/reason/ols.vim
@@ -7,8 +7,8 @@ call ale#Set('reason_ols_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#linter#Define('reason', {
\ 'name': 'ols',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale#handlers#ols#GetExecutable',
-\ 'command_callback': 'ale#handlers#ols#GetCommand',
+\ 'executable': function('ale#handlers#ols#GetExecutable'),
+\ 'command': function('ale#handlers#ols#GetCommand'),
\ 'language_callback': 'ale#handlers#ols#GetLanguage',
-\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot',
+\ 'project_root': function('ale#handlers#ols#GetProjectRoot'),
\})
diff --git a/ale_linters/rst/alex.vim b/ale_linters/rst/alex.vim
index e637eae7..e7ca6fa0 100644
--- a/ale_linters/rst/alex.vim
+++ b/ale_linters/rst/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for rst files
-call ale#linter#Define('rst', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('rst', '--text')
diff --git a/ale_linters/rst/rstcheck.vim b/ale_linters/rst/rstcheck.vim
index 8504738b..39e11d6e 100644
--- a/ale_linters/rst/rstcheck.vim
+++ b/ale_linters/rst/rstcheck.vim
@@ -32,7 +32,7 @@ endfunction
call ale#linter#Define('rst', {
\ 'name': 'rstcheck',
\ 'executable': 'rstcheck',
-\ 'command_callback': 'ale_linters#rst#rstcheck#GetCommand',
+\ 'command': function('ale_linters#rst#rstcheck#GetCommand'),
\ 'callback': 'ale_linters#rst#rstcheck#Handle',
\ 'output_stream': 'both',
\})
diff --git a/ale_linters/rst/textlint.vim b/ale_linters/rst/textlint.vim
new file mode 100644
index 00000000..56dd8db8
--- /dev/null
+++ b/ale_linters/rst/textlint.vim
@@ -0,0 +1,9 @@
+" Author: hokorobi <hokorobi.hokorobi@gmail.com>
+" Description: textlint, a proofreading tool (https://textlint.github.io/)
+
+call ale#linter#Define('rst', {
+\ 'name': 'textlint',
+\ 'executable': function('ale#handlers#textlint#GetExecutable'),
+\ 'command': function('ale#handlers#textlint#GetCommand'),
+\ 'callback': 'ale#handlers#textlint#HandleTextlintOutput',
+\})
diff --git a/ale_linters/ruby/brakeman.vim b/ale_linters/ruby/brakeman.vim
index 122e0b5b..a8088080 100644
--- a/ale_linters/ruby/brakeman.vim
+++ b/ale_linters/ruby/brakeman.vim
@@ -44,8 +44,8 @@ endfunction
call ale#linter#Define('ruby', {
\ 'name': 'brakeman',
-\ 'executable_callback': ale#VarFunc('ruby_brakeman_executable'),
-\ 'command_callback': 'ale_linters#ruby#brakeman#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'ruby_brakeman_executable')},
+\ 'command': function('ale_linters#ruby#brakeman#GetCommand'),
\ 'callback': 'ale_linters#ruby#brakeman#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/ruby/rails_best_practices.vim b/ale_linters/ruby/rails_best_practices.vim
index 20cadca8..a94fb671 100644
--- a/ale_linters/ruby/rails_best_practices.vim
+++ b/ale_linters/ruby/rails_best_practices.vim
@@ -30,8 +30,8 @@ function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort
endif
let l:executable = ale#Var(a:buffer, 'ruby_rails_best_practices_executable')
- let l:output_file = ale#Has('win32') ? '%t ' : '/dev/stdout '
- let l:cat_file = ale#Has('win32') ? '; type %t' : ''
+ let l:output_file = has('win32') ? '%t ' : '/dev/stdout '
+ let l:cat_file = has('win32') ? '; type %t' : ''
return ale#handlers#ruby#EscapeExecutable(l:executable, 'rails_best_practices')
\ . ' --silent -f json --output-file ' . l:output_file
@@ -42,8 +42,8 @@ endfunction
call ale#linter#Define('ruby', {
\ 'name': 'rails_best_practices',
-\ 'executable_callback': ale#VarFunc('ruby_rails_best_practices_executable'),
-\ 'command_callback': 'ale_linters#ruby#rails_best_practices#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'ruby_rails_best_practices_executable')},
+\ 'command': function('ale_linters#ruby#rails_best_practices#GetCommand'),
\ 'callback': 'ale_linters#ruby#rails_best_practices#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/ruby/reek.vim b/ale_linters/ruby/reek.vim
index 53363d31..e39e366f 100644
--- a/ale_linters/ruby/reek.vim
+++ b/ale_linters/ruby/reek.vim
@@ -6,26 +6,11 @@ call ale#Set('ruby_reek_show_wiki_link', 0)
call ale#Set('ruby_reek_options', '')
call ale#Set('ruby_reek_executable', 'reek')
-function! ale_linters#ruby#reek#VersionCheck(buffer) abort
- " If we have previously stored the version number in a cache, then
- " don't look it up again.
- if ale#semver#HasVersion('reek')
- " Returning an empty string skips this command.
- return ''
- endif
-
- let l:executable = ale#Var(a:buffer, 'ruby_reek_executable')
-
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'reek')
- \ . ' --version'
-endfunction
-
-function! ale_linters#ruby#reek#GetCommand(buffer, version_output) abort
- let l:version = ale#semver#GetVersion('reek', a:version_output)
+function! ale_linters#ruby#reek#GetCommand(buffer, version) abort
let l:executable = ale#Var(a:buffer, 'ruby_reek_executable')
" Tell reek what the filename is if the version of reek is new enough.
- let l:display_name_args = ale#semver#GTE(l:version, [5, 0, 0])
+ let l:display_name_args = ale#semver#GTE(a:version, [5, 0, 0])
\ ? ' --stdin-filename %s'
\ : ''
@@ -69,10 +54,12 @@ endfunction
call ale#linter#Define('ruby', {
\ 'name': 'reek',
-\ 'executable_callback': ale#VarFunc('ruby_reek_executable'),
-\ 'command_chain': [
-\ {'callback': 'ale_linters#ruby#reek#VersionCheck'},
-\ {'callback': 'ale_linters#ruby#reek#GetCommand'},
-\ ],
+\ 'executable': {b -> ale#Var(b, 'ruby_reek_executable')},
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale#Var(buffer, 'ruby_reek_executable'),
+\ '%e --version',
+\ function('ale_linters#ruby#reek#GetCommand'),
+\ )},
\ 'callback': 'ale_linters#ruby#reek#Handle',
\})
diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim
index 45218394..8b9e9c84 100644
--- a/ale_linters/ruby/rubocop.vim
+++ b/ale_linters/ruby/rubocop.vim
@@ -13,36 +13,6 @@ function! ale_linters#ruby#rubocop#GetCommand(buffer) abort
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
endfunction
-function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort
- try
- let l:errors = json_decode(a:lines[0])
- catch
- return []
- endtry
-
- if !has_key(l:errors, 'summary')
- \|| l:errors['summary']['offense_count'] == 0
- \|| empty(l:errors['files'])
- return []
- endif
-
- let l:output = []
-
- for l:error in l:errors['files'][0]['offenses']
- let l:start_col = l:error['location']['column'] + 0
- call add(l:output, {
- \ 'lnum': l:error['location']['line'] + 0,
- \ 'col': l:start_col,
- \ 'end_col': l:start_col + l:error['location']['length'] - 1,
- \ 'code': l:error['cop_name'],
- \ 'text': l:error['message'],
- \ 'type': ale_linters#ruby#rubocop#GetType(l:error['severity']),
- \})
- endfor
-
- return l:output
-endfunction
-
function! ale_linters#ruby#rubocop#GetType(severity) abort
if a:severity is? 'convention'
\|| a:severity is? 'warning'
@@ -55,7 +25,7 @@ endfunction
call ale#linter#Define('ruby', {
\ 'name': 'rubocop',
-\ 'executable_callback': ale#VarFunc('ruby_rubocop_executable'),
-\ 'command_callback': 'ale_linters#ruby#rubocop#GetCommand',
-\ 'callback': 'ale_linters#ruby#rubocop#Handle',
+\ 'executable': {b -> ale#Var(b, 'ruby_rubocop_executable')},
+\ 'command': function('ale_linters#ruby#rubocop#GetCommand'),
+\ 'callback': 'ale#ruby#HandleRubocopOutput',
\})
diff --git a/ale_linters/ruby/ruby.vim b/ale_linters/ruby/ruby.vim
index 2bc4ec4b..2dc55eb0 100644
--- a/ale_linters/ruby/ruby.vim
+++ b/ale_linters/ruby/ruby.vim
@@ -5,7 +5,7 @@ call ale#Set('ruby_ruby_executable', 'ruby')
call ale#linter#Define('ruby', {
\ 'name': 'ruby',
-\ 'executable_callback': ale#VarFunc('ruby_ruby_executable'),
+\ 'executable': {b -> ale#Var(b, 'ruby_ruby_executable')},
\ 'command': '%e -w -c -T1 %t',
\ 'output_stream': 'stderr',
\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
diff --git a/ale_linters/ruby/solargraph.vim b/ale_linters/ruby/solargraph.vim
index 5ff0a759..bf54a55c 100644
--- a/ale_linters/ruby/solargraph.vim
+++ b/ale_linters/ruby/solargraph.vim
@@ -15,8 +15,8 @@ call ale#linter#Define('ruby', {
\ 'name': 'solargraph',
\ 'lsp': 'stdio',
\ 'language': 'ruby',
-\ 'executable_callback': ale#VarFunc('ruby_solargraph_executable'),
-\ 'command_callback': 'ale_linters#ruby#solargraph#GetCommand',
-\ 'project_root_callback': 'ale#ruby#FindProjectRoot',
-\ 'initialization_options_callback': ale#VarFunc('ruby_solargraph_options'),
+\ 'executable': {b -> ale#Var(b, 'ruby_solargraph_executable')},
+\ 'command': function('ale_linters#ruby#solargraph#GetCommand'),
+\ 'project_root': function('ale#ruby#FindProjectRoot'),
+\ 'initialization_options': {b -> ale#Var(b, 'ruby_solargraph_options')},
\})
diff --git a/ale_linters/ruby/standardrb.vim b/ale_linters/ruby/standardrb.vim
new file mode 100644
index 00000000..f075a7d5
--- /dev/null
+++ b/ale_linters/ruby/standardrb.vim
@@ -0,0 +1,23 @@
+" Author: Justin Searls https://github.com/searls, ynonp - https://github.com/ynonp, Eddie Lebow https://github.com/elebow
+" based on the ale rubocop linter
+" Description: StandardRB - Ruby Style Guide, with linter & automatic code fixer
+
+call ale#Set('ruby_standardrb_executable', 'standardrb')
+call ale#Set('ruby_standardrb_options', '')
+
+function! ale_linters#ruby#standardrb#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'ruby_standardrb_executable')
+
+ return ale#handlers#ruby#EscapeExecutable(l:executable, 'standardrb')
+ \ . ' --format json --force-exclusion '
+ \ . ale#Var(a:buffer, 'ruby_standardrb_options')
+ \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
+endfunction
+
+" standardrb is based on RuboCop so the callback is the same
+call ale#linter#Define('ruby', {
+\ 'name': 'standardrb',
+\ 'executable': {b -> ale#Var(b, 'ruby_standardrb_executable')},
+\ 'command': function('ale_linters#ruby#standardrb#GetCommand'),
+\ 'callback': 'ale#ruby#HandleRubocopOutput',
+\})
diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim
index cf6187f8..f98dee9b 100644
--- a/ale_linters/rust/cargo.vim
+++ b/ale_linters/rust/cargo.vim
@@ -22,26 +22,18 @@ function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort
endif
endfunction
-function! ale_linters#rust#cargo#VersionCheck(buffer) abort
- return !ale#semver#HasVersion('cargo')
- \ ? 'cargo --version'
- \ : ''
-endfunction
-
-function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
- let l:version = ale#semver#GetVersion('cargo', a:version_output)
-
+function! ale_linters#rust#cargo#GetCommand(buffer, version) abort
let l:use_check = ale#Var(a:buffer, 'rust_cargo_use_check')
- \ && ale#semver#GTE(l:version, [0, 17, 0])
+ \ && ale#semver#GTE(a:version, [0, 17, 0])
let l:use_all_targets = l:use_check
\ && ale#Var(a:buffer, 'rust_cargo_check_all_targets')
- \ && ale#semver#GTE(l:version, [0, 22, 0])
+ \ && ale#semver#GTE(a:version, [0, 22, 0])
let l:use_examples = l:use_check
\ && ale#Var(a:buffer, 'rust_cargo_check_examples')
- \ && ale#semver#GTE(l:version, [0, 22, 0])
+ \ && ale#semver#GTE(a:version, [0, 22, 0])
let l:use_tests = l:use_check
\ && ale#Var(a:buffer, 'rust_cargo_check_tests')
- \ && ale#semver#GTE(l:version, [0, 22, 0])
+ \ && ale#semver#GTE(a:version, [0, 22, 0])
let l:include_features = ale#Var(a:buffer, 'rust_cargo_include_features')
@@ -93,11 +85,13 @@ endfunction
call ale#linter#Define('rust', {
\ 'name': 'cargo',
-\ 'executable_callback': 'ale_linters#rust#cargo#GetCargoExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#rust#cargo#VersionCheck'},
-\ {'callback': 'ale_linters#rust#cargo#GetCommand'},
-\ ],
+\ 'executable': function('ale_linters#rust#cargo#GetCargoExecutable'),
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale_linters#rust#cargo#GetCargoExecutable(buffer),
+\ '%e --version',
+\ function('ale_linters#rust#cargo#GetCommand'),
+\ )},
\ 'callback': 'ale#handlers#rust#HandleRustErrors',
\ 'output_stream': 'both',
\ 'lint_file': 1,
diff --git a/ale_linters/rust/rls.vim b/ale_linters/rust/rls.vim
index 60dd3667..111d7558 100644
--- a/ale_linters/rust/rls.vim
+++ b/ale_linters/rust/rls.vim
@@ -2,7 +2,8 @@
" Description: A language server for Rust
call ale#Set('rust_rls_executable', 'rls')
-call ale#Set('rust_rls_toolchain', 'nightly')
+call ale#Set('rust_rls_toolchain', '')
+call ale#Set('rust_rls_config', {})
function! ale_linters#rust#rls#GetCommand(buffer) abort
let l:toolchain = ale#Var(a:buffer, 'rust_rls_toolchain')
@@ -19,7 +20,8 @@ endfunction
call ale#linter#Define('rust', {
\ 'name': 'rls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('rust_rls_executable'),
-\ 'command_callback': 'ale_linters#rust#rls#GetCommand',
-\ 'project_root_callback': 'ale_linters#rust#rls#GetProjectRoot',
+\ 'lsp_config': {b -> ale#Var(b, 'rust_rls_config')},
+\ 'executable': {b -> ale#Var(b, 'rust_rls_executable')},
+\ 'command': function('ale_linters#rust#rls#GetCommand'),
+\ 'project_root': function('ale_linters#rust#rls#GetProjectRoot'),
\})
diff --git a/ale_linters/rust/rustc.vim b/ale_linters/rust/rustc.vim
index 33fb72f4..f140b58b 100644
--- a/ale_linters/rust/rustc.vim
+++ b/ale_linters/rust/rustc.vim
@@ -27,7 +27,7 @@ endfunction
call ale#linter#Define('rust', {
\ 'name': 'rustc',
\ 'executable': 'rustc',
-\ 'command_callback': 'ale_linters#rust#rustc#RustcCommand',
+\ 'command': function('ale_linters#rust#rustc#RustcCommand'),
\ 'callback': 'ale#handlers#rust#HandleRustErrors',
\ 'output_stream': 'stderr',
\})
diff --git a/ale_linters/sass/sasslint.vim b/ale_linters/sass/sasslint.vim
index 8d24185d..17cd3667 100644
--- a/ale_linters/sass/sasslint.vim
+++ b/ale_linters/sass/sasslint.vim
@@ -22,7 +22,7 @@ endfunction
call ale#linter#Define('sass', {
\ 'name': 'sasslint',
-\ 'executable_callback': 'ale_linters#sass#sasslint#GetExecutable',
-\ 'command_callback': 'ale_linters#sass#sasslint#GetCommand',
+\ 'executable': function('ale_linters#sass#sasslint#GetExecutable'),
+\ 'command': function('ale_linters#sass#sasslint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/sass/stylelint.vim b/ale_linters/sass/stylelint.vim
index b6286f18..7b14c6b4 100644
--- a/ale_linters/sass/stylelint.vim
+++ b/ale_linters/sass/stylelint.vim
@@ -5,9 +5,9 @@ call ale#Set('sass_stylelint_use_global', get(g:, 'ale_use_global_executables',
call ale#linter#Define('sass', {
\ 'name': 'stylelint',
-\ 'executable_callback': ale#node#FindExecutableFunc('sass_stylelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'sass_stylelint', [
\ 'node_modules/.bin/stylelint',
-\ ]),
+\ ])},
\ 'command': '%e --stdin-filename %s',
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/scala/fsc.vim b/ale_linters/scala/fsc.vim
index fbdce20e..94135235 100644
--- a/ale_linters/scala/fsc.vim
+++ b/ale_linters/scala/fsc.vim
@@ -7,7 +7,7 @@ endfunction
call ale#linter#Define('scala', {
\ 'name': 'fsc',
-\ 'executable_callback': {buf -> s:IsSbt(buf) ? '' : 'fsc'},
+\ 'executable': {buf -> s:IsSbt(buf) ? '' : 'fsc'},
\ 'command': '%e -Ystop-after:parser %t',
\ 'callback': 'ale#handlers#scala#HandleScalacLintFormat',
\ 'output_stream': 'stderr',
diff --git a/ale_linters/scala/sbtserver.vim b/ale_linters/scala/sbtserver.vim
index 694241d7..d4f137c2 100644
--- a/ale_linters/scala/sbtserver.vim
+++ b/ale_linters/scala/sbtserver.vim
@@ -25,7 +25,7 @@ endfunction
call ale#linter#Define('scala', {
\ 'name': 'sbtserver',
\ 'lsp': 'socket',
-\ 'address_callback': 'ale_linters#scala#sbtserver#GetAddress',
+\ 'address': function('ale_linters#scala#sbtserver#GetAddress'),
\ 'language': 'scala',
-\ 'project_root_callback': 'ale_linters#scala#sbtserver#GetProjectRoot',
+\ 'project_root': function('ale_linters#scala#sbtserver#GetProjectRoot'),
\})
diff --git a/ale_linters/scala/scalac.vim b/ale_linters/scala/scalac.vim
index 3dbdd925..1dd579b4 100644
--- a/ale_linters/scala/scalac.vim
+++ b/ale_linters/scala/scalac.vim
@@ -8,7 +8,7 @@ endfunction
call ale#linter#Define('scala', {
\ 'name': 'scalac',
-\ 'executable_callback': {buf -> s:IsSbt(buf) ? '' : 'scalac'},
+\ 'executable': {buf -> s:IsSbt(buf) ? '' : 'scalac'},
\ 'command': '%e -Ystop-after:parser %t',
\ 'callback': 'ale#handlers#scala#HandleScalacLintFormat',
\ 'output_stream': 'stderr',
diff --git a/ale_linters/scala/scalastyle.vim b/ale_linters/scala/scalastyle.vim
index 42228cf6..6e9e4c13 100644
--- a/ale_linters/scala/scalastyle.vim
+++ b/ale_linters/scala/scalastyle.vim
@@ -81,6 +81,6 @@ call ale#linter#Define('scala', {
\ 'name': 'scalastyle',
\ 'executable': 'scalastyle',
\ 'output_stream': 'stdout',
-\ 'command_callback': 'ale_linters#scala#scalastyle#GetCommand',
+\ 'command': function('ale_linters#scala#scalastyle#GetCommand'),
\ 'callback': 'ale_linters#scala#scalastyle#Handle',
\})
diff --git a/ale_linters/scss/sasslint.vim b/ale_linters/scss/sasslint.vim
index 8b725ba6..cf13546e 100644
--- a/ale_linters/scss/sasslint.vim
+++ b/ale_linters/scss/sasslint.vim
@@ -22,7 +22,7 @@ endfunction
call ale#linter#Define('scss', {
\ 'name': 'sasslint',
-\ 'executable_callback': 'ale_linters#scss#sasslint#GetExecutable',
-\ 'command_callback': 'ale_linters#scss#sasslint#GetCommand',
+\ 'executable': function('ale_linters#scss#sasslint#GetExecutable'),
+\ 'command': function('ale_linters#scss#sasslint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/scss/stylelint.vim b/ale_linters/scss/stylelint.vim
index 2bffa8e1..b5b21536 100644
--- a/ale_linters/scss/stylelint.vim
+++ b/ale_linters/scss/stylelint.vim
@@ -11,9 +11,9 @@ endfunction
call ale#linter#Define('scss', {
\ 'name': 'stylelint',
-\ 'executable_callback': ale#node#FindExecutableFunc('scss_stylelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'scss_stylelint', [
\ 'node_modules/.bin/stylelint',
-\ ]),
-\ 'command_callback': 'ale_linters#scss#stylelint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#scss#stylelint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/sh/language_server.vim b/ale_linters/sh/language_server.vim
index 385d1119..5a3b0e9a 100644
--- a/ale_linters/sh/language_server.vim
+++ b/ale_linters/sh/language_server.vim
@@ -26,7 +26,7 @@ endfunction
call ale#linter#Define('sh', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
-\ 'executable_callback': 'ale_linters#sh#language_server#GetExecutable',
-\ 'command_callback': 'ale_linters#sh#language_server#GetCommand',
-\ 'project_root_callback': 'ale_linters#sh#language_server#GetProjectRoot',
+\ 'executable': function('ale_linters#sh#language_server#GetExecutable'),
+\ 'command': function('ale_linters#sh#language_server#GetCommand'),
+\ 'project_root': function('ale_linters#sh#language_server#GetProjectRoot'),
\})
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index cf5e4e6c..189dc21d 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -51,7 +51,7 @@ endfunction
call ale#linter#Define('sh', {
\ 'name': 'shell',
\ 'output_stream': 'stderr',
-\ 'executable_callback': 'ale_linters#sh#shell#GetExecutable',
-\ 'command_callback': 'ale_linters#sh#shell#GetCommand',
+\ 'executable': function('ale_linters#sh#shell#GetExecutable'),
+\ 'command': function('ale_linters#sh#shell#GetCommand'),
\ 'callback': 'ale_linters#sh#shell#Handle',
\})
diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim
index 0f68e62c..1d8b6096 100644
--- a/ale_linters/sh/shellcheck.vim
+++ b/ale_linters/sh/shellcheck.vim
@@ -8,11 +8,9 @@
" let g:ale_sh_shellcheck_exclusions = 'SC2002,SC2004'
call ale#Set('sh_shellcheck_exclusions', get(g:, 'ale_linters_sh_shellcheck_exclusions', ''))
call ale#Set('sh_shellcheck_executable', 'shellcheck')
+call ale#Set('sh_shellcheck_dialect', 'auto')
call ale#Set('sh_shellcheck_options', '')
-
-function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort
- return ale#Var(a:buffer, 'sh_shellcheck_executable')
-endfunction
+call ale#Set('sh_shellcheck_change_directory', 1)
function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort
let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
@@ -38,26 +36,21 @@ function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort
return ''
endfunction
-function! ale_linters#sh#shellcheck#VersionCheck(buffer) abort
- let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer)
-
- " Don't check the version again if we've already cached it.
- return !ale#semver#HasVersion(l:executable)
- \ ? ale#Escape(l:executable) . ' --version'
- \ : ''
-endfunction
-
-function! ale_linters#sh#shellcheck#GetCommand(buffer, version_output) abort
- let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
-
+function! ale_linters#sh#shellcheck#GetCommand(buffer, version) 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)
- let l:external_option = ale#semver#GTE(l:version, [0, 4, 0]) ? ' -x' : ''
+ let l:dialect = ale#Var(a:buffer, 'sh_shellcheck_dialect')
+ let l:external_option = ale#semver#GTE(a:version, [0, 4, 0]) ? ' -x' : ''
+ let l:cd_string = ale#Var(a:buffer, 'sh_shellcheck_change_directory')
+ \ ? ale#path#BufferCdString(a:buffer)
+ \ : ''
+
+ if l:dialect is# 'auto'
+ let l:dialect = ale_linters#sh#shellcheck#GetDialectArgument(a:buffer)
+ endif
- return ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable)
+ return l:cd_string
+ \ . '%e'
\ . (!empty(l:dialect) ? ' -s ' . l:dialect : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '')
@@ -103,10 +96,12 @@ endfunction
call ale#linter#Define('sh', {
\ 'name': 'shellcheck',
-\ 'executable_callback': 'ale_linters#sh#shellcheck#GetExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#sh#shellcheck#VersionCheck'},
-\ {'callback': 'ale_linters#sh#shellcheck#GetCommand'},
-\ ],
+\ 'executable': {buffer -> ale#Var(buffer, 'sh_shellcheck_executable')},
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale#Var(buffer, 'sh_shellcheck_executable'),
+\ '%e --version',
+\ function('ale_linters#sh#shellcheck#GetCommand'),
+\ )},
\ 'callback': 'ale_linters#sh#shellcheck#Handle',
\})
diff --git a/ale_linters/slim/slimlint.vim b/ale_linters/slim/slimlint.vim
index 00c6b26c..1b365e25 100644
--- a/ale_linters/slim/slimlint.vim
+++ b/ale_linters/slim/slimlint.vim
@@ -11,11 +11,11 @@ function! ale_linters#slim#slimlint#GetCommand(buffer) abort
"
" See https://github.com/sds/slim-lint/blob/master/lib/slim_lint/linter/README.md#rubocop
if !empty(l:rubocop_config)
- if ale#Has('win32')
- let l:command = 'set SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' && ' . l:command
- else
- let l:command = 'SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' ' . l:command
- endif
+ if has('win32')
+ let l:command = 'set SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' && ' . l:command
+ else
+ let l:command = 'SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' ' . l:command
+ endif
endif
return l:command
@@ -50,6 +50,6 @@ endfunction
call ale#linter#Define('slim', {
\ 'name': 'slimlint',
\ 'executable': 'slim-lint',
-\ 'command_callback': 'ale_linters#slim#slimlint#GetCommand',
+\ 'command': function('ale_linters#slim#slimlint#GetCommand'),
\ 'callback': 'ale_linters#slim#slimlint#Handle'
\})
diff --git a/ale_linters/sml/smlnj.vim b/ale_linters/sml/smlnj.vim
index f15579ea..852ea170 100644
--- a/ale_linters/sml/smlnj.vim
+++ b/ale_linters/sml/smlnj.vim
@@ -3,7 +3,7 @@
call ale#linter#Define('sml', {
\ 'name': 'smlnj',
-\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjFile',
+\ 'executable': function('ale#handlers#sml#GetExecutableSmlnjFile'),
\ 'command': 'sml',
\ 'callback': 'ale#handlers#sml#Handle',
\})
diff --git a/ale_linters/sml/smlnj_cm.vim b/ale_linters/sml/smlnj_cm.vim
index bfa4bc05..9ad24af4 100644
--- a/ale_linters/sml/smlnj_cm.vim
+++ b/ale_linters/sml/smlnj_cm.vim
@@ -12,9 +12,9 @@ endfunction
call ale#linter#Define('sml', {
\ 'name': 'smlnj_cm',
\ 'aliases': ['smlnj-cm'],
-\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm',
+\ 'executable': function('ale#handlers#sml#GetExecutableSmlnjCm'),
\ 'lint_file': 1,
-\ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand',
+\ 'command': function('ale_linters#sml#smlnj_cm#GetCommand'),
\ 'callback': 'ale#handlers#sml#Handle',
\})
diff --git a/ale_linters/spec/rpmlint.vim b/ale_linters/spec/rpmlint.vim
index 486bef1e..92ef4d63 100644
--- a/ale_linters/spec/rpmlint.vim
+++ b/ale_linters/spec/rpmlint.vim
@@ -72,7 +72,7 @@ endfunction
call ale#linter#Define('spec', {
\ 'name': 'rpmlint',
-\ 'executable_callback': ale#VarFunc('spec_rpmlint_executable'),
-\ 'command_callback': 'ale_linters#spec#rpmlint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'spec_rpmlint_executable')},
+\ 'command': function('ale_linters#spec#rpmlint#GetCommand'),
\ 'callback': 'ale_linters#spec#rpmlint#Handle',
\})
diff --git a/ale_linters/stylus/stylelint.vim b/ale_linters/stylus/stylelint.vim
index 2256f3c0..ce6f9426 100644
--- a/ale_linters/stylus/stylelint.vim
+++ b/ale_linters/stylus/stylelint.vim
@@ -12,9 +12,9 @@ endfunction
call ale#linter#Define('stylus', {
\ 'name': 'stylelint',
-\ 'executable_callback': ale#node#FindExecutableFunc('stylus_stylelint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'stylus_stylelint', [
\ 'node_modules/.bin/stylelint',
-\ ]),
-\ 'command_callback': 'ale_linters#stylus#stylelint#GetCommand',
+\ ])},
+\ 'command': function('ale_linters#stylus#stylelint#GetCommand'),
\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/sugarss/stylelint.vim b/ale_linters/sugarss/stylelint.vim
new file mode 100644
index 00000000..6c705e46
--- /dev/null
+++ b/ale_linters/sugarss/stylelint.vim
@@ -0,0 +1,21 @@
+" Author: toastal <toastal@protonmail.com>
+" Description: `stylelint` linter for SugarSS files
+
+call ale#Set('sugarss_stylelint_executable', 'stylelint')
+call ale#Set('sugarss_stylelint_options', '')
+call ale#Set('sugarss_stylelint_use_global', get(g:, 'ale_use_global_executables', 0))
+
+function! ale_linters#sugarss#stylelint#GetCommand(buffer) abort
+ return '%e ' . ale#Pad(ale#Var(a:buffer, 'sugarss_stylelint_options'))
+ \ . ' --syntax=sugarss'
+ \ . ' --stdin-filename %s'
+endfunction
+
+call ale#linter#Define('sugarss', {
+\ 'name': 'stylelint',
+\ 'executable': {b -> ale#node#FindExecutable(b, 'sugarss_stylelint', [
+\ 'node_modules/.bin/stylelint',
+\ ])},
+\ 'command': function('ale_linters#sugarss#stylelint#GetCommand'),
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
+\})
diff --git a/ale_linters/swift/sourcekitlsp.vim b/ale_linters/swift/sourcekitlsp.vim
new file mode 100644
index 00000000..560893bf
--- /dev/null
+++ b/ale_linters/swift/sourcekitlsp.vim
@@ -0,0 +1,13 @@
+" Author: Dan Loman <https://github.com/namolnad>
+" Description: Support for sourcekit-lsp https://github.com/apple/sourcekit-lsp
+
+call ale#Set('sourcekit_lsp_executable', 'sourcekit-lsp')
+
+call ale#linter#Define('swift', {
+\ 'name': 'sourcekitlsp',
+\ 'lsp': 'stdio',
+\ 'executable': {b -> ale#Var(b, 'sourcekit_lsp_executable')},
+\ 'command': '%e',
+\ 'project_root': function('ale#swift#FindProjectRoot'),
+\ 'language': 'swift',
+\})
diff --git a/ale_linters/swift/swiftlint.vim b/ale_linters/swift/swiftlint.vim
index a1150658..237c45d3 100644
--- a/ale_linters/swift/swiftlint.vim
+++ b/ale_linters/swift/swiftlint.vim
@@ -6,10 +6,10 @@ call ale#Set('swift_swiftlint_use_global', get(g:, 'ale_use_global_executables',
function! ale_linters#swift#swiftlint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'swift_swiftlint', [
- \ 'Pods/SwiftLint/swiftlint',
- \ 'ios/Pods/SwiftLint/swiftlint',
- \ 'swiftlint',
- \])
+ \ 'Pods/SwiftLint/swiftlint',
+ \ 'ios/Pods/SwiftLint/swiftlint',
+ \ 'swiftlint',
+ \])
endfunction
function! ale_linters#swift#swiftlint#GetCommand(buffer) abort
@@ -17,7 +17,7 @@ function! ale_linters#swift#swiftlint#GetCommand(buffer) abort
let l:args = 'lint --use-stdin'
return ale#Escape(l:executable)
- \ . ' ' .l:args
+ \ . ' ' .l:args
endfunction
function! ale_linters#swift#swiftlint#Handle(buffer, lines) abort
@@ -26,10 +26,10 @@ function! ale_linters#swift#swiftlint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:item = {
- \ 'lnum': str2nr(l:match[2]),
- \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
- \ 'text': l:match[5],
- \}
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[5],
+ \}
if l:match[4] is# 'error'
let l:item.type = 'E'
@@ -63,7 +63,7 @@ endfunction
call ale#linter#Define('swift', {
\ 'name': 'swiftlint',
-\ 'executable_callback': 'ale_linters#swift#swiftlint#GetExecutable',
-\ 'command_callback': 'ale_linters#swift#swiftlint#GetCommand',
+\ 'executable': function('ale_linters#swift#swiftlint#GetExecutable'),
+\ 'command': function('ale_linters#swift#swiftlint#GetCommand'),
\ 'callback': 'ale_linters#swift#swiftlint#Handle',
\})
diff --git a/ale_linters/tcl/nagelfar.vim b/ale_linters/tcl/nagelfar.vim
index 05fe581b..5a4940e1 100644
--- a/ale_linters/tcl/nagelfar.vim
+++ b/ale_linters/tcl/nagelfar.vim
@@ -32,8 +32,8 @@ endfunction
call ale#linter#Define('tcl', {
\ 'name': 'nagelfar',
\ 'output_stream': 'stdout',
-\ 'executable_callback': ale#VarFunc('tcl_nagelfar_executable'),
-\ 'command_callback': 'ale_linters#tcl#nagelfar#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'tcl_nagelfar_executable')},
+\ 'command': function('ale_linters#tcl#nagelfar#GetCommand'),
\ 'callback': 'ale_linters#tcl#nagelfar#Handle',
\ 'lint_file': 1,
\})
diff --git a/ale_linters/terraform/tflint.vim b/ale_linters/terraform/tflint.vim
index 0d77835a..6d54a8b1 100644
--- a/ale_linters/terraform/tflint.vim
+++ b/ale_linters/terraform/tflint.vim
@@ -52,7 +52,7 @@ endfunction
call ale#linter#Define('terraform', {
\ 'name': 'tflint',
-\ 'executable_callback': ale#VarFunc('terraform_tflint_executable'),
-\ 'command_callback': 'ale_linters#terraform#tflint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'terraform_tflint_executable')},
+\ 'command': function('ale_linters#terraform#tflint#GetCommand'),
\ 'callback': 'ale_linters#terraform#tflint#Handle',
\})
diff --git a/ale_linters/tex/alex.vim b/ale_linters/tex/alex.vim
index 78c530f7..5d9aec66 100644
--- a/ale_linters/tex/alex.vim
+++ b/ale_linters/tex/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for TeX files
-call ale#linter#Define('tex', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('tex', '--text')
diff --git a/ale_linters/tex/chktex.vim b/ale_linters/tex/chktex.vim
index 7f1b0c72..160baf0d 100644
--- a/ale_linters/tex/chktex.vim
+++ b/ale_linters/tex/chktex.vim
@@ -49,6 +49,6 @@ endfunction
call ale#linter#Define('tex', {
\ 'name': 'chktex',
\ 'executable': 'chktex',
-\ 'command_callback': 'ale_linters#tex#chktex#GetCommand',
+\ 'command': function('ale_linters#tex#chktex#GetCommand'),
\ 'callback': 'ale_linters#tex#chktex#Handle'
\})
diff --git a/ale_linters/tex/lacheck.vim b/ale_linters/tex/lacheck.vim
index 5e5a94f1..19d69403 100644
--- a/ale_linters/tex/lacheck.vim
+++ b/ale_linters/tex/lacheck.vim
@@ -8,20 +8,26 @@ function! ale_linters#tex#lacheck#Handle(buffer, lines) abort
"
" "book.tex", line 37: possible unwanted space at "{"
" "book.tex", line 38: missing `\ ' after "etc."
- let l:pattern = '^".\+", line \(\d\+\): \(.\+\)$'
+ let l:pattern = '^"\(.\+\)", line \(\d\+\): \(.\+\)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
" lacheck follows `\input{}` commands. If the cwd is not the same as the
" file in the buffer then it will fail to find the inputed items. We do not
" want warnings from those items anyway
- if !empty(matchstr(l:match[2], '^Could not open ".\+"$'))
+ if !empty(matchstr(l:match[3], '^Could not open ".\+"$'))
+ continue
+ endif
+
+ " lacheck follows `\input{}` commands. We are only interested in
+ " reporting errors for the current buffer only.
+ if empty(matchstr(fnamemodify(l:match[1], ':t'), fnamemodify(bufname(a:buffer), ':t')))
continue
endif
call add(l:output, {
- \ 'lnum': l:match[1] + 0,
- \ 'text': l:match[2],
+ \ 'lnum': l:match[2] + 0,
+ \ 'text': l:match[3],
\ 'type': 'W',
\})
endfor
@@ -31,7 +37,7 @@ endfunction
call ale#linter#Define('tex', {
\ 'name': 'lacheck',
-\ 'executable_callback': ale#VarFunc('tex_lacheck_executable'),
+\ 'executable': {b -> ale#Var(b, 'tex_lacheck_executable')},
\ 'command': '%e %t',
\ 'callback': 'ale_linters#tex#lacheck#Handle'
\})
diff --git a/ale_linters/tex/textlint.vim b/ale_linters/tex/textlint.vim
new file mode 100644
index 00000000..5edac46d
--- /dev/null
+++ b/ale_linters/tex/textlint.vim
@@ -0,0 +1,9 @@
+" Author: TANIGUCHI Masaya <ta2gch@gmail.com>
+" Description: textlint for LaTeX files
+
+call ale#linter#Define('tex', {
+\ 'name': 'textlint',
+\ 'executable': function('ale#handlers#textlint#GetExecutable'),
+\ 'command': function('ale#handlers#textlint#GetCommand'),
+\ 'callback': 'ale#handlers#textlint#HandleTextlintOutput',
+\})
diff --git a/ale_linters/texinfo/alex.vim b/ale_linters/texinfo/alex.vim
index 4a884579..4d245524 100644
--- a/ale_linters/texinfo/alex.vim
+++ b/ale_linters/texinfo/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for texinfo files
-call ale#linter#Define('texinfo', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('texinfo', '--text')
diff --git a/ale_linters/text/alex.vim b/ale_linters/text/alex.vim
index c696367b..d87ed915 100644
--- a/ale_linters/text/alex.vim
+++ b/ale_linters/text/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for text files
-call ale#linter#Define('text', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('text', '--text')
diff --git a/ale_linters/text/languagetool.vim b/ale_linters/text/languagetool.vim
new file mode 100644
index 00000000..58c99ba2
--- /dev/null
+++ b/ale_linters/text/languagetool.vim
@@ -0,0 +1,4 @@
+" Author: Vincent (wahrwolf [ät] wolfpit.net)
+" Description: languagetool for text files
+
+call ale#handlers#languagetool#DefineLinter('text')
diff --git a/ale_linters/text/textlint.vim b/ale_linters/text/textlint.vim
index 8fafdd7d..67c4e378 100644
--- a/ale_linters/text/textlint.vim
+++ b/ale_linters/text/textlint.vim
@@ -3,7 +3,7 @@
call ale#linter#Define('text', {
\ 'name': 'textlint',
-\ 'executable_callback': 'ale#handlers#textlint#GetExecutable',
-\ 'command_callback': 'ale#handlers#textlint#GetCommand',
+\ 'executable': function('ale#handlers#textlint#GetExecutable'),
+\ 'command': function('ale#handlers#textlint#GetCommand'),
\ 'callback': 'ale#handlers#textlint#HandleTextlintOutput',
\})
diff --git a/ale_linters/thrift/thrift.vim b/ale_linters/thrift/thrift.vim
index 36a8656e..345c7abe 100644
--- a/ale_linters/thrift/thrift.vim
+++ b/ale_linters/thrift/thrift.vim
@@ -16,7 +16,7 @@ function! ale_linters#thrift#thrift#GetCommand(buffer) abort
let l:generators = ['cpp']
endif
- let l:output_dir = ale#engine#CreateDirectory(a:buffer)
+ let l:output_dir = ale#command#CreateDirectory(a:buffer)
return '%e'
\ . ale#Pad(join(map(copy(l:generators), "'--gen ' . v:val")))
@@ -80,9 +80,8 @@ endfunction
call ale#linter#Define('thrift', {
\ 'name': 'thrift',
-\ 'executable': 'thrift',
\ 'output_stream': 'both',
-\ 'executable_callback': ale#VarFunc('thrift_thrift_executable'),
-\ 'command_callback': 'ale_linters#thrift#thrift#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'thrift_thrift_executable')},
+\ 'command': function('ale_linters#thrift#thrift#GetCommand'),
\ 'callback': 'ale_linters#thrift#thrift#Handle',
\})
diff --git a/ale_linters/typescript/eslint.vim b/ale_linters/typescript/eslint.vim
index f1ae54e7..bf849337 100644
--- a/ale_linters/typescript/eslint.vim
+++ b/ale_linters/typescript/eslint.vim
@@ -3,7 +3,7 @@
call ale#linter#Define('typescript', {
\ 'name': 'eslint',
-\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
-\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'executable': function('ale#handlers#eslint#GetExecutable'),
+\ 'command': function('ale#handlers#eslint#GetCommand'),
\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/typescript/tslint.vim b/ale_linters/typescript/tslint.vim
index ccdca936..f70c2e45 100644
--- a/ale_linters/typescript/tslint.vim
+++ b/ale_linters/typescript/tslint.vim
@@ -69,7 +69,7 @@ endfunction
call ale#linter#Define('typescript', {
\ 'name': 'tslint',
-\ 'executable_callback': 'ale#handlers#tslint#GetExecutable',
-\ 'command_callback': 'ale_linters#typescript#tslint#GetCommand',
+\ 'executable': function('ale#handlers#tslint#GetExecutable'),
+\ 'command': function('ale_linters#typescript#tslint#GetCommand'),
\ 'callback': 'ale_linters#typescript#tslint#Handle',
\})
diff --git a/ale_linters/typescript/tsserver.vim b/ale_linters/typescript/tsserver.vim
index bac63229..840889f3 100644
--- a/ale_linters/typescript/tsserver.vim
+++ b/ale_linters/typescript/tsserver.vim
@@ -8,10 +8,10 @@ call ale#Set('typescript_tsserver_use_global', get(g:, 'ale_use_global_executabl
call ale#linter#Define('typescript', {
\ 'name': 'tsserver',
\ 'lsp': 'tsserver',
-\ 'executable_callback': ale#node#FindExecutableFunc('typescript_tsserver', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'typescript_tsserver', [
\ 'node_modules/.bin/tsserver',
-\ ]),
+\ ])},
\ 'command': '%e',
-\ 'project_root_callback': {-> ''},
+\ 'project_root': function('ale#handlers#tsserver#GetProjectRoot'),
\ 'language': '',
\})
diff --git a/ale_linters/typescript/xo.vim b/ale_linters/typescript/xo.vim
new file mode 100644
index 00000000..8b015efd
--- /dev/null
+++ b/ale_linters/typescript/xo.vim
@@ -0,0 +1,23 @@
+call ale#Set('typescript_xo_executable', 'xo')
+call ale#Set('typescript_xo_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('typescript_xo_options', '')
+
+function! ale_linters#typescript#xo#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'typescript_xo', [
+ \ 'node_modules/.bin/xo',
+ \])
+endfunction
+
+function! ale_linters#typescript#xo#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#typescript#xo#GetExecutable(a:buffer))
+ \ . ale#Pad(ale#Var(a:buffer, 'typescript_xo_options'))
+ \ . ' --reporter unix --stdin --stdin-filename %s'
+endfunction
+
+" xo uses eslint and the output format is the same
+call ale#linter#Define('typescript', {
+\ 'name': 'xo',
+\ 'executable': function('ale_linters#typescript#xo#GetExecutable'),
+\ 'command': function('ale_linters#typescript#xo#GetCommand'),
+\ 'callback': 'ale#handlers#eslint#Handle',
+\})
diff --git a/ale_linters/verilog/iverilog.vim b/ale_linters/verilog/iverilog.vim
index c64a3be6..e081f33f 100644
--- a/ale_linters/verilog/iverilog.vim
+++ b/ale_linters/verilog/iverilog.vim
@@ -38,6 +38,6 @@ call ale#linter#Define('verilog', {
\ 'name': 'iverilog',
\ 'output_stream': 'stderr',
\ 'executable': 'iverilog',
-\ 'command_callback': 'ale_linters#verilog#iverilog#GetCommand',
+\ 'command': function('ale_linters#verilog#iverilog#GetCommand'),
\ 'callback': 'ale_linters#verilog#iverilog#Handle',
\})
diff --git a/ale_linters/verilog/verilator.vim b/ale_linters/verilog/verilator.vim
index 83d5f59d..64bb6e41 100644
--- a/ale_linters/verilog/verilator.vim
+++ b/ale_linters/verilog/verilator.vim
@@ -10,7 +10,7 @@ function! ale_linters#verilog#verilator#GetCommand(buffer) abort
let l:filename = ale#util#Tempname() . '_verilator_linted.v'
" Create a special filename, so we can detect it in the handler.
- call ale#engine#ManageFile(a:buffer, l:filename)
+ call ale#command#ManageFile(a:buffer, l:filename)
let l:lines = getbufline(a:buffer, 1, '$')
call ale#util#Writefile(a:buffer, l:lines, l:filename)
@@ -53,7 +53,7 @@ call ale#linter#Define('verilog', {
\ 'name': 'verilator',
\ 'output_stream': 'stderr',
\ 'executable': 'verilator',
-\ 'command_callback': 'ale_linters#verilog#verilator#GetCommand',
+\ 'command': function('ale_linters#verilog#verilator#GetCommand'),
\ 'callback': 'ale_linters#verilog#verilator#Handle',
\ 'read_buffer': 0,
\})
diff --git a/ale_linters/verilog/vlog.vim b/ale_linters/verilog/vlog.vim
new file mode 100644
index 00000000..37d21c4c
--- /dev/null
+++ b/ale_linters/verilog/vlog.vim
@@ -0,0 +1,36 @@
+" Author: John Gentile <johncgentile17@gmail.com>
+" Description: Adds support for Mentor Graphics Questa/ModelSim `vlog` Verilog compiler/checker
+
+call ale#Set('verilog_vlog_executable', 'vlog')
+" See `$ vlog -h` for more options
+call ale#Set('verilog_vlog_options', '-quiet -lint')
+
+function! ale_linters#verilog#vlog#GetCommand(buffer) abort
+ return '%e ' . ale#Pad(ale#Var(a:buffer, 'verilog_vlog_options')) . ' %t'
+endfunction
+
+function! ale_linters#verilog#vlog#Handle(buffer, lines) abort
+ "Matches patterns like the following:
+ "** Warning: add.v(7): (vlog-2623) Undefined variable: C.
+ "** Error: file.v(1): (vlog-13294) Identifier must be declared with a port mode: C.
+ let l:pattern = '^**\s\(\w*\):[a-zA-Z0-9\-\.\_\/ ]\+(\(\d\+\)):\s\+\(.*\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'type': l:match[1] is? 'Error' ? 'E' : 'W',
+ \ 'text': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('verilog', {
+\ 'name': 'vlog',
+\ 'output_stream': 'stdout',
+\ 'executable': {b -> ale#Var(b, 'verilog_vlog_executable')},
+\ 'command': function('ale_linters#verilog#vlog#GetCommand'),
+\ 'callback': 'ale_linters#verilog#vlog#Handle',
+\})
diff --git a/ale_linters/verilog/xvlog.vim b/ale_linters/verilog/xvlog.vim
new file mode 100644
index 00000000..98b5aae7
--- /dev/null
+++ b/ale_linters/verilog/xvlog.vim
@@ -0,0 +1,35 @@
+" Author: John Gentile <johncgentile17@gmail.com>
+" Description: Adds support for Xilinx Vivado `xvlog` Verilog compiler/checker
+
+call ale#Set('verilog_xvlog_executable', 'xvlog')
+call ale#Set('verilog_xvlog_options', '')
+
+function! ale_linters#verilog#xvlog#GetCommand(buffer) abort
+ return '%e ' . ale#Pad(ale#Var(a:buffer, 'verilog_xvlog_options')) . ' %t'
+endfunction
+
+function! ale_linters#verilog#xvlog#Handle(buffer, lines) abort
+ "Matches patterns like the following:
+ " ERROR: [VRFC 10-1412] syntax error near output [/path/to/file.v:5]
+ let l:pattern = '^ERROR:\s\+\(\[.*\)\[.*:\([0-9]\+\)\]'
+ let l:output = []
+
+ " NOTE: `xvlog` only prints 'INFO' and 'ERROR' messages
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'type': 'E',
+ \ 'text': l:match[1],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('verilog', {
+\ 'name': 'xvlog',
+\ 'output_stream': 'stdout',
+\ 'executable': {b -> ale#Var(b, 'verilog_xvlog_executable')},
+\ 'command': function('ale_linters#verilog#xvlog#GetCommand'),
+\ 'callback': 'ale_linters#verilog#xvlog#Handle',
+\})
diff --git a/ale_linters/vhdl/ghdl.vim b/ale_linters/vhdl/ghdl.vim
new file mode 100644
index 00000000..b09e620b
--- /dev/null
+++ b/ale_linters/vhdl/ghdl.vim
@@ -0,0 +1,37 @@
+" Author: John Gentile <johncgentile17@gmail.com>
+" Description: Adds support for `ghdl` VHDL compiler/checker
+
+call ale#Set('vhdl_ghdl_executable', 'ghdl')
+" Compile w/VHDL-2008 support
+call ale#Set('vhdl_ghdl_options', '--std=08')
+
+function! ale_linters#vhdl#ghdl#GetCommand(buffer) abort
+ return '%e -s ' . ale#Pad(ale#Var(a:buffer, 'vhdl_ghdl_options')) . ' %t'
+endfunction
+
+function! ale_linters#vhdl#ghdl#Handle(buffer, lines) abort
+ " Look for 'error' lines like the following:
+ " dff_en.vhd:41:5:error: 'begin' is expected instead of 'if'
+ " /path/to/file.vhdl:12:8: no declaration for "i0"
+ let l:pattern = '^[a-zA-Z0-9\-\.\_\/ ]\+:\(\d\+\):\(\d\+\):\(.*\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col' : l:match[2] + 0,
+ \ 'text': l:match[3],
+ \ 'type': 'E',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('vhdl', {
+\ 'name': 'ghdl',
+\ 'output_stream': 'stderr',
+\ 'executable': {b -> ale#Var(b, 'vhdl_ghdl_executable')},
+\ 'command': function('ale_linters#vhdl#ghdl#GetCommand'),
+\ 'callback': 'ale_linters#vhdl#ghdl#Handle',
+\})
diff --git a/ale_linters/vhdl/vcom.vim b/ale_linters/vhdl/vcom.vim
new file mode 100644
index 00000000..1914fd33
--- /dev/null
+++ b/ale_linters/vhdl/vcom.vim
@@ -0,0 +1,38 @@
+" Author: John Gentile <johncgentile17@gmail.com>
+" Description: Adds support for Mentor Graphics Questa/ModelSim `vcom` VHDL compiler/checker
+
+call ale#Set('vhdl_vcom_executable', 'vcom')
+" Use VHDL-2008. See `$ vcom -h` for more options
+call ale#Set('vhdl_vcom_options', '-2008 -quiet -lint')
+
+function! ale_linters#vhdl#vcom#GetCommand(buffer) abort
+ return '%e ' . ale#Pad(ale#Var(a:buffer, 'vhdl_vcom_options')) . ' %t'
+endfunction
+
+function! ale_linters#vhdl#vcom#Handle(buffer, lines) abort
+ "Matches patterns like the following:
+ "** Warning: ../path/to/file.vhd(218): (vcom-1236) Shared variables must be of a protected type.
+ "** Error: tb_file.vhd(73): (vcom-1136) Unknown identifier "aresetn".
+ "** Error: tb_file.vhd(73): Bad resolution function (STD_LOGIC) for type (error).
+ "** Error: tb_file.vhd(73): near ":": (vcom-1576) expecting ';' or ')'.
+ let l:pattern = '^**\s\(\w*\):[a-zA-Z0-9\-\.\_\/ ]\+(\(\d\+\)):\s\+\(.*\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'type': l:match[1] is? 'Error' ? 'E' : 'W',
+ \ 'text': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('vhdl', {
+\ 'name': 'vcom',
+\ 'output_stream': 'stdout',
+\ 'executable': {b -> ale#Var(b, 'vhdl_vcom_executable')},
+\ 'command': function('ale_linters#vhdl#vcom#GetCommand'),
+\ 'callback': 'ale_linters#vhdl#vcom#Handle',
+\})
diff --git a/ale_linters/vhdl/xvhdl.vim b/ale_linters/vhdl/xvhdl.vim
new file mode 100644
index 00000000..8010ff14
--- /dev/null
+++ b/ale_linters/vhdl/xvhdl.vim
@@ -0,0 +1,37 @@
+" Author: John Gentile <johncgentile17@gmail.com>
+" Description: Adds support for Xilinx Vivado `xvhdl` VHDL compiler/checker
+
+call ale#Set('vhdl_xvhdl_executable', 'xvhdl')
+" Use VHDL-2008. See `$ xvhdl -h` for more options
+call ale#Set('vhdl_xvhdl_options', '--2008')
+
+function! ale_linters#vhdl#xvhdl#GetCommand(buffer) abort
+ return '%e ' . ale#Pad(ale#Var(a:buffer, 'vhdl_xvhdl_options')) . ' %t'
+endfunction
+
+function! ale_linters#vhdl#xvhdl#Handle(buffer, lines) abort
+ "Matches patterns like the following:
+ " ERROR: [VRFC 10-91] aresetn is not declared [/path/to/file.vhd:17]
+ " ERROR: [VRFC 10-91] m_axis_tx_tdata is not declared [/home/user/tx_data.vhd:128]
+ let l:pattern = '^ERROR:\s\+\(\[.*\)\[.*:\([0-9]\+\)\]'
+ let l:output = []
+
+ " NOTE: `xvhdl` only prints 'INFO' and 'ERROR' messages
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'type': 'E',
+ \ 'text': l:match[1],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('vhdl', {
+\ 'name': 'xvhdl',
+\ 'output_stream': 'stdout',
+\ 'executable': {b -> ale#Var(b, 'vhdl_xvhdl_executable')},
+\ 'command': function('ale_linters#vhdl#xvhdl#GetCommand'),
+\ 'callback': 'ale_linters#vhdl#xvhdl#Handle',
+\})
diff --git a/ale_linters/vim/ale_custom_linting_rules.vim b/ale_linters/vim/ale_custom_linting_rules.vim
index 3da44206..822eb30a 100644
--- a/ale_linters/vim/ale_custom_linting_rules.vim
+++ b/ale_linters/vim/ale_custom_linting_rules.vim
@@ -25,7 +25,7 @@ endfunction
function! ale_linters#vim#ale_custom_linting_rules#GetCommand(buffer) abort
let l:dir = s:GetALEProjectDir(a:buffer)
- let l:temp_dir = ale#engine#CreateDirectory(a:buffer)
+ let l:temp_dir = ale#command#CreateDirectory(a:buffer)
let l:temp_file = l:temp_dir . '/example.vim'
let l:lines = getbufline(a:buffer, 1, '$')
@@ -58,8 +58,8 @@ endfunction
call ale#linter#Define('vim', {
\ 'name': 'ale_custom_linting_rules',
-\ 'executable_callback': 'ale_linters#vim#ale_custom_linting_rules#GetExecutable',
-\ 'command_callback': 'ale_linters#vim#ale_custom_linting_rules#GetCommand',
+\ 'executable': function('ale_linters#vim#ale_custom_linting_rules#GetExecutable'),
+\ 'command': function('ale_linters#vim#ale_custom_linting_rules#GetCommand'),
\ 'callback': 'ale_linters#vim#ale_custom_linting_rules#Handle',
\ 'read_buffer': 0,
\})
diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim
index cf2d4afd..65e19126 100644
--- a/ale_linters/vim/vint.vim
+++ b/ale_linters/vim/vint.vim
@@ -7,29 +7,13 @@ call ale#Set('vim_vint_executable', 'vint')
let s:enable_neovim = has('nvim') ? ' --enable-neovim' : ''
let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"'
-function! ale_linters#vim#vint#GetExecutable(buffer) abort
- return ale#Var(a:buffer, 'vim_vint_executable')
-endfunction
-
-function! ale_linters#vim#vint#VersionCommand(buffer) abort
- let l:executable = ale_linters#vim#vint#GetExecutable(a:buffer)
-
- " Check the Vint version if we haven't checked it already.
- return !ale#semver#HasVersion(l:executable)
- \ ? ale#Escape(l:executable) . ' --version'
- \ : ''
-endfunction
-
-function! ale_linters#vim#vint#GetCommand(buffer, version_output) abort
- let l:executable = ale_linters#vim#vint#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
-
- let l:can_use_no_color_flag = empty(l:version)
- \ || ale#semver#GTE(l:version, [0, 3, 7])
+function! ale_linters#vim#vint#GetCommand(buffer, version) abort
+ let l:can_use_no_color_flag = empty(a:version)
+ \ || ale#semver#GTE(a:version, [0, 3, 7])
let l:warning_flag = ale#Var(a:buffer, 'vim_vint_show_style_issues') ? '-s' : '-w'
- return ale#Escape(l:executable)
+ return '%e'
\ . ' ' . l:warning_flag
\ . (l:can_use_no_color_flag ? ' --no-color' : '')
\ . s:enable_neovim
@@ -65,10 +49,12 @@ endfunction
call ale#linter#Define('vim', {
\ 'name': 'vint',
-\ 'executable_callback': 'ale_linters#vim#vint#GetExecutable',
-\ 'command_chain': [
-\ {'callback': 'ale_linters#vim#vint#VersionCommand', 'output_stream': 'stderr'},
-\ {'callback': 'ale_linters#vim#vint#GetCommand', 'output_stream': 'stdout'},
-\ ],
+\ 'executable': {buffer -> ale#Var(buffer, 'vim_vint_executable')},
+\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
+\ buffer,
+\ ale#Var(buffer, 'vim_vint_executable'),
+\ '%e --version',
+\ function('ale_linters#vim#vint#GetCommand'),
+\ )},
\ 'callback': 'ale_linters#vim#vint#Handle',
\})
diff --git a/ale_linters/vue/vls.vim b/ale_linters/vue/vls.vim
index 7116128b..ac451f3c 100644
--- a/ale_linters/vue/vls.vim
+++ b/ale_linters/vue/vls.vim
@@ -13,10 +13,10 @@ endfunction
call ale#linter#Define('vue', {
\ 'name': 'vls',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#node#FindExecutableFunc('vue_vls', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'vue_vls', [
\ 'node_modules/.bin/vls',
-\ ]),
+\ ])},
\ 'command': '%e --stdio',
\ 'language': 'vue',
-\ 'project_root_callback': 'ale_linters#vue#vls#GetProjectRoot',
+\ 'project_root': function('ale_linters#vue#vls#GetProjectRoot'),
\})
diff --git a/ale_linters/xhtml/alex.vim b/ale_linters/xhtml/alex.vim
index 60a9a7c9..97f3b59a 100644
--- a/ale_linters/xhtml/alex.vim
+++ b/ale_linters/xhtml/alex.vim
@@ -1,11 +1,4 @@
" Author: Johannes Wienke <languitar@semipol.de>
" Description: alex for XHTML files
-call ale#linter#Define('xhtml', {
-\ 'name': 'alex',
-\ 'executable': 'alex',
-\ 'command': 'alex %s -t',
-\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#alex#Handle',
-\ 'lint_file': 1,
-\})
+call ale#handlers#alex#DefineLinter('xhtml', '--text')
diff --git a/ale_linters/xml/xmllint.vim b/ale_linters/xml/xmllint.vim
index 59f43d16..553d0883 100644
--- a/ale_linters/xml/xmllint.vim
+++ b/ale_linters/xml/xmllint.vim
@@ -29,27 +29,27 @@ function! ale_linters#xml#xmllint#Handle(buffer, lines) abort
let l:match_message = matchlist(l:line, l:pattern_message)
if !empty(l:match_message)
- let l:line = l:match_message[2] + 0
- let l:type = l:match_message[4] =~? 'warning' ? 'W' : 'E'
- let l:text = l:match_message[3]
+ let l:line = l:match_message[2] + 0
+ let l:type = l:match_message[4] =~? 'warning' ? 'W' : 'E'
+ let l:text = l:match_message[3]
- call add(l:output, {
- \ 'lnum': l:line,
- \ 'text': l:text,
- \ 'type': l:type,
- \})
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
- continue
+ continue
endif
" Parse column position
let l:match_column_token = matchlist(l:line, l:pattern_column_token)
if !empty(l:output) && !empty(l:match_column_token)
- let l:previous = l:output[len(l:output) - 1]
- let l:previous['col'] = len(l:match_column_token[0])
+ let l:previous = l:output[len(l:output) - 1]
+ let l:previous['col'] = len(l:match_column_token[0])
- continue
+ continue
endif
endfor
@@ -59,7 +59,7 @@ endfunction
call ale#linter#Define('xml', {
\ 'name': 'xmllint',
\ 'output_stream': 'stderr',
-\ 'executable_callback': ale#VarFunc('xml_xmllint_executable'),
-\ 'command_callback': 'ale_linters#xml#xmllint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'xml_xmllint_executable')},
+\ 'command': function('ale_linters#xml#xmllint#GetCommand'),
\ 'callback': 'ale_linters#xml#xmllint#Handle',
\ })
diff --git a/ale_linters/yaml/swaglint.vim b/ale_linters/yaml/swaglint.vim
index 7362536e..1f140e37 100644
--- a/ale_linters/yaml/swaglint.vim
+++ b/ale_linters/yaml/swaglint.vim
@@ -32,9 +32,9 @@ endfunction
call ale#linter#Define('yaml', {
\ 'name': 'swaglint',
-\ 'executable_callback': ale#node#FindExecutableFunc('yaml_swaglint', [
+\ 'executable': {b -> ale#node#FindExecutable(b, 'yaml_swaglint', [
\ 'node_modules/.bin/swaglint',
-\ ]),
+\ ])},
\ 'command': '%e -r compact --stdin',
\ 'callback': 'ale_linters#yaml#swaglint#Handle',
\})
diff --git a/ale_linters/yaml/yamllint.vim b/ale_linters/yaml/yamllint.vim
index 9d2cc7c2..bedb7bf1 100644
--- a/ale_linters/yaml/yamllint.vim
+++ b/ale_linters/yaml/yamllint.vim
@@ -44,7 +44,7 @@ endfunction
call ale#linter#Define('yaml', {
\ 'name': 'yamllint',
-\ 'executable_callback': ale#VarFunc('yaml_yamllint_executable'),
-\ 'command_callback': 'ale_linters#yaml#yamllint#GetCommand',
+\ 'executable': {b -> ale#Var(b, 'yaml_yamllint_executable')},
+\ 'command': function('ale_linters#yaml#yamllint#GetCommand'),
\ 'callback': 'ale_linters#yaml#yamllint#Handle',
\})
diff --git a/ale_linters/yang/yang_lsp.vim b/ale_linters/yang/yang_lsp.vim
index 45776f98..81fcaa0e 100644
--- a/ale_linters/yang/yang_lsp.vim
+++ b/ale_linters/yang/yang_lsp.vim
@@ -9,7 +9,7 @@ endfunction
call ale#linter#Define('yang', {
\ 'name': 'yang_lsp',
\ 'lsp': 'stdio',
-\ 'executable_callback': ale#VarFunc('yang_lsp_executable'),
-\ 'project_root_callback': 'ale_linters#yang#yang_lsp#GetProjectRoot',
+\ 'executable': {b -> ale#Var(b, 'yang_lsp_executable')},
+\ 'project_root': function('ale_linters#yang#yang_lsp#GetProjectRoot'),
\ 'command': '%e',
\})
diff --git a/autoload/ale.vim b/autoload/ale.vim
index f6c23d72..a887834b 100644
--- a/autoload/ale.vim
+++ b/autoload/ale.vim
@@ -8,6 +8,7 @@ let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info')
let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning')
" Ignoring linters, for disabling some, or ignoring LSP diagnostics.
let g:ale_linters_ignore = get(g:, 'ale_linters_ignore', {})
+let g:ale_disable_lsp = get(g:, 'ale_disable_lsp', 0)
let s:lint_timer = -1
let s:getcmdwintype_exists = exists('*getcmdwintype')
@@ -42,6 +43,11 @@ function! ale#ShouldDoNothing(buffer) abort
return 1
endif
+ " Do nothing for diff buffers.
+ if getbufvar(a:buffer, '&diff')
+ return 1
+ endif
+
" Do nothing for blacklisted files.
if index(get(g:, 'ale_filetype_blacklist', []), l:filetype) >= 0
return 1
@@ -90,8 +96,9 @@ function! s:Lint(buffer, should_lint_file, timer_id) abort
" Apply ignore lists for linters only if needed.
let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
+ let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
let l:linters = !empty(l:ignore_config)
- \ ? ale#engine#ignore#Exclude(l:filetype, l:linters, l:ignore_config)
+ \ ? ale#engine#ignore#Exclude(l:filetype, l:linters, l:ignore_config, l:disable_lsp)
\ : l:linters
" Tell other sources that they can start checking the buffer now.
@@ -149,12 +156,19 @@ function! ale#Queue(delay, ...) abort
endif
endfunction
-let g:ale_has_override = get(g:, 'ale_has_override', {})
+let s:current_ale_version = [2, 4, 0]
-" Call has(), but check a global Dictionary so we can force flags on or off
-" for testing purposes.
+" A function used to check for ALE features in files outside of the project.
function! ale#Has(feature) abort
- return get(g:ale_has_override, a:feature, has(a:feature))
+ let l:match = matchlist(a:feature, '\c\v^ale-(\d+)\.(\d+)(\.(\d+))?$')
+
+ if !empty(l:match)
+ let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[4] + 0]
+
+ return ale#semver#GTE(s:current_ale_version, l:version)
+ endif
+
+ return 0
endfunction
" Given a buffer number and a variable name, look for that variable in the
@@ -169,11 +183,6 @@ function! ale#Var(buffer, variable_name) abort
return get(l:vars, l:full_name, g:[l:full_name])
endfunction
-" As above, but curry the arguments so only the buffer number is required.
-function! ale#VarFunc(variable_name) abort
- return {buf -> ale#Var(buf, a:variable_name)}
-endfunction
-
" Initialize a variable with a default value, if it isn't already set.
"
" Every variable name will be prefixed with 'ale_'.
diff --git a/autoload/ale/args.vim b/autoload/ale/args.vim
new file mode 100644
index 00000000..70afb2e5
--- /dev/null
+++ b/autoload/ale/args.vim
@@ -0,0 +1,43 @@
+" Author: w0rp <devw0rp@gmail.com>
+" Description: This module implements a function for parsing arguments for
+" commands.
+
+" Given a list of valid arguments like ['foo', 'bar'] and a string to parse,
+" parse the arguments from the string and return [parsed_args, remainder].
+"
+" Arguments must be prefixed in the string with a single minus (-), and a
+" double minus (--) denotes the end of arguments.
+function! ale#args#Parse(arg_list, string) abort
+ let l:parsed = {}
+ let l:end_of_args = 0
+ let l:word_list = split(a:string, ' ')
+ let l:index = 0
+
+ while l:index < len(l:word_list)
+ let l:word = l:word_list[l:index]
+
+ if l:word[:0] is# '-'
+ let l:index += 1
+
+ if l:word is# '--'
+ break
+ endif
+
+ let l:arg = l:word[1:]
+
+ if index(a:arg_list, l:arg) >= 0
+ let l:parsed[l:arg] = ''
+ else
+ throw 'Invalid argument: ' . l:word
+ endif
+ elseif l:word is# ''
+ let l:index += 1
+ else
+ break
+ endif
+ endwhile
+
+ let l:new_string = join(l:word_list[l:index :], ' ')
+
+ return [l:parsed, l:new_string]
+endfunction
diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim
index ed08ed09..ed90792d 100644
--- a/autoload/ale/assert.vim
+++ b/autoload/ale/assert.vim
@@ -1,7 +1,7 @@
-let s:chain_results = []
+let s:command_output = []
-function! ale#assert#WithChainResults(...) abort
- let s:chain_results = a:000
+function! ale#assert#GivenCommandOutput(...) abort
+ let s:command_output = a:000
endfunction
function! s:GetLinter() abort
@@ -19,6 +19,39 @@ function! s:GetLinter() abort
return l:filetype_linters[0]
endfunction
+function! s:FormatExe(command, executable) abort
+ return substitute(a:command, '%e', '\=ale#Escape(a:executable)', 'g')
+endfunction
+
+function! s:ProcessDeferredCommands(initial_result) abort
+ let l:result = a:initial_result
+ let l:command_index = 0
+ let l:command = []
+
+ while ale#command#IsDeferred(l:result)
+ call add(l:command, s:FormatExe(l:result.command, l:result.executable))
+
+ if get(g:, 'ale_run_synchronously_emulate_commands')
+ " Don't run commands, but simulate the results.
+ let l:Callback = g:ale_run_synchronously_callbacks[0]
+ let l:output = get(s:command_output, l:command_index, [])
+ call l:Callback(0, l:output)
+ unlet g:ale_run_synchronously_callbacks
+
+ let l:command_index += 1
+ else
+ " Run the commands in the shell, synchronously.
+ call ale#test#FlushJobs()
+ endif
+
+ let l:result = l:result.value
+ endwhile
+
+ call add(l:command, l:result)
+
+ return l:command
+endfunction
+
" Load the currently loaded linter for a test case, and check that the command
" matches the given string.
function! ale#assert#Linter(expected_executable, expected_command) abort
@@ -26,42 +59,25 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
let l:linter = s:GetLinter()
let l:executable = ale#linter#GetExecutable(l:buffer, l:linter)
- if has_key(l:linter, 'command_chain')
- let l:callbacks = map(copy(l:linter.command_chain), 'v:val.callback')
-
- " If the expected command is a string, just check the last one.
- if type(a:expected_command) is v:t_string
- if len(l:callbacks) is 1
- let l:command = call(l:callbacks[0], [l:buffer])
- else
- let l:input = get(s:chain_results, len(l:callbacks) - 2, [])
- let l:command = call(l:callbacks[-1], [l:buffer, l:input])
- endif
- else
- let l:command = []
- let l:chain_index = 0
-
- for l:Callback in l:callbacks
- if l:chain_index is 0
- call add(l:command, call(l:Callback, [l:buffer]))
- else
- let l:input = get(s:chain_results, l:chain_index - 1, [])
- call add(l:command, call(l:Callback, [l:buffer, l:input]))
- endif
-
- let l:chain_index += 1
- endfor
- endif
- else
- let l:command = ale#linter#GetCommand(l:buffer, l:linter)
+ while ale#command#IsDeferred(l:executable)
+ call ale#test#FlushJobs()
+ let l:executable = l:executable.value
+ endwhile
+
+ let l:command = s:ProcessDeferredCommands(
+ \ ale#linter#GetCommand(l:buffer, l:linter),
+ \)
+
+ if type(a:expected_command) isnot v:t_list
+ let l:command = l:command[-1]
endif
if type(l:command) is v:t_string
" Replace %e with the escaped executable, so tests keep passing after
" linters are changed to use %e.
- let l:command = substitute(l:command, '%e', '\=ale#Escape(l:executable)', 'g')
- else
- call map(l:command, 'substitute(v:val, ''%e'', ''\=ale#Escape(l:executable)'', ''g'')')
+ let l:command = s:FormatExe(l:command, l:executable)
+ elseif type(l:command) is v:t_list
+ call map(l:command, 's:FormatExe(v:val, l:executable)')
endif
AssertEqual
@@ -69,6 +85,17 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
\ [l:executable, l:command]
endfunction
+function! ale#assert#Fixer(expected_result) abort
+ let l:buffer = bufnr('')
+ let l:result = s:ProcessDeferredCommands(s:FixerFunction(l:buffer))
+
+ if type(a:expected_result) isnot v:t_list
+ let l:result = l:result[-1]
+ endif
+
+ AssertEqual a:expected_result, l:result
+endfunction
+
function! ale#assert#LinterNotExecuted() abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
@@ -104,7 +131,7 @@ endfunction
function! ale#assert#LSPProject(expected_root) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
- let l:root = ale#util#GetFunction(l:linter.project_root_callback)(l:buffer)
+ let l:root = ale#lsp_linter#FindProjectRoot(l:buffer, l:linter)
AssertEqual a:expected_root, l:root
endfunction
@@ -112,11 +139,27 @@ endfunction
function! ale#assert#LSPAddress(expected_address) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
- let l:address = ale#util#GetFunction(l:linter.address_callback)(l:buffer)
+ let l:address = ale#linter#GetAddress(l:buffer, l:linter)
AssertEqual a:expected_address, l:address
endfunction
+function! ale#assert#SetUpLinterTestCommands() abort
+ command! -nargs=+ GivenCommandOutput :call ale#assert#GivenCommandOutput(<args>)
+ command! -nargs=+ AssertLinter :call ale#assert#Linter(<args>)
+ command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted()
+ command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions(<args>)
+ command! -nargs=+ AssertLSPConfig :call ale#assert#LSPConfig(<args>)
+ command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage(<args>)
+ command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject(<args>)
+ command! -nargs=+ AssertLSPAddress :call ale#assert#LSPAddress(<args>)
+endfunction
+
+function! ale#assert#SetUpFixerTestCommands() abort
+ command! -nargs=+ GivenCommandOutput :call ale#assert#GivenCommandOutput(<args>)
+ command! -nargs=+ AssertFixer :call ale#assert#Fixer(<args>)
+endfunction
+
" A dummy function for making sure this module is loaded.
function! ale#assert#SetUpLinterTest(filetype, name) abort
" Set up a marker so ALE doesn't create real random temporary filenames.
@@ -129,6 +172,12 @@ function! ale#assert#SetUpLinterTest(filetype, name) abort
let l:prefix = 'ale_' . a:filetype . '_' . a:name
let b:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
+ Save g:ale_lsp_root
+ let g:ale_lsp_root = {}
+
+ Save b:ale_lsp_root
+ unlet! b:ale_lsp_root
+
Save g:ale_c_build_dir
unlet! g:ale_c_build_dir
@@ -151,22 +200,22 @@ function! ale#assert#SetUpLinterTest(filetype, name) abort
call ale#test#SetDirectory('/testplugin/test/command_callback')
endif
- command! -nargs=+ WithChainResults :call ale#assert#WithChainResults(<args>)
- command! -nargs=+ AssertLinter :call ale#assert#Linter(<args>)
- command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted()
- command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions(<args>)
- command! -nargs=+ AssertLSPConfig :call ale#assert#LSPConfig(<args>)
- command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage(<args>)
- command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject(<args>)
- command! -nargs=+ AssertLSPAddress :call ale#assert#LSPAddress(<args>)
+ call ale#assert#SetUpLinterTestCommands()
+
+ let g:ale_run_synchronously = 1
+ let g:ale_run_synchronously_emulate_commands = 1
endfunction
function! ale#assert#TearDownLinterTest() abort
unlet! g:ale_create_dummy_temporary_file
- let s:chain_results = []
-
- if exists(':WithChainResults')
- delcommand WithChainResults
+ unlet! g:ale_run_synchronously
+ unlet! g:ale_run_synchronously_callbacks
+ unlet! g:ale_run_synchronously_emulate_commands
+ unlet! g:ale_run_synchronously_command_results
+ let s:command_output = []
+
+ if exists(':GivenCommandOutput')
+ delcommand GivenCommandOutput
endif
if exists(':AssertLinter')
@@ -209,3 +258,62 @@ function! ale#assert#TearDownLinterTest() abort
call ale#semver#ResetVersionCache()
endif
endfunction
+
+function! ale#assert#SetUpFixerTest(filetype, name) abort
+ " Set up a marker so ALE doesn't create real random temporary filenames.
+ let g:ale_create_dummy_temporary_file = 1
+
+ let l:function_name = ale#fix#registry#GetFunc(a:name)
+ let s:FixerFunction = function(l:function_name)
+
+ let l:prefix = 'ale_' . a:filetype . '_' . a:name
+ let b:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
+
+ for l:key in filter(keys(g:), b:filter_expr)
+ execute 'Save g:' . l:key
+ unlet g:[l:key]
+ endfor
+
+ for l:key in filter(keys(b:), b:filter_expr)
+ unlet b:[l:key]
+ endfor
+
+ execute 'runtime autoload/ale/fixers/' . a:name . '.vim'
+
+ if !exists('g:dir')
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ endif
+
+ call ale#assert#SetUpFixerTestCommands()
+
+ let g:ale_run_synchronously = 1
+ let g:ale_run_synchronously_emulate_commands = 1
+endfunction
+
+function! ale#assert#TearDownFixerTest() abort
+ unlet! g:ale_create_dummy_temporary_file
+ unlet! g:ale_run_synchronously
+ unlet! g:ale_run_synchronously_callbacks
+ unlet! g:ale_run_synchronously_emulate_commands
+ unlet! g:ale_run_synchronously_command_results
+ let s:command_output = []
+ unlet! s:FixerFunction
+
+ if exists('g:dir')
+ call ale#test#RestoreDirectory()
+ endif
+
+ Restore
+
+ if exists('*ale#semver#ResetVersionCache')
+ call ale#semver#ResetVersionCache()
+ endif
+
+ if exists(':GivenCommandOutput')
+ delcommand GivenCommandOutput
+ endif
+
+ if exists(':AssertFixer')
+ delcommand AssertFixer
+ endif
+endfunction
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index ce5105b6..a9289e22 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -46,49 +46,76 @@ function! ale#c#FindProjectRoot(buffer) abort
return ''
endfunction
-function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
- let l:cflags_list = []
- let l:previous_options = []
+function! ale#c#AreSpecialCharsBalanced(option) abort
+ " Escape \"
+ let l:option_escaped = substitute(a:option, '\\"', '', 'g')
- let l:split_lines = split(a:cflag_line, '-')
- let l:option_index = 0
+ " Retain special chars only
+ let l:special_chars = substitute(l:option_escaped, '[^"''()`]', '', 'g')
+ let l:special_chars = split(l:special_chars, '\zs')
- while l:option_index < len(l:split_lines)
- let l:option = l:split_lines[l:option_index]
- let l:option_index = l:option_index + 1
- call add(l:previous_options, l:option)
- " Check if cflag contained a '-' and should not have been splitted
- let l:option_list = split(l:option, '\zs')
+ " Check if they are balanced
+ let l:stack = []
- if len(l:option_list) > 0 && l:option_list[-1] isnot# ' ' && l:option_index < len(l:split_lines)
- continue
- endif
+ for l:char in l:special_chars
+ if l:char is# ')'
+ if len(l:stack) == 0 || get(l:stack, -1) isnot# '('
+ return 0
+ endif
- let l:option = join(l:previous_options, '-')
- let l:previous_options = []
+ call remove(l:stack, -1)
+ elseif l:char is# '('
+ call add(l:stack, l:char)
+ else
+ if len(l:stack) > 0 && get(l:stack, -1) is# l:char
+ call remove(l:stack, -1)
+ else
+ call add(l:stack, l:char)
+ endif
+ endif
+ endfor
- let l:option = '-' . substitute(l:option, '^\s*\(.\{-}\)\s*$', '\1', '')
+ return len(l:stack) == 0
+endfunction
- " Fix relative paths if needed
- if stridx(l:option, '-I') >= 0 &&
- \ stridx(l:option, '-I' . s:sep) < 0
- let l:rel_path = join(split(l:option, '\zs')[2:], '')
- let l:rel_path = substitute(l:rel_path, '"', '', 'g')
- let l:rel_path = substitute(l:rel_path, '''', '', 'g')
- let l:option = ale#Escape('-I' . a:path_prefix .
- \ s:sep . l:rel_path)
- endif
+function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
+ let l:split_lines = split(a:cflag_line)
+ let l:option_index = 0
- " Parse the cflag
- if stridx(l:option, '-I') >= 0 ||
- \ stridx(l:option, '-D') >= 0
- if index(l:cflags_list, l:option) < 0
- call add(l:cflags_list, l:option)
+ while l:option_index < len(l:split_lines)
+ let l:next_option_index = l:option_index + 1
+
+ " Join space-separated option
+ while l:next_option_index < len(l:split_lines)
+ \&& stridx(l:split_lines[l:next_option_index], '-') != 0
+ let l:next_option_index += 1
+ endwhile
+
+ let l:option = join(l:split_lines[l:option_index : l:next_option_index-1], ' ')
+ call remove(l:split_lines, l:option_index, l:next_option_index-1)
+ call insert(l:split_lines, l:option, l:option_index)
+
+ " Ignore invalid or conflicting options
+ if stridx(l:option, '-') != 0
+ \|| stridx(l:option, '-o') == 0
+ \|| stridx(l:option, '-c') == 0
+ call remove(l:split_lines, l:option_index)
+ let l:option_index = l:option_index - 1
+ " Fix relative path
+ elseif stridx(l:option, '-I') == 0
+ if !(stridx(l:option, ':') == 2+1 || stridx(l:option, '/') == 2+0)
+ let l:option = '-I' . a:path_prefix . s:sep . l:option[2:]
+ call remove(l:split_lines, l:option_index)
+ call insert(l:split_lines, l:option, l:option_index)
endif
endif
+
+ let l:option_index = l:option_index + 1
endwhile
- return join(l:cflags_list, ' ')
+ call uniq(l:split_lines)
+
+ return join(l:split_lines, ' ')
endfunction
function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort
@@ -145,15 +172,17 @@ if !exists('s:compile_commands_cache')
let s:compile_commands_cache = {}
endif
-function! s:GetListFromCompileCommandsFile(compile_commands_file) abort
+function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort
+ let l:empty = [{}, {}]
+
if empty(a:compile_commands_file)
- return []
+ return l:empty
endif
let l:time = getftime(a:compile_commands_file)
if l:time < 0
- return []
+ return l:empty
endif
let l:key = a:compile_commands_file . ':' . l:time
@@ -162,32 +191,74 @@ function! s:GetListFromCompileCommandsFile(compile_commands_file) abort
return s:compile_commands_cache[l:key]
endif
- let l:data = []
- silent! let l:data = json_decode(join(readfile(a:compile_commands_file), ''))
+ let l:raw_data = []
+ silent! let l:raw_data = json_decode(join(readfile(a:compile_commands_file), ''))
+
+ let l:file_lookup = {}
+ let l:dir_lookup = {}
- if !empty(l:data)
- let s:compile_commands_cache[l:key] = l:data
+ for l:entry in l:raw_data
+ let l:basename = tolower(fnamemodify(l:entry.file, ':t'))
+ let l:file_lookup[l:basename] = get(l:file_lookup, l:basename, []) + [l:entry]
+
+ let l:dirbasename = tolower(fnamemodify(l:entry.directory, ':p:h:t'))
+ let l:dir_lookup[l:dirbasename] = get(l:dir_lookup, l:dirbasename, []) + [l:entry]
+ endfor
- return l:data
+ if !empty(l:file_lookup) && !empty(l:dir_lookup)
+ let l:result = [l:file_lookup, l:dir_lookup]
+ let s:compile_commands_cache[l:key] = l:result
+
+ return l:result
endif
- return []
+ return l:empty
endfunction
-function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort
+function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
" Search for an exact file match first.
- for l:item in a:json_list
- if bufnr(l:item.file) is a:buffer
- return ale#c#ParseCFlags(a:dir, l:item.command)
+ let l:basename = tolower(expand('#' . a:buffer . ':t'))
+ let l:file_list = get(a:file_lookup, l:basename, [])
+ " A source file matching the header filename.
+ let l:source_file = ''
+
+ if empty(l:file_list) && l:basename =~? '\.h$\|\.hpp$'
+ for l:suffix in ['.c', '.cpp']
+ let l:key = fnamemodify(l:basename, ':r') . l:suffix
+ let l:file_list = get(a:file_lookup, l:key, [])
+
+ if !empty(l:file_list)
+ let l:source_file = l:key
+ break
+ endif
+ endfor
+ endif
+
+ for l:item in l:file_list
+ " Load the flags for this file, or for a source file matching the
+ " header file.
+ if has_key(l:item, 'command')
+ \&& (
+ \ bufnr(l:item.file) is a:buffer
+ \ || (
+ \ !empty(l:source_file)
+ \ && l:item.file[-len(l:source_file):] is? l:source_file
+ \ )
+ \)
+ return ale#c#ParseCFlags(l:item.directory, l:item.command)
endif
endfor
" Look for any file in the same directory if we can't find an exact match.
let l:dir = ale#path#Simplify(expand('#' . a:buffer . ':p:h'))
- for l:item in a:json_list
+ let l:dirbasename = tolower(expand('#' . a:buffer . ':p:h:t'))
+ let l:dir_list = get(a:dir_lookup, l:dirbasename, [])
+
+ for l:item in l:dir_list
if ale#path#Simplify(fnamemodify(l:item.file, ':h')) is? l:dir
- return ale#c#ParseCFlags(a:dir, l:item.command)
+ \&& has_key(l:item, 'command')
+ return ale#c#ParseCFlags(l:item.directory, l:item.command)
endif
endfor
@@ -195,10 +266,11 @@ function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort
endfunction
function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort
- let l:dir = ale#path#Dirname(a:compile_commands_file)
- let l:json_list = s:GetListFromCompileCommandsFile(a:compile_commands_file)
+ let l:lookups = s:GetLookupFromCompileCommandsFile(a:compile_commands_file)
+ let l:file_lookup = l:lookups[0]
+ let l:dir_lookup = l:lookups[1]
- return ale#c#ParseCompileCommandsFlags(a:buffer, l:dir, l:json_list)
+ return ale#c#ParseCompileCommandsFlags(a:buffer, l:file_lookup, l:dir_lookup)
endfunction
function! ale#c#GetCFlags(buffer, output) abort
@@ -235,6 +307,20 @@ function! ale#c#GetMakeCommand(buffer) abort
return ''
endfunction
+function! ale#c#RunMakeCommand(buffer, Callback) abort
+ let l:command = ale#c#GetMakeCommand(a:buffer)
+
+ if empty(l:command)
+ return a:Callback(a:buffer, [])
+ endif
+
+ return ale#command#Run(
+ \ a:buffer,
+ \ l:command,
+ \ {b, output -> a:Callback(a:buffer, output)},
+ \)
+endfunction
+
" Given a buffer number, search for a project root, and output a List
" of directories to include based on some heuristics.
"
diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim
index 543c9953..1bbc4f4c 100644
--- a/autoload/ale/command.vim
+++ b/autoload/ale/command.vim
@@ -1,6 +1,117 @@
" Author: w0rp <devw0rp@gmail.com>
-" Description: Special command formatting for creating temporary files and
-" passing buffer filenames easily.
+" Description: Functions for formatting command strings, running commands, and
+" managing files during linting and fixing cycles.
+
+" This dictionary holds lists of files and directories to remove later.
+if !exists('s:buffer_data')
+ let s:buffer_data = {}
+endif
+
+" Used to get the data in tests.
+function! ale#command#GetData() abort
+ return deepcopy(s:buffer_data)
+endfunction
+
+function! ale#command#ClearData() abort
+ let s:buffer_data = {}
+endfunction
+
+function! ale#command#InitData(buffer) abort
+ if !has_key(s:buffer_data, a:buffer)
+ let s:buffer_data[a:buffer] = {
+ \ 'jobs': {},
+ \ 'file_list': [],
+ \ 'directory_list': [],
+ \}
+ endif
+endfunction
+
+function! ale#command#ManageFile(buffer, file) abort
+ call ale#command#InitData(a:buffer)
+ call add(s:buffer_data[a:buffer].file_list, a:file)
+endfunction
+
+function! ale#command#ManageDirectory(buffer, directory) abort
+ call ale#command#InitData(a:buffer)
+ call add(s:buffer_data[a:buffer].directory_list, a:directory)
+endfunction
+
+function! ale#command#CreateFile(buffer) abort
+ " This variable can be set to 1 in tests to stub this out.
+ if get(g:, 'ale_create_dummy_temporary_file')
+ return 'TEMP'
+ endif
+
+ let l:temporary_file = ale#util#Tempname()
+ call ale#command#ManageFile(a:buffer, l:temporary_file)
+
+ return l:temporary_file
+endfunction
+
+" Create a new temporary directory and manage it in one go.
+function! ale#command#CreateDirectory(buffer) abort
+ " This variable can be set to 1 in tests to stub this out.
+ if get(g:, 'ale_create_dummy_temporary_file')
+ return 'TEMP_DIR'
+ endif
+
+ let l:temporary_directory = ale#util#Tempname()
+ " Create the temporary directory for the file, unreadable by 'other'
+ " users.
+ call mkdir(l:temporary_directory, '', 0750)
+ call ale#command#ManageDirectory(a:buffer, l:temporary_directory)
+
+ return l:temporary_directory
+endfunction
+
+function! ale#command#RemoveManagedFiles(buffer) abort
+ let l:info = get(s:buffer_data, a:buffer, {})
+
+ if !empty(l:info) && empty(l:info.jobs)
+ " We can't delete anything in a sandbox, so wait until we escape from
+ " it to delete temporary files and directories.
+ if ale#util#InSandbox()
+ return
+ endif
+
+ " Delete files with a call akin to a plan `rm` command.
+ for l:filename in l:info.file_list
+ call delete(l:filename)
+ endfor
+
+ " Delete directories like `rm -rf`.
+ " Directories are handled differently from files, so paths that are
+ " intended to be single files can be set up for automatic deletion
+ " without accidentally deleting entire directories.
+ for l:directory in l:info.directory_list
+ call delete(l:directory, 'rf')
+ endfor
+
+ call remove(s:buffer_data, a:buffer)
+ endif
+endfunction
+
+function! ale#command#CreateTempFile(buffer, temporary_file, input) abort
+ if empty(a:temporary_file)
+ " There is no file, so we didn't create anything.
+ return 0
+ endif
+
+ " Use an existing list of lines of input if we have it, or get the lines
+ " from the file.
+ let l:lines = a:input isnot v:null ? a:input : getbufline(a:buffer, 1, '$')
+
+ let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
+ " Create the temporary directory for the file, unreadable by 'other'
+ " users.
+ call mkdir(l:temporary_directory, '', 0750)
+ " Automatically delete the directory later.
+ call ale#command#ManageDirectory(a:buffer, l:temporary_directory)
+ " Write the buffer out to a file.
+ call ale#util#Writefile(a:buffer, l:lines, a:temporary_file)
+
+ return 1
+endfunction
function! s:TemporaryFilename(buffer) abort
let l:filename = fnamemodify(bufname(a:buffer), ':t')
@@ -16,11 +127,17 @@ function! s:TemporaryFilename(buffer) abort
return ale#util#Tempname() . (has('win32') ? '\' : '/') . l:filename
endfunction
+" Given part of a command, replace any % with %%, so that no characters in
+" the string will be replaced with filenames, etc.
+function! ale#command#EscapeCommandPart(command_part) abort
+ return substitute(a:command_part, '%', '%%', 'g')
+endfunction
+
" Given a command string, replace every...
" %s -> with the current filename
" %t -> with the name of an unused file in a temporary directory
" %% -> with a literal %
-function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed) abort
+function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed, input) abort
let l:temporary_file = ''
let l:command = a:command
@@ -40,7 +157,7 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne
let l:command = substitute(l:command, '%s', '\=ale#Escape(l:filename)', 'g')
endif
- if l:command =~# '%t'
+ if a:input isnot v:false && l:command =~# '%t'
" Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function.
let l:temporary_file = s:TemporaryFilename(a:buffer)
@@ -58,5 +175,205 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne
let l:command = l:command . ' < ' . ale#Escape(l:temporary_file)
endif
- return [l:temporary_file, l:command]
+ let l:file_created = ale#command#CreateTempFile(
+ \ a:buffer,
+ \ l:temporary_file,
+ \ a:input,
+ \)
+
+ return [l:temporary_file, l:command, l:file_created]
+endfunction
+
+function! ale#command#StopJobs(buffer, job_type) abort
+ let l:info = get(s:buffer_data, a:buffer, {})
+
+ if !empty(l:info)
+ let l:new_map = {}
+
+ for [l:job_id, l:job_type] in items(l:info.jobs)
+ let l:job_id = str2nr(l:job_id)
+
+ if a:job_type is# 'all' || a:job_type is# l:job_type
+ call ale#job#Stop(l:job_id)
+ else
+ let l:new_map[l:job_id] = l:job_type
+ endif
+ endfor
+
+ let l:info.jobs = l:new_map
+ endif
+endfunction
+
+function! s:GatherOutput(line_list, job_id, line) abort
+ call add(a:line_list, a:line)
+endfunction
+
+function! s:ExitCallback(buffer, line_list, Callback, data) abort
+ if !has_key(s:buffer_data, a:buffer)
+ return
+ endif
+
+ let l:jobs = s:buffer_data[a:buffer].jobs
+
+ if !has_key(l:jobs, a:data.job_id)
+ return
+ endif
+
+ let l:job_type = remove(l:jobs, a:data.job_id)
+
+ if g:ale_history_enabled
+ call ale#history#SetExitCode(a:buffer, a:data.job_id, a:data.exit_code)
+
+ " Log the output of the command for ALEInfo if we should.
+ if g:ale_history_log_output && a:data.log_output is 1
+ call ale#history#RememberOutput(
+ \ a:buffer,
+ \ a:data.job_id,
+ \ a:line_list[:]
+ \)
+ endif
+ endif
+
+ " If the callback starts any new jobs, use the same job type for them.
+ call setbufvar(a:buffer, 'ale_job_type', l:job_type)
+ let l:value = a:Callback(a:buffer, a:line_list, {
+ \ 'exit_code': a:data.exit_code,
+ \ 'temporary_file': a:data.temporary_file,
+ \})
+
+ let l:result = a:data.result
+ let l:result.value = l:value
+
+ if get(l:result, 'result_callback', v:null) isnot v:null
+ call call(l:result.result_callback, [l:value])
+ endif
+endfunction
+
+function! ale#command#Run(buffer, command, Callback, ...) abort
+ let l:options = get(a:000, 0, {})
+
+ if len(a:000) > 1
+ throw 'Too many arguments!'
+ endif
+
+ let l:output_stream = get(l:options, 'output_stream', 'stdout')
+ let l:line_list = []
+
+ let [l:temporary_file, l:command, l:file_created] = ale#command#FormatCommand(
+ \ a:buffer,
+ \ get(l:options, 'executable', ''),
+ \ a:command,
+ \ get(l:options, 'read_buffer', 0),
+ \ get(l:options, 'input', v:null),
+ \)
+ let l:command = ale#job#PrepareCommand(a:buffer, l:command)
+ let l:job_options = {
+ \ 'exit_cb': {job_id, exit_code -> s:ExitCallback(
+ \ a:buffer,
+ \ l:line_list,
+ \ a:Callback,
+ \ {
+ \ 'job_id': job_id,
+ \ 'exit_code': exit_code,
+ \ 'temporary_file': l:temporary_file,
+ \ 'log_output': get(l:options, 'log_output', 1),
+ \ 'result': l:result,
+ \ }
+ \ )},
+ \ 'mode': 'nl',
+ \}
+
+ if l:output_stream is# 'stdout'
+ let l:job_options.out_cb = function('s:GatherOutput', [l:line_list])
+ elseif l:output_stream is# 'stderr'
+ let l:job_options.err_cb = function('s:GatherOutput', [l:line_list])
+ elseif l:output_stream is# 'both'
+ let l:job_options.out_cb = function('s:GatherOutput', [l:line_list])
+ let l:job_options.err_cb = function('s:GatherOutput', [l:line_list])
+ endif
+
+ let l:status = 'failed'
+
+ if get(g:, 'ale_run_synchronously') == 1
+ if get(g:, 'ale_emulate_job_failure') == 1
+ let l:job_id = 0
+ else
+ " Generate a fake job ID for tests.
+ let s:fake_job_id = get(s:, 'fake_job_id', 0) + 1
+ let l:job_id = s:fake_job_id
+ endif
+ elseif has('win32')
+ let l:job_id = ale#job#StartWithCmd(l:command, l:job_options)
+ else
+ let l:job_id = ale#job#Start(l:command, l:job_options)
+ endif
+
+ if l:job_id
+ let l:status = 'started'
+ let l:job_type = getbufvar(a:buffer, 'ale_job_type', 'all')
+
+ call ale#command#InitData(a:buffer)
+ let s:buffer_data[a:buffer].jobs[l:job_id] = l:job_type
+ endif
+
+ if g:ale_history_enabled
+ call ale#history#Add(a:buffer, l:status, l:job_id, l:command)
+ endif
+
+ if !l:job_id
+ return 0
+ endif
+
+ " We'll return this Dictionary. A `result_callback` can be assigned to it
+ " later for capturing the result of a:Callback.
+ "
+ " The `_deferred_job_id` is used for both checking the type of object, and
+ " for checking the job ID and status.
+ "
+ " The original command here is used in tests.
+ let l:result = {
+ \ '_deferred_job_id': l:job_id,
+ \ 'executable': get(l:options, 'executable', ''),
+ \ 'command': a:command,
+ \}
+
+ if get(g:, 'ale_run_synchronously') == 1 && l:job_id
+ if !exists('g:ale_run_synchronously_callbacks')
+ let g:ale_run_synchronously_callbacks = []
+ endif
+
+ if get(g:, 'ale_run_synchronously_emulate_commands', 0)
+ call add(
+ \ g:ale_run_synchronously_callbacks,
+ \ {exit_code, output -> [
+ \ extend(l:line_list, output),
+ \ l:job_options.exit_cb(l:job_id, exit_code),
+ \ ]}
+ \)
+ else
+ " Run a command synchronously if this test option is set.
+ call extend(l:line_list, systemlist(
+ \ type(l:command) is v:t_list
+ \ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
+ \ : l:command
+ \))
+
+ " Don't capture output when the callbacks aren't set.
+ if !has_key(l:job_options, 'out_cb')
+ \&& !has_key(l:job_options, 'err_cb')
+ let l:line_list = []
+ endif
+
+ call add(
+ \ g:ale_run_synchronously_callbacks,
+ \ {-> l:job_options.exit_cb(l:job_id, v:shell_error)}
+ \)
+ endif
+ endif
+
+ return l:result
+endfunction
+
+function! ale#command#IsDeferred(value) abort
+ return type(a:value) is v:t_dict && has_key(a:value, '_deferred_job_id')
endfunction
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index 9dd913f5..1d42c489 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -39,6 +39,9 @@ let s:LSP_COMPLETION_COLOR_KIND = 16
let s:LSP_COMPLETION_FILE_KIND = 17
let s:LSP_COMPLETION_REFERENCE_KIND = 18
+let s:LSP_INSERT_TEXT_FORMAT_PLAIN = 1
+let s:LSP_INSERT_TEXT_FORMAT_SNIPPET = 2
+
let s:lisp_regex = '\v[a-zA-Z_\-][a-zA-Z_\-0-9]*$'
" Regular expressions for checking the characters in the line before where
@@ -89,6 +92,10 @@ function! ale#completion#GetPrefix(filetype, line, column) abort
endfunction
function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
+ if empty(a:prefix)
+ return ''
+ endif
+
let l:char_list = s:GetFiletypeValue(s:trigger_character_map, a:filetype)
if index(l:char_list, a:prefix) >= 0
@@ -100,33 +107,38 @@ endfunction
function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words')
- let l:triggers = s:GetFiletypeValue(s:trigger_character_map, a:filetype)
- " For completing...
- " foo.
- " ^
- " We need to include all of the given suggestions.
- if index(l:triggers, a:prefix) >= 0
+ if empty(a:prefix)
let l:filtered_suggestions = a:suggestions
else
- let l:filtered_suggestions = []
-
- " 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: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) is v:t_string ? l:item : l:item.word
-
- " Add suggestions if the suggestion starts with a case-insensitive
- " match for the prefix.
- if l:word[: len(a:prefix) - 1] is? a:prefix
- call add(l:filtered_suggestions, l:item)
- endif
- endfor
+ let l:triggers = s:GetFiletypeValue(s:trigger_character_map, a:filetype)
+
+ " For completing...
+ " foo.
+ " ^
+ " We need to include all of the given suggestions.
+ if index(l:triggers, a:prefix) >= 0 || empty(a:prefix)
+ let l:filtered_suggestions = a:suggestions
+ else
+ let l:filtered_suggestions = []
+
+ " 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: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) is v:t_string ? l:item : l:item.word
+
+ " Add suggestions if the suggestion starts with a
+ " case-insensitive match for the prefix.
+ if l:word[: len(a:prefix) - 1] is? a:prefix
+ call add(l:filtered_suggestions, l:item)
+ endif
+ endfor
+ endif
endif
if !empty(l:excluded_words)
@@ -147,23 +159,29 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
endfunction
function! s:ReplaceCompletionOptions() abort
- " Remember the old omnifunc value, if there is one.
- " If we don't store an old one, we'll just never reset the option.
- " This will stop some random exceptions from appearing.
- if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
- let b:ale_old_omnifunc = &l:omnifunc
- endif
-
- let &l:omnifunc = 'ale#completion#OmniFunc'
+ let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
+
+ if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
+ " Remember the old omnifunc value, if there is one.
+ " If we don't store an old one, we'll just never reset the option.
+ " This will stop some random exceptions from appearing.
+ if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
+ let b:ale_old_omnifunc = &l:omnifunc
+ endif
- if !exists('b:ale_old_completopt')
- let b:ale_old_completopt = &l:completeopt
+ let &l:omnifunc = 'ale#completion#OmniFunc'
endif
- if &l:completeopt =~# 'preview'
- let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
- else
- let &l:completeopt = 'menu,menuone,noselect,noinsert'
+ if l:source is# 'ale-automatic'
+ if !exists('b:ale_old_completeopt')
+ let b:ale_old_completeopt = &l:completeopt
+ endif
+
+ if &l:completeopt =~# 'preview'
+ let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
+ else
+ let &l:completeopt = 'menu,menuone,noselect,noinsert'
+ endif
endif
endfunction
@@ -177,37 +195,55 @@ function! ale#completion#RestoreCompletionOptions() abort
unlet b:ale_old_omnifunc
endif
- if exists('b:ale_old_completopt')
- let &l:completeopt = b:ale_old_completopt
- unlet b:ale_old_completopt
+ if exists('b:ale_old_completeopt')
+ let &l:completeopt = b:ale_old_completeopt
+ unlet b:ale_old_completeopt
endif
endfunction
-function! ale#completion#OmniFunc(findstart, base) abort
- if a:findstart
- let l:line = b:ale_completion_info.line
- let l:column = b:ale_completion_info.column
- let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
- let l:up_to_column = getline(l:line)[: l:column - 2]
- let l:match = matchstr(l:up_to_column, l:regex)
+function! ale#completion#GetCompletionPosition() abort
+ if !exists('b:ale_completion_info')
+ return 0
+ endif
- return l:column - len(l:match) - 1
- else
- " Parse a new response if there is one.
- if exists('b:ale_completion_response')
- \&& exists('b:ale_completion_parser')
- let l:response = b:ale_completion_response
- let l:parser = b:ale_completion_parser
+ let l:line = b:ale_completion_info.line
+ let l:column = b:ale_completion_info.column
+ let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
+ let l:up_to_column = getline(l:line)[: l:column - 2]
+ let l:match = matchstr(l:up_to_column, l:regex)
- unlet b:ale_completion_response
- unlet b:ale_completion_parser
+ return l:column - len(l:match) - 1
+endfunction
- let b:ale_completion_result = function(l:parser)(l:response)
- endif
+function! ale#completion#GetCompletionResult() abort
+ " Parse a new response if there is one.
+ if exists('b:ale_completion_response')
+ \&& exists('b:ale_completion_parser')
+ let l:response = b:ale_completion_response
+ let l:parser = b:ale_completion_parser
+
+ unlet b:ale_completion_response
+ unlet b:ale_completion_parser
+
+ let b:ale_completion_result = function(l:parser)(l:response)
+ endif
+
+ if exists('b:ale_completion_result')
+ return b:ale_completion_result
+ endif
+
+ return v:null
+endfunction
+
+function! ale#completion#OmniFunc(findstart, base) abort
+ if a:findstart
+ return ale#completion#GetCompletionPosition()
+ else
+ let l:result = ale#completion#GetCompletionResult()
call s:ReplaceCompletionOptions()
- return get(b:, 'ale_completion_result', [])
+ return l:result isnot v:null ? l:result : []
endif
endfunction
@@ -223,17 +259,27 @@ function! ale#completion#Show(response, completion_parser) abort
" Replace completion options shortly before opening the menu.
call s:ReplaceCompletionOptions()
- call timer_start(0, {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")})
+ let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
+
+ if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
+ call timer_start(
+ \ 0,
+ \ {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")}
+ \)
+ endif
endfunction
function! s:CompletionStillValid(request_id) abort
- let [l:line, l:column] = getcurpos()[1:2]
+ let [l:line, l:column] = getpos('.')[1:2]
return ale#util#Mode() is# 'i'
\&& has_key(b:, 'ale_completion_info')
\&& b:ale_completion_info.request_id == a:request_id
\&& b:ale_completion_info.line == l:line
- \&& b:ale_completion_info.column == l:column
+ \&& (
+ \ b:ale_completion_info.column == l:column
+ \ || b:ale_completion_info.source is# 'deoplete'
+ \)
endfunction
function! ale#completion#ParseTSServerCompletions(response) abort
@@ -307,7 +353,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
endfunction
function! ale#completion#NullFilter(buffer, item) abort
- return 1
+ return 1
endfunction
function! ale#completion#ParseLSPCompletions(response) abort
@@ -337,7 +383,14 @@ function! ale#completion#ParseLSPCompletions(response) abort
continue
endif
- let l:word = matchstr(l:item.label, '\v^[^(]+')
+ if get(l:item, 'insertTextFormat') is s:LSP_INSERT_TEXT_FORMAT_PLAIN
+ \&& type(get(l:item, 'textEdit')) is v:t_dict
+ let l:text = l:item.textEdit.newText
+ else
+ let l:text = l:item.label
+ endif
+
+ let l:word = matchstr(l:text, '\v^[^(]+')
if empty(l:word)
continue
@@ -376,10 +429,10 @@ function! ale#completion#ParseLSPCompletions(response) abort
endfor
if has_key(l:info, 'prefix')
- return ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix)
+ let l:results = ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix)
endif
- return l:results
+ return l:results[: g:ale_completion_max_suggestions - 1]
endfunction
function! ale#completion#HandleTSServerResponse(conn_id, response) abort
@@ -437,10 +490,15 @@ function! ale#completion#HandleLSPResponse(conn_id, response) abort
\)
endfunction
-function! s:OnReady(linter, lsp_details, ...) abort
- let l:buffer = a:lsp_details.buffer
+function! s:OnReady(linter, lsp_details) abort
let l:id = a:lsp_details.connection_id
+ if !ale#lsp#HasCapability(l:id, 'completion')
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
" If we have sent a completion request already, don't send another.
if b:ale_completion_info.request_id
return
@@ -472,7 +530,7 @@ function! s:OnReady(linter, lsp_details, ...) abort
\ min([
\ b:ale_completion_info.line_length,
\ b:ale_completion_info.column,
- \ ]),
+ \ ]) + 1,
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
\)
endif
@@ -489,31 +547,14 @@ function! s:OnReady(linter, lsp_details, ...) abort
endif
endfunction
-function! s:GetLSPCompletions(linter) abort
- let l:buffer = bufnr('')
- let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
-
- if empty(l:lsp_details)
- return 0
- endif
-
- let l:id = l:lsp_details.connection_id
-
- let l:OnReady = function('s:OnReady', [a:linter, l:lsp_details])
-
- call ale#lsp#WaitForCapability(l:id, 'completion', l:OnReady)
-endfunction
-
-function! ale#completion#GetCompletions() abort
- if !g:ale_completion_enabled
- return
- endif
-
- let [l:line, l:column] = getcurpos()[1:2]
+" This function can be used to manually trigger autocomplete, even when
+" g:ale_completion_enabled is set to false
+function! ale#completion#GetCompletions(source) abort
+ let [l:line, l:column] = getpos('.')[1:2]
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
- if empty(l:prefix)
+ if a:source is# 'ale-automatic' && empty(l:prefix)
return
endif
@@ -526,24 +567,33 @@ function! ale#completion#GetCompletions() abort
\ 'prefix': l:prefix,
\ 'conn_id': 0,
\ 'request_id': 0,
+ \ 'source': a:source,
\}
+ unlet! b:ale_completion_result
+
+ let l:buffer = bufnr('')
+ let l:Callback = function('s:OnReady')
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
- call s:GetLSPCompletions(l:linter)
+ call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback)
endif
endfor
endfunction
function! s:TimerHandler(...) abort
+ if !g:ale_completion_enabled
+ return
+ endif
+
let s:timer_id = -1
- let [l:line, l:column] = getcurpos()[1:2]
+ let [l:line, l:column] = getpos('.')[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] && ale#util#Mode() is# 'i'
- call ale#completion#GetCompletions()
+ call ale#completion#GetCompletions('ale-automatic')
endif
endfunction
@@ -561,7 +611,7 @@ function! ale#completion#Queue() abort
return
endif
- let s:timer_pos = getcurpos()[1:2]
+ let s:timer_pos = getpos('.')[1:2]
if s:timer_pos == s:last_done_pos
" Do not ask for completions if the cursor rests on the position we
@@ -585,7 +635,7 @@ function! ale#completion#Done() abort
call ale#completion#RestoreCompletionOptions()
- let s:last_done_pos = getcurpos()[1:2]
+ let s:last_done_pos = getpos('.')[1:2]
endfunction
function! s:Setup(enabled) abort
diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim
index 6672c349..8c331c5c 100644
--- a/autoload/ale/cursor.vim
+++ b/autoload/ale/cursor.vim
@@ -22,10 +22,10 @@ function! ale#cursor#TruncatedEcho(original_message) abort
let l:shortmess_options = &l:shortmess
try
- let l:cursor_position = getcurpos()
+ let l:cursor_position = getpos('.')
" The message is truncated and saved to the history.
- setlocal shortmess+=T
+ silent! setlocal shortmess+=T
try
exec "norm! :echomsg l:message\n"
@@ -44,7 +44,7 @@ function! ale#cursor#TruncatedEcho(original_message) abort
" Reset the cursor position if we moved off the end of the line.
" Using :norm and :echomsg can move the cursor off the end of the
" line.
- if l:cursor_position != getcurpos()
+ if l:cursor_position != getpos('.')
call setpos('.', l:cursor_position)
endif
finally
@@ -114,7 +114,7 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort
call s:StopCursorTimer()
- let l:pos = getcurpos()[0:2]
+ let l:pos = getpos('.')[0:2]
" Check the current buffer, line, and column number against the last
" recorded position. If the position has actually changed, *then*
diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim
index 3aed38fe..e4bf5e7e 100644
--- a/autoload/ale/debugging.vim
+++ b/autoload/ale/debugging.vim
@@ -31,6 +31,7 @@ let s:global_variable_list = [
\ 'ale_list_vertical',
\ 'ale_list_window_size',
\ 'ale_loclist_msg_format',
+\ 'ale_lsp_root',
\ 'ale_max_buffer_history_size',
\ 'ale_max_signs',
\ 'ale_maximum_file_size',
diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim
index 984a4f9d..3915cac1 100644
--- a/autoload/ale/definition.vim
+++ b/autoload/ale/definition.vim
@@ -3,6 +3,9 @@
let s:go_to_definition_map = {}
+" Enable automatic updates of the tagstack
+let g:ale_update_tagstack = get(g:, 'ale_update_tagstack', 1)
+
" Used to get the definition map in tests.
function! ale#definition#GetMap() abort
return deepcopy(s:go_to_definition_map)
@@ -17,6 +20,20 @@ function! ale#definition#ClearLSPData() abort
let s:go_to_definition_map = {}
endfunction
+function! ale#definition#UpdateTagStack() abort
+ let l:should_update_tagstack = exists('*gettagstack') && exists('*settagstack') && g:ale_update_tagstack
+
+ if l:should_update_tagstack
+ " Grab the old location (to jump back to) and the word under the
+ " cursor (as a label for the tagstack)
+ let l:old_location = [bufnr('%'), line('.'), col('.'), 0]
+ let l:tagname = expand('<cword>')
+ let l:winid = win_getid()
+ call settagstack(l:winid, {'items': [{'from': l:old_location, 'tagname': l:tagname}]}, 'a')
+ call settagstack(l:winid, {'curidx': len(gettagstack(l:winid)['items']) + 1})
+ endif
+endfunction
+
function! ale#definition#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') is# 'definition'
\&& has_key(s:go_to_definition_map, a:response.request_seq)
@@ -27,6 +44,7 @@ function! ale#definition#HandleTSServerResponse(conn_id, response) abort
let l:line = a:response.body[0].start.line
let l:column = a:response.body[0].start.offset
+ call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)
endif
endif
@@ -51,16 +69,22 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
let l:line = l:item.range.start.line + 1
let l:column = l:item.range.start.character + 1
+ call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)
break
endfor
endif
endfunction
-function! s:OnReady(linter, lsp_details, line, column, options, ...) abort
- let l:buffer = a:lsp_details.buffer
+function! s:OnReady(line, column, options, capability, linter, lsp_details) abort
let l:id = a:lsp_details.connection_id
+ if !ale#lsp#HasCapability(l:id, a:capability)
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#definition#HandleTSServerResponse')
\ : function('ale#definition#HandleLSPResponse')
@@ -80,40 +104,53 @@ function! s:OnReady(linter, lsp_details, line, column, options, ...) abort
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
- let l:message = ale#lsp#message#Definition(l:buffer, a:line, a:column)
+ if a:capability is# 'definition'
+ let l:message = ale#lsp#message#Definition(l:buffer, a:line, a:column)
+ elseif a:capability is# 'typeDefinition'
+ let l:message = ale#lsp#message#TypeDefinition(l:buffer, a:line, a:column)
+ else
+ " XXX: log here?
+ return
+ endif
endif
let l:request_id = ale#lsp#Send(l:id, l:message)
let s:go_to_definition_map[l:request_id] = {
- \ 'open_in_tab': get(a:options, 'open_in_tab', 0),
+ \ 'open_in': get(a:options, 'open_in', 'current-buffer'),
\}
endfunction
-function! s:GoToLSPDefinition(linter, options) abort
+function! s:GoToLSPDefinition(linter, options, capability) abort
let l:buffer = bufnr('')
- let [l:line, l:column] = getcurpos()[1:2]
- let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
-
- if a:linter.lsp isnot# 'tsserver'
- let l:column = min([l:column, len(getline(l:line))])
- endif
-
- if empty(l:lsp_details)
- return 0
- endif
-
- let l:id = l:lsp_details.connection_id
-
- call ale#lsp#WaitForCapability(l:id, 'definition', function('s:OnReady', [
- \ a:linter, l:lsp_details, l:line, l:column, a:options
- \]))
+ let [l:line, l:column] = getpos('.')[1:2]
+ let l:column = min([l:column, len(getline(l:line))])
+
+ let l:Callback = function(
+ \ 's:OnReady',
+ \ [l:line, l:column, a:options, a:capability]
+ \)
+ call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
endfunction
function! ale#definition#GoTo(options) abort
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
- call s:GoToLSPDefinition(l:linter, a:options)
+ call s:GoToLSPDefinition(l:linter, a:options, 'definition')
+ endif
+ endfor
+endfunction
+
+function! ale#definition#GoToType(options) abort
+ for l:linter in ale#linter#Get(&filetype)
+ if !empty(l:linter.lsp)
+ " TODO: handle typeDefinition for tsserver if supported by the
+ " protocol
+ if l:linter.lsp is# 'tsserver'
+ continue
+ endif
+
+ call s:GoToLSPDefinition(l:linter, a:options, 'typeDefinition')
endif
endfor
endfunction
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index b44be73c..7db808d6 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -5,20 +5,10 @@
" Remapping of linter problems.
let g:ale_type_map = get(g:, 'ale_type_map', {})
-" Stores information for each job including:
-"
-" linter: The linter dictionary for the job.
-" buffer: The buffer number for the job.
-" output: The array of lines for the output of the job.
-if !has_key(s:, 'job_info_map')
- let s:job_info_map = {}
-endif
-
if !has_key(s:, 'executable_cache_map')
let s:executable_cache_map = {}
endif
-
function! ale#engine#CleanupEveryBuffer() abort
for l:key in keys(g:ale_buffer_info)
" The key could be a filename or a buffer number, so try and
@@ -34,6 +24,25 @@ function! ale#engine#CleanupEveryBuffer() abort
endfor
endfunction
+function! ale#engine#MarkLinterActive(info, linter) abort
+ let l:found = 0
+
+ for l:other_linter in a:info.active_linter_list
+ if l:other_linter.name is# a:linter.name
+ let l:found = 1
+ break
+ endif
+ endfor
+
+ if !l:found
+ call add(a:info.active_linter_list, a:linter)
+ endif
+endfunction
+
+function! ale#engine#MarkLinterInactive(info, linter_name) abort
+ call filter(a:info.active_linter_list, 'v:val.name isnot# a:linter_name')
+endfunction
+
function! ale#engine#ResetExecutableCache() abort
let s:executable_cache_map = {}
endfunction
@@ -71,18 +80,12 @@ endfunction
function! ale#engine#InitBufferInfo(buffer) abort
if !has_key(g:ale_buffer_info, a:buffer)
- " job_list will hold the list of job IDs
" active_linter_list will hold the list of active linter names
" 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
let g:ale_buffer_info[a:buffer] = {
- \ 'job_list': [],
\ 'active_linter_list': [],
\ 'active_other_sources_list': [],
\ 'loclist': [],
- \ 'temporary_file_list': [],
- \ 'temporary_directory_list': [],
\}
return 1
@@ -104,79 +107,37 @@ endfunction
" Register a temporary file to be managed with the ALE engine for
" a current job run.
function! ale#engine#ManageFile(buffer, filename) abort
- call ale#engine#InitBufferInfo(a:buffer)
- call add(g:ale_buffer_info[a:buffer].temporary_file_list, a:filename)
+ if !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom ''ale#engine#ManageFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
+ endif
+
+ call ale#command#ManageFile(a:buffer, a:filename)
endfunction
" Same as the above, but manage an entire directory.
function! ale#engine#ManageDirectory(buffer, directory) abort
- call ale#engine#InitBufferInfo(a:buffer)
- call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
+ if !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom ''ale#engine#ManageDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
+ endif
+
+ call ale#command#ManageDirectory(a:buffer, a:directory)
endfunction
function! ale#engine#CreateFile(buffer) abort
- " This variable can be set to 1 in tests to stub this out.
- if get(g:, 'ale_create_dummy_temporary_file')
- return 'TEMP'
+ if !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom ''ale#engine#CreateFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
- let l:temporary_file = ale#util#Tempname()
- call ale#engine#ManageFile(a:buffer, l:temporary_file)
-
- return l:temporary_file
+ return ale#command#CreateFile(a:buffer)
endfunction
" Create a new temporary directory and manage it in one go.
function! ale#engine#CreateDirectory(buffer) abort
- " This variable can be set to 1 in tests to stub this out.
- if get(g:, 'ale_create_dummy_temporary_file')
- return 'TEMP_DIR'
- endif
-
- let l:temporary_directory = ale#util#Tempname()
- " Create the temporary directory for the file, unreadable by 'other'
- " users.
- call mkdir(l:temporary_directory, '', 0750)
- call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
-
- return l:temporary_directory
-endfunction
-
-function! ale#engine#RemoveManagedFiles(buffer) abort
- 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.
- if ale#util#InSandbox()
- return
+ if !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom ''ale#engine#CreateDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
- " Delete files with a call akin to a plan `rm` command.
- if has_key(l:info, 'temporary_file_list')
- for l:filename in l:info.temporary_file_list
- call delete(l:filename)
- endfor
-
- let l:info.temporary_file_list = []
- endif
-
- " Delete directories like `rm -rf`.
- " Directories are handled differently from files, so paths that are
- " intended to be single files can be set up for automatic deletion without
- " accidentally deleting entire directories.
- if has_key(l:info, 'temporary_directory_list')
- for l:directory in l:info.temporary_directory_list
- call delete(l:directory, 'rf')
- endfor
-
- let l:info.temporary_directory_list = []
- endif
-endfunction
-
-function! s:GatherOutput(job_id, line) abort
- if has_key(s:job_info_map, a:job_id)
- call add(s:job_info_map[a:job_id].output, a:line)
- endif
+ return ale#command#CreateDirectory(a:buffer)
endfunction
function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort
@@ -189,7 +150,7 @@ function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_sour
if !a:from_other_source
" Remove this linter from the list of active linters.
" This may have already been done when the job exits.
- call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name')
+ call filter(l:info.active_linter_list, 'v:val.name isnot# a:linter_name')
endif
" Make some adjustments to the loclists to fix common problems, and also
@@ -222,27 +183,19 @@ function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_sour
call ale#engine#SetResults(a:buffer, l:info.loclist)
endfunction
-function! s:HandleExit(job_id, exit_code) abort
- if !has_key(s:job_info_map, a:job_id)
+function! s:HandleExit(job_info, buffer, output, data) abort
+ let l:buffer_info = get(g:ale_buffer_info, a:buffer)
+
+ if empty(l:buffer_info)
return
endif
- let l:job_info = s:job_info_map[a:job_id]
- let l:linter = l:job_info.linter
- let l:output = l:job_info.output
- let l:buffer = l:job_info.buffer
- let l:executable = l:job_info.executable
- let l:next_chain_index = l:job_info.next_chain_index
-
- if g:ale_history_enabled
- call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code)
- endif
+ let l:linter = a:job_info.linter
+ let l:executable = a:job_info.executable
+ let l:next_chain_index = a:job_info.next_chain_index
" 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 isnot# a:job_id')
- call filter(g:ale_buffer_info[l:buffer].active_linter_list, 'v:val isnot# l:linter.name')
+ call ale#engine#MarkLinterInactive(l:buffer_info, l:linter.name)
" Stop here if we land in the handle for a job completing if we're in
" a sandbox.
@@ -250,29 +203,32 @@ function! s:HandleExit(job_id, exit_code) abort
return
endif
- if has('nvim') && !empty(l:output) && empty(l:output[-1])
- call remove(l:output, -1)
+ if has('nvim') && !empty(a:output) && empty(a:output[-1])
+ call remove(a:output, -1)
endif
if l:next_chain_index < len(get(l:linter, 'command_chain', []))
- call s:InvokeChain(l:buffer, l:executable, l:linter, l:next_chain_index, l:output)
+ let [l:command, l:options] = ale#engine#ProcessChain(
+ \ a:buffer,
+ \ l:executable,
+ \ l:linter,
+ \ l:next_chain_index,
+ \ a:output,
+ \)
- return
- endif
+ call s:RunJob(l:command, l:options)
- " Log the output of the command for ALEInfo if we should.
- if g:ale_history_enabled && g:ale_history_log_output
- call ale#history#RememberOutput(l:buffer, a:job_id, l:output[:])
+ return
endif
try
- let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output)
+ let l:loclist = ale#util#GetFunction(l:linter.callback)(a:buffer, a:output)
" Handle the function being unknown, or being deleted.
catch /E700/
let l:loclist = []
endtry
- call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0)
+ call ale#engine#HandleLoclist(l:linter.name, a:buffer, l:loclist, 0)
endfunction
function! ale#engine#SetResults(buffer, loclist) abort
@@ -321,7 +277,7 @@ function! ale#engine#SetResults(buffer, loclist) abort
" Automatically remove all managed temporary files and directories
" now that all jobs have completed.
- call ale#engine#RemoveManagedFiles(a:buffer)
+ call ale#command#RemoveManagedFiles(a:buffer)
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd <nomodeline> User ALELintPost
@@ -472,33 +428,23 @@ endfunction
" Given part of a command, replace any % with %%, so that no characters in
" the string will be replaced with filenames, etc.
function! ale#engine#EscapeCommandPart(command_part) abort
- return substitute(a:command_part, '%', '%%', 'g')
-endfunction
-
-function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
- if empty(a:temporary_file)
- " There is no file, so we didn't create anything.
- return 0
- endif
-
- let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
- " Create the temporary directory for the file, unreadable by 'other'
- " users.
- call mkdir(l:temporary_directory, '', 0750)
- " Automatically delete the directory later.
- call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
- " Write the buffer out to a file.
- let l:lines = getbufline(a:buffer, 1, '$')
- call ale#util#Writefile(a:buffer, l:lines, a:temporary_file)
-
- return 1
+ " TODO: Emit deprecation warning here later.
+ return ale#command#EscapeCommandPart(a:command_part)
endfunction
" Run a job.
"
-" Returns 1 when the job was started successfully.
-function! s:RunJob(options) abort
- let l:command = a:options.command
+" Returns 1 when a job was started successfully.
+function! s:RunJob(command, options) abort
+ if ale#command#IsDeferred(a:command)
+ let a:command.result_callback = {
+ \ command -> s:RunJob(command, a:options)
+ \}
+
+ return 1
+ endif
+
+ let l:command = a:command
if empty(l:command)
return 0
@@ -512,201 +458,106 @@ function! s:RunJob(options) abort
let l:read_buffer = a:options.read_buffer
let l:info = g:ale_buffer_info[l:buffer]
- let [l:temporary_file, l:command] = ale#command#FormatCommand(
- \ l:buffer,
- \ l:executable,
- \ l:command,
- \ l:read_buffer,
- \)
-
- if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
- " If a temporary filename has been formatted in to the command, then
- " we do not need to send the Vim buffer to the command.
- let l:read_buffer = 0
- endif
-
- " Add a newline to commands which need it.
- " This is only used for Flow for now, and is not documented.
- if l:linter.add_newline
- if has('win32')
- let l:command = l:command . '; echo.'
- else
- let l:command = l:command . '; echo'
- endif
- endif
-
- let l:command = ale#job#PrepareCommand(l:buffer, l:command)
- let l:job_options = {
- \ 'mode': 'nl',
- \ 'exit_cb': function('s:HandleExit'),
- \}
-
- if l:output_stream is# 'stderr'
- let l:job_options.err_cb = function('s:GatherOutput')
- 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
- let l:job_options.out_cb = function('s:GatherOutput')
- endif
-
- if get(g:, 'ale_run_synchronously') == 1
- " Find a unique Job value to use, which will be the same as the ID for
- " running commands synchronously. This is only for test code.
- let l:job_id = len(s:job_info_map) + 1
-
- while has_key(s:job_info_map, l:job_id)
- let l:job_id += 1
- endwhile
- else
- let l:job_id = ale#job#Start(l:command, l:job_options)
- endif
-
- let l:status = 'failed'
+ let l:Callback = function('s:HandleExit', [{
+ \ 'linter': l:linter,
+ \ 'executable': l:executable,
+ \ 'next_chain_index': l:next_chain_index,
+ \}])
+ let l:result = ale#command#Run(l:buffer, l:command, l:Callback, {
+ \ 'output_stream': l:output_stream,
+ \ 'executable': l:executable,
+ \ 'read_buffer': l:read_buffer,
+ \ 'log_output': l:next_chain_index >= len(get(l:linter, 'command_chain', [])),
+ \})
" 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(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.
- let s:job_info_map[l:job_id] = {
- \ 'linter': l:linter,
- \ 'buffer': l:buffer,
- \ 'executable': l:executable,
- \ 'output': [],
- \ 'next_chain_index': l:next_chain_index,
- \}
-
- silent doautocmd <nomodeline> User ALEJobStarted
- endif
-
- if g:ale_history_enabled
- call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
+ if empty(l:result)
+ return 0
endif
- if get(g:, 'ale_run_synchronously') == 1
- " Run a command synchronously if this test option is set.
- let s:job_info_map[l:job_id].output = systemlist(
- \ type(l:command) is v:t_list
- \ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
- \ : l:command
- \)
+ call ale#engine#MarkLinterActive(l:info, l:linter)
- call l:job_options.exit_cb(l:job_id, v:shell_error)
- endif
+ silent doautocmd <nomodeline> User ALEJobStarted
- return l:job_id != 0
+ return 1
endfunction
" Determine which commands to run for a link in a command chain, or
" just a regular command.
-function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
+function! ale#engine#ProcessChain(buffer, executable, linter, chain_index, input) abort
let l:output_stream = get(a:linter, 'output_stream', 'stdout')
let l:read_buffer = a:linter.read_buffer
let l:chain_index = a:chain_index
let l:input = a:input
- if has_key(a:linter, 'command_chain')
- while l:chain_index < len(a:linter.command_chain)
- " Run a chain of commands, one asynchronous command after the other,
- " so that many programs can be run in a sequence.
- let l:chain_item = a:linter.command_chain[l:chain_index]
-
- if l:chain_index == 0
- " The first callback in the chain takes only a buffer number.
- let l:command = ale#util#GetFunction(l:chain_item.callback)(
- \ a:buffer
- \)
- else
- " The second callback in the chain takes some input too.
- let l:command = ale#util#GetFunction(l:chain_item.callback)(
- \ a:buffer,
- \ l:input
- \)
+ while l:chain_index < len(a:linter.command_chain)
+ " Run a chain of commands, one asynchronous command after the other,
+ " so that many programs can be run in a sequence.
+ let l:chain_item = a:linter.command_chain[l:chain_index]
+
+ if l:chain_index == 0
+ " The first callback in the chain takes only a buffer number.
+ let l:command = ale#util#GetFunction(l:chain_item.callback)(
+ \ a:buffer
+ \)
+ else
+ " The second callback in the chain takes some input too.
+ let l:command = ale#util#GetFunction(l:chain_item.callback)(
+ \ a:buffer,
+ \ l:input
+ \)
+ endif
+
+ " If we have a command to run, execute that.
+ if !empty(l:command)
+ " The chain item can override the output_stream option.
+ if has_key(l:chain_item, 'output_stream')
+ let l:output_stream = l:chain_item.output_stream
endif
- " If we have a command to run, execute that.
- if !empty(l:command)
- " The chain item can override the output_stream option.
- if has_key(l:chain_item, 'output_stream')
- let l:output_stream = l:chain_item.output_stream
- endif
-
- " The chain item can override the read_buffer option.
- if has_key(l:chain_item, 'read_buffer')
- let l:read_buffer = l:chain_item.read_buffer
- elseif l:chain_index != len(a:linter.command_chain) - 1
- " Don't read the buffer for commands besides the last one
- " in the chain by default.
- let l:read_buffer = 0
- endif
-
- break
+ " The chain item can override the read_buffer option.
+ if has_key(l:chain_item, 'read_buffer')
+ let l:read_buffer = l:chain_item.read_buffer
+ elseif l:chain_index != len(a:linter.command_chain) - 1
+ " Don't read the buffer for commands besides the last one
+ " in the chain by default.
+ let l:read_buffer = 0
endif
- " Command chain items can return an empty string to indicate that
- " a command should be skipped, so we should try the next item
- " with no input.
- let l:input = []
- let l:chain_index += 1
- endwhile
- else
- let l:command = ale#linter#GetCommand(a:buffer, a:linter)
- endif
+ break
+ endif
+
+ " Command chain items can return an empty string to indicate that
+ " a command should be skipped, so we should try the next item
+ " with no input.
+ let l:input = []
+ let l:chain_index += 1
+ endwhile
- return {
- \ 'command': l:command,
+ return [l:command, {
+ \ 'executable': a:executable,
\ 'buffer': a:buffer,
\ 'linter': a:linter,
\ 'output_stream': l:output_stream,
\ 'next_chain_index': l:chain_index + 1,
\ 'read_buffer': l:read_buffer,
- \}
+ \}]
endfunction
-function! s:InvokeChain(buffer, executable, linter, chain_index, input) abort
- let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
- let l:options.executable = a:executable
-
- return s:RunJob(l:options)
-endfunction
-
-function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
+function! s:StopCurrentJobs(buffer, clear_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 = []
+ call ale#command#StopJobs(a:buffer, 'linter')
- for l:job_id in get(l:info, 'job_list', [])
- let l:job_info = get(s:job_info_map, l:job_id, {})
-
- if !empty(l:job_info)
- if a:include_lint_file_jobs || !l:job_info.linter.lint_file
- call ale#job#Stop(l:job_id)
- 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
+ if a:clear_lint_file_jobs
+ call ale#command#StopJobs(a:buffer, 'file_linter')
+ let l:info.active_linter_list = []
+ else
+ " Keep jobs for linting files when we're only linting buffers.
+ call filter(l:info.active_linter_list, 'get(v:val, ''lint_file'')')
+ endif
endfunction
-
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove
" problems for linters which are no longer enabled.
@@ -757,6 +608,48 @@ function! s:AddProblemsFromOtherBuffers(buffer, linters) abort
endif
endfunction
+function! s:RunIfExecutable(buffer, linter, executable) abort
+ if ale#command#IsDeferred(a:executable)
+ let a:executable.result_callback = {
+ \ executable -> s:RunIfExecutable(a:buffer, a:linter, executable)
+ \}
+
+ return 1
+ endif
+
+ if ale#engine#IsExecutable(a:buffer, a:executable)
+ " Use different job types for file or linter jobs.
+ let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter'
+ call setbufvar(a:buffer, 'ale_job_type', l:job_type)
+
+ if has_key(a:linter, 'command_chain')
+ let [l:command, l:options] = ale#engine#ProcessChain(
+ \ a:buffer,
+ \ a:executable,
+ \ a:linter,
+ \ 0,
+ \ []
+ \)
+
+ return s:RunJob(l:command, l:options)
+ endif
+
+ let l:command = ale#linter#GetCommand(a:buffer, a:linter)
+ let l:options = {
+ \ 'executable': a:executable,
+ \ 'buffer': a:buffer,
+ \ 'linter': a:linter,
+ \ 'output_stream': get(a:linter, 'output_stream', 'stdout'),
+ \ 'next_chain_index': 1,
+ \ 'read_buffer': a:linter.read_buffer,
+ \}
+
+ return s:RunJob(l:command, l:options)
+ endif
+
+ return 0
+endfunction
+
" Run a linter for a buffer.
"
" Returns 1 if the linter was successfully run.
@@ -766,9 +659,7 @@ function! s:RunLinter(buffer, linter) abort
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
- if ale#engine#IsExecutable(a:buffer, l:executable)
- return s:InvokeChain(a:buffer, l:executable, a:linter, 0, [])
- endif
+ return s:RunIfExecutable(a:buffer, a:linter, l:executable)
endif
return 0
@@ -836,90 +727,3 @@ function! ale#engine#GetLoclist(buffer) abort
return g:ale_buffer_info[a:buffer].loclist
endfunction
-
-" This function can be called with a timeout to wait for all jobs to finish.
-" If the jobs to not finish in the given number of milliseconds,
-" an exception will be thrown.
-"
-" The time taken will be a very rough approximation, and more time may be
-" permitted than is specified.
-function! ale#engine#WaitForJobs(deadline) abort
- let l:start_time = ale#events#ClockMilliseconds()
-
- if l:start_time == 0
- throw 'Failed to read milliseconds from the clock!'
- endif
-
- let l:job_list = []
-
- " Gather all of the jobs from every buffer.
- for l:info in values(g:ale_buffer_info)
- call extend(l:job_list, get(l:info, 'job_list', []))
- endfor
-
- " NeoVim has a built-in API for this, so use that.
- if has('nvim')
- let l:nvim_code_list = jobwait(l:job_list, a:deadline)
-
- if index(l:nvim_code_list, -1) >= 0
- throw 'Jobs did not complete on time!'
- endif
-
- return
- endif
-
- let l:should_wait_more = 1
-
- while l:should_wait_more
- let l:should_wait_more = 0
-
- for l:job_id in l:job_list
- if ale#job#IsRunning(l:job_id)
- let l:now = ale#events#ClockMilliseconds()
-
- if l:now - l:start_time > a:deadline
- " Stop waiting after a timeout, so we don't wait forever.
- throw 'Jobs did not complete on time!'
- endif
-
- " Wait another 10 milliseconds
- let l:should_wait_more = 1
- sleep 10ms
- break
- endif
- endfor
- endwhile
-
- " Sleep for a small amount of time after all jobs finish.
- " This seems to be enough to let handlers after jobs end run, and
- " prevents the occasional failure where this function exits after jobs
- " end, but before handlers are run.
- sleep 10ms
-
- " We must check the buffer data again to see if new jobs started
- " for command_chain linters.
- let l:has_new_jobs = 0
-
- " Check again to see if any jobs are running.
- for l:info in values(g:ale_buffer_info)
- for l:job_id in get(l:info, 'job_list', [])
- if ale#job#IsRunning(l:job_id)
- let l:has_new_jobs = 1
- break
- endif
- endfor
- endfor
-
- if l:has_new_jobs
- " We have to wait more. Offset the timeout by the time taken so far.
- let l:now = ale#events#ClockMilliseconds()
- let l:new_deadline = a:deadline - (l:now - l:start_time)
-
- if l:new_deadline <= 0
- " Enough time passed already, so stop immediately.
- throw 'Jobs did not complete on time!'
- endif
-
- call ale#engine#WaitForJobs(l:new_deadline)
- endif
-endfunction
diff --git a/autoload/ale/engine/ignore.vim b/autoload/ale/engine/ignore.vim
index 2db2c6c1..80574656 100644
--- a/autoload/ale/engine/ignore.vim
+++ b/autoload/ale/engine/ignore.vim
@@ -22,7 +22,7 @@ function! ale#engine#ignore#GetList(filetype, config) abort
endfunction
" Given a List of linter descriptions, exclude the linters to be ignored.
-function! ale#engine#ignore#Exclude(filetype, all_linters, config) abort
+function! ale#engine#ignore#Exclude(filetype, all_linters, config, disable_lsp) abort
let l:names_to_remove = ale#engine#ignore#GetList(a:filetype, a:config)
let l:filtered_linters = []
@@ -37,6 +37,10 @@ function! ale#engine#ignore#Exclude(filetype, all_linters, config) abort
endif
endfor
+ if a:disable_lsp && has_key(l:linter, 'lsp') && l:linter.lsp isnot# ''
+ let l:should_include = 0
+ endif
+
if l:should_include
call add(l:filtered_linters, l:linter)
endif
diff --git a/autoload/ale/filetypes.vim b/autoload/ale/filetypes.vim
index 6174aa0e..6cdc9ece 100644
--- a/autoload/ale/filetypes.vim
+++ b/autoload/ale/filetypes.vim
@@ -5,7 +5,7 @@ function! ale#filetypes#LoadExtensionMap() abort
" Output includes:
" '*.erl setf erlang'
redir => l:output
- silent exec 'autocmd'
+ silent exec 'autocmd'
redir end
let l:map = {}
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index 03652ecf..92ae3e14 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -1,12 +1,4 @@
-if !has_key(s:, 'job_info_map')
- let s:job_info_map = {}
-endif
-
-function! s:GatherOutput(job_id, line) abort
- if has_key(s:job_info_map, a:job_id)
- call add(s:job_info_map[a:job_id].output, a:line)
- endif
-endfunction
+call ale#Set('fix_on_save_ignore', {})
" Apply fixes queued up for buffers which may be hidden.
" Vim doesn't let you modify hidden buffers.
@@ -66,11 +58,17 @@ function! ale#fix#ApplyQueuedFixes() abort
endfunction
function! ale#fix#ApplyFixes(buffer, output) abort
- call ale#fix#RemoveManagedFiles(a:buffer)
-
let l:data = g:ale_fix_buffer_data[a:buffer]
let l:data.output = a:output
let l:data.changes_made = l:data.lines_before != l:data.output
+ let l:data.done = 1
+
+ call ale#command#RemoveManagedFiles(a:buffer)
+
+ if !bufexists(a:buffer)
+ " Remove the buffer data when it doesn't exist.
+ call remove(g:ale_fix_buffer_data, a:buffer)
+ endif
if l:data.changes_made && bufexists(a:buffer)
let l:lines = getbufline(a:buffer, 1, '$')
@@ -83,279 +81,178 @@ function! ale#fix#ApplyFixes(buffer, output) abort
endif
endif
- if !bufexists(a:buffer)
- " Remove the buffer data when it doesn't exist.
- call remove(g:ale_fix_buffer_data, a:buffer)
- endif
-
- let l:data.done = 1
-
" We can only change the lines of a buffer which is currently open,
" so try and apply the fixes to the current buffer.
call ale#fix#ApplyQueuedFixes()
endfunction
-function! s:HandleExit(job_id, exit_code) abort
- if !has_key(s:job_info_map, a:job_id)
- return
- endif
-
- let l:job_info = remove(s:job_info_map, a:job_id)
- let l:buffer = l:job_info.buffer
+function! s:HandleExit(job_info, buffer, job_output, data) abort
+ let l:buffer_info = get(g:ale_fix_buffer_data, a:buffer, {})
- if g:ale_history_enabled
- call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code)
+ if empty(l:buffer_info)
+ return
endif
- if has_key(l:job_info, 'file_to_read')
- let l:job_info.output = readfile(l:job_info.file_to_read)
+ if a:job_info.read_temporary_file
+ let l:output = !empty(a:data.temporary_file)
+ \ ? readfile(a:data.temporary_file)
+ \ : []
+ else
+ let l:output = a:job_output
endif
- let l:ChainCallback = get(l:job_info, 'chain_with', v:null)
- let l:ProcessWith = get(l:job_info, 'process_with', v:null)
+ let l:ChainCallback = get(a:job_info, 'chain_with', v:null)
+ let l:ProcessWith = get(a:job_info, 'process_with', v:null)
" Post-process the output with a function if we have one.
if l:ProcessWith isnot v:null
- let l:job_info.output = call(
- \ ale#util#GetFunction(l:ProcessWith),
- \ [l:buffer, l:job_info.output]
- \)
+ let l:output = call(l:ProcessWith, [a:buffer, l:output])
endif
" Use the output of the job for changing the file if it isn't empty,
" otherwise skip this job and use the input from before.
"
" We'll use the input from before for chained commands.
- if l:ChainCallback is v:null && !empty(split(join(l:job_info.output)))
- let l:input = l:job_info.output
+ if l:ChainCallback is v:null && !empty(split(join(l:output)))
+ let l:input = l:output
else
- let l:input = l:job_info.input
+ let l:input = a:job_info.input
+ endif
+
+ if l:ChainCallback isnot v:null && !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom ''chain_with is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
let l:next_index = l:ChainCallback is v:null
- \ ? l:job_info.callback_index + 1
- \ : l:job_info.callback_index
+ \ ? a:job_info.callback_index + 1
+ \ : a:job_info.callback_index
call s:RunFixer({
- \ 'buffer': l:buffer,
+ \ 'buffer': a:buffer,
\ 'input': l:input,
- \ 'output': l:job_info.output,
- \ 'callback_list': l:job_info.callback_list,
+ \ 'output': l:output,
+ \ 'callback_list': a:job_info.callback_list,
\ 'callback_index': l:next_index,
\ 'chain_callback': l:ChainCallback,
\})
endfunction
-function! ale#fix#ManageDirectory(buffer, directory) abort
- call add(g:ale_fix_buffer_data[a:buffer].temporary_directory_list, a:directory)
-endfunction
+function! s:RunJob(result, options) abort
+ if ale#command#IsDeferred(a:result)
+ let a:result.result_callback = {x -> s:RunJob(x, a:options)}
-function! ale#fix#RemoveManagedFiles(buffer) abort
- if !has_key(g:ale_fix_buffer_data, a:buffer)
return
endif
- " We can't delete anything in a sandbox, so wait until we escape from
- " it to delete temporary files and directories.
- if ale#util#InSandbox()
- return
- endif
+ let l:buffer = a:options.buffer
+ let l:input = a:options.input
- " Delete directories like `rm -rf`.
- " Directories are handled differently from files, so paths that are
- " intended to be single files can be set up for automatic deletion without
- " accidentally deleting entire directories.
- for l:directory in g:ale_fix_buffer_data[a:buffer].temporary_directory_list
- call delete(l:directory, 'rf')
- endfor
+ if a:result is 0 || type(a:result) is v:t_list
+ if type(a:result) is v:t_list
+ let l:input = a:result
+ endif
- let g:ale_fix_buffer_data[a:buffer].temporary_directory_list = []
-endfunction
+ call s:RunFixer({
+ \ 'buffer': l:buffer,
+ \ 'input': l:input,
+ \ 'callback_index': a:options.callback_index + 1,
+ \ 'callback_list': a:options.callback_list,
+ \})
-function! s:CreateTemporaryFileForJob(buffer, temporary_file, input) abort
- if empty(a:temporary_file)
- " There is no file, so we didn't create anything.
- return 0
+ return
endif
- let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
- " Create the temporary directory for the file, unreadable by 'other'
- " users.
- call mkdir(l:temporary_directory, '', 0750)
- " Automatically delete the directory later.
- call ale#fix#ManageDirectory(a:buffer, l:temporary_directory)
- " Write the buffer out to a file.
- call ale#util#Writefile(a:buffer, a:input, a:temporary_file)
-
- return 1
-endfunction
-
-function! s:RunJob(options) abort
- let l:buffer = a:options.buffer
- let l:command = a:options.command
- let l:input = a:options.input
- let l:output_stream = a:options.output_stream
- let l:read_temporary_file = a:options.read_temporary_file
- let l:ChainWith = a:options.chain_with
- let l:read_buffer = a:options.read_buffer
+ let l:command = get(a:result, 'command', '')
+ let l:ChainWith = get(a:result, 'chain_with', v:null)
if empty(l:command)
- " If there's nothing further to chain the command with, stop here.
- if l:ChainWith is v:null
- return 0
- endif
-
- " If there's another chained callback to run, then run that.
+ " If the command is empty, skip to the next item, or call the
+ " chain_with function.
call s:RunFixer({
\ 'buffer': l:buffer,
\ 'input': l:input,
- \ 'callback_index': a:options.callback_index,
+ \ 'callback_index': a:options.callback_index + (l:ChainWith is v:null),
\ 'callback_list': a:options.callback_list,
\ 'chain_callback': l:ChainWith,
\ 'output': [],
\})
- return 1
+ return
endif
- let [l:temporary_file, l:command] = ale#command#FormatCommand(
- \ l:buffer,
- \ '',
- \ l:command,
- \ l:read_buffer,
- \)
- call s:CreateTemporaryFileForJob(l:buffer, l:temporary_file, l:input)
-
- let l:command = ale#job#PrepareCommand(l:buffer, l:command)
- let l:job_options = {
- \ 'mode': 'nl',
- \ 'exit_cb': function('s:HandleExit'),
- \}
+ let l:read_temporary_file = get(a:result, 'read_temporary_file', 0)
+ " Default to piping the buffer for the last fixer in the chain.
+ let l:read_buffer = get(a:result, 'read_buffer', l:ChainWith is v:null)
+ let l:output_stream = get(a:result, 'output_stream', 'stdout')
- let l:job_info = {
- \ 'buffer': l:buffer,
+ if l:read_temporary_file
+ let l:output_stream = 'none'
+ endif
+
+ let l:Callback = function('s:HandleExit', [{
\ 'input': l:input,
- \ 'output': [],
\ 'chain_with': l:ChainWith,
\ 'callback_index': a:options.callback_index,
\ 'callback_list': a:options.callback_list,
- \ 'process_with': a:options.process_with,
- \}
-
- if l:read_temporary_file
- " TODO: Check that a temporary file is set here.
- let l:job_info.file_to_read = l:temporary_file
- elseif l:output_stream is# 'stderr'
- let l:job_options.err_cb = function('s:GatherOutput')
- 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
- let l:job_options.out_cb = function('s:GatherOutput')
- endif
-
- if get(g:, 'ale_emulate_job_failure') == 1
- let l:job_id = 0
- elseif get(g:, 'ale_run_synchronously') == 1
- " Find a unique Job value to use, which will be the same as the ID for
- " running commands synchronously. This is only for test code.
- let l:job_id = len(s:job_info_map) + 1
-
- while has_key(s:job_info_map, l:job_id)
- let l:job_id += 1
- endwhile
- else
- let l:job_id = ale#job#Start(l:command, l:job_options)
- endif
-
- let l:status = l:job_id ? 'started' : 'failed'
-
- if g:ale_history_enabled
- call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
- endif
-
- if l:job_id == 0
- return 0
- endif
-
- let s:job_info_map[l:job_id] = l:job_info
-
- if get(g:, 'ale_run_synchronously') == 1
- " Run a command synchronously if this test option is set.
- let l:output = systemlist(
- \ type(l:command) is v:t_list
- \ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
- \ : l:command
- \)
-
- if !l:read_temporary_file
- let s:job_info_map[l:job_id].output = l:output
- endif
+ \ 'process_with': get(a:result, 'process_with', v:null),
+ \ 'read_temporary_file': l:read_temporary_file,
+ \}])
+ let l:run_result = ale#command#Run(l:buffer, l:command, l:Callback, {
+ \ 'output_stream': l:output_stream,
+ \ 'executable': '',
+ \ 'read_buffer': l:read_buffer,
+ \ 'input': l:input,
+ \ 'log_output': 0,
+ \})
- call l:job_options.exit_cb(l:job_id, v:shell_error)
+ if empty(l:run_result)
+ call s:RunFixer({
+ \ 'buffer': l:buffer,
+ \ 'input': l:input,
+ \ 'callback_index': a:options.callback_index + 1,
+ \ 'callback_list': a:options.callback_list,
+ \})
endif
-
- return 1
endfunction
function! s:RunFixer(options) abort
let l:buffer = a:options.buffer
let l:input = a:options.input
let l:index = a:options.callback_index
+
+ if len(a:options.callback_list) <= l:index
+ call ale#fix#ApplyFixes(l:buffer, l:input)
+
+ return
+ endif
+
let l:ChainCallback = get(a:options, 'chain_callback', v:null)
- while len(a:options.callback_list) > l:index
- let l:Function = l:ChainCallback isnot v:null
- \ ? ale#util#GetFunction(l:ChainCallback)
- \ : a:options.callback_list[l:index]
-
- if l:ChainCallback isnot v:null
- " Chained commands accept (buffer, output, [input])
- let l:result = ale#util#FunctionArgCount(l:Function) == 2
- \ ? call(l:Function, [l:buffer, a:options.output])
- \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)])
- else
- " Chained commands accept (buffer, [input])
- let l:result = ale#util#FunctionArgCount(l:Function) == 1
- \ ? call(l:Function, [l:buffer])
- \ : call(l:Function, [l:buffer, copy(l:input)])
- endif
+ let l:Function = l:ChainCallback isnot v:null
+ \ ? ale#util#GetFunction(l:ChainCallback)
+ \ : a:options.callback_list[l:index]
- if type(l:result) is v:t_number && l:result == 0
- " When `0` is returned, skip this item.
- let l:index += 1
- elseif type(l:result) is v:t_list
- let l:input = l:result
- let l:index += 1
- else
- let l:ChainWith = get(l:result, 'chain_with', v:null)
- " Default to piping the buffer for the last fixer in the chain.
- let l:read_buffer = get(l:result, 'read_buffer', l:ChainWith is v:null)
-
- let l:job_ran = s:RunJob({
- \ 'buffer': l:buffer,
- \ 'command': l:result.command,
- \ 'input': l:input,
- \ 'output_stream': get(l:result, 'output_stream', 'stdout'),
- \ 'read_temporary_file': get(l:result, 'read_temporary_file', 0),
- \ 'read_buffer': l:read_buffer,
- \ 'chain_with': l:ChainWith,
- \ 'callback_list': a:options.callback_list,
- \ 'callback_index': l:index,
- \ 'process_with': get(l:result, 'process_with', v:null),
- \})
-
- if !l:job_ran
- " The job failed to run, so skip to the next item.
- let l:index += 1
- else
- " Stop here, we will handle exit later on.
- return
- endif
- endif
- endwhile
+ " Record new jobs started as fixer jobs.
+ call setbufvar(l:buffer, 'ale_job_type', 'fixer')
- call ale#fix#ApplyFixes(l:buffer, l:input)
+ if l:ChainCallback isnot v:null
+ " Chained commands accept (buffer, output, [input])
+ let l:result = ale#util#FunctionArgCount(l:Function) == 2
+ \ ? call(l:Function, [l:buffer, a:options.output])
+ \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)])
+ else
+ " Regular fixer commands accept (buffer, [input])
+ let l:result = ale#util#FunctionArgCount(l:Function) == 1
+ \ ? call(l:Function, [l:buffer])
+ \ : call(l:Function, [l:buffer, copy(l:input)])
+ endif
+
+ call s:RunJob(l:result, {
+ \ 'buffer': l:buffer,
+ \ 'input': l:input,
+ \ 'callback_list': a:options.callback_list,
+ \ 'callback_index': l:index,
+ \})
endfunction
function! s:AddSubCallbacks(full_list, callbacks) abort
@@ -370,7 +267,21 @@ function! s:AddSubCallbacks(full_list, callbacks) abort
return 1
endfunction
-function! s:GetCallbacks(buffer, fixers) abort
+function! s:IgnoreFixers(callback_list, filetype, config) abort
+ if type(a:config) is v:t_list
+ let l:ignore_list = a:config
+ else
+ let l:ignore_list = []
+
+ for l:part in split(a:filetype , '\.')
+ call extend(l:ignore_list, get(a:config, l:part, []))
+ endfor
+ endif
+
+ call filter(a:callback_list, 'index(l:ignore_list, v:val) < 0')
+endfunction
+
+function! s:GetCallbacks(buffer, fixing_flag, fixers) abort
if len(a:fixers)
let l:callback_list = a:fixers
elseif type(get(b:, 'ale_fixers')) is v:t_list
@@ -395,8 +306,12 @@ function! s:GetCallbacks(buffer, fixers) abort
endif
endif
- if empty(l:callback_list)
- return []
+ if a:fixing_flag is# 'save_file'
+ let l:config = ale#Var(a:buffer, 'fix_on_save_ignore')
+
+ if !empty(l:config)
+ call s:IgnoreFixers(l:callback_list, &filetype, l:config)
+ endif
endif
let l:corrected_list = []
@@ -444,7 +359,7 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
endif
try
- let l:callback_list = s:GetCallbacks(a:buffer, a:000)
+ let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000)
catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
@@ -464,13 +379,9 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
return 0
endif
- for l:job_id in keys(s:job_info_map)
- call remove(s:job_info_map, l:job_id)
- call ale#job#Stop(l:job_id)
- endfor
-
+ call ale#command#StopJobs(a:buffer, 'fixer')
" Clean up any files we might have left behind from a previous run.
- call ale#fix#RemoveManagedFiles(a:buffer)
+ call ale#command#RemoveManagedFiles(a:buffer)
call ale#fix#InitBufferData(a:buffer, a:fixing_flag)
silent doautocmd <nomodeline> User ALEFixPre
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index 75fd1508..3a36f367 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -27,6 +27,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with black.',
\ },
+\ 'fecs': {
+\ 'function': 'ale#fixers#fecs#Fix',
+\ 'suggested_filetypes': ['javascript', 'css', 'html'],
+\ 'description': 'Apply fecs format to a file.',
+\ },
\ 'tidy': {
\ 'function': 'ale#fixers#tidy#Fix',
\ 'suggested_filetypes': ['html'],
@@ -115,9 +120,14 @@ let s:default_registry = {
\ 'suggested_filetypes': ['javascript'],
\ 'description': 'Fix JavaScript files using standard --fix',
\ },
+\ 'standardrb': {
+\ 'function': 'ale#fixers#standardrb#Fix',
+\ 'suggested_filetypes': ['ruby'],
+\ 'description': 'Fix ruby files with standardrb --fix',
+\ },
\ 'stylelint': {
\ 'function': 'ale#fixers#stylelint#Fix',
-\ 'suggested_filetypes': ['css', 'sass', 'scss', 'stylus'],
+\ 'suggested_filetypes': ['css', 'sass', 'scss', 'sugarss', 'stylus'],
\ 'description': 'Fix stylesheet files using stylelint --fix.',
\ },
\ 'swiftformat': {
@@ -137,8 +147,13 @@ let s:default_registry = {
\ },
\ 'clang-format': {
\ 'function': 'ale#fixers#clangformat#Fix',
-\ 'suggested_filetypes': ['c', 'cpp'],
-\ 'description': 'Fix C/C++ files with clang-format.',
+\ 'suggested_filetypes': ['c', 'cpp', 'cuda'],
+\ 'description': 'Fix C/C++ and cuda files with clang-format.',
+\ },
+\ 'cmakeformat': {
+\ 'function': 'ale#fixers#cmakeformat#Fix',
+\ 'suggested_filetypes': ['cmake'],
+\ 'description': 'Fix CMake files with cmake-format.',
\ },
\ 'gofmt': {
\ 'function': 'ale#fixers#gofmt#Fix',
@@ -165,11 +180,21 @@ let s:default_registry = {
\ 'suggested_filetypes': ['rust'],
\ 'description': 'Fix Rust files with Rustfmt.',
\ },
+\ 'textlint': {
+\ 'function': 'ale#fixers#textlint#Fix',
+\ 'suggested_filetypes': ['text','markdown','asciidoc','tex'],
+\ 'description': 'Fix text files with textlint --fix',
+\ },
\ 'hackfmt': {
\ 'function': 'ale#fixers#hackfmt#Fix',
\ 'suggested_filetypes': ['hack'],
\ 'description': 'Fix Hack files with hackfmt.',
\ },
+\ 'floskell': {
+\ 'function': 'ale#fixers#floskell#Fix',
+\ 'suggested_filetypes': ['haskell'],
+\ 'description': 'Fix Haskell files with floskell.',
+\ },
\ 'hfmt': {
\ 'function': 'ale#fixers#hfmt#Fix',
\ 'suggested_filetypes': ['haskell'],
@@ -195,6 +220,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['ocaml'],
\ 'description': 'Fix OCaml files with ocamlformat.',
\ },
+\ 'ocp-indent': {
+\ 'function': 'ale#fixers#ocp_indent#Fix',
+\ 'suggested_filetypes': ['ocaml'],
+\ 'description': 'Fix OCaml files with ocp-indent.',
+\ },
\ 'refmt': {
\ 'function': 'ale#fixers#refmt#Fix',
\ 'suggested_filetypes': ['reason'],
@@ -232,8 +262,8 @@ let s:default_registry = {
\ },
\ 'xo': {
\ 'function': 'ale#fixers#xo#Fix',
-\ 'suggested_filetypes': ['javascript'],
-\ 'description': 'Fix JavaScript files using xo --fix.',
+\ 'suggested_filetypes': ['javascript', 'typescript'],
+\ 'description': 'Fix JavaScript/TypeScript files using xo --fix.',
\ },
\ 'qmlfmt': {
\ 'function': 'ale#fixers#qmlfmt#Fix',
@@ -260,6 +290,21 @@ let s:default_registry = {
\ 'suggested_filetypes': ['hcl', 'terraform'],
\ 'description': 'Fix tf and hcl files with terraform fmt.',
\ },
+\ 'ktlint': {
+\ 'function': 'ale#fixers#ktlint#Fix',
+\ 'suggested_filetypes': ['kt'],
+\ 'description': 'Fix Kotlin files with ktlint.',
+\ },
+\ 'styler': {
+\ 'function': 'ale#fixers#styler#Fix',
+\ 'suggested_filetypes': ['r'],
+\ 'description': 'Fix R files with styler.',
+\ },
+\ 'latexindent': {
+\ 'function': 'ale#fixers#latexindent#Fix',
+\ 'suggested_filetypes': ['tex'],
+\ 'description' : 'Indent code within environments, commands, after headings and within special code blocks.',
+\ },
\}
" Reset the function registry to the default entries.
diff --git a/autoload/ale/fixers/black.vim b/autoload/ale/fixers/black.vim
index 4169322a..367b8d52 100644
--- a/autoload/ale/fixers/black.vim
+++ b/autoload/ale/fixers/black.vim
@@ -4,22 +4,33 @@
call ale#Set('python_black_executable', 'black')
call ale#Set('python_black_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_black_options', '')
+call ale#Set('python_black_auto_pipenv', 0)
+call ale#Set('python_black_change_directory', 1)
+
+function! ale#fixers#black#GetExecutable(buffer) abort
+ if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_black_auto_pipenv'))
+ \ && ale#python#PipenvPresent(a:buffer)
+ return 'pipenv'
+ endif
+
+ return ale#python#FindExecutable(a:buffer, 'python_black', ['black'])
+endfunction
function! ale#fixers#black#Fix(buffer) abort
- let l:executable = ale#python#FindExecutable(
- \ a:buffer,
- \ 'python_black',
- \ ['black'],
- \)
+ let l:cd_string = ale#Var(a:buffer, 'python_black_change_directory')
+ \ ? ale#path#BufferCdString(a:buffer)
+ \ : ''
- if !executable(l:executable)
- return 0
- endif
+ let l:executable = ale#fixers#black#GetExecutable(a:buffer)
+
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run black'
+ \ : ''
let l:options = ale#Var(a:buffer, 'python_black_options')
return {
- \ 'command': ale#Escape(l:executable)
+ \ 'command': l:cd_string . ale#Escape(l:executable) . l:exec_args
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' -',
\}
diff --git a/autoload/ale/fixers/cmakeformat.vim b/autoload/ale/fixers/cmakeformat.vim
new file mode 100644
index 00000000..f40ed6ed
--- /dev/null
+++ b/autoload/ale/fixers/cmakeformat.vim
@@ -0,0 +1,18 @@
+" Author: Attila Maczak <attila@maczak.hu>
+" Description: Integration of cmakeformat with ALE.
+
+call ale#Set('cmake_cmakeformat_executable', 'cmake-format')
+call ale#Set('cmake_cmakeformat_options', '')
+
+function! ale#fixers#cmakeformat#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'cmake_cmakeformat_executable')
+ let l:options = ale#Var(a:buffer, 'cmake_cmakeformat_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . ' -i '
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/eslint.vim b/autoload/ale/fixers/eslint.vim
index ea5b2a63..0f57cba6 100644
--- a/autoload/ale/fixers/eslint.vim
+++ b/autoload/ale/fixers/eslint.vim
@@ -3,15 +3,15 @@
function! ale#fixers#eslint#Fix(buffer) abort
let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
+ let l:command = ale#node#Executable(a:buffer, l:executable)
+ \ . ' --version'
- let l:command = ale#semver#HasVersion(l:executable)
- \ ? ''
- \ : ale#node#Executable(a:buffer, l:executable) . ' --version'
-
- return {
- \ 'command': l:command,
- \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion',
- \}
+ return ale#semver#RunWithVersionCheck(
+ \ a:buffer,
+ \ l:executable,
+ \ l:command,
+ \ function('ale#fixers#eslint#ApplyFixForVersion'),
+ \)
endfunction
function! ale#fixers#eslint#ProcessFixDryRunOutput(buffer, output) abort
@@ -33,10 +33,8 @@ function! ale#fixers#eslint#ProcessEslintDOutput(buffer, output) abort
return a:output
endfunction
-function! ale#fixers#eslint#ApplyFixForVersion(buffer, version_output) abort
+function! ale#fixers#eslint#ApplyFixForVersion(buffer, version) abort
let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
-
let l:config = ale#handlers#eslint#FindConfig(a:buffer)
if empty(l:config)
@@ -44,7 +42,7 @@ function! ale#fixers#eslint#ApplyFixForVersion(buffer, version_output) abort
endif
" Use --fix-to-stdout with eslint_d
- if l:executable =~# 'eslint_d$' && ale#semver#GTE(l:version, [3, 19, 0])
+ if l:executable =~# 'eslint_d$' && ale#semver#GTE(a:version, [3, 19, 0])
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ' --stdin-filename %s --stdin --fix-to-stdout',
@@ -53,7 +51,7 @@ function! ale#fixers#eslint#ApplyFixForVersion(buffer, version_output) abort
endif
" 4.9.0 is the first version with --fix-dry-run
- if ale#semver#GTE(l:version, [4, 9, 0])
+ if ale#semver#GTE(a:version, [4, 9, 0])
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
diff --git a/autoload/ale/fixers/fecs.vim b/autoload/ale/fixers/fecs.vim
new file mode 100644
index 00000000..d692bc97
--- /dev/null
+++ b/autoload/ale/fixers/fecs.vim
@@ -0,0 +1,17 @@
+" Author: harttle <yangjvn@126.com>
+" Description: Apply fecs format to a file.
+
+function! ale#fixers#fecs#Fix(buffer) abort
+ let l:executable = ale#handlers#fecs#GetExecutable(a:buffer)
+
+ if !executable(l:executable)
+ return 0
+ endif
+
+ let l:config_options = ' format --replace=true %t'
+
+ return {
+ \ 'command': ale#Escape(l:executable) . l:config_options,
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/floskell.vim b/autoload/ale/fixers/floskell.vim
new file mode 100644
index 00000000..f0015db7
--- /dev/null
+++ b/autoload/ale/fixers/floskell.vim
@@ -0,0 +1,20 @@
+" Author: robertjlooby <robertjlooby@gmail.com>
+" Description: Integration of floskell with ALE.
+
+call ale#Set('haskell_floskell_executable', 'floskell')
+
+function! ale#fixers#floskell#GetExecutable(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'haskell_floskell_executable')
+
+ return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'floskell')
+endfunction
+
+function! ale#fixers#floskell#Fix(buffer) abort
+ let l:executable = ale#fixers#floskell#GetExecutable(a:buffer)
+
+ return {
+ \ 'command': l:executable
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/jq.vim b/autoload/ale/fixers/jq.vim
index 1b743e49..cd9b9138 100644
--- a/autoload/ale/fixers/jq.vim
+++ b/autoload/ale/fixers/jq.vim
@@ -7,16 +7,16 @@ function! ale#fixers#jq#GetExecutable(buffer) abort
endfunction
function! ale#fixers#jq#Fix(buffer) abort
- let l:options = ale#Var(a:buffer, 'json_jq_options')
- let l:filters = ale#Var(a:buffer, 'json_jq_filters')
+ let l:options = ale#Var(a:buffer, 'json_jq_options')
+ let l:filters = ale#Var(a:buffer, 'json_jq_filters')
- if empty(l:filters)
- return 0
- endif
+ if empty(l:filters)
+ return 0
+ endif
- return {
- \ 'command': ale#Escape(ale#fixers#jq#GetExecutable(a:buffer))
- \ . ' ' . l:filters . ' '
- \ . l:options,
- \}
+ return {
+ \ 'command': ale#Escape(ale#fixers#jq#GetExecutable(a:buffer))
+ \ . ' ' . l:filters . ' '
+ \ . l:options,
+ \}
endfunction
diff --git a/autoload/ale/fixers/ktlint.vim b/autoload/ale/fixers/ktlint.vim
new file mode 100644
index 00000000..cb975d6c
--- /dev/null
+++ b/autoload/ale/fixers/ktlint.vim
@@ -0,0 +1,9 @@
+" Author: Michael Phillips <michaeljoelphillips@gmail.com>
+" Description: Fix Kotlin files with ktlint.
+
+function! ale#fixers#ktlint#Fix(buffer) abort
+ return {
+ \ 'command': ale#handlers#ktlint#GetCommand(a:buffer) . ' --format',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/latexindent.vim b/autoload/ale/fixers/latexindent.vim
new file mode 100644
index 00000000..b0a0884a
--- /dev/null
+++ b/autoload/ale/fixers/latexindent.vim
@@ -0,0 +1,18 @@
+" Author: riley-martine <riley.martine@protonmail.com>
+" Description: Integration of latexindent with ALE.
+
+call ale#Set('tex_latexindent_executable', 'latexindent')
+call ale#Set('tex_latexindent_options', '')
+
+function! ale#fixers#latexindent#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'tex_latexindent_executable')
+ let l:options = ale#Var(a:buffer, 'tex_latexindent_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . ' -l -w'
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/ocp_indent.vim b/autoload/ale/fixers/ocp_indent.vim
new file mode 100644
index 00000000..e1b047b3
--- /dev/null
+++ b/autoload/ale/fixers/ocp_indent.vim
@@ -0,0 +1,18 @@
+" Author: Kanenobu Mitsuru
+" Description: Integration of ocp-indent with ALE.
+
+call ale#Set('ocaml_ocp_indent_executable', 'ocp-indent')
+call ale#Set('ocaml_ocp_indent_options', '')
+call ale#Set('ocaml_ocp_indent_config', '')
+
+function! ale#fixers#ocp_indent#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'ocaml_ocp_indent_executable')
+ let l:config = ale#Var(a:buffer, 'ocaml_ocp_indent_config')
+ let l:options = ale#Var(a:buffer, 'ocaml_ocp_indent_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . (empty(l:config) ? '' : ' --config=' . ale#Escape(l:config))
+ \ . (empty(l:options) ? '': ' ' . l:options)
+ \}
+endfunction
diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim
index 58dea159..b7f0ecd7 100644
--- a/autoload/ale/fixers/prettier.vim
+++ b/autoload/ale/fixers/prettier.vim
@@ -15,16 +15,12 @@ function! ale#fixers#prettier#GetExecutable(buffer) abort
endfunction
function! ale#fixers#prettier#Fix(buffer) abort
- let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
-
- let l:command = ale#semver#HasVersion(l:executable)
- \ ? ''
- \ : ale#Escape(l:executable) . ' --version'
-
- return {
- \ 'command': l:command,
- \ 'chain_with': 'ale#fixers#prettier#ApplyFixForVersion',
- \}
+ return ale#semver#RunWithVersionCheck(
+ \ a:buffer,
+ \ ale#fixers#prettier#GetExecutable(a:buffer),
+ \ '%e --version',
+ \ function('ale#fixers#prettier#ApplyFixForVersion'),
+ \)
endfunction
function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort
@@ -38,15 +34,23 @@ function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort
return a:output
endfunction
-function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort
+function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'javascript_prettier_options')
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
let l:parser = ''
" Append the --parser flag depending on the current filetype (unless it's
" already set in g:javascript_prettier_options).
if empty(expand('#' . a:buffer . ':e')) && match(l:options, '--parser') == -1
+ " Mimic Prettier's defaults. In cases without a file extension or
+ " filetype (scratch buffer), Prettier needs `parser` set to know how
+ " to process the buffer.
+ if ale#semver#GTE(a:version, [1, 16, 0])
+ let l:parser = 'babel'
+ else
+ let l:parser = 'babylon'
+ endif
+
let l:prettier_parsers = {
\ 'typescript': 'typescript',
\ 'css': 'css',
@@ -60,7 +64,6 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort
\ 'yaml': 'yaml',
\ 'html': 'html',
\}
- let l:parser = ''
for l:filetype in split(getbufvar(a:buffer, '&filetype'), '\.')
if has_key(l:prettier_parsers, l:filetype)
@@ -86,7 +89,7 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort
endif
" 1.4.0 is the first version with --stdin-filepath
- if ale#semver#GTE(l:version, [1, 4, 0])
+ if ale#semver#GTE(a:version, [1, 4, 0])
return {
\ 'command': ale#path#BufferCdString(a:buffer)
\ . ale#Escape(l:executable)
diff --git a/autoload/ale/fixers/prettier_eslint.vim b/autoload/ale/fixers/prettier_eslint.vim
index bc0caadd..1e66f49e 100644
--- a/autoload/ale/fixers/prettier_eslint.vim
+++ b/autoload/ale/fixers/prettier_eslint.vim
@@ -2,13 +2,9 @@
" w0rp <devw0rp@gmail.com>, morhetz (Pavel Pertsev) <morhetz@gmail.com>
" Description: Integration between Prettier and ESLint.
-function! ale#fixers#prettier_eslint#SetOptionDefaults() abort
- call ale#Set('javascript_prettier_eslint_executable', 'prettier-eslint')
- call ale#Set('javascript_prettier_eslint_use_global', get(g:, 'ale_use_global_executables', 0))
- call ale#Set('javascript_prettier_eslint_options', '')
-endfunction
-
-call ale#fixers#prettier_eslint#SetOptionDefaults()
+call ale#Set('javascript_prettier_eslint_executable', 'prettier-eslint')
+call ale#Set('javascript_prettier_eslint_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('javascript_prettier_eslint_options', '')
function! ale#fixers#prettier_eslint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_prettier_eslint', [
@@ -18,26 +14,20 @@ function! ale#fixers#prettier_eslint#GetExecutable(buffer) abort
endfunction
function! ale#fixers#prettier_eslint#Fix(buffer) abort
- let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer)
-
- let l:command = ale#semver#HasVersion(l:executable)
- \ ? ''
- \ : ale#Escape(l:executable) . ' --version'
-
- return {
- \ 'command': l:command,
- \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion',
- \}
+ return ale#semver#RunWithVersionCheck(
+ \ a:buffer,
+ \ ale#fixers#prettier_eslint#GetExecutable(a:buffer),
+ \ '%e --version',
+ \ function('ale#fixers#prettier_eslint#ApplyFixForVersion'),
+ \)
endfunction
-function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version_output) abort
+function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version) abort
let l:options = ale#Var(a:buffer, 'javascript_prettier_eslint_options')
let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer)
- let l:version = ale#semver#GetVersion(l:executable, a:version_output)
-
" 4.2.0 is the first version with --eslint-config-path
- let l:config = ale#semver#GTE(l:version, [4, 2, 0])
+ let l:config = ale#semver#GTE(a:version, [4, 2, 0])
\ ? ale#handlers#eslint#FindConfig(a:buffer)
\ : ''
let l:eslint_config_option = !empty(l:config)
@@ -45,7 +35,7 @@ function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version_output)
\ : ''
" 4.4.0 is the first version with --stdin-filepath
- if ale#semver#GTE(l:version, [4, 4, 0])
+ if ale#semver#GTE(a:version, [4, 4, 0])
return {
\ 'command': ale#path#BufferCdString(a:buffer)
\ . ale#Escape(l:executable)
diff --git a/autoload/ale/fixers/qmlfmt.vim b/autoload/ale/fixers/qmlfmt.vim
index d750d1c4..90b25679 100644
--- a/autoload/ale/fixers/qmlfmt.vim
+++ b/autoload/ale/fixers/qmlfmt.vim
@@ -5,7 +5,7 @@ function! ale#fixers#qmlfmt#GetExecutable(buffer) abort
endfunction
function! ale#fixers#qmlfmt#Fix(buffer) abort
- return {
- \ 'command': ale#Escape(ale#fixers#qmlfmt#GetExecutable(a:buffer)),
- \}
+ return {
+ \ 'command': ale#Escape(ale#fixers#qmlfmt#GetExecutable(a:buffer)),
+ \}
endfunction
diff --git a/autoload/ale/fixers/standard.vim b/autoload/ale/fixers/standard.vim
index 2b1f6f92..77712d40 100644
--- a/autoload/ale/fixers/standard.vim
+++ b/autoload/ale/fixers/standard.vim
@@ -14,9 +14,11 @@ endfunction
function! ale#fixers#standard#Fix(buffer) abort
let l:executable = ale#fixers#standard#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'javascript_standard_options')
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --fix %t',
\ 'read_temporary_file': 1,
\}
diff --git a/autoload/ale/fixers/standardrb.vim b/autoload/ale/fixers/standardrb.vim
new file mode 100644
index 00000000..fab1e2bc
--- /dev/null
+++ b/autoload/ale/fixers/standardrb.vim
@@ -0,0 +1,23 @@
+" Author: Justin Searls - https://github.com/searls
+" Description: Fix Ruby files with StandardRB.
+
+call ale#Set('ruby_standardrb_options', '')
+call ale#Set('ruby_standardrb_executable', 'standardrb')
+
+function! ale#fixers#standardrb#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'ruby_standardrb_executable')
+ let l:config = ale#path#FindNearestFile(a:buffer, '.standard.yml')
+ let l:options = ale#Var(a:buffer, 'ruby_standardrb_options')
+
+ return ale#handlers#ruby#EscapeExecutable(l:executable, 'standardrb')
+ \ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --fix --force-exclusion %t'
+endfunction
+
+function! ale#fixers#standardrb#Fix(buffer) abort
+ return {
+ \ 'command': ale#fixers#standardrb#GetCommand(a:buffer),
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/stylelint.vim b/autoload/ale/fixers/stylelint.vim
index 30309c7e..6bfb2fde 100644
--- a/autoload/ale/fixers/stylelint.vim
+++ b/autoload/ale/fixers/stylelint.vim
@@ -5,13 +5,12 @@ call ale#Set('stylelint_executable', 'stylelint')
call ale#Set('stylelint_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#stylelint#GetExecutable(buffer) abort
- return ale#node#FindExecutable(a:buffer, 'stylelint', [
- \ 'node_modules/stylelint/bin/stylelint.js',
- \ 'node_modules/.bin/stylelint',
- \])
+ return ale#node#FindExecutable(a:buffer, 'stylelint', [
+ \ 'node_modules/stylelint/bin/stylelint.js',
+ \ 'node_modules/.bin/stylelint',
+ \])
endfunction
-
function! ale#fixers#stylelint#Fix(buffer) abort
let l:executable = ale#fixers#stylelint#GetExecutable(a:buffer)
diff --git a/autoload/ale/fixers/styler.vim b/autoload/ale/fixers/styler.vim
new file mode 100644
index 00000000..7ff3275c
--- /dev/null
+++ b/autoload/ale/fixers/styler.vim
@@ -0,0 +1,16 @@
+" Author: tvatter <thibault.vatter@gmail.com>
+" Description: Fixing R files with styler.
+
+call ale#Set('r_styler_executable', 'Rscript')
+call ale#Set('r_styler_options', 'tidyverse_style')
+
+function! ale#fixers#styler#Fix(buffer) abort
+ return {
+ \ 'command': 'Rscript --vanilla -e '
+ \ . '"suppressPackageStartupMessages(library(styler));'
+ \ . 'style_file(commandArgs(TRUE), style = '
+ \ . ale#Var(a:buffer, 'r_styler_options') . ')"'
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/textlint.vim b/autoload/ale/fixers/textlint.vim
new file mode 100644
index 00000000..38ab2bfd
--- /dev/null
+++ b/autoload/ale/fixers/textlint.vim
@@ -0,0 +1,15 @@
+" Author: TANIGUCHI Masaya <ta2gch@gmail.com>
+" Description: Integration of textlint with ALE.
+
+function! ale#fixers#textlint#Fix(buffer) abort
+ let l:executable = ale#handlers#textlint#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'textlint_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . ' --fix'
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/go.vim b/autoload/ale/go.vim
index a166480a..cd7d9503 100644
--- a/autoload/ale/go.vim
+++ b/autoload/ale/go.vim
@@ -9,18 +9,18 @@ function! ale#go#FindProjectRoot(buffer) abort
let l:filename = ale#path#Simplify(expand('#' . a:buffer . ':p'))
for l:name in split($GOPATH, l:sep)
- let l:path_dir = ale#path#Simplify(l:name)
+ let l:path_dir = ale#path#Simplify(l:name)
- " Use the directory from GOPATH if the current filename starts with it.
- if l:filename[: len(l:path_dir) - 1] is? l:path_dir
- return l:path_dir
- endif
+ " Use the directory from GOPATH if the current filename starts with it.
+ if l:filename[: len(l:path_dir) - 1] is? l:path_dir
+ return l:path_dir
+ endif
endfor
let l:default_go_path = ale#path#Simplify(expand('~/go'))
if isdirectory(l:default_go_path)
- return l:default_go_path
+ return l:default_go_path
endif
return ''
diff --git a/autoload/ale/handlers/alex.vim b/autoload/ale/handlers/alex.vim
index 853d3137..190a7f86 100644
--- a/autoload/ale/handlers/alex.vim
+++ b/autoload/ale/handlers/alex.vim
@@ -1,10 +1,24 @@
+scriptencoding utf-8
" Author: Johannes Wienke <languitar@semipol.de>
" Description: Error handling for errors in alex output format
+function! ale#handlers#alex#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'alex', [
+ \ 'node_modules/.bin/alex',
+ \ 'node_modules/alex/cli.js',
+ \])
+endfunction
+
+function! ale#handlers#alex#CreateCommandCallback(flags) abort
+ return {b -> ale#node#Executable(b, ale#handlers#alex#GetExecutable(b))
+ \ . ' %s '
+ \ . a:flags}
+endfunction
+
function! ale#handlers#alex#Handle(buffer, lines) abort
" Example output:
" 6:256-6:262 warning Be careful with “killed”, it’s profane in some cases killed retext-profanities
- let l:pattern = '^ *\(\d\+\):\(\d\+\)-\(\d\+\):\(\d\+\) \+warning \+\(.\{-\}\) \+\(.\{-\}\) \+\(.\{-\}\)$'
+ let l:pattern = '\v^ *(\d+):(\d+)-(\d+):(\d+) +warning +(.{-}) +(.{-}) +(.{-})$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
@@ -20,3 +34,21 @@ function! ale#handlers#alex#Handle(buffer, lines) abort
return l:output
endfunction
+
+" Define a linter for a specific filetype. Accept flags to adapt to the filetype.
+" no flags treat input as markdown
+" --html treat input as HTML
+" --text treat input as plaintext
+function! ale#handlers#alex#DefineLinter(filetype, flags) abort
+ call ale#Set('alex_executable', 'alex')
+ call ale#Set('alex_use_global', get(g:, 'ale_use_global_executables', 0))
+
+ call ale#linter#Define(a:filetype, {
+ \ 'name': 'alex',
+ \ 'executable': function('ale#handlers#alex#GetExecutable'),
+ \ 'command': ale#handlers#alex#CreateCommandCallback(a:flags),
+ \ 'output_stream': 'stderr',
+ \ 'callback': 'ale#handlers#alex#Handle',
+ \ 'lint_file': 1,
+ \})
+endfunction
diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim
index eda033e4..bc7c0321 100644
--- a/autoload/ale/handlers/eslint.vim
+++ b/autoload/ale/handlers/eslint.vim
@@ -121,7 +121,7 @@ function! ale#handlers#eslint#Handle(buffer, lines) abort
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.'
+ if l:text =~# '^File ignored'
continue
endif
endif
diff --git a/autoload/ale/handlers/fecs.vim b/autoload/ale/handlers/fecs.vim
new file mode 100644
index 00000000..5362edb9
--- /dev/null
+++ b/autoload/ale/handlers/fecs.vim
@@ -0,0 +1,52 @@
+" Author: harttle <yangjvn@126.com>
+" Description: fecs http://fecs.baidu.com/
+
+call ale#Set('javascript_fecs_executable', 'fecs')
+call ale#Set('javascript_fecs_use_global', get(g:, 'ale_use_global_executables', 0))
+
+function! ale#handlers#fecs#GetCommand(buffer) abort
+ return '%e check --colors=false --rule=true %t'
+endfunction
+
+function! ale#handlers#fecs#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'javascript_fecs', [
+ \ 'node_modules/.bin/fecs',
+ \ 'node_modules/fecs/bin/fecs',
+ \])
+endfunction
+
+function! ale#handlers#fecs#Handle(buffer, lines) abort
+ " Matches patterns looking like the following
+ "
+ " fecs WARN → line 20, col 25: Unexpected console statement. (no-console)
+ " fecs ERROR → line 24, col 36: Missing radix parameter. (radix)
+ "
+ let l:pattern = '\v^.*(WARN|ERROR)\s+→\s+line (\d+),\s+col\s+(\d+):\s+(.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:obj = {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4]
+ \}
+
+ let l:code_match = matchlist(l:match[4], '\v^(.{-})\s*\((.+)\)$')
+
+ if !empty(l:code_match)
+ let l:obj.code = l:code_match[2]
+ let l:obj.text = l:code_match[1]
+ endif
+
+ if l:match[1] is# 'WARN'
+ let l:obj.type = 'W'
+ elseif l:match[1] is# 'ERROR'
+ let l:obj.type = 'E'
+ endif
+
+ call add(l:output, l:obj)
+ endfor
+
+ return l:output
+endfunction
+
diff --git a/autoload/ale/handlers/flawfinder.vim b/autoload/ale/handlers/flawfinder.vim
index a650d6dd..b7d2bec3 100644
--- a/autoload/ale/handlers/flawfinder.vim
+++ b/autoload/ale/handlers/flawfinder.vim
@@ -1,3 +1,4 @@
+scriptencoding utf-8
" Author: Christian Gibbons <cgibbons@gmu.edu>
" Description: This file defines a handler function that should work for the
" flawfinder format with the -CDQS flags.
@@ -30,7 +31,7 @@ function! ale#handlers#flawfinder#HandleFlawfinderFormat(buffer, lines) abort
\ 'lnum': str2nr(l:match[2]),
\ 'col': str2nr(l:match[3]),
\ 'type': (l:severity < ale#Var(a:buffer, 'c_flawfinder_error_severity'))
- \ ? 'W' : 'E',
+ \ ? 'W' : 'E',
\ 'text': s:RemoveUnicodeQuotes(join(split(l:match[4])[1:]) . ': ' . l:match[5]),
\}
diff --git a/autoload/ale/handlers/haskell.vim b/autoload/ale/handlers/haskell.vim
index 9e495b36..3613b1bb 100644
--- a/autoload/ale/handlers/haskell.vim
+++ b/autoload/ale/handlers/haskell.vim
@@ -66,11 +66,11 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
let l:errors = matchlist(l:match[4], '\v([wW]arning|[eE]rror): ?(.*)')
if len(l:errors) > 0
- let l:ghc_type = l:errors[1]
- let l:text = l:errors[2]
+ let l:ghc_type = l:errors[1]
+ let l:text = l:errors[2]
else
- let l:ghc_type = ''
- let l:text = l:match[4][:0] is# ' ' ? l:match[4][1:] : l:match[4]
+ let l:ghc_type = ''
+ let l:text = l:match[4][:0] is# ' ' ? l:match[4][1:] : l:match[4]
endif
if l:ghc_type is? 'Warning'
diff --git a/autoload/ale/handlers/ktlint.vim b/autoload/ale/handlers/ktlint.vim
new file mode 100644
index 00000000..ad999485
--- /dev/null
+++ b/autoload/ale/handlers/ktlint.vim
@@ -0,0 +1,45 @@
+" Author: Michael Phillips <michaeljoelphillips@gmail.com>
+" Description: Handler functions for ktlint.
+
+call ale#Set('kotlin_ktlint_executable', 'ktlint')
+call ale#Set('kotlin_ktlint_rulesets', [])
+call ale#Set('kotlin_ktlint_options', '')
+
+function! ale#handlers#ktlint#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'kotlin_ktlint_executable')
+ let l:options = ale#Var(a:buffer, 'kotlin_ktlint_options')
+ let l:rulesets = ale#handlers#ktlint#GetRulesets(a:buffer)
+
+ return ale#Escape(l:executable)
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . (empty(l:rulesets) ? '' : ' ' . l:rulesets)
+ \ . ' %t'
+endfunction
+
+function! ale#handlers#ktlint#GetRulesets(buffer) abort
+ let l:rulesets = map(ale#Var(a:buffer, 'kotlin_ktlint_rulesets'), '''--ruleset '' . v:val')
+
+ return join(l:rulesets, ' ')
+endfunction
+
+function! ale#handlers#ktlint#Handle(buffer, lines) abort
+ let l:message_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(.*\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:message_pattern)
+ let l:line = l:match[2] + 0
+ let l:column = l:match[3] + 0
+ let l:text = l:match[4]
+
+ let l:type = l:text =~? 'not a valid kotlin file' ? 'E' : 'W'
+
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'col': l:column,
+ \ 'text': l:text,
+ \ 'type': l:type
+ \})
+ endfor
+
+ return l:output
+endfunction
diff --git a/autoload/ale/handlers/languagetool.vim b/autoload/ale/handlers/languagetool.vim
new file mode 100644
index 00000000..10e049df
--- /dev/null
+++ b/autoload/ale/handlers/languagetool.vim
@@ -0,0 +1,74 @@
+" Author: Vincent (wahrwolf [at] wolfpit.net)
+" Description: languagetool for markdown files
+"
+call ale#Set('languagetool_executable', 'languagetool')
+
+function! ale#handlers#languagetool#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'languagetool_executable')
+endfunction
+
+function! ale#handlers#languagetool#GetCommand(buffer) abort
+ let l:executable = ale#handlers#languagetool#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable) . ' --autoDetect %s'
+endfunction
+
+function! ale#handlers#languagetool#HandleOutput(buffer, lines) abort
+ " Match lines like:
+ " 1.) Line 5, column 1, Rule ID:
+ let l:head_pattern = '^\v.+.\) Line (\d+), column (\d+), Rule ID. (.+)$'
+ let l:head_matches = ale#util#GetMatches(a:lines, l:head_pattern)
+
+ " Match lines like:
+ " Message: Did you forget a comma after a conjunctive/linking adverb?
+ let l:message_pattern = '^\vMessage. (.+)$'
+ let l:message_matches = ale#util#GetMatches(a:lines, l:message_pattern)
+
+ " Match lines like:
+ " ^^^^^ "
+ let l:markers_pattern = '^\v *(\^+) *$'
+ let l:markers_matches = ale#util#GetMatches(a:lines, l:markers_pattern)
+
+ let l:output = []
+
+
+ " Okay tbh I was to lazy to figure out a smarter solution here
+ " We just check that the arrays are same sized and merge everything
+ " together
+ let l:i = 0
+
+ while l:i < len(l:head_matches)
+ \ && (
+ \ (len(l:head_matches) == len(l:markers_matches))
+ \ && (len(l:head_matches) == len(l:message_matches))
+ \ )
+ let l:item = {
+ \ 'lnum' : str2nr(l:head_matches[l:i][1]),
+ \ 'col' : str2nr(l:head_matches[l:i][2]),
+ \ 'end_col' : str2nr(l:head_matches[l:i][2]) + len(l:markers_matches[l:i][1])-1,
+ \ 'type' : 'W',
+ \ 'code' : l:head_matches[l:i][3],
+ \ 'text' : l:message_matches[l:i][1]
+ \}
+ call add(l:output, l:item)
+ let l:i+=1
+ endwhile
+
+ return l:output
+endfunction
+
+" Define the languagetool linter for a given filetype.
+" TODO:
+" - Add language detection settings based on user env (for mothertongue)
+" - Add fixer
+" - Add config options for rules
+function! ale#handlers#languagetool#DefineLinter(filetype) abort
+ call ale#linter#Define(a:filetype, {
+ \ 'name': 'languagetool',
+ \ 'executable': function('ale#handlers#languagetool#GetExecutable'),
+ \ 'command': function('ale#handlers#languagetool#GetCommand'),
+ \ 'output_stream': 'stdout',
+ \ 'callback': 'ale#handlers#languagetool#HandleOutput',
+ \ 'lint_file': 1,
+ \})
+endfunction
diff --git a/autoload/ale/handlers/markdownlint.vim b/autoload/ale/handlers/markdownlint.vim
index 12fc501c..daaa1d66 100644
--- a/autoload/ale/handlers/markdownlint.vim
+++ b/autoload/ale/handlers/markdownlint.vim
@@ -7,10 +7,10 @@ function! ale#handlers#markdownlint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'lnum': l:match[1] + 0,
- \ 'text': '(' . l:match[2] . l:match[3] . l:match[4] . ')' . l:match[5],
- \ 'type': 'W',
- \ })
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': '(' . l:match[2] . l:match[3] . l:match[4] . ')' . l:match[5],
+ \ 'type': 'W',
+ \})
endfor
return l:output
diff --git a/autoload/ale/handlers/redpen.vim b/autoload/ale/handlers/redpen.vim
index 84e331ed..195057ca 100644
--- a/autoload/ale/handlers/redpen.vim
+++ b/autoload/ale/handlers/redpen.vim
@@ -4,10 +4,10 @@
function! ale#handlers#redpen#HandleRedpenOutput(buffer, lines) abort
" Only one file was passed to redpen. So response array has only one
" element.
- let l:res = json_decode(join(a:lines))[0]
+ let l:res = get(ale#util#FuzzyJSONDecode(a:lines, []), 0, {})
let l:output = []
- for l:err in l:res.errors
+ for l:err in get(l:res, 'errors', [])
let l:item = {
\ 'text': l:err.message,
\ 'type': 'W',
diff --git a/autoload/ale/handlers/rust.vim b/autoload/ale/handlers/rust.vim
index c6a4b670..dda6466e 100644
--- a/autoload/ale/handlers/rust.vim
+++ b/autoload/ale/handlers/rust.vim
@@ -60,7 +60,7 @@ function! ale#handlers#rust#HandleRustErrors(buffer, lines) abort
\ 'lnum': l:span.line_start,
\ 'end_lnum': l:span.line_end,
\ 'col': l:span.column_start,
- \ 'end_col': l:span.column_end,
+ \ 'end_col': l:span.column_end-1,
\ 'text': empty(l:span.label) ? l:error.message : printf('%s: %s', l:error.message, l:span.label),
\ 'type': toupper(l:error.level[0]),
\})
diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim
index e96dd3ce..75eaf71f 100644
--- a/autoload/ale/handlers/sh.vim
+++ b/autoload/ale/handlers/sh.vim
@@ -9,7 +9,7 @@ function! ale#handlers#sh#GetShellType(buffer) abort
" Remove options like -e, etc.
let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g')
- for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'sh']
+ for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh']
if l:command =~# l:possible_shell . '\s*$'
return l:possible_shell
endif
diff --git a/autoload/ale/handlers/sml.vim b/autoload/ale/handlers/sml.vim
index 92c5f83b..594ee4f7 100644
--- a/autoload/ale/handlers/sml.vim
+++ b/autoload/ale/handlers/sml.vim
@@ -26,7 +26,6 @@ function! ale#handlers#sml#GetCmFile(buffer) abort
endfunction
" Only one of smlnj or smlnj-cm can be enabled at a time.
-" executable_callback is called before *every* lint attempt
function! s:GetExecutable(buffer, source) abort
if ale#handlers#sml#GetCmFile(a:buffer) is# ''
" No CM file found; only allow single-file mode to be enabled
@@ -64,26 +63,27 @@ function! ale#handlers#sml#Handle(buffer, lines) abort
let l:match2 = matchlist(l:line, l:pattern2)
if len(l:match2) != 0
- call add(l:out, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match2[1] + 0,
- \ 'col' : l:match2[2] - 1,
- \ 'text': l:match2[3],
- \ 'type': l:match2[3] =~# '^Warning' ? 'W' : 'E',
- \})
- continue
+ call add(l:out, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match2[1] + 0,
+ \ 'col' : l:match2[2] - 1,
+ \ 'text': l:match2[3],
+ \ 'type': l:match2[3] =~# '^Warning' ? 'W' : 'E',
+ \})
+
+ continue
endif
let l:match = matchlist(l:line, l:pattern)
if len(l:match) != 0
- call add(l:out, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'text': l:match[2] . ': ' . l:match[3],
- \ 'type': l:match[2] is# 'error' ? 'E' : 'W',
- \})
- continue
+ call add(l:out, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': l:match[2] . ': ' . l:match[3],
+ \ 'type': l:match[2] is# 'error' ? 'E' : 'W',
+ \})
+ continue
endif
endfor
diff --git a/autoload/ale/handlers/tsserver.vim b/autoload/ale/handlers/tsserver.vim
new file mode 100644
index 00000000..f78499ac
--- /dev/null
+++ b/autoload/ale/handlers/tsserver.vim
@@ -0,0 +1,8 @@
+" Author: Derek Sifford <dereksifford@gmail.com>
+" Description: Handlers for tsserver
+
+function! ale#handlers#tsserver#GetProjectRoot(buffer) abort
+ let l:tsconfig_file = ale#path#FindNearestFile(a:buffer, 'tsconfig.json')
+
+ return !empty(l:tsconfig_file) ? fnamemodify(l:tsconfig_file, ':h') : ''
+endfunction
diff --git a/autoload/ale/handlers/writegood.vim b/autoload/ale/handlers/writegood.vim
index aff66d5f..8ae61a38 100644
--- a/autoload/ale/handlers/writegood.vim
+++ b/autoload/ale/handlers/writegood.vim
@@ -65,8 +65,8 @@ function! ale#handlers#writegood#DefineLinter(filetype) abort
call ale#linter#Define(a:filetype, {
\ 'name': 'writegood',
\ 'aliases': ['write-good'],
- \ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
- \ 'command_callback': 'ale#handlers#writegood#GetCommand',
+ \ 'executable': function('ale#handlers#writegood#GetExecutable'),
+ \ 'command': function('ale#handlers#writegood#GetCommand'),
\ 'callback': 'ale#handlers#writegood#Handle',
\})
endfunction
diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim
index ae1f3e7d..172f9d54 100644
--- a/autoload/ale/highlight.vim
+++ b/autoload/ale/highlight.vim
@@ -26,6 +26,41 @@ endif
let s:MAX_POS_VALUES = 8
let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
+" Check if we have neovim's buffer highlight API
+"
+" Below we define some functions' implementation conditionally if this API
+" exists or not.
+"
+" The API itself is more ergonomic and neovim performs highlights positions
+" rebases during edits so we see less stalled highlights.
+let s:nvim_api = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace')
+
+function! ale#highlight#HasNeovimApi() abort
+ return s:nvim_api
+endfunction
+
+function! ale#highlight#nvim_buf_clear_namespace(...) abort
+ return call('nvim_buf_clear_namespace', a:000)
+endfunction
+
+function! ale#highlight#nvim_buf_add_highlight(...) abort
+ return call('nvim_buf_add_highlight', a:000)
+endfunction
+
+function! s:ale_nvim_highlight_id(bufnr) abort
+ let l:id = getbufvar(a:bufnr, 'ale_nvim_highlight_id', -1)
+
+ if l:id is -1
+ " NOTE: This will highlight nothing but will allocate new id
+ let l:id = ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, 0, '', 0, 0, -1
+ \)
+ call setbufvar(a:bufnr, 'ale_nvim_highlight_id', l:id)
+ endif
+
+ return l:id
+endfunction
+
function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort
if a:line >= a:end_line
" For single lines, just return the one position.
@@ -49,12 +84,90 @@ endfunction
" Given a loclist for current items to highlight, remove all highlights
" except these which have matching loclist item entries.
+
function! ale#highlight#RemoveHighlights() abort
- for l:match in getmatches()
- if l:match.group =~# '^ALE'
- call matchdelete(l:match.id)
+ if ale#highlight#HasNeovimApi()
+ if get(b:, 'ale_nvim_highlight_id', 0)
+ let l:bufnr = bufnr('%')
+ " NOTE: 0, -1 means from 0 line till the end of buffer
+ call ale#highlight#nvim_buf_clear_namespace(
+ \ l:bufnr,
+ \ b:ale_nvim_highlight_id,
+ \ 0, -1
+ \)
endif
- endfor
+ else
+ for l:match in getmatches()
+ if l:match.group =~# '^ALE'
+ call matchdelete(l:match.id)
+ endif
+ endfor
+ endif
+endfunction
+
+function! s:highlight_line(bufnr, lnum, group) abort
+ if ale#highlight#HasNeovimApi()
+ let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ a:lnum - 1, 0, -1
+ \)
+ else
+ call matchaddpos(a:group, [a:lnum])
+ endif
+endfunction
+
+function! s:highlight_range(bufnr, range, group) abort
+ if ale#highlight#HasNeovimApi()
+ let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
+ " NOTE: lines and columns indicies are 0-based in nvim_buf_* API.
+ let l:lnum = a:range.lnum - 1
+ let l:end_lnum = a:range.end_lnum - 1
+ let l:col = a:range.col - 1
+ let l:end_col = a:range.end_col
+
+ if l:lnum >= l:end_lnum
+ " For single lines, just return the one position.
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:lnum, l:col, l:end_col
+ \)
+ else
+ " highlight first line from start till the line end
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:lnum, l:col, -1
+ \)
+
+ " highlight all lines between the first and last entirely
+ let l:cur = l:lnum + 1
+
+ while l:cur < l:end_lnum
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:cur, 0, -1
+ \ )
+ let l:cur += 1
+ endwhile
+
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:end_lnum, 0, l:end_col
+ \)
+ endif
+ else
+ " Set all of the positions, which are chunked into Lists which
+ " are as large as will be accepted by matchaddpos.
+ call map(
+ \ ale#highlight#CreatePositions(
+ \ a:range.lnum,
+ \ a:range.col,
+ \ a:range.end_lnum,
+ \ a:range.end_col
+ \ ),
+ \ 'matchaddpos(a:group, v:val)'
+ \)
+ endif
endfunction
function! ale#highlight#UpdateHighlights() abort
@@ -79,17 +192,14 @@ function! ale#highlight#UpdateHighlights() abort
let l:group = 'ALEError'
endif
- let l:line = l:item.lnum
- let l:col = l:item.col
- let l:end_line = get(l:item, 'end_lnum', l:line)
- let l:end_col = get(l:item, 'end_col', l:col)
+ let l:range = {
+ \ 'lnum': l:item.lnum,
+ \ 'col': l:item.col,
+ \ 'end_lnum': get(l:item, 'end_lnum', l:item.lnum),
+ \ 'end_col': get(l:item, 'end_col', l:item.col)
+ \}
- " Set all of the positions, which are chunked into Lists which
- " are as large as will be accepted by matchaddpos.
- call map(
- \ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col),
- \ 'matchaddpos(l:group, v:val)'
- \)
+ call s:highlight_range(l:item.bufnr, l:range, l:group)
endfor
" If highlights are enabled and signs are not enabled, we should still
@@ -111,7 +221,7 @@ function! ale#highlight#UpdateHighlights() abort
endif
if l:available_groups[l:group]
- call matchaddpos(l:group, [l:item.lnum])
+ call s:highlight_line(l:item.bufnr, l:item.lnum, l:group)
endif
endfor
endif
diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim
index 69db276e..2af35aa4 100644
--- a/autoload/ale/hover.vim
+++ b/autoload/ale/hover.vim
@@ -24,7 +24,21 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'success', v:false) is v:true
\&& get(a:response, 'body', v:null) isnot v:null
- if get(l:options, 'hover_from_balloonexpr', 0)
+ " If we pass the show_documentation flag, we should show the full
+ " documentation, and always in the preview window.
+ if get(l:options, 'show_documentation', 0)
+ let l:documentation = get(a:response.body, 'documentation', '')
+
+ " displayString is not included here, because it can be very
+ " noisy and run on for many lines for complex types. A less
+ " verbose alternative may be nice in future.
+ if !empty(l:documentation)
+ call ale#preview#Show(split(l:documentation, "\n"), {
+ \ 'filetype': 'ale-preview.message',
+ \ 'stay_here': 1,
+ \})
+ endif
+ elseif get(l:options, 'hover_from_balloonexpr', 0)
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(a:response.body.displayString)
@@ -43,7 +57,7 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
" If the call did __not__ come from balloonexpr...
if !get(l:options, 'hover_from_balloonexpr', 0)
let l:buffer = bufnr('')
- let [l:line, l:column] = getcurpos()[1:2]
+ let [l:line, l:column] = getpos('.')[1:2]
let l:end = len(getline(l:line))
if l:buffer isnot l:options.buffer
@@ -64,8 +78,8 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
let l:result = l:result.contents
if type(l:result) is v:t_string
- " The result can be just a string.
- let l:result = [l:result]
+ " The result can be just a string.
+ let l:result = [l:result]
endif
if type(l:result) is v:t_dict
@@ -92,10 +106,15 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
endif
endfunction
-function! s:OnReady(linter, lsp_details, line, column, opt, ...) abort
- let l:buffer = a:lsp_details.buffer
+function! s:OnReady(line, column, opt, linter, lsp_details) abort
let l:id = a:lsp_details.connection_id
+ if !ale#lsp#HasCapability(l:id, 'hover')
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
@@ -126,23 +145,10 @@ function! s:OnReady(linter, lsp_details, line, column, opt, ...) abort
\ 'line': a:line,
\ 'column': l:column,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0),
+ \ 'show_documentation': get(a:opt, 'show_documentation', 0),
\}
endfunction
-function! s:ShowDetails(linter, buffer, line, column, opt, ...) abort
- let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
-
- if empty(l:lsp_details)
- return 0
- endif
-
- let l:id = l:lsp_details.connection_id
-
- call ale#lsp#WaitForCapability(l:id, 'hover', function('s:OnReady', [
- \ a:linter, l:lsp_details, a:line, a:column, a:opt
- \]))
-endfunction
-
" Obtain Hover information for the specified position
" Pass optional arguments in the dictionary opt.
" Currently, only one key/value is useful:
@@ -153,9 +159,31 @@ endfunction
" - in the balloon if opt.called_from_balloonexpr and balloon_show is detected
" - as status message otherwise
function! ale#hover#Show(buffer, line, col, opt) abort
+ let l:show_documentation = get(a:opt, 'show_documentation', 0)
+ let l:Callback = function('s:OnReady', [a:line, a:col, a:opt])
+
for l:linter in ale#linter#Get(getbufvar(a:buffer, '&filetype'))
+ " Only tsserver supports documentation requests at the moment.
if !empty(l:linter.lsp)
- call s:ShowDetails(l:linter, a:buffer, a:line, a:col, a:opt)
+ \&& (!l:show_documentation || l:linter.lsp is# 'tsserver')
+ call ale#lsp_linter#StartLSP(a:buffer, l:linter, l:Callback)
endif
endfor
endfunction
+
+" This function implements the :ALEHover command.
+function! ale#hover#ShowAtCursor() abort
+ let l:buffer = bufnr('')
+ let l:pos = getpos('.')
+
+ call ale#hover#Show(l:buffer, l:pos[1], l:pos[2], {})
+endfunction
+
+" This function implements the :ALEDocumentation command.
+function! ale#hover#ShowDocumentationAtCursor() abort
+ let l:buffer = bufnr('')
+ let l:pos = getpos('.')
+ let l:options = {'show_documentation': 1}
+
+ call ale#hover#Show(l:buffer, l:pos[1], l:pos[2], l:options)
+endfunction
diff --git a/autoload/ale/job.vim b/autoload/ale/job.vim
index 0117c7dd..14b3e484 100644
--- a/autoload/ale/job.vim
+++ b/autoload/ale/job.vim
@@ -99,7 +99,8 @@ function! s:VimCloseCallback(channel) abort
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, get(l:info, 'exit_code', 1))
+ " We have to remove the callback, so we don't call it twice.
+ call ale#util#GetFunction(remove(l:info, 'exit_cb'))(l:job_id, get(l:info, 'exit_code', 1))
endif
finally
" Automatically forget about the job after it's done.
@@ -124,7 +125,8 @@ function! s:VimExitCallback(job, exit_code) abort
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)
+ " We have to remove the callback, so we don't call it twice.
+ call ale#util#GetFunction(remove(l:info, 'exit_cb'))(l:job_id, a:exit_code)
endif
finally
" Automatically forget about the job after it's done.
@@ -173,10 +175,6 @@ endfunction
function! ale#job#PrepareCommand(buffer, command) abort
let l:wrapper = ale#Var(a:buffer, 'command_wrapper')
- let l:command = !empty(l:wrapper)
- \ ? s:PrepareWrappedCommand(l:wrapper, a:command)
- \ : a:command
-
" The command will be executed in a subshell. This fixes a number of
" issues, including reading the PATH variables correctly, %PATHEXT%
" expansion on Windows, etc.
@@ -184,6 +182,17 @@ function! ale#job#PrepareCommand(buffer, command) abort
" NeoVim handles this issue automatically if the command is a String,
" but we'll do this explicitly, so we use the same exact command for both
" versions.
+ let l:command = !empty(l:wrapper)
+ \ ? s:PrepareWrappedCommand(l:wrapper, a:command)
+ \ : a:command
+
+ " If a custom shell is specified, use that.
+ if exists('g:ale_shell')
+ let l:shell_arguments = get(g:, 'ale_shell_arguments', &shellcmdflag)
+
+ return split(g:ale_shell) + split(l:shell_arguments) + [l:command]
+ endif
+
if has('win32')
return 'cmd /s/c "' . l:command . '"'
endif
@@ -267,12 +276,34 @@ function! ale#job#Start(command, options) abort
return l:job_id
endfunction
+" Force running commands in a Windows CMD command line.
+" This means the same command syntax works everywhere.
+function! ale#job#StartWithCmd(command, options) abort
+ let l:shell = &l:shell
+ let l:shellcmdflag = &l:shellcmdflag
+ let &l:shell = 'cmd'
+ let &l:shellcmdflag = '/c'
+
+ try
+ let l:job_id = ale#job#Start(a:command, a:options)
+ finally
+ let &l:shell = l:shell
+ let &l:shellcmdflag = l:shellcmdflag
+ endtry
+
+ return l:job_id
+endfunction
+
" Send raw data to the job.
function! ale#job#SendRaw(job_id, string) abort
if has('nvim')
call jobsend(a:job_id, a:string)
else
- call ch_sendraw(job_getchannel(s:job_map[a:job_id].job), a:string)
+ let l:job = s:job_map[a:job_id].job
+
+ if ch_status(l:job) is# 'open'
+ call ch_sendraw(job_getchannel(l:job), a:string)
+ endif
endif
endfunction
@@ -296,6 +327,20 @@ function! ale#job#IsRunning(job_id) abort
return 0
endfunction
+function! ale#job#HasOpenChannel(job_id) abort
+ if ale#job#IsRunning(a:job_id)
+ if has('nvim')
+ " TODO: Implement a check for NeoVim.
+ return 1
+ endif
+
+ " Check if the Job's channel can be written to.
+ return ch_status(s:job_map[a:job_id].job) is# 'open'
+ endif
+
+ return 0
+endfunction
+
" Given a Job ID, stop that job.
" Invalid job IDs will be ignored.
function! ale#job#Stop(job_id) abort
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 1cbc9ffe..8c657675 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -32,7 +32,7 @@ let s:default_ale_linter_aliases = {
" NOTE: Update the g:ale_linters documentation when modifying this.
let s:default_ale_linters = {
\ 'csh': ['shell'],
-\ 'elixir': ['credo', 'dialyxir', 'dogma', 'elixir-ls'],
+\ 'elixir': ['credo', 'dialyxir', 'dogma'],
\ 'go': ['gofmt', 'golint', 'go vet'],
\ 'hack': ['hack'],
\ 'help': [],
@@ -80,7 +80,6 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
let l:obj = {
- \ 'add_newline': get(a:linter, 'add_newline', 0),
\ 'name': get(a:linter, 'name'),
\ 'lsp': get(a:linter, 'lsp', ''),
\}
@@ -121,7 +120,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
let l:obj.executable = a:linter.executable
if type(l:obj.executable) isnot v:t_string
- throw '`executable` must be a string if defined'
+ \&& type(l:obj.executable) isnot v:t_func
+ throw '`executable` must be a String or Function if defined'
endif
else
throw 'Either `executable` or `executable_callback` must be defined'
@@ -177,7 +177,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
let l:obj.command = a:linter.command
if type(l:obj.command) isnot v:t_string
- throw '`command` must be a string if defined'
+ \&& type(l:obj.command) isnot v:t_func
+ throw '`command` must be a String or Function if defined'
endif
else
throw 'Either `command`, `executable_callback`, `command_chain` '
@@ -194,9 +195,16 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
if !l:needs_address
- if has_key(a:linter, 'address_callback')
- throw '`address_callback` cannot be used when lsp != ''socket'''
+ if has_key(a:linter, 'address') || has_key(a:linter, 'address_callback')
+ throw '`address` or `address_callback` cannot be used when lsp != ''socket'''
endif
+ elseif has_key(a:linter, 'address')
+ if type(a:linter.address) isnot v:t_string
+ \&& type(a:linter.address) isnot v:t_func
+ throw '`address` must be a String or Function if defined'
+ endif
+
+ let l:obj.address = a:linter.address
elseif has_key(a:linter, 'address_callback')
let l:obj.address_callback = a:linter.address_callback
@@ -204,7 +212,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
throw '`address_callback` must be a callback if defined'
endif
else
- throw '`address_callback` must be defined for getting the LSP address'
+ throw '`address` or `address_callback` must be defined for getting the LSP address'
endif
if l:needs_lsp_details
@@ -221,20 +229,34 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
else
" Default to using the filetype as the language.
- let l:obj.language = get(a:linter, 'language', a:filetype)
-
- if type(l:obj.language) isnot v:t_string
- throw '`language` must be a string'
+ let l:Language = get(a:linter, 'language', a:filetype)
+
+ if type(l:Language) is v:t_string
+ " Make 'language_callback' return the 'language' value.
+ let l:obj.language = l:Language
+ let l:obj.language_callback = function('s:LanguageGetter')
+ elseif type(l:Language) is v:t_func
+ let l:obj.language_callback = l:Language
+ else
+ throw '`language` must be a String or Funcref'
endif
-
- " Make 'language_callback' return the 'language' value.
- let l:obj.language_callback = function('s:LanguageGetter')
endif
- let l:obj.project_root_callback = get(a:linter, 'project_root_callback')
+ if has_key(a:linter, 'project_root')
+ let l:obj.project_root = a:linter.project_root
- if !s:IsCallback(l:obj.project_root_callback)
- throw '`project_root_callback` must be a callback for LSP linters'
+ if type(l:obj.project_root) isnot v:t_string
+ \&& type(l:obj.project_root) isnot v:t_func
+ throw '`project_root` must be a String or Function if defined'
+ endif
+ elseif has_key(a:linter, 'project_root_callback')
+ let l:obj.project_root_callback = a:linter.project_root_callback
+
+ if !s:IsCallback(l:obj.project_root_callback)
+ throw '`project_root_callback` must be a callback if defined'
+ endif
+ else
+ throw '`project_root` or `project_root_callback` must be defined for LSP linters'
endif
if has_key(a:linter, 'completion_filter')
@@ -258,6 +280,11 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
elseif has_key(a:linter, 'initialization_options')
let l:obj.initialization_options = a:linter.initialization_options
+
+ if type(l:obj.initialization_options) isnot v:t_dict
+ \&& type(l:obj.initialization_options) isnot v:t_func
+ throw '`initialization_options` must be a String or Function if defined'
+ endif
endif
if has_key(a:linter, 'lsp_config_callback')
@@ -272,7 +299,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
elseif has_key(a:linter, 'lsp_config')
if type(a:linter.lsp_config) isnot v:t_dict
- throw '`lsp_config` must be a Dictionary'
+ \&& type(a:linter.lsp_config) isnot v:t_func
+ throw '`lsp_config` must be a Dictionary or Function if defined'
endif
let l:obj.lsp_config = a:linter.lsp_config
@@ -312,6 +340,14 @@ function! ale#linter#PreProcess(filetype, linter) abort
throw '`aliases` must be a List of String values'
endif
+ for l:key in filter(keys(a:linter), 'v:val[-9:] is# ''_callback'' || v:val is# ''command_chain''')
+ if !get(g:, 'ale_ignore_2_4_warnings')
+ execute 'echom l:key . '' is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
+ endif
+
+ break
+ endfor
+
return l:obj
endfunction
@@ -477,22 +513,34 @@ endfunction
" Given a buffer and linter, get the executable String for the linter.
function! ale#linter#GetExecutable(buffer, linter) abort
- return has_key(a:linter, 'executable_callback')
- \ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
+ let l:Executable = has_key(a:linter, 'executable_callback')
+ \ ? function(a:linter.executable_callback)
\ : a:linter.executable
+
+ return type(l:Executable) is v:t_func
+ \ ? l:Executable(a:buffer)
+ \ : l:Executable
endfunction
" Given a buffer and linter, get the command String for the linter.
" The command_chain key is not supported.
function! ale#linter#GetCommand(buffer, linter) abort
- return has_key(a:linter, 'command_callback')
- \ ? ale#util#GetFunction(a:linter.command_callback)(a:buffer)
+ let l:Command = has_key(a:linter, 'command_callback')
+ \ ? function(a:linter.command_callback)
\ : a:linter.command
+
+ return type(l:Command) is v:t_func
+ \ ? l:Command(a:buffer)
+ \ : l:Command
endfunction
" Given a buffer and linter, get the address for connecting to the server.
function! ale#linter#GetAddress(buffer, linter) abort
- return has_key(a:linter, 'address_callback')
- \ ? ale#util#GetFunction(a:linter.address_callback)(a:buffer)
+ let l:Address = has_key(a:linter, 'address_callback')
+ \ ? function(a:linter.address_callback)
\ : a:linter.address
+
+ return type(l:Address) is v:t_func
+ \ ? l:Address(a:buffer)
+ \ : l:Address
endfunction
diff --git a/autoload/ale/list.vim b/autoload/ale/list.vim
index 3417575c..63d97f35 100644
--- a/autoload/ale/list.vim
+++ b/autoload/ale/list.vim
@@ -115,7 +115,7 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort
let l:open_type = ''
if ale#Var(a:buffer, 'list_vertical') == 1
- let l:open_type = 'vert '
+ let l:open_type = 'vert rightbelow '
endif
if g:ale_set_quickfix
diff --git a/autoload/ale/loclist_jumping.vim b/autoload/ale/loclist_jumping.vim
index fd5ff922..1d3ef7e5 100644
--- a/autoload/ale/loclist_jumping.vim
+++ b/autoload/ale/loclist_jumping.vim
@@ -9,14 +9,26 @@
" If there are no items or we have hit the end with wrapping off, an empty
" List will be returned, otherwise a pair of [line_number, column_number] will
" be returned.
-function! ale#loclist_jumping#FindNearest(direction, wrap) abort
+function! ale#loclist_jumping#FindNearest(direction, wrap, ...) abort
let l:buffer = bufnr('')
- let l:pos = getcurpos()
+ let l:pos = getpos('.')
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
" 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]}
+ if a:0 > 0
+ let l:filter = a:1
+ else
+ let l:filter = 'any'
+ endif
+
+ if a:0 > 1
+ let l:subtype_filter = a:2
+ else
+ let l:subtype_filter = 'any'
+ endif
+
" When searching backwards, so we can find the next smallest match.
if a:direction is# 'before'
call reverse(l:loclist)
@@ -41,29 +53,61 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort
\ l:search_item
\)
- if a:direction is# 'before' && l:cmp_value < 0
- return [l:item.lnum, l:item.col]
- endif
+ if (l:filter is# 'any' || l:filter is# l:item.type)
+ \&& (
+ \ l:subtype_filter is# 'any'
+ \ || l:subtype_filter is# get(l:item, 'sub_type', '')
+ \)
- if a:direction is# 'after' && l:cmp_value > 0
- return [l:item.lnum, l:item.col]
+ if a:direction is# 'before' && l:cmp_value < 0
+ return [l:item.lnum, l:item.col]
+ endif
+
+ if a:direction is# 'after' && l:cmp_value > 0
+ return [l:item.lnum, l:item.col]
+ endif
endif
endfor
" If we found nothing, and the wrap option is set to 1, then we should
" wrap around the list of warnings/errors
- if a:wrap && !empty(l:loclist)
- let l:item = l:loclist[0]
-
- return [l:item.lnum, l:item.col]
+ if a:wrap
+ for l:item in l:loclist
+ if (l:filter is# 'any' || l:filter is# l:item.type)
+ \&& (
+ \ l:subtype_filter is# 'any'
+ \ || l:subtype_filter is# get(l:item, 'sub_type', '')
+ \)
+ return [l:item.lnum, l:item.col]
+ endif
+ endfor
endif
return []
endfunction
" As before, find the nearest match, but position the cursor at it.
-function! ale#loclist_jumping#Jump(direction, wrap) abort
- let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap)
+function! ale#loclist_jumping#Jump(direction, ...) abort
+ if a:0 > 0
+ let l:wrap = a:1
+ else
+ let l:wrap = 0
+ endif
+
+ if a:0 > 1
+ let l:filter = a:2
+ else
+ let l:filter = 'any'
+ endif
+
+ if a:0 > 2
+ let l:subtype_filter = a:3
+ else
+ let l:subtype_filter = 'any'
+ endif
+
+ let l:nearest = ale#loclist_jumping#FindNearest(a:direction,
+ \ l:wrap, l:filter, l:subtype_filter)
if !empty(l:nearest)
normal! m`
@@ -71,6 +115,36 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort
endif
endfunction
+function! ale#loclist_jumping#WrapJump(direction, sargs) abort
+ let [l:args, l:rest] = ale#args#Parse(['error', 'warning', 'info', 'wrap',
+ \ 'style', 'nostyle'], a:sargs)
+
+ let l:wrap = 0
+ let l:type_filter = 'any'
+ let l:subtype_filter = 'any'
+
+ if get(l:args, 'wrap', 'nil') is# ''
+ let l:wrap = 1
+ endif
+
+ if get(l:args, 'error', 'nil') is# ''
+ let l:type_filter = 'E'
+ elseif get(l:args, 'warning', 'nil') is# ''
+ let l:type_filter = 'W'
+ elseif get(l:args, 'info', 'nil') is# ''
+ let l:type_filter = 'I'
+ endif
+
+ if get(l:args, 'nostyle', 'nil') is# ''
+ let l:subtype_filter = 'style'
+ elseif get(l:args, 'style', 'nil') is# ''
+ let l:subtype_filter = ''
+ endif
+
+ call ale#loclist_jumping#Jump(a:direction, l:wrap, l:type_filter,
+ \ l:subtype_filter)
+endfunction
+
function! ale#loclist_jumping#JumpToIndex(index) abort
let l:buffer = bufnr('')
let l:info = get(g:ale_buffer_info, l:buffer, {'loclist': []})
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index f55096c2..7186d2a9 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -21,7 +21,6 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
" init_options: Options to send to the server.
" config: Configuration settings to send to the server.
" callback_list: A list of callbacks for handling LSP responses.
- " message_queue: Messages queued for sending to callbacks.
" capabilities_queue: The list of callbacks to call with capabilities.
" capabilities: Features the server supports.
let s:connections[l:conn_id] = {
@@ -35,14 +34,14 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
\ 'init_options': a:init_options,
\ 'config': {},
\ 'callback_list': [],
- \ 'message_queue': [],
- \ 'capabilities_queue': [],
+ \ 'init_queue': [],
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
+ \ 'typeDefinition': 0,
\ 'symbol_search': 0,
\ },
\}
@@ -58,6 +57,15 @@ function! ale#lsp#RemoveConnectionWithID(id) abort
endif
endfunction
+function! ale#lsp#ResetConnections() abort
+ let s:connections = {}
+endfunction
+
+" Used only in tests.
+function! ale#lsp#GetConnections() abort
+ return s:connections
+endfunction
+
" This is only needed for tests
function! ale#lsp#MarkDocumentAsOpen(id, buffer) abort
let l:conn = get(s:connections, a:id, {})
@@ -207,6 +215,10 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.definition = 1
endif
+ if get(a:capabilities, 'typeDefinitionProvider') is v:true
+ let a:conn.capabilities.typeDefinition = 1
+ endif
+
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
let a:conn.capabilities.symbol_search = 1
endif
@@ -245,22 +257,15 @@ function! ale#lsp#HandleInitResponse(conn, response) abort
return
endif
- " After the server starts, send messages we had queued previously.
- for l:message_data in a:conn.message_queue
- call s:SendMessageData(a:conn, l:message_data)
- endfor
-
- " Remove the messages now.
- let a:conn.message_queue = []
+ " The initialized message must be sent before everything else.
+ call ale#lsp#Send(a:conn.id, ale#lsp#message#Initialized())
" Call capabilities callbacks queued for the project.
- for [l:capability, l:Callback] in a:conn.capabilities_queue
- if a:conn.capabilities[l:capability]
- call call(l:Callback, [a:conn.id])
- endif
+ for l:Callback in a:conn.init_queue
+ call l:Callback()
endfor
- let a:conn.capabilities_queue = []
+ let a:conn.init_queue = []
endfunction
function! ale#lsp#HandleMessage(conn_id, message) abort
@@ -314,19 +319,35 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn.capabilities.symbol_search = 1
endfunction
+function! s:SendInitMessage(conn) abort
+ let [l:init_id, l:init_data] = ale#lsp#CreateMessageData(
+ \ ale#lsp#message#Initialize(a:conn.root, a:conn.init_options),
+ \)
+ let a:conn.init_request_id = l:init_id
+ call s:SendMessageData(a:conn, l:init_data)
+endfunction
+
" Start a program for LSP servers.
"
" 1 will be returned if the program is running, or 0 if the program could
" not be started.
function! ale#lsp#StartProgram(conn_id, executable, command) abort
let l:conn = s:connections[a:conn_id]
+ let l:started = 0
- if !has_key(l:conn, 'job_id') || !ale#job#IsRunning(l:conn.job_id)
+ if !has_key(l:conn, 'job_id') || !ale#job#HasOpenChannel(l:conn.job_id)
let l:options = {
\ 'mode': 'raw',
\ 'out_cb': {_, message -> ale#lsp#HandleMessage(a:conn_id, message)},
\}
- let l:job_id = ale#job#Start(a:command, l:options)
+
+ if has('win32')
+ let l:job_id = ale#job#StartWithCmd(a:command, l:options)
+ else
+ let l:job_id = ale#job#Start(a:command, l:options)
+ endif
+
+ let l:started = 1
else
let l:job_id = l:conn.job_id
endif
@@ -335,6 +356,10 @@ function! ale#lsp#StartProgram(conn_id, executable, command) abort
let l:conn.job_id = l:job_id
endif
+ if l:started && !l:conn.is_tsserver
+ call s:SendInitMessage(l:conn)
+ endif
+
return l:job_id > 0
endfunction
@@ -344,11 +369,14 @@ endfunction
" not be opened.
function! ale#lsp#ConnectToAddress(conn_id, address) abort
let l:conn = s:connections[a:conn_id]
+ let l:started = 0
if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id)
let l:channel_id = ale#socket#Open(a:address, {
\ 'callback': {_, mess -> ale#lsp#HandleMessage(a:conn_id, mess)},
\})
+
+ let l:started = 1
else
let l:channel_id = l:conn.channel_id
endif
@@ -357,6 +385,10 @@ function! ale#lsp#ConnectToAddress(conn_id, address) abort
let l:conn.channel_id = l:channel_id
endif
+ if l:started
+ call s:SendInitMessage(l:conn)
+ endif
+
return l:channel_id >= 0
endfunction
@@ -421,26 +453,12 @@ function! ale#lsp#Send(conn_id, message) abort
return 0
endif
- " If we haven't initialized the server yet, then send the message for it.
- if !l:conn.initialized && !l:conn.init_request_id
- let [l:init_id, l:init_data] = ale#lsp#CreateMessageData(
- \ ale#lsp#message#Initialize(l:conn.root, l:conn.init_options),
- \)
-
- let l:conn.init_request_id = l:init_id
-
- call s:SendMessageData(l:conn, l:init_data)
+ if !l:conn.initialized
+ throw 'LSP server not initialized yet!'
endif
let [l:id, l:data] = ale#lsp#CreateMessageData(a:message)
-
- if l:conn.initialized
- " Send the message now.
- call s:SendMessageData(l:conn, l:data)
- else
- " Add the message we wanted to send to a List to send later.
- call add(l:conn.message_queue, l:data)
- endif
+ call s:SendMessageData(l:conn, l:data)
return l:id == 0 ? -1 : l:id
endfunction
@@ -491,26 +509,32 @@ function! ale#lsp#NotifyForChanges(conn_id, buffer) abort
return l:notified
endfunction
-" Given some LSP details that must contain at least `connection_id` and
-" `project_root` keys,
-function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort
+" Wait for an LSP server to be initialized.
+function! ale#lsp#OnInit(conn_id, Callback) abort
let l:conn = get(s:connections, a:conn_id, {})
if empty(l:conn)
return
endif
+ if l:conn.initialized
+ call a:Callback()
+ else
+ call add(l:conn.init_queue, a:Callback)
+ endif
+endfunction
+
+" Check if an LSP has a given capability.
+function! ale#lsp#HasCapability(conn_id, capability) abort
+ let l:conn = get(s:connections, a:conn_id, {})
+
+ if empty(l:conn)
+ return 0
+ endif
+
if type(get(l:conn.capabilities, a:capability, v:null)) isnot v:t_number
throw 'Invalid capability ' . a:capability
endif
- if l:conn.initialized
- if l:conn.capabilities[a:capability]
- " The project has been initialized, so call the callback now.
- call call(a:callback, [a:conn_id])
- endif
- else
- " Call the callback later, once we have the information we need.
- call add(l:conn.capabilities_queue, [a:capability, a:callback])
- endif
+ return l:conn.capabilities[a:capability]
endfunction
diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim
index 9fffb83a..4ad94c4b 100644
--- a/autoload/ale/lsp/message.vim
+++ b/autoload/ale/lsp/message.vim
@@ -3,6 +3,10 @@
"
" Messages in this movie will be returned in the format
" [is_notification, method_name, params?]
+"
+" All functions which accept line and column arguments expect them to be 1-based
+" (the same format as being returned by getpos() and friends), those then
+" will be converted to 0-based as specified by LSP.
let g:ale_lsp_next_version_id = 1
" The LSP protocols demands that we send every change to a document, including
@@ -37,7 +41,7 @@ function! ale#lsp#message#Initialize(root_path, initialization_options) abort
endfunction
function! ale#lsp#message#Initialized() abort
- return [1, 'initialized']
+ return [1, 'initialized', {}]
endfunction
function! ale#lsp#message#Shutdown() abort
@@ -98,7 +102,7 @@ function! ale#lsp#message#Completion(buffer, line, column, trigger_character) ab
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
- \ 'position': {'line': a:line - 1, 'character': a:column},
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
if !empty(a:trigger_character)
@@ -116,7 +120,16 @@ function! ale#lsp#message#Definition(buffer, line, column) abort
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
- \ 'position': {'line': a:line - 1, 'character': a:column},
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
+ \}]
+endfunction
+
+function! ale#lsp#message#TypeDefinition(buffer, line, column) abort
+ return [0, 'textDocument/typeDefinition', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
+ \ },
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
endfunction
@@ -125,7 +138,7 @@ function! ale#lsp#message#References(buffer, line, column) abort
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
- \ 'position': {'line': a:line - 1, 'character': a:column},
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
\ 'context': {'includeDeclaration': v:false},
\}]
endfunction
@@ -141,12 +154,12 @@ function! ale#lsp#message#Hover(buffer, line, column) abort
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
- \ 'position': {'line': a:line - 1, 'character': a:column},
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
endfunction
function! ale#lsp#message#DidChangeConfiguration(buffer, config) abort
- return [0, 'workspace/didChangeConfiguration', {
+ return [1, 'workspace/didChangeConfiguration', {
\ 'settings': a:config,
\}]
endfunction
diff --git a/autoload/ale/lsp/response.vim b/autoload/ale/lsp/response.vim
index 08b36808..9ce05260 100644
--- a/autoload/ale/lsp/response.vim
+++ b/autoload/ale/lsp/response.vim
@@ -28,12 +28,12 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
for l:diagnostic in a:response.params.diagnostics
let l:severity = get(l:diagnostic, 'severity', 0)
let l:loclist_item = {
- \ 'text': l:diagnostic.message,
+ \ 'text': substitute(l:diagnostic.message, '\(\r\n\|\n\|\r\)', ' ', 'g'),
\ 'type': 'E',
\ 'lnum': l:diagnostic.range.start.line + 1,
\ 'col': l:diagnostic.range.start.character + 1,
\ 'end_lnum': l:diagnostic.range.end.line + 1,
- \ 'end_col': l:diagnostic.range.end.character + 1,
+ \ 'end_col': l:diagnostic.range.end.character,
\}
if l:severity == s:SEVERITY_WARNING
@@ -58,16 +58,20 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
if has_key(l:diagnostic, 'relatedInformation')
let l:related = deepcopy(l:diagnostic.relatedInformation)
call map(l:related, {key, val ->
- \ ale#path#FromURI(val.location.uri) .
- \ ':' . (val.location.range.start.line + 1) .
- \ ':' . (val.location.range.start.character + 1) .
- \ ":\n\t" . val.message
- \ })
+ \ ale#path#FromURI(val.location.uri) .
+ \ ':' . (val.location.range.start.line + 1) .
+ \ ':' . (val.location.range.start.character + 1) .
+ \ ":\n\t" . val.message
+ \})
let l:loclist_item.detail = l:diagnostic.message . "\n" . join(l:related, "\n")
endif
if has_key(l:diagnostic, 'source')
- let l:loclist_item.detail = printf('[%s] %s', l:diagnostic.source, l:diagnostic.message)
+ let l:loclist_item.detail = printf(
+ \ '[%s] %s',
+ \ l:diagnostic.source,
+ \ l:diagnostic.message
+ \)
endif
call add(l:loclist, l:loclist_item)
diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim
index 42d67398..3a596d62 100644
--- a/autoload/ale/lsp_linter.vim
+++ b/autoload/ale/lsp_linter.vim
@@ -27,12 +27,13 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort
let l:linter_name = s:lsp_linter_map[a:conn_id]
let l:filename = ale#path#FromURI(a:response.params.uri)
let l:buffer = bufnr(l:filename)
+ let l:info = get(g:ale_buffer_info, l:buffer, {})
- if s:ShouldIgnore(l:buffer, l:linter_name)
+ if empty(l:info)
return
endif
- if l:buffer <= 0
+ if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
@@ -50,6 +51,8 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort
return
endif
+ call ale#engine#MarkLinterInactive(l:info, l:linter_name)
+
if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
@@ -129,113 +132,242 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
endfunction
function! ale#lsp_linter#GetOptions(buffer, linter) abort
- let l:initialization_options = {}
-
if has_key(a:linter, 'initialization_options_callback')
- let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
- elseif has_key(a:linter, 'initialization_options')
- let l:initialization_options = a:linter.initialization_options
+ return ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
+ endif
+
+ if has_key(a:linter, 'initialization_options')
+ let l:Options = a:linter.initialization_options
+
+ if type(l:Options) is v:t_func
+ let l:Options = l:Options(a:buffer)
+ endif
+
+ return l:Options
endif
- return l:initialization_options
+ return {}
endfunction
function! ale#lsp_linter#GetConfig(buffer, linter) abort
- let l:config = {}
-
if has_key(a:linter, 'lsp_config_callback')
- let l:config = ale#util#GetFunction(a:linter.lsp_config_callback)(a:buffer)
- elseif has_key(a:linter, 'lsp_config')
- let l:config = a:linter.lsp_config
+ return ale#util#GetFunction(a:linter.lsp_config_callback)(a:buffer)
+ endif
+
+ if has_key(a:linter, 'lsp_config')
+ let l:Config = a:linter.lsp_config
+
+ if type(l:Config) is v:t_func
+ let l:Config = l:Config(a:buffer)
+ endif
+
+ return l:Config
endif
- return l:config
+ return {}
endfunction
-" Given a buffer, an LSP linter, start up an LSP linter and get ready to
-" receive messages for the document.
-function! ale#lsp_linter#StartLSP(buffer, linter) abort
- let l:command = ''
- let l:address = ''
- let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
+function! ale#lsp_linter#FindProjectRoot(buffer, linter) abort
+ let l:buffer_ale_root = getbufvar(a:buffer, 'ale_lsp_root', {})
- 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 {}
+ if type(l:buffer_ale_root) is v:t_string
+ return l:buffer_ale_root
endif
- let l:init_options = ale#lsp_linter#GetOptions(a:buffer, a:linter)
+ " Try to get a buffer-local setting for the root
+ if has_key(l:buffer_ale_root, a:linter.name)
+ let l:Root = l:buffer_ale_root[a:linter.name]
- if a:linter.lsp is# 'socket'
- let l:address = ale#linter#GetAddress(a:buffer, a:linter)
- let l:conn_id = ale#lsp#Register(l:address, l:root, l:init_options)
- let l:ready = ale#lsp#ConnectToAddress(l:conn_id, l:address)
- else
- let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
+ if type(l:Root) is v:t_func
+ return l:Root(a:buffer)
+ else
+ return l:Root
+ endif
+ endif
+
+ " Try to get a global setting for the root
+ if has_key(g:ale_lsp_root, a:linter.name)
+ let l:Root = g:ale_lsp_root[a:linter.name]
- if empty(l:executable) || !executable(l:executable)
- return {}
+ if type(l:Root) is v:t_func
+ return l:Root(a:buffer)
+ else
+ return l:Root
endif
+ endif
- let l:conn_id = ale#lsp#Register(l:executable, l:root, l:init_options)
+ " Fall back to the linter-specific configuration
+ if has_key(a:linter, 'project_root')
+ let l:Root = a:linter.project_root
- let l:command = ale#linter#GetCommand(a:buffer, a:linter)
- " Format the command, so %e can be formatted into it.
- let l:command = ale#command#FormatCommand(a:buffer, l:executable, l:command, 0)[1]
- let l:command = ale#job#PrepareCommand(a:buffer, l:command)
- let l:ready = ale#lsp#StartProgram(l:conn_id, l:executable, l:command)
+ return type(l:Root) is v:t_func ? l:Root(a:buffer) : l:Root
endif
- if !l:ready
+ return ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
+endfunction
+
+" This function is accessible so tests can call it.
+function! ale#lsp_linter#OnInit(linter, details, Callback) abort
+ let l:buffer = a:details.buffer
+ let l:conn_id = a:details.connection_id
+ let l:command = a:details.command
+
+ let l:config = ale#lsp_linter#GetConfig(l:buffer, a:linter)
+ let l:language_id = ale#util#GetFunction(a:linter.language_callback)(l:buffer)
+
+ call ale#lsp#UpdateConfig(l:conn_id, l:buffer, l:config)
+
+ if ale#lsp#OpenDocument(l:conn_id, l:buffer, l:language_id)
if g:ale_history_enabled && !empty(l:command)
- call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
+ call ale#history#Add(l:buffer, 'started', l:conn_id, l:command)
endif
-
- return {}
endif
- " tsserver behaves differently, so tell the LSP API that it is tsserver.
+ " The change message needs to be sent for tsserver before doing anything.
if a:linter.lsp is# 'tsserver'
- call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
+ call ale#lsp#NotifyForChanges(l:conn_id, l:buffer)
+ endif
+
+ call a:Callback(a:linter, a:details)
+endfunction
+
+function! s:StartLSP(options, address, executable, command) abort
+ let l:buffer = a:options.buffer
+ let l:linter = a:options.linter
+ let l:root = a:options.root
+ let l:Callback = a:options.callback
+
+ let l:init_options = ale#lsp_linter#GetOptions(l:buffer, l:linter)
+
+ if l:linter.lsp is# 'socket'
+ let l:conn_id = ale#lsp#Register(a:address, l:root, l:init_options)
+ let l:ready = ale#lsp#ConnectToAddress(l:conn_id, a:address)
+ let l:command = ''
+ else
+ let l:conn_id = ale#lsp#Register(a:executable, l:root, l:init_options)
+
+ " tsserver behaves differently, so tell the LSP API that it is tsserver.
+ if l:linter.lsp is# 'tsserver'
+ call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
+ endif
+
+ let l:command = ale#command#FormatCommand(l:buffer, a:executable, a:command, 0, v:false)[1]
+ let l:command = ale#job#PrepareCommand(l:buffer, l:command)
+ let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command)
endif
- let l:config = ale#lsp_linter#GetConfig(a:buffer, a:linter)
- let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
+ if !l:ready
+ if g:ale_history_enabled && !empty(a:command)
+ call ale#history#Add(l:buffer, 'failed', l:conn_id, a:command)
+ endif
+
+ return 0
+ endif
let l:details = {
- \ 'buffer': a:buffer,
+ \ 'buffer': l:buffer,
\ 'connection_id': l:conn_id,
\ 'command': l:command,
\ 'project_root': l:root,
- \ 'language_id': l:language_id,
\}
- call ale#lsp#UpdateConfig(l:conn_id, a:buffer, l:config)
+ call ale#lsp#OnInit(l:conn_id, {->
+ \ ale#lsp_linter#OnInit(l:linter, l:details, l:Callback)
+ \})
- if ale#lsp#OpenDocument(l:conn_id, a:buffer, l:language_id)
- if g:ale_history_enabled && !empty(l:command)
- call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
- endif
+ return 1
+endfunction
+
+function! s:StartWithAddress(options, address) abort
+ if ale#command#IsDeferred(a:address)
+ let a:address.result_callback = {
+ \ address -> s:StartWithAddress(a:options, address)
+ \}
+
+ return 1
endif
- " The change message needs to be sent for tsserver before doing anything.
- if a:linter.lsp is# 'tsserver'
- call ale#lsp#NotifyForChanges(l:conn_id, a:buffer)
+ if empty(a:address)
+ return 0
endif
- return l:details
+ return s:StartLSP(a:options, a:address, '', '')
endfunction
-function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
- let l:info = g:ale_buffer_info[a:buffer]
- let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
+function! s:StartWithCommand(options, executable, command) abort
+ if ale#command#IsDeferred(a:command)
+ let a:command.result_callback = {
+ \ command -> s:StartWithCommand(a:options, a:executable, command)
+ \}
+
+ return 1
+ endif
+
+ if empty(a:command)
+ return 0
+ endif
+
+ return s:StartLSP(a:options, '', a:executable, a:command)
+endfunction
+
+function! s:StartIfExecutable(options, executable) abort
+ if ale#command#IsDeferred(a:executable)
+ let a:executable.result_callback = {
+ \ executable -> s:StartIfExecutable(a:options, executable)
+ \}
+
+ return 1
+ endif
- if empty(l:lsp_details)
+ if !ale#engine#IsExecutable(a:options.buffer, a:executable)
return 0
endif
- let l:id = l:lsp_details.connection_id
+ let l:command = ale#linter#GetCommand(a:options.buffer, a:options.linter)
+
+ return s:StartWithCommand(a:options, a:executable, l:command)
+endfunction
+
+" Given a buffer, an LSP linter, start up an LSP linter and get ready to
+" receive messages for the document.
+function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
+ let l:command = ''
+ let l:address = ''
+ let l:root = ale#lsp_linter#FindProjectRoot(a:buffer, a:linter)
+
+ 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 0
+ endif
+
+ let l:options = {
+ \ 'buffer': a:buffer,
+ \ 'linter': a:linter,
+ \ 'callback': a:Callback,
+ \ 'root': l:root,
+ \}
+
+ if a:linter.lsp is# 'socket'
+ let l:address = ale#linter#GetAddress(a:buffer, a:linter)
+
+ return s:StartWithAddress(l:options, l:address)
+ endif
+
+ let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
+
+ return s:StartIfExecutable(l:options, l:executable)
+endfunction
+
+function! s:CheckWithLSP(linter, details) abort
+ let l:buffer = a:details.buffer
+ let l:info = get(g:ale_buffer_info, l:buffer)
+
+ if empty(l:info)
+ return
+ endif
+
+ let l:id = a:details.connection_id
" Register a callback now for handling errors now.
let l:Callback = function('ale#lsp_linter#HandleLSPResponse')
@@ -245,26 +377,26 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let s:lsp_linter_map[l:id] = a:linter.name
if a:linter.lsp is# 'tsserver'
- let l:message = ale#lsp#tsserver_message#Geterr(a:buffer)
+ let l:message = ale#lsp#tsserver_message#Geterr(l:buffer)
let l:notified = ale#lsp#Send(l:id, l:message) != 0
+
+ if l:notified
+ call ale#engine#MarkLinterActive(l:info, a:linter)
+ endif
else
- let l:notified = ale#lsp#NotifyForChanges(l:id, a:buffer)
+ let l:notified = ale#lsp#NotifyForChanges(l:id, l:buffer)
endif
" If this was a file save event, also notify the server of that.
if a:linter.lsp isnot# 'tsserver'
- \&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
- let l:save_message = ale#lsp#message#DidSave(a:buffer)
+ \&& getbufvar(l:buffer, 'ale_save_event_fired', 0)
+ let l:save_message = ale#lsp#message#DidSave(l:buffer)
let l:notified = ale#lsp#Send(l:id, l:save_message) != 0
endif
+endfunction
- if l:notified
- 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:notified
+function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
+ return ale#lsp_linter#StartLSP(a:buffer, a:linter, function('s:CheckWithLSP'))
endfunction
" Clear LSP linter data for the linting engine.
diff --git a/autoload/ale/node.vim b/autoload/ale/node.vim
index 5c579c75..69060122 100644
--- a/autoload/ale/node.vim
+++ b/autoload/ale/node.vim
@@ -23,11 +23,6 @@ function! ale#node#FindExecutable(buffer, base_var_name, path_list) abort
return ale#Var(a:buffer, a:base_var_name . '_executable')
endfunction
-" As above, but curry the arguments so only the buffer number is required.
-function! ale#node#FindExecutableFunc(base_var_name, path_list) abort
- return {buf -> ale#node#FindExecutable(buf, a:base_var_name, a:path_list)}
-endfunction
-
" Create a executable string which executes a Node.js script command with a
" Node.js executable if needed.
"
@@ -37,7 +32,7 @@ endfunction
"
" The executable is only prefixed for Windows machines
function! ale#node#Executable(buffer, executable) abort
- if ale#Has('win32') && a:executable =~? '\.js$'
+ if has('win32') && a:executable =~? '\.js$'
let l:node = ale#Var(a:buffer, 'windows_node_executable_path')
return ale#Escape(l:node) . ' ' . ale#Escape(a:executable)
diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim
index 89b119f4..60d42eb5 100644
--- a/autoload/ale/path.vim
+++ b/autoload/ale/path.vim
@@ -197,14 +197,20 @@ function! ale#path#ToURI(path) abort
endfunction
function! ale#path#FromURI(uri) abort
- let l:i = len('file://')
- let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri
+ if a:uri[:6] is? 'file://'
+ let l:encoded_path = a:uri[7:]
+ elseif a:uri[:4] is? 'file:'
+ let l:encoded_path = a:uri[5:]
+ else
+ let l:encoded_path = a:uri
+ endif
let l:path = ale#uri#Decode(l:encoded_path)
" If the path is like /C:/foo/bar, it should be C:\foo\bar instead.
- if l:path =~# '^/[a-zA-Z]:'
+ if has('win32') && l:path =~# '^/[a-zA-Z][:|]'
let l:path = substitute(l:path[1:], '/', '\\', 'g')
+ let l:path = l:path[0] . ':' . l:path[2:]
endif
return l:path
diff --git a/autoload/ale/powershell.vim b/autoload/ale/powershell.vim
new file mode 100644
index 00000000..8c163206
--- /dev/null
+++ b/autoload/ale/powershell.vim
@@ -0,0 +1,32 @@
+" Author: zigford <zigford@gmail.com>
+" Description: Functions for integrating with Powershell linters.
+
+" Write a powershell script to a temp file for execution
+" return the command used to execute it
+function! s:TemporaryPSScript(buffer, input) abort
+ let l:filename = 'script.ps1'
+ " Create a temp dir to house our temp .ps1 script
+ " a temp dir is needed as powershell needs the .ps1
+ " extension
+ let l:tempdir = ale#util#Tempname() . (has('win32') ? '\' : '/')
+ let l:tempscript = l:tempdir . l:filename
+ " Create the temporary directory for the file, unreadable by 'other'
+ " users.
+ call mkdir(l:tempdir, '', 0750)
+ " Automatically delete the directory later.
+ call ale#command#ManageDirectory(a:buffer, l:tempdir)
+ " Write the script input out to a file.
+ call ale#util#Writefile(a:buffer, a:input, l:tempscript)
+
+ return l:tempscript
+endfunction
+
+function! ale#powershell#RunPowerShell(buffer, base_var_name, command) abort
+ let l:executable = ale#Var(a:buffer, a:base_var_name . '_executable')
+ let l:tempscript = s:TemporaryPSScript(a:buffer, a:command)
+
+ return ale#Escape(l:executable)
+ \ . ' -Exe Bypass -NoProfile -File '
+ \ . ale#Escape(l:tempscript)
+ \ . ' %t'
+endfunction
diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim
index 1f50e0ad..6d58aca9 100644
--- a/autoload/ale/preview.vim
+++ b/autoload/ale/preview.vim
@@ -41,16 +41,24 @@ endfunction
" Show a location selection preview window, given some items.
" Each item should have 'filename', 'line', and 'column' keys.
-function! ale#preview#ShowSelection(item_list) abort
+function! ale#preview#ShowSelection(item_list, ...) abort
+ let l:options = get(a:000, 0, {})
+ let l:sep = has('win32') ? '\' : '/'
let l:lines = []
" Create lines to display to users.
for l:item in a:item_list
let l:match = get(l:item, 'match', '')
+ let l:filename = l:item.filename
+
+ if get(l:options, 'use_relative_paths')
+ let l:cwd = getcwd() " no-custom-checks
+ let l:filename = substitute(l:filename, '^' . l:cwd . l:sep, '', '')
+ endif
call add(
\ l:lines,
- \ l:item.filename
+ \ l:filename
\ . ':' . l:item.line
\ . ':' . l:item.column
\ . (!empty(l:match) ? ' ' . l:match : ''),
@@ -63,7 +71,7 @@ endfunction
function! s:Open(open_in_tab) abort
let l:item_list = get(b:, 'ale_preview_item_list', [])
- let l:item = get(l:item_list, getcurpos()[1] - 1, {})
+ let l:item = get(l:item_list, getpos('.')[1] - 1, {})
if empty(l:item)
return
diff --git a/autoload/ale/python.vim b/autoload/ale/python.vim
index 8d6bf1f0..2f28214b 100644
--- a/autoload/ale/python.vim
+++ b/autoload/ale/python.vim
@@ -27,6 +27,9 @@ function! ale#python#FindProjectRootIni(buffer) abort
\|| filereadable(l:path . '/pycodestyle.cfg')
\|| filereadable(l:path . '/flake8.cfg')
\|| filereadable(l:path . '/.flake8rc')
+ \|| filereadable(l:path . '/pylama.ini')
+ \|| filereadable(l:path . '/pylintrc')
+ \|| filereadable(l:path . '/.pylintrc')
\|| filereadable(l:path . '/Pipfile')
\|| filereadable(l:path . '/Pipfile.lock')
return l:path
@@ -48,7 +51,7 @@ function! ale#python#FindProjectRoot(buffer) abort
let l:ini_root = ale#python#FindProjectRootIni(a:buffer)
if !empty(l:ini_root)
- return l:ini_root
+ return l:ini_root
endif
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
@@ -110,6 +113,44 @@ function! ale#python#FindExecutable(buffer, base_var_name, path_list) abort
return ale#Var(a:buffer, a:base_var_name . '_executable')
endfunction
+" Handle traceback.print_exception() output starting in the first a:limit lines.
+function! ale#python#HandleTraceback(lines, limit) abort
+ let l:nlines = len(a:lines)
+ let l:limit = a:limit > l:nlines ? l:nlines : a:limit
+ let l:start = 0
+
+ while l:start < l:limit
+ if a:lines[l:start] is# 'Traceback (most recent call last):'
+ break
+ endif
+
+ let l:start += 1
+ endwhile
+
+ if l:start >= l:limit
+ return []
+ endif
+
+ let l:end = l:start + 1
+
+ " Traceback entries are always prefixed with 2 spaces.
+ " SyntaxError marker (if present) is prefixed with at least 4 spaces.
+ " Final exc line starts with exception class name (never a space).
+ while l:end < l:nlines && a:lines[l:end][0] is# ' '
+ let l:end += 1
+ endwhile
+
+ let l:exc_line = l:end < l:nlines
+ \ ? a:lines[l:end]
+ \ : 'An exception was thrown.'
+
+ return [{
+ \ 'lnum': 1,
+ \ 'text': l:exc_line . ' (See :ALEDetail)',
+ \ 'detail': join(a:lines[(l:start):(l:end)], "\n"),
+ \}]
+endfunction
+
" Detects whether a pipenv environment is present.
function! ale#python#PipenvPresent(buffer) abort
return findfile('Pipfile.lock', expand('#' . a:buffer . ':p:h') . ';') isnot# ''
diff --git a/autoload/ale/references.vim b/autoload/ale/references.vim
index d00a1fa9..b9725e1e 100644
--- a/autoload/ale/references.vim
+++ b/autoload/ale/references.vim
@@ -17,7 +17,7 @@ endfunction
function! ale#references#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') is# 'references'
\&& has_key(s:references_map, a:response.request_seq)
- call remove(s:references_map, a:response.request_seq)
+ let l:options = remove(s:references_map, a:response.request_seq)
if get(a:response, 'success', v:false) is v:true
let l:item_list = []
@@ -27,13 +27,14 @@ function! ale#references#HandleTSServerResponse(conn_id, response) abort
\ 'filename': l:response_item.file,
\ 'line': l:response_item.start.line,
\ 'column': l:response_item.start.offset,
+ \ 'match': substitute(l:response_item.lineText, '^\s*\(.\{-}\)\s*$', '\1', ''),
\})
endfor
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
- call ale#preview#ShowSelection(l:item_list)
+ call ale#preview#ShowSelection(l:item_list, l:options)
endif
endif
endif
@@ -42,32 +43,39 @@ endfunction
function! ale#references#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:references_map, a:response.id)
- call remove(s:references_map, a:response.id)
+ let l:options = remove(s:references_map, a:response.id)
" The result can be a Dictionary item, a List of the same, or null.
let l:result = get(a:response, 'result', [])
let l:item_list = []
- for l:response_item in l:result
- call add(l:item_list, {
- \ 'filename': ale#path#FromURI(l:response_item.uri),
- \ 'line': l:response_item.range.start.line + 1,
- \ 'column': l:response_item.range.start.character + 1,
- \})
- endfor
+ if type(l:result) is v:t_list
+ for l:response_item in l:result
+ call add(l:item_list, {
+ \ 'filename': ale#path#FromURI(l:response_item.uri),
+ \ 'line': l:response_item.range.start.line + 1,
+ \ 'column': l:response_item.range.start.character + 1,
+ \})
+ endfor
+ endif
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
- call ale#preview#ShowSelection(l:item_list)
+ call ale#preview#ShowSelection(l:item_list, l:options)
endif
endif
endfunction
-function! s:OnReady(linter, lsp_details, line, column, ...) abort
- let l:buffer = a:lsp_details.buffer
+function! s:OnReady(line, column, options, linter, lsp_details) abort
let l:id = a:lsp_details.connection_id
+ if !ale#lsp#HasCapability(l:id, 'references')
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse')
@@ -90,34 +98,30 @@ function! s:OnReady(linter, lsp_details, line, column, ...) abort
let l:request_id = ale#lsp#Send(l:id, l:message)
- let s:references_map[l:request_id] = {}
+ let s:references_map[l:request_id] = {
+ \ 'use_relative_paths': has_key(a:options, 'use_relative_paths') ? a:options.use_relative_paths : 0
+ \}
endfunction
-function! s:FindReferences(linter) abort
- let l:buffer = bufnr('')
- let [l:line, l:column] = getcurpos()[1:2]
-
- if a:linter.lsp isnot# 'tsserver'
- let l:column = min([l:column, len(getline(l:line))])
- endif
+function! ale#references#Find(...) abort
+ let l:options = {}
- let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
-
- if empty(l:lsp_details)
- return 0
+ if len(a:000) > 0
+ for l:option in a:000
+ if l:option is? '-relative'
+ let l:options.use_relative_paths = 1
+ endif
+ endfor
endif
- let l:id = l:lsp_details.connection_id
-
- call ale#lsp#WaitForCapability(l:id, 'references', function('s:OnReady', [
- \ a:linter, l:lsp_details, l:line, l:column
- \]))
-endfunction
+ let l:buffer = bufnr('')
+ let [l:line, l:column] = getpos('.')[1:2]
+ let l:column = min([l:column, len(getline(l:line))])
+ let l:Callback = function('s:OnReady', [l:line, l:column, l:options])
-function! ale#references#Find() abort
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
- call s:FindReferences(l:linter)
+ call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback)
endif
endfor
endfunction
diff --git a/autoload/ale/ruby.vim b/autoload/ale/ruby.vim
index 5f0aa50d..15e835c9 100644
--- a/autoload/ale/ruby.vim
+++ b/autoload/ale/ruby.vim
@@ -1,7 +1,7 @@
" Author: Eddie Lebow https://github.com/elebow
" Description: Functions for integrating with Ruby tools
-" Find the nearest dir contining "app", "db", and "config", and assume it is
+" Find the nearest dir containing "app", "db", and "config", and assume it is
" the root of a Rails app.
function! ale#ruby#FindRailsRoot(buffer) abort
for l:name in ['app', 'config', 'db']
@@ -26,7 +26,7 @@ function! ale#ruby#FindProjectRoot(buffer) abort
let l:dir = ale#ruby#FindRailsRoot(a:buffer)
if isdirectory(l:dir)
- return l:dir
+ return l:dir
endif
for l:name in ['.solargraph.yml', 'Rakefile', 'Gemfile']
@@ -42,3 +42,35 @@ function! ale#ruby#FindProjectRoot(buffer) abort
return ''
endfunction
+
+" Handle output from rubocop and linters that depend on it (e.b. standardrb)
+function! ale#ruby#HandleRubocopOutput(buffer, lines) abort
+ try
+ let l:errors = json_decode(a:lines[0])
+ catch
+ return []
+ endtry
+
+ if !has_key(l:errors, 'summary')
+ \|| l:errors['summary']['offense_count'] == 0
+ \|| empty(l:errors['files'])
+ return []
+ endif
+
+ let l:output = []
+
+ for l:error in l:errors['files'][0]['offenses']
+ let l:start_col = l:error['location']['column'] + 0
+ call add(l:output, {
+ \ 'lnum': l:error['location']['line'] + 0,
+ \ 'col': l:start_col,
+ \ 'end_col': l:start_col + l:error['location']['length'] - 1,
+ \ 'code': l:error['cop_name'],
+ \ 'text': l:error['message'],
+ \ 'type': ale_linters#ruby#rubocop#GetType(l:error['severity']),
+ \})
+ endfor
+
+ return l:output
+endfunction
+
diff --git a/autoload/ale/semver.vim b/autoload/ale/semver.vim
index 6b0fd34a..e3eb49c0 100644
--- a/autoload/ale/semver.vim
+++ b/autoload/ale/semver.vim
@@ -5,31 +5,52 @@ function! ale#semver#ResetVersionCache() abort
let s:version_cache = {}
endfunction
+function! ale#semver#ParseVersion(version_lines) abort
+ for l:line in a:version_lines
+ let l:match = matchlist(l:line, '\v(\d+)\.(\d+)(\.(\d+))?')
+
+ if !empty(l:match)
+ return [l:match[1] + 0, l:match[2] + 0, l:match[4] + 0]
+ endif
+ endfor
+
+ return []
+endfunction
+
" Given an executable name and some lines of output, which can be empty,
" parse the version from the lines of output, or return the cached version
" triple [major, minor, patch]
"
" If the version cannot be found, an empty List will be returned instead.
-function! ale#semver#GetVersion(executable, version_lines) abort
+function! s:GetVersion(executable, version_lines) abort
let l:version = get(s:version_cache, a:executable, [])
+ let l:parsed_version = ale#semver#ParseVersion(a:version_lines)
- for l:line in a:version_lines
- let l:match = matchlist(l:line, '\v(\d+)\.(\d+)\.(\d+)')
-
- if !empty(l:match)
- let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0]
- let s:version_cache[a:executable] = l:version
-
- break
- endif
- endfor
+ if !empty(l:parsed_version)
+ let l:version = l:parsed_version
+ let s:version_cache[a:executable] = l:version
+ endif
return l:version
endfunction
-" Return 1 if the semver version has been cached for a given executable.
-function! ale#semver#HasVersion(executable) abort
- return has_key(s:version_cache, a:executable)
+function! ale#semver#RunWithVersionCheck(buffer, executable, command, Callback) abort
+ if empty(a:executable)
+ return ''
+ endif
+
+ let l:cache = s:version_cache
+
+ if has_key(s:version_cache, a:executable)
+ return a:Callback(a:buffer, s:version_cache[a:executable])
+ endif
+
+ return ale#command#Run(
+ \ a:buffer,
+ \ a:command,
+ \ {_, output -> a:Callback(a:buffer, s:GetVersion(a:executable, output))},
+ \ {'output_stream': 'both', 'executable': a:executable}
+ \)
endfunction
" Given two triples of integers [major, minor, patch], compare the triples
diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim
index af863682..7395b0e2 100644
--- a/autoload/ale/sign.vim
+++ b/autoload/ale/sign.vim
@@ -64,16 +64,21 @@ if !hlexists('ALESignColumnWithoutErrors')
call ale#sign#SetUpDefaultColumnWithoutErrorsHighlight()
endif
+" Spaces and backslashes need to be escaped for signs.
+function! s:EscapeSignText(sign_text) abort
+ return substitute(substitute(a:sign_text, ' *$', '', ''), '\\\| ', '\\\0', 'g')
+endfunction
+
" Signs show up on the left for error markers.
-execute 'sign define ALEErrorSign text=' . g:ale_sign_error
+execute 'sign define ALEErrorSign text=' . s:EscapeSignText(g:ale_sign_error)
\ . ' texthl=ALEErrorSign linehl=ALEErrorLine'
-execute 'sign define ALEStyleErrorSign text=' . g:ale_sign_style_error
+execute 'sign define ALEStyleErrorSign text=' . s:EscapeSignText(g:ale_sign_style_error)
\ . ' texthl=ALEStyleErrorSign linehl=ALEErrorLine'
-execute 'sign define ALEWarningSign text=' . g:ale_sign_warning
+execute 'sign define ALEWarningSign text=' . s:EscapeSignText(g:ale_sign_warning)
\ . ' texthl=ALEWarningSign linehl=ALEWarningLine'
-execute 'sign define ALEStyleWarningSign text=' . g:ale_sign_style_warning
+execute 'sign define ALEStyleWarningSign text=' . s:EscapeSignText(g:ale_sign_style_warning)
\ . ' texthl=ALEStyleWarningSign linehl=ALEWarningLine'
-execute 'sign define ALEInfoSign text=' . g:ale_sign_info
+execute 'sign define ALEInfoSign text=' . s:EscapeSignText(g:ale_sign_info)
\ . ' texthl=ALEInfoSign linehl=ALEInfoLine'
sign define ALEDummySign
@@ -116,7 +121,7 @@ endfunction
" Read sign data for a buffer to a list of lines.
function! ale#sign#ReadSigns(buffer) abort
redir => l:output
- silent execute 'sign place buffer=' . a:buffer
+ silent execute 'sign place buffer=' . a:buffer
redir end
return split(l:output, "\n")
diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim
index 94fbd6c9..6b93ba51 100644
--- a/autoload/ale/statusline.vim
+++ b/autoload/ale/statusline.vim
@@ -1,4 +1,5 @@
" Author: KabbAmine <amine.kabb@gmail.com>
+" Additions by: petpetpetpet <chris@freelanceninjas.com>
" Description: Statusline related function(s)
function! s:CreateCountDict() abort
@@ -26,19 +27,42 @@ function! ale#statusline#Update(buffer, loclist) abort
let l:count = s:CreateCountDict()
let l:count.total = len(l:loclist)
+ " Allows easy access to the first instance of each problem type.
+ let l:first_problems = {}
+
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
+
+ if l:count.style_warning == 1
+ let l:first_problems.style_warning = l:entry
+ endif
else
let l:count.warning += 1
+
+ if l:count.warning == 1
+ let l:first_problems.warning = l:entry
+ endif
endif
elseif l:entry.type is# 'I'
let l:count.info += 1
+
+ if l:count.info == 1
+ let l:first_problems.info = l:entry
+ endif
elseif get(l:entry, 'sub_type', '') is# 'style'
let l:count.style_error += 1
+
+ if l:count.style_error == 1
+ let l:first_problems.style_error = l:entry
+ endif
else
let l:count.error += 1
+
+ if l:count.error == 1
+ let l:first_problems.error = l:entry
+ endif
endif
endfor
@@ -47,24 +71,65 @@ function! ale#statusline#Update(buffer, loclist) abort
let l:count[1] = l:count.total - l:count[0]
let g:ale_buffer_info[a:buffer].count = l:count
+ let g:ale_buffer_info[a:buffer].first_problems = l:first_problems
endfunction
" Get the counts for the buffer, and update the counts if needed.
-function! s:GetCounts(buffer) abort
+function! s:UpdateCacheIfNecessary(buffer) abort
+ " Cache is cold, so manually ask for an update.
+ if !has_key(g:ale_buffer_info[a:buffer], 'count')
+ call ale#statusline#Update(
+ \ a:buffer,
+ \ g:ale_buffer_info[a:buffer].loclist
+ \)
+ endif
+endfunction
+
+function! s:BufferCacheExists(buffer) abort
if !exists('g:ale_buffer_info') || !has_key(g:ale_buffer_info, a:buffer)
- return s:CreateCountDict()
+ return 0
endif
- " Cache is cold, so manually ask for an update.
- if !has_key(g:ale_buffer_info[a:buffer], 'count')
- call ale#statusline#Update(a:buffer, g:ale_buffer_info[a:buffer].loclist)
+ return 1
+endfunction
+
+" Get the counts for the buffer, and update the counts if needed.
+function! s:GetCounts(buffer) abort
+ if !s:BufferCacheExists(a:buffer)
+ return s:CreateCountDict()
endif
+ call s:UpdateCacheIfNecessary(a:buffer)
+
return g:ale_buffer_info[a:buffer].count
endfunction
+" Get the dict of first_problems, update the buffer info cache if necessary.
+function! s:GetFirstProblems(buffer) abort
+ if !s:BufferCacheExists(a:buffer)
+ return {}
+ endif
+
+ call s:UpdateCacheIfNecessary(a:buffer)
+
+ return g:ale_buffer_info[a:buffer].first_problems
+endfunction
+
" Returns a Dictionary with counts for use in third party integrations.
function! ale#statusline#Count(buffer) abort
" The Dictionary is copied here before exposing it to other plugins.
return copy(s:GetCounts(a:buffer))
endfunction
+
+" Returns a copy of the *first* locline instance of the specified problem
+" type. (so this would allow an external integration to know all the info
+" about the first style warning in the file, for example.)
+function! ale#statusline#FirstProblem(buffer, type) abort
+ let l:first_problems = s:GetFirstProblems(a:buffer)
+
+ if !empty(l:first_problems) && has_key(l:first_problems, a:type)
+ return copy(l:first_problems[a:type])
+ endif
+
+ return {}
+endfunction
diff --git a/autoload/ale/swift.vim b/autoload/ale/swift.vim
new file mode 100644
index 00000000..b31b8dc5
--- /dev/null
+++ b/autoload/ale/swift.vim
@@ -0,0 +1,13 @@
+" Author: Dan Loman <https://github.com/namolnad>
+" Description: Functions for integrating with Swift tools
+
+" Find the nearest dir containing a Package.swift file and assume it is the root of the Swift project.
+function! ale#swift#FindProjectRoot(buffer) abort
+ let l:swift_config = ale#path#FindNearestFile(a:buffer, 'Package.swift')
+
+ if !empty(l:swift_config)
+ return fnamemodify(l:swift_config, ':h')
+ endif
+
+ return ''
+endfunction
diff --git a/autoload/ale/symbol.vim b/autoload/ale/symbol.vim
index 5180cb86..ae4151ab 100644
--- a/autoload/ale/symbol.vim
+++ b/autoload/ale/symbol.vim
@@ -52,12 +52,18 @@ function! ale#symbol#HandleLSPResponse(conn_id, response) abort
if empty(l:item_list)
call ale#util#Execute('echom ''No symbols found.''')
else
- call ale#preview#ShowSelection(l:item_list)
+ call ale#preview#ShowSelection(l:item_list, l:options)
endif
endif
endfunction
-function! s:OnReady(linter, lsp_details, query, ...) abort
+function! s:OnReady(query, options, linter, lsp_details) abort
+ let l:id = a:lsp_details.connection_id
+
+ if !ale#lsp#HasCapability(l:id, 'symbol_search')
+ return
+ endif
+
let l:buffer = a:lsp_details.buffer
" If we already made a request, stop here.
@@ -65,8 +71,6 @@ function! s:OnReady(linter, lsp_details, query, ...) abort
return
endif
- let l:id = a:lsp_details.connection_id
-
let l:Callback = function('ale#symbol#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
@@ -76,34 +80,31 @@ function! s:OnReady(linter, lsp_details, query, ...) abort
call setbufvar(l:buffer, 'ale_symbol_request_made', 1)
let s:symbol_map[l:request_id] = {
\ 'buffer': l:buffer,
+ \ 'use_relative_paths': has_key(a:options, 'use_relative_paths') ? a:options.use_relative_paths : 0
\}
endfunction
-function! s:Search(linter, buffer, query) abort
- let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
-
- if !empty(l:lsp_details)
- call ale#lsp#WaitForCapability(
- \ l:lsp_details.connection_id,
- \ 'symbol_search',
- \ function('s:OnReady', [a:linter, l:lsp_details, a:query]),
- \)
- endif
-endfunction
+function! ale#symbol#Search(args) abort
+ let [l:opts, l:query] = ale#args#Parse(['relative'], a:args)
-function! ale#symbol#Search(query) abort
- if type(a:query) isnot v:t_string || empty(a:query)
+ if empty(l:query)
throw 'A non-empty string must be provided!'
endif
let l:buffer = bufnr('')
+ let l:options = {}
+
+ if has_key(l:opts, 'relative')
+ let l:options.use_relative_paths = 1
+ endif
" Set a flag so we only make one request.
call setbufvar(l:buffer, 'ale_symbol_request_made', 0)
+ let l:Callback = function('s:OnReady', [l:query, l:options])
for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype'))
if !empty(l:linter.lsp) && l:linter.lsp isnot# 'tsserver'
- call s:Search(l:linter, l:buffer, a:query)
+ call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback)
endif
endfor
endfunction
diff --git a/autoload/ale/test.vim b/autoload/ale/test.vim
index 083b546f..082d91ff 100644
--- a/autoload/ale/test.vim
+++ b/autoload/ale/test.vim
@@ -55,9 +55,9 @@ endfunction
function! s:RemoveModule(results) abort
for l:item in a:results
- if has_key(l:item, 'module')
- call remove(l:item, 'module')
- endif
+ if has_key(l:item, 'module')
+ call remove(l:item, 'module')
+ endif
endfor
endfunction
@@ -75,3 +75,113 @@ function! ale#test#GetQflistWithoutModule() abort
return l:results
endfunction
+
+function! ale#test#GetPreviewWindowText() abort
+ for l:window in range(1, winnr('$'))
+ if getwinvar(l:window, '&previewwindow', 0)
+ let l:buffer = winbufnr(l:window)
+
+ return getbufline(l:buffer, 1, '$')
+ endif
+ endfor
+endfunction
+
+" This function can be called with a timeout to wait for all jobs to finish.
+" If the jobs to not finish in the given number of milliseconds,
+" an exception will be thrown.
+"
+" The time taken will be a very rough approximation, and more time may be
+" permitted than is specified.
+function! ale#test#WaitForJobs(deadline) abort
+ let l:start_time = ale#events#ClockMilliseconds()
+
+ if l:start_time == 0
+ throw 'Failed to read milliseconds from the clock!'
+ endif
+
+ let l:job_list = []
+
+ " Gather all of the jobs from every buffer.
+ for [l:buffer, l:data] in items(ale#command#GetData())
+ call extend(l:job_list, map(keys(l:data.jobs), 'str2nr(v:val)'))
+ endfor
+
+ " NeoVim has a built-in API for this, so use that.
+ if has('nvim')
+ let l:nvim_code_list = jobwait(l:job_list, a:deadline)
+
+ if index(l:nvim_code_list, -1) >= 0
+ throw 'Jobs did not complete on time!'
+ endif
+
+ return
+ endif
+
+ let l:should_wait_more = 1
+
+ while l:should_wait_more
+ let l:should_wait_more = 0
+
+ for l:job_id in l:job_list
+ if ale#job#IsRunning(l:job_id)
+ let l:now = ale#events#ClockMilliseconds()
+
+ if l:now - l:start_time > a:deadline
+ " Stop waiting after a timeout, so we don't wait forever.
+ throw 'Jobs did not complete on time!'
+ endif
+
+ " Wait another 10 milliseconds
+ let l:should_wait_more = 1
+ sleep 10ms
+ break
+ endif
+ endfor
+ endwhile
+
+ " Sleep for a small amount of time after all jobs finish.
+ " This seems to be enough to let handlers after jobs end run, and
+ " prevents the occasional failure where this function exits after jobs
+ " end, but before handlers are run.
+ sleep 10ms
+
+ " We must check the buffer data again to see if new jobs started
+ " for command_chain linters.
+ let l:has_new_jobs = 0
+
+ " Check again to see if any jobs are running.
+ for l:info in values(g:ale_buffer_info)
+ for [l:job_id, l:linter] in get(l:info, 'job_list', [])
+ if ale#job#IsRunning(l:job_id)
+ let l:has_new_jobs = 1
+ break
+ endif
+ endfor
+ endfor
+
+ if l:has_new_jobs
+ " We have to wait more. Offset the timeout by the time taken so far.
+ let l:now = ale#events#ClockMilliseconds()
+ let l:new_deadline = a:deadline - (l:now - l:start_time)
+
+ if l:new_deadline <= 0
+ " Enough time passed already, so stop immediately.
+ throw 'Jobs did not complete on time!'
+ endif
+
+ call ale#test#WaitForJobs(l:new_deadline)
+ endif
+endfunction
+
+function! ale#test#FlushJobs() abort
+ " The variable is checked for in a loop, as calling one series of
+ " callbacks can trigger a further series of callbacks.
+ while exists('g:ale_run_synchronously_callbacks')
+ let l:callbacks = g:ale_run_synchronously_callbacks
+ unlet g:ale_run_synchronously_callbacks
+
+ for l:Callback in l:callbacks
+ call l:Callback()
+ endfor
+ endwhile
+endfunction
diff --git a/autoload/ale/toggle.vim b/autoload/ale/toggle.vim
index 8e642b3f..1311e527 100644
--- a/autoload/ale/toggle.vim
+++ b/autoload/ale/toggle.vim
@@ -13,6 +13,10 @@ function! s:DisablePostamble() abort
if g:ale_set_highlights
call ale#highlight#UpdateHighlights()
endif
+
+ if g:ale_virtualtext_cursor
+ call ale#virtualtext#Clear()
+ endif
endfunction
function! ale#toggle#Toggle() abort
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index bb478957..e7563608 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -87,17 +87,31 @@ function! ale#util#GetFunction(string_or_ref) abort
return a:string_or_ref
endfunction
+" Open the file (at the given line).
+" options['open_in'] can be:
+" current-buffer (default)
+" tab
+" vertical-split
+" horizontal-split
function! ale#util#Open(filename, line, column, options) abort
- if get(a:options, 'open_in_tab', 0)
- call ale#util#Execute('tabedit +' . a:line . ' ' . fnameescape(a:filename))
+ let l:open_in = get(a:options, 'open_in', 'current-buffer')
+ let l:args_to_open = '+' . a:line . ' ' . fnameescape(a:filename)
+
+ if l:open_in is# 'tab'
+ call ale#util#Execute('tabedit ' . l:args_to_open)
+ elseif l:open_in is# 'horizontal-split'
+ call ale#util#Execute('split ' . l:args_to_open)
+ elseif l:open_in is# 'vertical-split'
+ call ale#util#Execute('vsplit ' . l:args_to_open)
elseif bufnr(a:filename) isnot bufnr('')
" Open another file only if we need to.
- call ale#util#Execute('edit +' . a:line . ' ' . fnameescape(a:filename))
+ call ale#util#Execute('edit ' . l:args_to_open)
else
normal! m`
endif
call cursor(a:line, a:column)
+ normal! zz
endfunction
let g:ale#util#error_priority = 5
@@ -408,7 +422,7 @@ function! ale#util#Writefile(buffer, lines, filename) abort
\ ? map(copy(a:lines), 'substitute(v:val, ''\r*$'', ''\r'', '''')')
\ : a:lines
- call writefile(l:corrected_lines, a:filename) " no-custom-checks
+ call writefile(l:corrected_lines, a:filename, 'S') " no-custom-checks
endfunction
if !exists('s:patial_timers')
@@ -456,7 +470,7 @@ endfunction
function! ale#util#FindItemAtCursor(buffer) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
let l:loclist = get(l:info, 'loclist', [])
- let l:pos = getcurpos()
+ let l:pos = getpos('.')
let l:index = ale#util#BinarySearch(l:loclist, a:buffer, l:pos[1], l:pos[2])
let l:loc = l:index >= 0 ? l:loclist[l:index] : {}
diff --git a/autoload/ale/virtualtext.vim b/autoload/ale/virtualtext.vim
index c4ce37dd..d7c5360e 100644
--- a/autoload/ale/virtualtext.vim
+++ b/autoload/ale/virtualtext.vim
@@ -47,7 +47,6 @@ function! ale#virtualtext#ShowMessage(message, hl_group) abort
return
endif
- let l:cursor_position = getcurpos()
let l:line = line('.')
let l:buffer = bufnr('')
let l:prefix = get(g:, 'ale_virtualtext_prefix', '> ')
@@ -82,7 +81,7 @@ function! ale#virtualtext#ShowCursorWarning(...) abort
call ale#virtualtext#Clear()
if !empty(l:loc)
- let l:msg = get(l:loc, 'detail', l:loc.text)
+ let l:msg = l:loc.text
let l:hl_group = 'ALEVirtualTextInfo'
let l:type = get(l:loc, 'type', 'E')
@@ -117,7 +116,7 @@ function! ale#virtualtext#ShowCursorWarningWithDelay() abort
call s:StopCursorTimer()
- let l:pos = getcurpos()[0:2]
+ let l:pos = getpos('.')[0:2]
" Check the current buffer, line, and column number against the last
" recorded position. If the position has actually changed, *then*
diff --git a/doc/ale-asciidoc.txt b/doc/ale-asciidoc.txt
index b6b64fd3..86629fd4 100644
--- a/doc/ale-asciidoc.txt
+++ b/doc/ale-asciidoc.txt
@@ -9,4 +9,10 @@ See |ale-write-good-options|
===============================================================================
+textlint *ale-asciidoc-textlint*
+
+See |ale-text-textlint|
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-chef.txt b/doc/ale-chef.txt
index 5024e279..75e144ec 100644
--- a/doc/ale-chef.txt
+++ b/doc/ale-chef.txt
@@ -3,6 +3,26 @@ ALE Chef Integration *ale-chef-options*
===============================================================================
+cookstyle *ale-chef-cookstyle*
+
+g:ale_chef_cookstyle_options *g:ale_chef_cookstyle_options*
+ *b:ale_chef_cookstyle_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to modify flags given to cookstyle.
+
+
+g:ale_chef_cookstyle_executable *g:ale_chef_cookstyle_executable*
+ *b:ale_chef_cookstyle_executable*
+ Type: |String|
+ Default: `'cookstyle'`
+
+ This variable can be changed to point to the cookstyle binary in case it's
+ not on the $PATH or a specific version/path must be used.
+
+
+===============================================================================
foodcritic *ale-chef-foodcritic*
g:ale_chef_foodcritic_options *g:ale_chef_foodcritic_options*
diff --git a/doc/ale-clojure.txt b/doc/ale-clojure.txt
index a83e336f..2bf00c03 100644
--- a/doc/ale-clojure.txt
+++ b/doc/ale-clojure.txt
@@ -3,6 +3,13 @@ ALE Clojure Integration *ale-clojure-options*
===============================================================================
+clj-kondo *ale-clojure-clj-kondo*
+
+A minimal and opinionated linter for code that sparks joy.
+
+https://github.com/borkdude/clj-kondo
+
+===============================================================================
joker *ale-clojure-joker*
Joker is a small Clojure interpreter and linter written in Go.
diff --git a/doc/ale-cmake.txt b/doc/ale-cmake.txt
index fb46336f..602637b1 100644
--- a/doc/ale-cmake.txt
+++ b/doc/ale-cmake.txt
@@ -22,4 +22,22 @@ g:ale_cmake_cmakelint_options *g:ale_cmake_cmakelint_options*
===============================================================================
+cmake-format *ale-cmake-cmakeformat*
+
+g:ale_cmake_cmakeformat_executable *g:ale_cmake_cmakeformat_executable*
+ *b:ale_cmake_cmakeformat_executable*
+ Type: |String|
+ Default: `'cmakeformat'`
+
+ This variable can be set to change the path the cmake-format.
+
+
+g:ale_cmake_cmakeformat_options *g:ale_cmake_cmakeformat_options*
+ *b:ale_cmake_cmakeformat_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to cmake-format.
+
+===============================================================================
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 f3cae385..ff74b263 100644
--- a/doc/ale-css.txt
+++ b/doc/ale-css.txt
@@ -3,6 +3,14 @@ ALE CSS Integration *ale-css-options*
===============================================================================
+fecs *ale-css-fecs*
+
+`fecs` options for CSS is the same as the options for JavaScript, and both of
+them reads `./.fecsrc` as the default configuration file. See:
+|ale-javascript-fecs|.
+
+
+===============================================================================
prettier *ale-css-prettier*
See |ale-javascript-prettier| for information about the available options.
diff --git a/doc/ale-cuda.txt b/doc/ale-cuda.txt
index 052b3363..0e53f756 100644
--- a/doc/ale-cuda.txt
+++ b/doc/ale-cuda.txt
@@ -22,4 +22,11 @@ g:ale_cuda_nvcc_options *g:ale_cuda_nvcc_options*
This variable can be changed to modify flags given to nvcc.
===============================================================================
+clang-format *ale-cuda-clangformat*
+
+See |ale-c-clangformat| for information about the available options.
+Note that the C options are also used for cuda.
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-development.txt b/doc/ale-development.txt
index 1e168130..9c9f0394 100644
--- a/doc/ale-development.txt
+++ b/doc/ale-development.txt
@@ -1,4 +1,5 @@
*ale-development.txt* For Vim version 8.0.
+*ale-dev*
*ale-development*
ALE Development Documentation
@@ -11,6 +12,7 @@ CONTENTS *ale-development-contents*
3. Coding Standards.....................|ale-coding-standards|
4. Testing ALE..........................|ale-development-tests|
4.1. Writing Linter Tests.............|ale-development-linter-tests|
+ 4.2. Writing Fixer Tests..............|ale-development-fixer-tests|
===============================================================================
1. Introduction *ale-development-introduction*
@@ -143,7 +145,7 @@ Apply the following rules when writing Bash scripts.
* Try to write scripts so they will run on Linux, BSD, or Mac OSX.
===============================================================================
-4. Testing ALE *ale-development-tests*
+4. Testing ALE *ale-development-tests* *ale-dev-tests* *ale-tests*
ALE is tested with a suite of tests executed in Travis CI and AppVeyor. ALE
runs tests with the following versions of Vim in the following environments.
@@ -287,10 +289,10 @@ and should be written like so. >
AssertLinter 'some-command', ale#Escape('some-command') . ' --foo'
Execute(Check chained commands):
- " WithChainResults can be called with 1 or more list for passing output
+ " GivenCommandOutput can be called with 1 or more list for passing output
" to chained commands. The output for each callback defaults to an empty
" list.
- WithChainResults ['v2.1.2']
+ GivenCommandOutput ['v2.1.2']
" Given a List of commands, check all of them.
" Given a String, only the last command in the chain will be checked.
AssertLinter 'some-command', [
@@ -301,7 +303,7 @@ and should be written like so. >
The full list of commands that will be temporarily defined for linter tests
given the above setup are as follows.
-`WithChainResults [...]` - Define output for command chain functions.
+`GivenCommandOutput [...]` - Define output for ale#command#Run.
`AssertLinter executable, command` - Check the executable and command.
`AssertLinterNotExecuted` - Check that linters will not be executed.
`AssertLSPLanguage language` - Check the language given to an LSP server.
@@ -310,5 +312,46 @@ given the above setup are as follows.
`AssertLSPProject project_root` - Check the root given to an LSP server.
`AssertLSPAddress address` - Check the address to an LSP server.
+
+===============================================================================
+4.2 Writing Fixer Tests *ale-development-fixer-tests*
+
+Tests for ALE fixers should go in the `test/fixers` directory, and should
+be written like so. >
+
+ Before:
+ " Load the fixer and set up a series of commands, reset fixer variables,
+ " clear caches, etc.
+ "
+ " Vader's 'Save' command will be called here for fixer variables.
+ call ale#assert#SetUpFixerTest('filetype', 'fixer_name')
+
+ After:
+ " Reset fixers, variables, etc.
+ "
+ " Vader's 'Restore' command will be called here.
+ call ale#assert#TearDownFixerTest()
+
+ Execute(The default command should be correct):
+ " AssertFixer checks the result of the loaded fixer function.
+ AssertFixer {'command': ale#Escape('some-command') . ' --foo'}
+
+ Execute(Check chained commands):
+ " Same as above for linter tests.
+ GivenCommandOutput ['v2.1.2']
+ " Given a List of commands, check all of them.
+ " Given anything else, only the last result will be checked.
+ AssertFixer [
+ \ ale#Escape('some-command') . ' --version',
+ \ {'command': ale#Escape('some-command') . ' --foo'}
+ \]
+<
+The full list of commands that will be temporarily defined for fixer tests
+given the above setup are as follows.
+
+`GivenCommandOutput [...]` - Define output for ale#command#Run.
+`AssertFixer results` - Check the fixer results
+
+
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-elixir.txt b/doc/ale-elixir.txt
index 45c6de1d..5864f728 100644
--- a/doc/ale-elixir.txt
+++ b/doc/ale-elixir.txt
@@ -72,6 +72,18 @@ g:ale_elixir_elixir_ls_config *g:ale_elixir_elixir_ls_config*
\ }
<
Consult the ElixirLS documentation for more information about settings.
+===============================================================================
+credo *ale-elixir-credo*
+
+Credo (https://github.com/rrrene/credo)
+
+g:ale_elixir_credo_strict *g:ale_elixir_credo_strict*
+
+ Type: Integer
+ Default: 0
+
+ Tells credo to run in strict mode or suggest mode. Set variable to 1 to
+ enable --strict mode.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-elm.txt b/doc/ale-elm.txt
index de7d8939..bb7a6132 100644
--- a/doc/ale-elm.txt
+++ b/doc/ale-elm.txt
@@ -29,6 +29,24 @@ g:ale_elm_format_options *g:ale_elm_format_options*
This variable can be set to pass additional options to elm-format.
===============================================================================
+elm-lsp *ale-elm-elm-lsp*
+
+g:ale_elm_lsp_executable *g:ale_elm_lsp_executable*
+ *b:ale_elm_lsp_executable*
+ Type: |String|
+ Default: `'elm-lsp'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_elm_lsp_use_global *g:ale_elm_lsp_use_global*
+ *b:ale_elm_lsp_use_global*
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+===============================================================================
elm-make *ale-elm-elm-make*
g:ale_elm_make_executable *g:ale_elm_make_executable*
diff --git a/doc/ale-go.txt b/doc/ale-go.txt
index 43289bd5..611e3fdd 100644
--- a/doc/ale-go.txt
+++ b/doc/ale-go.txt
@@ -7,7 +7,7 @@ Integration Information
The `gometalinter` linter is disabled by default. ALE enables `gofmt`,
`golint` and `go vet` by default. It also supports `staticcheck`, `go
-build`, `gosimple`, and `golangserver`.
+build`, `gosimple`, `golangserver`.
To enable `gometalinter`, update |g:ale_linters| as appropriate:
>
@@ -31,6 +31,23 @@ g:ale_go_go_executable *g:ale_go_go_options*
===============================================================================
+bingo *ale-go-bingo*
+
+g:ale_go_bingo_executable *g:ale_go_bingo_executable*
+ *b:ale_go_bingo_executable*
+ Type: |String|
+ Default: `'bingo'`
+
+ Location of the bingo binary file.
+
+
+g:ale_go_bingo_options *g:ale_go_bingo_options*
+ *b:ale_go_bingo_options*
+ Type: |String|
+ Default: `''`
+
+
+===============================================================================
gobuild *ale-go-gobuild*
g:ale_go_gobuild_options *g:ale_go_gobuild_options*
@@ -54,6 +71,60 @@ g:ale_go_gofmt_options *g:ale_go_gofmt_options*
===============================================================================
+golangci-lint *ale-go-golangci-lint*
+
+`golangci-lint` is a `lint_file` linter, which only lints files that are
+written to disk. This differs from the default behavior of linting the buffer.
+See: |ale-lint-file|
+
+g:ale_go_golangci_lint_executable *g:ale_go_golangci_lint_executable*
+ *b:ale_go_golangci_lint_executable*
+ Type: |String|
+ Default: `'golangci-lint'`
+
+ The executable that will be run for golangci-lint.
+
+
+g:ale_go_golangci_lint_options *g:ale_go_golangci_lint_options*
+ *b:ale_go_golangci_lint_options*
+ Type: |String|
+ Default: `'--enable-all'`
+
+ This variable can be changed to alter the command-line arguments to the
+ golangci-lint invocation.
+
+
+g:ale_go_golangci_lint_package *g:ale_go_golangci_lint_package*
+ *b:ale_go_golangci_lint_package*
+ Type: |Number|
+ Default: `0`
+
+ When set to `1`, the whole Go package will be checked instead of only the
+ current file.
+
+
+===============================================================================
+golangserver *ale-go-golangserver*
+
+g:ale_go_langserver_executable *g:ale_go_langserver_executable*
+ *b:ale_go_langserver_executable*
+ Type: |String|
+ Default: `'go-langserver'`
+
+ Location of the go-langserver binary file.
+
+
+g:ale_go_langserver_options *g:ale_go_langserver_options*
+ *b:ale_go_langserver_options*
+ Type: |String|
+ Default: `''`
+
+ Additional options passed to the go-langserver command. Note that the
+ `-gocodecompletion` option is ignored because it is handled automatically
+ by the |g:ale_completion_enabled| variable.
+
+
+===============================================================================
golint *ale-go-golint*
g:ale_go_golint_executable *g:ale_go_golint_executable*
@@ -73,17 +144,6 @@ g:ale_go_golint_options *g:ale_go_golint_options*
===============================================================================
-govet *ale-go-govet*
-
-g:ale_go_govet_options *g:ale_go_govet_options*
- *b:ale_go_govet_options*
- Type: |String|
- Default: `''`
-
- This variable can be set to pass additional options to the go vet linter.
-
-
-===============================================================================
gometalinter *ale-go-gometalinter*
`gometalinter` is a `lint_file` linter, which only lints files that are
@@ -122,72 +182,47 @@ g:ale_go_gometalinter_lint_package *g:ale_go_gometalinter_lint_package*
===============================================================================
-staticcheck *ale-go-staticcheck*
+gopls *ale-go-gopls*
-g:ale_go_staticcheck_options *g:ale_go_staticcheck_options*
- *b:ale_go_staticcheck_options*
+g:ale_go_gopls_executable *g:ale_go_gopls_executable*
+ *b:ale_go_gopls_executable*
Type: |String|
- Default: `''`
-
- This variable can be set to pass additional options to the staticcheck
- linter.
+ Default: `'gopls'`
+ Location of the gopls binary file.
-g:ale_go_staticcheck_lint_package *g:ale_go_staticcheck_lint_package*
- *b:ale_go_staticcheck_lint_package*
- Type: |Number|
- Default: `0`
- When set to `1`, the whole Go package will be checked instead of only the
- current file.
+g:ale_go_gopls_options *g:ale_go_gopls_options*
+ *b:ale_go_gopls_options*
+ Type: |String|
+ Default: `''`
===============================================================================
-golangserver *ale-go-golangserver*
-
-g:ale_go_langserver_executable *g:ale_go_langserver_executable*
- *b:ale_go_langserver_executable*
- Type: |String|
- Default: `'go-langserver'`
-
- Location of the go-langserver binary file.
+govet *ale-go-govet*
-g:ale_go_langserver_options *g:ale_go_langserver_options*
- *b:ale_go_langserver_options*
+g:ale_go_govet_options *g:ale_go_govet_options*
+ *b:ale_go_govet_options*
Type: |String|
Default: `''`
- Additional options passed to the go-langserver command. Note that the
- `-gocodecompletion` option is ignored because it is handled automatically
- by the |g:ale_completion_enabled| variable.
+ This variable can be set to pass additional options to the go vet linter.
===============================================================================
-golangci-lint *ale-go-golangci-lint*
-
-`golangci-lint` is a `lint_file` linter, which only lints files that are
-written to disk. This differs from the default behavior of linting the buffer.
-See: |ale-lint-file|
-
-g:ale_go_golangci_lint_executable *g:ale_go_golangci_lint_executable*
- *b:ale_go_golangci_lint_executable*
- Type: |String|
- Default: `'golangci-lint'`
-
- The executable that will be run for golangci-lint.
-
+staticcheck *ale-go-staticcheck*
-g:ale_go_golangci_lint_options *g:ale_go_golangci_lint_options*
- *b:ale_go_golangci_lint_options*
+g:ale_go_staticcheck_options *g:ale_go_staticcheck_options*
+ *b:ale_go_staticcheck_options*
Type: |String|
- Default: `'--enable-all'`
+ Default: `''`
- This variable can be changed to alter the command-line arguments to the
- golangci-lint invocation.
+ This variable can be set to pass additional options to the staticcheck
+ linter.
-g:ale_go_golangci_lint_package *g:ale_go_golangci_lint_package*
- *b:ale_go_golangci_lint_package*
+g:ale_go_staticcheck_lint_package *g:ale_go_staticcheck_lint_package*
+ *b:ale_go_staticcheck_lint_package*
Type: |Number|
Default: `0`
diff --git a/doc/ale-haskell.txt b/doc/ale-haskell.txt
index a4db683b..e2073e37 100644
--- a/doc/ale-haskell.txt
+++ b/doc/ale-haskell.txt
@@ -13,6 +13,16 @@ g:ale_haskell_brittany_executable *g:ale_haskell_brittany_executable*
This variable can be changed to use a different executable for brittany.
===============================================================================
+floskell *ale-haskell-floskell*
+
+g:ale_haskell_floskell_executable *g:ale_haskell_floskell_executable*
+ *b:ale_haskell_floskell_executable*
+ Type: |String|
+ Default: `'floskell'`
+
+ This variable can be changed to use a different executable for floskell.
+
+===============================================================================
ghc *ale-haskell-ghc*
g:ale_haskell_ghc_options *g:ale_haskell_ghc_options*
@@ -108,6 +118,17 @@ g:ale_haskell_stack_build_options *g:ale_haskell_stack_build_options*
programs will be slower unless you separately rebuild them outside of ALE.
===============================================================================
+stack-ghc *ale-haskell-stack-ghc*
+
+g:ale_haskell_stack_ghc_options *g:ale_haskell_stack_ghc_options*
+ *b:ale_haskell_stack_ghc_options*
+ Type: |String|
+ Default: `'-fno-code -v0'`
+
+ This variable can be changed to modify flags given to ghc through `stack
+ ghc`
+
+===============================================================================
stylish-haskell *ale-haskell-stylish-haskell*
g:ale_haskell_stylish_haskell_executable
diff --git a/doc/ale-html.txt b/doc/ale-html.txt
index 1d30929f..5d6b20e2 100644
--- a/doc/ale-html.txt
+++ b/doc/ale-html.txt
@@ -3,6 +3,14 @@ ALE HTML Integration *ale-html-options*
===============================================================================
+fecs *ale-html-fecs*
+
+`fecs` options for HTMl is the same as the options for JavaScript,
+and both of them reads `./.fecsrc` as the default configuration file.
+See: |ale-javascript-fecs|.
+
+
+===============================================================================
htmlhint *ale-html-htmlhint*
g:ale_html_htmlhint_executable *g:ale_html_htmlhint_executable*
diff --git a/doc/ale-java.txt b/doc/ale-java.txt
index 8e40aea0..2805c9c8 100644
--- a/doc/ale-java.txt
+++ b/doc/ale-java.txt
@@ -79,14 +79,19 @@ g:ale_java_pmd_options *g:ale_java_pmd_options*
javalsp *ale-java-javalsp*
To enable Java LSP linter you need to download and build the vscode-javac
-language server from https://github.com/georgewfraser/vscode-javac. Simply
-download the source code and then build the plugin using maven:
+language server from https://github.com/georgewfraser/java-language-server.
+Simply download the source code and then build a distribution:
- mvn package
+ scripts/link_mac.sh
-This generates a out/fat-jar.jar file that contains the language server. To
-let ALE use this language server you need to set the g:ale_java_javalsp_jar
-variable to the absolute path of this jar file.
+or
+
+ scripts/link_windows.sh
+
+This generates a dist/mac or dist/windows directory that contains the
+language server. To let ALE use this language server you need to set the
+g:ale_java_javalsp_executable variable to the absolute path of the java
+executable in this directory.
g:ale_java_javalsp_executable *g:ale_java_javalsp_executable*
*b:ale_java_javalsp_executable*
@@ -95,13 +100,45 @@ g:ale_java_javalsp_executable *g:ale_java_javalsp_executable*
This variable can be changed to use a different executable for java.
-g:ale_java_javalsp_jar *g:ale_java_javalsp_jar*
- *b:ale_java_javalsp_jar*
+
+===============================================================================
+eclipselsp *ale-java-eclipselsp*
+
+To enable Eclipse LSP linter you need to clone and build the eclipse.jdt.ls
+language server from https://github.com/eclipse/eclipse.jdt.ls. Simply
+clone the source code repo and then build the plugin:
+
+ ./mvnw clean verify
+
+Note: currently, the build can only run when launched with JDK 8. JDK 9 or more
+recent versions can be used to run the server though.
+
+After build completes the files required to run the language server will be
+located inside the repository folder `eclipse.jdt.ls`. Please ensure to set
+|g:ale_java_eclipselsp_path| to the absolute path of that folder.
+
+You could customize compiler options and code assists of the server.
+Under your project folder, modify the file `.settings/org.eclipse.jdt.core.prefs`
+with options presented at
+https://help.eclipse.org/neon/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/JavaCore.html.
+
+g:ale_java_eclipselsp_path *g:ale_java_eclipselsp_path*
+ *b:ale_java_eclipselsp_path*
Type: |String|
- Default: `'fat-jar.jar'`
+ Default: `'$HOME/eclipse.jdt.ls'`
+
+ Absolute path to the location of the eclipse.jdt.ls repository folder. Or if
+ you have VSCode extension installed the absolute path to the VSCode extensions
+ folder (e.g. $HOME/.vscode/extensions in Linux).
+
+
+g:ale_java_eclipselsp_executable *g:ale_java_eclipse_executable*
+ *b:ale_java_eclipse_executable*
+ Type: |String|
+ Default: `'java'`
- Path to the location of the vscode-javac language server plugin.
+ This variable can be set to change the executable path used for java.
===============================================================================
diff --git a/doc/ale-javascript.txt b/doc/ale-javascript.txt
index 53a70fd7..ea0a7089 100644
--- a/doc/ale-javascript.txt
+++ b/doc/ale-javascript.txt
@@ -74,6 +74,33 @@ g:ale_javascript_eslint_suppress_missing_config
===============================================================================
+fecs *ale-javascript-fecs*
+
+`fecs` is a lint tool for HTML/CSS/JavaScript, can be installed via:
+
+ `$ npm install --save-dev fecs`
+
+And the configuration file is located at `./fecsrc`, see http://fecs.baidu.com
+for more options.
+
+
+g:ale_javascript_fecs_executable *g:ale_javascript_fecs_executable*
+ *b:ale_javascript_fecs_executable*
+ Type: |String|
+ Default: `'fecs'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_javascript_fecs_use_global *g:ale_javascript_fecs_use_global*
+ *b:ale_javascript_fecs_use_global*
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+===============================================================================
flow *ale-javascript-flow*
g:ale_javascript_flow_executable *g:ale_javascript_flow_executable*
diff --git a/doc/ale-json.txt b/doc/ale-json.txt
index 6a0a9fae..96499a04 100644
--- a/doc/ale-json.txt
+++ b/doc/ale-json.txt
@@ -52,7 +52,21 @@ g:ale_json_fixjson_use_global *g:ale_json_fixjson_use_global*
===============================================================================
jsonlint *ale-json-jsonlint*
-There are no options available.
+g:ale_json_jsonlint_executable *g:ale_json_jsonlint_executable*
+ *b:ale_json_jsonlint_executable*
+
+ Type: |String|
+ Default: `'jsonlint'`
+
+ The executable that will be run for jsonlint.
+
+g:ale_json_jsonlint_use_global *g:ale_json_jsonlint_use_global*
+ *b:ale_json_jsonlint_use_global*
+
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
===============================================================================
diff --git a/doc/ale-kotlin.txt b/doc/ale-kotlin.txt
index 9f9fd16e..4028531f 100644
--- a/doc/ale-kotlin.txt
+++ b/doc/ale-kotlin.txt
@@ -84,9 +84,17 @@ g:ale_kotlin_ktlint_rulesets *g:ale_kotlin_ktlint_rulesets*
This list should contain paths to ruleset jars and/or strings of maven
artifact triples. Example:
>
- let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-rulset.jar',
+ let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-ruleset.jar',
'com.ktlint.rulesets:mycustomrule:1.0.0']
+g:ale_kotlin_ktlint_options *g:ale_kotlin_ktlint_options*
+ Type: |String|
+ Default: `''`
+
+ Additional options to pass to ktlint for both linting and fixing. Example:
+ >
+ let g:ale_kotlin_ktlint_options = '--android'
+
===============================================================================
languageserver *ale-kotlin-languageserver*
diff --git a/doc/ale-latex.txt b/doc/ale-latex.txt
index 87fbd4e8..bedbabcd 100644
--- a/doc/ale-latex.txt
+++ b/doc/ale-latex.txt
@@ -9,4 +9,10 @@ See |ale-write-good-options|
===============================================================================
+textlint *ale-latex-textlint*
+
+See |ale-text-textlint|
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-ocaml.txt b/doc/ale-ocaml.txt
index adf17716..8b644c17 100644
--- a/doc/ale-ocaml.txt
+++ b/doc/ale-ocaml.txt
@@ -51,4 +51,32 @@ g:ale_ocaml_ocamlformat_options *g:ale_ocaml_ocamlformat_options*
This variable can be set to pass additional options to the ocamlformat fixer.
===============================================================================
+ocp-indent *ale-ocaml-ocp-indent*
+
+g:ale_ocaml_ocp_indent_executable *g:ale_ocaml_ocp_indent_executable*
+ *b:ale_ocaml_ocp_indent_executable*
+ Type: |String|
+ Default: `ocp-indent`
+
+ This variable can be set to pass the path of the ocp-indent.
+
+g:ale_ocaml_ocp_indent_options *g:ale_ocaml_ocp_indent_options*
+ *b:ale_ocaml_ocp_indent_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to the ocp-indent.
+
+g:ale_ocaml_ocp_indent_config *g:ale_ocaml_ocp_indent_config*
+ *b:ale_ocaml_ocp_indent_config*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional config to the ocp-indent.
+ Expand after "--config=".
+
+ "ocp-indent" can also be enabled from ocamlformat config.
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-php.txt b/doc/ale-php.txt
index 83bc0fd5..6f53cead 100644
--- a/doc/ale-php.txt
+++ b/doc/ale-php.txt
@@ -154,11 +154,13 @@ g:ale_php_phpstan_executable *g:ale_php_phpstan_executable*
g:ale_php_phpstan_level *g:ale_php_phpstan_level*
*b:ale_php_phpstan_level*
- Type: |Number|
- Default: `4`
+ Type: |String|
+ Default: `''`
This variable controls the rule levels. 0 is the loosest and 4 is the
- strictest.
+ strictest. If this option isn't set, the rule level will be controlled by
+ the configuration file. If no configuration file can be detected, `'4'` will
+ be used instead.
g:ale_php_phpstan_configuration *g:ale_php_phpstan_configuration*
diff --git a/doc/ale-powershell.txt b/doc/ale-powershell.txt
new file mode 100644
index 00000000..c28ef9ea
--- /dev/null
+++ b/doc/ale-powershell.txt
@@ -0,0 +1,77 @@
+===============================================================================
+ALE PowerShell Integration *ale-powershell-options*
+
+
+===============================================================================
+powershell *ale-powershell-powershell*
+
+g:ale_powershell_powershell_executable *g:ale_powershell_powershell_executable*
+ *b:ale_powershell_powershell_executable*
+ Type: String
+ Default: `'pwsh'`
+
+ This variable can be changed to use a different executable for powershell.
+
+>
+ " Use powershell.exe rather than the default pwsh
+ let g:ale_powershell_powershell_executable = 'powershell.exe'
+>
+
+===============================================================================
+psscriptanalyzer *ale-powershell-psscriptanalyzer*
+
+Installation
+-------------------------------------------------------------------------------
+
+Install PSScriptAnalyzer by any means, so long as it can be automatically
+imported in PowerShell.
+Some PowerShell plugins set the filetype of files to `ps1`. To continue using
+these plugins, use the ale_linter_aliases global to alias `ps1` to `powershell`
+
+>
+ " Allow ps1 filetype to work with powershell linters
+ let g:ale_linter_aliases = {'ps1': 'powershell'}
+<
+
+g:ale_powershell_psscriptanalyzer_executable
+*g:ale_powershell_psscriptanalyzer_executable*
+ *b:ale_powershell_psscriptanalyzer_executable*
+ Type: |String|
+ Default: `'pwsh'`
+
+ This variable sets executable used for powershell.
+
+ For example, on Windows you could set powershell to be Windows Powershell:
+>
+ let g:ale_powershell_psscriptanalyzer_executable = 'powershell.exe'
+<
+
+g:ale_powershell_psscriptanalyzer_module
+*g:ale_powershell_psscriptanalyzer_module*
+ *b:ale_powershell_psscriptanalyzer_module*
+ Type: |String
+ Default: `'psscriptanalyzer'`
+
+ This variable sets the name of the psscriptanalyzer module.
+ for psscriptanalyzer invocation.
+
+
+g:ale_powershell_psscriptanalyzer_exclusions
+*g:ale_powershell_psscriptanalyzer_exclusions*
+ *b:ale_powershell_psscriptanalyzer_exclusions*
+ Type: |String|
+ Default: `''`
+
+ Set this variable to exclude test(s) for psscriptanalyzer
+ (-ExcludeRule option). To exclude more than one option, separate them with
+ commas.
+
+>
+ " Suppress Write-Host and Global vars warnings
+ let g:ale_powershell_psscriptanalyzer_exclusions =
+ \ 'PSAvoidUsingWriteHost,PSAvoidGlobalVars'
+<
+
+
+===============================================================================
+ 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 f3f2801a..dd946ad4 100644
--- a/doc/ale-python.txt
+++ b/doc/ale-python.txt
@@ -31,6 +31,9 @@ ALE will look for configuration files with the following filenames. >
pycodestyle.cfg
flake8.cfg
.flake8rc
+ pylama.ini
+ pylintrc
+ .pylintrc
Pipfile
Pipfile.lock
<
@@ -66,6 +69,56 @@ g:ale_python_autopep8_use_global *g:ale_python_autopep8_use_global*
===============================================================================
+bandit *ale-python-bandit*
+
+g:ale_python_bandit_executable *g:ale_python_bandit_executable*
+ *b:ale_python_bandit_executable*
+ Type: |String|
+ Default: `'bandit'`
+
+ See |ale-integrations-local-executables|
+
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `bandit'`.
+
+
+g:ale_python_bandit_options *g:ale_python_bandit_options*
+ *b:ale_python_bandit_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to add command-line arguments to the
+ bandit invocation.
+
+
+g:ale_python_bandit_use_config *g:ale_python_bandit_use_config*
+ *b:ale_python_bandit_use_config*
+ Type: |Number|
+ Default: `1`
+
+ If this variable is true and a `.bandit` file exists in the directory of the
+ file being checked or a parent directory, an `--ini` option is added to the
+ `bandit` command for the nearest `.bandit` file. Set this variable false to
+ disable adding the `--ini` option automatically.
+
+
+g:ale_python_bandit_use_global *g:ale_python_bandit_use_global*
+ *b:ale_python_bandit_use_global*
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_python_bandit_auto_pipenv *g:ale_python_bandit_auto_pipenv*
+ *b:ale_python_bandit_auto_pipenv*
+ Type: |Number|
+ Default: `0`
+
+ Detect whether the file is inside a pipenv, and set the executable to `pipenv`
+ if true. This is overridden by a manually-set executable.
+
+
+===============================================================================
black *ale-python-black*
g:ale_python_black_executable *g:ale_python_black_executable*
@@ -75,7 +128,7 @@ g:ale_python_black_executable *g:ale_python_black_executable*
See |ale-integrations-local-executables|
-autopep8
+
g:ale_python_black_options *g:ale_python_black_options*
*b:ale_python_black_options*
Type: |String|
@@ -92,6 +145,25 @@ g:ale_python_black_use_global *g:ale_python_black_use_global*
See |ale-integrations-local-executables|
+g:ale_python_black_auto_pipenv *g:ale_python_black_auto_pipenv*
+ *b:ale_python_black_auto_pipenv*
+ Type: |Number|
+ Default: `0`
+
+ Detect whether the file is inside a pipenv, and set the executable to `pipenv`
+ if true. This is overridden by a manually-set executable.
+
+g:ale_python_black_change_directory *g:ale_python_black_change_directory*
+ *b:ale_python_black_change_directory*
+ Type: |Number|
+ Default: `1`
+
+ If set to `1`, ALE will switch to the directory the Python file being
+ checked with `black` is in before checking it. This helps `black` find
+ configuration files more easily. This option can be turned off if you want
+ to control the directory Python is executed from yourself.
+
+
===============================================================================
flake8 *ale-python-flake8*
@@ -392,6 +464,60 @@ g:ale_python_pyflakes_auto_pipenv *g:ale_python_pyflakes_auto_pipenv*
===============================================================================
+pylama *ale-python-pylama*
+
+g:ale_python_pylama_change_directory *g:ale_python_pylama_change_directory*
+ *b:ale_python_pylama_change_directory*
+ Type: |Number|
+ Default: `1`
+
+ If set to `1`, `pylama` will be run from a detected project root, per
+ |ale-python-root|. This is useful because `pylama` only searches for
+ configuration files in its current directory and applies file masks using
+ paths relative to its current directory. This option can be turned off if
+ you want to control the directory in which `pylama` is executed.
+
+
+g:ale_python_pylama_executable *g:ale_python_pylama_executable*
+ *b:ale_python_pylama_executable*
+ Type: |String|
+ Default: `'pylama'`
+
+ This variable can be changed to modify the executable used for pylama. Set
+ this to `'pipenv'` to invoke `'pipenv` `run` `pylama'`.
+
+
+g:ale_python_pylama_options *g:ale_python_pylama_options*
+ *b:ale_python_pylama_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to add command-line arguments to the pylama
+ invocation.
+
+
+g:ale_python_pylama_use_global *g:ale_python_pylama_use_global*
+ *b:ale_python_pylama_use_global*
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ This variable controls whether or not ALE will search for pylama in a
+ virtualenv directory first. If this variable is set to `1`, then ALE will
+ always use |g:ale_python_pylama_executable| for the executable path.
+
+ Both variables can be set with `b:` buffer variables instead.
+
+
+g:ale_python_pylama_auto_pipenv *g:ale_python_pylama_auto_pipenv*
+ *b:ale_python_pylama_auto_pipenv*
+ Type: |Number|
+ Default: `0`
+
+ Detect whether the file is inside a pipenv, and set the executable to `pipenv`
+ if true. This is overridden by a manually-set executable.
+
+
+===============================================================================
pylint *ale-python-pylint*
g:ale_python_pylint_change_directory *g:ale_python_pylint_change_directory*
@@ -399,10 +525,12 @@ g:ale_python_pylint_change_directory *g:ale_python_pylint_change_directory*
Type: |Number|
Default: `1`
- If set to `1`, ALE will switch to the directory the Python file being
- checked with `pylint` is in before checking it. This helps `pylint` find
- configuration files more easily. This option can be turned off if you want
- to control the directory Python is executed from yourself.
+ If set to `1`, `pylint` will be run from a detected project root, per
+ |ale-python-root|. Since `pylint` only checks for `pylintrc` in the packages
+ above its current directory before falling back to user and global `pylintrc`
+ files, this is necessary for `pylint` to use a project `pylintrc` file, if
+ present. This option can be turned off if you want to control the directory
+ Python is executed from yourself.
g:ale_python_pylint_executable *g:ale_python_pylint_executable*
@@ -485,6 +613,24 @@ g:ale_python_pyls_auto_pipenv *g:ale_python_pyls_auto_pipenv*
if true. This is overridden by a manually-set executable.
+g:ale_python_pyls_config *g:ale_python_pyls_config*
+ *b:ale_python_pyls_config*
+ Type: |Dictionary|
+ Default: `{}`
+
+ Dictionary with configuration settings for pyls. For example, to disable
+ the pycodestyle linter: >
+ {
+ \ 'pyls': {
+ \ 'plugins': {
+ \ 'pycodestyle': {
+ \ 'enabled': v:false
+ \ }
+ \ }
+ \ },
+ \ }
+<
+
===============================================================================
pyre *ale-python-pyre*
@@ -540,6 +686,15 @@ g:ale_python_vulture_executable *g:ale_python_vulture_executable*
See |ale-integrations-local-executables|
+g:ale_python_vulture_options *g:ale_python_vulture_options*
+ *b:ale_python_vulture_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to add command-line arguments to the vulture
+ invocation.
+
+
g:ale_python_vulture_use_global *g:ale_python_vulture_use_global*
*b:ale_python_vulture_use_global*
Type: |Number|
diff --git a/doc/ale-r.txt b/doc/ale-r.txt
index f85f48fd..b5ccebe5 100644
--- a/doc/ale-r.txt
+++ b/doc/ale-r.txt
@@ -25,5 +25,21 @@ g:ale_r_lintr_lint_package *g:ale_r_lintr_lint_package*
of `lintr::lint`. This prevents erroneous namespace warnings when linting
package files.
+
+===============================================================================
+styler *ale-r-styler*
+
+g:ale_r_styler_options *g:ale_r_styler_options*
+ *b:ale_r_styler_options*
+ Type: |String|
+ Default: `'styler::tidyverse_style'`
+
+ This option can be configured to change the options for styler.
+
+ The value of this option will be used as the `style` argument for the
+ `styler::style_file` options. Consult the styler documentation
+ for more information.
+
+
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-restructuredtext.txt b/doc/ale-restructuredtext.txt
index 02fbc4ad..e308b072 100644
--- a/doc/ale-restructuredtext.txt
+++ b/doc/ale-restructuredtext.txt
@@ -3,6 +3,20 @@ ALE reStructuredText Integration *ale-restructuredtext-options*
===============================================================================
+textlint *ale-restructuredtext-textlint*
+
+To use textlint at reStructuredText, please install `textlint-plugin-rst`.
+https://github.com/jimo1001/textlint-plugin-rst
+>
+ $ npm install textlint-plugin-rst
+
+To install `textlint-plugin-rst`, `docutils-ast-writer` python package
+must be installed.
+See: https://github.com/jimo1001/docutils-ast-writer
+
+See |ale-text-textlint|
+
+===============================================================================
write-good *ale-restructuredtext-write-good*
See |ale-write-good-options|
diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt
index f8a41999..bf971e7c 100644
--- a/doc/ale-ruby.txt
+++ b/doc/ale-ruby.txt
@@ -130,4 +130,26 @@ g:ale_ruby_solargraph_executable *g:ale_ruby_solargraph_executable*
===============================================================================
+standardrb *ale-ruby-standardrb*
+
+g:ale_ruby_standardrb_executable *g:ale_ruby_standardrb_executable*
+ *b:ale_ruby_standardrb_executable*
+ Type: String
+ Default: `'standardrb'`
+
+ Override the invoked standardrb binary. Set this to `'bundle'` to invoke
+ `'bundle` `exec` standardrb'.
+
+
+g:ale_ruby_standardrb_options *g:ale_ruby_standardrb_options*
+ *b:ale_ruby_standardrb_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be change to modify flags given to standardrb.
+
+
+===============================================================================
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt
index 7510dfbd..44a79b18 100644
--- a/doc/ale-rust.txt
+++ b/doc/ale-rust.txt
@@ -164,14 +164,32 @@ g:ale_rust_rls_executable *g:ale_rust_rls_executable*
g:ale_rust_rls_toolchain *g:ale_rust_rls_toolchain*
*b:ale_rust_rls_toolchain*
Type: |String|
- Default: `'nightly'`
+ Default: `''`
This option can be set to change the toolchain used for `rls`. Possible
- values include `'nightly'`, `'beta'`, and `'stable'`.
+ values include `'nightly'`, `'beta'`, `'stable'`, and `''`. When using
+ option `''`, rls will automatically find the default toolchain set by
+ rustup. If you want to use `rls` from a specific toolchain version, you may
+ also use values like `'channel-yyyy-mm-dd-arch-target'` as long as
+ `'rls +{toolchain_name} -V'` runs correctly in your command line.
The `rls` server will only be started once per executable.
+g:ale_rust_rls_config *g:ale_rust_rls_config*
+ *b:ale_rust_rls_config*
+ Type: |Dictionary|
+ Default: `{}`
+
+ Dictionary with configuration settings for rls. For example, to force
+ using clippy as linter: >
+ {
+ \ 'rust': {
+ \ 'clippy_preference': 'on'
+ \ }
+ \ }
+
+
===============================================================================
rustc *ale-rust-rustc*
diff --git a/doc/ale-sass.txt b/doc/ale-sass.txt
index 735f44b2..22d7c472 100644
--- a/doc/ale-sass.txt
+++ b/doc/ale-sass.txt
@@ -1,5 +1,5 @@
===============================================================================
-ALE SASS Integration *ale-sass-options*
+ALE Sass Integration *ale-sass-options*
===============================================================================
diff --git a/doc/ale-sh.txt b/doc/ale-sh.txt
index 7557e522..3eac9038 100644
--- a/doc/ale-sh.txt
+++ b/doc/ale-sh.txt
@@ -61,6 +61,30 @@ g:ale_sh_shellcheck_options *g:ale_sh_shellcheck_options*
let g:ale_sh_shellcheck_options = '-x'
<
+
+g:ale_sh_shellcheck_change_directory *g:ale_sh_shellcheck_change_directory*
+ *b:ale_sh_shellcheck_change_directory*
+ Type: |Number|
+ Default: `1`
+
+ If set to `1`, ALE will switch to the directory the shell file being
+ checked with `shellcheck` is in before checking it. This helps `shellcheck`
+ determine the path to sourced files more easily. This option can be turned
+ off if you want to control the directory `shellcheck` is executed from
+ yourself.
+
+
+g:ale_sh_shellcheck_dialect *g:ale_sh_shellcheck_dialect*
+ *b:ale_sh_shellcheck_dialect*
+ Type: |String|
+ Default: `'auto'`
+
+ This variable specifies the shellcheck dialect (`-s` option). The value
+ `'auto'` causes ALE to detect the dialect automatically, based on the shebang
+ line (if present) or the value of `b:is_bash`, `b:is_sh`, or `b:is_kornshell`
+ (set and used by |sh.vim|).
+
+
g:ale_sh_shellcheck_exclusions *g:ale_sh_shellcheck_exclusions*
*b:ale_sh_shellcheck_exclusions*
Type: |String|
diff --git a/doc/ale-sugarss.txt b/doc/ale-sugarss.txt
new file mode 100644
index 00000000..8e991e54
--- /dev/null
+++ b/doc/ale-sugarss.txt
@@ -0,0 +1,31 @@
+===============================================================================
+ALE SugarSS Integration *ale-sugarss-options*
+
+
+===============================================================================
+stylelint *ale-sugarss-stylelint*
+
+g:ale_sugarss_stylelint_executable *g:ale_sugarss_stylelint_executable*
+ *b:ale_sugarss_stylelint_executable*
+ Type: |String|
+ Default: `'stylelint'`
+
+ See |ale-integrations-local-executables|
+
+g:ale_sugarss_stylelint_options *g:ale_sugarss_stylelint_options*
+ *b:ale_sugarss_stylelint_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to stylelint.
+
+g:ale_sugarss_stylelint_use_global *g:ale_sugarss_stylelint_use_global*
+ *b:ale_sugarss_stylelint_use_global*
+ Type: |String|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
new file mode 100644
index 00000000..d6fbafa6
--- /dev/null
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -0,0 +1,484 @@
+*ale-supported-languages-and-tools.txt* For Vim version 8.0.
+*ale-supported-list*
+
+ALE Supported Languages and Tools
+
+===============================================================================
+
+The following languages and tools are supported by ALE.
+
+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|
+
+* Ada
+ * `gcc`
+* Ansible
+ * `ansible-lint`
+* API Blueprint
+ * `drafter`
+* AsciiDoc
+ * `alex`!!
+ * `proselint`
+ * `redpen`
+ * `textlint`
+ * `vale`
+ * `write-good`
+* ASM
+ * `gcc`
+* Awk
+ * `gawk`
+* Bash
+ * `language-server`
+ * `shell` (-n flag)
+ * `shellcheck`
+ * `shfmt`
+* BibTeX
+ * `bibclean`
+* Bourne Shell
+ * `shell` (-n flag)
+ * `shellcheck`
+ * `shfmt`
+* C
+ * `ccls`
+ * `clang`
+ * `clangd`
+ * `clang-format`
+ * `clangtidy`!!
+ * `cppcheck`
+ * `cpplint`!!
+ * `cquery`
+ * `flawfinder`
+ * `gcc`
+ * `uncrustify`
+* C#
+ * `mcs`
+ * `mcsc`!!
+ * `uncrustify`
+* C++ (filetype cpp)
+ * `ccls`
+ * `clang`
+ * `clangcheck`!!
+ * `clangd`
+ * `clang-format`
+ * `clangtidy`!!
+ * `clazy`!!
+ * `cppcheck`
+ * `cpplint`!!
+ * `cquery`
+ * `flawfinder`
+ * `gcc`
+ * `uncrustify`
+* Chef
+ * `cookstyle`
+ * `foodcritic`
+* Clojure
+ * `clj-kondo`
+ * `joker`
+* CloudFormation
+ * `cfn-python-lint`
+* CMake
+ * `cmake-format`
+ * `cmakelint`
+* CoffeeScript
+ * `coffee`
+ * `coffeelint`
+* Crystal
+ * `ameba`!!
+ * `crystal`!!
+* CSS
+ * `csslint`
+ * `fecs`
+ * `prettier`
+ * `stylelint`
+* Cucumber
+ * `cucumber`
+* CUDA
+ * `nvcc`!!
+* Cypher
+ * `cypher-lint`
+* Cython (pyrex filetype)
+ * `cython`
+* D
+ * `dls`
+ * `dmd`
+ * `uncrustify`
+* Dafny
+ * `dafny`!!
+* Dart
+ * `dartanalyzer`!!
+ * `dartfmt`!!
+ * `language_server`
+* Dockerfile
+ * `dockerfile_lint`
+ * `hadolint`
+* Elixir
+ * `credo`
+ * `dialyxir`
+ * `dogma`
+ * `elixir-ls`
+ * `mix`!!
+* Elm
+ * `elm-format`
+ * `elm-lsp`
+ * `elm-make`
+* Erb
+ * `erb`
+ * `erubi`
+ * `erubis`
+ * `ruumba`
+* Erlang
+ * `erlc`
+ * `SyntaxErl`
+* Fish
+ * `fish` (-n flag)
+* Fortran
+ * `gcc`
+ * `language_server`
+* Fountain
+ * `proselint`
+* FusionScript
+ * `fusion-lint`
+* Git Commit Messages
+ * `gitlint`
+* GLSL
+ * glslang
+ * `glslls`
+* Go
+ * `bingo`
+ * `go build`!!
+ * `gofmt`
+ * `goimports`
+ * `golangci-lint`!!
+ * `golangserver`
+ * `golint`
+ * `gometalinter`!!
+ * `go mod`!!
+ * `gopls`
+ * `gosimple`!!
+ * `gotype`!!
+ * `go vet`!!
+ * `staticcheck`!!
+* GraphQL
+ * `eslint`
+ * `gqlint`
+ * `prettier`
+* Hack
+ * `hack`
+ * `hackfmt`
+ * `hhast`
+* Haml
+ * `haml-lint`
+* Handlebars
+ * `ember-template-lint`
+* Haskell
+ * `brittany`
+ * `cabal-ghc`
+ * `floskell`
+ * `ghc`
+ * `ghc-mod`
+ * `hdevtools`
+ * `hfmt`
+ * `hie`
+ * `hlint`
+ * `stack-build`!!
+ * `stack-ghc`
+ * `stylish-haskell`
+* HCL
+ * `terraform-fmt`
+* HTML
+ * `alex`!!
+ * `fecs`
+ * `HTMLHint`
+ * `prettier`
+ * `proselint`
+ * `tidy`
+ * `write-good`
+* Idris
+ * `idris`
+* ISPC
+ * `ispc`!!
+* Java
+ * `checkstyle`
+ * `eclipselsp`
+ * `google-java-format`
+ * `javac`
+ * `javalsp`
+ * `PMD`
+ * `uncrustify`
+* JavaScript
+ * `eslint`
+ * `fecs`
+ * `flow`
+ * `jscs`
+ * `jshint`
+ * `prettier`
+ * `prettier-eslint`
+ * `prettier-standard`
+ * `standard`
+ * `tsserver`
+ * `xo`
+* JSON
+ * `fixjson`
+ * `jq`
+ * `jsonlint`
+ * `prettier`
+* Julia
+ * `languageserver`
+* Kotlin
+ * `kotlinc`!!
+ * `ktlint`!!
+ * `languageserver`
+* LaTeX (tex)
+ * `alex`!!
+ * `chktex`
+ * `lacheck`
+ * `proselint`
+ * `redpen`
+ * `textlint`
+ * `vale`
+ * `write-good`
+* Less
+ * `lessc`
+ * `prettier`
+ * `stylelint`
+* LLVM
+ * `llc`
+* Lua
+ * `luac`
+ * `luacheck`
+* Mail
+ * `alex`!!
+ * `languagetool`!!
+ * `proselint`
+ * `vale`
+* Make
+ * `checkmake`
+* Markdown
+ * `alex`!!
+ * `languagetool`!!
+ * `markdownlint`!!
+ * `mdl`
+ * `prettier`
+ * `proselint`
+ * `redpen`
+ * `remark-lint`
+ * `textlint`
+ * `vale`
+ * `write-good`
+* MATLAB
+ * `mlint`
+* Mercury
+ * `mmc`!!
+* NASM
+ * `nasm`!!
+* Nim
+ * `nim check`!!
+* nix
+ * `nix-instantiate`
+* nroff
+ * `alex`!!
+ * `proselint`
+ * `write-good`
+* Objective-C
+ * `ccls`
+ * `clang`
+ * `clangd`
+ * `uncrustify`
+* Objective-C++
+ * `clang`
+ * `clangd`
+ * `uncrustify`
+* OCaml
+ * `merlin` (see |ale-ocaml-merlin|)
+ * `ocamlformat`
+ * `ocp-indent`
+ * `ols`
+* Pawn
+ * `uncrustify`
+* Perl
+ * `perl -c`
+ * `perl-critic`
+ * `perltidy`
+* Perl6
+ * `perl6 -c`
+* PHP
+ * `langserver`
+ * `phan`
+ * `phpcbf`
+ * `phpcs`
+ * `php-cs-fixer`
+ * `php -l`
+ * `phpmd`
+ * `phpstan`
+ * `psalm`!!
+* PO
+ * `alex`!!
+ * `msgfmt`
+ * `proselint`
+ * `write-good`
+* Pod
+ * `alex`!!
+ * `proselint`
+ * `write-good`
+* Pony
+ * `ponyc`
+* PowerShell
+ * `powershell`
+ * `psscriptanalyzer`
+* Prolog
+ * `swipl`
+* proto
+ * `protoc-gen-lint`
+* Pug
+ * `pug-lint`
+* Puppet
+ * `languageserver`
+ * `puppet`
+ * `puppet-lint`
+* Python
+ * `autopep8`
+ * `bandit`
+ * `black`
+ * `flake8`
+ * `isort`
+ * `mypy`
+ * `prospector`
+ * `pycodestyle`
+ * `pydocstyle`
+ * `pyflakes`
+ * `pylama`!!
+ * `pylint`!!
+ * `pyls`
+ * `pyre`
+ * `vulture`!!
+ * `yapf`
+* QML
+ * `qmlfmt`
+ * `qmllint`
+* R
+ * `lintr`
+ * `styler`
+* Racket
+ * `raco`
+* ReasonML
+ * `merlin`
+ * `ols`
+ * `refmt`
+* reStructuredText
+ * `alex`!!
+ * `proselint`
+ * `redpen`
+ * `rstcheck`
+ * `textlint`
+ * `vale`
+ * `write-good`
+* Re:VIEW
+ * `redpen`
+* RPM spec
+ * `rpmlint`
+* Ruby
+ * `brakeman`
+ * `rails_best_practices`!!
+ * `reek`
+ * `rubocop`
+ * `ruby`
+ * `rufo`
+ * `solargraph`
+ * `standardrb`
+* Rust
+ * `cargo`!!
+ * `rls`
+ * `rustc` (see |ale-integration-rust|)
+ * `rustfmt`
+* Sass
+ * `sass-lint`
+ * `stylelint`
+* Scala
+ * `fsc`
+ * `sbtserver`
+ * `scalac`
+ * `scalafmt`
+ * `scalastyle`
+* SCSS
+ * `prettier`
+ * `sass-lint`
+ * `scss-lint`
+ * `stylelint`
+* Slim
+ * `slim-lint`
+* SML
+ * `smlnj`
+* Solidity
+ * `solhint`
+ * `solium`
+* SQL
+ * `sqlfmt`
+ * `sqlint`
+* Stylus
+ * `stylelint`
+* SugarSS
+ * `stylelint`
+* Swift
+ * `sourcekit-lsp`
+ * `swiftformat`
+ * `swiftlint`
+* Tcl
+ * `nagelfar`!!
+* Terraform
+ * `fmt`
+ * `tflint`
+* Texinfo
+ * `alex`!!
+ * `proselint`
+ * `write-good`
+* Text^
+ * `alex`!!
+ * `languagetool`!!
+ * `proselint`
+ * `redpen`
+ * `textlint`
+ * `vale`
+ * `write-good`
+* Thrift
+ * `thrift`
+* TypeScript
+ * `eslint`
+ * `fecs`
+ * `prettier`
+ * `tslint`
+ * `tsserver`
+ * `typecheck`
+* VALA
+ * `uncrustify`
+* Verilog
+ * `iverilog`
+ * `verilator`
+ * `vlog`
+ * `xvlog`
+* VHDL
+ * `ghdl`
+ * `vcom`
+ * `xvhdl`
+* Vim
+ * `vint`
+* Vim help^
+ * `alex`!!
+ * `proselint`
+ * `write-good`
+* Vue
+ * `prettier`
+ * `vls`
+* XHTML
+ * `alex`!!
+ * `proselint`
+ * `write-good`
+* XML
+ * `xmllint`
+* YAML
+ * `prettier`
+ * `swaglint`
+ * `yamllint`
+* YANG
+ * `yang-lsp`
diff --git a/doc/ale-swift.txt b/doc/ale-swift.txt
new file mode 100644
index 00000000..8fa0c06c
--- /dev/null
+++ b/doc/ale-swift.txt
@@ -0,0 +1,21 @@
+===============================================================================
+ALE Swift Integration *ale-swift-options*
+
+
+===============================================================================
+sourcekitlsp *ale-swift-sourcekitlsp*
+
+To enable the SourceKit-LSP you need to install and build the executable as
+described here: https://github.com/apple/sourcekit-lsp#building-sourcekit-lsp
+
+
+g:ale_sourcekit_lsp_executable *g:ale_sourcekit_lsp_executable*
+ *b:ale_sourcekit_lsp_executable*
+ Type: |String|
+ Default: `'sourcekit-lsp'`
+
+ See |ale-integrations-local-executables|
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
+
diff --git a/doc/ale-tex.txt b/doc/ale-tex.txt
index 24aa3112..b1b09117 100644
--- a/doc/ale-tex.txt
+++ b/doc/ale-tex.txt
@@ -32,5 +32,26 @@ g:ale_lacheck_executable *g:ale_lacheck_executable*
This variable can be changed to change the path to lacheck.
+
+===============================================================================
+latexindent *ale-tex-latexindent*
+
+g:ale_tex_latexindent_executable *g:ale_tex_latexindent_executable*
+ *b:ale_tex_latexindent_executable*
+ Type: |String|
+ Default: `'latexindent'`
+
+ This variable can be changed to change the path to latexindent.
+
+
+g:ale_tex_latexindent_options *g:ale_tex_latexindent_options*
+ *b:ale_tex_latexindent_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to modify flags given to latexindent.
+
+
+
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-verilog.txt b/doc/ale-verilog.txt
index 2b8ce5e2..94b820b8 100644
--- a/doc/ale-verilog.txt
+++ b/doc/ale-verilog.txt
@@ -3,7 +3,7 @@ ALE Verilog/SystemVerilog Integration *ale-verilog-options*
===============================================================================
-ALE can use two different linters for Verilog HDL:
+ALE can use four different linters for Verilog HDL:
iverilog:
Using `iverilog -t null -Wall`
@@ -11,6 +11,12 @@ ALE can use two different linters for Verilog HDL:
verilator
Using `verilator --lint-only -Wall`
+ ModelSim/Questa
+ Using `vlog -quiet -lint`
+
+ Vivado
+ Using `xvlog`
+
By default, both 'verilog' and 'systemverilog' filetypes are checked.
You can limit 'systemverilog' files to be checked using only 'verilator' by
@@ -20,6 +26,20 @@ defining 'g:ale_linters' variable:
\ let g:ale_linters = {'systemverilog' : ['verilator'],}
<
+Linters/compilers that utilize a "work" directory for analyzing designs- such
+as ModelSim and Vivado- can be passed the location of these directories as
+part of their respective option strings listed below. This is useful for
+holistic analysis of a file (e.g. a design with components, packages, or other
+code defined external to the current file as part of a larger project) or
+when wanting to simply pass an alternative location for the auto-generated
+work directories (such as '/tmp') so as to not muddle the current directory.
+Since these type of linters often use this work directory for holding compiled
+design data as part of a single build process, they sometimes cannot handle
+the frequent, asynchronous application launches when linting while text is
+changing. This can happen in the form of hangs or crashes. To help prevent
+this when using these linters, it may help to run linting less frequently; for
+example, only when a file is saved.
+
===============================================================================
iverilog *ale-verilog-iverilog*
@@ -39,5 +59,44 @@ g:ale_verilog_verilator_options *g:ale_verilog_verilator_options*
For example `'-sv --default-language "1800-2012"'` if you want to enable
SystemVerilog parsing and select the 2012 version of the language.
+
+===============================================================================
+vlog *ale-verilog-vlog*
+
+g:ale_verilog_vlog_executable *g:ale_verilog_vlog_executable*
+ *b:ale_verilog_vlog_executable*
+ Type: |String|
+ Default: `'vlog'`
+
+ This variable can be changed to the path to the 'vlog' executable.
+
+
+g:ale_verilog_vlog_options *g:ale_verilog_vlog_options*
+ *b:ale_verilog_vlog_options*
+ Type: |String|
+ Default: `'-quiet -lint'`
+
+ This variable can be changed to modify the flags/options passed to 'vlog'.
+
+
+===============================================================================
+xvlog *ale-verilog-xvlog*
+
+g:ale_verilog_xvlog_executable *g:ale_verilog_xvlog_executable*
+ *b:ale_verilog_xvlog_executable*
+ Type: |String|
+ Default: `'xvlog'`
+
+ This variable can be changed to the path to the 'xvlog' executable.
+
+
+g:ale_verilog_xvlog_options *g:ale_verilog_xvlog_options*
+ *b:ale_verilog_xvlog_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to modify the flags/options passed to 'xvlog'.
+
+
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-vhdl.txt b/doc/ale-vhdl.txt
new file mode 100644
index 00000000..3fea947d
--- /dev/null
+++ b/doc/ale-vhdl.txt
@@ -0,0 +1,92 @@
+===============================================================================
+ALE VHDL Integration *ale-vhdl-options*
+
+
+===============================================================================
+ALE can use three different linters for VHDL:
+
+ iverilog:
+ Using `iverilog -t null -Wall`
+
+ ModelSim/Questa
+ Using `vcom -2008 -quiet -lint`
+
+ Vivado
+ Using `xvhdl --2008`
+
+Note all linters default to VHDL-2008 support. This, and other options, can be
+changed with each linter's respective option variable.
+
+Linters/compilers that utilize a "work" directory for analyzing designs- such
+as ModelSim and Vivado- can be passed the location of these directories as
+part of their respective option strings listed below. This is useful for
+holistic analysis of a file (e.g. a design with components, packages, or other
+code defined external to the current file as part of a larger project) or
+when wanting to simply pass an alternative location for the auto-generated
+work directories (such as '/tmp') so as to not muddle the current directory.
+Since these type of linters often use this work directory for holding compiled
+design data as part of a single build process, they sometimes cannot handle
+the frequent, asynchronous application launches when linting while text is
+changing. This can happen in the form of hangs or crashes. To help prevent
+this when using these linters, it may help to run linting less frequently; for
+example, only when a file is saved.
+
+===============================================================================
+ghdl *ale-vhdl-ghdl*
+
+g:ale_vhdl_ghdl_executable *g:ale_vhdl_ghdl_executable*
+ *b:ale_vhdl_ghdl_executable*
+ Type: |String|
+ Default: `'ghdl'`
+
+ This variable can be changed to the path to the 'ghdl' executable.
+
+
+g:ale_vhdl_ghdl_options *g:ale_vhdl_ghdl_options*
+ *b:ale_vhdl_ghdl_options*
+ Type: |String|
+ Default: `'--std=08'`
+
+ This variable can be changed to modify the flags/options passed to 'ghdl'.
+
+
+===============================================================================
+vcom *ale-vhdl-vcom*
+
+g:ale_vhdl_vcom_executable *g:ale_vhdl_vcom_executable*
+ *b:ale_vhdl_vcom_executable*
+ Type: |String|
+ Default: `'vcom'`
+
+ This variable can be changed to the path to the 'vcom' executable.
+
+
+g:ale_vhdl_vcom_options *g:ale_vhdl_vcom_options*
+ *b:ale_vhdl_vcom_options*
+ Type: |String|
+ Default: `'-2008 -quiet -lint'`
+
+ This variable can be changed to modify the flags/options passed to 'vcom'.
+
+
+===============================================================================
+xvhdl *ale-vhdl-xvhdl*
+
+g:ale_vhdl_xvhdl_executable *g:ale_vhdl_xvhdl_executable*
+ *b:ale_vhdl_xvhdl_executable*
+ Type: |String|
+ Default: `'xvhdl'`
+
+ This variable can be changed to the path to the 'xvhdl' executable.
+
+
+g:ale_vhdl_xvhdl_options *g:ale_vhdl_xvhdl_options*
+ *b:ale_vhdl_xvhdl_options*
+ Type: |String|
+ Default: `'--2008'`
+
+ This variable can be changed to modify the flags/options passed to 'xvhdl'.
+
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale.txt b/doc/ale.txt
index 68d685d9..b777e575 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -1,4 +1,4 @@
-*ale.txt* For Vim version 8.0.
+*ale.txt* Plugin to lint and fix files asynchronously
*ale*
ALE - Asynchronous Lint Engine
@@ -14,351 +14,17 @@ CONTENTS *ale-contents*
5. Language Server Protocol Support.....|ale-lsp|
5.1 Completion........................|ale-completion|
5.2 Go To Definition..................|ale-go-to-definition|
- 5.3 Find References...................|ale-find-references|
- 5.4 Hovering..........................|ale-hover|
- 5.5 Symbol Search.....................|ale-symbol-search|
+ 5.3 Go To Type Definition.............|ale-go-to-type-definition|
+ 5.4 Find References...................|ale-find-references|
+ 5.5 Hovering..........................|ale-hover|
+ 5.6 Symbol Search.....................|ale-symbol-search|
6. Global Options.......................|ale-options|
6.1 Highlights........................|ale-highlights|
- 6.2 Options for write-good Linter.....|ale-write-good-options|
- 7. Integration Documentation............|ale-integrations|
- ada...................................|ale-ada-options|
- gcc.................................|ale-ada-gcc|
- ansible...............................|ale-ansible-options|
- ansible-lint........................|ale-ansible-ansible-lint|
- asciidoc..............................|ale-asciidoc-options|
- write-good..........................|ale-asciidoc-write-good|
- asm...................................|ale-asm-options|
- gcc.................................|ale-asm-gcc|
- awk...................................|ale-awk-options|
- gawk................................|ale-awk-gawk|
- bib...................................|ale-bib-options|
- bibclean............................|ale-bib-bibclean|
- c.....................................|ale-c-options|
- clang...............................|ale-c-clang|
- clangd..............................|ale-c-clangd|
- clang-format........................|ale-c-clangformat|
- clangtidy...........................|ale-c-clangtidy|
- cppcheck............................|ale-c-cppcheck|
- cquery..............................|ale-c-cquery|
- flawfinder..........................|ale-c-flawfinder|
- gcc.................................|ale-c-gcc|
- uncrustify..........................|ale-c-uncrustify|
- ccls................................|ale-c-ccls|
- chef..................................|ale-chef-options|
- foodcritic..........................|ale-chef-foodcritic|
- clojure...............................|ale-clojure-options|
- joker...............................|ale-clojure-joker|
- cloudformation........................|ale-cloudformation-options|
- cfn-python-lint.....................|ale-cloudformation-cfn-python-lint|
- cmake.................................|ale-cmake-options|
- cmakelint...........................|ale-cmake-cmakelint|
- cpp...................................|ale-cpp-options|
- clang...............................|ale-cpp-clang|
- clangd..............................|ale-cpp-clangd|
- clangcheck..........................|ale-cpp-clangcheck|
- clang-format........................|ale-cpp-clangformat|
- clangtidy...........................|ale-cpp-clangtidy|
- clazy...............................|ale-cpp-clazy|
- cppcheck............................|ale-cpp-cppcheck|
- cpplint.............................|ale-cpp-cpplint|
- cquery..............................|ale-cpp-cquery|
- flawfinder..........................|ale-cpp-flawfinder|
- gcc.................................|ale-cpp-gcc|
- uncrustify..........................|ale-cpp-uncrustify|
- ccls................................|ale-cpp-ccls|
- c#....................................|ale-cs-options|
- mcs.................................|ale-cs-mcs|
- mcsc................................|ale-cs-mcsc|
- uncrustify..........................|ale-cs-uncrustify|
- css...................................|ale-css-options|
- prettier............................|ale-css-prettier|
- stylelint...........................|ale-css-stylelint|
- cuda..................................|ale-cuda-options|
- nvcc................................|ale-cuda-nvcc|
- d.....................................|ale-d-options|
- dls.................................|ale-d-dls|
- uncrustify..........................|ale-d-uncrustify|
- dart..................................|ale-dart-options|
- dartanalyzer........................|ale-dart-dartanalyzer|
- dartfmt.............................|ale-dart-dartfmt|
- dockerfile............................|ale-dockerfile-options|
- dockerfile_lint.....................|ale-dockerfile-dockerfile_lint|
- hadolint............................|ale-dockerfile-hadolint|
- elixir................................|ale-elixir-options|
- mix.................................|ale-elixir-mix|
- mix_format..........................|ale-elixir-mix-format|
- dialyxir............................|ale-elixir-dialyxir|
- elixir-ls...........................|ale-elixir-elixir-ls|
- elm...................................|ale-elm-options|
- elm-format..........................|ale-elm-elm-format|
- elm-make............................|ale-elm-elm-make|
- erlang................................|ale-erlang-options|
- erlc................................|ale-erlang-erlc|
- syntaxerl...........................|ale-erlang-syntaxerl|
- eruby.................................|ale-eruby-options|
- ruumba..............................|ale-eruby-ruumba|
- fish..................................|ale-fish-options|
- fortran...............................|ale-fortran-options|
- gcc.................................|ale-fortran-gcc|
- language_server.....................|ale-fortran-language-server|
- fountain..............................|ale-fountain-options|
- fusionscript..........................|ale-fuse-options|
- fusion-lint.........................|ale-fuse-fusionlint|
- git commit............................|ale-gitcommit-options|
- gitlint.............................|ale-gitcommit-gitlint|
- glsl..................................|ale-glsl-options|
- glslang.............................|ale-glsl-glslang|
- glslls..............................|ale-glsl-glslls|
- go....................................|ale-go-options|
- gobuild.............................|ale-go-gobuild|
- gofmt...............................|ale-go-gofmt|
- golint..............................|ale-go-golint|
- govet...............................|ale-go-govet|
- gometalinter........................|ale-go-gometalinter|
- staticcheck.........................|ale-go-staticcheck|
- golangserver........................|ale-go-golangserver|
- golangci-lint.......................|ale-go-golangci-lint|
- graphql...............................|ale-graphql-options|
- eslint..............................|ale-graphql-eslint|
- gqlint..............................|ale-graphql-gqlint|
- prettier............................|ale-graphql-prettier|
- hack..................................|ale-hack-options|
- hack................................|ale-hack-hack|
- hackfmt.............................|ale-hack-hackfmt|
- hhast...............................|ale-hack-hhast|
- handlebars............................|ale-handlebars-options|
- ember-template-lint.................|ale-handlebars-embertemplatelint|
- haskell...............................|ale-haskell-options|
- brittany............................|ale-haskell-brittany|
- ghc.................................|ale-haskell-ghc|
- ghc-mod.............................|ale-haskell-ghc-mod|
- cabal-ghc...........................|ale-haskell-cabal-ghc|
- hdevtools...........................|ale-haskell-hdevtools|
- hfmt................................|ale-haskell-hfmt|
- hlint...............................|ale-haskell-hlint|
- stack-build.........................|ale-haskell-stack-build|
- stylish-haskell.....................|ale-haskell-stylish-haskell|
- hie.................................|ale-haskell-hie|
- hcl...................................|ale-hcl-options|
- terraform-fmt.......................|ale-hcl-terraform-fmt|
- html..................................|ale-html-options|
- htmlhint............................|ale-html-htmlhint|
- tidy................................|ale-html-tidy|
- prettier............................|ale-html-prettier|
- stylelint...........................|ale-html-stylelint|
- write-good..........................|ale-html-write-good|
- idris.................................|ale-idris-options|
- idris...............................|ale-idris-idris|
- ispc..................................|ale-ispc-options|
- ispc................................|ale-ispc-ispc|
- java..................................|ale-java-options|
- checkstyle..........................|ale-java-checkstyle|
- javac...............................|ale-java-javac|
- google-java-format..................|ale-java-google-java-format|
- pmd.................................|ale-java-pmd|
- javalsp.............................|ale-java-javalsp|
- uncrustify..........................|ale-java-uncrustify|
- javascript............................|ale-javascript-options|
- eslint..............................|ale-javascript-eslint|
- flow................................|ale-javascript-flow|
- importjs............................|ale-javascript-importjs|
- jscs................................|ale-javascript-jscs|
- jshint..............................|ale-javascript-jshint|
- prettier............................|ale-javascript-prettier|
- prettier-eslint.....................|ale-javascript-prettier-eslint|
- prettier-standard...................|ale-javascript-prettier-standard|
- standard............................|ale-javascript-standard|
- xo..................................|ale-javascript-xo|
- json..................................|ale-json-options|
- fixjson.............................|ale-json-fixjson|
- jsonlint............................|ale-json-jsonlint|
- jq..................................|ale-json-jq|
- prettier............................|ale-json-prettier|
- julia.................................|ale-julia-options|
- languageserver......................|ale-julia-languageserver|
- kotlin................................|ale-kotlin-options|
- kotlinc.............................|ale-kotlin-kotlinc|
- ktlint..............................|ale-kotlin-ktlint|
- languageserver......................|ale-kotlin-languageserver|
- latex.................................|ale-latex-options|
- write-good..........................|ale-latex-write-good|
- less..................................|ale-less-options|
- lessc...............................|ale-less-lessc|
- prettier............................|ale-less-prettier|
- stylelint...........................|ale-less-stylelint|
- llvm..................................|ale-llvm-options|
- llc.................................|ale-llvm-llc|
- lua...................................|ale-lua-options|
- luac................................|ale-lua-luac|
- luacheck............................|ale-lua-luacheck|
- markdown..............................|ale-markdown-options|
- mdl.................................|ale-markdown-mdl|
- prettier............................|ale-markdown-prettier|
- remark-lint.........................|ale-markdown-remark-lint|
- textlint............................|ale-markdown-textlint|
- write-good..........................|ale-markdown-write-good|
- mercury...............................|ale-mercury-options|
- mmc.................................|ale-mercury-mmc|
- nasm..................................|ale-nasm-options|
- nasm................................|ale-nasm-nasm|
- nroff.................................|ale-nroff-options|
- write-good..........................|ale-nroff-write-good|
- objc..................................|ale-objc-options|
- clang...............................|ale-objc-clang|
- clangd..............................|ale-objc-clangd|
- uncrustify..........................|ale-objc-uncrustify|
- ccls................................|ale-objc-ccls|
- objcpp................................|ale-objcpp-options|
- clang...............................|ale-objcpp-clang|
- clangd..............................|ale-objcpp-clangd|
- uncrustify..........................|ale-objcpp-uncrustify|
- ocaml.................................|ale-ocaml-options|
- merlin..............................|ale-ocaml-merlin|
- ols.................................|ale-ocaml-ols|
- ocamlformat.........................|ale-ocaml-ocamlformat|
- pawn..................................|ale-pawn-options|
- uncrustify..........................|ale-pawn-uncrustify|
- perl..................................|ale-perl-options|
- perl................................|ale-perl-perl|
- perlcritic..........................|ale-perl-perlcritic|
- perltidy............................|ale-perl-perltidy|
- perl6.................................|ale-perl6-options|
- perl6...............................|ale-perl6-perl6|
- php...................................|ale-php-options|
- langserver..........................|ale-php-langserver|
- phan................................|ale-php-phan|
- phpcbf..............................|ale-php-phpcbf|
- phpcs...............................|ale-php-phpcs|
- phpmd...............................|ale-php-phpmd|
- phpstan.............................|ale-php-phpstan|
- psalm...............................|ale-php-psalm|
- php-cs-fixer........................|ale-php-php-cs-fixer|
- php.................................|ale-php-php|
- po....................................|ale-po-options|
- write-good..........................|ale-po-write-good|
- pod...................................|ale-pod-options|
- write-good..........................|ale-pod-write-good|
- pony..................................|ale-pony-options|
- ponyc...............................|ale-pony-ponyc|
- prolog................................|ale-prolog-options|
- swipl...............................|ale-prolog-swipl|
- proto.................................|ale-proto-options|
- protoc-gen-lint.....................|ale-proto-protoc-gen-lint|
- pug...................................|ale-pug-options|
- puglint.............................|ale-pug-puglint|
- puppet................................|ale-puppet-options|
- puppet..............................|ale-puppet-puppet|
- puppetlint..........................|ale-puppet-puppetlint|
- puppet-languageserver...............|ale-puppet-languageserver|
- pyrex (cython)........................|ale-pyrex-options|
- cython..............................|ale-pyrex-cython|
- python................................|ale-python-options|
- autopep8............................|ale-python-autopep8|
- black...............................|ale-python-black|
- flake8..............................|ale-python-flake8|
- isort...............................|ale-python-isort|
- mypy................................|ale-python-mypy|
- prospector..........................|ale-python-prospector|
- pycodestyle.........................|ale-python-pycodestyle|
- pydocstyle..........................|ale-python-pydocstyle|
- pyflakes............................|ale-python-pyflakes|
- pylint..............................|ale-python-pylint|
- pyls................................|ale-python-pyls|
- pyre................................|ale-python-pyre|
- vulture.............................|ale-python-vulture|
- yapf................................|ale-python-yapf|
- qml...................................|ale-qml-options|
- qmlfmt..............................|ale-qml-qmlfmt|
- r.....................................|ale-r-options|
- lintr...............................|ale-r-lintr|
- reasonml..............................|ale-reasonml-options|
- merlin..............................|ale-reasonml-merlin|
- ols.................................|ale-reasonml-ols|
- refmt...............................|ale-reasonml-refmt|
- restructuredtext......................|ale-restructuredtext-options|
- write-good..........................|ale-restructuredtext-write-good|
- ruby..................................|ale-ruby-options|
- brakeman............................|ale-ruby-brakeman|
- rails_best_practices................|ale-ruby-rails_best_practices|
- reek................................|ale-ruby-reek|
- rubocop.............................|ale-ruby-rubocop|
- ruby................................|ale-ruby-ruby|
- rufo................................|ale-ruby-rufo|
- solargraph..........................|ale-ruby-solargraph|
- rust..................................|ale-rust-options|
- cargo...............................|ale-rust-cargo|
- rls.................................|ale-rust-rls|
- rustc...............................|ale-rust-rustc|
- rustfmt.............................|ale-rust-rustfmt|
- sass..................................|ale-sass-options|
- sasslint............................|ale-sass-sasslint|
- stylelint...........................|ale-sass-stylelint|
- scala.................................|ale-scala-options|
- sbtserver...........................|ale-scala-sbtserver|
- scalafmt............................|ale-scala-scalafmt|
- scalastyle..........................|ale-scala-scalastyle|
- scss..................................|ale-scss-options|
- prettier............................|ale-scss-prettier|
- sasslint............................|ale-scss-sasslint|
- stylelint...........................|ale-scss-stylelint|
- sh....................................|ale-sh-options|
- sh-language-server..................|ale-sh-language-server|
- shell...............................|ale-sh-shell|
- shellcheck..........................|ale-sh-shellcheck|
- shfmt...............................|ale-sh-shfmt|
- sml...................................|ale-sml-options|
- smlnj...............................|ale-sml-smlnj|
- solidity..............................|ale-solidity-options|
- solhint.............................|ale-solidity-solhint|
- solium..............................|ale-solidity-solium|
- spec..................................|ale-spec-options|
- rpmlint.............................|ale-spec-rpmlint|
- sql...................................|ale-sql-options|
- sqlfmt..............................|ale-sql-sqlfmt|
- stylus................................|ale-stylus-options|
- stylelint...........................|ale-stylus-stylelint|
- tcl...................................|ale-tcl-options|
- nagelfar............................|ale-tcl-nagelfar|
- terraform.............................|ale-terraform-options|
- fmt.................................|ale-terraform-fmt|
- tflint..............................|ale-terraform-tflint|
- tex...................................|ale-tex-options|
- chktex..............................|ale-tex-chktex|
- lacheck.............................|ale-tex-lacheck|
- texinfo...............................|ale-texinfo-options|
- write-good..........................|ale-texinfo-write-good|
- text..................................|ale-text-options|
- textlint............................|ale-text-textlint|
- write-good..........................|ale-text-write-good|
- thrift................................|ale-thrift-options|
- thrift..............................|ale-thrift-thrift|
- typescript............................|ale-typescript-options|
- eslint..............................|ale-typescript-eslint|
- prettier............................|ale-typescript-prettier|
- tslint..............................|ale-typescript-tslint|
- tsserver............................|ale-typescript-tsserver|
- vala..................................|ale-vala-options|
- uncrustify..........................|ale-vala-uncrustify|
- verilog/systemverilog.................|ale-verilog-options|
- iverilog............................|ale-verilog-iverilog|
- verilator...........................|ale-verilog-verilator|
- vim...................................|ale-vim-options|
- vint................................|ale-vim-vint|
- vim help..............................|ale-vim-help-options|
- write-good..........................|ale-vim-help-write-good|
- vue...................................|ale-vue-options|
- prettier............................|ale-vue-prettier|
- vls.................................|ale-vue-vls|
- xhtml.................................|ale-xhtml-options|
- write-good..........................|ale-xhtml-write-good|
- xml...................................|ale-xml-options|
- xmllint.............................|ale-xml-xmllint|
- yaml..................................|ale-yaml-options|
- prettier............................|ale-yaml-prettier|
- swaglint............................|ale-yaml-swaglint|
- yamllint............................|ale-yaml-yamllint|
- yang..................................|ale-yang-options|
- yang-lsp............................|ale-yang-lsp|
+ 7. Linter/Fixer Options.................|ale-integration-options|
+ 7.1 Options for alex..................|ale-alex-options|
+ 7.2 Options for languagetool..........|ale-languagetool-options|
+ 7.3 Options for write-good............|ale-write-good-options|
+ 7.4 Other Linter/Fixer Options........|ale-other-integration-options|
8. Commands/Keybinds....................|ale-commands|
9. API..................................|ale-api|
10. Special Thanks......................|ale-special-thanks|
@@ -394,124 +60,8 @@ developer documentation. See |ale-development|
===============================================================================
2. Supported Languages & Tools *ale-support*
-The following languages and tools are supported.
-
-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|
-
-* Ada: `gcc`
-* ASM: `gcc`
-* Ansible: `ansible-lint`
-* API Blueprint: `drafter`
-* AsciiDoc: `alex`!!, `proselint`, `redpen`, `write-good`, `vale`
-* Awk: `gawk`
-* Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt`
-* BibTeX: `bibclean`
-* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt`
-* C: `cppcheck`, `cpplint`!!, `clang`, `clangd`, `clangtidy`!!, `clang-format`, `cquery`, `flawfinder`, `gcc`, `uncrustify`, `ccls`
-* C++ (filetype cpp): `clang`, `clangd`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `clazy`!!, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc`, `uncrustify`, `ccls`
-* CUDA: `nvcc`!!
-* C#: `mcs`, `mcsc`!!, `uncrustify`
-* Chef: `foodcritic`
-* Clojure: `joker`
-* CloudFormation: `cfn-python-lint`
-* CMake: `cmakelint`
-* CoffeeScript: `coffee`, `coffeelint`
-* Crystal: `crystal`!!
-* CSS: `csslint`, `prettier`, `stylelint`
-* Cucumber: `cucumber`
-* Cython (pyrex filetype): `cython`
-* D: `dls`, `dmd`, `uncrustify`
-* Dafny: `dafny`!!
-* Dart: `dartanalyzer`!!, `language_server`, dartfmt!!
-* Dockerfile: `dockerfile_lint`, `hadolint`
-* Elixir: `credo`, `dialyxir`, `dogma`, `mix`!!, `elixir-ls`
-* Elm: `elm-format, elm-make`
-* Erb: `erb`, `erubi`, `erubis`, `ruumba`
-* Erlang: `erlc`, `SyntaxErl`
-* Fish: `fish` (-n flag)
-* Fortran: `gcc`, `language_server`
-* Fountain: `proselint`
-* FusionScript: `fusion-lint`
-* Git Commit Messages: `gitlint`
-* GLSL: glslang, `glslls`
-* Go: `gofmt`, `goimports`, `go mod`!!, `go vet`!!, `golint`, `gotype`!!, `gometalinter`!!, `go build`!!, `gosimple`!!, `staticcheck`!!, `golangserver`, `golangci-lint`!!
-* GraphQL: `eslint`, `gqlint`, `prettier`
-* Hack: `hack`, `hackfmt`, `hhast`
-* Haml: `haml-lint`
-* Handlebars: `ember-template-lint`
-* Haskell: `brittany`, `ghc`, `cabal-ghc`, `stylish-haskell`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `hlint`, `hdevtools`, `hfmt`, `hie`
-* HCL: `terraform-fmt`
-* HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `prettier`, `write-good`
-* Idris: `idris`
-* ISPC: `ispc`!!
-* Java: `checkstyle`, `javac`, `google-java-format`, `PMD`, `javalsp`, `uncrustify`
-* JavaScript: `eslint`, `flow`, `jscs`, `jshint`, `prettier`, `prettier-eslint`, `prettier-standard`, `standard`, `xo`
-* JSON: `fixjson`, `jsonlint`, `jq`, `prettier`
-* Julia: `languageserver`
-* Kotlin: `kotlinc`!!, `ktlint`!!, `languageserver`
-* LaTeX (tex): `alex`!!, `chktex`, `lacheck`, `proselint`, `redpen`, `vale`, `write-good`
-* Less: `lessc`, `prettier`, `stylelint`
-* LLVM: `llc`
-* Lua: `luac`, `luacheck`
-* Mail: `alex`!!, `proselint`, `vale`
-* Make: `checkmake`
-* Markdown: `alex`!!, `markdownlint`!!, `mdl`, `prettier`, `proselint`, `redpen`, `remark-lint`, `textlint`, `vale`, `write-good`
-* MATLAB: `mlint`
-* Mercury: `mmc`!!
-* NASM: `nasm`!!
-* Nim: `nim check`!!
-* nix: `nix-instantiate`
-* nroff: `alex`!!, `proselint`, `write-good`
-* Objective-C: `clang`, `clangd`, `uncrustify`, `ccls`
-* Objective-C++: `clang`, `clangd`, `uncrustify`
-* OCaml: `merlin` (see |ale-ocaml-merlin|), `ols`, `ocamlformat`
-* Pawn: `uncrustify`
-* Perl: `perl -c`, `perl-critic`, `perltidy`
-* Perl6: `perl6 -c`
-* PHP: `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf`, `php-cs-fixer`, `psalm`!!
-* PO: `alex`!!, `msgfmt`, `proselint`, `write-good`
-* Pod: `alex`!!, `proselint`, `write-good`
-* Pony: `ponyc`
-* Prolog: `swipl`
-* proto: `protoc-gen-lint`
-* Pug: `pug-lint`
-* Puppet: `languageserver`, `puppet`, `puppet-lint`
-* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pydocstyle`, `pyls`, `pyre`, `pylint`!!, `vulture`!!, `yapf`
-* QML: `qmlfmt`, `qmllint`
-* R: `lintr`
-* ReasonML: `merlin`, `ols`, `refmt`
-* reStructuredText: `alex`!!, `proselint`, `redpen`, `rstcheck`, `vale`, `write-good`
-* Re:VIEW: `redpen`
-* RPM spec: `rpmlint`
-* Ruby: `brakeman`, `rails_best_practices`!!, `reek`, `rubocop`, `ruby`, `rufo`, `solargraph`
-* Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|), `rustfmt`
-* SASS: `sass-lint`, `stylelint`
-* SCSS: `prettier`, `sass-lint`, `scss-lint`, `stylelint`
-* Scala: `fsc`, `sbtserver`, `scalac`, `scalafmt`, `scalastyle`
-* Slim: `slim-lint`
-* SML: `smlnj`
-* Solidity: `solhint`, `solium`
-* Stylus: `stylelint`
-* SQL: `sqlint`, `sqlfmt`
-* Swift: `swiftlint`, `swiftformat`
-* Tcl: `nagelfar`!!
-* Terraform: `fmt`, `tflint`
-* Texinfo: `alex`!!, `proselint`, `write-good`
-* Text^: `alex`!!, `proselint`, `redpen`, `textlint`, `vale`, `write-good`
-* Thrift: `thrift`
-* TypeScript: `eslint`, `prettier`, `tslint`, `tsserver`, `typecheck`
-* VALA: `uncrustify`
-* Verilog: `iverilog`, `verilator`
-* Vim: `vint`
-* Vim help^: `alex`!!, `proselint`, `write-good`
-* Vue: `prettier`, `vls`
-* XHTML: `alex`!!, `proselint`, `write-good`
-* XML: `xmllint`
-* YAML: `prettier`, `swaglint`, `yamllint`
-* YANG: `yang-lsp`
+ALE supports a wide variety of languages and tools. See |ale-supported-list|
+for the full list.
===============================================================================
3. Linting *ale-lint*
@@ -676,8 +226,8 @@ 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.
+|List| for new lines to set, a |Dictionary| for describing a command to be
+run in the background, or the result of |ale#command#Run()|.
Functions receiving a variable number of arguments will not receive the second
argument `lines`. Functions should name two arguments if the `lines` argument
@@ -707,26 +257,6 @@ are supported for running the commands.
A |List| of |String|s must be returned.
- `chain_with` An optional key for defining a callback to call next.
-
- The callback must accept two or three arguments,
- `(buffer, output)` or `(buffer, output, input)` .
- Functions receiving a variable number of arguments will
- only receive the first two values. The `output` argument
- will contain the lines of output from the command run.
- The `input` argument is the List of lines for the
- buffer, after applying any previous fixers.
-
- The callback must return the same values returned for
- any fixer function. This allows fixer functions to be
- chained recursively.
-
- When the command string returned for a fixer is an empty
- string, the next command in the chain will still be run.
- This allows commands to be skipped, like version checks
- that are cached. An empty List will be passed to the
- next callback in the chain for the `output`.
-
`read_buffer` An optional key for disabling reading the buffer.
When set to `0`, ALE will not pipe the buffer's data
@@ -734,10 +264,7 @@ are supported for running the commands.
the buffer is not read when `read_temporary_file` is
`1`.
- This option defaults to `0` when `chain_with` is defined
- as anything other than `v:null`, and defaults to `1`
- otherwise. This is so earlier commands in a chain
- do not receive the buffer's data by default.
+ This option defaults to `1`.
*ale-fix-configuration*
@@ -782,6 +309,9 @@ by default.
|g:ale_fix_on_save| - Fix files when they are saved.
+Fixers can be disabled on save with |g:ale_fix_on_save_ignore|. They will
+still be run when you manually run |ALEFix|.
+
===============================================================================
5. Language Server Protocol Support *ale-lsp*
@@ -791,37 +321,44 @@ servers. LSP linters can be used in combination with any other linter, and
will automatically connect to LSP servers when needed. ALE also supports
`tsserver` for TypeScript, which uses a different but very similar protocol.
-ALE supports the following LSP/tsserver features:
-
-1. Diagnostics/linting - Enabled via selecting linters as usual.
-2. Completion
-3. Go to definition
-
-------------------------------------------------------------------------------
5.1 Completion *ale-completion*
-ALE offers limited support for automatic completion of code while you type.
+ALE offers support for automatic completion of code while you type.
Completion is only supported while at least one LSP linter is enabled. ALE
will only suggest symbols provided by the LSP servers.
-Suggestions will be made while you type after completion is enabled.
-Completion can be enabled by setting |g:ale_completion_enabled| to `1`. This
-setting must be set to `1` before ALE is loaded. 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|.
+ *ale-deoplete-integration*
+
+ALE integrates with Deoplete for offering automatic completion data. ALE's
+completion source for Deoplete is named `'ale'`, and should enabled
+automatically if Deoplete is enabled and configured correctly. Deoplete
+integration should not be combined with ALE's own implementation.
+
+ALE also offers its own completion implementation, which does not require any
+other plugins. Suggestions will be made while you type after completion is
+enabled. Completion can be enabled by setting |g:ale_completion_enabled| to
+`1`. This setting must be set to `1` before ALE is loaded. 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|.
If you don't like some of the suggestions you see, you can filter them out
with |g:ale_completion_excluded_words| or |b:ale_completion_excluded_words|.
- *ale-completion-completopt-bug*
+The |ALEComplete| command can be used to show completion suggestions manually,
+even when |g:ale_completion_enabled| is set to `0`. For manually requesting
+completion information with Deoplete, consult Deoplete's documentation.
-ALE implements completion as you type by temporarily adjusting |completeopt|
-before opening the omnicomplete menu with <C-x><C-o>. In some versions of Vim,
-the value set for the option will not be respected. If you experience issues
-with Vim automatically inserting text while you type, set the following option
-in vimrc, and your issues should go away. >
+ *ale-completion-completeopt-bug*
+
+ALE Automatic completion implementation replaces |completeopt| before opening
+the omnicomplete menu with <C-x><C-o>. In some versions of Vim, the value set
+for the option will not be respected. If you experience issues with Vim
+automatically inserting text while you type, set the following option in
+vimrc, and your issues should go away. >
set completeopt=menu,menuone,preview,noselect,noinsert
<
@@ -835,10 +372,29 @@ information returned by LSP servers. The following commands are supported:
|ALEGoToDefinition| - Open the definition of the symbol under the cursor.
|ALEGoToDefinitionInTab| - The same, but for opening the file in a new tab.
+|ALEGoToDefinitionInSplit| - The same, but in a new split.
+|ALEGoToDefinitionInVSplit| - The same, but in a new vertical split.
+ALE will update Vim's |tagstack| automatically unless |g:ale_update_tagstack| is
+set to `0`.
-------------------------------------------------------------------------------
-5.3 Find References *ale-find-references*
+5.3 Go To Type Definition *ale-go-to-type-definition*
+
+ALE supports jumping to the files and locations where symbols' types are
+defined through any enabled LSP linters. The locations ALE will jump to depend
+on the information returned by LSP servers. The following commands are
+supported:
+
+|ALEGoToTypeDefinition| - Open the definition of the symbol's type under
+ the cursor.
+|ALEGoToTypeDefinitionInTab| - The same, but for opening the file in a new tab.
+|ALEGoToTypeDefinitionInSplit| - The same, but in a new split.
+|ALEGoToTypeDefinitionInVSplit| - The same, but in a new vertical split.
+
+
+-------------------------------------------------------------------------------
+5.4 Find References *ale-find-references*
ALE supports finding references for symbols though any enabled LSP linters.
ALE will display a preview window showing the places where a symbol is
@@ -847,9 +403,11 @@ supported:
|ALEFindReferences| - Find references for the word under the cursor.
+Options:
+ `-relative` Show file paths in the results relative to the working dir
-------------------------------------------------------------------------------
-5.4 Hovering *ale-hover*
+5.5 Hovering *ale-hover*
ALE supports "hover" information for printing brief information about symbols
at the cursor taken from LSP linters. The following commands are supported:
@@ -876,14 +434,19 @@ settings. For example: >
set ttymouse=xterm
<
+Documentation for symbols at the cursor can be retrieved using the
+|ALEDocumentation| command. This command is only available for `tsserver`.
+
-------------------------------------------------------------------------------
-5.5 Symbol Search *ale-symbol-search*
+5.6 Symbol Search *ale-symbol-search*
ALE supports searching for workspace symbols via LSP linters. The following
commands are supported:
|ALESymbolSearch| - Search for symbols in the workspace.
+Options:
+ `-relative` Show file paths in the results relative to the working dir
===============================================================================
6. Global Options *ale-options*
@@ -1058,6 +621,16 @@ g:ale_cursor_detail *g:ale_cursor_detail*
loaded for messages to be displayed. See |ale-lint-settings-on-startup|.
+g:ale_disable_lsp *g:ale_disable_lsp*
+ *b:ale_disable_lsp*
+
+ Type: |Number|
+ Default: `0`
+
+ When this option is set to `1`, ALE ignores all linters powered by LSP.
+ Please see also |ale-lsp|.
+
+
g:ale_echo_cursor *g:ale_echo_cursor*
Type: |Number|
@@ -1207,6 +780,43 @@ b:ale_fix_on_save *b:ale_fix_on_save*
Fixing files can be disabled or enabled for individual buffers by setting
`b:ale_fix_on_save` to `0` or `1`.
+ Some fixers can be excluded from being run automatically when you save files
+ with the |g:ale_fix_on_save_ignore| setting.
+
+
+g:ale_fix_on_save_ignore *g:ale_fix_on_save_ignore*
+b:ale_fix_on_save_ignore *b:ale_fix_on_save_ignore*
+
+ Type: |Dictionary| or |List|
+ Default: `{}`
+
+ Given a |Dictionary| mapping filetypes to |Lists| of fixers to ignore, or
+ just a |List| of fixers to ignore, exclude those fixers from being run
+ automatically when files are saved.
+
+ You can disable some fixers in your ftplugin file: >
+
+ " Disable fixers 'b' and 'c' when fixing on safe for this buffer.
+ let b:ale_fix_on_save_ignore = ['b', 'c']
+ " Alternatively, define ignore lists for different filetypes.
+ let b:ale_fix_on_save_ignore = {'foo': ['b'], 'bar': ['c']}
+<
+ You can disable some fixers globally per filetype like so: >
+
+ let g:ale_fixers = {'foo': ['a', 'b'], 'bar': ['c', 'd']}
+ let g:ale_fix_on_save = 1
+ " For filetype `foo.bar`, only fixers 'b' and 'd' will be run on save.
+ let g:ale_fix_on_save_ignore = {'foo': ['a'], 'bar': ['c']}
+ " Alternatively, disable these fixers on save for all filetypes.
+ let g:ale_fix_on_save_ignore = ['a', 'c']
+<
+ You can ignore fixers based on matching |Funcref| values too: >
+
+ let g:AddBar = {buffer, lines -> lines + ['bar']}
+ let g:ale_fixers = {'foo': g:AddBar}
+ " The lambda fixer will be ignored, as it will be found in the ignore list.
+ let g:ale_fix_on_save_ignore = [g:AddBar]
+<
g:ale_history_enabled *g:ale_history_enabled*
@@ -1431,7 +1041,7 @@ g:ale_linters *g:ale_linters*
{
\ 'csh': ['shell'],
- \ 'elixir': ['credo', 'dialyxir', 'dogma', 'elixir-ls'],
+ \ 'elixir': ['credo', 'dialyxir', 'dogma'],
\ 'go': ['gofmt', 'golint', 'go vet'],
\ 'hack': ['hack'],
\ 'help': [],
@@ -1540,6 +1150,22 @@ b:ale_loclist_msg_format *b:ale_loclist_msg_format*
The strings for configuring `%severity%` are also used for this option.
+g:ale_lsp_root *g:ale_lsp_root*
+b:ale_lsp_root *b:ale_lsp_root*
+
+ Type: |Dictionary| or |String|
+ Default: {}
+
+ This option is used to determine the project root for the LSP linter. If the
+ value is a |Dictionary|, it maps a linter to either a string containing the
+ project root or a |Funcref| to call to look up the root. The funcref is
+ provided the buffer number as its argument.
+
+ The buffer-specific variable may additionally be a string containing the
+ project root itself.
+
+ If neither variable yields a result, a linter-specific function is invoked to
+ detect a project root. If this, too, yields no result, the linter is disabled.
g:ale_max_buffer_history_size *g:ale_max_buffer_history_size*
@@ -1783,6 +1409,33 @@ g:ale_set_signs *g:ale_set_signs*
To limit the number of signs ALE will set, see |g:ale_max_signs|.
+g:ale_shell *g:ale_shell*
+
+ Type: |String|
+ Default: not set
+
+ Override the shell used by ALE for executing commands. ALE uses 'shell' by
+ default, but falls back in `/bin/sh` if the default shell looks like `fish`
+ or `pwsh`, which are not compatible with all of the commands run by ALE. The
+ shell specified with this option will be used even if it might not work in
+ all cases.
+
+ For Windows, ALE uses `cmd` when this option isn't set. Setting this option
+ will apply shell escaping to the command string, even on Windows.
+
+ NOTE: Consider setting |g:ale_shell_arguments| if this option is defined.
+
+
+g:ale_shell_arguments *g:ale_shell_arguments*
+
+ Type: |String|
+ Default: not set
+
+ This option specifies the arguments to use for executing a command with a
+ custom shell, per |g:ale_shell|. If this option is not set, 'shellcmdflag'
+ will be used instead.
+
+
g:ale_sign_column_always *g:ale_sign_column_always*
Type: |Number|
@@ -1848,6 +1501,14 @@ g:ale_sign_warning *g:ale_sign_warning*
The sign for warnings in the sign gutter.
+g:ale_update_tagstack *g:ale_update_tagstack*
+ *b:ale_update_tagstack*
+ Type: |Number|
+ Default: `1`
+
+ This option can be set to disable updating Vim's |tagstack| automatically.
+
+
g:ale_type_map *g:ale_type_map*
*b:ale_type_map*
Type: |Dictionary|
@@ -2129,11 +1790,79 @@ ALEWarningSign *ALEWarningSign*
The highlight for warning signs. See |g:ale_set_signs|.
+===============================================================================
+7. Linter/Fixer Options *ale-integration-options*
+
+Linter and fixer options are documented below and in individual help files.
+
+Every option for programs can be set globally, or individually for each
+buffer. For example, `b:ale_python_flake8_executable` will override any
+values set for `g:ale_python_flake8_executable`.
+
+ *ale-integrations-local-executables*
+
+Some tools will prefer to search for locally-installed executables, unless
+configured otherwise. For example, the `eslint` linter will search for
+various executable paths in `node_modules`. The `flake8` linter will search
+for virtualenv directories.
+
+If you prefer to use global executables for those tools, set the relevant
+`_use_global` and `_executable` options for those linters. >
+
+ " Use the global executable with a special name for eslint.
+ let g:ale_javascript_eslint_executable = 'special-eslint'
+ let g:ale_javascript_eslint_use_global = 1
+
+ " Use the global executable with a special name for flake8.
+ let g:ale_python_flake8_executable = '/foo/bar/flake8'
+ let g:ale_python_flake8_use_global = 1
+<
+|g:ale_use_global_executables| can be set to `1` in your vimrc file to make
+ALE use global executables for all linters by default.
+
+The option |g:ale_virtualenv_dir_names| controls the local virtualenv paths
+ALE will use to search for Python executables.
+
+
-------------------------------------------------------------------------------
-6.2. Options for write-good *ale-write-good-options*
+7.1. Options for alex *ale-alex-options*
+
+The options for `alex` are shared between all filetypes, so options can be
+configured once.
-The options for the write-good linter are global because it does not make
-sense to have them specified on a per-language basis.
+g:ale_alex_executable *g:ale_alex_executable*
+ *b:ale_alex_executable*
+ Type: |String|
+ Default: `'alex'`
+
+ See |ale-integrations-local-executables|
+
+
+g:ale_alex_use_global *g:ale_alex_use_global*
+ *b:ale_alex_use_global*
+ Type: |Number|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+-------------------------------------------------------------------------------
+7.2. Options for languagetool *ale-languagetool-options*
+
+g:ale_languagetool_executable *g:ale_languagetool_executable*
+ *b:ale_languagetool_executable*
+
+ Type: |String|
+ Default: `'languagetool'`
+
+ The executable to run for languagetool.
+
+
+-------------------------------------------------------------------------------
+7.3. Options for write-good *ale-write-good-options*
+
+The options for `write-good` are shared between all filetypes, so options can
+be configured once.
g:ale_writegood_executable *g:ale_writegood_executable*
*b:ale_writegood_executable*
@@ -2159,44 +1888,412 @@ g:ale_writegood_use_global *g:ale_writegood_use_global*
See |ale-integrations-local-executables|
-===============================================================================
-7. Integration Documentation *ale-integrations*
-
-Linter and fixer options are documented in individual help files. See the
-table of contents at |ale-contents|.
+-------------------------------------------------------------------------------
+7.4. Other Linter/Fixer Options *ale-other-integration-options*
+
+ALE supports a very wide variety of tools. Other linter or fixer options are
+documented in additional help files.
+
+ ada.....................................|ale-ada-options|
+ gcc...................................|ale-ada-gcc|
+ ansible.................................|ale-ansible-options|
+ ansible-lint..........................|ale-ansible-ansible-lint|
+ asciidoc................................|ale-asciidoc-options|
+ write-good............................|ale-asciidoc-write-good|
+ textlint..............................|ale-asciidoc-textlint|
+ asm.....................................|ale-asm-options|
+ gcc...................................|ale-asm-gcc|
+ awk.....................................|ale-awk-options|
+ gawk..................................|ale-awk-gawk|
+ bib.....................................|ale-bib-options|
+ bibclean..............................|ale-bib-bibclean|
+ c.......................................|ale-c-options|
+ clang.................................|ale-c-clang|
+ clangd................................|ale-c-clangd|
+ clang-format..........................|ale-c-clangformat|
+ clangtidy.............................|ale-c-clangtidy|
+ cppcheck..............................|ale-c-cppcheck|
+ cquery................................|ale-c-cquery|
+ flawfinder............................|ale-c-flawfinder|
+ gcc...................................|ale-c-gcc|
+ uncrustify............................|ale-c-uncrustify|
+ ccls..................................|ale-c-ccls|
+ chef....................................|ale-chef-options|
+ cookstyle.............................|ale-chef-cookstyle|
+ foodcritic............................|ale-chef-foodcritic|
+ clojure.................................|ale-clojure-options|
+ clj-kondo.............................|ale-clojure-clj-kondo|
+ joker.................................|ale-clojure-joker|
+ cloudformation..........................|ale-cloudformation-options|
+ cfn-python-lint.......................|ale-cloudformation-cfn-python-lint|
+ cmake...................................|ale-cmake-options|
+ cmakelint.............................|ale-cmake-cmakelint|
+ cmake-format..........................|ale-cmake-cmakeformat|
+ cpp.....................................|ale-cpp-options|
+ clang.................................|ale-cpp-clang|
+ clangd................................|ale-cpp-clangd|
+ clangcheck............................|ale-cpp-clangcheck|
+ clang-format..........................|ale-cpp-clangformat|
+ clangtidy.............................|ale-cpp-clangtidy|
+ clazy.................................|ale-cpp-clazy|
+ cppcheck..............................|ale-cpp-cppcheck|
+ cpplint...............................|ale-cpp-cpplint|
+ cquery................................|ale-cpp-cquery|
+ flawfinder............................|ale-cpp-flawfinder|
+ gcc...................................|ale-cpp-gcc|
+ uncrustify............................|ale-cpp-uncrustify|
+ ccls..................................|ale-cpp-ccls|
+ c#......................................|ale-cs-options|
+ mcs...................................|ale-cs-mcs|
+ mcsc..................................|ale-cs-mcsc|
+ uncrustify............................|ale-cs-uncrustify|
+ css.....................................|ale-css-options|
+ fecs..................................|ale-css-fecs|
+ prettier..............................|ale-css-prettier|
+ stylelint.............................|ale-css-stylelint|
+ cuda....................................|ale-cuda-options|
+ nvcc..................................|ale-cuda-nvcc|
+ clang-format..........................|ale-cuda-clangformat|
+ d.......................................|ale-d-options|
+ dls...................................|ale-d-dls|
+ uncrustify............................|ale-d-uncrustify|
+ dart....................................|ale-dart-options|
+ dartanalyzer..........................|ale-dart-dartanalyzer|
+ dartfmt...............................|ale-dart-dartfmt|
+ dockerfile..............................|ale-dockerfile-options|
+ dockerfile_lint.......................|ale-dockerfile-dockerfile_lint|
+ hadolint..............................|ale-dockerfile-hadolint|
+ elixir..................................|ale-elixir-options|
+ mix...................................|ale-elixir-mix|
+ mix_format............................|ale-elixir-mix-format|
+ dialyxir..............................|ale-elixir-dialyxir|
+ elixir-ls.............................|ale-elixir-elixir-ls|
+ credo.................................|ale-elixir-credo|
+ elm.....................................|ale-elm-options|
+ elm-format............................|ale-elm-elm-format|
+ elm-lsp...............................|ale-elm-elm-lsp|
+ elm-make..............................|ale-elm-elm-make|
+ erlang..................................|ale-erlang-options|
+ erlc..................................|ale-erlang-erlc|
+ syntaxerl.............................|ale-erlang-syntaxerl|
+ eruby...................................|ale-eruby-options|
+ ruumba................................|ale-eruby-ruumba|
+ fish....................................|ale-fish-options|
+ fortran.................................|ale-fortran-options|
+ gcc...................................|ale-fortran-gcc|
+ language_server.......................|ale-fortran-language-server|
+ fountain................................|ale-fountain-options|
+ fusionscript............................|ale-fuse-options|
+ fusion-lint...........................|ale-fuse-fusionlint|
+ git commit..............................|ale-gitcommit-options|
+ gitlint...............................|ale-gitcommit-gitlint|
+ glsl....................................|ale-glsl-options|
+ glslang...............................|ale-glsl-glslang|
+ glslls................................|ale-glsl-glslls|
+ go......................................|ale-go-options|
+ bingo.................................|ale-go-bingo|
+ gobuild...............................|ale-go-gobuild|
+ gofmt.................................|ale-go-gofmt|
+ golangci-lint.........................|ale-go-golangci-lint|
+ golangserver..........................|ale-go-golangserver|
+ golint................................|ale-go-golint|
+ gometalinter..........................|ale-go-gometalinter|
+ gopls.................................|ale-go-gopls|
+ govet.................................|ale-go-govet|
+ staticcheck...........................|ale-go-staticcheck|
+ graphql.................................|ale-graphql-options|
+ eslint................................|ale-graphql-eslint|
+ gqlint................................|ale-graphql-gqlint|
+ prettier..............................|ale-graphql-prettier|
+ hack....................................|ale-hack-options|
+ hack..................................|ale-hack-hack|
+ hackfmt...............................|ale-hack-hackfmt|
+ hhast.................................|ale-hack-hhast|
+ handlebars..............................|ale-handlebars-options|
+ ember-template-lint...................|ale-handlebars-embertemplatelint|
+ haskell.................................|ale-haskell-options|
+ brittany..............................|ale-haskell-brittany|
+ floskell..............................|ale-haskell-floskell|
+ ghc...................................|ale-haskell-ghc|
+ ghc-mod...............................|ale-haskell-ghc-mod|
+ cabal-ghc.............................|ale-haskell-cabal-ghc|
+ hdevtools.............................|ale-haskell-hdevtools|
+ hfmt..................................|ale-haskell-hfmt|
+ hlint.................................|ale-haskell-hlint|
+ stack-build...........................|ale-haskell-stack-build|
+ stack-ghc.............................|ale-haskell-stack-ghc|
+ stylish-haskell.......................|ale-haskell-stylish-haskell|
+ hie...................................|ale-haskell-hie|
+ hcl.....................................|ale-hcl-options|
+ terraform-fmt.........................|ale-hcl-terraform-fmt|
+ html....................................|ale-html-options|
+ fecs..................................|ale-html-fecs|
+ htmlhint..............................|ale-html-htmlhint|
+ tidy..................................|ale-html-tidy|
+ prettier..............................|ale-html-prettier|
+ stylelint.............................|ale-html-stylelint|
+ write-good............................|ale-html-write-good|
+ idris...................................|ale-idris-options|
+ idris.................................|ale-idris-idris|
+ ispc....................................|ale-ispc-options|
+ ispc..................................|ale-ispc-ispc|
+ java....................................|ale-java-options|
+ checkstyle............................|ale-java-checkstyle|
+ javac.................................|ale-java-javac|
+ google-java-format....................|ale-java-google-java-format|
+ pmd...................................|ale-java-pmd|
+ javalsp...............................|ale-java-javalsp|
+ eclipselsp............................|ale-java-eclipselsp|
+ uncrustify............................|ale-java-uncrustify|
+ javascript..............................|ale-javascript-options|
+ eslint................................|ale-javascript-eslint|
+ fecs..................................|ale-javascript-fecs|
+ flow..................................|ale-javascript-flow|
+ importjs..............................|ale-javascript-importjs|
+ jscs..................................|ale-javascript-jscs|
+ jshint................................|ale-javascript-jshint|
+ prettier..............................|ale-javascript-prettier|
+ prettier-eslint.......................|ale-javascript-prettier-eslint|
+ prettier-standard.....................|ale-javascript-prettier-standard|
+ standard..............................|ale-javascript-standard|
+ xo....................................|ale-javascript-xo|
+ json....................................|ale-json-options|
+ fixjson...............................|ale-json-fixjson|
+ jsonlint..............................|ale-json-jsonlint|
+ jq....................................|ale-json-jq|
+ prettier..............................|ale-json-prettier|
+ julia...................................|ale-julia-options|
+ languageserver........................|ale-julia-languageserver|
+ kotlin..................................|ale-kotlin-options|
+ kotlinc...............................|ale-kotlin-kotlinc|
+ ktlint................................|ale-kotlin-ktlint|
+ languageserver........................|ale-kotlin-languageserver|
+ latex...................................|ale-latex-options|
+ write-good............................|ale-latex-write-good|
+ textlint..............................|ale-latex-textlint|
+ less....................................|ale-less-options|
+ lessc.................................|ale-less-lessc|
+ prettier..............................|ale-less-prettier|
+ stylelint.............................|ale-less-stylelint|
+ llvm....................................|ale-llvm-options|
+ llc...................................|ale-llvm-llc|
+ lua.....................................|ale-lua-options|
+ luac..................................|ale-lua-luac|
+ luacheck..............................|ale-lua-luacheck|
+ markdown................................|ale-markdown-options|
+ mdl...................................|ale-markdown-mdl|
+ prettier..............................|ale-markdown-prettier|
+ remark-lint...........................|ale-markdown-remark-lint|
+ textlint..............................|ale-markdown-textlint|
+ write-good............................|ale-markdown-write-good|
+ mercury.................................|ale-mercury-options|
+ mmc...................................|ale-mercury-mmc|
+ nasm....................................|ale-nasm-options|
+ nasm..................................|ale-nasm-nasm|
+ nroff...................................|ale-nroff-options|
+ write-good............................|ale-nroff-write-good|
+ objc....................................|ale-objc-options|
+ clang.................................|ale-objc-clang|
+ clangd................................|ale-objc-clangd|
+ uncrustify............................|ale-objc-uncrustify|
+ ccls..................................|ale-objc-ccls|
+ objcpp..................................|ale-objcpp-options|
+ clang.................................|ale-objcpp-clang|
+ clangd................................|ale-objcpp-clangd|
+ uncrustify............................|ale-objcpp-uncrustify|
+ ocaml...................................|ale-ocaml-options|
+ merlin................................|ale-ocaml-merlin|
+ ols...................................|ale-ocaml-ols|
+ ocamlformat...........................|ale-ocaml-ocamlformat|
+ ocp-indent............................|ale-ocaml-ocp-indent|
+ pawn....................................|ale-pawn-options|
+ uncrustify............................|ale-pawn-uncrustify|
+ perl....................................|ale-perl-options|
+ perl..................................|ale-perl-perl|
+ perlcritic............................|ale-perl-perlcritic|
+ perltidy..............................|ale-perl-perltidy|
+ perl6...................................|ale-perl6-options|
+ perl6.................................|ale-perl6-perl6|
+ php.....................................|ale-php-options|
+ langserver............................|ale-php-langserver|
+ phan..................................|ale-php-phan|
+ phpcbf................................|ale-php-phpcbf|
+ phpcs.................................|ale-php-phpcs|
+ phpmd.................................|ale-php-phpmd|
+ phpstan...............................|ale-php-phpstan|
+ psalm.................................|ale-php-psalm|
+ php-cs-fixer..........................|ale-php-php-cs-fixer|
+ php...................................|ale-php-php|
+ po......................................|ale-po-options|
+ write-good............................|ale-po-write-good|
+ pod.....................................|ale-pod-options|
+ write-good............................|ale-pod-write-good|
+ pony....................................|ale-pony-options|
+ ponyc.................................|ale-pony-ponyc|
+ powershell............................|ale-powershell-options|
+ powershell..........................|ale-powershell-powershell|
+ psscriptanalyzer....................|ale-powershell-psscriptanalyzer|
+ prolog..................................|ale-prolog-options|
+ swipl.................................|ale-prolog-swipl|
+ proto...................................|ale-proto-options|
+ protoc-gen-lint.......................|ale-proto-protoc-gen-lint|
+ pug.....................................|ale-pug-options|
+ puglint...............................|ale-pug-puglint|
+ puppet..................................|ale-puppet-options|
+ puppet................................|ale-puppet-puppet|
+ puppetlint............................|ale-puppet-puppetlint|
+ puppet-languageserver.................|ale-puppet-languageserver|
+ pyrex (cython)..........................|ale-pyrex-options|
+ cython................................|ale-pyrex-cython|
+ python..................................|ale-python-options|
+ autopep8..............................|ale-python-autopep8|
+ bandit................................|ale-python-bandit|
+ black.................................|ale-python-black|
+ flake8................................|ale-python-flake8|
+ isort.................................|ale-python-isort|
+ mypy..................................|ale-python-mypy|
+ prospector............................|ale-python-prospector|
+ pycodestyle...........................|ale-python-pycodestyle|
+ pydocstyle............................|ale-python-pydocstyle|
+ pyflakes..............................|ale-python-pyflakes|
+ pylama................................|ale-python-pylama|
+ pylint................................|ale-python-pylint|
+ pyls..................................|ale-python-pyls|
+ pyre..................................|ale-python-pyre|
+ vulture...............................|ale-python-vulture|
+ yapf..................................|ale-python-yapf|
+ qml.....................................|ale-qml-options|
+ qmlfmt................................|ale-qml-qmlfmt|
+ r.......................................|ale-r-options|
+ lintr.................................|ale-r-lintr|
+ styler................................|ale-r-styler|
+ reasonml................................|ale-reasonml-options|
+ merlin................................|ale-reasonml-merlin|
+ ols...................................|ale-reasonml-ols|
+ refmt.................................|ale-reasonml-refmt|
+ restructuredtext........................|ale-restructuredtext-options|
+ textlint..............................|ale-restructuredtext-textlint|
+ write-good............................|ale-restructuredtext-write-good|
+ ruby....................................|ale-ruby-options|
+ brakeman..............................|ale-ruby-brakeman|
+ rails_best_practices..................|ale-ruby-rails_best_practices|
+ reek..................................|ale-ruby-reek|
+ rubocop...............................|ale-ruby-rubocop|
+ ruby..................................|ale-ruby-ruby|
+ rufo..................................|ale-ruby-rufo|
+ solargraph............................|ale-ruby-solargraph|
+ standardrb............................|ale-ruby-standardrb|
+ rust....................................|ale-rust-options|
+ cargo.................................|ale-rust-cargo|
+ rls...................................|ale-rust-rls|
+ rustc.................................|ale-rust-rustc|
+ rustfmt...............................|ale-rust-rustfmt|
+ sass....................................|ale-sass-options|
+ sasslint..............................|ale-sass-sasslint|
+ stylelint.............................|ale-sass-stylelint|
+ scala...................................|ale-scala-options|
+ sbtserver.............................|ale-scala-sbtserver|
+ scalafmt..............................|ale-scala-scalafmt|
+ scalastyle............................|ale-scala-scalastyle|
+ scss....................................|ale-scss-options|
+ prettier..............................|ale-scss-prettier|
+ sasslint..............................|ale-scss-sasslint|
+ stylelint.............................|ale-scss-stylelint|
+ sh......................................|ale-sh-options|
+ sh-language-server....................|ale-sh-language-server|
+ shell.................................|ale-sh-shell|
+ shellcheck............................|ale-sh-shellcheck|
+ shfmt.................................|ale-sh-shfmt|
+ sml.....................................|ale-sml-options|
+ smlnj.................................|ale-sml-smlnj|
+ solidity................................|ale-solidity-options|
+ solhint...............................|ale-solidity-solhint|
+ solium................................|ale-solidity-solium|
+ spec....................................|ale-spec-options|
+ rpmlint...............................|ale-spec-rpmlint|
+ sql.....................................|ale-sql-options|
+ sqlfmt................................|ale-sql-sqlfmt|
+ stylus..................................|ale-stylus-options|
+ stylelint.............................|ale-stylus-stylelint|
+ sugarss.................................|ale-sugarss-options|
+ stylelint.............................|ale-sugarss-stylelint|
+ swift...................................|ale-swift-options|
+ sourcekitlsp..........................|ale-swift-sourcekitlsp|
+ tcl.....................................|ale-tcl-options|
+ nagelfar..............................|ale-tcl-nagelfar|
+ terraform...............................|ale-terraform-options|
+ fmt...................................|ale-terraform-fmt|
+ tflint................................|ale-terraform-tflint|
+ tex.....................................|ale-tex-options|
+ chktex................................|ale-tex-chktex|
+ lacheck...............................|ale-tex-lacheck|
+ latexindent...........................|ale-tex-latexindent|
+ texinfo.................................|ale-texinfo-options|
+ write-good............................|ale-texinfo-write-good|
+ text....................................|ale-text-options|
+ textlint..............................|ale-text-textlint|
+ write-good............................|ale-text-write-good|
+ thrift..................................|ale-thrift-options|
+ thrift................................|ale-thrift-thrift|
+ typescript..............................|ale-typescript-options|
+ eslint................................|ale-typescript-eslint|
+ prettier..............................|ale-typescript-prettier|
+ tslint................................|ale-typescript-tslint|
+ tsserver..............................|ale-typescript-tsserver|
+ vala....................................|ale-vala-options|
+ uncrustify............................|ale-vala-uncrustify|
+ verilog/systemverilog...................|ale-verilog-options|
+ iverilog..............................|ale-verilog-iverilog|
+ verilator.............................|ale-verilog-verilator|
+ vlog..................................|ale-verilog-vlog|
+ xvlog.................................|ale-verilog-xvlog|
+ vhdl....................................|ale-vhdl-options|
+ ghdl..................................|ale-vhdl-ghdl|
+ vcom..................................|ale-vhdl-vcom|
+ xvhdl.................................|ale-vhdl-xvhdl|
+ vim.....................................|ale-vim-options|
+ vint..................................|ale-vim-vint|
+ vim help................................|ale-vim-help-options|
+ write-good............................|ale-vim-help-write-good|
+ vue.....................................|ale-vue-options|
+ prettier..............................|ale-vue-prettier|
+ vls...................................|ale-vue-vls|
+ xhtml...................................|ale-xhtml-options|
+ write-good............................|ale-xhtml-write-good|
+ xml.....................................|ale-xml-options|
+ xmllint...............................|ale-xml-xmllint|
+ yaml....................................|ale-yaml-options|
+ prettier..............................|ale-yaml-prettier|
+ swaglint..............................|ale-yaml-swaglint|
+ yamllint..............................|ale-yaml-yamllint|
+ yang....................................|ale-yang-options|
+ yang-lsp..............................|ale-yang-lsp|
-Every option for programs can be set globally, or individually for each
-buffer. For example, `b:ale_python_flake8_executable` will override any
-values set for `g:ale_python_flake8_executable`.
- *ale-integrations-local-executables*
+===============================================================================
+8. Commands/Keybinds *ale-commands*
-Some tools will prefer to search for locally-installed executables, unless
-configured otherwise. For example, the `eslint` linter will search for
-various executable paths in `node_modules`. The `flake8` linter will search
-for virtualenv directories.
+ALEComplete *ALEComplete*
-If you prefer to use global executables for those tools, set the relevant
-`_use_global` and `_executable` options for those linters. >
+ Manually trigger LSP autocomplete and show the menu. Works only when called
+ from insert mode. >
- " Use the global executable with a special name for eslint.
- let g:ale_javascript_eslint_executable = 'special-eslint'
- let g:ale_javascript_eslint_use_global = 1
+ inoremap <silent> <C-Space> <C-\><C-O>:AleComplete<CR>
+<
+ A plug mapping `<Plug>(ale_complete)` is defined for this command. >
- " Use the global executable with a special name for flake8.
- let g:ale_python_flake8_executable = '/foo/bar/flake8'
- let g:ale_python_flake8_use_global = 1
+ imap <C-Space> <Plug>(ale_complete)
<
+ALEDocumentation *ALEDocumentation*
-|g:ale_use_global_executables| can be set to `1` in your vimrc file to make
-ALE use global executables for all linters by default.
+ Similar to the |ALEHover| command, retrieve documentation information for
+ the symbol at the cursor. Documentation data will always be shown in a
+ preview window, no matter how small the documentation content is.
-The option |g:ale_virtualenv_dir_names| controls the local virtualenv paths
-ALE will use to search for Python executables.
+ NOTE: This command is only available for `tsserver`.
+ A plug mapping `<Plug>(ale_documentation)` is defined for this command.
-===============================================================================
-8. Commands/Keybinds *ale-commands*
ALEFindReferences *ALEFindReferences*
@@ -2247,6 +2344,61 @@ ALEGoToDefinitionInTab *ALEGoToDefinitionInTab*
command.
+ALEGoToDefinitionInSplit *ALEGoToDefinitionInSplit*
+
+ The same as |ALEGoToDefinition|, but opens results in a new split.
+
+ A plug mapping `<Plug>(ale_go_to_definition_in_split)` is defined for this
+ command.
+
+
+ALEGoToDefinitionInVSplit *ALEGoToDefinitionInVSplit*
+
+ The same as |ALEGoToDefinition|, but opens results in a new vertical split.
+
+ A plug mapping `<Plug>(ale_go_to_definition_in_vsplit)` is defined for this
+ command.
+
+
+ALEGoToTypeDefinition *ALEGoToTypeDefinition*
+
+ This works similar to |ALEGoToDefinition| but instead jumps to the
+ definition of a type of a symbol under the cursor. ALE will jump to a
+ definition if an LSP server provides a location to jump to. Otherwise, ALE
+ will do nothing.
+
+ You can jump back to the position you were at before going to the definition
+ of something with jump motions like CTRL-O. See |jump-motions|.
+
+ A plug mapping `<Plug>(ale_go_to_type_definition)` is defined for this
+ command.
+
+
+ALEGoToTypeDefinitionInTab *ALEGoToTypeDefinitionInTab*
+
+ The same as |ALEGoToTypeDefinition|, but opens results in a new tab.
+
+ A plug mapping `<Plug>(ale_go_to_type_definition_in_tab)` is defined for
+ this command.
+
+
+ALEGoToTypeDefinitionInSplit *ALEGoToTypeDefinitionInSplit*
+
+ The same as |ALEGoToTypeDefinition|, but opens results in a new split.
+
+ A plug mapping `<Plug>(ale_go_to_type_definition_in_split)` is defined for
+ this command.
+
+
+ALEGoToTypeDefinitionInVSplit *ALEGoToTypeDefinitionInVSplit*
+
+ The same as |ALEGoToTypeDefinition|, but opens results in a new vertical
+ split.
+
+ A plug mapping `<Plug>(ale_go_to_type_definition_in_vsplit)` is defined for
+ this command.
+
+
ALEHover *ALEHover*
Print brief information about the symbol under the cursor, taken from any
@@ -2295,14 +2447,36 @@ ALELast *ALELast*
`ALEPreviousWrap` and `ALENextWrap` will wrap around the file to find
the last or first warning or error in the file, respectively.
+ `ALEPrevious` and `ALENext` take optional flags arguments to custom their
+ behaviour :
+ `-wrap` enable wrapping around the file
+ `-error`, `-warning` and `-info` enable jumping to errors, warnings or infos
+ respectively, ignoring anything else. They are mutually exclusive and if
+ several are provided the priority is the following: error > warning > info.
+ `-style` and `-nostyle` allow you to jump respectively to style error or
+ warning and to not style error or warning. They also are mutually
+ exclusive and nostyle has priority over style.
+
+ Flags can be combined to create create custom jumping. Thus you can use
+ ":ALENext -wrap -error -nosyle" to jump to the next error which is not a
+ style error while going back to the begining of the file if needed.
+
`ALEFirst` goes to the first error or warning in the buffer, while `ALELast`
goes to the last one.
The following |<Plug>| mappings are defined for the commands: >
<Plug>(ale_previous) - ALEPrevious
<Plug>(ale_previous_wrap) - ALEPreviousWrap
+ <Plug>(ale_previous_error) - ALEPrevious -error
+ <Plug>(ale_previous_wrap_error) - ALEPrevious -wrap -error
+ <Plug>(ale_previous_warning) - ALEPrevious -warning
+ <Plug>(ale_previous_wrap_warning) - ALEPrevious -wrap -warning
<Plug>(ale_next) - ALENext
<Plug>(ale_next_wrap) - ALENextWrap
+ <Plug>(ale_next_error) - ALENext -error
+ <Plug>(ale_next_wrap_error) - ALENext -wrap -error
+ <Plug>(ale_next_warning) - ALENext -warning
+ <Plug>(ale_next_wrap_warning) - ALENext -wrap -warning
<Plug>(ale_first) - ALEFirst
<Plug>(ale_last) - ALELast
<
@@ -2434,6 +2608,14 @@ ale#Env(variable_name, value) *ale#Env()*
'set VAR="some value" && command' # On Windows
+ale#Has(feature) *ale#Has()*
+
+ Return `1` if ALE supports a given feature, like |has()| for Vim features.
+
+ ALE versions can be checked with version strings in the format
+ `ale#Has('ale-x.y.z')`, such as `ale#Has('ale-2.4.0')`.
+
+
ale#Pad(string) *ale#Pad()*
Given a string or any |empty()| value, return either the string prefixed
@@ -2462,26 +2644,105 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
is broken, or when developing ALE itself.
-ale#engine#CreateDirectory(buffer) *ale#engine#CreateDirectory()*
+ale#command#CreateDirectory(buffer) *ale#command#CreateDirectory()*
Create a new temporary directory with a unique name, and manage that
- directory with |ale#engine#ManageDirectory()|, so it will be removed as soon
+ directory with |ale#command#ManageDirectory()|, so it will be removed as soon
as possible.
It is advised to only call this function from a callback function for
returning a linter command to run.
-ale#engine#CreateFile(buffer) *ale#engine#CreateFile()*
+ale#command#CreateFile(buffer) *ale#command#CreateFile()*
Create a new temporary file with a unique name, and manage that file with
- |ale#engine#ManageFile()|, so it will be removed as soon as possible.
+ |ale#command#ManageFile()|, so it will be removed as soon as possible.
It is advised to only call this function from a callback function for
returning a linter command to run.
-ale#engine#EscapeCommandPart(command_part) *ale#engine#EscapeCommandPart()*
+ale#command#Run(buffer, command, callback, [options]) *ale#command#Run()*
+
+ Start running a job in the background, and pass the results to the given
+ callback later.
+
+ This function can be used for computing the results of ALE linter or fixer
+ functions asynchronously with jobs. `buffer` must match the buffer being
+ linted or fixed, `command` must be a |String| for a shell command to
+ execute, `callback` must be defined as a |Funcref| to call later with the
+ results, and an optional |Dictionary| of `options` can be provided.
+
+ The `callback` will receive the arguments `(buffer, output, metadata)`,
+ where the `buffer` will match the buffer given to the function, the `output`
+ will be a `List` of lines of output from the job that was run, and the
+ `metadata` will be a |Dictionary| with additional information about the job
+ that was run, including:
+
+ `exit_code` - A |Number| with the exit code for the program that was run.
+
+ The result of this function is either a special |Dictionary| ALE will use
+ for waiting for the command to finish, or `0` if the job is not started. The
+ The return value of the `callback` will be used as the eventual result for
+ whatever value is being given to ALE. For example: >
+
+ function! s:GetCommand(buffer, output, meta) abort
+ " Do something with a:output here, from the foo command.
+
+ " This is used as the command to run for linting.
+ return 'final command'
+ endfunction
+
+ " ...
+
+ 'command': {b -> ale#command#Run(b, 'foo', function('s:GetCommand'))}
+<
+ The result of a callback can also be the result of another call to this
+ function, so that several commands can be arbitrarily chained together. For
+ example: >
+
+ function! s:GetAnotherCommand(buffer, output, meta) abort
+ " We can finally return this command.
+ return 'last command'
+ endfunction
+
+ function! s:GetCommand(buffer, output, meta) abort
+ " We can return another deferred result.
+ return ale#command#Run(
+ \ a:buffer,
+ \ 'second command',
+ \ function('s:GetAnotherCommand')
+ \)
+ endfunction
+
+ " ...
+
+ 'command': {b -> ale#command#Run(b, 'foo', function('s:GetCommand'))}
+<
+
+ The following `options` can be provided.
+
+ `output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or `'none`' for
+ selecting which output streams to read lines from.
+
+ The default is `'stdout'`
+
+ `executable` - An executable for formatting into `%e` in the command.
+ If this option is not provided, formatting commands with
+ `%e` will not work.
+
+ `read_buffer` - If set to `1`, the buffer will be piped into the
+ command.
+
+ The default is `0`.
+
+ `input` - When creating temporary files with `%t` or piping text
+ into a command `input` can be set to a |List| of text to
+ use instead of the buffer's text.
+
+
+ale#command#EscapeCommandPart(command_part) *ale#command#EscapeCommandPart()*
Given a |String|, return a |String| with all `%` characters replaced with
`%%` instead. This function can be used to escape strings which are
@@ -2490,27 +2751,22 @@ ale#engine#EscapeCommandPart(command_part) *ale#engine#EscapeCommandPart()*
specially.
-ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*
+ale#command#ManageDirectory(buffer, directory) *ale#command#ManageDirectory()*
- Given a buffer number, this function will return the list of problems
- reported by ALE for a given buffer in the format accepted by |setqflist()|.
-
- A reference to the buffer's list of problems will be returned. The list must
- be copied before applying |map()| or |filter()|.
-
-
-ale#engine#IsCheckingBuffer(buffer) *ale#engine#IsCheckingBuffer()*
-
- Given a buffer number, returns `1` when ALE is busy checking that buffer.
+ Like |ale#command#ManageFile()|, but directories and all of their contents
+ will be deleted, akin to `rm -rf directory`, which could lead to loss of
+ data if mistakes are made. This command will also delete any temporary
+ filenames given to it.
- This function can be used for status lines, tab names, etc.
+ It is advised to use |ale#command#ManageFile()| instead for deleting single
+ files.
-ale#engine#ManageFile(buffer, filename) *ale#engine#ManageFile()*
+ale#command#ManageFile(buffer, filename) *ale#command#ManageFile()*
- Given a buffer number for a buffer currently running some linting tasks
- and a filename, register a filename with ALE for automatic deletion after
- linting is complete, or when Vim exits.
+ Given a buffer number for a buffer currently running some linting or fixing
+ tasks and a filename, register a filename with ALE for automatic deletion
+ after linting or fixing is complete, or when Vim exits.
If Vim exits suddenly, ALE will try its best to remove temporary files, but
ALE cannot guarantee with absolute certainty that the files will be removed.
@@ -2521,18 +2777,23 @@ ale#engine#ManageFile(buffer, filename) *ale#engine#ManageFile()*
files and symlinks given to this function. This is to prevent entire
directories from being accidentally deleted, say in cases of writing
`dir . '/' . filename` where `filename` is actually `''`, etc. ALE instead
- manages directories separetly with the |ale#engine#ManageDirectory| function.
+ manages directories separately with the |ale#command#ManageDirectory| function.
-ale#engine#ManageDirectory(buffer, directory) *ale#engine#ManageDirectory()*
+ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*
- Like |ale#engine#ManageFile()|, but directories and all of their contents
- will be deleted, akin to `rm -rf directory`, which could lead to loss of
- data if mistakes are made. This command will also delete any temporary
- filenames given to it.
+ Given a buffer number, this function will return the list of problems
+ reported by ALE for a given buffer in the format accepted by |setqflist()|.
- It is advised to use |ale#engine#ManageFile()| instead for deleting single
- files.
+ A reference to the buffer's list of problems will be returned. The list must
+ be copied before applying |map()| or |filter()|.
+
+
+ale#engine#IsCheckingBuffer(buffer) *ale#engine#IsCheckingBuffer()*
+
+ Given a buffer number, returns `1` when ALE is busy checking that buffer.
+
+ This function can be used for status lines, tab names, etc.
ale#fix#registry#Add(name, func, filetypes, desc, [aliases])
@@ -2624,7 +2885,7 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
and have been checked at least once.
Temporary files in directories used for Vim
- temporary files with `tempname()` will be asssumed
+ temporary files with `tempname()` will be assumed
to be the buffer being checked, unless the `bufnr`
key is also set with a valid number for some other
buffer.
@@ -2647,74 +2908,30 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
Human-readable |String| error code.
`executable` A |String| naming the executable itself which
- will be run. This value will be used to check if the
- program requested is installed or not.
+ will be run, or a |Funcref| for a function to call
+ for computing the executable, accepting a buffer
+ number.
+
+ The result can be computed with |ale#command#Run()|.
- Either this or the `executable_callback` argument
- must be provided.
+ This value will be used to check if the program
+ requested is installed or not.
- `executable_callback ` A |String| or |Funcref| for a callback function
- accepting a buffer number. A |String| should be
- returned for the executable to check. This can be
- used in place of `executable` when more complicated
- processing is needed.
+ If an `executable` is not defined, the command will
+ be run without checking if a program is executable
+ first. Defining an executable path is recommended to
+ avoid starting too many processes.
+
+ `command` A |String| for a command to run asynchronously, or a
+ |Funcref| for a function to call for computing the
+ command, accepting a buffer number.
+
+ The result can be computed with |ale#command#Run()|.
- `command` A |String| for an executable to run asynchronously.
This command will be fed the lines from the buffer to
check, and will produce the lines of output given to
the `callback`.
- `command_callback` A |String| or |Funcref| for a callback function
- accepting a buffer number. A |String| should be
- returned for a command to run. This can be used in
- place of `command` when more complicated processing
- is needed.
-
- If an empty string is returned from the callback,
- no jobs for linting will be run for that linter.
- This can be used for skipping a linter call,
- say if no configuration file was found.
-
- *ale-command-chain*
- `command_chain` A |List| of |Dictionary| items defining a series
- of commands to be run. At least one |Dictionary|
- should be provided. Each Dictionary must contain the
- key `callback`, defining a |String| or |Funcref| for
- a function returning a |String| for a command to run.
-
- The callback functions for each command after the
- first command in in the chain should accept two
- arguments `(buffer, output)`, a buffer number and a
- |List| of lines of output from the previous command
- in the chain.
-
- The first callback function in a chain accepts only
- a `(buffer)` argument, as there are no previous
- commands to run which return `output`.
-
- If an empty string is returned for a command in a
- chain, that command in the chain will be skipped,
- and the next function in the chain will be called
- immediately instead. If the last command in a chain
- returns an empty string, then no linting will be
- performed.
-
- Commands in the chain will all use the
- `output_stream` value provided in the root
- |Dictionary|. Each command in the chain can also
- provide an `output_stream` key to override this value.
- See the `output_stream` description for more
- information.
-
- Commands in the chain all behave as if `read_buffer`
- is set to `0` by default, except for the last command
- in the chain, which uses the value set for
- `read_buffer` in the root |Dictionary|. Each command
- in the chain can also provide a `read_buffer` key
- to override these values.
- See the `read_buffer` description for more
- information.
-
`output_stream` A |String| for the output stream the lines of output
should be read from for the command which is run. The
accepted values are `'stdout'`, `'stderr'`, and
@@ -2760,21 +2977,20 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
linter will be defined as an LSP linter which keeps a
process for a language server running, and
communicates with it directly via a |channel|.
- `executable` or `executable_callback` must be set,
- and `command` or `command_callback` must be set.
+ `executable` and `command` must be set.
When this argument is set to `'socket'`, then the
linter will be defined as an LSP linter via a TCP
- socket connection. `address_callback` must be set
- with a callback returning an address to connect to.
+ socket connection. `address` must be set.
+
ALE will not start a server automatically.
- When this argument is not empty
- `project_root_callback` must be defined.
+ When this argument is not empty `project_root` must
+ be defined.
- `language` or `language_callback` can be defined to
- describe the language for a file. The filetype will
- be used as the language by default.
+ `language` can be defined to describe the language
+ for a file. The filetype will be used as the language
+ by default.
LSP linters handle diagnostics automatically, so
the `callback` argument must not be defined.
@@ -2782,45 +2998,41 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
An optional `completion_filter` callback may be
defined for filtering completion results.
- An optional `initialization_options` or
- `initialization_options_callback` may be defined to
- pass initialization options to the LSP.
+ `initialization_options` may be defined to pass
+ initialization options to the LSP.
- An optional `lsp_config` or `lsp_config_callback` may
- be defined to pass configuration settings to the LSP.
+ `lsp_config` may be defined to pass configuration
+ settings to the LSP.
- `address_callback` A |String| or |Funcref| for a callback function
- accepting a buffer number. A |String| should be
- returned with an address to connect to.
+ `address` A |String| representing an address to connect to,
+ or a |Funcref| accepting a buffer number and
+ returning the |String|.
+
+ The result can be computed with |ale#command#Run()|.
This argument must only be set if the `lsp` argument
is set to `'socket'`.
- `project_root_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
+ `project_root` A |String| representing a path to the project for
+ the file being checked with the language server, or
+ a |Funcref| accepting a buffer number and returning
+ the |String|.
+
+ 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` A |String| representing the name of the language
- being checked. This string will be sent to the LSP to
- tell it what type of language is being checked.
-
- If this or `language_callback` isn't set, the
- language will default to the value of the filetype
- given to |ale#linter#Define|.
-
- `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.
+ being checked, or a |Funcref| accepting a buffer
+ number and returning the |String|. This string will
+ be sent to the LSP to tell it what type of language
+ is being checked.
- This option can be used instead of `language` if a
- linter can check multiple languages.
+ If a language isn't provided, the language will
+ default to the value of the filetype given to
+ |ale#linter#Define|.
`completion_filter` A |String| or |Funcref| for a callback function
accepting a buffer number and a completion item.
@@ -2838,38 +3050,24 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
setting can make it easier to guess the linter name
by offering a few alternatives.
- `initialization_options` A |Dictionary| of initialization options for LSPs.
+ `initialization_options` A |Dictionary| of initialization options for LSPs,
+ or a |Funcref| for a callback function accepting
+ a buffer number and returning the |Dictionary|.
+
This will be fed (as JSON) to the LSP in the
initialize command.
- `initialization_options_callback`
- A |String| or |Funcref| for a callback function
- accepting a buffer number. A |Dictionary| should be
- returned for initialization options to pass the LSP.
- This can be used in place of `initialization_options`
- when more complicated processing is needed.
+ `lsp_config` A |Dictionary| for configuring a language server,
+ or a |Funcref| for a callback function accepting
+ a buffer number and returning the |Dictionary|.
- `lsp_config` A |Dictionary| of configuration settings for LSPs.
This will be fed (as JSON) to the LSP in the
workspace/didChangeConfiguration command.
- `lsp_config_callback` A |String| or |Funcref| for a callback function
- accepting a buffer number. A |Dictionary| should be
- returned for configuration settings to pass the LSP.
- This can be used in place of `lsp_config` when more
- complicated processing is needed.
-
- Only one of `command`, `command_callback`, or `command_chain` should be
- specified. `command_callback` is generally recommended when a command string
- needs to be generated dynamically, or any global options are used.
- `command_chain` is recommended where any system calls need to be made to
- retrieve some kind of information before running the final command.
-
If temporary files or directories are created for commands run with
- `command_callback` or `command_chain`, then these tempoary files or
- directories can be managed by ALE, for automatic deletion.
- See |ale#engine#ManageFile()| and |ale#engine#ManageDirectory| for more
- information.
+ `command`, then these temporary files or directories can be managed by ALE,
+ for automatic deletion. See |ale#command#ManageFile()| and
+ |ale#command#ManageDirectory| for more information.
*ale-command-format-strings*
@@ -2887,16 +3085,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
strings will reference the one temporary file. The temporary file will be
created inside a temporary directory, and the entire temporary directory
will be automatically deleted, following the behaviour of
- |ale#engine#ManageDirectory|. This option can be used for some linters which
+ |ale#command#ManageDirectory|. This option can be used for some linters which
do not support reading from stdin.
For example: >
'command': 'ghc -fno-code -v0 %t',
<
Any substring `%e` will be replaced with the escaped executable supplied
- with `executable` or `executable_callback`. This provides a convenient way
- to define a command string which needs to include a dynamic executable name,
- but which is otherwise static.
+ with `executable`. This provides a convenient way to define a command string
+ which needs to include a dynamic executable name, but which is otherwise
+ static.
For example: >
'command': '%e --some-argument',
@@ -2907,7 +3105,7 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
If a callback for a command generates part of a command string which might
possibly contain `%%`, `%s`, `%t`, or `%e`, where the special formatting
- behavior is not desired, the |ale#engine#EscapeCommandPart()| function can
+ behavior is not desired, the |ale#command#EscapeCommandPart()| function can
be used to replace those characters to avoid formatting issues.
*ale-linter-loading-behavior*
@@ -2993,6 +3191,21 @@ ale#statusline#Count(buffer) *ale#statusline#Count()*
`total` -> The total number of problems.
+ale#statusline#FirstProblem(buffer, type) *ale#statusline#FirstProblem()*
+
+ Returns a copy of the first entry in the `loclist` that matches the supplied
+ buffer number and problem type. If there is no such enty, an empty dictionary
+ is returned.
+ Problem type should be one of the strings listed below:
+
+ `error` -> Returns the first `loclist` item with type `E` and
+ `sub_type != 'style'`
+ `warning` -> First item with type `W` and `sub_type != 'style'`
+ `info` -> First item with type `I`
+ `style_error` -> First item with type `E` and `sub_type == 'style'`
+ `style_warning` -> First item with type `W` and `sub_type == 'style'`
+
+
b:ale_linted *b:ale_linted*
`b:ale_linted` is set to the number of times a buffer has been checked by
diff --git a/plugin/ale.vim b/plugin/ale.vim
index 7231d1bd..cf39d632 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -87,6 +87,9 @@ let g:ale_lint_on_save = get(g:, 'ale_lint_on_save', 1)
" This flag can be set to 1 to enable linting when the filetype is changed.
let g:ale_lint_on_filetype_changed = get(g:, 'ale_lint_on_filetype_changed', 1)
+" This Dictionary configures the default LSP roots for various linters.
+let g:ale_lsp_root = get(g:, 'ale_lsp_root', {})
+
" This flag can be set to 1 to enable automatically fixing files on save.
let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 0)
@@ -148,9 +151,12 @@ if g:ale_completion_enabled
endif
" Define commands for moving through warnings and errors.
-command! -bar ALEPrevious :call ale#loclist_jumping#Jump('before', 0)
+command! -bar -nargs=* ALEPrevious
+\ :call ale#loclist_jumping#WrapJump('before', <q-args>)
+command! -bar -nargs=* ALENext
+\ :call ale#loclist_jumping#WrapJump('after', <q-args>)
+
command! -bar ALEPreviousWrap :call ale#loclist_jumping#Jump('before', 1)
-command! -bar ALENext :call ale#loclist_jumping#Jump('after', 0)
command! -bar ALENextWrap :call ale#loclist_jumping#Jump('after', 1)
command! -bar ALEFirst :call ale#loclist_jumping#JumpToIndex(0)
command! -bar ALELast :call ale#loclist_jumping#JumpToIndex(-1)
@@ -188,23 +194,43 @@ command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype)
" Go to definition for tsserver and LSP
command! -bar ALEGoToDefinition :call ale#definition#GoTo({})
-command! -bar ALEGoToDefinitionInTab :call ale#definition#GoTo({'open_in_tab': 1})
+command! -bar ALEGoToDefinitionInTab :call ale#definition#GoTo({'open_in': 'tab'})
+command! -bar ALEGoToDefinitionInSplit :call ale#definition#GoTo({'open_in': 'horizontal-split'})
+command! -bar ALEGoToDefinitionInVSplit :call ale#definition#GoTo({'open_in': 'vertical-split'})
+
+" Go to type definition for tsserver and LSP
+command! -bar ALEGoToTypeDefinition :call ale#definition#GoToType({})
+command! -bar ALEGoToTypeDefinitionInTab :call ale#definition#GoToType({'open_in': 'tab'})
+command! -bar ALEGoToTypeDefinitionInSplit :call ale#definition#GoToType({'open_in': 'horizontal-split'})
+command! -bar ALEGoToTypeDefinitionInVSplit :call ale#definition#GoToType({'open_in': 'vertical-split'})
" Find references for tsserver and LSP
-command! -bar ALEFindReferences :call ale#references#Find()
+command! -bar -nargs=* ALEFindReferences :call ale#references#Find(<f-args>)
-" Get information for the cursor.
-command! -bar ALEHover :call ale#hover#Show(bufnr(''), getcurpos()[1],
- \ getcurpos()[2], {})
+" Show summary information for the cursor.
+command! -bar ALEHover :call ale#hover#ShowAtCursor()
+
+" Show documentation for the cursor.
+command! -bar ALEDocumentation :call ale#hover#ShowDocumentationAtCursor()
" Search for appearances of a symbol, such as a type name or function name.
command! -nargs=1 ALESymbolSearch :call ale#symbol#Search(<q-args>)
+command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual')
+
" <Plug> mappings for commands
nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return>
nnoremap <silent> <Plug>(ale_previous_wrap) :ALEPreviousWrap<Return>
+nnoremap <silent> <Plug>(ale_previous_error) :ALEPrevious -error<Return>
+nnoremap <silent> <Plug>(ale_previous_wrap_error) :ALEPrevious -wrap -error<Return>
+nnoremap <silent> <Plug>(ale_previous_warning) :ALEPrevious -warning<Return>
+nnoremap <silent> <Plug>(ale_previous_wrap_warning) :ALEPrevious -wrap -warning<Return>
nnoremap <silent> <Plug>(ale_next) :ALENext<Return>
nnoremap <silent> <Plug>(ale_next_wrap) :ALENextWrap<Return>
+nnoremap <silent> <Plug>(ale_next_error) :ALENext -error<Return>
+nnoremap <silent> <Plug>(ale_next_wrap_error) :ALENext -wrap -error<Return>
+nnoremap <silent> <Plug>(ale_next_warning) :ALENext -warning<Return>
+nnoremap <silent> <Plug>(ale_next_wrap_warning) :ALENext -wrap -warning<Return>
nnoremap <silent> <Plug>(ale_first) :ALEFirst<Return>
nnoremap <silent> <Plug>(ale_last) :ALELast<Return>
nnoremap <silent> <Plug>(ale_toggle) :ALEToggle<Return>
@@ -220,8 +246,16 @@ nnoremap <silent> <Plug>(ale_detail) :ALEDetail<Return>
nnoremap <silent> <Plug>(ale_fix) :ALEFix<Return>
nnoremap <silent> <Plug>(ale_go_to_definition) :ALEGoToDefinition<Return>
nnoremap <silent> <Plug>(ale_go_to_definition_in_tab) :ALEGoToDefinitionInTab<Return>
+nnoremap <silent> <Plug>(ale_go_to_definition_in_split) :ALEGoToDefinitionInSplit<Return>
+nnoremap <silent> <Plug>(ale_go_to_definition_in_vsplit) :ALEGoToDefinitionInVSplit<Return>
+nnoremap <silent> <Plug>(ale_go_to_type_definition) :ALEGoToTypeDefinition<Return>
+nnoremap <silent> <Plug>(ale_go_to_type_definition_in_tab) :ALEGoToTypeDefinitionInTab<Return>
+nnoremap <silent> <Plug>(ale_go_to_type_definition_in_split) :ALEGoToTypeDefinitionInSplit<Return>
+nnoremap <silent> <Plug>(ale_go_to_type_definition_in_vsplit) :ALEGoToTypeDefinitionInVSplit<Return>
nnoremap <silent> <Plug>(ale_find_references) :ALEFindReferences<Return>
nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return>
+nnoremap <silent> <Plug>(ale_documentation) :ALEDocumentation<Return>
+inoremap <silent> <Plug>(ale_complete) <C-\><C-O>:ALEComplete<Return>
" Set up autocmd groups now.
call ale#events#Init()
@@ -235,6 +269,6 @@ augroup ALECleanupGroup
autocmd QuitPre * call ale#events#QuitEvent(str2nr(expand('<abuf>')))
if exists('##VimSuspend')
- autocmd VimSuspend * if exists('*ale#engine#CleanupEveryBuffer') | call ale#engine#CleanupEveryBuffer() | endif
+ autocmd VimSuspend * if exists('*ale#engine#CleanupEveryBuffer') | call ale#engine#CleanupEveryBuffer() | endif
endif
augroup END
diff --git a/rplugin/python3/deoplete/sources/ale.py b/rplugin/python3/deoplete/sources/ale.py
new file mode 100644
index 00000000..1addfae3
--- /dev/null
+++ b/rplugin/python3/deoplete/sources/ale.py
@@ -0,0 +1,50 @@
+"""
+A Deoplete source for ALE completion via tsserver and LSP.
+"""
+__author__ = 'Joao Paulo, w0rp'
+
+try:
+ from deoplete.source.base import Base
+except ImportError:
+ # Mock the Base class if deoplete isn't available, as mock isn't available
+ # in the Docker image.
+ class Base(object):
+ def __init__(self, vim):
+ pass
+
+
+# Make sure this code is valid in Python 2, used for running unit tests.
+class Source(Base):
+
+ def __init__(self, vim):
+ super(Source, self).__init__(vim)
+
+ self.name = 'ale'
+ self.mark = '[L]'
+ self.rank = 100
+ self.is_bytepos = True
+ self.min_pattern_length = 1
+
+ # Returns an integer for the start position, as with omnifunc.
+ def get_completion_position(self):
+ return self.vim.call('ale#completion#GetCompletionPosition')
+
+ def gather_candidates(self, context):
+ if context.get('is_refresh'):
+ context['is_async'] = False
+
+ if context['is_async']:
+ # Result is the same as for omnifunc, or None.
+ result = self.vim.call('ale#completion#GetCompletionResult')
+
+ if result is not None:
+ context['is_async'] = False
+
+ return result
+ else:
+ context['is_async'] = True
+
+ # Request some completion results.
+ self.vim.call('ale#completion#GetCompletions', 'deoplete')
+
+ return []
diff --git a/supported-tools.md b/supported-tools.md
new file mode 100644
index 00000000..18d69388
--- /dev/null
+++ b/supported-tools.md
@@ -0,0 +1,493 @@
+# ALE Supported Languages and Tools
+
+This plugin supports the following languages and tools. All available
+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.
+-->
+
+**Legend**
+
+| Key | Definition |
+| ------------- | -------------------------------- |
+| :floppy_disk: | Only checked when saved to disk |
+| :warning: | Disabled by default |
+
+---
+
+* Ada
+ * [gcc](https://gcc.gnu.org)
+* Ansible
+ * [ansible-lint](https://github.com/willthames/ansible-lint)
+* API Blueprint
+ * [drafter](https://github.com/apiaryio/drafter)
+* AsciiDoc
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [redpen](http://redpen.cc/)
+ * [textlint](https://textlint.github.io/)
+ * [vale](https://github.com/ValeLint/vale)
+ * [write-good](https://github.com/btford/write-good)
+* ASM
+ * [gcc](https://gcc.gnu.org)
+* Awk
+ * [gawk](https://www.gnu.org/software/gawk/)
+* Bash
+ * [language-server](https://github.com/mads-hartmann/bash-language-server)
+ * shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set)
+ * [shellcheck](https://www.shellcheck.net/)
+ * [shfmt](https://github.com/mvdan/sh)
+* BibTeX
+ * [bibclean](http://ftp.math.utah.edu/pub/bibclean/)
+* Bourne Shell
+ * shell [-n flag](http://linux.die.net/man/1/sh)
+ * [shellcheck](https://www.shellcheck.net/)
+ * [shfmt](https://github.com/mvdan/sh)
+* C
+ * [ccls](https://github.com/MaskRay/ccls)
+ * [clang](http://clang.llvm.org/)
+ * [clangd](https://clang.llvm.org/extra/clangd.html)
+ * [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
+ * [clangtidy](http://clang.llvm.org/extra/clang-tidy/) :floppy_disk:
+ * [cppcheck](http://cppcheck.sourceforge.net)
+ * [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint)
+ * [cquery](https://github.com/cquery-project/cquery)
+ * [flawfinder](https://www.dwheeler.com/flawfinder/)
+ * [gcc](https://gcc.gnu.org/)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* C#
+ * [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details
+ * [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) :floppy_disk: see:`help ale-cs-mcsc` for details and configuration
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* C++ (filetype cpp)
+ * [ccls](https://github.com/MaskRay/ccls)
+ * [clang](http://clang.llvm.org/)
+ * [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) :floppy_disk:
+ * [clangd](https://clang.llvm.org/extra/clangd.html)
+ * [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
+ * [clangtidy](http://clang.llvm.org/extra/clang-tidy/) :floppy_disk:
+ * [clazy](https://github.com/KDE/clazy) :floppy_disk:
+ * [cppcheck](http://cppcheck.sourceforge.net)
+ * [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) :floppy_disk:
+ * [cquery](https://github.com/cquery-project/cquery)
+ * [flawfinder](https://www.dwheeler.com/flawfinder/)
+ * [gcc](https://gcc.gnu.org/)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* Chef
+ * [cookstyle](https://docs.chef.io/cookstyle.html)
+ * [foodcritic](http://www.foodcritic.io/)
+* Clojure
+ * [clj-kondo](https://github.com/borkdude/clj-kondo)
+ * [joker](https://github.com/candid82/joker)
+* CloudFormation
+ * [cfn-python-lint](https://github.com/awslabs/cfn-python-lint)
+* CMake
+ * [cmake-format](https://github.com/cheshirekow/cmake_format)
+ * [cmakelint](https://github.com/richq/cmake-lint)
+* CoffeeScript
+ * [coffee](http://coffeescript.org/)
+ * [coffeelint](https://www.npmjs.com/package/coffeelint)
+* Crystal
+ * [ameba](https://github.com/veelenga/ameba) :floppy_disk:
+ * [crystal](https://crystal-lang.org/) :floppy_disk:
+* CSS
+ * [csslint](http://csslint.net/)
+ * [fecs](http://fecs.baidu.com/)
+ * [prettier](https://github.com/prettier/prettier)
+ * [stylelint](https://github.com/stylelint/stylelint)
+* Cucumber
+ * [cucumber](https://cucumber.io/)
+* CUDA
+ * [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html)
+* Cypher
+ * [cypher-lint](https://github.com/cleishm/libcypher-parser)
+* Cython (pyrex filetype)
+ * [cython](http://cython.org/)
+* D
+ * [dls](https://github.com/d-language-server/dls)
+ * [dmd](https://dlang.org/dmd-linux.html)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* Dafny
+ * [dafny](https://rise4fun.com/Dafny) :floppy_disk:
+* Dart
+ * [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) :floppy_disk:
+ * [dartfmt](https://github.com/dart-lang/sdk/tree/master/utils/dartfmt)
+ * [language_server](https://github.com/natebosch/dart_language_server)
+* Dockerfile
+ * [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint)
+ * [hadolint](https://github.com/hadolint/hadolint)
+* Elixir
+ * [credo](https://github.com/rrrene/credo)
+ * [dialyxir](https://github.com/jeremyjh/dialyxir)
+ * [dogma](https://github.com/lpil/dogma)
+ * [elixir-ls](https://github.com/JakeBecker/elixir-ls) :warning:
+ * [mix](https://hexdocs.pm/mix/Mix.html) :warning: :floppy_disk:
+* Elm
+ * [elm-format](https://github.com/avh4/elm-format)
+ * [elm-lsp](https://github.com/antew/elm-lsp)
+ * [elm-make](https://github.com/elm-lang/elm-make)
+* Erb
+ * [erb](https://apidock.com/ruby/ERB)
+ * [erubi](https://github.com/jeremyevans/erubi)
+ * [erubis](https://github.com/kwatch/erubis)
+ * [ruumba](https://github.com/ericqweinstein/ruumba)
+* Erlang
+ * [erlc](http://erlang.org/doc/man/erlc.html)
+ * [SyntaxErl](https://github.com/ten0s/syntaxerl)
+* Fish
+ * fish [-n flag](https://linux.die.net/man/1/fish)
+* Fortran
+ * [gcc](https://gcc.gnu.org/)
+ * [language_server](https://github.com/hansec/fortran-language-server)
+* Fountain
+ * [proselint](http://proselint.com/)
+* FusionScript
+ * [fusion-lint](https://github.com/RyanSquared/fusionscript)
+* Git Commit Messages
+ * [gitlint](https://github.com/jorisroovers/gitlint)
+* GLSL
+ * [glslang](https://github.com/KhronosGroup/glslang)
+ * [glslls](https://github.com/svenstaro/glsl-language-server)
+* Go
+ * [bingo](https://github.com/saibing/bingo) :warning:
+ * [go build](https://golang.org/cmd/go/) :warning: :floppy_disk:
+ * [gofmt](https://golang.org/cmd/gofmt/)
+ * [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) :warning:
+ * [golangci-lint](https://github.com/golangci/golangci-lint) :warning: :floppy_disk:
+ * [golangserver](https://github.com/sourcegraph/go-langserver) :warning:
+ * [golint](https://godoc.org/github.com/golang/lint)
+ * [gometalinter](https://github.com/alecthomas/gometalinter) :warning: :floppy_disk:
+ * [go mod](https://golang.org/cmd/go/) :warning: :floppy_disk:
+ * [gopls](https://github.com/golang/go/wiki/gopls) :warning:
+ * [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) :warning: :floppy_disk:
+ * [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype) :warning: :floppy_disk:
+ * [go vet](https://golang.org/cmd/vet/) :floppy_disk:
+ * [staticcheck](https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck) :warning: :floppy_disk:
+* GraphQL
+ * [eslint](http://eslint.org/)
+ * [gqlint](https://github.com/happylinks/gqlint)
+ * [prettier](https://github.com/prettier/prettier)
+* Hack
+ * [hack](http://hacklang.org/)
+ * [hackfmt](https://github.com/facebook/hhvm/tree/master/hphp/hack/hackfmt)
+ * [hhast](https://github.com/hhvm/hhast) :warning: (see `:help ale-integration-hack`)
+* Haml
+ * [haml-lint](https://github.com/brigade/haml-lint)
+* Handlebars
+ * [ember-template-lint](https://github.com/rwjblue/ember-template-lint)
+* Haskell
+ * [brittany](https://github.com/lspitzner/brittany)
+ * [cabal-ghc](https://www.haskell.org/cabal/)
+ * [floskell](https://github.com/ennocramer/floskell)
+ * [ghc](https://www.haskell.org/ghc/)
+ * [ghc-mod](https://github.com/DanielG/ghc-mod)
+ * [hdevtools](https://hackage.haskell.org/package/hdevtools)
+ * [hfmt](https://github.com/danstiner/hfmt)
+ * [hie](https://github.com/haskell/haskell-ide-engine)
+ * [hlint](https://hackage.haskell.org/package/hlint)
+ * [stack-build](https://haskellstack.org/) :floppy_disk:
+ * [stack-ghc](https://haskellstack.org/)
+ * [stylish-haskell](https://github.com/jaspervdj/stylish-haskell)
+* HCL
+ * [terraform-fmt](https://github.com/hashicorp/terraform)
+* HTML
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [fecs](http://fecs.baidu.com/)
+ * [HTMLHint](http://htmlhint.com/)
+ * [prettier](https://github.com/prettier/prettier)
+ * [proselint](http://proselint.com/)
+ * [tidy](http://www.html-tidy.org/)
+ * [write-good](https://github.com/btford/write-good)
+* Idris
+ * [idris](http://www.idris-lang.org/)
+* ISPC
+ * [ispc](https://ispc.github.io/) :floppy_disk:
+* Java
+ * [checkstyle](http://checkstyle.sourceforge.net)
+ * [eclipselsp](https://github.com/eclipse/eclipse.jdt.ls)
+ * [google-java-format](https://github.com/google/google-java-format)
+ * [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
+ * [javalsp](https://github.com/georgewfraser/vscode-javac)
+ * [PMD](https://pmd.github.io/)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* JavaScript
+ * [eslint](http://eslint.org/)
+ * [fecs](http://fecs.baidu.com/)
+ * [flow](https://flowtype.org/)
+ * [jscs](http://jscs.info/)
+ * [jshint](http://jshint.com/)
+ * [prettier](https://github.com/prettier/prettier)
+ * [prettier-eslint](https://github.com/prettier/prettier-eslint-cli)
+ * [prettier-standard](https://github.com/sheerun/prettier-standard)
+ * [standard](http://standardjs.com/)
+ * [tsserver](https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29)
+ * [xo](https://github.com/sindresorhus/xo)
+* JSON
+ * [fixjson](https://github.com/rhysd/fixjson)
+ * [jq](https://stedolan.github.io/jq/)
+ * [jsonlint](http://zaa.ch/jsonlint/)
+ * [prettier](https://github.com/prettier/prettier)
+* Julia
+ * [languageserver](https://github.com/JuliaEditorSupport/LanguageServer.jl)
+* Kotlin
+ * [kotlinc](https://kotlinlang.org) :floppy_disk:
+ * [ktlint](https://ktlint.github.io) :floppy_disk:
+ * [languageserver](https://github.com/fwcd/KotlinLanguageServer) see `:help ale-integration-kotlin` for configuration instructions
+* LaTeX
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [chktex](http://www.nongnu.org/chktex/)
+ * [lacheck](https://www.ctan.org/pkg/lacheck)
+ * [proselint](http://proselint.com/)
+ * [redpen](http://redpen.cc/)
+ * [textlint](https://textlint.github.io/)
+ * [vale](https://github.com/ValeLint/vale)
+ * [write-good](https://github.com/btford/write-good)
+* Less
+ * [lessc](https://www.npmjs.com/package/less)
+ * [prettier](https://github.com/prettier/prettier)
+ * [stylelint](https://github.com/stylelint/stylelint)
+* LLVM
+ * [llc](https://llvm.org/docs/CommandGuide/llc.html)
+* Lua
+ * [luac](https://www.lua.org/manual/5.1/luac.html)
+ * [luacheck](https://github.com/mpeterv/luacheck)
+* Mail
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [languagetool](https://languagetool.org/) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [vale](https://github.com/ValeLint/vale)
+* Make
+ * [checkmake](https://github.com/mrtazz/checkmake)
+* Markdown
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [languagetool](https://languagetool.org/) :floppy_disk:
+ * [markdownlint](https://github.com/DavidAnson/markdownlint) :floppy_disk:
+ * [mdl](https://github.com/mivok/markdownlint)
+ * [prettier](https://github.com/prettier/prettier)
+ * [proselint](http://proselint.com/)
+ * [redpen](http://redpen.cc/)
+ * [remark-lint](https://github.com/wooorm/remark-lint)
+ * [textlint](https://textlint.github.io/)
+ * [vale](https://github.com/ValeLint/vale)
+ * [write-good](https://github.com/btford/write-good)
+* MATLAB
+ * [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html)
+* Mercury
+ * [mmc](http://mercurylang.org) :floppy_disk:
+* NASM
+ * [nasm](https://www.nasm.us/) :floppy_disk:
+* Nim
+ * [nim check](https://nim-lang.org/docs/nimc.html) :floppy_disk:
+* nix
+ * [nix-instantiate](http://nixos.org/nix/manual/#sec-nix-instantiate)
+* nroff
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [write-good](https://github.com/btford/write-good)
+* Objective-C
+ * [ccls](https://github.com/MaskRay/ccls)
+ * [clang](http://clang.llvm.org/)
+ * [clangd](https://clang.llvm.org/extra/clangd.html)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* Objective-C++
+ * [clang](http://clang.llvm.org/)
+ * [clangd](https://clang.llvm.org/extra/clangd.html)
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* OCaml
+ * [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions
+ * [ocamlformat](https://github.com/ocaml-ppx/ocamlformat)
+ * [ocp-indent](https://github.com/OCamlPro/ocp-indent)
+ * [ols](https://github.com/freebroccolo/ocaml-language-server)
+* Pawn
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* Perl
+ * [perl -c](https://perl.org/) :warning:
+ * [perl-critic](https://metacpan.org/pod/Perl::Critic)
+ * [perltidy](https://metacpan.org/pod/distribution/Perl-Tidy/bin/perltidy)
+* Perl6
+ * [perl6 -c](https://perl6.org) :warning:
+* PHP
+ * [langserver](https://github.com/felixfbecker/php-language-server)
+ * [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions
+ * [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer)
+ * [phpcs](https://github.com/squizlabs/PHP_CodeSniffer)
+ * [php-cs-fixer](http://cs.sensiolabs.org/)
+ * [php -l](https://secure.php.net/)
+ * [phpmd](https://phpmd.org)
+ * [phpstan](https://github.com/phpstan/phpstan)
+ * [psalm](https://getpsalm.org) :floppy_disk:
+* PO
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html)
+ * [proselint](http://proselint.com/)
+ * [write-good](https://github.com/btford/write-good)
+* Pod
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [write-good](https://github.com/btford/write-good)
+* Pony
+ * [ponyc](https://github.com/ponylang/ponyc)
+* PowerShell
+ * [powershell](https://github.com/PowerShell/PowerShell) :floppy_disk
+ * [psscriptanalyzer](https://github.com/PowerShell/PSScriptAnalyzer) :floppy_disk
+* Prolog
+ * [swipl](https://github.com/SWI-Prolog/swipl-devel)
+* proto
+ * [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint)
+* Pug
+ * [pug-lint](https://github.com/pugjs/pug-lint)
+* Puppet
+ * [languageserver](https://github.com/lingua-pupuli/puppet-editor-services)
+ * [puppet](https://puppet.com)
+ * [puppet-lint](https://puppet-lint.com)
+* Python
+ * [autopep8](https://github.com/hhatto/autopep8)
+ * [bandit](https://github.com/PyCQA/bandit) :warning:
+ * [black](https://github.com/ambv/black)
+ * [flake8](http://flake8.pycqa.org/en/latest/)
+ * [isort](https://github.com/timothycrosley/isort)
+ * [mypy](http://mypy-lang.org/)
+ * [prospector](https://github.com/PyCQA/prospector) :warning:
+ * [pycodestyle](https://github.com/PyCQA/pycodestyle) :warning:
+ * [pydocstyle](https://www.pydocstyle.org/) :warning:
+ * [pyflakes](https://github.com/PyCQA/pyflakes)
+ * [pylama](https://github.com/klen/pylama) :floppy_disk:
+ * [pylint](https://www.pylint.org/) :floppy_disk:
+ * [pyls](https://github.com/palantir/python-language-server) :warning:
+ * [pyre](https://github.com/facebook/pyre-check) :warning:
+ * [vulture](https://github.com/jendrikseipp/vulture) :warning: :floppy_disk:
+ * [yapf](https://github.com/google/yapf)
+* QML
+ * [qmlfmt](https://github.com/jesperhh/qmlfmt)
+ * [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint)
+* R
+ * [lintr](https://github.com/jimhester/lintr)
+ * [styler](https://github.com/r-lib/styler)
+* Racket
+ * [raco](https://docs.racket-lang.org/raco/)
+* ReasonML
+ * [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-reasonml-ols` for configuration instructions
+ * [ols](https://github.com/freebroccolo/ocaml-language-server)
+ * [refmt](https://github.com/reasonml/reason-cli)
+* reStructuredText
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [redpen](http://redpen.cc/)
+ * [rstcheck](https://github.com/myint/rstcheck)
+ * [textlint](https://textlint.github.io/)
+ * [vale](https://github.com/ValeLint/vale)
+ * [write-good](https://github.com/btford/write-good)
+* Re:VIEW
+ * [redpen](http://redpen.cc/)
+* RPM spec
+ * [rpmlint](https://github.com/rpm-software-management/rpmlint) :warning: (see `:help ale-integration-spec`)
+* Ruby
+ * [brakeman](http://brakemanscanner.org/) :floppy_disk:
+ * [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) :floppy_disk:
+ * [reek](https://github.com/troessner/reek)
+ * [rubocop](https://github.com/bbatsov/rubocop)
+ * [ruby](https://www.ruby-lang.org)
+ * [rufo](https://github.com/ruby-formatter/rufo)
+ * [solargraph](https://solargraph.org)
+ * [standardrb](https://github.com/testdouble/standard)
+* Rust
+ * [cargo](https://github.com/rust-lang/cargo) :floppy_disk: (see `:help ale-integration-rust` for configuration instructions)
+ * [rls](https://github.com/rust-lang-nursery/rls) :warning:
+ * [rustc](https://www.rust-lang.org/) :warning:
+ * [rustfmt](https://github.com/rust-lang-nursery/rustfmt)
+* Sass
+ * [sass-lint](https://www.npmjs.com/package/sass-lint)
+ * [stylelint](https://github.com/stylelint/stylelint)
+* Scala
+ * [fsc](https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/tools/fsc.html)
+ * [sbtserver](https://www.scala-sbt.org/1.x/docs/sbt-server.html)
+ * [scalac](http://scala-lang.org)
+ * [scalafmt](https://scalameta.org/scalafmt/)
+ * [scalastyle](http://www.scalastyle.org)
+* SCSS
+ * [prettier](https://github.com/prettier/prettier)
+ * [sass-lint](https://www.npmjs.com/package/sass-lint)
+ * [scss-lint](https://github.com/brigade/scss-lint)
+ * [stylelint](https://github.com/stylelint/stylelint)
+* Slim
+ * [slim-lint](https://github.com/sds/slim-lint)
+* SML
+ * [smlnj](http://www.smlnj.org/)
+* Solidity
+ * [solhint](https://github.com/protofire/solhint)
+ * [solium](https://github.com/duaraghav8/Solium)
+* SQL
+ * [sqlfmt](https://github.com/jackc/sqlfmt)
+ * [sqlint](https://github.com/purcell/sqlint)
+* Stylus
+ * [stylelint](https://github.com/stylelint/stylelint)
+* SugarSS
+ * [stylelint](https://github.com/stylelint/stylelint)
+* Swift
+ * [sourcekit-lsp](https://github.com/apple/sourcekit-lsp)
+ * [swiftformat](https://github.com/nicklockwood/SwiftFormat)
+ * [swiftlint](https://github.com/realm/SwiftLint)
+* Tcl
+ * [nagelfar](http://nagelfar.sourceforge.net) :floppy_disk:
+* Terraform
+ * [fmt](https://github.com/hashicorp/terraform)
+ * [tflint](https://github.com/wata727/tflint)
+* Texinfo
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [write-good](https://github.com/btford/write-good)
+* Text
+ * [alex](https://github.com/wooorm/alex) :warning: :floppy_disk:
+ * [languagetool](https://languagetool.org/) :floppy_disk:
+ * [proselint](http://proselint.com/) :warning:
+ * [redpen](http://redpen.cc/) :warning:
+ * [textlint](https://textlint.github.io/) :warning:
+ * [vale](https://github.com/ValeLint/vale) :warning:
+ * [write-good](https://github.com/btford/write-good) :warning:
+* Thrift
+ * [thrift](http://thrift.apache.org/)
+* TypeScript
+ * [eslint](http://eslint.org/)
+ * [fecs](http://fecs.baidu.com/)
+ * [prettier](https://github.com/prettier/prettier)
+ * [tslint](https://github.com/palantir/tslint)
+ * [tsserver](https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29)
+ * typecheck
+* VALA
+ * [uncrustify](https://github.com/uncrustify/uncrustify)
+* Verilog
+ * [iverilog](https://github.com/steveicarus/iverilog)
+ * [verilator](http://www.veripool.org/projects/verilator/wiki/Intro)
+ * [vlog](https://www.mentor.com/products/fv/questa/)
+ * [xvlog](https://www.xilinx.com/products/design-tools/vivado.html)
+* VHDL
+ * [ghdl](https://github.com/ghdl/ghdl)
+ * [vcom](https://www.mentor.com/products/fv/questa/)
+ * [xvhdl](https://www.xilinx.com/products/design-tools/vivado.html)
+* Vim
+ * [vint](https://github.com/Kuniwak/vint)
+* Vim help
+ * [alex](https://github.com/wooorm/alex) :warning: :floppy_disk:
+ * [proselint](http://proselint.com/) :warning:
+ * [write-good](https://github.com/btford/write-good) :warning:
+* Vue
+ * [prettier](https://github.com/prettier/prettier)
+ * [vls](https://github.com/vuejs/vetur/tree/master/server)
+* XHTML
+ * [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [proselint](http://proselint.com/)
+ * [write-good](https://github.com/btford/write-good)
+* XML
+ * [xmllint](http://xmlsoft.org/xmllint.html)
+* YAML
+ * [prettier](https://github.com/prettier/prettier)
+ * [swaglint](https://github.com/byCedric/swaglint)
+ * [yamllint](https://yamllint.readthedocs.io/)
+* YANG
+ * [yang-lsp](https://github.com/theia-ide/yang-lsp)
diff --git a/test/elm-test-files/app/filetest.elm b/test/command_callback/alex-node-modules-2/node_modules/alex/cli.js
index e69de29b..e69de29b 100644
--- a/test/elm-test-files/app/filetest.elm
+++ b/test/command_callback/alex-node-modules-2/node_modules/alex/cli.js
diff --git a/test/elm-test-files/app/node_modules/.bin/elm b/test/command_callback/alex-node-modules/node_modules/.bin/alex
index e69de29b..e69de29b 100644
--- a/test/elm-test-files/app/node_modules/.bin/elm
+++ b/test/command_callback/alex-node-modules/node_modules/.bin/alex
diff --git a/test/command_callback/fecs_paths/fecs b/test/command_callback/fecs_paths/fecs
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/fecs_paths/fecs
diff --git a/test/command_callback/fecs_paths/fecs.exe b/test/command_callback/fecs_paths/fecs.exe
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/fecs_paths/fecs.exe
diff --git a/test/command_callback/java_paths_no_main/src/test/java/com/something/dummy b/test/command_callback/java_paths_no_main/src/test/java/com/something/dummy
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/java_paths_no_main/src/test/java/com/something/dummy
diff --git a/test/command_callback/php-langserver-project/with-composer/composer.json b/test/command_callback/php-langserver-project/with-composer/composer.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php-langserver-project/with-composer/composer.json
diff --git a/test/command_callback/php-langserver-project/with-composer/vendor/bin/php-language-server.php b/test/command_callback/php-langserver-project/with-composer/vendor/bin/php-language-server.php
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php-langserver-project/with-composer/vendor/bin/php-language-server.php
diff --git a/test/command_callback/php-langserver-project/with-git/vendor/bin/php-language-server.php b/test/command_callback/php-langserver-project/with-git/vendor/bin/php-language-server.php
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php-langserver-project/with-git/vendor/bin/php-language-server.php
diff --git a/test/command_callback/python_paths/with_bandit/.bandit b/test/command_callback/python_paths/with_bandit/.bandit
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_bandit/.bandit
diff --git a/test/command_callback/python_paths/with_bandit/namespace/foo/__init__.py b/test/command_callback/python_paths/with_bandit/namespace/foo/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_bandit/namespace/foo/__init__.py
diff --git a/test/command_callback/python_paths/with_bandit/namespace/foo/bar.py b/test/command_callback/python_paths/with_bandit/namespace/foo/bar.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_bandit/namespace/foo/bar.py
diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylama.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylama.exe
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/pylama.exe
diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/vulture.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/vulture.exe
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/vulture.exe
diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/pylama b/test/command_callback/python_paths/with_virtualenv/env/bin/pylama
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/bin/pylama
diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/vulture b/test/command_callback/python_paths/with_virtualenv/env/bin/vulture
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/bin/vulture
diff --git a/test/command_callback/ruby_paths/with_config/.standard.yml b/test/command_callback/ruby_paths/with_config/.standard.yml
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/ruby_paths/with_config/.standard.yml
diff --git a/test/command_callback/test_alex_command_callback.vader b/test/command_callback/test_alex_command_callback.vader
new file mode 100644
index 00000000..98769e0b
--- /dev/null
+++ b/test/command_callback/test_alex_command_callback.vader
@@ -0,0 +1,34 @@
+Before:
+ call ale#assert#SetUpLinterTest('tex', 'alex')
+ call ale#test#SetFilename('test_file.tex')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The global executable should be used when the local one cannot be found):
+ AssertLinter 'alex',
+ \ ale#Escape('alex') . ' %s --text',
+
+Execute(Should use the node_modules/.bin executable, if available):
+ call ale#test#SetFilename('alex-node-modules/test_file.tex')
+
+ AssertLinter ale#path#Simplify(g:dir . '/alex-node-modules/node_modules/.bin/alex'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/alex-node-modules/node_modules/.bin/alex'))
+ \ . ' %s --text',
+
+Execute(Should use the node_modules/alex executable, if available):
+ call ale#test#SetFilename('alex-node-modules-2/test_file.tex')
+
+ AssertLinter ale#path#Simplify(g:dir . '/alex-node-modules-2/node_modules/alex/cli.js'),
+ \ (has('win32') ? 'node.exe ' : '')
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/alex-node-modules-2/node_modules/alex/cli.js'))
+ \ . ' %s --text',
+
+Execute(Should let users configure a global executable and override local paths):
+ call ale#test#SetFilename('write-good-node-modules-2/test_file.tex')
+
+ let g:ale_alex_executable = '/path/to/custom/alex'
+ let g:ale_alex_use_global = 1
+
+ AssertLinter '/path/to/custom/alex',
+ \ ale#Escape('/path/to/custom/alex') . ' %s --text'
diff --git a/test/command_callback/test_ameba_command_callback.vader b/test/command_callback/test_ameba_command_callback.vader
new file mode 100644
index 00000000..7746b44f
--- /dev/null
+++ b/test/command_callback/test_ameba_command_callback.vader
@@ -0,0 +1,20 @@
+Before:
+ call ale#assert#SetUpLinterTest('crystal', 'ameba')
+ call ale#test#SetFilename('dummy.cr')
+
+ let g:ale_crystal_ameba_executable = 'bin/ameba'
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(Executable should default to bin/ameba):
+ AssertLinter 'bin/ameba', ale#Escape('bin/ameba')
+ \ . ' --format json '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.cr'))
+
+Execute(Should be able to set a custom executable):
+ let g:ale_crystal_ameba_executable = 'ameba'
+
+ AssertLinter 'ameba' , ale#Escape('ameba')
+ \ . ' --format json '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.cr'))
diff --git a/test/command_callback/test_asciidoc_textlint_command_callbacks.vader b/test/command_callback/test_asciidoc_textlint_command_callbacks.vader
new file mode 100644
index 00000000..623833b2
--- /dev/null
+++ b/test/command_callback/test_asciidoc_textlint_command_callbacks.vader
@@ -0,0 +1,65 @@
+" Author: januswel, w0rp
+
+Before:
+ " This is just one language for the linter.
+ call ale#assert#SetUpLinterTest('asciidoc', 'textlint')
+
+ " The configuration is shared between many languages.
+ Save g:ale_textlint_executable
+ Save g:ale_textlint_use_global
+ Save g:ale_textlint_options
+
+ let g:ale_textlint_executable = 'textlint'
+ let g:ale_textlint_use_global = 0
+ let g:ale_textlint_options = ''
+
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+After:
+ unlet! b:command_tail
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The executable should be configurable):
+ let b:ale_textlint_executable = 'foobar'
+
+ AssertLinter 'foobar',
+ \ ale#Escape('foobar') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The options should be configurable):
+ let b:ale_textlint_options = '--something'
+
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' --something -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from .bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_bin_path/foo.txt')
+
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'))
+ \ . ' -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from textlint/bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_textlint_bin_path/foo.txt')
+
+ if has('win32')
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape('node.exe') . ' ' . ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ else
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ endif
diff --git a/test/command_callback/test_bandit_command_callback.vader b/test/command_callback/test_bandit_command_callback.vader
new file mode 100644
index 00000000..274ce901
--- /dev/null
+++ b/test/command_callback/test_bandit_command_callback.vader
@@ -0,0 +1,71 @@
+Before:
+ call ale#assert#SetUpLinterTest('python', 'bandit')
+ let b:bandit_flags = ' --format custom '
+ \ . '--msg-template "{line}:{test_id}:{severity}:{msg}" '
+
+After:
+ call ale#assert#TearDownLinterTest()
+ unlet! b:bandit_flags
+
+Execute(The bandit command callback should return default string):
+ AssertLinter 'bandit',
+ \ ale#Escape('bandit')
+ \ . b:bandit_flags
+ \ . ' -'
+
+Execute(The bandit command callback should allow options):
+ let g:ale_python_bandit_options = '--configfile bandit.yaml'
+
+ AssertLinter 'bandit',
+ \ ale#Escape('bandit')
+ \ . b:bandit_flags
+ \ . ' --configfile bandit.yaml -'
+
+Execute(The bandit executable should be configurable):
+ let g:ale_python_bandit_executable = '~/.local/bin/bandit'
+
+ AssertLinter '~/.local/bin/bandit',
+ \ ale#Escape('~/.local/bin/bandit')
+ \ . b:bandit_flags
+ \ . ' -'
+
+Execute(Setting executable to 'pipenv' appends 'run bandit'):
+ let g:ale_python_bandit_executable = 'path/to/pipenv'
+
+ AssertLinter 'path/to/pipenv',
+ \ ale#Escape('path/to/pipenv')
+ \ . ' run bandit'
+ \ . b:bandit_flags
+ \ . ' -'
+
+Execute(Pipenv is detected when python_bandit_auto_pipenv is set):
+ let g:ale_python_bandit_auto_pipenv = 1
+ call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+
+ AssertLinter 'pipenv',
+ \ ale#Escape('pipenv')
+ \ . ' run bandit'
+ \ . b:bandit_flags
+ \ . ' -'
+
+Execute(The bandit command callback should add .bandit by default):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_bandit/namespace/foo/bar.py')
+
+ let b:config_path = ale#path#Simplify(
+ \ g:dir . '/python_paths/with_bandit/.bandit'
+ \)
+
+ AssertLinter 'bandit',
+ \ ale#Escape('bandit')
+ \ . ' --ini ' . ale#Escape(b:config_path)
+ \ . b:bandit_flags
+ \ . ' -'
+
+Execute(The bandit command callback should support not using .bandit):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_bandit/subdir/foo/bar.py')
+ let g:ale_python_bandit_use_config = 0
+
+ AssertLinter 'bandit',
+ \ ale#Escape('bandit')
+ \ . b:bandit_flags
+ \ . ' -'
diff --git a/test/command_callback/test_bingo_command_callback.vader b/test/command_callback/test_bingo_command_callback.vader
new file mode 100644
index 00000000..f4cb3231
--- /dev/null
+++ b/test/command_callback/test_bingo_command_callback.vader
@@ -0,0 +1,46 @@
+Before:
+ call ale#assert#SetUpLinterTest('go', 'bingo')
+
+After:
+ Restore
+
+ if isdirectory(g:dir . '/.git')
+ call delete(g:dir . '/.git', 'd')
+ endif
+
+ unlet! b:ale_completion_enabled
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(should set correct defaults):
+ AssertLinter 'bingo', ale#Escape('bingo') . ' --mode stdio'
+
+Execute(should configure bingo callback executable):
+ let b:ale_go_bingo_executable = 'boo'
+ let b:ale_go_bingo_options = ''
+
+ AssertLinter 'boo', ale#Escape('boo')
+
+Execute(should set bingo options):
+ call ale#test#SetFilename('go_paths/go1/prj1/file.go')
+ " let b:ale_completion_enabled = 1
+ let b:ale_go_bingo_options = ''
+
+ AssertLinter 'bingo',
+ \ ale#Escape('bingo') . ''
+
+ let b:ale_go_bingo_options = '--mode stdio --trace'
+
+ AssertLinter 'bingo',
+ \ ale#Escape('bingo') . ' --mode stdio --trace'
+
+Execute(Should return directory for 'go.mod' if found in parent directory):
+ call ale#test#SetFilename('../go_files/test.go')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/../go_files')
+
+Execute(Should return nearest directory with '.git' if found in parent directory):
+ call ale#test#SetFilename('test.go')
+ call mkdir(g:dir . '/.git')
+
+ AssertLSPProject g:dir
diff --git a/test/command_callback/test_c_clang_command_callbacks.vader b/test/command_callback/test_c_clang_command_callbacks.vader
index 7d2ff0bf..b8c02e4d 100644
--- a/test/command_callback/test_c_clang_command_callbacks.vader
+++ b/test/command_callback/test_c_clang_command_callbacks.vader
@@ -13,8 +13,8 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The executable should be configurable):
- AssertLinter 'clang', ['', ale#Escape('clang') . b:command_tail]
+ AssertLinter 'clang', [ale#Escape('clang') . b:command_tail]
let b:ale_c_clang_executable = 'foobar'
- AssertLinter 'foobar', ['', ale#Escape('foobar') . b:command_tail]
+ AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
diff --git a/test/command_callback/test_c_gcc_command_callbacks.vader b/test/command_callback/test_c_gcc_command_callbacks.vader
index 1f51c3bc..c5b316c8 100644
--- a/test/command_callback/test_c_gcc_command_callbacks.vader
+++ b/test/command_callback/test_c_gcc_command_callbacks.vader
@@ -14,8 +14,8 @@ After:
unlet! b:command_tail
Execute(The executable should be configurable):
- AssertLinter 'gcc', ['', ale#Escape('gcc') . b:command_tail]
+ AssertLinter 'gcc', [ale#Escape('gcc') . b:command_tail]
let b:ale_c_gcc_executable = 'foobar'
- AssertLinter 'foobar', ['', ale#Escape('foobar') . b:command_tail]
+ AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
diff --git a/test/command_callback/test_cargo_command_callbacks.vader b/test/command_callback/test_cargo_command_callbacks.vader
index f0afbc91..438c97db 100644
--- a/test/command_callback/test_cargo_command_callbacks.vader
+++ b/test/command_callback/test_cargo_command_callbacks.vader
@@ -1,106 +1,113 @@
Before:
call ale#assert#SetUpLinterTest('rust', 'cargo')
+ call ale#test#SetFilename('cargo_paths/test.rs')
+ let g:cd = 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/cargo_paths')) . ' && '
let g:suffix = ' --frozen --message-format=json -q'
+ let g:ale_rust_cargo_avoid_whole_workspace = 0
" Test with version 0.22.0 by default.
- WithChainResults ['cargo 0.22.0 (3423351a5 2017-10-06)']
+ GivenCommandOutput ['cargo 0.22.0 (3423351a5 2017-10-06)']
After:
call ale#assert#TearDownLinterTest()
+ unlet! g:cd
unlet! g:suffix
Execute(The linter should not be executed when there's no Cargo.toml file):
+ call ale#test#SetFilename('../foo.rs')
AssertLinterNotExecuted
Execute(The linter should be executed when there is a Cargo.toml file):
- call ale#test#SetFilename('cargo_paths/test.rs')
-
- WithChainResults []
- AssertLinter 'cargo',
- \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/cargo_paths')) . ' && '
- \ . 'cargo build --frozen --message-format=json -q'
-
-Execute(The default command should be correct):
- WithChainResults []
- AssertLinter '', ['cargo --version', 'cargo build' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', 'cargo build --frozen --message-format=json -q'
Execute(`cargo check` should be used when the version is new enough):
- WithChainResults ['cargo 0.17.0 (3423351a5 2017-10-06)']
- AssertLinter '', ['cargo --version', 'cargo check' . g:suffix]
+ GivenCommandOutput ['cargo 0.17.0 (3423351a5 2017-10-06)']
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
+ \ 'cargo check' . g:suffix,
+ \]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo check' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo check' . g:suffix]
Execute(`cargo build` should be used when cargo is too old):
- WithChainResults ['cargo 0.16.0 (3423351a5 2017-10-06)']
- AssertLinter '', ['cargo --version', 'cargo build' . g:suffix]
+ GivenCommandOutput ['cargo 0.16.0 (3423351a5 2017-10-06)']
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
+ \ 'cargo build' . g:suffix,
+ \]
- WithChainResults []
- AssertLinter '', ['', 'cargo build' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo build' . g:suffix]
Execute(`cargo build` should be used when g:ale_rust_cargo_use_check is set to 0):
let g:ale_rust_cargo_use_check = 0
- WithChainResults ['cargo 0.24.0 (3423351a5 2017-10-06)']
- AssertLinter '', ['cargo --version', 'cargo build' . g:suffix]
+ GivenCommandOutput ['cargo 0.24.0 (3423351a5 2017-10-06)']
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
+ \ 'cargo build' . g:suffix,
+ \]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo build' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo build' . g:suffix]
Execute(`cargo check` should be used when the version is new enough):
- AssertLinter '', ['cargo --version', 'cargo check' . g:suffix]
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
+ \ 'cargo check' . g:suffix,
+ \]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo check' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo check' . g:suffix]
Execute(--all-targets should be used when g:ale_rust_cargo_check_all_targets is set to 1):
let g:ale_rust_cargo_check_all_targets = 1
- AssertLinter '', ['cargo --version', 'cargo check --all-targets' . g:suffix]
-
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --all-targets' . g:suffix]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo check --all-targets' . g:suffix]
+ AssertLinter 'cargo', ['cargo check --all-targets' . g:suffix]
Execute(--tests should be used when g:ale_rust_cargo_check_tests is set to 1):
let g:ale_rust_cargo_check_tests = 1
- AssertLinter '', ['cargo --version', 'cargo check --tests' . g:suffix]
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --tests' . g:suffix]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo check --tests' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo check --tests' . g:suffix]
Execute(--examples should be used when g:ale_rust_cargo_check_examples is set to 1):
let g:ale_rust_cargo_check_examples = 1
- AssertLinter '', ['cargo --version', 'cargo check --examples' . g:suffix]
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --examples' . g:suffix]
" We should cache the version check
- WithChainResults []
- AssertLinter '', ['', 'cargo check --examples' . g:suffix]
+ GivenCommandOutput []
+ AssertLinter 'cargo', ['cargo check --examples' . g:suffix]
Execute(--no-default-features should be used when g:ale_rust_cargo_default_feature_behavior is none):
let b:ale_rust_cargo_default_feature_behavior = 'none'
- AssertLinter '', ['cargo --version', 'cargo check --frozen --message-format=json -q --no-default-features']
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --frozen --message-format=json -q --no-default-features']
Execute(g:ale_rust_cargo_include_features added when g:ale_rust_cargo_default_feature_behavior is none):
let b:ale_rust_cargo_default_feature_behavior = 'none'
let b:ale_rust_cargo_include_features = 'foo bar'
- AssertLinter '', ['cargo --version', 'cargo check --frozen --message-format=json -q --no-default-features --features ' . ale#Escape('foo bar')]
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --frozen --message-format=json -q --no-default-features --features ' . ale#Escape('foo bar')]
Execute(g:ale_rust_cargo_include_features added and escaped):
let b:ale_rust_cargo_default_feature_behavior = 'default'
let b:ale_rust_cargo_include_features = "foo bar baz"
- AssertLinter '', ['cargo --version', 'cargo check --frozen --message-format=json -q --features ' . ale#Escape('foo bar baz')]
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --frozen --message-format=json -q --features ' . ale#Escape('foo bar baz')]
Execute(--all-features should be used when g:ale_rust_cargo_default_feature_behavior is all):
let b:ale_rust_cargo_default_feature_behavior = 'all'
@@ -108,14 +115,15 @@ Execute(--all-features should be used when g:ale_rust_cargo_default_feature_beha
" since it won't do anything
let b:ale_rust_cargo_include_features = 'foo bar'
- WithChainResults ['cargo 0.22.0 (3423351a5 2017-10-06)']
- AssertLinter '', ['cargo --version', 'cargo check --frozen --message-format=json -q --all-features']
+ GivenCommandOutput ['cargo 0.22.0 (3423351a5 2017-10-06)']
+ AssertLinter 'cargo', [ale#Escape('cargo') . ' --version', 'cargo check --frozen --message-format=json -q --all-features']
Execute(When a crate belongs to a workspace we should cd into the crate):
+ let g:ale_rust_cargo_avoid_whole_workspace = 1
call ale#test#SetFilename('cargo_workspace_paths/subpath/test.rs')
AssertLinter 'cargo', [
- \ 'cargo --version',
+ \ ale#Escape('cargo') . ' --version',
\ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/cargo_workspace_paths/subpath')) . ' && '
\ . 'cargo check --frozen --message-format=json -q',
\]
@@ -125,22 +133,22 @@ Execute(When a crate belongs to a workspace we chdir into the crate, unless we d
call ale#test#SetFilename('cargo_workspace_paths/subpath/test.rs')
AssertLinter 'cargo', [
- \ 'cargo --version',
+ \ ale#Escape('cargo') . ' --version',
\ 'cargo check --frozen --message-format=json -q',
\]
Execute(When ale_rust_cargo_use_clippy is set, cargo-clippy is used as linter):
let b:ale_rust_cargo_use_clippy = 1
- AssertLinter '', [
- \ 'cargo --version',
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
\ 'cargo clippy --frozen --message-format=json -q ',
\]
Execute(When ale_rust_cargo_clippy_options is set, cargo-clippy appends it to commandline):
let b:ale_rust_cargo_use_clippy = 1
let b:ale_rust_cargo_clippy_options = '-- -D warnings'
- AssertLinter '', [
- \ 'cargo --version',
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
\ 'cargo clippy --frozen --message-format=json -q -- -D warnings',
\]
@@ -148,7 +156,7 @@ Execute(cargo-check does not refer ale_rust_cargo_clippy_options):
let b:ale_rust_cargo_use_clippy = 0
let b:ale_rust_cargo_use_check = 1
let b:ale_rust_cargo_clippy_options = '-- -D warnings'
- AssertLinter '', [
- \ 'cargo --version',
+ AssertLinter 'cargo', [
+ \ ale#Escape('cargo') . ' --version',
\ 'cargo check --frozen --message-format=json -q',
\]
diff --git a/test/command_callback/test_cookstyle_command_callback.vader b/test/command_callback/test_cookstyle_command_callback.vader
new file mode 100644
index 00000000..ad7391cc
--- /dev/null
+++ b/test/command_callback/test_cookstyle_command_callback.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('chef', 'cookstyle')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'cookstyle', ale#Escape('cookstyle') . ' --force-exclusion --format json --stdin %s'
+
+Execute(The executable path should be configurable):
+ let b:ale_chef_cookstyle_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' --force-exclusion --format json --stdin %s'
+
+Execute(The linter options should be configurable):
+ let b:ale_chef_cookstyle_options = '--parallel'
+
+ AssertLinter 'cookstyle', ale#Escape('cookstyle') . ' --parallel --force-exclusion --format json --stdin %s'
+
diff --git a/test/command_callback/test_cypher_cypher_lint_command_callback.vader b/test/command_callback/test_cypher_cypher_lint_command_callback.vader
new file mode 100644
index 00000000..6b64dc1f
--- /dev/null
+++ b/test/command_callback/test_cypher_cypher_lint_command_callback.vader
@@ -0,0 +1,8 @@
+Before:
+ call ale#assert#SetUpLinterTest('cypher', 'cypher_lint')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command and executable should be correct):
+ AssertLinter 'cypher-lint', 'cypher-lint'
diff --git a/test/command_callback/test_eclipselsp_command_callback.vader b/test/command_callback/test_eclipselsp_command_callback.vader
new file mode 100644
index 00000000..c0ad89a5
--- /dev/null
+++ b/test/command_callback/test_eclipselsp_command_callback.vader
@@ -0,0 +1,87 @@
+Before:
+ call ale#assert#SetUpLinterTest('java', 'eclipselsp')
+ let b:ale_java_eclipselsp_path = '/home/user/eclipse.dst.ls'
+ call ale#test#SetFilename('dummy.java')
+
+ let b:cfg = '/testplugin/test/config_linux'
+
+ if has('win32')
+ let b:cfg = 'C:\testplugin\test\config_win'
+ elseif has('macunix')
+ let b:cfg = '/testplugin/test/config_mac'
+ endif
+
+
+After:
+ unlet b:ale_java_eclipselsp_path
+ call ale#assert#TearDownLinterTest()
+
+Execute(VersionCheck should return correct version):
+
+ " OpenJDK Java 1.8
+ AssertEqual [1, 8, 0], ale_linters#java#eclipselsp#VersionCheck([
+ \ 'openjdk version "1.8.0_191"',
+ \ 'OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-0ubuntu0.18.04.1-b12)',
+ \ 'OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)'
+ \])
+
+ " OpenJDK Java 10
+ AssertEqual [10, 0, 2], ale_linters#java#eclipselsp#VersionCheck([
+ \ 'openjdk version "10.0.2" 2018-07-17',
+ \ 'OpenJDK Runtime Environment (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4)',
+ \ 'OpenJDK 64-Bit Server VM (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode)'
+ \])
+
+ " Oracle Java 1.8
+ AssertEqual [1, 8, 0], ale_linters#java#eclipselsp#VersionCheck([
+ \ 'java version "1.8.0_161"',
+ \ 'Java(TM) SE Runtime Environment (build 1.8.0_161-b12)',
+ \ 'Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)'
+ \])
+
+ " Oracle Java 10
+ AssertEqual [10, 0, 1], ale_linters#java#eclipselsp#VersionCheck([
+ \ 'java version "10.0.1" 2018-04-17',
+ \ 'Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)',
+ \ 'Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)'
+ \])
+
+ AssertEqual [], ale_linters#java#eclipselsp#VersionCheck(['x'])
+
+ AssertEqual [], ale_linters#java#eclipselsp#VersionCheck([])
+
+Execute(The eclipselsp callback should return the correct default value):
+ let cmd = [ ale#Escape('java'),
+ \ '-Declipse.application=org.eclipse.jdt.ls.core.id1',
+ \ '-Dosgi.bundles.defaultStartLevel=4',
+ \ '-Declipse.product=org.eclipse.jdt.ls.core.product',
+ \ '-Dlog.level=ALL',
+ \ '-noverify',
+ \ '-Xmx1G',
+ \ '-jar',
+ \ '',
+ \ '-configuration',
+ \ b:cfg,
+ \ '-data',
+ \ ''
+ \]
+ AssertLinter 'java', join(cmd, ' ')
+
+Execute(The eclipselsp callback should allow custom executable):
+ let b:ale_java_eclipselsp_executable='/bin/foobar'
+ let cmd = [ ale#Escape('/bin/foobar'),
+ \ '-Declipse.application=org.eclipse.jdt.ls.core.id1',
+ \ '-Dosgi.bundles.defaultStartLevel=4',
+ \ '-Declipse.product=org.eclipse.jdt.ls.core.product',
+ \ '-Dlog.level=ALL',
+ \ '-noverify',
+ \ '-Xmx1G',
+ \ '-jar',
+ \ '',
+ \ '-configuration',
+ \ b:cfg,
+ \ '-data',
+ \ ''
+ \]
+ AssertLinter '/bin/foobar', join(cmd, ' ')
+
diff --git a/test/command_callback/test_elixir_credo.vader b/test/command_callback/test_elixir_credo.vader
new file mode 100644
index 00000000..1a146db8
--- /dev/null
+++ b/test/command_callback/test_elixir_credo.vader
@@ -0,0 +1,28 @@
+Before:
+ call ale#assert#SetUpLinterTest('elixir', 'credo')
+ call ale#test#SetFilename('elixir_paths/mix_project/lib/app.ex')
+
+
+After:
+ unlet! g:ale_elixir_credo_strict
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(Builds credo command with --strict mode when set to 1):
+ let g:ale_elixir_credo_strict = 1
+
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
+ \ . 'mix help credo && mix credo --strict --format=flycheck --read-from-stdin %s'
+
+Execute(Builds credo command with suggest mode by default):
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
+ \ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
+
+Execute(Builds credo command with suggest mode when set to 0):
+ let g:ale_elixir_credo_strict = 0
+
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
+ \ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
diff --git a/test/command_callback/test_elixir_ls_command_callbacks.vader b/test/command_callback/test_elixir_ls_command_callbacks.vader
index ca785054..41a5bc34 100644
--- a/test/command_callback/test_elixir_ls_command_callbacks.vader
+++ b/test/command_callback/test_elixir_ls_command_callbacks.vader
@@ -1,25 +1,24 @@
Before:
call ale#assert#SetUpLinterTest('elixir', 'elixir_ls')
- let g:ale_has_override['win32'] = 0
-
After:
- let g:ale_has_override = {}
-
call ale#assert#TearDownLinterTest()
-Execute(should set correct defaults (unix)):
- AssertLinter 'elixir-ls/language_server.sh', 'elixir-ls/language_server.sh'
-
-Execute(should set correct defaults (win32)):
- let g:ale_has_override['win32'] = 1
-
- AssertLinter 'elixir-ls\language_server.bat', 'elixir-ls\language_server.bat'
+Execute(should set correct defaults):
+ if has('win32')
+ AssertLinter 'elixir-ls\language_server.bat', 'elixir-ls\language_server.bat'
+ else
+ AssertLinter 'elixir-ls/language_server.sh', 'elixir-ls/language_server.sh'
+ endif
Execute(should configure elixir-ls release location):
let b:ale_elixir_elixir_ls_release = 'boo'
- AssertLinter 'boo/language_server.sh', 'boo/language_server.sh'
+ if has('win32')
+ AssertLinter 'boo\language_server.bat', 'boo\language_server.bat'
+ else
+ AssertLinter 'boo/language_server.sh', 'boo/language_server.sh'
+ endif
Execute(should set correct LSP values):
call ale#test#SetFilename('elixir_paths/umbrella_project/apps/app1/lib/app.ex')
diff --git a/test/command_callback/test_elm_lsp_command_callbacks.vader b/test/command_callback/test_elm_lsp_command_callbacks.vader
new file mode 100644
index 00000000..d06ef134
--- /dev/null
+++ b/test/command_callback/test_elm_lsp_command_callbacks.vader
@@ -0,0 +1,29 @@
+Before:
+ call ale#assert#SetUpLinterTest('elm', 'elm_lsp')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default executable path should be correct):
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
+
+ AssertLinter 'elm-lsp', ale#Escape('elm-lsp') . ' --stdio'
+
+Execute(The project root should be detected correctly):
+ AssertLSPProject ''
+
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/../elm-test-files/newapp')
+
+Execute(Should let users configure a global executable and override local paths):
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
+
+ let g:ale_elm_lsp_executable = '/path/to/custom/elm-lsp'
+ let g:ale_elm_lsp_use_global = 1
+
+ AssertLinter '/path/to/custom/elm-lsp',
+ \ ale#Escape('/path/to/custom/elm-lsp') . ' --stdio'
+
+Execute(The language should be correct):
+ AssertLSPLanguage 'elm'
diff --git a/test/command_callback/test_elm_make_command_callback.vader b/test/command_callback/test_elm_make_command_callback.vader
index 6d95676f..7ad439f9 100644
--- a/test/command_callback/test_elm_make_command_callback.vader
+++ b/test/command_callback/test_elm_make_command_callback.vader
@@ -7,26 +7,57 @@ After:
call ale#assert#TearDownLinterTest()
Execute(should get valid executable with default params):
- call ale#test#SetFilename('../elm-test-files/app/testfile.elm')
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
- let g:executable = ale#path#Simplify(g:dir . '/../elm-test-files/app/node_modules/.bin/elm')
+ let g:executable = ale#path#Simplify(g:dir . '/../elm-test-files/newapp/node_modules/.bin/elm')
AssertLinter g:executable,
- \ ale#Escape(g:executable) . ' make --report=json --output=/dev/null %t'
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/newapp')) . ' && '
+ \ . ale#Escape(g:executable) . ' make --report=json --output=/dev/null %t'
+
+Execute(should get elm-test executable for test code with elm >= 0.19):
+ call ale#test#SetFilename('../elm-test-files/newapp/tests/TestSuite.elm')
+
+ let g:executable = ale#path#Simplify(g:dir . '/../elm-test-files/newapp/node_modules/.bin/elm-test')
+
+ AssertLinter g:executable,
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/newapp')) . ' && '
+ \ . ale#Escape(g:executable) . ' make --report=json --output=/dev/null --compiler '
+ \ . ale#path#Simplify(g:dir . '/../elm-test-files/newapp/node_modules/.bin/elm') . ' %t'
+
+Execute(should fallback to elm executable with elm >= 0.19):
+ call ale#test#SetFilename('../elm-test-files/newapp-notests/tests/TestMain.elm')
+
+ let g:executable = ale#path#Simplify(g:dir . '/../elm-test-files/newapp-notests/node_modules/.bin/elm')
+
+ AssertLinter g:executable,
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/newapp-notests')) . ' && '
+ \ . ale#Escape(g:executable) . ' make --report=json --output=/dev/null %t'
+
+Execute(should get plain elm executable for test code with elm < 0.19):
+ call ale#test#SetFilename('../elm-test-files/oldapp/tests/TestSuite.elm')
+
+ let g:executable = ale#path#Simplify(g:dir . '/../elm-test-files/oldapp/node_modules/.bin/elm')
+
+ AssertLinter g:executable,
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/oldapp')) . ' && '
+ \ . ale#Escape(g:executable) . ' make --report=json --output=/dev/null %t'
Execute(should get valid executable with 'use_global' params):
let g:ale_elm_make_use_global = 1
- call ale#test#SetFilename('../elm-test-files/app/testfile.elm')
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
AssertLinter 'elm',
- \ ale#Escape('elm') . ' make --report=json --output=/dev/null %t'
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/newapp')) . ' && '
+ \ . ale#Escape('elm') . ' make --report=json --output=/dev/null %t'
Execute(should get valid executable with 'use_global' and 'executable' params):
let g:ale_elm_make_executable = 'other-elm'
let g:ale_elm_make_use_global = 1
- call ale#test#SetFilename('../elm-test-files/app/testfile.elm')
+ call ale#test#SetFilename('../elm-test-files/newapp/src/Main.elm')
AssertLinter 'other-elm',
- \ ale#Escape('other-elm') . ' make --report=json --output=/dev/null %t'
+ \ 'cd ' . ale#Escape(ale#path#Simplify(g:dir . '/../elm-test-files/newapp')) . ' && '
+ \ . ale#Escape('other-elm') . ' make --report=json --output=/dev/null %t'
diff --git a/test/command_callback/test_erlang_syntaxerl_command_callback.vader b/test/command_callback/test_erlang_syntaxerl_command_callback.vader
index 28cff9b9..e7cc26ea 100644
--- a/test/command_callback/test_erlang_syntaxerl_command_callback.vader
+++ b/test/command_callback/test_erlang_syntaxerl_command_callback.vader
@@ -19,7 +19,7 @@ Execute (The executable should be configurable):
\]
Execute (The -b option should be used when available):
- WithChainResults [
+ GivenCommandOutput [
\ 'Syntax checker for Erlang (0.14.0)',
\ 'Usage: syntaxerl [-d | --debug] <FILENAME>',
\ ' syntaxerl <-h | --help>',
@@ -31,7 +31,7 @@ Execute (The -b option should be used when available):
\ ale#Escape('syntaxerl') . ' %t',
\]
- WithChainResults [
+ GivenCommandOutput [
\ 'Syntax checker for Erlang (0.14.0)',
\ 'Usage: syntaxerl [-b | --base <FILENAME>] [-d | --debug] <FILENAME>',
\ ' syntaxerl <-h | --help>',
diff --git a/test/command_callback/test_erubi_command_callback.vader b/test/command_callback/test_erubi_command_callback.vader
index 0643efc2..0d88ff93 100644
--- a/test/command_callback/test_erubi_command_callback.vader
+++ b/test/command_callback/test_erubi_command_callback.vader
@@ -21,7 +21,7 @@ Execute(Executable should filter invalid eRuby when inside a Rails project):
\]
Execute(Command should be blank if the first command in the chain returns output):
- WithChainResults [
+ GivenCommandOutput [
\ "/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- erubi/capture_end (LoadError)",
\ " from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'",
\]
diff --git a/test/command_callback/test_fecs_command_callback.vader b/test/command_callback/test_fecs_command_callback.vader
new file mode 100644
index 00000000..f70ad084
--- /dev/null
+++ b/test/command_callback/test_fecs_command_callback.vader
@@ -0,0 +1,8 @@
+Before:
+ call ale#assert#SetUpLinterTest('javascript', 'fecs')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'fecs', ale#Escape('fecs') . ' check --colors=false --rule=true %t'
diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader
index ede511e0..f082a63a 100644
--- a/test/command_callback/test_flake8_command_callback.vader
+++ b/test/command_callback/test_flake8_command_callback.vader
@@ -3,7 +3,7 @@ Before:
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
- WithChainResults ['3.0.0']
+ GivenCommandOutput ['3.0.0']
After:
unlet! b:executable
@@ -18,16 +18,15 @@ Execute(The flake8 callbacks should return the correct default values):
\]
" The version check should be cached.
- WithChainResults []
+ GivenCommandOutput []
AssertLinter 'flake8', [
- \ '',
\ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
\]
" Try with older versions.
call ale#semver#ResetVersionCache()
- WithChainResults ['2.9.9']
+ GivenCommandOutput ['2.9.9']
AssertLinter 'flake8', [
\ ale#Escape('flake8') . ' --version',
\ ale#path#BufferCdString(bufnr(''))
@@ -45,7 +44,7 @@ Execute(The option for disabling changing directories should work):
Execute(The flake8 command callback should let you set options):
let g:ale_python_flake8_options = '--some-option'
- WithChainResults ['3.0.4']
+ GivenCommandOutput ['3.0.4']
AssertLinter 'flake8', [
\ ale#Escape('flake8') . ' --version',
\ ale#path#BufferCdString(bufnr(''))
@@ -54,7 +53,7 @@ Execute(The flake8 command callback should let you set options):
\]
call ale#semver#ResetVersionCache()
- WithChainResults ['2.9.9']
+ GivenCommandOutput ['2.9.9']
AssertLinter 'flake8', [
\ ale#Escape('flake8') . ' --version',
\ ale#path#BufferCdString(bufnr(''))
@@ -129,7 +128,7 @@ Execute(Using `python -m flake8` should be supported for running flake8):
let g:ale_python_flake8_executable = 'python'
let g:ale_python_flake8_options = '-m flake8 --some-option'
- WithChainResults ['2.9.9']
+ GivenCommandOutput ['2.9.9']
AssertLinter 'python', [
\ ale#Escape('python') . ' -m flake8 --version',
\ ale#path#BufferCdString(bufnr(''))
@@ -142,7 +141,7 @@ Execute(Using `python -m flake8` should be supported for running flake8):
" Leading spaces shouldn't matter
let g:ale_python_flake8_options = ' -m flake8 --some-option'
- WithChainResults ['2.9.9']
+ GivenCommandOutput ['2.9.9']
AssertLinter 'python', [
\ ale#Escape('python') . ' -m flake8 --version',
\ ale#path#BufferCdString(bufnr(''))
@@ -154,14 +153,14 @@ Execute(Setting executable to 'pipenv' should append 'run flake8'):
let g:ale_python_flake8_executable = 'path/to/pipenv'
" FIXME: pipenv should check the version with flake8.
- WithChainResults []
+ GivenCommandOutput []
AssertLinter 'path/to/pipenv',
\ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('path/to/pipenv') . ' run flake8 --format=default -'
Execute(Pipenv is detected when python_flake8_auto_pipenv is set):
let g:ale_python_flake8_auto_pipenv = 1
- call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+ call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
\ ale#path#BufferCdString(bufnr(''))
diff --git a/test/command_callback/test_ghdl_command_callbacks.vader b/test/command_callback/test_ghdl_command_callbacks.vader
new file mode 100644
index 00000000..f254e11f
--- /dev/null
+++ b/test/command_callback/test_ghdl_command_callbacks.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('vhdl', 'ghdl')
+
+After:
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The executable should be configurable):
+ AssertLinter 'ghdl', ale#Escape('ghdl') . ' -s --std=08 %t'
+
+ let b:ale_vhdl_ghdl_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' -s --std=08 %t'
+
+Execute(The options should be configurable):
+ let b:ale_vhdl_ghdl_options = '--something'
+
+ AssertLinter 'ghdl', ale#Escape('ghdl') . ' -s --something %t'
diff --git a/test/command_callback/test_gobuild_command_callback.vader b/test/command_callback/test_gobuild_command_callback.vader
index c6e324f2..79015b75 100644
--- a/test/command_callback/test_gobuild_command_callback.vader
+++ b/test/command_callback/test_gobuild_command_callback.vader
@@ -3,7 +3,7 @@ Before:
call ale#assert#SetUpLinterTest('go', 'gobuild')
- WithChainResults ['/foo/bar', '/foo/baz']
+ GivenCommandOutput ['/foo/bar', '/foo/baz']
After:
Restore
diff --git a/test/command_callback/test_gopls_command_callback.vader b/test/command_callback/test_gopls_command_callback.vader
new file mode 100644
index 00000000..0e079a73
--- /dev/null
+++ b/test/command_callback/test_gopls_command_callback.vader
@@ -0,0 +1,46 @@
+Before:
+ call ale#assert#SetUpLinterTest('go', 'gopls')
+
+After:
+ Restore
+
+ if isdirectory(g:dir . '/.git')
+ call delete(g:dir . '/.git', 'd')
+ endif
+
+ unlet! b:ale_completion_enabled
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(should set correct defaults):
+ AssertLinter 'gopls', ale#Escape('gopls') . ' --mode stdio'
+
+Execute(should configure gopls callback executable):
+ let b:ale_go_gopls_executable = 'boo'
+ let b:ale_go_gopls_options = ''
+
+ AssertLinter 'boo', ale#Escape('boo')
+
+Execute(should set gopls options):
+ call ale#test#SetFilename('go_paths/go1/prj1/file.go')
+ " let b:ale_completion_enabled = 1
+ let b:ale_go_gopls_options = ''
+
+ AssertLinter 'gopls',
+ \ ale#Escape('gopls') . ''
+
+ let b:ale_go_gopls_options = '--mode stdio --trace'
+
+ AssertLinter 'gopls',
+ \ ale#Escape('gopls') . ' --mode stdio --trace'
+
+Execute(Should return directory for 'go.mod' if found in parent directory):
+ call ale#test#SetFilename('../go_files/test.go')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/../go_files')
+
+Execute(Should return nearest directory with '.git' if found in parent directory):
+ call ale#test#SetFilename('test.go')
+ call mkdir(g:dir . '/.git')
+
+ AssertLSPProject g:dir
diff --git a/test/command_callback/test_gotype_command_callback.vader b/test/command_callback/test_gotype_command_callback.vader
index 1898a0cb..113f4823 100644
--- a/test/command_callback/test_gotype_command_callback.vader
+++ b/test/command_callback/test_gotype_command_callback.vader
@@ -7,7 +7,7 @@ After:
Execute(The default gotype command should be correct):
AssertLinter 'gotype',
- \ ale#path#CdString(expand('%:p:h')) . ' gotype .'
+ \ ale#path#CdString(expand('%:p:h')) . ' gotype -e .'
Execute(The gotype callback should ignore test files):
call ale#test#SetFilename('bla_test.go')
diff --git a/test/command_callback/test_graphql_gqlint_command_callbacks.vader b/test/command_callback/test_graphql_gqlint_command_callbacks.vader
new file mode 100644
index 00000000..0f4e9770
--- /dev/null
+++ b/test/command_callback/test_graphql_gqlint_command_callbacks.vader
@@ -0,0 +1,11 @@
+Before:
+ call ale#assert#SetUpLinterTest('graphql', 'gqlint')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The linter should run from the directory of the file in the buffer):
+ AssertLinter 'gqlint',
+ \ ale#path#CdString(expand('%:p:h'))
+ \ . 'gqlint --reporter=simple'
+ \ . ' %t'
diff --git a/test/command_callback/test_haskell_cabal_ghc_command_callbacks.vader b/test/command_callback/test_haskell_cabal_ghc_command_callbacks.vader
index 650aefa3..9e3712d6 100644
--- a/test/command_callback/test_haskell_cabal_ghc_command_callbacks.vader
+++ b/test/command_callback/test_haskell_cabal_ghc_command_callbacks.vader
@@ -13,11 +13,11 @@ After:
Execute(The options should be used in the command):
AssertEqual
- \ 'cabal exec -- ghc -fno-code -v0 %t',
+ \ ale#path#BufferCdString(bufnr('')) . 'cabal exec -- ghc -fno-code -v0 %t',
\ ale_linters#haskell#cabal_ghc#GetCommand(bufnr(''))
let b:ale_haskell_cabal_ghc_options = 'foobar'
AssertEqual
- \ 'cabal exec -- ghc foobar %t',
+ \ ale#path#BufferCdString(bufnr('')) . 'cabal exec -- ghc foobar %t',
\ ale_linters#haskell#cabal_ghc#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_haskell_stack_ghc_command_callback.vader b/test/command_callback/test_haskell_stack_ghc_command_callback.vader
index 4adab583..aa13fb2b 100644
--- a/test/command_callback/test_haskell_stack_ghc_command_callback.vader
+++ b/test/command_callback/test_haskell_stack_ghc_command_callback.vader
@@ -10,5 +10,8 @@ Execute(The linter should not be executed when there's no stack.yaml file):
Execute(The linter should be executed when there is a stack.yaml file):
call ale#test#SetFilename('stack_ghc_paths/test.hs')
- AssertLinter 'stack', 'stack ghc -- -fno-code -v0 %t'
+ AssertLinter 'stack', ale#path#BufferCdString(bufnr('')) . 'stack ghc -- -fno-code -v0 %t'
+ let b:ale_haskell_stack_ghc_options = 'foobar'
+
+ AssertLinter 'stack', ale#path#BufferCdString(bufnr('')) . 'stack ghc -- foobar %t'
diff --git a/test/command_callback/test_javac_command_callback.vader b/test/command_callback/test_javac_command_callback.vader
index 2dcb6a1b..42c64e54 100644
--- a/test/command_callback/test_javac_command_callback.vader
+++ b/test/command_callback/test_javac_command_callback.vader
@@ -48,30 +48,30 @@ Execute(The executable should be configurable):
\ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
Execute(The javac callback should include discovered classpaths):
- WithChainResults [
+ let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [
\ '[DEBUG] Ignore this.',
\ '[INFO] Something we should ignore.',
\ '/foo/bar.jar',
\ '/xyz/abc.jar',
- \]
+ \], {})
- AssertLinter 'javac',
+ AssertEqual
\ g:prefix
\ . ' -cp '
\ . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep))
- \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
+ \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t',
+ \ substitute(b:command, '%e', '\=ale#Escape(''javac'')', 'g')
Execute(The javac callback should combine discovered classpaths and manual ones):
let g:ale_java_javac_classpath = 'configured.jar'
-
- WithChainResults [
+ let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [
\ '[DEBUG] Ignore this.',
\ '[INFO] Something we should ignore.',
\ '/foo/bar.jar',
\ '/xyz/abc.jar',
- \]
+ \], {})
- AssertLinter 'javac',
+ AssertEqual
\ g:prefix
\ . ' -cp '
\ . ale#Escape(join(
@@ -82,11 +82,18 @@ Execute(The javac callback should combine discovered classpaths and manual ones)
\ ],
\ g:cp_sep
\ ))
- \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
+ \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t',
+ \ substitute(b:command, '%e', '\=ale#Escape(''javac'')', 'g')
let g:ale_java_javac_classpath = 'configured.jar' . g:cp_sep . 'configured2.jar'
+ let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [
+ \ '[DEBUG] Ignore this.',
+ \ '[INFO] Something we should ignore.',
+ \ '/foo/bar.jar',
+ \ '/xyz/abc.jar',
+ \], {})
- AssertLinter 'javac',
+ AssertEqual
\ g:prefix
\ . ' -cp '
\ . ale#Escape(join(
@@ -98,7 +105,8 @@ Execute(The javac callback should combine discovered classpaths and manual ones)
\ ],
\ g:cp_sep
\ ))
- \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
+ \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t',
+ \ substitute(b:command, '%e', '\=ale#Escape(''javac'')', 'g')
Execute(The javac callback should detect source directories):
call ale#engine#Cleanup(bufnr(''))
@@ -117,25 +125,25 @@ Execute(The javac callback should combine detected source directories and classp
call ale#test#SetFilename('java_paths/src/main/java/com/something/dummy.java')
call ale#engine#InitBufferInfo(bufnr(''))
- WithChainResults [
+ let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [
\ '[DEBUG] Ignore this.',
\ '[INFO] Something we should ignore.',
\ '/foo/bar.jar',
\ '/xyz/abc.jar',
- \]
- AssertLinter 'javac',
+ \], {})
+
+ AssertEqual
\ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
\ . ' -cp ' . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep))
\ . ' -sourcepath ' . ale#Escape(
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/')
\ )
- \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
+ \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t',
+ \ substitute(b:command, '%e', '\=ale#Escape(''javac'')', 'g')
Execute(The javac callback should use g:ale_java_javac_options correctly):
let g:ale_java_javac_options = '--anything --else'
- let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [])
-
AssertLinter 'javac',
\ g:prefix . ' -d ' . ale#Escape('TEMP_DIR') . ' --anything --else %t'
@@ -166,3 +174,15 @@ Execute(The javac callback should include src/main/jaxb when available):
\ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/jaxb/'),
\ ], g:cp_sep))
\ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
+
+Execute(The javac callback should add -sourcepath even if src/java/main doesn't exist):
+ call ale#engine#Cleanup(bufnr(''))
+ call ale#test#SetFilename('java_paths_no_main/src/test/java/com/something/dummy.java')
+ call ale#engine#InitBufferInfo(bufnr(''))
+
+ AssertLinter 'javac',
+ \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ . ' -sourcepath ' . ale#Escape(join([
+ \ ale#path#Simplify(g:dir . '/java_paths_no_main/src/test/java/'),
+ \ ], g:cp_sep))
+ \ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
diff --git a/test/command_callback/test_javalsp_command_callback.vader b/test/command_callback/test_javalsp_command_callback.vader
index aedb4a4b..8bfaa8ee 100644
--- a/test/command_callback/test_javalsp_command_callback.vader
+++ b/test/command_callback/test_javalsp_command_callback.vader
@@ -6,9 +6,9 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The javalsp callback should return the correct default value):
- AssertLinter 'java', ale#Escape('java') . ' -cp javacs.jar -Xverify:none org.javacs.Main'
+ AssertLinter 'java', ale#Escape('java') . ' -Xverify:none -m javacs/org.javacs.Main'
Execute(The javalsp java executable should be configurable):
let b:ale_java_javalsp_executable = '/bin/foobar'
- AssertLinter '/bin/foobar', ale#Escape('/bin/foobar') . ' -cp javacs.jar -Xverify:none org.javacs.Main'
+ AssertLinter '/bin/foobar', ale#Escape('/bin/foobar') . ' -Xverify:none -m javacs/org.javacs.Main'
diff --git a/test/command_callback/test_javascript_tsserver_command_callback.vader b/test/command_callback/test_javascript_tsserver_command_callback.vader
index 638dd873..04230238 100644
--- a/test/command_callback/test_javascript_tsserver_command_callback.vader
+++ b/test/command_callback/test_javascript_tsserver_command_callback.vader
@@ -6,3 +6,11 @@ After:
Execute(The default command should be correct):
AssertLinter 'tsserver', ale#Escape('tsserver')
+
+Execute(should resolve correct path when nested 1):
+ call ale#test#SetFilename('tsserver_paths/src/level-1/level-2/file3.ts')
+ AssertLSPProject ale#path#Simplify(g:dir . '/tsserver_paths/src/level-1')
+
+Execute(should resolve correct path when nested 2):
+ call ale#test#SetFilename('tsserver_paths/src/file1.ts')
+ AssertLSPProject ale#path#Simplify(g:dir . '/tsserver_paths')
diff --git a/test/command_callback/test_kotlinc_command_callback.vader b/test/command_callback/test_kotlinc_command_callback.vader
new file mode 100644
index 00000000..fe94bffa
--- /dev/null
+++ b/test/command_callback/test_kotlinc_command_callback.vader
@@ -0,0 +1,9 @@
+Before:
+ call ale#assert#SetUpLinterTest('kotlin', 'kotlinc')
+ call ale#test#SetFilename('test.kt')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'kotlinc', 'kotlinc ' . ale#Escape(expand('%:p'))
diff --git a/test/command_callback/test_languagetool_command_callback.vader b/test/command_callback/test_languagetool_command_callback.vader
new file mode 100644
index 00000000..a79662b9
--- /dev/null
+++ b/test/command_callback/test_languagetool_command_callback.vader
@@ -0,0 +1,15 @@
+Before:
+ call ale#assert#SetUpLinterTest('text', 'languagetool')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'languagetool', ale#Escape('languagetool')
+ \ . ' --autoDetect %s'
+
+Execute(Should be able to set a custom executable):
+ let g:ale_languagetool_executable = 'foobar'
+
+ AssertLinter 'foobar' , ale#Escape('foobar')
+ \ . ' --autoDetect %s'
diff --git a/test/command_callback/test_php_langserver_callbacks.vader b/test/command_callback/test_php_langserver_callbacks.vader
index 3b0a427e..59c3fe6c 100644
--- a/test/command_callback/test_php_langserver_callbacks.vader
+++ b/test/command_callback/test_php_langserver_callbacks.vader
@@ -2,10 +2,6 @@ Before:
call ale#assert#SetUpLinterTest('php', 'langserver')
After:
- if isdirectory(g:dir . '/.git')
- call delete(g:dir . '/.git', 'd')
- endif
-
call ale#assert#TearDownLinterTest()
Execute(The default executable path should be correct):
@@ -23,7 +19,12 @@ Execute(Vendor executables should be detected):
\ ))
Execute(The project path should be correct for .git directories):
- call ale#test#SetFilename('php-langserver-project/test.php')
- call mkdir(g:dir . '/.git')
+ call ale#test#SetFilename('php-langserver-project/with-git/test.php')
+ silent! call mkdir('php-langserver-project/with-git/.git')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/php-langserver-project/with-git')
+
+Execute(The project path should be correct for composer.json file):
+ call ale#test#SetFilename('php-langserver-project/with-composer/test.php')
- AssertLSPProject g:dir
+ AssertLSPProject ale#path#Simplify(g:dir . '/php-langserver-project/with-composer')
diff --git a/test/command_callback/test_phpstan_command_callbacks.vader b/test/command_callback/test_phpstan_command_callbacks.vader
index 665661a3..3714fc64 100644
--- a/test/command_callback/test_phpstan_command_callbacks.vader
+++ b/test/command_callback/test_phpstan_command_callbacks.vader
@@ -1,34 +1,66 @@
Before:
+ call delete('./phpstan.neon')
call ale#assert#SetUpLinterTest('php', 'phpstan')
- WithChainResults ['0.10.2']
+ GivenCommandOutput ['0.10.2']
After:
+ call delete('./phpstan.neon')
call ale#assert#TearDownLinterTest()
Execute(Custom executables should be used for the executable and command):
let g:ale_php_phpstan_executable = 'phpstan_test'
AssertLinter 'phpstan_test',
- \ ale#Escape('phpstan_test') . ' analyze -l4 --errorFormat raw %s'
+ \ ale#Escape('phpstan_test') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('4') . ' %s'
Execute(project with level set to 3):
call ale#test#SetFilename('phpstan-test-files/foo/test.php')
let g:ale_php_phpstan_level = 3
AssertLinter 'phpstan',
- \ ale#Escape('phpstan') . ' analyze -l3 --errorFormat raw %s'
+ \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('3') . ' %s'
Execute(Custom phpstan configuration file):
let g:ale_php_phpstan_configuration = 'phpstan_config'
AssertLinter 'phpstan',
- \ ale#Escape('phpstan') . ' analyze -l4 --errorFormat raw -c phpstan_config %s'
+ \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -c ' . ale#Escape('phpstan_config') . ' -l ' . ale#Escape('4') . ' %s'
Execute(Choose the right format for error format param):
- WithChainResults ['0.10.3']
+ GivenCommandOutput ['0.10.3']
AssertLinter 'phpstan', [
\ ale#Escape('phpstan') . ' --version',
- \ ale#Escape('phpstan') . ' analyze -l4 --error-format raw %s'
+ \ ale#Escape('phpstan') . ' analyze --no-progress --error-format raw -l ' . ale#Escape('4') . ' %s'
+ \ ]
+
+Execute(Configuration file exists in current directory):
+ call writefile(['parameters:', ' level: 7'], './phpstan.neon')
+ let g:ale_php_phpstan_level = ''
+ let g:ale_php_phpstan_configuration = ''
+
+ AssertLinter 'phpstan', [
+ \ ale#Escape('phpstan') . ' --version',
+ \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw %s'
+ \ ]
+
+Execute(Configuration file exists in current directory, but force phpstan level):
+ call writefile(['parameters:', ' level: 7'], './phpstan.neon')
+ let g:ale_php_phpstan_configuration = ''
+ let g:ale_php_phpstan_level = '7'
+
+ AssertLinter 'phpstan', [
+ \ ale#Escape('phpstan') . ' --version',
+ \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('7') . ' %s'
+ \ ]
+
+Execute(Configuration file exists in current directory, but force phpstan configuration):
+ call writefile(['parameters:', ' level: 7'], './phpstan.neon')
+ let g:ale_php_phpstan_level = ''
+ let g:ale_php_phpstan_configuration = 'phpstan.custom.neon'
+
+ AssertLinter 'phpstan', [
+ \ ale#Escape('phpstan') . ' --version',
+ \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -c ' . ale#Escape('phpstan.custom.neon') . ' %s'
\ ]
diff --git a/test/command_callback/test_psalm_command_callbacks.vader b/test/command_callback/test_psalm_command_callbacks.vader
index d731054f..33d770c2 100644
--- a/test/command_callback/test_psalm_command_callbacks.vader
+++ b/test/command_callback/test_psalm_command_callbacks.vader
@@ -24,6 +24,9 @@ Execute(Vendor executables should be detected):
Execute(The project path should be correct for .git directories):
call ale#test#SetFilename('psalm-project/test.php')
- call mkdir(g:dir . '/.git')
- AssertLSPProject g:dir \ No newline at end of file
+ if !isdirectory(g:dir . '/.git')
+ call mkdir(g:dir . '/.git')
+ endif
+
+ AssertLSPProject g:dir
diff --git a/test/command_callback/test_pylama_command_callback.vader b/test/command_callback/test_pylama_command_callback.vader
new file mode 100644
index 00000000..417cb5c9
--- /dev/null
+++ b/test/command_callback/test_pylama_command_callback.vader
@@ -0,0 +1,85 @@
+Before:
+ call ale#assert#SetUpLinterTest('python', 'pylama')
+ call ale#test#SetFilename('test.py')
+
+ let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
+ let b:command_tail = ' %s'
+
+After:
+ unlet! b:bin_dir
+ unlet! b:executable
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The pylama command callback should return a default):
+ AssertLinter 'pylama',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('pylama') . b:command_tail
+
+Execute(The option for disabling changing directories should work):
+ let g:ale_python_pylama_change_directory = 0
+
+ AssertLinter 'pylama', ale#Escape('pylama') . b:command_tail
+
+Execute(The pylama executable should be configurable, and escaped properly):
+ let g:ale_python_pylama_executable = 'executable with spaces'
+
+ AssertLinter 'executable with spaces',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('executable with spaces') . b:command_tail
+
+Execute(The pylama command callback should let you set options):
+ let g:ale_python_pylama_options = '--some-option'
+
+ AssertLinter 'pylama',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('pylama') . ' --some-option' . b:command_tail
+
+Execute(The pylama command callback should switch directories to the detected project root):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/bar.py')
+
+ AssertLinter 'pylama',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir'))
+ \ . ale#Escape('pylama') . b:command_tail
+
+Execute(The pylama command callback shouldn't detect virtualenv directories where they don't exist):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/bar.py')
+
+ AssertLinter 'pylama',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir'))
+ \ . ale#Escape('pylama') . b:command_tail
+
+Execute(The pylama command callback should detect virtualenv directories and switch to the project root):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+
+ let b:executable = ale#path#Simplify(
+ \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/pylama'
+ \)
+
+ AssertLinter b:executable,
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
+ \ . ale#Escape(b:executable) . b:command_tail
+
+Execute(You should able able to use the global pylama instead):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+ let g:ale_python_pylama_use_global = 1
+
+ AssertLinter 'pylama',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
+ \ . ale#Escape('pylama') . b:command_tail
+
+Execute(Setting executable to 'pipenv' appends 'run pylama'):
+ let g:ale_python_pylama_executable = 'path/to/pipenv'
+
+ AssertLinter 'path/to/pipenv',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('path/to/pipenv') . ' run pylama' . b:command_tail
+
+Execute(Pipenv is detected when python_pylama_auto_pipenv is set):
+ let g:ale_python_pylama_auto_pipenv = 1
+ call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+
+ AssertLinter 'pipenv',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('pipenv') . ' run pylama' . b:command_tail
diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader
index 6b21b127..c41c8398 100644
--- a/test/command_callback/test_pylint_command_callback.vader
+++ b/test/command_callback/test_pylint_command_callback.vader
@@ -39,7 +39,7 @@ Execute(The pylint callbacks shouldn't detect virtualenv directories where they
silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/bar.py')
AssertLinter 'pylint',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir'))
\ . ale#Escape('pylint') . ' ' . b:command_tail
Execute(The pylint callbacks should detect virtualenv directories):
@@ -50,7 +50,7 @@ Execute(The pylint callbacks should detect virtualenv directories):
\)
AssertLinter b:executable,
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
\ . ale#Escape(b:executable) . ' ' . b:command_tail
Execute(You should able able to use the global pylint instead):
@@ -58,7 +58,7 @@ Execute(You should able able to use the global pylint instead):
let g:ale_python_pylint_use_global = 1
AssertLinter 'pylint',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
\ . ale#Escape('pylint') . ' ' . b:command_tail
Execute(Setting executable to 'pipenv' appends 'run pylint'):
diff --git a/test/command_callback/test_pyls_command_callback.vader b/test/command_callback/test_pyls_command_callback.vader
index 531b5b3b..aff2fcd7 100644
--- a/test/command_callback/test_pyls_command_callback.vader
+++ b/test/command_callback/test_pyls_command_callback.vader
@@ -45,3 +45,8 @@ Execute(Pipenv is detected when python_pyls_auto_pipenv is set):
AssertLinter 'pipenv',
\ ale#Escape('pipenv') . ' run pyls'
+
+Execute(Should accept configuration settings):
+ AssertLSPConfig {}
+ let b:ale_python_pyls_config = {'pyls': {'plugins': {'preload': {'enabled': v:false}}}}
+ AssertLSPConfig {'pyls': {'plugins': {'preload': {'enabled': v:false}}}}
diff --git a/test/command_callback/test_racket_raco_command_callback.vader b/test/command_callback/test_racket_raco_command_callback.vader
new file mode 100644
index 00000000..fb83ffa1
--- /dev/null
+++ b/test/command_callback/test_racket_raco_command_callback.vader
@@ -0,0 +1,10 @@
+Before:
+ call ale#assert#SetUpLinterTest('racket', 'raco')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command and executable should be correct):
+ AssertLinter 'raco', 'raco expand %s'
+
+
diff --git a/test/command_callback/test_reek_command_callback.vader b/test/command_callback/test_reek_command_callback.vader
index a7cb7fb9..798c3314 100644
--- a/test/command_callback/test_reek_command_callback.vader
+++ b/test/command_callback/test_reek_command_callback.vader
@@ -5,7 +5,7 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The reek callbacks should return the correct default values):
- WithChainResults ['reek 5.0.0']
+ GivenCommandOutput ['reek 5.0.0']
AssertLinter 'reek', [
\ ale#Escape('reek') . ' --version',
\ ale#Escape('reek') . ' -f json --no-progress --no-color --force-exclusion --stdin-filename %s',
@@ -14,7 +14,7 @@ Execute(The reek callbacks should return the correct default values):
" Try with older versions.
call ale#semver#ResetVersionCache()
- WithChainResults ['reek 4.8.2']
+ GivenCommandOutput ['reek 4.8.2']
AssertLinter 'reek', [
\ ale#Escape('reek') . ' --version',
\ ale#Escape('reek') . ' -f json --no-progress --no-color --force-exclusion',
@@ -23,7 +23,7 @@ Execute(The reek callbacks should return the correct default values):
Execute(Setting bundle appends 'exec reek'):
let g:ale_ruby_reek_executable = 'bundle'
- WithChainResults ['reek 5.0.0']
+ GivenCommandOutput ['reek 5.0.0']
AssertLinter 'bundle', ale#Escape('bundle')
\ . ' exec reek'
\ . ' -f json --no-progress --no-color --force-exclusion --stdin-filename %s',
@@ -31,20 +31,19 @@ Execute(Setting bundle appends 'exec reek'):
" Try with older versions.
call ale#semver#ResetVersionCache()
- WithChainResults ['reek 4.8.2']
+ GivenCommandOutput ['reek 4.8.2']
AssertLinter 'bundle', ale#Escape('bundle')
\ . ' exec reek'
\ . ' -f json --no-progress --no-color --force-exclusion'
Execute(The reek version check should be cached):
- WithChainResults ['reek 5.0.0']
+ GivenCommandOutput ['reek 5.0.0']
AssertLinter 'reek', [
\ ale#Escape('reek') . ' --version',
\ ale#Escape('reek') . ' -f json --no-progress --no-color --force-exclusion --stdin-filename %s',
\]
- WithChainResults []
+ GivenCommandOutput []
AssertLinter 'reek', [
- \ '',
\ ale#Escape('reek') . ' -f json --no-progress --no-color --force-exclusion --stdin-filename %s',
\]
diff --git a/test/command_callback/test_rst_textlint_command_callbacks.vader b/test/command_callback/test_rst_textlint_command_callbacks.vader
new file mode 100644
index 00000000..9bc4c5c2
--- /dev/null
+++ b/test/command_callback/test_rst_textlint_command_callbacks.vader
@@ -0,0 +1,65 @@
+" Author: januswel, w0rp
+
+Before:
+ " This is just one language for the linter.
+ call ale#assert#SetUpLinterTest('rst', 'textlint')
+
+ " The configuration is shared between many languages.
+ Save g:ale_textlint_executable
+ Save g:ale_textlint_use_global
+ Save g:ale_textlint_options
+
+ let g:ale_textlint_executable = 'textlint'
+ let g:ale_textlint_use_global = 0
+ let g:ale_textlint_options = ''
+
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+After:
+ unlet! b:command_tail
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The executable should be configurable):
+ let b:ale_textlint_executable = 'foobar'
+
+ AssertLinter 'foobar',
+ \ ale#Escape('foobar') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The options should be configurable):
+ let b:ale_textlint_options = '--something'
+
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' --something -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from .bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_bin_path/foo.txt')
+
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'))
+ \ . ' -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from textlint/bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_textlint_bin_path/foo.txt')
+
+ if has('win32')
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape('node.exe') . ' ' . ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ else
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ endif
diff --git a/test/command_callback/test_rust_rls_callbacks.vader b/test/command_callback/test_rust_rls_callbacks.vader
index a710161d..ec8c8811 100644
--- a/test/command_callback/test_rust_rls_callbacks.vader
+++ b/test/command_callback/test_rust_rls_callbacks.vader
@@ -5,7 +5,7 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The default executable path should be correct):
- AssertLinter 'rls', ale#Escape('rls') . ' +' . ale#Escape('nightly')
+ AssertLinter 'rls', ale#Escape('rls')
Execute(The toolchain should be configurable):
let g:ale_rust_rls_toolchain = 'stable'
@@ -23,3 +23,8 @@ Execute(The project root should be detected correctly):
call ale#test#SetFilename('rust-rls-project/test.rs')
AssertLSPProject ale#path#Simplify(g:dir . '/rust-rls-project')
+
+Execute(Should accept configuration settings):
+ AssertLSPConfig {}
+ let b:ale_rust_rls_config = {'rust': {'clippy_preference': 'on'}}
+ AssertLSPConfig {'rust': {'clippy_preference': 'on'}}
diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader
index 22a9ccb5..1d5b056b 100644
--- a/test/command_callback/test_shellcheck_command_callback.vader
+++ b/test/command_callback/test_shellcheck_command_callback.vader
@@ -14,6 +14,11 @@ After:
Execute(The default shellcheck command should be correct):
AssertLinter 'shellcheck', b:prefix . ale#Escape('shellcheck') . b:suffix
+Execute(The option disabling changing directories should work):
+ let g:ale_sh_shellcheck_change_directory = 0
+
+ AssertLinter 'shellcheck', ale#Escape('shellcheck') . b:suffix
+
Execute(The shellcheck command should accept options):
let b:ale_sh_shellcheck_options = '--foobar'
@@ -33,6 +38,18 @@ Execute(The shellcheck command should include the dialect):
AssertLinter 'shellcheck',
\ b:prefix . ale#Escape('shellcheck') . ' -s bash' . b:suffix
+Execute(The shellcheck command should use ale_sh_shellcheck_dialect):
+ let b:ale_sh_shellcheck_dialect = 'ksh93'
+
+ AssertLinter 'shellcheck',
+ \ b:prefix . ale#Escape('shellcheck') . ' -s ksh93' . b:suffix
+
+Execute(The shellcheck command should allow unspecified dialect):
+ let b:ale_sh_shellcheck_dialect = ''
+
+ AssertLinter 'shellcheck',
+ \ b:prefix . ale#Escape('shellcheck') . b:suffix
+
Execute(The shellcheck command should include the dialect before options and exclusions):
let b:is_bash = 1
let b:ale_sh_shellcheck_options = '--foobar'
@@ -48,7 +65,7 @@ Execute(The -x option should be added when the version is new enough):
\ b:prefix . ale#Escape('shellcheck') . b:suffix,
\]
- WithChainResults [
+ GivenCommandOutput [
\ 'ShellCheck - shell script analysis tool',
\ 'version: 0.4.4',
\ 'license: GNU General Public License, version 3',
@@ -60,14 +77,13 @@ Execute(The -x option should be added when the version is new enough):
\]
" We should cache the version check
- WithChainResults []
+ GivenCommandOutput []
AssertLinter 'shellcheck', [
- \ '',
\ b:prefix . ale#Escape('shellcheck') . ' -x' . b:suffix,
\]
Execute(The -x option should not be added when the version is too old):
- WithChainResults [
+ GivenCommandOutput [
\ 'ShellCheck - shell script analysis tool',
\ 'version: 0.3.9',
\ 'license: GNU General Public License, version 3',
@@ -79,7 +95,7 @@ Execute(The -x option should not be added when the version is too old):
\]
Execute(The version check shouldn't be run again for old versions):
- WithChainResults [
+ GivenCommandOutput [
\ 'ShellCheck - shell script analysis tool',
\ 'version: 0.3.9',
\ 'license: GNU General Public License, version 3',
@@ -90,6 +106,5 @@ Execute(The version check shouldn't be run again for old versions):
\ b:prefix . ale#Escape('shellcheck') . b:suffix,
\]
AssertLinter 'shellcheck', [
- \ '',
\ b:prefix . ale#Escape('shellcheck') . b:suffix,
\]
diff --git a/test/command_callback/test_standardrb_command_callback.vader b/test/command_callback/test_standardrb_command_callback.vader
new file mode 100644
index 00000000..7bc1c976
--- /dev/null
+++ b/test/command_callback/test_standardrb_command_callback.vader
@@ -0,0 +1,29 @@
+Before:
+ call ale#assert#SetUpLinterTest('ruby', 'standardrb')
+ call ale#test#SetFilename('dummy.rb')
+
+ let g:ale_ruby_standardrb_executable = 'standardrb'
+ let g:ale_ruby_standardrb_options = ''
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(Executable should default to standardrb):
+ AssertLinter 'standardrb', ale#Escape('standardrb')
+ \ . ' --format json --force-exclusion --stdin '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+
+Execute(Should be able to set a custom executable):
+ let g:ale_ruby_standardrb_executable = 'bin/standardrb'
+
+ AssertLinter 'bin/standardrb' , ale#Escape('bin/standardrb')
+ \ . ' --format json --force-exclusion --stdin '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+
+Execute(Setting bundle appends 'exec standardrb'):
+ let g:ale_ruby_standardrb_executable = 'path to/bundle'
+
+ AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
+ \ . ' exec standardrb'
+ \ . ' --format json --force-exclusion --stdin '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
diff --git a/test/command_callback/test_sugarss_stylelint_command_callback.vader b/test/command_callback/test_sugarss_stylelint_command_callback.vader
new file mode 100644
index 00000000..448436fb
--- /dev/null
+++ b/test/command_callback/test_sugarss_stylelint_command_callback.vader
@@ -0,0 +1,31 @@
+Before:
+ call ale#assert#SetUpLinterTest('sugarss', 'stylelint')
+ unlet! b:executable
+
+After:
+ unlet! b:executable
+ call ale#assert#TearDownLinterTest()
+
+Execute(node_modules directories should be discovered):
+ call ale#test#SetFilename('stylelint_paths/nested/testfile.sss')
+
+ let b:executable = ale#path#Simplify(
+ \ g:dir
+ \ . '/stylelint_paths/node_modules/.bin/stylelint'
+ \)
+
+ AssertLinter b:executable, ale#Escape(b:executable) . ' --syntax=sugarss --stdin-filename %s'
+
+Execute(The global override should work):
+ let b:ale_sugarss_stylelint_executable = 'foobar'
+ let b:ale_sugarss_stylelint_use_global = 1
+
+ call ale#test#SetFilename('stylelint_paths/nested/testfile.sss')
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' --syntax=sugarss --stdin-filename %s'
+
+Execute(Extra options should be configurable):
+ let b:ale_sugarss_stylelint_options = '--configFile ''/absolute/path/to/file'''
+
+ AssertLinter 'stylelint',
+ \ ale#Escape('stylelint') . ' --configFile ''/absolute/path/to/file'' --syntax=sugarss --stdin-filename %s'
diff --git a/test/command_callback/test_swift_sourcekitlsp_command_callbacks.vader b/test/command_callback/test_swift_sourcekitlsp_command_callbacks.vader
new file mode 100644
index 00000000..4611c6de
--- /dev/null
+++ b/test/command_callback/test_swift_sourcekitlsp_command_callbacks.vader
@@ -0,0 +1,21 @@
+Before:
+ call ale#assert#SetUpLinterTest('swift', 'sourcekitlsp')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default executable path should be correct):
+ call ale#test#SetFilename('../swift-test-files/swift-package-project/src/folder/dummy.swift')
+
+ AssertLinter 'sourcekit-lsp', ale#Escape('sourcekit-lsp')
+
+Execute(Should let users configure a global executable and override local paths):
+ call ale#test#SetFilename('../swift-test-files/swift-package-project/src/folder/dummy.swift')
+
+ let g:ale_sourcekit_lsp_executable = '/path/to/custom/sourcekitlsp'
+
+ AssertLinter '/path/to/custom/sourcekitlsp',
+ \ ale#Escape('/path/to/custom/sourcekitlsp')
+
+Execute(The language should be correct):
+ AssertLSPLanguage 'swift'
diff --git a/test/command_callback/test_tex_lacheck_command_callback.vader b/test/command_callback/test_tex_lacheck_command_callback.vader
new file mode 100644
index 00000000..b404cc78
--- /dev/null
+++ b/test/command_callback/test_tex_lacheck_command_callback.vader
@@ -0,0 +1,13 @@
+Before:
+ call ale#assert#SetUpLinterTest('tex', 'lacheck')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(Executable should default to lacheck):
+ AssertLinter 'lacheck', ale#Escape('lacheck') . ' %t'
+
+Execute(Should be able to set a custom executable):
+ let g:ale_tex_lacheck_executable = 'bin/foo'
+
+ AssertLinter 'bin/foo' , ale#Escape('bin/foo') . ' %t'
diff --git a/test/command_callback/test_tex_textlint_command_callbacks.vader b/test/command_callback/test_tex_textlint_command_callbacks.vader
new file mode 100644
index 00000000..d1e2ab91
--- /dev/null
+++ b/test/command_callback/test_tex_textlint_command_callbacks.vader
@@ -0,0 +1,65 @@
+" Author: januswel, w0rp
+
+Before:
+ " This is just one language for the linter.
+ call ale#assert#SetUpLinterTest('tex', 'textlint')
+
+ " The configuration is shared between many languages.
+ Save g:ale_textlint_executable
+ Save g:ale_textlint_use_global
+ Save g:ale_textlint_options
+
+ let g:ale_textlint_executable = 'textlint'
+ let g:ale_textlint_use_global = 0
+ let g:ale_textlint_options = ''
+
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+After:
+ unlet! b:command_tail
+ unlet! b:ale_textlint_executable
+ unlet! b:ale_textlint_use_global
+ unlet! b:ale_textlint_options
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The executable should be configurable):
+ let b:ale_textlint_executable = 'foobar'
+
+ AssertLinter 'foobar',
+ \ ale#Escape('foobar') . ' -f json --stdin --stdin-filename %s'
+
+Execute(The options should be configurable):
+ let b:ale_textlint_options = '--something'
+
+ AssertLinter 'textlint',
+ \ ale#Escape('textlint') . ' --something -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from .bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_bin_path/foo.txt')
+
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_bin_path/node_modules/.bin/textlint'))
+ \ . ' -f json --stdin --stdin-filename %s'
+
+Execute(The local executable from textlint/bin should be used if available):
+ call ale#test#SetFilename('textlint_paths/with_textlint_bin_path/foo.txt')
+
+ if has('win32')
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape('node.exe') . ' ' . ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ else
+ AssertLinter
+ \ ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'),
+ \ ale#Escape(ale#path#Simplify(g:dir . '/textlint_paths/with_textlint_bin_path/node_modules/textlint/bin/textlint.js'))
+ \ . ' -f json --stdin --stdin-filename %s'
+ endif
diff --git a/test/command_callback/test_vcom_command_callbacks.vader b/test/command_callback/test_vcom_command_callbacks.vader
new file mode 100644
index 00000000..77218f74
--- /dev/null
+++ b/test/command_callback/test_vcom_command_callbacks.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('vhdl', 'vcom')
+
+After:
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The executable should be configurable):
+ AssertLinter 'vcom', ale#Escape('vcom') . ' -2008 -quiet -lint %t'
+
+ let b:ale_vhdl_vcom_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' -2008 -quiet -lint %t'
+
+Execute(The options should be configurable):
+ let b:ale_vhdl_vcom_options = '--something'
+
+ AssertLinter 'vcom', ale#Escape('vcom') . ' --something %t'
diff --git a/test/command_callback/test_vlog_command_callbacks.vader b/test/command_callback/test_vlog_command_callbacks.vader
new file mode 100644
index 00000000..a07944f7
--- /dev/null
+++ b/test/command_callback/test_vlog_command_callbacks.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('verilog', 'vlog')
+
+After:
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The executable should be configurable):
+ AssertLinter 'vlog', ale#Escape('vlog') . ' -quiet -lint %t'
+
+ let b:ale_verilog_vlog_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' -quiet -lint %t'
+
+Execute(The options should be configurable):
+ let b:ale_verilog_vlog_options = '--something'
+
+ AssertLinter 'vlog', ale#Escape('vlog') . ' --something %t'
diff --git a/test/command_callback/test_vulture_command_callback.vader b/test/command_callback/test_vulture_command_callback.vader
new file mode 100644
index 00000000..d6c866b9
--- /dev/null
+++ b/test/command_callback/test_vulture_command_callback.vader
@@ -0,0 +1,68 @@
+Before:
+ call ale#assert#SetUpLinterTest('python', 'vulture')
+ call ale#test#SetFilename('test.py')
+
+ let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
+
+After:
+ unlet! b:bin_dir
+ unlet! b:executable
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The vulture command callback should lint file directory by default):
+ AssertLinter 'vulture',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('vulture') . ' .'
+
+Execute(The vulture command callback should lint project root, when present):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/no_virtualenv/subdir/foo/bar.py')
+
+ AssertLinter 'vulture',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/no_virtualenv/subdir'))
+ \ . ale#Escape('vulture') . ' .'
+
+Execute(The option for disabling change directory works and only lints file):
+ let g:ale_python_vulture_change_directory = 0
+
+ AssertLinter 'vulture', ale#Escape('vulture') . ' %s'
+
+Execute(The vulture executable should be configurable, and escaped properly):
+ let g:ale_python_vulture_executable = 'executable with spaces'
+
+ AssertLinter 'executable with spaces',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('executable with spaces') . ' .'
+
+Execute(The vulture command callback should let you set options):
+ let g:ale_python_vulture_options = '--some-option'
+
+ AssertLinter 'vulture',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('vulture') . ' --some-option .'
+
+Execute(The vulture command callback should detect virtualenv directories and switch to the project root):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+
+ let b:executable = ale#path#Simplify(
+ \ g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/vulture'
+ \)
+
+ AssertLinter b:executable,
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
+ \ . ale#Escape(b:executable) . ' .'
+
+Execute(You should able able to use the global vulture instead):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+ let g:ale_python_vulture_use_global = 1
+
+ AssertLinter 'vulture',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir'))
+ \ . ale#Escape('vulture') . ' .'
+
+Execute(Setting executable to 'pipenv' appends 'run vulture'):
+ let g:ale_python_vulture_executable = 'path/to/pipenv'
+
+ AssertLinter 'path/to/pipenv',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('path/to/pipenv') . ' run vulture' . ' .'
diff --git a/test/command_callback/test_xo_command_callback.vader b/test/command_callback/test_xo_command_callback.vader
new file mode 100644
index 00000000..12de595d
--- /dev/null
+++ b/test/command_callback/test_xo_command_callback.vader
@@ -0,0 +1,20 @@
+Before:
+ call ale#assert#SetUpLinterTest('typescript', 'xo')
+ call ale#test#SetFilename('testfile.ts')
+ unlet! b:executable
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The XO executable should be called):
+ AssertLinter 'xo', ale#Escape('xo') . ' --reporter unix --stdin --stdin-filename %s'
+
+Execute(The XO executable should be configurable):
+ let b:ale_typescript_xo_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' --reporter unix --stdin --stdin-filename %s'
+
+Execute(The XO options should be configurable):
+ let b:ale_typescript_xo_options = '--wat'
+
+ AssertLinter 'xo', ale#Escape('xo') . ' --wat --reporter unix --stdin --stdin-filename %s'
diff --git a/test/command_callback/test_xvhdl_command_callbacks.vader b/test/command_callback/test_xvhdl_command_callbacks.vader
new file mode 100644
index 00000000..86f9a32d
--- /dev/null
+++ b/test/command_callback/test_xvhdl_command_callbacks.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('vhdl', 'xvhdl')
+
+After:
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The executable should be configurable):
+ AssertLinter 'xvhdl', ale#Escape('xvhdl') . ' --2008 %t'
+
+ let b:ale_vhdl_xvhdl_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' --2008 %t'
+
+Execute(The options should be configurable):
+ let b:ale_vhdl_xvhdl_options = '--something'
+
+ AssertLinter 'xvhdl', ale#Escape('xvhdl') . ' --something %t'
diff --git a/test/command_callback/test_xvlog_command_callbacks.vader b/test/command_callback/test_xvlog_command_callbacks.vader
new file mode 100644
index 00000000..564ac979
--- /dev/null
+++ b/test/command_callback/test_xvlog_command_callbacks.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpLinterTest('verilog', 'xvlog')
+
+After:
+ unlet! b:command_tail
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The executable should be configurable):
+ AssertLinter 'xvlog', ale#Escape('xvlog') . ' %t'
+
+ let b:ale_verilog_xvlog_executable = 'foobar'
+
+ AssertLinter 'foobar', ale#Escape('foobar') . ' %t'
+
+Execute(The options should be configurable):
+ let b:ale_verilog_xvlog_options = '--something'
+
+ AssertLinter 'xvlog', ale#Escape('xvlog') . ' --something %t'
diff --git a/test/command_callback/tex_paths/sample1.tex b/test/command_callback/tex_paths/sample1.tex
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tex_paths/sample1.tex
diff --git a/test/command_callback/tex_paths/sample2.tex b/test/command_callback/tex_paths/sample2.tex
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tex_paths/sample2.tex
diff --git a/test/command_callback/tsserver_paths/src/file1.ts b/test/command_callback/tsserver_paths/src/file1.ts
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tsserver_paths/src/file1.ts
diff --git a/test/command_callback/tsserver_paths/src/level-1/file2.ts b/test/command_callback/tsserver_paths/src/level-1/file2.ts
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tsserver_paths/src/level-1/file2.ts
diff --git a/test/command_callback/tsserver_paths/src/level-1/level-2/file3.ts b/test/command_callback/tsserver_paths/src/level-1/level-2/file3.ts
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tsserver_paths/src/level-1/level-2/file3.ts
diff --git a/test/command_callback/tsserver_paths/src/level-1/tsconfig.json b/test/command_callback/tsserver_paths/src/level-1/tsconfig.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tsserver_paths/src/level-1/tsconfig.json
diff --git a/test/command_callback/tsserver_paths/tsconfig.json b/test/command_callback/tsserver_paths/tsconfig.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/tsserver_paths/tsconfig.json
diff --git a/test/compile_database_perf/test.sh b/test/compile_database_perf/test.sh
new file mode 100755
index 00000000..15a2b442
--- /dev/null
+++ b/test/compile_database_perf/test.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Generate source files for ALE to read. They don't have to be very long, the delay is in reading compile_commands, not actually running tests
+mkdir -p gen_src
+for i in {1..400}; do echo "const char *GeneratedFunc${i}() { return \"Word ${i}\"; }" > gen_src/source${i}.cpp; done
+
+# Create the compile_commands database
+echo "[ {" > compile_commands.json
+
+for i in {1..399}; do
+ {
+ echo "\"command\": \"clang++ -c $(pwd)/gen_src/source${i}.cpp -o $(pwd)/build/obj/Debug/source${i}.o -MF $(pwd)/build/obj/Debug/source${i}.d -MMD -MP\","
+ echo "\"directory\": \"$(pwd)/build\","
+ echo "\"file\": \"$(pwd)/gen_src/source${i}.cpp\""
+ echo "}, {"
+ } >> compile_commands.json
+done
+
+{
+ echo "\"command\": \"clang++ -c $(pwd)/gen_src/source400.cpp -o $(pwd)/build/obj/Debug/source400.o -MF $(pwd)/build/obj/Debug/source400.d -MMD -MP\","
+ echo "\"directory\": \"$(pwd)/build\","
+ echo "\"file\": \"$(pwd)/gen_src/source400.cpp\""
+ echo "} ]"
+} >> compile_commands.json
+
+# Start up vim and switch back and forth between files -- at least one of the files must be near the bottom of compile_commands.json
+time vim -c "for i in range(0,20) | edit gen_src/source10.cpp | edit gen_src/source400.cpp | endfor" \
+ -c "noautocmd qa!" \
+ `find . | grep "source..\.cpp"`
diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader
index 3f0bfa70..f79773cc 100644
--- a/test/completion/test_completion_events.vader
+++ b/test/completion/test_completion_events.vader
@@ -27,7 +27,7 @@ Before:
let g:get_completions_called = 0
" We just want to check if the function is called.
- function! ale#completion#GetCompletions()
+ function! ale#completion#GetCompletions(source)
let g:get_completions_called = 1
endfunction
@@ -53,10 +53,11 @@ After:
unlet! g:fake_mode
unlet! g:get_completions_called
unlet! b:ale_old_omnifunc
- unlet! b:ale_old_completopt
+ unlet! b:ale_old_completeopt
unlet! b:ale_completion_info
unlet! b:ale_completion_response
unlet! b:ale_completion_parser
+ unlet! b:ale_completion_result
unlet! b:ale_complete_done_time
delfunction CheckCompletionCalled
@@ -86,7 +87,7 @@ Execute(ale#completion#GetCompletions should not be called when the cursor posit
call setpos('.', [bufnr(''), 1, 2, 0])
" We just want to check if the function is called.
- function! ale#completion#GetCompletions()
+ function! ale#completion#GetCompletions(source)
let g:get_completions_called = 1
endfunction
@@ -105,7 +106,7 @@ Execute(ale#completion#GetCompletions should not be called if you switch to norm
let g:fake_mode = 'n'
" We just want to check if the function is called.
- function! ale#completion#GetCompletions()
+ function! ale#completion#GetCompletions(source)
let g:get_completions_called = 1
endfunction
@@ -124,6 +125,7 @@ Execute(Completion should not be done shortly after the CompleteDone function):
Execute(ale#completion#Show() should remember the omnifunc setting and replace it):
let &l:omnifunc = 'FooBar'
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#Show('Response', 'Parser')
AssertEqual 'FooBar', b:ale_old_omnifunc
@@ -136,9 +138,10 @@ Execute(ale#completion#Show() should remember the omnifunc setting and replace i
Execute(ale#completion#Show() should remember the completeopt setting and replace it):
let &l:completeopt = 'menu'
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#Show('Response', 'Parser')
- AssertEqual 'menu', b:ale_old_completopt
+ AssertEqual 'menu', b:ale_old_completeopt
AssertEqual 'menu,menuone,noselect,noinsert', &l:completeopt
AssertEqual [], g:feedkeys_calls
@@ -148,38 +151,70 @@ Execute(ale#completion#Show() should remember the completeopt setting and replac
Execute(ale#completion#Show() should set the preview option if it's set):
let &l:completeopt = 'menu,preview'
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#Show('Response', 'Parser')
- AssertEqual 'menu,preview', b:ale_old_completopt
+ AssertEqual 'menu,preview', b:ale_old_completeopt
AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt
AssertEqual [], g:feedkeys_calls
sleep 1ms
AssertEqual [["\<Plug>(ale_show_completion_menu)"]], g:feedkeys_calls
+Execute(ale#completion#Show() should not replace the completeopt setting for manual completion):
+ let b:ale_completion_info = {'source': 'ale-manual'}
+
+ let &l:completeopt = 'menu,preview'
+
+ call ale#completion#Show('Response', 'Parser')
+
+ Assert !exists('b:ale_old_completeopt')
+
+ AssertEqual [], g:feedkeys_calls
+ sleep 1ms
+ AssertEqual [["\<Plug>(ale_show_completion_menu)"]], g:feedkeys_calls
+
Execute(ale#completion#OmniFunc() should also remember the completeopt setting and replace it):
let &l:completeopt = 'menu'
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#OmniFunc(0, '')
- AssertEqual 'menu', b:ale_old_completopt
+ AssertEqual 'menu', b:ale_old_completeopt
AssertEqual 'menu,menuone,noselect,noinsert', &l:completeopt
Execute(ale#completion#OmniFunc() should set the preview option if it's set):
let &l:completeopt = 'menu,preview'
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#OmniFunc(0, '')
- AssertEqual 'menu,preview', b:ale_old_completopt
+ AssertEqual 'menu,preview', b:ale_old_completeopt
AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt
-Execute(ale#completion#Show() should make the correct feedkeys() call):
+Execute(ale#completion#Show() should make the correct feedkeys() call for automatic completion):
+ let b:ale_completion_info = {'source': 'ale-automatic'}
+ call ale#completion#Show('Response', 'Parser')
+
+ AssertEqual [], g:feedkeys_calls
+ sleep 1ms
+ AssertEqual [["\<Plug>(ale_show_completion_menu)"]], g:feedkeys_calls
+
+Execute(ale#completion#Show() should make the correct feedkeys() call for manual completion):
+ let b:ale_completion_info = {'source': 'ale-automatic'}
call ale#completion#Show('Response', 'Parser')
AssertEqual [], g:feedkeys_calls
sleep 1ms
AssertEqual [["\<Plug>(ale_show_completion_menu)"]], g:feedkeys_calls
+Execute(ale#completion#Show() should not call feedkeys() for other sources):
+ let b:ale_completion_info = {'source': 'deoplete'}
+ call ale#completion#Show('Response', 'Parser')
+
+ sleep 1ms
+ AssertEqual [], g:feedkeys_calls
+
Execute(ale#completion#Show() shouldn't do anything if you switch back to normal mode):
let &l:completeopt = 'menu,preview'
let g:fake_mode = 'n'
@@ -188,7 +223,7 @@ Execute(ale#completion#Show() shouldn't do anything if you switch back to normal
AssertEqual 'menu,preview', &l:completeopt
Assert !exists('b:ale_old_omnifunc')
- Assert !exists('b:ale_old_completopt')
+ Assert !exists('b:ale_old_completeopt')
Assert !exists('b:ale_completion_response')
Assert !exists('b:ale_completion_parser')
AssertEqual [], g:feedkeys_calls
@@ -209,12 +244,12 @@ Execute(ale#completion#Done() should restore old omnifunc values):
Assert !has_key(b:, 'ale_old_omnifunc')
Execute(ale#completion#Done() should restore the old completeopt setting):
- let b:ale_old_completopt = 'menu'
+ let b:ale_old_completeopt = 'menu'
call ale#completion#Done()
AssertEqual 'menu', &l:completeopt
- Assert !has_key(b:, 'ale_old_completopt')
+ Assert !has_key(b:, 'ale_old_completeopt')
Execute(ale#completion#Done() should leave settings alone when none were remembered):
let &l:omnifunc = 'BazBoz'
@@ -234,9 +269,46 @@ Execute(The completion request_id should be reset when queuing again):
AssertEqual 0, b:ale_completion_info.request_id
-Execute(b:ale_completion_info should be set up correctly when requesting completions):
+Execute(b:ale_completion_info should be set up correctly when requesting completions automatically):
+ let b:ale_completion_result = []
+ call setpos('.', [bufnr(''), 3, 14, 0])
+ call ale#completion#GetCompletions('ale-automatic')
+
+ AssertEqual
+ \ {
+ \ 'request_id': 0,
+ \ 'conn_id': 0,
+ \ 'column': 14,
+ \ 'line_length': 14,
+ \ 'line': 3,
+ \ 'prefix': 'ab',
+ \ 'source': 'ale-automatic',
+ \ },
+ \ b:ale_completion_info
+ Assert !exists('b:ale_completion_result')
+
+Execute(b:ale_completion_info should be set up correctly when requesting completions manually):
+ let b:ale_completion_result = []
+ call setpos('.', [bufnr(''), 3, 14, 0])
+ ALEComplete
+
+ AssertEqual
+ \ {
+ \ 'request_id': 0,
+ \ 'conn_id': 0,
+ \ 'column': 14,
+ \ 'line_length': 14,
+ \ 'line': 3,
+ \ 'prefix': 'ab',
+ \ 'source': 'ale-manual',
+ \ },
+ \ b:ale_completion_info
+ Assert !exists('b:ale_completion_result')
+
+Execute(b:ale_completion_info should be set up correctly for other sources):
+ let b:ale_completion_result = []
call setpos('.', [bufnr(''), 3, 14, 0])
- call ale#completion#GetCompletions()
+ call ale#completion#GetCompletions('deoplete')
AssertEqual
\ {
@@ -246,8 +318,10 @@ Execute(b:ale_completion_info should be set up correctly when requesting complet
\ 'line_length': 14,
\ 'line': 3,
\ 'prefix': 'ab',
+ \ 'source': 'deoplete',
\ },
\ b:ale_completion_info
+ Assert !exists('b:ale_completion_result')
Execute(The correct keybinds should be configured):
redir => g:output
@@ -264,7 +338,7 @@ Execute(The correct keybinds should be configured):
Execute(Running the normal mode <Plug> keybind should reset the settings):
let b:ale_old_omnifunc = 'FooBar'
- let b:ale_old_completopt = 'menu'
+ let b:ale_old_completeopt = 'menu'
" We can't run the keybind, but we can call the function.
call ale#completion#RestoreCompletionOptions()
@@ -272,4 +346,4 @@ Execute(Running the normal mode <Plug> keybind should reset the settings):
AssertEqual 'FooBar', &l:omnifunc
AssertEqual 'menu', &l:completeopt
Assert !has_key(b:, 'ale_old_omnifunc')
- Assert !has_key(b:, 'ale_old_completopt')
+ Assert !has_key(b:, 'ale_old_completeopt')
diff --git a/test/completion/test_completion_filtering.vader b/test/completion/test_completion_filtering.vader
index ffb313ef..c5f14266 100644
--- a/test/completion/test_completion_filtering.vader
+++ b/test/completion/test_completion_filtering.vader
@@ -16,6 +16,9 @@ Execute(Prefix filtering should work for Lists of strings):
AssertEqual
\ ['FooBar', 'FongBar', 'baz', 'foo'],
\ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.')
+ AssertEqual
+ \ ['FooBar', 'FongBar', 'baz', 'foo'],
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '')
Execute(Prefix filtering should work for completion items):
AssertEqual
@@ -102,6 +105,9 @@ Execute(Excluding words from completion results should work with lists of String
AssertEqual
\ ['Deutsch'],
\ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.')
+ AssertEqual
+ \ ['Deutsch'],
+ \ ale#completion#Filter(bufnr(''), '', ['Deutsch'], '')
Execute(Filtering shouldn't modify the original list):
let b:ale_completion_excluded_words = ['it', 'describe']
diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader
index 130f31b9..25536436 100644
--- a/test/completion/test_lsp_completion_messages.vader
+++ b/test/completion/test_lsp_completion_messages.vader
@@ -16,18 +16,20 @@ Before:
let g:capability_checked = ''
let g:conn_id = v:null
let g:Callback = ''
- let g:wait_callback_list = []
+ let g:init_callback_list = []
- function! ale#lsp_linter#StartLSP(buffer, linter) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
- return {
+ let l:details = {
+ \ 'command': 'foobar',
\ 'buffer': a:buffer,
\ 'connection_id': g:conn_id,
\ 'project_root': '/foo/bar',
- \ 'language_id': 'python',
\}
+
+ call add(g:init_callback_list, {-> a:Callback(a:linter, l:details)})
endfunction
" Pretend we're in insert mode for most tests.
@@ -35,9 +37,10 @@ Before:
return 'i'
endfunction
- function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort
+ function! ale#lsp#HasCapability(conn_id, capability) abort
let g:capability_checked = a:capability
- call add(g:wait_callback_list, a:callback)
+
+ return 1
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
@@ -60,11 +63,11 @@ After:
unlet! g:message_list
unlet! g:capability_checked
- unlet! g:wait_callback_list
+ unlet! g:init_callback_list
unlet! g:conn_id
unlet! g:Callback
unlet! b:ale_old_omnifunc
- unlet! b:ale_old_completopt
+ unlet! b:ale_old_completeopt
unlet! b:ale_completion_info
unlet! b:ale_completion_response
unlet! b:ale_completion_parser
@@ -99,14 +102,15 @@ Execute(The right message should be sent for the initial tsserver request):
" The cursor position needs to match what was saved before.
call setpos('.', [bufnr(''), 1, 3, 0])
- call ale#completion#GetCompletions()
+ call ale#completion#GetCompletions('ale-automatic')
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual 1, len(g:wait_callback_list)
+ AssertEqual 1, len(g:init_callback_list)
+ call map(g:init_callback_list, 'v:val()')
+
AssertEqual 'completion', g:capability_checked
- call map(g:wait_callback_list, 'v:val([g:conn_id, ''/foo/bar''])')
" We should send the right callback.
AssertEqual
@@ -125,6 +129,7 @@ Execute(The right message should be sent for the initial tsserver request):
\ 'request_id': 1,
\ 'line': 1,
\ 'prefix': 'fo',
+ \ 'source': 'ale-automatic',
\ },
\ get(b:, 'ale_completion_info', {})
@@ -186,14 +191,15 @@ Execute(The right message should be sent for the initial LSP request):
" The cursor position needs to match what was saved before.
call setpos('.', [bufnr(''), 1, 5, 0])
- call ale#completion#GetCompletions()
+ call ale#completion#GetCompletions('ale-automatic')
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual 1, len(g:wait_callback_list)
+ AssertEqual 1, len(g:init_callback_list)
+ call map(g:init_callback_list, 'v:val()')
+
AssertEqual 'completion', g:capability_checked
- call map(g:wait_callback_list, 'v:val([g:conn_id, ''/foo/bar''])')
" We should send the right callback.
AssertEqual
@@ -228,6 +234,7 @@ Execute(The right message should be sent for the initial LSP request):
\ 'request_id': 1,
\ 'line': 1,
\ 'prefix': 'fo',
+ \ 'source': 'ale-automatic',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\ },
\ get(b:, 'ale_completion_info', {})
@@ -253,14 +260,15 @@ Execute(Two completion requests shouldn't be sent in a row):
" The cursor position needs to match what was saved before.
call setpos('.', [bufnr(''), 1, 5, 0])
- call ale#completion#GetCompletions()
+ call ale#completion#GetCompletions('ale-automatic')
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual 2, len(g:wait_callback_list)
+ AssertEqual 2, len(g:init_callback_list)
+ call map(g:init_callback_list, 'v:val()')
+
AssertEqual 'completion', g:capability_checked
- call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])')
" We should only send one completion message for two LSP servers.
AssertEqual
diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader
index 71e53ab6..a8e00260 100644
--- a/test/completion/test_lsp_completion_parsing.vader
+++ b/test/completion/test_lsp_completion_parsing.vader
@@ -471,3 +471,34 @@ Execute(Should handle documentation in the markdown format):
\ ],
\ },
\ })
+
+Execute(Should handle completion messages with textEdit objects):
+ AssertEqual
+ \ [
+ \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1},
+ \ ],
+ \ ale#completion#ParseLSPCompletions({
+ \ 'id': 226,
+ \ 'jsonrpc': '2.0',
+ \ 'result': {
+ \ 'isIncomplete': v:false,
+ \ 'items': [
+ \ {
+ \ 'detail': 'PlayTimeCallback',
+ \ 'filterText': 'next_callback',
+ \ 'insertText': 'next_callback',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' next_callback',
+ \ 'sortText': '3ee19999next_callback',
+ \ 'textEdit': {
+ \ 'newText': 'next_callback',
+ \ 'range': {
+ \ 'end': {'character': 13, 'line': 12},
+ \ 'start': {'character': 4, 'line': 12},
+ \ },
+ \ },
+ \ },
+ \ ],
+ \ },
+ \ })
diff --git a/test/elm-test-files/newapp-notests/elm.json b/test/elm-test-files/newapp-notests/elm.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp-notests/elm.json
diff --git a/test/elm-test-files/newapp-notests/node_modules/.bin/elm b/test/elm-test-files/newapp-notests/node_modules/.bin/elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp-notests/node_modules/.bin/elm
diff --git a/test/elm-test-files/newapp-notests/tests/TestMain.elm b/test/elm-test-files/newapp-notests/tests/TestMain.elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp-notests/tests/TestMain.elm
diff --git a/test/elm-test-files/newapp/elm.json b/test/elm-test-files/newapp/elm.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp/elm.json
diff --git a/test/elm-test-files/newapp/node_modules/.bin/elm b/test/elm-test-files/newapp/node_modules/.bin/elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp/node_modules/.bin/elm
diff --git a/test/elm-test-files/newapp/node_modules/.bin/elm-test b/test/elm-test-files/newapp/node_modules/.bin/elm-test
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp/node_modules/.bin/elm-test
diff --git a/test/elm-test-files/newapp/src/Main.elm b/test/elm-test-files/newapp/src/Main.elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp/src/Main.elm
diff --git a/test/elm-test-files/newapp/tests/TestSuite.elm b/test/elm-test-files/newapp/tests/TestSuite.elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/newapp/tests/TestSuite.elm
diff --git a/test/elm-test-files/oldapp/elm-package.json b/test/elm-test-files/oldapp/elm-package.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/oldapp/elm-package.json
diff --git a/test/elm-test-files/oldapp/node_modules/.bin/elm b/test/elm-test-files/oldapp/node_modules/.bin/elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/oldapp/node_modules/.bin/elm
diff --git a/test/elm-test-files/oldapp/node_modules/.bin/elm-test b/test/elm-test-files/oldapp/node_modules/.bin/elm-test
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/oldapp/node_modules/.bin/elm-test
diff --git a/test/elm-test-files/oldapp/src/Main.elm b/test/elm-test-files/oldapp/src/Main.elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/oldapp/src/Main.elm
diff --git a/test/elm-test-files/oldapp/tests/TestSuite.elm b/test/elm-test-files/oldapp/tests/TestSuite.elm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/elm-test-files/oldapp/tests/TestSuite.elm
diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader
index 90407681..9e5ad027 100644
--- a/test/fix/test_ale_fix.vader
+++ b/test/fix/test_ale_fix.vader
@@ -55,10 +55,20 @@ Before:
return {'command': 'cat - <(echo d)'}
endfunction
+ function CatLineDeferred(buffer, lines) abort
+ return ale#command#Run(a:buffer, 'echo', {
+ \ -> ale#command#Run(a:buffer, 'echo', {-> {'command': 'cat - <(echo d)'}})
+ \})
+ endfunction
+
function ReplaceWithTempFile(buffer, lines) abort
return {'command': 'echo x > %t', 'read_temporary_file': 1}
endfunction
+ function CatWithTempFile(buffer, lines) abort
+ return {'command': 'cat %t <(echo d)'}
+ endfunction
+
function RemoveLastLine(buffer, lines) abort
return ['a', 'b']
endfunction
@@ -175,8 +185,10 @@ Before:
After:
Restore
+ unlet! g:test_filename
unlet! g:ale_run_synchronously
unlet! g:ale_set_lists_synchronously
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:ale_emulate_job_failure
unlet! b:ale_fixers
unlet! b:ale_fix_on_save
@@ -186,7 +198,9 @@ After:
delfunction DoNothing
delfunction CatLine
delfunction CatLineOneArg
+ delfunction CatLineDeferred
delfunction ReplaceWithTempFile
+ delfunction CatWithTempFile
delfunction RemoveLastLine
delfunction RemoveLastLineOneArg
delfunction TestCallback
@@ -217,8 +231,8 @@ After:
setlocal buftype=nofile
- if filereadable('fix_test_file')
- call delete('fix_test_file')
+ if exists('g:test_filename') && filereadable(g:test_filename)
+ call delete(g:test_filename)
endif
call setloclist(0, [])
@@ -235,11 +249,13 @@ Given testft (A file with three lines):
Execute(ALEFix should complain when there are no functions to call):
ALEFix
+ call ale#test#FlushJobs()
AssertEqual 'No fixers have been defined. Try :ALEFixSuggest', GetLastMessage()
Execute(ALEFix should apply simple functions):
let g:ale_fixers.testft = ['AddCarets']
ALEFix
+ call ale#test#FlushJobs()
Expect(The first function should be used):
^a
@@ -249,6 +265,7 @@ Expect(The first function should be used):
Execute(ALEFix should apply simple functions in a chain):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
ALEFix
+ call ale#test#FlushJobs()
Expect(Both functions should be used):
$^a
@@ -258,6 +275,7 @@ Expect(Both functions should be used):
Execute(ALEFix should allow 0 to be returned to skip functions):
let g:ale_fixers.testft = ['DoNothing', 'AddDollars']
ALEFix
+ call ale#test#FlushJobs()
Expect(Only the second function should be applied):
$a
@@ -268,6 +286,7 @@ Execute(The * fixers shouldn't be used if an empty list is set for fixers):
let g:ale_fixers.testft = []
let g:ale_fixers['*'] = ['AddDollars']
ALEFix
+ call ale#test#FlushJobs()
Expect(Nothing should be changed):
a
@@ -277,6 +296,7 @@ Expect(Nothing should be changed):
Execute(* fixers should be used if no filetype is matched):
let g:ale_fixers = {'*': ['AddDollars']}
ALEFix
+ call ale#test#FlushJobs()
Expect(The file should be changed):
$a
@@ -290,6 +310,7 @@ Execute(ALEFix should allow commands to be run):
else
let g:ale_fixers.testft = ['CatLine']
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(An extra line should be added):
@@ -301,6 +322,7 @@ Expect(An extra line should be added):
Execute(ALEFix should use fixers passed in commandline when provided):
let g:ale_fixers.testft = ['RemoveLastLine']
ALEFix AddCarets AddDollars
+ call ale#test#FlushJobs()
Expect(Only fixers passed via command line should be run):
$^a
@@ -315,11 +337,28 @@ Execute(ALEFix should allow temporary files to be read):
else
let g:ale_fixers.testft = ['ReplaceWithTempFile']
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(The line we wrote to the temporary file should be used here):
x
+Execute(ALEFix should not read the temporary file when the option is not set):
+ if has('win32')
+ " Just skip this test on Windows, we can't run it.
+ call setline(1, ['a', 'b', 'c', 'd'])
+ else
+ let g:ale_fixers.testft = ['CatWithTempFile']
+ ALEFix
+ call ale#test#FlushJobs()
+ endif
+
+Expect(An extra line should be added):
+ a
+ b
+ c
+ d
+
Execute(ALEFix should allow jobs and simple functions to be combined):
if has('win32')
" Just skip this test on Windows, we can't run it.
@@ -328,6 +367,7 @@ Execute(ALEFix should allow jobs and simple functions to be combined):
else
let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars']
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(The lines from the temporary file should be modified):
@@ -340,6 +380,7 @@ Execute(ALEFix should send lines modified by functions to jobs):
else
let g:ale_fixers.testft = ['AddDollars', 'CatLine']
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(The lines should first be modified by the function, then the job):
@@ -352,6 +393,7 @@ Execute(ALEFix should skip commands when jobs fail to run):
let g:ale_emulate_job_failure = 1
let g:ale_fixers.testft = ['CatLine', 'AddDollars']
ALEFix
+ call ale#test#FlushJobs()
Expect(Only the second function should be applied):
$a
@@ -361,6 +403,7 @@ Expect(Only the second function should be applied):
Execute(ALEFix should handle strings for selecting a single function):
let g:ale_fixers.testft = 'AddCarets'
ALEFix
+ call ale#test#FlushJobs()
Expect(The first function should be used):
^a
@@ -371,6 +414,7 @@ Execute(ALEFix should use functions from the registry):
call ale#fix#registry#Add('add_carets', 'AddCarets', [], 'Add some carets')
let g:ale_fixers.testft = ['add_carets']
ALEFix
+ call ale#test#FlushJobs()
Expect(The registry function should be used):
^a
@@ -380,6 +424,7 @@ Expect(The registry function should be used):
Execute(ALEFix should be able to remove the last line for files):
let g:ale_fixers.testft = ['RemoveLastLine']
ALEFix
+ call ale#test#FlushJobs()
Expect(There should be only two lines):
a
@@ -388,6 +433,7 @@ Expect(There should be only two lines):
Execute(ALEFix should accept funcrefs):
let g:ale_fixers.testft = [function('RemoveLastLine')]
ALEFix
+ call ale#test#FlushJobs()
Expect(There should be only two lines):
a
@@ -401,6 +447,7 @@ Execute(ALEFix should accept lambdas):
else
let g:ale_fixers.testft = [{buffer, lines -> lines + ['d']}]
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(There should be an extra line):
@@ -413,6 +460,7 @@ Execute(ALEFix should user buffer-local fixer settings):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
let b:ale_fixers = {'testft': ['RemoveLastLine']}
ALEFix
+ call ale#test#FlushJobs()
Expect(There should be only two lines):
a
@@ -422,6 +470,7 @@ Execute(ALEFix should allow Lists to be used for buffer-local fixer settings):
let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
let b:ale_fixers = ['RemoveLastLine']
ALEFix
+ call ale#test#FlushJobs()
Expect(There should be only two lines):
a
@@ -437,8 +486,9 @@ Execute(ALEFix should fix files on the save event):
let g:ale_lint_on_save = 1
let g:ale_enabled = 1
- noautocmd silent file fix_test_file
- call writefile(getline(1, '$'), 'fix_test_file')
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
+ call writefile(getline(1, '$'), g:test_filename)
let g:ale_fixers.testft = ['AddDollars']
@@ -447,10 +497,11 @@ Execute(ALEFix should fix files on the save event):
call SetUpLinters()
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
" We should save the file.
- AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file')
- Assert !&modified, 'The was marked as ''modified'''
+ AssertEqual ['$a', '$b', '$c'], readfile(g:test_filename)
+ Assert !&modified, 'The file was marked as ''modified'''
if !has('win32')
" We should have run the linter.
@@ -477,8 +528,9 @@ Execute(ALEFix should not fix files on :wq):
let g:ale_lint_on_save = 1
let g:ale_enabled = 1
- noautocmd silent file fix_test_file
- call writefile(getline(1, '$'), 'fix_test_file')
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
+ call writefile(getline(1, '$'), g:test_filename)
let g:ale_fixers.testft = ['AddDollars']
@@ -491,7 +543,7 @@ Execute(ALEFix should not fix files on :wq):
call ale#events#SaveEvent(bufnr(''))
" We should save the file.
- AssertEqual ['a', 'b', 'c'], readfile('fix_test_file')
+ AssertEqual ['a', 'b', 'c'], readfile(g:test_filename)
Assert &modified, 'The was not marked as ''modified'''
" We should not run the linter.
@@ -512,14 +564,16 @@ Execute(ALEFix should still lint with no linters to be applied):
let g:ale_lint_on_save = 1
let g:ale_enabled = 1
- noautocmd silent file fix_test_file
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
let g:ale_fixers.testft = []
call SetUpLinters()
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
- Assert !filereadable('fix_test_file'), 'The file should not have been saved'
+ Assert !filereadable(g:test_filename), 'The file should not have been saved'
if !has('win32')
" We have run the linter.
@@ -546,14 +600,16 @@ Execute(ALEFix should still lint when nothing was fixed on save):
let g:ale_lint_on_save = 1
let g:ale_enabled = 1
- noautocmd silent file fix_test_file
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
let g:ale_fixers.testft = ['DoNothing']
call SetUpLinters()
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
- Assert !filereadable('fix_test_file'), 'The file should not have been saved'
+ Assert !filereadable(g:test_filename), 'The file should not have been saved'
if !has('win32')
" We should have run the linter.
@@ -581,7 +637,8 @@ Given testft (A file with three lines):
c
Execute(ale#fix#InitBufferData() should set up the correct data):
- noautocmd silent file fix_test_file
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
call ale#fix#InitBufferData(bufnr(''), 'save_file')
@@ -597,6 +654,7 @@ Execute(ale#fix#InitBufferData() should set up the correct data):
Execute(ALEFix simple functions should be able to accept one argument, the buffer):
let g:ale_fixers.testft = ['RemoveLastLineOneArg']
ALEFix
+ call ale#test#FlushJobs()
Expect(There should be only two lines):
a
@@ -632,6 +690,7 @@ Execute(ALEFix functions returning jobs should be able to accept one argument):
else
let g:ale_fixers.testft = ['CatLine']
ALEFix
+ call ale#test#FlushJobs()
endif
Expect(An extra line should be added):
@@ -643,22 +702,26 @@ Expect(An extra line should be added):
Execute(ALE should print a message telling you something isn't a valid fixer when you type some nonsense):
let g:ale_fixers.testft = ['CatLine', 'invalidname']
ALEFix
+ call ale#test#FlushJobs()
AssertEqual 'There is no fixer named `invalidname`. Check :ALEFixSuggest', GetLastMessage()
Execute(ALE should complain about invalid fixers with minuses in the name):
let g:ale_fixers.testft = ['foo-bar']
ALEFix
+ call ale#test#FlushJobs()
AssertEqual 'There is no fixer named `foo-bar`. Check :ALEFixSuggest', GetLastMessage()
Execute(ALE should tolerate valid fixers with minuses in the name):
let g:ale_fixers.testft = ['prettier-standard']
ALEFix
+ call ale#test#FlushJobs()
Execute(Test fixing with chained callbacks):
let g:ale_fixers.testft = ['FirstChainCallback']
ALEFix
+ call ale#test#FlushJobs()
" The buffer shouldn't be piped in for earlier commands in the chain.
AssertEqual
@@ -677,6 +740,7 @@ Expect(The echoed line should be added):
Execute(Test fixing with chained callback where the first command is skipped):
let g:ale_fixers.testft = ['FirstChainCallbackSkipped']
ALEFix
+ call ale#test#FlushJobs()
Expect(The default line should be added):
a
@@ -687,6 +751,7 @@ Expect(The default line should be added):
Execute(Test fixing with chained callback where the second command is skipped):
let g:ale_fixers.testft = ['FirstChainCallbackSecondSkipped']
ALEFix
+ call ale#test#FlushJobs()
Expect(The default line should be added):
a
@@ -697,6 +762,7 @@ Expect(The default line should be added):
Execute(Test fixing with chained callback where the final callback is skipped):
let g:ale_fixers.testft = ['ChainWhereLastIsSkipped']
ALEFix
+ call ale#test#FlushJobs()
Expect(The lines should be the same):
a
@@ -706,6 +772,7 @@ Expect(The lines should be the same):
Execute(Empty output should be ignored):
let g:ale_fixers.testft = ['IgnoredEmptyOutput']
ALEFix
+ call ale#test#FlushJobs()
Expect(The lines should be the same):
a
@@ -715,6 +782,7 @@ Expect(The lines should be the same):
Execute(A temporary file shouldn't be piped into the command when disabled):
let g:ale_fixers.testft = ['EchoLineNoPipe']
ALEFix
+ call ale#test#FlushJobs()
AssertEqual
\ string(ale#job#PrepareCommand(bufnr(''), 'echo new line')),
@@ -731,6 +799,7 @@ Expect(The new line should be used):
Execute(Post-processing should work):
let g:ale_fixers.testft = ['FixWithJSONPostProcessing']
ALEFix
+ call ale#test#FlushJobs()
Expect(The lines in the JSON should be used):
x
@@ -740,5 +809,23 @@ Expect(The lines in the JSON should be used):
Execute(ALEFix should apply autocmds):
let g:ale_fixers.testft = ['AddCarets']
ALEFix
+ call ale#test#FlushJobs()
+
AssertEqual g:pre_success, 1
AssertEqual g:post_success, 1
+
+Execute(ALEFix should support ale#command#Run):
+ if has('win32')
+ " Just skip this test on Windows, we can't run it.
+ call setline(1, ['a', 'b', 'c', 'd'])
+ else
+ let g:ale_fixers.testft = ['CatLineDeferred']
+ ALEFix
+ call ale#test#FlushJobs()
+ endif
+
+Expect(The extra line should be added):
+ a
+ b
+ c
+ d
diff --git a/test/fix/test_ale_fix_ignore.vader b/test/fix/test_ale_fix_ignore.vader
new file mode 100644
index 00000000..5eb9b9ab
--- /dev/null
+++ b/test/fix/test_ale_fix_ignore.vader
@@ -0,0 +1,110 @@
+Before:
+ Save g:ale_fixers
+ Save g:ale_fix_on_save
+ Save g:ale_fix_on_save_ignore
+
+ let g:ale_fix_on_save = 1
+ let g:ale_fixers = {'abc': ['a', 'b'], 'xyz': ['c', 'd']}
+ unlet! b:ale_fixers
+ unlet! b:ale_fix_on_save_ignore
+
+ function FixerA(buffer, lines) abort
+ return a:lines + ['a']
+ endfunction
+
+ function FixerB(buffer, lines) abort
+ return a:lines + ['b']
+ endfunction
+
+ function FixerC(buffer, lines) abort
+ return a:lines + ['c']
+ endfunction
+
+ function FixerD(buffer, lines) abort
+ return a:lines + ['d']
+ endfunction
+
+ set filetype=abc.xyz
+ let g:test_filename = tempname()
+ execute 'noautocmd silent file ' . fnameescape(g:test_filename)
+
+ call ale#fix#registry#Add('a', 'FixerA', ['abc'], '')
+ call ale#fix#registry#Add('b', 'FixerB', ['abc'], '')
+ call ale#fix#registry#Add('c', 'FixerC', ['xyz'], '')
+ call ale#fix#registry#Add('d', 'FixerD', ['xyz'], '')
+
+After:
+ Restore
+
+ if exists('g:test_filename') && filereadable(g:test_filename)
+ call delete(g:test_filename)
+ endif
+
+ unlet! b:ale_fixers
+ unlet! b:ale_fix_on_save_ignore
+ unlet! g:test_filename
+
+ delfunction FixerA
+ delfunction FixerB
+ delfunction FixerC
+ delfunction FixerD
+
+ call ale#fix#registry#ResetToDefaults()
+
+Given abc.xyz (An empty file):
+Execute(Ignoring with a filetype in a global Dictionary should work):
+ let g:ale_fix_on_save_ignore = {'abc': ['b'], 'xyz': ['c']}
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'd'], getline(1, '$')
+
+Execute(Ignoring with a filetype in a global List should work):
+ let g:ale_fix_on_save_ignore = ['b', 'c']
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'd'], getline(1, '$')
+
+Execute(Ignoring with a filetype in a local Dictionary should work):
+ let g:ale_fix_on_save_ignore = {'abc': ['b'], 'xyz': ['c']}
+ " The local Dictionary should entirely replace the global one.
+ let b:ale_fix_on_save_ignore = {'abc': ['b']}
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'c', 'd'], getline(1, '$')
+
+Execute(Ignoring with a filetype in a local List should work):
+ let g:ale_fix_on_save_ignore = {'abc': ['b'], 'xyz': ['c']}
+ " The local List should entirely replace the global Dictionary.
+ let b:ale_fix_on_save_ignore = ['b']
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'c', 'd'], getline(1, '$')
+
+Execute(Ignoring functions by reference with a Dictionary should work):
+ let g:ale_fixers = {
+ \ 'abc': [function('FixerA'), function('FixerB')],
+ \ 'xyz': [function('FixerC'), function('FixerD')],
+ \}
+ let b:ale_fix_on_save_ignore = {
+ \ 'abc': [function('FixerB')],
+ \ 'xyz': [function('FixerC')],
+ \}
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'd'], getline(1, '$')
+
+Execute(Ignoring functions by reference with a List should work):
+ let g:ale_fixers = {
+ \ 'abc': [function('FixerA'), function('FixerB')],
+ \ 'xyz': [function('FixerC'), function('FixerD')],
+ \}
+ let b:ale_fix_on_save_ignore = [function('FixerB'), function('FixerC')]
+
+ call ale#events#SaveEvent(bufnr(''))
+
+ AssertEqual ['', 'a', 'd'], getline(1, '$')
diff --git a/test/fixers/test_black_fixer_callback.vader b/test/fixers/test_black_fixer_callback.vader
index 365b0fa6..25ad05db 100644
--- a/test/fixers/test_black_fixer_callback.vader
+++ b/test/fixers/test_black_fixer_callback.vader
@@ -5,6 +5,7 @@ Before:
" Use an invalid global executable, so we don't match it.
let g:ale_python_black_executable = 'xxxinvalid'
let g:ale_python_black_options = ''
+ let g:ale_python_black_auto_pipenv = 0
call ale#test#SetDirectory('/testplugin/test/fixers')
silent cd ..
@@ -21,19 +22,26 @@ After:
call ale#test#RestoreDirectory()
Execute(The black callback should return the correct default values):
- AssertEqual
- \ 0,
- \ ale#fixers#black#Fix(bufnr(''))
-
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
AssertEqual
- \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' -'},
+ \ {'command': ale#path#BufferCdString(bufnr('')) . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' -'},
\ ale#fixers#black#Fix(bufnr(''))
Execute(The black callback should include options):
let g:ale_python_black_options = '--some-option'
+ let g:ale_python_black_change_directory = 0
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
AssertEqual
\ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' --some-option -' },
\ ale#fixers#black#Fix(bufnr(''))
+
+Execute(Pipenv is detected when python_black_auto_pipenv is set):
+ let g:ale_python_black_auto_pipenv = 1
+ let g:ale_python_black_change_directory = 0
+
+ call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+
+ AssertEqual
+ \ {'command': ale#Escape('pipenv') . ' run black -'},
+ \ ale#fixers#black#Fix(bufnr(''))
diff --git a/test/fixers/test_cmakeformat_fixer_callback.vader b/test/fixers/test_cmakeformat_fixer_callback.vader
new file mode 100644
index 00000000..9263d6fa
--- /dev/null
+++ b/test/fixers/test_cmakeformat_fixer_callback.vader
@@ -0,0 +1,40 @@
+Before:
+ Save g:ale_cmake_cmakeformat_executable
+ Save g:ale_cmake_cmakeformat_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_cmake_cmakeformat_executable = 'xxxinvalid'
+ let g:ale_cmake_cmakeformat_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The cmakeformat callback should return the correct default values):
+ call ale#test#SetFilename('../cmake_files/CMakeList.txt')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' -i '
+ \ . ' %t',
+ \ },
+ \ ale#fixers#cmakeformat#Fix(bufnr(''))
+
+Execute(The cmakeformat callback should include custom cmakeformat options):
+ let g:ale_cmake_cmakeformat_options = "-r '(a) -> a'"
+ call ale#test#SetFilename('../cmake_files/CMakeList.txt')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' -i '
+ \ . ' ' . g:ale_cmake_cmakeformat_options
+ \ . ' %t',
+ \ },
+ \ ale#fixers#cmakeformat#Fix(bufnr(''))
diff --git a/test/fixers/test_eslint_fixer_callback.vader b/test/fixers/test_eslint_fixer_callback.vader
index 774595e3..7ea9c2cf 100644
--- a/test/fixers/test_eslint_fixer_callback.vader
+++ b/test/fixers/test_eslint_fixer_callback.vader
@@ -1,143 +1,129 @@
Before:
- call ale#test#SetDirectory('/testplugin/test/fixers')
+ call ale#assert#SetUpFixerTest('javascript', 'eslint')
+ runtime autoload/ale/handlers/eslint.vim
After:
- call ale#test#RestoreDirectory()
- call ale#semver#ResetVersionCache()
+ call ale#assert#TearDownFixerTest()
Execute(The executable path should be correct):
call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js')
" eslint_d output with an older eslint version is used here.
- AssertEqual
+ GivenCommandOutput ['v4.4.1 (eslint_d v5.1.0)']
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
\ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js'))
\ . ' --fix %t',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['v4.4.1 (eslint_d v5.1.0)'])
+ \ }
Execute(The lower priority configuration file in a nested directory should be preferred):
call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-config/testfile.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
\ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/subdir-with-config/.eslintrc'))
\ . ' --fix %t',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(package.json should be used as a last resort):
call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-package-json/testfile.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
\ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/.eslintrc.js'))
\ . ' --fix %t',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
call ale#test#SetFilename('../eslint-test-files/package.json')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
\ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/node_modules/.bin/eslint'))
\ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/package.json'))
\ . ' --fix %t',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(The version check should be correct):
- call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js')
+ call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-config/testfile.js')
- AssertEqual
+ " We should run the command to get the version the first time.
+ GivenCommandOutput ['4.9.0']
+ AssertFixer [
+ \ (has('win32') ? 'node.exe ' : '')
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
+ \ . ' --version',
\ {
- \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion',
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
- \ . ' --version'
+ \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
+ \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
\ },
- \ ale#fixers#eslint#Fix(bufnr(''))
+ \]
+
+ AssertFixer [
+ \ {
+ \ 'command': (has('win32') ? 'node.exe ' : '')
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
+ \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
+ \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
+ \ },
+ \]
Execute(--fix-dry-run should be used for 4.9.0 and up):
call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.js')
- AssertEqual
+ GivenCommandOutput ['4.9.0']
+ AssertFixer
\ {
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
\ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
\ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0'])
+ \ }
Execute(--fix-to-stdout should be used for eslint_d):
call ale#test#SetFilename('../eslint-test-files/app-with-eslint-d/testfile.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
\ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d'))
\ . ' -c ' . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/package.json'))
\ . ' --fix %t',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), [''])
+ \ }
" The option should be used when eslint_d is new enough.
" We look at the ESLint version instead of the eslint_d version.
- AssertEqual
+ GivenCommandOutput ['v3.19.0 (eslint_d v4.2.0)']
+ AssertFixer
\ {
\ 'command':
\ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d'))
\ . ' --stdin-filename %s --stdin --fix-to-stdout',
\ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['v3.19.0 (eslint_d v4.2.0)'])
+ \ }
" The option should be used for new versions too.
- AssertEqual
+ GivenCommandOutput ['4.9.0']
+ AssertFixer
\ {
\ 'command':
\ ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/app-with-eslint-d/node_modules/.bin/eslint_d'))
\ . ' --stdin-filename %s --stdin --fix-to-stdout',
\ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0'])
-
-Execute(The version number should be cached):
- call ale#test#SetFilename('../eslint-test-files/react-app/subdir-with-config/testfile.js')
-
- " Call the second callback with the version output.
- call ale#fixers#eslint#ApplyFixForVersion(bufnr(''), ['4.9.0'])
-
- " The version command should be skipped.
- AssertEqual
- \ {
- \ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion',
- \ 'command': '',
- \ },
- \ ale#fixers#eslint#Fix(bufnr(''))
-
- " Call it again without the version output. We should use the newer command.
- AssertEqual
- \ {
- \ 'command': (has('win32') ? 'node.exe ' : '')
- \ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
- \ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
- \ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
- \ },
- \ ale#fixers#eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(The --fix-dry-run post-processor should handle JSON output correctly):
AssertEqual
diff --git a/test/fixers/test_fecs_fixer_callback.vader b/test/fixers/test_fecs_fixer_callback.vader
new file mode 100644
index 00000000..809b4d46
--- /dev/null
+++ b/test/fixers/test_fecs_fixer_callback.vader
@@ -0,0 +1,26 @@
+Before:
+ call ale#assert#SetUpFixerTest('javascript', 'fecs')
+ runtime autoload/ale/handlers/fecs.vim
+
+After:
+ call ale#assert#TearDownFixerTest()
+
+Execute(The fecs fixer should respect to g:ale_javascript_fecs_executable):
+ let g:ale_javascript_fecs_executable = 'fecs_paths/fecs'
+ let g:ale_javascript_fecs_use_global = 1
+ silent cd ../command_callback
+ let g:dir = getcwd()
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape(g:ale_javascript_fecs_executable) . ' format --replace=true %t',
+ \ 'read_temporary_file': 1,
+ \ },
+ \ ale#fixers#fecs#Fix(bufnr(''))
+
+Execute(The fecs fixer should return 0 when executable not found):
+ let g:ale_javascript_fecs_executable = 'fecs-invalid'
+ let g:ale_javascript_fecs_use_global = 1
+ AssertEqual
+ \ 0,
+ \ ale#fixers#fecs#Fix(bufnr(''))
diff --git a/test/fixers/test_floskell_fixer_callback.vader b/test/fixers/test_floskell_fixer_callback.vader
new file mode 100644
index 00000000..66fe9200
--- /dev/null
+++ b/test/fixers/test_floskell_fixer_callback.vader
@@ -0,0 +1,23 @@
+Before:
+ Save g:ale_haskell_floskell_executable
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_haskell_floskell_executable = 'xxxinvalid'
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The floskell callback should return the correct default values):
+ call ale#test#SetFilename('../haskell_files/testfile.hs')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' %t',
+ \ },
+ \ ale#fixers#floskell#Fix(bufnr(''))
diff --git a/test/fixers/test_ktlint_fixer_callback.vader b/test/fixers/test_ktlint_fixer_callback.vader
new file mode 100644
index 00000000..47b37788
--- /dev/null
+++ b/test/fixers/test_ktlint_fixer_callback.vader
@@ -0,0 +1,44 @@
+Before:
+ Save g:ale_kotlin_ktlint_executable
+ Save g:ale_kotlin_ktlint_options
+ Save g:ale_kotlin_ktlint_rulesets
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_kotlin_ktlint_executable = 'xxxinvalid'
+ let g:ale_kotlin_ktlint_options = ''
+ let g:ale_kotlin_ktlint_rulesets = []
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The ktlint callback should return the correct default values):
+ call ale#test#SetFilename('../kotlin_files/testfile.kt')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' %t'
+ \ . ' --format',
+ \ 'read_temporary_file': 1,
+ \ },
+ \ ale#fixers#ktlint#Fix(bufnr(''))
+
+Execute(The ktlint callback should include custom ktlint options):
+ let g:ale_kotlin_ktlint_options = "--android"
+ let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom/ruleset.jar']
+ call ale#test#SetFilename('../kotlin_files/testfile.kt')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' ' . g:ale_kotlin_ktlint_options
+ \ . ' --ruleset /path/to/custom/ruleset.jar'
+ \ . ' %t'
+ \ . ' --format',
+ \ 'read_temporary_file': 1,
+ \ },
+ \ ale#fixers#ktlint#Fix(bufnr(''))
diff --git a/test/fixers/test_latexindent_fixer_callback.vader b/test/fixers/test_latexindent_fixer_callback.vader
new file mode 100644
index 00000000..d0da94a1
--- /dev/null
+++ b/test/fixers/test_latexindent_fixer_callback.vader
@@ -0,0 +1,40 @@
+Before:
+ Save g:ale_tex_latexindent_executable
+ Save g:ale_tex_latexindent_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_tex_latexindent_executable = 'xxxinvalid'
+ let g:ale_tex_latexindent_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The latexindent callback should return the correct default values):
+ call ale#test#SetFilename('../tex_files/testfile.tex')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' -l -w'
+ \ . ' %t',
+ \ },
+ \ ale#fixers#latexindent#Fix(bufnr(''))
+
+Execute(The latexindent callback should include custom gofmt options):
+ let g:ale_tex_latexindent_options = "-l '~/.indentconfig.yaml'"
+ call ale#test#SetFilename('../tex_files/testfile.tex')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' -l -w'
+ \ . ' ' . g:ale_tex_latexindent_options
+ \ . ' %t',
+ \ },
+ \ ale#fixers#latexindent#Fix(bufnr(''))
diff --git a/test/fixers/test_ocp_indent_fixer_callback.vader b/test/fixers/test_ocp_indent_fixer_callback.vader
new file mode 100644
index 00000000..1f61f383
--- /dev/null
+++ b/test/fixers/test_ocp_indent_fixer_callback.vader
@@ -0,0 +1,34 @@
+Before:
+ Save g:ale_ocaml_ocp_indent_executable
+ Save g:ale_ocaml_ocpindent_options
+
+ " Use an invalid global executable
+ let g:ale_ocaml_ocp_indent_executable = 'xxxinvalid'
+ let g:ale_ocaml_ocp_indent_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The ocp_indent callback should return the correct default values):
+ call ale#test#SetFilename('../ocaml-test-files/ocp_inden_testfile.re')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid')
+ \ },
+ \ ale#fixers#ocp_indent#Fix(bufnr(''))
+
+Execute(The ocp_indent callback should include custom ocp_indent options):
+ let g:ale_ocaml_ocp_indent_config = "base=4, type=4"
+ call ale#test#SetFilename('../ocaml-test-files/ocp_inden_testfile.re')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' --config=' . ale#Escape(g:ale_ocaml_ocp_indent_config)
+ \ },
+ \ ale#fixers#ocp_indent#Fix(bufnr(''))
diff --git a/test/fixers/test_prettier_eslint_fixer.callback.vader b/test/fixers/test_prettier_eslint_fixer.callback.vader
index 5c899d86..90e11672 100644
--- a/test/fixers/test_prettier_eslint_fixer.callback.vader
+++ b/test/fixers/test_prettier_eslint_fixer.callback.vader
@@ -1,54 +1,36 @@
Before:
- call ale#test#SetDirectory('/testplugin/test/fixers')
-
- Save g:ale_javascript_prettier_eslint_executable
- Save g:ale_javascript_prettier_eslint_use_global
- Save g:ale_javascript_prettier_eslint_options
-
- unlet! g:ale_javascript_prettier_eslint_executable
- unlet! g:ale_javascript_prettier_eslint_use_global
- unlet! g:ale_javascript_prettier_eslint_options
-
- call ale#fixers#prettier_eslint#SetOptionDefaults()
+ call ale#assert#SetUpFixerTest('javascript', 'prettier_eslint')
After:
- Restore
-
- unlet! b:ale_javascript_prettier_eslint_executable
- unlet! b:ale_javascript_prettier_eslint_use_global
- unlet! b:ale_javascript_prettier_eslint_options
-
- call ale#test#RestoreDirectory()
- call ale#semver#ResetVersionCache()
+ call ale#assert#TearDownFixerTest()
Execute(The default command should be correct):
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
\ ale#Escape('prettier-eslint')
\ . ' %t'
\ . ' --write'
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(Additional options should be used when set):
let b:ale_javascript_prettier_eslint_options = '--foobar'
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
\ ale#Escape('prettier-eslint')
\ . ' %t'
\ . ' --foobar --write'
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(--eslint-config-path should be set for 4.2.0 and up):
call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js')
- AssertEqual
+ GivenCommandOutput ['4.2.0']
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
@@ -56,58 +38,57 @@ Execute(--eslint-config-path should be set for 4.2.0 and up):
\ . ' %t'
\ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js'))
\ . ' --write'
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.2.0'])
+ \ }
Execute(--eslint-config-path shouldn't be used for older versions):
call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command':
\ ale#Escape('prettier-eslint')
\ . ' %t'
\ . ' --write'
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(The version check should be correct):
- AssertEqual
+ AssertFixer [
+ \ ale#Escape('prettier-eslint') . ' --version',
\ {
- \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion',
- \ 'command': ale#Escape('prettier-eslint') . ' --version',
- \ },
- \ ale#fixers#prettier_eslint#Fix(bufnr(''))
+ \ 'read_temporary_file': 1,
+ \ 'command':
+ \ ale#Escape('prettier-eslint')
+ \ . ' %t'
+ \ . ' --write'
+ \ }
+ \]
Execute(The new --stdin-filepath option should be used when the version is new enough):
call ale#test#SetFilename('eslint-test-files/react-app/foo/bar.js')
- AssertEqual
+ GivenCommandOutput ['4.4.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape('prettier-eslint')
\ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js'))
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.4.0'])
+ \ }
Execute(The version number should be cached):
- call ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), ['4.4.0'])
-
- " The version command should be skipped.
- AssertEqual
+ GivenCommandOutput ['4.4.0']
+ AssertFixer
\ {
- \ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion',
- \ 'command': '',
- \ },
- \ ale#fixers#prettier_eslint#Fix(bufnr(''))
+ \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ . ale#Escape('prettier-eslint')
+ \ . ' --stdin-filepath %s --stdin',
+ \ }
- " The newer command should be used.
- AssertEqual
+ GivenCommandOutput []
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape('prettier-eslint')
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier_eslint#ApplyFixForVersion(bufnr(''), [])
+ \ }
diff --git a/test/fixers/test_prettier_fixer_callback.vader b/test/fixers/test_prettier_fixer_callback.vader
index 7f25471b..9be161ac 100644
--- a/test/fixers/test_prettier_fixer_callback.vader
+++ b/test/fixers/test_prettier_fixer_callback.vader
@@ -1,268 +1,284 @@
Before:
- call ale#test#SetDirectory('/testplugin/test/fixers')
- Save g:ale_javascript_prettier_executable
- Save g:ale_javascript_prettier_options
+ call ale#assert#SetUpFixerTest('javascript', 'prettier')
- " 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()
- call ale#semver#ResetVersionCache()
+ call ale#assert#TearDownFixerTest()
Execute(The prettier callback should return the correct default values):
call ale#test#SetFilename('../prettier-test-files/testfile.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': ale#Escape(g:ale_javascript_prettier_executable)
\ . ' %t'
\ . ' --write',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(The --config option should not be set automatically):
let g:ale_javascript_prettier_use_local_config = 1
call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js')
- AssertEqual
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': ale#Escape(g:ale_javascript_prettier_executable)
\ . ' %t'
\ . ' --write',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(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
+ AssertFixer
\ {
\ 'read_temporary_file': 1,
\ 'command': ale#Escape(g:ale_javascript_prettier_executable)
\ . ' %t'
\ . ' --no-semi'
\ . ' --write',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), [])
+ \ }
Execute(The version check should be correct):
call ale#test#SetFilename('../prettier-test-files/testfile.js')
- AssertEqual
- \ {
- \ 'chain_with': 'ale#fixers#prettier#ApplyFixForVersion',
- \ 'command': ale#Escape(g:ale_javascript_prettier_executable)
- \ . ' --version',
- \ },
- \ ale#fixers#prettier#Fix(bufnr(''))
+ AssertFixer [
+ \ ale#Escape('prettier') . ' --version',
+ \ {'read_temporary_file': 1, 'command': ale#Escape('prettier') . ' %t --write'}
+ \]
Execute(--stdin-filepath should be used when prettier is new enough):
let g:ale_javascript_prettier_options = '--no-semi'
call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js')
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --no-semi'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(The version number should be cached):
call ale#test#SetFilename('../prettier-test-files/with_config/testfile.js')
- " Call the second callback with the version output.
- call ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
+ \ {
+ \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ . ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' --stdin-filepath %s --stdin',
+ \ }
- " Call it again without the version output. We should use the newer command.
- AssertEqual
+ GivenCommandOutput []
+ AssertFixer
+ \ {
+ \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ . ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' --stdin-filepath %s --stdin',
+ \ }
+
+Execute(Should set --parser to `babylon` by default, < 1.16.0):
+ call ale#test#SetFilename('../prettier-test-files/testfile')
+
+ set filetype=javascript
+
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' --parser babylon'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), [])
+ \ }
+
+Execute(Should set --parser to `babel` by default, >= 1.16.0):
+ call ale#test#SetFilename('../prettier-test-files/testfile')
+
+ set filetype=javascript
+
+ GivenCommandOutput ['1.16.0']
+ AssertFixer
+ \ {
+ \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ . ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' --parser babel'
+ \ . ' --stdin-filepath %s --stdin',
+ \ }
Execute(Should set --parser based on filetype, TypeScript):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=typescript
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser typescript'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, CSS):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=css
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser css'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, LESS):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=less
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser less'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, SCSS):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=scss
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser scss'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, JSON):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=json
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser json'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, JSON5):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=json5
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser json5'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, GraphQL):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=graphql
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser graphql'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, Markdown):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=markdown
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser markdown'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, Vue):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=vue
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser vue'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, YAML):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=yaml
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser yaml'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on filetype, HTML):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=html
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser html'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(Should set --parser based on first filetype of multiple filetypes):
call ale#test#SetFilename('../prettier-test-files/testfile')
set filetype=css.scss
- AssertEqual
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
\ {
\ 'command': ale#path#CdString(expand('%:p:h'))
\ . ale#Escape(g:ale_javascript_prettier_executable)
\ . ' --parser css'
\ . ' --stdin-filepath %s --stdin',
- \ },
- \ ale#fixers#prettier#ApplyFixForVersion(bufnr(''), ['1.6.0'])
+ \ }
Execute(The prettier_d post-processor should permit regular JavaScript content):
AssertEqual
diff --git a/test/fixers/test_standard_fixer_callback.vader b/test/fixers/test_standard_fixer_callback.vader
index 38f2d54a..db9f20f6 100644
--- a/test/fixers/test_standard_fixer_callback.vader
+++ b/test/fixers/test_standard_fixer_callback.vader
@@ -1,6 +1,9 @@
Before:
call ale#test#SetDirectory('/testplugin/test/fixers')
+ unlet! b:ale_javascript_standard_executable
+ unlet! b:ale_javascript_standard_options
+
After:
call ale#test#RestoreDirectory()
@@ -15,3 +18,14 @@ Execute(The executable path should be correct):
\ . ' --fix %t',
\ },
\ ale#fixers#standard#Fix(bufnr(''))
+
+Execute(Custom options should be supported):
+ let b:ale_javascript_standard_use_global = 1
+ let b:ale_javascript_standard_options = '--foo-bar'
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('standard') . ' --foo-bar --fix %t',
+ \ },
+ \ ale#fixers#standard#Fix(bufnr(''))
diff --git a/test/fixers/test_standardrb_fixer_callback.vader b/test/fixers/test_standardrb_fixer_callback.vader
new file mode 100644
index 00000000..99234b79
--- /dev/null
+++ b/test/fixers/test_standardrb_fixer_callback.vader
@@ -0,0 +1,54 @@
+Before:
+ Save g:ale_ruby_standardrb_executable
+ Save g:ale_ruby_standardrb_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_ruby_standardrb_executable = 'xxxinvalid'
+ let g:ale_ruby_standardrb_options = ''
+
+ 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 standardrb callback should return the correct default values):
+ call ale#test#SetFilename('ruby_paths/dummy.rb')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_ruby_standardrb_executable)
+ \ . ' --fix --force-exclusion %t',
+ \ },
+ \ ale#fixers#standardrb#Fix(bufnr(''))
+
+Execute(The standardrb callback should include configuration files):
+ call ale#test#SetFilename('ruby_paths/with_config/dummy.rb')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_ruby_standardrb_executable)
+ \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.standard.yml'))
+ \ . ' --fix --force-exclusion %t',
+ \ },
+ \ ale#fixers#standardrb#Fix(bufnr(''))
+
+Execute(The standardrb callback should include custom rubocop options):
+ let g:ale_ruby_standardrb_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_standardrb_executable)
+ \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.standard.yml'))
+ \ . ' --except Lint/Debugger'
+ \ . ' --fix --force-exclusion %t',
+ \ },
+ \ ale#fixers#standardrb#Fix(bufnr(''))
diff --git a/test/fixers/test_styler_fixer_callback.vader b/test/fixers/test_styler_fixer_callback.vader
new file mode 100644
index 00000000..85e45c1d
--- /dev/null
+++ b/test/fixers/test_styler_fixer_callback.vader
@@ -0,0 +1,21 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The styler callback should include custom styler options):
+ let g:ale_r_styler_options = "a_custom_option"
+
+ AssertEqual
+ \ {
+ \ 'command': 'Rscript --vanilla -e '
+ \ . '"suppressPackageStartupMessages(library(styler));'
+ \ . 'style_file(commandArgs(TRUE), style = '
+ \ . 'a_custom_option)"'
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \ },
+ \ ale#fixers#styler#Fix(bufnr(''))
diff --git a/test/fixers/test_textlint_fixer_callback.vader b/test/fixers/test_textlint_fixer_callback.vader
new file mode 100644
index 00000000..2848cfa5
--- /dev/null
+++ b/test/fixers/test_textlint_fixer_callback.vader
@@ -0,0 +1,42 @@
+Before:
+ Save g:ale_textlint_executable
+ Save g:ale_textlint_options
+ Save g:ale_textlint_use_global
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_textlint_executable = 'xxxinvalid'
+ let g:ale_textlint_options = ''
+ let g:ale_textlint_use_global = 0
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The textlint callback should return the correct default values):
+ call ale#test#SetFilename('../markdown_files/testfile.md')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' --fix'
+ \ . ' %t',
+ \ },
+ \ ale#fixers#textlint#Fix(bufnr(''))
+
+Execute(The textlint callback should include custom textlint options):
+ let g:ale_textlint_options = "--quiet"
+ call ale#test#SetFilename('../markdown_files/testfile.md')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' --fix'
+ \ . ' ' . g:ale_textlint_options
+ \ . ' %t',
+ \ },
+ \ ale#fixers#textlint#Fix(bufnr(''))
diff --git a/test/handler/test_ameba_handler.vader b/test/handler/test_ameba_handler.vader
new file mode 100644
index 00000000..a6f43170
--- /dev/null
+++ b/test/handler/test_ameba_handler.vader
@@ -0,0 +1,44 @@
+Before:
+ runtime ale_linters/crystal/ameba.vim
+
+After:
+ unlet! g:lines
+ call ale#linter#Reset()
+
+Execute(The ameba handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 24,
+ \ 'col': 28,
+ \ 'end_col': 29,
+ \ 'text': 'Trailing whitespace detected',
+ \ 'code': 'Layout/TrailingWhitespace',
+ \ 'type': 'W',
+ \ },
+ \ ],
+ \ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
+ \ '{"sources":[{"path":"my_file_with_issues.cr","issues":[{"rule_name":"Layout/TrailingWhitespace","message":"Trailing whitespace detected","location":{"line":24,"column":28},"end_location":{"line":null,"column":null}}]},{"path":"my_file_without_issues.cr","issues":[]}],"metadata":{"ameba_version":"0.8.1","crystal_version":"0.26.1"},"summary":{"target_sources_count":2,"issues_count":1}}'
+ \ ])
+
+Execute(The ameba handler should handle when files are checked and no offenses are found):
+ AssertEqual
+ \ [],
+ \ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
+ \ '{"sources":[{"path":"my_file_with_issues.cr",issues":[]},{"path":"my_file_without_issues.cr",issues":[]}],"metadata":{ameba_version":"0.8.1",crystal_version":"0.26.1"},"summary":{target_sources_count":2,issues_count":0}}'
+ \ ])
+
+Execute(The ameba handler should handle when no files are checked):
+ AssertEqual
+ \ [],
+ \ ale_linters#crystal#ameba#HandleAmebaOutput(123, [
+ \ '{"sources":[],"metadata":{ameba_version":"0.8.1",crystal_version":"0.26.1"},"summary":{target_sources_count":0,issues_count":0}}'
+ \ ])
+
+Execute(The ameba handler should handle blank output without any errors):
+ AssertEqual
+ \ [],
+ \ ale_linters#crystal#ameba#HandleAmebaOutput(123, ['{}'])
+ AssertEqual
+ \ [],
+ \ ale_linters#crystal#ameba#HandleAmebaOutput(123, [])
diff --git a/test/handler/test_bandit_handler.vader b/test/handler/test_bandit_handler.vader
new file mode 100644
index 00000000..a2793a46
--- /dev/null
+++ b/test/handler/test_bandit_handler.vader
@@ -0,0 +1,42 @@
+Before:
+ runtime ale_linters/python/bandit.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The bandit handler for Python should parse input correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'bufnr': 0,
+ \ 'lnum': 2,
+ \ 'code': 'B404',
+ \ 'type': 'I',
+ \ 'text': 'Consider possible security implications associated with subprocess module.',
+ \ },
+ \ {
+ \ 'bufnr': 0,
+ \ 'lnum': 4,
+ \ 'code': 'B305',
+ \ 'type': 'W',
+ \ 'text': 'Use of insecure cipher mode cryptography.hazmat.primitives.ciphers.modes.ECB.',
+ \ },
+ \ {
+ \ 'bufnr': 0,
+ \ 'lnum': 6,
+ \ 'code': 'B609',
+ \ 'type': 'E',
+ \ 'text': 'Possible wildcard injection in call: subprocess.Popen',
+ \ },
+ \ ],
+ \ ale_linters#python#bandit#Handle(0, [
+ \ '[main] INFO profile include tests: None',
+ \ '[main] INFO profile exclude tests: None',
+ \ '[main] INFO cli include tests: None',
+ \ '[main] INFO cli exclude tests: None',
+ \ '[main] INFO running on Python 3.7.2',
+ \ '[node_visitor] INFO Unable to find qualified name for module: <stdin>',
+ \ '2:B404:LOW:Consider possible security implications associated with subprocess module.',
+ \ '4:B305:MEDIUM:Use of insecure cipher mode cryptography.hazmat.primitives.ciphers.modes.ECB.',
+ \ '6:B609:HIGH:Possible wildcard injection in call: subprocess.Popen',
+ \ ])
diff --git a/test/handler/test_clojure_clj_kondo_handler.vader b/test/handler/test_clojure_clj_kondo_handler.vader
new file mode 100644
index 00000000..45db9049
--- /dev/null
+++ b/test/handler/test_clojure_clj_kondo_handler.vader
@@ -0,0 +1,75 @@
+Before:
+ runtime ale_linters/clojure/clj_kondo.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(the clojure clj-kondo handler should be able to handle errors):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 123,
+ \ 'col': 44,
+ \ 'type': 'E',
+ \ 'text': 'error: Unexpected )',
+ \ },
+ \ ],
+ \ ale_linters#clojure#clj_kondo#HandleCljKondoFormat(0, [
+ \ 'test.clj:123:44: error: Unexpected )',
+ \ ])
+
+Execute(the clojure clj-kondo handler should be able to handle warnings):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 654,
+ \ 'col': 321,
+ \ 'type': 'W',
+ \ 'text': 'warning: inline def',
+ \ }
+ \ ],
+ \ ale_linters#clojure#clj_kondo#HandleCljKondoFormat(0, [
+ \ 'test.clj:654:321: warning: inline def'
+ \ ])
+
+Execute(the clojure clj-kondo handler should be able to handle exceptions):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 123,
+ \ 'col': 321,
+ \ 'type': 'E',
+ \ 'text': 'Exception: something horrible happen',
+ \ }
+ \ ],
+ \ ale_linters#clojure#clj_kondo#HandleCljKondoFormat(0, [
+ \ 'test.clj:123:321: Exception: something horrible happen'
+ \ ])
+
+Execute(the clojure clj-kondo handler should be able to handle errors from stdin):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 16,
+ \ 'col': 1,
+ \ 'type': 'E',
+ \ 'text': 'error: Unexpected )',
+ \ },
+ \ ],
+ \ ale_linters#clojure#clj_kondo#HandleCljKondoFormat(0, [
+ \ '<stdin>:16:1: error: Unexpected )',
+ \ ])
+
+Execute(the clojure clj-kondo handler should be able to handle windows files):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 123,
+ \ 'col': 44,
+ \ 'type': 'E',
+ \ 'text': 'error: Unexpected )',
+ \ }
+ \ ],
+ \ ale_linters#clojure#clj_kondo#HandleCljKondoFormat(0, [
+ \ 'C:\my\operating\system\is\silly\core.clj:123:44: error: Unexpected )',
+ \ ])
diff --git a/test/handler/test_cookstyle_handler.vader b/test/handler/test_cookstyle_handler.vader
new file mode 100644
index 00000000..7d705a19
--- /dev/null
+++ b/test/handler/test_cookstyle_handler.vader
@@ -0,0 +1,22 @@
+Before:
+ runtime ale_linters/chef/cookstyle.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(Basic warnings should be handled):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 58,
+ \ 'col': 24,
+ \ 'code': 'Style/UnneededInterpolation',
+ \ 'type': 'W',
+ \ 'end_col': 40,
+ \ 'text': 'Style/UnneededInterpolation: Prefer `to_s` over string interpolation.',
+ \ }
+ \ ],
+ \ ale_linters#chef#cookstyle#Handle(bufnr(''), [
+ \ '{"metadata":{"rubocop_version":"0.62.0","ruby_engine":"ruby","ruby_version":"2.6.0","ruby_patchlevel":"0","ruby_platform":"x86_64-linux"},"files":[{"path":"recipes/default.rb","offenses":[{"severity":"convention","message":"Style/UnneededInterpolation: Prefer `to_s` over string interpolation.","cop_name":"Style/UnneededInterpolation","corrected":false,"location":{"start_line":58,"start_column":24,"last_line":58,"last_column":40,"length":17,"line":58,"column":24}}]}],"summary":{"offense_count":1,"target_file_count":1,"inspected_file_count":1}}'
+ \ ])
+
diff --git a/test/handler/test_cypher_lint_handler.vader b/test/handler/test_cypher_lint_handler.vader
new file mode 100644
index 00000000..066adae4
--- /dev/null
+++ b/test/handler/test_cypher_lint_handler.vader
@@ -0,0 +1,21 @@
+Before:
+ runtime ale_linters/cypher/cypher_lint.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The cypher-lint handler should handle errors for the current file correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'col': 75,
+ \ 'type': 'E',
+ \ 'text': "Invalid input ',': expected an identifier, shortestPath, allShortestPaths or '('",
+ \ },
+ \ ],
+ \ ale_linters#cypher#cypher_lint#Handle(bufnr(''), [
+ \ "shakespeare.cql:1:75: Invalid input ',': expected an identifier, shortestPath, allShortestPaths or '('",
+ \ "CREATE (shakespeare:Author {firstname:'William', lastname:'Shakespeare'}),,",
+ \ " ^",
+ \ ])
diff --git a/test/handler/test_eslint_handler.vader b/test/handler/test_eslint_handler.vader
index 4a57927b..1bb438a6 100644
--- a/test/handler/test_eslint_handler.vader
+++ b/test/handler/test_eslint_handler.vader
@@ -342,6 +342,17 @@ Execute(eslint should warn about ignored files by default):
\ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]',
\ ])
+ AssertEqual
+ \ [{
+ \ 'lnum': 0,
+ \ 'col': 0,
+ \ 'type': 'W',
+ \ 'text': 'File ignored by default. Use "--ignore-pattern ''!node_modules/*''" to override.',
+ \ }],
+ \ ale#handlers#eslint#Handle(bufnr(''), [
+ \ '/path/to/some/ignored.js:0:0: File ignored by default. Use "--ignore-pattern ''!node_modules/*''" to override. [Warning]',
+ \ ])
+
Execute(eslint should not warn about ignored files when explicitly disabled):
let g:ale_javascript_eslint_suppress_eslintignore = 1
@@ -351,6 +362,12 @@ Execute(eslint should not warn about ignored files when explicitly disabled):
\ '/path/to/some/ignored.js:0:0: File ignored because of a matching ignore pattern. Use "--no-ignore" to override. [Warning]',
\ ])
+ AssertEqual
+ \ [],
+ \ ale#handlers#eslint#Handle(bufnr(''), [
+ \ '/path/to/some/ignored.js:0:0: File ignored by default. Use "--ignore-pattern ''!node_modules/*''" to override. [Warning]',
+ \ ])
+
Execute(eslint should handle react errors correctly):
AssertEqual
\ [
diff --git a/test/handler/test_fecs_handler.vader b/test/handler/test_fecs_handler.vader
new file mode 100644
index 00000000..7c216b8d
--- /dev/null
+++ b/test/handler/test_fecs_handler.vader
@@ -0,0 +1,35 @@
+Before:
+ runtime autoload/ale/handlers/fecs.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(fecs should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 20,
+ \ 'col': 25,
+ \ 'text': 'Unexpected console statement.',
+ \ 'code': 'no-console',
+ \ 'type': 'W',
+ \ },
+ \ {
+ \ 'lnum': 24,
+ \ 'col': 36,
+ \ 'text': 'Missing radix parameter.',
+ \ 'code': 'radix',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 25,
+ \ 'col': 6,
+ \ 'text': 'Missing static property value.',
+ \ 'type': 'E',
+ \ },
+ \ ],
+ \ ale#handlers#fecs#Handle(347, [
+ \ 'fecs WARN → line 20, col 25: Unexpected console statement. (no-console)',
+ \ 'fecs ERROR → line 24, col 36: Missing radix parameter. (radix)',
+ \ 'fecs ERROR → line 25, col 6: Missing static property value.',
+ \ ])
diff --git a/test/handler/test_flake8_handler.vader b/test/handler/test_flake8_handler.vader
index cdf20bc0..1c9956fa 100644
--- a/test/handler/test_flake8_handler.vader
+++ b/test/handler/test_flake8_handler.vader
@@ -113,7 +113,7 @@ Execute(The flake8 handler should handle stack traces):
\ [
\ {
\ 'lnum': 1,
- \ 'text': 'An exception was thrown. See :ALEDetail',
+ \ 'text': 'ImportError: No module named parser (See :ALEDetail)',
\ 'detail': join([
\ 'Traceback (most recent call last):',
\ ' File "/usr/local/bin/flake8", line 7, in <module>',
@@ -258,3 +258,19 @@ Execute(E112 should be a syntax error):
\ ale_linters#python#flake8#Handle(bufnr(''), [
\ 'foo.py:6:1: E112 expected an indented block',
\ ])
+
+Execute(Compatibility with hacking which uses older style flake8):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 6,
+ \ 'col': 1,
+ \ 'vcol': 1,
+ \ 'code': 'H306',
+ \ 'type': 'W',
+ \ 'text': 'imports not in alphabetical order (smtplib, io)',
+ \ },
+ \ ],
+ \ ale_linters#python#flake8#Handle(bufnr(''), [
+ \ 'foo.py:6:1: H306: imports not in alphabetical order (smtplib, io)',
+ \ ])
diff --git a/test/handler/test_flow_handler.vader b/test/handler/test_flow_handler.vader
index 3a575a01..055ba026 100644
--- a/test/handler/test_flow_handler.vader
+++ b/test/handler/test_flow_handler.vader
@@ -499,7 +499,8 @@ Execute(The flow handler should handle extra errors):
\ 'col': 35,
\ 'type': 'E',
\ 'text': 'props of React element `New`: This type is incompatible with object type',
- \ 'detail': 'Property `setVector` is incompatible: number This type is incompatible with function type ',
+ \ 'detail': 'props of React element `New`: This type is incompatible with object type'
+ \ . "\nProperty `setVector` is incompatible: number This type is incompatible with function type ",
\ }
\]
diff --git a/test/handler/test_ghdl_handler.vader b/test/handler/test_ghdl_handler.vader
new file mode 100644
index 00000000..a0f5edac
--- /dev/null
+++ b/test/handler/test_ghdl_handler.vader
@@ -0,0 +1,26 @@
+Before:
+ runtime ale_linters/vhdl/ghdl.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The ghdl handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 41,
+ \ 'col' : 5,
+ \ 'type': 'E',
+ \ 'text': "error: 'begin' is expected instead of 'if'"
+ \ },
+ \ {
+ \ 'lnum': 12,
+ \ 'col' : 8,
+ \ 'type': 'E',
+ \ 'text': ' no declaration for "i0"'
+ \ },
+ \ ],
+ \ ale_linters#vhdl#ghdl#Handle(bufnr(''), [
+ \ "dff_en.vhd:41:5:error: 'begin' is expected instead of 'if'",
+ \ '/path/to/file.vhdl:12:8: no declaration for "i0"',
+ \ ])
diff --git a/test/handler/test_ktlint_handler.vader b/test/handler/test_ktlint_handler.vader
new file mode 100644
index 00000000..f0d634e6
--- /dev/null
+++ b/test/handler/test_ktlint_handler.vader
@@ -0,0 +1,21 @@
+Before:
+ Save g:ale_kotlin_ktlint_rulesets
+
+ let g:ale_kotlin_ktlint_rulesets = []
+
+After:
+ Restore
+
+Execute(The ktlint handler method GetRulesets should properly parse custom rulesets):
+ let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom/ruleset.jar', '/path/to/other/ruleset.jar']
+
+ AssertEqual
+ \ '--ruleset /path/to/custom/ruleset.jar --ruleset /path/to/other/ruleset.jar',
+ \ ale#handlers#ktlint#GetRulesets(bufnr(''))
+
+Execute(The ktlint handler method GetRulesets should return an empty string when no rulesets have been configured):
+ let g:ale_kotlin_ktlint_rulesets = []
+
+ AssertEqual
+ \ '',
+ \ ale#handlers#ktlint#GetRulesets(bufnr(''))
diff --git a/test/handler/test_lacheck_handler.vader b/test/handler/test_lacheck_handler.vader
new file mode 100644
index 00000000..0bcc3be8
--- /dev/null
+++ b/test/handler/test_lacheck_handler.vader
@@ -0,0 +1,36 @@
+Before:
+ runtime ale_linters/tex/lacheck.vim
+ call ale#test#SetDirectory('/testplugin/test')
+
+After:
+ call ale#linter#Reset()
+ call ale#test#RestoreDirectory()
+
+Execute(The lacheck handler should parse lines correctly):
+
+ call ale#test#SetFilename('command_callback/tex_paths/sample1.tex')
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'type': 'W',
+ \ 'text': 'perhaps you should insert a `~'' before "\ref"'
+ \ }
+ \ ],
+ \ ale_linters#tex#lacheck#Handle(bufnr(''), [
+ \ "** sample1:",
+ \ "\"sample1.tex\", line 1: perhaps you should insert a `~' before \"\\ref\""
+ \ ])
+
+Execute(The lacheck handler should ignore errors from input files):
+
+ call ale#test#SetFilename('ale_test.tex')
+
+ AssertEqual
+ \ [
+ \ ],
+ \ ale_linters#tex#lacheck#Handle(255, [
+ \ "** ale_input:",
+ \ "\"ale_input.tex\", line 1: perhaps you should insert a `~' before \"\\ref\""
+ \ ])
diff --git a/test/handler/test_languagetool_handler.vader b/test/handler/test_languagetool_handler.vader
new file mode 100644
index 00000000..61d3abfd
--- /dev/null
+++ b/test/handler/test_languagetool_handler.vader
@@ -0,0 +1,62 @@
+Before:
+ runtime! ale_linters/text/languagetool.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(languagetool handler should report 3 errors):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 19,
+ \ 'end_col': 20,
+ \ 'text': 'This sentence does not start with an uppercase letter',
+ \ 'type': 'W',
+ \ 'code': 'UPPERCASE_SENTENCE_START',
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 36,
+ \ 'end_col': 42,
+ \ 'text': "Did you mean 'to see'?",
+ \ 'type': 'W',
+ \ 'code': 'TOO_TO[1]',
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 44,
+ \ 'end_col': 45,
+ \ 'text': "Use 'a' instead of 'an' if the following word doesn't start with a vowel sound, e.g. 'a sentence', 'a university'",
+ \ 'type': 'W',
+ \ 'code': 'EN_A_VS_AN',
+ \ }
+ \ ],
+ \ ale#handlers#languagetool#HandleOutput(bufnr(''), [
+ \ '1.) Line 3, column 19, Rule ID: UPPERCASE_SENTENCE_START',
+ \ 'Message: This sentence does not start with an uppercase letter',
+ \ 'Suggestion: Or',
+ \ '...red phrases for details on potential errors. or use this text too see an few of of the probl...',
+ \ ' ^^ ',
+ \ '',
+ \ '2.) Line 3, column 36, Rule ID: TOO_TO[1]',
+ \ "Message: Did you mean 'to see'?",
+ \ 'Suggestion: to see',
+ \ '...etails on potential errors. or use this text too see an few of of the problems that LanguageTool ...',
+ \ ' ^^^^^^^ ',
+ \ '',
+ \ '3.) Line 3, column 44, Rule ID: EN_A_VS_AN',
+ \ "Message: Use 'a' instead of 'an' if the following word doesn't start with a vowel sound, e.g. 'a sentence', 'a university'",
+ \ 'Suggestion: a',
+ \ '...n potential errors. or use this text too see an few of of the problems that LanguageTool can...',
+ \ ' ^^ ',
+ \ 'Time: 2629ms for 8 sentences (3.0 sentences/sec)'
+ \ ])
+
+Execute(languagetool handler should report no errors on empty input):
+ AssertEqual
+ \ [],
+ \ ale#handlers#languagetool#HandleOutput(bufnr(''), [
+ \ '',
+ \ 'Time: 2629ms for 8 sentences (3.0 sentences/sec)'
+ \ ])
diff --git a/test/handler/test_perl_handler.vader b/test/handler/test_perl_handler.vader
index e769550c..060b1ffe 100644
--- a/test/handler/test_perl_handler.vader
+++ b/test/handler/test_perl_handler.vader
@@ -91,3 +91,19 @@ Execute(The Perl linter reports errors even when mixed with warnings):
\ 'syntax error at - line 8, at EOF',
\ 'Execution of t.pl aborted due to compilation errors.',
\ ])
+
+Execute(The Perl linter reports errors even when an additional file location is included):
+ AssertEqual
+ \ [
+ \ {'lnum': '5', 'type': 'E', 'text': '"my" variable $foo masks earlier declaration in same scope'},
+ \ {'lnum': '6', 'type': 'E', 'text': '"my" variable $foo masks earlier declaration in same scope'},
+ \ {'lnum': '11', 'type': 'E', 'text': 'Global symbol "$asdf" requires explicit package name (did you forget to declare "my $asdf"?)'},
+ \ {'lnum': '12', 'type': 'E', 'text': 'Global symbol "$asdf" requires explicit package name (did you forget to declare "my $asdf"?)'},
+ \ ],
+ \ ale_linters#perl#perl#Handle(bufnr(''), [
+ \ '"my" variable $foo masks earlier declaration in same scope at - line 5.',
+ \ '"my" variable $foo masks earlier declaration in same scope at - line 6, at <DATA> line 1.',
+ \ 'Global symbol "$asdf" requires explicit package name (did you forget to declare "my $asdf"?) at - line 11.',
+ \ 'Global symbol "$asdf" requires explicit package name (did you forget to declare "my $asdf"?) at - line 12, <DATA> line 1.',
+ \ 'Execution of t.pl aborted due to compilation errors.',
+ \ ])
diff --git a/test/handler/test_phpstan_handler.vader b/test/handler/test_phpstan_handler.vader
index 207a7758..67fdb759 100644
--- a/test/handler/test_phpstan_handler.vader
+++ b/test/handler/test_phpstan_handler.vader
@@ -14,7 +14,7 @@ Execute(Output without errors should be parsed correctly):
AssertEqual
\ [],
- \ ale_linters#php#phpstan#Handle(bufnr(''), [" 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%"])
+ \ ale_linters#php#phpstan#Handle(bufnr(''), [])
Execute(Output with some errors should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/foo/test.php')
@@ -24,21 +24,20 @@ Execute(Output with some errors should be parsed correctly):
\ {
\ 'lnum': 9,
\ 'text': 'Call to method format() on an unknown class DateTimeImutable.',
- \ 'type': 'W'
+ \ 'type': 'E'
\ },
\ {
\ 'lnum': 16,
\ 'text': 'Sample message.',
- \ 'type': 'W'
+ \ 'type': 'E'
\ },
\ {
\ 'lnum': 192,
\ 'text': 'Invalid command testCommand.',
- \ 'type': 'W'
+ \ 'type': 'E'
\ }
\ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [
- \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%',
\ 'phpstan-test-files/foo/test.php:9:Call to method format() on an unknown class DateTimeImutable.',
\ 'phpstan-test-files/foo/test.php:16:Sample message.',
\ 'phpstan-test-files/foo/test.php:192:Invalid command testCommand.',
@@ -52,11 +51,10 @@ Execute(Output should be parsed correctly with Windows paths):
\ {
\ 'lnum': 9,
\ 'text': 'Access to an undefined property Test::$var.',
- \ 'type': 'W'
+ \ 'type': 'E'
\ }
\ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [
- \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%',
\ 'D:\phpstan-test-files\foo\test.php:9:Access to an undefined property Test::$var.',
\])
@@ -68,10 +66,9 @@ Execute(Output for .inc files should be parsed correctly):
\ {
\ 'lnum': 9,
\ 'text': 'Access to an undefined property Test::$var.',
- \ 'type': 'W'
+ \ 'type': 'E'
\ }
\ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [
- \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%',
\ '/phpstan-test-files/foo/test.inc:9:Access to an undefined property Test::$var.',
\])
diff --git a/test/handler/test_powershell_handler.vader b/test/handler/test_powershell_handler.vader
new file mode 100755
index 00000000..635bcd20
--- /dev/null
+++ b/test/handler/test_powershell_handler.vader
@@ -0,0 +1,62 @@
+Before:
+ runtime ale_linters/powershell/powershell.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The powershell handler should process syntax errors from parsing a powershell script):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 8,
+ \ 'col': 29,
+ \ 'type': 'E',
+ \ 'text': 'Missing closing ''}'' in statement block or type definition.',
+ \ 'code': 'ParseException',
+ \ },
+ \ ],
+ \ ale_linters#powershell#powershell#Handle(bufnr(''), [
+ \ "At line:8 char:29",
+ \ "+ Invoke-Command -ScriptBlock {",
+ \ "+ ~",
+ \ "Missing closing '}' in statement block or type definition.",
+ \ "At /home/harrisj/tester.ps1:5 char:5",
+ \ "+ [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Contents);",
+ \ "+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
+ \ "+ CategoryInfo : NotSpecified: (:) [], ParseException",
+ \ "+ FullyQualifiedErrorId : ParseException"
+ \ ])
+
+Execute(The powershell handler should process multiple syntax errors from parsing a powershell script):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 11,
+ \ 'col': 31,
+ \ 'type': 'E',
+ \ 'text': 'The string is missing the terminator: ".',
+ \ 'code': 'ParseException'
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 16,
+ \ 'type': 'E',
+ \ 'text': 'Missing closing ''}'' in statement block or type definition.',
+ \ 'code': 'ParseException'
+ \ },
+ \ ],
+ \ ale_linters#powershell#powershell#Handle(bufnr(''), [
+ \ 'At line:11 char:31',
+ \ '+ write-verbose ''deleted''',
+ \ '+ ~',
+ \ 'The string is missing the terminator: ".',
+ \ 'At line:3 char:16',
+ \ '+ invoke-command {',
+ \ '+ ~',
+ \ 'Missing closing ''}'' in statement block or type definition.',
+ \ 'At /var/folders/qv/15ybvt050v9cgwrm7c95x4r4zc4qsg/T/vwhzIc8/1/script.ps1:1 char:150',
+ \ '+ ... ontents); [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Con ...',
+ \ '+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~',
+ \ '+ CategoryInfo : NotSpecified: (:) [], ParseException',
+ \ '+ FullyQualifiedErrorId : ParseException'
+ \ ])
diff --git a/test/handler/test_psscriptanalyzer_handler.vader b/test/handler/test_psscriptanalyzer_handler.vader
new file mode 100644
index 00000000..060d5941
--- /dev/null
+++ b/test/handler/test_psscriptanalyzer_handler.vader
@@ -0,0 +1,42 @@
+Before:
+ runtime ale_linters/powershell/psscriptanalyzer.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The psscriptanalyzer handler should handle basic information or warnings):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'type': 'I',
+ \ 'text': 'The cmdlet ''Get-GithubRepo'' does not have a help comment.',
+ \ 'code': 'PSProvideCommentHelp',
+ \ },
+ \ {
+ \ 'lnum': 9,
+ \ 'type': 'W',
+ \ 'text': '''%'' is an alias of ''ForEach-Object''. Alias can introduce possible problems and make scripts hard to maintain. Please consider changing alias to its full content.',
+ \ 'code': 'PSAvoidUsingCmdletAliases',
+ \ },
+ \ {
+ \ 'lnum': 23,
+ \ 'type': 'E',
+ \ 'text': 'The ComputerName parameter of a cmdlet should not be hardcoded as this will expose sensitive information about the system.',
+ \ 'code': 'PSAvoidUsingComputerNameHardcoded',
+ \ },
+ \ ],
+ \ ale_linters#powershell#psscriptanalyzer#Handle(bufnr(''), [
+ \ '1',
+ \ 'Information',
+ \ 'The cmdlet ''Get-GithubRepo'' does not have a help comment.',
+ \ 'PSProvideCommentHelp',
+ \ '9',
+ \ 'Warning',
+ \ '''%'' is an alias of ''ForEach-Object''. Alias can introduce possible problems and make scripts hard to maintain. Please consider changing alias to its full content.',
+ \ 'PSAvoidUsingCmdletAliases',
+ \ '23',
+ \ 'Error',
+ \ 'The ComputerName parameter of a cmdlet should not be hardcoded as this will expose sensitive information about the system.',
+ \ 'PSAvoidUsingComputerNameHardcoded',
+ \ ])
diff --git a/test/handler/test_pydocstyle_handler.vader b/test/handler/test_pydocstyle_handler.vader
index d155dc9a..cfb75307 100644
--- a/test/handler/test_pydocstyle_handler.vader
+++ b/test/handler/test_pydocstyle_handler.vader
@@ -66,7 +66,7 @@ Execute(Basic pydocstyle warnings should be handled):
\ ],
\ ale_linters#python#pydocstyle#Handle(bufnr(''), [
\ 'Checking file ' . fnamemodify(bufname(bufnr('')), ':p') . '.',
- \ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':1 at module level:',
+ \ './mydir/myfile.py:1 at module level:',
\ ' D100: Missing docstring in public module',
\ '',
\ ' All modules should normally have docstrings. [...] all functions and',
@@ -80,7 +80,7 @@ Execute(Basic pydocstyle warnings should be handled):
\ ' 1: # 2: 3: s 4: a 5: m 6: p 7: l ...',
\ '',
\ '',
- \ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':4 in public function `main`:',
+ \ 'C:\mydir\myfile.py:4 in public function `main`:',
\ ' D205: 1 blank line required between summary line and description (found 0)',
\ '',
\ ' Multi-line docstrings consist of a summary line just like a one-line',
@@ -92,7 +92,7 @@ Execute(Basic pydocstyle warnings should be handled):
\ ' 3: d 4: e 5: f 6: 7: m 8: a 9: i ...',
\ '',
\ '',
- \ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':4 in public function `main`:',
+ \ 'myfile.py:4 in public function `main`:',
\ ' D400: First line should end with a period (not ''e'')',
\ '',
\ ' The [first line of a] docstring is a phrase ending in a period.',
diff --git a/test/handler/test_pylama_handler.vader b/test/handler/test_pylama_handler.vader
new file mode 100644
index 00000000..d21c65d3
--- /dev/null
+++ b/test/handler/test_pylama_handler.vader
@@ -0,0 +1,193 @@
+Before:
+ Save g:ale_warn_about_trailing_whitespace
+
+ let g:ale_warn_about_trailing_whitespace = 1
+
+ runtime ale_linters/python/pylama.vim
+
+After:
+ Restore
+
+ call ale#linter#Reset()
+
+ silent file something_else.py
+
+Execute(The pylama handler should handle no messages):
+ AssertEqual [], ale_linters#python#pylama#Handle(bufnr(''), [])
+
+Execute(The pylama handler should handle basic warnings and syntax errors):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 8,
+ \ 'col': 1,
+ \ 'code': 'W0611',
+ \ 'type': 'W',
+ \ 'sub_type': '',
+ \ 'text': '''foo'' imported but unused [pyflakes]',
+ \ },
+ \ {
+ \ 'lnum': 8,
+ \ 'col': 0,
+ \ 'code': 'E0401',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'Unable to import ''foo'' [pylint]',
+ \ },
+ \ {
+ \ 'lnum': 10,
+ \ 'col': 1,
+ \ 'code': 'E302',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'expected 2 blank lines, found 1 [pycodestyle]',
+ \ },
+ \ {
+ \ 'lnum': 11,
+ \ 'col': 1,
+ \ 'code': 'D401',
+ \ 'type': 'W',
+ \ 'sub_type': 'style',
+ \ 'text': 'First line should be in imperative mood (''Get'', not ''Gets'') [pydocstyle]',
+ \ },
+ \ {
+ \ 'lnum': 15,
+ \ 'col': 81,
+ \ 'code': 'E501',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'line too long (96 > 80 characters) [pycodestyle]',
+ \ },
+ \ {
+ \ 'lnum': 16,
+ \ 'col': 1,
+ \ 'code': 'D203',
+ \ 'type': 'W',
+ \ 'sub_type': 'style',
+ \ 'text': '1 blank line required before class docstring (found 0) [pydocstyle]',
+ \ },
+ \ {
+ \ 'lnum': 18,
+ \ 'col': 1,
+ \ 'code': 'D107',
+ \ 'type': 'W',
+ \ 'sub_type': 'style',
+ \ 'text': 'Missing docstring in __init__ [pydocstyle]',
+ \ },
+ \ {
+ \ 'lnum': 20,
+ \ 'col': 0,
+ \ 'code': 'C4001',
+ \ 'type': 'W',
+ \ 'sub_type': 'style',
+ \ 'text': 'Invalid string quote ", should be '' [pylint]',
+ \ },
+ \ ],
+ \ ale_linters#python#pylama#Handle(bufnr(''), [
+ \ 'No config file found, using default configuration',
+ \ 'index.py:8:1: W0611 ''foo'' imported but unused [pyflakes]',
+ \ 'index.py:8:0: E0401 Unable to import ''foo'' [pylint]',
+ \ 'index.py:10:1: E302 expected 2 blank lines, found 1 [pycodestyle]',
+ \ 'index.py:11:1: D401 First line should be in imperative mood (''Get'', not ''Gets'') [pydocstyle]',
+ \ 'index.py:15:81: E501 line too long (96 > 80 characters) [pycodestyle]',
+ \ 'index.py:16:1: D203 1 blank line required before class docstring (found 0) [pydocstyle]',
+ \ 'index.py:18:1: D107 Missing docstring in __init__ [pydocstyle]',
+ \ 'index.py:20:0: C4001 Invalid string quote ", should be '' [pylint]',
+ \ ])
+
+Execute(The pylama handler should handle tracebacks with parsable messages):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'text': 'ParseError: Cannot parse file. (See :ALEDetail)',
+ \ 'detail': join([
+ \ 'Traceback (most recent call last):',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pylama/core.py", line 66, in run',
+ \ ' path, code=code, ignore=ignore, select=select, params=lparams)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pylama/lint/pylama_pydocstyle.py", line 37, in run',
+ \ ' } for e in PyDocChecker().check_source(*check_source_args)]',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/checker.py", line 64, in check_source',
+ \ ' module = parse(StringIO(source), filename)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/parser.py", line 340, in __call__',
+ \ ' return self.parse(*args, **kwargs)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/parser.py", line 328, in parse',
+ \ ' six.raise_from(ParseError(), error)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/six.py", line 737, in raise_from',
+ \ ' raise value',
+ \ 'ParseError: Cannot parse file.',
+ \ ], "\n"),
+ \ },
+ \ {
+ \ 'lnum': 11,
+ \ 'col': 1,
+ \ 'code': 'E302',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'expected 2 blank lines, found 1 [pycodestyle]',
+ \ },
+ \ {
+ \ 'lnum': 16,
+ \ 'col': 81,
+ \ 'code': 'E501',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'line too long (96 > 80 characters) [pycodestyle]',
+ \ },
+ \ ],
+ \ ale_linters#python#pylama#Handle(bufnr(''), [
+ \ 'Traceback (most recent call last):',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pylama/core.py", line 66, in run',
+ \ ' path, code=code, ignore=ignore, select=select, params=lparams)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pylama/lint/pylama_pydocstyle.py", line 37, in run',
+ \ ' } for e in PyDocChecker().check_source(*check_source_args)]',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/checker.py", line 64, in check_source',
+ \ ' module = parse(StringIO(source), filename)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/parser.py", line 340, in __call__',
+ \ ' return self.parse(*args, **kwargs)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/pydocstyle/parser.py", line 328, in parse',
+ \ ' six.raise_from(ParseError(), error)',
+ \ ' File "/usr/local/lib/python2.7/site-packages/six.py", line 737, in raise_from',
+ \ ' raise value',
+ \ 'ParseError: Cannot parse file.',
+ \ '',
+ \ 'index.py:11:1: E302 expected 2 blank lines, found 1 [pycodestyle]',
+ \ 'index.py:16:81: E501 line too long (96 > 80 characters) [pycodestyle]',
+ \ ])
+
+" Note: This is probably a bug, since all pylama plugins produce codes, but
+" should be handled for compatibility.
+" Note: The pylama isort plugin is distributed in the isort package.
+Execute(The pylama handler should handle messages without codes):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 0,
+ \ 'col': 0,
+ \ 'code': '',
+ \ 'type': 'W',
+ \ 'sub_type': '',
+ \ 'text': 'Incorrectly sorted imports. [isort]'
+ \ },
+ \ ],
+ \ ale_linters#python#pylama#Handle(bufnr(''), [
+ \ 'index.py:0:0: Incorrectly sorted imports. [isort]',
+ \ ])
+
+" Note: This is a pylama bug, but should be handled for compatibility.
+" See https://github.com/klen/pylama/pull/146
+Execute(The pylama handler should handle message codes followed by a colon):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 31,
+ \ 'col': 1,
+ \ 'code': 'E800',
+ \ 'type': 'E',
+ \ 'sub_type': '',
+ \ 'text': 'Found commented out code: # needs_sphinx = ''1.0'' [eradicate]',
+ \ },
+ \ ],
+ \ ale_linters#python#pylama#Handle(bufnr(''), [
+ \ 'index.py:31:1: E800: Found commented out code: # needs_sphinx = ''1.0'' [eradicate]',
+ \ ])
diff --git a/test/handler/test_raco_handler.vader b/test/handler/test_raco_handler.vader
new file mode 100644
index 00000000..217fe2f9
--- /dev/null
+++ b/test/handler/test_raco_handler.vader
@@ -0,0 +1,26 @@
+Before:
+ runtime ale_linters/racket/raco.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The raco handler should handle errors for the current file correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 4,
+ \ 'col': 1,
+ \ 'type': 'E',
+ \ 'text': 'dfine: unbound identifier in modulemessage',
+ \ },
+ \ ],
+ \ ale_linters#racket#raco#Handle(bufnr(''), [
+ \ 'foo.rkt:4:1: dfine: unbound identifier in modulemessage',
+ \ ' in: dfine',
+ \ ' context...:',
+ \ ' /usr/local/Cellar/racket/6.5/share/racket/pkgs/compiler-lib/compiler/commands/expand.rkt:34:15: loop',
+ \ ' /usr/local/Cellar/racket/6.5/share/racket/pkgs/compiler-lib/compiler/commands/expand.rkt:10:2: show-program',
+ \ ' /usr/local/Cellar/racket/6.5/share/racket/pkgs/compiler-lib/compiler/commands/expand.rkt: [running body]',
+ \ ' /usr/local/Cellar/minimal-racket/6.6/share/racket/collects/raco/raco.rkt: [running body]',
+ \ ' /usr/local/Cellar/minimal-racket/6.6/share/racket/collects/raco/main.rkt: [running body]',
+ \ ])
diff --git a/test/handler/test_redpen_handler.vader b/test/handler/test_redpen_handler.vader
index 4490bcba..0b030e2d 100644
--- a/test/handler/test_redpen_handler.vader
+++ b/test/handler/test_redpen_handler.vader
@@ -80,7 +80,7 @@ Execute(redpen handler should handle errors output):
\ ']',
\ ])
-Execute(redpen handler should no error output):
+Execute(The redpen handler should handle an empty error list):
AssertEqual
\ [],
\ ale#handlers#redpen#HandleRedpenOutput(bufnr(''), [
@@ -91,3 +91,8 @@ Execute(redpen handler should no error output):
\ ' }',
\ ']',
\ ])
+
+Execute(The redpen handler should handle totally empty output):
+ AssertEqual
+ \ [],
+ \ ale#handlers#redpen#HandleRedpenOutput(bufnr(''), [])
diff --git a/test/handler/test_rubocop_handler.vader b/test/handler/test_rubocop_handler.vader
index ef0137d6..d7868f26 100644
--- a/test/handler/test_rubocop_handler.vader
+++ b/test/handler/test_rubocop_handler.vader
@@ -41,21 +41,21 @@ Execute(The rubocop handler should parse lines correctly):
\ 'type': 'E',
\ },
\ ],
- \ ale_linters#ruby#rubocop#Handle(347, [
+ \ ale#ruby#HandleRubocopOutput(347, [
\ '{"metadata":{"rubocop_version":"0.47.1","ruby_engine":"ruby","ruby_version":"2.1.5","ruby_patchlevel":"273","ruby_platform":"x86_64-linux-gnu"},"files":[{"path":"my_great_file.rb","offenses":[{"severity":"convention","message":"Prefer single-quoted strings...","cop_name":"Style/SomeCop","corrected":false,"location":{"line":83,"column":29,"length":7}},{"severity":"fatal","message":"Some error","cop_name":"Style/SomeOtherCop","corrected":false,"location":{"line":12,"column":2,"length":1}},{"severity":"warning","message":"Regular warning","cop_name":"Style/WarningCop","corrected":false,"location":{"line":10,"column":5,"length":8}},{"severity":"error","message":"Another error","cop_name":"Style/SpaceBeforeBlockBraces","corrected":false,"location":{"line":11,"column":1,"length":1}}]}],"summary":{"offense_count":4,"target_file_count":1,"inspected_file_count":1}}'
\ ])
Execute(The rubocop handler should handle when files are checked and no offenses are found):
AssertEqual
\ [],
- \ ale_linters#ruby#rubocop#Handle(347, [
+ \ ale#ruby#HandleRubocopOutput(347, [
\ '{"metadata":{"rubocop_version":"0.47.1","ruby_engine":"ruby","ruby_version":"2.1.5","ruby_patchlevel":"273","ruby_platform":"x86_64-linux-gnu"},"files":[{"path":"my_great_file.rb","offenses":[]}],"summary":{"offense_count":0,"target_file_count":1,"inspected_file_count":1}}'
\ ])
Execute(The rubocop handler should handle when no files are checked):
AssertEqual
\ [],
- \ ale_linters#ruby#rubocop#Handle(347, [
+ \ ale#ruby#HandleRubocopOutput(347, [
\ '{"metadata":{"rubocop_version":"0.47.1","ruby_engine":"ruby","ruby_version":"2.1.5","ruby_patchlevel":"273","ruby_platform":"x86_64-linux-gnu"},"files":[],"summary":{"offense_count":0,"target_file_count":0,"inspected_file_count":0}}'
\ ])
@@ -66,11 +66,11 @@ Execute(The rubocop handler should handle output without any errors):
AssertEqual
\ [],
- \ ale_linters#ruby#rubocop#Handle(347, g:lines)
+ \ ale#ruby#HandleRubocopOutput(347, g:lines)
\
AssertEqual
\ [],
- \ ale_linters#ruby#rubocop#Handle(347, ['{}'])
+ \ ale#ruby#HandleRubocopOutput(347, ['{}'])
AssertEqual
\ [],
- \ ale_linters#ruby#rubocop#Handle(347, [])
+ \ ale#ruby#HandleRubocopOutput(347, [])
diff --git a/test/handler/test_rust_handler.vader b/test/handler/test_rust_handler.vader
index 4764e713..56db9b36 100644
--- a/test/handler/test_rust_handler.vader
+++ b/test/handler/test_rust_handler.vader
@@ -8,7 +8,7 @@ Execute(The Rust handler should handle rustc output):
\ 'end_lnum': 15,
\ 'type': 'E',
\ 'col': 5,
- \ 'end_col': 8,
+ \ 'end_col': 7,
\ 'text': 'expected one of `.`, `;`, `?`, `}`, or an operator, found `for`',
\ },
\ {
@@ -16,7 +16,7 @@ Execute(The Rust handler should handle rustc output):
\ 'end_lnum': 13,
\ 'type': 'E',
\ 'col': 7,
- \ 'end_col': 10,
+ \ 'end_col': 9,
\ 'text': 'no method named `wat` found for type `std::string::String` in the current scope',
\ },
\ ],
@@ -84,7 +84,7 @@ Execute(The Rust handler should handle cargo output):
\ 'end_lnum': 15,
\ 'type': 'E',
\ 'col': 5,
- \ 'end_col': 8,
+ \ 'end_col': 7,
\ 'text': 'expected one of `.`, `;`, `?`, `}`, or an operator, found `for`',
\ },
\ {
@@ -92,7 +92,7 @@ Execute(The Rust handler should handle cargo output):
\ 'end_lnum': 13,
\ 'type': 'E',
\ 'col': 7,
- \ 'end_col': 10,
+ \ 'end_col': 9,
\ 'text': 'no method named `wat` found for type `std::string::String` in the current scope',
\ },
\ ],
@@ -158,7 +158,7 @@ Execute(The Rust handler should should errors from expansion spans):
\ 'end_lnum': 4,
\ 'type': 'E',
\ 'col': 21,
- \ 'end_col': 23,
+ \ 'end_col': 22,
\ 'text': 'mismatched types: expected bool, found integral variable',
\ },
\ ],
@@ -208,7 +208,7 @@ Execute(The Rust handler should show detailed errors):
\ 'end_lnum': 4,
\ 'type': 'E',
\ 'col': 21,
- \ 'end_col': 23,
+ \ 'end_col': 22,
\ 'text': 'mismatched types: expected bool, found integral variable',
\ },
\ ],
@@ -296,7 +296,7 @@ Execute(The Rust handler should remove secondary spans if set):
\ 'lnum': 1,
\ 'end_lnum': 1,
\ 'type': 'E',
- \ 'end_col': 21,
+ \ 'end_col': 20,
\ 'col': 1,
\ 'text': 'this function takes 1 parameter but 0 were supplied: defined here',
\ },
@@ -304,7 +304,7 @@ Execute(The Rust handler should remove secondary spans if set):
\ 'lnum': 1,
\ 'end_lnum': 1,
\ 'type': 'E',
- \ 'end_col': 46,
+ \ 'end_col': 45,
\ 'col': 40,
\ 'text': 'this function takes 1 parameter but 0 were supplied: expected 1 parameter',
\ },
@@ -371,7 +371,7 @@ Execute(The Rust handler should remove secondary spans if set):
\ 'lnum': 1,
\ 'end_lnum': 1,
\ 'type': 'E',
- \ 'end_col': 46,
+ \ 'end_col': 45,
\ 'col': 40,
\ 'text': 'this function takes 1 parameter but 0 were supplied: expected 1 parameter',
\ },
diff --git a/test/handler/test_vcom_handler.vader b/test/handler/test_vcom_handler.vader
new file mode 100644
index 00000000..943b525a
--- /dev/null
+++ b/test/handler/test_vcom_handler.vader
@@ -0,0 +1,36 @@
+Before:
+ runtime ale_linters/vhdl/vcom.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The vcom handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 218,
+ \ 'type': 'W',
+ \ 'text': '(vcom-1236) Shared variables must be of a protected type.'
+ \ },
+ \ {
+ \ 'lnum': 73,
+ \ 'type': 'E',
+ \ 'text': '(vcom-1136) Unknown identifier "aresetn".'
+ \ },
+ \ {
+ \ 'lnum': 73,
+ \ 'type': 'E',
+ \ 'text': 'Bad resolution function (STD_LOGIC) for type (error).'
+ \ },
+ \ {
+ \ 'lnum': 73,
+ \ 'type': 'E',
+ \ 'text': 'near ":": (vcom-1576) expecting ";" or ")".'
+ \ },
+ \ ],
+ \ ale_linters#vhdl#vcom#Handle(bufnr(''), [
+ \ '** Warning: ../path/to/file.vhd(218): (vcom-1236) Shared variables must be of a protected type.',
+ \ '** Error: tb_file.vhd(73): (vcom-1136) Unknown identifier "aresetn".',
+ \ '** Error: tb_file.vhd(73): Bad resolution function (STD_LOGIC) for type (error).',
+ \ '** Error: tb_file.vhd(73): near ":": (vcom-1576) expecting ";" or ")".',
+ \ ])
diff --git a/test/handler/test_vlog_handler.vader b/test/handler/test_vlog_handler.vader
new file mode 100644
index 00000000..a70665db
--- /dev/null
+++ b/test/handler/test_vlog_handler.vader
@@ -0,0 +1,24 @@
+Before:
+ runtime ale_linters/verilog/vlog.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The vlog handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 7,
+ \ 'type': 'W',
+ \ 'text': '(vlog-2623) Undefined variable: C.'
+ \ },
+ \ {
+ \ 'lnum': 1,
+ \ 'type': 'E',
+ \ 'text': '(vlog-13294) Identifier must be declared with a port mode: C.'
+ \ },
+ \ ],
+ \ ale_linters#verilog#vlog#Handle(bufnr(''), [
+ \ '** Warning: add.v(7): (vlog-2623) Undefined variable: C.',
+ \ '** Error: file.v(1): (vlog-13294) Identifier must be declared with a port mode: C.',
+ \ ])
diff --git a/test/handler/test_vulture_handler.vader b/test/handler/test_vulture_handler.vader
index c6bd7643..b28055db 100644
--- a/test/handler/test_vulture_handler.vader
+++ b/test/handler/test_vulture_handler.vader
@@ -70,7 +70,7 @@ Execute(Vulture exception should be handled):
\ [
\ {
\ 'lnum': 1,
- \ 'text': 'An exception was thrown. See :ALEDetail',
+ \ 'text': 'BaddestException: Everything gone wrong (See :ALEDetail)',
\ 'detail': join([
\ 'Traceback (most recent call last):',
\ ' File "/usr/lib/python3.6/site-packages/vulture/__init__.py", line 13, in <module>',
diff --git a/test/handler/test_xvhdl_handler.vader b/test/handler/test_xvhdl_handler.vader
new file mode 100644
index 00000000..b90539b8
--- /dev/null
+++ b/test/handler/test_xvhdl_handler.vader
@@ -0,0 +1,24 @@
+Before:
+ runtime ale_linters/vhdl/xvhdl.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The xvhdl handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 17,
+ \ 'type': 'E',
+ \ 'text': '[VRFC 10-91] aresetn is not declared '
+ \ },
+ \ {
+ \ 'lnum': 128,
+ \ 'type': 'E',
+ \ 'text': '[VRFC 10-91] m_axis_tx_tdata is not declared '
+ \ },
+ \ ],
+ \ ale_linters#vhdl#xvhdl#Handle(bufnr(''), [
+ \ 'ERROR: [VRFC 10-91] aresetn is not declared [/path/to/file.vhd:17]',
+ \ 'ERROR: [VRFC 10-91] m_axis_tx_tdata is not declared [/home/user/tx_data.vhd:128]',
+ \ ])
diff --git a/test/handler/test_xvlog_handler.vader b/test/handler/test_xvlog_handler.vader
new file mode 100644
index 00000000..2e1f83fc
--- /dev/null
+++ b/test/handler/test_xvlog_handler.vader
@@ -0,0 +1,18 @@
+Before:
+ runtime ale_linters/verilog/xvlog.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The xvlog handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 5,
+ \ 'type': 'E',
+ \ 'text': '[VRFC 10-1412] syntax error near output '
+ \ },
+ \ ],
+ \ ale_linters#verilog#xvlog#Handle(bufnr(''), [
+ \ 'ERROR: [VRFC 10-1412] syntax error near output [/path/to/file.v:5]',
+ \ ])
diff --git a/test/jsonlint-test-files/app-without-jsonlint/src/app.json b/test/jsonlint-test-files/app-without-jsonlint/src/app.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/jsonlint-test-files/app-without-jsonlint/src/app.json
diff --git a/test/jsonlint-test-files/app/node_modules/.bin/jsonlint b/test/jsonlint-test-files/app/node_modules/.bin/jsonlint
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/jsonlint-test-files/app/node_modules/.bin/jsonlint
diff --git a/test/jsonlint-test-files/app/src/app.json b/test/jsonlint-test-files/app/src/app.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/jsonlint-test-files/app/src/app.json
diff --git a/test/jsonlint-test-files/node_modules/jsonlint/lib/cli.js b/test/jsonlint-test-files/node_modules/jsonlint/lib/cli.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/jsonlint-test-files/node_modules/jsonlint/lib/cli.js
diff --git a/test/kotlin_files/testfile.kt b/test/kotlin_files/testfile.kt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/kotlin_files/testfile.kt
diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader
index f8ff8f70..423138af 100644
--- a/test/lsp/test_did_save_event.vader
+++ b/test/lsp/test_did_save_event.vader
@@ -34,16 +34,19 @@ Before:
\ })
let g:ale_linters = {'foobar': ['dummy_linter']}
- function! ale#lsp_linter#StartLSP(buffer, linter) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
-
- return {
+ let l:details = {
+ \ 'command': 'foobar',
\ 'buffer': a:buffer,
\ 'connection_id': g:conn_id,
\ 'project_root': '/foo/bar',
- \ 'language_id': 'foobar',
\}
+
+ call a:Callback(a:linter, l:details)
+
+ return 1
endfunction
" Replace the Send function for LSP, so we can monitor calls to it.
@@ -61,6 +64,7 @@ After:
unlet! b:ale_enabled
unlet! b:ale_linters
unlet! g:message_list
+ unlet! b:ale_save_event_fired
delfunction LanguageCallback
delfunction ProjectRootCallback
diff --git a/test/lsp/test_lsp_client_messages.vader b/test/lsp/test_lsp_client_messages.vader
index 71768ce5..2abdf6ca 100644
--- a/test/lsp/test_lsp_client_messages.vader
+++ b/test/lsp/test_lsp_client_messages.vader
@@ -23,7 +23,7 @@ Execute(ale#lsp#message#Initialize() should return correct messages):
\ ale#lsp#message#Initialize('/foo/bar', {'foo': 'bar'})
Execute(ale#lsp#message#Initialized() should return correct messages):
- AssertEqual [1, 'initialized'], ale#lsp#message#Initialized()
+ AssertEqual [1, 'initialized', {}], ale#lsp#message#Initialized()
Execute(ale#lsp#message#Shutdown() should return correct messages):
AssertEqual [0, 'shutdown'], ale#lsp#message#Shutdown()
@@ -112,7 +112,7 @@ Execute(ale#lsp#message#Completion() should return correct messages):
\ 'textDocument': {
\ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
\ },
- \ 'position': {'line': 11, 'character': 34},
+ \ 'position': {'line': 11, 'character': 33},
\ }
\ ],
\ ale#lsp#message#Completion(bufnr(''), 12, 34, '')
@@ -126,7 +126,7 @@ Execute(ale#lsp#message#Completion() should return correct messages with a trigg
\ 'textDocument': {
\ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
\ },
- \ 'position': {'line': 11, 'character': 34},
+ \ 'position': {'line': 11, 'character': 33},
\ 'context': {'triggerKind': 2, 'triggerCharacter': '.'},
\ }
\ ],
@@ -141,11 +141,25 @@ Execute(ale#lsp#message#Definition() should return correct messages):
\ 'textDocument': {
\ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
\ },
- \ 'position': {'line': 11, 'character': 34},
+ \ 'position': {'line': 11, 'character': 33},
\ }
\ ],
\ ale#lsp#message#Definition(bufnr(''), 12, 34)
+Execute(ale#lsp#message#TypeDefinition() should return correct messages):
+ AssertEqual
+ \ [
+ \ 0,
+ \ 'textDocument/typeDefinition',
+ \ {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
+ \ },
+ \ 'position': {'line': 11, 'character': 33},
+ \ }
+ \ ],
+ \ ale#lsp#message#TypeDefinition(bufnr(''), 12, 34)
+
Execute(ale#lsp#message#References() should return correct messages):
AssertEqual
\ [
@@ -155,7 +169,7 @@ Execute(ale#lsp#message#References() should return correct messages):
\ 'textDocument': {
\ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
\ },
- \ 'position': {'line': 11, 'character': 34},
+ \ 'position': {'line': 11, 'character': 33},
\ 'context': {'includeDeclaration': v:false},
\ }
\ ],
@@ -181,7 +195,7 @@ Execute(ale#lsp#message#Hover() should return correct messages):
\ 'textDocument': {
\ 'uri': ale#path#ToURI(g:dir . '/foo/bar.ts'),
\ },
- \ 'position': {'line': 11, 'character': 34},
+ \ 'position': {'line': 11, 'character': 33},
\ }
\ ],
\ ale#lsp#message#Hover(bufnr(''), 12, 34)
@@ -192,7 +206,7 @@ Execute(ale#lsp#message#DidChangeConfiguration() should return correct messages)
\ }
AssertEqual
\ [
- \ 0,
+ \ 1,
\ 'workspace/didChangeConfiguration',
\ {
\ 'settings': {
diff --git a/test/lsp/test_lsp_command_formatting.vader b/test/lsp/test_lsp_command_formatting.vader
index 9721f37f..ec3b4120 100644
--- a/test/lsp/test_lsp_command_formatting.vader
+++ b/test/lsp/test_lsp_command_formatting.vader
@@ -17,12 +17,14 @@ Execute(Command formatting should be applied correctly for LSP linters):
call ale#lsp_linter#StartLSP(
\ bufnr(''),
\ {
+ \ 'name': 'linter',
\ 'language_callback': {-> 'x'},
\ 'project_root_callback': {-> '/foo/bar'},
\ 'lsp': 'stdio',
\ 'executable': has('win32') ? 'cmd': 'true',
\ 'command': '%e --foo',
\ },
+ \ {-> 0}
\)
if has('win32')
diff --git a/test/lsp/test_lsp_root_detection.vader b/test/lsp/test_lsp_root_detection.vader
new file mode 100644
index 00000000..b7827248
--- /dev/null
+++ b/test/lsp/test_lsp_root_detection.vader
@@ -0,0 +1,62 @@
+Before:
+ call ale#assert#SetUpLinterTest('c', 'clangd')
+
+ function! Hook1(buffer)
+ return 'abc123'
+ endfunction
+
+After:
+ let g:ale_lsp_root = {}
+ unlet! b:ale_lsp_root
+ delfunction Hook1
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(The buffer-specific variable can be a string):
+ let b:ale_lsp_root = '/some/path'
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject '/some/path'
+
+Execute(The buffer-specific variable can be a dictionary):
+ let b:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject '/some/path'
+
+Execute(The buffer-specific variable can have funcrefs):
+ let b:ale_lsp_root = {'clangd': function('Hook1'), 'golangserver': '/path'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject 'abc123'
+
+Execute(The global variable can be a dictionary):
+ let g:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject '/some/path'
+
+Execute(The global variable can have funcrefs):
+ let g:ale_lsp_root = {'clangd': function('Hook1'), 'golangserver': '/path'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject 'abc123'
+
+Execute(The buffer-specific variable overrides the global variable):
+ let b:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
+ let g:ale_lsp_root = {'clangd': '/not/this/path', 'golangserver': '/elsewhere'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject '/some/path'
+
+Execute(The global variable is queried if the buffer-specific has no value):
+ let b:ale_lsp_root = {'golangserver': '/other/path'}
+ let g:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/elsewhere'}
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject '/some/path'
+
+Execute(The default hook value is acceptable):
+ call ale#test#SetFilename('other-file.c')
+
+ AssertLSPProject ''
diff --git a/test/lsp/test_lsp_startup.vader b/test/lsp/test_lsp_startup.vader
new file mode 100644
index 00000000..028ec9b1
--- /dev/null
+++ b/test/lsp/test_lsp_startup.vader
@@ -0,0 +1,366 @@
+Before:
+ Save g:ale_run_synchronously
+
+ let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
+ unlet! g:ale_run_synchronously_emulate_commands
+
+ runtime autoload/ale/lsp.vim
+ runtime autoload/ale/lsp_linter.vim
+ runtime autoload/ale/engine.vim
+ runtime autoload/ale/job.vim
+ runtime autoload/ale/socket.vim
+
+ let g:job_map = {}
+ let g:emulate_job_failure = 0
+ let g:next_job_id = 1
+
+ let g:socket_map = {}
+ let g:emulate_socket_failure = 0
+ let g:next_channel_id = 0
+
+ let g:message_buffer = ''
+ let g:calls = []
+
+ function! ale#engine#IsExecutable(buffer, executable) abort
+ return !empty(a:executable)
+ endfunction
+
+ function! ale#job#HasOpenChannel(job_id) abort
+ return has_key(g:job_map, a:job_id)
+ endfunction
+
+ function! ale#job#Stop(job_id) abort
+ if has_key(g:job_map, a:job_id)
+ call remove(g:job_map, a:job_id)
+ endif
+ endfunction
+
+ function! ale#job#Start(command, options) abort
+ if g:emulate_job_failure
+ return 0
+ endif
+
+ let l:job_id = g:next_job_id
+ let g:next_job_id += 1
+ let g:job_map[l:job_id] = [a:command, a:options]
+
+ return l:job_id
+ endfunction
+
+ function! ale#job#SendRaw(job_id, data) abort
+ let g:message_buffer .= a:data
+ endfunction
+
+ function! ale#socket#IsOpen(channel_id) abort
+ return has_key(g:socket_map, a:channel_id)
+ endfunction
+
+ function! ale#socket#Close(channel_id) abort
+ if has_key(g:socket_map, a:channel_id)
+ call remove(g:socket_map, a:channel_id)
+ endif
+ endfunction
+
+ function! ale#socket#Open(address, options) abort
+ if g:emulate_socket_failure
+ return -1
+ endif
+
+ let l:channel_id = g:next_channel_id
+ let g:next_channel_id += 1
+ let g:socket_map[l:channel_id] = [a:address, a:options]
+
+ return l:channel_id
+ endfunction
+
+ function! ale#socket#Send(channel_id, data) abort
+ let g:message_buffer .= a:data
+ endfunction
+
+ function! PopMessages() abort
+ let l:message_list = []
+
+ for l:line in split(g:message_buffer, '\(\r\|\n\|Content-Length\)\+')
+ if l:line[:0] is '{'
+ let l:data = json_decode(l:line)
+
+ call add(l:message_list, l:data)
+ endif
+ endfor
+
+ let g:message_buffer = ''
+
+ return l:message_list
+ endfunction
+
+ function! SendMessage(message) abort
+ let l:conn_id = keys(ale#lsp#GetConnections())[0]
+ let l:body = json_encode(a:message)
+ let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
+
+ call ale#lsp#HandleMessage(l:conn_id, l:data)
+ endfunction
+
+ function! Start() abort
+ let l:linter = values(ale#linter#GetLintersLoaded())[0][0]
+
+ return ale#lsp_linter#StartLSP(
+ \ bufnr(''),
+ \ l:linter,
+ \ {linter, details -> add(g:calls, [linter.name, details])},
+ \)
+ endfunction
+
+ function! AssertInitSuccess(linter_name, conn_prefix, language, root, command) abort
+ let l:messages = PopMessages()
+
+ if a:linter_name is# 'tsserver'
+ AssertEqual
+ \ [
+ \ {
+ \ 'seq': v:null,
+ \ 'arguments': {
+ \ 'file': expand('%:p'),
+ \ },
+ \ 'type': 'request',
+ \ 'command': 'open',
+ \ },
+ \ ],
+ \ l:messages
+ else
+ AssertEqual
+ \ [
+ \ {
+ \ 'method': 'initialize',
+ \ 'jsonrpc': '2.0',
+ \ 'id': 1,
+ \ 'params': {
+ \ 'initializationOptions': {},
+ \ 'rootUri': ale#path#ToURI(a:root),
+ \ 'capabilities': {},
+ \ 'rootPath': a:root,
+ \ 'processId': getpid(),
+ \ },
+ \ },
+ \ ],
+ \ l:messages
+
+ call SendMessage({
+ \ 'jsonrpc': '2.0',
+ \ 'id': 1,
+ \ 'result': {
+ \ 'capabilities': {
+ \ 'renameProvider': v:true,
+ \ 'executeCommandProvider': {
+ \ 'commands': [],
+ \ },
+ \ 'hoverProvider': v:true,
+ \ 'documentSymbolProvider': v:true,
+ \ 'documentRangeFormattingProvider': v:true,
+ \ 'codeLensProvider': {
+ \ 'resolveProvider': v:false
+ \ },
+ \ 'referencesProvider': v:true,
+ \ 'textDocumentSync': 2,
+ \ 'documentFormattingProvider': v:true,
+ \ 'codeActionProvider': v:true,
+ \ 'signatureHelpProvider': {
+ \ 'triggerCharacters': ['(', ','],
+ \ },
+ \ 'completionProvider': {
+ \ 'triggerCharacters': ['.'],
+ \ 'resolveProvider': v:false
+ \ },
+ \ 'definitionProvider': v:true,
+ \ 'experimental': {},
+ \ 'documentHighlightProvider': v:true,
+ \ 'workspaceSymbolProvider': v:true,
+ \ },
+ \ },
+ \})
+
+ let l:messages = PopMessages()
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'method': 'initialized',
+ \ 'jsonrpc': '2.0',
+ \ 'params': {},
+ \ },
+ \ {
+ \ 'method': 'textDocument/didOpen',
+ \ 'jsonrpc': '2.0',
+ \ 'params': {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': ale#lsp#message#GetNextVersionID() - 1,
+ \ 'languageId': a:language,
+ \ 'text': "\n",
+ \ },
+ \ },
+ \ },
+ \ ],
+ \ l:messages
+ endif
+
+ AssertEqual
+ \ [
+ \ [
+ \ a:linter_name,
+ \ {
+ \ 'connection_id': a:conn_prefix . ':' . a:root,
+ \ 'project_root': a:root,
+ \ 'buffer': bufnr(''),
+ \ 'command': !empty(a:command) ? ale#job#PrepareCommand(bufnr(''), a:command) : '',
+ \ },
+ \ ],
+ \ ],
+ \ g:calls
+ endfunction
+
+ function! AssertInitFailure() abort
+ let l:messages = PopMessages()
+
+ AssertEqual [], l:messages
+ AssertEqual [], g:calls
+ endfunction
+
+ call ale#linter#Reset()
+
+After:
+ Restore
+
+ call ale#linter#Reset()
+ call ale#lsp#ResetConnections()
+
+ unlet! g:ale_run_synchronously_callbacks
+ unlet! g:job_map
+ unlet! g:emulate_job_failure
+ unlet! g:next_job_id
+
+ unlet! g:socket_map
+ unlet! g:emulate_socket_failure
+ unlet! g:next_channel_id
+
+ unlet! g:message_buffer
+ unlet! g:calls
+
+ delfunction PopMessages
+ delfunction Start
+ delfunction AssertInitSuccess
+ delfunction AssertInitFailure
+
+ runtime autoload/ale/engine.vim
+ runtime autoload/ale/job.vim
+ runtime autoload/ale/socket.vim
+
+Execute(tsserver should be started correctly):
+ runtime ale_linters/typescript/tsserver.vim
+
+ Assert Start()
+ call AssertInitSuccess('tsserver', 'tsserver', '', '', ale#Escape('tsserver'))
+
+Execute(tsserver failures should be handled appropriately):
+ runtime ale_linters/typescript/tsserver.vim
+
+ let g:emulate_job_failure = 1
+
+ Assert !Start()
+ call AssertInitFailure()
+
+Execute(LSP jobs should start correctly):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'stdio',
+ \ 'executable': 'foo',
+ \ 'command': 'foo',
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ Assert Start()
+ call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', 'foo')
+
+Execute(LSP job failures should be handled):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'stdio',
+ \ 'executable': 'foo',
+ \ 'command': 'foo',
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ let g:emulate_job_failure = 1
+
+ Assert !Start()
+ call AssertInitFailure()
+
+Execute(LSP TCP connections should start correctly):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo',
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ Assert Start()
+ call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', '')
+
+Execute(LSP TCP connection failures should be handled):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo',
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ let g:emulate_socket_failure = 1
+
+ Assert !Start()
+ call AssertInitFailure()
+
+Execute(Deferred executables should be handled correctly):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'stdio',
+ \ 'executable': {b -> ale#command#Run(b, 'echo', {-> 'foo'})},
+ \ 'command': '%e -c',
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ Assert Start()
+ call ale#test#FlushJobs()
+ call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', ale#Escape('foo') . ' -c')
+
+Execute(Deferred commands should be handled correctly):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'stdio',
+ \ 'executable': 'foo',
+ \ 'command': {b -> ale#command#Run(b, 'echo', {-> '%e -c'})},
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ Assert Start()
+ call ale#test#FlushJobs()
+ call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', ale#Escape('foo') . ' -c')
+
+Execute(Deferred addresses should be handled correctly):
+ call ale#linter#Define('foobar', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address': {b -> ale#command#Run(b, 'echo', {-> 'localhost:1234'})},
+ \ 'project_root': '/foo/bar',
+ \ 'initialization_options': {},
+ \})
+
+ Assert Start()
+ call ale#test#FlushJobs()
+ call AssertInitSuccess('foo', 'localhost:1234', 'foobar', '/foo/bar', '')
diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader
index 2f59535d..0372765d 100644
--- a/test/lsp/test_other_initialize_message_handling.vader
+++ b/test/lsp/test_other_initialize_message_handling.vader
@@ -1,5 +1,9 @@
Before:
+ runtime autoload/ale/lsp.vim
+
+ let g:message_list = []
let b:conn = {
+ \ 'id': 1,
\ 'is_tsserver': 0,
\ 'data': '',
\ 'root': '/foo/bar',
@@ -10,7 +14,7 @@ Before:
\ 'config': {},
\ 'callback_list': [],
\ 'message_queue': [],
- \ 'capabilities_queue': [],
+ \ 'init_queue': [],
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
@@ -21,8 +25,17 @@ Before:
\ },
\}
+ function! ale#lsp#Send(conn_id, message) abort
+ call add(g:message_list, a:message)
+
+ return 42
+ endfunction
+
After:
unlet! b:conn
+ unlet! g:message_list
+
+ runtime autoload/ale/lsp.vim
Execute(Messages with no method and capabilities should initialize projects):
call ale#lsp#HandleInitResponse(b:conn, {
@@ -30,15 +43,18 @@ Execute(Messages with no method and capabilities should initialize projects):
\})
AssertEqual 1, b:conn.initialized
+ AssertEqual [[1, 'initialized', {}]], g:message_list
Execute(Other messages should not initialize projects):
call ale#lsp#HandleInitResponse(b:conn, {'method': 'lolwat'})
AssertEqual 0, b:conn.initialized
+ AssertEqual [], g:message_list
call ale#lsp#HandleInitResponse(b:conn, {'result': {'x': {}}})
AssertEqual 0, b:conn.initialized
+ AssertEqual [], g:message_list
Execute(Capabilities should bet set up correctly):
call ale#lsp#HandleInitResponse(b:conn, {
@@ -86,6 +102,7 @@ Execute(Capabilities should bet set up correctly):
\ 'symbol_search': 1,
\ },
\ b:conn.capabilities
+ AssertEqual [[1, 'initialized', {}]], g:message_list
Execute(Disabled capabilities should be recognised correctly):
call ale#lsp#HandleInitResponse(b:conn, {
@@ -128,6 +145,7 @@ Execute(Disabled capabilities should be recognised correctly):
\ 'symbol_search': 0,
\ },
\ b:conn.capabilities
+ AssertEqual [[1, 'initialized', {}]], g:message_list
Execute(Results that are not dictionaries should be handled correctly):
call ale#lsp#HandleInitResponse(b:conn, {
@@ -135,3 +153,4 @@ Execute(Results that are not dictionaries should be handled correctly):
\ 'id': 1,
\ 'result': v:null,
\})
+ AssertEqual [], g:message_list
diff --git a/test/lsp/test_read_lsp_diagnostics.vader b/test/lsp/test_read_lsp_diagnostics.vader
index a5c5ded3..377e73d9 100644
--- a/test/lsp/test_read_lsp_diagnostics.vader
+++ b/test/lsp/test_read_lsp_diagnostics.vader
@@ -17,7 +17,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle errors):
\ 'lnum': 3,
\ 'col': 11,
\ 'end_lnum': 5,
- \ 'end_col': 16,
+ \ 'end_col': 15,
\ 'code': 'some-error',
\ }
\ ],
@@ -38,7 +38,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle warnings):
\ 'lnum': 2,
\ 'col': 4,
\ 'end_lnum': 2,
- \ 'end_col': 4,
+ \ 'end_col': 3,
\ 'code': 'some-warning',
\ }
\ ],
@@ -59,7 +59,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should treat messages with missing se
\ 'lnum': 3,
\ 'col': 11,
\ 'end_lnum': 5,
- \ 'end_col': 16,
+ \ 'end_col': 15,
\ 'code': 'some-error',
\ }
\ ],
@@ -79,7 +79,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle messages without codes)
\ 'lnum': 3,
\ 'col': 11,
\ 'end_lnum': 5,
- \ 'end_col': 16,
+ \ 'end_col': 15,
\ }
\ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [
@@ -98,7 +98,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should include sources in detail):
\ 'lnum': 10,
\ 'col': 15,
\ 'end_lnum': 12,
- \ 'end_col': 23,
+ \ 'end_col': 22,
\ }
\ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [
@@ -109,6 +109,26 @@ Execute(ale#lsp#response#ReadDiagnostics() should include sources in detail):
\ }
\ ]}})
+Execute(ale#lsp#response#ReadDiagnostics() should keep detail with line breaks but replace with spaces in text):
+ AssertEqual [
+ \ {
+ \ 'type': 'E',
+ \ 'text': 'cannot borrow `cap` as mutable more than once at a time mutable borrow starts here in previous iteration of loop',
+ \ 'detail': "[rustc] cannot borrow `cap` as mutable\r\nmore than once at a time\n\nmutable borrow starts here\rin previous iteration of loop",
+ \ 'lnum': 10,
+ \ 'col': 15,
+ \ 'end_lnum': 12,
+ \ 'end_col': 22,
+ \ }
+ \ ],
+ \ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [
+ \ {
+ \ 'range': Range(9, 14, 11, 22),
+ \ 'message': "cannot borrow `cap` as mutable\r\nmore than once at a time\n\nmutable borrow starts here\rin previous iteration of loop",
+ \ 'source': 'rustc',
+ \ }
+ \ ]}})
+
Execute(ale#lsp#response#ReadDiagnostics() should consider -1 to be a meaningless code):
AssertEqual [
\ {
@@ -117,7 +137,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should consider -1 to be a meaningles
\ 'lnum': 3,
\ 'col': 11,
\ 'end_lnum': 5,
- \ 'end_col': 16,
+ \ 'end_col': 15,
\ }
\ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [
@@ -136,7 +156,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages):
\ 'lnum': 1,
\ 'col': 3,
\ 'end_lnum': 1,
- \ 'end_col': 3,
+ \ 'end_col': 2,
\ },
\ {
\ 'type': 'W',
@@ -144,7 +164,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages):
\ 'lnum': 2,
\ 'col': 5,
\ 'end_lnum': 2,
- \ 'end_col': 5,
+ \ 'end_col': 4,
\ },
\ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [
@@ -167,7 +187,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should use relatedInformation for det
\ 'lnum': 1,
\ 'col': 3,
\ 'end_lnum': 1,
- \ 'end_col': 3,
+ \ 'end_col': 2,
\ 'detail': "Something went wrong!\n/tmp/someotherfile.txt:43:80:\n\tmight be this"
\ }
\ ],
diff --git a/test/lsp/test_reset_lsp.vader b/test/lsp/test_reset_lsp.vader
index 2bec13dc..310b3d62 100644
--- a/test/lsp/test_reset_lsp.vader
+++ b/test/lsp/test_reset_lsp.vader
@@ -18,14 +18,17 @@ Before:
endfunction
call ale#engine#InitBufferInfo(bufnr(''))
+ " Call this function first, so we can be sure the module is loaded before we
+ " check if it exists.
+ call ale#lsp_linter#ClearLSPData()
call ale#linter#Define('testft', {
\ 'name': 'lsplinter',
\ 'lsp': 'tsserver',
- \ 'executable_callback': 'EmptyString',
- \ 'command_callback': 'EmptyString',
- \ 'project_root_callback': 'EmptyString',
- \ 'language_callback': 'EmptyString',
+ \ 'executable': function('EmptyString'),
+ \ 'command': function('EmptyString'),
+ \ 'project_root': function('EmptyString'),
+ \ 'language': function('EmptyString'),
\})
call ale#linter#Define('testft', {
@@ -68,7 +71,10 @@ Execute(ALEStopAllLSPs should clear the loclist):
\ 'linter_name': 'otherlinter',
\ },
\]
- let g:ale_buffer_info[bufnr('')].active_linter_list = ['lsplinter', 'otherlinter']
+ let g:ale_buffer_info[bufnr('')].active_linter_list = [
+ \ {'name': 'lsplinter'},
+ \ {'name': 'otherlinter'},
+ \]
ALEStopAllLSPs
@@ -87,4 +93,6 @@ Execute(ALEStopAllLSPs should clear the loclist):
\]
" The LSP linter should be removed from the active linter list.
- AssertEqual g:ale_buffer_info[bufnr('')].active_linter_list, ['otherlinter']
+ AssertEqual
+ \ ['otherlinter'],
+ \ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
diff --git a/test/lsp/test_update_config.vader b/test/lsp/test_update_config.vader
index 07068bc8..698477ec 100644
--- a/test/lsp/test_update_config.vader
+++ b/test/lsp/test_update_config.vader
@@ -3,6 +3,10 @@ Before:
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
+ " Stub out this function, so we test updating configs.
+ function! ale#lsp#Send(conn_id, message) abort
+ endfunction
+
After:
Restore
diff --git a/test/markdown_files/testfile.md b/test/markdown_files/testfile.md
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/markdown_files/testfile.md
diff --git a/test/python/test_deoplete_source.py b/test/python/test_deoplete_source.py
new file mode 100644
index 00000000..960abe3a
--- /dev/null
+++ b/test/python/test_deoplete_source.py
@@ -0,0 +1,130 @@
+import unittest
+import imp
+
+ale_module = imp.load_source(
+ 'deoplete.sources.ale',
+ '/testplugin/rplugin/python3/deoplete/sources/ale.py',
+)
+
+
+class VimMock(object):
+ def __init__(self, call_list, call_results):
+ self.__call_list = call_list
+ self.__call_results = call_results
+
+ def call(self, function, *args):
+ self.__call_list.append((function, args))
+
+ return self.__call_results.get(function, 0)
+
+
+class DeopleteSourceTest(unittest.TestCase):
+ def setUp(self):
+ super(DeopleteSourceTest, self).setUp()
+
+ self.call_list = []
+ self.call_results = {}
+ self.source = ale_module.Source('vim')
+ self.source.vim = VimMock(self.call_list, self.call_results)
+
+ def test_attributes(self):
+ """
+ Check all of the attributes we set.
+ """
+ attributes = dict(
+ (key, getattr(self.source, key))
+ for key in
+ dir(self.source)
+ if not key.startswith('__')
+ and key != 'vim'
+ and not hasattr(getattr(self.source, key), '__self__')
+ )
+
+ self.assertEqual(attributes, {
+ 'is_bytepos': True,
+ 'mark': '[L]',
+ 'min_pattern_length': 1,
+ 'name': 'ale',
+ 'rank': 100,
+ })
+
+ def test_completion_position(self):
+ self.call_results['ale#completion#GetCompletionPosition'] = 2
+
+ self.assertEqual(self.source.get_completion_position(), 2)
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletionPosition', ()),
+ ])
+
+ def test_request_completion_results(self):
+ context = {'is_async': False}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ def test_refresh_completion_results(self):
+ context = {'is_async': False}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ context = {'is_async': True, 'is_refresh': True}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True, 'is_refresh': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ def test_poll_no_result(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = None
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletionResult', ()),
+ ])
+
+ def test_poll_empty_result_ready(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = []
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': False})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletionResult', ()),
+ ])
+
+ def test_poll_non_empty_result_ready(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = [
+ {
+ 'word': 'foobar',
+ 'kind': 'v',
+ 'icase': 1,
+ 'menu': '',
+ 'info': '',
+ },
+ ]
+
+ self.assertEqual(self.source.gather_candidates(context), [
+ {
+ 'word': 'foobar',
+ 'kind': 'v',
+ 'icase': 1,
+ 'menu': '',
+ 'info': '',
+ },
+ ])
+ self.assertEqual(context, {'is_async': False})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletionResult', ()),
+ ])
diff --git a/test/script/block-padding-checker b/test/script/block-padding-checker
index b13c9b92..2feab6d0 100755
--- a/test/script/block-padding-checker
+++ b/test/script/block-padding-checker
@@ -10,7 +10,8 @@ import re
INDENTATION_RE = re.compile(r'^ *')
COMMENT_LINE_RE = re.compile(r'^ *"')
-COMMAND_RE = re.compile(r'^ *([a-zA-Z]+)')
+COMMAND_RE = re.compile(r'^ *([a-zA-Z\\]+)')
+OPERATOR_END_RE = re.compile(r'(&&|\|\||\+|-|\*\| /)$')
START_BLOCKS = set(['if', 'for', 'while', 'try', 'function'])
END_BLOCKS = set(['endif', 'endfor', 'endwhile', 'endtry', 'endfunction'])
@@ -21,6 +22,7 @@ WHITESPACE_BEFORE_SET = START_BLOCKS | TERMINATORS
WHITESPACE_FORBIDDEN_BEFORE_SET = END_BLOCKS | MIDDLE_BLOCKS
WHITESPACE_AFTER_SET = END_BLOCKS
WHITESPACE_FORBIDDEN_AFTER_SET = START_BLOCKS | MIDDLE_BLOCKS
+SAME_INDENTATION_SET = set(['\\'])
def remove_comment_lines(line_iter):
@@ -44,7 +46,7 @@ def check_lines(line_iter):
):
yield (
line_number,
- 'Blank line forbidden after `%s`' % (command,)
+ 'Blank line forbidden after `%s`' % (previous_command,)
)
previous_line_blank = True
@@ -56,6 +58,26 @@ def check_lines(line_iter):
if command_match:
command = command_match.group(1)
+ if (
+ command in SAME_INDENTATION_SET
+ and previous_indentation_level is not None
+ and indentation_level != previous_indentation_level
+ ):
+ yield (
+ line_number,
+ 'Line continuation should match previous indentation'
+ )
+
+ if (
+ previous_indentation_level is not None
+ and indentation_level != previous_indentation_level
+ and abs(indentation_level - previous_indentation_level) != 4 # noqa
+ ):
+ yield (
+ line_number,
+ 'Indentation should be 4 spaces'
+ )
+
# Check for commands requiring blank lines before them, if they
# aren't at the start of a block.
if (
@@ -98,6 +120,12 @@ def check_lines(line_iter):
previous_line_blank = False
previous_indentation_level = indentation_level
+ if OPERATOR_END_RE.search(line):
+ yield (
+ line_number,
+ 'Put operators at the start of lines instead'
+ )
+
def main():
status = 0
diff --git a/test/script/check-supported-tools-tables b/test/script/check-supported-tools-tables
index 220c7427..beb580d7 100755
--- a/test/script/check-supported-tools-tables
+++ b/test/script/check-supported-tools-tables
@@ -6,66 +6,56 @@ set -u
# 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 -t doc.XXXXXXXX)"
+doc_sorted_file="$(mktemp -t doc-sorted.XXXXXXXX)"
readme_file="$(mktemp -t readme.XXXXXXXX)"
-sed -n "$ale_help_start_line,$ale_help_end_line"p doc/ale.txt \
- | grep '\* .*: ' \
- | sed 's/^*//' \
- | sed 's/[`!^]//g;s/([^)]*)//g' \
- | sed 's/ *\([,:]\)/\1/g' \
- | sed 's/ */ /g' \
- | sed 's/^ *//;s/ *$//' \
- | sed 's/^/ /' \
- > "$doc_file"
-
-sed -n "$readme_start_line,$readme_end_line"p README.md \
- | grep '| .* |' \
- | sed '/^| Language/d;/^| ---/d' \
- | sed 's/^|//' \
- | sed 's/ \{0,1\}|/:/' \
- | sed 's/[`!^|]//g;s/([^)]*)//g' \
- | sed 's/\[//g;s/\]//g' \
- | sed 's/see[^,]*//g' \
- | sed 's/ *\([,:]\)/\1/g' \
- | sed 's/ */ /g' \
- | sed 's/^ *//;s/ *$//' \
- | sed 's/^/ /' \
- | sed 's/ *-n flag//g' \
- > "$readme_file"
+while read -r; do
+ if [[ "$REPLY" =~ ^! ]]; then
+ language="${REPLY/!/}"
+ else
+ # shellcheck disable=SC2001
+ echo "$language - $REPLY"
+ fi
+done < <(
+ grep '^\*\|^ *\*' doc/ale-supported-languages-and-tools.txt \
+ | sed -e '1,2d' \
+ | sed 's/^\* */!/' \
+ | sed 's/^ *\* *\|!!\|\^\|(.*)\|`//g' \
+ | sed 's/ *$//'
+) > "$doc_file"
+
+while read -r; do
+ if [[ "$REPLY" =~ ^! ]]; then
+ language="${REPLY/!/}"
+ else
+ # shellcheck disable=SC2001
+ echo "$language - $REPLY"
+ fi
+done < <(
+ grep '^\*\|^ *\*' supported-tools.md \
+ | sed 's/^\* */!/' \
+ | sed 's/^ *\* *\|:floppy_disk:\|:warning:\|(.*)\|\[\|\].*\|-n flag//g' \
+ | sed 's/ *$//'
+) > "$readme_file"
exit_code=0
+# Sort the tools ignoring case, and complain when things are out of order.
+sort -f -k1,2 "$doc_file" -o "$doc_sorted_file"
+
+diff -U0 "$doc_sorted_file" "$doc_file" || exit_code=$?
+
+if ((exit_code)); then
+ echo
+ echo "The supported tools list isn't sorted properly"
+ echo
+fi
+
diff -U0 "$readme_file" "$doc_file" || exit_code=$?
rm "$doc_file"
+rm "$doc_sorted_file"
rm "$readme_file"
exit "$exit_code"
diff --git a/test/script/check-toc b/test/script/check-toc
index 09d794ee..f3a57ed2 100755
--- a/test/script/check-toc
+++ b/test/script/check-toc
@@ -6,19 +6,24 @@ set -u
# This script checks that the table of contents for the supported tools is
# sorted, and that the table matches the files.
-toc_start_line="$( \
- grep -m1 -n 'Integration Documentation.*|ale-integrations|' doc/ale.txt \
+toc_section_start_line="$(
+ grep -m1 -n '^7\..*\*ale-other-integration-options\*' doc/ale.txt \
+ | sed 's/\([0-9]*\).*/\1/' \
+)"
+toc_start_offset="$( \
+ tail -n +"$toc_section_start_line" doc/ale.txt \
+ | grep -m1 -n '^ .*\.\.\.' \
| sed 's/\([0-9]*\).*/\1/' \
)"
# shellcheck disable=SC2003
-toc_start_line="$(expr "$toc_start_line" + 1)"
+toc_start_line="$(expr "$toc_section_start_line" + "$toc_start_offset" - 1)"
toc_section_size="$( \
tail -n +"$toc_start_line" doc/ale.txt \
- | grep -m1 -n '^ [0-9]\+\.' \
+ | grep -m1 -n '^===*$' \
| sed 's/\([0-9]*\).*/\1/' \
)"
# shellcheck disable=SC2003
-toc_end_line="$(expr "$toc_start_line" + "$toc_section_size" - 2)"
+toc_end_line="$(expr "$toc_start_line" + "$toc_section_size" - 4)"
toc_file="$(mktemp -t table-of-contents.XXXXXXXX)"
heading_file="$(mktemp -t headings.XXXXXXXX)"
@@ -26,7 +31,7 @@ tagged_toc_file="$(mktemp -t ale.txt.XXXXXXXX)"
sorted_toc_file="$(mktemp -t sorted-ale.txt.XXXXXXXX)"
sed -n "$toc_start_line,$toc_end_line"p doc/ale.txt \
- | sed 's/^ \( *[^.][^.]*\)\.\.*|\(..*\)|/\1, \2/' \
+ | sed 's/^ \( *[^.][^.]*\)\.\.*|\(..*\)|/\1, \2/' \
> "$toc_file"
# Get all of the doc files in a natural sorted order.
diff --git a/test/script/custom-checks b/test/script/custom-checks
index d4027fec..20dbfb80 100755
--- a/test/script/custom-checks
+++ b/test/script/custom-checks
@@ -67,4 +67,14 @@ echo
test/script/check-toc || exit_code=$?
+echo '========================================'
+echo 'Check Python code'
+echo '========================================'
+echo
+
+docker run --rm -v "$PWD:/testplugin" "$DOCKER_RUN_IMAGE" \
+ python -W ignore -m unittest discover /testplugin/test/python \
+ || exit_code=$?
+echo
+
exit $exit_code
diff --git a/test/script/custom-linting-rules b/test/script/custom-linting-rules
index 77e87db4..981a9459 100755
--- a/test/script/custom-linting-rules
+++ b/test/script/custom-linting-rules
@@ -106,6 +106,7 @@ 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 'tempname(' 'Use ale#util#Tempname instead of tempname'
+check_errors 'getcurpos(' "Use getpos('.') instead of getcurpos() if you don't need curswant, to avoid a bug that changes curswant"
check_errors "expand(['\"]%" "Use expand('#' . a:buffer . '...') instead. You might get a filename for the wrong buffer."
check_errors 'getcwd()' "Do not use getcwd(), as it could run from the wrong buffer. Use expand('#' . a:buffer . ':p:h') instead."
check_errors '==#' "Use 'is#' instead of '==#'. 0 ==# 'foobar' is true"
diff --git a/test/sign/test_linting_sets_signs.vader b/test/sign/test_linting_sets_signs.vader
index 3ccecf45..a8d5761f 100644
--- a/test/sign/test_linting_sets_signs.vader
+++ b/test/sign/test_linting_sets_signs.vader
@@ -13,6 +13,7 @@ Before:
let g:ale_buffer_info = {}
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
let g:ale_set_signs = 1
" Disable features we don't need for these tests.
let g:ale_set_quickfix = 0
@@ -58,10 +59,12 @@ After:
delfunction TestCallback
delfunction CollectSigns
+ unlet! g:ale_run_synchronously_callbacks
sign unplace *
call ale#linter#Reset()
Execute(The signs should be updated after linting is done):
ALELint
+ call ale#test#FlushJobs()
AssertEqual [['1', 'ALEWarningSign'], ['2', 'ALEErrorSign']], CollectSigns()
diff --git a/test/sign/test_sign_placement.vader b/test/sign/test_sign_placement.vader
index e2d95ff0..f0b3ba2f 100644
--- a/test/sign/test_sign_placement.vader
+++ b/test/sign/test_sign_placement.vader
@@ -87,6 +87,7 @@ Before:
After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:loclist
delfunction GenerateResults
delfunction ParseSigns
@@ -134,6 +135,7 @@ Given testft(A file with warnings/errors):
Execute(The current signs should be set for running a job):
ALELint
+ call ale#test#FlushJobs()
AssertEqual
\ [
diff --git a/test/smoke_test.vader b/test/smoke_test.vader
index c87f95b2..53e08a8d 100644
--- a/test/smoke_test.vader
+++ b/test/smoke_test.vader
@@ -66,7 +66,7 @@ Execute(Linters should run with the default options):
" where tests fail randomly.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#Queue(0, '')
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
let g:results = ale#test#GetLoclistWithoutModule()
@@ -110,7 +110,7 @@ Execute(Linters should run in PowerShell too):
\})
call ale#Queue(0, '')
- call ale#engine#WaitForJobs(4000)
+ call ale#test#WaitForJobs(4000)
AssertEqual [
\ {
@@ -140,7 +140,7 @@ Execute(Linters should run in PowerShell too):
Execute(Previous errors should be removed when linters change):
call ale#Queue(0, '')
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
call ale#linter#Reset()
@@ -167,7 +167,7 @@ Execute(Previous errors should be removed when linters change):
" where tests fail randomly.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#Queue(0, '')
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
let g:results = ale#test#GetLoclistWithoutModule()
diff --git a/test/swift-test-files/non-swift-package-project/src/folder/dummy.swift b/test/swift-test-files/non-swift-package-project/src/folder/dummy.swift
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/swift-test-files/non-swift-package-project/src/folder/dummy.swift
diff --git a/test/swift-test-files/swift-package-project/Package.swift b/test/swift-test-files/swift-package-project/Package.swift
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/swift-test-files/swift-package-project/Package.swift
diff --git a/test/swift-test-files/swift-package-project/src/folder/dummy.swift b/test/swift-test-files/swift-package-project/src/folder/dummy.swift
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/swift-test-files/swift-package-project/src/folder/dummy.swift
diff --git a/test/test_ale_has.vader b/test/test_ale_has.vader
new file mode 100644
index 00000000..eb1da039
--- /dev/null
+++ b/test/test_ale_has.vader
@@ -0,0 +1,7 @@
+Execute(Checks for versions below the current version should succeed):
+ AssertEqual 1, ale#Has('ale-2.4.0')
+ AssertEqual 1, ale#Has('ALE-2.2.1')
+ AssertEqual 1, ale#Has('ALE-1.0.0')
+
+Execute(Checks for newer versions should fail):
+ AssertEqual 0, ale#Has('ALE-20.0.0')
diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader
index 325c2aa8..29c19b8e 100644
--- a/test/test_ale_info.vader
+++ b/test/test_ale_info.vader
@@ -97,6 +97,7 @@ Before:
\ 'let g:ale_list_vertical = 0',
\ 'let g:ale_list_window_size = 10',
\ 'let g:ale_loclist_msg_format = ''%code: %%s''',
+ \ 'let g:ale_lsp_root = {}',
\ 'let g:ale_max_buffer_history_size = 20',
\ 'let g:ale_max_signs = -1',
\ 'let g:ale_maximum_file_size = 0',
diff --git a/test/test_ale_lint_command.vader b/test/test_ale_lint_command.vader
index bc2ebabe..ba7308dd 100644
--- a/test/test_ale_lint_command.vader
+++ b/test/test_ale_lint_command.vader
@@ -1,7 +1,9 @@
Before:
Save g:ale_buffer_info
+ Save g:ale_enabled
let g:ale_buffer_info = {}
+ let g:ale_enabled = 1
let g:expected_loclist = [{
\ 'bufnr': bufnr('%'),
@@ -58,7 +60,7 @@ Execute(ALELint should run the linters):
" Try to run the linter a few times, as it fails randomly in NeoVim.
for b:i in range(5)
ALELint
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
if !has('nvim')
" Sleep so the delayed list function can run.
diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader
index db891009..d0bca329 100644
--- a/test/test_ale_toggle.vader
+++ b/test/test_ale_toggle.vader
@@ -10,6 +10,7 @@ Before:
let g:ale_set_signs = 1
let g:ale_set_lists_synchronously = 1
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
let g:ale_pattern_options = {}
let g:ale_pattern_options_enabled = 1
let g:ale_set_balloons =
@@ -85,6 +86,7 @@ Before:
After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:expected_loclist
unlet! g:expected_groups
unlet! b:ale_enabled
@@ -113,6 +115,7 @@ Execute(ALEToggle should reset everything and then run again):
AssertEqual 'foobar', &filetype
ALELint
+ call ale#test#FlushJobs()
" First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@@ -135,6 +138,7 @@ Execute(ALEToggle should reset everything and then run again):
" Toggle ALE on, everything should be set up and run again.
ALEToggle
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
@@ -157,6 +161,7 @@ Execute(ALEToggle should skip filename keys and preserve them):
\}
ALELint
+ call ale#test#FlushJobs()
" Now Toggle ALE off.
ALEToggle
@@ -174,6 +179,7 @@ Execute(ALEToggle should skip filename keys and preserve them):
" Toggle ALE on again.
ALEToggle
+ call ale#test#FlushJobs()
AssertEqual
\ {
@@ -188,15 +194,18 @@ Execute(ALEToggle should skip filename keys and preserve them):
Execute(ALEDisable should reset everything and stay disabled):
ALELint
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisable
+ call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled
ALEDisable
+ call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled
@@ -205,6 +214,7 @@ Execute(ALEEnable should enable ALE and lint again):
let g:ale_enabled = 0
ALEEnable
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, g:ale_enabled
@@ -213,6 +223,7 @@ Execute(ALEReset should reset everything for a buffer):
AssertEqual 'foobar', &filetype
ALELint
+ call ale#test#FlushJobs()
" First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@@ -224,6 +235,7 @@ Execute(ALEReset should reset everything for a buffer):
" Now Toggle ALE off.
ALEReset
+ call ale#test#FlushJobs()
" Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
@@ -237,6 +249,7 @@ Execute(ALEToggleBuffer should reset everything and then run again):
AssertEqual 'foobar', &filetype
ALELint
+ call ale#test#FlushJobs()
" First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@@ -257,6 +270,7 @@ Execute(ALEToggleBuffer should reset everything and then run again):
" Toggle ALE on, everything should be set up and run again.
ALEToggleBuffer
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
@@ -268,10 +282,12 @@ Execute(ALEToggleBuffer should reset everything and then run again):
Execute(ALEDisableBuffer should reset everything and stay disabled):
ALELint
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisableBuffer
+ call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, b:ale_enabled
@@ -280,6 +296,7 @@ Execute(ALEEnableBuffer should enable ALE and lint again):
let b:ale_enabled = 0
ALEEnableBuffer
+ call ale#test#FlushJobs()
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, b:ale_enabled
@@ -303,6 +320,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
AssertEqual 'foobar', &filetype
ALELint
+ call ale#test#FlushJobs()
" First check that everything is there...
AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
@@ -314,6 +332,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
" Now Toggle ALE off.
ALEResetBuffer
+ call ale#test#FlushJobs()
" Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader
index 8a9b7189..045554a3 100644
--- a/test/test_c_flag_parsing.vader
+++ b/test/test_c_flag_parsing.vader
@@ -14,14 +14,25 @@ Execute(The CFlags parser should be able to parse include directives):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
- \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')),
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'),
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c'])
+ AssertEqual
+ \ '-isystem ' . '/usr/include/dir',
+ \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -isystem /usr/include/dir -c file.c'])
+
+Execute(ParseCFlags should ignore -c and -o):
+ call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
+
+ AssertEqual
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'),
+ \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c -o a.out'])
+
Execute(The CFlags parser should be able to parse macro directives):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
- \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
\ . ' -DTEST=1',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=1 -c file.c'])
@@ -29,7 +40,7 @@ Execute(The CFlags parser should be able to parse macro directives with spaces):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
- \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
\ . ' -DTEST=$(( 2 * 4 ))',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c'])
@@ -37,14 +48,14 @@ Execute(The CFlags parser should be able to parse shell directives with spaces):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
- \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=`date +%s` -c file.c'])
Execute(ParseCFlags should be able to parse flags with relative paths):
AssertEqual
- \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
@@ -56,8 +67,8 @@ Execute(ParseCFlags should be able to parse flags with relative paths):
Execute(ParseCFlags should be able to parse -Dgoal):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
@@ -66,29 +77,16 @@ Execute(ParseCFlags should be able to parse -Dgoal):
\ . ' -DTEST=`date +%s` -c file.c'
\ )
-Execute(ParseCFlags should ignore -T and other arguments):
- AssertEqual
- \ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
- \ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
- \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir --sysroot=subdir '
- \ . '-I'. ale#path#Simplify('kernel/include')
- \ . ' -DTEST=`date +%s` -c file.c'
- \ )
-
Execute(ParseCFlags should handle paths with spaces in double quotes):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/"dir with spaces"')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
+ \ 'gcc -Dgoal=9 -Isubdir '
\ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
@@ -96,29 +94,28 @@ Execute(ParseCFlags should handle paths with spaces in double quotes):
Execute(ParseCFlags should handle paths with spaces in single quotes):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. "/test_c_projects/makefile_project/'dir with spaces'")
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
- \ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include')
+ \ 'gcc -Dgoal=9 -Isubdir '
+ \ . "-I'dir with spaces'" . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should handle paths with minuses):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
- \ . '-I''dir with spaces''' . ' -Idir-with-dash'
+ \ 'gcc -Dgoal=9 -Isubdir '
+ \ . ' -Idir-with-dash'
\ . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
@@ -126,17 +123,14 @@ Execute(ParseCFlags should handle paths with minuses):
Execute(ParseCFlags should handle -D with minuses):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
\ . ' -Dmacro-with-dash'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
+ \ 'gcc -Dgoal=9 -Isubdir '
\ . '-Dmacro-with-dash '
- \ . '-I''dir with spaces''' . ' -Idir-with-dash'
\ . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
@@ -144,16 +138,11 @@ Execute(ParseCFlags should handle -D with minuses):
Execute(ParseCFlags should handle flags at the end of the line):
AssertEqual
\ '-Dgoal=9'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' -Dmacro-with-dash'
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
- \ . ' ' . ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')),
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')
+ \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'),
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
- \ . '-Dmacro-with-dash '
- \ . '-I''dir with spaces''' . ' -Idir-with-dash'
+ \ 'gcc -Dgoal=9 -Isubdir '
\ . ' -I'. ale#path#Simplify('kernel/include')
\ )
@@ -161,19 +150,145 @@ Execute(FlagsFromCompileCommands should tolerate empty values):
AssertEqual '', ale#c#FlagsFromCompileCommands(bufnr(''), '')
Execute(ParseCompileCommandsFlags should tolerate empty values):
- AssertEqual '', ale#c#ParseCompileCommandsFlags(bufnr(''), '', [])
+ AssertEqual '', ale#c#ParseCompileCommandsFlags(bufnr(''), {}, {})
Execute(ParseCompileCommandsFlags should parse some basic flags):
- noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
AssertEqual
- \ '-I' . ale#path#Simplify('/usr/include/xmms2'),
- \ ale#c#ParseCompileCommandsFlags(bufnr(''), ale#path#Simplify('/foo/bar/xmms2-mpris'), [
+ \ '-I/usr/include/xmms2',
+ \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [
\ {
- \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
- \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
\ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
- \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
- \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ },
+ \ ] }, {})
+
+Execute(ParseCompileCommandsFlags should tolerate items without commands):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+
+ AssertEqual
+ \ '',
+ \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ },
+ \ ] }, {})
+
+Execute(ParseCompileCommandsFlags should fall back to files in the same directory):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+
+ AssertEqual
+ \ '-I/usr/include/xmms2',
+ \ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c',
+ \ },
+ \ ] })
+
+Execute(ParseCompileCommandsFlags should take commands from matching .c files for .h files):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h'))
+
+ AssertEqual
+ \ '-I/usr/include/xmms2',
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ 'xmms2-mpris.c': [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should take commands from matching .cpp files for .hpp files):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.hpp'))
+
+ AssertEqual
+ \ '-I/usr/include/xmms2',
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ 'xmms2-mpris.cpp': [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-mpris.cpp',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.cpp',
+ \ },
+ \ ],
\ },
- \ ])
+ \ {
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should take commands from matching .cpp files for .h files):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h'))
+
+ AssertEqual
+ \ '-I/usr/include/xmms2',
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ 'xmms2-mpris.cpp': [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-mpris.cpp',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.cpp',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should not take commands from .c files for .h files with different names):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/other.h'))
+
+ AssertEqual
+ \ '',
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ 'xmms2-mpris.c': [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2'
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ },
+ \ )
+
+Execute(ParseCFlags should handle parenthesis and quotes):
+ AssertEqual
+ \ '-Dgoal=9 -Dtest1="('' '')" file1.o -Dtest2=''(` `)'' file2.o -Dtest3=`(" ")` file3.o',
+ \ ale#c#ParseCFlags(
+ \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
+ \ 'gcc -Dgoal=9 '
+ \ . '-Dtest1="('' '')" file1.o '
+ \ . '-Dtest2=''(` `)'' file2.o '
+ \ . '-Dtest3=`(" ")` file3.o '
+ \ )
diff --git a/test/test_command_chain.vader b/test/test_command_chain.vader
index 591f6f40..329ebc97 100644
--- a/test/test_command_chain.vader
+++ b/test/test_command_chain.vader
@@ -1,6 +1,7 @@
Before:
Save &shell, g:ale_run_synchronously
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
if !has('win32')
set shell=/bin/sh
@@ -47,6 +48,7 @@ Before:
After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:first_echo_called
unlet! g:second_echo_called
unlet! g:final_callback_called
@@ -63,6 +65,7 @@ Given foobar (Some imaginary filetype):
Execute(Check the results of running the chain):
AssertEqual 'foobar', &filetype
call ale#Queue(0)
+ call ale#test#FlushJobs()
Assert g:first_echo_called, 'The first chain item was not called'
Assert g:second_echo_called, 'The second chain item was not called'
diff --git a/test/test_deferred_command_string.vader b/test/test_deferred_command_string.vader
new file mode 100644
index 00000000..026be6fe
--- /dev/null
+++ b/test/test_deferred_command_string.vader
@@ -0,0 +1,50 @@
+Before:
+ Save g:ale_run_synchronously
+ Save g:ale_emulate_job_failure
+ Save g:ale_buffer_info
+
+ let g:ale_run_synchronously = 1
+ let g:ale_buffer_info = {}
+ let b:ale_history = []
+
+ call ale#linter#Reset()
+ call ale#assert#SetUpLinterTestCommands()
+ call ale#linter#Define('foobar', {
+ \ 'name': 'lint_file_linter',
+ \ 'callback': 'LintFileCallback',
+ \ 'executable': 'echo',
+ \ 'command': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 'foo'})})},
+ \ 'read_buffer': 0,
+ \})
+
+ " Run the test commands in the shell.
+ let g:ale_run_synchronously_emulate_commands = 0
+
+After:
+ Restore
+
+ call ale#assert#TearDownLinterTest()
+ unlet! g:ale_run_synchronously_callbacks
+
+Given foobar (Some imaginary filetype):
+Execute(It should be possible to compute an executable to check based on the result of commands):
+ AssertLinter 'echo', 'foo'
+
+ ALELint
+ call ale#test#FlushJobs()
+
+ AssertEqual
+ \ 1,
+ \ len(filter(copy(b:ale_history), 'string(v:val.command) =~# ''foo'''))
+
+Execute(It handle the deferred command failing):
+ let g:ale_emulate_job_failure = 1
+
+ AssertLinter 'echo', 0
+
+ ALELint
+ call ale#test#FlushJobs()
+
+ AssertEqual
+ \ 0,
+ \ len(filter(copy(b:ale_history), 'string(v:val.command) =~# ''foo'''))
diff --git a/test/test_deferred_executable_string.vader b/test/test_deferred_executable_string.vader
new file mode 100644
index 00000000..3bdc5251
--- /dev/null
+++ b/test/test_deferred_executable_string.vader
@@ -0,0 +1,46 @@
+Before:
+ Save g:ale_run_synchronously
+ Save g:ale_emulate_job_failure
+ Save g:ale_buffer_info
+
+ let g:ale_run_synchronously = 1
+ let g:ale_buffer_info = {}
+ let b:ale_history = []
+
+ call ale#linter#Reset()
+ call ale#assert#SetUpLinterTestCommands()
+ call ale#linter#Define('foobar', {
+ \ 'name': 'lint_file_linter',
+ \ 'callback': 'LintFileCallback',
+ \ 'executable': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 'foo'})})},
+ \ 'command': 'echo',
+ \ 'read_buffer': 0,
+ \})
+
+After:
+ Restore
+
+ call ale#assert#TearDownLinterTest()
+
+Given foobar (Some imaginary filetype):
+Execute(It should be possible to compute an executable to check based on the result of commands):
+ AssertLinter 'foo', 'echo'
+
+ ALELint
+ call ale#test#FlushJobs()
+
+ AssertEqual
+ \ [{'status': 0, 'job_id': 'executable', 'command': 'foo'}],
+ \ filter(copy(b:ale_history), 'v:val.job_id is# ''executable''')
+
+Execute(It handle the deferred command failing):
+ let g:ale_emulate_job_failure = 1
+
+ AssertLinter 0, 'echo'
+
+ ALELint
+ call ale#test#FlushJobs()
+
+ AssertEqual
+ \ [],
+ \ filter(copy(b:ale_history), 'v:val.job_id is# ''executable''')
diff --git a/test/test_engine_invocation.vader b/test/test_engine_invocation.vader
index c56895d3..af713953 100644
--- a/test/test_engine_invocation.vader
+++ b/test/test_engine_invocation.vader
@@ -34,7 +34,10 @@ Before:
\}
function! ProcessIndex(chain_index)
- return ale#engine#ProcessChain(347, g:linter, a:chain_index, [])
+ let [l:command, l:options] = ale#engine#ProcessChain(347, '', g:linter, a:chain_index, [])
+ let l:options.command = l:command
+
+ return l:options
endfunction
After:
@@ -64,40 +67,6 @@ Execute(Engine invocation should return the command for the fourth item correctl
AssertEqual 'fourth', g:result.command
AssertEqual 4, g:result.next_chain_index
-Execute(Engine invocation should return the command for a single callback correctly):
- unlet g:linter.command_chain
- let g:linter.command_callback = 'FirstChainFunction'
-
- let g:result = ProcessIndex(0)
-
- AssertEqual 'first', g:result.command
-
-Execute(Engine invocation should return the command for a command string correctly):
- unlet g:linter.command_chain
- let g:linter.command = 'foo bar'
-
- let g:result = ProcessIndex(0)
-
- AssertEqual 'foo bar', g:result.command
-
-Execute(Engine invocation should process read_buffer correctly for simple commands):
- unlet g:linter.command_chain
- let g:linter.command = 'foo bar'
- let g:linter.read_buffer = 0
-
- let g:result = ProcessIndex(0)
-
- AssertEqual 'foo bar', g:result.command
- AssertEqual 0, g:result.read_buffer
-
- let g:linter.command_callback = 'FirstChainFunction'
- unlet g:linter.command
-
- let g:result = ProcessIndex(0)
-
- AssertEqual 'first', g:result.command
- AssertEqual 0, g:result.read_buffer
-
Execute(Engine invocation should allow read_buffer to be enabled for a command in the middle of a chain):
let g:linter.command_chain[2].read_buffer = 1
diff --git a/test/test_engine_lsp_response_handling.vader b/test/test_engine_lsp_response_handling.vader
index 04f12ad6..84febe39 100644
--- a/test/test_engine_lsp_response_handling.vader
+++ b/test/test_engine_lsp_response_handling.vader
@@ -5,12 +5,15 @@ Before:
let g:ale_buffer_info = {}
unlet! g:ale_lsp_error_messages
+ unlet! b:ale_linters
call ale#test#SetDirectory('/testplugin/test')
After:
Restore
+ unlet! b:ale_linters
+
call ale#test#RestoreDirectory()
call ale#linter#Reset()
call ale#lsp_linter#ClearLSPData()
@@ -162,6 +165,47 @@ Execute(tsserver semantic error responses should be handled correctly):
\ ],
\ ale#test#GetLoclistWithoutModule()
+Execute(tsserver errors should mark tsserver no longer active):
+ let b:ale_linters = ['tsserver']
+ runtime ale_linters/typescript/tsserver.vim
+ call ale#test#SetFilename('filename.ts')
+ call ale#engine#InitBufferInfo(bufnr(''))
+
+ let g:ale_buffer_info[bufnr('')].active_linter_list = ale#linter#Get('typescript')
+ Assert !empty(g:ale_buffer_info[bufnr('')].active_linter_list)
+
+ call ale#lsp_linter#HandleLSPResponse(1, {
+ \ 'seq': 0,
+ \ 'type': 'event',
+ \ 'event': 'semanticDiag',
+ \ 'body': {
+ \ 'file': g:dir . '/filename.ts',
+ \ 'diagnostics':[],
+ \ },
+ \})
+
+ AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
+
+Execute(LSP errors should mark linters no longer active):
+ let b:ale_linters = ['pyls']
+ runtime ale_linters/python/pyls.vim
+ call ale#test#SetFilename('filename.py')
+ call ale#engine#InitBufferInfo(bufnr(''))
+ call ale#lsp_linter#SetLSPLinterMap({1: 'pyls'})
+
+ let g:ale_buffer_info[bufnr('')].active_linter_list = ale#linter#Get('python')
+ Assert !empty(g:ale_buffer_info[bufnr('')].active_linter_list)
+
+ call ale#lsp_linter#HandleLSPResponse(1, {
+ \ 'method': 'textDocument/publishDiagnostics',
+ \ 'params': {
+ \ 'uri': ale#path#ToURI(g:dir . '/filename.py'),
+ \ 'diagnostics': [],
+ \ },
+ \})
+
+ AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
+
Execute(LSP errors should be logged in the history):
call ale#lsp_linter#SetLSPLinterMap({'347': 'foobar'})
call ale#lsp_linter#HandleLSPResponse(347, {
diff --git a/test/test_errors_removed_after_filetype_changed.vader b/test/test_errors_removed_after_filetype_changed.vader
index 651a74f2..7ad97f94 100644
--- a/test/test_errors_removed_after_filetype_changed.vader
+++ b/test/test_errors_removed_after_filetype_changed.vader
@@ -3,12 +3,13 @@ Before:
Save g:ale_buffer_info
Save g:ale_echo_cursor
Save g:ale_run_synchronously
- Save g:ale_run_synchronously
Save g:ale_set_highlights
Save g:ale_set_loclist
Save g:ale_set_quickfix
Save g:ale_set_signs
+ let g:ale_buffer_info = {}
+
" Enable only the one feature we need.
let g:ale_set_signs = 0
let g:ale_set_quickfix = 0
@@ -17,6 +18,7 @@ Before:
let g:ale_echo_cursor = 0
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
call setloclist(0, [])
noautocmd let &filetype = 'foobar'
@@ -44,6 +46,8 @@ Before:
After:
Restore
+
+ unlet! g:ale_run_synchronously_callbacks
delfunction TestCallback
call ale#linter#Reset()
@@ -51,6 +55,7 @@ After:
Execute(Error should be removed when the filetype changes to something else we cannot check):
call ale#Queue(0)
+ call ale#test#FlushJobs()
sleep 1ms
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@@ -58,6 +63,7 @@ Execute(Error should be removed when the filetype changes to something else we c
noautocmd let &filetype = 'foobar2'
call ale#Queue(0)
+ call ale#test#FlushJobs()
sleep 1ms
" We should get some items from the second filetype.
@@ -66,6 +72,7 @@ Execute(Error should be removed when the filetype changes to something else we c
noautocmd let &filetype = 'xxx'
call ale#Queue(0)
+ call ale#test#FlushJobs()
sleep 1ms
AssertEqual 0, len(ale#test#GetLoclistWithoutModule())
diff --git a/test/test_eslint_executable_detection.vader b/test/test_eslint_executable_detection.vader
index c1438ed8..5599576e 100644
--- a/test/test_eslint_executable_detection.vader
+++ b/test/test_eslint_executable_detection.vader
@@ -6,7 +6,6 @@ Before:
runtime ale_linters/javascript/eslint.vim
After:
- let g:ale_has_override = {}
let g:ale_javascript_eslint_executable = 'eslint'
let g:ale_javascript_eslint_use_global = 0
@@ -54,11 +53,17 @@ Execute(eslint_d should be detected correctly):
Execute(eslint.js executables should be run with node on Windows):
call ale#test#SetFilename('eslint-test-files/react-app/subdir/testfile.js')
- let g:ale_has_override['win32'] = 1
" We have to execute the file with node.
- AssertEqual
- \ ale#Escape('node.exe') . ' '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
- \ . ' -f unix --stdin --stdin-filename %s',
- \ ale#handlers#eslint#GetCommand(bufnr(''))
+ if has('win32')
+ AssertEqual
+ \ ale#Escape('node.exe') . ' '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
+ \ . ' -f unix --stdin --stdin-filename %s',
+ \ ale#handlers#eslint#GetCommand(bufnr(''))
+ else
+ AssertEqual
+ \ ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/node_modules/eslint/bin/eslint.js'))
+ \ . ' -f unix --stdin --stdin-filename %s',
+ \ ale#handlers#eslint#GetCommand(bufnr(''))
+ endif
diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader
index 4f190226..af028041 100644
--- a/test/test_filetype_linter_defaults.vader
+++ b/test/test_filetype_linter_defaults.vader
@@ -61,7 +61,7 @@ Execute(The defaults for the zsh filetype should be correct):
Execute(The defaults for the verilog filetype should be correct):
" This filetype isn't configured with default, so we can test loading all
" available linters with this.
- AssertEqual ['iverilog', 'verilator'], GetLinterNames('verilog')
+ AssertEqual ['iverilog', 'verilator', 'vlog', 'xvlog'], GetLinterNames('verilog')
let g:ale_linters_explicit = 1
diff --git a/test/test_find_references.vader b/test/test_find_references.vader
index 88b2d762..1a147849 100644
--- a/test/test_find_references.vader
+++ b/test/test_find_references.vader
@@ -8,30 +8,38 @@ Before:
let g:message_list = []
let g:preview_called = 0
let g:item_list = []
+ let g:options = {}
let g:capability_checked = ''
let g:conn_id = v:null
- let g:WaitCallback = v:null
+ let g:InitCallback = v:null
- runtime autoload/ale/linter.vim
+ runtime autoload/ale/lsp_linter.vim
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
runtime autoload/ale/preview.vim
- function! ale#lsp_linter#StartLSP(buffer, linter) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
- return {
+ if a:linter.lsp is# 'tsserver'
+ call ale#lsp#MarkConnectionAsTsserver(g:conn_id)
+ endif
+
+ let l:details = {
+ \ 'command': 'foobar',
\ 'buffer': a:buffer,
\ 'connection_id': g:conn_id,
\ 'project_root': '/foo/bar',
- \ 'language_id': 'python',
\}
+
+ let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)}
endfunction
- function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort
+ function! ale#lsp#HasCapability(conn_id, capability) abort
let g:capability_checked = a:capability
- let g:WaitCallback = a:callback
+
+ return 1
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
@@ -48,9 +56,10 @@ Before:
call add(g:expr_list, a:expr)
endfunction
- function! ale#preview#ShowSelection(item_list) abort
+ function! ale#preview#ShowSelection(item_list, options) abort
let g:preview_called = 1
let g:item_list = a:item_list
+ let g:options = a:options
endfunction
After:
@@ -63,13 +72,14 @@ After:
call ale#linter#Reset()
unlet! g:capability_checked
- unlet! g:WaitCallback
+ unlet! g:InitCallback
unlet! g:old_filename
unlet! g:conn_id
unlet! g:Callback
unlet! g:message_list
unlet! g:expr_list
unlet! b:ale_linters
+ unlet! g:options
unlet! g:item_list
unlet! g:preview_called
@@ -135,9 +145,9 @@ Execute(Results should be shown for tsserver responses):
AssertEqual
\ [
- \ {'filename': '/foo/bar/app.ts', 'column': 9, 'line': 9},
- \ {'filename': '/foo/bar/app.ts', 'column': 3, 'line': 804},
- \ {'filename': '/foo/bar/other/app.ts', 'column': 3, 'line': 51},
+ \ {'filename': '/foo/bar/app.ts', 'column': 9, 'line': 9, 'match': 'import {doSomething} from ''./whatever'''},
+ \ {'filename': '/foo/bar/app.ts', 'column': 3, 'line': 804, 'match': 'doSomething()'},
+ \ {'filename': '/foo/bar/other/app.ts', 'column': 3, 'line': 51, 'match': 'doSomething()'},
\ ],
\ g:item_list
AssertEqual {}, ale#references#GetMap()
@@ -162,6 +172,8 @@ Execute(The preview window should not be opened for empty tsserver responses):
AssertEqual ['echom ''No references found.'''], g:expr_list
Execute(tsserver reference requests should be sent):
+ call ale#linter#Reset()
+
runtime ale_linters/typescript/tsserver.vim
call setpos('.', [bufnr(''), 2, 5, 0])
@@ -170,17 +182,30 @@ Execute(tsserver reference requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'references', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'references', g:capability_checked
AssertEqual
\ 'function(''ale#references#HandleTSServerResponse'')',
\ string(g:Callback)
AssertEqual
- \ [[0, 'ts@references', {'file': expand('%:p'), 'line': 2, 'offset': 5}]],
+ \ [
+ \ ale#lsp#tsserver_message#Change(bufnr('')),
+ \ [0, 'ts@references', {'file': expand('%:p'), 'line': 2, 'offset': 5}]
+ \ ],
\ g:message_list
- AssertEqual {'42': {}}, ale#references#GetMap()
+ AssertEqual {'42': {'use_relative_paths': 0}}, ale#references#GetMap()
+
+Execute('-relative' argument should enable 'use_relative_paths' in HandleTSServerResponse):
+ runtime ale_linters/typescript/tsserver.vim
+ call setpos('.', [bufnr(''), 2, 5, 0])
+
+ ALEFindReferences -relative
+
+ call g:InitCallback()
+
+ AssertEqual {'42': {'use_relative_paths': 1}}, ale#references#GetMap()
Given python(Some Python file):
foo
@@ -228,14 +253,15 @@ Execute(LSP reference responses should be handled):
Execute(Preview windows should not be opened for empty LSP reference responses):
call ale#references#SetMap({3: {}})
- call ale#references#HandleLSPResponse(
- \ 1,
- \ {
- \ 'id': 3,
- \ 'result': [
- \ ],
- \ }
- \)
+ call ale#references#HandleLSPResponse(1, {'id': 3, 'result': []})
+
+ Assert !g:preview_called
+ AssertEqual {}, ale#references#GetMap()
+ AssertEqual ['echom ''No references found.'''], g:expr_list
+
+Execute(LSP reference responses with a null result should be handled):
+ call ale#references#SetMap({3: {}})
+ call ale#references#HandleLSPResponse(1, {'id': 3, 'result': v:null})
Assert !g:preview_called
AssertEqual {}, ale#references#GetMap()
@@ -251,10 +277,10 @@ Execute(LSP reference requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'references', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'references', g:capability_checked
AssertEqual
\ 'function(''ale#references#HandleLSPResponse'')',
\ string(g:Callback)
@@ -270,10 +296,21 @@ Execute(LSP reference requests should be sent):
\ }],
\ [0, 'textDocument/references', {
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
- \ 'position': {'line': 0, 'character': 3},
+ \ 'position': {'line': 0, 'character': 2},
\ 'context': {'includeDeclaration': v:false},
\ }],
\ ],
\ g:message_list
- AssertEqual {'42': {}}, ale#references#GetMap()
+ AssertEqual {'42': {'use_relative_paths': 0}}, ale#references#GetMap()
+
+Execute('-relative' argument should enable 'use_relative_paths' in HandleLSPResponse):
+ runtime ale_linters/python/pyls.vim
+ let b:ale_linters = ['pyls']
+ call setpos('.', [bufnr(''), 1, 5, 0])
+
+ ALEFindReferences -relative
+
+ call g:InitCallback()
+
+ AssertEqual {'42': {'use_relative_paths': 1}}, ale#references#GetMap()
diff --git a/test/test_flow_command.vader b/test/test_flow_command.vader
index c673ce0a..4805e121 100644
--- a/test/test_flow_command.vader
+++ b/test/test_flow_command.vader
@@ -1,45 +1,46 @@
Before:
runtime ale_linters/javascript/flow.vim
- call ale#test#SetDirectory('/testplugin/test')
+ call ale#assert#SetUpLinterTest('javascript', 'flow')
+ call ale#test#SetDirectory('/testplugin/test/')
After:
unlet! b:ale_javascript_flow_use_respect_pragma
-
- call ale#test#RestoreDirectory()
- call ale#linter#Reset()
- call ale#semver#ResetVersionCache()
+ call ale#assert#TearDownLinterTest()
Execute(flow should return a command to run if a .flowconfig file exists):
call ale#test#SetFilename('flow/a/sub/dummy')
- AssertEqual
+ AssertLinter 'flow',
\ ale#Escape('flow')
- \ . ' check-contents --respect-pragma --json --from ale %s',
- \ ale_linters#javascript#flow#GetCommand(bufnr('%'), [])
+ \ . ' check-contents --respect-pragma --json --from ale %s < %t'
+ \ . (!has('win32') ? '; echo' : '')
Execute(flow should not use the respect pragma argument if the option is off):
call ale#test#SetFilename('flow/a/sub/dummy')
let b:ale_javascript_flow_use_respect_pragma = 0
- AssertEqual
+ AssertLinter 'flow',
\ ale#Escape('flow')
- \ . ' check-contents --json --from ale %s',
- \ ale_linters#javascript#flow#GetCommand(bufnr('%'), [])
+ \ . ' check-contents --json --from ale %s < %t'
+ \ . (!has('win32') ? '; echo' : '')
Execute(flow should should not use --respect-pragma for old versions):
call ale#test#SetFilename('flow/a/sub/dummy')
- AssertEqual
+ GivenCommandOutput [
+ \ 'Warning: `flow --version` is deprecated in favor of `flow version`',
+ \ 'Flow, a static type checker for JavaScript, version 0.27.0',
+ \]
+ AssertLinter 'flow', [
+ \ ale#Escape('flow') . ' --version',
\ ale#Escape('flow')
- \ . ' check-contents --json --from ale %s',
- \ ale_linters#javascript#flow#GetCommand(bufnr('%'), [
- \ 'Warning: `flow --version` is deprecated in favor of `flow version`',
- \ 'Flow, a static type checker for JavaScript, version 0.27.0',
- \ ])
+ \ . ' check-contents --json --from ale %s < %t'
+ \ . (!has('win32') ? '; echo' : ''),
+ \]
Execute(flow should not return a command to run if no .flowconfig file exists):
call ale#test#SetFilename('flow/b/sub/dummy')
- AssertEqual '', ale_linters#javascript#flow#GetCommand(bufnr('%'), [])
+ AssertLinterNotExecuted
diff --git a/test/test_format_command.vader b/test/test_format_command.vader
index 71285efa..15435326 100644
--- a/test/test_format_command.vader
+++ b/test/test_format_command.vader
@@ -8,28 +8,41 @@ Before:
AssertEqual 'dummy.txt', fnamemodify(a:filename, ':t')
endfunction
+ runtime autoload/ale/command.vim
+
+ function! ale#command#CreateTempFile(buffer, temporary_file, input) abort
+ return !empty(a:temporary_file)
+ endfunction
+
After:
unlet! g:result
unlet! g:match
delfunction CheckTempFile
+ runtime autoload/ale/command.vim
+
Execute(FormatCommand should do nothing to basic command strings):
- AssertEqual ['', 'awesome-linter do something'], ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0)
+ AssertEqual
+ \ ['', 'awesome-linter do something', 0],
+ \ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null)
Execute(FormatCommand should handle %%, and ignore other percents):
- AssertEqual ['', '% %%d %%f %x %'], ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0)
+ AssertEqual
+ \ ['', '% %%d %%f %x %', 0],
+ \ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null)
Execute(FormatCommand should convert %s to the current filename):
AssertEqual
\ [
\ '',
- \ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p'))
+ \ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p')),
+ \ 0,
\ ],
- \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0)
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null)
Execute(FormatCommand should convert %t to a new temporary filename):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null)
call CheckTempFile(g:result[0])
@@ -42,8 +55,22 @@ Execute(FormatCommand should convert %t to a new temporary filename):
" The two temporary filenames formatted in should be the same.
AssertEqual g:match[1], g:match[2]
+Execute(FormatCommand should not convert %t to a new temporary filename when the input is given as v:false):
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false)
+
+ AssertEqual ['', 'foo %t bar %t', 0], g:result
+
+Execute(FormatCommand should signal that files are created when temporary files are needed):
+ AssertEqual
+ \ 1,
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null)[2]
+
+ AssertEqual
+ \ 0,
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null)[2]
+
Execute(FormatCommand should let you combine %s and %t):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null)
call CheckTempFile(g:result[0])
@@ -59,31 +86,31 @@ Execute(FormatCommand should let you combine %s and %t):
Execute(FormatCommand should replace %e with the escaped executable):
if has('win32')
AssertEqual
- \ ['', 'foo foo'],
- \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0)
+ \ ['', 'foo foo', 0],
+ \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
AssertEqual
- \ ['', '"foo bar"'],
- \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0)
+ \ ['', '"foo bar"', 0],
+ \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
AssertEqual
- \ ['', '%e %e'],
- \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0)
+ \ ['', '%e %e', 0],
+ \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
else
AssertEqual
- \ ['', '''foo'' ''foo'''],
- \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0)
+ \ ['', '''foo'' ''foo''', 0],
+ \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
AssertEqual
- \ ['', '''foo bar'''],
- \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0)
+ \ ['', '''foo bar''', 0],
+ \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
AssertEqual
- \ ['', '%e %e'],
- \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0)
+ \ ['', '%e %e', 0],
+ \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
endif
Execute(EscapeCommandPart should escape all percent signs):
AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%')
Execute(EscapeCommandPart should pipe in temporary files appropriately):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null)
call CheckTempFile(g:result[0])
@@ -91,7 +118,7 @@ Execute(EscapeCommandPart should pipe in temporary files appropriately):
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
AssertEqual ale#Escape(g:result[0]), g:match[1]
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null)
call CheckTempFile(g:result[0])
diff --git a/test/test_format_temporary_file_creation.vader b/test/test_format_temporary_file_creation.vader
index 385af908..10409400 100644
--- a/test/test_format_temporary_file_creation.vader
+++ b/test/test_format_temporary_file_creation.vader
@@ -13,6 +13,7 @@ Before:
let g:ale_echo_cursor = 0
let g:ale_enabled = 1
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
let g:ale_set_highlights = 0
let g:ale_set_loclist = 0
let g:ale_set_quickfix = 0
@@ -41,6 +42,7 @@ Before:
After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:output
delfunction TestCallback
@@ -56,5 +58,6 @@ Execute(ALE should be able to read the %t file):
AssertEqual 'foobar', &filetype
ALELint
+ call ale#test#FlushJobs()
AssertEqual ['foo', 'bar', 'baz'], g:output
diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader
index 66c24fb6..3479d7b5 100644
--- a/test/test_go_to_definition.vader
+++ b/test/test_go_to_definition.vader
@@ -8,27 +8,35 @@ Before:
let g:expr_list = []
let g:capability_checked = ''
let g:conn_id = v:null
- let g:WaitCallback = v:null
+ let g:InitCallback = v:null
runtime autoload/ale/linter.vim
+ runtime autoload/ale/lsp_linter.vim
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
- function! ale#lsp_linter#StartLSP(buffer, linter) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
- return {
+ if a:linter.lsp is# 'tsserver'
+ call ale#lsp#MarkConnectionAsTsserver(g:conn_id)
+ endif
+
+ let l:details = {
+ \ 'command': 'foobar',
\ 'buffer': a:buffer,
\ 'connection_id': g:conn_id,
\ 'project_root': '/foo/bar',
- \ 'language_id': 'python',
\}
+
+ let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)}
endfunction
- function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort
+ function! ale#lsp#HasCapability(conn_id, capability) abort
let g:capability_checked = a:capability
- let g:WaitCallback = a:callback
+
+ return 1
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
@@ -55,7 +63,7 @@ After:
call ale#linter#Reset()
unlet! g:capability_checked
- unlet! g:WaitCallback
+ unlet! g:InitCallback
unlet! g:old_filename
unlet! g:conn_id
unlet! g:Callback
@@ -70,8 +78,17 @@ After:
Execute(Other messages for the tsserver handler should be ignored):
call ale#definition#HandleTSServerResponse(1, {'command': 'foo'})
+Execute(Tagstack should be incremented if supported):
+ if exists('*gettagstack') && exists('*settagstack')
+ let original_stack_depth = gettagstack().length
+ endif
+ call ale#definition#UpdateTagStack()
+ if exists('*gettagstack') && exists('*settagstack')
+ AssertEqual original_stack_depth + 1, gettagstack().length
+ endif
+
Execute(Failed definition responses should be handled correctly):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleTSServerResponse(
\ 1,
\ {'command': 'definition', 'request_seq': 3}
@@ -79,7 +96,7 @@ Execute(Failed definition responses should be handled correctly):
AssertEqual {}, ale#definition#GetMap()
Execute(Failed definition responses with no files should be handled correctly):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleTSServerResponse(
\ 1,
\ {
@@ -97,7 +114,7 @@ Given typescript(Some typescript file):
bazxyzxyzxyz
Execute(Other files should be jumped to for definition responses):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleTSServerResponse(
\ 1,
\ {
@@ -122,7 +139,7 @@ Execute(Other files should be jumped to for definition responses):
AssertEqual {}, ale#definition#GetMap()
Execute(Other files should be jumped to for definition responses in tabs too):
- call ale#definition#SetMap({3: {'open_in_tab': 1}})
+ call ale#definition#SetMap({3: {'open_in': 'tab'}})
call ale#definition#HandleTSServerResponse(
\ 1,
\ {
@@ -146,7 +163,57 @@ Execute(Other files should be jumped to for definition responses in tabs too):
AssertEqual [3, 7], getpos('.')[1:2]
AssertEqual {}, ale#definition#GetMap()
-Execute(tsserver completion requests should be sent):
+Execute(Other files should be jumped to for definition responses in splits too):
+ call ale#definition#SetMap({3: {'open_in': 'horizontal-split'}})
+ call ale#definition#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'definition',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': [
+ \ {
+ \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'),
+ \ 'start': {'line': 3, 'offset': 7},
+ \ },
+ \ ],
+ \ }
+ \)
+
+ AssertEqual
+ \ [
+ \ 'split +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')),
+ \ ],
+ \ g:expr_list
+ AssertEqual [3, 7], getpos('.')[1:2]
+ AssertEqual {}, ale#definition#GetMap()
+
+Execute(Other files should be jumped to for definition responses in vsplits too):
+ call ale#definition#SetMap({3: {'open_in': 'vertical-split'}})
+ call ale#definition#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'definition',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': [
+ \ {
+ \ 'file': ale#path#Simplify(g:dir . '/completion_dummy_file'),
+ \ 'start': {'line': 3, 'offset': 7},
+ \ },
+ \ ],
+ \ }
+ \)
+
+ AssertEqual
+ \ [
+ \ 'vsplit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')),
+ \ ],
+ \ g:expr_list
+ AssertEqual [3, 7], getpos('.')[1:2]
+ AssertEqual {}, ale#definition#GetMap()
+
+Execute(tsserver definition requests should be sent):
runtime ale_linters/typescript/tsserver.vim
call setpos('.', [bufnr(''), 2, 5, 0])
@@ -155,19 +222,22 @@ Execute(tsserver completion requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'definition', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'definition', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleTSServerResponse'')',
\ string(g:Callback)
AssertEqual
- \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]],
+ \ [
+ \ ale#lsp#tsserver_message#Change(bufnr('')),
+ \ [0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]
+ \ ],
\ g:message_list
- AssertEqual {'42': {'open_in_tab': 0}}, ale#definition#GetMap()
+ AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
-Execute(tsserver tab completion requests should be sent):
+Execute(tsserver tab definition requests should be sent):
runtime ale_linters/typescript/tsserver.vim
call setpos('.', [bufnr(''), 2, 5, 0])
@@ -176,17 +246,20 @@ Execute(tsserver tab completion requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'definition', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'definition', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleTSServerResponse'')',
\ string(g:Callback)
AssertEqual
- \ [[0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]],
+ \ [
+ \ ale#lsp#tsserver_message#Change(bufnr('')),
+ \ [0, 'ts@definition', {'file': expand('%:p'), 'line': 2, 'offset': 5}]
+ \ ],
\ g:message_list
- AssertEqual {'42': {'open_in_tab': 1}}, ale#definition#GetMap()
+ AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()
Given python(Some Python file):
foo
@@ -194,7 +267,7 @@ Given python(Some Python file):
bazxyzxyzxyz
Execute(Other files should be jumped to for LSP definition responses):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(
\ 1,
\ {
@@ -217,7 +290,7 @@ Execute(Other files should be jumped to for LSP definition responses):
AssertEqual {}, ale#definition#GetMap()
Execute(Locations inside the same file should be jumped to without using :edit):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(
\ 1,
\ {
@@ -239,7 +312,7 @@ Execute(Locations inside the same file should be jumped to without using :edit):
AssertEqual {}, ale#definition#GetMap()
Execute(Other files should be jumped to in tabs for LSP definition responses):
- call ale#definition#SetMap({3: {'open_in_tab': 1}})
+ call ale#definition#SetMap({3: {'open_in': 'tab'}})
call ale#definition#HandleLSPResponse(
\ 1,
\ {
@@ -262,7 +335,7 @@ Execute(Other files should be jumped to in tabs for LSP definition responses):
AssertEqual {}, ale#definition#GetMap()
Execute(Definition responses with lists should be handled):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(
\ 1,
\ {
@@ -293,12 +366,12 @@ Execute(Definition responses with lists should be handled):
AssertEqual {}, ale#definition#GetMap()
Execute(Definition responses with null response should be handled):
- call ale#definition#SetMap({3: {'open_in_tab': 0}})
+ call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(1, {'id': 3, 'result': v:null})
AssertEqual [], g:expr_list
-Execute(LSP completion requests should be sent):
+Execute(LSP definition requests should be sent):
runtime ale_linters/python/pyls.vim
let b:ale_linters = ['pyls']
call setpos('.', [bufnr(''), 1, 5, 0])
@@ -308,10 +381,10 @@ Execute(LSP completion requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'definition', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'definition', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback)
@@ -327,14 +400,50 @@ Execute(LSP completion requests should be sent):
\ }],
\ [0, 'textDocument/definition', {
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
- \ 'position': {'line': 0, 'character': 3},
+ \ 'position': {'line': 0, 'character': 2},
+ \ }],
+ \ ],
+ \ g:message_list
+
+ AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
+
+Execute(LSP type definition requests should be sent):
+ runtime ale_linters/python/pyls.vim
+ let b:ale_linters = ['pyls']
+ call setpos('.', [bufnr(''), 1, 5, 0])
+
+ ALEGoToTypeDefinition
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'typeDefinition', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#definition#HandleLSPResponse'')',
+ \ string(g:Callback)
+
+ AssertEqual
+ \ [
+ \ [1, 'textDocument/didChange', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 1,
+ \ },
+ \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}]
+ \ }],
+ \ [0, 'textDocument/typeDefinition', {
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
+ \ 'position': {'line': 0, 'character': 2},
\ }],
\ ],
\ g:message_list
- AssertEqual {'42': {'open_in_tab': 0}}, ale#definition#GetMap()
+ AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
-Execute(LSP tab completion requests should be sent):
+Execute(LSP tab definition requests should be sent):
runtime ale_linters/python/pyls.vim
let b:ale_linters = ['pyls']
call setpos('.', [bufnr(''), 1, 5, 0])
@@ -344,10 +453,10 @@ Execute(LSP tab completion requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual type(function('type')), type(g:WaitCallback)
- AssertEqual 'definition', g:capability_checked
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'definition', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback)
@@ -363,9 +472,45 @@ Execute(LSP tab completion requests should be sent):
\ }],
\ [0, 'textDocument/definition', {
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
- \ 'position': {'line': 0, 'character': 3},
+ \ 'position': {'line': 0, 'character': 2},
+ \ }],
+ \ ],
+ \ g:message_list
+
+ AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()
+
+Execute(LSP tab type definition requests should be sent):
+ runtime ale_linters/python/pyls.vim
+ let b:ale_linters = ['pyls']
+ call setpos('.', [bufnr(''), 1, 5, 0])
+
+ ALEGoToTypeDefinitionInTab
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'typeDefinition', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#definition#HandleLSPResponse'')',
+ \ string(g:Callback)
+
+ AssertEqual
+ \ [
+ \ [1, 'textDocument/didChange', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 1,
+ \ },
+ \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}]
+ \ }],
+ \ [0, 'textDocument/typeDefinition', {
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
+ \ 'position': {'line': 0, 'character': 2},
\ }],
\ ],
\ g:message_list
- AssertEqual {'42': {'open_in_tab': 1}}, ale#definition#GetMap()
+ AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()
diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader
index 53dcea06..619a964e 100644
--- a/test/test_highlight_placement.vader
+++ b/test/test_highlight_placement.vader
@@ -9,6 +9,7 @@ Before:
Save g:ale_set_signs
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
let g:ale_set_highlights = 1
let g:ale_set_signs = 1
let g:ale_buffer_info = {}
@@ -64,6 +65,7 @@ Before:
After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! g:items
unlet! b:ale_enabled
@@ -81,6 +83,7 @@ Given testft(A Javscript file with warnings/errors):
Execute(Highlights should be set when a linter runs):
ALELint
+ call ale#test#FlushJobs()
AssertEqual
\ [
diff --git a/test/test_history_saving.vader b/test/test_history_saving.vader
index d7a307b5..18b64db5 100644
--- a/test/test_history_saving.vader
+++ b/test/test_history_saving.vader
@@ -2,6 +2,10 @@ Before:
Save g:ale_max_buffer_history_size
Save g:ale_history_log_output
Save g:ale_run_synchronously
+ Save g:ale_enabled
+
+ let g:ale_enabled = 1
+ let g:ale_run_synchronously = 1
unlet! b:ale_fixers
unlet! b:ale_enabled
@@ -68,27 +72,19 @@ Given foobar (Some imaginary filetype):
Execute(History should be set when commands are run):
AssertEqual 'foobar', &filetype
- let g:expected_results = ['command', 'exit_code', 'job_id', 'status']
-
- " Retry this test until it works. This one can randomly fail.
- for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
- let b:ale_history = []
- call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ let b:ale_history = []
+ ALELint
+ call ale#test#FlushJobs()
- let g:history = filter(
- \ copy(ale#history#Get(bufnr(''))),
- \ 'v:val.job_id isnot# ''executable''',
- \)
+ let g:history = filter(
+ \ copy(ale#history#Get(bufnr(''))),
+ \ 'v:val.job_id isnot# ''executable''',
+ \)
- AssertEqual 1, len(g:history)
-
- if sort(keys(g:history[0])) == g:expected_results
- break
- endif
- endfor
-
- AssertEqual g:expected_results, sort(keys(g:history[0]))
+ AssertEqual 1, len(g:history)
+ AssertEqual
+ \ ['command', 'exit_code', 'job_id', 'status'],
+ \ sort(keys(g:history[0]))
if has('win32')
AssertEqual 'cmd /s/c "echo command history test"', g:history[0].command
@@ -106,8 +102,8 @@ Execute(History should be not set when disabled):
let g:ale_history_enabled = 0
- call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ ALELint
+ call ale#test#FlushJobs()
AssertEqual [], ale#history#Get(bufnr(''))
@@ -115,24 +111,21 @@ Execute(History should include command output if logging is enabled):
AssertEqual 'foobar', &filetype
let g:ale_history_log_output = 1
- let g:expected_results = ['command history test']
" Retry this test until it works. This one can randomly fail.
- for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
- let b:ale_history = []
- call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ let b:ale_history = []
+ ALELint
+ call ale#test#FlushJobs()
- let g:history = ale#history#Get(bufnr(''))
+ let g:history = ale#history#Get(bufnr(''))
- AssertEqual 1, len(g:history)
-
- if get(g:history[0], 'output', []) == g:expected_results
- break
- endif
- endfor
-
- AssertEqual g:expected_results, get(g:history[0], 'output', [])
+ AssertEqual 1, len(g:history)
+ AssertEqual
+ \ ['command history test'],
+ \ map(
+ \ copy(get(g:history[0], 'output', [])),
+ \ 'substitute(v:val, ''[\r ]*$'', '''', ''g'')'
+ \ )
Execute(History items should be popped after going over the max):
let b:ale_history = map(range(20), '{''status'': ''started'', ''job_id'': v:val, ''command'': ''foobar''}')
@@ -169,10 +162,13 @@ Execute(The history should be updated when fixers are run):
let b:ale_fixers = {'foobar': ['TestFixer']}
let b:ale_enabled = 0
- let g:ale_run_synchronously = 1
ALEFix
+ AssertEqual ['started'], map(copy(b:ale_history), 'v:val.status')
+
+ call ale#test#FlushJobs()
+
AssertEqual ['finished'], map(copy(b:ale_history), 'v:val.status')
if has('win32')
diff --git a/test/test_hover.vader b/test/test_hover.vader
index a18fc651..917694a2 100644
--- a/test/test_hover.vader
+++ b/test/test_hover.vader
@@ -15,6 +15,7 @@ Before:
let g:Callback = a:callback
return {
+ \ 'command': 'foobar',
\ 'connection_id': 347,
\ 'project_root': '/foo/bar',
\}
@@ -149,3 +150,23 @@ Execute(LSP hover response with lists of strings and marked strings should be ha
AssertEqual ["foo\nbar\n"], g:echo_list
AssertEqual {}, ale#hover#GetMap()
+
+Execute(tsserver responses for documentation requests should be handled):
+ call ale#hover#SetMap({3: {'show_documentation': 1}})
+
+ call ale#hover#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'quickinfo',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': {
+ \ 'documentation': 'foo is a very good method',
+ \ 'displayString': 'foo bar',
+ \ },
+ \ }
+ \)
+
+ " The preview window should show the text.
+ AssertEqual ['foo is a very good method'], ale#test#GetPreviewWindowText()
+ silent! pclose
diff --git a/test/test_ignoring_linters.vader b/test/test_ignoring_linters.vader
index 866f9e0d..2d9c67de 100644
--- a/test/test_ignoring_linters.vader
+++ b/test/test_ignoring_linters.vader
@@ -30,6 +30,7 @@ Execute(Exclude should ignore some invalid values):
\ {'name': 'linter3', 'aliases': []},
\ ],
\ 'foo',
+ \ 0,
\ )
AssertEqual
\ [
@@ -45,6 +46,7 @@ Execute(Exclude should ignore some invalid values):
\ {'name': 'linter3', 'aliases': []},
\ ],
\ 0,
+ \ 0,
\ )
AssertEqual
\ [
@@ -60,6 +62,7 @@ Execute(Exclude should ignore some invalid values):
\ {'name': 'linter3', 'aliases': []},
\ ],
\ v:null,
+ \ 0,
\ )
Execute(Exclude should handle Lists):
@@ -75,6 +78,7 @@ Execute(Exclude should handle Lists):
\ {'name': 'linter3', 'aliases': []},
\ ],
\ ['linter1', 'alias1'],
+ \ 0,
\ )
Execute(Exclude should handle Dictionaries):
@@ -90,11 +94,51 @@ Execute(Exclude should handle Dictionaries):
\ {'name': 'linter3', 'aliases': []},
\ ],
\ {'foo': ['linter1'], 'bar': ['alias1']},
+ \ 0,
+ \ )
+
+Execute(Exclude should filter LSP linters when g:ale_disable_lsp is set to 1):
+ let g:ale_disable_lsp = 1
+ AssertEqual
+ \ [
+ \ {'name': 'linter1', 'aliases': [], 'lsp': ''},
+ \ {'name': 'linter2', 'aliases': []},
+ \ ],
+ \ ale#engine#ignore#Exclude(
+ \ 'foo',
+ \ [
+ \ {'name': 'linter1', 'aliases': [], 'lsp': ''},
+ \ {'name': 'linter2', 'aliases': []},
+ \ {'name': 'linter3', 'aliases': [], 'lsp': 'stdio'},
+ \ ],
+ \ [],
+ \ 1,
+ \ )
+
+Execute(Exclude should filter LSP linters when b:ale_disable_lsp is set to 1):
+ let b:ale_disable_lsp = 1
+ AssertEqual
+ \ [
+ \ {'name': 'linter1', 'aliases': [], 'lsp': ''},
+ \ {'name': 'linter2', 'aliases': []},
+ \ ],
+ \ ale#engine#ignore#Exclude(
+ \ 'foo',
+ \ [
+ \ {'name': 'linter1', 'aliases': [], 'lsp': ''},
+ \ {'name': 'linter2', 'aliases': []},
+ \ {'name': 'linter3', 'aliases': [], 'lsp': 'stdio'},
+ \ ],
+ \ [],
+ \ 1,
\ )
Before:
Save g:ale_linters_ignore
Save g:ale_buffer_info
+ Save g:ale_disable_lsp
+
+ let g:ale_disable_lsp = 0
let g:linters = []
let g:loclist = []
@@ -127,6 +171,7 @@ After:
unlet! b:ale_linters_ignore
unlet! b:ale_quitting
unlet! b:ale_save_event_fired
+ unlet! b:ale_disable_lsp
unlet! g:linters
unlet! g:loclist
unlet! g:lsp_message
@@ -242,7 +287,7 @@ Execute(Buffer ignore lists should be applied for LSP linters):
\ 'lnum': 1,
\ 'col': 10,
\ 'type': 'E',
- \ 'end_col': 10,
+ \ 'end_col': 9,
\ 'end_lnum': 1,
\ 'text': 'x',
\ }
diff --git a/test/test_jsonlint_executable_detection.vader b/test/test_jsonlint_executable_detection.vader
new file mode 100644
index 00000000..de52f846
--- /dev/null
+++ b/test/test_jsonlint_executable_detection.vader
@@ -0,0 +1,45 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test')
+
+ runtime ale_linters/json/jsonlint.vim
+
+After:
+ let g:ale_json_jsonlint_executable = 'jsonlint'
+ let g:ale_json_jsonlint_use_global = 0
+
+ call ale#test#RestoreDirectory()
+ call ale#linter#Reset()
+
+Execute(local executable should be detected correctly):
+ call ale#test#SetFilename('jsonlint-test-files/app/src/app.json')
+
+ AssertEqual
+ \ ale#path#Simplify(g:dir . '/jsonlint-test-files/app/node_modules/.bin/jsonlint'),
+ \ ale_linters#json#jsonlint#GetExecutable(bufnr(''))
+
+Execute(recursively executable should be detected correctly):
+ call ale#test#SetFilename('jsonlint-test-files/app-without-jsonlint/src/app.json')
+
+ AssertEqual
+ \ ale#path#Simplify(g:dir . '/jsonlint-test-files/node_modules/jsonlint/lib/cli.js'),
+ \ ale_linters#json#jsonlint#GetExecutable(bufnr(''))
+
+Execute(use_global should override project executable):
+ let g:ale_json_jsonlint_use_global = 1
+
+ call ale#test#SetFilename('jsonlint-test-files/app/src/app.json')
+
+ AssertEqual
+ \ 'jsonlint',
+ \ ale_linters#json#jsonlint#GetExecutable(bufnr(''))
+
+Execute(manually defined should override default executable):
+ let g:ale_json_jsonlint_use_global = 1
+ let g:ale_json_jsonlint_executable = 'custom_jsonlint'
+
+ call ale#test#SetFilename('jsonlint-test-files/app/src/app.json')
+
+ AssertEqual
+ \ 'custom_jsonlint',
+ \ ale_linters#json#jsonlint#GetExecutable(bufnr(''))
+
diff --git a/test/test_lint_file_linters.vader b/test/test_lint_file_linters.vader
index f67fad44..d16f4aa1 100644
--- a/test/test_lint_file_linters.vader
+++ b/test/test_lint_file_linters.vader
@@ -8,6 +8,7 @@ Before:
let g:ale_buffer_info = {}
let g:ale_run_synchronously = 1
+ unlet! g:ale_run_synchronously_callbacks
let g:ale_set_lists_synchronously = 1
let b:ale_save_event_fired = 0
@@ -89,6 +90,7 @@ After:
Restore
+ unlet! g:ale_run_synchronously_callbacks
unlet! b:ale_save_event_fired
unlet! b:ale_enabled
unlet g:buffer_result
@@ -111,6 +113,7 @@ Given foobar (Some imaginary filetype):
Execute(Running linters without 'lint_file' should run only buffer linters):
call ale#Queue(0)
+ call ale#test#FlushJobs()
AssertEqual [
\ {
@@ -131,6 +134,7 @@ Execute(Running linters with 'lint_file' should run all linters):
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file')
+ call ale#test#FlushJobs()
AssertEqual [
\ {
@@ -163,6 +167,7 @@ Execute(Linter errors from files should be kept):
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file')
+ call ale#test#FlushJobs()
" Change the results for the buffer callback.
let g:buffer_result = [
@@ -175,6 +180,7 @@ Execute(Linter errors from files should be kept):
\]
call ale#Queue(0)
+ call ale#test#FlushJobs()
AssertEqual [
\ {
@@ -202,6 +208,7 @@ Execute(Linter errors from files should be kept when no other linters are run):
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file')
+ call ale#test#FlushJobs()
AssertEqual [
\ {
@@ -240,11 +247,13 @@ Execute(The Save event should respect the buffer number):
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#events#SaveEvent(bufnr('') + 1)
+ call ale#test#FlushJobs()
" We shouldn't get any prblems yet.
AssertEqual [], GetSimplerLoclist()
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
" We should get them now we used the right buffer number.
AssertEqual [
@@ -268,6 +277,7 @@ Execute(The Save event should set b:ale_save_event_fired to 1):
call ale#linter#Reset()
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
" This flag needs to be set so windows can be opened, etc.
AssertEqual 1, b:ale_save_event_fired
@@ -276,6 +286,7 @@ 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(''), [])
+ call ale#test#FlushJobs()
AssertEqual 0, b:ale_save_event_fired
@@ -289,15 +300,18 @@ Execute(lint_file linters should stay running after checking without them):
" The lint_file linter should still be running.
AssertEqual
\ ['lint_file_linter', 'buffer_linter'],
- \ g:ale_buffer_info[bufnr('')].active_linter_list
+ \ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
" We should have 1 job for each linter.
- AssertEqual 2, len(g:ale_buffer_info[bufnr('')].job_list)
+ AssertEqual
+ \ 2,
+ \ len(keys(get(get(ale#command#GetData(), bufnr(''), {}), 'jobs', {})))
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
Execute(The save event should not lint the buffer when ALE is disabled):
let g:ale_enabled = 0
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual [], GetSimplerLoclist()
AssertEqual 0, b:ale_save_event_fired
diff --git a/test/test_lint_on_enter_when_file_changed.vader b/test/test_lint_on_enter_when_file_changed.vader
index 67f43c17..88493005 100644
--- a/test/test_lint_on_enter_when_file_changed.vader
+++ b/test/test_lint_on_enter_when_file_changed.vader
@@ -50,6 +50,7 @@ Execute(The file changed event function should set b:ale_file_changed):
Execute(The file changed event function should lint the current buffer when it has changed):
set filetype=foobar
call ale#events#FileChangedEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual [{
\ 'bufnr': bufnr(''),
@@ -68,6 +69,7 @@ Execute(The buffer should be checked after entering it after the file has change
set filetype=foobar
call ale#events#ReadOrEnterEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual [{
\ 'bufnr': bufnr(''),
diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader
index d967761d..cd32ebc8 100644
--- a/test/test_linter_defintion_processing.vader
+++ b/test/test_linter_defintion_processing.vader
@@ -1,4 +1,10 @@
Before:
+ Save g:ale_lsp_root
+ Save b:ale_lsp_root
+
+ let g:ale_lsp_root = {}
+ unlet! b:ale_lsp_root
+
let g:linter = {}
After:
@@ -48,7 +54,7 @@ Execute (PreProcess should throw when executable is not a string):
\ 'executable': 123,
\ 'command': 'echo',
\})
- AssertEqual '`executable` must be a string if defined', g:vader_exception
+ AssertEqual '`executable` must be a String or Function if defined', g:vader_exception
Execute (PreProcess should throw when executable_callback is not a callback):
AssertThrows call ale#linter#PreProcess('testft', {
@@ -59,6 +65,14 @@ Execute (PreProcess should throw when executable_callback is not a callback):
\})
AssertEqual '`executable_callback` must be a callback if defined', g:vader_exception
+Execute (PreProcess should allow executable to be a callback):
+ call ale#linter#PreProcess('testft', {
+ \ 'name': 'foo',
+ \ 'callback': 'SomeFunction',
+ \ 'executable': function('type'),
+ \ 'command': 'echo',
+ \})
+
Execute (PreProcess should throw when there is no command):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
@@ -74,7 +88,15 @@ Execute (PreProcess should throw when command is not a string):
\ 'executable': 'echo',
\ 'command': [],
\})
- AssertEqual '`command` must be a string if defined', g:vader_exception
+ AssertEqual '`command` must be a String or Function if defined', g:vader_exception
+
+Execute (PreProcess should allow command to be a callback):
+ call ale#linter#PreProcess('testft', {
+ \ 'name': 'foo',
+ \ 'callback': 'SomeFunction',
+ \ 'executable': 'echo',
+ \ 'command': function('type'),
+ \})
Execute (PreProcess should throw when command_callback is not a callback):
AssertThrows call ale#linter#PreProcess('testft', {
@@ -445,6 +467,18 @@ Execute(PreProcess should complain about using language and language_callback to
AssertThrows call ale#linter#PreProcess('testft', g:linter)
AssertEqual 'Only one of `language` or `language_callback` should be set', g:vader_exception
+Execute(PreProcess should complain about invalid language values):
+ let g:linter = {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 0,
+ \ 'project_root_callback': 'x',
+ \}
+
+ AssertThrows call ale#linter#PreProcess('testft', g:linter)
+ AssertEqual '`language` must be a String or Funcref', g:vader_exception
+
Execute(PreProcess should use the filetype as the language string by default):
let g:linter = {
\ 'name': 'x',
@@ -455,6 +489,17 @@ Execute(PreProcess should use the filetype as the language string by default):
AssertEqual 'testft', ale#linter#PreProcess('testft', g:linter).language_callback(0)
+Execute(PreProcess should allow language to be set to a callback):
+ let g:linter = {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': {-> 'foo'},
+ \ 'project_root_callback': 'x',
+ \}
+
+ AssertEqual 'foo', ale#linter#PreProcess('testft', g:linter).language_callback(0)
+
Execute(PreProcess should require an address_callback for LSP socket configurations):
let g:linter = {
\ 'name': 'x',
@@ -462,7 +507,7 @@ Execute(PreProcess should require an address_callback for LSP socket configurati
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`address_callback` must be defined for getting the LSP address', g:vader_exception
+ AssertEqual '`address` or `address_callback` must be defined for getting the LSP address', g:vader_exception
Execute(PreProcess should complain about address_callback for non-LSP linters):
let g:linter = {
@@ -474,7 +519,112 @@ Execute(PreProcess should complain about address_callback for non-LSP linters):
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`address_callback` cannot be used when lsp != ''socket''', g:vader_exception
+ AssertEqual '`address` or `address_callback` cannot be used when lsp != ''socket''', g:vader_exception
+
+Execute(PreProcess accept valid address_callback values):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address_callback': {-> 'foo:123'},
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \})
+
+ AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
+
+Execute(PreProcess accept address as a String):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \})
+
+ AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
+
+Execute(PreProcess accept address as a Function):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': {-> 'foo:123'},
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \})
+
+ AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
+
+Execute(PreProcess should complain about invalid address values):
+ AssertThrows call ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 0,
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \})
+ AssertEqual '`address` must be a String or Function if defined', g:vader_exception
+
+Execute(PreProcess should accept allow the project root be set as a String):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root': '/foo/bar',
+ \})
+
+ AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
+
+Execute(PreProcess should accept allow the project root be set as a Function):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root': {-> '/foo/bar'},
+ \})
+
+ AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
+
+Execute(PreProcess should complain when the project_root valid is invalid):
+ AssertThrows call ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root': 0,
+ \})
+ AssertEqual '`project_root` must be a String or Function if defined', g:vader_exception
+
+Execute(PreProcess should accept project_root_callback as a String):
+ call ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'Foobar',
+ \})
+
+Execute(PreProcess should accept project_root_callback as a Function):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root_callback': {-> '/foo/bar'},
+ \})
+
+ AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
+
+Execute(PreProcess should complain when the project_root_callback valid is invalid):
+ AssertThrows call ale#linter#PreProcess('testft', {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address': 'foo:123',
+ \ 'language': 'x',
+ \ 'project_root_callback': 0,
+ \})
+ AssertEqual '`project_root_callback` must be a callback if defined', g:vader_exception
Execute(PreProcess should complain about using initialization_options and initialization_options_callback together):
let g:linter = {
@@ -501,6 +651,41 @@ Execute(PreProcess should throw when initialization_options_callback is not a ca
\})
AssertEqual '`initialization_options_callback` must be a callback if defined', g:vader_exception
+Execute(PreProcess should throw when initialization_options is not a Dictionary or callback):
+ AssertThrows call ale#linter#PreProcess('testft', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'initialization_options': 0,
+ \})
+ AssertEqual '`initialization_options` must be a String or Function if defined', g:vader_exception
+
+Execute(PreProcess should accept initialization_options as a Dictionary):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'initialization_options': {'foo': v:true},
+ \})
+
+ AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)
+
+Execute(PreProcess should accept initialization_options as a Funcref):
+ let g:linter = ale#linter#PreProcess('testft', {
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'initialization_options': {-> {'foo': v:true}},
+ \})
+
+ AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)
+
Execute(PreProcess should complain about using lsp_config and lsp_config_callback together):
let g:linter = {
\ 'name': 'x',
@@ -527,22 +712,30 @@ Execute(PreProcess should throw when lsp_config_callback is not a callback):
AssertEqual '`lsp_config_callback` must be a callback if defined', g:vader_exception
Execute(PreProcess should accept LSP configuration options via lsp_config):
- let g:ale_lsp_configuration = {
- \ 'foo': 'bar'
+ let g:linter = {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language_callback': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'lsp_config': {'foo': 'bar'},
\}
+ AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)
+
+Execute(PreProcess should accept LSP configuration options via lsp_config as a function):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
\ 'address_callback': 'X',
\ 'language_callback': 'x',
\ 'project_root_callback': 'x',
- \ 'lsp_config': g:ale_lsp_configuration,
+ \ 'lsp_config': {-> {'foo': 'bar'}},
\}
- AssertEqual {'foo': 'bar'}, ale#linter#PreProcess('testft', g:linter).lsp_config
+ AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)
-Execute(PreProcess should throw when lsp_config is not a Dictionary):
+Execute(PreProcess should throw when lsp_config is not a Dictionary or Function):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'lsp': 'socket',
@@ -551,4 +744,4 @@ Execute(PreProcess should throw when lsp_config is not a Dictionary):
\ 'project_root_callback': 'x',
\ 'lsp_config': 'x',
\})
- AssertEqual '`lsp_config` must be a Dictionary', g:vader_exception
+ AssertEqual '`lsp_config` must be a Dictionary or Function if defined', g:vader_exception
diff --git a/test/test_linter_retrieval.vader b/test/test_linter_retrieval.vader
index a1c34622..6f9b3db4 100644
--- a/test/test_linter_retrieval.vader
+++ b/test/test_linter_retrieval.vader
@@ -2,8 +2,8 @@ Before:
Save g:ale_linters
Save g:ale_linter_aliases
- let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout', 'read_buffer': 1, 'lint_file': 0, 'aliases': [], 'lsp': '', 'add_newline': 0}
- let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout', 'read_buffer': 0, 'lint_file': 1, 'aliases': [], 'lsp': '', 'add_newline': 0}
+ let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout', 'read_buffer': 1, 'lint_file': 0, 'aliases': [], 'lsp': ''}
+ let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout', 'read_buffer': 0, 'lint_file': 1, 'aliases': [], 'lsp': ''}
call ale#linter#Reset()
call ale#linter#PreventLoading('testft')
call ale#linter#PreventLoading('javascript')
@@ -160,7 +160,7 @@ Execute (Buffer-local overrides for aliases should be used):
Execute (Linters should be loaded from disk appropriately):
call ale#linter#Reset()
- AssertEqual [{'name': 'testlinter', 'output_stream': 'stdout', 'executable': 'testlinter', 'command': 'testlinter', 'callback': 'testCB', 'read_buffer': 1, 'lint_file': 0, 'aliases': [], 'lsp': '', 'add_newline': 0}], ale#linter#Get('testft')
+ AssertEqual [{'name': 'testlinter', 'output_stream': 'stdout', 'executable': 'testlinter', 'command': 'testlinter', 'callback': 'testCB', 'read_buffer': 1, 'lint_file': 0, 'aliases': [], 'lsp': ''}], ale#linter#Get('testft')
Execute (Linters for later filetypes should replace the former ones):
@@ -178,5 +178,5 @@ Execute (Linters for later filetypes should replace the former ones):
\})
AssertEqual [
- \ {'output_stream': 'stdout', 'lint_file': 0, 'read_buffer': 1, 'name': 'eslint', 'executable': 'x', 'lsp': '', 'aliases': [], 'command': 'x', 'callback': 'x', 'add_newline': 0}
+ \ {'output_stream': 'stdout', 'lint_file': 0, 'read_buffer': 1, 'name': 'eslint', 'executable': 'x', 'lsp': '', 'aliases': [], 'command': 'x', 'callback': 'x'}
\], ale#linter#Get('javascript.typescript')
diff --git a/test/test_linting_blacklist.vader b/test/test_linting_blacklist.vader
index 73190b7f..2bcc9576 100644
--- a/test/test_linting_blacklist.vader
+++ b/test/test_linting_blacklist.vader
@@ -11,6 +11,6 @@ Given unite (A Unite.vim file):
Execute(Running ALE on a blacklisted file shouldn't change anything):
call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
AssertEqual {}, g:ale_buffer_info
diff --git a/test/test_linting_updates_loclist.vader b/test/test_linting_updates_loclist.vader
index 921cdb08..8a162703 100644
--- a/test/test_linting_updates_loclist.vader
+++ b/test/test_linting_updates_loclist.vader
@@ -64,6 +64,7 @@ Given foobar (Some JavaScript with problems):
Execute(The loclist should be updated after linting is done):
ALELint
+ call ale#test#FlushJobs()
AssertEqual
\ [
diff --git a/test/test_loclist_jumping.vader b/test/test_loclist_jumping.vader
index da9a1f57..3b6f0688 100644
--- a/test/test_loclist_jumping.vader
+++ b/test/test_loclist_jumping.vader
@@ -5,23 +5,24 @@ Before:
\ {'type': 'E', 'bufnr': bufnr('') - 1, 'lnum': 3, 'col': 2},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 2},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 3},
- \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 1},
+ \ {'type': 'W', 'sub_type': 'style', 'bufnr': bufnr(''), 'lnum': 2, 'col': 1},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 2},
- \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3},
- \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6},
+ \ {'type': 'W', 'sub_type': 'style', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3},
+ \ {'type': 'W', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 700},
\ {'type': 'E', 'bufnr': bufnr('') + 1, 'lnum': 3, 'col': 2},
\ ],
\ },
\}
- function! TestJump(position, wrap, pos)
+ function! TestJump(position, wrap, filter, subtype_filter, pos)
call cursor(a:pos)
if type(a:position) == type(0)
call ale#loclist_jumping#JumpToIndex(a:position)
else
- call ale#loclist_jumping#Jump(a:position, a:wrap)
+ call ale#loclist_jumping#Jump(a:position, a:wrap, a:filter,
+ \ a:subtype_filter)
endif
return getcurpos()[1:2]
@@ -36,46 +37,61 @@ Given foobar (Some imaginary filetype):
12345678
Execute(loclist jumping should jump correctly when not wrapping):
- AssertEqual [2, 1], TestJump('before', 0, [2, 2])
- AssertEqual [1, 3], TestJump('before', 0, [2, 1])
- AssertEqual [2, 3], TestJump('after', 0, [2, 2])
- AssertEqual [2, 1], TestJump('after', 0, [1, 3])
- AssertEqual [2, 6], TestJump('after', 0, [2, 4])
- AssertEqual [2, 8], TestJump('after', 0, [2, 6])
+ AssertEqual [2, 1], TestJump('before', 0, 'any', 'any', [2, 2])
+ AssertEqual [1, 3], TestJump('before', 0, 'any', 'any', [2, 1])
+ AssertEqual [2, 3], TestJump('after', 0, 'any', 'any', [2, 2])
+ AssertEqual [2, 1], TestJump('after', 0, 'any', 'any', [1, 3])
+ AssertEqual [2, 6], TestJump('after', 0, 'any', 'any', [2, 4])
+ AssertEqual [2, 8], TestJump('after', 0, 'any', 'any', [2, 6])
Execute(loclist jumping should jump correctly when wrapping):
- AssertEqual [2, 1], TestJump('before', 1, [2, 2])
- AssertEqual [1, 3], TestJump('before', 1, [2, 1])
- AssertEqual [2, 3], TestJump('after', 1, [2, 2])
- AssertEqual [2, 1], TestJump('after', 1, [1, 3])
- AssertEqual [2, 6], TestJump('after', 1, [2, 4])
-
- AssertEqual [1, 2], TestJump('after', 1, [2, 8])
- AssertEqual [2, 8], TestJump('before', 1, [1, 2])
+ AssertEqual [2, 1], TestJump('before', 1, 'any', 'any', [2, 2])
+ AssertEqual [1, 3], TestJump('before', 1, 'any', 'any', [2, 1])
+ AssertEqual [2, 3], TestJump('after', 1, 'any', 'any', [2, 2])
+ AssertEqual [2, 1], TestJump('after', 1, 'any', 'any', [1, 3])
+ AssertEqual [2, 6], TestJump('after', 1, 'any', 'any', [2, 4])
+
+ AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [2, 8])
+ AssertEqual [2, 8], TestJump('before', 1, 'any', 'any', [1, 2])
+
+Execute(loclist jumping should jump correctly with warning filters):
+ AssertEqual [2, 1], TestJump('after', 0, 'W', 'any', [1, 2])
+ AssertEqual [2, 6], TestJump('after', 0, 'W', 'any', [2, 3])
+ AssertEqual [2, 1], TestJump('after', 1, 'W', 'any', [2, 6])
+
+Execute(loclist jumping should jump correctly with error filters):
+ AssertEqual [1, 2], TestJump('after', 1, 'E', 'any', [2, 700])
+ AssertEqual [2, 2], TestJump('before', 0, 'E', 'any', [2, 700])
+ AssertEqual [2, 2], TestJump('after', 1, 'E', 'any', [1, 3])
+
+Execute(loclist jumping should jump correctly with sub type filters):
+ AssertEqual [2, 3], TestJump('after', 0, 'any', 'style', [2, 1])
+ AssertEqual [2, 2], TestJump('after', 0, 'any', '', [1, 3])
+ AssertEqual [2, 1], TestJump('after', 1, 'any', 'style', [2, 6])
Execute(loclist jumping not jump when the loclist is empty):
let g:ale_buffer_info[bufnr('%')].loclist = []
- AssertEqual [1, 6], TestJump('before', 0, [1, 6])
- AssertEqual [1, 6], TestJump('before', 1, [1, 6])
- AssertEqual [1, 6], TestJump('after', 0, [1, 6])
- AssertEqual [1, 6], TestJump('after', 1, [1, 6])
+ AssertEqual [1, 6], TestJump('before', 0, 'any', 'any', [1, 6])
+ AssertEqual [1, 6], TestJump('before', 1, 'any', 'any', [1, 6])
+ AssertEqual [1, 6], TestJump('after', 0, 'any', 'any', [1, 6])
+ AssertEqual [1, 6], TestJump('after', 1, 'any', 'any', [1, 6])
Execute(We should be able to jump to the last item):
- AssertEqual [2, 8], TestJump(-1, 0, [1, 6])
+ AssertEqual [2, 8], TestJump(-1, 0, 'any', 'any', [1, 6])
Execute(We shouldn't move when jumping to the last item where there are none):
let g:ale_buffer_info[bufnr('%')].loclist = []
- AssertEqual [1, 6], TestJump(-1, 0, [1, 6])
+ AssertEqual [1, 6], TestJump(-1, 0, 'any', 'any', [1, 6])
Execute(We should be able to jump to the first item):
- AssertEqual [1, 2], TestJump(0, 0, [1, 6])
+ AssertEqual [1, 2], TestJump(0, 0, 'any', 'any', [1, 6])
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])
+ AssertEqual [1, 6], TestJump(0, 0, 'any', 'any', [1, 6])
Execute(We should be able to jump when the error line is blank):
" Add a blank line at the end.
@@ -84,7 +100,7 @@ Execute(We should be able to jump when the error line is blank):
call add(g:ale_buffer_info[bufnr('%')].loclist, {'type': 'E', '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])
+ AssertEqual [2, 8], TestJump('before', 0, 'any', 'any', [3, 1])
+ AssertEqual [2, 8], TestJump('before', 1, 'any', 'any', [3, 1])
+ AssertEqual [3, 1], TestJump('after', 0, 'any', 'any', [3, 1])
+ AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [3, 1])
diff --git a/test/test_no_linting_on_write_quit.vader b/test/test_no_linting_on_write_quit.vader
index 12ef38ed..bbd80d8f 100644
--- a/test/test_no_linting_on_write_quit.vader
+++ b/test/test_no_linting_on_write_quit.vader
@@ -58,6 +58,7 @@ Execute(No linting should be done on :wq or :x):
" First try just the SaveEvent, to be sure that we set errors in the test.
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@@ -65,6 +66,7 @@ Execute(No linting should be done on :wq or :x):
call setloclist(0, [])
call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule()
@@ -73,11 +75,13 @@ Execute(No linting should be for :w after :q fails):
let g:ale_fix_on_save = 0
call ale#events#QuitEvent(bufnr(''))
+ call ale#test#FlushJobs()
" Simulate 2 seconds passing.
let b:ale_quitting -= 1000
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@@ -86,6 +90,7 @@ Execute(No linting should be done on :wq or :x after fixing files):
let g:ale_fix_on_save = 1
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
@@ -93,6 +98,7 @@ Execute(No linting should be done on :wq or :x after fixing files):
call setloclist(0, [])
call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual [], ale#test#GetLoclistWithoutModule()
@@ -101,10 +107,12 @@ Execute(Linting should be done after :q fails and fixing files):
let g:ale_fix_on_save = 1
call ale#events#QuitEvent(bufnr(''))
+ call ale#test#FlushJobs()
" Simulate 2 seconds passing.
let b:ale_quitting -= 1000
call ale#events#SaveEvent(bufnr(''))
+ call ale#test#FlushJobs()
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
diff --git a/test/test_nvim_api_highlight.vader b/test/test_nvim_api_highlight.vader
new file mode 100644
index 00000000..829c3ad7
--- /dev/null
+++ b/test/test_nvim_api_highlight.vader
@@ -0,0 +1,160 @@
+Before:
+ Save g:ale_enabled
+ Save g:ale_set_signs
+
+ let g:nvim_buf_clear_namespace_calls = []
+ let g:nvim_buf_add_highlight_calls = []
+
+ call ale#test#SetDirectory('/testplugin/test/highlight')
+ call ale#test#SetFilename('dummy.txt')
+
+ runtime autoload/ale/highlight.vim
+
+ let g:ale_set_signs = 1
+ let g:ale_enabled = 1
+ let b:ale_nvim_highlight_id = 42
+ let b:ale_highlight_items = []
+
+ function! ale#highlight#nvim_buf_clear_namespace(...) abort
+ call add(g:nvim_buf_clear_namespace_calls, a:000)
+ endfunction
+
+ function! ale#highlight#nvim_buf_add_highlight(...) abort
+ call add(g:nvim_buf_add_highlight_calls, a:000)
+ return 42 " returns namespace id
+ endfunction
+
+After:
+ Restore
+
+ unlet! b:ale_enabled
+ unlet! b:ale_nvim_highlight_id
+ unlet! b:ale_highlight_items
+
+ unlet! g:nvim_buf_clear_namespace_calls
+ unlet! g:nvim_buf_add_highlight_calls
+
+ call ale#test#RestoreDirectory()
+ call ale#linter#Reset()
+
+ runtime autoload/ale/highlight.vim
+
+Given foobar (Some imaginary filetype):
+ <contents>
+
+Execute(Check usage of nvim_buf_clear_namespace):
+ if ale#highlight#HasNeovimApi()
+ call ale#highlight#SetHighlights(bufnr(''), [])
+
+ AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
+ AssertEqual
+ \ [[bufnr(''), 42, 0, -1]],
+ \ g:nvim_buf_clear_namespace_calls
+ endif
+
+Execute(Check usage of nvim_buf_add_highlight / single char):
+ if ale#highlight#HasNeovimApi()
+ call ale#highlight#SetHighlights(bufnr(''), [
+ \ {
+ \ 'bufnr': bufnr(''),
+ \ 'type': 'E',
+ \ 'lnum': 2,
+ \ 'col': 4,
+ \ }
+ \])
+
+ " Highlights are cleared on update
+ AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
+ AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
+
+ " Should highlight a single char by lnum, col
+ AssertEqual 1, len(g:nvim_buf_add_highlight_calls)
+ AssertEqual
+ \ [[bufnr(''), 42, 'ALEError', 1, 3, 4]],
+ \ g:nvim_buf_add_highlight_calls
+ endif
+
+Execute(Check usage of nvim_buf_add_highlight / single line span):
+ if ale#highlight#HasNeovimApi()
+ call ale#highlight#SetHighlights(bufnr(''), [
+ \ {
+ \ 'bufnr': bufnr(''),
+ \ 'type': 'E',
+ \ 'lnum': 2,
+ \ 'col': 4,
+ \ 'end_lnum': 2,
+ \ 'end_col': 10,
+ \ }
+ \])
+
+ " Highlights are cleared on update
+ AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
+ AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
+
+ " Should highlight a span between col and end_col on lnum
+ AssertEqual 1, len(g:nvim_buf_add_highlight_calls)
+ AssertEqual
+ \ [[bufnr(''), 42, 'ALEError', 1, 3, 10]],
+ \ g:nvim_buf_add_highlight_calls
+ endif
+
+Execute(Check usage of nvim_buf_add_highlight / multiple lines span):
+ if ale#highlight#HasNeovimApi()
+ call ale#highlight#SetHighlights(bufnr(''), [
+ \ {
+ \ 'bufnr': bufnr(''),
+ \ 'type': 'E',
+ \ 'lnum': 2,
+ \ 'col': 4,
+ \ 'end_lnum': 5,
+ \ 'end_col': 10,
+ \ }
+ \])
+
+ " Highlights are cleared on update
+ AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
+ AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
+
+ " Should highlight all lines from lnum till end_lnum
+ AssertEqual 4, len(g:nvim_buf_add_highlight_calls)
+ AssertEqual
+ \ [
+ \ [bufnr(''), 42, 'ALEError', 1, 3, -1],
+ \ [bufnr(''), 42, 'ALEError', 2, 0, -1],
+ \ [bufnr(''), 42, 'ALEError', 3, 0, -1],
+ \ [bufnr(''), 42, 'ALEError', 4, 0, 10]
+ \ ],
+ \ g:nvim_buf_add_highlight_calls
+ endif
+
+Execute(Check usage of nvim_buf_add_highlight / line highights):
+ let g:ale_set_signs = 0
+
+ if ale#highlight#HasNeovimApi()
+ call ale#highlight#SetHighlights(bufnr(''), [
+ \ {
+ \ 'bufnr': bufnr(''),
+ \ 'type': 'E',
+ \ 'lnum': 2,
+ \ 'col': 4,
+ \ 'end_lnum': 5,
+ \ 'end_col': 10,
+ \ }
+ \])
+
+ " Highlights are cleared on update
+ AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
+ AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
+
+ " Now the last highlight should be put on the entire line
+ AssertEqual 5, len(g:nvim_buf_add_highlight_calls)
+ AssertEqual
+ \ [
+ \ [bufnr(''), 42, 'ALEError', 1, 3, -1],
+ \ [bufnr(''), 42, 'ALEError', 2, 0, -1],
+ \ [bufnr(''), 42, 'ALEError', 3, 0, -1],
+ \ [bufnr(''), 42, 'ALEError', 4, 0, 10],
+ \ [bufnr(''), 42, 'ALEErrorLine', 1, 0, -1]
+ \ ],
+ \ g:nvim_buf_add_highlight_calls
+ endif
diff --git a/test/test_parse_command_args.vader b/test/test_parse_command_args.vader
new file mode 100644
index 00000000..0103b967
--- /dev/null
+++ b/test/test_parse_command_args.vader
@@ -0,0 +1,52 @@
+After:
+ unlet! b:parse_result
+
+ if exists(':ParseTest')
+ delcommand ParseTest
+ endif
+
+Execute(ale#args#Parse should handle empty input):
+ AssertEqual
+ \ [{}, ''],
+ \ ale#args#Parse([], '')
+ AssertEqual
+ \ [{}, ''],
+ \ ale#args#Parse(['foo', 'bar'], '')
+
+Execute(ale#args#Parse should parse commands correctly):
+ AssertEqual
+ \ [{'foo': '', 'bar': ''}, 'leave these alone'],
+ \ ale#args#Parse(['foo', 'bar'], '-foo -bar leave these alone')
+ AssertEqual
+ \ [{'foo': ''}, 'leave these alone'],
+ \ ale#args#Parse(['foo', 'bar'], '-foo leave these alone')
+
+Execute(ale#args#Parse should raise errors for unknown arguments):
+ AssertThrows call ale#args#Parse(['foo', 'bar'], '-nope leave these alone')
+ AssertEqual 'Invalid argument: -nope', g:vader_exception
+
+Execute(ale#args#Parse should stop parsing arguments after --):
+ AssertEqual
+ \ [{'foo': ''}, ' --nope leave these alone'],
+ \ ale#args#Parse(['foo', 'bar'], '-foo -- --nope leave these alone')
+ AssertEqual
+ \ [{}, '--'],
+ \ ale#args#Parse(['foo', 'bar'], '-- --')
+ AssertEqual
+ \ [{}, ''],
+ \ ale#args#Parse(['foo', 'bar'], '--')
+
+Execute(ale#args#Parse should work for an example command):
+ command! -nargs=* ParseTest let b:parse_result = ale#args#Parse(['foo', 'bar'], <q-args>)
+
+ ParseTest
+ AssertEqual [{}, ''], b:parse_result
+
+ ParseTest -foo
+ AssertEqual [{'foo': ''}, ''], b:parse_result
+
+ ParseTest -foo -bar
+ AssertEqual [{'foo': '', 'bar': ''}, ''], b:parse_result
+
+ ParseTest -foo -bar leave these alone
+ AssertEqual [{'foo': '', 'bar': ''}, 'leave these alone'], b:parse_result
diff --git a/test/test_path_uri.vader b/test/test_path_uri.vader
index a3e68d98..cc2287cb 100644
--- a/test/test_path_uri.vader
+++ b/test/test_path_uri.vader
@@ -2,8 +2,53 @@ Execute(ale#path#ToURI should work for Windows paths):
AssertEqual 'file:///C:/foo/bar/baz.tst', ale#path#ToURI('C:\foo\bar\baz.tst')
AssertEqual 'foo/bar/baz.tst', ale#path#ToURI('foo\bar\baz.tst')
+Execute(ale#path#FromURI should work for Unix paths):
+ AssertEqual '/foo/bar/baz.tst', ale#path#FromURI('file:///foo/bar/baz.tst')
+ AssertEqual '/foo/bar/baz.tst', ale#path#FromURI('file:/foo/bar/baz.tst')
+ AssertEqual '/foo/bar/baz.tst', ale#path#FromURI('FILE:///foo/bar/baz.tst')
+ AssertEqual '/foo/bar/baz.tst', ale#path#FromURI('FILE:/foo/bar/baz.tst')
+
Execute(ale#path#FromURI should work for Windows paths):
- AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:///C:/foo/bar/baz.tst')
+ if has('win32')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:///C:/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:/C:/foo/bar/baz.tst')
+ AssertEqual 'c:\foo\bar\baz.tst', ale#path#FromURI('file:///c:/foo/bar/baz.tst')
+ AssertEqual 'c:\foo\bar\baz.tst', ale#path#FromURI('file:/c:/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('FILE:///C:/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('FILE:/C:/foo/bar/baz.tst')
+ else
+ AssertEqual '/C:/foo/bar/baz.tst', ale#path#FromURI('file:///C:/foo/bar/baz.tst')
+ AssertEqual '/C:/foo/bar/baz.tst', ale#path#FromURI('file:/C:/foo/bar/baz.tst')
+ AssertEqual '/c:/foo/bar/baz.tst', ale#path#FromURI('file:///c:/foo/bar/baz.tst')
+ AssertEqual '/c:/foo/bar/baz.tst', ale#path#FromURI('file:/c:/foo/bar/baz.tst')
+ AssertEqual '/C:/foo/bar/baz.tst', ale#path#FromURI('FILE:///C:/foo/bar/baz.tst')
+ AssertEqual '/C:/foo/bar/baz.tst', ale#path#FromURI('FILE:/C:/foo/bar/baz.tst')
+ endif
+
+Execute(ale#path#FromURI parse Windows paths with a pipe):
+ if has('win32')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:///C|/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:/C|/foo/bar/baz.tst')
+ AssertEqual 'c:\foo\bar\baz.tst', ale#path#FromURI('file:///c|/foo/bar/baz.tst')
+ AssertEqual 'c:\foo\bar\baz.tst', ale#path#FromURI('file:/c|/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('FILE:///C|/foo/bar/baz.tst')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('FILE:/C|/foo/bar/baz.tst')
+ else
+ AssertEqual '/C|/foo/bar/baz.tst', ale#path#FromURI('file:///C|/foo/bar/baz.tst')
+ AssertEqual '/C|/foo/bar/baz.tst', ale#path#FromURI('file:/C|/foo/bar/baz.tst')
+ AssertEqual '/c|/foo/bar/baz.tst', ale#path#FromURI('file:///c|/foo/bar/baz.tst')
+ AssertEqual '/c|/foo/bar/baz.tst', ale#path#FromURI('file:/c|/foo/bar/baz.tst')
+ AssertEqual '/C|/foo/bar/baz.tst', ale#path#FromURI('FILE:///C|/foo/bar/baz.tst')
+ AssertEqual '/C|/foo/bar/baz.tst', ale#path#FromURI('FILE:/C|/foo/bar/baz.tst')
+ endif
+
+Execute(ale#path#FromURI should handle the colon for the drive letter being encoded):
+ " These URIs shouldn't be created, but we'll handle them anyway.
+ if has('win32')
+ AssertEqual 'C:\foo\bar\baz.tst', ale#path#FromURI('file:///C%3A/foo/bar/baz.tst')
+ else
+ AssertEqual '/C:/foo/bar/baz.tst', ale#path#FromURI('file:///C%3A/foo/bar/baz.tst')
+ endif
Execute(ale#path#ToURI should work for Unix paths):
AssertEqual 'file:///foo/bar/baz.tst', ale#path#ToURI('/foo/bar/baz.tst')
diff --git a/test/test_prepare_command.vader b/test/test_prepare_command.vader
index 75e4c0c6..6a71eaed 100644
--- a/test/test_prepare_command.vader
+++ b/test/test_prepare_command.vader
@@ -1,6 +1,11 @@
Before:
Save &shell
Save &shellcmdflag
+ Save g:ale_shell
+ Save g:ale_shell_arguments
+
+ unlet! g:ale_shell
+ unlet! g:ale_shell_arguments
After:
Restore
@@ -43,6 +48,7 @@ Execute(Other shells should be used when set):
if !has('win32')
let &shell = '/bin/bash'
let &shellcmdflag = '-c'
+ let g:ale_shell = &shell
AssertEqual ['/bin/bash', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar')
endif
@@ -54,3 +60,16 @@ Execute(cmd /s/c as a string should be used on Windows):
AssertEqual 'cmd /s/c "foobar"', ale#job#PrepareCommand(bufnr(''), 'foobar')
endif
+
+Execute(Setting ale_shell should cause ale#job#PrepareCommand to use set shell):
+ let g:ale_shell = '/foo/bar'
+
+ if has('win32')
+ AssertEqual ['/foo/bar', '/c', 'foobar'], ale#job#PrepareCommand(bufnr(''), "foobar")
+ else
+ AssertEqual ['/foo/bar', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), "foobar")
+ endif
+
+ let g:ale_shell_arguments = '-x'
+
+ AssertEqual ['/foo/bar', '-x', 'foobar'], ale#job#PrepareCommand(bufnr(''), "foobar")
diff --git a/test/test_python_traceback.vader b/test/test_python_traceback.vader
new file mode 100644
index 00000000..6a659986
--- /dev/null
+++ b/test/test_python_traceback.vader
@@ -0,0 +1,79 @@
+Execute(ale#python#HandleTraceback returns empty List for empty lines):
+ AssertEqual
+ \ [],
+ \ ale#python#HandleTraceback([], 10)
+
+Execute(ale#python#HandleTraceback returns traceback, when present):
+ AssertEqual
+ \ [{
+ \ 'lnum': 1,
+ \ 'text': 'Exception: Example error (See :ALEDetail)',
+ \ 'detail': join([
+ \ 'Traceback (most recent call last):',
+ \ ' File "./example.py", line 5, in <module>',
+ \ ' raise Exception(''Example message'')',
+ \ 'Exception: Example error',
+ \ ], "\n"),
+ \ }],
+ \ ale#python#HandleTraceback([
+ \ 'Traceback (most recent call last):',
+ \ ' File "./example.py", line 5, in <module>',
+ \ ' raise Exception(''Example message'')',
+ \ 'Exception: Example error',
+ \ ], 1)
+
+" SyntaxError has extra output lines about the source
+Execute(ale#python#HandleTraceback returns SyntaxError traceback):
+ AssertEqual
+ \ [{
+ \ 'lnum': 1,
+ \ 'text': 'SyntaxError: invalid syntax (See :ALEDetail)',
+ \ 'detail': join([
+ \ 'Traceback (most recent call last):',
+ \ ' File "<string>", line 1, in <module>',
+ \ ' File "example.py", line 5',
+ \ ' +',
+ \ ' ^',
+ \ 'SyntaxError: invalid syntax',
+ \ ], "\n"),
+ \ }],
+ \ ale#python#HandleTraceback([
+ \ 'Traceback (most recent call last):',
+ \ ' File "<string>", line 1, in <module>',
+ \ ' File "example.py", line 5',
+ \ ' +',
+ \ ' ^',
+ \ 'SyntaxError: invalid syntax',
+ \ ], 1)
+
+Execute(ale#python#HandleTraceback ignores traceback after line limit):
+ AssertEqual
+ \ [],
+ \ ale#python#HandleTraceback([
+ \ '',
+ \ 'Traceback (most recent call last):',
+ \ ' File "./example.py", line 5, in <module>',
+ \ ' raise Exception(''Example message'')',
+ \ 'Exception: Example error',
+ \ ], 1)
+
+Execute(ale#python#HandleTraceback doesn't include later lines in detail):
+ AssertEqual
+ \ [{
+ \ 'lnum': 1,
+ \ 'text': 'Exception: Example error (See :ALEDetail)',
+ \ 'detail': join([
+ \ 'Traceback (most recent call last):',
+ \ ' File "./example.py", line 5, in <module>',
+ \ ' raise Exception(''Example message'')',
+ \ 'Exception: Example error',
+ \ ], "\n"),
+ \ }],
+ \ ale#python#HandleTraceback([
+ \ 'Traceback (most recent call last):',
+ \ ' File "./example.py", line 5, in <module>',
+ \ ' raise Exception(''Example message'')',
+ \ 'Exception: Example error',
+ \ 'file:1:2: Style issue',
+ \ 'file:3:4: Non-style issue',
+ \ ], 1)
diff --git a/test/test_redundant_tsserver_rendering_avoided.vader b/test/test_redundant_tsserver_rendering_avoided.vader
index a292b014..6e0b2d30 100644
--- a/test/test_redundant_tsserver_rendering_avoided.vader
+++ b/test/test_redundant_tsserver_rendering_avoided.vader
@@ -42,7 +42,7 @@ Before:
runtime autoload/ale/engine.vim
- let g:ale_buffer_info = {bufnr(''): {'loclist': []}}
+ let g:ale_buffer_info = {bufnr(''): {'loclist': [], 'active_linter_list': []}}
let g:ale_handle_loclist_called = 0
function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort
diff --git a/test/test_sandbox_execution.vader b/test/test_sandbox_execution.vader
index 5a4974ba..cf994ce8 100644
--- a/test/test_sandbox_execution.vader
+++ b/test/test_sandbox_execution.vader
@@ -59,7 +59,7 @@ Execute(ALE shouldn't blow up if file cleanup happens in a sandbox):
\ 'temporary_file_list': ['/tmp/foo'],
\ 'temporary_directory_list': ['/tmp/bar'],
\}
- sandbox call ale#engine#RemoveManagedFiles(3)
+ sandbox call ale#command#RemoveManagedFiles(3)
AssertEqual ['/tmp/foo'], g:ale_buffer_info[3].temporary_file_list
AssertEqual ['/tmp/bar'], g:ale_buffer_info[3].temporary_directory_list
diff --git a/test/test_semver_utils.vader b/test/test_semver_utils.vader
index 30e9e818..b38feb06 100644
--- a/test/test_semver_utils.vader
+++ b/test/test_semver_utils.vader
@@ -1,27 +1,20 @@
After:
call ale#semver#ResetVersionCache()
-Execute(GetVersion should return the version from the lines of output):
+Execute(ParseVersion should return the version from the lines of output):
" We should be able to parse the semver string from flake8
- AssertEqual [3, 0, 4], ale#semver#GetVersion('dummy', [
+ AssertEqual [3, 0, 4], ale#semver#ParseVersion([
\ '3.0.4 (mccabe: 0.5.2, pyflakes: 1.2.3, pycodestyle: 2.0.0) CPython 2.7.12 on Linux',
\ '1.2.3',
\])
-Execute(GetVersion should return an empty list when no vesrion can be found):
- AssertEqual [], ale#semver#GetVersion('dummy', ['x'])
- AssertEqual [], ale#semver#GetVersion('dummy', [])
+Execute(ParseVersion should return an empty list when no vesrion can be found):
+ AssertEqual [], ale#semver#ParseVersion(['x'])
+ AssertEqual [], ale#semver#ParseVersion([])
-Execute(GetVersion should cache the version):
- AssertEqual [], ale#semver#GetVersion('dummy', [])
- AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', ['Version 3.4.7'])
- AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', [])
-
-Execute(HasVersion should return 1 when the version has been cached):
- call ale#semver#GetVersion('dummy', [])
- AssertEqual 0, ale#semver#HasVersion('dummy')
- call ale#semver#GetVersion('dummy', ['3.4.7'])
- AssertEqual 1, ale#semver#HasVersion('dummy')
+Execute(ParseVersion should tolerate missing patch numbers):
+ " This goes against the semver spec, but we handle it anyway.
+ AssertEqual [3, 4, 0], ale#semver#ParseVersion(['Version 3.4'])
Execute(GTE should compare triples correctly):
Assert ale#semver#GTE([3, 0, 4], [3, 0, 0])
diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader
index adb8d70d..88ee462d 100644
--- a/test/test_shell_detection.vader
+++ b/test/test_shell_detection.vader
@@ -52,11 +52,27 @@ Execute(zsh should be detected appropriately):
Given(A file with a csh hash bang and arguments):
#!/usr/bin/env csh -eu --foobar
-Execute(zsh should be detected appropriately):
+Execute(csh 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 ksh hashbang):
+ #!/bin/ksh
+
+Execute(/bin/ksh should be detected appropriately):
+ AssertEqual 'ksh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'ksh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'ksh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a ksh as an argument to env):
+ #!/usr/bin/env ksh
+
+Execute(ksh should be detected appropriately):
+ AssertEqual 'ksh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'ksh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'ksh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
Given(A file with a sh hash bang and arguments):
#!/usr/bin/env sh -eu --foobar
diff --git a/test/test_should_do_nothing_conditions.vader b/test/test_should_do_nothing_conditions.vader
index de2e2782..6dfed555 100644
--- a/test/test_should_do_nothing_conditions.vader
+++ b/test/test_should_do_nothing_conditions.vader
@@ -35,6 +35,10 @@ After:
unlet! b:funky_command_created
unlet! b:fake_mode
+ if &diff is 1
+ let &diff = 0
+ endif
+
runtime autoload/ale/util.vim
Given foobar(An empty file):
@@ -70,6 +74,11 @@ Execute(DoNothing should return 1 when an operator is pending):
AssertEqual 1, ale#ShouldDoNothing(bufnr(''))
+Execute(DoNothing should return 1 for diff buffers):
+ let &diff = 1
+
+ AssertEqual 1, ale#ShouldDoNothing(bufnr(''))
+
Execute(The DoNothing check should work if the ALE globals aren't defined):
unlet! g:ale_filetype_blacklist
unlet! g:ale_maximum_file_size
diff --git a/test/test_statusline.vader b/test/test_statusline.vader
index d928e7ee..f76cbfa9 100644
--- a/test/test_statusline.vader
+++ b/test/test_statusline.vader
@@ -28,25 +28,9 @@ Before:
return l:res
endfunction
-After:
- Restore
-
- delfunction Counts
-
-Execute (Count should be 0 when data is empty):
- 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})}}
- AssertEqual Counts({'error': 1, 'warning': 2}), ale#statusline#Count(44)
-
-Execute (The count should be correct after an update):
- let g:ale_buffer_info = {'44': {}}
- call ale#statusline#Update(44, [])
- AssertEqual Counts({}), ale#statusline#Count(44)
-
-Execute (Count should be match the loclist):
- let g:ale_buffer_info = {
+ " A test simplified loclist that will be used for some of the
+ " tests in this module.
+ let g:test_buffer_info = {
\ bufnr(''): {
\ 'loclist': [
\ {'bufnr': bufnr('') - 1, 'type': 'E'},
@@ -77,6 +61,61 @@ Execute (Count should be match the loclist):
\ ],
\ },
\}
+After:
+ Restore
+
+ delfunction Counts
+ unlet g:test_buffer_info
+
+Execute (Count should be 0 when data is empty):
+ AssertEqual Counts({}), ale#statusline#Count(bufnr(''))
+
+Execute (FirstProblem should be 0 when data is empty):
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'error')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'warning')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'style_error')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'style_warning')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'info')
+
+Execute (Count should read data from the cache):
+ let g:ale_buffer_info = {'44': {'count': Counts({'error': 1, 'warning': 2})}}
+ AssertEqual Counts({'error': 1, 'warning': 2}), ale#statusline#Count(44)
+
+Execute (FirstProblem should read data from the cache):
+ let g:ale_buffer_info =
+ \{"44":
+ \{'count': 0,
+ \'first_problems':
+ \{'error': {'lnum': 3},
+ \'warning': {'lnum': 44},
+ \'style_error': {'lnum': 22},
+ \'style_warning': {'lnum': 223},
+ \'info': {'lnum': 2}
+ \}
+ \}
+ \}
+ AssertEqual {'lnum': 3}, ale#statusline#FirstProblem(44, 'error')
+ AssertEqual {'lnum': 44}, ale#statusline#FirstProblem(44, 'warning')
+ AssertEqual {'lnum': 223}, ale#statusline#FirstProblem(44, 'style_warning')
+ AssertEqual {'lnum': 22}, ale#statusline#FirstProblem(44, 'style_error')
+ AssertEqual {'lnum': 2}, ale#statusline#FirstProblem(44, 'info')
+
+Execute (The count should be correct after an update):
+ let g:ale_buffer_info = {'44': {}}
+ call ale#statusline#Update(44, [])
+ AssertEqual Counts({}), ale#statusline#Count(44)
+
+Execute (FirstProblem should be correct after an update):
+ let g:ale_buffer_info = {'44': {}}
+ call ale#statusline#Update(44, [])
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'error')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'warning')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'style_error')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'style_warning')
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'info')
+
+Execute (Count should match the loclist):
+ let g:ale_buffer_info = g:test_buffer_info
AssertEqual {
\ 'error': 1,
\ 'style_error': 2,
@@ -88,8 +127,22 @@ Execute (Count should be match the loclist):
\ 'total': 15,
\}, ale#statusline#Count(bufnr(''))
+Execute (FirstProblem should pull the first matching value from the loclist):
+ let g:ale_buffer_info = g:test_buffer_info
+ AssertEqual {'bufnr': bufnr(''), 'type': 'E'}, ale#statusline#FirstProblem(bufnr(''), 'error')
+ AssertEqual {'bufnr': bufnr(''), 'type': 'W'}, ale#statusline#FirstProblem(bufnr(''), 'warning')
+ AssertEqual {'bufnr': bufnr(''), 'type': 'E', 'sub_type': 'style'}, ale#statusline#FirstProblem(bufnr(''), 'style_error')
+ AssertEqual {'bufnr': bufnr(''), 'type': 'W', 'sub_type': 'style'}, ale#statusline#FirstProblem(bufnr(''), 'style_warning')
+ AssertEqual {'bufnr': bufnr(''), 'type': 'I'}, ale#statusline#FirstProblem(bufnr(''), 'info')
+
Execute (Output should be empty for non-existent buffer):
+ let g:ale_buffer_info = g:test_buffer_info
AssertEqual Counts({}), ale#statusline#Count(9001)
+ AssertEqual {}, ale#statusline#FirstProblem(9001, 'error')
+ AssertEqual {}, ale#statusline#FirstProblem(9001, 'warning')
+ AssertEqual {}, ale#statusline#FirstProblem(9001, 'style_error')
+ AssertEqual {}, ale#statusline#FirstProblem(9001, 'style_warning')
+ AssertEqual {}, ale#statusline#FirstProblem(9001, 'info')
Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
unlet! g:ale_statusline_format
@@ -98,3 +151,7 @@ Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
Execute(ale#statusline#Count should return 0 counts when globals are undefined):
unlet! g:ale_statusline_format
AssertEqual Counts({}), ale#statusline#Count(1)
+
+Execute(FirstProblem should return an empty dict when globals are undefined):
+ unlet! g:ale_statusline_format
+ AssertEqual {}, ale#statusline#FirstProblem(bufnr(''), 'info')
diff --git a/test/test_swift_find_project_root.vader b/test/test_swift_find_project_root.vader
new file mode 100644
index 00000000..7cb1cc29
--- /dev/null
+++ b/test/test_swift_find_project_root.vader
@@ -0,0 +1,18 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test')
+
+After:
+ call ale#test#RestoreDirectory()
+
+Execute(Detect root of Swift project with Package.swift correctly):
+ call ale#test#SetFilename('swift-test-files/swift-package-project/src/folder/dummy.swift')
+ AssertEqual
+ \ ale#path#Simplify(g:dir . '/swift-test-files/swift-package-project'),
+ \ ale#swift#FindProjectRoot(bufnr(''))
+
+Execute(Detect no root in case of non-Package.swift project):
+ call ale#test#SetFilename('swift-test-files/non-swift-package-project/src/folder/dummy.swift')
+ AssertEqual
+ \ '',
+ \ ale#swift#FindProjectRoot(bufnr(''))
+
diff --git a/test/test_swiftlint_executable_detection.vader b/test/test_swiftlint_executable_detection.vader
index a8e14c84..dfd4930b 100644
--- a/test/test_swiftlint_executable_detection.vader
+++ b/test/test_swiftlint_executable_detection.vader
@@ -6,7 +6,6 @@ Before:
runtime ale_linters/swift/swiftlint.vim
After:
- let g:ale_has_override = {}
let g:ale_swift_swiftlint_executable = 'swiftlint'
let g:ale_swift_swiftlint_use_global = 0
diff --git a/test/test_symbol_search.vader b/test/test_symbol_search.vader
index d8b7a4a6..053a8b04 100644
--- a/test/test_symbol_search.vader
+++ b/test/test_symbol_search.vader
@@ -7,30 +7,33 @@ Before:
let g:message_list = []
let g:preview_called = 0
let g:item_list = []
+ let g:options = {}
let g:capability_checked = ''
let g:conn_id = v:null
- let g:WaitCallback = v:null
+ let g:InitCallback = v:null
runtime autoload/ale/lsp_linter.vim
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
runtime autoload/ale/preview.vim
- function! ale#lsp_linter#StartLSP(buffer, linter) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
-
- return {
+ let l:details = {
\ 'buffer': a:buffer,
\ 'connection_id': g:conn_id,
\ 'project_root': '/foo/bar',
\ 'language_id': 'python',
\}
+
+ let g:InitCallback = {-> a:Callback(a:linter, l:details)}
endfunction
- function! ale#lsp#WaitForCapability(conn_id, capability, callback) abort
+ function! ale#lsp#HasCapability(conn_id, capability) abort
let g:capability_checked = a:capability
- let g:WaitCallback = a:callback
+
+ return 1
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
@@ -47,9 +50,10 @@ Before:
call add(g:expr_list, a:expr)
endfunction
- function! ale#preview#ShowSelection(item_list) abort
+ function! ale#preview#ShowSelection(item_list, options) abort
let g:preview_called = 1
let g:item_list = a:item_list
+ let g:options = a:options
endfunction
After:
@@ -57,12 +61,13 @@ After:
call ale#linter#Reset()
unlet! g:capability_checked
- unlet! g:WaitCallback
+ unlet! g:InitCallback
unlet! g:conn_id
unlet! g:Callback
unlet! g:message_list
unlet! g:expr_list
unlet! b:ale_linters
+ unlet! g:options
unlet! g:item_list
unlet! g:preview_called
@@ -156,10 +161,10 @@ Execute(LSP symbol requests should be sent):
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
- AssertEqual 'symbol_search', g:capability_checked
- AssertEqual type(function('type')), type(g:WaitCallback)
- call call(g:WaitCallback, [g:conn_id, '/foo/bar'])
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+ AssertEqual 'symbol_search', g:capability_checked
AssertEqual
\ 'function(''ale#symbol#HandleLSPResponse'')',
\ string(g:Callback)
@@ -170,4 +175,15 @@ Execute(LSP symbol requests should be sent):
\ ],
\ g:message_list
- AssertEqual {'42': {'buffer': bufnr('')}}, ale#symbol#GetMap()
+ AssertEqual {'42': {'buffer': bufnr(''), 'use_relative_paths': 0}}, ale#symbol#GetMap()
+
+Execute('-relative' argument should enable 'use_relative_paths' in HandleLSPResponse):
+ runtime ale_linters/python/pyls.vim
+ let b:ale_linters = ['pyls']
+ call setpos('.', [bufnr(''), 1, 5, 0])
+
+ ALESymbolSearch -relative foo bar
+
+ call g:InitCallback()
+
+ AssertEqual {'42': {'buffer': bufnr(''), 'use_relative_paths': 1}}, ale#symbol#GetMap()
diff --git a/test/test_temporary_file_management.vader b/test/test_temporary_file_management.vader
index 4847706a..9fff1ace 100644
--- a/test/test_temporary_file_management.vader
+++ b/test/test_temporary_file_management.vader
@@ -13,21 +13,21 @@ Before:
" We are registering a temporary file, so we should delete it.
let g:filename = tempname()
call writefile(['foo'], g:filename)
- call ale#engine#ManageFile(a:buffer, g:filename)
+ call ale#command#ManageFile(a:buffer, g:filename)
" We are registering this directory appropriately, so we should delete
" the whole thing.
let g:directory = tempname()
call mkdir(g:directory)
call writefile(['foo'], g:directory . '/bar')
- call ale#engine#ManageDirectory(a:buffer, g:directory)
+ call ale#command#ManageDirectory(a:buffer, g:directory)
" We are registering this directory as temporary file, so we
" shouldn't delete it.
let g:preserved_directory = tempname()
call mkdir(g:preserved_directory)
call writefile(['foo'], g:preserved_directory . '/bar')
- call ale#engine#ManageFile(a:buffer, g:preserved_directory)
+ call ale#command#ManageFile(a:buffer, g:preserved_directory)
return g:command
endfunction
@@ -42,6 +42,7 @@ Before:
\ 'callback': 'TestCallback',
\ 'command_callback': 'TestCommandCallback',
\})
+ call ale#command#ClearData()
After:
Restore
@@ -58,6 +59,7 @@ After:
delfunction TestCommandCallback
delfunction TestCallback
call ale#linter#Reset()
+ call ale#command#ClearData()
Given foobar (Some imaginary filetype):
foo
@@ -68,7 +70,7 @@ Execute(ALE should delete managed files/directories appropriately after linting)
AssertEqual 'foobar', &filetype
call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ call ale#test#FlushJobs()
Assert !filereadable(g:filename), 'The temporary file was not deleted'
Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
@@ -80,7 +82,7 @@ Execute(ALE should delete managed files even if no command is run):
let g:command = ''
call ale#Queue(0)
- call ale#engine#WaitForJobs(2000)
+ call ale#test#WaitForJobs(2000)
Assert !filereadable(g:filename), 'The temporary file was not deleted'
Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
@@ -95,11 +97,11 @@ Execute(ALE should delete managed files when the buffer is removed):
Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
Assert isdirectory(g:preserved_directory), 'The tempoary directory was not kept'
-Execute(ALE should create and delete directories for ale#engine#CreateDirectory()):
+Execute(ALE should create and delete directories for ale#command#CreateDirectory()):
call ale#engine#InitBufferInfo(bufnr('%'))
- let b:dir = ale#engine#CreateDirectory(bufnr('%'))
- let b:dir2 = ale#engine#CreateDirectory(bufnr('%'))
+ let b:dir = ale#command#CreateDirectory(bufnr('%'))
+ let b:dir2 = ale#command#CreateDirectory(bufnr('%'))
Assert isdirectory(b:dir), 'The directory was not created'
@@ -117,16 +119,28 @@ Execute(ALE should create and delete directories for ale#engine#CreateDirectory(
Assert !isdirectory(b:dir), 'The directory was not deleted'
Assert !isdirectory(b:dir2), 'The second directory was not deleted'
-Execute(ale#engine#ManageFile should add the file even if the buffer info hasn't be set yet):
- let g:ale_buffer_info = {}
- call ale#engine#ManageFile(bufnr(''), '/foo/bar')
+Execute(ale#command#ManageFile should add the file even if the buffer info hasn't been set yet):
+ call ale#command#ManageFile(bufnr(''), '/foo/bar')
+
AssertEqual
- \ ['/foo/bar'],
- \ g:ale_buffer_info[bufnr('')].temporary_file_list
+ \ {
+ \ bufnr(''): {
+ \ 'jobs': {},
+ \ 'file_list': ['/foo/bar'],
+ \ 'directory_list': [],
+ \ },
+ \ },
+ \ ale#command#GetData()
+
+Execute(ale#command#ManageDirectory should add the directory even if the buffer info hasn't been set yet):
+ call ale#command#ManageDirectory(bufnr(''), '/foo/bar')
-Execute(ale#engine#ManageDirectory should add the directory even if the buffer info hasn't be set yet):
- let g:ale_buffer_info = {}
- call ale#engine#ManageDirectory(bufnr(''), '/foo/bar')
AssertEqual
- \ ['/foo/bar'],
- \ g:ale_buffer_info[bufnr('')].temporary_directory_list
+ \ {
+ \ bufnr(''): {
+ \ 'jobs': {},
+ \ 'file_list': [],
+ \ 'directory_list': ['/foo/bar'],
+ \ },
+ \ },
+ \ ale#command#GetData()
diff --git a/test/tex_files/testfile.tex b/test/tex_files/testfile.tex
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/tex_files/testfile.tex
diff --git a/test/vimrc b/test/vimrc
index 9548f861..018b8f67 100644
--- a/test/vimrc
+++ b/test/vimrc
@@ -35,3 +35,5 @@ set ttimeoutlen=0
execute 'set encoding=utf-8'
let g:mapleader=','
+
+let g:ale_ignore_2_4_warnings = 1