avoid treesitter races in netrw previews
This commit is contained in:
parent
f31a9b7d74
commit
fe94b30c31
4 changed files with 117 additions and 1 deletions
|
|
@ -2,10 +2,12 @@ local M = {}
|
|||
|
||||
local uv = vim.uv
|
||||
local states = {}
|
||||
local preview_buffers = {}
|
||||
local preview_group = vim.api.nvim_create_augroup('netrw_live_preview', { clear = true })
|
||||
|
||||
local DEBOUNCE_MS = 90
|
||||
local MAX_ENTRIES = 200
|
||||
local PREVIEW_BUFFER_CLEANUP_MS = 2000
|
||||
|
||||
local function is_valid_buffer(buf)
|
||||
return type(buf) == 'number' and buf > 0 and vim.api.nvim_buf_is_valid(buf)
|
||||
|
|
@ -53,6 +55,46 @@ local function close_timer(state, key)
|
|||
state[key] = nil
|
||||
end
|
||||
|
||||
local function restore_preview_buffer(buf, for_editing)
|
||||
local metadata = preview_buffers[buf]
|
||||
if not metadata or not is_valid_buffer(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
vim.b[buf].netrw_live_preview_buffer = nil
|
||||
vim.bo[buf].bufhidden = metadata.bufhidden
|
||||
vim.bo[buf].buflisted = for_editing or metadata.buflisted
|
||||
vim.bo[buf].syntax = metadata.syntax
|
||||
if metadata.treesitter_active then
|
||||
pcall(vim.treesitter.start, buf)
|
||||
end
|
||||
preview_buffers[buf] = nil
|
||||
end
|
||||
|
||||
local function schedule_preview_buffer_cleanup(state)
|
||||
if not state or state.preview_cleanup_scheduled then
|
||||
return
|
||||
end
|
||||
|
||||
state.preview_cleanup_scheduled = true
|
||||
vim.defer_fn(function()
|
||||
state.preview_cleanup_scheduled = false
|
||||
|
||||
for buf, metadata in pairs(preview_buffers) do
|
||||
local is_preview = is_valid_buffer(buf) and vim.b[buf].netrw_live_preview_buffer == true
|
||||
local is_visible = is_valid_buffer(buf) and #vim.fn.win_findbuf(buf) > 0
|
||||
if not is_preview or not is_visible then
|
||||
if is_preview and metadata.loaded then
|
||||
restore_preview_buffer(buf, false)
|
||||
end
|
||||
if not is_preview then
|
||||
preview_buffers[buf] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end, PREVIEW_BUFFER_CLEANUP_MS)
|
||||
end
|
||||
|
||||
local function close_preview(state)
|
||||
if not state then
|
||||
return
|
||||
|
|
@ -61,6 +103,7 @@ local function close_preview(state)
|
|||
local preview = find_preview_window(state.tab)
|
||||
if preview then
|
||||
pcall(vim.api.nvim_win_close, preview, false)
|
||||
schedule_preview_buffer_cleanup(state)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -395,7 +438,48 @@ end
|
|||
|
||||
local function show_native_preview(state, entry)
|
||||
state.native_preview_active = true
|
||||
|
||||
local existing_buf = entry.path and vim.fn.bufnr(entry.path) or -1
|
||||
local existing = is_valid_buffer(existing_buf) and {
|
||||
buf = existing_buf,
|
||||
loaded = vim.api.nvim_buf_is_loaded(existing_buf),
|
||||
buflisted = vim.bo[existing_buf].buflisted,
|
||||
bufhidden = vim.bo[existing_buf].bufhidden,
|
||||
syntax = vim.bo[existing_buf].syntax,
|
||||
treesitter_active = vim.treesitter.highlighter.active[existing_buf] ~= nil,
|
||||
} or nil
|
||||
|
||||
local ok, shown = pcall(native_preview, entry)
|
||||
|
||||
local preview = find_preview_window(state.tab)
|
||||
if preview then
|
||||
local preview_buf = vim.api.nvim_win_get_buf(preview)
|
||||
local preview_only = #vim.fn.win_findbuf(preview_buf) == 1
|
||||
if vim.bo[preview_buf].buftype == '' and preview_only then
|
||||
preview_buffers[preview_buf] = preview_buffers[preview_buf] or {
|
||||
loaded = existing ~= nil and existing.buf == preview_buf and existing.loaded,
|
||||
buflisted = existing ~= nil and existing.buf == preview_buf and existing.buflisted or false,
|
||||
bufhidden = existing ~= nil and existing.buf == preview_buf and existing.bufhidden or '',
|
||||
syntax = existing ~= nil and existing.buf == preview_buf and existing.syntax or '',
|
||||
treesitter_active = existing ~= nil
|
||||
and existing.buf == preview_buf
|
||||
and existing.loaded
|
||||
and existing.treesitter_active
|
||||
or vim.treesitter.highlighter.active[preview_buf] ~= nil,
|
||||
}
|
||||
|
||||
vim.bo[preview_buf].bufhidden = 'hide'
|
||||
vim.bo[preview_buf].buflisted = preview_buffers[preview_buf].buflisted
|
||||
vim.b[preview_buf].netrw_live_preview_buffer = true
|
||||
pcall(vim.treesitter.stop, preview_buf)
|
||||
|
||||
local filetype = vim.bo[preview_buf].filetype
|
||||
if filetype ~= '' then
|
||||
vim.bo[preview_buf].syntax = filetype
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if is_valid_window(state.win) then
|
||||
pcall(vim.api.nvim_set_current_win, state.win)
|
||||
end
|
||||
|
|
@ -445,7 +529,12 @@ local function preview_selected(state, allow_create)
|
|||
end
|
||||
|
||||
local function schedule_preview(state)
|
||||
if not state or not find_preview_window(state.tab) then
|
||||
if not state then
|
||||
return
|
||||
end
|
||||
|
||||
if not find_preview_window(state.tab) then
|
||||
schedule_preview_buffer_cleanup(state)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -459,6 +548,26 @@ local function schedule_preview(state)
|
|||
end))
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd('BufEnter', {
|
||||
group = preview_group,
|
||||
desc = 'Restore normal editing behavior for a file previously used as a netrw preview',
|
||||
callback = function(args)
|
||||
local buf = args.buf
|
||||
if not is_valid_buffer(buf) or vim.b[buf].netrw_live_preview_buffer ~= true then
|
||||
return
|
||||
end
|
||||
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
if is_valid_window(win) and vim.wo[win].previewwindow then
|
||||
return
|
||||
end
|
||||
|
||||
if preview_buffers[buf] then
|
||||
restore_preview_buffer(buf, true)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function state_for(buf)
|
||||
local state = states[buf]
|
||||
if not state then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue