mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	test: add type annotations
This commit is contained in:
		@@ -22,6 +22,9 @@ function Response:send(value, is_error)
 | 
				
			|||||||
  self._msgpack_rpc_stream._stream:write(data)
 | 
					  self._msgpack_rpc_stream._stream:write(data)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class test.MsgpackRpcStream
 | 
				
			||||||
 | 
					--- @field private _stream test.Stream
 | 
				
			||||||
 | 
					--- @field private __pack table
 | 
				
			||||||
local MsgpackRpcStream = {}
 | 
					local MsgpackRpcStream = {}
 | 
				
			||||||
MsgpackRpcStream.__index = MsgpackRpcStream
 | 
					MsgpackRpcStream.__index = MsgpackRpcStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,12 @@
 | 
				
			|||||||
local uv = vim.uv
 | 
					local uv = vim.uv
 | 
				
			||||||
local MsgpackRpcStream = require('test.client.msgpack_rpc_stream')
 | 
					local MsgpackRpcStream = require('test.client.msgpack_rpc_stream')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class test.Session
 | 
				
			||||||
 | 
					--- @field private _pending_messages string[]
 | 
				
			||||||
 | 
					--- @field private _msgpack_rpc_stream test.MsgpackRpcStream
 | 
				
			||||||
 | 
					--- @field private _prepare uv.uv_prepare_t
 | 
				
			||||||
 | 
					--- @field private _timer uv.uv_timer_t
 | 
				
			||||||
 | 
					--- @field private _is_running boolean
 | 
				
			||||||
local Session = {}
 | 
					local Session = {}
 | 
				
			||||||
Session.__index = Session
 | 
					Session.__index = Session
 | 
				
			||||||
if package.loaded['jit'] then
 | 
					if package.loaded['jit'] then
 | 
				
			||||||
@@ -26,7 +32,7 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
local function coroutine_exec(func, ...)
 | 
					local function coroutine_exec(func, ...)
 | 
				
			||||||
  local args = { ... }
 | 
					  local args = { ... }
 | 
				
			||||||
  local on_complete
 | 
					  local on_complete --- @type function?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if #args > 0 and type(args[#args]) == 'function' then
 | 
					  if #args > 0 and type(args[#args]) == 'function' then
 | 
				
			||||||
    -- completion callback
 | 
					    -- completion callback
 | 
				
			||||||
@@ -54,6 +60,8 @@ function Session.new(stream)
 | 
				
			|||||||
  }, Session)
 | 
					  }, Session)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param timeout integer?
 | 
				
			||||||
 | 
					--- @return string?
 | 
				
			||||||
function Session:next_message(timeout)
 | 
					function Session:next_message(timeout)
 | 
				
			||||||
  local function on_request(method, args, response)
 | 
					  local function on_request(method, args, response)
 | 
				
			||||||
    table.insert(self._pending_messages, { 'request', method, args, response })
 | 
					    table.insert(self._pending_messages, { 'request', method, args, response })
 | 
				
			||||||
@@ -86,6 +94,10 @@ function Session:notify(method, ...)
 | 
				
			|||||||
  self._msgpack_rpc_stream:write(method, { ... })
 | 
					  self._msgpack_rpc_stream:write(method, { ... })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param method string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return boolean
 | 
				
			||||||
 | 
					--- @return table
 | 
				
			||||||
function Session:request(method, ...)
 | 
					function Session:request(method, ...)
 | 
				
			||||||
  local args = { ... }
 | 
					  local args = { ... }
 | 
				
			||||||
  local err, result
 | 
					  local err, result
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,28 @@
 | 
				
			|||||||
local uv = vim.uv
 | 
					local uv = vim.uv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class test.Stream
 | 
				
			||||||
 | 
					--- @field write fun(self, data: string|string[])
 | 
				
			||||||
 | 
					--- @field read_start fun(self, cb: fun(chunk: string))
 | 
				
			||||||
 | 
					--- @field read_stop fun(self)
 | 
				
			||||||
 | 
					--- @field close fun(self, signal?: string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class vim.StdioStream : test.Stream
 | 
				
			||||||
 | 
					--- @field private _in uv.uv_pipe_t
 | 
				
			||||||
 | 
					--- @field private _out uv.uv_pipe_t
 | 
				
			||||||
local StdioStream = {}
 | 
					local StdioStream = {}
 | 
				
			||||||
StdioStream.__index = StdioStream
 | 
					StdioStream.__index = StdioStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function StdioStream.open()
 | 
					function StdioStream.open()
 | 
				
			||||||
  local self = setmetatable({
 | 
					  local self = setmetatable({
 | 
				
			||||||
    _in = uv.new_pipe(false),
 | 
					    _in = assert(uv.new_pipe(false)),
 | 
				
			||||||
    _out = uv.new_pipe(false),
 | 
					    _out = assert(uv.new_pipe(false)),
 | 
				
			||||||
  }, StdioStream)
 | 
					  }, StdioStream)
 | 
				
			||||||
  self._in:open(0)
 | 
					  self._in:open(0)
 | 
				
			||||||
  self._out:open(1)
 | 
					  self._out:open(1)
 | 
				
			||||||
  return self
 | 
					  return self
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param data string|string[]
 | 
				
			||||||
function StdioStream:write(data)
 | 
					function StdioStream:write(data)
 | 
				
			||||||
  self._out:write(data)
 | 
					  self._out:write(data)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
@@ -35,11 +45,14 @@ function StdioStream:close()
 | 
				
			|||||||
  self._out:close()
 | 
					  self._out:close()
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class test.SocketStream : test.Stream
 | 
				
			||||||
 | 
					--- @field package _stream_error? string
 | 
				
			||||||
 | 
					--- @field package _socket uv.uv_pipe_t
 | 
				
			||||||
local SocketStream = {}
 | 
					local SocketStream = {}
 | 
				
			||||||
SocketStream.__index = SocketStream
 | 
					SocketStream.__index = SocketStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function SocketStream.open(file)
 | 
					function SocketStream.open(file)
 | 
				
			||||||
  local socket = uv.new_pipe(false)
 | 
					  local socket = assert(uv.new_pipe(false))
 | 
				
			||||||
  local self = setmetatable({
 | 
					  local self = setmetatable({
 | 
				
			||||||
    _socket = socket,
 | 
					    _socket = socket,
 | 
				
			||||||
    _stream_error = nil,
 | 
					    _stream_error = nil,
 | 
				
			||||||
@@ -51,7 +64,7 @@ function SocketStream.open(file)
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function SocketStream.connect(host, port)
 | 
					function SocketStream.connect(host, port)
 | 
				
			||||||
  local socket = uv.new_tcp()
 | 
					  local socket = assert(uv.new_tcp())
 | 
				
			||||||
  local self = setmetatable({
 | 
					  local self = setmetatable({
 | 
				
			||||||
    _socket = socket,
 | 
					    _socket = socket,
 | 
				
			||||||
    _stream_error = nil,
 | 
					    _stream_error = nil,
 | 
				
			||||||
@@ -96,9 +109,20 @@ function SocketStream:close()
 | 
				
			|||||||
  uv.close(self._socket)
 | 
					  uv.close(self._socket)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @class test.ChildProcessStream : test.Stream
 | 
				
			||||||
 | 
					--- @field private _proc uv.uv_process_t
 | 
				
			||||||
 | 
					--- @field private _pid integer
 | 
				
			||||||
 | 
					--- @field private _child_stdin uv.uv_pipe_t
 | 
				
			||||||
 | 
					--- @field private _child_stdout uv.uv_pipe_t
 | 
				
			||||||
 | 
					--- @field status integer
 | 
				
			||||||
 | 
					--- @field signal integer
 | 
				
			||||||
local ChildProcessStream = {}
 | 
					local ChildProcessStream = {}
 | 
				
			||||||
ChildProcessStream.__index = ChildProcessStream
 | 
					ChildProcessStream.__index = ChildProcessStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param argv string[]
 | 
				
			||||||
 | 
					--- @param env string[]?
 | 
				
			||||||
 | 
					--- @param io_extra uv.uv_pipe_t?
 | 
				
			||||||
 | 
					--- @return test.ChildProcessStream
 | 
				
			||||||
function ChildProcessStream.spawn(argv, env, io_extra)
 | 
					function ChildProcessStream.spawn(argv, env, io_extra)
 | 
				
			||||||
  local self = setmetatable({
 | 
					  local self = setmetatable({
 | 
				
			||||||
    _child_stdin = uv.new_pipe(false),
 | 
					    _child_stdin = uv.new_pipe(false),
 | 
				
			||||||
@@ -106,13 +130,15 @@ function ChildProcessStream.spawn(argv, env, io_extra)
 | 
				
			|||||||
    _exiting = false,
 | 
					    _exiting = false,
 | 
				
			||||||
  }, ChildProcessStream)
 | 
					  }, ChildProcessStream)
 | 
				
			||||||
  local prog = argv[1]
 | 
					  local prog = argv[1]
 | 
				
			||||||
  local args = {}
 | 
					  local args = {} --- @type string[]
 | 
				
			||||||
  for i = 2, #argv do
 | 
					  for i = 2, #argv do
 | 
				
			||||||
    args[#args + 1] = argv[i]
 | 
					    args[#args + 1] = argv[i]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					  --- @diagnostic disable-next-line:missing-fields
 | 
				
			||||||
  self._proc, self._pid = uv.spawn(prog, {
 | 
					  self._proc, self._pid = uv.spawn(prog, {
 | 
				
			||||||
    stdio = { self._child_stdin, self._child_stdout, 2, io_extra },
 | 
					    stdio = { self._child_stdin, self._child_stdout, 2, io_extra },
 | 
				
			||||||
    args = args,
 | 
					    args = args,
 | 
				
			||||||
 | 
					    --- @diagnostic disable-next-line:assign-type-mismatch
 | 
				
			||||||
    env = env,
 | 
					    env = env,
 | 
				
			||||||
  }, function(status, signal)
 | 
					  }, function(status, signal)
 | 
				
			||||||
    self.status = status
 | 
					    self.status = status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ if module.nvim_dir == module.nvim_prog then
 | 
				
			|||||||
  module.nvim_dir = '.'
 | 
					  module.nvim_dir = '.'
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local prepend_argv
 | 
					local prepend_argv --- @type string[]?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if os.getenv('VALGRIND') then
 | 
					if os.getenv('VALGRIND') then
 | 
				
			||||||
  local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
 | 
					  local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
 | 
				
			||||||
@@ -79,7 +79,7 @@ elseif os.getenv('GDB') then
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if prepend_argv then
 | 
					if prepend_argv then
 | 
				
			||||||
  local new_nvim_argv = {}
 | 
					  local new_nvim_argv = {} --- @type string[]
 | 
				
			||||||
  local len = #prepend_argv
 | 
					  local len = #prepend_argv
 | 
				
			||||||
  for i = 1, len do
 | 
					  for i = 1, len do
 | 
				
			||||||
    new_nvim_argv[i] = prepend_argv[i]
 | 
					    new_nvim_argv[i] = prepend_argv[i]
 | 
				
			||||||
@@ -91,10 +91,13 @@ if prepend_argv then
 | 
				
			|||||||
  module.prepend_argv = prepend_argv
 | 
					  module.prepend_argv = prepend_argv
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local session, loop_running, last_error, method_error
 | 
					local session --- @type test.Session?
 | 
				
			||||||
 | 
					local loop_running --- @type boolean?
 | 
				
			||||||
 | 
					local last_error --- @type string?
 | 
				
			||||||
 | 
					local method_error --- @type string?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if not is_os('win') then
 | 
					if not is_os('win') then
 | 
				
			||||||
  local sigpipe_handler = uv.new_signal()
 | 
					  local sigpipe_handler = assert(uv.new_signal())
 | 
				
			||||||
  uv.signal_start(sigpipe_handler, 'sigpipe', function()
 | 
					  uv.signal_start(sigpipe_handler, 'sigpipe', function()
 | 
				
			||||||
    print('warning: got SIGPIPE signal. Likely related to a crash in nvim')
 | 
					    print('warning: got SIGPIPE signal. Likely related to a crash in nvim')
 | 
				
			||||||
  end)
 | 
					  end)
 | 
				
			||||||
@@ -108,10 +111,15 @@ function module.set_session(s)
 | 
				
			|||||||
  session = s
 | 
					  session = s
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param method string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.request(method, ...)
 | 
					function module.request(method, ...)
 | 
				
			||||||
 | 
					  assert(session)
 | 
				
			||||||
  local status, rv = session:request(method, ...)
 | 
					  local status, rv = session:request(method, ...)
 | 
				
			||||||
  if not status then
 | 
					  if not status then
 | 
				
			||||||
    if loop_running then
 | 
					    if loop_running then
 | 
				
			||||||
 | 
					      --- @type string
 | 
				
			||||||
      last_error = rv[2]
 | 
					      last_error = rv[2]
 | 
				
			||||||
      session:stop()
 | 
					      session:stop()
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
@@ -121,12 +129,18 @@ function module.request(method, ...)
 | 
				
			|||||||
  return rv
 | 
					  return rv
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param method string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.request_lua(method, ...)
 | 
					function module.request_lua(method, ...)
 | 
				
			||||||
  return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
 | 
					  return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param timeout? integer
 | 
				
			||||||
 | 
					--- @return string?
 | 
				
			||||||
function module.next_msg(timeout)
 | 
					function module.next_msg(timeout)
 | 
				
			||||||
  return session:next_message(timeout and timeout or 10000)
 | 
					  assert(session)
 | 
				
			||||||
 | 
					  return session:next_message(timeout or 10000)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function module.expect_twostreams(msgs1, msgs2)
 | 
					function module.expect_twostreams(msgs1, msgs2)
 | 
				
			||||||
@@ -164,6 +178,7 @@ function module.expect_msg_seq(...)
 | 
				
			|||||||
    error('invalid args')
 | 
					    error('invalid args')
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  local ignore = arg1['ignore'] and arg1['ignore'] or {}
 | 
					  local ignore = arg1['ignore'] and arg1['ignore'] or {}
 | 
				
			||||||
 | 
					  --- @type string[]
 | 
				
			||||||
  local seqs = arg1['seqs'] and arg1['seqs'] or { ... }
 | 
					  local seqs = arg1['seqs'] and arg1['seqs'] or { ... }
 | 
				
			||||||
  if type(ignore) ~= 'table' then
 | 
					  if type(ignore) ~= 'table' then
 | 
				
			||||||
    error("'ignore' arg must be a list of strings")
 | 
					    error("'ignore' arg must be a list of strings")
 | 
				
			||||||
@@ -213,6 +228,7 @@ function module.expect_msg_seq(...)
 | 
				
			|||||||
    local message = result
 | 
					    local message = result
 | 
				
			||||||
    if type(result) == 'table' then
 | 
					    if type(result) == 'table' then
 | 
				
			||||||
      -- 'eq' returns several things
 | 
					      -- 'eq' returns several things
 | 
				
			||||||
 | 
					      --- @type string
 | 
				
			||||||
      message = result.message
 | 
					      message = result.message
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    final_error = cat_err(final_error, message)
 | 
					    final_error = cat_err(final_error, message)
 | 
				
			||||||
@@ -234,8 +250,16 @@ function module.set_method_error(err)
 | 
				
			|||||||
  method_error = err
 | 
					  method_error = err
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param lsession test.Session
 | 
				
			||||||
 | 
					--- @param request_cb function
 | 
				
			||||||
 | 
					--- @param notification_cb function
 | 
				
			||||||
 | 
					--- @param setup_cb function
 | 
				
			||||||
 | 
					--- @param timeout integer
 | 
				
			||||||
 | 
					--- @return {[1]: integer, [2]: string}
 | 
				
			||||||
function module.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
 | 
					function module.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
 | 
				
			||||||
  local on_request, on_notification, on_setup
 | 
					  local on_request --- @type function?
 | 
				
			||||||
 | 
					  local on_notification --- @type function?
 | 
				
			||||||
 | 
					  local on_setup --- @type function?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if request_cb then
 | 
					  if request_cb then
 | 
				
			||||||
    function on_request(method, args)
 | 
					    function on_request(method, args)
 | 
				
			||||||
@@ -273,11 +297,12 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function module.run(request_cb, notification_cb, setup_cb, timeout)
 | 
					function module.run(request_cb, notification_cb, setup_cb, timeout)
 | 
				
			||||||
 | 
					  assert(session)
 | 
				
			||||||
  return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
 | 
					  return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function module.stop()
 | 
					function module.stop()
 | 
				
			||||||
  session:stop()
 | 
					  assert(session):stop()
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function module.nvim_prog_abs()
 | 
					function module.nvim_prog_abs()
 | 
				
			||||||
@@ -301,6 +326,7 @@ function module.expect_exit(fn_or_timeout, ...)
 | 
				
			|||||||
      eof_err_msg,
 | 
					      eof_err_msg,
 | 
				
			||||||
      module.pcall_err(function(timeout, fn, ...)
 | 
					      module.pcall_err(function(timeout, fn, ...)
 | 
				
			||||||
        fn(...)
 | 
					        fn(...)
 | 
				
			||||||
 | 
					        assert(session)
 | 
				
			||||||
        while session:next_message(timeout) do
 | 
					        while session:next_message(timeout) do
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
        if session.eof_err then
 | 
					        if session.eof_err then
 | 
				
			||||||
@@ -311,14 +337,18 @@ function module.expect_exit(fn_or_timeout, ...)
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Executes a Vimscript function via Lua.
 | 
					--- Executes a Vimscript function via Lua.
 | 
				
			||||||
-- Fails on Vimscript error, but does not update v:errmsg.
 | 
					--- Fails on Vimscript error, but does not update v:errmsg.
 | 
				
			||||||
 | 
					--- @param name string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.call_lua(name, ...)
 | 
					function module.call_lua(name, ...)
 | 
				
			||||||
  return module.exec_lua([[return vim.call(...)]], name, ...)
 | 
					  return module.exec_lua([[return vim.call(...)]], name, ...)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Sends user input to Nvim.
 | 
					--- Sends user input to Nvim.
 | 
				
			||||||
-- Does not fail on Vimscript error, but v:errmsg will be updated.
 | 
					--- Does not fail on Vimscript error, but v:errmsg will be updated.
 | 
				
			||||||
 | 
					--- @param input string
 | 
				
			||||||
local function nvim_feed(input)
 | 
					local function nvim_feed(input)
 | 
				
			||||||
  while #input > 0 do
 | 
					  while #input > 0 do
 | 
				
			||||||
    local written = module.request('nvim_input', input)
 | 
					    local written = module.request('nvim_input', input)
 | 
				
			||||||
@@ -330,22 +360,27 @@ local function nvim_feed(input)
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param ... string
 | 
				
			||||||
function module.feed(...)
 | 
					function module.feed(...)
 | 
				
			||||||
  for _, v in ipairs({ ... }) do
 | 
					  for _, v in ipairs({ ... }) do
 | 
				
			||||||
    nvim_feed(dedent(v))
 | 
					    nvim_feed(dedent(v))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param ... string
 | 
				
			||||||
function module.rawfeed(...)
 | 
					function module.rawfeed(...)
 | 
				
			||||||
  for _, v in ipairs({ ... }) do
 | 
					  for _, v in ipairs({ ... }) do
 | 
				
			||||||
    nvim_feed(dedent(v))
 | 
					    nvim_feed(dedent(v))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---@param ... string[]?
 | 
				
			||||||
 | 
					---@return string[]
 | 
				
			||||||
function module.merge_args(...)
 | 
					function module.merge_args(...)
 | 
				
			||||||
  local i = 1
 | 
					  local i = 1
 | 
				
			||||||
  local argv = {}
 | 
					  local argv = {} --- @type string[]
 | 
				
			||||||
  for anum = 1, select('#', ...) do
 | 
					  for anum = 1, select('#', ...) do
 | 
				
			||||||
 | 
					    --- @type string[]?
 | 
				
			||||||
    local args = select(anum, ...)
 | 
					    local args = select(anum, ...)
 | 
				
			||||||
    if args then
 | 
					    if args then
 | 
				
			||||||
      for _, arg in ipairs(args) do
 | 
					      for _, arg in ipairs(args) do
 | 
				
			||||||
@@ -357,26 +392,29 @@ function module.merge_args(...)
 | 
				
			|||||||
  return argv
 | 
					  return argv
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--  Removes Nvim startup args from `args` matching items in `args_rm`.
 | 
					--- Removes Nvim startup args from `args` matching items in `args_rm`.
 | 
				
			||||||
--
 | 
					---
 | 
				
			||||||
--  - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
 | 
					--- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
 | 
				
			||||||
--  - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
 | 
					--- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
 | 
				
			||||||
--
 | 
					---
 | 
				
			||||||
--  Example:
 | 
					--- Example:
 | 
				
			||||||
--      args={'--headless', '-u', 'NONE'}
 | 
					---     args={'--headless', '-u', 'NONE'}
 | 
				
			||||||
--      args_rm={'--cmd', '-u'}
 | 
					---     args_rm={'--cmd', '-u'}
 | 
				
			||||||
--  Result:
 | 
					--- Result:
 | 
				
			||||||
--      {'--headless'}
 | 
					---     {'--headless'}
 | 
				
			||||||
--
 | 
					---
 | 
				
			||||||
--  All matching cases are removed.
 | 
					--- All matching cases are removed.
 | 
				
			||||||
--
 | 
					---
 | 
				
			||||||
--  Example:
 | 
					--- Example:
 | 
				
			||||||
--      args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
 | 
					---     args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
 | 
				
			||||||
--      args_rm={'--cmd', '-u'}
 | 
					---     args_rm={'--cmd', '-u'}
 | 
				
			||||||
--  Result:
 | 
					--- Result:
 | 
				
			||||||
--      {'-N'}
 | 
					---     {'-N'}
 | 
				
			||||||
 | 
					--- @param args string[]
 | 
				
			||||||
 | 
					--- @param args_rm string[]
 | 
				
			||||||
 | 
					--- @return string[]
 | 
				
			||||||
local function remove_args(args, args_rm)
 | 
					local function remove_args(args, args_rm)
 | 
				
			||||||
  local new_args = {}
 | 
					  local new_args = {} --- @type string[]
 | 
				
			||||||
  local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' }
 | 
					  local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' }
 | 
				
			||||||
  if not args_rm or #args_rm == 0 then
 | 
					  if not args_rm or #args_rm == 0 then
 | 
				
			||||||
    return { unpack(args) }
 | 
					    return { unpack(args) }
 | 
				
			||||||
@@ -421,7 +459,12 @@ function module.check_close()
 | 
				
			|||||||
  session = nil
 | 
					  session = nil
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- @param io_extra used for stdin_fd, see :help ui-option
 | 
					--- @param argv string[]
 | 
				
			||||||
 | 
					--- @param merge boolean?
 | 
				
			||||||
 | 
					--- @param env string[]?
 | 
				
			||||||
 | 
					--- @param keep boolean
 | 
				
			||||||
 | 
					--- @param io_extra uv.uv_pipe_t? used for stdin_fd, see :help ui-option
 | 
				
			||||||
 | 
					--- @return test.Session
 | 
				
			||||||
function module.spawn(argv, merge, env, keep, io_extra)
 | 
					function module.spawn(argv, merge, env, keep, io_extra)
 | 
				
			||||||
  if not keep then
 | 
					  if not keep then
 | 
				
			||||||
    module.check_close()
 | 
					    module.check_close()
 | 
				
			||||||
@@ -457,16 +500,27 @@ function module.clear(...)
 | 
				
			|||||||
  return module.get_session()
 | 
					  return module.get_session()
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- same params as clear, but does returns the session instead
 | 
					--- same params as clear, but does returns the session instead
 | 
				
			||||||
-- of replacing the default session
 | 
					--- of replacing the default session
 | 
				
			||||||
 | 
					--- @return test.Session
 | 
				
			||||||
function module.spawn_argv(keep, ...)
 | 
					function module.spawn_argv(keep, ...)
 | 
				
			||||||
  local argv, env, io_extra = module.new_argv(...)
 | 
					  local argv, env, io_extra = module.new_argv(...)
 | 
				
			||||||
  return module.spawn(argv, nil, env, keep, io_extra)
 | 
					  return module.spawn(argv, nil, env, keep, io_extra)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Builds an argument list for use in clear().
 | 
					--- @class test.new_argv.Opts
 | 
				
			||||||
--
 | 
					--- @field args? string[]
 | 
				
			||||||
---@see clear() for parameters.
 | 
					--- @field args_rm? string[]
 | 
				
			||||||
 | 
					--- @field env? table<string,string>
 | 
				
			||||||
 | 
					--- @field io_extra? uv.uv_pipe_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Builds an argument list for use in clear().
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					--- @see clear() for parameters.
 | 
				
			||||||
 | 
					--- @param ... string
 | 
				
			||||||
 | 
					--- @return string[]
 | 
				
			||||||
 | 
					--- @return string[]?
 | 
				
			||||||
 | 
					--- @return uv.uv_pipe_t?
 | 
				
			||||||
function module.new_argv(...)
 | 
					function module.new_argv(...)
 | 
				
			||||||
  local args = { unpack(module.nvim_argv) }
 | 
					  local args = { unpack(module.nvim_argv) }
 | 
				
			||||||
  table.insert(args, '--headless')
 | 
					  table.insert(args, '--headless')
 | 
				
			||||||
@@ -475,16 +529,17 @@ function module.new_argv(...)
 | 
				
			|||||||
    table.insert(args, '--listen')
 | 
					    table.insert(args, '--listen')
 | 
				
			||||||
    table.insert(args, _G._nvim_test_id)
 | 
					    table.insert(args, _G._nvim_test_id)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  local new_args
 | 
					  local new_args --- @type string[]
 | 
				
			||||||
  local io_extra
 | 
					  local io_extra --- @type uv.uv_pipe_t?
 | 
				
			||||||
  local env = nil
 | 
					  local env --- @type string[]?
 | 
				
			||||||
 | 
					  --- @type test.new_argv.Opts|string
 | 
				
			||||||
  local opts = select(1, ...)
 | 
					  local opts = select(1, ...)
 | 
				
			||||||
  if type(opts) ~= 'table' then
 | 
					  if type(opts) ~= 'table' then
 | 
				
			||||||
    new_args = { ... }
 | 
					    new_args = { ... }
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    args = remove_args(args, opts.args_rm)
 | 
					    args = remove_args(args, opts.args_rm)
 | 
				
			||||||
    if opts.env then
 | 
					    if opts.env then
 | 
				
			||||||
      local env_opt = {}
 | 
					      local env_opt = {} --- @type table<string,string>
 | 
				
			||||||
      for k, v in pairs(opts.env) do
 | 
					      for k, v in pairs(opts.env) do
 | 
				
			||||||
        assert(type(k) == 'string')
 | 
					        assert(type(k) == 'string')
 | 
				
			||||||
        assert(type(v) == 'string')
 | 
					        assert(type(v) == 'string')
 | 
				
			||||||
@@ -523,6 +578,7 @@ function module.new_argv(...)
 | 
				
			|||||||
  return args, env, io_extra
 | 
					  return args, env, io_extra
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param ... string
 | 
				
			||||||
function module.insert(...)
 | 
					function module.insert(...)
 | 
				
			||||||
  nvim_feed('i')
 | 
					  nvim_feed('i')
 | 
				
			||||||
  for _, v in ipairs({ ... }) do
 | 
					  for _, v in ipairs({ ... }) do
 | 
				
			||||||
@@ -532,8 +588,9 @@ function module.insert(...)
 | 
				
			|||||||
  nvim_feed('<ESC>')
 | 
					  nvim_feed('<ESC>')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Executes an ex-command by user input. Because nvim_input() is used, Vimscript
 | 
					--- Executes an ex-command by user input. Because nvim_input() is used, Vimscript
 | 
				
			||||||
-- errors will not manifest as client (lua) errors. Use command() for that.
 | 
					--- errors will not manifest as client (lua) errors. Use command() for that.
 | 
				
			||||||
 | 
					--- @param ... string
 | 
				
			||||||
function module.feed_command(...)
 | 
					function module.feed_command(...)
 | 
				
			||||||
  for _, v in ipairs({ ... }) do
 | 
					  for _, v in ipairs({ ... }) do
 | 
				
			||||||
    if v:sub(1, 1) ~= '/' then
 | 
					    if v:sub(1, 1) ~= '/' then
 | 
				
			||||||
@@ -587,6 +644,9 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function module.create_callindex(func)
 | 
					function module.create_callindex(func)
 | 
				
			||||||
  return setmetatable({}, {
 | 
					  return setmetatable({}, {
 | 
				
			||||||
 | 
					    --- @param tbl table<any,function>
 | 
				
			||||||
 | 
					    --- @param arg1 string
 | 
				
			||||||
 | 
					    --- @return function
 | 
				
			||||||
    __index = function(tbl, arg1)
 | 
					    __index = function(tbl, arg1)
 | 
				
			||||||
      local ret = function(...)
 | 
					      local ret = function(...)
 | 
				
			||||||
        return func(arg1, ...)
 | 
					        return func(arg1, ...)
 | 
				
			||||||
@@ -597,12 +657,17 @@ function module.create_callindex(func)
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param method string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
function module.nvim_async(method, ...)
 | 
					function module.nvim_async(method, ...)
 | 
				
			||||||
  session:notify(method, ...)
 | 
					  assert(session):notify(method, ...)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Executes a Vimscript function via RPC.
 | 
					--- Executes a Vimscript function via RPC.
 | 
				
			||||||
-- Fails on Vimscript error, but does not update v:errmsg.
 | 
					--- Fails on Vimscript error, but does not update v:errmsg.
 | 
				
			||||||
 | 
					--- @param name string
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.call(name, ...)
 | 
					function module.call(name, ...)
 | 
				
			||||||
  return module.request('nvim_call_function', name, { ... })
 | 
					  return module.request('nvim_call_function', name, { ... })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
@@ -637,6 +702,7 @@ module.api = vim.api
 | 
				
			|||||||
module.fn = vim.fn
 | 
					module.fn = vim.fn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for name, fns in pairs(module.rpc) do
 | 
					for name, fns in pairs(module.rpc) do
 | 
				
			||||||
 | 
					  --- @diagnostic disable-next-line:no-unknown
 | 
				
			||||||
  module[name] = fns
 | 
					  module[name] = fns
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -673,6 +739,10 @@ function module.expect_any(contents)
 | 
				
			|||||||
  return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true))
 | 
					  return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param expected any[]
 | 
				
			||||||
 | 
					--- @param received any[]
 | 
				
			||||||
 | 
					--- @param kind string
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.expect_events(expected, received, kind)
 | 
					function module.expect_events(expected, received, kind)
 | 
				
			||||||
  if not pcall(eq, expected, received) then
 | 
					  if not pcall(eq, expected, received) then
 | 
				
			||||||
    local msg = 'unexpected ' .. kind .. ' received.\n\n'
 | 
					    local msg = 'unexpected ' .. kind .. ' received.\n\n'
 | 
				
			||||||
@@ -712,6 +782,7 @@ function module.assert_visible(bufnr, visible)
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param path string
 | 
				
			||||||
local function do_rmdir(path)
 | 
					local function do_rmdir(path)
 | 
				
			||||||
  local stat = uv.fs_stat(path)
 | 
					  local stat = uv.fs_stat(path)
 | 
				
			||||||
  if stat == nil then
 | 
					  if stat == nil then
 | 
				
			||||||
@@ -779,14 +850,17 @@ function module.exc_exec(cmd)
 | 
				
			|||||||
  return ret
 | 
					  return ret
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param cond boolean
 | 
				
			||||||
 | 
					--- @param reason string
 | 
				
			||||||
 | 
					--- @return boolean
 | 
				
			||||||
function module.skip(cond, reason)
 | 
					function module.skip(cond, reason)
 | 
				
			||||||
  if cond then
 | 
					  if cond then
 | 
				
			||||||
 | 
					    --- @type fun(reason: string)
 | 
				
			||||||
    local pending = getfenv(2).pending
 | 
					    local pending = getfenv(2).pending
 | 
				
			||||||
    pending(reason or 'FIXME')
 | 
					    pending(reason or 'FIXME')
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					  return false
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Calls pending() and returns `true` if the system is too slow to
 | 
					-- Calls pending() and returns `true` if the system is too slow to
 | 
				
			||||||
@@ -809,6 +883,8 @@ function module.exec(code)
 | 
				
			|||||||
  module.api.nvim_exec2(code, {})
 | 
					  module.api.nvim_exec2(code, {})
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param code string
 | 
				
			||||||
 | 
					--- @return string
 | 
				
			||||||
function module.exec_capture(code)
 | 
					function module.exec_capture(code)
 | 
				
			||||||
  return module.api.nvim_exec2(code, { output = true }).output
 | 
					  return module.api.nvim_exec2(code, { output = true }).output
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
@@ -855,19 +931,24 @@ function module.new_pipename()
 | 
				
			|||||||
  return pipename
 | 
					  return pipename
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param provider string
 | 
				
			||||||
 | 
					--- @return string|false?
 | 
				
			||||||
function module.missing_provider(provider)
 | 
					function module.missing_provider(provider)
 | 
				
			||||||
  if provider == 'ruby' or provider == 'node' or provider == 'perl' then
 | 
					  if provider == 'ruby' or provider == 'node' or provider == 'perl' then
 | 
				
			||||||
 | 
					    --- @type string?
 | 
				
			||||||
    local e = module.fn['provider#' .. provider .. '#Detect']()[2]
 | 
					    local e = module.fn['provider#' .. provider .. '#Detect']()[2]
 | 
				
			||||||
    return e ~= '' and e or false
 | 
					    return e ~= '' and e or false
 | 
				
			||||||
  elseif provider == 'python' or provider == 'python3' then
 | 
					  elseif provider == 'python' or provider == 'python3' then
 | 
				
			||||||
    local py_major_version = (provider == 'python3' and 3 or 2)
 | 
					    local py_major_version = (provider == 'python3' and 3 or 2)
 | 
				
			||||||
 | 
					    --- @type string?
 | 
				
			||||||
    local e = module.fn['provider#pythonx#Detect'](py_major_version)[2]
 | 
					    local e = module.fn['provider#pythonx#Detect'](py_major_version)[2]
 | 
				
			||||||
    return e ~= '' and e or false
 | 
					    return e ~= '' and e or false
 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    assert(false, 'Unknown provider: ' .. provider)
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					  assert(false, 'Unknown provider: ' .. provider)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param obj string|table
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.alter_slashes(obj)
 | 
					function module.alter_slashes(obj)
 | 
				
			||||||
  if not is_os('win') then
 | 
					  if not is_os('win') then
 | 
				
			||||||
    return obj
 | 
					    return obj
 | 
				
			||||||
@@ -876,14 +957,14 @@ function module.alter_slashes(obj)
 | 
				
			|||||||
    local ret = obj:gsub('/', '\\')
 | 
					    local ret = obj:gsub('/', '\\')
 | 
				
			||||||
    return ret
 | 
					    return ret
 | 
				
			||||||
  elseif type(obj) == 'table' then
 | 
					  elseif type(obj) == 'table' then
 | 
				
			||||||
    local ret = {}
 | 
					    --- @cast obj table<any,any>
 | 
				
			||||||
 | 
					    local ret = {} --- @type table<any,any>
 | 
				
			||||||
    for k, v in pairs(obj) do
 | 
					    for k, v in pairs(obj) do
 | 
				
			||||||
      ret[k] = module.alter_slashes(v)
 | 
					      ret[k] = module.alter_slashes(v)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    return ret
 | 
					    return ret
 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    assert(false, 'expected string or table of strings, got ' .. type(obj))
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					  assert(false, 'expected string or table of strings, got ' .. type(obj))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local load_factor = 1
 | 
					local load_factor = 1
 | 
				
			||||||
@@ -893,18 +974,25 @@ if global_helpers.is_ci() then
 | 
				
			|||||||
  module.request('nvim_command', 'source test/old/testdir/load.vim')
 | 
					  module.request('nvim_command', 'source test/old/testdir/load.vim')
 | 
				
			||||||
  load_factor = module.request('nvim_eval', 'g:test_load_factor')
 | 
					  load_factor = module.request('nvim_eval', 'g:test_load_factor')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param num number
 | 
				
			||||||
 | 
					--- @return number
 | 
				
			||||||
function module.load_adjust(num)
 | 
					function module.load_adjust(num)
 | 
				
			||||||
  return math.ceil(num * load_factor)
 | 
					  return math.ceil(num * load_factor)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param ctx table<string,any>
 | 
				
			||||||
 | 
					--- @return table
 | 
				
			||||||
function module.parse_context(ctx)
 | 
					function module.parse_context(ctx)
 | 
				
			||||||
  local parsed = {}
 | 
					  local parsed = {} --- @type table<string,any>
 | 
				
			||||||
  for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do
 | 
					  for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do
 | 
				
			||||||
 | 
					    --- @param v any
 | 
				
			||||||
    parsed[item] = vim.tbl_filter(function(v)
 | 
					    parsed[item] = vim.tbl_filter(function(v)
 | 
				
			||||||
      return type(v) == 'table'
 | 
					      return type(v) == 'table'
 | 
				
			||||||
    end, module.call('msgpackparse', ctx[item]))
 | 
					    end, module.call('msgpackparse', ctx[item]))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  parsed['bufs'] = parsed['bufs'][1]
 | 
					  parsed['bufs'] = parsed['bufs'][1]
 | 
				
			||||||
 | 
					  --- @param v any
 | 
				
			||||||
  return vim.tbl_map(function(v)
 | 
					  return vim.tbl_map(function(v)
 | 
				
			||||||
    if #v == 0 then
 | 
					    if #v == 0 then
 | 
				
			||||||
      return nil
 | 
					      return nil
 | 
				
			||||||
@@ -918,7 +1006,9 @@ function module.add_builddir_to_rtp()
 | 
				
			|||||||
  module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir))
 | 
					  module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Kill (reap) a process by PID.
 | 
					--- Kill (reap) a process by PID.
 | 
				
			||||||
 | 
					--- @param pid string
 | 
				
			||||||
 | 
					--- @return boolean?
 | 
				
			||||||
function module.os_kill(pid)
 | 
					function module.os_kill(pid)
 | 
				
			||||||
  return os.execute(
 | 
					  return os.execute(
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
@@ -928,7 +1018,9 @@ function module.os_kill(pid)
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Create folder with non existing parents
 | 
					--- Create folder with non existing parents
 | 
				
			||||||
 | 
					--- @param path string
 | 
				
			||||||
 | 
					--- @return boolean?
 | 
				
			||||||
function module.mkdir_p(path)
 | 
					function module.mkdir_p(path)
 | 
				
			||||||
  return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path))
 | 
					  return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,8 +63,12 @@ function module.popen_r(...)
 | 
				
			|||||||
  return io.popen(module.argss_to_cmd(...), 'r')
 | 
					  return io.popen(module.argss_to_cmd(...), 'r')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Calls fn() until it succeeds, up to `max` times or until `max_ms`
 | 
					--- Calls fn() until it succeeds, up to `max` times or until `max_ms`
 | 
				
			||||||
-- milliseconds have passed.
 | 
					--- milliseconds have passed.
 | 
				
			||||||
 | 
					--- @param max integer?
 | 
				
			||||||
 | 
					--- @param max_ms integer?
 | 
				
			||||||
 | 
					--- @param fn function
 | 
				
			||||||
 | 
					--- @return any
 | 
				
			||||||
function module.retry(max, max_ms, fn)
 | 
					function module.retry(max, max_ms, fn)
 | 
				
			||||||
  luaassert(max == nil or max > 0)
 | 
					  luaassert(max == nil or max > 0)
 | 
				
			||||||
  luaassert(max_ms == nil or max_ms > 0)
 | 
					  luaassert(max_ms == nil or max_ms > 0)
 | 
				
			||||||
@@ -72,6 +76,7 @@ function module.retry(max, max_ms, fn)
 | 
				
			|||||||
  local timeout = (max_ms and max_ms or 10000)
 | 
					  local timeout = (max_ms and max_ms or 10000)
 | 
				
			||||||
  local start_time = uv.now()
 | 
					  local start_time = uv.now()
 | 
				
			||||||
  while true do
 | 
					  while true do
 | 
				
			||||||
 | 
					    --- @type boolean, any
 | 
				
			||||||
    local status, result = pcall(fn)
 | 
					    local status, result = pcall(fn)
 | 
				
			||||||
    if status then
 | 
					    if status then
 | 
				
			||||||
      return result
 | 
					      return result
 | 
				
			||||||
@@ -121,6 +126,9 @@ function module.fail(msg)
 | 
				
			|||||||
  return luaassert.epicfail(msg)
 | 
					  return luaassert.epicfail(msg)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param pat string
 | 
				
			||||||
 | 
					--- @param actual string
 | 
				
			||||||
 | 
					--- @return boolean
 | 
				
			||||||
function module.matches(pat, actual)
 | 
					function module.matches(pat, actual)
 | 
				
			||||||
  if nil ~= string.match(actual, pat) then
 | 
					  if nil ~= string.match(actual, pat) then
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
@@ -170,10 +178,16 @@ end
 | 
				
			|||||||
--- Asserts that `pat` does NOT match any line in the tail of `logfile`.
 | 
					--- Asserts that `pat` does NOT match any line in the tail of `logfile`.
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
--- @see assert_log
 | 
					--- @see assert_log
 | 
				
			||||||
 | 
					--- @param pat (string) Lua pattern to match lines in the log file
 | 
				
			||||||
 | 
					--- @param logfile? (string) Full path to log file (default=$NVIM_LOG_FILE)
 | 
				
			||||||
 | 
					--- @param nrlines? (number) Search up to this many log lines
 | 
				
			||||||
function module.assert_nolog(pat, logfile, nrlines)
 | 
					function module.assert_nolog(pat, logfile, nrlines)
 | 
				
			||||||
  return module.assert_log(pat, logfile, nrlines, true)
 | 
					  return module.assert_log(pat, logfile, nrlines, true)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param fn fun(...): any
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return boolean, any
 | 
				
			||||||
function module.pcall(fn, ...)
 | 
					function module.pcall(fn, ...)
 | 
				
			||||||
  luaassert(type(fn) == 'function')
 | 
					  luaassert(type(fn) == 'function')
 | 
				
			||||||
  local status, rv = pcall(fn, ...)
 | 
					  local status, rv = pcall(fn, ...)
 | 
				
			||||||
@@ -221,6 +235,8 @@ end
 | 
				
			|||||||
--    -- Match Lua pattern.
 | 
					--    -- Match Lua pattern.
 | 
				
			||||||
--    matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2'))
 | 
					--    matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2'))
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 | 
					--- @param fn function
 | 
				
			||||||
 | 
					--- @return string
 | 
				
			||||||
function module.pcall_err_withfile(fn, ...)
 | 
					function module.pcall_err_withfile(fn, ...)
 | 
				
			||||||
  luaassert(type(fn) == 'function')
 | 
					  luaassert(type(fn) == 'function')
 | 
				
			||||||
  local status, rv = module.pcall(fn, ...)
 | 
					  local status, rv = module.pcall(fn, ...)
 | 
				
			||||||
@@ -230,19 +246,29 @@ function module.pcall_err_withfile(fn, ...)
 | 
				
			|||||||
  return rv
 | 
					  return rv
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param fn function
 | 
				
			||||||
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return string
 | 
				
			||||||
function module.pcall_err_withtrace(fn, ...)
 | 
					function module.pcall_err_withtrace(fn, ...)
 | 
				
			||||||
  local errmsg = module.pcall_err_withfile(fn, ...)
 | 
					  local errmsg = module.pcall_err_withfile(fn, ...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return errmsg
 | 
					  return (
 | 
				
			||||||
 | 
					    errmsg
 | 
				
			||||||
      :gsub('^%.%.%./helpers%.lua:0: ', '')
 | 
					      :gsub('^%.%.%./helpers%.lua:0: ', '')
 | 
				
			||||||
      :gsub('^Error executing lua:- ', '')
 | 
					      :gsub('^Error executing lua:- ', '')
 | 
				
			||||||
      :gsub('^%[string "<nvim>"%]:0: ', '')
 | 
					      :gsub('^%[string "<nvim>"%]:0: ', '')
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function module.pcall_err(...)
 | 
					--- @param fn function
 | 
				
			||||||
  return module.remove_trace(module.pcall_err_withtrace(...))
 | 
					--- @param ... any
 | 
				
			||||||
 | 
					--- @return string
 | 
				
			||||||
 | 
					function module.pcall_err(fn, ...)
 | 
				
			||||||
 | 
					  return module.remove_trace(module.pcall_err_withtrace(fn, ...))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param s string
 | 
				
			||||||
 | 
					--- @return string
 | 
				
			||||||
function module.remove_trace(s)
 | 
					function module.remove_trace(s)
 | 
				
			||||||
  return (s:gsub('\n%s*stack traceback:.*', ''))
 | 
					  return (s:gsub('\n%s*stack traceback:.*', ''))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
@@ -252,9 +278,9 @@ end
 | 
				
			|||||||
-- exc_re:        exclude pattern(s) (string or table)
 | 
					-- exc_re:        exclude pattern(s) (string or table)
 | 
				
			||||||
function module.glob(initial_path, re, exc_re)
 | 
					function module.glob(initial_path, re, exc_re)
 | 
				
			||||||
  exc_re = type(exc_re) == 'table' and exc_re or { exc_re }
 | 
					  exc_re = type(exc_re) == 'table' and exc_re or { exc_re }
 | 
				
			||||||
  local paths_to_check = { initial_path }
 | 
					  local paths_to_check = { initial_path } --- @type string[]
 | 
				
			||||||
  local ret = {}
 | 
					  local ret = {} --- @type string[]
 | 
				
			||||||
  local checked_files = {}
 | 
					  local checked_files = {} --- @type table<string,true>
 | 
				
			||||||
  local function is_excluded(path)
 | 
					  local function is_excluded(path)
 | 
				
			||||||
    for _, pat in pairs(exc_re) do
 | 
					    for _, pat in pairs(exc_re) do
 | 
				
			||||||
      if path:match(pat) then
 | 
					      if path:match(pat) then
 | 
				
			||||||
@@ -301,7 +327,7 @@ function module.check_logs()
 | 
				
			|||||||
        local file = log_dir .. '/' .. tail
 | 
					        local file = log_dir .. '/' .. tail
 | 
				
			||||||
        local fd = assert(io.open(file))
 | 
					        local fd = assert(io.open(file))
 | 
				
			||||||
        local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20)
 | 
					        local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20)
 | 
				
			||||||
        local lines = {}
 | 
					        local lines = {} --- @type string[]
 | 
				
			||||||
        local warning_line = 0
 | 
					        local warning_line = 0
 | 
				
			||||||
        for line in fd:lines() do
 | 
					        for line in fd:lines() do
 | 
				
			||||||
          local cur_warning_line = check_logs_useless_lines[line]
 | 
					          local cur_warning_line = check_logs_useless_lines[line]
 | 
				
			||||||
@@ -313,6 +339,7 @@ function module.check_logs()
 | 
				
			|||||||
        end
 | 
					        end
 | 
				
			||||||
        fd:close()
 | 
					        fd:close()
 | 
				
			||||||
        if #lines > 0 then
 | 
					        if #lines > 0 then
 | 
				
			||||||
 | 
					          --- @type boolean?, file*?
 | 
				
			||||||
          local status, f
 | 
					          local status, f
 | 
				
			||||||
          local out = io.stdout
 | 
					          local out = io.stdout
 | 
				
			||||||
          if os.getenv('SYMBOLIZER') then
 | 
					          if os.getenv('SYMBOLIZER') then
 | 
				
			||||||
@@ -320,6 +347,7 @@ function module.check_logs()
 | 
				
			|||||||
          end
 | 
					          end
 | 
				
			||||||
          out:write(start_msg .. '\n')
 | 
					          out:write(start_msg .. '\n')
 | 
				
			||||||
          if status then
 | 
					          if status then
 | 
				
			||||||
 | 
					            assert(f)
 | 
				
			||||||
            for line in f:lines() do
 | 
					            for line in f:lines() do
 | 
				
			||||||
              out:write('= ' .. line .. '\n')
 | 
					              out:write('= ' .. line .. '\n')
 | 
				
			||||||
            end
 | 
					            end
 | 
				
			||||||
@@ -364,9 +392,11 @@ local function tmpdir_get()
 | 
				
			|||||||
  return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP')
 | 
					  return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Is temp directory `dir` defined local to the project workspace?
 | 
					--- Is temp directory `dir` defined local to the project workspace?
 | 
				
			||||||
 | 
					--- @param dir string?
 | 
				
			||||||
 | 
					--- @return boolean
 | 
				
			||||||
local function tmpdir_is_local(dir)
 | 
					local function tmpdir_is_local(dir)
 | 
				
			||||||
  return not not (dir and string.find(dir, 'Xtest'))
 | 
					  return not not (dir and dir:find('Xtest'))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Creates a new temporary file for use by tests.
 | 
					--- Creates a new temporary file for use by tests.
 | 
				
			||||||
@@ -410,6 +440,7 @@ function module.check_cores(app, force) -- luacheck: ignore
 | 
				
			|||||||
    return
 | 
					    return
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  app = app or 'build/bin/nvim' -- luacheck: ignore
 | 
					  app = app or 'build/bin/nvim' -- luacheck: ignore
 | 
				
			||||||
 | 
					  --- @type string, string?, string[]
 | 
				
			||||||
  local initial_path, re, exc_re
 | 
					  local initial_path, re, exc_re
 | 
				
			||||||
  local gdb_db_cmd =
 | 
					  local gdb_db_cmd =
 | 
				
			||||||
    'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
 | 
					    'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
 | 
				
			||||||
@@ -422,14 +453,14 @@ function module.check_cores(app, force) -- luacheck: ignore
 | 
				
			|||||||
      and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
 | 
					      and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
 | 
				
			||||||
    or nil
 | 
					    or nil
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  local db_cmd
 | 
					  local db_cmd --- @type string
 | 
				
			||||||
  local test_glob_dir = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
 | 
					  local test_glob_dir = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
 | 
				
			||||||
  if test_glob_dir and test_glob_dir ~= '' then
 | 
					  if test_glob_dir and test_glob_dir ~= '' then
 | 
				
			||||||
    initial_path = test_glob_dir
 | 
					    initial_path = test_glob_dir
 | 
				
			||||||
    re = os.getenv('NVIM_TEST_CORE_GLOB_RE')
 | 
					    re = os.getenv('NVIM_TEST_CORE_GLOB_RE')
 | 
				
			||||||
    exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir }
 | 
					    exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir }
 | 
				
			||||||
    db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd
 | 
					    db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd
 | 
				
			||||||
    random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP')
 | 
					    random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP') ~= ''
 | 
				
			||||||
  elseif module.is_os('mac') then
 | 
					  elseif module.is_os('mac') then
 | 
				
			||||||
    initial_path = '/cores'
 | 
					    initial_path = '/cores'
 | 
				
			||||||
    re = nil
 | 
					    re = nil
 | 
				
			||||||
@@ -487,17 +518,24 @@ function module.repeated_read_cmd(...)
 | 
				
			|||||||
  return nil
 | 
					  return nil
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @generic T
 | 
				
			||||||
 | 
					--- @param orig T
 | 
				
			||||||
 | 
					--- @return T
 | 
				
			||||||
function module.shallowcopy(orig)
 | 
					function module.shallowcopy(orig)
 | 
				
			||||||
  if type(orig) ~= 'table' then
 | 
					  if type(orig) ~= 'table' then
 | 
				
			||||||
    return orig
 | 
					    return orig
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  local copy = {}
 | 
					  --- @cast orig table<any,any>
 | 
				
			||||||
 | 
					  local copy = {} --- @type table<any,any>
 | 
				
			||||||
  for orig_key, orig_value in pairs(orig) do
 | 
					  for orig_key, orig_value in pairs(orig) do
 | 
				
			||||||
    copy[orig_key] = orig_value
 | 
					    copy[orig_key] = orig_value
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return copy
 | 
					  return copy
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param d1 table<any,any>
 | 
				
			||||||
 | 
					--- @param d2 table<any,any>
 | 
				
			||||||
 | 
					--- @return table<any,any>
 | 
				
			||||||
function module.mergedicts_copy(d1, d2)
 | 
					function module.mergedicts_copy(d1, d2)
 | 
				
			||||||
  local ret = module.shallowcopy(d1)
 | 
					  local ret = module.shallowcopy(d1)
 | 
				
			||||||
  for k, v in pairs(d2) do
 | 
					  for k, v in pairs(d2) do
 | 
				
			||||||
@@ -512,11 +550,13 @@ function module.mergedicts_copy(d1, d2)
 | 
				
			|||||||
  return ret
 | 
					  return ret
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2
 | 
					--- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2
 | 
				
			||||||
--
 | 
					---
 | 
				
			||||||
-- Note: does not do copies of d2 values used.
 | 
					--- Note: does not do copies of d2 values used.
 | 
				
			||||||
 | 
					--- @param d1 table<any,any>
 | 
				
			||||||
 | 
					--- @param d2 table<any,any>
 | 
				
			||||||
function module.dictdiff(d1, d2)
 | 
					function module.dictdiff(d1, d2)
 | 
				
			||||||
  local ret = {}
 | 
					  local ret = {} --- @type table<any,any>
 | 
				
			||||||
  local hasdiff = false
 | 
					  local hasdiff = false
 | 
				
			||||||
  for k, v in pairs(d1) do
 | 
					  for k, v in pairs(d1) do
 | 
				
			||||||
    if d2[k] == nil then
 | 
					    if d2[k] == nil then
 | 
				
			||||||
@@ -554,8 +594,9 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- Concat list-like tables.
 | 
					-- Concat list-like tables.
 | 
				
			||||||
function module.concat_tables(...)
 | 
					function module.concat_tables(...)
 | 
				
			||||||
  local ret = {}
 | 
					  local ret = {} --- @type table<any,any>
 | 
				
			||||||
  for i = 1, select('#', ...) do
 | 
					  for i = 1, select('#', ...) do
 | 
				
			||||||
 | 
					    --- @type table<any,any>
 | 
				
			||||||
    local tbl = select(i, ...)
 | 
					    local tbl = select(i, ...)
 | 
				
			||||||
    if tbl then
 | 
					    if tbl then
 | 
				
			||||||
      for _, v in ipairs(tbl) do
 | 
					      for _, v in ipairs(tbl) do
 | 
				
			||||||
@@ -787,10 +828,10 @@ function module.hexdump(str)
 | 
				
			|||||||
  return dump .. hex .. string.rep('   ', 8 - len % 8) .. asc
 | 
					  return dump .. hex .. string.rep('   ', 8 - len % 8) .. asc
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Reads text lines from `filename` into a table.
 | 
					--- Reads text lines from `filename` into a table.
 | 
				
			||||||
--
 | 
					--- @param filename string path to file
 | 
				
			||||||
-- filename: path to file
 | 
					--- @param start? integer start line (1-indexed), negative means "lines before end" (tail)
 | 
				
			||||||
-- start: start line (1-indexed), negative means "lines before end" (tail)
 | 
					--- @return string[]?
 | 
				
			||||||
function module.read_file_list(filename, start)
 | 
					function module.read_file_list(filename, start)
 | 
				
			||||||
  local lnum = (start ~= nil and type(start) == 'number') and start or 1
 | 
					  local lnum = (start ~= nil and type(start) == 'number') and start or 1
 | 
				
			||||||
  local tail = (lnum < 0)
 | 
					  local tail = (lnum < 0)
 | 
				
			||||||
@@ -826,9 +867,9 @@ function module.read_file_list(filename, start)
 | 
				
			|||||||
  return lines
 | 
					  return lines
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Reads the entire contents of `filename` into a string.
 | 
					--- Reads the entire contents of `filename` into a string.
 | 
				
			||||||
--
 | 
					--- @param filename string
 | 
				
			||||||
-- filename: path to file
 | 
					--- @return string?
 | 
				
			||||||
function module.read_file(filename)
 | 
					function module.read_file(filename)
 | 
				
			||||||
  local file = io.open(filename, 'r')
 | 
					  local file = io.open(filename, 'r')
 | 
				
			||||||
  if not file then
 | 
					  if not file then
 | 
				
			||||||
@@ -844,6 +885,7 @@ function module.write_file(name, text, no_dedent, append)
 | 
				
			|||||||
  local file = assert(io.open(name, (append and 'a' or 'w')))
 | 
					  local file = assert(io.open(name, (append and 'a' or 'w')))
 | 
				
			||||||
  if type(text) == 'table' then
 | 
					  if type(text) == 'table' then
 | 
				
			||||||
    -- Byte blob
 | 
					    -- Byte blob
 | 
				
			||||||
 | 
					    --- @type string[]
 | 
				
			||||||
    local bytes = text
 | 
					    local bytes = text
 | 
				
			||||||
    text = ''
 | 
					    text = ''
 | 
				
			||||||
    for _, char in ipairs(bytes) do
 | 
					    for _, char in ipairs(bytes) do
 | 
				
			||||||
@@ -857,6 +899,8 @@ function module.write_file(name, text, no_dedent, append)
 | 
				
			|||||||
  file:close()
 | 
					  file:close()
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- @param name? 'cirrus'|'github'
 | 
				
			||||||
 | 
					--- @return boolean
 | 
				
			||||||
function module.is_ci(name)
 | 
					function module.is_ci(name)
 | 
				
			||||||
  local any = (name == nil)
 | 
					  local any = (name == nil)
 | 
				
			||||||
  luaassert(any or name == 'github' or name == 'cirrus')
 | 
					  luaassert(any or name == 'github' or name == 'cirrus')
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user