mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-06 20:54:26 +08:00
#3600 Implement pull model with Neovim Client
Some checks failed
CI / build_image (push) Has been cancelled
CI / test_ale (--linters-only) (push) Has been cancelled
CI / test_ale (--lua-only) (push) Has been cancelled
CI / test_ale (--neovim-07-only) (push) Has been cancelled
CI / test_ale (--neovim-08-only) (push) Has been cancelled
CI / test_ale (--vim-80-only) (push) Has been cancelled
CI / test_ale (--vim-90-only) (push) Has been cancelled
Some checks failed
CI / build_image (push) Has been cancelled
CI / test_ale (--linters-only) (push) Has been cancelled
CI / test_ale (--lua-only) (push) Has been cancelled
CI / test_ale (--neovim-07-only) (push) Has been cancelled
CI / test_ale (--neovim-08-only) (push) Has been cancelled
CI / test_ale (--vim-80-only) (push) Has been cancelled
CI / test_ale (--vim-90-only) (push) Has been cancelled
Implement the diagnostics pull model with the LSP Neovim client. We must handle messages a little different and tweak client capabilities for pull diagnostics to work through the Neovim client.
This commit is contained in:
@@ -100,7 +100,7 @@ endfunction
|
|||||||
" Handle LSP diagnostics for a given URI.
|
" Handle LSP diagnostics for a given URI.
|
||||||
" The special value 'unchanged' can be used for diagnostics to indicate
|
" The special value 'unchanged' can be used for diagnostics to indicate
|
||||||
" that diagnostics haven't changed since we last checked.
|
" that diagnostics haven't changed since we last checked.
|
||||||
function! s:HandleLSPDiagnostics(conn_id, uri, diagnostics) abort
|
function! ale#lsp_linter#HandleLSPDiagnostics(conn_id, uri, diagnostics) abort
|
||||||
let l:linter = get(s:lsp_linter_map, a:conn_id)
|
let l:linter = get(s:lsp_linter_map, a:conn_id)
|
||||||
|
|
||||||
if empty(l:linter)
|
if empty(l:linter)
|
||||||
@@ -233,14 +233,14 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
|
|||||||
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 s: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 has_key(s:diagnostic_uri_map, get(a:response, 'id'))
|
||||||
let l:uri = remove(s:diagnostic_uri_map, a:response.id)
|
let l:uri = remove(s:diagnostic_uri_map, a:response.id)
|
||||||
let l:diagnostics = a:response.result.kind is# 'unchanged'
|
let l:diagnostics = a:response.result.kind is# 'unchanged'
|
||||||
\ ? 'unchanged'
|
\ ? 'unchanged'
|
||||||
\ : a:response.result.items
|
\ : a:response.result.items
|
||||||
|
|
||||||
call s:HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
call ale#lsp_linter#HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
||||||
elseif l:method is# 'window/showMessage'
|
elseif l:method is# 'window/showMessage'
|
||||||
call ale#lsp_window#HandleShowMessage(
|
call ale#lsp_window#HandleShowMessage(
|
||||||
\ s:lsp_linter_map[a:conn_id].name,
|
\ s:lsp_linter_map[a:conn_id].name,
|
||||||
|
|||||||
@@ -38,13 +38,36 @@ module.start = function(config)
|
|||||||
-- functions so all of the functionality in ALE works.
|
-- functions so all of the functionality in ALE works.
|
||||||
["textDocument/publishDiagnostics"] = function(err, result, _, _)
|
["textDocument/publishDiagnostics"] = function(err, result, _, _)
|
||||||
if err == nil then
|
if err == nil then
|
||||||
vim.fn["ale#lsp_linter#HandleLSPResponse"](config.name, {
|
vim.fn["ale#lsp_linter#HandleLSPDiagnostics"](
|
||||||
jsonrpc = "2.0",
|
config.name,
|
||||||
method = "textDocument/publishDiagnostics",
|
result.uri,
|
||||||
params = result
|
result.diagnostics
|
||||||
})
|
)
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
|
-- Handle pull model diagnostic data.
|
||||||
|
["textDocument/diagnostic"] = function(err, result, request, _)
|
||||||
|
if err == nil then
|
||||||
|
local diagnostics
|
||||||
|
|
||||||
|
if result.kind == "unchanged" then
|
||||||
|
diagnostics = "unchanged"
|
||||||
|
else
|
||||||
|
diagnostics = result.items
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.fn["ale#lsp_linter#HandleLSPDiagnostics"](
|
||||||
|
config.name,
|
||||||
|
request.params.textDocument.uri,
|
||||||
|
diagnostics
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
-- When the pull model is enabled we have to handle and return
|
||||||
|
-- some kind of data for a server diagnostic refresh request.
|
||||||
|
["workspace/diagnostic/refresh"] = function()
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
config.on_init = function(client, _)
|
config.on_init = function(client, _)
|
||||||
@@ -70,6 +93,16 @@ module.start = function(config)
|
|||||||
return vim.fn["ale#lsp#GetLanguage"](config.name, bufnr)
|
return vim.fn["ale#lsp#GetLanguage"](config.name, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||||
|
|
||||||
|
-- Language servers like Pyright do not enable the diagnostics pull model
|
||||||
|
-- unless dynamicRegistration is enabled for diagnostics.
|
||||||
|
if capabilities.textDocument.diagnostic ~= nil then
|
||||||
|
capabilities.textDocument.diagnostic.dynamicRegistration = true
|
||||||
|
config.capabilities = capabilities
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: missing-fields
|
||||||
return vim.lsp.start(config, {
|
return vim.lsp.start(config, {
|
||||||
attach = false,
|
attach = false,
|
||||||
silent = true,
|
silent = true,
|
||||||
@@ -93,6 +126,10 @@ end
|
|||||||
module.send_message = function(args)
|
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 client == nil then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
if args.is_notification then
|
if args.is_notification then
|
||||||
-- For notifications we send a request and expect no direct response.
|
-- 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)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ describe("ale.lsp.start", function()
|
|||||||
local rpc_connect_calls
|
local rpc_connect_calls
|
||||||
local vim_fn_calls
|
local vim_fn_calls
|
||||||
local defer_calls
|
local defer_calls
|
||||||
|
local nvim_default_capabilities
|
||||||
|
|
||||||
setup(function()
|
setup(function()
|
||||||
_G.vim = {
|
_G.vim = {
|
||||||
@@ -21,7 +22,7 @@ describe("ale.lsp.start", function()
|
|||||||
return "python"
|
return "python"
|
||||||
end
|
end
|
||||||
|
|
||||||
if key ~= "ale#lsp_linter#HandleLSPResponse"
|
if key ~= "ale#lsp_linter#HandleLSPDiagnostics"
|
||||||
and key ~= "ale#lsp#UpdateCapabilities"
|
and key ~= "ale#lsp#UpdateCapabilities"
|
||||||
and key ~= "ale#lsp#CallInitCallbacks"
|
and key ~= "ale#lsp#CallInitCallbacks"
|
||||||
then
|
then
|
||||||
@@ -49,6 +50,11 @@ describe("ale.lsp.start", function()
|
|||||||
|
|
||||||
return 42
|
return 42
|
||||||
end,
|
end,
|
||||||
|
protocol = {
|
||||||
|
make_client_capabilities = function()
|
||||||
|
return nvim_default_capabilities
|
||||||
|
end,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
@@ -62,6 +68,9 @@ describe("ale.lsp.start", function()
|
|||||||
rpc_connect_calls = {}
|
rpc_connect_calls = {}
|
||||||
vim_fn_calls = {}
|
vim_fn_calls = {}
|
||||||
defer_calls = {}
|
defer_calls = {}
|
||||||
|
nvim_default_capabilities = {
|
||||||
|
textDocument = {},
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should start lsp programs with the correct arguments", function()
|
it("should start lsp programs with the correct arguments", function()
|
||||||
@@ -148,6 +157,24 @@ describe("ale.lsp.start", function()
|
|||||||
eq({{"ale#lsp#GetLanguage", "server:/code", 347}}, vim_fn_calls)
|
eq({{"ale#lsp#GetLanguage", "server:/code", 347}}, vim_fn_calls)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("should enable dynamicRegistration for the pull model", function()
|
||||||
|
nvim_default_capabilities = {textDocument = {diagnostic = {}}}
|
||||||
|
|
||||||
|
lsp.start({name = "server:/code"})
|
||||||
|
eq(1, #start_calls)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
{
|
||||||
|
textDocument = {
|
||||||
|
diagnostic = {
|
||||||
|
dynamicRegistration = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
start_calls[1][1].capabilities
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
it("should initialize clients with ALE correctly", function()
|
it("should initialize clients with ALE correctly", function()
|
||||||
lsp.start({name = "server:/code"})
|
lsp.start({name = "server:/code"})
|
||||||
|
|
||||||
@@ -185,39 +212,150 @@ describe("ale.lsp.start", function()
|
|||||||
handler_names[key] = true
|
handler_names[key] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
eq({["textDocument/publishDiagnostics"] = true}, handler_names)
|
eq({
|
||||||
|
["textDocument/publishDiagnostics"] = true,
|
||||||
|
["textDocument/diagnostic"] = true,
|
||||||
|
["workspace/diagnostic/refresh"] = true,
|
||||||
|
}, handler_names)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle push model published diagnostics", function()
|
||||||
|
lsp.start({name = "server:/code"})
|
||||||
|
|
||||||
|
eq(1, #start_calls)
|
||||||
|
|
||||||
|
local handlers = start_calls[1][1].handlers
|
||||||
|
|
||||||
|
eq("function", type(handlers["textDocument/publishDiagnostics"]))
|
||||||
|
|
||||||
handlers["textDocument/publishDiagnostics"](nil, {
|
handlers["textDocument/publishDiagnostics"](nil, {
|
||||||
{
|
uri = "file://code/foo.py",
|
||||||
lnum = 1,
|
diagnostics = {
|
||||||
end_lnum = 2,
|
{
|
||||||
col = 3,
|
lnum = 1,
|
||||||
end_col = 5,
|
end_lnum = 2,
|
||||||
severity = 1,
|
col = 3,
|
||||||
code = "123",
|
end_col = 5,
|
||||||
message = "Warning message",
|
severity = 1,
|
||||||
|
code = "123",
|
||||||
|
message = "Warning message",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
eq({
|
eq({
|
||||||
{
|
{
|
||||||
"ale#lsp_linter#HandleLSPResponse",
|
"ale#lsp_linter#HandleLSPDiagnostics",
|
||||||
"server:/code",
|
"server:/code",
|
||||||
|
"file://code/foo.py",
|
||||||
{
|
{
|
||||||
jsonrpc = "2.0",
|
{
|
||||||
method = "textDocument/publishDiagnostics",
|
lnum = 1,
|
||||||
params = {
|
end_lnum = 2,
|
||||||
{
|
col = 3,
|
||||||
lnum = 1,
|
end_col = 5,
|
||||||
end_lnum = 2,
|
severity = 1,
|
||||||
col = 3,
|
code = "123",
|
||||||
end_col = 5,
|
message = "Warning message",
|
||||||
severity = 1,
|
},
|
||||||
code = "123",
|
},
|
||||||
message = "Warning message",
|
},
|
||||||
},
|
}, vim_fn_calls)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should respond to workspace diagnostic refresh requests", function()
|
||||||
|
lsp.start({name = "server:/code"})
|
||||||
|
|
||||||
|
eq(1, #start_calls)
|
||||||
|
|
||||||
|
local handlers = start_calls[1][1].handlers
|
||||||
|
|
||||||
|
eq("function", type(handlers["workspace/diagnostic/refresh"]))
|
||||||
|
|
||||||
|
eq({}, handlers["workspace/diagnostic/refresh"]())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle pull model diagnostics", function()
|
||||||
|
lsp.start({name = "server:/code"})
|
||||||
|
|
||||||
|
eq(1, #start_calls)
|
||||||
|
|
||||||
|
local handlers = start_calls[1][1].handlers
|
||||||
|
|
||||||
|
eq("function", type(handlers["textDocument/diagnostic"]))
|
||||||
|
|
||||||
|
handlers["textDocument/diagnostic"](
|
||||||
|
nil,
|
||||||
|
{
|
||||||
|
kind = "full",
|
||||||
|
items = {
|
||||||
|
{
|
||||||
|
lnum = 1,
|
||||||
|
end_lnum = 2,
|
||||||
|
col = 3,
|
||||||
|
end_col = 5,
|
||||||
|
severity = 1,
|
||||||
|
code = "123",
|
||||||
|
message = "Warning message",
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params = {
|
||||||
|
textDocument = {
|
||||||
|
uri = "file://code/foo.py",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
eq({
|
||||||
|
{
|
||||||
|
"ale#lsp_linter#HandleLSPDiagnostics",
|
||||||
|
"server:/code",
|
||||||
|
"file://code/foo.py",
|
||||||
|
{
|
||||||
|
{
|
||||||
|
lnum = 1,
|
||||||
|
end_lnum = 2,
|
||||||
|
col = 3,
|
||||||
|
end_col = 5,
|
||||||
|
severity = 1,
|
||||||
|
code = "123",
|
||||||
|
message = "Warning message",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, vim_fn_calls)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle unchanged pull model diagnostics", function()
|
||||||
|
lsp.start({name = "server:/code"})
|
||||||
|
|
||||||
|
eq(1, #start_calls)
|
||||||
|
|
||||||
|
local handlers = start_calls[1][1].handlers
|
||||||
|
|
||||||
|
eq("function", type(handlers["textDocument/diagnostic"]))
|
||||||
|
|
||||||
|
handlers["textDocument/diagnostic"](
|
||||||
|
nil,
|
||||||
|
{kind = "unchanged"},
|
||||||
|
{
|
||||||
|
params = {
|
||||||
|
textDocument = {
|
||||||
|
uri = "file://code/foo.py",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
eq({
|
||||||
|
{
|
||||||
|
"ale#lsp_linter#HandleLSPDiagnostics",
|
||||||
|
"server:/code",
|
||||||
|
"file://code/foo.py",
|
||||||
|
"unchanged",
|
||||||
},
|
},
|
||||||
}, vim_fn_calls)
|
}, vim_fn_calls)
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user