feat(api): mark nvim_create_autocmd as api-fast #40836

Problem:
nvim_create_autocmd() isn't |api-fast|, so modules that create autocmds
(e.g. vim.treesitter.query) can't be require()d in a fast event context.

Solution:
Mark it |api-fast|. Compile autocmd patterns with RE_NOBREAK so
aucmd_next() won't os_breakcheck() mid-iteration, where a fast
nvim_create_autocmd() could realloc the autocmds vector and dangle the
caller's AutoPat/AutoCmd.

RE_NOBREAK is low-risk because:
- aucmd_next()'s loop checks CTRL-C: `(for (… i < apc->ausize && !got_int; …)`.
- `line_breakcheck()` (`autocmd.c:1912`) runs once per matched autocmd.
- Each autocmd _execution_ runs through `do_cmdline`, which has its own
  breakchecks.

However this does admit risk of a pathological case:
a catastrophic-backtracking glob matched against a very long `User`
event-pattern.

Co-authored-by: Riley Bruins <ribru17@hotmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
This commit is contained in:
Justin M. Keyes
2026-07-19 11:38:14 -04:00
committed by GitHub
parent 746f5682a7
commit d1b3a9924d
5 changed files with 20 additions and 4 deletions

View File

@@ -24,7 +24,11 @@ describe('|api-fast| functions', function()
keycode = vim.keycode('<C-a>'),
keytrans = vim.fn.keytrans(vim.keycode('<C-Home>')),
nr2char = vim.fn.nr2char(65),
replace_termcodes = vim.api.nvim_replace_termcodes('<Esc>', true, true, true),
nvim_create_autocmd = vim.api.nvim_create_autocmd('User', {
pattern = 'Fast',
callback = function() end,
}) > 0,
nvim_replace_termcodes = vim.api.nvim_replace_termcodes('<Esc>', true, true, true),
str2list = vim.fn.str2list('AB'),
strcharlen = vim.fn.strcharlen('abc'),
strcharpart = vim.fn.strcharpart('héllo', 1, 2),
@@ -53,7 +57,8 @@ describe('|api-fast| functions', function()
keycode = '\1', -- <C-a>
keytrans = '<C-Home>',
nr2char = 'A',
replace_termcodes = '\27', -- <Esc>
nvim_create_autocmd = true,
nvim_replace_termcodes = '\27', -- <Esc>
str2list = { 65, 66 },
strcharlen = 3,
strcharpart = 'él',
@@ -87,6 +92,9 @@ describe('|api-fast| functions', function()
-- for `veryfast_breakcheck`). Small input could pass (false negative).
local big = ('héllo wörld '):rep(100000) -- 1.2M chars.
local aupat = ('a'):rep(80000) -- Must stay under the "E339: Pattern too long" limit.
vim.api.nvim_create_autocmd('User', { pattern = aupat, callback = function() end })
local cases = {
byteidx = function()
vim.fn.byteidx(big, 500000)
@@ -106,7 +114,10 @@ describe('|api-fast| functions', function()
nr2char = function()
vim.fn.nr2char(65)
end,
replace_termcodes = function()
nvim_create_autocmd = function()
vim.api.nvim_exec_autocmds('User', { pattern = aupat })
end,
nvim_replace_termcodes = function()
vim.api.nvim_replace_termcodes('<Esc>', true, true, true)
end,
str2list = function()