perf: cache repo root and harden async paths

Problem: get_repo_root() shells out on every call, causing 4-6
redundant subprocesses per gdiff_file() invocation.
highlight_vim_syntax() leaks a scratch buffer if nvim_buf_call errors.
lib.ensure() silently drops callbacks during download, permanently
missing intra-line highlights. The debounce timer callback can operate
on an invalid buffer.

Solution: Cache get_repo_root() results by parent directory. Wrap
nvim_buf_call and nvim_buf_delete in pcall so the scratch buffer is
always cleaned up. Queue pending callbacks in lib.ensure() so all
callers receive the handle once the download completes. Guard the
debounce timer callback with nvim_buf_is_valid.
This commit is contained in:
Barrett Ruth 2026-02-09 12:36:51 -05:00
parent a2053a132b
commit 3c3b27a2cb
4 changed files with 30 additions and 14 deletions

View file

@ -1,13 +1,19 @@
local M = {} local M = {}
local repo_root_cache = {}
---@param filepath string ---@param filepath string
---@return string? ---@return string?
function M.get_repo_root(filepath) function M.get_repo_root(filepath)
local dir = vim.fn.fnamemodify(filepath, ':h') local dir = vim.fn.fnamemodify(filepath, ':h')
if repo_root_cache[dir] ~= nil then
return repo_root_cache[dir]
end
local result = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--show-toplevel' }) local result = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--show-toplevel' })
if vim.v.shell_error ~= 0 then if vim.v.shell_error ~= 0 then
return nil return nil
end end
repo_root_cache[dir] = result[1]
return result[1] return result[1]
end end

View file

@ -238,7 +238,7 @@ local function highlight_vim_syntax(bufnr, ns, hunk, code_lines, covered_lines,
local spans = {} local spans = {}
vim.api.nvim_buf_call(scratch, function() pcall(vim.api.nvim_buf_call, scratch, function()
vim.cmd('setlocal syntax=' .. ft) vim.cmd('setlocal syntax=' .. ft)
vim.cmd('redraw') vim.cmd('redraw')
@ -256,7 +256,7 @@ local function highlight_vim_syntax(bufnr, ns, hunk, code_lines, covered_lines,
spans = M.coalesce_syntax_spans(query_fn, code_lines) spans = M.coalesce_syntax_spans(query_fn, code_lines)
end) end)
vim.api.nvim_buf_delete(scratch, { force = true }) pcall(vim.api.nvim_buf_delete, scratch, { force = true })
local hunk_line_count = #hunk.lines local hunk_line_count = #hunk.lines
local extmark_count = 0 local extmark_count = 0

View file

@ -200,7 +200,9 @@ local function create_debounced_highlight(bufnr)
timer = nil timer = nil
t:close() t:close()
end end
highlight_buffer(bufnr) if vim.api.nvim_buf_is_valid(bufnr) then
highlight_buffer(bufnr)
end
end) end)
) )
end end

View file

@ -8,6 +8,9 @@ local cached_handle = nil
---@type boolean ---@type boolean
local download_in_progress = false local download_in_progress = false
---@type fun(handle: table?)[]
local pending_callbacks = {}
---@return string ---@return string
local function get_os() local function get_os()
local os_name = jit.os:lower() local os_name = jit.os:lower()
@ -164,9 +167,10 @@ function M.ensure(callback)
return return
end end
table.insert(pending_callbacks, callback)
if download_in_progress then if download_in_progress then
dbg('download already in progress') dbg('download already in progress, queued callback')
callback(nil)
return return
end end
@ -192,21 +196,25 @@ function M.ensure(callback)
vim.system(cmd, {}, function(result) vim.system(cmd, {}, function(result)
download_in_progress = false download_in_progress = false
vim.schedule(function() vim.schedule(function()
local handle = nil
if result.code ~= 0 then if result.code ~= 0 then
vim.notify('[diffs] failed to download libvscode_diff', vim.log.levels.WARN) vim.notify('[diffs] failed to download libvscode_diff', vim.log.levels.WARN)
dbg('curl failed: %s', result.stderr or '') dbg('curl failed: %s', result.stderr or '')
callback(nil) else
return local f = io.open(version_path(), 'w')
if f then
f:write(EXPECTED_VERSION)
f:close()
end
vim.notify('[diffs] libvscode_diff downloaded', vim.log.levels.INFO)
handle = M.load()
end end
local f = io.open(version_path(), 'w') local cbs = pending_callbacks
if f then pending_callbacks = {}
f:write(EXPECTED_VERSION) for _, cb in ipairs(cbs) do
f:close() cb(handle)
end end
vim.notify('[diffs] libvscode_diff downloaded', vim.log.levels.INFO)
callback(M.load())
end) end)
end) end)
end end