Merge pull request #1 from w0rp/master

update
This commit is contained in:
sridhars 2018-07-24 16:42:26 -05:00 committed by GitHub
commit 4446cf15be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 1078 additions and 512 deletions

View File

@ -3,5 +3,10 @@ sudo: required
services: services:
- docker - docker
language: generic language: generic
env:
- OPTIONS=--vim-80-only
- OPTIONS=--vim-81-only
- OPTIONS=--neovim-only
- OPTIONS=--linters-only
script: | script: |
./run-tests -v ./run-tests -v $OPTIONS

View File

@ -1,6 +1,7 @@
FROM tweekmonster/vim-testbed:latest FROM tweekmonster/vim-testbed:latest
RUN install_vim -tag v8.0.0027 -build \ RUN install_vim -tag v8.0.0027 -build \
-tag v8.1.0204 -build \
-tag neovim:v0.2.0 -build \ -tag neovim:v0.2.0 -build \
-tag neovim:v0.3.0 -build -tag neovim:v0.3.0 -build

View File

@ -4,7 +4,8 @@
![ALE Logo by Mark Grealish - https://www.bhalash.com/](img/logo.jpg?raw=true) ![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 ALE (Asynchronous Lint Engine) is a plugin for providing linting in NeoVim
0.2.0+ and Vim 8 while you edit your text files. 0.2.0+ and Vim 8 while you edit your text files, and acts as a Vim
[Language Server Protocol](https://langserver.org/) client.
![linting example](img/example.gif?raw=true) ![linting example](img/example.gif?raw=true)
@ -96,7 +97,7 @@ formatting.
| Awk | [gawk](https://www.gnu.org/software/gawk/)| | 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) | | 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) |
| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [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 | [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), [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/) | | 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) | | 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| | 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|
@ -118,7 +119,7 @@ formatting.
| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) | | Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) |
| Erlang | [erlc](http://erlang.org/doc/man/erlc.html), [SyntaxErl](https://github.com/ten0s/syntaxerl) | | 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) | Fish | fish [-n flag](https://linux.die.net/man/1/fish)
| Fortran | [gcc](https://gcc.gnu.org/) | | Fortran | [gcc](https://gcc.gnu.org/), [language_server](https://github.com/hansec/fortran-language-server) |
| Fountain | [proselint](http://proselint.com/) | | Fountain | [proselint](http://proselint.com/) |
| FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) | | FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) |
| Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) | | Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) |
@ -127,7 +128,7 @@ formatting.
| GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) | | GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) |
| Haml | [haml-lint](https://github.com/brigade/haml-lint) | | Haml | [haml-lint](https://github.com/brigade/haml-lint) |
| Handlebars | [ember-template-lint](https://github.com/rwjblue/ember-template-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/), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [stack-ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools), [hfmt](https://github.com/danstiner/hfmt) | | Haskell | [brittany](https://github.com/lspitzner/brittany), [ghc](https://www.haskell.org/ghc/), [cabal-ghc](https://www.haskell.org/cabal/), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [stack-ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools), [hfmt](https://github.com/danstiner/hfmt) |
| HTML | [alex](https://github.com/wooorm/alex) !!, [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/), [write-good](https://github.com/btford/write-good) | | HTML | [alex](https://github.com/wooorm/alex) !!, [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/), [write-good](https://github.com/btford/write-good) |
| Idris | [idris](http://www.idris-lang.org/) | | Idris | [idris](http://www.idris-lang.org/) |
| 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/) | | 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/) |

View File

@ -31,6 +31,6 @@ call ale#linter#Define('apiblueprint', {
\ 'name': 'drafter', \ 'name': 'drafter',
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'drafter', \ 'executable': 'drafter',
\ 'command': 'drafter --use-line-num --validate %t', \ 'command': 'drafter --use-line-num --validate',
\ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors', \ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors',
\}) \})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for AsciiDoc files " Description: write-good for AsciiDoc files
call ale#linter#Define('asciidoc', { call ale#handlers#writegood#DefineLinter('asciidoc')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

29
ale_linters/c/clangd.vim Normal file
View File

@ -0,0 +1,29 @@
" Author: Andrey Melentyev <andrey.melentyev@protonmail.com>
" Description: Clangd language server
call ale#Set('c_clangd_executable', 'clangd')
call ale#Set('c_clangd_options', '')
function! ale_linters#c#clangd#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#c#clangd#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'c_clangd_executable')
endfunction
function! ale_linters#c#clangd#GetCommand(buffer) abort
let l:executable = ale_linters#c#clangd#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'c_clangd_options')
return ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '')
endfunction
call ale#linter#Define('c', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#c#clangd#GetExecutable',
\ 'command_callback': 'ale_linters#c#clangd#GetCommand',
\ 'project_root_callback': 'ale_linters#c#clangd#GetProjectRoot',
\})

View File

@ -28,7 +28,7 @@ endfunction
call ale#linter#Define('elixir', { call ale#linter#Define('elixir', {
\ 'name': 'dialyxir', \ 'name': 'dialyxir',
\ 'executable': 'mix', \ 'executable': 'mix',
\ 'command': 'mix dialyzer', \ 'command': 'mix help dialyzer && mix dialyzer',
\ 'callback': 'ale_linters#elixir#dialyxir#Handle', \ 'callback': 'ale_linters#elixir#dialyxir#Handle',
\}) \})

View File

@ -0,0 +1,27 @@
" Author: unpairedbracket ben.spiers22@gmail.com
" Description: A language server for fortran
call ale#Set('fortran_language_server_executable', 'fortls')
call ale#Set('fortran_language_server_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#fortran#language_server#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'fortran_language_server_executable')
endfunction
function! ale_linters#fortran#language_server#GetCommand(buffer) abort
return ale#Escape(ale_linters#fortran#language_server#GetExecutable(a:buffer))
endfunction
function! ale_linters#fortran#language_server#GetProjectRoot(buffer) abort
let l:fortls_file = ale#path#FindNearestFile(a:buffer, '.fortls')
return !empty(l:fortls_file) ? fnamemodify(l:fortls_file, ':h') : ''
endfunction
call ale#linter#Define('fortran', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#fortran#language_server#GetExecutable',
\ 'command_callback': 'ale_linters#fortran#language_server#GetCommand',
\ 'project_root_callback': 'ale_linters#fortran#language_server#GetProjectRoot',
\})

View File

@ -70,7 +70,8 @@ function! ale_linters#go#gobuild#Handler(buffer, lines) abort
endfunction endfunction
call ale#linter#Define('go', { call ale#linter#Define('go', {
\ 'name': 'go build', \ 'name': 'gobuild',
\ 'aliases': ['go build'],
\ 'executable': 'go', \ 'executable': 'go',
\ 'command_chain': [ \ 'command_chain': [
\ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'},

View File

@ -9,7 +9,8 @@ function! ale_linters#go#govet#GetCommand(buffer) abort
endfunction endfunction
call ale#linter#Define('go', { call ale#linter#Define('go', {
\ 'name': 'go vet', \ 'name': 'govet',
\ 'aliases': ['go vet'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'go', \ 'executable': 'go',
\ 'command_callback': 'ale_linters#go#govet#GetCommand', \ 'command_callback': 'ale_linters#go#govet#GetCommand',

View File

@ -0,0 +1,19 @@
" Author: Eric Wolf <ericwolf42@gmail.com>
" Description: ghc for Haskell files called with cabal exec
call ale#Set('haskell_cabal_ghc_options', '-fno-code -v0')
function! ale_linters#haskell#cabal_ghc#GetCommand(buffer) abort
return 'cabal exec -- ghc '
\ . ale#Var(a:buffer, 'haskell_cabal_ghc_options')
\ . ' %t'
endfunction
call ale#linter#Define('haskell', {
\ 'name': 'cabal_ghc',
\ 'aliases': ['cabal-ghc'],
\ 'output_stream': 'stderr',
\ 'executable': 'cabal',
\ 'command_callback': 'ale_linters#haskell#cabal_ghc#GetCommand',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})

View File

@ -2,14 +2,16 @@
" Description: ghc-mod for Haskell files " Description: ghc-mod for Haskell files
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'ghc-mod', \ 'name': 'ghc_mod',
\ 'aliases': ['ghc-mod'],
\ 'executable': 'ghc-mod', \ 'executable': 'ghc-mod',
\ 'command': 'ghc-mod --map-file %s=%t check %s', \ 'command': 'ghc-mod --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\}) \})
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc-mod', \ 'name': 'stack_ghc_mod',
\ 'aliases': ['stack-ghc-mod'],
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s', \ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat',

View File

@ -13,7 +13,8 @@ function! ale_linters#haskell#stack_build#GetCommand(buffer) abort
endfunction endfunction
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-build', \ 'name': 'stack_build',
\ 'aliases': ['stack-build'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand', \ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand',

View File

@ -2,7 +2,8 @@
" Description: ghc for Haskell files, using Stack " Description: ghc for Haskell files, using Stack
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc', \ 'name': 'stack_ghc',
\ 'aliases': ['stack-ghc'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command': 'stack ghc -- -fno-code -v0 %t', \ 'command': 'stack ghc -- -fno-code -v0 %t',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for vim Help files " Description: write-good for vim Help files
call ale#linter#Define('help', { call ale#handlers#writegood#DefineLinter('help')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for nroff files " Description: write-good for html files
call ale#linter#Define('html', { call ale#handlers#writegood#DefineLinter('html')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,3 +1,4 @@
scriptencoding utf-8
" Author rhysd https://rhysd.github.io/, Dirk Roorda (dirkroorda), Adrián González Rus (@adrigzr) " Author rhysd https://rhysd.github.io/, Dirk Roorda (dirkroorda), Adrián González Rus (@adrigzr)
" Description: remark-lint for Markdown files " Description: remark-lint for Markdown files
call ale#Set('markdown_remark_lint_executable', 'remark') call ale#Set('markdown_remark_lint_executable', 'remark')
@ -43,7 +44,8 @@ function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort
endfunction endfunction
call ale#linter#Define('markdown', { call ale#linter#Define('markdown', {
\ 'name': 'remark-lint', \ 'name': 'remark_lint',
\ 'aliases': ['remark-lint'],
\ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable', \ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable',
\ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand', \ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand',
\ 'callback': 'ale_linters#markdown#remark_lint#Handle', \ 'callback': 'ale_linters#markdown#remark_lint#Handle',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Markdown files " Description: write-good for Markdown files
call ale#linter#Define('markdown', { call ale#handlers#writegood#DefineLinter('markdown')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for nroff files " Description: write-good for nroff files
call ale#linter#Define('nroff', { call ale#handlers#writegood#DefineLinter('nroff')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Cian Butler https://github.com/butlerx " Author: Cian Butler https://github.com/butlerx
" Description: write-good for PO files " Description: write-good for PO files
call ale#linter#Define('po', { call ale#handlers#writegood#DefineLinter('po')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Pod files " Description: write-good for Pod files
call ale#linter#Define('pod', { call ale#handlers#writegood#DefineLinter('pod')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for reStructuredText files " Description: write-good for reStructuredText files
call ale#linter#Define('rst', { call ale#handlers#writegood#DefineLinter('rst')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -9,7 +9,8 @@ endfunction
" Using CM requires that we set "lint_file: 1", since it reads the files " Using CM requires that we set "lint_file: 1", since it reads the files
" from the disk itself. " from the disk itself.
call ale#linter#Define('sml', { call ale#linter#Define('sml', {
\ 'name': 'smlnj-cm', \ 'name': 'smlnj_cm',
\ 'aliases': ['smlnj-cm'],
\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm', \ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm',
\ 'lint_file': 1, \ 'lint_file': 1,
\ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand', \ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for TeX files " Description: write-good for TeX files
call ale#linter#Define('tex', { call ale#handlers#writegood#DefineLinter('tex')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Texinfo files " Description: write-good for Texinfo files
call ale#linter#Define('texinfo', { call ale#handlers#writegood#DefineLinter('texinfo')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for text files " Description: write-good for text files
call ale#linter#Define('text', { call ale#handlers#writegood#DefineLinter('text')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for XHTML files " Description: write-good for XHTML files
call ale#linter#Define('xhtml', { call ale#handlers#writegood#DefineLinter('xhtml')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -192,12 +192,7 @@ endfunction
" Every variable name will be prefixed with 'ale_'. " Every variable name will be prefixed with 'ale_'.
function! ale#Var(buffer, variable_name) abort function! ale#Var(buffer, variable_name) abort
let l:full_name = 'ale_' . a:variable_name let l:full_name = 'ale_' . a:variable_name
let l:vars = getbufvar(str2nr(a:buffer), '', 0) let l:vars = getbufvar(str2nr(a:buffer), '', {})
if l:vars is 0
" Look for variables from deleted buffers, saved from :ALEFix
let l:vars = get(get(g:ale_fix_buffer_data, a:buffer, {}), 'vars', {})
endif
return get(l:vars, l:full_name, g:[l:full_name]) return get(l:vars, l:full_name, g:[l:full_name])
endfunction endfunction

View File

@ -34,26 +34,25 @@ function! ale#balloon#Expr() abort
endfunction endfunction
function! ale#balloon#Disable() abort function! ale#balloon#Disable() abort
if has('balloon_eval_term') if has('balloon_eval')
set noballoonevalterm set noballooneval
set balloonexpr=
endif endif
set noballooneval if has('balloon_eval_term')
set balloonexpr= set noballoonevalterm
set balloonexpr=
endif
endfunction endfunction
function! ale#balloon#Enable() abort function! ale#balloon#Enable() abort
if !has('balloon_eval') && !has('balloon_eval_term')
return
endif
if has('balloon_eval') if has('balloon_eval')
set ballooneval set ballooneval
set balloonexpr=ale#balloon#Expr()
endif endif
if has('balloon_eval_term') if has('balloon_eval_term')
set balloonevalterm set balloonevalterm
set balloonexpr=ale#balloon#Expr()
endif endif
set balloonexpr=ale#balloon#Expr()
endfunction endfunction

View File

@ -4,8 +4,11 @@
call ale#Set('c_parse_makefile', 0) call ale#Set('c_parse_makefile', 0)
let s:sep = has('win32') ? '\' : '/' let s:sep = has('win32') ? '\' : '/'
" Set just so tests can override it.
let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
function! ale#c#FindProjectRoot(buffer) abort function! ale#c#FindProjectRoot(buffer) abort
for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] for l:project_filename in g:__ale_c_project_filenames
let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename) let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename)
if !empty(l:full_path) if !empty(l:full_path)

View File

@ -336,7 +336,9 @@ function! ale#completion#ParseLSPCompletions(response) abort
endif endif
" See :help complete-items for Vim completion kinds " See :help complete-items for Vim completion kinds
if l:item.kind is s:LSP_COMPLETION_METHOD_KIND if !has_key(l:item, 'kind')
let l:kind = 'v'
elseif l:item.kind is s:LSP_COMPLETION_METHOD_KIND
let l:kind = 'm' let l:kind = 'm'
elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND
let l:kind = 'm' let l:kind = 'm'
@ -422,54 +424,65 @@ endfunction
function! s:GetLSPCompletions(linter) abort function! s:GetLSPCompletions(linter) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let l:Callback = a:linter.lsp is# 'tsserver' let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' function! OnReady(...) abort closure
let l:message = ale#lsp#tsserver_message#Completions( " If we have sent a completion request already, don't send another.
\ l:buffer, if b:ale_completion_info.request_id
\ b:ale_completion_info.line, return
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
\)
else
" Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby.
call ale#lsp#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
" this correctly.
let l:message = ale#lsp#message#Completion(
\ l:buffer,
\ b:ale_completion_info.line,
\ min([
\ b:ale_completion_info.line_length,
\ b:ale_completion_info.column,
\ ]),
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
\)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
if l:request_id
let b:ale_completion_info.conn_id = l:id
let b:ale_completion_info.request_id = l:request_id
if has_key(a:linter, 'completion_filter')
let b:ale_completion_info.completion_filter = a:linter.completion_filter
endif endif
endif
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions(
\ l:buffer,
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
\)
else
" Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
let l:message = ale#lsp#message#Completion(
\ l:buffer,
\ b:ale_completion_info.line,
\ min([
\ b:ale_completion_info.line_length,
\ b:ale_completion_info.column,
\ ]),
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
\)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
if l:request_id
let b:ale_completion_info.conn_id = l:id
let b:ale_completion_info.request_id = l:request_id
if has_key(a:linter, 'completion_filter')
let b:ale_completion_info.completion_filter = a:linter.completion_filter
endif
endif
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'completion', function('OnReady'))
endfunction endfunction
function! ale#completion#GetCompletions() abort function! ale#completion#GetCompletions() abort

View File

@ -60,43 +60,50 @@ endfunction
function! s:GoToLSPDefinition(linter, options) abort function! s:GoToLSPDefinition(linter, options) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2] let [l:line, l:column] = getcurpos()[1:2]
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
let l:Callback = a:linter.lsp is# 'tsserver' if a:linter.lsp isnot# 'tsserver'
\ ? function('ale#definition#HandleTSServerResponse') let l:column = min([l:column, len(getline(l:line))])
\ : function('ale#definition#HandleLSPResponse') endif
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' function! OnReady(...) abort closure
let l:message = ale#lsp#tsserver_message#Definition( let l:Callback = a:linter.lsp is# 'tsserver'
\ l:buffer, \ ? function('ale#definition#HandleTSServerResponse')
\ l:line, \ : function('ale#definition#HandleLSPResponse')
\ l:column call ale#lsp#RegisterCallback(l:id, l:Callback)
\)
else
" Send a message saying the buffer has changed first, or the
" definition position probably won't make sense.
call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))]) if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Definition(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" definition position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
" For LSP completions, we need to clamp the column to the length of " For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement " the line. python-language-server and perhaps others do not implement
" this correctly. " this correctly.
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column) let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
endif endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_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] = { let s:go_to_definition_map[l:request_id] = {
\ 'open_in_tab': get(a:options, 'open_in_tab', 0), \ 'open_in_tab': get(a:options, 'open_in_tab', 0),
\} \}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'definition', function('OnReady'))
endfunction endfunction
function! ale#definition#GoTo(options) abort function! ale#definition#GoTo(options) abort

View File

@ -420,9 +420,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
" The 'done' flag tells the function for applying changes when fixing " The 'done' flag tells the function for applying changes when fixing
" is complete. " is complete.
let g:ale_fix_buffer_data[a:buffer] = { let g:ale_fix_buffer_data[a:buffer] = {
\ 'vars': getbufvar(a:buffer, ''),
\ 'lines_before': getbufline(a:buffer, 1, '$'), \ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'filename': expand('#' . a:buffer . ':p'),
\ 'done': 0, \ 'done': 0,
\ 'should_save': a:fixing_flag is# 'save_file', \ 'should_save': a:fixing_flag is# 'save_file',
\ 'temporary_directory_list': [], \ 'temporary_directory_list': [],

View File

@ -59,3 +59,14 @@ function! ale#handlers#writegood#Handle(buffer, lines) abort
return l:output return l:output
endfunction endfunction
" Define the writegood linter for a given filetype.
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',
\ 'callback': 'ale#handlers#writegood#Handle',
\})
endfunction

View File

@ -93,45 +93,51 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
endfunction endfunction
function! s:ShowDetails(linter, buffer, line, column, opt) abort function! s:ShowDetails(linter, buffer, line, column, opt) abort
let l:Callback = a:linter.lsp is# 'tsserver' let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id 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 let l:language_id = l:lsp_details.language_id
if a:linter.lsp is# 'tsserver' function! OnReady(...) abort closure
let l:column = a:column let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:message = ale#lsp#tsserver_message#Quickinfo( if a:linter.lsp is# 'tsserver'
\ a:buffer, let l:column = a:column
\ a:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" hover position probably won't make sense.
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#tsserver_message#Quickinfo(
\ a:buffer,
\ a:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" hover position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:root, a:buffer)
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column) let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif
let s:hover_map[l:request_id] = { let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
\ 'buffer': a:buffer,
\ 'line': a:line, let s:hover_map[l:request_id] = {
\ 'column': l:column, \ 'buffer': a:buffer,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0), \ 'line': a:line,
\} \ 'column': l:column,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0),
\}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'hover', function('OnReady'))
endfunction endfunction
" Obtain Hover information for the specified position " Obtain Hover information for the specified position

View File

@ -15,13 +15,23 @@ function! ale#lsp#NewConnection(initialization_options) abort
" open_documents: A Dictionary mapping buffers to b:changedtick, keeping " open_documents: A Dictionary mapping buffers to b:changedtick, keeping
" track of when documents were opened, and when we last changed them. " track of when documents were opened, and when we last changed them.
" callback_list: A list of callbacks for handling LSP responses. " callback_list: A list of callbacks for handling LSP responses.
" initialization_options: Options to send to the server.
" capabilities: Features the server supports.
let l:conn = { let l:conn = {
\ 'is_tsserver': 0,
\ 'id': '', \ 'id': '',
\ 'data': '', \ 'data': '',
\ 'projects': {}, \ 'projects': {},
\ 'open_documents': {}, \ 'open_documents': {},
\ 'callback_list': [], \ 'callback_list': [],
\ 'initialization_options': a:initialization_options, \ 'initialization_options': a:initialization_options,
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ },
\} \}
call add(s:connections, l:conn) call add(s:connections, l:conn)
@ -44,6 +54,11 @@ function! s:FindConnection(key, value) abort
return {} return {}
endfunction endfunction
" Get the capabilities for a connection, or an empty Dictionary.
function! ale#lsp#GetConnectionCapabilities(id) abort
return get(s:FindConnection('id', a:id), 'capabilities', {})
endfunction
function! ale#lsp#GetNextMessageID() abort function! ale#lsp#GetNextMessageID() abort
" Use the current ID " Use the current ID
let l:id = g:ale_lsp_next_message_id let l:id = g:ale_lsp_next_message_id
@ -174,6 +189,16 @@ function! s:MarkProjectAsInitialized(conn, project) abort
" Remove the messages now. " Remove the messages now.
let a:conn.message_queue = [] let a:conn.message_queue = []
" Call capabilities callbacks queued for the project.
for [l:capability, l:Callback] in a:project.capabilities_queue
if a:conn.is_tsserver || a:conn.capabilities[l:capability]
call call(l:Callback, [a:conn.id, a:project.root])
endif
endfor
" Clear the queued callbacks now.
let a:project.capabilities_queue = []
endfunction endfunction
function! s:HandleInitializeResponse(conn, response) abort function! s:HandleInitializeResponse(conn, response) abort
@ -185,6 +210,38 @@ function! s:HandleInitializeResponse(conn, response) abort
endif endif
endfunction endfunction
" Update capabilities from the server, so we know which features the server
" supports.
function! s:UpdateCapabilities(conn, capabilities) abort
if type(a:capabilities) != type({})
return
endif
if get(a:capabilities, 'hoverProvider') is v:true
let a:conn.capabilities.hover = 1
endif
if get(a:capabilities, 'referencesProvider') is v:true
let a:conn.capabilities.references = 1
endif
if !empty(get(a:capabilities, 'completionProvider'))
let a:conn.capabilities.completion = 1
endif
if type(get(a:capabilities, 'completionProvider')) is type({})
let l:chars = get(a:capabilities.completionProvider, 'triggerCharacters')
if type(l:chars) is type([])
let a:conn.capabilities.completion_trigger_characters = l:chars
endif
endif
if get(a:capabilities, 'definitionProvider') is v:true
let a:conn.capabilities.definition = 1
endif
endfunction
function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
let l:uninitialized_projects = [] let l:uninitialized_projects = []
@ -200,6 +257,8 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
if get(a:response, 'method', '') is# '' if get(a:response, 'method', '') is# ''
if has_key(get(a:response, 'result', {}), 'capabilities') if has_key(get(a:response, 'result', {}), 'capabilities')
call s:UpdateCapabilities(a:conn, a:response.result.capabilities)
for [l:dir, l:project] in l:uninitialized_projects for [l:dir, l:project] in l:uninitialized_projects
call s:MarkProjectAsInitialized(a:conn, l:project) call s:MarkProjectAsInitialized(a:conn, l:project)
endfor endfor
@ -254,22 +313,43 @@ function! s:HandleCommandMessage(job_id, message) abort
call ale#lsp#HandleMessage(l:conn, a:message) call ale#lsp#HandleMessage(l:conn, a:message)
endfunction endfunction
function! ale#lsp#RegisterProject(conn, project_root) abort " Given a connection ID, mark it as a tsserver connection, so it will be
" handled that way.
function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn = s:FindConnection('id', a:conn_id)
if !empty(l:conn)
let l:conn.is_tsserver = 1
endif
endfunction
" Register a project for an LSP connection.
"
" This function will throw if the connection doesn't exist.
function! ale#lsp#RegisterProject(conn_id, project_root) abort
let l:conn = s:FindConnection('id', a:conn_id)
" Empty strings can't be used for Dictionary keys in NeoVim, due to E713. " Empty strings can't be used for Dictionary keys in NeoVim, due to E713.
" This appears to be a nonsensical bug in NeoVim. " This appears to be a nonsensical bug in NeoVim.
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
if !has_key(a:conn.projects, l:key) if !has_key(l:conn.projects, l:key)
" Tools without project roots are ready right away, like tsserver. " Tools without project roots are ready right away, like tsserver.
let a:conn.projects[l:key] = { let l:conn.projects[l:key] = {
\ 'root': a:project_root,
\ 'initialized': empty(a:project_root), \ 'initialized': empty(a:project_root),
\ 'init_request_id': 0, \ 'init_request_id': 0,
\ 'message_queue': [], \ 'message_queue': [],
\ 'capabilities_queue': [],
\} \}
endif endif
endfunction endfunction
function! ale#lsp#GetProject(conn, project_root) abort function! ale#lsp#GetProject(conn, project_root) abort
if empty(a:conn)
return {}
endif
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
return get(a:conn.projects, l:key, {}) return get(a:conn.projects, l:key, {})
@ -279,7 +359,7 @@ endfunction
" "
" The job ID will be returned for for the program if it ran, otherwise " The job ID will be returned for for the program if it ran, otherwise
" 0 will be returned. " 0 will be returned.
function! ale#lsp#StartProgram(executable, command, project_root, callback, initialization_options) abort function! ale#lsp#StartProgram(executable, command, init_options) abort
if !executable(a:executable) if !executable(a:executable)
return 0 return 0
endif endif
@ -287,7 +367,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
let l:conn = s:FindConnection('executable', a:executable) let l:conn = s:FindConnection('executable', a:executable)
" Get the current connection or a new one. " Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options) let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options)
let l:conn.executable = a:executable let l:conn.executable = a:executable
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id) if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
@ -305,18 +385,15 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
endif endif
let l:conn.id = l:job_id let l:conn.id = l:job_id
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
call ale#lsp#RegisterProject(l:conn, a:project_root)
return l:job_id return l:job_id
endfunction endfunction
" Connect to an address and set up a callback for handling responses. " Connect to an address and set up a callback for handling responses.
function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort function! ale#lsp#ConnectToAddress(address, init_options) abort
let l:conn = s:FindConnection('id', a:address) let l:conn = s:FindConnection('id', a:address)
" Get the current connection or a new one. " Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options) let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options)
if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id) if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id)
let l:conn.channel_id = ale#socket#Open(a:address, { let l:conn.channel_id = ale#socket#Open(a:address, {
@ -329,13 +406,21 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback, initializati
endif endif
let l:conn.id = a:address let l:conn.id = a:address
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
call ale#lsp#RegisterProject(l:conn, a:project_root)
return a:address return a:address
endfunction endfunction
" Given a connection ID and a callback, register that callback for handling
" messages if the connection exists.
function! ale#lsp#RegisterCallback(conn_id, callback) abort
let l:conn = s:FindConnection('id', a:conn_id)
if !empty(l:conn)
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
endif
endfunction
" Stop all LSP connections, closing all jobs and channels, and removing any " Stop all LSP connections, closing all jobs and channels, and removing any
" queued messages. " queued messages.
function! ale#lsp#StopAll() abort function! ale#lsp#StopAll() abort
@ -373,11 +458,6 @@ function! ale#lsp#Send(conn_id, message, ...) abort
let l:project_root = get(a:000, 0, '') let l:project_root = get(a:000, 0, '')
let l:conn = s:FindConnection('id', a:conn_id) let l:conn = s:FindConnection('id', a:conn_id)
if empty(l:conn)
return 0
endif
let l:project = ale#lsp#GetProject(l:conn, l:project_root) let l:project = ale#lsp#GetProject(l:conn, l:project_root)
if empty(l:project) if empty(l:project)
@ -411,45 +491,22 @@ function! ale#lsp#Send(conn_id, message, ...) abort
return l:id == 0 ? -1 : l:id return l:id == 0 ? -1 : l:id
endfunction endfunction
" 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. " 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. " If a document is opened, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#OpenDocument(basic_details) abort function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort
let l:d = s:ExtendDocumentDetails(a:basic_details) let l:conn = s:FindConnection('id', a:conn_id)
let l:opened = 0 let l:opened = 0
if !empty(l:d.conn) && !l:d.document_open " FIXME: Return 1 if the document is already open?
if empty(l:d.language_id) if !empty(l:conn) && !has_key(l:conn.open_documents, a:buffer)
let l:message = ale#lsp#tsserver_message#Open(l:d.buffer) if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Open(a:buffer)
else else
let l:message = ale#lsp#message#DidOpen(l:d.buffer, l:d.language_id) let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id)
endif endif
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root) call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick') let l:conn.open_documents[a:buffer] = getbufvar(a:buffer, 'changedtick')
let l:opened = 1 let l:opened = 1
endif endif
@ -458,25 +515,50 @@ endfunction
" Notify LSP servers or tsserver that a document has changed, if needed. " 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. " If a notification is sent, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#NotifyForChanges(basic_details) abort function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort
let l:d = s:ExtendDocumentDetails(a:basic_details) let l:conn = s:FindConnection('id', a:conn_id)
let l:notified = 0 let l:notified = 0
if l:d.document_open if !empty(l:conn) && has_key(l:conn.open_documents, a:buffer)
let l:new_tick = getbufvar(l:d.buffer, 'changedtick') let l:new_tick = getbufvar(a:buffer, 'changedtick')
if l:d.conn.open_documents[l:d.buffer] < l:new_tick if l:conn.open_documents[a:buffer] < l:new_tick
if empty(l:d.language_id) if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Change(l:d.buffer) let l:message = ale#lsp#tsserver_message#Change(a:buffer)
else else
let l:message = ale#lsp#message#DidChange(l:d.buffer) let l:message = ale#lsp#message#DidChange(a:buffer)
endif endif
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root) call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:d.conn.open_documents[l:d.buffer] = l:new_tick let l:conn.open_documents[a:buffer] = l:new_tick
let l:notified = 1 let l:notified = 1
endif endif
endif endif
return l:notified return l:notified
endfunction endfunction
" Given some LSP details that must contain at least `connection_id` and
" `project_root` keys,
function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:project = ale#lsp#GetProject(l:conn, a:project_root)
if empty(l:project)
return 0
endif
if type(get(l:conn.capabilities, a:capability, v:null)) isnot type(0)
throw 'Invalid capability ' . a:capability
endif
if l:project.initialized
if l:conn.is_tsserver || l:conn.capabilities[a:capability]
" The project has been initialized, so call the callback now.
call call(a:callback, [a:conn_id, a:project_root])
endif
else
" Call the callback later, once we have the information we need.
call add(l:project.capabilities_queue, [a:capability, a:callback])
endif
endfunction

View File

@ -126,10 +126,9 @@ function! ale#lsp_linter#GetOptions(buffer, linter) abort
return l:initialization_options return l:initialization_options
endfunction endfunction
" Given a buffer, an LSP linter, and a callback to register for handling " Given a buffer, an LSP linter, start up an LSP linter and get ready to
" messages, start up an LSP linter and get ready to receive errors or " receive messages for the document.
" completions. function! ale#lsp_linter#StartLSP(buffer, linter) abort
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let l:command = '' let l:command = ''
let l:address = '' let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer) let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
@ -140,16 +139,11 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {} return {}
endif endif
let l:initialization_options = ale#lsp_linter#GetOptions(a:buffer, a:linter) let l:init_options = ale#lsp_linter#GetOptions(a:buffer, a:linter)
if a:linter.lsp is# 'socket' if a:linter.lsp is# 'socket'
let l:address = ale#linter#GetAddress(a:buffer, a:linter) let l:address = ale#linter#GetAddress(a:buffer, a:linter)
let l:conn_id = ale#lsp#ConnectToAddress( let l:conn_id = ale#lsp#ConnectToAddress(l:address, l:init_options)
\ l:address,
\ l:root,
\ a:callback,
\ l:initialization_options,
\)
else else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
@ -164,14 +158,10 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let l:conn_id = ale#lsp#StartProgram( let l:conn_id = ale#lsp#StartProgram(
\ l:executable, \ l:executable,
\ l:command, \ l:command,
\ l:root, \ l:init_options,
\ a:callback,
\ l:initialization_options,
\) \)
endif endif
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
if empty(l:conn_id) if empty(l:conn_id)
if g:ale_history_enabled && !empty(l:command) if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command) call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
@ -180,6 +170,16 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {} return {}
endif endif
" tsserver behaves differently, so tell the LSP API that it is tsserver.
if a:linter.lsp is# 'tsserver'
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
endif
" Register the project now the connection is ready.
call ale#lsp#RegisterProject(l:conn_id, l:root)
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
let l:details = { let l:details = {
\ 'buffer': a:buffer, \ 'buffer': a:buffer,
\ 'connection_id': l:conn_id, \ 'connection_id': l:conn_id,
@ -188,7 +188,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
\ 'language_id': l:language_id, \ 'language_id': l:language_id,
\} \}
if ale#lsp#OpenDocument(l:details) if ale#lsp#OpenDocument(l:conn_id, l:root, a:buffer, l:language_id)
if g:ale_history_enabled && !empty(l:command) if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command) call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
endif endif
@ -196,7 +196,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
" The change message needs to be sent for tsserver before doing anything. " The change message needs to be sent for tsserver before doing anything.
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
call ale#lsp#NotifyForChanges(l:details) call ale#lsp#NotifyForChanges(l:conn_id, l:root, a:buffer)
endif endif
return l:details return l:details
@ -204,11 +204,7 @@ endfunction
function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:info = g:ale_buffer_info[a:buffer] let l:info = g:ale_buffer_info[a:buffer]
let l:lsp_details = ale#lsp_linter#StartLSP( let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
\ a:buffer,
\ a:linter,
\ function('ale#lsp_linter#HandleLSPResponse'),
\)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
@ -217,25 +213,25 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root let l:root = l:lsp_details.project_root
" Register a callback now for handling errors now.
let l:Callback = function('ale#lsp_linter#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
" Remember the linter this connection is for. " Remember the linter this connection is for.
let s:lsp_linter_map[l:id] = a:linter.name let s:lsp_linter_map[l:id] = a:linter.name
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Geterr(a:buffer) 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 = ale#lsp#Send(l:id, l:message, l:root) != 0
let l:notified = l:request_id != 0
else else
let l:notified = ale#lsp#NotifyForChanges(l:lsp_details) let l:notified = ale#lsp#NotifyForChanges(l:id, l:root, a:buffer)
endif endif
" If this was a file save event, also notify the server of that. " If this was a file save event, also notify the server of that.
if a:linter.lsp isnot# 'tsserver' if a:linter.lsp isnot# 'tsserver'
\&& getbufvar(a:buffer, 'ale_save_event_fired', 0) \&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
let l:save_message = ale#lsp#message#DidSave(a:buffer) 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 = ale#lsp#Send(l:id, l:save_message, l:root) != 0
let l:notified = l:request_id != 0
endif endif
if l:notified if l:notified

View File

@ -68,37 +68,46 @@ function! s:FindReferences(linter) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2] let [l:line, l:column] = getcurpos()[1:2]
let l:Callback = a:linter.lsp is# 'tsserver' if a:linter.lsp isnot# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse') let l:column = min([l:column, len(getline(l:line))])
\ : function('ale#references#HandleLSPResponse') endif
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' function! OnReady(...) abort closure
let l:message = ale#lsp#tsserver_message#References( let l:Callback = a:linter.lsp is# 'tsserver'
\ l:buffer, \ ? function('ale#references#HandleTSServerResponse')
\ l:line, \ : function('ale#references#HandleLSPResponse')
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" references position probably won't make sense.
call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))]) call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column) if a:linter.lsp is# 'tsserver'
endif let l:message = ale#lsp#tsserver_message#References(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" references position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
let s:references_map[l:request_id] = {} let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:references_map[l:request_id] = {}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'references', function('OnReady'))
endfunction endfunction
function! ale#references#Find() abort function! ale#references#Find() abort

View File

@ -52,3 +52,26 @@ function! ale#test#SetFilename(path) abort
silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path)) silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path))
endfunction endfunction
function! s:RemoveModule(results) abort
for l:item in a:results
if has_key(l:item, 'module')
call remove(l:item, 'module')
endif
endfor
endfunction
" Return loclist data without the module string, only in newer Vim versions.
function! ale#test#GetLoclistWithoutModule() abort
let l:results = getloclist(0)
call s:RemoveModule(l:results)
return l:results
endfunction
function! ale#test#GetQflistWithoutModule() abort
let l:results = getqflist()
call s:RemoveModule(l:results)
return l:results
endfunction

View File

@ -43,7 +43,7 @@ function! ale#toggle#Toggle() abort
call s:CleanupEveryBuffer() call s:CleanupEveryBuffer()
call s:DisablePostamble() call s:DisablePostamble()
if has('balloon_eval') if exists('*ale#balloon#Disable')
call ale#balloon#Disable() call ale#balloon#Disable()
endif endif
endif endif

View File

@ -63,6 +63,25 @@ g:ale_c_clang_options *g:ale_c_clang_options*
This variable can be changed to modify flags given to clang. This variable can be changed to modify flags given to clang.
===============================================================================
clangd *ale-c-clangd*
g:ale_c_clangd_executable *g:ale_c_clangd_executable*
*b:ale_c_clangd_executable*
Type: |String|
Default: `'clangd'`
This variable can be changed to use a different executable for clangd.
g:ale_c_clangd_options *g:ale_c_clangd_options*
*b:ale_c_clangd_options*
Type: |String|
Default: `''`
This variable can be changed to modify flags given to clangd.
=============================================================================== ===============================================================================
clang-format *ale-c-clangformat* clang-format *ale-c-clangformat*

View File

@ -115,6 +115,9 @@ these are reported with ALE's `custom-linting-rules` script. See
* Don't use the `tempname()` function. It doesn't work when `$TMPDIR` isn't * Don't use the `tempname()` function. It doesn't work when `$TMPDIR` isn't
set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR` set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR`
appropriately where needed. appropriately where needed.
* Use `snake_case` names for linter names, so they can be used as part of
variable names. You can define `aliases` for linters, for other names people
might try to configure linters with.
Apply the following guidelines when writing Vader test files. Apply the following guidelines when writing Vader test files.
@ -145,9 +148,10 @@ 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. runs tests with the following versions of Vim in the following environments.
1. Vim 8.0.0027 on Linux via Travis CI. 1. Vim 8.0.0027 on Linux via Travis CI.
2. NeoVim 0.2.0 on Linux via Travis CI. 2. Vim 8.1.0204 on Linux via Travis CI.
3. NeoVim 0.3.0 on Linux via Travis CI. 3. NeoVim 0.2.0 on Linux via Travis CI.
4. Vim 8 (stable builds) on Windows via AppVeyor. 4. NeoVim 0.3.0 on Linux via Travis CI.
5. Vim 8 (stable builds) on Windows via AppVeyor.
If you are developing ALE code on Linux, Mac OSX, or BSD, you can run ALEs If you are developing ALE code on Linux, Mac OSX, or BSD, you can run ALEs
tests by installing Docker and running the `run-tests` script. Follow the tests by installing Docker and running the `run-tests` script. Follow the

View File

@ -32,5 +32,24 @@ g:ale_fortran_gcc_use_free_form *g:ale_fortran_gcc_use_free_form*
instead, for checking files with fixed form layouts. instead, for checking files with fixed form layouts.
===============================================================================
language_server *ale-fortran-language-server*
g:ale_fortran_language_server_executable *g:ale_fortran_language_server_executable*
*b:ale_fortran_language_server_executable*
Type: |String|
Default: `'fortls'`
This variable can be changed to modify the executable used for the Fortran
Language Server.
g:ale_fortran_language_server_use_global *g:ale_fortran_language_server_use_global*
*b:ale_fortran_language_server_use_global*
Type: |Number|
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: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -22,6 +22,17 @@ g:ale_haskell_ghc_options *g:ale_haskell_ghc_options*
This variable can be changed to modify flags given to ghc. This variable can be changed to modify flags given to ghc.
===============================================================================
cabal-ghc *ale-haskell-cabal-ghc*
g:ale_haskell_cabal_ghc_options *g:ale_haskell_cabal_ghc_options*
*b:ale_haskell_cabal_ghc_options*
Type: |String|
Default: `'-fno-code -v0'`
This variable can be changed to modify flags given to ghc through cabal
exec.
=============================================================================== ===============================================================================
hdevtools *ale-haskell-hdevtools* hdevtools *ale-haskell-hdevtools*

View File

@ -26,6 +26,7 @@ CONTENTS *ale-contents*
gawk................................|ale-awk-gawk| gawk................................|ale-awk-gawk|
c.....................................|ale-c-options| c.....................................|ale-c-options|
clang...............................|ale-c-clang| clang...............................|ale-c-clang|
clangd..............................|ale-c-clangd|
clang-format........................|ale-c-clangformat| clang-format........................|ale-c-clangformat|
clangtidy...........................|ale-c-clangtidy| clangtidy...........................|ale-c-clangtidy|
cppcheck............................|ale-c-cppcheck| cppcheck............................|ale-c-cppcheck|
@ -76,6 +77,7 @@ CONTENTS *ale-contents*
fish..................................|ale-fish-options| fish..................................|ale-fish-options|
fortran...............................|ale-fortran-options| fortran...............................|ale-fortran-options|
gcc.................................|ale-fortran-gcc| gcc.................................|ale-fortran-gcc|
language_server.....................|ale-fortran-language-server|
fountain..............................|ale-fountain-options| fountain..............................|ale-fountain-options|
fusionscript..........................|ale-fuse-options| fusionscript..........................|ale-fuse-options|
fusion-lint.........................|ale-fuse-fusionlint| fusion-lint.........................|ale-fuse-fusionlint|
@ -98,6 +100,7 @@ CONTENTS *ale-contents*
haskell...............................|ale-haskell-options| haskell...............................|ale-haskell-options|
brittany............................|ale-haskell-brittany| brittany............................|ale-haskell-brittany|
ghc.................................|ale-haskell-ghc| ghc.................................|ale-haskell-ghc|
cabal-ghc...........................|ale-haskell-cabal-ghc|
hdevtools...........................|ale-haskell-hdevtools| hdevtools...........................|ale-haskell-hdevtools|
hfmt................................|ale-haskell-hfmt| hfmt................................|ale-haskell-hfmt|
stack-build.........................|ale-haskell-stack-build| stack-build.........................|ale-haskell-stack-build|
@ -333,7 +336,7 @@ Notes:
* Awk: `gawk` * Awk: `gawk`
* Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt` * Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt`
* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt` * Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt`
* C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc` * C: `cppcheck`, `cpplint`!!, `clang`, `clangd`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc`
* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc` * C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc`
* CUDA: `nvcc`!! * CUDA: `nvcc`!!
* C#: `mcs`, `mcsc`!! * C#: `mcs`, `mcsc`!!
@ -355,7 +358,7 @@ Notes:
* Erb: `erb`, `erubi`, `erubis` * Erb: `erb`, `erubi`, `erubis`
* Erlang: `erlc`, `SyntaxErl` * Erlang: `erlc`, `SyntaxErl`
* Fish: `fish` (-n flag) * Fish: `fish` (-n flag)
* Fortran: `gcc` * Fortran: `gcc`, `language_server`
* Fountain: `proselint` * Fountain: `proselint`
* FusionScript: `fusion-lint` * FusionScript: `fusion-lint`
* Git Commit Messages: `gitlint` * Git Commit Messages: `gitlint`
@ -364,7 +367,7 @@ Notes:
* GraphQL: `eslint`, `gqlint`, `prettier` * GraphQL: `eslint`, `gqlint`, `prettier`
* Haml: `haml-lint` * Haml: `haml-lint`
* Handlebars: `ember-template-lint` * Handlebars: `ember-template-lint`
* Haskell: `brittany`, `ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`, `hfmt` * Haskell: `brittany`, `ghc`, `cabal-ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`, `hfmt`
* HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `write-good` * HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `write-good`
* Idris: `idris` * Idris: `idris`
* Java: `checkstyle`, `javac`, `google-java-format`, `PMD` * Java: `checkstyle`, `javac`, `google-java-format`, `PMD`
@ -1413,8 +1416,7 @@ g:ale_set_balloons *g:ale_set_balloons*
*b:ale_set_balloons* *b:ale_set_balloons*
Type: |Number| Type: |Number|
Default: `(has('balloon_eval') && has('gui_running'))` Default: `has('balloon_eval') && has('gui_running')`
`|| (has('balloon_eval_term') && !has('gui_running'))`
When this option is set to `1`, balloon messages will be displayed for When this option is set to `1`, balloon messages will be displayed for
problems or hover information if available. problems or hover information if available.
@ -1424,6 +1426,12 @@ g:ale_set_balloons *g:ale_set_balloons*
supporting "Hover" information, per |ale-hover|, then brief information supporting "Hover" information, per |ale-hover|, then brief information
about the symbol under the cursor will be displayed in a balloon. about the symbol under the cursor will be displayed in a balloon.
Balloons can be enabled for terminal versions of Vim that support balloons,
but some versions of Vim will produce strange mouse behavior when balloons
are enabled. To configure balloons for your terminal, you should first
configure your |ttymouse| setting, and then consider setting
`g:ale_set_balloons` to `1` before ALE is loaded.
`b:ale_set_balloons` can be set to `0` to disable balloons for a buffer. `b:ale_set_balloons` can be set to `0` to disable balloons for a buffer.
Balloons cannot be enabled for a specific buffer when not initially enabled Balloons cannot be enabled for a specific buffer when not initially enabled
globally. globally.

View File

@ -110,10 +110,7 @@ let g:ale_set_highlights = get(g:, 'ale_set_highlights', has('syntax'))
let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1) let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1)
" This flag can be set to 0 to disable balloon support. " This flag can be set to 0 to disable balloon support.
let g:ale_set_balloons = get(g:, 'ale_set_balloons', let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has('gui_running'))
\ (has('balloon_eval') && has('gui_running'))
\ || (has('balloon_eval_term') && !has('gui_running'))
\)
" This flag can be set to 0 to disable warnings for trailing whitespace " This flag can be set to 0 to disable warnings for trailing whitespace
let g:ale_warn_about_trailing_whitespace = get(g:, 'ale_warn_about_trailing_whitespace', 1) let g:ale_warn_about_trailing_whitespace = get(g:, 'ale_warn_about_trailing_whitespace', 1)

View File

@ -10,7 +10,7 @@ set -u
# #
image=w0rp/ale image=w0rp/ale
current_image_id=71553d0ab3e8 current_image_id=67896c9c2c0f
# Used in all test scripts for running the selected Docker image. # Used in all test scripts for running the selected Docker image.
DOCKER_RUN_IMAGE="$image" DOCKER_RUN_IMAGE="$image"
@ -22,7 +22,8 @@ verbose_flag=''
quiet_flag='' quiet_flag=''
run_neovim_02_tests=1 run_neovim_02_tests=1
run_neovim_03_tests=1 run_neovim_03_tests=1
run_vim_tests=1 run_vim_80_tests=1
run_vim_81_tests=1
run_linters=1 run_linters=1
while [ $# -ne 0 ]; do while [ $# -ne 0 ]; do
@ -36,19 +37,22 @@ while [ $# -ne 0 ]; do
shift shift
;; ;;
--neovim-only) --neovim-only)
run_vim_tests=0 run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0 run_linters=0
shift shift
;; ;;
--neovim-02-only) --neovim-02-only)
run_neovim_03_tests=0 run_neovim_03_tests=0
run_vim_tests=0 run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0 run_linters=0
shift shift
;; ;;
--neovim-03-only) --neovim-03-only)
run_neovim_02_tests=0 run_neovim_02_tests=0
run_vim_tests=0 run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0 run_linters=0
shift shift
;; ;;
@ -58,8 +62,23 @@ while [ $# -ne 0 ]; do
run_linters=0 run_linters=0
shift shift
;; ;;
--vim-80-only)
run_neovim_02_tests=0
run_neovim_03_tests=0
run_vim_81_tests=0
run_linters=0
shift
;;
--vim-81-only)
run_neovim_02_tests=0
run_neovim_03_tests=0
run_vim_80_tests=0
run_linters=0
shift
;;
--linters-only) --linters-only)
run_vim_tests=0 run_vim_80_tests=0
run_vim_81_tests=0
run_neovim_02_tests=0 run_neovim_02_tests=0
run_neovim_03_tests=0 run_neovim_03_tests=0
shift shift
@ -76,7 +95,9 @@ while [ $# -ne 0 ]; do
echo ' --neovim-only Run tests only for NeoVim 0.2 and 0.3' echo ' --neovim-only Run tests only for NeoVim 0.2 and 0.3'
echo ' --neovim-02-only Run tests only for NeoVim 0.2' echo ' --neovim-02-only Run tests only for NeoVim 0.2'
echo ' --neovim-03-only Run tests only for NeoVim 0.3' echo ' --neovim-03-only Run tests only for NeoVim 0.3'
echo ' --vim-only Run tests only for Vim' echo ' --vim-only Run tests only for Vim 8.0 and 8.1'
echo ' --vim-80-only Run tests only for Vim 8.0'
echo ' --vim-81-only Run tests only for Vim 8.1'
echo ' --linters-only Run only Vint and custom checks' echo ' --linters-only Run only Vint and custom checks'
echo ' --help Show this help text' echo ' --help Show this help text'
echo ' -- Stop parsing options after this' echo ' -- Stop parsing options after this'
@ -120,7 +141,8 @@ file_number=0
pid_list='' pid_list=''
for vim in $(docker run --rm "$DOCKER_RUN_IMAGE" ls /vim-build/bin | grep '^neovim\|^vim' ); do for vim in $(docker run --rm "$DOCKER_RUN_IMAGE" ls /vim-build/bin | grep '^neovim\|^vim' ); do
if ( [[ $vim =~ ^vim ]] && ((run_vim_tests)) ) \ if ( [[ $vim =~ ^vim-v8.0 ]] && ((run_vim_80_tests)) ) \
|| ( [[ $vim =~ ^vim-v8.1 ]] && ((run_vim_81_tests)) ) \
|| ( [[ $vim =~ ^neovim-v0.2 ]] && ((run_neovim_02_tests)) ) \ || ( [[ $vim =~ ^neovim-v0.2 ]] && ((run_neovim_02_tests)) ) \
|| ( [[ $vim =~ ^neovim-v0.3 ]] && ((run_neovim_03_tests)) ); then || ( [[ $vim =~ ^neovim-v0.3 ]] && ((run_neovim_03_tests)) ); then
echo "Starting Vim: $vim..." echo "Starting Vim: $vim..."

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,32 @@
Before:
call ale#assert#SetUpLinterTest('c', 'clangd')
Save &filetype
let &filetype = 'c'
After:
call ale#assert#TearDownLinterTest()
Execute(The language string should be correct):
AssertLSPLanguage 'c'
Execute(The default executable should be correct):
AssertLinter 'clangd', ale#Escape('clangd')
Execute(The project root should be detected correctly):
AssertLSPProject ''
call ale#test#SetFilename('clangd_paths/dummy.c')
AssertLSPProject ale#path#Simplify(g:dir . '/clangd_paths')
Execute(The executable should be configurable):
let g:ale_c_clangd_executable = 'foobar'
AssertLinter 'foobar', ale#Escape('foobar')
Execute(The options should be configurable):
let b:ale_c_clangd_options = '-compile-commands-dir=foo'
AssertLinter 'clangd', ale#Escape('clangd') . ' ' . b:ale_c_clangd_options

View File

@ -0,0 +1,18 @@
Before:
call ale#assert#SetUpLinterTest('fortran', 'language_server')
After:
call ale#assert#TearDownLinterTest()
Execute(The default executable path should be correct):
AssertLinter 'fortls', ale#Escape('fortls')
Execute(The project root should be detected correctly):
AssertLSPProject ''
call ale#test#SetFilename('fortran-fortls-project/test.F90')
AssertLSPProject ale#path#Simplify(g:dir . '/fortran-fortls-project')
Execute(The language should be correct):
AssertLSPLanguage 'fortran'

View File

@ -0,0 +1,23 @@
Before:
Save g:ale_haskell_cabal_ghc_options
unlet! g:ale_haskell_cabal_ghc_options
unlet! b:ale_haskell_cabal_ghc_options
runtime ale_linters/haskell/cabal_ghc.vim
After:
Restore
unlet! b:ale_haskell_cabal_ghc_options
call ale#linter#Reset()
Execute(The options should be used in the command):
AssertEqual
\ '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_linters#haskell#cabal_ghc#GetCommand(bufnr(''))

View File

@ -32,8 +32,16 @@ Before:
endfunction endfunction
let g:ale_completion_delay = 0 let g:ale_completion_delay = 0
call ale#completion#Queue()
sleep 1m " Run this check a few times, as it can fail randomly.
for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#completion#Queue()
sleep 1m
if g:get_completions_called is a:expect_success
break
endif
endfor
AssertEqual a:expect_success, g:get_completions_called AssertEqual a:expect_success, g:get_completions_called
endfunction endfunction

View File

@ -13,11 +13,11 @@ Before:
runtime autoload/ale/lsp.vim runtime autoload/ale/lsp.vim
let g:message_list = [] let g:message_list = []
let g:capability_checked = ''
let g:Callback = '' let g:Callback = ''
let g:wait_callback_list = []
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort function! ale#lsp_linter#StartLSP(buffer, linter) abort
let g:Callback = a:callback
let l:conn = ale#lsp#NewConnection({}) let l:conn = ale#lsp#NewConnection({})
let l:conn.id = 347 let l:conn.id = 347
let l:conn.open_documents = {a:buffer : -1} let l:conn.open_documents = {a:buffer : -1}
@ -35,15 +35,28 @@ Before:
return 'i' return 'i'
endfunction endfunction
function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort
let g:capability_checked = a:capability
call add(g:wait_callback_list, a:callback)
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
let g:Callback = a:callback
endfunction
" Replace the Send function for LSP, so we can monitor calls to it. " Replace the Send function for LSP, so we can monitor calls to it.
function! ale#lsp#Send(conn_id, message, ...) abort function! ale#lsp#Send(conn_id, message, ...) abort
call add(g:message_list, a:message) call add(g:message_list, a:message)
return 1
endfunction endfunction
After: After:
Restore Restore
unlet! g:message_list unlet! g:message_list
unlet! g:capability_checked
unlet! g:wait_callback_list
unlet! g:Callback unlet! g:Callback
unlet! b:ale_old_omnifunc unlet! b:ale_old_omnifunc
unlet! b:ale_old_completopt unlet! b:ale_old_completopt
@ -84,6 +97,13 @@ Execute(The right message should be sent for the initial tsserver request):
call ale#completion#GetCompletions() call ale#completion#GetCompletions()
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual 1, len(g:wait_callback_list)
AssertEqual 'completion', g:capability_checked
call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])')
" We should send the right callback. " We should send the right callback.
AssertEqual AssertEqual
\ 'function(''ale#completion#HandleTSServerResponse'')', \ 'function(''ale#completion#HandleTSServerResponse'')',
@ -96,9 +116,9 @@ Execute(The right message should be sent for the initial tsserver request):
AssertEqual AssertEqual
\ { \ {
\ 'line_length': 3, \ 'line_length': 3,
\ 'conn_id': 0, \ 'conn_id': 347,
\ 'column': 3, \ 'column': 3,
\ 'request_id': 0, \ 'request_id': 1,
\ 'line': 1, \ 'line': 1,
\ 'prefix': 'fo', \ 'prefix': 'fo',
\ }, \ },
@ -164,6 +184,13 @@ Execute(The right message should be sent for the initial LSP request):
call ale#completion#GetCompletions() call ale#completion#GetCompletions()
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual 1, len(g:wait_callback_list)
AssertEqual 'completion', g:capability_checked
call map(g:wait_callback_list, 'v:val([347, ''/foo/bar''])')
" We should send the right callback. " We should send the right callback.
AssertEqual AssertEqual
\ 'function(''ale#completion#HandleLSPResponse'')', \ 'function(''ale#completion#HandleLSPResponse'')',
@ -192,10 +219,58 @@ Execute(The right message should be sent for the initial LSP request):
AssertEqual AssertEqual
\ { \ {
\ 'line_length': 3, \ 'line_length': 3,
\ 'conn_id': 0, \ 'conn_id': 347,
\ 'column': 3, \ 'column': 3,
\ 'request_id': 0, \ 'request_id': 1,
\ 'line': 1, \ 'line': 1,
\ 'prefix': 'fo', \ 'prefix': 'fo',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\ }, \ },
\ get(b:, 'ale_completion_info', {}) \ get(b:, 'ale_completion_info', {})
Execute(Two completion requests shouldn't be sent in a row):
call ale#linter#PreventLoading('python')
call ale#linter#Define('python', {
\ 'name': 'foo',
\ 'lsp': 'stdio',
\ 'executable': 'foo',
\ 'command': 'foo',
\ 'project_root_callback': {-> '/foo/bar'},
\})
call ale#linter#Define('python', {
\ 'name': 'bar',
\ 'lsp': 'stdio',
\ 'executable': 'foo',
\ 'command': 'foo',
\ 'project_root_callback': {-> '/foo/bar'},
\})
let b:ale_linters = ['foo', 'bar']
" The cursor position needs to match what was saved before.
call setpos('.', [bufnr(''), 1, 5, 0])
call ale#completion#GetCompletions()
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual 2, len(g:wait_callback_list)
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
\ [
\ [1, 'textDocument/didChange', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('%:p')),
\ 'version': g:ale_lsp_next_version_id - 1,
\ },
\ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}]
\ }],
\ [0, 'textDocument/completion', {
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
\ 'position': {'line': 0, 'character': 3},
\ }],
\ ],
\ g:message_list

View File

@ -430,10 +430,10 @@ Execute(Should handle Python completion results correctly):
\ } \ }
\ }) \ })
Execute(Should handle missing detail keys): Execute(Should handle missing keys):
AssertEqual AssertEqual
\ [ \ [
\ {'word': 'x', 'menu': '', 'info': 'y', 'kind': 'f', 'icase': 1}, \ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1},
\ ], \ ],
\ ale#completion#ParseLSPCompletions({ \ ale#completion#ParseLSPCompletions({
\ 'jsonrpc': '2.0', \ 'jsonrpc': '2.0',
@ -443,8 +443,6 @@ Execute(Should handle missing detail keys):
\ 'items': [ \ 'items': [
\ { \ {
\ 'label': 'x', \ 'label': 'x',
\ 'kind': 3,
\ 'documentation': 'y',
\ }, \ },
\ ] \ ]
\ } \ }

View File

@ -458,7 +458,7 @@ Execute(ALEFix should save files on the save event):
\ 'nr': -1, \ 'nr': -1,
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\}], getloclist(0) \}], ale#test#GetLoclistWithoutModule()
endif endif
Expect(The buffer should be modified): Expect(The buffer should be modified):
@ -497,7 +497,7 @@ Execute(ALEFix should still lint with no linters to be applied):
\ 'nr': -1, \ 'nr': -1,
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\}], getloclist(0) \}], ale#test#GetLoclistWithoutModule()
endif endif
Expect(The buffer should be the same): Expect(The buffer should be the same):
@ -531,7 +531,7 @@ Execute(ALEFix should still lint when nothing was fixed on save):
\ 'nr': -1, \ 'nr': -1,
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\}], getloclist(0) \}], ale#test#GetLoclistWithoutModule()
endif endif
Expect(The buffer should be the same): Expect(The buffer should be the same):
@ -552,8 +552,6 @@ Execute(ale#fix#InitBufferData() should set up the correct data):
AssertEqual { AssertEqual {
\ bufnr(''): { \ bufnr(''): {
\ 'temporary_directory_list': [], \ 'temporary_directory_list': [],
\ 'vars': b:,
\ 'filename': ale#path#Simplify(getcwd() . '/fix_test_file'),
\ 'done': 0, \ 'done': 0,
\ 'lines_before': ['a', 'b', 'c'], \ 'lines_before': ['a', 'b', 'c'],
\ 'should_save': 1, \ 'should_save': 1,

View File

@ -14,7 +14,6 @@ Before:
let g:ale_lsp_next_message_id = 1 let g:ale_lsp_next_message_id = 1
let g:ale_run_synchronously = 1 let g:ale_run_synchronously = 1
let g:message_list = [] let g:message_list = []
let g:Callback = ''
function! LanguageCallback() abort function! LanguageCallback() abort
return 'foobar' return 'foobar'
@ -34,9 +33,7 @@ Before:
\ }) \ })
let g:ale_linters = {'foobar': ['dummy_linter']} let g:ale_linters = {'foobar': ['dummy_linter']}
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort function! ale#lsp_linter#StartLSP(buffer, linter) abort
let g:Callback = a:callback
let l:conn = ale#lsp#NewConnection({}) let l:conn = ale#lsp#NewConnection({})
let l:conn.id = 347 let l:conn.id = 347
let l:conn.open_documents = {a:buffer : -1} let l:conn.open_documents = {a:buffer : -1}
@ -59,7 +56,6 @@ After:
unlet! b:ale_enabled unlet! b:ale_enabled
unlet! b:ale_linters unlet! b:ale_linters
unlet! g:Callback
unlet! g:message_list unlet! g:message_list
delfunction LanguageCallback delfunction LanguageCallback

View File

@ -23,15 +23,14 @@ Execute(Command formatting should be applied correctly for LSP linters):
\ 'executable': has('win32') ? 'cmd': 'true', \ 'executable': has('win32') ? 'cmd': 'true',
\ 'command': '%e --foo', \ 'command': '%e --foo',
\ }, \ },
\ {->0}
\) \)
if has('win32') if has('win32')
AssertEqual AssertEqual
\ ['cmd', 'cmd /s/c "cmd --foo"', '/foo/bar'], \ ['cmd', 'cmd /s/c "cmd --foo"', {}],
\ g:args[:2] \ g:args
else else
AssertEqual AssertEqual
\ ['true', [&shell, '-c', '''true'' --foo'], '/foo/bar'], \ ['true', [&shell, '-c', '''true'' --foo'], {}],
\ g:args[:2] \ g:args
endif endif

View File

@ -2,6 +2,10 @@ Before:
let g:ale_lsp_next_message_id = 1 let g:ale_lsp_next_message_id = 1
After: After:
if exists('b:conn') && has_key(b:conn, 'id')
call ale#lsp#RemoveConnectionWithID(b:conn.id)
endif
unlet! b:data unlet! b:data
unlet! b:conn unlet! b:conn
@ -223,17 +227,20 @@ Execute(ale#lsp#ReadMessageData() should handle a message with part of a second
\ ) \ )
Execute(Projects with regular project roots should be registered correctly): Execute(Projects with regular project roots should be registered correctly):
let b:conn = {'projects': {}} let b:conn = ale#lsp#NewConnection({})
call ale#lsp#RegisterProject(b:conn.id, '/foo/bar')
call ale#lsp#RegisterProject(b:conn, '/foo/bar')
AssertEqual AssertEqual
\ { \ {
\ 'projects': { \ '/foo/bar': {
\ '/foo/bar': {'initialized': 0, 'message_queue': [], 'init_request_id': 0}, \ 'root': '/foo/bar',
\ 'initialized': 0,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ 'init_request_id': 0,
\ }, \ },
\ }, \ },
\ b:conn \ b:conn.projects
Execute(Projects with regular project roots should be fetched correctly): Execute(Projects with regular project roots should be fetched correctly):
let b:conn = { let b:conn = {
@ -247,17 +254,20 @@ Execute(Projects with regular project roots should be fetched correctly):
\ ale#lsp#GetProject(b:conn, '/foo/bar') \ ale#lsp#GetProject(b:conn, '/foo/bar')
Execute(Projects with empty project roots should be registered correctly): Execute(Projects with empty project roots should be registered correctly):
let b:conn = {'projects': {}} let b:conn = ale#lsp#NewConnection({})
call ale#lsp#RegisterProject(b:conn.id, '')
call ale#lsp#RegisterProject(b:conn, '')
AssertEqual AssertEqual
\ { \ {
\ 'projects': { \ '<<EMPTY>>': {
\ '<<EMPTY>>': {'initialized': 1, 'message_queue': [], 'init_request_id': 0}, \ 'root': '',
\ 'initialized': 1,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ 'init_request_id': 0,
\ }, \ },
\ }, \ },
\ b:conn \ b:conn.projects
Execute(Projects with empty project roots should be fetched correctly): Execute(Projects with empty project roots should be fetched correctly):
let b:conn = { let b:conn = {

View File

@ -3,12 +3,20 @@ Before:
\ 'initialized': 0, \ 'initialized': 0,
\ 'init_request_id': 3, \ 'init_request_id': 3,
\ 'message_queue': [], \ 'message_queue': [],
\ 'capabilities_queue': [],
\} \}
let b:conn = { let b:conn = {
\ 'projects': { \ 'projects': {
\ '/foo/bar': b:project, \ '/foo/bar': b:project,
\ }, \ },
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ },
\} \}
After: After:
@ -27,6 +35,7 @@ Execute(publishDiagnostics messages with files inside project directories should
\ 'initialized': 0, \ 'initialized': 0,
\ 'init_request_id': 3, \ 'init_request_id': 3,
\ 'message_queue': [], \ 'message_queue': [],
\ 'capabilities_queue': [],
\ }, \ },
\ b:project \ b:project
@ -40,6 +49,7 @@ Execute(publishDiagnostics messages with files inside project directories should
\ 'initialized': 1, \ 'initialized': 1,
\ 'init_request_id': 3, \ 'init_request_id': 3,
\ 'message_queue': [], \ 'message_queue': [],
\ 'capabilities_queue': [],
\ }, \ },
\ b:project \ b:project
@ -53,6 +63,7 @@ Execute(Messages with no method and capabilities should initialize projects):
\ 'initialized': 1, \ 'initialized': 1,
\ 'init_request_id': 3, \ 'init_request_id': 3,
\ 'message_queue': [], \ 'message_queue': [],
\ 'capabilities_queue': [],
\ }, \ },
\ b:project \ b:project
@ -64,3 +75,109 @@ Execute(Other messages should not initialize projects):
call ale#lsp#HandleOtherInitializeResponses(b:conn, {'result': {'x': {}}}) call ale#lsp#HandleOtherInitializeResponses(b:conn, {'result': {'x': {}}})
AssertEqual 0, b:project.initialized AssertEqual 0, b:project.initialized
Execute(Capabilities should bet set up correctly):
call ale#lsp#HandleOtherInitializeResponses(b:conn, {
\ '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
\ },
\ },
\})
AssertEqual
\ {
\ 'capabilities': {
\ 'completion_trigger_characters': ['.'],
\ 'completion': 1,
\ 'references': 1,
\ 'hover': 1,
\ 'definition': 1,
\ },
\ 'message_queue': [],
\ 'projects': {
\ '/foo/bar': {
\ 'initialized': 1,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ 'init_request_id': 3,
\ },
\ },
\ },
\ b:conn
Execute(Disabled capabilities should be recognised correctly):
call ale#lsp#HandleOtherInitializeResponses(b:conn, {
\ 'jsonrpc': '2.0',
\ 'id': 1,
\ 'result': {
\ 'capabilities': {
\ 'renameProvider': v:true,
\ 'executeCommandProvider': {
\ 'commands': [],
\ },
\ 'hoverProvider': v:false,
\ 'documentSymbolProvider': v:true,
\ 'documentRangeFormattingProvider': v:true,
\ 'codeLensProvider': {
\ 'resolveProvider': v:false
\ },
\ 'referencesProvider': v:false,
\ 'textDocumentSync': 2,
\ 'documentFormattingProvider': v:true,
\ 'codeActionProvider': v:true,
\ 'signatureHelpProvider': {
\ 'triggerCharacters': ['(', ','],
\ },
\ 'definitionProvider': v:false,
\ 'experimental': {},
\ 'documentHighlightProvider': v:true
\ },
\ },
\})
AssertEqual
\ {
\ 'capabilities': {
\ 'completion_trigger_characters': [],
\ 'completion': 0,
\ 'references': 0,
\ 'hover': 0,
\ 'definition': 0,
\ },
\ 'message_queue': [],
\ 'projects': {
\ '/foo/bar': {
\ 'initialized': 1,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ 'init_request_id': 3,
\ },
\ },
\ },
\ b:conn

View File

@ -52,12 +52,17 @@ directories=("$@")
check_errors() { check_errors() {
regex="$1" regex="$1"
message="$2" message="$2"
include_arg=''
if [ $# -gt 2 ]; then
include_arg="--include $3"
fi
for directory in "${directories[@]}"; do for directory in "${directories[@]}"; do
while IFS= read -r match; do while IFS= read -r match; do
RETURN_CODE=1 RETURN_CODE=1
echo "$match $message" echo "$match $message"
done < <(grep -n "$regex" "$directory"/**/*.vim \ done < <(grep -n "$regex" $include_arg "$directory"/**/*.vim \
| grep -v 'no-custom-checks' \ | grep -v 'no-custom-checks' \
| grep -o '^[^:]\+:[0-9]\+' \ | grep -o '^[^:]\+:[0-9]\+' \
| sed 's:^\./::') | sed 's:^\./::')
@ -75,6 +80,7 @@ if (( FIX_ERRORS )); then
done done
fi fi
# The arguments are: regex, explanation, [filename_filter]
check_errors \ check_errors \
'^function.*) *$' \ '^function.*) *$' \
'Function without abort keyword (See :help except-compat)' 'Function without abort keyword (See :help except-compat)'
@ -95,5 +101,6 @@ check_errors '==?' "Use 'is?' instead of '==?'. 0 ==? 'foobar' is true"
check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false" check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false"
check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false" check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false"
check_errors '^ *:\?echo' "Stray echo line. Use \`execute echo\` if you want to echo something" check_errors '^ *:\?echo' "Stray echo line. Use \`execute echo\` if you want to echo something"
check_errors $'name.:.*\'[a-z_]*[^a-z_0-9][a-z_0-9]*\',$' 'Use snake_case names for linters' '*/ale_linters/*'
exit $RETURN_CODE exit $RETURN_CODE

View File

@ -35,7 +35,6 @@ After:
unlet! g:i unlet! g:i
unlet! g:results unlet! g:results
unlet! g:item
unlet! g:expected_results unlet! g:expected_results
delfunction TestCallback delfunction TestCallback
@ -69,13 +68,7 @@ Execute(Linters should run with the default options):
call ale#Lint() call ale#Lint()
call ale#engine#WaitForJobs(2000) call ale#engine#WaitForJobs(2000)
let g:results = getloclist(0) let g:results = ale#test#GetLoclistWithoutModule()
for g:item in g:results
if has_key(g:item, 'module')
call remove(g:item, 'module')
endif
endfor
if g:results == g:expected_results if g:results == g:expected_results
break break
@ -142,7 +135,7 @@ Execute(Linters should run in PowerShell too):
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\ }, \ },
\], getloclist(0) \], ale#test#GetLoclistWithoutModule()
endif endif
Execute(Previous errors should be removed when linters change): Execute(Previous errors should be removed when linters change):
@ -176,13 +169,7 @@ Execute(Previous errors should be removed when linters change):
call ale#Lint() call ale#Lint()
call ale#engine#WaitForJobs(2000) call ale#engine#WaitForJobs(2000)
let g:results = getloclist(0) let g:results = ale#test#GetLoclistWithoutModule()
for g:item in g:results
if has_key(g:item, 'module')
call remove(g:item, 'module')
endif
endfor
if g:results == g:expected_results if g:results == g:expected_results
break break

View File

@ -66,10 +66,10 @@ Execute(ALELint should run the linters):
sleep 1ms sleep 1ms
endif endif
if getloclist(0) == g:expected_loclist if ale#test#GetLoclistWithoutModule() == g:expected_loclist
break break
endif endif
endfor endfor
" Check the loclist " Check the loclist
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()

View File

@ -115,7 +115,7 @@ Execute(ALEToggle should reset everything and then run again):
ALELint ALELint
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -128,7 +128,7 @@ Execute(ALEToggle should reset everything and then run again):
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
AssertEqual [], getloclist(0), 'The loclist was not cleared' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared'
AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual [], getmatches(), 'The highlights were not cleared' AssertEqual [], getmatches(), 'The highlights were not cleared'
AssertEqual g:expected_groups, ParseAuGroups() AssertEqual g:expected_groups, ParseAuGroups()
@ -136,7 +136,7 @@ Execute(ALEToggle should reset everything and then run again):
" Toggle ALE on, everything should be set up and run again. " Toggle ALE on, everything should be set up and run again.
ALEToggle ALEToggle
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -189,16 +189,16 @@ Execute(ALEToggle should skip filename keys and preserve them):
Execute(ALEDisable should reset everything and stay disabled): Execute(ALEDisable should reset everything and stay disabled):
ALELint ALELint
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisable ALEDisable
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled AssertEqual 0, g:ale_enabled
ALEDisable ALEDisable
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, g:ale_enabled AssertEqual 0, g:ale_enabled
Execute(ALEEnable should enable ALE and lint again): Execute(ALEEnable should enable ALE and lint again):
@ -206,7 +206,7 @@ Execute(ALEEnable should enable ALE and lint again):
ALEEnable ALEEnable
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, g:ale_enabled AssertEqual 1, g:ale_enabled
Execute(ALEReset should reset everything for a buffer): Execute(ALEReset should reset everything for a buffer):
@ -215,7 +215,7 @@ Execute(ALEReset should reset everything for a buffer):
ALELint ALELint
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -227,7 +227,7 @@ Execute(ALEReset should reset everything for a buffer):
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
AssertEqual [], getloclist(0), 'The loclist was not cleared' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared'
AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual [], getmatches(), 'The highlights were not cleared' AssertEqual [], getmatches(), 'The highlights were not cleared'
@ -239,7 +239,7 @@ Execute(ALEToggleBuffer should reset everything and then run again):
ALELint ALELint
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -251,14 +251,14 @@ Execute(ALEToggleBuffer should reset everything and then run again):
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
AssertEqual [], getloclist(0), 'The loclist was not cleared' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared'
AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual [], getmatches(), 'The highlights were not cleared' AssertEqual [], getmatches(), 'The highlights were not cleared'
" Toggle ALE on, everything should be set up and run again. " Toggle ALE on, everything should be set up and run again.
ALEToggleBuffer ALEToggleBuffer
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -269,11 +269,11 @@ Execute(ALEToggleBuffer should reset everything and then run again):
Execute(ALEDisableBuffer should reset everything and stay disabled): Execute(ALEDisableBuffer should reset everything and stay disabled):
ALELint ALELint
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
ALEDisableBuffer ALEDisableBuffer
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, b:ale_enabled AssertEqual 0, b:ale_enabled
Execute(ALEEnableBuffer should enable ALE and lint again): Execute(ALEEnableBuffer should enable ALE and lint again):
@ -281,7 +281,7 @@ Execute(ALEEnableBuffer should enable ALE and lint again):
ALEEnableBuffer ALEEnableBuffer
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual 1, b:ale_enabled AssertEqual 1, b:ale_enabled
Execute(ALEEnableBuffer should complain when ALE is disabled globally): Execute(ALEEnableBuffer should complain when ALE is disabled globally):
@ -292,7 +292,7 @@ Execute(ALEEnableBuffer should complain when ALE is disabled globally):
ALEEnableBuffer ALEEnableBuffer
redir END redir END
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 0, b:ale_enabled AssertEqual 0, b:ale_enabled
AssertEqual 0, g:ale_enabled AssertEqual 0, g:ale_enabled
AssertEqual AssertEqual
@ -305,7 +305,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
ALELint ALELint
" First check that everything is there... " First check that everything is there...
AssertEqual g:expected_loclist, getloclist(0) AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule()
AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%'))
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
@ -317,7 +317,7 @@ Execute(ALEResetBuffer should reset everything for a buffer):
" Everything should be cleared. " Everything should be cleared.
Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
AssertEqual [], getloclist(0), 'The loclist was not cleared' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared'
AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual [], getmatches(), 'The highlights were not cleared' AssertEqual [], getmatches(), 'The highlights were not cleared'
@ -326,40 +326,60 @@ Execute(ALEResetBuffer should reset everything for a buffer):
Execute(Disabling ALE should disable balloons): Execute(Disabling ALE should disable balloons):
" These tests won't run in the console, but we can run them manually in GVim. " These tests won't run in the console, but we can run them manually in GVim.
if has('balloon_eval') && has('gui_running') || if has('balloon_eval') && has('gui_running')
\ has('balloon_eval_term') && !has('gui_running') \|| (has('balloon_eval_term') && !has('gui_running'))
call ale#linter#Reset() call ale#linter#Reset()
" Enable balloons, so we can check the expr value. " Enable balloons, so we can check the expr value.
call ale#balloon#Enable() call ale#balloon#Enable()
AssertEqual 1, &ballooneval if has('balloon_eval') && has('gui_running')
AssertEqual 1, &ballooneval
else
AssertEqual 1, &balloonevalterm
endif
AssertEqual 'ale#balloon#Expr()', &balloonexpr AssertEqual 'ale#balloon#Expr()', &balloonexpr
" Toggle ALE off. " Toggle ALE off.
ALEToggle ALEToggle
" The balloon settings should be reset. " The balloon settings should be reset.
AssertEqual 0, &ballooneval if has('balloon_eval') && has('gui_running')
AssertEqual 0, &ballooneval
else
AssertEqual 0, &balloonevalterm
endif
AssertEqual '', &balloonexpr AssertEqual '', &balloonexpr
endif endif
Execute(Enabling ALE should enable balloons if the setting is on): Execute(Enabling ALE should enable balloons if the setting is on):
if has('balloon_eval') && has('gui_running') || if has('balloon_eval') && has('gui_running')
\ has('balloon_eval_term') && !has('gui_running') \|| (has('balloon_eval_term') && !has('gui_running'))
call ale#linter#Reset() call ale#linter#Reset()
call ale#balloon#Disable() call ale#balloon#Disable()
ALEDisable ALEDisable
let g:ale_set_balloons = 0 let g:ale_set_balloons = 0
ALEEnable ALEEnable
AssertEqual 0, &ballooneval if has('balloon_eval') && has('gui_running')
AssertEqual 0, &ballooneval
else
AssertEqual 0, &balloonevalterm
endif
AssertEqual '', &balloonexpr AssertEqual '', &balloonexpr
ALEDisable ALEDisable
let g:ale_set_balloons = 1 let g:ale_set_balloons = 1
ALEEnable ALEEnable
AssertEqual 1, &ballooneval if has('balloon_eval') && has('gui_running')
AssertEqual 1, &ballooneval
else
AssertEqual 1, &balloonevalterm
endif
AssertEqual 'ale#balloon#Expr()', &balloonexpr AssertEqual 'ale#balloon#Expr()', &balloonexpr
endif endif

View File

@ -5,8 +5,6 @@ After:
unlet! g:ale_some_variable unlet! g:ale_some_variable
unlet! b:undefined_variable_name unlet! b:undefined_variable_name
let g:ale_fix_buffer_data = {}
Execute(ale#Var should return global variables): Execute(ale#Var should return global variables):
AssertEqual 'abc', ale#Var(bufnr(''), 'some_variable') AssertEqual 'abc', ale#Var(bufnr(''), 'some_variable')
@ -24,13 +22,3 @@ Execute(ale#Var should throw exceptions for undefined variables):
let b:undefined_variable_name = 'def' let b:undefined_variable_name = 'def'
AssertThrows call ale#Var(bufnr(''), 'undefined_variable_name') AssertThrows call ale#Var(bufnr(''), 'undefined_variable_name')
Execute(ale#Var return variables from deleted buffers, saved for fixing things):
let g:ale_fix_buffer_data[1347347] = {'vars': {'ale_some_variable': 'def'}}
AssertEqual 'def', ale#Var(1347347, 'some_variable')
Execute(ale#Var should return the global variable for unknown variables):
let g:ale_fix_buffer_data = {}
AssertEqual 'abc', ale#Var(1347347, 'some_variable')

View File

@ -1,8 +1,21 @@
Before: Before:
" Make sure the c.vim file is loaded first.
call ale#c#FindProjectRoot(bufnr(''))
Save g:ale_c_gcc_options Save g:ale_c_gcc_options
Save g:ale_c_clang_options Save g:ale_c_clang_options
Save g:ale_cpp_gcc_options Save g:ale_cpp_gcc_options
Save g:ale_cpp_clang_options Save g:ale_cpp_clang_options
Save g:__ale_c_project_filenames
let g:original_project_filenames = g:__ale_c_project_filenames
" Remove the .git/HEAD dir for C import paths for these tests.
" The tests run inside of a git repo.
let g:__ale_c_project_filenames = filter(
\ copy(g:__ale_c_project_filenames),
\ 'v:val isnot# ''.git/HEAD'''
\)
call ale#test#SetDirectory('/testplugin/test') call ale#test#SetDirectory('/testplugin/test')
@ -14,23 +27,11 @@ Before:
After: After:
Restore Restore
unlet! g:original_project_filenames
call ale#test#RestoreDirectory() call ale#test#RestoreDirectory()
call ale#linter#Reset() call ale#linter#Reset()
" Run this only once for this series of tests. The cleanup Execute step
" will run at the bottom of this file.
"
" We need to move .git/HEAD away so we don't match it, as we need to test
" functions which look for .git/HEAD.
Execute(Move .git/HEAD to a temp dir):
let g:temp_head_filename = tempname()
let g:head_filename = findfile('.git/HEAD', ';')
if !empty(g:head_filename)
call writefile(readfile(g:head_filename, 'b'), g:temp_head_filename, 'b')
call delete(g:head_filename)
endif
Execute(The C GCC handler should include 'include' directories for projects with a Makefile): Execute(The C GCC handler should include 'include' directories for projects with a Makefile):
runtime! ale_linters/c/gcc.vim runtime! ale_linters/c/gcc.vim
@ -239,27 +240,6 @@ Execute(The C++ Clang handler should include root directories for projects with
\ . ' -' \ . ' -'
\ , ale_linters#cpp#clang#GetCommand(bufnr(''), []) \ , ale_linters#cpp#clang#GetCommand(bufnr(''), [])
Execute(The C++ Clang handler shoud use the include directory based on the .git location):
runtime! ale_linters/cpp/clang.vim
if !isdirectory(g:dir . '/test_c_projects/git_and_nested_makefiles/.git')
call mkdir(g:dir . '/test_c_projects/git_and_nested_makefiles/.git')
endif
if !filereadable(g:dir . '/test_c_projects/git_and_nested_makefiles/.git/HEAD')
call writefile([], g:dir . '/test_c_projects/git_and_nested_makefiles/.git/HEAD')
endif
call ale#test#SetFilename('test_c_projects/git_and_nested_makefiles/src/file.cpp')
AssertEqual
\ ale#Escape('clang++')
\ . ' -S -x c++ -fsyntax-only '
\ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/git_and_nested_makefiles/src')) . ' '
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/git_and_nested_makefiles/include')) . ' '
\ . ' -'
\ , ale_linters#cpp#clang#GetCommand(bufnr(''), [])
Execute(The C++ ClangTidy handler should include json folders for projects with suitable build directory in them): Execute(The C++ ClangTidy handler should include json folders for projects with suitable build directory in them):
runtime! ale_linters/cpp/clangtidy.vim runtime! ale_linters/cpp/clangtidy.vim
@ -270,12 +250,3 @@ Execute(The C++ ClangTidy handler should include json folders for projects with
\ . ' -checks=' . ale#Escape('*') . ' %s ' \ . ' -checks=' . ale#Escape('*') . ' %s '
\ . '-p ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/json_project/build')) \ . '-p ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/json_project/build'))
\ , ale_linters#cpp#clangtidy#GetCommand(bufnr('')) \ , ale_linters#cpp#clangtidy#GetCommand(bufnr(''))
Execute(Move .git/HEAD back):
if !empty(g:head_filename)
call writefile(readfile(g:temp_head_filename, 'b'), g:head_filename, 'b')
call delete(g:temp_head_filename)
endif
unlet! g:temp_head_filename
unlet! g:head_filename

View File

@ -68,7 +68,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ 'pattern': '', \ 'pattern': '',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
" After we get empty syntax errors, we should clear them. " After we get empty syntax errors, we should clear them.
call ale#lsp_linter#HandleLSPResponse(1, { call ale#lsp_linter#HandleLSPResponse(1, {
@ -85,7 +85,7 @@ Execute(tsserver syntax error responses should be handled correctly):
AssertEqual AssertEqual
\ [ \ [
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
Execute(tsserver semantic error responses should be handled correctly): Execute(tsserver semantic error responses should be handled correctly):
runtime ale_linters/typescript/tsserver.vim runtime ale_linters/typescript/tsserver.vim
@ -141,7 +141,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ 'pattern': '', \ 'pattern': '',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
" After we get empty syntax errors, we should clear them. " After we get empty syntax errors, we should clear them.
call ale#lsp_linter#HandleLSPResponse(1, { call ale#lsp_linter#HandleLSPResponse(1, {
@ -158,7 +158,7 @@ Execute(tsserver semantic error responses should be handled correctly):
AssertEqual AssertEqual
\ [ \ [
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
Execute(LSP errors should be logged in the history): Execute(LSP errors should be logged in the history):
call ale#lsp_linter#SetLSPLinterMap({'347': 'foobar'}) call ale#lsp_linter#SetLSPLinterMap({'347': 'foobar'})

View File

@ -53,7 +53,7 @@ Execute(Error should be removed when the filetype changes to something else we c
call ale#Queue(0) call ale#Queue(0)
sleep 1ms sleep 1ms
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
noautocmd let &filetype = 'foobar2' noautocmd let &filetype = 'foobar2'
@ -61,11 +61,11 @@ Execute(Error should be removed when the filetype changes to something else we c
sleep 1ms sleep 1ms
" We should get some items from the second filetype. " We should get some items from the second filetype.
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
noautocmd let &filetype = 'xxx' noautocmd let &filetype = 'xxx'
call ale#Queue(0) call ale#Queue(0)
sleep 1ms sleep 1ms
AssertEqual 0, len(getloclist(0)) AssertEqual 0, len(ale#test#GetLoclistWithoutModule())

View File

@ -22,7 +22,7 @@ Execute(The defaults for the csh filetype should be correct):
AssertEqual [], GetLinterNames('csh') AssertEqual [], GetLinterNames('csh')
Execute(The defaults for the go filetype should be correct): Execute(The defaults for the go filetype should be correct):
AssertEqual ['gofmt', 'golint', 'go vet'], GetLinterNames('go') AssertEqual ['gofmt', 'golint', 'govet'], GetLinterNames('go')
let g:ale_linters_explicit = 1 let g:ale_linters_explicit = 1

View File

@ -3,20 +3,20 @@ Before:
call ale#test#SetFilename('dummy.txt') call ale#test#SetFilename('dummy.txt')
let g:old_filename = expand('%:p') let g:old_filename = expand('%:p')
let g:Callback = 0 let g:Callback = ''
let g:expr_list = [] let g:expr_list = []
let g:message_list = [] let g:message_list = []
let g:preview_called = 0 let g:preview_called = 0
let g:item_list = [] let g:item_list = []
let g:capability_checked = ''
let g:WaitCallback = v:null
runtime autoload/ale/linter.vim runtime autoload/ale/linter.vim
runtime autoload/ale/lsp.vim runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim runtime autoload/ale/util.vim
runtime autoload/ale/preview.vim runtime autoload/ale/preview.vim
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort function! ale#lsp_linter#StartLSP(buffer, linter) abort
let g:Callback = a:callback
let l:conn = ale#lsp#NewConnection({}) let l:conn = ale#lsp#NewConnection({})
let l:conn.id = 347 let l:conn.id = 347
let l:conn.open_documents = {a:buffer : -1} let l:conn.open_documents = {a:buffer : -1}
@ -29,6 +29,15 @@ Before:
\} \}
endfunction endfunction
function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort
let g:capability_checked = a:capability
let g:WaitCallback = a:callback
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
let g:Callback = a:callback
endfunction
function! ale#lsp#Send(conn_id, message, root) abort function! ale#lsp#Send(conn_id, message, root) abort
call add(g:message_list, a:message) call add(g:message_list, a:message)
@ -50,6 +59,8 @@ After:
call ale#test#RestoreDirectory() call ale#test#RestoreDirectory()
call ale#linter#Reset() call ale#linter#Reset()
unlet! g:capability_checked
unlet! g:WaitCallback
unlet! g:old_filename unlet! g:old_filename
unlet! g:Callback unlet! g:Callback
unlet! g:message_list unlet! g:message_list
@ -152,6 +163,13 @@ Execute(tsserver reference requests should be sent):
ALEFindReferences ALEFindReferences
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#references#HandleTSServerResponse'')', \ 'function(''ale#references#HandleTSServerResponse'')',
\ string(g:Callback) \ string(g:Callback)
@ -226,6 +244,13 @@ Execute(LSP reference requests should be sent):
ALEFindReferences ALEFindReferences
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#references#HandleLSPResponse'')', \ 'function(''ale#references#HandleLSPResponse'')',
\ string(g:Callback) \ string(g:Callback)

View File

@ -3,17 +3,17 @@ Before:
call ale#test#SetFilename('dummy.txt') call ale#test#SetFilename('dummy.txt')
let g:old_filename = expand('%:p') let g:old_filename = expand('%:p')
let g:Callback = 0 let g:Callback = ''
let g:message_list = [] let g:message_list = []
let g:expr_list = [] let g:expr_list = []
let g:capability_checked = ''
let g:WaitCallback = v:null
runtime autoload/ale/linter.vim runtime autoload/ale/linter.vim
runtime autoload/ale/lsp.vim runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim runtime autoload/ale/util.vim
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort function! ale#lsp_linter#StartLSP(buffer, linter) abort
let g:Callback = a:callback
let l:conn = ale#lsp#NewConnection({}) let l:conn = ale#lsp#NewConnection({})
let l:conn.id = 347 let l:conn.id = 347
let l:conn.open_documents = {a:buffer : -1} let l:conn.open_documents = {a:buffer : -1}
@ -26,6 +26,15 @@ Before:
\} \}
endfunction endfunction
function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort
let g:capability_checked = a:capability
let g:WaitCallback = a:callback
endfunction
function! ale#lsp#RegisterCallback(conn_id, callback) abort
let g:Callback = a:callback
endfunction
function! ale#lsp#Send(conn_id, message, root) abort function! ale#lsp#Send(conn_id, message, root) abort
call add(g:message_list, a:message) call add(g:message_list, a:message)
@ -42,6 +51,8 @@ After:
call ale#test#RestoreDirectory() call ale#test#RestoreDirectory()
call ale#linter#Reset() call ale#linter#Reset()
unlet! g:capability_checked
unlet! g:WaitCallback
unlet! g:old_filename unlet! g:old_filename
unlet! g:Callback unlet! g:Callback
unlet! g:message_list unlet! g:message_list
@ -137,6 +148,13 @@ Execute(tsserver completion requests should be sent):
ALEGoToDefinition ALEGoToDefinition
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#definition#HandleTSServerResponse'')', \ 'function(''ale#definition#HandleTSServerResponse'')',
\ string(g:Callback) \ string(g:Callback)
@ -151,6 +169,13 @@ Execute(tsserver tab completion requests should be sent):
ALEGoToDefinitionInTab ALEGoToDefinitionInTab
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#definition#HandleTSServerResponse'')', \ 'function(''ale#definition#HandleTSServerResponse'')',
\ string(g:Callback) \ string(g:Callback)
@ -276,6 +301,13 @@ Execute(LSP completion requests should be sent):
ALEGoToDefinition ALEGoToDefinition
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')', \ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback) \ string(g:Callback)
@ -305,6 +337,13 @@ Execute(LSP tab completion requests should be sent):
ALEGoToDefinitionInTab ALEGoToDefinitionInTab
" 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, [347, '/foo/bar'])
AssertEqual AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')', \ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback) \ string(g:Callback)

View File

@ -51,7 +51,7 @@ Before:
function! GetSimplerLoclist() function! GetSimplerLoclist()
let l:loclist = [] let l:loclist = []
for l:item in getloclist(0) for l:item in ale#test#GetLoclistWithoutModule()
call add(l:loclist, { call add(l:loclist, {
\ 'lnum': l:item.lnum, \ 'lnum': l:item.lnum,
\ 'col': l:item.col, \ 'col': l:item.col,

View File

@ -61,7 +61,7 @@ Execute(The file changed event function should lint the current buffer when it h
\ 'nr': -1, \ 'nr': -1,
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\ }], getloclist(0) \ }], ale#test#GetLoclistWithoutModule()
Execute(The buffer should be checked after entering it after the file has changed): Execute(The buffer should be checked after entering it after the file has changed):
let b:ale_file_changed = 1 let b:ale_file_changed = 1
@ -79,4 +79,4 @@ Execute(The buffer should be checked after entering it after the file has change
\ 'nr': -1, \ 'nr': -1,
\ 'pattern': '', \ 'pattern': '',
\ 'valid': 1, \ 'valid': 1,
\ }], getloclist(0) \ }], ale#test#GetLoclistWithoutModule()

View File

@ -53,7 +53,7 @@ Execute(Formatting with codes should work for the loclist):
\ 'text': 'nocode', \ 'text': 'nocode',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
call remove(g:loclist, 0) call remove(g:loclist, 0)
call AddItem({'text': 'withcode', 'code': 'E123'}) call AddItem({'text': 'withcode', 'code': 'E123'})
@ -73,7 +73,7 @@ Execute(Formatting with codes should work for the loclist):
\ 'text': 'E123: withcode', \ 'text': 'E123: withcode',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
Execute(Formatting with codes should work for the quickfix list): Execute(Formatting with codes should work for the quickfix list):
let g:ale_set_loclist = 0 let g:ale_set_loclist = 0
@ -96,7 +96,7 @@ Execute(Formatting with codes should work for the quickfix list):
\ 'text': 'nocode', \ 'text': 'nocode',
\ }, \ },
\ ], \ ],
\ getqflist() \ ale#test#GetQflistWithoutModule()
call remove(g:loclist, 0) call remove(g:loclist, 0)
call AddItem({'text': 'withcode', 'code': 'E123'}) call AddItem({'text': 'withcode', 'code': 'E123'})
@ -116,7 +116,7 @@ Execute(Formatting with codes should work for the quickfix list):
\ 'text': 'E123: withcode', \ 'text': 'E123: withcode',
\ }, \ },
\ ], \ ],
\ getqflist() \ ale#test#GetQflistWithoutModule()
Execute(Formatting with the linter name should work for the loclist): Execute(Formatting with the linter name should work for the loclist):
let g:ale_loclist_msg_format = '(%linter%) %s' let g:ale_loclist_msg_format = '(%linter%) %s'
@ -138,7 +138,7 @@ Execute(Formatting with the linter name should work for the loclist):
\ 'text': '(some_linter) whatever', \ 'text': '(some_linter) whatever',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()
Execute(Formatting with the linter name should work for the quickfix list): Execute(Formatting with the linter name should work for the quickfix list):
let g:ale_loclist_msg_format = '(%linter%) %s' let g:ale_loclist_msg_format = '(%linter%) %s'
@ -162,7 +162,7 @@ Execute(Formatting with the linter name should work for the quickfix list):
\ 'text': '(some_linter) whatever', \ 'text': '(some_linter) whatever',
\ }, \ },
\ ], \ ],
\ getqflist() \ ale#test#GetQflistWithoutModule()
Execute(The buffer loclist format option should take precedence): Execute(The buffer loclist format option should take precedence):
let g:ale_loclist_msg_format = '(%linter%) %s' let g:ale_loclist_msg_format = '(%linter%) %s'
@ -185,4 +185,4 @@ Execute(The buffer loclist format option should take precedence):
\ 'text': 'FOO whatever', \ 'text': 'FOO whatever',
\ }, \ },
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()

View File

@ -120,7 +120,7 @@ Execute(The quickfix window should be vertical for the loclist with appropriate
call ale#list#SetLists(bufnr('%'), g:loclist) call ale#list#SetLists(bufnr('%'), g:loclist)
AssertEqual 1, GetQuickfixIsVertical(b:ale_list_window_size) AssertEqual 1, GetQuickfixIsVertical(8)
Execute(The quickfix window should be horizontal for the loclist with appropriate variables): Execute(The quickfix window should be horizontal for the loclist with appropriate variables):
let g:ale_open_list = 1 let g:ale_open_list = 1
@ -129,7 +129,7 @@ Execute(The quickfix window should be horizontal for the loclist with appropriat
call ale#list#SetLists(bufnr('%'), g:loclist) call ale#list#SetLists(bufnr('%'), g:loclist)
AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size) AssertEqual 0, GetQuickfixIsVertical(8)
Execute(The quickfix window should stay open for just the loclist): Execute(The quickfix window should stay open for just the loclist):
let g:ale_open_list = 1 let g:ale_open_list = 1
@ -207,7 +207,7 @@ Execute(The quickfix window should be vertical for the quickfix with appropriate
call ale#list#SetLists(bufnr('%'), g:loclist) call ale#list#SetLists(bufnr('%'), g:loclist)
AssertEqual 1, GetQuickfixIsVertical(b:ale_list_window_size) AssertEqual 1, GetQuickfixIsVertical(8)
Execute(The quickfix window should be horizontal for the quickfix with appropriate variables): Execute(The quickfix window should be horizontal for the quickfix with appropriate variables):
let g:ale_open_list = 1 let g:ale_open_list = 1
@ -216,7 +216,7 @@ Execute(The quickfix window should be horizontal for the quickfix with appropria
call ale#list#SetLists(bufnr('%'), g:loclist) call ale#list#SetLists(bufnr('%'), g:loclist)
AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size) AssertEqual 0, GetQuickfixIsVertical(8)
Execute(The buffer ale_open_list option should be respected): Execute(The buffer ale_open_list option should be respected):
let b:ale_open_list = 1 let b:ale_open_list = 1

View File

@ -38,7 +38,7 @@ Execute(The loclist titles should be set appropriately):
\ 'nr': 0, \ 'nr': 0,
\ 'type': 'E', \ 'type': 'E',
\ 'pattern': '', \ 'pattern': '',
\}], getloclist(0) \}], ale#test#GetLoclistWithoutModule()
if !has('nvim') if !has('nvim')
AssertEqual AssertEqual
@ -68,7 +68,7 @@ Execute(The quickfix titles should be set appropriately):
\ 'nr': 0, \ 'nr': 0,
\ 'type': 'E', \ 'type': 'E',
\ 'pattern': '', \ 'pattern': '',
\}], getqflist() \}], ale#test#GetQflistWithoutModule()
if !has('nvim') if !has('nvim')
AssertEqual AssertEqual

View File

@ -59,14 +59,14 @@ 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. " First try just the SaveEvent, to be sure that we set errors in the test.
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
" Now try doing it again, but where we run the quit event first. " Now try doing it again, but where we run the quit event first.
call setloclist(0, []) call setloclist(0, [])
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
Execute(No linting should be for :w after :q fails): Execute(No linting should be for :w after :q fails):
let g:ale_lint_on_save = 1 let g:ale_lint_on_save = 1
@ -79,7 +79,7 @@ Execute(No linting should be for :w after :q fails):
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
Execute(No linting should be done on :wq or :x after fixing files): Execute(No linting should be done on :wq or :x after fixing files):
let g:ale_lint_on_save = 0 let g:ale_lint_on_save = 0
@ -87,14 +87,14 @@ Execute(No linting should be done on :wq or :x after fixing files):
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
" Now try doing it again, but where we run the quit event first. " Now try doing it again, but where we run the quit event first.
call setloclist(0, []) call setloclist(0, [])
call ale#events#QuitEvent(bufnr('')) call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
Execute(Linting should be done after :q fails and fixing files): Execute(Linting should be done after :q fails and fixing files):
let g:ale_lint_on_save = 0 let g:ale_lint_on_save = 0
@ -107,4 +107,4 @@ Execute(Linting should be done after :q fails and fixing files):
call ale#events#SaveEvent(bufnr('')) call ale#events#SaveEvent(bufnr(''))
AssertEqual 1, len(getloclist(0)) AssertEqual 1, len(ale#test#GetLoclistWithoutModule())

View File

@ -27,4 +27,4 @@ Execute(The loclist shouldn't be cleared when opening the loclist):
:lopen :lopen
:q :q
AssertEqual 1, len(getloclist(0)), 'The loclist was cleared' AssertEqual 1, len(ale#test#GetLoclistWithoutModule()), 'The loclist was cleared'

View File

@ -26,4 +26,4 @@ Execute(The SetLists function should work when run in a timer):
\ 'nr': 0, \ 'nr': 0,
\ 'type': 'E', \ 'type': 'E',
\ 'pattern': '', \ 'pattern': '',
\}], getloclist(0) \}], ale#test#GetLoclistWithoutModule()

View File

@ -21,6 +21,6 @@ Execute(Errors should be set in the loclist for the original buffer, not the new
\ g:ale_buffer_info[(g:original_buffer)].loclist, \ g:ale_buffer_info[(g:original_buffer)].loclist,
\ ) \ )
AssertEqual [], getloclist(0) AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 1, len(getloclist(bufwinid(g:original_buffer))) AssertEqual 1, len(getloclist(bufwinid(g:original_buffer)))
AssertEqual 'foo', getloclist(bufwinid(g:original_buffer))[0].text AssertEqual 'foo', getloclist(bufwinid(g:original_buffer))[0].text

View File

@ -95,4 +95,4 @@ Execute(Problems found from previously opened buffers should be set when linting
\ {'lnum': 2, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'bar'}, \ {'lnum': 2, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'bar'},
\ {'lnum': 3, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'baz'}, \ {'lnum': 3, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'baz'},
\ ], \ ],
\ getloclist(0) \ ale#test#GetLoclistWithoutModule()