mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-08 13:34:46 +08:00
Handle other LSP capabilities via ALE
Save capabilities from language servers ALE connects to via Neovim's LSP client and handle responses to requests ALE sends to language servers. This enables ALE to work with Neovim's language client for its commands while also letting users directly use the connected clients for native Neovim keybinds and so on.
This commit is contained in:
@@ -195,101 +195,107 @@ 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! s:UpdateCapabilities(conn, capabilities) abort
|
function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
||||||
|
let l:conn = get(s:connections, a:conn_id, {})
|
||||||
|
|
||||||
|
if empty(l:conn)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
if type(a:capabilities) isnot v:t_dict
|
if type(a:capabilities) isnot v:t_dict
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'hoverProvider') is v:true
|
if get(a:capabilities, 'hoverProvider') is v:true
|
||||||
let a:conn.capabilities.hover = 1
|
let l:conn.capabilities.hover = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'hoverProvider')) is v:t_dict
|
if type(get(a:capabilities, 'hoverProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.hover = 1
|
let l:conn.capabilities.hover = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'referencesProvider') is v:true
|
if get(a:capabilities, 'referencesProvider') is v:true
|
||||||
let a:conn.capabilities.references = 1
|
let l:conn.capabilities.references = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'referencesProvider')) is v:t_dict
|
if type(get(a:capabilities, 'referencesProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.references = 1
|
let l:conn.capabilities.references = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'renameProvider') is v:true
|
if get(a:capabilities, 'renameProvider') is v:true
|
||||||
let a:conn.capabilities.rename = 1
|
let l:conn.capabilities.rename = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'renameProvider')) is v:t_dict
|
if type(get(a:capabilities, 'renameProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.rename = 1
|
let l:conn.capabilities.rename = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'codeActionProvider') is v:true
|
if get(a:capabilities, 'codeActionProvider') is v:true
|
||||||
let a:conn.capabilities.code_actions = 1
|
let l:conn.capabilities.code_actions = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'codeActionProvider')) is v:t_dict
|
if type(get(a:capabilities, 'codeActionProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.code_actions = 1
|
let l:conn.capabilities.code_actions = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !empty(get(a:capabilities, 'completionProvider'))
|
if !empty(get(a:capabilities, 'completionProvider'))
|
||||||
let a:conn.capabilities.completion = 1
|
let l:conn.capabilities.completion = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'completionProvider')) is v:t_dict
|
if type(get(a:capabilities, 'completionProvider')) is v:t_dict
|
||||||
let l:chars = get(a:capabilities.completionProvider, 'triggerCharacters')
|
let l:chars = get(a:capabilities.completionProvider, 'triggerCharacters')
|
||||||
|
|
||||||
if type(l:chars) is v:t_list
|
if type(l:chars) is v:t_list
|
||||||
let a:conn.capabilities.completion_trigger_characters = l:chars
|
let l:conn.capabilities.completion_trigger_characters = l:chars
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'definitionProvider') is v:true
|
if get(a:capabilities, 'definitionProvider') is v:true
|
||||||
let a:conn.capabilities.definition = 1
|
let l:conn.capabilities.definition = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'definitionProvider')) is v:t_dict
|
if type(get(a:capabilities, 'definitionProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.definition = 1
|
let l:conn.capabilities.definition = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'typeDefinitionProvider') is v:true
|
if get(a:capabilities, 'typeDefinitionProvider') is v:true
|
||||||
let a:conn.capabilities.typeDefinition = 1
|
let l:conn.capabilities.typeDefinition = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'typeDefinitionProvider')) is v:t_dict
|
if type(get(a:capabilities, 'typeDefinitionProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.typeDefinition = 1
|
let l:conn.capabilities.typeDefinition = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'implementationProvider') is v:true
|
if get(a:capabilities, 'implementationProvider') is v:true
|
||||||
let a:conn.capabilities.implementation = 1
|
let l:conn.capabilities.implementation = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'implementationProvider')) is v:t_dict
|
if type(get(a:capabilities, 'implementationProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.implementation = 1
|
let l:conn.capabilities.implementation = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
|
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
|
||||||
let a:conn.capabilities.symbol_search = 1
|
let l:conn.capabilities.symbol_search = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'workspaceSymbolProvider')) is v:t_dict
|
if type(get(a:capabilities, 'workspaceSymbolProvider')) is v:t_dict
|
||||||
let a:conn.capabilities.symbol_search = 1
|
let l:conn.capabilities.symbol_search = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(a:capabilities, 'textDocumentSync')) is v:t_dict
|
if type(get(a:capabilities, 'textDocumentSync')) is v:t_dict
|
||||||
let l:syncOptions = get(a:capabilities, 'textDocumentSync')
|
let l:syncOptions = get(a:capabilities, 'textDocumentSync')
|
||||||
|
|
||||||
if get(l:syncOptions, 'save') is v:true
|
if get(l:syncOptions, 'save') is v:true
|
||||||
let a:conn.capabilities.did_save = 1
|
let l:conn.capabilities.did_save = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if type(get(l:syncOptions, 'save')) is v:t_dict
|
if type(get(l:syncOptions, 'save')) is v:t_dict
|
||||||
let a:conn.capabilities.did_save = 1
|
let l:conn.capabilities.did_save = 1
|
||||||
|
|
||||||
let l:saveOptions = get(l:syncOptions, 'save')
|
let l:saveOptions = get(l:syncOptions, 'save')
|
||||||
|
|
||||||
if get(l:saveOptions, 'includeText') is v:true
|
if get(l:saveOptions, 'includeText') is v:true
|
||||||
let a:conn.capabilities.includeText = 1
|
let l:conn.capabilities.includeText = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
@@ -334,7 +340,7 @@ function! ale#lsp#HandleInitResponse(conn, response) abort
|
|||||||
let a:conn.initialized = 1
|
let a:conn.initialized = 1
|
||||||
elseif type(get(a:response, 'result')) is v:t_dict
|
elseif type(get(a:response, 'result')) is v:t_dict
|
||||||
\&& has_key(a:response.result, 'capabilities')
|
\&& has_key(a:response.result, 'capabilities')
|
||||||
call s:UpdateCapabilities(a:conn, a:response.result.capabilities)
|
call ale#lsp#UpdateCapabilities(a:conn.id, a:response.result.capabilities)
|
||||||
|
|
||||||
let a:conn.initialized = 1
|
let a:conn.initialized = 1
|
||||||
endif
|
endif
|
||||||
@@ -385,6 +391,20 @@ function! ale#lsp#HandleMessage(conn_id, message) abort
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Handle a JSON response from a language server.
|
||||||
|
" This is called from Lua for integration with Neovim's LSP API.
|
||||||
|
function! ale#lsp#HandleResponse(conn_id, response) abort
|
||||||
|
let l:conn = get(s:connections, a:conn_id, {})
|
||||||
|
|
||||||
|
if empty(l:conn)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
for l:Callback in l:conn.callback_list
|
||||||
|
call ale#util#GetFunction(l:Callback)(a:conn_id, a:response)
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Given a connection ID, mark it as a tsserver connection, so it will be
|
" Given a connection ID, mark it as a tsserver connection, so it will be
|
||||||
" handled that way.
|
" handled that way.
|
||||||
function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
|
function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
|
||||||
|
|||||||
@@ -20,7 +20,15 @@ module.start = function(config)
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
config.on_init = function(_, _)
|
config.on_init = function(client, _)
|
||||||
|
-- Tell ALE about server capabilities as soon as we can.
|
||||||
|
-- This will inform ALE commands what can be done with each server,
|
||||||
|
-- such as "go to definition" support, etc.
|
||||||
|
vim.fn["ale#lsp#UpdateCapabilities"](
|
||||||
|
config.name,
|
||||||
|
client.server_capabilities
|
||||||
|
)
|
||||||
|
|
||||||
-- Neovim calls `on_init` before marking a client as active, meaning
|
-- Neovim calls `on_init` before marking a client as active, meaning
|
||||||
-- we can't get a client via get_client_by_id until after `on_init` is
|
-- we can't get a client via get_client_by_id until after `on_init` is
|
||||||
-- called. By deferring execution of calling the init callbacks we
|
-- called. By deferring execution of calling the init callbacks we
|
||||||
@@ -55,6 +63,7 @@ module.send_message = function(args)
|
|||||||
local client = vim.lsp.get_client_by_id(args.client_id)
|
local client = vim.lsp.get_client_by_id(args.client_id)
|
||||||
|
|
||||||
if args.is_notification then
|
if args.is_notification then
|
||||||
|
-- For notifications we send a request and expect no direct response.
|
||||||
local success = client.notify(args.method, args.params)
|
local success = client.notify(args.method, args.params)
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
@@ -64,10 +73,23 @@ module.send_message = function(args)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- NOTE: We aren't yet handling reponses to requests properly!
|
local success, request_id
|
||||||
-- NOTE: There is a fourth argument for a bufnr here, and it's not
|
|
||||||
-- clear what that argument is for or why we need it.
|
-- For request we send a request and handle the response.
|
||||||
local success, request_id = client.request(args.method, args.params)
|
--
|
||||||
|
-- We set the bufnr to -1 to prevent Neovim from flushing anything, as ALE
|
||||||
|
-- already flushes changes to files before sending requests.
|
||||||
|
success, request_id = client.request(
|
||||||
|
args.method,
|
||||||
|
args.params,
|
||||||
|
function(_, result, _, _)
|
||||||
|
vim.fn["ale#lsp#HandleResponse"](client.name, {
|
||||||
|
id = request_id,
|
||||||
|
result = result,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
-1
|
||||||
|
)
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
return request_id
|
return request_id
|
||||||
|
|||||||
Reference in New Issue
Block a user