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',
\ 'lsp': 'tsserver',
\ 'executable_callback': 'ale_linters#typescript#tsserver#GetExecutable',
\ 'command_callback': 'ale_linters#typescript#tsserver#GetExecutable',
\ 'callback': 'ale_linters#typescript#tsserver#Handle',
\})

View File

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

View File

@ -502,11 +502,8 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
let l:input = []
let l:chain_index += 1
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
let l:command = a:linter.command
let l:command = ale#linter#GetCommand(a:buffer, a:linter)
endif
if empty(l:command)
@ -563,7 +560,9 @@ endfunction
function! s:CheckWithTSServer(buffer, linter, executable) abort
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(
\ a:executable,
\ l:command,
@ -598,9 +597,7 @@ endfunction
function! ale#engine#Invoke(buffer, linter) abort
if empty(a:linter.lsp) || a:linter.lsp ==# 'tsserver'
let l:executable = has_key(a:linter, 'executable_callback')
\ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
\ : a:linter.executable
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
" Run this program if it can be executed.
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'
endif
let l:needs_executable = 0
let l:needs_address = 0
let l:needs_command = 0
let l:needs_address = l:obj.lsp ==# 'socket'
let l:needs_executable = l:obj.lsp !=# 'socket'
let l:needs_command = l:obj.lsp !=# 'socket'
if l:obj.lsp ==# 'tsserver'
let l:needs_executable = 1
elseif l:obj.lsp ==# 'lsp'
let l:needs_address = 1
elseif !empty(l:obj.lsp)
if index(['', 'socket', 'stdio', 'tsserver'], l:obj.lsp) < 0
throw '`lsp` must be either `''lsp''` or `''tsserver''` if defined'
else
let l:needs_executable = 1
let l:needs_command = 1
endif
if !l:needs_executable
if has_key(a:linter, 'executable')
\|| 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
elseif has_key(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')
\|| has_key(a:linter, 'command_callback')
\|| 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
elseif has_key(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 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
elseif has_key(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)
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 = {
\ 'name': 'x',
\ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'tsserver',
\ 'callback': 'x',
\}
@ -379,46 +380,48 @@ Execute(PreProcess should accept tsserver LSP configuration):
call remove(g:linter, 'executable')
let g:linter.executable_callback = 'X'
call ale#linter#PreProcess(g:linter).lsp
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 ale#linter#PreProcess(g:linter)
call remove(g:linter, 'command')
let g:linter.command_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 ale#linter#PreProcess(g:linter)
call remove(g:linter, 'command_callback')
let g:linter.command_chain = []
Execute(PreProcess should accept stdio LSP configuration):
let g:linter = {
\ 'name': 'x',
\ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'stdio',
\ '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
AssertEqual 'stdio', ale#linter#PreProcess(g:linter).lsp
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):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'lsp',
\ 'lsp': 'socket',
\ '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 = {
\ 'name': 'x',
\ 'lsp': 'lsp',
\ 'lsp': 'socket',
\ 'callback': 'x',
\}
@ -435,4 +438,4 @@ Execute(PreProcess should complain about address_callback for non-LSP linters):
\}
AssertThrows call ale#linter#PreProcess(g:linter)
AssertEqual '`address_callback` cannot be used when lsp != ''lsp''', g:vader_exception
AssertEqual '`address_callback` cannot be used when lsp != ''socket''', g:vader_exception