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:
- docker
language: generic
env:
- OPTIONS=--vim-80-only
- OPTIONS=--vim-81-only
- OPTIONS=--neovim-only
- OPTIONS=--linters-only
script: |
./run-tests -v
./run-tests -v $OPTIONS

View File

@ -1,6 +1,7 @@
FROM tweekmonster/vim-testbed:latest
RUN install_vim -tag v8.0.0027 -build \
-tag v8.1.0204 -build \
-tag neovim:v0.2.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 (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)
@ -96,7 +97,7 @@ formatting.
| 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) |
| 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/) |
| 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|
@ -118,7 +119,7 @@ formatting.
| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) |
| Erlang | [erlc](http://erlang.org/doc/man/erlc.html), [SyntaxErl](https://github.com/ten0s/syntaxerl) |
| Fish | fish [-n flag](https://linux.die.net/man/1/fish)
| Fortran | [gcc](https://gcc.gnu.org/) |
| Fortran | [gcc](https://gcc.gnu.org/), [language_server](https://github.com/hansec/fortran-language-server) |
| Fountain | [proselint](http://proselint.com/) |
| FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) |
| Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) |
@ -127,7 +128,7 @@ formatting.
| GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) |
| Haml | [haml-lint](https://github.com/brigade/haml-lint) |
| Handlebars | [ember-template-lint](https://github.com/rwjblue/ember-template-lint) |
| Haskell | [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) |
| 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/) |

View File

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

View File

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

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', {
\ 'name': 'dialyxir',
\ 'executable': 'mix',
\ 'command': 'mix dialyzer',
\ 'command': 'mix help dialyzer && mix dialyzer',
\ '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
call ale#linter#Define('go', {
\ 'name': 'go build',
\ 'name': 'gobuild',
\ 'aliases': ['go build'],
\ 'executable': 'go',
\ 'command_chain': [
\ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'},

View File

@ -9,7 +9,8 @@ function! ale_linters#go#govet#GetCommand(buffer) abort
endfunction
call ale#linter#Define('go', {
\ 'name': 'go vet',
\ 'name': 'govet',
\ 'aliases': ['go vet'],
\ 'output_stream': 'stderr',
\ 'executable': 'go',
\ '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
call ale#linter#Define('haskell', {
\ 'name': 'ghc-mod',
\ 'name': 'ghc_mod',
\ 'aliases': ['ghc-mod'],
\ 'executable': 'ghc-mod',
\ 'command': 'ghc-mod --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc-mod',
\ 'name': 'stack_ghc_mod',
\ 'aliases': ['stack-ghc-mod'],
\ 'executable': 'stack',
\ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -192,12 +192,7 @@ endfunction
" Every variable name will be prefixed with 'ale_'.
function! ale#Var(buffer, variable_name) abort
let l:full_name = 'ale_' . a:variable_name
let l:vars = getbufvar(str2nr(a:buffer), '', 0)
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
let l:vars = getbufvar(str2nr(a:buffer), '', {})
return get(l:vars, l:full_name, g:[l:full_name])
endfunction

View File

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

View File

@ -4,8 +4,11 @@
call ale#Set('c_parse_makefile', 0)
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
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)
if !empty(l:full_path)

View File

@ -336,7 +336,9 @@ function! ale#completion#ParseLSPCompletions(response) abort
endif
" 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'
elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND
let l:kind = 'm'
@ -422,54 +424,65 @@ endfunction
function! s:GetLSPCompletions(linter) abort
let l:buffer = bufnr('')
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
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)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions(
\ 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: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
function! OnReady(...) abort closure
" If we have sent a completion request already, don't send another.
if b:ale_completion_info.request_id
return
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
function! ale#completion#GetCompletions() abort

View File

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

View File

@ -59,3 +59,14 @@ function! ale#handlers#writegood#Handle(buffer, lines) abort
return l:output
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
function! s:ShowDetails(linter, buffer, line, column, opt) abort
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback)
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
let l:language_id = l:lsp_details.language_id
if a:linter.lsp is# 'tsserver'
let l:column = a:column
function! OnReady(...) abort closure
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(
\ 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:lsp_details)
if a:linter.lsp is# 'tsserver'
let l:column = a:column
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)
endif
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
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] = {
\ 'buffer': a:buffer,
\ 'line': a:line,
\ 'column': l:column,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0),
\}
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:hover_map[l:request_id] = {
\ 'buffer': a:buffer,
\ '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
" 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
" track of when documents were opened, and when we last changed them.
" 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 = {
\ 'is_tsserver': 0,
\ 'id': '',
\ 'data': '',
\ 'projects': {},
\ 'open_documents': {},
\ 'callback_list': [],
\ 'initialization_options': a:initialization_options,
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ },
\}
call add(s:connections, l:conn)
@ -44,6 +54,11 @@ function! s:FindConnection(key, value) abort
return {}
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
" Use the current ID
let l:id = g:ale_lsp_next_message_id
@ -174,6 +189,16 @@ function! s:MarkProjectAsInitialized(conn, project) abort
" Remove the messages now.
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
function! s:HandleInitializeResponse(conn, response) abort
@ -185,6 +210,38 @@ function! s:HandleInitializeResponse(conn, response) abort
endif
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
let l:uninitialized_projects = []
@ -200,6 +257,8 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
if get(a:response, 'method', '') is# ''
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
call s:MarkProjectAsInitialized(a:conn, l:project)
endfor
@ -254,22 +313,43 @@ function! s:HandleCommandMessage(job_id, message) abort
call ale#lsp#HandleMessage(l:conn, a:message)
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.
" This appears to be a nonsensical bug in NeoVim.
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.
let a:conn.projects[l:key] = {
let l:conn.projects[l:key] = {
\ 'root': a:project_root,
\ 'initialized': empty(a:project_root),
\ 'init_request_id': 0,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\}
endif
endfunction
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
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
" 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)
return 0
endif
@ -287,7 +367,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
let l:conn = s:FindConnection('executable', a:executable)
" 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
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
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
endfunction
" 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)
" 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)
let l:conn.channel_id = ale#socket#Open(a:address, {
@ -329,13 +406,21 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback, initializati
endif
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
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
" queued messages.
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: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)
if empty(l:project)
@ -411,45 +491,22 @@ function! ale#lsp#Send(conn_id, message, ...) abort
return l:id == 0 ? -1 : l:id
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.
" If a document is opened, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#OpenDocument(basic_details) abort
let l:d = s:ExtendDocumentDetails(a:basic_details)
function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:opened = 0
if !empty(l:d.conn) && !l:d.document_open
if empty(l:d.language_id)
let l:message = ale#lsp#tsserver_message#Open(l:d.buffer)
" FIXME: Return 1 if the document is already open?
if !empty(l:conn) && !has_key(l:conn.open_documents, a:buffer)
if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Open(a:buffer)
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
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick')
call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:conn.open_documents[a:buffer] = getbufvar(a:buffer, 'changedtick')
let l:opened = 1
endif
@ -458,25 +515,50 @@ endfunction
" Notify LSP servers or tsserver that a document has changed, if needed.
" If a notification is sent, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#NotifyForChanges(basic_details) abort
let l:d = s:ExtendDocumentDetails(a:basic_details)
function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:notified = 0
if l:d.document_open
let l:new_tick = getbufvar(l:d.buffer, 'changedtick')
if !empty(l:conn) && has_key(l:conn.open_documents, a:buffer)
let l:new_tick = getbufvar(a:buffer, 'changedtick')
if l:d.conn.open_documents[l:d.buffer] < l:new_tick
if empty(l:d.language_id)
let l:message = ale#lsp#tsserver_message#Change(l:d.buffer)
if l:conn.open_documents[a:buffer] < l:new_tick
if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Change(a:buffer)
else
let l:message = ale#lsp#message#DidChange(l:d.buffer)
let l:message = ale#lsp#message#DidChange(a:buffer)
endif
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
let l:d.conn.open_documents[l:d.buffer] = l:new_tick
call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:conn.open_documents[a:buffer] = l:new_tick
let l:notified = 1
endif
endif
return l:notified
endfunction
" Given some LSP details that must contain at least `connection_id` and
" `project_root` keys,
function! ale#lsp#WaitForCapability(conn_id, 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
endfunction
" Given a buffer, an LSP linter, and a callback to register for handling
" messages, start up an LSP linter and get ready to receive errors or
" completions.
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
" Given a buffer, an LSP linter, start up an LSP linter and get ready to
" receive messages for the document.
function! ale#lsp_linter#StartLSP(buffer, linter) abort
let l:command = ''
let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
@ -140,16 +139,11 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {}
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'
let l:address = ale#linter#GetAddress(a:buffer, a:linter)
let l:conn_id = ale#lsp#ConnectToAddress(
\ l:address,
\ l:root,
\ a:callback,
\ l:initialization_options,
\)
let l:conn_id = ale#lsp#ConnectToAddress(l:address, l:init_options)
else
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(
\ l:executable,
\ l:command,
\ l:root,
\ a:callback,
\ l:initialization_options,
\ l:init_options,
\)
endif
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
if empty(l:conn_id)
if g:ale_history_enabled && !empty(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 {}
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 = {
\ 'buffer': a:buffer,
\ 'connection_id': l:conn_id,
@ -188,7 +188,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
\ '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)
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
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.
if a:linter.lsp is# 'tsserver'
call ale#lsp#NotifyForChanges(l:details)
call ale#lsp#NotifyForChanges(l:conn_id, l:root, a:buffer)
endif
return l:details
@ -204,11 +204,7 @@ endfunction
function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:info = g:ale_buffer_info[a:buffer]
let l:lsp_details = ale#lsp_linter#StartLSP(
\ a:buffer,
\ a:linter,
\ function('ale#lsp_linter#HandleLSPResponse'),
\)
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
if empty(l:lsp_details)
return 0
@ -217,25 +213,25 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:id = l:lsp_details.connection_id
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.
let s:lsp_linter_map[l:id] = a:linter.name
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Geterr(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
let l:notified = l:request_id != 0
let l:notified = ale#lsp#Send(l:id, l:message, l:root) != 0
else
let l:notified = ale#lsp#NotifyForChanges(l:lsp_details)
let l:notified = ale#lsp#NotifyForChanges(l:id, l:root, a:buffer)
endif
" If this was a file save event, also notify the server of that.
if a:linter.lsp isnot# 'tsserver'
\&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
let l:save_message = ale#lsp#message#DidSave(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root)
let l:notified = l:request_id != 0
let l:notified = ale#lsp#Send(l:id, l:save_message, l:root) != 0
endif
if l:notified

View File

@ -68,37 +68,46 @@ function! s:FindReferences(linter) abort
let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2]
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse')
if a:linter.lsp isnot# 'tsserver'
let l:column = min([l:column, len(getline(l:line))])
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)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#References(
\ 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:lsp_details)
function! OnReady(...) abort closure
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse')
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)
endif
if a:linter.lsp is# 'tsserver'
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
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))
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:DisablePostamble()
if has('balloon_eval')
if exists('*ale#balloon#Disable')
call ale#balloon#Disable()
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.
===============================================================================
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*

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
set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR`
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.
@ -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.
1. Vim 8.0.0027 on Linux via Travis CI.
2. NeoVim 0.2.0 on Linux via Travis CI.
3. NeoVim 0.3.0 on Linux via Travis CI.
4. Vim 8 (stable builds) on Windows via AppVeyor.
2. Vim 8.1.0204 on Linux via Travis CI.
3. NeoVim 0.2.0 on Linux via Travis CI.
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
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.
===============================================================================
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:

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.
===============================================================================
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*

View File

@ -26,6 +26,7 @@ CONTENTS *ale-contents*
gawk................................|ale-awk-gawk|
c.....................................|ale-c-options|
clang...............................|ale-c-clang|
clangd..............................|ale-c-clangd|
clang-format........................|ale-c-clangformat|
clangtidy...........................|ale-c-clangtidy|
cppcheck............................|ale-c-cppcheck|
@ -76,6 +77,7 @@ CONTENTS *ale-contents*
fish..................................|ale-fish-options|
fortran...............................|ale-fortran-options|
gcc.................................|ale-fortran-gcc|
language_server.....................|ale-fortran-language-server|
fountain..............................|ale-fountain-options|
fusionscript..........................|ale-fuse-options|
fusion-lint.........................|ale-fuse-fusionlint|
@ -98,6 +100,7 @@ CONTENTS *ale-contents*
haskell...............................|ale-haskell-options|
brittany............................|ale-haskell-brittany|
ghc.................................|ale-haskell-ghc|
cabal-ghc...........................|ale-haskell-cabal-ghc|
hdevtools...........................|ale-haskell-hdevtools|
hfmt................................|ale-haskell-hfmt|
stack-build.........................|ale-haskell-stack-build|
@ -333,7 +336,7 @@ Notes:
* Awk: `gawk`
* Bash: `language-server`, `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`
* CUDA: `nvcc`!!
* C#: `mcs`, `mcsc`!!
@ -355,7 +358,7 @@ Notes:
* Erb: `erb`, `erubi`, `erubis`
* Erlang: `erlc`, `SyntaxErl`
* Fish: `fish` (-n flag)
* Fortran: `gcc`
* Fortran: `gcc`, `language_server`
* Fountain: `proselint`
* FusionScript: `fusion-lint`
* Git Commit Messages: `gitlint`
@ -364,7 +367,7 @@ Notes:
* GraphQL: `eslint`, `gqlint`, `prettier`
* Haml: `haml-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`
* Idris: `idris`
* Java: `checkstyle`, `javac`, `google-java-format`, `PMD`
@ -1413,8 +1416,7 @@ g:ale_set_balloons *g:ale_set_balloons*
*b:ale_set_balloons*
Type: |Number|
Default: `(has('balloon_eval') && has('gui_running'))`
`|| (has('balloon_eval_term') && !has('gui_running'))`
Default: `has('balloon_eval') && has('gui_running')`
When this option is set to `1`, balloon messages will be displayed for
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
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.
Balloons cannot be enabled for a specific buffer when not initially enabled
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)
" This flag can be set to 0 to disable balloon support.
let g:ale_set_balloons = get(g:, 'ale_set_balloons',
\ (has('balloon_eval') && has('gui_running'))
\ || (has('balloon_eval_term') && !has('gui_running'))
\)
let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has('gui_running'))
" 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)

View File

@ -10,7 +10,7 @@ set -u
#
image=w0rp/ale
current_image_id=71553d0ab3e8
current_image_id=67896c9c2c0f
# Used in all test scripts for running the selected Docker image.
DOCKER_RUN_IMAGE="$image"
@ -22,7 +22,8 @@ verbose_flag=''
quiet_flag=''
run_neovim_02_tests=1
run_neovim_03_tests=1
run_vim_tests=1
run_vim_80_tests=1
run_vim_81_tests=1
run_linters=1
while [ $# -ne 0 ]; do
@ -36,19 +37,22 @@ while [ $# -ne 0 ]; do
shift
;;
--neovim-only)
run_vim_tests=0
run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0
shift
;;
--neovim-02-only)
run_neovim_03_tests=0
run_vim_tests=0
run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0
shift
;;
--neovim-03-only)
run_neovim_02_tests=0
run_vim_tests=0
run_vim_80_tests=0
run_vim_81_tests=0
run_linters=0
shift
;;
@ -58,8 +62,23 @@ while [ $# -ne 0 ]; do
run_linters=0
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)
run_vim_tests=0
run_vim_80_tests=0
run_vim_81_tests=0
run_neovim_02_tests=0
run_neovim_03_tests=0
shift
@ -76,7 +95,9 @@ while [ $# -ne 0 ]; do
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-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 ' --help Show this help text'
echo ' -- Stop parsing options after this'
@ -120,7 +141,8 @@ file_number=0
pid_list=''
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.3 ]] && ((run_neovim_03_tests)) ); then
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
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
endfunction

View File

@ -13,11 +13,11 @@ Before:
runtime autoload/ale/lsp.vim
let g:message_list = []
let g:capability_checked = ''
let g:Callback = ''
let g:wait_callback_list = []
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
function! ale#lsp_linter#StartLSP(buffer, linter) abort
let l:conn = ale#lsp#NewConnection({})
let l:conn.id = 347
let l:conn.open_documents = {a:buffer : -1}
@ -35,15 +35,28 @@ Before:
return 'i'
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.
function! ale#lsp#Send(conn_id, message, ...) abort
call add(g:message_list, a:message)
return 1
endfunction
After:
Restore
unlet! g:message_list
unlet! g:capability_checked
unlet! g:wait_callback_list
unlet! g:Callback
unlet! b:ale_old_omnifunc
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()
" 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.
AssertEqual
\ 'function(''ale#completion#HandleTSServerResponse'')',
@ -96,9 +116,9 @@ Execute(The right message should be sent for the initial tsserver request):
AssertEqual
\ {
\ 'line_length': 3,
\ 'conn_id': 0,
\ 'conn_id': 347,
\ 'column': 3,
\ 'request_id': 0,
\ 'request_id': 1,
\ 'line': 1,
\ 'prefix': 'fo',
\ },
@ -164,6 +184,13 @@ Execute(The right message should be sent for the initial LSP request):
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.
AssertEqual
\ 'function(''ale#completion#HandleLSPResponse'')',
@ -192,10 +219,58 @@ Execute(The right message should be sent for the initial LSP request):
AssertEqual
\ {
\ 'line_length': 3,
\ 'conn_id': 0,
\ 'conn_id': 347,
\ 'column': 3,
\ 'request_id': 0,
\ 'request_id': 1,
\ 'line': 1,
\ 'prefix': 'fo',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\ },
\ 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
\ [
\ {'word': 'x', 'menu': '', 'info': 'y', 'kind': 'f', 'icase': 1},
\ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1},
\ ],
\ ale#completion#ParseLSPCompletions({
\ 'jsonrpc': '2.0',
@ -443,8 +443,6 @@ Execute(Should handle missing detail keys):
\ 'items': [
\ {
\ 'label': 'x',
\ 'kind': 3,
\ 'documentation': 'y',
\ },
\ ]
\ }

View File

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

View File

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

View File

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

View File

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

View File

@ -3,12 +3,20 @@ Before:
\ 'initialized': 0,
\ 'init_request_id': 3,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\}
let b:conn = {
\ 'projects': {
\ '/foo/bar': b:project,
\ },
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ },
\}
After:
@ -27,6 +35,7 @@ Execute(publishDiagnostics messages with files inside project directories should
\ 'initialized': 0,
\ 'init_request_id': 3,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ },
\ b:project
@ -40,6 +49,7 @@ Execute(publishDiagnostics messages with files inside project directories should
\ 'initialized': 1,
\ 'init_request_id': 3,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ },
\ b:project
@ -53,6 +63,7 @@ Execute(Messages with no method and capabilities should initialize projects):
\ 'initialized': 1,
\ 'init_request_id': 3,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\ },
\ b:project
@ -64,3 +75,109 @@ Execute(Other messages should not initialize projects):
call ale#lsp#HandleOtherInitializeResponses(b:conn, {'result': {'x': {}}})
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() {
regex="$1"
message="$2"
include_arg=''
if [ $# -gt 2 ]; then
include_arg="--include $3"
fi
for directory in "${directories[@]}"; do
while IFS= read -r match; do
RETURN_CODE=1
echo "$match $message"
done < <(grep -n "$regex" "$directory"/**/*.vim \
done < <(grep -n "$regex" $include_arg "$directory"/**/*.vim \
| grep -v 'no-custom-checks' \
| grep -o '^[^:]\+:[0-9]\+' \
| sed 's:^\./::')
@ -75,6 +80,7 @@ if (( FIX_ERRORS )); then
done
fi
# The arguments are: regex, explanation, [filename_filter]
check_errors \
'^function.*) *$' \
'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 '^ *:\?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

View File

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

View File

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

View File

@ -5,8 +5,6 @@ After:
unlet! g:ale_some_variable
unlet! b:undefined_variable_name
let g:ale_fix_buffer_data = {}
Execute(ale#Var should return global variables):
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'
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:
" Make sure the c.vim file is loaded first.
call ale#c#FindProjectRoot(bufnr(''))
Save g:ale_c_gcc_options
Save g:ale_c_clang_options
Save g:ale_cpp_gcc_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')
@ -14,23 +27,11 @@ Before:
After:
Restore
unlet! g:original_project_filenames
call ale#test#RestoreDirectory()
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):
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(''), [])
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):
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 '
\ . '-p ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/json_project/build'))
\ , ale_linters#cpp#clangtidy#GetCommand(bufnr(''))
Execute(Move .git/HEAD back):
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': '',
\ },
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
" After we get empty syntax errors, we should clear them.
call ale#lsp_linter#HandleLSPResponse(1, {
@ -85,7 +85,7 @@ Execute(tsserver syntax error responses should be handled correctly):
AssertEqual
\ [
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
Execute(tsserver semantic error responses should be handled correctly):
runtime ale_linters/typescript/tsserver.vim
@ -141,7 +141,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ 'pattern': '',
\ },
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
" After we get empty syntax errors, we should clear them.
call ale#lsp_linter#HandleLSPResponse(1, {
@ -158,7 +158,7 @@ Execute(tsserver semantic error responses should be handled correctly):
AssertEqual
\ [
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
Execute(LSP errors should be logged in the history):
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)
sleep 1ms
AssertEqual 1, len(getloclist(0))
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
noautocmd let &filetype = 'foobar2'
@ -61,11 +61,11 @@ Execute(Error should be removed when the filetype changes to something else we c
sleep 1ms
" We should get some items from the second filetype.
AssertEqual 1, len(getloclist(0))
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
noautocmd let &filetype = 'xxx'
call ale#Queue(0)
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')
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

View File

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

View File

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

View File

@ -51,7 +51,7 @@ Before:
function! GetSimplerLoclist()
let l:loclist = []
for l:item in getloclist(0)
for l:item in ale#test#GetLoclistWithoutModule()
call add(l:loclist, {
\ 'lnum': l:item.lnum,
\ '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,
\ 'pattern': '',
\ 'valid': 1,
\ }], getloclist(0)
\ }], ale#test#GetLoclistWithoutModule()
Execute(The buffer should be checked after entering it after the file has changed):
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,
\ 'pattern': '',
\ 'valid': 1,
\ }], getloclist(0)
\ }], ale#test#GetLoclistWithoutModule()

View File

@ -53,7 +53,7 @@ Execute(Formatting with codes should work for the loclist):
\ 'text': 'nocode',
\ },
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
call remove(g:loclist, 0)
call AddItem({'text': 'withcode', 'code': 'E123'})
@ -73,7 +73,7 @@ Execute(Formatting with codes should work for the loclist):
\ 'text': 'E123: withcode',
\ },
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
Execute(Formatting with codes should work for the quickfix list):
let g:ale_set_loclist = 0
@ -96,7 +96,7 @@ Execute(Formatting with codes should work for the quickfix list):
\ 'text': 'nocode',
\ },
\ ],
\ getqflist()
\ ale#test#GetQflistWithoutModule()
call remove(g:loclist, 0)
call AddItem({'text': 'withcode', 'code': 'E123'})
@ -116,7 +116,7 @@ Execute(Formatting with codes should work for the quickfix list):
\ 'text': 'E123: withcode',
\ },
\ ],
\ getqflist()
\ ale#test#GetQflistWithoutModule()
Execute(Formatting with the linter name should work for the loclist):
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',
\ },
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()
Execute(Formatting with the linter name should work for the quickfix list):
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',
\ },
\ ],
\ getqflist()
\ ale#test#GetQflistWithoutModule()
Execute(The buffer loclist format option should take precedence):
let g:ale_loclist_msg_format = '(%linter%) %s'
@ -185,4 +185,4 @@ Execute(The buffer loclist format option should take precedence):
\ '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)
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):
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)
AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size)
AssertEqual 0, GetQuickfixIsVertical(8)
Execute(The quickfix window should stay open for just the loclist):
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)
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):
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)
AssertEqual 0, GetQuickfixIsVertical(b:ale_list_window_size)
AssertEqual 0, GetQuickfixIsVertical(8)
Execute(The buffer ale_open_list option should be respected):
let b:ale_open_list = 1

View File

@ -38,7 +38,7 @@ Execute(The loclist titles should be set appropriately):
\ 'nr': 0,
\ 'type': 'E',
\ 'pattern': '',
\}], getloclist(0)
\}], ale#test#GetLoclistWithoutModule()
if !has('nvim')
AssertEqual
@ -68,7 +68,7 @@ Execute(The quickfix titles should be set appropriately):
\ 'nr': 0,
\ 'type': 'E',
\ 'pattern': '',
\}], getqflist()
\}], ale#test#GetQflistWithoutModule()
if !has('nvim')
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.
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.
call setloclist(0, [])
call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr(''))
AssertEqual [], getloclist(0)
AssertEqual [], ale#test#GetLoclistWithoutModule()
Execute(No linting should be for :w after :q fails):
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(''))
AssertEqual 1, len(getloclist(0))
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
Execute(No linting should be done on :wq or :x after fixing files):
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(''))
AssertEqual 1, len(getloclist(0))
AssertEqual 1, len(ale#test#GetLoclistWithoutModule())
" Now try doing it again, but where we run the quit event first.
call setloclist(0, [])
call ale#events#QuitEvent(bufnr(''))
call ale#events#SaveEvent(bufnr(''))
AssertEqual [], getloclist(0)
AssertEqual [], ale#test#GetLoclistWithoutModule()
Execute(Linting should be done after :q fails and fixing files):
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(''))
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
: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,
\ 'type': 'E',
\ '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,
\ )
AssertEqual [], getloclist(0)
AssertEqual [], ale#test#GetLoclistWithoutModule()
AssertEqual 1, len(getloclist(bufwinid(g:original_buffer)))
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': 3, 'bufnr': bufnr(''), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'E', 'pattern': '', 'text': 'baz'},
\ ],
\ getloclist(0)
\ ale#test#GetLoclistWithoutModule()