mirror of
https://github.com/dense-analysis/ale.git
synced 2026-05-17 22:09:47 +08:00
Close #5112 - Support newer Pyright and other LSPs
Add support for dynamic capability registration for the diagnostic pull model to support Pyright >= 1.1.407 and other language servers. This is a rather complex and intricate change tested with Pyright and gopls, and may need further tweaking if something breaks with another server.
This commit is contained in:
+141
-1
@@ -23,6 +23,7 @@ function! ale#lsp#Register(executable_or_address, project, language, init_option
|
|||||||
" config: Configuration settings to send to the server.
|
" config: Configuration settings to send to the server.
|
||||||
" callback_list: A list of callbacks for handling LSP responses.
|
" callback_list: A list of callbacks for handling LSP responses.
|
||||||
" capabilities_queue: The list of callbacks to call with capabilities.
|
" capabilities_queue: The list of callbacks to call with capabilities.
|
||||||
|
" dynamic_registrations: A map of dynamically registered capabilities.
|
||||||
" capabilities: Features the server supports.
|
" capabilities: Features the server supports.
|
||||||
let s:connections[l:conn_id] = {
|
let s:connections[l:conn_id] = {
|
||||||
\ 'id': l:conn_id,
|
\ 'id': l:conn_id,
|
||||||
@@ -37,6 +38,8 @@ function! ale#lsp#Register(executable_or_address, project, language, init_option
|
|||||||
\ 'config': {},
|
\ 'config': {},
|
||||||
\ 'callback_list': [],
|
\ 'callback_list': [],
|
||||||
\ 'init_queue': [],
|
\ 'init_queue': [],
|
||||||
|
\ 'dynamic_registrations': {},
|
||||||
|
\ 'static_pull_model': 0,
|
||||||
\ 'capabilities': {
|
\ 'capabilities': {
|
||||||
\ 'hover': 0,
|
\ 'hover': 0,
|
||||||
\ 'rename': 0,
|
\ 'rename': 0,
|
||||||
@@ -196,6 +199,27 @@ function! ale#lsp#ReadMessageData(data) abort
|
|||||||
return [l:remainder, l:response_list]
|
return [l:remainder, l:response_list]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:UpdatePullModelCapability(conn) abort
|
||||||
|
let a:conn.capabilities.pull_model = get(a:conn, 'static_pull_model', 0)
|
||||||
|
|
||||||
|
if a:conn.capabilities.pull_model
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
for l:registration in values(get(a:conn, 'dynamic_registrations', {}))
|
||||||
|
if get(l:registration, 'method', '') is# 'textDocument/diagnostic'
|
||||||
|
let l:options = get(l:registration, 'registerOptions', {})
|
||||||
|
|
||||||
|
if type(l:options) is v:t_dict
|
||||||
|
\&& type(get(l:options, 'interFileDependencies')) is v:t_bool
|
||||||
|
let a:conn.capabilities.pull_model = 1
|
||||||
|
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Update capabilities from the server, so we know which features the server
|
" Update capabilities from the server, so we know which features the server
|
||||||
" supports.
|
" supports.
|
||||||
function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
||||||
@@ -280,7 +304,8 @@ function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
|||||||
" Check if the language server supports pull model diagnostics.
|
" Check if the language server supports pull model diagnostics.
|
||||||
if type(get(a:capabilities, 'diagnosticProvider')) is v:t_dict
|
if type(get(a:capabilities, 'diagnosticProvider')) is v:t_dict
|
||||||
if type(get(a:capabilities.diagnosticProvider, 'interFileDependencies')) is v:t_bool
|
if type(get(a:capabilities.diagnosticProvider, 'interFileDependencies')) is v:t_bool
|
||||||
let l:conn.capabilities.pull_model = 1
|
let l:conn.static_pull_model = 1
|
||||||
|
call s:UpdatePullModelCapability(l:conn)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -311,6 +336,121 @@ function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Update capabilities registered dynamically with client/registerCapability.
|
||||||
|
" Returns 1 when pull diagnostics were registered.
|
||||||
|
function! ale#lsp#RegisterCapabilities(conn_id, registrations) abort
|
||||||
|
let l:conn = get(s:connections, a:conn_id, {})
|
||||||
|
|
||||||
|
if empty(l:conn) || type(a:registrations) isnot v:t_list
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key(l:conn, 'dynamic_registrations')
|
||||||
|
let l:conn.dynamic_registrations = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:registered_pull_diagnostics = 0
|
||||||
|
|
||||||
|
for l:registration in a:registrations
|
||||||
|
if type(l:registration) isnot v:t_dict
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:id = get(l:registration, 'id', '')
|
||||||
|
|
||||||
|
if empty(l:id)
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:conn.dynamic_registrations[l:id] = l:registration
|
||||||
|
|
||||||
|
if get(l:registration, 'method', '') is# 'textDocument/diagnostic'
|
||||||
|
let l:options = get(l:registration, 'registerOptions', {})
|
||||||
|
|
||||||
|
if type(l:options) is v:t_dict
|
||||||
|
\&& type(get(l:options, 'interFileDependencies')) is v:t_bool
|
||||||
|
let l:registered_pull_diagnostics = 1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
call s:UpdatePullModelCapability(l:conn)
|
||||||
|
|
||||||
|
return l:registered_pull_diagnostics
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Update capabilities removed dynamically with client/unregisterCapability.
|
||||||
|
" The LSP spec names the list "unregisterations".
|
||||||
|
function! ale#lsp#UnregisterCapabilities(conn_id, unregisterations) abort
|
||||||
|
let l:conn = get(s:connections, a:conn_id, {})
|
||||||
|
|
||||||
|
if empty(l:conn) || type(a:unregisterations) isnot v:t_list
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key(l:conn, 'dynamic_registrations')
|
||||||
|
let l:conn.dynamic_registrations = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:unregistered_pull_diagnostics = 0
|
||||||
|
|
||||||
|
for l:unregistration in a:unregisterations
|
||||||
|
if type(l:unregistration) isnot v:t_dict
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:id = get(l:unregistration, 'id', '')
|
||||||
|
let l:method = get(l:unregistration, 'method', '')
|
||||||
|
|
||||||
|
if empty(l:id) || !has_key(l:conn.dynamic_registrations, l:id)
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:registration = l:conn.dynamic_registrations[l:id]
|
||||||
|
|
||||||
|
if get(l:registration, 'method', '') isnot# l:method
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if l:method is# 'textDocument/diagnostic'
|
||||||
|
let l:unregistered_pull_diagnostics = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
call remove(l:conn.dynamic_registrations, l:id)
|
||||||
|
endfor
|
||||||
|
|
||||||
|
call s:UpdatePullModelCapability(l:conn)
|
||||||
|
|
||||||
|
return l:unregistered_pull_diagnostics
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Send textDocument/diagnostic requests for all open documents on a connection.
|
||||||
|
" Returns a list of request details so callers can map responses back to URIs.
|
||||||
|
function! ale#lsp#SendDiagnosticsForOpenDocuments(conn_id) abort
|
||||||
|
let l:conn = get(s:connections, a:conn_id, {})
|
||||||
|
let l:request_list = []
|
||||||
|
|
||||||
|
if empty(l:conn) || !ale#lsp#HasCapability(a:conn_id, 'pull_model')
|
||||||
|
return l:request_list
|
||||||
|
endif
|
||||||
|
|
||||||
|
for l:buffer_string in sort(keys(l:conn.open_documents))
|
||||||
|
let l:buffer = str2nr(l:buffer_string)
|
||||||
|
let l:message = ale#lsp#message#Diagnostic(l:buffer)
|
||||||
|
let l:request_id = ale#lsp#Send(a:conn_id, l:message)
|
||||||
|
|
||||||
|
if l:request_id > 0
|
||||||
|
call add(l:request_list, {
|
||||||
|
\ 'id': l:request_id,
|
||||||
|
\ 'buffer': l:buffer,
|
||||||
|
\ 'uri': l:message[2].textDocument.uri,
|
||||||
|
\})
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return l:request_list
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Update a connection's configuration dictionary and notify LSP servers
|
" Update a connection's configuration dictionary and notify LSP servers
|
||||||
" of any changes since the last update. Returns 1 if a configuration
|
" of any changes since the last update. Returns 1 if a configuration
|
||||||
" update was sent; otherwise 0 will be returned.
|
" update was sent; otherwise 0 will be returned.
|
||||||
|
|||||||
@@ -210,6 +210,24 @@ function! s:HandleLSPErrorMessage(linter, response) abort
|
|||||||
call ale#lsp_linter#AddErrorMessage(a:linter.name, l:message)
|
call ale#lsp_linter#AddErrorMessage(a:linter.name, l:message)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:SendPullDiagnosticsForOpenDocuments(conn_id) abort
|
||||||
|
let l:linter = get(s:lsp_linter_map, a:conn_id)
|
||||||
|
|
||||||
|
if empty(l:linter)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
for l:request in ale#lsp#SendDiagnosticsForOpenDocuments(a:conn_id)
|
||||||
|
let l:info = get(g:ale_buffer_info, l:request.buffer, {})
|
||||||
|
|
||||||
|
if !empty(l:info)
|
||||||
|
call ale#engine#MarkLinterActive(l:info, l:linter)
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:diagnostic_uri_map[l:request.id] = l:request.uri
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! ale#lsp_linter#AddErrorMessage(linter_name, message) abort
|
function! ale#lsp_linter#AddErrorMessage(linter_name, message) abort
|
||||||
" This global variable is set here so we don't load the debugging.vim file
|
" This global variable is set here so we don't load the debugging.vim file
|
||||||
" until someone uses :ALEInfo.
|
" until someone uses :ALEInfo.
|
||||||
@@ -228,20 +246,39 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
|
|||||||
if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
|
if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
|
||||||
let l:linter = get(s:lsp_linter_map, a:conn_id, {})
|
let l:linter = get(s:lsp_linter_map, a:conn_id, {})
|
||||||
|
|
||||||
|
" Clean up values in the map on error.
|
||||||
|
if empty(l:method) && has_key(s:diagnostic_uri_map, get(a:response, 'id'))
|
||||||
|
call remove(s:diagnostic_uri_map, a:response.id)
|
||||||
|
endif
|
||||||
|
|
||||||
call s:HandleLSPErrorMessage(l:linter, a:response)
|
call s:HandleLSPErrorMessage(l:linter, a:response)
|
||||||
elseif l:method is# 'textDocument/publishDiagnostics'
|
elseif l:method is# 'textDocument/publishDiagnostics'
|
||||||
let l:uri = a:response.params.uri
|
let l:uri = a:response.params.uri
|
||||||
let l:diagnostics = a:response.params.diagnostics
|
let l:diagnostics = a:response.params.diagnostics
|
||||||
|
|
||||||
call ale#lsp_linter#HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
call ale#lsp_linter#HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
||||||
elseif has_key(s:diagnostic_uri_map, get(a:response, 'id'))
|
elseif l:method is# 'workspace/diagnostic/refresh'
|
||||||
let l:uri = remove(s:diagnostic_uri_map, a:response.id)
|
call ale#lsp#SendResponse(a:conn_id, a:response.id, v:null)
|
||||||
let l:diagnostics = a:response.result.kind is# 'unchanged'
|
call s:SendPullDiagnosticsForOpenDocuments(a:conn_id)
|
||||||
\ ? 'unchanged'
|
|
||||||
\ : a:response.result.items
|
|
||||||
|
|
||||||
call ale#lsp_linter#HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
|
||||||
elseif l:method is# 'client/registerCapability'
|
elseif l:method is# 'client/registerCapability'
|
||||||
|
let l:registered_pull_diagnostics = ale#lsp#RegisterCapabilities(
|
||||||
|
\ a:conn_id,
|
||||||
|
\ get(get(a:response, 'params', {}), 'registrations', []),
|
||||||
|
\)
|
||||||
|
|
||||||
|
call ale#lsp#SendResponse(a:conn_id, a:response.id, v:null)
|
||||||
|
|
||||||
|
if l:registered_pull_diagnostics
|
||||||
|
call s:SendPullDiagnosticsForOpenDocuments(a:conn_id)
|
||||||
|
endif
|
||||||
|
elseif l:method is# 'client/unregisterCapability'
|
||||||
|
let l:unregisterations = get(
|
||||||
|
\ get(a:response, 'params', {}),
|
||||||
|
\ 'unregisterations',
|
||||||
|
\ get(get(a:response, 'params', {}), 'unregistrations', []),
|
||||||
|
\)
|
||||||
|
|
||||||
|
call ale#lsp#UnregisterCapabilities(a:conn_id, l:unregisterations)
|
||||||
call ale#lsp#SendResponse(a:conn_id, a:response.id, v:null)
|
call ale#lsp#SendResponse(a:conn_id, a:response.id, v:null)
|
||||||
elseif l:method is# 'workspace/configuration'
|
elseif l:method is# 'workspace/configuration'
|
||||||
let l:items = get(get(a:response, 'params', {}), 'items', [])
|
let l:items = get(get(a:response, 'params', {}), 'items', [])
|
||||||
@@ -253,6 +290,18 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
|
|||||||
\ g:ale_lsp_show_message_format,
|
\ g:ale_lsp_show_message_format,
|
||||||
\ a:response.params
|
\ a:response.params
|
||||||
\)
|
\)
|
||||||
|
elseif empty(l:method) && has_key(s:diagnostic_uri_map, get(a:response, 'id'))
|
||||||
|
let l:uri = remove(s:diagnostic_uri_map, a:response.id)
|
||||||
|
let l:diagnostics = 'unchanged'
|
||||||
|
|
||||||
|
if type(get(a:response, 'result')) is v:t_dict
|
||||||
|
if get(a:response.result, 'kind', '') isnot# 'unchanged'
|
||||||
|
\&& type(get(a:response.result, 'items')) is v:t_list
|
||||||
|
let l:diagnostics = a:response.result.items
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
call ale#lsp_linter#HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
||||||
elseif get(a:response, 'type', '') is# 'event'
|
elseif get(a:response, 'type', '') is# 'event'
|
||||||
\&& get(a:response, 'event', '') is# 'semanticDiag'
|
\&& get(a:response, 'event', '') is# 'semanticDiag'
|
||||||
call s:HandleTSServerDiagnostics(a:response, 'semantic')
|
call s:HandleTSServerDiagnostics(a:response, 'semantic')
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
Before:
|
Before:
|
||||||
|
runtime autoload/ale/lsp.vim
|
||||||
|
|
||||||
Save g:ale_set_lists_synchronously
|
Save g:ale_set_lists_synchronously
|
||||||
Save g:ale_buffer_info
|
Save g:ale_buffer_info
|
||||||
Save g:ale_lsp_error_messages
|
Save g:ale_lsp_error_messages
|
||||||
@@ -40,6 +42,7 @@ After:
|
|||||||
call ale#test#RestoreDirectory()
|
call ale#test#RestoreDirectory()
|
||||||
call ale#linter#Reset()
|
call ale#linter#Reset()
|
||||||
call ale#lsp_linter#ClearLSPData()
|
call ale#lsp_linter#ClearLSPData()
|
||||||
|
call ale#lsp_linter#ClearDiagnosticURIMap()
|
||||||
|
|
||||||
Given foobar(An empty file):
|
Given foobar(An empty file):
|
||||||
Execute(tsserver syntax error responses should be handled correctly):
|
Execute(tsserver syntax error responses should be handled correctly):
|
||||||
@@ -534,6 +537,60 @@ Execute(LSP pull model diagnostic responses that are 'unchanged' should be handl
|
|||||||
\ g:ale_buffer_info[bufnr('')].loclist
|
\ g:ale_buffer_info[bufnr('')].loclist
|
||||||
AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
|
AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
|
||||||
|
|
||||||
|
Execute(LSP pull model diagnostic errors should clear pending requests):
|
||||||
|
let b:ale_linters = ['eclipselsp']
|
||||||
|
runtime ale_linters/java/eclipselsp.vim
|
||||||
|
|
||||||
|
if has('win32')
|
||||||
|
call ale#test#SetFilename('filename,[]^$.ts')
|
||||||
|
else
|
||||||
|
call ale#test#SetFilename('filename*?,{}[]^$.java')
|
||||||
|
endif
|
||||||
|
|
||||||
|
call ale#engine#InitBufferInfo(bufnr(''))
|
||||||
|
let g:ale_buffer_info[bufnr('')].loclist = [
|
||||||
|
\ {
|
||||||
|
\ 'lnum': 1,
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'col': 1,
|
||||||
|
\ 'pattern': '',
|
||||||
|
\ 'valid': 1,
|
||||||
|
\ 'vcol': 0,
|
||||||
|
\ 'nr': -1,
|
||||||
|
\ 'type': 'W',
|
||||||
|
\ 'text': 'Missing JRE 1-8'
|
||||||
|
\ },
|
||||||
|
\]
|
||||||
|
|
||||||
|
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'eclipselsp', 'aliases': [], 'lsp': 'stdio'}})
|
||||||
|
call ale#lsp_linter#SetDiagnosticURIMap({'347': ale#util#ToURI(expand('%:p'))})
|
||||||
|
|
||||||
|
call ale#lsp_linter#HandleLSPResponse(1, {
|
||||||
|
\ 'jsonrpc': '2.0',
|
||||||
|
\ 'id': 347,
|
||||||
|
\ 'error': {
|
||||||
|
\ 'code': -32802,
|
||||||
|
\ 'message': 'server cancelled',
|
||||||
|
\ },
|
||||||
|
\})
|
||||||
|
|
||||||
|
AssertEqual {}, ale#lsp_linter#GetDiagnosticURIMap()
|
||||||
|
AssertEqual
|
||||||
|
\ [
|
||||||
|
\ {
|
||||||
|
\ 'lnum': 1,
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'col': 1,
|
||||||
|
\ 'pattern': '',
|
||||||
|
\ 'valid': 1,
|
||||||
|
\ 'vcol': 0,
|
||||||
|
\ 'nr': -1,
|
||||||
|
\ 'type': 'W',
|
||||||
|
\ 'text': 'Missing JRE 1-8'
|
||||||
|
\ }
|
||||||
|
\ ],
|
||||||
|
\ g:ale_buffer_info[bufnr('')].loclist
|
||||||
|
|
||||||
Execute(workspace/configuration requests should be answered with the connection config):
|
Execute(workspace/configuration requests should be answered with the connection config):
|
||||||
let g:sent_responses = []
|
let g:sent_responses = []
|
||||||
|
|
||||||
@@ -564,26 +621,134 @@ Execute(workspace/configuration requests should be answered with the connection
|
|||||||
|
|
||||||
Execute(client/registerCapability requests should be acknowledged):
|
Execute(client/registerCapability requests should be acknowledged):
|
||||||
let g:sent_responses = []
|
let g:sent_responses = []
|
||||||
|
let g:registered_capabilities = []
|
||||||
|
let g:sent_pull_diagnostics = []
|
||||||
|
|
||||||
function! ale#lsp#SendResponse(conn_id, id, result) abort
|
function! ale#lsp#SendResponse(conn_id, id, result) abort
|
||||||
call add(g:sent_responses, [a:conn_id, a:id, a:result])
|
call add(g:sent_responses, [a:conn_id, a:id, a:result])
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! ale#lsp#RegisterCapabilities(conn_id, registrations) abort
|
||||||
|
call add(g:registered_capabilities, [a:conn_id, a:registrations])
|
||||||
|
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#lsp#SendDiagnosticsForOpenDocuments(conn_id) abort
|
||||||
|
call add(g:sent_pull_diagnostics, a:conn_id)
|
||||||
|
|
||||||
|
return []
|
||||||
|
endfunction
|
||||||
|
|
||||||
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'expert', 'aliases': [], 'lsp': 'stdio'}})
|
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'expert', 'aliases': [], 'lsp': 'stdio'}})
|
||||||
call ale#lsp_linter#HandleLSPResponse(1, {
|
call ale#lsp_linter#HandleLSPResponse(1, {
|
||||||
\ 'jsonrpc': '2.0',
|
\ 'jsonrpc': '2.0',
|
||||||
\ 'id': 12,
|
\ 'id': 12,
|
||||||
\ 'method': 'client/registerCapability',
|
\ 'method': 'client/registerCapability',
|
||||||
\ 'params': {
|
\ 'params': {
|
||||||
\ 'registrations': [{'id': 'abc', 'method': 'textDocument/didSave'}],
|
\ 'registrations': [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'abc',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {'interFileDependencies': v:true},
|
||||||
|
\ },
|
||||||
|
\ ],
|
||||||
\ },
|
\ },
|
||||||
\})
|
\})
|
||||||
|
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ [[1, 12, v:null]],
|
\ [[1, 12, v:null]],
|
||||||
\ g:sent_responses
|
\ g:sent_responses
|
||||||
|
AssertEqual
|
||||||
|
\ [[1, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'abc',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {'interFileDependencies': v:true},
|
||||||
|
\ },
|
||||||
|
\ ]]],
|
||||||
|
\ g:registered_capabilities
|
||||||
|
AssertEqual [1], g:sent_pull_diagnostics
|
||||||
|
|
||||||
unlet! g:sent_responses
|
unlet! g:sent_responses
|
||||||
|
unlet! g:registered_capabilities
|
||||||
|
unlet! g:sent_pull_diagnostics
|
||||||
|
runtime autoload/ale/lsp.vim
|
||||||
|
|
||||||
|
Execute(workspace/diagnostic/refresh requests should pull diagnostics):
|
||||||
|
let g:sent_responses = []
|
||||||
|
let g:sent_pull_diagnostics = []
|
||||||
|
|
||||||
|
function! ale#lsp#SendResponse(conn_id, id, result) abort
|
||||||
|
call add(g:sent_responses, [a:conn_id, a:id, a:result])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#lsp#SendDiagnosticsForOpenDocuments(conn_id) abort
|
||||||
|
call add(g:sent_pull_diagnostics, a:conn_id)
|
||||||
|
|
||||||
|
return []
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'expert', 'aliases': [], 'lsp': 'stdio'}})
|
||||||
|
call ale#lsp_linter#SetDiagnosticURIMap({'13': 'file://foo.py'})
|
||||||
|
|
||||||
|
call ale#lsp_linter#HandleLSPResponse(1, {
|
||||||
|
\ 'jsonrpc': '2.0',
|
||||||
|
\ 'id': 13,
|
||||||
|
\ 'method': 'workspace/diagnostic/refresh',
|
||||||
|
\})
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ [[1, 13, v:null]],
|
||||||
|
\ g:sent_responses
|
||||||
|
AssertEqual [1], g:sent_pull_diagnostics
|
||||||
|
AssertEqual {'13': 'file://foo.py'}, ale#lsp_linter#GetDiagnosticURIMap()
|
||||||
|
|
||||||
|
unlet! g:sent_responses
|
||||||
|
unlet! g:sent_pull_diagnostics
|
||||||
|
runtime autoload/ale/lsp.vim
|
||||||
|
|
||||||
|
Execute(client/unregisterCapability requests should unregister and be acknowledged):
|
||||||
|
let g:sent_responses = []
|
||||||
|
let g:unregistered_capabilities = []
|
||||||
|
|
||||||
|
function! ale#lsp#SendResponse(conn_id, id, result) abort
|
||||||
|
call add(g:sent_responses, [a:conn_id, a:id, a:result])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#lsp#UnregisterCapabilities(conn_id, unregisterations) abort
|
||||||
|
call add(g:unregistered_capabilities, [a:conn_id, a:unregisterations])
|
||||||
|
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'expert', 'aliases': [], 'lsp': 'stdio'}})
|
||||||
|
call ale#lsp_linter#SetDiagnosticURIMap({'14': 'file://foo.py'})
|
||||||
|
|
||||||
|
call ale#lsp_linter#HandleLSPResponse(1, {
|
||||||
|
\ 'jsonrpc': '2.0',
|
||||||
|
\ 'id': 14,
|
||||||
|
\ 'method': 'client/unregisterCapability',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'unregisterations': [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'abc',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ },
|
||||||
|
\ ],
|
||||||
|
\ },
|
||||||
|
\})
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ [[1, 14, v:null]],
|
||||||
|
\ g:sent_responses
|
||||||
|
AssertEqual
|
||||||
|
\ [[1, [{'id': 'abc', 'method': 'textDocument/diagnostic'}]]],
|
||||||
|
\ g:unregistered_capabilities
|
||||||
|
AssertEqual {'14': 'file://foo.py'}, ale#lsp_linter#GetDiagnosticURIMap()
|
||||||
|
|
||||||
|
unlet! g:sent_responses
|
||||||
|
unlet! g:unregistered_capabilities
|
||||||
runtime autoload/ale/lsp.vim
|
runtime autoload/ale/lsp.vim
|
||||||
|
|
||||||
Execute(LSP errors should be logged in the history):
|
Execute(LSP errors should be logged in the history):
|
||||||
|
|||||||
@@ -214,6 +214,128 @@ Execute(Capabilities should be enabled when sent as Dictionaries):
|
|||||||
\ b:conn.capabilities
|
\ b:conn.capabilities
|
||||||
AssertEqual [[1, 'initialized', {}]], g:message_list
|
AssertEqual [[1, 'initialized', {}]], g:message_list
|
||||||
|
|
||||||
|
Execute(Pull model diagnostics should be enabled when registered dynamically):
|
||||||
|
AssertEqual 0, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ 1,
|
||||||
|
\ ale#lsp#RegisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ 'workspaceDiagnostics': v:false,
|
||||||
|
\ 'documentSelector': v:null,
|
||||||
|
\ 'identifier': 'Pyright',
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 1, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
Execute(Pull model diagnostics should be disabled when unregistered dynamically):
|
||||||
|
call ale#lsp#RegisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 1, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ 1,
|
||||||
|
\ ale#lsp#UnregisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 0, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
Execute(Pull model diagnostics should stay enabled while another registration exists):
|
||||||
|
call ale#lsp#RegisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics-one',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics-two',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
call ale#lsp#UnregisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics-one',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 1, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
Execute(Unregistering non-diagnostic registrations should not change diagnostics):
|
||||||
|
call ale#lsp#RegisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ {
|
||||||
|
\ 'id': 'workspace-symbols',
|
||||||
|
\ 'method': 'workspace/symbol',
|
||||||
|
\ 'registerOptions': {},
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
call ale#lsp#UnregisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'workspace-symbols',
|
||||||
|
\ 'method': 'workspace/symbol',
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 1, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
|
Execute(Static pull model diagnostics should not be disabled by unregistering dynamic registrations):
|
||||||
|
call ale#lsp#UpdateCapabilities(b:conn.id, {
|
||||||
|
\ 'diagnosticProvider': {
|
||||||
|
\ 'interFileDependencies': v:false,
|
||||||
|
\ },
|
||||||
|
\})
|
||||||
|
|
||||||
|
call ale#lsp#RegisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ 'registerOptions': {
|
||||||
|
\ 'interFileDependencies': v:true,
|
||||||
|
\ },
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
call ale#lsp#UnregisterCapabilities(b:conn.id, [
|
||||||
|
\ {
|
||||||
|
\ 'id': 'pyright-diagnostics',
|
||||||
|
\ 'method': 'textDocument/diagnostic',
|
||||||
|
\ },
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
AssertEqual 1, b:conn.capabilities.pull_model
|
||||||
|
|
||||||
Execute(Results that are not dictionaries should be handled correctly):
|
Execute(Results that are not dictionaries should be handled correctly):
|
||||||
call ale#lsp#HandleInitResponse(b:conn, {
|
call ale#lsp#HandleInitResponse(b:conn, {
|
||||||
\ 'jsonrpc': '2.0',
|
\ 'jsonrpc': '2.0',
|
||||||
|
|||||||
Reference in New Issue
Block a user