Files
neovim/test/functional/plugin/lsp/helpers.lua
dundargoc e016f5bee6 test: reduce exec_lua calls
`exec_lua` makes code slighly harder to read, so it's beneficial to
remove it in cases where it's possible or convenient.

Not all `exec_lua` calls should be removed even if the test passes as it
changes the semantics of the test even if it happens to pass.

From https://github.com/neovim/neovim/pull/28155#discussion_r1548185779:

"Note for tests like this, which fundamentally are about conversion, you
end up changing what conversion you are testing. Even if the result
happens to be same (as they often are, as we like the rules to be
consistent if possible), you are now testing the RPC conversion rules
instead of the vim script to in-process lua conversion rules."

From https://github.com/neovim/neovim/pull/28155#discussion_r1548190152:

"A test like this specifies that the cursor is valid immediately and not
after a separate cycle of normal (or an other input-processing) mode."
2024-04-04 13:10:11 +02:00

220 lines
5.3 KiB
Lua

local helpers = require('test.functional.helpers')(nil)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local run = helpers.run
local stop = helpers.stop
local api = helpers.api
local NIL = vim.NIL
local M = {}
function M.clear_notrace()
-- problem: here be dragons
-- solution: don't look too closely for dragons
clear {
env = {
NVIM_LUA_NOTRACK = '1',
NVIM_APPNAME = 'nvim_lsp_test',
VIMRUNTIME = os.getenv 'VIMRUNTIME',
},
}
end
M.create_server_definition = [[
function _create_server(opts)
opts = opts or {}
local server = {}
server.messages = {}
function server.cmd(dispatchers)
local closing = false
local handlers = opts.handlers or {}
local srv = {}
function srv.request(method, params, callback)
table.insert(server.messages, {
method = method,
params = params,
})
local handler = handlers[method]
if handler then
local response, err = handler(method, params)
callback(err, response)
elseif method == 'initialize' then
callback(nil, {
capabilities = opts.capabilities or {}
})
elseif method == 'shutdown' then
callback(nil, nil)
end
local request_id = #server.messages
return true, request_id
end
function srv.notify(method, params)
table.insert(server.messages, {
method = method,
params = params
})
if method == 'exit' then
dispatchers.on_exit(0, 15)
end
end
function srv.is_closing()
return closing
end
function srv.terminate()
closing = true
end
return srv
end
return server
end
]]
-- Fake LSP server.
M.fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua'
M.fake_lsp_logfile = 'Xtest-fake-lsp.log'
local function fake_lsp_server_setup(test_name, timeout_ms, options, settings)
exec_lua(
[=[
lsp = require('vim.lsp')
local test_name, fake_lsp_code, fake_lsp_logfile, timeout, options, settings = ...
TEST_RPC_CLIENT_ID = lsp.start_client {
cmd_env = {
NVIM_LOG_FILE = fake_lsp_logfile;
NVIM_LUA_NOTRACK = "1";
NVIM_APPNAME = "nvim_lsp_test";
};
cmd = {
vim.v.progpath, '-l', fake_lsp_code, test_name, tostring(timeout),
};
handlers = setmetatable({}, {
__index = function(t, method)
return function(...)
return vim.rpcrequest(1, 'handler', ...)
end
end;
});
workspace_folders = {{
uri = 'file://' .. vim.uv.cwd(),
name = 'test_folder',
}};
before_init = function(params, config)
vim.schedule(function()
vim.rpcrequest(1, "setup")
end)
end,
on_init = function(client, result)
TEST_RPC_CLIENT = client
vim.rpcrequest(1, "init", result)
end;
flags = {
allow_incremental_sync = options.allow_incremental_sync or false;
debounce_text_changes = options.debounce_text_changes or 0;
};
settings = settings;
on_exit = function(...)
vim.rpcnotify(1, "exit", ...)
end;
}
]=],
test_name,
M.fake_lsp_code,
M.fake_lsp_logfile,
timeout_ms or 1e3,
options or {},
settings or {}
)
end
--- @class test.lsp.Config
--- @field test_name string
--- @field timeout_ms? integer
--- @field options? table
--- @field settings? table
---
--- @field on_setup? fun()
--- @field on_init? fun(client: vim.lsp.Client, ...)
--- @field on_handler? fun(...)
--- @field on_exit? fun(code: integer, signal: integer)
--- @param config test.lsp.Config
function M.test_rpc_server(config)
if config.test_name then
M.clear_notrace()
fake_lsp_server_setup(
config.test_name,
config.timeout_ms or 1e3,
config.options,
config.settings
)
end
local client = setmetatable({}, {
__index = function(_, name)
-- Workaround for not being able to yield() inside __index for Lua 5.1 :(
-- Otherwise I would just return the value here.
return function(...)
return exec_lua(
[=[
local name = ...
if type(TEST_RPC_CLIENT[name]) == 'function' then
return TEST_RPC_CLIENT[name](select(2, ...))
else
return TEST_RPC_CLIENT[name]
end
]=],
name,
...
)
end
end,
})
--- @type integer, integer
local code, signal
local function on_request(method, args)
if method == 'setup' then
if config.on_setup then
config.on_setup()
end
return NIL
end
if method == 'init' then
if config.on_init then
config.on_init(client, unpack(args))
end
return NIL
end
if method == 'handler' then
if config.on_handler then
config.on_handler(unpack(args))
end
end
return NIL
end
local function on_notify(method, args)
if method == 'exit' then
code, signal = unpack(args)
return stop()
end
end
-- TODO specify timeout?
-- run(on_request, on_notify, nil, 1000)
run(on_request, on_notify, nil)
if config.on_exit then
config.on_exit(code, signal)
end
stop()
if config.test_name then
api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
end
end
return M