summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--README.md16
-rw-r--r--ale_linters/cloudformation/cfn_python_lint.vim35
-rw-r--r--ale_linters/cpp/cquery.vim34
-rw-r--r--ale_linters/gitcommit/gitlint.vim4
-rw-r--r--ale_linters/puppet/puppet.vim7
-rw-r--r--ale_linters/python/flake8.vim6
-rw-r--r--ale_linters/python/mypy.vim6
-rw-r--r--ale_linters/python/prospector.vim9
-rw-r--r--ale_linters/python/pycodestyle.vim8
-rw-r--r--ale_linters/python/pyflakes.vim8
-rw-r--r--ale_linters/python/pylint.vim8
-rw-r--r--ale_linters/python/pyls.vim6
-rw-r--r--ale_linters/r/lintr.vim2
-rw-r--r--ale_linters/rust/cargo.vim10
-rw-r--r--autoload/ale/completion.vim9
-rw-r--r--autoload/ale/definition.vim9
-rw-r--r--autoload/ale/engine.vim137
-rw-r--r--autoload/ale/fix/registry.vim10
-rw-r--r--autoload/ale/fixers/qmlfmt.vim11
-rw-r--r--autoload/ale/fixers/scalafmt.vim26
-rw-r--r--autoload/ale/hover.vim8
-rw-r--r--autoload/ale/linter.vim84
-rw-r--r--autoload/ale/lsp.vim97
-rw-r--r--autoload/ale/lsp/message.vim5
-rw-r--r--autoload/ale/lsp/reset.vim4
-rw-r--r--autoload/ale/lsp_linter.vim228
-rw-r--r--autoload/ale/pattern_options.vim3
-rw-r--r--autoload/ale/references.vim7
-rw-r--r--doc/ale-cloudformation.txt14
-rw-r--r--doc/ale-cpp.txt20
-rw-r--r--doc/ale-gitcommit.txt10
-rw-r--r--doc/ale-python.txt27
-rw-r--r--doc/ale-rust.txt20
-rw-r--r--doc/ale-scala.txt25
-rw-r--r--doc/ale.txt25
-rw-r--r--test/command_callback/scala_paths/dummy.scala0
-rw-r--r--test/command_callback/test_cargo_command_callbacks.vader38
-rw-r--r--test/command_callback/test_cpp_cquery_command_callbacks.vader48
-rw-r--r--test/command_callback/test_flake8_command_callback.vader8
-rw-r--r--test/command_callback/test_lintr_command_callback.vader6
-rw-r--r--test/command_callback/test_mypy_command_callback.vader9
-rw-r--r--test/command_callback/test_prospector_command_callback.vader23
-rw-r--r--test/command_callback/test_pycodestyle_command_callback.vader7
-rw-r--r--test/command_callback/test_pyflakes_command_callback.vader7
-rw-r--r--test/command_callback/test_pylint_command_callback.vader9
-rw-r--r--test/command_callback/test_pyls_command_callback.vader7
-rw-r--r--test/completion/test_lsp_completion_messages.vader9
-rw-r--r--test/completion/test_lsp_completion_parsing.vader20
-rw-r--r--test/fixers/test_qmlfmt_fixer_callback.vader12
-rw-r--r--test/fixers/test_scalafmt_fixer_callback.vader69
-rw-r--r--test/handler/test_cfn_python_lint_handler.vader33
-rw-r--r--test/handler/test_gitlint_handler.vader33
-rw-r--r--test/handler/test_puppet_handler.vader8
-rw-r--r--test/lsp/test_did_save_event.vader9
-rw-r--r--test/lsp/test_lsp_client_messages.vader4
-rw-r--r--test/test_engine_lsp_response_handling.vader18
-rw-r--r--test/test_find_references.vader9
-rw-r--r--test/test_go_to_definition.vader22
-rw-r--r--test/test_hover.vader2
-rw-r--r--test/test_linter_defintion_processing.vader25
-rw-r--r--test/test_pattern_options.vader11
62 files changed, 1097 insertions, 289 deletions
diff --git a/.travis.yml b/.travis.yml
index 24237322..f5389f74 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,6 @@
sudo: required
services:
- docker
-language: python
+language: generic
script: |
./run-tests
diff --git a/README.md b/README.md
index 54fdc2b2..c328e4a2 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,10 @@ features, including:
* Finding references (`:ALEFindReferences`)
* Hover information (`:ALEHover`)
+If you don't care about Language Server Protocol, ALE won't load any of the code
+for working with it unless needed. One of ALE's general missions is that you
+won't pay for the features that you don't use.
+
## Table of Contents
1. [Supported Languages and Tools](#supported-languages)
@@ -93,11 +97,12 @@ formatting.
| Bash | shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) |
-| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) |
+| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [cquery](https://github.com/cquery-project/cquery), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) |
| CUDA | [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) |
| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details, [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) !! see:`help ale-cs-mcsc` for details and configuration|
| Chef | [foodcritic](http://www.foodcritic.io/) |
| Clojure | [joker](https://github.com/candid82/joker) |
+| 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/) !! |
@@ -164,7 +169,7 @@ formatting.
| Rust | cargo !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.org/), [rustfmt](https://github.com/rust-lang-nursery/rustfmt) |
| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) |
| SCSS | [prettier](https://github.com/prettier/prettier), [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint) |
-| Scala | [fsc](https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/tools/fsc.html), [scalac](http://scala-lang.org), [scalastyle](http://www.scalastyle.org) |
+| Scala | [fsc](https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/tools/fsc.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) |
@@ -253,7 +258,7 @@ See `:help ale-fix` for complete information on how to fix files with ALE.
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 TypeSript.
+Protocol linters, or from `tsserver` for TypeScript.
```vim
" Enable completion where available.
@@ -749,8 +754,9 @@ ALE cannot easily detect which compiler flags to use.
Some tools and build configurations can generate
[compile_commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
-files. The `cppcheck`, `clangcheck` and `clangtidy` linters can read these
-files for automatically determining the appropriate compiler flags to use.
+files. The `cppcheck`, `clangcheck`, `clangtidy` and `cquery` linters can read
+these files for automatically determining the appropriate compiler flags to
+use.
For linting with compilers like `gcc` and `clang`, and with other tools, you
will need to tell ALE which compiler flags to use yourself. You can use
diff --git a/ale_linters/cloudformation/cfn_python_lint.vim b/ale_linters/cloudformation/cfn_python_lint.vim
new file mode 100644
index 00000000..25e86ca3
--- /dev/null
+++ b/ale_linters/cloudformation/cfn_python_lint.vim
@@ -0,0 +1,35 @@
+" Author: Yasuhiro Kiyota <yasuhiroki.duck@gmail.com>
+" Description: Support cfn-python-lint for AWS Cloudformation template file
+
+function! ale_linters#cloudformation#cfn_python_lint#Handle(buffer, lines) abort
+ " Matches patterns line the following:
+ "
+ " sample.template.yaml:96:7:96:15: [E3012] Property Resources/Sample/Properties/FromPort should be of type Integer
+ let l:pattern = '\v^(.*):(\d+):(\d+):(\d+):(\d+): \[([[:alnum:]]+)\] (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:code = l:match[6]
+
+ if ale#path#IsBufferPath(a:buffer, l:match[1])
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'end_lnum': l:match[4] + 0,
+ \ 'end_col': l:match[5] + 0,
+ \ 'text': l:match[7],
+ \ 'code': l:code,
+ \ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
+ \})
+ endif
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('cloudformation', {
+\ 'name': 'cloudformation',
+\ 'executable': 'cfn-lint',
+\ 'command': 'cfn-lint --template %t --format parseable',
+\ 'callback': 'ale_linters#cloudformation#cfn_python_lint#Handle',
+\})
diff --git a/ale_linters/cpp/cquery.vim b/ale_linters/cpp/cquery.vim
new file mode 100644
index 00000000..2fd77d46
--- /dev/null
+++ b/ale_linters/cpp/cquery.vim
@@ -0,0 +1,34 @@
+" Author: Ben Falconer <ben@falconers.me.uk>
+" Description: A language server for C++
+
+call ale#Set('cpp_cquery_executable', 'cquery')
+call ale#Set('cpp_cquery_cache_directory', expand('~/.cache/cquery'))
+
+function! ale_linters#cpp#cquery#GetProjectRoot(buffer) abort
+ let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
+
+ return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : ''
+endfunction
+
+function! ale_linters#cpp#cquery#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_cquery_executable')
+endfunction
+
+function! ale_linters#cpp#cquery#GetCommand(buffer) abort
+ let l:executable = ale_linters#cpp#cquery#GetExecutable(a:buffer)
+ return ale#Escape(l:executable)
+endfunction
+
+function! ale_linters#cpp#cquery#GetInitializationOptions(buffer) abort
+ return {'cacheDirectory': ale#Var(a:buffer, 'cpp_cquery_cache_directory')}
+endfunction
+
+call ale#linter#Define('cpp', {
+\ 'name': 'cquery',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#cpp#cquery#GetExecutable',
+\ 'command_callback': 'ale_linters#cpp#cquery#GetCommand',
+\ 'project_root_callback': 'ale_linters#cpp#cquery#GetProjectRoot',
+\ 'initialization_options_callback': 'ale_linters#cpp#cquery#GetInitializationOptions',
+\ 'language': 'cpp',
+\})
diff --git a/ale_linters/gitcommit/gitlint.vim b/ale_linters/gitcommit/gitlint.vim
index 0b4ca0fc..64731055 100644
--- a/ale_linters/gitcommit/gitlint.vim
+++ b/ale_linters/gitcommit/gitlint.vim
@@ -28,6 +28,10 @@ function! ale_linters#gitcommit#gitlint#Handle(buffer, lines) abort
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:code = l:match[2]
+ if l:code is# 'T2' && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ continue
+ endif
+
let l:item = {
\ 'lnum': l:match[1] + 0,
\ 'text': l:match[3],
diff --git a/ale_linters/puppet/puppet.vim b/ale_linters/puppet/puppet.vim
index 8beeb61e..4ca0dd55 100644
--- a/ale_linters/puppet/puppet.vim
+++ b/ale_linters/puppet/puppet.vim
@@ -4,14 +4,15 @@ function! ale_linters#puppet#puppet#Handle(buffer, lines) abort
" Matches patterns like the following:
" Error: Could not parse for environment production: Syntax error at ':' at /root/puppetcode/modules/nginx/manifests/init.pp:43:12
" Error: Could not parse for environment production: Syntax error at '='; expected '}' at /root/puppetcode/modules/pancakes/manifests/init.pp:5"
+ " Error: Could not parse for environment production: Syntax error at 'parameter1' (file: /tmp/modules/mariadb/manifests/slave.pp, line: 4, column: 5)
- let l:pattern = '^Error: .*: \(.\+\) at .\+\.pp:\(\d\+\):\=\(\d*\)'
+ let l:pattern = '^Error: .*: \(.\+\) \((file:\|at\) .\+\.pp\(, line: \|:\)\(\d\+\)\(, column: \|:\)\=\(\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,
+ \ 'lnum': l:match[4] + 0,
+ \ 'col': l:match[6] + 0,
\ 'text': l:match[1],
\})
endfor
diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim
index 7398b1dc..fdc4ac94 100644
--- a/ale_linters/python/flake8.vim
+++ b/ale_linters/python/flake8.vim
@@ -52,6 +52,10 @@ function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
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'
+ \ : ''
+
" 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])
@@ -61,7 +65,7 @@ function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
let l:options = ale#Var(a:buffer, 'python_flake8_options')
return l:cd_string
- \ . ale#Escape(l:executable)
+ \ . ale#Escape(l:executable) . l:exec_args
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --format=default'
\ . l:display_name_args . ' -'
diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim
index e8ceb6a3..b38ccdeb 100644
--- a/ale_linters/python/mypy.vim
+++ b/ale_linters/python/mypy.vim
@@ -23,10 +23,14 @@ function! ale_linters#python#mypy#GetCommand(buffer) abort
let l:dir = s:GetDir(a:buffer)
let l:executable = ale_linters#python#mypy#GetExecutable(a:buffer)
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run mypy'
+ \ : ''
+
" We have to always switch to an explicit directory for a command so
" we can know with certainty the base path for the 'filename' keys below.
return ale#path#CdString(l:dir)
- \ . ale#Escape(l:executable)
+ \ . ale#Escape(l:executable) . l:exec_args
\ . ' --show-column-numbers '
\ . ale#Var(a:buffer, 'python_mypy_options')
\ . ' --shadow-file %s %t %s'
diff --git a/ale_linters/python/prospector.vim b/ale_linters/python/prospector.vim
index b3d11aa8..eadfee47 100644
--- a/ale_linters/python/prospector.vim
+++ b/ale_linters/python/prospector.vim
@@ -14,7 +14,14 @@ function! ale_linters#python#prospector#GetExecutable(buffer) abort
endfunction
function! ale_linters#python#prospector#GetCommand(buffer) abort
- return ale#Escape(ale_linters#python#prospector#GetExecutable(a:buffer))
+ let l:executable = ale_linters#python#prospector#GetExecutable(a:buffer)
+
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run prospector'
+ \ : ''
+
+ return ale#Escape(l:executable)
+ \ . l:exec_args
\ . ' ' . ale#Var(a:buffer, 'python_prospector_options')
\ . ' --messages-only --absolute-paths --zero-exit --output-format json'
\ . ' %s'
diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim
index 9254f4ab..de96363f 100644
--- a/ale_linters/python/pycodestyle.vim
+++ b/ale_linters/python/pycodestyle.vim
@@ -10,7 +10,13 @@ function! ale_linters#python#pycodestyle#GetExecutable(buffer) abort
endfunction
function! ale_linters#python#pycodestyle#GetCommand(buffer) abort
- return ale#Escape(ale_linters#python#pycodestyle#GetExecutable(a:buffer))
+ let l:executable = ale_linters#python#pycodestyle#GetExecutable(a:buffer)
+
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run pycodestyle'
+ \ : ''
+
+ return ale#Escape(l:executable) . l:exec_args
\ . ' '
\ . ale#Var(a:buffer, 'python_pycodestyle_options')
\ . ' -'
diff --git a/ale_linters/python/pyflakes.vim b/ale_linters/python/pyflakes.vim
index 475c3a6d..86ff8773 100644
--- a/ale_linters/python/pyflakes.vim
+++ b/ale_linters/python/pyflakes.vim
@@ -11,7 +11,13 @@ endfunction
function! ale_linters#python#pyflakes#GetCommand(buffer) abort
let l:executable = ale_linters#python#pyflakes#GetExecutable(a:buffer)
- return ale#Escape(l:executable) . ' %t'
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run pyflakes'
+ \ : ''
+
+ return ale#Escape(l:executable)
+ \ . l:exec_args
+ \ . ' %t'
endfunction
function! ale_linters#python#pyflakes#Handle(buffer, lines) abort
diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim
index a6fc8ed4..9239f835 100644
--- a/ale_linters/python/pylint.vim
+++ b/ale_linters/python/pylint.vim
@@ -15,8 +15,14 @@ function! ale_linters#python#pylint#GetCommand(buffer) abort
\ ? ale#path#BufferCdString(a:buffer)
\ : ''
+ let l:executable = ale_linters#python#pylint#GetExecutable(a:buffer)
+
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run pylint'
+ \ : ''
+
return l:cd_string
- \ . ale#Escape(ale_linters#python#pylint#GetExecutable(a:buffer))
+ \ . ale#Escape(l:executable) . l:exec_args
\ . ' ' . ale#Var(a:buffer, 'python_pylint_options')
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n'
\ . ' %s'
diff --git a/ale_linters/python/pyls.vim b/ale_linters/python/pyls.vim
index 883b38f5..010cb31f 100644
--- a/ale_linters/python/pyls.vim
+++ b/ale_linters/python/pyls.vim
@@ -11,7 +11,11 @@ endfunction
function! ale_linters#python#pyls#GetCommand(buffer) abort
let l:executable = ale_linters#python#pyls#GetExecutable(a:buffer)
- return ale#Escape(l:executable)
+ let l:exec_args = l:executable =~? 'pipenv$'
+ \ ? ' run pyls'
+ \ : ''
+
+ return ale#Escape(l:executable) . l:exec_args
endfunction
call ale#linter#Define('python', {
diff --git a/ale_linters/r/lintr.vim b/ale_linters/r/lintr.vim
index 51e5c562..8f74c9b8 100644
--- a/ale_linters/r/lintr.vim
+++ b/ale_linters/r/lintr.vim
@@ -22,7 +22,7 @@ function! ale_linters#r#lintr#GetCommand(buffer) abort
\ . l:lint_cmd
return ale#path#BufferCdString(a:buffer)
- \ . 'Rscript -e '
+ \ . 'Rscript --vanilla -e '
\ . ale#Escape(l:cmd_string) . ' %t'
endfunction
diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim
index 09f41bbb..ef0c3bd1 100644
--- a/ale_linters/rust/cargo.vim
+++ b/ale_linters/rust/cargo.vim
@@ -4,6 +4,8 @@
call ale#Set('rust_cargo_use_check', 1)
call ale#Set('rust_cargo_check_all_targets', 0)
+call ale#Set('rust_cargo_check_examples', 0)
+call ale#Set('rust_cargo_check_tests', 0)
call ale#Set('rust_cargo_default_feature_behavior', 'default')
call ale#Set('rust_cargo_include_features', '')
@@ -31,6 +33,12 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
let l:use_all_targets = l:use_check
\ && ale#Var(a:buffer, 'rust_cargo_check_all_targets')
\ && ale#semver#GTE(l:version, [0, 22, 0])
+ let l:use_examples = l:use_check
+ \ && ale#Var(a:buffer, 'rust_cargo_check_examples')
+ \ && ale#semver#GTE(l: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])
let l:include_features = ale#Var(a:buffer, 'rust_cargo_include_features')
if !empty(l:include_features)
@@ -50,6 +58,8 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
return 'cargo '
\ . (l:use_check ? 'check' : 'build')
\ . (l:use_all_targets ? ' --all-targets' : '')
+ \ . (l:use_examples ? ' --examples' : '')
+ \ . (l:use_tests ? ' --tests' : '')
\ . ' --frozen --message-format=json -q'
\ . l:default_feature
\ . l:include_features
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index 6fa2619b..4823b00c 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -317,7 +317,7 @@ function! ale#completion#ParseLSPCompletions(response) abort
\ 'word': l:word,
\ 'kind': l:kind,
\ 'icase': 1,
- \ 'menu': l:item.detail,
+ \ 'menu': get(l:item, 'detail', ''),
\ 'info': get(l:item, 'documentation', ''),
\})
endfor
@@ -389,14 +389,13 @@ function! s:GetLSPCompletions(linter) abort
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
- let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
+ let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
- let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions(
@@ -408,7 +407,7 @@ function! s:GetLSPCompletions(linter) abort
else
" Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby.
- call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
+ call ale#lsp#NotifyForChanges(l:lsp_details)
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
@@ -424,7 +423,7 @@ function! s:GetLSPCompletions(linter) abort
\)
endif
- let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
+ let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
if l:request_id
let b:ale_completion_info.conn_id = l:id
diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim
index a3bfcd17..6c70b64c 100644
--- a/autoload/ale/definition.vim
+++ b/autoload/ale/definition.vim
@@ -22,7 +22,7 @@ function! ale#definition#HandleTSServerResponse(conn_id, response) abort
\&& has_key(s:go_to_definition_map, a:response.request_seq)
let l:options = remove(s:go_to_definition_map, a:response.request_seq)
- if get(a:response, 'success', v:false) is v:true
+ if get(a:response, 'success', v:false) is v:true && !empty(a:response.body)
let l:filename = a:response.body[0].file
let l:line = a:response.body[0].start.line
let l:column = a:response.body[0].start.offset
@@ -65,14 +65,13 @@ function! s:GoToLSPDefinition(linter, options) abort
\ ? function('ale#definition#HandleTSServerResponse')
\ : function('ale#definition#HandleLSPResponse')
- let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
+ let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
- let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Definition(
@@ -83,7 +82,7 @@ function! s:GoToLSPDefinition(linter, options) abort
else
" Send a message saying the buffer has changed first, or the
" definition position probably won't make sense.
- call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
+ call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))])
@@ -93,7 +92,7 @@ function! s:GoToLSPDefinition(linter, options) abort
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
endif
- let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
+ let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:go_to_definition_map[l:request_id] = {
\ 'open_in_tab': get(a:options, 'open_in_tab', 0),
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 10589869..563c37a2 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -14,11 +14,6 @@ if !has_key(s:, 'job_info_map')
let s:job_info_map = {}
endif
-" Associates LSP connection IDs with linter names.
-if !has_key(s:, 'lsp_linter_map')
- let s:lsp_linter_map = {}
-endif
-
if !has_key(s:, 'executable_cache_map')
let s:executable_cache_map = {}
endif
@@ -79,16 +74,6 @@ function! ale#engine#InitBufferInfo(buffer) abort
return 0
endfunction
-" Clear LSP linter data for the linting engine.
-function! ale#engine#ClearLSPData() abort
- let s:lsp_linter_map = {}
-endfunction
-
-" Just for tests.
-function! ale#engine#SetLSPLinterMap(replacement_map) abort
- let s:lsp_linter_map = a:replacement_map
-endfunction
-
" This function is documented and part of the public API.
"
" Return 1 if ALE is busy checking a given buffer
@@ -241,88 +226,6 @@ function! s:HandleExit(job_id, exit_code) abort
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist)
endfunction
-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)
-
- if l:buffer <= 0
- return
- endif
-
- let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
-
- call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
-endfunction
-
-function! s:HandleTSServerDiagnostics(response, error_type) abort
- let l:buffer = bufnr(a:response.body.file)
- let l:info = get(g:ale_buffer_info, l:buffer, {})
-
- if empty(l:info)
- return
- endif
-
- let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response)
-
- " tsserver sends syntax and semantic errors in separate messages, so we
- " have to collect the messages separately for each buffer and join them
- " back together again.
- if a:error_type is# 'syntax'
- let l:info.syntax_loclist = l:thislist
- else
- let l:info.semantic_loclist = l:thislist
- endif
-
- let l:loclist = get(l:info, 'semantic_loclist', [])
- \ + get(l:info, 'syntax_loclist', [])
-
- call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
-endfunction
-
-function! s:HandleLSPErrorMessage(linter_name, response) abort
- if !g:ale_history_enabled || !g:ale_history_log_output
- return
- endif
-
- if empty(a:linter_name)
- return
- endif
-
- let l:message = ale#lsp#response#GetErrorMessage(a:response)
-
- if empty(l:message)
- return
- endif
-
- " This global variable is set here so we don't load the debugging.vim file
- " until someone uses :ALEInfo.
- let g:ale_lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
-
- if !has_key(g:ale_lsp_error_messages, a:linter_name)
- let g:ale_lsp_error_messages[a:linter_name] = []
- endif
-
- call add(g:ale_lsp_error_messages[a:linter_name], l:message)
-endfunction
-
-function! ale#engine#HandleLSPResponse(conn_id, response) abort
- let l:method = get(a:response, 'method', '')
- let l:linter_name = get(s:lsp_linter_map, a:conn_id, '')
-
- if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
- call s:HandleLSPErrorMessage(l:linter_name, a:response)
- elseif l:method is# 'textDocument/publishDiagnostics'
- call s:HandleLSPDiagnostics(a:conn_id, a:response)
- elseif get(a:response, 'type', '') is# 'event'
- \&& get(a:response, 'event', '') is# 'semanticDiag'
- call s:HandleTSServerDiagnostics(a:response, 'semantic')
- elseif get(a:response, 'type', '') is# 'event'
- \&& get(a:response, 'event', '') is# 'syntaxDiag'
- call s:HandleTSServerDiagnostics(a:response, 'syntax')
- endif
-endfunction
-
function! ale#engine#SetResults(buffer, loclist) abort
let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer)
@@ -739,44 +642,6 @@ function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
let l:info.active_linter_list = l:new_active_linter_list
endfunction
-function! s:CheckWithLSP(buffer, linter) abort
- let l:info = g:ale_buffer_info[a:buffer]
- let l:lsp_details = ale#linter#StartLSP(
- \ a:buffer,
- \ a:linter,
- \ function('ale#engine#HandleLSPResponse'),
- \)
-
- if empty(l:lsp_details)
- return 0
- endif
-
- let l:id = l:lsp_details.connection_id
- let l:root = l:lsp_details.project_root
-
- " Remember the linter this connection is for.
- let s:lsp_linter_map[l:id] = a:linter.name
-
- let l:change_message = a:linter.lsp is# 'tsserver'
- \ ? ale#lsp#tsserver_message#Geterr(a:buffer)
- \ : ale#lsp#message#DidChange(a:buffer)
- let l:request_id = ale#lsp#Send(l:id, l:change_message, l:root)
-
- " If this was a file save event, also notify the server of that.
- if a:linter.lsp isnot# 'tsserver'
- \&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
- let l:save_message = ale#lsp#message#DidSave(a:buffer)
- let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root)
- endif
-
- if l:request_id != 0
- if index(l:info.active_linter_list, a:linter.name) < 0
- call add(l:info.active_linter_list, a:linter.name)
- endif
- endif
-
- return l:request_id != 0
-endfunction
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove
@@ -832,7 +697,7 @@ endfunction
" Returns 1 if the linter was successfully run.
function! s:RunLinter(buffer, linter) abort
if !empty(a:linter.lsp)
- return s:CheckWithLSP(a:buffer, a:linter)
+ return ale#lsp_linter#CheckWithLSP(a:buffer, a:linter)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index 17532b3b..7b55acfc 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -95,6 +95,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['ruby'],
\ 'description': 'Fix ruby files with rufo',
\ },
+\ 'scalafmt': {
+\ 'function': 'ale#fixers#scalafmt#Fix',
+\ 'suggested_filetypes': ['scala'],
+\ 'description': 'Fix Scala files using scalafmt',
+\ },
\ 'standard': {
\ 'function': 'ale#fixers#standard#Fix',
\ 'suggested_filetypes': ['javascript'],
@@ -195,6 +200,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['javascript'],
\ 'description': 'Fix JavaScript files using xo --fix.',
\ },
+\ 'qmlfmt': {
+\ 'function': 'ale#fixers#qmlfmt#Fix',
+\ 'suggested_filetypes': ['qml'],
+\ 'description': 'Fix QML files with qmlfmt.',
+\ },
\}
" Reset the function registry to the default entries.
diff --git a/autoload/ale/fixers/qmlfmt.vim b/autoload/ale/fixers/qmlfmt.vim
new file mode 100644
index 00000000..d750d1c4
--- /dev/null
+++ b/autoload/ale/fixers/qmlfmt.vim
@@ -0,0 +1,11 @@
+call ale#Set('qml_qmlfmt_executable', 'qmlfmt')
+
+function! ale#fixers#qmlfmt#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'qml_qmlfmt_executable')
+endfunction
+
+function! ale#fixers#qmlfmt#Fix(buffer) abort
+ return {
+ \ 'command': ale#Escape(ale#fixers#qmlfmt#GetExecutable(a:buffer)),
+ \}
+endfunction
diff --git a/autoload/ale/fixers/scalafmt.vim b/autoload/ale/fixers/scalafmt.vim
new file mode 100644
index 00000000..07d28275
--- /dev/null
+++ b/autoload/ale/fixers/scalafmt.vim
@@ -0,0 +1,26 @@
+" Author: Jeffrey Lau https://github.com/zoonfafer
+" Description: Integration of Scalafmt with ALE.
+
+call ale#Set('scala_scalafmt_executable', 'scalafmt')
+call ale#Set('scala_scalafmt_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('scala_scalafmt_options', '')
+
+function! ale#fixers#scalafmt#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'scala_scalafmt_executable')
+ let l:options = ale#Var(a:buffer, 'scala_scalafmt_options')
+ let l:exec_args = l:executable =~? 'ng$'
+ \ ? ' scalafmt'
+ \ : ''
+
+ return ale#Escape(l:executable) . l:exec_args
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . ' %t'
+
+endfunction
+
+function! ale#fixers#scalafmt#Fix(buffer) abort
+ return {
+ \ 'command': ale#fixers#scalafmt#GetCommand(a:buffer),
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim
index 3bf92488..6d131adc 100644
--- a/autoload/ale/hover.vim
+++ b/autoload/ale/hover.vim
@@ -97,14 +97,14 @@ function! s:ShowDetails(linter, buffer, line, column, opt) abort
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
- let l:lsp_details = ale#linter#StartLSP(a:buffer, a:linter, l:Callback)
+ let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
- let l:root = l:lsp_details.project_root
+ let l:language_id = l:lsp_details.language_id
if a:linter.lsp is# 'tsserver'
let l:column = a:column
@@ -117,14 +117,14 @@ function! s:ShowDetails(linter, buffer, line, column, opt) abort
else
" Send a message saying the buffer has changed first, or the
" hover position probably won't make sense.
- call ale#lsp#Send(l:id, ale#lsp#message#DidChange(a:buffer), l:root)
+ call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif
- let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
+ let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:hover_map[l:request_id] = {
\ 'buffer': a:buffer,
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index c791ba46..cc7be518 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -227,6 +227,21 @@ function! ale#linter#PreProcess(linter) abort
throw '`completion_filter` must be a callback'
endif
endif
+
+ if has_key(a:linter, 'initialization_options_callback')
+ if has_key(a:linter, 'initialization_options')
+ throw 'Only one of `initialization_options` or '
+ \ . '`initialization_options_callback` should be set'
+ endif
+
+ let l:obj.initialization_options_callback = a:linter.initialization_options_callback
+
+ if !s:IsCallback(l:obj.initialization_options_callback)
+ throw '`initialization_options_callback` must be a callback if defined'
+ endif
+ elseif has_key(a:linter, 'initialization_options')
+ let l:obj.initialization_options = a:linter.initialization_options
+ endif
endif
let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout')
@@ -436,72 +451,3 @@ function! ale#linter#GetAddress(buffer, linter) abort
\ ? ale#util#GetFunction(a:linter.address_callback)(a:buffer)
\ : a:linter.address
endfunction
-
-" Given a buffer, an LSP linter, and a callback to register for handling
-" messages, start up an LSP linter and get ready to receive errors or
-" completions.
-function! ale#linter#StartLSP(buffer, linter, callback) abort
- let l:command = ''
- let l:address = ''
- let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
-
- if empty(l:root) && a:linter.lsp isnot# 'tsserver'
- " If there's no project root, then we can't check files with LSP,
- " unless we are using tsserver, which doesn't use project roots.
- return {}
- endif
-
- if a:linter.lsp is# 'socket'
- let l:address = ale#linter#GetAddress(a:buffer, a:linter)
- let l:conn_id = ale#lsp#ConnectToAddress(
- \ l:address,
- \ l:root,
- \ a:callback,
- \)
- else
- let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
-
- if !executable(l:executable)
- return {}
- endif
-
- let l:command = ale#job#PrepareCommand(
- \ a:buffer,
- \ ale#linter#GetCommand(a:buffer, a:linter),
- \)
- let l:conn_id = ale#lsp#StartProgram(
- \ l:executable,
- \ l:command,
- \ l:root,
- \ a:callback,
- \)
- endif
-
- let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
-
- if !l:conn_id
- if g:ale_history_enabled && !empty(l:command)
- call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
- endif
-
- return {}
- endif
-
- if ale#lsp#OpenDocumentIfNeeded(l:conn_id, a:buffer, l:root, l:language_id)
- if g:ale_history_enabled && !empty(l:command)
- call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
- endif
- endif
-
- " The change message needs to be sent for tsserver before doing anything.
- if a:linter.lsp is# 'tsserver'
- call ale#lsp#Send(l:conn_id, ale#lsp#tsserver_message#Change(a:buffer))
- endif
-
- return {
- \ 'connection_id': l:conn_id,
- \ 'command': l:command,
- \ 'project_root': l:root,
- \ 'language_id': l:language_id,
- \}
-endfunction
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index 8db9348f..29759f66 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -6,18 +6,22 @@
let s:connections = []
let g:ale_lsp_next_message_id = 1
-function! s:NewConnection() abort
+" Exposed only so tests can get at it.
+" Do not call this function basically anywhere.
+function! ale#lsp#NewConnection(initialization_options) abort
" id: The job ID as a Number, or the server address as a string.
" data: The message data received so far.
" executable: An executable only set for program connections.
- " open_documents: A list of buffers we told the server we opened.
+ " open_documents: A Dictionary mapping buffers to b:changedtick, keeping
+ " track of when documents were opened, and when we last changed them.
" callback_list: A list of callbacks for handling LSP responses.
let l:conn = {
\ 'id': '',
\ 'data': '',
\ 'projects': {},
- \ 'open_documents': [],
+ \ 'open_documents': {},
\ 'callback_list': [],
+ \ 'initialization_options': a:initialization_options,
\}
call add(s:connections, l:conn)
@@ -25,6 +29,11 @@ function! s:NewConnection() abort
return l:conn
endfunction
+" Remove an LSP connection with a given ID. This is only for tests.
+function! ale#lsp#RemoveConnectionWithID(id) abort
+ call filter(s:connections, 'v:val.id isnot a:id')
+endfunction
+
function! s:FindConnection(key, value) abort
for l:conn in s:connections
if has_key(l:conn, a:key) && get(l:conn, a:key) == a:value
@@ -207,6 +216,11 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
endfunction
function! ale#lsp#HandleMessage(conn, message) abort
+ if type(a:message) != type('')
+ " Ignore messages that aren't strings.
+ return
+ endif
+
let a:conn.data .= a:message
" Parse the objects now if we can, and keep the remaining text.
@@ -266,7 +280,7 @@ endfunction
"
" The job ID will be returned for for the program if it ran, otherwise
" 0 will be returned.
-function! ale#lsp#StartProgram(executable, command, project_root, callback) abort
+function! ale#lsp#StartProgram(executable, command, project_root, callback, initialization_options) abort
if !executable(a:executable)
return 0
endif
@@ -274,7 +288,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback) abor
let l:conn = s:FindConnection('executable', a:executable)
" Get the current connection or a new one.
- let l:conn = !empty(l:conn) ? l:conn : s:NewConnection()
+ let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options)
let l:conn.executable = a:executable
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
@@ -300,10 +314,10 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback) abor
endfunction
" Connect to an address and set up a callback for handling responses.
-function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
+function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort
let l:conn = s:FindConnection('id', a:address)
" Get the current connection or a new one.
- let l:conn = !empty(l:conn) ? l:conn : s:NewConnection()
+ let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options)
if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) isnot# 'open'
let l:conn.channnel = ch_open(a:address, {
@@ -378,7 +392,7 @@ function! ale#lsp#Send(conn_id, message, ...) abort
" Only send the init message once.
if !l:project.init_request_id
let [l:init_id, l:init_data] = ale#lsp#CreateMessageData(
- \ ale#lsp#message#Initialize(l:project_root),
+ \ ale#lsp#message#Initialize(l:project_root, l:conn.initialization_options),
\)
let l:project.init_request_id = l:init_id
@@ -400,21 +414,72 @@ function! ale#lsp#Send(conn_id, message, ...) abort
return l:id == 0 ? -1 : l:id
endfunction
-function! ale#lsp#OpenDocumentIfNeeded(conn_id, buffer, project_root, language_id) abort
- let l:conn = s:FindConnection('id', a:conn_id)
+" The Document details Dictionary should contain the following keys.
+"
+" buffer - The buffer number for the document.
+" connection_id - The connection ID for the LSP server.
+" command - The command to run to start the LSP connection.
+" project_root - The project root for the LSP project.
+" language_id - The language ID for the project, like 'python', 'rust', etc.
+
+" Create a new Dictionary containing more connection details, with the
+" following information added:
+"
+" conn - An existing LSP connection for the document.
+" document_open - 1 if the document is currently open, 0 otherwise.
+function! s:ExtendDocumentDetails(details) abort
+ let l:extended = copy(a:details)
+ let l:conn = s:FindConnection('id', a:details.connection_id)
+
+ let l:extended.conn = l:conn
+ let l:extended.document_open = !empty(l:conn)
+ \ && has_key(l:conn.open_documents, a:details.buffer)
+
+ return l:extended
+endfunction
+
+" Notify LSP servers or tsserver if a document is opened, if needed.
+" If a document is opened, 1 will be returned, otherwise 0 will be returned.
+function! ale#lsp#OpenDocument(basic_details) abort
+ let l:d = s:ExtendDocumentDetails(a:basic_details)
let l:opened = 0
- if !empty(l:conn) && index(l:conn.open_documents, a:buffer) < 0
- if empty(a:language_id)
- let l:message = ale#lsp#tsserver_message#Open(a:buffer)
+ if !empty(l:d.conn) && !l:d.document_open
+ if empty(l:d.language_id)
+ let l:message = ale#lsp#tsserver_message#Open(l:d.buffer)
else
- let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id)
+ let l:message = ale#lsp#message#DidOpen(l:d.buffer, l:d.language_id)
endif
- call ale#lsp#Send(a:conn_id, l:message, a:project_root)
- call add(l:conn.open_documents, a:buffer)
+ call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
+ let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick')
let l:opened = 1
endif
return l:opened
endfunction
+
+" Notify LSP servers or tsserver that a document has changed, if needed.
+" If a notification is sent, 1 will be returned, otherwise 0 will be returned.
+function! ale#lsp#NotifyForChanges(basic_details) abort
+ let l:d = s:ExtendDocumentDetails(a:basic_details)
+ let l:notified = 0
+
+ if l:d.document_open
+ let l:new_tick = getbufvar(l:d.buffer, 'changedtick')
+
+ if l:d.conn.open_documents[l:d.buffer] < l:new_tick
+ if empty(l:d.language_id)
+ let l:message = ale#lsp#tsserver_message#Change(l:d.buffer)
+ else
+ let l:message = ale#lsp#message#DidChange(l:d.buffer)
+ endif
+
+ call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
+ let l:d.conn.open_documents[l:d.buffer] = l:new_tick
+ let l:notified = 1
+ endif
+ endif
+
+ return l:notified
+endfunction
diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim
index 5637fa2e..9e05156d 100644
--- a/autoload/ale/lsp/message.vim
+++ b/autoload/ale/lsp/message.vim
@@ -24,12 +24,15 @@ function! ale#lsp#message#GetNextVersionID() abort
return l:id
endfunction
-function! ale#lsp#message#Initialize(root_path) abort
+function! ale#lsp#message#Initialize(root_path, initialization_options) abort
" TODO: Define needed capabilities.
+ " NOTE: rootPath is deprecated in favour of rootUri
return [0, 'initialize', {
\ 'processId': getpid(),
\ 'rootPath': a:root_path,
\ 'capabilities': {},
+ \ 'initializationOptions': a:initialization_options,
+ \ 'rootUri': ale#path#ToURI(a:root_path),
\}]
endfunction
diff --git a/autoload/ale/lsp/reset.vim b/autoload/ale/lsp/reset.vim
index c206ed08..c7c97a47 100644
--- a/autoload/ale/lsp/reset.vim
+++ b/autoload/ale/lsp/reset.vim
@@ -7,9 +7,9 @@ function! ale#lsp#reset#StopAllLSPs() abort
call ale#definition#ClearLSPData()
endif
- if exists('*ale#engine#ClearLSPData')
+ if exists('*ale#lsp_linter#ClearLSPData')
" Clear the mapping for connections, etc.
- call ale#engine#ClearLSPData()
+ call ale#lsp_linter#ClearLSPData()
" Remove the problems for all of the LSP linters in every buffer.
for l:buffer_string in keys(g:ale_buffer_info)
diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim
new file mode 100644
index 00000000..4aef8ff5
--- /dev/null
+++ b/autoload/ale/lsp_linter.vim
@@ -0,0 +1,228 @@
+" Author: w0rp <devw0rp@gmail.com>
+" Description: Integration between linters and LSP/tsserver.
+
+" This code isn't loaded if a user never users LSP features or linters.
+
+" Associates LSP connection IDs with linter names.
+if !has_key(s:, 'lsp_linter_map')
+ let s:lsp_linter_map = {}
+endif
+
+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)
+
+ if l:buffer <= 0
+ return
+ endif
+
+ let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
+
+ call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
+endfunction
+
+function! s:HandleTSServerDiagnostics(response, error_type) abort
+ let l:buffer = bufnr(a:response.body.file)
+ let l:info = get(g:ale_buffer_info, l:buffer, {})
+
+ if empty(l:info)
+ return
+ endif
+
+ let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response)
+
+ " tsserver sends syntax and semantic errors in separate messages, so we
+ " have to collect the messages separately for each buffer and join them
+ " back together again.
+ if a:error_type is# 'syntax'
+ let l:info.syntax_loclist = l:thislist
+ else
+ let l:info.semantic_loclist = l:thislist
+ endif
+
+ let l:loclist = get(l:info, 'semantic_loclist', [])
+ \ + get(l:info, 'syntax_loclist', [])
+
+ call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
+endfunction
+
+function! s:HandleLSPErrorMessage(linter_name, response) abort
+ if !g:ale_history_enabled || !g:ale_history_log_output
+ return
+ endif
+
+ if empty(a:linter_name)
+ return
+ endif
+
+ let l:message = ale#lsp#response#GetErrorMessage(a:response)
+
+ if empty(l:message)
+ return
+ endif
+
+ " This global variable is set here so we don't load the debugging.vim file
+ " until someone uses :ALEInfo.
+ let g:ale_lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
+
+ if !has_key(g:ale_lsp_error_messages, a:linter_name)
+ let g:ale_lsp_error_messages[a:linter_name] = []
+ endif
+
+ call add(g:ale_lsp_error_messages[a:linter_name], l:message)
+endfunction
+
+function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
+ let l:method = get(a:response, 'method', '')
+ let l:linter_name = get(s:lsp_linter_map, a:conn_id, '')
+
+ if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
+ call s:HandleLSPErrorMessage(l:linter_name, a:response)
+ elseif l:method is# 'textDocument/publishDiagnostics'
+ call s:HandleLSPDiagnostics(a:conn_id, a:response)
+ elseif get(a:response, 'type', '') is# 'event'
+ \&& get(a:response, 'event', '') is# 'semanticDiag'
+ call s:HandleTSServerDiagnostics(a:response, 'semantic')
+ elseif get(a:response, 'type', '') is# 'event'
+ \&& get(a:response, 'event', '') is# 'syntaxDiag'
+ call s:HandleTSServerDiagnostics(a:response, 'syntax')
+ endif
+endfunction
+
+" Given a buffer, an LSP linter, and a callback to register for handling
+" messages, start up an LSP linter and get ready to receive errors or
+" completions.
+function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
+ let l:command = ''
+ let l:address = ''
+ let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
+
+ if empty(l:root) && a:linter.lsp isnot# 'tsserver'
+ " If there's no project root, then we can't check files with LSP,
+ " unless we are using tsserver, which doesn't use project roots.
+ return {}
+ endif
+
+ 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
+ endif
+
+ if a:linter.lsp is# 'socket'
+ let l:address = ale#linter#GetAddress(a:buffer, a:linter)
+ let l:conn_id = ale#lsp#ConnectToAddress(
+ \ l:address,
+ \ l:root,
+ \ a:callback,
+ \ l:initialization_options,
+ \)
+ else
+ let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
+
+ if !executable(l:executable)
+ return {}
+ endif
+
+ let l:command = ale#job#PrepareCommand(
+ \ a:buffer,
+ \ ale#linter#GetCommand(a:buffer, a:linter),
+ \)
+ let l:conn_id = ale#lsp#StartProgram(
+ \ l:executable,
+ \ l:command,
+ \ l:root,
+ \ a:callback,
+ \ l:initialization_options,
+ \)
+ endif
+
+ let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
+
+ if !l:conn_id
+ if g:ale_history_enabled && !empty(l:command)
+ call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
+ endif
+
+ return {}
+ endif
+
+ let l:details = {
+ \ 'buffer': a:buffer,
+ \ 'connection_id': l:conn_id,
+ \ 'command': l:command,
+ \ 'project_root': l:root,
+ \ 'language_id': l:language_id,
+ \}
+
+ if ale#lsp#OpenDocument(l:details)
+ if g:ale_history_enabled && !empty(l:command)
+ call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
+ endif
+ endif
+
+ " The change message needs to be sent for tsserver before doing anything.
+ if a:linter.lsp is# 'tsserver'
+ call ale#lsp#NotifyForChanges(l:details)
+ endif
+
+ return l:details
+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('ale#lsp_linter#HandleLSPResponse'),
+ \)
+
+ if empty(l:lsp_details)
+ return 0
+ endif
+
+ let l:id = l:lsp_details.connection_id
+ let l:root = l:lsp_details.project_root
+
+ " Remember the linter this connection is for.
+ 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:request_id = ale#lsp#Send(l:id, l:message, l:root)
+
+ let l:notified = l:request_id != 0
+ else
+ let l:notified = ale#lsp#NotifyForChanges(l:lsp_details)
+ 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)
+ let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root)
+
+ let l:notified = l:request_id != 0
+ endif
+
+ 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
+endfunction
+
+" Clear LSP linter data for the linting engine.
+function! ale#lsp_linter#ClearLSPData() abort
+ let s:lsp_linter_map = {}
+endfunction
+
+" Just for tests.
+function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
+ let s:lsp_linter_map = a:replacement_map
+endfunction
diff --git a/autoload/ale/pattern_options.vim b/autoload/ale/pattern_options.vim
index c445a9ed..d1f91785 100644
--- a/autoload/ale/pattern_options.vim
+++ b/autoload/ale/pattern_options.vim
@@ -23,7 +23,8 @@ function! s:CmpPatterns(left_item, right_item) abort
endfunction
function! ale#pattern_options#SetOptions(buffer) abort
- if !g:ale_pattern_options_enabled || empty(g:ale_pattern_options)
+ if !get(g:, 'ale_pattern_options_enabled', 0)
+ \|| empty(get(g:, 'ale_pattern_options', 0))
return
endif
diff --git a/autoload/ale/references.vim b/autoload/ale/references.vim
index 9777519d..89df69eb 100644
--- a/autoload/ale/references.vim
+++ b/autoload/ale/references.vim
@@ -72,14 +72,13 @@ function! s:FindReferences(linter) abort
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse')
- let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
+ let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
- let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#References(
@@ -90,14 +89,14 @@ function! s:FindReferences(linter) abort
else
" Send a message saying the buffer has changed first, or the
" references position probably won't make sense.
- call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
+ call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))])
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
- let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
+ let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:references_map[l:request_id] = {}
endfunction
diff --git a/doc/ale-cloudformation.txt b/doc/ale-cloudformation.txt
new file mode 100644
index 00000000..59c6af06
--- /dev/null
+++ b/doc/ale-cloudformation.txt
@@ -0,0 +1,14 @@
+===============================================================================
+ALE CloudFormation Integration *ale-cloudformation-options*
+
+
+===============================================================================
+cfn-python-lint *ale-cloudformation-cfn-python-lint*
+
+cfn-python-lint is a linter for AWS CloudFormation template file.
+
+https://github.com/awslabs/cfn-python-lint
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
+
diff --git a/doc/ale-cpp.txt b/doc/ale-cpp.txt
index 05e54799..8bd111e4 100644
--- a/doc/ale-cpp.txt
+++ b/doc/ale-cpp.txt
@@ -157,6 +157,26 @@ g:ale_cpp_cpplint_options *g:ale_cpp_cpplint_options*
===============================================================================
+cquery *ale-cpp-cquery*
+
+g:ale_cpp_cquery_executable *g:ale_cpp_cquery_executable*
+ *b:ale_cpp_cquery_executable*
+ Type: |String|
+ Default: `'cquery'`
+
+ This variable can be changed to use a different executable for cquery.
+
+
+g:ale_cpp_cquery_cache_directory *g:ale_cpp_cquery_cache_directory*
+ *b:ale_cpp_cquery_cache_directory*
+ Type: |String|
+ Default: `'~/.cache/cquery'`
+
+ This variable can be changed to decide which directory cquery uses for its
+ cache.
+
+
+===============================================================================
flawfinder *ale-cpp-flawfinder*
g:ale_cpp_flawfinder_executable *g:ale_cpp_flawfinder_executable*
diff --git a/doc/ale-gitcommit.txt b/doc/ale-gitcommit.txt
index 19ba8e94..38f3fd90 100644
--- a/doc/ale-gitcommit.txt
+++ b/doc/ale-gitcommit.txt
@@ -19,12 +19,14 @@ g:ale_gitcommit_gitlint_options *g:ale_gitcommit_gitlint_options*
Default: `''`
This variable can be changed to add command-line arguments to the gitlint
- invocation.
+ invocation. For example, you can specify the path to a configuration file. >
- For example, to dinamically set the gitlint configuration file path, you
- may want to set >
+ let g:ale_gitcommit_gitlint_options = '-C /home/user/.config/gitlint.ini'
+<
+ You can also disable particular error codes using this option. For example,
+ you can ignore errors for git commits with a missing body. >
- let g:ale_gitcommit_gitlint_options = '-C /home/user/.config/gitlint.ini'
+ let g:ale_gitcommit_gitlint_options = '--ignore B6'
<
g:ale_gitcommit_gitlint_use_global *g:ale_gitcommit_gitlint_use_global*
diff --git a/doc/ale-python.txt b/doc/ale-python.txt
index 55641892..b24b531d 100644
--- a/doc/ale-python.txt
+++ b/doc/ale-python.txt
@@ -100,7 +100,8 @@ g:ale_python_flake8_executable *g:ale_python_flake8_executable*
Type: |String|
Default: `'flake8'`
- This variable can be changed to modify the executable used for flake8.
+ This variable can be changed to modify the executable used for flake8. Set
+ this to `'pipenv'` to invoke `'pipenv` `run` `flake8'`.
g:ale_python_flake8_options *g:ale_python_flake8_options*
@@ -169,6 +170,8 @@ g:ale_python_mypy_executable *g:ale_python_mypy_executable*
See |ale-integrations-local-executables|
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `mypy'`.
+
g:ale_python_mypy_ignore_invalid_syntax
*g:ale_python_mypy_ignore_invalid_syntax*
*b:ale_python_mypy_ignore_invalid_syntax*
@@ -207,6 +210,8 @@ g:ale_python_prospector_executable *g:ale_python_prospector_executable*
See |ale-integrations-local-executables|
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `prospector'`.
+
g:ale_python_prospector_options *g:ale_python_prospector_options*
*b:ale_python_prospector_options*
@@ -248,6 +253,8 @@ g:ale_python_pycodestyle_executable *g:ale_python_pycodestyle_executable*
See |ale-integrations-local-executables|
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `pycodestyle'`.
+
g:ale_python_pycodestyle_options *g:ale_python_pycodestyle_options*
*b:ale_python_pycodestyle_options*
@@ -267,6 +274,20 @@ g:ale_python_pycodestyle_use_global *g:ale_python_pycodestyle_use_global*
===============================================================================
+pyflakes *ale-python-pyflakes*
+
+
+g:ale_python_pyflakes_executable *g:ale_python_pyflakes_executable*
+ *b:ale_python_pyflakes_executable*
+ Type: |String|
+ Default: `'pyflakes'`
+
+ See |ale-integrations-local-executables|
+
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `pyflakes'`.
+
+
+===============================================================================
pylint *ale-python-pylint*
g:ale_python_pylint_change_directory *g:ale_python_pylint_change_directory*
@@ -287,6 +308,8 @@ g:ale_python_pylint_executable *g:ale_python_pylint_executable*
See |ale-integrations-local-executables|
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `pylint'`.
+
g:ale_python_pylint_options *g:ale_python_pylint_options*
*b:ale_python_pylint_options*
@@ -329,6 +352,8 @@ g:ale_python_pyls_executable *g:ale_python_pyls_executable*
See |ale-integrations-local-executables|
+ Set this to `'pipenv'` to invoke `'pipenv` `run` `pyls'`.
+
g:ale_python_pyls_use_global *g:ale_python_pyls_use_global*
*b:ale_python_pyls_use_global*
diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt
index dad9ae66..f9afe17d 100644
--- a/doc/ale-rust.txt
+++ b/doc/ale-rust.txt
@@ -59,6 +59,26 @@ g:ale_rust_cargo_check_all_targets *g:ale_rust_cargo_check_all_targets*
is used. See |g:ale_rust_cargo_use_check|,
+g:ale_rust_cargo_check_tests *g:ale_rust_cargo_check_tests*
+ *b:ale_rust_cargo_check_tests*
+ Type: |Number|
+ Default: `0`
+
+ When set to `1`, ALE will set the `--tests` option when `cargo check`
+ is used. This allows for linting of tests which are normally excluded.
+ See |g:ale_rust_cargo_use_check|,
+
+
+g:ale_rust_cargo_check_examples *g:ale_rust_cargo_check_examples*
+ *b:ale_rust_cargo_check_examples*
+ Type: |Number|
+ Default: `0`
+
+ When set to `1`, ALE will set the `--examples` option when `cargo check`
+ is used. This allows for linting of examples which are normally excluded.
+ See |g:ale_rust_cargo_use_check|,
+
+
g:ale_rust_cargo_default_feature_behavior
*g:ale_rust_cargo_default_feature_behavior*
*b:ale_rust_cargo_default_feature_behavior*
diff --git a/doc/ale-scala.txt b/doc/ale-scala.txt
index 9c9472f6..b5474f3b 100644
--- a/doc/ale-scala.txt
+++ b/doc/ale-scala.txt
@@ -3,6 +3,31 @@ ALE Scala Integration *ale-scala-options*
===============================================================================
+scalafmt *ale-scala-scalafmt*
+
+If Nailgun is used, override `g:ale_scala_scalafmt_executable` like so: >
+ let g:ale_scala_scalafmt_executable = 'ng'
+
+
+g:ale_scala_scalafmt_executable *g:ale_scala_scalafmt_executable*
+ *b:ale_scala_scalafmt_executable*
+ Type: |String|
+ Default: `'scalafmt'`
+
+ Override the invoked `scalafmt` binary. This is useful for running `scalafmt`
+ with Nailgun.
+
+
+g:ale_scala_scalafmt_options *g:ale_scala_scalafmt_options*
+ *b:ale_scala_scalafmt_options*
+ Type: |String|
+ Default: `''`
+
+ A string containing additional options to pass to `'scalafmt'`, or
+ `'ng scalafmt'` if Nailgun is used.
+
+
+===============================================================================
scalastyle *ale-scala-scalastyle*
`scalastyle` requires a configuration file for a project to run. When no
diff --git a/doc/ale.txt b/doc/ale.txt
index 2b4452d9..4fe3a23a 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -35,6 +35,8 @@ CONTENTS *ale-contents*
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|
@@ -44,6 +46,7 @@ CONTENTS *ale-contents*
clangtidy...........................|ale-cpp-clangtidy|
cppcheck............................|ale-cpp-cppcheck|
cpplint.............................|ale-cpp-cpplint|
+ cquery..............................|ale-cpp-cquery|
flawfinder..........................|ale-cpp-flawfinder|
gcc.................................|ale-cpp-gcc|
c#....................................|ale-cs-options|
@@ -190,6 +193,7 @@ CONTENTS *ale-contents*
mypy................................|ale-python-mypy|
prospector..........................|ale-python-prospector|
pycodestyle.........................|ale-python-pycodestyle|
+ pyflakes............................|ale-python-pyflakes|
pylint..............................|ale-python-pylint|
pyls................................|ale-python-pyls|
yapf................................|ale-python-yapf|
@@ -218,6 +222,7 @@ CONTENTS *ale-contents*
sass..................................|ale-sass-options|
stylelint...........................|ale-sass-stylelint|
scala.................................|ale-scala-options|
+ scalafmt............................|ale-scala-scalafmt|
scalastyle..........................|ale-scala-scalastyle|
scss..................................|ale-scss-options|
prettier............................|ale-scss-prettier|
@@ -317,11 +322,12 @@ Notes:
* Bash: `shell` (-n flag), `shellcheck`, `shfmt`
* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt`
* C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc`
-* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `flawfinder`, `gcc`
+* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc`
* CUDA: `nvcc`!!
* C#: `mcs`, `mcsc`!!
* Chef: `foodcritic`
* Clojure: `joker`
+* CloudFormation: `cfn-python-lint`
* CMake: `cmakelint`
* CoffeeScript: `coffee`, `coffeelint`
* Crystal: `crystal`!!
@@ -388,7 +394,7 @@ Notes:
* Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|), `rustfmt`
* SASS: `sass-lint`, `stylelint`
* SCSS: `prettier`, `sass-lint`, `scss-lint`, `stylelint`
-* Scala: `fsc`, `scalac`, `scalastyle`
+* Scala: `fsc`, `scalac`, `scalafmt`, `scalastyle`
* Slim: `slim-lint`
* SML: `smlnj`
* Solidity: `solhint`, `solium`
@@ -2329,6 +2335,10 @@ 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.
+
`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
@@ -2370,6 +2380,17 @@ 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.
+ 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.
+
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.
diff --git a/test/command_callback/scala_paths/dummy.scala b/test/command_callback/scala_paths/dummy.scala
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/scala_paths/dummy.scala
diff --git a/test/command_callback/test_cargo_command_callbacks.vader b/test/command_callback/test_cargo_command_callbacks.vader
index 9c06f27d..f674645f 100644
--- a/test/command_callback/test_cargo_command_callbacks.vader
+++ b/test/command_callback/test_cargo_command_callbacks.vader
@@ -1,11 +1,15 @@
Before:
Save g:ale_rust_cargo_use_check
Save g:ale_rust_cargo_check_all_targets
+ Save g:ale_rust_cargo_check_tests
+ Save g:ale_rust_cargo_check_examples
Save g:ale_rust_cargo_default_feature_behavior
Save g:ale_rust_cargo_include_features
unlet! g:ale_rust_cargo_use_check
- unlet! g:ale_cargo_check_all_targets
+ unlet! g:ale_rust_cargo_check_all_targets
+ unlet! g:ale_rust_cargo_check_tests
+ unlet! g:ale_rust_cargo_check_examples
unlet! g:ale_rust_cargo_default_feature_behavior
unlet! g:ale_rust_cargo_include_features
@@ -119,6 +123,38 @@ Execute(--all-targets should be used when g:ale_rust_cargo_check_all_targets is
AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr(''))
+Execute(--tests should be used when g:ale_rust_cargo_check_tests is set to 1):
+ let g:ale_rust_cargo_check_tests = 1
+
+ AssertEqual
+ \ 'cargo check --tests' . g:suffix,
+ \ ale_linters#rust#cargo#GetCommand(bufnr(''), [
+ \ 'cargo 0.22.0 (3423351a5 2017-10-06)',
+ \ ])
+
+ " We should cache the version check
+ AssertEqual
+ \ 'cargo check --tests' . g:suffix,
+ \ ale_linters#rust#cargo#GetCommand(bufnr(''), [])
+
+ AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr(''))
+
+Execute(--examples should be used when g:ale_rust_cargo_check_examples is set to 1):
+ let g:ale_rust_cargo_check_examples = 1
+
+ AssertEqual
+ \ 'cargo check --examples' . g:suffix,
+ \ ale_linters#rust#cargo#GetCommand(bufnr(''), [
+ \ 'cargo 0.22.0 (3423351a5 2017-10-06)',
+ \ ])
+
+ " We should cache the version check
+ AssertEqual
+ \ 'cargo check --examples' . g:suffix,
+ \ ale_linters#rust#cargo#GetCommand(bufnr(''), [])
+
+ AssertEqual '', ale_linters#rust#cargo#VersionCheck(bufnr(''))
+
Execute(--no-default-features should be used when g:ale_rust_cargo_default_feature_behavior is none):
let g:ale_rust_cargo_default_feature_behavior = 'none'
diff --git a/test/command_callback/test_cpp_cquery_command_callbacks.vader b/test/command_callback/test_cpp_cquery_command_callbacks.vader
new file mode 100644
index 00000000..89a3e225
--- /dev/null
+++ b/test/command_callback/test_cpp_cquery_command_callbacks.vader
@@ -0,0 +1,48 @@
+" Author: Ben Falconer <ben@falconers.me.uk>
+" Description: A language server for C++
+
+Before:
+ Save g:ale_cpp_cquery_executable
+ Save g:ale_cpp_cquery_cache_directory
+
+ unlet! g:ale_cpp_cquery_executable
+ unlet! b:ale_cpp_cquery_executable
+ unlet! g:ale_cpp_cquery_cache_directory
+ unlet! b:ale_cpp_cquery_cache_directory
+
+ runtime ale_linters/cpp/cquery.vim
+
+After:
+ Restore
+ unlet! b:ale_cpp_cquery_executable
+ unlet! b:ale_cpp_cquery_cache_directory
+ call ale#linter#Reset()
+
+Execute(The executable should be configurable):
+ AssertEqual 'cquery', ale_linters#cpp#cquery#GetExecutable(bufnr(''))
+
+ let b:ale_cpp_cquery_executable = 'foobar'
+
+ AssertEqual 'foobar', ale_linters#cpp#cquery#GetExecutable(bufnr(''))
+
+Execute(The executable should be used in the command):
+ AssertEqual
+ \ ale#Escape('cquery'),
+ \ ale_linters#cpp#cquery#GetCommand(bufnr(''))
+
+ let b:ale_cpp_cquery_executable = 'foobar'
+
+ AssertEqual
+ \ ale#Escape('foobar'),
+ \ ale_linters#cpp#cquery#GetCommand(bufnr(''))
+
+Execute(The cache directory should be configurable):
+ AssertEqual
+ \ {'cacheDirectory': expand('$HOME/.cache/cquery')},
+ \ ale_linters#cpp#cquery#GetInitializationOptions(bufnr(''))
+
+ let b:ale_cpp_cquery_cache_directory = '/foo/bar'
+
+ AssertEqual
+ \ {'cacheDirectory': '/foo/bar'},
+ \ ale_linters#cpp#cquery#GetInitializationOptions(bufnr(''))
diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader
index ecd06332..1cc50836 100644
--- a/test/command_callback/test_flake8_command_callback.vader
+++ b/test/command_callback/test_flake8_command_callback.vader
@@ -174,3 +174,11 @@ Execute(Using `python -m flake8` should be supported for running flake8):
\ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('python') . ' -m flake8 --some-option --format=default -',
\ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9'])
+
+Execute(Setting executable to 'pipenv' appends 'run flake8'):
+ let g:ale_python_flake8_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('path/to/pipenv') . ' run flake8 --format=default --stdin-display-name %s -',
+ \ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.0'])
diff --git a/test/command_callback/test_lintr_command_callback.vader b/test/command_callback/test_lintr_command_callback.vader
index e655328b..2f7dfb1d 100644
--- a/test/command_callback/test_lintr_command_callback.vader
+++ b/test/command_callback/test_lintr_command_callback.vader
@@ -16,7 +16,7 @@ After:
Execute(The default lintr command should be correct):
AssertEqual
\ 'cd ' . ale#Escape(getcwd()) . ' && '
- \ . 'Rscript -e '
+ \ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint(cache = FALSE, commandArgs(TRUE), '
\ . 'with_defaults())')
@@ -28,7 +28,7 @@ Execute(The lintr options should be configurable):
AssertEqual
\ 'cd ' . ale#Escape(getcwd()) . ' && '
- \ . 'Rscript -e '
+ \ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint(cache = FALSE, commandArgs(TRUE), '
\ . 'with_defaults(object_usage_linter = NULL))')
@@ -40,7 +40,7 @@ Execute(If the lint_package flag is set, lintr::lint_package should be called):
AssertEqual
\ 'cd ' . ale#Escape(getcwd()) . ' && '
- \ . 'Rscript -e '
+ \ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint_package(cache = FALSE, '
\ . 'linters = with_defaults())')
diff --git a/test/command_callback/test_mypy_command_callback.vader b/test/command_callback/test_mypy_command_callback.vader
index 6a0add52..0fb4cc54 100644
--- a/test/command_callback/test_mypy_command_callback.vader
+++ b/test/command_callback/test_mypy_command_callback.vader
@@ -95,3 +95,12 @@ Execute(You should able able to use the global mypy instead):
\ . ' --show-column-numbers '
\ . '--shadow-file %s %t %s',
\ ale_linters#python#mypy#GetCommand(bufnr(''))
+
+Execute(Setting executable to 'pipenv' appends 'run mypy'):
+ let g:ale_python_mypy_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('path/to/pipenv') . ' run mypy'
+ \ . ' --show-column-numbers --shadow-file %s %t %s',
+ \ ale_linters#python#mypy#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_prospector_command_callback.vader b/test/command_callback/test_prospector_command_callback.vader
new file mode 100644
index 00000000..04cd58ed
--- /dev/null
+++ b/test/command_callback/test_prospector_command_callback.vader
@@ -0,0 +1,23 @@
+Before:
+ Save g:ale_python_mypy_executable
+ Save g:ale_python_mypy_options
+
+ unlet! g:ale_python_mypy_executable
+ unlet! g:ale_python_mypy_options
+
+ runtime ale_linters/python/prospector.vim
+
+After:
+ Restore
+
+ unlet! b:executable
+
+ call ale#linter#Reset()
+
+Execute(Setting executable to 'pipenv' appends 'run prospector'):
+ let g:ale_python_prospector_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#Escape('path/to/pipenv') . ' run prospector'
+ \ . ' --messages-only --absolute-paths --zero-exit --output-format json %s',
+ \ ale_linters#python#prospector#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_pycodestyle_command_callback.vader b/test/command_callback/test_pycodestyle_command_callback.vader
index 5b309e19..90b07a24 100644
--- a/test/command_callback/test_pycodestyle_command_callback.vader
+++ b/test/command_callback/test_pycodestyle_command_callback.vader
@@ -25,3 +25,10 @@ Execute(The pycodestyle executable should be configurable):
AssertEqual ale#Escape('~/.local/bin/pycodestyle') . ' -',
\ ale_linters#python#pycodestyle#GetCommand(bufnr(''))
+
+Execute(Setting executable to 'pipenv' appends 'run pycodestyle'):
+ let g:ale_python_pycodestyle_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#Escape('path/to/pipenv') . ' run pycodestyle -',
+ \ ale_linters#python#pycodestyle#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_pyflakes_command_callback.vader b/test/command_callback/test_pyflakes_command_callback.vader
index e8486ca8..491432e9 100644
--- a/test/command_callback/test_pyflakes_command_callback.vader
+++ b/test/command_callback/test_pyflakes_command_callback.vader
@@ -47,3 +47,10 @@ Execute(You should be able to override the pyflakes virtualenv lookup):
AssertEqual ale#Escape('pyflakes') . ' %t',
\ ale_linters#python#pyflakes#GetCommand(bufnr(''))
+
+Execute(Setting executable to 'pipenv' appends 'run pyflakes'):
+ let g:ale_python_pyflakes_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#Escape('path/to/pipenv') . ' run pyflakes %t',
+ \ ale_linters#python#pyflakes#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader
index 1cdc34d4..f8cb5800 100644
--- a/test/command_callback/test_pylint_command_callback.vader
+++ b/test/command_callback/test_pylint_command_callback.vader
@@ -102,3 +102,12 @@ Execute(You should able able to use the global pylint instead):
\ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('pylint') . ' ' . b:command_tail,
\ ale_linters#python#pylint#GetCommand(bufnr(''))
+
+Execute(Setting executable to 'pipenv' appends 'run pylint'):
+ let g:ale_python_pylint_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('path/to/pipenv') . ' run pylint'
+ \ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s',
+ \ ale_linters#python#pylint#GetCommand(bufnr(''))
diff --git a/test/command_callback/test_pyls_command_callback.vader b/test/command_callback/test_pyls_command_callback.vader
index 06ea718f..4bef4742 100644
--- a/test/command_callback/test_pyls_command_callback.vader
+++ b/test/command_callback/test_pyls_command_callback.vader
@@ -47,3 +47,10 @@ Execute(You should be able to override the pyls virtualenv lookup):
AssertEqual ale#Escape('pyls'),
\ ale_linters#python#pyls#GetCommand(bufnr(''))
+
+Execute(Setting executable to 'pipenv' appends 'run pyls'):
+ let g:ale_python_pyls_executable = 'path/to/pipenv'
+
+ AssertEqual
+ \ ale#Escape('path/to/pipenv') . ' run pyls',
+ \ ale_linters#python#pyls#GetCommand(bufnr(''))
diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader
index 734b330c..8ba2ad38 100644
--- a/test/completion/test_lsp_completion_messages.vader
+++ b/test/completion/test_lsp_completion_messages.vader
@@ -15,12 +15,18 @@ Before:
let g:message_list = []
let g:Callback = ''
- function! ale#linter#StartLSP(buffer, linter, callback) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
+ let l:conn = ale#lsp#NewConnection({})
+ let l:conn.id = 347
+ let l:conn.open_documents = {a:buffer : -1}
+
return {
+ \ 'buffer': a:buffer,
\ 'connection_id': 347,
\ 'project_root': '/foo/bar',
+ \ 'language_id': 'python',
\}
endfunction
@@ -43,6 +49,7 @@ After:
unlet! b:ale_linters
unlet! b:ale_tsserver_completion_names
+ call ale#lsp#RemoveConnectionWithID(347)
call ale#test#RestoreDirectory()
call ale#linter#Reset()
diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader
index 23bbd14c..736353e3 100644
--- a/test/completion/test_lsp_completion_parsing.vader
+++ b/test/completion/test_lsp_completion_parsing.vader
@@ -429,3 +429,23 @@ Execute(Should handle Python completion results correctly):
\ ]
\ }
\ })
+
+Execute(Should handle missing detail keys):
+ AssertEqual
+ \ [
+ \ {'word': 'x', 'menu': '', 'info': 'y', 'kind': 'f', 'icase': 1},
+ \ ],
+ \ ale#completion#ParseLSPCompletions({
+ \ 'jsonrpc': '2.0',
+ \ 'id': 6,
+ \ 'result': {
+ \ 'isIncomplete': v:false,
+ \ 'items': [
+ \ {
+ \ 'label': 'x',
+ \ 'kind': 3,
+ \ 'documentation': 'y',
+ \ },
+ \ ]
+ \ }
+ \ })
diff --git a/test/fixers/test_qmlfmt_fixer_callback.vader b/test/fixers/test_qmlfmt_fixer_callback.vader
new file mode 100644
index 00000000..e216f2e1
--- /dev/null
+++ b/test/fixers/test_qmlfmt_fixer_callback.vader
@@ -0,0 +1,12 @@
+Before:
+ Save g:ale_qml_qmlfmt_executable
+
+After:
+ Restore
+
+Execute(The qmlfmt fixer should use the options you set):
+ let g:ale_qml_qmlfmt_executable = 'foo-exe'
+
+ AssertEqual
+ \ {'command': ale#Escape('foo-exe')},
+ \ ale#fixers#qmlfmt#Fix(bufnr(''))
diff --git a/test/fixers/test_scalafmt_fixer_callback.vader b/test/fixers/test_scalafmt_fixer_callback.vader
new file mode 100644
index 00000000..d82fda43
--- /dev/null
+++ b/test/fixers/test_scalafmt_fixer_callback.vader
@@ -0,0 +1,69 @@
+Before:
+ Save g:ale_scala_scalafmt_executable
+ Save g:ale_scala_scalafmt_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_scala_scalafmt_executable = 'xxxinvalid'
+ let g:ale_scala_scalafmt_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 scalafmt callback should return the correct default values):
+ call ale#test#SetFilename('scala_paths/dummy.scala')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_scala_scalafmt_executable)
+ \ . ' %t',
+ \ },
+ \ ale#fixers#scalafmt#Fix(bufnr(''))
+
+Execute(The scalafmt callback should use ng with scalafmt automatically):
+ let g:ale_scala_scalafmt_executable = 'ng'
+ call ale#test#SetFilename('scala_paths/dummy.scala')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('ng')
+ \ . ' scalafmt'
+ \ . ' %t',
+ \ },
+ \ ale#fixers#scalafmt#Fix(bufnr(''))
+
+Execute(The scalafmt callback should include custom scalafmt options):
+ let g:ale_scala_scalafmt_options = '--diff'
+ call ale#test#SetFilename('scala_paths/dummy.scala')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape(g:ale_scala_scalafmt_executable)
+ \ . ' --diff'
+ \ . ' %t',
+ \ },
+ \ ale#fixers#scalafmt#Fix(bufnr(''))
+
+Execute(The scalafmt callback should include custom scalafmt options and use ng with scalafmt):
+ let g:ale_scala_scalafmt_options = '--diff'
+ let g:ale_scala_scalafmt_executable = 'ng'
+ call ale#test#SetFilename('scala_paths/dummy.scala')
+
+ AssertEqual
+ \ {
+ \ 'read_temporary_file': 1,
+ \ 'command': ale#Escape('ng')
+ \ . ' scalafmt'
+ \ . ' --diff'
+ \ . ' %t',
+ \ },
+ \ ale#fixers#scalafmt#Fix(bufnr(''))
diff --git a/test/handler/test_cfn_python_lint_handler.vader b/test/handler/test_cfn_python_lint_handler.vader
new file mode 100644
index 00000000..a053e84e
--- /dev/null
+++ b/test/handler/test_cfn_python_lint_handler.vader
@@ -0,0 +1,33 @@
+Before:
+ runtime! ale_linters/cloudformation/cfn_python_lint.vim
+ call ale#test#SetFilename('sample.template.yaml')
+
+After:
+ call ale#linter#Reset()
+
+Execute(The cfn_python_lint handler should parse items correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 96,
+ \ 'col': 7,
+ \ 'end_lnum': 96,
+ \ 'end_col': 15,
+ \ 'text': 'Property Resources/Sample/Properties/FromPort should be of type Integer',
+ \ 'code': 'E3012',
+ \ 'type': 'E',
+ \ },
+ \ {
+ \ 'lnum': 97,
+ \ 'col': 7,
+ \ 'end_lnum': 97,
+ \ 'end_col': 15,
+ \ 'text': 'AllowedPattern and/or AllowedValues for Parameter should be specified at Parameters/SampleIpAddress. Example for AllowedPattern "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$"',
+ \ 'code': 'W2509',
+ \ 'type': 'W',
+ \ },
+ \ ],
+ \ ale_linters#cloudformation#cfn_python_lint#Handle(bufnr(''), [
+ \ fnamemodify(tempname(), ':h') . '/sample.template.yaml:96:7:96:15: [E3012] Property Resources/Sample/Properties/FromPort should be of type Integer',
+ \ fnamemodify(tempname(), ':h') . '/sample.template.yaml:97:7:97:15: [W2509] AllowedPattern and/or AllowedValues for Parameter should be specified at Parameters/SampleIpAddress. Example for AllowedPattern "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$"',
+ \ ])
diff --git a/test/handler/test_gitlint_handler.vader b/test/handler/test_gitlint_handler.vader
index 73ee988f..60d632a0 100644
--- a/test/handler/test_gitlint_handler.vader
+++ b/test/handler/test_gitlint_handler.vader
@@ -1,8 +1,16 @@
Before:
- runtime ale_linters/gitcommit/gitlint.vim
+ Save g:ale_warn_about_trailing_whitespace
+
+ let g:ale_warn_about_trailing_whitespace = 1
+
+ runtime ale_linters/gitcommit/gitlint.vim
After:
- call ale#linter#Reset()
+ Restore
+
+ unlet! b:ale_warn_about_trailing_whitespace
+
+ call ale#linter#Reset()
Execute(The gitlint handler should handle basic warnings and syntax errors):
AssertEqual
@@ -39,3 +47,24 @@ Execute(The gitlint handler should handle basic warnings and syntax errors):
\ '8: T1 Title exceeds max length (92>72): "some very long commit subject line where the author can''t wait to explain what he just fixed"'
\ ])
+Execute(Disabling trailing whitespace warnings should work):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 8,
+ \ 'type': 'E',
+ \ 'text': 'Trailing whitespace',
+ \ 'code': 'T2',
+ \ },
+ \ ],
+ \ ale_linters#gitcommit#gitlint#Handle(bufnr(''), [
+ \ '8: T2 Trailing whitespace',
+ \])
+
+ let b:ale_warn_about_trailing_whitespace = 0
+
+ AssertEqual
+ \ [],
+ \ ale_linters#gitcommit#gitlint#Handle(bufnr(''), [
+ \ '8: T2 Trailing whitespace',
+ \ ])
diff --git a/test/handler/test_puppet_handler.vader b/test/handler/test_puppet_handler.vader
index 0d274fd2..e73c9dc7 100644
--- a/test/handler/test_puppet_handler.vader
+++ b/test/handler/test_puppet_handler.vader
@@ -37,9 +37,15 @@ Execute(The puppet handler should parse lines and column correctly):
\ 'lnum': 54,
\ 'col': 9,
\ 'text': "Syntax error at ':'"
- \ }
+ \ },
+ \ {
+ \ 'lnum': 45,
+ \ 'col': 12,
+ \ 'text': "Syntax error at 'parameter1'"
+ \ },
\ ],
\ ale_linters#puppet#puppet#Handle(255, [
\ "Error: Could not parse for environment production: Syntax error at ':' at /root/puppetcode/modules/nginx/manifests/init.pp:43:12",
\ "Error: Could not parse for environment production: Syntax error at ':' at C:/puppet/modules/nginx/manifests/init.pp:54:9",
+ \ "Error: Could not parse for environment production: Syntax error at 'parameter1' (file: /tmp/modules/mariadb/manifests/slave.pp, line: 45, column: 12)",
\ ])
diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader
index 042a3ce2..97774372 100644
--- a/test/lsp/test_did_save_event.vader
+++ b/test/lsp/test_did_save_event.vader
@@ -34,12 +34,18 @@ Before:
\ })
let g:ale_linters = {'foobar': ['dummy_linter']}
- function! ale#linter#StartLSP(buffer, linter, callback) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
+ let l:conn = ale#lsp#NewConnection({})
+ let l:conn.id = 347
+ let l:conn.open_documents = {a:buffer : -1}
+
return {
+ \ 'buffer': a:buffer,
\ 'connection_id': 347,
\ 'project_root': '/foo/bar',
+ \ 'language_id': 'foobar',
\}
endfunction
@@ -59,6 +65,7 @@ After:
delfunction LanguageCallback
delfunction ProjectRootCallback
+ call ale#lsp#RemoveConnectionWithID(347)
call ale#test#RestoreDirectory()
call ale#linter#Reset()
diff --git a/test/lsp/test_lsp_client_messages.vader b/test/lsp/test_lsp_client_messages.vader
index 89a29c8f..dc28c2e9 100644
--- a/test/lsp/test_lsp_client_messages.vader
+++ b/test/lsp/test_lsp_client_messages.vader
@@ -16,9 +16,11 @@ Execute(ale#lsp#message#Initialize() should return correct messages):
\ 'processId': getpid(),
\ 'rootPath': '/foo/bar',
\ 'capabilities': {},
+ \ 'initializationOptions': {'foo': 'bar'},
+ \ 'rootUri': 'file:///foo/bar',
\ }
\ ],
- \ ale#lsp#message#Initialize('/foo/bar')
+ \ ale#lsp#message#Initialize('/foo/bar', {'foo': 'bar'})
Execute(ale#lsp#message#Initialized() should return correct messages):
AssertEqual [1, 'initialized'], ale#lsp#message#Initialized()
diff --git a/test/test_engine_lsp_response_handling.vader b/test/test_engine_lsp_response_handling.vader
index 3d317e9c..18bad0a1 100644
--- a/test/test_engine_lsp_response_handling.vader
+++ b/test/test_engine_lsp_response_handling.vader
@@ -11,7 +11,7 @@ After:
call ale#test#RestoreDirectory()
call ale#linter#Reset()
- call ale#engine#ClearLSPData()
+ call ale#lsp_linter#ClearLSPData()
Given foobar(An empty file):
Execute(tsserver syntax error responses should be handled correctly):
@@ -21,7 +21,7 @@ Execute(tsserver syntax error responses should be handled correctly):
" When we get syntax errors and no semantic errors, we should keep the
" syntax errors.
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'syntaxDiag',
@@ -43,7 +43,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ ],
\ },
\})
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'semanticDiag',
@@ -71,7 +71,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ getloclist(0)
" After we get empty syntax errors, we should clear them.
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'syntaxDiag',
@@ -94,7 +94,7 @@ Execute(tsserver semantic error responses should be handled correctly):
" When we get syntax errors and no semantic errors, we should keep the
" syntax errors.
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'syntaxDiag',
@@ -104,7 +104,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ ],
\ },
\})
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'semanticDiag',
@@ -144,7 +144,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ getloclist(0)
" After we get empty syntax errors, we should clear them.
- call ale#engine#HandleLSPResponse(1, {
+ call ale#lsp_linter#HandleLSPResponse(1, {
\ 'seq': 0,
\ 'type': 'event',
\ 'event': 'semanticDiag',
@@ -161,8 +161,8 @@ Execute(tsserver semantic error responses should be handled correctly):
\ getloclist(0)
Execute(LSP errors should be logged in the history):
- call ale#engine#SetLSPLinterMap({'347': 'foobar'})
- call ale#engine#HandleLSPResponse(347, {
+ call ale#lsp_linter#SetLSPLinterMap({'347': 'foobar'})
+ call ale#lsp_linter#HandleLSPResponse(347, {
\ 'jsonrpc': '2.0',
\ 'error': {
\ 'code': -32602,
diff --git a/test/test_find_references.vader b/test/test_find_references.vader
index 6ab8e8fb..c2290ca3 100644
--- a/test/test_find_references.vader
+++ b/test/test_find_references.vader
@@ -14,12 +14,18 @@ Before:
runtime autoload/ale/util.vim
runtime autoload/ale/preview.vim
- function! ale#linter#StartLSP(buffer, linter, callback) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
+ let l:conn = ale#lsp#NewConnection({})
+ let l:conn.id = 347
+ let l:conn.open_documents = {a:buffer : -1}
+
return {
+ \ 'buffer': a:buffer,
\ 'connection_id': 347,
\ 'project_root': '/foo/bar',
+ \ 'language_id': 'python',
\}
endfunction
@@ -39,6 +45,7 @@ Before:
endfunction
After:
+ call ale#lsp#RemoveConnectionWithID(347)
call ale#references#SetMap({})
call ale#test#RestoreDirectory()
call ale#linter#Reset()
diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader
index 5bd75675..3a1d7458 100644
--- a/test/test_go_to_definition.vader
+++ b/test/test_go_to_definition.vader
@@ -11,12 +11,18 @@ Before:
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
- function! ale#linter#StartLSP(buffer, linter, callback) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
+ let l:conn = ale#lsp#NewConnection({})
+ let l:conn.id = 347
+ let l:conn.open_documents = {a:buffer : -1}
+
return {
+ \ 'buffer': a:buffer,
\ 'connection_id': 347,
\ 'project_root': '/foo/bar',
+ \ 'language_id': 'python',
\}
endfunction
@@ -31,6 +37,7 @@ Before:
endfunction
After:
+ call ale#lsp#RemoveConnectionWithID(347)
call ale#definition#SetMap({})
call ale#test#RestoreDirectory()
call ale#linter#Reset()
@@ -56,6 +63,19 @@ 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#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'definition',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': [],
+ \ }
+ \)
+ AssertEqual {}, ale#definition#GetMap()
+
Given typescript(Some typescript file):
foo
somelongerline
diff --git a/test/test_hover.vader b/test/test_hover.vader
index 18dcebaf..15f164f0 100644
--- a/test/test_hover.vader
+++ b/test/test_hover.vader
@@ -11,7 +11,7 @@ Before:
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
- function! ale#linter#StartLSP(buffer, linter, callback) abort
+ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
return {
diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader
index 653587b6..48a4a394 100644
--- a/test/test_linter_defintion_processing.vader
+++ b/test/test_linter_defintion_processing.vader
@@ -465,3 +465,28 @@ Execute(PreProcess should complain about address_callback for non-LSP linters):
AssertThrows call ale#linter#PreProcess(g:linter)
AssertEqual '`address_callback` cannot be used when lsp != ''socket''', g:vader_exception
+
+Execute(PreProcess should complain about using initialization_options and initialization_options_callback together):
+ let g:linter = {
+ \ 'name': 'x',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'initialization_options': 'x',
+ \ 'initialization_options_callback': 'x',
+ \}
+
+ AssertThrows call ale#linter#PreProcess(g:linter)
+ AssertEqual 'Only one of `initialization_options` or `initialization_options_callback` should be set', g:vader_exception
+
+Execute (PreProcess should throw when initialization_options_callback is not a callback):
+ AssertThrows call ale#linter#PreProcess({
+ \ 'name': 'foo',
+ \ 'lsp': 'socket',
+ \ 'address_callback': 'X',
+ \ 'language': 'x',
+ \ 'project_root_callback': 'x',
+ \ 'initialization_options_callback': {},
+ \})
+ AssertEqual '`initialization_options_callback` must be a callback if defined', g:vader_exception
diff --git a/test/test_pattern_options.vader b/test/test_pattern_options.vader
index 0e26eaaa..f439afbd 100644
--- a/test/test_pattern_options.vader
+++ b/test/test_pattern_options.vader
@@ -90,3 +90,14 @@ Execute(Patterns should be applied after the Dictionary changes):
call ale#pattern_options#SetOptions(bufnr(''))
AssertEqual 666, b:some_option
+
+Execute(SetOptions should tolerate settings being unset):
+ " This might happen if ALE is loaded in a weird way, so tolerate it.
+ unlet! g:ale_pattern_options
+ unlet! g:ale_pattern_options_enabled
+
+ call ale#pattern_options#SetOptions(bufnr(''))
+
+ let g:ale_pattern_options_enabled = 1
+
+ call ale#pattern_options#SetOptions(bufnr(''))