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 NIL = helpers.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"; 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, fixture_filename, logfile, timeout, options, settings = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { NVIM_LOG_FILE = logfile; NVIM_LUA_NOTRACK = "1"; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), "-c", string.format("lua TIMEOUT = %d", timeout), "-c", "luafile "..fixture_filename, }; handlers = setmetatable({}, { __index = function(t, method) return function(...) return vim.rpcrequest(1, 'handler', ...) end end; }); workspace_folders = {{ uri = 'file://' .. vim.loop.cwd(), name = 'test_folder', }}; 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 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; }) local code, signal local function on_request(method, args) 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, config.on_setup, 1000) run(on_request, on_notify, config.on_setup) if config.on_exit then config.on_exit(code, signal) end stop() if config.test_name then exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") end end return M