mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00

`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."
220 lines
5.3 KiB
Lua
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
|