Files
ale/test/lua/ale_lsp_spec.lua
w0rp dd23b92ee9
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
#3600 Implement pull model with Neovim Client
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.
2025-03-27 12:40:11 +00:00

363 lines
10 KiB
Lua

local eq = assert.are.same
local lsp = require("ale.lsp")
describe("ale.lsp.start", function()
local start_calls
local rpc_connect_calls
local vim_fn_calls
local defer_calls
local nvim_default_capabilities
setup(function()
_G.vim = {
defer_fn = function(func, delay)
table.insert(defer_calls, {func, delay})
end,
fn = setmetatable({}, {
__index = function(_, key)
return function(...)
table.insert(vim_fn_calls, {key, ...})
if key == "ale#lsp#GetLanguage" then
return "python"
end
if key ~= "ale#lsp_linter#HandleLSPDiagnostics"
and key ~= "ale#lsp#UpdateCapabilities"
and key ~= "ale#lsp#CallInitCallbacks"
then
assert(false, "Invalid ALE function: " .. key)
end
return nil
end
end,
}),
lsp = {
rpc = {
connect = function(host, port)
return function(dispatch)
table.insert(rpc_connect_calls, {
host = host,
port = port,
dispatch = dispatch,
})
end
end,
},
start = function(...)
table.insert(start_calls, {...})
return 42
end,
protocol = {
make_client_capabilities = function()
return nvim_default_capabilities
end,
},
},
}
end)
teardown(function()
_G.vim = nil
end)
before_each(function()
start_calls = {}
rpc_connect_calls = {}
vim_fn_calls = {}
defer_calls = {}
nvim_default_capabilities = {
textDocument = {},
}
end)
it("should start lsp programs with the correct arguments", function()
lsp.start({
name = "server:/code",
cmd = "server",
root_dir = "/code",
-- This Boolean value somehow ends up in Dictionaries from
-- Vim for init_options, and we need to remove it.
init_options = {[true] = 123},
})
-- Remove arguments with functions we can't apply equality checks
-- for easily.
for _, args in pairs(start_calls) do
args[1].handlers = nil
args[1].on_init = nil
args[1].get_language_id = nil
end
eq({
{
{
cmd = "server",
name = "server:/code",
root_dir = "/code",
init_options = {},
},
{attach = false, silent = true}
}
}, start_calls)
eq({}, vim_fn_calls)
end)
it("should start lsp socket connections with the correct arguments", function()
lsp.start({
name = "localhost:1234:/code",
host = "localhost",
port = 1234,
root_dir = "/code",
init_options = {foo = "bar"},
})
local cmd
-- Remove arguments with functions we can't apply equality checks
-- for easily.
for _, args in pairs(start_calls) do
cmd = args[1].cmd
args[1].cmd = nil
args[1].handlers = nil
args[1].on_init = nil
args[1].get_language_id = nil
end
eq({
{
{
name = "localhost:1234:/code",
root_dir = "/code",
init_options = {foo = "bar"},
},
{attach = false, silent = true}
}
}, start_calls)
cmd("dispatch_value")
eq({
{dispatch = "dispatch_value", host = "localhost", port = 1234},
}, rpc_connect_calls)
eq({}, vim_fn_calls)
end)
it("should return the client_id value from vim.lsp.start", function()
eq(42, lsp.start({}))
end)
it("should implement get_language_id correctly", function()
lsp.start({name = "server:/code"})
eq(1, #start_calls)
eq("python", start_calls[1][1].get_language_id(347, "ftype"))
eq({{"ale#lsp#GetLanguage", "server:/code", 347}}, vim_fn_calls)
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()
lsp.start({name = "server:/code"})
eq(1, #start_calls)
start_calls[1][1].on_init({server_capabilities = {cap = 1}})
eq({
{"ale#lsp#UpdateCapabilities", "server:/code", {cap = 1}},
}, vim_fn_calls)
eq(1, #defer_calls)
eq(2, #defer_calls[1])
eq("function", type(defer_calls[1][1]))
eq(0, defer_calls[1][2])
defer_calls[1][1]()
eq({
{"ale#lsp#UpdateCapabilities", "server:/code", {cap = 1}},
{"ale#lsp#CallInitCallbacks", "server:/code"},
}, vim_fn_calls)
end)
it("should configure handlers correctly", function()
lsp.start({name = "server:/code"})
eq(1, #start_calls)
local handlers = start_calls[1][1].handlers
local handler_names = {}
-- get keys from handlers
for key, _ in pairs(handlers) do
-- add key to handler_names mapping
handler_names[key] = true
end
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, {
uri = "file://code/foo.py",
diagnostics = {
{
lnum = 1,
end_lnum = 2,
col = 3,
end_col = 5,
severity = 1,
code = "123",
message = "Warning message",
}
},
})
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 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)
end)
end)