fix(lua): vim.regex():match_str abort in Luv callback #41008

This commit is contained in:
Justin M. Keyes 2026-07-27 07:01:54 -04:00 committed by GitHub
parent 8ee8869a91
commit 80cd482577
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 32 additions and 5 deletions

View File

@ -5033,8 +5033,10 @@ vim.re.updatelocale() *vim.re.updatelocale()*
==============================================================================
Lua module: vim.regex *vim.regex*
Vim regexes can be used directly from Lua. Currently they only allow matching
within a single line.
The `vim.regex` interface allows Lua code to use Vim regexes.
• Matches within a single line.
• Does not check for interrupts (CTRL-C), so can be used from an |api-fast|
context.
*regex:match_line()*

View File

@ -4,8 +4,9 @@ error('Cannot require a meta file')
-- luacheck: no unused args
--- @brief Vim regexes can be used directly from Lua. Currently they only allow
--- matching within a single line.
--- @brief The `vim.regex` interface allows Lua code to use Vim regexes.
--- - Matches within a single line.
--- - Does not check for interrupts (CTRL-C), so can be used from an |api-fast| context.
--- Parses the Vim regex `re` and returns a regex object. Regexes are "magic" and case-sensitive by
--- default, regardless of 'magic' and 'ignorecase'. They can be controlled with flags, see |/magic|

View File

@ -304,7 +304,8 @@ int nlua_regex(lua_State *lstate)
regprog_T *prog = NULL;
TRY_WRAP(&err, {
prog = vim_regcomp(text, RE_AUTO | RE_MAGIC | RE_STRICT);
// RE_NOBREAK: so vim.regex() works in api-fast context. #18111
prog = vim_regcomp(text, RE_AUTO | RE_MAGIC | RE_STRICT | RE_NOBREAK);
});
if (ERROR_SET(&err)) {

View File

@ -1814,6 +1814,29 @@ describe('lua stdlib', function()
-- vim.regex() error inside :silent! should not crash. #20546
command([[silent! lua vim.regex('\\z')]])
assert_alive()
-- match_str() in a fast (luv) callback does not abort. #18111
eq(
{ true, true },
exec_lua(function()
local re = vim.regex('^.*\\.go$')
local res
local timer = assert(vim.uv.new_timer())
timer:start(10, 0, function()
-- Enough matches to reach BREAKCHECK_SKIP, which would abort (reentrant event loop).
local ok = true
for _ = 1, 3000 do
ok = ok and re:match_str('internal/config/config.go') ~= nil
end
res = { ok, vim.in_fast_event() }
timer:close()
end)
vim.wait(1000, function()
return res ~= nil
end)
return res
end)
)
end)
it('vim.defer_fn', function()