aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmark/decor_spec.lua42
-rw-r--r--test/client/rpc_stream.lua (renamed from test/client/msgpack_rpc_stream.lua)49
-rw-r--r--test/client/session.lua58
-rw-r--r--test/client/uv_stream.lua105
-rw-r--r--test/functional/api/command_spec.lua9
-rw-r--r--test/functional/api/server_requests_spec.lua16
-rw-r--r--test/functional/core/channels_spec.lua7
-rw-r--r--test/functional/core/exit_spec.lua6
-rw-r--r--test/functional/core/fileio_spec.lua13
-rw-r--r--test/functional/core/main_spec.lua100
-rw-r--r--test/functional/core/remote_spec.lua18
-rw-r--r--test/functional/core/startup_spec.lua94
-rw-r--r--test/functional/editor/completion_spec.lua26
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua18
-rw-r--r--test/functional/ex_cmds/wundo_spec.lua16
-rw-r--r--test/functional/func/memoize_spec.lua142
-rw-r--r--test/functional/legacy/cmdline_spec.lua54
-rw-r--r--test/functional/legacy/messages_spec.lua2
-rw-r--r--test/functional/legacy/window_cmd_spec.lua2
-rw-r--r--test/functional/lua/fs_spec.lua26
-rw-r--r--test/functional/lua/ui_event_spec.lua13
-rw-r--r--test/functional/options/defaults_spec.lua16
-rw-r--r--test/functional/plugin/health_spec.lua12
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua28
-rw-r--r--test/functional/shada/marks_spec.lua10
-rw-r--r--test/functional/shada/shada_spec.lua18
-rw-r--r--test/functional/terminal/buffer_spec.lua13
-rw-r--r--test/functional/terminal/highlight_spec.lua3
-rw-r--r--test/functional/terminal/tui_spec.lua13
-rw-r--r--test/functional/testnvim.lua149
-rw-r--r--test/functional/treesitter/fold_spec.lua75
-rw-r--r--test/functional/treesitter/inspect_tree_spec.lua29
-rw-r--r--test/functional/treesitter/query_spec.lua4
-rw-r--r--test/functional/ui/cmdline_spec.lua12
-rw-r--r--test/functional/ui/messages_spec.lua17
-rw-r--r--test/functional/ui/popupmenu_spec.lua105
-rw-r--r--test/functional/ui/screen_basic_spec.lua6
-rw-r--r--test/old/testdir/test_autocmd.vim24
-rw-r--r--test/old/testdir/test_cmdline.vim12
-rw-r--r--test/old/testdir/test_filetype.vim18
-rw-r--r--test/old/testdir/test_stacktrace.vim132
-rw-r--r--test/old/testdir/test_window_cmd.vim1
-rw-r--r--test/unit/fixtures/vterm_test.c271
-rw-r--r--test/unit/fixtures/vterm_test.h27
-rw-r--r--test/unit/vterm_spec.lua14
45 files changed, 1453 insertions, 372 deletions
diff --git a/test/benchmark/decor_spec.lua b/test/benchmark/decor_spec.lua
index 0994023c2d..1b7e763a09 100644
--- a/test/benchmark/decor_spec.lua
+++ b/test/benchmark/decor_spec.lua
@@ -6,8 +6,7 @@ describe('decor perf', function()
before_each(n.clear)
it('can handle long lines', function()
- local screen = Screen.new(100, 101)
- screen:attach()
+ Screen.new(100, 101)
local result = exec_lua [==[
local ephemeral_pattern = {
@@ -99,4 +98,43 @@ describe('decor perf', function()
print('\nTotal ' .. fmt(total) .. '\nDecoration provider: ' .. fmt(provider))
end)
+
+ it('can handle full screen of highlighting', function()
+ Screen.new(100, 51)
+
+ local result = exec_lua(function()
+ local long_line = 'local a={' .. ('a=5,'):rep(22) .. '}'
+ local lines = {}
+ for _ = 1, 50 do
+ table.insert(lines, long_line)
+ end
+ vim.api.nvim_buf_set_lines(0, 0, 0, false, lines)
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+ vim.treesitter.start(0, 'lua')
+
+ local total = {}
+ for _ = 1, 100 do
+ local tic = vim.uv.hrtime()
+ vim.cmd 'redraw!'
+ local toc = vim.uv.hrtime()
+ table.insert(total, toc - tic)
+ end
+
+ return { total }
+ end)
+
+ local total = unpack(result)
+ table.sort(total)
+
+ local ms = 1 / 1000000
+ local res = string.format(
+ 'min, 25%%, median, 75%%, max:\n\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms',
+ total[1] * ms,
+ total[1 + math.floor(#total * 0.25)] * ms,
+ total[1 + math.floor(#total * 0.5)] * ms,
+ total[1 + math.floor(#total * 0.75)] * ms,
+ total[#total] * ms
+ )
+ print('\nTotal ' .. res)
+ end)
end)
diff --git a/test/client/msgpack_rpc_stream.lua b/test/client/rpc_stream.lua
index 7131940a58..9f2672bcf9 100644
--- a/test/client/msgpack_rpc_stream.lua
+++ b/test/client/rpc_stream.lua
@@ -1,34 +1,41 @@
+---
+--- Reading/writing of msgpack over any of the stream types from `uv_stream.lua`.
+--- Does not implement the RPC protocol, see `session.lua` for that.
+---
+
local mpack = vim.mpack
local Response = {}
Response.__index = Response
-function Response.new(msgpack_rpc_stream, request_id)
+function Response.new(rpc_stream, request_id)
return setmetatable({
- _msgpack_rpc_stream = msgpack_rpc_stream,
+ _rpc_stream = rpc_stream,
_request_id = request_id,
}, Response)
end
function Response:send(value, is_error)
- local data = self._msgpack_rpc_stream._session:reply(self._request_id)
+ local data = self._rpc_stream._session:reply(self._request_id)
if is_error then
- data = data .. self._msgpack_rpc_stream._pack(value)
- data = data .. self._msgpack_rpc_stream._pack(mpack.NIL)
+ data = data .. self._rpc_stream._pack(value)
+ data = data .. self._rpc_stream._pack(mpack.NIL)
else
- data = data .. self._msgpack_rpc_stream._pack(mpack.NIL)
- data = data .. self._msgpack_rpc_stream._pack(value)
+ data = data .. self._rpc_stream._pack(mpack.NIL)
+ data = data .. self._rpc_stream._pack(value)
end
- self._msgpack_rpc_stream._stream:write(data)
+ self._rpc_stream._stream:write(data)
end
---- @class test.MsgpackRpcStream
+--- Nvim msgpack RPC stream.
+---
+--- @class test.RpcStream
--- @field private _stream test.Stream
--- @field private __pack table
-local MsgpackRpcStream = {}
-MsgpackRpcStream.__index = MsgpackRpcStream
+local RpcStream = {}
+RpcStream.__index = RpcStream
-function MsgpackRpcStream.new(stream)
+function RpcStream.new(stream)
return setmetatable({
_stream = stream,
_pack = mpack.Packer(),
@@ -50,10 +57,10 @@ function MsgpackRpcStream.new(stream)
},
}),
}),
- }, MsgpackRpcStream)
+ }, RpcStream)
end
-function MsgpackRpcStream:write(method, args, response_cb)
+function RpcStream:write(method, args, response_cb)
local data
if response_cb then
assert(type(response_cb) == 'function')
@@ -66,10 +73,10 @@ function MsgpackRpcStream:write(method, args, response_cb)
self._stream:write(data)
end
-function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
+function RpcStream:read_start(on_request, on_notification, on_eof)
self._stream:read_start(function(data)
if not data then
- return eof_cb()
+ return on_eof()
end
local type, id_or_cb, method_or_error, args_or_result
local pos = 1
@@ -78,9 +85,9 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
type, id_or_cb, method_or_error, args_or_result, pos = self._session:receive(data, pos)
if type == 'request' or type == 'notification' then
if type == 'request' then
- request_cb(method_or_error, args_or_result, Response.new(self, id_or_cb))
+ on_request(method_or_error, args_or_result, Response.new(self, id_or_cb))
else
- notification_cb(method_or_error, args_or_result)
+ on_notification(method_or_error, args_or_result)
end
elseif type == 'response' then
if method_or_error == mpack.NIL then
@@ -94,12 +101,12 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
end)
end
-function MsgpackRpcStream:read_stop()
+function RpcStream:read_stop()
self._stream:read_stop()
end
-function MsgpackRpcStream:close(signal)
+function RpcStream:close(signal)
self._stream:close(signal)
end
-return MsgpackRpcStream
+return RpcStream
diff --git a/test/client/session.lua b/test/client/session.lua
index f1f46c5efe..a5839e012a 100644
--- a/test/client/session.lua
+++ b/test/client/session.lua
@@ -1,12 +1,18 @@
+---
+--- Nvim msgpack-RPC protocol session. Manages requests/notifications/responses.
+---
+
local uv = vim.uv
-local MsgpackRpcStream = require('test.client.msgpack_rpc_stream')
+local RpcStream = require('test.client.rpc_stream')
+--- Nvim msgpack-RPC protocol session. Manages requests/notifications/responses.
+---
--- @class test.Session
---- @field private _pending_messages string[]
---- @field private _msgpack_rpc_stream test.MsgpackRpcStream
+--- @field private _pending_messages string[] Requests/notifications received from the remote end.
+--- @field private _rpc_stream test.RpcStream
--- @field private _prepare uv.uv_prepare_t
--- @field private _timer uv.uv_timer_t
---- @field private _is_running boolean
+--- @field private _is_running boolean true during `Session:run()` scope.
--- @field exec_lua_setup boolean
local Session = {}
Session.__index = Session
@@ -51,9 +57,10 @@ local function coroutine_exec(func, ...)
end))
end
+--- Creates a new msgpack-RPC session.
function Session.new(stream)
return setmetatable({
- _msgpack_rpc_stream = MsgpackRpcStream.new(stream),
+ _rpc_stream = RpcStream.new(stream),
_pending_messages = {},
_prepare = uv.new_prepare(),
_timer = uv.new_timer(),
@@ -91,10 +98,13 @@ function Session:next_message(timeout)
return table.remove(self._pending_messages, 1)
end
+--- Sends a notification to the RPC endpoint.
function Session:notify(method, ...)
- self._msgpack_rpc_stream:write(method, { ... })
+ self._rpc_stream:write(method, { ... })
end
+--- Sends a request to the RPC endpoint.
+---
--- @param method string
--- @param ... any
--- @return boolean, table
@@ -114,8 +124,16 @@ function Session:request(method, ...)
return true, result
end
---- Runs the event loop.
+--- Processes incoming RPC requests/notifications until exhausted.
+---
+--- TODO(justinmk): luaclient2 avoids this via uvutil.cb_wait() + uvutil.add_idle_call()?
+---
+--- @param request_cb function Handles requests from the sever to the local end.
+--- @param notification_cb function Handles notifications from the sever to the local end.
+--- @param setup_cb function
+--- @param timeout number
function Session:run(request_cb, notification_cb, setup_cb, timeout)
+ --- Handles an incoming request.
local function on_request(method, args, response)
coroutine_exec(request_cb, method, args, function(status, result, flag)
if status then
@@ -126,6 +144,7 @@ function Session:run(request_cb, notification_cb, setup_cb, timeout)
end)
end
+ --- Handles an incoming notification.
local function on_notification(method, args)
coroutine_exec(notification_cb, method, args)
end
@@ -160,39 +179,45 @@ function Session:close(signal)
if not self._prepare:is_closing() then
self._prepare:close()
end
- self._msgpack_rpc_stream:close(signal)
+ self._rpc_stream:close(signal)
self.closed = true
end
+--- Sends a request to the RPC endpoint, without blocking (schedules a coroutine).
function Session:_yielding_request(method, args)
return coroutine.yield(function(co)
- self._msgpack_rpc_stream:write(method, args, function(err, result)
+ self._rpc_stream:write(method, args, function(err, result)
resume(co, err, result)
end)
end)
end
+--- Sends a request to the RPC endpoint, and blocks (polls event loop) until a response is received.
function Session:_blocking_request(method, args)
local err, result
+ -- Invoked when a request is received from the remote end.
local function on_request(method_, args_, response)
table.insert(self._pending_messages, { 'request', method_, args_, response })
end
+ -- Invoked when a notification is received from the remote end.
local function on_notification(method_, args_)
table.insert(self._pending_messages, { 'notification', method_, args_ })
end
- self._msgpack_rpc_stream:write(method, args, function(e, r)
+ self._rpc_stream:write(method, args, function(e, r)
err = e
result = r
uv.stop()
end)
+ -- Poll for incoming requests/notifications received from the remote end.
self:_run(on_request, on_notification)
return (err or self.eof_err), result
end
+--- Polls for incoming requests/notifications received from the remote end.
function Session:_run(request_cb, notification_cb, timeout)
if type(timeout) == 'number' then
self._prepare:start(function()
@@ -202,14 +227,21 @@ function Session:_run(request_cb, notification_cb, timeout)
self._prepare:stop()
end)
end
- self._msgpack_rpc_stream:read_start(request_cb, notification_cb, function()
+ self._rpc_stream:read_start(request_cb, notification_cb, function()
uv.stop()
- self.eof_err = { 1, 'EOF was received from Nvim. Likely the Nvim process crashed.' }
+
+ --- @diagnostic disable-next-line: invisible
+ local stderr = self._rpc_stream._stream.stderr --[[@as string?]]
+ -- See if `ProcStream.stderr` has anything useful.
+ stderr = '' ~= ((stderr or ''):match('^%s*(.*%S)') or '') and ' stderr:\n' .. stderr or ''
+
+ self.eof_err = { 1, 'EOF was received from Nvim. Likely the Nvim process crashed.' .. stderr }
end)
uv.run()
self._prepare:stop()
self._timer:stop()
- self._msgpack_rpc_stream:read_stop()
+ self._rpc_stream:read_stop()
end
+--- Nvim msgpack-RPC session.
return Session
diff --git a/test/client/uv_stream.lua b/test/client/uv_stream.lua
index adf002ba1e..6e1a6995be 100644
--- a/test/client/uv_stream.lua
+++ b/test/client/uv_stream.lua
@@ -1,3 +1,8 @@
+---
+--- Basic stream types.
+--- See `rpc_stream.lua` for the msgpack layer.
+---
+
local uv = vim.uv
--- @class test.Stream
@@ -6,6 +11,8 @@ local uv = vim.uv
--- @field read_stop fun(self)
--- @field close fun(self, signal?: string)
+--- Stream over given pipes.
+---
--- @class vim.StdioStream : test.Stream
--- @field private _in uv.uv_pipe_t
--- @field private _out uv.uv_pipe_t
@@ -45,6 +52,8 @@ function StdioStream:close()
self._out:close()
end
+--- Stream over a named pipe or TCP socket.
+---
--- @class test.SocketStream : test.Stream
--- @field package _stream_error? string
--- @field package _socket uv.uv_pipe_t
@@ -109,26 +118,54 @@ function SocketStream:close()
uv.close(self._socket)
end
---- @class test.ChildProcessStream : test.Stream
+--- Stream over child process stdio.
+---
+--- @class test.ProcStream : 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 private _child_stderr uv.uv_pipe_t
+--- Collects stdout (if `collect_text=true`). Treats data as text (CRLF converted to LF).
+--- @field stdout string
+--- Collects stderr as raw data.
+--- @field stderr string
+--- Gets stderr+stdout as text (CRLF converted to LF).
+--- @field output fun(): string
+--- @field stdout_eof boolean
+--- @field stderr_eof boolean
+--- Collects text into the `stdout` field.
+--- @field collect_text boolean
+--- Exit code
--- @field status integer
--- @field signal integer
-local ChildProcessStream = {}
-ChildProcessStream.__index = ChildProcessStream
+local ProcStream = {}
+ProcStream.__index = ProcStream
+--- Starts child process specified by `argv`.
+---
--- @param argv string[]
--- @param env string[]?
--- @param io_extra uv.uv_pipe_t?
---- @return test.ChildProcessStream
-function ChildProcessStream.spawn(argv, env, io_extra)
+--- @return test.ProcStream
+function ProcStream.spawn(argv, env, io_extra)
local self = setmetatable({
- _child_stdin = uv.new_pipe(false),
- _child_stdout = uv.new_pipe(false),
+ collect_text = false,
+ output = function(self)
+ if not self.collect_text then
+ error('set collect_text=true')
+ end
+ return (self.stderr .. self.stdout):gsub('\r\n', '\n')
+ end,
+ stdout = '',
+ stderr = '',
+ stdout_eof = false,
+ stderr_eof = false,
+ _child_stdin = assert(uv.new_pipe(false)),
+ _child_stdout = assert(uv.new_pipe(false)),
+ _child_stderr = assert(uv.new_pipe(false)),
_exiting = false,
- }, ChildProcessStream)
+ }, ProcStream)
local prog = argv[1]
local args = {} --- @type string[]
for i = 2, #argv do
@@ -136,13 +173,14 @@ function ChildProcessStream.spawn(argv, env, io_extra)
end
--- @diagnostic disable-next-line:missing-fields
self._proc, self._pid = uv.spawn(prog, {
- stdio = { self._child_stdin, self._child_stdout, 1, io_extra },
+ stdio = { self._child_stdin, self._child_stdout, self._child_stderr, io_extra },
args = args,
--- @diagnostic disable-next-line:assign-type-mismatch
env = env,
}, function(status, signal)
- self.status = status
self.signal = signal
+ -- "Abort" exit may not set status; force to nonzero in that case.
+ self.status = (0 ~= (status or 0) or 0 == (signal or 0)) and status or (128 + (signal or 0))
end)
if not self._proc then
@@ -153,24 +191,54 @@ function ChildProcessStream.spawn(argv, env, io_extra)
return self
end
-function ChildProcessStream:write(data)
+function ProcStream:write(data)
self._child_stdin:write(data)
end
-function ChildProcessStream:read_start(cb)
- self._child_stdout:read_start(function(err, chunk)
- if err then
- error(err)
+function ProcStream:on_read(stream, cb, err, chunk)
+ if err then
+ error(err) -- stream read failed?
+ elseif chunk then
+ -- Always collect stderr, in case it gives useful info on failure.
+ if stream == 'stderr' then
+ self.stderr = self.stderr .. chunk --[[@as string]]
+ elseif stream == 'stdout' and self.collect_text then
+ -- Set `stdout` and convert CRLF => LF.
+ self.stdout = (self.stdout .. chunk):gsub('\r\n', '\n')
end
+ else
+ -- stderr_eof/stdout_eof
+ self[stream .. '_eof'] = true ---@type boolean
+ end
+
+ -- Handler provided by the caller.
+ if cb then
cb(chunk)
+ end
+end
+
+--- Collects output until the process exits.
+function ProcStream:wait()
+ while not (self.stdout_eof and self.stderr_eof and (self.status or self.signal)) do
+ uv.run('once')
+ end
+end
+
+function ProcStream:read_start(on_stdout, on_stderr)
+ self._child_stdout:read_start(function(err, chunk)
+ self:on_read('stdout', on_stdout, err, chunk)
+ end)
+ self._child_stderr:read_start(function(err, chunk)
+ self:on_read('stderr', on_stderr, err, chunk)
end)
end
-function ChildProcessStream:read_stop()
+function ProcStream:read_stop()
self._child_stdout:read_stop()
+ self._child_stderr:read_stop()
end
-function ChildProcessStream:close(signal)
+function ProcStream:close(signal)
if self._closed then
return
end
@@ -178,6 +246,7 @@ function ChildProcessStream:close(signal)
self:read_stop()
self._child_stdin:close()
self._child_stdout:close()
+ self._child_stderr:close()
if type(signal) == 'string' then
self._proc:kill('sig' .. signal)
end
@@ -189,6 +258,6 @@ end
return {
StdioStream = StdioStream,
- ChildProcessStream = ChildProcessStream,
+ ProcStream = ProcStream,
SocketStream = SocketStream,
}
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index a16c6a88e3..fabd9be6d6 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -651,6 +651,11 @@ describe('nvim_create_user_command', function()
api.nvim_set_current_buf(bufnr)
command('Hello')
assert_alive()
+ eq(
+ 'Invalid buffer id: 1234',
+ pcall_err(api.nvim_buf_create_user_command, 1234, 'Hello', '', {})
+ )
+ assert_alive()
end)
it('can use a Lua complete function', function()
@@ -771,5 +776,9 @@ describe('nvim_del_user_command', function()
command('Hello')
api.nvim_buf_del_user_command(0, 'Hello')
matches('Not an editor command: Hello', pcall_err(command, 'Hello'))
+ eq('Invalid command (not found): Hello', pcall_err(api.nvim_buf_del_user_command, 0, 'Hello'))
+ eq('Invalid command (not found): Bye', pcall_err(api.nvim_buf_del_user_command, 0, 'Bye'))
+ eq('Invalid buffer id: 1234', pcall_err(api.nvim_buf_del_user_command, 1234, 'Hello'))
+ assert_alive()
end)
end)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index bdd340f6c6..c022ba28de 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -9,7 +9,6 @@ local nvim_prog, command, fn = n.nvim_prog, n.command, n.fn
local source, next_msg = n.source, n.next_msg
local ok = t.ok
local api = n.api
-local spawn, merge_args = n.spawn, n.merge_args
local set_session = n.set_session
local pcall_err = t.pcall_err
local assert_alive = n.assert_alive
@@ -281,10 +280,9 @@ describe('server -> client', function()
end)
describe('connecting to another (peer) nvim', function()
- local nvim_argv = merge_args(n.nvim_argv, { '--headless' })
local function connect_test(server, mode, address)
local serverpid = fn.getpid()
- local client = spawn(nvim_argv, false, nil, true)
+ local client = n.new_session(true)
set_session(client)
local clientpid = fn.getpid()
@@ -312,7 +310,7 @@ describe('server -> client', function()
end
it('via named pipe', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverlist()[1]
local first = string.sub(address, 1, 1)
@@ -321,7 +319,7 @@ describe('server -> client', function()
end)
it('via ipv4 address', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local status, address = pcall(fn.serverstart, '127.0.0.1:')
if not status then
@@ -332,7 +330,7 @@ describe('server -> client', function()
end)
it('via ipv6 address', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local status, address = pcall(fn.serverstart, '::1:')
if not status then
@@ -343,7 +341,7 @@ describe('server -> client', function()
end)
it('via hostname', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverstart('localhost:')
eq('localhost:', string.sub(address, 1, 10))
@@ -351,10 +349,10 @@ describe('server -> client', function()
end)
it('does not crash on receiving UI events', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverlist()[1]
- local client = spawn(nvim_argv, false, nil, true)
+ local client = n.new_session(true)
set_session(client)
local id = fn.sockconnect('pipe', address, { rpc = true })
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index dee13d19ae..7b10eb05ef 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -5,7 +5,6 @@ local clear, eq, eval, next_msg, ok, source = n.clear, t.eq, n.eval, n.next_msg,
local command, fn, api = n.command, n.fn, n.api
local matches = t.matches
local sleep = vim.uv.sleep
-local spawn, nvim_argv = n.spawn, n.nvim_argv
local get_session, set_session = n.get_session, n.set_session
local nvim_prog = n.nvim_prog
local is_os = t.is_os
@@ -33,10 +32,10 @@ describe('channels', function()
end)
pending('can connect to socket', function()
- local server = spawn(nvim_argv, nil, nil, true)
+ local server = n.new_session(true)
set_session(server)
local address = fn.serverlist()[1]
- local client = spawn(nvim_argv, nil, nil, true)
+ local client = n.new_session(true)
set_session(client)
source(init)
@@ -63,7 +62,7 @@ describe('channels', function()
it('dont crash due to garbage in rpc #23781', function()
local client = get_session()
- local server = spawn(nvim_argv, nil, nil, true)
+ local server = n.new_session(true)
set_session(server)
local address = fn.serverlist()[1]
set_session(client)
diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua
index 34c3eedbd2..65f6bc28a6 100644
--- a/test/functional/core/exit_spec.lua
+++ b/test/functional/core/exit_spec.lua
@@ -8,8 +8,6 @@ local feed = n.feed
local eval = n.eval
local eq = t.eq
local run = n.run
-local fn = n.fn
-local nvim_prog = n.nvim_prog
local pcall_err = t.pcall_err
local exec_capture = n.exec_capture
local poke_eventloop = n.poke_eventloop
@@ -69,8 +67,8 @@ describe(':cquit', function()
poke_eventloop()
assert_alive()
else
- fn.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline })
- eq(exit_code, eval('v:shell_error'))
+ local p = n.spawn_wait('--cmd', cmdline)
+ eq(exit_code, p.status)
end
end
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index cf9715f848..b1a8e21762 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -31,7 +31,6 @@ local feed_command = n.feed_command
local skip = t.skip
local is_os = t.is_os
local is_ci = t.is_ci
-local spawn = n.spawn
local set_session = n.set_session
describe('fileio', function()
@@ -51,12 +50,11 @@ describe('fileio', function()
rmdir('Xtest_backupdir with spaces')
end)
- local args = { nvim_prog, '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir' }
+ local args = { '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir' }
--- Starts a new nvim session and returns an attached screen.
- local function startup(extra_args)
- extra_args = extra_args or {}
- local argv = vim.iter({ args, '--embed', extra_args }):flatten():totable()
- local screen_nvim = spawn(argv)
+ local function startup()
+ local argv = vim.iter({ args, '--embed' }):flatten():totable()
+ local screen_nvim = n.new_session(false, { args = argv, merge = false })
set_session(screen_nvim)
local screen = Screen.new(70, 10)
screen:set_default_attr_ids({
@@ -100,7 +98,8 @@ describe('fileio', function()
eq('foozubbaz', trim(read_file('Xtest_startup_file1')))
-- 4. Exit caused by deadly signal (+ 'swapfile').
- local j = fn.jobstart(vim.iter({ args, '--embed' }):flatten():totable(), { rpc = true })
+ local j =
+ fn.jobstart(vim.iter({ nvim_prog, args, '--embed' }):flatten():totable(), { rpc = true })
fn.rpcrequest(
j,
'nvim_exec2',
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
index ce4ba1905f..6add49ceae 100644
--- a/test/functional/core/main_spec.lua
+++ b/test/functional/core/main_spec.lua
@@ -9,7 +9,6 @@ local feed = n.feed
local eval = n.eval
local clear = n.clear
local fn = n.fn
-local nvim_prog_abs = n.nvim_prog_abs
local write_file = t.write_file
local is_os = t.is_os
local skip = t.skip
@@ -35,7 +34,7 @@ describe('command-line option', function()
it('treats - as stdin', function()
eq(nil, uv.fs_stat(fname))
fn.system({
- nvim_prog_abs(),
+ n.nvim_prog,
'-u',
'NONE',
'-i',
@@ -56,41 +55,29 @@ describe('command-line option', function()
eq(nil, uv.fs_stat(fname))
eq(true, not not dollar_fname:find('%$%w+'))
write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n')
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
+ local p = n.spawn_wait(
'--cmd',
'set noswapfile shortmess+=IFW fileformats=unix',
'-s',
dollar_fname,
- fname,
- })
- eq(0, eval('v:shell_error'))
+ fname
+ )
+ eq(0, p.status)
local attrs = uv.fs_stat(fname)
eq(#'100500\n', attrs.size)
end)
- it('does not crash when run completion in ex mode', function()
- fn.system({
- nvim_prog_abs(),
- '--clean',
- '-e',
- '-s',
- '--cmd',
- 'exe "norm! i\\<C-X>\\<C-V>"',
- })
- eq(0, eval('v:shell_error'))
+ it('does not crash when run completion in Ex mode', function()
+ local p =
+ n.spawn_wait('--clean', '-e', '-s', '--cmd', 'exe "norm! i\\<C-X>\\<C-V>"', '--cmd', 'qa!')
+ eq(0, p.status)
end)
it('does not crash after reading from stdin in non-headless mode', function()
skip(is_os('win'))
local screen = Screen.new(40, 8)
local args = {
- nvim_prog_abs(),
+ n.nvim_prog,
'-u',
'NONE',
'-i',
@@ -138,51 +125,40 @@ describe('command-line option', function()
]=]
end)
- it('errors out when trying to use nonexistent file with -s', function()
+ it('fails when trying to use nonexistent file with -s', function()
+ local p = n.spawn_wait(
+ '--cmd',
+ 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd',
+ 'language C',
+ '-s',
+ nonexistent_fname
+ )
eq(
'Cannot open for reading: "' .. nonexistent_fname .. '": no such file or directory\n',
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- 'set noswapfile shortmess+=IFW fileformats=unix',
- '--cmd',
- 'language C',
- '-s',
- nonexistent_fname,
- })
+ --- TODO(justinmk): using `p.output` because Nvim emits CRLF even on non-Win. Emit LF instead?
+ p:output()
)
- eq(2, eval('v:shell_error'))
+ eq(2, p.status)
end)
it('errors out when trying to use -s twice', function()
write_file(fname, ':call setline(1, "1")\n:wqall!\n')
write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n')
- eq(
- 'Attempt to open script file again: "-s ' .. dollar_fname .. '"\n',
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- 'set noswapfile shortmess+=IFW fileformats=unix',
- '--cmd',
- 'language C',
- '-s',
- fname,
- '-s',
- dollar_fname,
- fname_2,
- })
+ local p = n.spawn_wait(
+ '--cmd',
+ 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd',
+ 'language C',
+ '-s',
+ fname,
+ '-s',
+ dollar_fname,
+ fname_2
)
- eq(2, eval('v:shell_error'))
+ --- TODO(justinmk): using `p.output` because Nvim emits CRLF even on non-Win. Emit LF instead?
+ eq('Attempt to open script file again: "-s ' .. dollar_fname .. '"\n', p:output())
+ eq(2, p.status)
eq(nil, uv.fs_stat(fname_2))
end)
end)
@@ -190,8 +166,8 @@ describe('command-line option', function()
it('nvim -v, :version', function()
matches('Run ":verbose version"', fn.execute(':version'))
matches('fall%-back for %$VIM: .*Run :checkhealth', fn.execute(':verbose version'))
- matches('Run "nvim %-V1 %-v"', fn.system({ nvim_prog_abs(), '-v' }))
- matches('fall%-back for %$VIM: .*Run :checkhealth', fn.system({ nvim_prog_abs(), '-V1', '-v' }))
+ matches('Run "nvim %-V1 %-v"', n.spawn_wait('-v').stdout)
+ matches('fall%-back for %$VIM: .*Run :checkhealth', n.spawn_wait('-V1', '-v').stdout)
end)
if is_os('win') then
@@ -205,7 +181,7 @@ describe('command-line option', function()
eq(
'some text',
fn.system({
- nvim_prog_abs(),
+ n.nvim_prog,
'-es',
'+%print',
'+q',
diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua
index 6cc28ddeef..1cfa0535f6 100644
--- a/test/functional/core/remote_spec.lua
+++ b/test/functional/core/remote_spec.lua
@@ -10,10 +10,8 @@ local expect = n.expect
local fn = n.fn
local insert = n.insert
local nvim_prog = n.nvim_prog
-local new_argv = n.new_argv
local neq = t.neq
local set_session = n.set_session
-local spawn = n.spawn
local tmpname = t.tmpname
local write_file = t.write_file
@@ -32,8 +30,7 @@ describe('Remote', function()
describe('connect to server and', function()
local server
before_each(function()
- server = spawn(new_argv(), true)
- set_session(server)
+ server = n.clear()
end)
after_each(function()
@@ -49,7 +46,7 @@ describe('Remote', function()
-- to wait for the remote instance to exit and calling jobwait blocks
-- the event loop. If the server event loop is blocked, it can't process
-- our incoming --remote calls.
- local client_starter = spawn(new_argv(), false, nil, true)
+ local client_starter = n.new_session(true)
set_session(client_starter)
-- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
eq(
@@ -144,15 +141,8 @@ describe('Remote', function()
describe('exits with error on', function()
local function run_and_check_exit_code(...)
- local bogus_argv = new_argv(...)
-
- -- Create an nvim instance just to run the remote-invoking nvim. We want
- -- to wait for the remote instance to exit and calling jobwait blocks
- -- the event loop. If the server event loop is blocked, it can't process
- -- our incoming --remote calls.
- clear()
- -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
- eq({ 2 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], bogus_argv))
+ local p = n.spawn_wait { args = { ... } }
+ eq(2, p.status)
end
it('bogus subcommand', function()
run_and_check_exit_code('--remote-bogus')
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 76b0755441..8ecd3dca97 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -77,22 +77,9 @@ describe('startup', function()
end)
it('--startuptime does not crash on error #31125', function()
- eq(
- "E484: Can't open file .",
- fn.system({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--startuptime',
- '.',
- '-c',
- '42cquit',
- })
- )
- eq(42, api.nvim_get_vvar('shell_error'))
+ local p = n.spawn_wait('--startuptime', '.', '-c', '42cquit')
+ eq("E484: Can't open file .", p.stderr)
+ eq(42, p.status)
end)
it('-D does not hang #12647', function()
@@ -149,13 +136,18 @@ describe('startup', function()
vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') })
vim.list_extend(args, lua_args or {})
local out = fn.system(args, input):gsub('\r\n', '\n')
- return eq(dedent(expected), out)
+ if type(expected) == 'function' then
+ return expected(out)
+ else
+ return eq(dedent(expected), out)
+ end
end
it('failure modes', function()
-- nvim -l <empty>
- matches('nvim%.?e?x?e?: Argument missing after: "%-l"', fn.system({ nvim_prog, '-l' }))
- eq(1, eval('v:shell_error'))
+ local proc = n.spawn_wait('-l')
+ matches('nvim%.?e?x?e?: Argument missing after: "%-l"', proc.stderr)
+ eq(1, proc.status)
end)
it('os.exit() sets Nvim exitcode', function()
@@ -182,13 +174,13 @@ describe('startup', function()
end)
it('Lua-error sets Nvim exitcode', function()
+ local proc = n.spawn_wait('-l', 'test/functional/fixtures/startup-fail.lua')
+ matches('E5113: .* my pearls!!', proc:output())
+ eq(0, proc.signal)
+ eq(1, proc.status)
+
eq(0, eval('v:shell_error'))
matches(
- 'E5113: .* my pearls!!',
- fn.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' })
- )
- eq(1, eval('v:shell_error'))
- matches(
'E5113: .* %[string "error%("whoa"%)"%]:1: whoa',
fn.system({ nvim_prog, '-l', '-' }, 'error("whoa")')
)
@@ -295,14 +287,30 @@ describe('startup', function()
eq(0, eval('v:shell_error'))
end)
- it('disables swapfile/shada/config/plugins', function()
+ it('disables swapfile/shada/config/plugins unless overridden', function()
+ local script = [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format(
+ vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]
+ finally(function()
+ os.remove('Xtest_shada')
+ end)
+
assert_l_out(
'updatecount=0 shadafile=NONE loadplugins=false scripts=1\n',
nil,
nil,
'-',
- [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format(
- vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]
+ script
+ )
+
+ -- User can override.
+ assert_l_out(
+ function(out)
+ return matches('updatecount=99 shadafile=Xtest_shada loadplugins=true scripts=2%d\n', out)
+ end,
+ { '+set updatecount=99', '-i', 'Xtest_shada', '+set loadplugins', '-u', 'NORC' },
+ nil,
+ '-',
+ script
)
end)
end)
@@ -585,19 +593,21 @@ describe('startup', function()
eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, { 'set encoding', '' }))
end)
- it('-es/-Es disables swapfile, user config #8540', function()
+ it('-es/-Es disables swapfile/shada/config #8540', function()
for _, arg in ipairs({ '-es', '-Es' }) do
local out = fn.system({
nvim_prog,
arg,
- '+set swapfile? updatecount? shadafile?',
+ '+set updatecount? shadafile? loadplugins?',
'+put =map(getscriptinfo(), {-> v:val.name})',
'+%print',
})
local line1 = string.match(out, '^.-\n')
-- updatecount=0 means swapfile was disabled.
- eq(' swapfile updatecount=0 shadafile=\n', line1)
- -- Standard plugins were loaded, but not user config.
+ eq(' updatecount=0 shadafile=NONE loadplugins\n', line1)
+ -- Standard plugins were loaded, but not user config. #31878
+ local nrlines = #vim.split(out, '\n')
+ ok(nrlines > 20, '>20', nrlines)
ok(string.find(out, 'man.lua') ~= nil)
ok(string.find(out, 'init.vim') == nil)
end
@@ -606,15 +616,15 @@ describe('startup', function()
it('fails on --embed with -es/-Es/-l', function()
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-es' })
+ n.spawn_wait('--embed', '-es').stderr
)
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-Es' })
+ n.spawn_wait('--embed', '-Es').stderr
)
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-l', 'foo.lua' })
+ n.spawn_wait('--embed', '-l', 'foo.lua').stderr
)
end)
@@ -698,20 +708,8 @@ describe('startup', function()
end)
it('get command line arguments from v:argv', function()
- local out = fn.system({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- nvim_set,
- '-c',
- [[echo v:argv[-1:] len(v:argv) > 1]],
- '+q',
- })
- eq("['+q'] 1", out)
+ local p = n.spawn_wait('--cmd', nvim_set, '-c', [[echo v:argv[-1:] len(v:argv) > 1]], '+q')
+ eq("['+q'] 1", p.stderr)
end)
end)
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index c20d925713..7c68de272b 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -10,6 +10,7 @@ local fn = n.fn
local command = n.command
local api = n.api
local poke_eventloop = n.poke_eventloop
+local exec_lua = n.exec_lua
describe('completion', function()
local screen
@@ -1326,4 +1327,29 @@ describe('completion', function()
]],
})
end)
+
+ describe('nvim__complete_set', function()
+ it("fails when 'completeopt' does not include popup", function()
+ exec_lua([[
+ function _G.omni_test(findstart, base)
+ if findstart == 1 then
+ return vim.fn.col('.') - 1
+ end
+ return { { word = 'one' } }
+ end
+ vim.api.nvim_create_autocmd('CompleteChanged', {
+ callback = function()
+ local ok, err = pcall(vim.api.nvim__complete_set, 0, { info = '1info' })
+ if not ok then
+ vim.g.err_msg = err
+ end
+ end,
+ })
+ vim.opt.completeopt = 'menu,menuone'
+ vim.opt.omnifunc = 'v:lua.omni_test'
+ ]])
+ feed('S<C-X><C-O>')
+ eq('completeopt option does not include popup', api.nvim_get_var('err_msg'))
+ end)
+ end)
end)
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 08f7663075..d1f598a9d8 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -12,12 +12,10 @@ local fn = n.fn
local nvim_prog = n.nvim_prog
local ok = t.ok
local rmdir = n.rmdir
-local new_argv = n.new_argv
local new_pipename = n.new_pipename
local pesc = vim.pesc
local os_kill = n.os_kill
local set_session = n.set_session
-local spawn = n.spawn
local async_meths = n.async_meths
local expect_msg_seq = n.expect_msg_seq
local pcall_err = t.pcall_err
@@ -56,7 +54,7 @@ describe("preserve and (R)ecover with custom 'directory'", function()
local nvim0
before_each(function()
- nvim0 = spawn(new_argv())
+ nvim0 = n.new_session(false)
set_session(nvim0)
rmdir(swapdir)
mkdir(swapdir)
@@ -76,7 +74,8 @@ describe("preserve and (R)ecover with custom 'directory'", function()
local function test_recover(swappath1)
-- Start another Nvim instance.
- local nvim2 = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed' }, true)
+ local nvim2 =
+ n.new_session(false, { args = { '-u', 'NONE', '-i', 'NONE', '--embed' }, merge = false })
set_session(nvim2)
exec(init)
@@ -141,7 +140,7 @@ describe('swapfile detection', function()
set swapfile fileformat=unix nomodified undolevels=-1 nohidden
]]
before_each(function()
- nvim0 = spawn(new_argv())
+ nvim0 = n.new_session(false)
set_session(nvim0)
rmdir(swapdir)
mkdir(swapdir)
@@ -168,7 +167,8 @@ describe('swapfile detection', function()
command('preserve')
-- Start another Nvim instance.
- local nvim2 = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed' }, true, nil, true)
+ local nvim2 =
+ n.new_session(true, { args = { '-u', 'NONE', '-i', 'NONE', '--embed' }, merge = false })
set_session(nvim2)
local screen2 = Screen.new(256, 40)
screen2._default_attr_ids = nil
@@ -251,7 +251,7 @@ describe('swapfile detection', function()
command('preserve') -- Make sure the swap file exists.
local nvimpid = fn.getpid()
- local nvim1 = spawn(new_argv(), true, nil, true)
+ local nvim1 = n.new_session(true)
set_session(nvim1)
local screen = Screen.new(75, 18)
exec(init)
@@ -273,7 +273,7 @@ describe('swapfile detection', function()
[1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg
})
- local nvim1 = spawn(new_argv(), true, nil, true)
+ local nvim1 = n.new_session(true)
set_session(nvim1)
screen:attach()
exec(init)
@@ -292,7 +292,7 @@ describe('swapfile detection', function()
]])
nvim1:close()
- local nvim2 = spawn(new_argv(), true, nil, true)
+ local nvim2 = n.new_session(true)
set_session(nvim2)
screen:attach()
exec(init)
diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua
index 2299f33f06..9b81c6d06d 100644
--- a/test/functional/ex_cmds/wundo_spec.lua
+++ b/test/functional/ex_cmds/wundo_spec.lua
@@ -5,8 +5,6 @@ local n = require('test.functional.testnvim')()
local command = n.command
local clear = n.clear
local eval = n.eval
-local spawn = n.spawn
-local nvim_prog = n.nvim_prog
local set_session = n.set_session
describe(':wundo', function()
@@ -24,15 +22,11 @@ end)
describe('u_* functions', function()
it('safely fail on new, non-empty buffer', function()
- local session = spawn({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--embed',
- '-c',
- 'set undodir=. undofile',
+ local session = n.new_session(false, {
+ args = {
+ '-c',
+ 'set undodir=. undofile',
+ },
})
set_session(session)
command('echo "True"') -- Should not error out due to crashed Neovim
diff --git a/test/functional/func/memoize_spec.lua b/test/functional/func/memoize_spec.lua
new file mode 100644
index 0000000000..ca518ab88d
--- /dev/null
+++ b/test/functional/func/memoize_spec.lua
@@ -0,0 +1,142 @@
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+local clear = n.clear
+local exec_lua = n.exec_lua
+local eq = t.eq
+
+describe('vim.func._memoize', function()
+ before_each(clear)
+
+ it('caches function results based on their parameters', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(1, exec_lua([[return _G.count]]))
+ end)
+
+ it('caches function results using a weak table by default', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat-2', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ ]])
+
+ eq(3, exec_lua([[return _G.count]]))
+ end)
+
+ it('can cache using a strong table', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat-2', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end, false)
+
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ ]])
+
+ eq(1, exec_lua([[return _G.count]]))
+ end)
+
+ it('can clear a single cache entry', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize(function(arg1, arg2)
+ return tostring(arg1) .. '%%' .. tostring(arg2)
+ end, function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder:clear(3, -4)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(2, exec_lua([[return _G.count]]))
+ end)
+
+ it('can clear the entire cache', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize(function(arg1, arg2)
+ return tostring(arg1) .. '%%' .. tostring(arg2)
+ end, function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(1, 2)
+ adder(3, -4)
+ adder(1, 2)
+ adder(3, -4)
+ adder(1, 2)
+ adder(3, -4)
+ adder:clear()
+ adder(1, 2)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(4, exec_lua([[return _G.count]]))
+ end)
+
+ it('can cache functions that return nil', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return nil
+ end)
+
+ collectgarbage('stop')
+ adder(1, 2)
+ adder(1, 2)
+ adder(1, 2)
+ adder(1, 2)
+ adder:clear()
+ adder(1, 2)
+ collectgarbage('restart')
+ ]])
+
+ eq(2, exec_lua([[return _G.count]]))
+ end)
+end)
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index 3addcb957c..819b40323a 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -159,6 +159,50 @@ describe('cmdline', function()
endfunc
]])
+ feed(':resize -3<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*2
+ {3:[No Name] }|
+ |*4
+ ]])
+
+ -- :resize now also changes 'cmdheight' accordingly
+ feed(':set cmdheight+=1<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {3:[No Name] }|
+ |*5
+ ]])
+
+ -- using more space moves the status line up
+ feed(':set cmdheight+=1<CR>')
+ screen:expect([[
+ ^ |
+ {3:[No Name] }|
+ |*6
+ ]])
+
+ -- reducing cmdheight moves status line down
+ feed(':set cmdheight-=3<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*3
+ {3:[No Name] }|
+ |*3
+ ]])
+
+ -- reducing window size and then setting cmdheight
+ feed(':resize -1<CR>')
+ feed(':set cmdheight=1<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:[No Name] }|
+ |
+ ]])
+
-- setting 'cmdheight' works after outputting two messages
feed(':call EchoTwo()')
screen:expect([[
@@ -185,6 +229,16 @@ describe('cmdline', function()
bar |
{6:Press ENTER or type command to continue}^ |
]])
+
+ -- window commands do not reduce 'cmdheight' to value lower than :set by user
+ feed('<CR>:wincmd _<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ {3:[No Name] }|
+ :wincmd _ |
+ |
+ ]])
end)
-- oldtest: Test_cmdheight_tabline()
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index adf75c2836..db5e3f6857 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -82,7 +82,7 @@ describe('messages', function()
NoSuchFil^e |
three |
{1:~ }|*5
- from DebugSilent visual |
+ |
{9:E447: Can't find file "NoSuchFile" in path} |
]])
end)
diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua
index b58bf0bf43..fac982354c 100644
--- a/test/functional/legacy/window_cmd_spec.lua
+++ b/test/functional/legacy/window_cmd_spec.lua
@@ -299,7 +299,7 @@ describe('splitkeep', function()
c |
{1:~ }|
{3:[No Name] }|
- |
+ :call win_move_statusline(win, 1) |
]])
end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index 1c6ff5ac6d..218f9bbc46 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -340,6 +340,9 @@ describe('vim.fs', function()
end)
describe('normalize()', function()
+ -- local function vim.fs.normalize(path, opts)
+ -- return exec_lua([[return vim.fs.vim.fs.normalize(...)]], path, opts)
+ -- end
it('removes trailing /', function()
eq('/home/user', vim.fs.normalize('/home/user/'))
end)
@@ -373,6 +376,29 @@ describe('vim.fs', function()
eq('/foo/bar', vim.fs.normalize('/foo//bar////', posix_opts))
end)
+ it('normalizes drive letter', function()
+ eq('C:/', vim.fs.normalize('C:/', win_opts))
+ eq('C:/', vim.fs.normalize('c:/', win_opts))
+ eq('D:/', vim.fs.normalize('d:/', win_opts))
+ eq('C:', vim.fs.normalize('C:', win_opts))
+ eq('C:', vim.fs.normalize('c:', win_opts))
+ eq('D:', vim.fs.normalize('d:', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('C:/foo/test/', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('c:/foo/test/', win_opts))
+ eq('D:foo/test', vim.fs.normalize('D:foo/test/', win_opts))
+ eq('D:foo/test', vim.fs.normalize('d:foo/test/', win_opts))
+ end)
+
+ it('does not change case on paths, see #31833', function()
+ eq('TEST', vim.fs.normalize('TEST', win_opts))
+ eq('test', vim.fs.normalize('test', win_opts))
+ eq('C:/FOO/test', vim.fs.normalize('C:/FOO/test', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('C:/foo/test', win_opts))
+ eq('//SERVER/SHARE/FOO/BAR', vim.fs.normalize('//SERVER/SHARE/FOO/BAR', win_opts))
+ eq('//server/share/foo/bar', vim.fs.normalize('//server/share/foo/bar', win_opts))
+ eq('C:/FOO/test', vim.fs.normalize('c:/FOO/test', win_opts))
+ end)
+
it('allows backslashes on unix-based os', function()
eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world', posix_opts))
end)
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index 27640d6066..af6b2ceac3 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -106,20 +106,15 @@ describe('vim.ui_attach', function()
end)
it('does not crash on exit', function()
- fn.system({
- n.nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ local p = n.spawn_wait(
'--cmd',
[[ lua ns = vim.api.nvim_create_namespace 'testspace' ]],
'--cmd',
[[ lua vim.ui_attach(ns, {ext_popupmenu=true}, function() end) ]],
'--cmd',
- 'quitall!',
- })
- eq(0, n.eval('v:shell_error'))
+ 'quitall!'
+ )
+ eq(0, p.status)
end)
it('can receive accurate message kinds even if they are history', function()
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index e7f47ef4e9..a82279e775 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -14,7 +14,6 @@ local api = n.api
local command = n.command
local clear = n.clear
local exc_exec = n.exc_exec
-local exec_lua = n.exec_lua
local eval = n.eval
local eq = t.eq
local ok = t.ok
@@ -929,17 +928,12 @@ describe('stdpath()', function()
assert_alive() -- Check for crash. #8393
-- Check that Nvim rejects invalid APPNAMEs
- -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
local function test_appname(testAppname, expected_exitcode)
- local lua_code = string.format(
- [[
- local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '--listen', 'x', '+qall!' }, { env = { NVIM_APPNAME = %q } })
- return vim.fn.jobwait({ child }, %d)[1]
- ]],
- testAppname,
- 3000
- )
- eq(expected_exitcode, exec_lua(lua_code))
+ local p = n.spawn_wait({
+ args = { '--listen', 'x', '+qall!' },
+ env = { NVIM_APPNAME = testAppname },
+ })
+ eq(expected_exitcode, p.status)
end
-- Invalid appnames:
test_appname('a/../b', 1)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 753da64522..406b5c3c16 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -66,6 +66,18 @@ describe(':checkhealth', function()
eq({}, getcompletion('', 'checkhealth'))
assert_alive()
end)
+
+ it('vim.g.health', function()
+ clear()
+ command("let g:health = {'style':'float'}")
+ command('checkhealth lsp')
+ eq(
+ 'editor',
+ exec_lua([[
+ return vim.api.nvim_win_get_config(0).relative
+ ]])
+ )
+ end)
end)
describe('vim.health', function()
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index ce6e6b2535..1e3e759e0b 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -301,4 +301,32 @@ describe('vim.lsp.util', function()
end)
end)
end)
+
+ it('open_floating_preview zindex greater than current window', function()
+ local screen = Screen.new()
+ exec_lua(function()
+ vim.api.nvim_open_win(0, true, {
+ relative = 'editor',
+ border = 'single',
+ height = 11,
+ width = 51,
+ row = 2,
+ col = 2,
+ })
+ vim.keymap.set('n', 'K', function()
+ vim.lsp.util.open_floating_preview({ 'foo' }, '', { border = 'single' })
+ end, {})
+ end)
+ feed('K')
+ screen:expect([[
+ ┌───────────────────────────────────────────────────┐|
+ │{4:^ }│|
+ │┌───┐{11: }│|
+ ││{4:foo}│{11: }│|
+ │└───┘{11: }│|
+ │{11:~ }│|*7
+ └───────────────────────────────────────────────────┘|
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index d680f0b83b..837978dee1 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -218,7 +218,7 @@ describe('ShaDa support code', function()
-- -c temporary sets lnum to zero to make `+/pat` work, so calling setpcmark()
-- during -c used to add item with zero lnum to jump list.
it('does not create incorrect file for non-existent buffers when writing from -c', function()
- local argv = n.new_argv {
+ local p = n.spawn_wait {
args_rm = {
'-i',
'--embed', -- no --embed
@@ -232,12 +232,13 @@ describe('ShaDa support code', function()
'qall',
},
}
- eq('', fn.system(argv))
+ eq('', p:output())
+ eq(0, p.status)
eq(0, exc_exec('rshada'))
end)
it('does not create incorrect file for non-existent buffers opened from -c', function()
- local argv = n.new_argv {
+ local p = n.spawn_wait {
args_rm = {
'-i',
'--embed', -- no --embed
@@ -251,7 +252,8 @@ describe('ShaDa support code', function()
'autocmd VimEnter * qall',
},
}
- eq('', fn.system(argv))
+ eq('', p:output())
+ eq(0, p.status)
eq(0, exc_exec('rshada'))
end)
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index 5debdc6c77..78fe19b984 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -6,8 +6,7 @@ local uv = vim.uv
local paths = t.paths
local api, nvim_command, fn, eq = n.api, n.command, n.fn, t.eq
-local write_file, spawn, set_session, nvim_prog, exc_exec =
- t.write_file, n.spawn, n.set_session, n.nvim_prog, n.exc_exec
+local write_file, set_session, exc_exec = t.write_file, n.set_session, n.exc_exec
local is_os = t.is_os
local skip = t.skip
@@ -254,7 +253,7 @@ describe('ShaDa support code', function()
it('does not crash when ShaDa file directory is not writable', function()
skip(is_os('win'))
- fn.mkdir(dirname, '', 0)
+ fn.mkdir(dirname, '', '0')
eq(0, fn.filewritable(dirname))
reset { shadafile = dirshada, args = { '--cmd', 'set shada=' } }
api.nvim_set_option_value('shada', "'10", {})
@@ -270,10 +269,10 @@ end)
describe('ShaDa support code', function()
it('does not write NONE file', function()
- local session = spawn(
- { nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--headless', '--cmd', 'qall' },
- true
- )
+ local session = n.new_session(false, {
+ merge = false,
+ args = { '-u', 'NONE', '-i', 'NONE', '--embed', '--headless', '--cmd', 'qall' },
+ })
session:close()
eq(nil, uv.fs_stat('NONE'))
eq(nil, uv.fs_stat('NONE.tmp.a'))
@@ -281,7 +280,10 @@ describe('ShaDa support code', function()
it('does not read NONE file', function()
write_file('NONE', '\005\001\015\131\161na\162rX\194\162rc\145\196\001-')
- local session = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--headless' }, true)
+ local session = n.new_session(
+ false,
+ { merge = false, args = { '-u', 'NONE', '-i', 'NONE', '--embed', '--headless' } }
+ )
set_session(session)
eq('', fn.getreg('a'))
session:close()
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index b6de687af9..cc807ba555 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -435,6 +435,19 @@ describe(':terminal buffer', function()
]])
end)
+ it('handles unprintable chars', function()
+ local screen = Screen.new(50, 7)
+ feed 'i'
+ local chan = api.nvim_open_term(0, {})
+ api.nvim_chan_send(chan, '\239\187\191') -- '\xef\xbb\xbf'
+ screen:expect([[
+ {18:<feff>}^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ eq('\239\187\191', api.nvim_get_current_line())
+ end)
+
it("handles bell respecting 'belloff' and 'visualbell'", function()
local screen = Screen.new(50, 7)
local chan = api.nvim_open_term(0, {})
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index c43d139f70..0afbd010f7 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -6,7 +6,6 @@ local tt = require('test.functional.testterm')
local feed, clear = n.feed, n.clear
local api = n.api
local testprg, command = n.testprg, n.command
-local nvim_prog_abs = n.nvim_prog_abs
local fn = n.fn
local nvim_set = n.nvim_set
local is_os = t.is_os
@@ -151,7 +150,7 @@ it(':terminal highlight has lower precedence than editor #9964', function()
})
-- Child nvim process in :terminal (with cterm colors).
fn.jobstart({
- nvim_prog_abs(),
+ n.nvim_prog,
'-n',
'-u',
'NORC',
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index f9145f9b63..3624a7bc2b 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -26,7 +26,6 @@ local api = n.api
local is_ci = t.is_ci
local is_os = t.is_os
local new_pipename = n.new_pipename
-local spawn_argv = n.spawn_argv
local set_session = n.set_session
local write_file = t.write_file
local eval = n.eval
@@ -3320,8 +3319,8 @@ describe('TUI as a client', function()
end)
it('connects to remote instance (with its own TUI)', function()
- local server_super = spawn_argv(false) -- equivalent to clear()
- local client_super = spawn_argv(true)
+ local server_super = n.new_session(false)
+ local client_super = n.new_session(true)
set_session(server_super)
local server_pipe = new_pipename()
@@ -3395,8 +3394,8 @@ describe('TUI as a client', function()
end)
it('connects to remote instance (--headless)', function()
- local server = spawn_argv(false) -- equivalent to clear()
- local client_super = spawn_argv(true, { env = { NVIM_LOG_FILE = testlog } })
+ local server = n.new_session(false)
+ local client_super = n.new_session(true, { env = { NVIM_LOG_FILE = testlog } })
set_session(server)
local server_pipe = api.nvim_get_vvar('servername')
@@ -3462,8 +3461,8 @@ describe('TUI as a client', function()
end)
local function test_remote_tui_quit(status)
- local server_super = spawn_argv(false) -- equivalent to clear()
- local client_super = spawn_argv(true)
+ local server_super = n.new_session(false)
+ local client_super = n.new_session(true)
set_session(server_super)
local server_pipe = new_pipename()
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
index 675ad9e3d7..59cb593cf7 100644
--- a/test/functional/testnvim.lua
+++ b/test/functional/testnvim.lua
@@ -4,7 +4,7 @@ local t = require('test.testutil')
local Session = require('test.client.session')
local uv_stream = require('test.client.uv_stream')
local SocketStream = uv_stream.SocketStream
-local ChildProcessStream = uv_stream.ChildProcessStream
+local ProcStream = uv_stream.ProcStream
local check_cores = t.check_cores
local check_logs = t.check_logs
@@ -318,24 +318,14 @@ function M.stop()
assert(session):stop()
end
-function M.nvim_prog_abs()
- -- system(['build/bin/nvim']) does not work for whatever reason. It must
- -- be executable searched in $PATH or something starting with / or ./.
- if M.nvim_prog:match('[/\\]') then
- return M.request('nvim_call_function', 'fnamemodify', { M.nvim_prog, ':p' })
- else
- return M.nvim_prog
- end
-end
-
-- Use for commands which expect nvim to quit.
-- The first argument can also be a timeout.
function M.expect_exit(fn_or_timeout, ...)
local eof_err_msg = 'EOF was received from Nvim. Likely the Nvim process crashed.'
if type(fn_or_timeout) == 'function' then
- eq(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
+ t.matches(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
else
- eq(
+ t.matches(
eof_err_msg,
t.pcall_err(function(timeout, fn, ...)
fn(...)
@@ -465,22 +455,6 @@ function M.check_close()
session = nil
end
---- @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 M.spawn(argv, merge, env, keep, io_extra)
- if not keep then
- M.check_close()
- end
-
- local child_stream =
- ChildProcessStream.spawn(merge and M.merge_args(prepend_argv, argv) or argv, env, io_extra)
- return Session.new(child_stream)
-end
-
-- Creates a new Session connected by domain socket (named pipe) or TCP.
function M.connect(file_or_address)
local addr, port = string.match(file_or_address, '(.*):(%d+)')
@@ -489,61 +463,112 @@ function M.connect(file_or_address)
return Session.new(stream)
end
--- Starts (and returns) a new global Nvim session.
---
--- Parameters are interpreted as startup args, OR a map with these keys:
--- args: List: Args appended to the default `nvim_argv` set.
--- args_rm: List: Args removed from the default set. All cases are
--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
--- (and its value) from the default set.
--- env: Map: Defines the environment of the new session.
---
--- Example:
--- clear('-e')
--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- Starts a new, global Nvim session and clears the current one.
+---
+--- Note: Use `new_session()` to start a session without replacing the current one.
+---
+--- Parameters are interpreted as startup args, OR a map with these keys:
+--- - args: List: Args appended to the default `nvim_argv` set.
+--- - args_rm: List: Args removed from the default set. All cases are
+--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
+--- (and its value) from the default set.
+--- - env: Map: Defines the environment of the new session.
+---
+--- Example:
+--- ```
+--- clear('-e')
+--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- ```
+---
+--- @param ... string Nvim CLI args
+--- @return test.Session
+--- @overload fun(opts: test.session.Opts): test.Session
function M.clear(...)
- M.set_session(M.spawn_argv(false, ...))
+ M.set_session(M.new_session(false, ...))
return M.get_session()
end
---- same params as clear, but does returns the session instead
---- of replacing the default session
+--- Starts a new Nvim process with the given args and returns a msgpack-RPC session.
+---
+--- Does not replace the current global session, unlike `clear()`.
+---
+--- @param keep boolean (default: false) Don't close the current global session.
+--- @param ... string Nvim CLI args (or see overload)
--- @return test.Session
-function M.spawn_argv(keep, ...)
- local argv, env, io_extra = M.new_argv(...)
- return M.spawn(argv, nil, env, keep, io_extra)
+--- @overload fun(keep: boolean, opts: test.session.Opts): test.Session
+function M.new_session(keep, ...)
+ if not keep then
+ M.check_close()
+ end
+
+ local argv, env, io_extra = M._new_argv(...)
+
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ return Session.new(proc)
end
---- @class test.new_argv.Opts
+--- Starts a (non-RPC, `--headless --listen "Tx"`) Nvim process, waits for exit, and returns result.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
+--- @return test.ProcStream
+--- @overload fun(opts: test.session.Opts): test.ProcStream
+function M.spawn_wait(...)
+ local opts = type(...) == 'string' and { args = { ... } } or ...
+ opts.args_rm = opts.args_rm and opts.args_rm or {}
+ table.insert(opts.args_rm, '--embed')
+ local argv, env, io_extra = M._new_argv(opts)
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ proc.collect_text = true
+ proc:read_start()
+ proc:wait()
+ proc:close()
+ return proc
+end
+
+--- @class test.session.Opts
+--- Nvim CLI args
--- @field args? string[]
+--- Remove these args from the default `nvim_argv` args set. Ignored if `merge=false`.
--- @field args_rm? string[]
+--- (default: true) Merge `args` with the default set. Else use only the provided `args`.
+--- @field merge? boolean
+--- Environment variables
--- @field env? table<string,string>
+--- Used for stdin_fd, see `:help ui-option`
--- @field io_extra? uv.uv_pipe_t
---- Builds an argument list for use in clear().
+--- @private
---
---- @see clear() for parameters.
---- @param ... string
+--- Builds an argument list for use in `new_session()`, `clear()`, and `spawn_wait()`.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
--- @return string[]
--- @return string[]?
--- @return uv.uv_pipe_t?
-function M.new_argv(...)
- local args = { unpack(M.nvim_argv) }
- table.insert(args, '--headless')
- if _G._nvim_test_id then
- -- Set the server name to the test-id for logging. #8519
- table.insert(args, '--listen')
- table.insert(args, _G._nvim_test_id)
+--- @overload fun(opts: test.session.Opts): string[], string[]?, uv.uv_pipe_t?
+function M._new_argv(...)
+ --- @type test.session.Opts|string
+ local opts = select(1, ...)
+ local merge = type(opts) ~= 'table' and true or opts.merge ~= false
+
+ local args = merge and { unpack(M.nvim_argv) } or { M.nvim_prog }
+ if merge then
+ table.insert(args, '--headless')
+ if _G._nvim_test_id then
+ -- Set the server name to the test-id for logging. #8519
+ table.insert(args, '--listen')
+ table.insert(args, _G._nvim_test_id)
+ end
end
+
local new_args --- @type string[]
local io_extra --- @type uv.uv_pipe_t?
- local env --- @type string[]?
- --- @type test.new_argv.Opts|string
- local opts = select(1, ...)
+ local env --- @type string[]? List of "key=value" env vars.
+
if type(opts) ~= 'table' then
new_args = { ... }
else
- args = remove_args(args, opts.args_rm)
+ args = merge and remove_args(args, opts.args_rm) or args
if opts.env then
local env_opt = {} --- @type table<string,string>
for k, v in pairs(opts.env) do
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
index e38e58ff92..9f7fdf529f 100644
--- a/test/functional/treesitter/fold_spec.lua
+++ b/test/functional/treesitter/fold_spec.lua
@@ -5,6 +5,7 @@ local Screen = require('test.functional.ui.screen')
local clear = n.clear
local eq = t.eq
local insert = n.insert
+local write_file = t.write_file
local exec_lua = n.exec_lua
local command = n.command
local feed = n.feed
@@ -767,4 +768,78 @@ t2]])
]],
}
end)
+
+ it("doesn't call get_parser too often when parser is not available", function()
+ -- spy on vim.treesitter.get_parser() to keep track of how many times it is called
+ exec_lua(function()
+ _G.count = 0
+ vim.treesitter.get_parser = (function(wrapped)
+ return function(...)
+ _G.count = _G.count + 1
+ return wrapped(...)
+ end
+ end)(vim.treesitter.get_parser)
+ end)
+
+ insert(test_text)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ eq(
+ 1,
+ exec_lua [[ return _G.count ]],
+ 'count should not be as high as the # of lines; actually only once for the buffer.'
+ )
+ end)
+
+ it('can detect a new parser and refresh folds accordingly', function()
+ write_file('test_fold_file.txt', test_text)
+ command [[
+ e test_fold_file.txt
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ -- reload buffer as c filetype to simulate new parser being found
+ feed('GA// vim: ft=c<Esc>')
+ command([[w | e]])
+
+ eq({
+ [1] = '>1',
+ [2] = '1',
+ [3] = '1',
+ [4] = '1',
+ [5] = '>2',
+ [6] = '2',
+ [7] = '2',
+ [8] = '1',
+ [9] = '1',
+ [10] = '>2',
+ [11] = '2',
+ [12] = '2',
+ [13] = '2',
+ [14] = '2',
+ [15] = '>3',
+ [16] = '3',
+ [17] = '3',
+ [18] = '2',
+ [19] = '1',
+ }, get_fold_levels())
+ end)
end)
diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua
index 1f7d15cc96..47f3421cfe 100644
--- a/test/functional/treesitter/inspect_tree_spec.lua
+++ b/test/functional/treesitter/inspect_tree_spec.lua
@@ -120,14 +120,17 @@ describe('vim.treesitter.inspect_tree', function()
end)
it('updates source and tree buffer windows and closes them correctly', function()
+ local name = t.tmpname()
+ n.command('edit ' .. name)
insert([[
print()
]])
+ n.command('set filetype=lua | write')
-- setup two windows for the source buffer
exec_lua(function()
_G.source_win = vim.api.nvim_get_current_win()
- vim.api.nvim_open_win(0, false, {
+ _G.source_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
@@ -135,40 +138,44 @@ describe('vim.treesitter.inspect_tree', function()
-- setup three windows for the tree buffer
exec_lua(function()
- vim.treesitter.start(0, 'lua')
vim.treesitter.inspect_tree()
_G.tree_win = vim.api.nvim_get_current_win()
- _G.tree_win_copy_1 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
- _G.tree_win_copy_2 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win3 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
end)
- -- close original source window
- exec_lua('vim.api.nvim_win_close(source_win, false)')
+ -- close original source window without closing tree views
+ exec_lua('vim.api.nvim_set_current_win(source_win)')
+ feed(':quit<CR>')
+ eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win2)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win3)'))
-- navigates correctly to the remaining source buffer window
+ exec_lua('vim.api.nvim_set_current_win(tree_win)')
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close original tree window
exec_lua(function()
- vim.api.nvim_set_current_win(_G.tree_win_copy_1)
+ vim.api.nvim_set_current_win(_G.tree_win2)
vim.api.nvim_win_close(_G.tree_win, false)
end)
-- navigates correctly to the remaining source buffer window
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close source buffer window and all remaining tree windows
- t.pcall_err(exec_lua, 'vim.api.nvim_win_close(0, false)')
-
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_1)'))
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_2)'))
+ n.expect_exit(n.command, 'quit')
end)
end)
diff --git a/test/functional/treesitter/query_spec.lua b/test/functional/treesitter/query_spec.lua
index 634f8af83d..6e21ed1d99 100644
--- a/test/functional/treesitter/query_spec.lua
+++ b/test/functional/treesitter/query_spec.lua
@@ -835,9 +835,9 @@ void ui_refresh(void)
local result = exec_lua(function()
local query0 = vim.treesitter.query.parse('c', query)
- local match_preds = query0.match_preds
+ local match_preds = query0._match_predicates
local called = 0
- function query0:match_preds(...)
+ function query0:_match_predicates(...)
called = called + 1
return match_preds(self, ...)
end
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 93ea2b9186..a2722a4139 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -1038,6 +1038,18 @@ describe('cmdline redraw', function()
]],
}
end)
+
+ it('silent prompt', function()
+ command([[nmap <silent> T :call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
+ feed('T')
+ screen:expect([[
+ |
+ {3: }|
+ |
+ {6:Save changes?} |
+ {6:[Y]es, (N)o, (C)ancel: }^ |
+ ]])
+ end)
end)
describe('statusline is redrawn on entering cmdline', function()
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 1e51652c4f..77ffc475b0 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -1121,7 +1121,7 @@ stack traceback:
]],
messages = {
{
- content = { { 'wildmenu wildmode' } },
+ content = { { 'wildmenu wildmode\n' } },
history = false,
kind = 'wildlist',
},
@@ -1335,6 +1335,21 @@ stack traceback:
feed('i')
n.assert_alive()
end)
+
+ it(':digraph contains newlines', function()
+ command('digraph')
+ screen:expect({
+ condition = function()
+ local nl = 0
+ eq('list_cmd', screen.messages[1].kind)
+ for _, chunk in ipairs(screen.messages[1].content) do
+ nl = nl + (chunk[2]:find('\n') and 1 or 0)
+ end
+ eq(682, nl)
+ screen.messages = {}
+ end,
+ })
+ end)
end)
describe('ui/builtin messages', function()
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index d1228d3607..60d59190ce 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -1680,7 +1680,7 @@ describe('builtin popupmenu', function()
end)
end
- describe('floating window preview #popup', function()
+ describe('floating window preview popup', function()
it('pum popup preview', function()
--row must > 10
screen:try_resize(40, 11)
@@ -1693,14 +1693,29 @@ describe('builtin popupmenu', function()
endfunc
set omnifunc=Omni_test
set completeopt=menu,popup
-
funct Set_info()
let comp_info = complete_info()
if comp_info['selected'] == 2
call nvim__complete_set(comp_info['selected'], {"info": "3info"})
endif
endfunc
- autocmd CompleteChanged * call Set_info()
+ funct TsHl()
+ let comp_info = complete_info()
+ if get(comp_info, 'previewbufnr', 0) > 0
+ call v:lua.vim.treesitter.start(comp_info['preview_bufnr'], 'markdown')
+ endif
+ if comp_info['selected'] == 0
+ call nvim__complete_set(comp_info['selected'], {"info": "```lua\nfunction test()\n print('foo')\nend\n```"})
+ endif
+ endfunc
+ augroup Group
+ au!
+ autocmd CompleteChanged * :call Set_info()
+ augroup END
+ funct TestTs()
+ autocmd! Group
+ autocmd CompleteChanged * call TsHl()
+ endfunc
]])
feed('Gi<C-x><C-o>')
--floating preview in right
@@ -2004,6 +2019,90 @@ describe('builtin popupmenu', function()
]],
}
end
+ feed('<C-E><Esc>')
+
+ -- works when scroll with treesitter highlight
+ command('call TestTs()')
+ feed('S<C-x><C-o>')
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ one^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 5
+ {s:one }|
+ {n:two }|
+ {n:looooooooooooooong }|
+ ## grid 9
+ {n:```lua }|
+ {n:function test()}|
+ {n: print('foo') }|
+ {n:end }|
+ {n:``` }|
+ {n: }|
+ ]],
+ float_pos = {
+ [5] = { -1, 'NW', 2, 1, 0, false, 100 },
+ [9] = { 1005, 'NW', 1, 1, 19, false, 50 },
+ },
+ win_viewport = {
+ [2] = {
+ win = 1000,
+ topline = 0,
+ botline = 2,
+ curline = 0,
+ curcol = 3,
+ linecount = 1,
+ sum_scroll_delta = 0,
+ },
+ [9] = {
+ win = 1005,
+ topline = 0,
+ botline = 6,
+ curline = 0,
+ curcol = 0,
+ linecount = 5,
+ sum_scroll_delta = 0,
+ },
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ [9] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1005,
+ },
+ },
+ })
+ else
+ screen:expect({
+ grid = [[
+ one^ |
+ {s:one }{n:```lua }{1: }|
+ {n:two function test()}{1: }|
+ {n:looooooooooooooong print('foo') }{1: }|
+ {1:~ }{n:end }{1: }|
+ {1:~ }{n:``` }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }|*3
+ {2:-- }{5:match 1 of 3} |
+ ]],
+ })
+ end
end)
end)
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 666d98c3b2..295c40b9b6 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -2,7 +2,7 @@ local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local spawn, set_session, clear = n.spawn, n.set_session, n.clear
+local set_session, clear = n.set_session, n.clear
local feed, command = n.feed, n.command
local exec = n.exec
local insert = n.insert
@@ -26,7 +26,7 @@ describe('screen', function()
}
before_each(function()
- local screen_nvim = spawn(nvim_argv)
+ local screen_nvim = n.new_session(false, { args = nvim_argv, merge = false })
set_session(screen_nvim)
screen = Screen.new()
end)
@@ -766,7 +766,7 @@ describe('Screen default colors', function()
'colorscheme vim',
'--embed',
}
- local screen_nvim = spawn(nvim_argv)
+ local screen_nvim = n.new_session(false, { args = nvim_argv, merge = false })
set_session(screen_nvim)
screen = Screen.new(53, 14, { rgb = true, ext_termcolors = termcolors or nil })
end
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
index 40c09e61ac..d5f7c928de 100644
--- a/test/old/testdir/test_autocmd.vim
+++ b/test/old/testdir/test_autocmd.vim
@@ -4181,4 +4181,28 @@ func Test_autocmd_BufWinLeave_with_vsp()
exe "bw! " .. dummy
endfunc
+func Test_OptionSet_cmdheight()
+ set mouse=a laststatus=2
+ au OptionSet cmdheight :let &l:ch = v:option_new
+
+ resize -1
+ call assert_equal(2, &l:ch)
+ resize +1
+ call assert_equal(1, &l:ch)
+
+ call Ntest_setmouse(&lines - 1, 1)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call Ntest_setmouse(&lines - 2, 1)
+ call feedkeys("\<LeftDrag>", 'xt')
+ call assert_equal(2, &l:ch)
+
+ tabnew | resize +1
+ call assert_equal(1, &l:ch)
+ tabfirst
+ call assert_equal(2, &l:ch)
+
+ tabonly
+ set cmdheight& mouse& laststatus&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index cbce0e908d..d4ad63d43e 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -291,8 +291,8 @@ func Test_changing_cmdheight()
call term_sendkeys(buf, ":resize -3\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_1', {})
- " using the space available doesn't change the status line
- call term_sendkeys(buf, ":set cmdheight+=3\<CR>")
+ " :resize now also changes 'cmdheight' accordingly
+ call term_sendkeys(buf, ":set cmdheight+=1\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_2', {})
" using more space moves the status line up
@@ -300,10 +300,10 @@ func Test_changing_cmdheight()
call VerifyScreenDump(buf, 'Test_changing_cmdheight_3', {})
" reducing cmdheight moves status line down
- call term_sendkeys(buf, ":set cmdheight-=2\<CR>")
+ call term_sendkeys(buf, ":set cmdheight-=3\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_4', {})
- " reducing window size and then setting cmdheight
+ " reducing window size and then setting cmdheight
call term_sendkeys(buf, ":resize -1\<CR>")
call term_sendkeys(buf, ":set cmdheight=1\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_5', {})
@@ -316,6 +316,10 @@ func Test_changing_cmdheight()
call term_sendkeys(buf, ":call EchoOne()\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_7', {})
+ " window commands do not reduce 'cmdheight' to value lower than :set by user
+ call term_sendkeys(buf, "\<CR>:wincmd _\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_8', {})
+
" clean up
call StopVimInTerminal(buf)
endfunc
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 685f700130..d890884eb5 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -672,7 +672,6 @@ func s:GetFilenameChecks() abort
\ 'samba': ['smb.conf'],
\ 'sas': ['file.sas'],
\ 'sass': ['file.sass'],
- \ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
\ 'scala': ['file.scala'],
\ 'scheme': ['file.scm', 'file.ss', 'file.sld', 'file.stsg', 'any/local/share/supertux2/config', '.lips_repl_history'],
@@ -691,6 +690,7 @@ func s:GetFilenameChecks() abort
\ '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile', 'user-dirs.defaults', 'user-dirs.dirs',
\ 'makepkg.conf', '.makepkg.conf', 'file.mdd', '.env', '.envrc', 'devscripts.conf', '.devscripts', 'file.lo',
\ 'file.la', 'file.lai'],
+ \ 'shaderslang': ['file.slang'],
\ 'sieve': ['file.siv', 'file.sieve'],
\ 'sil': ['file.sil'],
\ 'simula': ['file.sim'],
@@ -2321,6 +2321,22 @@ func Test_cmd_file()
filetype off
endfunc
+func Test_sa_file()
+ filetype on
+
+ call writefile([';* XXX-a.sa: XXX for TI C6000 DSP *;', '.no_mdep'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('tiasm', &filetype)
+ bwipe!
+
+ call writefile(['-- comment'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('sather', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
func Test_sig_file()
filetype on
diff --git a/test/old/testdir/test_stacktrace.vim b/test/old/testdir/test_stacktrace.vim
new file mode 100644
index 0000000000..1e47deefdd
--- /dev/null
+++ b/test/old/testdir/test_stacktrace.vim
@@ -0,0 +1,132 @@
+" Test for getstacktrace() and v:stacktrace
+
+source vim9.vim
+
+let s:thisfile = expand('%:p')
+let s:testdir = s:thisfile->fnamemodify(':h')
+
+func Filepath(name)
+ return s:testdir .. '/' .. a:name
+endfunc
+
+func AssertStacktrace(expect, actual)
+ call assert_equal(#{lnum: 581, filepath: Filepath('runtest.vim')}, a:actual[0])
+ call assert_equal(a:expect, a:actual[-len(a:expect):])
+endfunc
+
+func Test_getstacktrace()
+ let g:stacktrace = []
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ source Xscript2
+ func Xfunc1()
+ " Xfunc1
+ call Xfunc2()
+ endfunc
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ func Xfunc2()
+ " Xfunc2
+ let g:stacktrace = getstacktrace()
+ endfunc
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ call Xfunc1()
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_getstacktrace'), lnum: 37, filepath: s:thisfile},
+ \ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
+ \ ], g:stacktrace)
+ unlet g:stacktrace
+endfunc
+
+func Test_getstacktrace_event()
+ let g:stacktrace = []
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ func Xfunc()
+ " Xfunc
+ let g:stacktrace = getstacktrace()
+ endfunc
+ augroup test_stacktrace
+ autocmd SourcePre * call Xfunc()
+ augroup END
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ source Xscript2
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_getstacktrace_event'), lnum: 64, filepath: s:thisfile},
+ \ #{event: 'SourcePre Autocommands for "*"', lnum: 7, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc'), lnum: 4, filepath: Filepath('Xscript1')},
+ \ ], g:stacktrace)
+ augroup test_stacktrace
+ autocmd!
+ augroup END
+ unlet g:stacktrace
+endfunc
+
+func Test_vstacktrace()
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ source Xscript2
+ func Xfunc1()
+ " Xfunc1
+ call Xfunc2()
+ endfunc
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ func Xfunc2()
+ " Xfunc2
+ throw 'Exception from Xfunc2'
+ endfunc
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ call assert_equal([], v:stacktrace)
+ try
+ call Xfunc1()
+ catch
+ let stacktrace = v:stacktrace
+ endtry
+ call assert_equal([], v:stacktrace)
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_vstacktrace'), lnum: 97, filepath: s:thisfile},
+ \ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
+ \ ], stacktrace)
+endfunc
+
+func Test_zzz_stacktrace_vim9()
+ let lines =<< trim [SCRIPT]
+ var stacktrace = getstacktrace()
+ assert_notequal([], stacktrace)
+ for d in stacktrace
+ assert_true(has_key(d, 'lnum'))
+ endfor
+ try
+ throw 'Exception from s:Func'
+ catch
+ assert_notequal([], v:stacktrace)
+ assert_equal(len(stacktrace), len(v:stacktrace))
+ for d in v:stacktrace
+ assert_true(has_key(d, 'lnum'))
+ endfor
+ endtry
+ [SCRIPT]
+ call CheckDefSuccess(lines)
+ " FIXME: v:stacktrace is not cleared after the exception handling, and this
+ " test has to be run as the last one because of this.
+ " call assert_equal([], v:stacktrace)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim
index 343bc9fd83..24517f90cb 100644
--- a/test/old/testdir/test_window_cmd.vim
+++ b/test/old/testdir/test_window_cmd.vim
@@ -56,7 +56,6 @@ func Test_window_cmd_cmdwin_with_vsp()
endfunc
func Test_cmdheight_not_changed()
- throw 'Skipped: N/A'
set cmdheight=2
set winminheight=0
augroup Maximize
diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c
index 8755e32e7a..7522962a05 100644
--- a/test/unit/fixtures/vterm_test.c
+++ b/test/unit/fixtures/vterm_test.c
@@ -1,7 +1,11 @@
#include <stdio.h>
+#include <string.h>
+
#include "nvim/grid.h"
#include "nvim/mbyte.h"
-
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/vterm_internal_defs.h"
#include "vterm_test.h"
int parser_text(const char bytes[], size_t len, void *user)
@@ -204,7 +208,8 @@ int selection_query(VTermSelectionMask mask, void *user)
return 1;
}
-static void print_schar(FILE *f, schar_T schar) {
+static void print_schar(FILE *f, schar_T schar)
+{
char buf[MAX_SCHAR_SIZE];
schar_get(buf, schar);
StrCharInfo ci = utf_ptr2StrCharInfo(buf);
@@ -319,6 +324,34 @@ void print_color(const VTermColor *col)
fclose(f);
}
+static VTermValueType vterm_get_prop_type(VTermProp prop)
+{
+ switch (prop) {
+ case VTERM_PROP_CURSORVISIBLE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORBLINK:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_ALTSCREEN:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_TITLE:
+ return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_ICONNAME:
+ return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_REVERSE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORSHAPE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_MOUSE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_FOCUSREPORT:
+ return VTERM_VALUETYPE_BOOL;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+ return 0; // UNREACHABLE
+}
+
bool want_state_settermprop;
int state_settermprop(VTermProp prop, VTermValue *val, void *user)
{
@@ -463,14 +496,14 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
}
int eol = cols;
- while (eol && !cells[eol-1].schar) {
+ while (eol && !cells[eol - 1].schar) {
eol--;
}
FILE *f = fopen(VTERM_TEST_FILE, "a");
fprintf(f, "sb_pushline %d =", cols);
for (int c = 0; c < eol; c++) {
- fprintf(f, " ");
+ fprintf(f, " ");
print_schar(f, cells[c].schar);
}
fprintf(f, "\n");
@@ -488,7 +521,7 @@ int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
// All lines of scrollback contain "ABCDE"
for (int col = 0; col < cols; col++) {
- if(col < 5) {
+ if (col < 5) {
cells[col].schar = schar_from_ascii((uint32_t)('A' + col));
} else {
cells[col].schar = 0;
@@ -524,3 +557,231 @@ void term_output(const char *s, size_t len, void *user)
}
fclose(f);
}
+
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
+{
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ val->boolean = state->pen.bold;
+ return 1;
+
+ case VTERM_ATTR_UNDERLINE:
+ val->number = state->pen.underline;
+ return 1;
+
+ case VTERM_ATTR_ITALIC:
+ val->boolean = state->pen.italic;
+ return 1;
+
+ case VTERM_ATTR_BLINK:
+ val->boolean = state->pen.blink;
+ return 1;
+
+ case VTERM_ATTR_REVERSE:
+ val->boolean = state->pen.reverse;
+ return 1;
+
+ case VTERM_ATTR_CONCEAL:
+ val->boolean = state->pen.conceal;
+ return 1;
+
+ case VTERM_ATTR_STRIKE:
+ val->boolean = state->pen.strike;
+ return 1;
+
+ case VTERM_ATTR_FONT:
+ val->number = state->pen.font;
+ return 1;
+
+ case VTERM_ATTR_FOREGROUND:
+ val->color = state->pen.fg;
+ return 1;
+
+ case VTERM_ATTR_BACKGROUND:
+ val->color = state->pen.bg;
+ return 1;
+
+ case VTERM_ATTR_SMALL:
+ val->boolean = state->pen.small;
+ return 1;
+
+ case VTERM_ATTR_BASELINE:
+ val->number = state->pen.baseline;
+ return 1;
+
+ case VTERM_ATTR_URI:
+ val->number = state->pen.uri;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
+{
+ if ((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos,
+ VTermAttrMask attrs)
+{
+ ScreenCell *target = getcell(screen, pos.row, pos.col);
+
+ // TODO(vterm): bounds check
+ extent->start_row = pos.row;
+ extent->end_row = pos.row + 1;
+
+ if (extent->start_col < 0) {
+ extent->start_col = 0;
+ }
+ if (extent->end_col < 0) {
+ extent->end_col = screen->cols;
+ }
+
+ int col;
+
+ for (col = pos.col - 1; col >= extent->start_col; col--) {
+ if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) {
+ break;
+ }
+ }
+ extent->start_col = col + 1;
+
+ for (col = pos.col + 1; col < extent->end_col; col++) {
+ if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) {
+ break;
+ }
+ }
+ extent->end_col = col - 1;
+
+ return 1;
+}
+
+/// Does not NUL-terminate the buffer
+size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len,
+ const VTermRect rect)
+{
+ size_t outpos = 0;
+ int padding = 0;
+
+#define PUT(bytes, thislen) \
+ if (true) { \
+ if (buffer && outpos + thislen <= len) \
+ memcpy((char *)buffer + outpos, bytes, thislen); \
+ outpos += thislen; \
+ } \
+
+ for (int row = rect.start_row; row < rect.end_row; row++) {
+ for (int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if (cell->schar == 0) {
+ // Erased cell, might need a space
+ padding++;
+ } else if (cell->schar == (uint32_t)-1) {
+ // Gap behind a double-width char, do nothing
+ } else {
+ while (padding) {
+ PUT(" ", 1);
+ padding--;
+ }
+ char buf[MAX_SCHAR_SIZE + 1];
+ size_t thislen = schar_get(buf, cell->schar);
+ PUT(buf, thislen);
+ }
+ }
+
+ if (row < rect.end_row - 1) {
+ PUT("\n", 1);
+ padding = 0;
+ }
+ }
+
+ return outpos;
+}
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
+{
+ // This cell is EOL if this and every cell to the right is black
+ for (; pos.col < screen->cols; pos.col++) {
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+ if (cell->schar != 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
+{
+ *cursorpos = state->pos;
+}
+
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
+{
+ state->bold_is_highbright = bold_is_highbright;
+}
+
+/// Compares two colours. Returns true if the colors are equal, false otherwise.
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
+{
+ // First make sure that the two colours are of the same type (RGB/Indexed)
+ if (a->type != b->type) {
+ return false;
+ }
+
+ // Depending on the type inspect the corresponding members
+ if (VTERM_COLOR_IS_INDEXED(a)) {
+ return a->indexed.idx == b->indexed.idx;
+ } else if (VTERM_COLOR_IS_RGB(a)) {
+ return (a->rgb.red == b->rgb.red)
+ && (a->rgb.green == b->rgb.green)
+ && (a->rgb.blue == b->rgb.blue);
+ }
+
+ return 0;
+}
diff --git a/test/unit/fixtures/vterm_test.h b/test/unit/fixtures/vterm_test.h
index a05e7d499e..ef6463af6d 100644
--- a/test/unit/fixtures/vterm_test.h
+++ b/test/unit/fixtures/vterm_test.h
@@ -2,8 +2,17 @@
#include <stdint.h>
#include "nvim/macros_defs.h"
-#include "vterm/vterm.h"
+#include "nvim/vterm/vterm.h"
+EXTERN VTermPos state_pos;
+EXTERN bool want_state_putglyph INIT (=false);
+EXTERN bool want_state_movecursor INIT(= false);
+EXTERN bool want_state_erase INIT(= false);
+EXTERN bool want_state_scrollrect INIT(= false);
+EXTERN bool want_state_moverect INIT(= false);
+EXTERN bool want_state_settermprop INIT(= false);
+EXTERN bool want_state_scrollback INIT(= false);
+EXTERN bool want_screen_scrollback INIT(= false);
int parser_text(const char bytes[], size_t len, void *user);
int parser_csi(const char *leader, const long args[], int argcount, const char *intermed,
char command, void *user);
@@ -27,12 +36,10 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user);
int screen_sb_popline(int cols, VTermScreenCell *cells, void *user);
int screen_sb_clear(void *user);
void term_output(const char *s, size_t len, void *user);
-EXTERN VTermPos state_pos;
-EXTERN bool want_state_putglyph INIT (=false);
-EXTERN bool want_state_movecursor INIT(= false);
-EXTERN bool want_state_erase INIT(= false);
-EXTERN bool want_state_scrollrect INIT(= false);
-EXTERN bool want_state_moverect INIT(= false);
-EXTERN bool want_state_settermprop INIT(= false);
-EXTERN bool want_state_scrollback INIT(= false);
-EXTERN bool want_screen_scrollback INIT(= false);
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
+size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len, VTermRect rect);
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua
index 2457525fb7..db0aa3c575 100644
--- a/test/unit/vterm_spec.lua
+++ b/test/unit/vterm_spec.lua
@@ -61,7 +61,6 @@ local bit = require('bit')
--- @field vterm_screen_enable_reflow function
--- @field vterm_screen_get_attrs_extent function
--- @field vterm_screen_get_cell function
---- @field vterm_screen_get_chars fun(any, any, any, any):any
--- @field vterm_screen_get_text fun(any, any, any, any):any
--- @field vterm_screen_is_eol fun(any, any):any
--- @field vterm_screen_reset function
@@ -79,10 +78,17 @@ local bit = require('bit')
--- @field vterm_state_set_selection_callbacks function
--- @field vterm_state_set_unrecognised_fallbacks function
local vterm = t.cimport(
- './src/nvim/mbyte.h',
'./src/nvim/grid.h',
- './src/vterm/vterm.h',
- './src/vterm/vterm_internal.h',
+ './src/nvim/mbyte.h',
+ './src/nvim/vterm/encoding.h',
+ './src/nvim/vterm/keyboard.h',
+ './src/nvim/vterm/mouse.h',
+ './src/nvim/vterm/parser.h',
+ './src/nvim/vterm/pen.h',
+ './src/nvim/vterm/screen.h',
+ './src/nvim/vterm/state.h',
+ './src/nvim/vterm/vterm.h',
+ './src/nvim/vterm/vterm_internal.h',
'./test/unit/fixtures/vterm_test.h'
)