fix(init): prevent infinite filetype retry loop on did_filetype

Problem: the deferred filetype retry in ensure_cache created an infinite
invalidation loop. did_filetype() stays 1 for the lifetime of a buffer
opened via FileType autocmd — it never resets in vim.schedule callbacks.
Each retry reparsed, found has_nil_ft=true again, and scheduled another
retry. Each iteration cleared all extmarks via pending_clear, causing the
deferred syntax pass to bail on its tick check. Result: language syntax
highlighting never settled on buffers containing files with
function-handled extensions (.sh, .bash, .conf, etc).

Solution: add ft_retry_pending guard table. Set before scheduling the
retry, check before scheduling again. The flag is set while the callback
is pending and during the synchronous redraw inside it, preventing the
reparse from scheduling another iteration. Cleared after the callback
completes. Cleaned up on BufWipeout.
This commit is contained in:
Barrett Ruth 2026-03-04 13:44:57 -05:00
parent 907387b64f
commit 0a10f3882d
Signed by: barrett
GPG key ID: A6C96C9349D2FC81

View file

@ -169,6 +169,9 @@ local fast_hl_opts = {} ---@type diffs.HunkOpts
---@type table<integer, boolean> ---@type table<integer, boolean>
local attached_buffers = {} local attached_buffers = {}
---@type table<integer, boolean>
local ft_retry_pending = {}
---@type table<integer, boolean> ---@type table<integer, boolean>
local diff_windows = {} local diff_windows = {}
@ -334,13 +337,15 @@ local function ensure_cache(bufnr)
has_nil_ft = true has_nil_ft = true
end end
end end
if has_nil_ft and vim.fn.did_filetype() ~= 0 then if has_nil_ft and vim.fn.did_filetype() ~= 0 and not ft_retry_pending[bufnr] then
ft_retry_pending[bufnr] = true
vim.schedule(function() vim.schedule(function()
if vim.api.nvim_buf_is_valid(bufnr) and hunk_cache[bufnr] then if vim.api.nvim_buf_is_valid(bufnr) and hunk_cache[bufnr] then
dbg('retrying filetype detection for buffer %d (was blocked by did_filetype)', bufnr) dbg('retrying filetype detection for buffer %d (was blocked by did_filetype)', bufnr)
invalidate_cache(bufnr) invalidate_cache(bufnr)
vim.cmd('redraw') vim.cmd.redraw()
end end
ft_retry_pending[bufnr] = nil
end) end)
end end
end end
@ -864,6 +869,7 @@ function M.attach(bufnr)
callback = function() callback = function()
attached_buffers[bufnr] = nil attached_buffers[bufnr] = nil
hunk_cache[bufnr] = nil hunk_cache[bufnr] = nil
ft_retry_pending[bufnr] = nil
if neogit_augroup then if neogit_augroup then
pcall(vim.api.nvim_del_augroup_by_id, neogit_augroup) pcall(vim.api.nvim_del_augroup_by_id, neogit_augroup)
end end