Refactor LSP custom request handling
This commit is contained in:
parent
7053d468cc
commit
3321685940
4 changed files with 67 additions and 60 deletions
|
@ -5,9 +5,6 @@
|
||||||
let s:connections = get(s:, 'connections', {})
|
let s:connections = get(s:, 'connections', {})
|
||||||
let g:ale_lsp_next_message_id = 1
|
let g:ale_lsp_next_message_id = 1
|
||||||
|
|
||||||
" A Dictionary to track one-shot callbacks for custom LSP requests
|
|
||||||
let s:custom_callbacks = get(s:, 'custom_callbacks', {})
|
|
||||||
|
|
||||||
" Given an id, which can be an executable or address, and a project path,
|
" Given an id, which can be an executable or address, and a project path,
|
||||||
" create a new connection if needed. Return a unique ID for the connection.
|
" create a new connection if needed. Return a unique ID for the connection.
|
||||||
function! ale#lsp#Register(executable_or_address, project, init_options) abort
|
function! ale#lsp#Register(executable_or_address, project, init_options) abort
|
||||||
|
@ -299,19 +296,10 @@ function! ale#lsp#HandleMessage(conn_id, message) abort
|
||||||
" responses.
|
" responses.
|
||||||
if l:conn.initialized
|
if l:conn.initialized
|
||||||
for l:response in l:response_list
|
for l:response in l:response_list
|
||||||
if has_key(l:response, 'id') && has_key(s:custom_callbacks, l:response.id)
|
" Call all of the registered handlers with the response.
|
||||||
" Response to a custom request, call the registered one-shot handler.
|
for l:Callback in l:conn.callback_list
|
||||||
try
|
call ale#util#GetFunction(l:Callback)(a:conn_id, l:response)
|
||||||
call s:custom_callbacks[l:response.id](l:response)
|
endfor
|
||||||
finally
|
|
||||||
call remove(s:custom_callbacks, l:response.id)
|
|
||||||
endtry
|
|
||||||
else
|
|
||||||
" Call all of the registered handlers with the response.
|
|
||||||
for l:Callback in l:conn.callback_list
|
|
||||||
call ale#util#GetFunction(l:Callback)(a:conn_id, l:response)
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
endfor
|
endfor
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -537,18 +525,6 @@ function! ale#lsp#Send(conn_id, message) abort
|
||||||
return l:id == 0 ? -1 : l:id
|
return l:id == 0 ? -1 : l:id
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Send a custom request to an LSP server.
|
|
||||||
" The given callback is called on response.
|
|
||||||
function! ale#lsp#SendCustomRequest(conn_id, message, Callback) abort
|
|
||||||
let l:id = ale#lsp#Send(a:conn_id, a:message)
|
|
||||||
|
|
||||||
if l:id > 0
|
|
||||||
let s:custom_callbacks[l:id] = a:Callback
|
|
||||||
endif
|
|
||||||
|
|
||||||
return l:id
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Notify LSP servers or tsserver if a document is opened, if needed.
|
" Notify LSP servers or tsserver if a document is opened, if needed.
|
||||||
" If a document is opened, 1 will be returned, otherwise 0 will be returned.
|
" If a document is opened, 1 will be returned, otherwise 0 will be returned.
|
||||||
function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort
|
function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort
|
||||||
|
|
|
@ -8,6 +8,9 @@ if !has_key(s:, 'lsp_linter_map')
|
||||||
let s:lsp_linter_map = {}
|
let s:lsp_linter_map = {}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" A Dictionary to track one-shot callbacks for custom LSP requests
|
||||||
|
let s:custom_callbacks_map = get(s:, 'custom_callbacks_map', {})
|
||||||
|
|
||||||
" Check if diagnostics for a particular linter should be ignored.
|
" Check if diagnostics for a particular linter should be ignored.
|
||||||
function! s:ShouldIgnore(buffer, linter_name) abort
|
function! s:ShouldIgnore(buffer, linter_name) abort
|
||||||
" Ignore all diagnostics if LSP integration is disabled.
|
" Ignore all diagnostics if LSP integration is disabled.
|
||||||
|
@ -407,6 +410,7 @@ endfunction
|
||||||
" Clear LSP linter data for the linting engine.
|
" Clear LSP linter data for the linting engine.
|
||||||
function! ale#lsp_linter#ClearLSPData() abort
|
function! ale#lsp_linter#ClearLSPData() abort
|
||||||
let s:lsp_linter_map = {}
|
let s:lsp_linter_map = {}
|
||||||
|
let s:custom_callbacks_map = {}
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Just for tests.
|
" Just for tests.
|
||||||
|
@ -414,6 +418,24 @@ function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
|
||||||
let s:lsp_linter_map = a:replacement_map
|
let s:lsp_linter_map = a:replacement_map
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:HandleLSPResponseToCustomRequests(conn_id, response) abort
|
||||||
|
if has_key(a:response, 'id')
|
||||||
|
\&& has_key(s:custom_callbacks_map, a:response.id)
|
||||||
|
let l:Callback = remove(s:custom_callbacks_map, a:response.id)
|
||||||
|
call l:Callback(a:response)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:OnReadyForCustomRequests(message, Callback, linter, lsp_details) abort
|
||||||
|
let l:id = a:lsp_details.connection_id
|
||||||
|
let l:Callback = function('s:HandleLSPResponseToCustomRequests')
|
||||||
|
|
||||||
|
call ale#lsp#RegisterCallback(l:id, l:Callback)
|
||||||
|
|
||||||
|
let l:request_id = ale#lsp#Send(l:id, a:message)
|
||||||
|
let s:custom_callbacks_map[l:request_id] = a:Callback
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Send a custom request to an LSP linter.
|
" Send a custom request to an LSP linter.
|
||||||
function! ale#lsp_linter#SendRequest(buffer, linter_name, method, parameters, Callback) abort
|
function! ale#lsp_linter#SendRequest(buffer, linter_name, method, parameters, Callback) abort
|
||||||
let l:filetype = ale#linter#ResolveFiletype(getbufvar(a:buffer, '&filetype'))
|
let l:filetype = ale#linter#ResolveFiletype(getbufvar(a:buffer, '&filetype'))
|
||||||
|
@ -425,17 +447,13 @@ function! ale#lsp_linter#SendRequest(buffer, linter_name, method, parameters, Ca
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:linter = l:linter_list[0]
|
let l:linter = l:linter_list[0]
|
||||||
let l:executable_or_address = ''
|
|
||||||
|
|
||||||
if l:linter.lsp is# 'socket'
|
if empty(l:linter.lsp)
|
||||||
let l:executable_or_address = ale#linter#GetAddress(a:buffer, l:linter)
|
throw 'Linter "' . a:linter_name . '" does not support LSP!'
|
||||||
else
|
|
||||||
let l:executable_or_address = ale#linter#GetExecutable(a:buffer, l:linter)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:root = ale#util#GetFunction(l:linter.project_root)(a:buffer)
|
|
||||||
let l:conn_id = l:executable_or_address . ':' . l:root
|
|
||||||
let l:message = [0, a:method, a:parameters]
|
let l:message = [0, a:method, a:parameters]
|
||||||
|
let l:Callback = function('s:OnReadyForCustomRequests', [l:message, a:Callback])
|
||||||
|
|
||||||
return ale#lsp#SendCustomRequest(l:conn_id, l:message, a:Callback) == 0
|
return ale#lsp_linter#StartLSP(a:buffer, l:linter, l:Callback)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
|
@ -3205,9 +3205,6 @@ ale#lsp_linter#SendRequest(buffer, linter_name, method, parameters, Callback)
|
||||||
request is received, and takes as unique argument a dictionary representing
|
request is received, and takes as unique argument a dictionary representing
|
||||||
the response to the request obtained from the server.
|
the response to the request obtained from the server.
|
||||||
|
|
||||||
The function returns zero if the request is succesfully sent, non-zero
|
|
||||||
otherwise.
|
|
||||||
|
|
||||||
|
|
||||||
ale#other_source#ShowResults(buffer, linter_name, loclist)
|
ale#other_source#ShowResults(buffer, linter_name, loclist)
|
||||||
*ale#other_source#ShowResults()*
|
*ale#other_source#ShowResults()*
|
||||||
|
|
|
@ -7,13 +7,15 @@ Before:
|
||||||
let g:callback_result = 0
|
let g:callback_result = 0
|
||||||
let g:conn_id = -1
|
let g:conn_id = -1
|
||||||
let g:executable = 'ccls'
|
let g:executable = 'ccls'
|
||||||
|
let g:executable_or_address = ''
|
||||||
let g:linter_name = 'ccls'
|
let g:linter_name = 'ccls'
|
||||||
let g:magic_number = 42
|
let g:magic_number = 42
|
||||||
let g:message = -1
|
let g:message_list = []
|
||||||
let g:message_id = 1
|
let g:message_id = 1
|
||||||
let g:method = '$ccls/call'
|
let g:method = '$ccls/call'
|
||||||
let g:parameters = {}
|
let g:parameters = {}
|
||||||
let g:project = '/project/root'
|
let g:project_root = '/project/root'
|
||||||
|
let g:response = ''
|
||||||
let g:return_value = -1
|
let g:return_value = -1
|
||||||
|
|
||||||
let g:linter_list = [{
|
let g:linter_list = [{
|
||||||
|
@ -21,7 +23,7 @@ Before:
|
||||||
\ 'lint_file': 0,
|
\ 'lint_file': 0,
|
||||||
\ 'language': 'cpp',
|
\ 'language': 'cpp',
|
||||||
\ 'name': g:linter_name,
|
\ 'name': g:linter_name,
|
||||||
\ 'project_root': {b -> g:project},
|
\ 'project_root': {b -> g:project_root},
|
||||||
\ 'aliases': [],
|
\ 'aliases': [],
|
||||||
\ 'language_callback': {b -> 'cpp'},
|
\ 'language_callback': {b -> 'cpp'},
|
||||||
\ 'read_buffer': 1,
|
\ 'read_buffer': 1,
|
||||||
|
@ -34,10 +36,20 @@ Before:
|
||||||
return 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
return 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Register the server with given executable or address
|
" Replace the StartLSP function to mock an LSP linter
|
||||||
function! InitServer(executable_or_address) abort
|
function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
|
||||||
let g:conn_id = ale#lsp#Register(a:executable_or_address, g:project, {})
|
let g:conn_id = ale#lsp#Register(g:executable_or_address, g:project_root, {})
|
||||||
|
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
|
||||||
call ale#lsp#HandleMessage(g:conn_id, Encode({'method': 'initialize'}))
|
call ale#lsp#HandleMessage(g:conn_id, Encode({'method': 'initialize'}))
|
||||||
|
|
||||||
|
let l:details = {
|
||||||
|
\ 'command': g:executable,
|
||||||
|
\ 'buffer': a:buffer,
|
||||||
|
\ 'connection_id': g:conn_id,
|
||||||
|
\ 'project_root': g:project_root,
|
||||||
|
\}
|
||||||
|
|
||||||
|
call ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Dummy callback
|
" Dummy callback
|
||||||
|
@ -52,25 +64,29 @@ Before:
|
||||||
|
|
||||||
" Replace the Send function to mock an LSP linter
|
" Replace the Send function to mock an LSP linter
|
||||||
function! ale#lsp#Send(conn_id, message) abort
|
function! ale#lsp#Send(conn_id, message) abort
|
||||||
let g:message = a:message
|
call add(g:message_list, a:message)
|
||||||
return g:message_id
|
return g:message_id
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Code for a test case
|
" Code for a test case
|
||||||
function! TestCase() abort
|
function! TestCase() abort
|
||||||
" Test sending a custom request
|
" Test sending a custom request
|
||||||
let g:return_value = ale#lsp_linter#SendRequest(bufnr('%'), g:linter_name, g:method, g:parameters, function('Callback'))
|
let g:return_value = ale#lsp_linter#SendRequest(
|
||||||
|
\ bufnr('%'),
|
||||||
|
\ g:linter_name,
|
||||||
|
\ g:method,
|
||||||
|
\ g:parameters,
|
||||||
|
\ function('Callback'))
|
||||||
|
|
||||||
AssertEqual
|
Assert index(g:message_list, [0, g:method, g:parameters]) >= 0
|
||||||
\ 0,
|
|
||||||
\ g:return_value
|
|
||||||
|
|
||||||
AssertEqual
|
|
||||||
\ [0, g:method, g:parameters],
|
|
||||||
\ g:message
|
|
||||||
|
|
||||||
" Mock an incoming response to the request
|
" Mock an incoming response to the request
|
||||||
call ale#lsp#HandleMessage(g:conn_id, Encode({'id': g:message_id, 'jsonrpc': '2.0', 'result': {'value': g:magic_number}}))
|
let g:response = Encode({
|
||||||
|
\ 'id': g:message_id,
|
||||||
|
\ 'jsonrpc': '2.0',
|
||||||
|
\ 'result': {'value': g:magic_number}
|
||||||
|
\ })
|
||||||
|
call ale#lsp#HandleMessage(g:conn_id, g:response)
|
||||||
|
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ g:magic_number,
|
\ g:magic_number,
|
||||||
|
@ -87,15 +103,15 @@ After:
|
||||||
unlet! g:executable
|
unlet! g:executable
|
||||||
unlet! g:linter_name
|
unlet! g:linter_name
|
||||||
unlet! g:magic_number
|
unlet! g:magic_number
|
||||||
unlet! g:message
|
unlet! g:message_list
|
||||||
unlet! g:message_id
|
unlet! g:message_id
|
||||||
unlet! g:method
|
unlet! g:method
|
||||||
unlet! g:parameters
|
unlet! g:parameters
|
||||||
unlet! g:project
|
unlet! g:project_root
|
||||||
|
unlet! g:response
|
||||||
unlet! g:return_value
|
unlet! g:return_value
|
||||||
|
|
||||||
delfunction Encode
|
delfunction Encode
|
||||||
delfunction InitServer
|
|
||||||
delfunction Callback
|
delfunction Callback
|
||||||
delfunction TestCase
|
delfunction TestCase
|
||||||
|
|
||||||
|
@ -105,7 +121,7 @@ After:
|
||||||
|
|
||||||
Given cpp(Empty cpp file):
|
Given cpp(Empty cpp file):
|
||||||
Execute(Test custom request to server identified by executable):
|
Execute(Test custom request to server identified by executable):
|
||||||
call InitServer(g:executable)
|
let g:executable_or_address = g:executable
|
||||||
let g:linter_list[0].executable = {b -> g:executable}
|
let g:linter_list[0].executable = {b -> g:executable}
|
||||||
let g:linter_list[0].lsp = 'stdio'
|
let g:linter_list[0].lsp = 'stdio'
|
||||||
|
|
||||||
|
@ -113,7 +129,7 @@ Execute(Test custom request to server identified by executable):
|
||||||
|
|
||||||
Given cpp(Empty cpp file):
|
Given cpp(Empty cpp file):
|
||||||
Execute(Test custom request to server identified by address):
|
Execute(Test custom request to server identified by address):
|
||||||
call InitServer(g:address)
|
let g:executable_or_address = g:address
|
||||||
let g:linter_list[0].address = {b -> g:address}
|
let g:linter_list[0].address = {b -> g:address}
|
||||||
let g:linter_list[0].lsp = 'socket'
|
let g:linter_list[0].lsp = 'socket'
|
||||||
|
|
||||||
|
|
Reference in a new issue