2024-01-12 04:28:20 -07:00
|
|
|
local uv = vim.uv
|
2016-04-23 16:53:11 -07:00
|
|
|
local global_helpers = require('test.helpers')
|
|
|
|
|
2023-01-31 15:35:04 -07:00
|
|
|
local Session = require('test.client.session')
|
|
|
|
local uv_stream = require('test.client.uv_stream')
|
|
|
|
local SocketStream = uv_stream.SocketStream
|
|
|
|
local ChildProcessStream = uv_stream.ChildProcessStream
|
2016-10-22 09:15:46 -07:00
|
|
|
|
2016-11-04 08:20:58 -07:00
|
|
|
local check_cores = global_helpers.check_cores
|
2016-04-23 16:53:11 -07:00
|
|
|
local check_logs = global_helpers.check_logs
|
2018-04-27 01:07:26 -07:00
|
|
|
local dedent = global_helpers.dedent
|
2016-04-23 16:53:11 -07:00
|
|
|
local eq = global_helpers.eq
|
2019-09-04 06:58:04 -07:00
|
|
|
local is_os = global_helpers.is_os
|
2018-04-27 01:07:26 -07:00
|
|
|
local ok = global_helpers.ok
|
2024-01-12 04:41:09 -07:00
|
|
|
local sleep = uv.sleep
|
2020-09-21 01:37:28 -07:00
|
|
|
local fail = global_helpers.fail
|
2014-10-08 08:56:28 -07:00
|
|
|
|
2023-11-12 05:54:27 -07:00
|
|
|
local module = {}
|
2019-08-08 07:03:25 -07:00
|
|
|
|
2022-09-24 17:20:47 -07:00
|
|
|
local runtime_set = 'set runtimepath^=./build/lib/nvim/'
|
2024-01-15 12:41:22 -07:00
|
|
|
module.nvim_prog = (os.getenv('NVIM_PRG') or global_helpers.paths.test_build_dir .. '/bin/nvim')
|
2017-02-09 19:39:00 -07:00
|
|
|
-- Default settings for the test session.
|
2019-08-08 07:03:25 -07:00
|
|
|
module.nvim_set = (
|
2023-12-06 19:16:00 -07:00
|
|
|
'set shortmess+=IS background=light termguicolors noswapfile noautoindent startofline'
|
2019-08-08 07:03:25 -07:00
|
|
|
.. ' laststatus=1 undodir=. directory=. viewdir=. backupdir=.'
|
2023-12-06 19:16:00 -07:00
|
|
|
.. ' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid'
|
|
|
|
)
|
2019-08-08 07:03:25 -07:00
|
|
|
module.nvim_argv = {
|
|
|
|
module.nvim_prog,
|
|
|
|
'-u',
|
|
|
|
'NONE',
|
|
|
|
'-i',
|
|
|
|
'NONE',
|
2022-09-24 17:20:47 -07:00
|
|
|
-- XXX: find treesitter parsers.
|
|
|
|
'--cmd',
|
|
|
|
runtime_set,
|
2021-08-15 21:24:59 -07:00
|
|
|
'--cmd',
|
|
|
|
module.nvim_set,
|
2023-12-06 19:16:00 -07:00
|
|
|
-- Remove default mappings.
|
|
|
|
'--cmd',
|
|
|
|
'mapclear | mapclear!',
|
feat(highlight): update default color scheme
Problem: Default color scheme is suboptimal.
Solution: Start using new color scheme. Introduce new `vim` color scheme
for opt-in backward compatibility.
------
Main design ideas
- Be "Neovim branded".
- Be minimal for 256 colors with a bit more shades for true colors.
- Be accessible through high enough contrast ratios.
- Be suitable for dark and light backgrounds via exchange of dark and
light palettes.
------
Palettes
- Have dark and light variants. Implemented through exporeted
`NvimDark*` and `NvimLight*` hex colors.
- Palettes have 4 shades of grey for UI elements and 6 colors (red,
yellow, green, cyan, blue, magenta).
- Actual values are computed procedurally in Oklch color space based on
a handful of hyperparameters.
- Each color has a 256 colors variant with perceptually closest color.
------
Highlight groups
Use:
- Grey shades for general UI according to their design.
- Bold text for keywords (`Statement` highlight group). This is an
important choice to increase accessibility for people with color
deficiencies, as it doesn't rely on actual color.
- Green for strings, `DiffAdd` (as background), `DiagnosticOk`, and some
minor text UI elements.
- Cyan as main syntax color, i.e. for function usage (`Function`
highlight group), `DiffText`, `DiagnosticInfo`, and some minor text UI
elements.
- Red to generally mean high user attention, i.e. errors; in particular
for `ErrorMsg`, `DiffDelete`, `DiagnosticError`.
- Yellow very sparingly only with true colors to mean mild user
attention, i.e. warnings. That is, `DiagnosticWarn` and `WarningMsg`.
- Blue very sparingly only with true colors as `DiagnosticHint` and some
additional important syntax group (like `Identifier`).
- Magenta very carefully (if at all).
------
Notes
- To make tests work without relatively larege updates, each one is
prepended with an equivalent of the call `:colorscheme vim`.
Plus some tests which spawn new Neovim instances also now use 'vim'
color scheme.
In some cases tests are updated to fit new default color scheme.
2023-11-29 13:16:09 -07:00
|
|
|
-- Make screentest work after changing to the new default color scheme
|
|
|
|
-- Source 'vim' color scheme without side effects
|
|
|
|
-- TODO: rewrite tests
|
2023-12-19 09:20:03 -07:00
|
|
|
'--cmd',
|
|
|
|
'lua dofile("runtime/colors/vim.lua")',
|
feat(highlight): update default color scheme
Problem: Default color scheme is suboptimal.
Solution: Start using new color scheme. Introduce new `vim` color scheme
for opt-in backward compatibility.
------
Main design ideas
- Be "Neovim branded".
- Be minimal for 256 colors with a bit more shades for true colors.
- Be accessible through high enough contrast ratios.
- Be suitable for dark and light backgrounds via exchange of dark and
light palettes.
------
Palettes
- Have dark and light variants. Implemented through exporeted
`NvimDark*` and `NvimLight*` hex colors.
- Palettes have 4 shades of grey for UI elements and 6 colors (red,
yellow, green, cyan, blue, magenta).
- Actual values are computed procedurally in Oklch color space based on
a handful of hyperparameters.
- Each color has a 256 colors variant with perceptually closest color.
------
Highlight groups
Use:
- Grey shades for general UI according to their design.
- Bold text for keywords (`Statement` highlight group). This is an
important choice to increase accessibility for people with color
deficiencies, as it doesn't rely on actual color.
- Green for strings, `DiffAdd` (as background), `DiagnosticOk`, and some
minor text UI elements.
- Cyan as main syntax color, i.e. for function usage (`Function`
highlight group), `DiffText`, `DiagnosticInfo`, and some minor text UI
elements.
- Red to generally mean high user attention, i.e. errors; in particular
for `ErrorMsg`, `DiffDelete`, `DiagnosticError`.
- Yellow very sparingly only with true colors to mean mild user
attention, i.e. warnings. That is, `DiagnosticWarn` and `WarningMsg`.
- Blue very sparingly only with true colors as `DiagnosticHint` and some
additional important syntax group (like `Identifier`).
- Magenta very carefully (if at all).
------
Notes
- To make tests work without relatively larege updates, each one is
prepended with an equivalent of the call `:colorscheme vim`.
Plus some tests which spawn new Neovim instances also now use 'vim'
color scheme.
In some cases tests are updated to fit new default color scheme.
2023-11-29 13:16:09 -07:00
|
|
|
'--cmd',
|
|
|
|
'unlet g:colors_name',
|
2021-08-16 08:31:14 -07:00
|
|
|
'--embed',
|
|
|
|
}
|
|
|
|
|
2017-04-09 16:38:20 -07:00
|
|
|
-- Directory containing nvim.
|
2019-08-08 07:03:25 -07:00
|
|
|
module.nvim_dir = module.nvim_prog:gsub('[/\\][^/\\]+$', '')
|
|
|
|
if module.nvim_dir == module.nvim_prog then
|
|
|
|
module.nvim_dir = '.'
|
2015-02-28 07:39:04 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
local prepend_argv --- @type string[]?
|
2014-10-08 08:56:28 -07:00
|
|
|
|
|
|
|
if os.getenv('VALGRIND') then
|
|
|
|
local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
|
2014-11-22 08:29:03 -07:00
|
|
|
prepend_argv = {
|
|
|
|
'valgrind',
|
|
|
|
'-q',
|
|
|
|
'--tool=memcheck',
|
|
|
|
'--leak-check=yes',
|
|
|
|
'--track-origins=yes',
|
|
|
|
'--show-possibly-lost=no',
|
2016-06-07 21:24:23 -07:00
|
|
|
'--suppressions=src/.valgrind.supp',
|
2014-11-22 08:29:03 -07:00
|
|
|
'--log-file=' .. log_file,
|
|
|
|
}
|
|
|
|
if os.getenv('GDB') then
|
|
|
|
table.insert(prepend_argv, '--vgdb=yes')
|
|
|
|
table.insert(prepend_argv, '--vgdb-error=0')
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
2014-11-22 08:29:03 -07:00
|
|
|
elseif os.getenv('GDB') then
|
2023-12-13 06:04:24 -07:00
|
|
|
local gdbserver_port = os.getenv('GDBSERVER_PORT') or '7777'
|
2014-11-22 08:29:03 -07:00
|
|
|
prepend_argv = { 'gdbserver', 'localhost:' .. gdbserver_port }
|
|
|
|
end
|
|
|
|
|
|
|
|
if prepend_argv then
|
2024-01-15 12:49:08 -07:00
|
|
|
local new_nvim_argv = {} --- @type string[]
|
2014-11-22 08:29:03 -07:00
|
|
|
local len = #prepend_argv
|
2015-04-26 05:31:39 -07:00
|
|
|
for i = 1, len do
|
|
|
|
new_nvim_argv[i] = prepend_argv[i]
|
|
|
|
end
|
2019-08-08 07:03:25 -07:00
|
|
|
for i = 1, #module.nvim_argv do
|
|
|
|
new_nvim_argv[i + len] = module.nvim_argv[i]
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
2019-08-08 07:03:25 -07:00
|
|
|
module.nvim_argv = new_nvim_argv
|
|
|
|
module.prepend_argv = prepend_argv
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
local session --- @type test.Session?
|
|
|
|
local loop_running --- @type boolean?
|
|
|
|
local last_error --- @type string?
|
|
|
|
local method_error --- @type string?
|
2014-10-08 08:56:28 -07:00
|
|
|
|
refactor(map): enhanced implementation, Clean Code™, etc etc
This involves two redesigns of the map.c implementations:
1. Change of macro style and code organization
The old khash.h and map.c implementation used huge #define blocks with a
lot of backslash line continuations.
This instead uses the "implementation file" .c.h pattern. Such a file is
meant to be included multiple times, with different macros set prior to
inclusion as parameters. we already use this pattern e.g. for
eval/typval_encode.c.h to implement different typval encoders reusing a
similar structure.
We can structure this code into two parts. one that only depends on key
type and is enough to implement sets, and one which depends on both key
and value to implement maps (as a wrapper around sets, with an added
value[] array)
2. Separate the main hash buckets from the key / value arrays
Change the hack buckets to only contain an index into separate key /
value arrays
This is a common pattern in modern, state of the art hashmap
implementations. Even though this leads to one more allocated array, it
is this often is a net reduction of memory consumption. Consider
key+value consuming at least 12 bytes per pair. On average, we will have
twice as many buckets per item.
Thus old implementation:
2*12 = 24 bytes per item
New implementation
1*12 + 2*4 = 20 bytes per item
And the difference gets bigger with larger items.
One might think we have pulled a fast one here, as wouldn't the average size of
the new key/value arrays be 1.5 slots per items due to amortized grows?
But remember, these arrays are fully dense, and thus the accessed memory,
measured in _cache lines_, the unit which actually matters, will be the
fully used memory but just rounded up to the nearest cache line
boundary.
This has some other interesting properties, such as an insert-only
set/map will be fully ordered by insert only. Preserving this ordering
in face of deletions is more tricky tho. As we currently don't use
ordered maps, the "delete" operation maintains compactness of the item
arrays in the simplest way by breaking the ordering. It would be
possible to implement an order-preserving delete although at some cost,
like allowing the items array to become non-dense until the next rehash.
Finally, in face of these two major changes, all code used in khash.h
has been integrated into map.c and friends. Given the heavy edits it
makes no sense to "layer" the code into a vendored and a wrapper part.
Rather, the layered cake follows the specialization depth: code shared
for all maps, code specialized to a key type (and its equivalence
relation), and finally code specialized to value+key type.
2023-05-17 07:08:06 -07:00
|
|
|
if not is_os('win') then
|
2024-01-15 12:49:08 -07:00
|
|
|
local sigpipe_handler = assert(uv.new_signal())
|
2024-01-12 04:28:20 -07:00
|
|
|
uv.signal_start(sigpipe_handler, 'sigpipe', function()
|
refactor(map): enhanced implementation, Clean Code™, etc etc
This involves two redesigns of the map.c implementations:
1. Change of macro style and code organization
The old khash.h and map.c implementation used huge #define blocks with a
lot of backslash line continuations.
This instead uses the "implementation file" .c.h pattern. Such a file is
meant to be included multiple times, with different macros set prior to
inclusion as parameters. we already use this pattern e.g. for
eval/typval_encode.c.h to implement different typval encoders reusing a
similar structure.
We can structure this code into two parts. one that only depends on key
type and is enough to implement sets, and one which depends on both key
and value to implement maps (as a wrapper around sets, with an added
value[] array)
2. Separate the main hash buckets from the key / value arrays
Change the hack buckets to only contain an index into separate key /
value arrays
This is a common pattern in modern, state of the art hashmap
implementations. Even though this leads to one more allocated array, it
is this often is a net reduction of memory consumption. Consider
key+value consuming at least 12 bytes per pair. On average, we will have
twice as many buckets per item.
Thus old implementation:
2*12 = 24 bytes per item
New implementation
1*12 + 2*4 = 20 bytes per item
And the difference gets bigger with larger items.
One might think we have pulled a fast one here, as wouldn't the average size of
the new key/value arrays be 1.5 slots per items due to amortized grows?
But remember, these arrays are fully dense, and thus the accessed memory,
measured in _cache lines_, the unit which actually matters, will be the
fully used memory but just rounded up to the nearest cache line
boundary.
This has some other interesting properties, such as an insert-only
set/map will be fully ordered by insert only. Preserving this ordering
in face of deletions is more tricky tho. As we currently don't use
ordered maps, the "delete" operation maintains compactness of the item
arrays in the simplest way by breaking the ordering. It would be
possible to implement an order-preserving delete although at some cost,
like allowing the items array to become non-dense until the next rehash.
Finally, in face of these two major changes, all code used in khash.h
has been integrated into map.c and friends. Given the heavy edits it
makes no sense to "layer" the code into a vendored and a wrapper part.
Rather, the layered cake follows the specialization depth: code shared
for all maps, code specialized to a key type (and its equivalence
relation), and finally code specialized to value+key type.
2023-05-17 07:08:06 -07:00
|
|
|
print('warning: got SIGPIPE signal. Likely related to a crash in nvim')
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.get_session()
|
2017-12-09 03:26:06 -07:00
|
|
|
return session
|
|
|
|
end
|
|
|
|
|
2021-06-12 14:23:05 -07:00
|
|
|
function module.set_session(s)
|
2015-04-11 21:48:16 -07:00
|
|
|
session = s
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param method string
|
|
|
|
--- @param ... any
|
|
|
|
--- @return any
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.request(method, ...)
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session)
|
2014-10-08 08:56:28 -07:00
|
|
|
local status, rv = session:request(method, ...)
|
|
|
|
if not status then
|
2014-10-27 14:34:42 -07:00
|
|
|
if loop_running then
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type string
|
2014-10-27 14:34:42 -07:00
|
|
|
last_error = rv[2]
|
|
|
|
session:stop()
|
|
|
|
else
|
|
|
|
error(rv[2])
|
|
|
|
end
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
return rv
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param method string
|
|
|
|
--- @param ... any
|
|
|
|
--- @return any
|
2022-11-14 03:01:35 -07:00
|
|
|
function module.request_lua(method, ...)
|
|
|
|
return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param timeout? integer
|
|
|
|
--- @return string?
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.next_msg(timeout)
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session)
|
|
|
|
return session:next_message(timeout or 10000)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.expect_twostreams(msgs1, msgs2)
|
2017-06-10 06:25:23 -07:00
|
|
|
local pos1, pos2 = 1, 1
|
|
|
|
while pos1 <= #msgs1 or pos2 <= #msgs2 do
|
2019-08-08 07:03:25 -07:00
|
|
|
local msg = module.next_msg()
|
2017-06-10 06:25:23 -07:00
|
|
|
if pos1 <= #msgs1 and pcall(eq, msgs1[pos1], msg) then
|
|
|
|
pos1 = pos1 + 1
|
|
|
|
elseif pos2 <= #msgs2 then
|
|
|
|
eq(msgs2[pos2], msg)
|
|
|
|
pos2 = pos2 + 1
|
|
|
|
else
|
|
|
|
-- already failed, but show the right error message
|
|
|
|
eq(msgs1[pos1], msg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-08 16:24:38 -07:00
|
|
|
-- Expects a sequence of next_msg() results. If multiple sequences are
|
2018-02-16 11:42:05 -07:00
|
|
|
-- passed they are tried until one succeeds, in order of shortest to longest.
|
2019-04-12 17:13:29 -07:00
|
|
|
--
|
|
|
|
-- Can be called with positional args (list of sequences only):
|
|
|
|
-- expect_msg_seq(seq1, seq2, ...)
|
|
|
|
-- or keyword args:
|
|
|
|
-- expect_msg_seq{ignore={...}, seqs={seq1, seq2, ...}}
|
|
|
|
--
|
|
|
|
-- ignore: List of ignored event names.
|
|
|
|
-- seqs: List of one or more potential event sequences.
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.expect_msg_seq(...)
|
2018-02-16 11:42:05 -07:00
|
|
|
if select('#', ...) < 1 then
|
|
|
|
error('need at least 1 argument')
|
|
|
|
end
|
2019-04-12 17:13:29 -07:00
|
|
|
local arg1 = select(1, ...)
|
|
|
|
if (arg1['seqs'] and select('#', ...) > 1) or type(arg1) ~= 'table' then
|
|
|
|
error('invalid args')
|
|
|
|
end
|
|
|
|
local ignore = arg1['ignore'] and arg1['ignore'] or {}
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type string[]
|
2019-04-12 17:13:29 -07:00
|
|
|
local seqs = arg1['seqs'] and arg1['seqs'] or { ... }
|
|
|
|
if type(ignore) ~= 'table' then
|
|
|
|
error("'ignore' arg must be a list of strings")
|
|
|
|
end
|
2018-02-16 11:42:05 -07:00
|
|
|
table.sort(seqs, function(a, b) -- Sort ascending, by (shallow) length.
|
|
|
|
return #a < #b
|
|
|
|
end)
|
|
|
|
|
|
|
|
local actual_seq = {}
|
2019-04-12 17:13:29 -07:00
|
|
|
local nr_ignored = 0
|
2018-02-16 11:42:05 -07:00
|
|
|
local final_error = ''
|
|
|
|
local function cat_err(err1, err2)
|
|
|
|
if err1 == nil then
|
|
|
|
return err2
|
|
|
|
end
|
|
|
|
return string.format('%s\n%s\n%s', err1, string.rep('=', 78), err2)
|
|
|
|
end
|
2019-08-09 06:32:38 -07:00
|
|
|
local msg_timeout = module.load_adjust(10000) -- Big timeout for ASAN/valgrind.
|
2018-02-16 11:42:05 -07:00
|
|
|
for anum = 1, #seqs do
|
|
|
|
local expected_seq = seqs[anum]
|
|
|
|
-- Collect enough messages to compare the next expected sequence.
|
|
|
|
while #actual_seq < #expected_seq do
|
2019-08-09 06:32:38 -07:00
|
|
|
local msg = module.next_msg(msg_timeout)
|
2019-04-12 17:13:29 -07:00
|
|
|
local msg_type = msg and msg[2] or nil
|
2018-02-16 11:42:05 -07:00
|
|
|
if msg == nil then
|
|
|
|
error(
|
|
|
|
cat_err(
|
|
|
|
final_error,
|
2019-04-12 17:13:29 -07:00
|
|
|
string.format(
|
|
|
|
'got %d messages (ignored %d), expected %d',
|
|
|
|
#actual_seq,
|
|
|
|
nr_ignored,
|
|
|
|
#expected_seq
|
|
|
|
)
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
|
|
|
)
|
2024-01-12 04:28:20 -07:00
|
|
|
elseif vim.tbl_contains(ignore, msg_type) then
|
2019-04-12 17:13:29 -07:00
|
|
|
nr_ignored = nr_ignored + 1
|
|
|
|
else
|
|
|
|
table.insert(actual_seq, msg)
|
2018-02-16 11:42:05 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
local status, result = pcall(eq, expected_seq, actual_seq)
|
|
|
|
if status then
|
|
|
|
return result
|
|
|
|
end
|
2018-11-09 07:52:57 -07:00
|
|
|
local message = result
|
|
|
|
if type(result) == 'table' then
|
|
|
|
-- 'eq' returns several things
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type string
|
2018-11-09 07:52:57 -07:00
|
|
|
message = result.message
|
|
|
|
end
|
|
|
|
final_error = cat_err(final_error, message)
|
2018-02-16 11:42:05 -07:00
|
|
|
end
|
|
|
|
error(final_error)
|
|
|
|
end
|
|
|
|
|
2017-12-09 03:26:06 -07:00
|
|
|
local function call_and_stop_on_error(lsession, ...)
|
2023-01-31 15:35:04 -07:00
|
|
|
local status, result = Session.safe_pcall(...) -- luacheck: ignore
|
2014-11-06 19:54:49 -07:00
|
|
|
if not status then
|
2017-12-09 03:26:06 -07:00
|
|
|
lsession:stop()
|
2014-11-06 19:54:49 -07:00
|
|
|
last_error = result
|
|
|
|
return ''
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.set_method_error(err)
|
2019-01-24 11:15:39 -07:00
|
|
|
method_error = err
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param lsession test.Session
|
2024-01-22 17:53:15 -07:00
|
|
|
--- @param request_cb function?
|
|
|
|
--- @param notification_cb function?
|
|
|
|
--- @param setup_cb function?
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param timeout integer
|
|
|
|
--- @return {[1]: integer, [2]: string}
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
|
2024-01-15 12:49:08 -07:00
|
|
|
local on_request --- @type function?
|
|
|
|
local on_notification --- @type function?
|
|
|
|
local on_setup --- @type function?
|
2014-11-06 19:54:49 -07:00
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if request_cb then
|
|
|
|
function on_request(method, args)
|
2019-01-24 11:15:39 -07:00
|
|
|
method_error = nil
|
|
|
|
local result = call_and_stop_on_error(lsession, request_cb, method, args)
|
|
|
|
if method_error ~= nil then
|
|
|
|
return method_error, true
|
|
|
|
end
|
|
|
|
return result
|
2014-12-08 18:31:45 -07:00
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if notification_cb then
|
|
|
|
function on_notification(method, args)
|
2017-12-09 03:26:06 -07:00
|
|
|
call_and_stop_on_error(lsession, notification_cb, method, args)
|
2014-12-08 18:31:45 -07:00
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if setup_cb then
|
|
|
|
function on_setup()
|
2017-12-09 03:26:06 -07:00
|
|
|
call_and_stop_on_error(lsession, setup_cb)
|
2014-12-08 18:31:45 -07:00
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-10-27 14:34:42 -07:00
|
|
|
loop_running = true
|
2022-05-02 12:10:01 -07:00
|
|
|
lsession:run(on_request, on_notification, on_setup, timeout)
|
2014-10-27 14:34:42 -07:00
|
|
|
loop_running = false
|
|
|
|
if last_error then
|
|
|
|
local err = last_error
|
|
|
|
last_error = nil
|
|
|
|
error(err)
|
|
|
|
end
|
2022-07-25 01:16:33 -07:00
|
|
|
|
2022-05-02 12:10:01 -07:00
|
|
|
return lsession.eof_err
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.run(request_cb, notification_cb, setup_cb, timeout)
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session)
|
2022-07-25 01:16:33 -07:00
|
|
|
return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
|
2017-12-09 03:26:06 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.stop()
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session):stop()
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.nvim_prog_abs()
|
2019-05-02 00:56:22 -07:00
|
|
|
-- system(['build/bin/nvim']) does not work for whatever reason. It must
|
|
|
|
-- be executable searched in $PATH or something starting with / or ./.
|
2019-08-08 07:03:25 -07:00
|
|
|
if module.nvim_prog:match('[/\\]') then
|
|
|
|
return module.request('nvim_call_function', 'fnamemodify', { module.nvim_prog, ':p' })
|
2019-05-02 00:56:22 -07:00
|
|
|
else
|
2019-08-08 07:03:25 -07:00
|
|
|
return module.nvim_prog
|
2019-05-02 00:56:22 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-19 04:36:29 -07:00
|
|
|
-- Use for commands which expect nvim to quit.
|
|
|
|
-- The first argument can also be a timeout.
|
|
|
|
function module.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, module.pcall_err(fn_or_timeout, ...))
|
|
|
|
else
|
|
|
|
eq(
|
|
|
|
eof_err_msg,
|
|
|
|
module.pcall_err(function(timeout, fn, ...)
|
|
|
|
fn(...)
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session)
|
2022-07-19 04:36:29 -07:00
|
|
|
while session:next_message(timeout) do
|
|
|
|
end
|
|
|
|
if session.eof_err then
|
|
|
|
error(session.eof_err[2])
|
|
|
|
end
|
|
|
|
end, fn_or_timeout, ...)
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
2022-07-19 04:36:29 -07:00
|
|
|
end
|
2022-06-08 14:22:50 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Executes a Vimscript function via Lua.
|
|
|
|
--- Fails on Vimscript error, but does not update v:errmsg.
|
|
|
|
--- @param name string
|
|
|
|
--- @param ... any
|
|
|
|
--- @return any
|
2022-11-14 03:01:35 -07:00
|
|
|
function module.call_lua(name, ...)
|
|
|
|
return module.exec_lua([[return vim.call(...)]], name, ...)
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Sends user input to Nvim.
|
|
|
|
--- Does not fail on Vimscript error, but v:errmsg will be updated.
|
|
|
|
--- @param input string
|
2014-11-21 09:06:03 -07:00
|
|
|
local function nvim_feed(input)
|
|
|
|
while #input > 0 do
|
2019-08-08 07:03:25 -07:00
|
|
|
local written = module.request('nvim_input', input)
|
2019-08-09 16:26:43 -07:00
|
|
|
if written == nil then
|
|
|
|
module.assert_alive()
|
2019-09-01 15:51:02 -07:00
|
|
|
error('crash? (nvim_input returned nil)')
|
2019-08-09 16:26:43 -07:00
|
|
|
end
|
2014-11-21 09:06:03 -07:00
|
|
|
input = input:sub(written + 1)
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param ... string
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.feed(...)
|
2014-09-29 05:43:52 -07:00
|
|
|
for _, v in ipairs({ ... }) do
|
2014-12-08 18:31:45 -07:00
|
|
|
nvim_feed(dedent(v))
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param ... string
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.rawfeed(...)
|
2014-09-29 05:43:52 -07:00
|
|
|
for _, v in ipairs({ ... }) do
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed(dedent(v))
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
---@param ... string[]?
|
|
|
|
---@return string[]
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.merge_args(...)
|
2015-08-15 07:25:10 -07:00
|
|
|
local i = 1
|
2024-01-15 12:49:08 -07:00
|
|
|
local argv = {} --- @type string[]
|
2015-08-15 07:25:10 -07:00
|
|
|
for anum = 1, select('#', ...) do
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type string[]?
|
2015-08-15 07:25:10 -07:00
|
|
|
local args = select(anum, ...)
|
|
|
|
if args then
|
|
|
|
for _, arg in ipairs(args) do
|
|
|
|
argv[i] = arg
|
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return argv
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Removes Nvim startup args from `args` matching items in `args_rm`.
|
|
|
|
---
|
|
|
|
--- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
|
|
|
|
--- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
|
|
|
|
---
|
|
|
|
--- Example:
|
|
|
|
--- args={'--headless', '-u', 'NONE'}
|
|
|
|
--- args_rm={'--cmd', '-u'}
|
|
|
|
--- Result:
|
|
|
|
--- {'--headless'}
|
|
|
|
---
|
|
|
|
--- All matching cases are removed.
|
|
|
|
---
|
|
|
|
--- Example:
|
|
|
|
--- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
|
|
|
|
--- args_rm={'--cmd', '-u'}
|
|
|
|
--- Result:
|
|
|
|
--- {'-N'}
|
|
|
|
--- @param args string[]
|
|
|
|
--- @param args_rm string[]
|
|
|
|
--- @return string[]
|
2019-04-16 16:08:48 -07:00
|
|
|
local function remove_args(args, args_rm)
|
2024-01-15 12:49:08 -07:00
|
|
|
local new_args = {} --- @type string[]
|
2019-04-16 16:08:48 -07:00
|
|
|
local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' }
|
|
|
|
if not args_rm or #args_rm == 0 then
|
|
|
|
return { unpack(args) }
|
|
|
|
end
|
|
|
|
for _, v in ipairs(args_rm) do
|
|
|
|
assert(type(v) == 'string')
|
|
|
|
end
|
|
|
|
local last = ''
|
|
|
|
for _, arg in ipairs(args) do
|
2024-01-12 04:28:20 -07:00
|
|
|
if vim.tbl_contains(skip_following, last) then
|
2019-04-16 16:08:48 -07:00
|
|
|
last = ''
|
2024-01-12 04:28:20 -07:00
|
|
|
elseif vim.tbl_contains(args_rm, arg) then
|
2019-04-16 16:08:48 -07:00
|
|
|
last = arg
|
2024-01-12 04:28:20 -07:00
|
|
|
elseif arg == runtime_set and vim.tbl_contains(args_rm, 'runtimepath') then
|
2022-09-24 17:20:47 -07:00
|
|
|
table.remove(new_args) -- Remove the preceding "--cmd".
|
|
|
|
last = ''
|
2019-04-16 16:08:48 -07:00
|
|
|
else
|
|
|
|
table.insert(new_args, arg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return new_args
|
|
|
|
end
|
|
|
|
|
2023-01-14 18:34:21 -07:00
|
|
|
function module.check_close()
|
|
|
|
if not session then
|
|
|
|
return
|
|
|
|
end
|
2024-01-12 04:28:20 -07:00
|
|
|
local start_time = uv.now()
|
2023-01-14 18:34:21 -07:00
|
|
|
session:close()
|
2024-01-12 04:28:20 -07:00
|
|
|
uv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
|
|
|
|
local end_time = uv.now()
|
2022-09-20 13:03:16 -07:00
|
|
|
local delta = end_time - start_time
|
|
|
|
if delta > 500 then
|
|
|
|
print(
|
|
|
|
'nvim took '
|
|
|
|
.. delta
|
|
|
|
.. ' milliseconds to exit after last test\n'
|
|
|
|
.. 'This indicates a likely problem with the test even if it passed!\n'
|
|
|
|
)
|
|
|
|
io.stdout:flush()
|
|
|
|
end
|
2023-01-14 18:34:21 -07:00
|
|
|
session = nil
|
2022-09-20 13:03:16 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @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
|
2022-04-22 11:56:31 -07:00
|
|
|
function module.spawn(argv, merge, env, keep, io_extra)
|
2023-01-14 18:34:21 -07:00
|
|
|
if not keep then
|
|
|
|
module.check_close()
|
2021-06-12 14:23:05 -07:00
|
|
|
end
|
|
|
|
|
2016-06-26 08:16:54 -07:00
|
|
|
local child_stream =
|
2019-08-08 07:03:25 -07:00
|
|
|
ChildProcessStream.spawn(merge and module.merge_args(prepend_argv, argv) or argv, env, io_extra)
|
2016-04-13 05:21:32 -07:00
|
|
|
return Session.new(child_stream)
|
2015-03-17 04:45:13 -07:00
|
|
|
end
|
|
|
|
|
2016-06-26 12:35:14 -07:00
|
|
|
-- Creates a new Session connected by domain socket (named pipe) or TCP.
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.connect(file_or_address)
|
2016-06-26 12:35:14 -07:00
|
|
|
local addr, port = string.match(file_or_address, '(.*):(%d+)')
|
2023-01-31 15:35:04 -07:00
|
|
|
local stream = (addr and port) and SocketStream.connect(addr, port)
|
2016-06-26 12:35:14 -07:00
|
|
|
or SocketStream.open(file_or_address)
|
|
|
|
return Session.new(stream)
|
|
|
|
end
|
|
|
|
|
test: 'nofsync' with deadly signal #26415
Problem:
The test for 'nofsync' swapfile preservation on a deadly signal, does
not actually assert anything.
followup to 1fd29a28841dee3d25ff079eb24fc160eb02cb3c
Solution:
Check that swapfile contents are present after getting SIGTERM.
TODO: this doesn't really verify that 'fsync' was called; it still
passes with this patch:
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 216e39f3e81c..7a635520401d 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -838,7 +838,7 @@ void preserve_exit(const char *errmsg)
if (errmsg != NULL) {
os_errmsg("Vim: preserving files...\r\n");
}
- ml_sync_all(false, false, true); // preserve all swap files
+ ml_sync_all(false, false, false); // preserve all swap files
break;
}
}
However it correctly fails with this patch, at least:
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 216e39f3e81c..f2306c310ddc 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -838,7 +838,6 @@ void preserve_exit(const char *errmsg)
if (errmsg != NULL) {
os_errmsg("Vim: preserving files...\r\n");
}
- ml_sync_all(false, false, true); // preserve all swap files
break;
}
}
2023-12-06 08:11:36 -07:00
|
|
|
-- Starts (and returns) a new global Nvim session.
|
2019-04-16 16:08:48 -07:00
|
|
|
--
|
2018-05-29 22:21:45 -07:00
|
|
|
-- Parameters are interpreted as startup args, OR a map with these keys:
|
2019-04-16 16:08:48 -07:00
|
|
|
-- 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.
|
2018-05-29 22:21:45 -07:00
|
|
|
--
|
|
|
|
-- Example:
|
|
|
|
-- clear('-e')
|
2019-04-16 16:08:48 -07:00
|
|
|
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.clear(...)
|
2022-05-02 12:10:01 -07:00
|
|
|
module.set_session(module.spawn_argv(false, ...))
|
test: 'nofsync' with deadly signal #26415
Problem:
The test for 'nofsync' swapfile preservation on a deadly signal, does
not actually assert anything.
followup to 1fd29a28841dee3d25ff079eb24fc160eb02cb3c
Solution:
Check that swapfile contents are present after getting SIGTERM.
TODO: this doesn't really verify that 'fsync' was called; it still
passes with this patch:
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 216e39f3e81c..7a635520401d 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -838,7 +838,7 @@ void preserve_exit(const char *errmsg)
if (errmsg != NULL) {
os_errmsg("Vim: preserving files...\r\n");
}
- ml_sync_all(false, false, true); // preserve all swap files
+ ml_sync_all(false, false, false); // preserve all swap files
break;
}
}
However it correctly fails with this patch, at least:
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 216e39f3e81c..f2306c310ddc 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -838,7 +838,6 @@ void preserve_exit(const char *errmsg)
if (errmsg != NULL) {
os_errmsg("Vim: preserving files...\r\n");
}
- ml_sync_all(false, false, true); // preserve all swap files
break;
}
}
2023-12-06 08:11:36 -07:00
|
|
|
return module.get_session()
|
2022-05-02 12:10:01 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- same params as clear, but does returns the session instead
|
|
|
|
--- of replacing the default session
|
|
|
|
--- @return test.Session
|
2022-05-02 12:10:01 -07:00
|
|
|
function module.spawn_argv(keep, ...)
|
2022-04-22 11:56:31 -07:00
|
|
|
local argv, env, io_extra = module.new_argv(...)
|
2022-05-02 12:10:01 -07:00
|
|
|
return module.spawn(argv, nil, env, keep, io_extra)
|
2019-08-09 01:23:57 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @class test.new_argv.Opts
|
|
|
|
--- @field args? string[]
|
|
|
|
--- @field args_rm? string[]
|
|
|
|
--- @field env? table<string,string>
|
|
|
|
--- @field io_extra? uv.uv_pipe_t
|
|
|
|
|
|
|
|
--- Builds an argument list for use in clear().
|
|
|
|
---
|
|
|
|
--- @see clear() for parameters.
|
|
|
|
--- @param ... string
|
|
|
|
--- @return string[]
|
|
|
|
--- @return string[]?
|
|
|
|
--- @return uv.uv_pipe_t?
|
2019-08-09 01:23:57 -07:00
|
|
|
function module.new_argv(...)
|
2019-08-08 07:03:25 -07:00
|
|
|
local args = { unpack(module.nvim_argv) }
|
2019-04-16 16:38:59 -07:00
|
|
|
table.insert(args, '--headless')
|
2022-06-01 11:28:14 -07:00
|
|
|
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
|
2024-01-15 12:49:08 -07:00
|
|
|
local new_args --- @type string[]
|
|
|
|
local io_extra --- @type uv.uv_pipe_t?
|
|
|
|
local env --- @type string[]?
|
|
|
|
--- @type test.new_argv.Opts|string
|
2016-06-26 08:16:54 -07:00
|
|
|
local opts = select(1, ...)
|
2022-05-23 21:44:15 -07:00
|
|
|
if type(opts) ~= 'table' then
|
|
|
|
new_args = { ... }
|
|
|
|
else
|
2019-04-16 16:08:48 -07:00
|
|
|
args = remove_args(args, opts.args_rm)
|
2016-06-26 08:16:54 -07:00
|
|
|
if opts.env then
|
2024-01-15 12:49:08 -07:00
|
|
|
local env_opt = {} --- @type table<string,string>
|
2016-06-26 08:16:54 -07:00
|
|
|
for k, v in pairs(opts.env) do
|
|
|
|
assert(type(k) == 'string')
|
|
|
|
assert(type(v) == 'string')
|
2022-05-23 21:44:15 -07:00
|
|
|
env_opt[k] = v
|
2016-06-26 08:16:54 -07:00
|
|
|
end
|
|
|
|
for _, k in ipairs({
|
|
|
|
'HOME',
|
|
|
|
'ASAN_OPTIONS',
|
2019-08-20 17:30:18 -07:00
|
|
|
'TSAN_OPTIONS',
|
|
|
|
'MSAN_OPTIONS',
|
2019-04-16 16:08:48 -07:00
|
|
|
'LD_LIBRARY_PATH',
|
|
|
|
'PATH',
|
2016-06-26 08:16:54 -07:00
|
|
|
'NVIM_LOG_FILE',
|
2016-08-15 20:25:02 -07:00
|
|
|
'NVIM_RPLUGIN_MANIFEST',
|
2019-06-14 04:39:57 -07:00
|
|
|
'GCOV_ERROR_FILE',
|
2020-11-30 16:15:12 -07:00
|
|
|
'XDG_DATA_DIRS',
|
2019-10-06 13:26:54 -07:00
|
|
|
'TMPDIR',
|
2022-01-07 14:37:41 -07:00
|
|
|
'VIMRUNTIME',
|
2016-06-26 08:16:54 -07:00
|
|
|
}) do
|
2022-06-01 11:28:14 -07:00
|
|
|
-- Set these from the environment unless the caller defined them.
|
2022-05-23 21:44:15 -07:00
|
|
|
if not env_opt[k] then
|
|
|
|
env_opt[k] = os.getenv(k)
|
2017-02-02 20:18:16 -07:00
|
|
|
end
|
2016-06-26 08:16:54 -07:00
|
|
|
end
|
2022-06-01 11:28:14 -07:00
|
|
|
env = {}
|
2022-05-23 21:44:15 -07:00
|
|
|
for k, v in pairs(env_opt) do
|
2016-06-26 08:16:54 -07:00
|
|
|
env[#env + 1] = k .. '=' .. v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
new_args = opts.args or {}
|
2022-04-22 11:56:31 -07:00
|
|
|
io_extra = opts.io_extra
|
2016-06-26 08:16:54 -07:00
|
|
|
end
|
|
|
|
for _, arg in ipairs(new_args) do
|
2016-06-23 02:15:08 -07:00
|
|
|
table.insert(args, arg)
|
2015-08-29 08:10:06 -07:00
|
|
|
end
|
2022-04-22 11:56:31 -07:00
|
|
|
return args, env, io_extra
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param ... string
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.insert(...)
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed('i')
|
2014-12-08 18:31:45 -07:00
|
|
|
for _, v in ipairs({ ... }) do
|
|
|
|
local escaped = v:gsub('<', '<lt>')
|
2019-08-08 07:03:25 -07:00
|
|
|
module.rawfeed(escaped)
|
2014-12-08 18:31:45 -07:00
|
|
|
end
|
|
|
|
nvim_feed('<ESC>')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Executes an ex-command by user input. Because nvim_input() is used, Vimscript
|
|
|
|
--- errors will not manifest as client (lua) errors. Use command() for that.
|
|
|
|
--- @param ... string
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.feed_command(...)
|
2014-09-29 05:43:52 -07:00
|
|
|
for _, v in ipairs({ ... }) do
|
|
|
|
if v:sub(1, 1) ~= '/' then
|
|
|
|
-- not a search command, prefix with colon
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed(':')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
2014-12-08 18:31:45 -07:00
|
|
|
nvim_feed(v:gsub('<', '<lt>'))
|
|
|
|
nvim_feed('<CR>')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-03-25 09:58:48 -07:00
|
|
|
-- @deprecated use nvim_exec2()
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.source(code)
|
2022-03-27 10:25:55 -07:00
|
|
|
module.exec(dedent(code))
|
2014-11-11 02:12:19 -07:00
|
|
|
end
|
|
|
|
|
2019-12-31 12:21:57 -07:00
|
|
|
function module.has_powershell()
|
2022-11-21 17:13:30 -07:00
|
|
|
return module.eval('executable("' .. (is_os('win') and 'powershell' or 'pwsh') .. '")') == 1
|
2019-12-31 12:21:57 -07:00
|
|
|
end
|
|
|
|
|
2022-06-22 05:51:52 -07:00
|
|
|
--- Sets Nvim shell to powershell.
|
|
|
|
---
|
|
|
|
--- @param fake (boolean) If true, a fake will be used if powershell is not
|
|
|
|
--- found on the system.
|
|
|
|
--- @returns true if powershell was found on the system, else false.
|
|
|
|
function module.set_shell_powershell(fake)
|
|
|
|
local found = module.has_powershell()
|
|
|
|
if not fake then
|
|
|
|
assert(found)
|
|
|
|
end
|
2022-11-21 17:13:30 -07:00
|
|
|
local shell = found and (is_os('win') and 'powershell' or 'pwsh') or module.testprg('pwsh-test')
|
2023-03-19 14:25:12 -07:00
|
|
|
local cmd = 'Remove-Item -Force '
|
|
|
|
.. table.concat(
|
2023-04-22 03:04:05 -07:00
|
|
|
is_os('win') and { 'alias:cat', 'alias:echo', 'alias:sleep', 'alias:sort', 'alias:tee' }
|
2019-10-10 21:30:20 -07:00
|
|
|
or { 'alias:echo' },
|
|
|
|
','
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
2019-10-10 21:30:20 -07:00
|
|
|
.. ';'
|
2022-03-27 10:25:55 -07:00
|
|
|
module.exec([[
|
2019-10-10 01:16:02 -07:00
|
|
|
let &shell = ']] .. shell .. [['
|
2021-07-02 05:15:40 -07:00
|
|
|
set shellquote= shellxquote=
|
2023-03-19 14:25:12 -07:00
|
|
|
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command '
|
|
|
|
let &shellcmdflag .= '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();'
|
|
|
|
let &shellcmdflag .= '$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';'
|
|
|
|
let &shellcmdflag .= ']] .. cmd .. [['
|
|
|
|
let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode'
|
2023-04-22 03:04:05 -07:00
|
|
|
let &shellpipe = '2>&1 | %%{ "$_" } | tee %s; exit $LastExitCode'
|
2017-01-11 21:08:19 -07:00
|
|
|
]])
|
2022-06-22 05:51:52 -07:00
|
|
|
return found
|
2017-01-11 21:08:19 -07:00
|
|
|
end
|
|
|
|
|
2024-01-22 17:53:15 -07:00
|
|
|
---@param func function
|
|
|
|
---@return table<string,function>
|
2022-11-14 03:01:35 -07:00
|
|
|
function module.create_callindex(func)
|
2024-01-15 09:10:51 -07:00
|
|
|
return setmetatable({}, {
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param tbl table<any,function>
|
|
|
|
--- @param arg1 string
|
|
|
|
--- @return function
|
2022-11-14 03:01:35 -07:00
|
|
|
__index = function(tbl, arg1)
|
|
|
|
local ret = function(...)
|
|
|
|
return func(arg1, ...)
|
|
|
|
end
|
|
|
|
tbl[arg1] = ret
|
|
|
|
return ret
|
|
|
|
end,
|
|
|
|
})
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param method string
|
|
|
|
--- @param ... any
|
2024-01-15 09:10:51 -07:00
|
|
|
function module.nvim_async(method, ...)
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(session):notify(method, ...)
|
2016-06-08 02:26:06 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Executes a Vimscript function via RPC.
|
|
|
|
--- Fails on Vimscript error, but does not update v:errmsg.
|
|
|
|
--- @param name string
|
|
|
|
--- @param ... any
|
|
|
|
--- @return any
|
2024-01-15 09:10:51 -07:00
|
|
|
function module.call(name, ...)
|
|
|
|
return module.request('nvim_call_function', name, { ... })
|
2015-07-27 04:39:38 -07:00
|
|
|
end
|
|
|
|
|
2022-11-14 03:01:35 -07:00
|
|
|
module.async_meths = module.create_callindex(module.nvim_async)
|
2014-10-08 09:56:01 -07:00
|
|
|
|
2024-01-15 09:10:51 -07:00
|
|
|
module.rpc = {
|
|
|
|
fn = module.create_callindex(module.call),
|
|
|
|
api = module.create_callindex(module.request),
|
|
|
|
}
|
2014-10-08 09:56:01 -07:00
|
|
|
|
2024-01-15 09:10:51 -07:00
|
|
|
module.lua = {
|
|
|
|
fn = module.create_callindex(module.call_lua),
|
|
|
|
api = module.create_callindex(module.request_lua),
|
|
|
|
}
|
2022-11-14 03:01:35 -07:00
|
|
|
|
|
|
|
module.describe_lua_and_rpc = function(describe)
|
|
|
|
return function(what, tests)
|
|
|
|
local function d(flavour)
|
|
|
|
describe(string.format('%s (%s)', what, flavour), function(...)
|
|
|
|
return tests(module[flavour].api, ...)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
d('rpc')
|
|
|
|
d('lua')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
2022-11-14 03:01:35 -07:00
|
|
|
end
|
|
|
|
|
2024-01-12 05:44:54 -07:00
|
|
|
--- add for typing. The for loop after will overwrite this
|
2024-01-12 10:59:57 -07:00
|
|
|
module.api = vim.api
|
|
|
|
module.fn = vim.fn
|
2024-01-12 05:44:54 -07:00
|
|
|
|
2024-01-12 10:59:57 -07:00
|
|
|
for name, fns in pairs(module.rpc) do
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @diagnostic disable-next-line:no-unknown
|
2024-01-12 10:59:57 -07:00
|
|
|
module[name] = fns
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2024-01-12 06:11:28 -07:00
|
|
|
-- Executes an ex-command. Vimscript errors manifest as client (lua) errors, but
|
|
|
|
-- v:errmsg will not be updated.
|
2024-01-12 10:59:57 -07:00
|
|
|
module.command = module.api.nvim_command
|
2024-01-12 06:11:28 -07:00
|
|
|
|
2024-01-15 09:10:51 -07:00
|
|
|
-- Evaluates a Vimscript expression.
|
|
|
|
-- Fails on Vimscript error, but does not update v:errmsg.
|
|
|
|
module.eval = module.api.nvim_eval
|
|
|
|
|
2020-10-19 11:17:51 -07:00
|
|
|
function module.poke_eventloop()
|
|
|
|
-- Execute 'nvim_eval' (a deferred function) to
|
|
|
|
-- force at least one main_loop iteration
|
2024-01-15 09:10:51 -07:00
|
|
|
module.api.nvim_eval('1')
|
2015-02-12 09:29:48 -07:00
|
|
|
end
|
|
|
|
|
2020-01-08 10:32:49 -07:00
|
|
|
function module.buf_lines(bufnr)
|
|
|
|
return module.exec_lua('return vim.api.nvim_buf_get_lines((...), 0, -1, false)', bufnr)
|
|
|
|
end
|
|
|
|
|
2021-08-22 13:55:28 -07:00
|
|
|
---@see buf_lines()
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.curbuf_contents()
|
2020-10-19 11:17:51 -07:00
|
|
|
module.poke_eventloop() -- Before inspecting the buffer, do whatever.
|
2024-01-12 10:59:57 -07:00
|
|
|
return table.concat(module.api.nvim_buf_get_lines(0, 0, -1, true), '\n')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.expect(contents)
|
|
|
|
return eq(dedent(contents), module.curbuf_contents())
|
2014-11-21 09:06:03 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.expect_any(contents)
|
2017-02-28 01:34:02 -07:00
|
|
|
contents = dedent(contents)
|
2019-08-08 07:03:25 -07:00
|
|
|
return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true))
|
2017-02-28 01:34:02 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param expected any[]
|
|
|
|
--- @param received any[]
|
|
|
|
--- @param kind string
|
|
|
|
--- @return any
|
2020-09-21 01:37:28 -07:00
|
|
|
function module.expect_events(expected, received, kind)
|
|
|
|
if not pcall(eq, expected, received) then
|
|
|
|
local msg = 'unexpected ' .. kind .. ' received.\n\n'
|
|
|
|
|
|
|
|
msg = msg .. 'received events:\n'
|
|
|
|
for _, e in ipairs(received) do
|
2024-01-12 04:51:31 -07:00
|
|
|
msg = msg .. ' ' .. vim.inspect(e) .. ';\n'
|
2020-09-21 01:37:28 -07:00
|
|
|
end
|
|
|
|
msg = msg .. '\nexpected events:\n'
|
|
|
|
for _, e in ipairs(expected) do
|
2024-01-12 04:51:31 -07:00
|
|
|
msg = msg .. ' ' .. vim.inspect(e) .. ';\n'
|
2020-09-21 01:37:28 -07:00
|
|
|
end
|
|
|
|
fail(msg)
|
|
|
|
end
|
|
|
|
return received
|
|
|
|
end
|
|
|
|
|
2019-08-09 16:26:43 -07:00
|
|
|
-- Checks that the Nvim session did not terminate.
|
|
|
|
function module.assert_alive()
|
2019-09-01 15:51:02 -07:00
|
|
|
assert(2 == module.eval('1+1'), 'crash? request failed')
|
2019-08-09 16:26:43 -07:00
|
|
|
end
|
|
|
|
|
2020-01-13 01:19:20 -07:00
|
|
|
-- Asserts that buffer is loaded and visible in the current tabpage.
|
|
|
|
function module.assert_visible(bufnr, visible)
|
|
|
|
assert(type(visible) == 'boolean')
|
2024-01-12 10:59:57 -07:00
|
|
|
eq(visible, module.api.nvim_buf_is_loaded(bufnr))
|
2020-01-13 01:19:20 -07:00
|
|
|
if visible then
|
|
|
|
assert(
|
2024-01-12 10:59:57 -07:00
|
|
|
-1 ~= module.fn.bufwinnr(bufnr),
|
2020-01-13 01:19:20 -07:00
|
|
|
'expected buffer to be visible in current tabpage: ' .. tostring(bufnr)
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
2020-01-13 01:19:20 -07:00
|
|
|
else
|
|
|
|
assert(
|
2024-01-12 10:59:57 -07:00
|
|
|
-1 == module.fn.bufwinnr(bufnr),
|
2020-01-13 01:19:20 -07:00
|
|
|
'expected buffer NOT visible in current tabpage: ' .. tostring(bufnr)
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
2020-01-13 01:19:20 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param path string
|
2016-09-18 14:43:32 -07:00
|
|
|
local function do_rmdir(path)
|
2024-01-12 04:28:20 -07:00
|
|
|
local stat = uv.fs_stat(path)
|
2023-04-04 12:59:06 -07:00
|
|
|
if stat == nil then
|
|
|
|
return
|
2019-09-06 09:19:57 -07:00
|
|
|
end
|
2023-04-04 12:59:06 -07:00
|
|
|
if stat.type ~= 'directory' then
|
2019-09-06 09:19:57 -07:00
|
|
|
error(string.format('rmdir: not a directory: %s', path))
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
2023-04-04 12:59:06 -07:00
|
|
|
for file in vim.fs.dir(path) do
|
2016-03-06 15:53:55 -07:00
|
|
|
if file ~= '.' and file ~= '..' then
|
2016-06-04 23:51:50 -07:00
|
|
|
local abspath = path .. '/' .. file
|
2023-04-04 12:59:06 -07:00
|
|
|
if global_helpers.isdir(abspath) then
|
2017-04-10 13:57:49 -07:00
|
|
|
do_rmdir(abspath) -- recurse
|
2016-06-04 23:51:50 -07:00
|
|
|
else
|
|
|
|
local ret, err = os.remove(abspath)
|
|
|
|
if not ret then
|
2017-04-10 13:57:49 -07:00
|
|
|
if not session then
|
2017-04-10 10:12:56 -07:00
|
|
|
error('os.remove: ' .. err)
|
2017-04-10 13:57:49 -07:00
|
|
|
else
|
|
|
|
-- Try Nvim delete(): it handles `readonly` attribute on Windows,
|
|
|
|
-- and avoids Lua cross-version/platform incompatibilities.
|
2019-08-08 07:03:25 -07:00
|
|
|
if -1 == module.call('delete', abspath) then
|
2017-04-10 13:57:49 -07:00
|
|
|
local hint = (is_os('win') and ' (hint: try :%bwipeout! before rmdir())' or '')
|
|
|
|
error('delete() failed' .. hint .. ': ' .. abspath)
|
|
|
|
end
|
2017-04-10 10:12:56 -07:00
|
|
|
end
|
2016-06-04 23:51:50 -07:00
|
|
|
end
|
2016-03-06 15:53:55 -07:00
|
|
|
end
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
|
|
|
end
|
2024-01-12 04:28:20 -07:00
|
|
|
local ret, err = uv.fs_rmdir(path)
|
2015-07-20 07:51:53 -07:00
|
|
|
if not ret then
|
2023-04-04 12:59:06 -07:00
|
|
|
error('luv.fs_rmdir(' .. path .. '): ' .. err)
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-12 04:28:20 -07:00
|
|
|
local start_dir = uv.cwd()
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.rmdir(path)
|
2016-09-19 18:02:48 -07:00
|
|
|
local ret, _ = pcall(do_rmdir, path)
|
2019-09-04 06:58:04 -07:00
|
|
|
if not ret and is_os('win') then
|
2017-01-03 22:35:21 -07:00
|
|
|
-- Maybe "Permission denied"; try again after changing the nvim
|
|
|
|
-- process to the top-level directory.
|
2019-08-08 07:03:25 -07:00
|
|
|
module.command([[exe 'cd '.fnameescape(']] .. start_dir .. "')")
|
2017-01-03 22:35:21 -07:00
|
|
|
ret, _ = pcall(do_rmdir, path)
|
|
|
|
end
|
2016-09-18 14:43:32 -07:00
|
|
|
-- During teardown, the nvim process may not exit quickly enough, then rmdir()
|
|
|
|
-- will fail (on Windows).
|
|
|
|
if not ret then -- Try again.
|
|
|
|
sleep(1000)
|
|
|
|
do_rmdir(path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.exc_exec(cmd)
|
|
|
|
module.command(([[
|
2015-09-18 15:53:58 -07:00
|
|
|
try
|
|
|
|
execute "%s"
|
|
|
|
catch
|
|
|
|
let g:__exception = v:exception
|
|
|
|
endtry
|
|
|
|
]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0')))
|
2019-08-08 07:03:25 -07:00
|
|
|
local ret = module.eval('get(g:, "__exception", 0)')
|
|
|
|
module.command('unlet! g:__exception')
|
2015-09-18 15:53:58 -07:00
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param cond boolean
|
|
|
|
--- @param reason string
|
|
|
|
--- @return boolean
|
2022-11-13 06:52:19 -07:00
|
|
|
function module.skip(cond, reason)
|
|
|
|
if cond then
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type fun(reason: string)
|
2022-11-13 06:52:19 -07:00
|
|
|
local pending = getfenv(2).pending
|
|
|
|
pending(reason or 'FIXME')
|
2016-08-15 16:42:12 -07:00
|
|
|
return true
|
|
|
|
end
|
2024-01-15 12:49:08 -07:00
|
|
|
return false
|
2016-08-15 16:42:12 -07:00
|
|
|
end
|
|
|
|
|
2017-01-03 20:10:38 -07:00
|
|
|
-- Calls pending() and returns `true` if the system is too slow to
|
|
|
|
-- run fragile or expensive tests. Else returns `false`.
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.skip_fragile(pending_fn, cond)
|
2017-01-03 20:10:38 -07:00
|
|
|
if pending_fn == nil or type(pending_fn) ~= type(function() end) then
|
|
|
|
error('invalid pending_fn')
|
|
|
|
end
|
|
|
|
if cond then
|
|
|
|
pending_fn('skipped (test is fragile on this system)', function() end)
|
|
|
|
return true
|
|
|
|
elseif os.getenv('TEST_SKIP_FRAGILE') then
|
|
|
|
pending_fn('skipped (TEST_SKIP_FRAGILE)', function() end)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2020-09-08 00:47:10 -07:00
|
|
|
function module.exec(code)
|
2024-01-12 10:59:57 -07:00
|
|
|
module.api.nvim_exec2(code, {})
|
2020-09-08 00:47:10 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param code string
|
|
|
|
--- @return string
|
2020-09-08 00:47:10 -07:00
|
|
|
function module.exec_capture(code)
|
2024-01-12 10:59:57 -07:00
|
|
|
return module.api.nvim_exec2(code, { output = true }).output
|
2020-09-08 00:47:10 -07:00
|
|
|
end
|
|
|
|
|
2023-11-12 05:54:27 -07:00
|
|
|
--- @param code string
|
|
|
|
--- @return any
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.exec_lua(code, ...)
|
2024-01-15 09:10:51 -07:00
|
|
|
return module.api.nvim_exec_lua(code, { ... })
|
2019-06-23 11:10:28 -07:00
|
|
|
end
|
|
|
|
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.get_pathsep()
|
2022-11-21 17:13:30 -07:00
|
|
|
return is_os('win') and '\\' or '/'
|
2016-11-05 11:34:22 -07:00
|
|
|
end
|
|
|
|
|
2022-06-22 05:51:52 -07:00
|
|
|
--- Gets the filesystem root dir, namely "/" or "C:/".
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.pathroot()
|
2018-01-24 03:01:14 -07:00
|
|
|
local pathsep = package.config:sub(1, 1)
|
2022-11-21 17:13:30 -07:00
|
|
|
return is_os('win') and (module.nvim_dir:sub(1, 2) .. pathsep) or '/'
|
2018-01-24 03:01:14 -07:00
|
|
|
end
|
|
|
|
|
2022-06-22 05:51:52 -07:00
|
|
|
--- Gets the full `…/build/bin/{name}` path of a test program produced by
|
|
|
|
--- `test/functional/fixtures/CMakeLists.txt`.
|
|
|
|
---
|
|
|
|
--- @param name (string) Name of the test program.
|
|
|
|
function module.testprg(name)
|
2022-11-21 17:13:30 -07:00
|
|
|
local ext = module.is_os('win') and '.exe' or ''
|
2022-06-22 05:51:52 -07:00
|
|
|
return ('%s/%s%s'):format(module.nvim_dir, name, ext)
|
|
|
|
end
|
|
|
|
|
feat(extmark): support proper multiline ranges
The removes the previous restriction that nvim_buf_set_extmark()
could not be used to highlight arbitrary multi-line regions
The problem can be summarized as follows: let's assume an extmark with a
hl_group is placed covering the region (5,0) to (50,0) Now, consider
what happens if nvim needs to redraw a window covering the lines 20-30.
It needs to be able to ask the marktree what extmarks cover this region,
even if they don't begin or end here.
Therefore the marktree needs to be augmented with the information covers
a point, not just what marks begin or end there. To do this, we augment
each node with a field "intersect" which is a set the ids of the
marks which overlap this node, but only if it is not part of the set of
any parent. This ensures the number of nodes that need to be explicitly
marked grows only logarithmically with the total number of explicitly
nodes (and thus the number of of overlapping marks).
Thus we can quickly iterate all marks which overlaps any query position
by looking up what leaf node contains that position. Then we only need
to consider all "start" marks within that leaf node, and the "intersect"
set of that node and all its parents.
Now, and the major source of complexity is that the tree restructuring
operations (to ensure that each node has T-1 <= size <= 2*T-1) also need
to update these sets. If a full inner node is split in two, one of the
new parents might start to completely overlap some ranges and its ids
will need to be moved from its children's sets to its own set.
Similarly, if two undersized nodes gets joined into one, it might no
longer completely overlap some ranges, and now the children which do
needs to have the have the ids in its set instead. And then there are
the pivots! Yes the pivot operations when a child gets moved from one
parent to another.
2020-11-22 02:10:37 -07:00
|
|
|
function module.is_asan()
|
|
|
|
local version = module.eval('execute("verbose version")')
|
|
|
|
return version:match('-fsanitize=[a-z,]*address')
|
|
|
|
end
|
|
|
|
|
2022-05-03 06:08:35 -07:00
|
|
|
-- Returns a valid, platform-independent Nvim listen address.
|
2017-11-26 15:15:17 -07:00
|
|
|
-- Useful for communicating with child instances.
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.new_pipename()
|
2017-11-26 15:15:17 -07:00
|
|
|
-- HACK: Start a server temporarily, get the name, then stop it.
|
2019-08-08 07:03:25 -07:00
|
|
|
local pipename = module.eval('serverstart()')
|
2024-01-12 10:59:57 -07:00
|
|
|
module.fn.serverstop(pipename)
|
2023-11-23 08:05:52 -07:00
|
|
|
-- Remove the pipe so that trying to connect to it without a server listening
|
|
|
|
-- will be an error instead of a hang.
|
|
|
|
os.remove(pipename)
|
2017-11-26 15:15:17 -07:00
|
|
|
return pipename
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param provider string
|
2024-01-07 05:05:03 -07:00
|
|
|
--- @return string|boolean?
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.missing_provider(provider)
|
2024-01-22 14:07:14 -07:00
|
|
|
if provider == 'ruby' or provider == 'perl' then
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @type string?
|
2024-01-22 14:07:14 -07:00
|
|
|
local e = module.exec_lua("return {require('vim.provider." .. provider .. "').detect()}")[2]
|
2020-09-05 15:02:46 -07:00
|
|
|
return e ~= '' and e or false
|
2024-01-22 14:07:14 -07:00
|
|
|
elseif provider == 'node' then
|
|
|
|
--- @type string?
|
|
|
|
local e = module.fn['provider#node#Detect']()[2]
|
|
|
|
return e ~= '' and e or false
|
2024-01-07 05:05:03 -07:00
|
|
|
elseif provider == 'python' then
|
|
|
|
return module.exec_lua([[return {require('vim.provider.python').detect_by_module('neovim')}]])[2]
|
2017-05-12 10:03:05 -07:00
|
|
|
end
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(false, 'Unknown provider: ' .. provider)
|
2017-05-12 10:03:05 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param obj string|table
|
|
|
|
--- @return any
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.alter_slashes(obj)
|
2022-11-21 17:13:30 -07:00
|
|
|
if not is_os('win') then
|
2017-05-22 14:46:57 -07:00
|
|
|
return obj
|
|
|
|
end
|
|
|
|
if type(obj) == 'string' then
|
|
|
|
local ret = obj:gsub('/', '\\')
|
|
|
|
return ret
|
|
|
|
elseif type(obj) == 'table' then
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @cast obj table<any,any>
|
|
|
|
local ret = {} --- @type table<any,any>
|
2017-05-22 14:46:57 -07:00
|
|
|
for k, v in pairs(obj) do
|
2019-08-08 07:03:25 -07:00
|
|
|
ret[k] = module.alter_slashes(v)
|
2017-05-22 14:46:57 -07:00
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
2024-01-15 12:49:08 -07:00
|
|
|
assert(false, 'expected string or table of strings, got ' .. type(obj))
|
2017-05-22 14:46:57 -07:00
|
|
|
end
|
|
|
|
|
2019-08-09 06:32:38 -07:00
|
|
|
local load_factor = 1
|
2022-11-21 17:13:30 -07:00
|
|
|
if global_helpers.is_ci() then
|
2019-08-09 06:32:38 -07:00
|
|
|
-- Compute load factor only once (but outside of any tests).
|
|
|
|
module.clear()
|
2023-03-06 20:13:04 -07:00
|
|
|
module.request('nvim_command', 'source test/old/testdir/load.vim')
|
2019-08-09 06:32:38 -07:00
|
|
|
load_factor = module.request('nvim_eval', 'g:test_load_factor')
|
|
|
|
end
|
2024-01-15 12:49:08 -07:00
|
|
|
|
|
|
|
--- @param num number
|
|
|
|
--- @return number
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.load_adjust(num)
|
2019-01-16 13:21:10 -07:00
|
|
|
return math.ceil(num * load_factor)
|
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param ctx table<string,any>
|
|
|
|
--- @return table
|
2019-08-08 07:03:25 -07:00
|
|
|
function module.parse_context(ctx)
|
2024-01-15 12:49:08 -07:00
|
|
|
local parsed = {} --- @type table<string,any>
|
2019-09-14 18:52:16 -07:00
|
|
|
for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param v any
|
2024-01-12 04:28:20 -07:00
|
|
|
parsed[item] = vim.tbl_filter(function(v)
|
2019-07-17 13:00:50 -07:00
|
|
|
return type(v) == 'table'
|
2019-08-08 07:03:25 -07:00
|
|
|
end, module.call('msgpackparse', ctx[item]))
|
2019-07-17 13:00:50 -07:00
|
|
|
end
|
2019-09-14 18:52:16 -07:00
|
|
|
parsed['bufs'] = parsed['bufs'][1]
|
2024-01-15 12:49:08 -07:00
|
|
|
--- @param v any
|
2024-01-12 04:28:20 -07:00
|
|
|
return vim.tbl_map(function(v)
|
2019-07-17 13:00:50 -07:00
|
|
|
if #v == 0 then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
return v
|
|
|
|
end, parsed)
|
|
|
|
end
|
|
|
|
|
2019-08-28 13:47:54 -07:00
|
|
|
function module.add_builddir_to_rtp()
|
|
|
|
-- Add runtime from build dir for doc/tags (used with :help).
|
2024-01-15 12:41:22 -07:00
|
|
|
module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir))
|
2019-08-28 13:47:54 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Kill (reap) a process by PID.
|
|
|
|
--- @param pid string
|
|
|
|
--- @return boolean?
|
2019-09-11 18:26:35 -07:00
|
|
|
function module.os_kill(pid)
|
2022-11-21 17:13:30 -07:00
|
|
|
return os.execute(
|
2024-01-02 18:09:18 -07:00
|
|
|
(
|
2019-09-11 18:26:35 -07:00
|
|
|
is_os('win') and 'taskkill /f /t /pid ' .. pid .. ' > nul'
|
|
|
|
or 'kill -9 ' .. pid .. ' > /dev/null'
|
|
|
|
)
|
2024-01-02 18:09:18 -07:00
|
|
|
)
|
2019-09-11 18:26:35 -07:00
|
|
|
end
|
|
|
|
|
2024-01-15 12:49:08 -07:00
|
|
|
--- Create folder with non existing parents
|
|
|
|
--- @param path string
|
|
|
|
--- @return boolean?
|
2021-05-31 04:35:13 -07:00
|
|
|
function module.mkdir_p(path)
|
2022-11-21 17:13:30 -07:00
|
|
|
return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path))
|
2021-05-31 04:35:13 -07:00
|
|
|
end
|
|
|
|
|
2023-11-12 05:54:27 -07:00
|
|
|
--- @class test.functional.helpers: test.helpers
|
2024-01-12 04:28:20 -07:00
|
|
|
module = vim.tbl_extend('error', module, global_helpers)
|
2017-01-22 06:13:10 -07:00
|
|
|
|
2023-11-12 05:54:27 -07:00
|
|
|
--- @return test.functional.helpers
|
2016-04-23 16:53:11 -07:00
|
|
|
return function(after_each)
|
|
|
|
if after_each then
|
2016-11-04 08:20:58 -07:00
|
|
|
after_each(function()
|
|
|
|
check_logs()
|
|
|
|
check_cores('build/bin/nvim')
|
2018-12-01 08:44:36 -07:00
|
|
|
if session then
|
|
|
|
local msg = session:next_message(0)
|
|
|
|
if msg then
|
|
|
|
if msg[1] == 'notification' and msg[2] == 'nvim_error_event' then
|
|
|
|
error(msg[3][2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-11-04 08:20:58 -07:00
|
|
|
end)
|
2016-04-23 16:53:11 -07:00
|
|
|
end
|
2017-01-28 14:52:48 -07:00
|
|
|
return module
|
2016-04-23 16:53:11 -07:00
|
|
|
end
|