Support an 'stdio' type for linter defintions, and require a command for LSP connections via programs

This commit is contained in:
w0rp 2017-07-02 13:17:24 +01:00
parent b731bd77ab
commit 857ca941d2
5 changed files with 59 additions and 51 deletions

View File

@ -19,5 +19,6 @@ call ale#linter#Define('typescript', {
\ 'name': 'tsserver', \ 'name': 'tsserver',
\ 'lsp': 'tsserver', \ 'lsp': 'tsserver',
\ 'executable_callback': 'ale_linters#typescript#tsserver#GetExecutable', \ 'executable_callback': 'ale_linters#typescript#tsserver#GetExecutable',
\ 'command_callback': 'ale_linters#typescript#tsserver#GetExecutable',
\ 'callback': 'ale_linters#typescript#tsserver#Handle', \ 'callback': 'ale_linters#typescript#tsserver#Handle',
\}) \})

View File

@ -171,11 +171,10 @@ endfunction
function! s:GetCompletionsForTSServer(linter) abort function! s:GetCompletionsForTSServer(linter) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let l:executable = has_key(a:linter, 'executable_callback') let l:executable = ale#linter#GetExecutable(l:buffer, a:linter)
\ ? ale#util#GetFunction(a:linter.executable_callback)(l:buffer) let l:command = ale#job#PrepareCommand(
\ : a:linter.executable \ ale#linter#GetCommand(l:buffer, a:linter),
let l:command = ale#job#PrepareCommand(l:executable) \)
let l:id = ale#lsp#StartProgram( let l:id = ale#lsp#StartProgram(
\ l:executable, \ l:executable,
\ l:command, \ l:command,

View File

@ -502,11 +502,8 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
let l:input = [] let l:input = []
let l:chain_index += 1 let l:chain_index += 1
endwhile endwhile
elseif has_key(a:linter, 'command_callback')
" If there is a callback for generating a command, call that instead.
let l:command = ale#util#GetFunction(a:linter.command_callback)(a:buffer)
else else
let l:command = a:linter.command let l:command = ale#linter#GetCommand(a:buffer, a:linter)
endif endif
if empty(l:command) if empty(l:command)
@ -563,7 +560,9 @@ endfunction
function! s:CheckWithTSServer(buffer, linter, executable) abort function! s:CheckWithTSServer(buffer, linter, executable) abort
let l:info = g:ale_buffer_info[a:buffer] let l:info = g:ale_buffer_info[a:buffer]
let l:command = ale#job#PrepareCommand(a:executable) let l:command = ale#job#PrepareCommand(
\ ale#linter#GetCommand(a:buffer, a:linter),
\)
let l:id = ale#lsp#StartProgram( let l:id = ale#lsp#StartProgram(
\ a:executable, \ a:executable,
\ l:command, \ l:command,
@ -598,9 +597,7 @@ endfunction
function! ale#engine#Invoke(buffer, linter) abort function! ale#engine#Invoke(buffer, linter) abort
if empty(a:linter.lsp) || a:linter.lsp ==# 'tsserver' if empty(a:linter.lsp) || a:linter.lsp ==# 'tsserver'
let l:executable = has_key(a:linter, 'executable_callback') let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
\ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
\ : a:linter.executable
" Run this program if it can be executed. " Run this program if it can be executed.
if s:IsExecutable(l:executable) if s:IsExecutable(l:executable)

View File

@ -63,25 +63,18 @@ function! ale#linter#PreProcess(linter) abort
throw '`callback` must be defined with a callback to accept output' throw '`callback` must be defined with a callback to accept output'
endif endif
let l:needs_executable = 0 let l:needs_address = l:obj.lsp ==# 'socket'
let l:needs_address = 0 let l:needs_executable = l:obj.lsp !=# 'socket'
let l:needs_command = 0 let l:needs_command = l:obj.lsp !=# 'socket'
if l:obj.lsp ==# 'tsserver' if index(['', 'socket', 'stdio', 'tsserver'], l:obj.lsp) < 0
let l:needs_executable = 1
elseif l:obj.lsp ==# 'lsp'
let l:needs_address = 1
elseif !empty(l:obj.lsp)
throw '`lsp` must be either `''lsp''` or `''tsserver''` if defined' throw '`lsp` must be either `''lsp''` or `''tsserver''` if defined'
else
let l:needs_executable = 1
let l:needs_command = 1
endif endif
if !l:needs_executable if !l:needs_executable
if has_key(a:linter, 'executable') if has_key(a:linter, 'executable')
\|| has_key(a:linter, 'executable_callback') \|| has_key(a:linter, 'executable_callback')
throw '`executable` and `executable_callback` cannot be used when lsp == ''lsp''' throw '`executable` and `executable_callback` cannot be used when lsp == ''socket'''
endif endif
elseif has_key(a:linter, 'executable_callback') elseif has_key(a:linter, 'executable_callback')
let l:obj.executable_callback = a:linter.executable_callback let l:obj.executable_callback = a:linter.executable_callback
@ -103,7 +96,7 @@ function! ale#linter#PreProcess(linter) abort
if has_key(a:linter, 'command') if has_key(a:linter, 'command')
\|| has_key(a:linter, 'command_callback') \|| has_key(a:linter, 'command_callback')
\|| has_key(a:linter, 'command_chain') \|| has_key(a:linter, 'command_chain')
throw '`command` and `command_callback` and `command_chain` cannot be used when `lsp` is set' throw '`command` and `command_callback` and `command_chain` cannot be used when lsp == ''socket'''
endif endif
elseif has_key(a:linter, 'command_chain') elseif has_key(a:linter, 'command_chain')
let l:obj.command_chain = a:linter.command_chain let l:obj.command_chain = a:linter.command_chain
@ -167,7 +160,7 @@ function! ale#linter#PreProcess(linter) abort
if !l:needs_address if !l:needs_address
if has_key(a:linter, 'address_callback') if has_key(a:linter, 'address_callback')
throw '`address_callback` cannot be used when lsp != ''lsp''' throw '`address_callback` cannot be used when lsp != ''socket'''
endif endif
elseif has_key(a:linter, 'address_callback') elseif has_key(a:linter, 'address_callback')
let l:obj.address_callback = a:linter.address_callback let l:obj.address_callback = a:linter.address_callback
@ -334,3 +327,18 @@ function! ale#linter#Get(original_filetypes) abort
return reverse(l:combined_linters) return reverse(l:combined_linters)
endfunction endfunction
" Given a buffer and linter, get the executable String for the linter.
function! ale#linter#GetExecutable(buffer, linter) abort
return has_key(a:linter, 'executable_callback')
\ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
\ : a:linter.executable
endfunction
" Given a buffer and linter, get the command String for the linter.
" The command_chain key is not supported.
function! ale#linter#GetCommand(buffer, linter) abort
return has_key(a:linter, 'command_callback')
\ ? ale#util#GetFunction(a:linter.command_callback)(a:buffer)
\ : a:linter.command
endfunction

View File

@ -370,6 +370,7 @@ Execute(PreProcess should accept tsserver LSP configuration):
let g:linter = { let g:linter = {
\ 'name': 'x', \ 'name': 'x',
\ 'executable': 'x', \ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'tsserver', \ 'lsp': 'tsserver',
\ 'callback': 'x', \ 'callback': 'x',
\} \}
@ -379,46 +380,48 @@ Execute(PreProcess should accept tsserver LSP configuration):
call remove(g:linter, 'executable') call remove(g:linter, 'executable')
let g:linter.executable_callback = 'X' let g:linter.executable_callback = 'X'
call ale#linter#PreProcess(g:linter).lsp call ale#linter#PreProcess(g:linter)
Execute(PreProcess should complain about commands being set for LSP configurations):
let g:linter = {
\ 'name': 'x',
\ 'executable': 'x',
\ 'lsp': 'tsserver',
\ 'command': 'x',
\ 'callback': 'x',
\}
AssertThrows call ale#linter#PreProcess(g:linter)
AssertEqual '`command` and `command_callback` and `command_chain` cannot be used when `lsp` is set', g:vader_exception
call remove(g:linter, 'command') call remove(g:linter, 'command')
let g:linter.command_callback = 'X' let g:linter.command_callback = 'X'
AssertThrows call ale#linter#PreProcess(g:linter) call ale#linter#PreProcess(g:linter)
AssertEqual '`command` and `command_callback` and `command_chain` cannot be used when `lsp` is set', g:vader_exception
call remove(g:linter, 'command_callback') Execute(PreProcess should accept stdio LSP configuration):
let g:linter.command_chain = [] let g:linter = {
\ 'name': 'x',
\ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'stdio',
\ 'callback': 'x',
\}
AssertThrows call ale#linter#PreProcess(g:linter) AssertEqual 'stdio', ale#linter#PreProcess(g:linter).lsp
AssertEqual '`command` and `command_callback` and `command_chain` cannot be used when `lsp` is set', g:vader_exception
call remove(g:linter, 'executable')
let g:linter.executable_callback = 'X'
call ale#linter#PreProcess(g:linter)
call remove(g:linter, 'command')
let g:linter.command_callback = 'X'
call ale#linter#PreProcess(g:linter)
Execute(PreProcess should accept LSP server configurations): Execute(PreProcess should accept LSP server configurations):
let g:linter = { let g:linter = {
\ 'name': 'x', \ 'name': 'x',
\ 'lsp': 'lsp', \ 'lsp': 'socket',
\ 'callback': 'x', \ 'callback': 'x',
\ 'address_callback': 'X', \ 'address_callback': 'X',
\} \}
AssertEqual 'lsp', ale#linter#PreProcess(g:linter).lsp AssertEqual 'socket', ale#linter#PreProcess(g:linter).lsp
Execute(PreProcess should require an address_callback for LSP server configurations): Execute(PreProcess should require an address_callback for LSP socket configurations):
let g:linter = { let g:linter = {
\ 'name': 'x', \ 'name': 'x',
\ 'lsp': 'lsp', \ 'lsp': 'socket',
\ 'callback': 'x', \ 'callback': 'x',
\} \}
@ -435,4 +438,4 @@ Execute(PreProcess should complain about address_callback for non-LSP linters):
\} \}
AssertThrows call ale#linter#PreProcess(g:linter) AssertThrows call ale#linter#PreProcess(g:linter)
AssertEqual '`address_callback` cannot be used when lsp != ''lsp''', g:vader_exception AssertEqual '`address_callback` cannot be used when lsp != ''socket''', g:vader_exception