perf: cache repo root and harden async paths (#100)

## Problem

`get_repo_root()` shells out to `git rev-parse` on every call, causing
4-6
redundant subprocesses per `gdiff_file()` invocation. Three other minor
issues: `highlight_vim_syntax()` leaks a scratch buffer if
`nvim_buf_call`
errors, `lib.ensure()` silently drops callbacks during download so hunks
highlighted mid-download permanently miss intra-line highlights, and the
debounce timer callback can operate on a deleted buffer.

## Solution

Cache `get_repo_root()` results by parent directory — repo roots don't
change within a session. Wrap `nvim_buf_call` and `nvim_buf_delete` in
pcall so the scratch buffer is always cleaned up. Replace the early
`callback(nil)` in `lib.ensure()` with a pending callback queue that
fires
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:39:13 -05:00 committed by GitHub
parent a2053a132b
commit f5a090baae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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