perf: async parallel cache initialization and remove deepcopy (#6)
Some checks are pending
quality / changes (push) Waiting to run
quality / Lua Format Check (push) Blocked by required conditions
quality / Lua Lint Check (push) Blocked by required conditions
quality / Lua Type Check (push) Blocked by required conditions
quality / Markdown Format Check (push) Blocked by required conditions
test / Test (Neovim nightly) (push) Waiting to run
test / Test (Neovim stable) (push) Waiting to run
luarocks / quality (push) Waiting to run
luarocks / publish (push) Blocked by required conditions

Problem: first completion request blocked the UI with three sequential
synchronous vim.system():wait() calls (man page, command names,
command list), and every subsequent completion unnecessarily
deep-copied the entire cache.

Solution: run all three system calls concurrently via vim.system
callbacks, merging results when all complete. Queue pending completion
requests during loading. Return cached items directly instead of
deep-copying.
This commit is contained in:
Barrett Ruth 2026-02-20 20:39:32 -05:00 committed by GitHub
parent 7003996643
commit c0c59d1e57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 37 deletions

View file

@ -3,6 +3,9 @@ local M = {}
---@type blink.cmp.CompletionItem[]? ---@type blink.cmp.CompletionItem[]?
local cache = nil local cache = nil
local loading = false
---@type {ctx: blink.cmp.Context, callback: fun(response: blink.cmp.CompletionResponse)}[]
local pending = {}
function M.new() function M.new()
return setmetatable({}, { __index = M }) return setmetatable({}, { __index = M })
@ -13,18 +16,17 @@ function M.enabled()
return vim.bo.filetype == 'tmux' return vim.bo.filetype == 'tmux'
end end
---@param man_stdout string
---@param names_stdout string
---@return table<string, string> ---@return table<string, string>
local function parse_descriptions() local function parse_descriptions(man_stdout, names_stdout)
local result = vim.system({ 'bash', '-c', 'MANWIDTH=80 man -P cat tmux 2>/dev/null' }):wait()
local stdout = result.stdout or ''
local lines = {} local lines = {}
for line in (stdout .. '\n'):gmatch('(.-)\n') do for line in (man_stdout .. '\n'):gmatch('(.-)\n') do
lines[#lines + 1] = line lines[#lines + 1] = line
end end
local cmd_result = vim.system({ 'tmux', 'list-commands', '-F', '#{command_list_name}' }):wait()
local cmds = {} local cmds = {}
for name in (cmd_result.stdout or ''):gmatch('[^\n]+') do for name in names_stdout:gmatch('[^\n]+') do
cmds[name] = true cmds[name] = true
end end
@ -126,27 +128,66 @@ end
---@param ctx blink.cmp.Context ---@param ctx blink.cmp.Context
---@param callback fun(response: blink.cmp.CompletionResponse) ---@param callback fun(response: blink.cmp.CompletionResponse)
---@return fun() local function respond(ctx, callback)
function M:get_completions(ctx, callback)
if not cache then
local ok, descs = pcall(parse_descriptions)
if not ok then
descs = {}
end
local result = vim.system({ 'tmux', 'list-commands' }):wait()
cache = parse(result.stdout or '', descs)
end
local before = ctx.line:sub(1, ctx.cursor[2]) local before = ctx.line:sub(1, ctx.cursor[2])
if before:match('^%s*[a-z-]*$') then if before:match('^%s*[a-z-]*$') then
callback({ callback({
is_incomplete_forward = false, is_incomplete_forward = false,
is_incomplete_backward = false, is_incomplete_backward = false,
items = vim.deepcopy(cache), items = cache,
}) })
else else
callback({ items = {} }) callback({ items = {} })
end end
end
---@param ctx blink.cmp.Context
---@param callback fun(response: blink.cmp.CompletionResponse)
---@return fun()
function M:get_completions(ctx, callback)
if cache then
respond(ctx, callback)
return function() end
end
pending[#pending + 1] = { ctx = ctx, callback = callback }
if not loading then
loading = true
local man_out, names_out, cmds_out
local remaining = 3
local function on_all_done()
remaining = remaining - 1
if remaining > 0 then
return
end
vim.schedule(function()
local ok, descs = pcall(parse_descriptions, man_out, names_out)
if not ok then
descs = {}
end
cache = parse(cmds_out, descs)
loading = false
for _, p in ipairs(pending) do
respond(p.ctx, p.callback)
end
pending = {}
end)
end
vim.system({ 'bash', '-c', 'MANWIDTH=80 man -P cat tmux 2>/dev/null' }, {}, function(result)
man_out = result.stdout or ''
on_all_done()
end)
vim.system({ 'tmux', 'list-commands', '-F', '#{command_list_name}' }, {}, function(result)
names_out = result.stdout or ''
on_all_done()
end)
vim.system({ 'tmux', 'list-commands' }, {}, function(result)
cmds_out = result.stdout or ''
on_all_done()
end)
end
return function() end return function() end
end end

View file

@ -26,37 +26,38 @@ local MAN_PAGE = table.concat({
}, '\n') }, '\n')
local function mock_system() local function mock_system()
local original = vim.system local original_system = vim.system
local original_schedule = vim.schedule
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
vim.system = function(cmd) vim.system = function(cmd, _, on_exit)
local result
if cmd[1] == 'bash' then if cmd[1] == 'bash' then
return { result = { stdout = MAN_PAGE, code = 0 }
wait = function()
return { stdout = MAN_PAGE, code = 0 }
end,
}
elseif cmd[1] == 'tmux' and cmd[2] == 'list-commands' then elseif cmd[1] == 'tmux' and cmd[2] == 'list-commands' then
if cmd[3] == '-F' then if cmd[3] == '-F' then
return { result = { stdout = TMUX_NAMES, code = 0 }
wait = function() else
return { stdout = TMUX_NAMES, code = 0 } result = { stdout = TMUX_COMMANDS, code = 0 }
end,
}
end end
return { else
wait = function() result = { stdout = '', code = 1 }
return { stdout = TMUX_COMMANDS, code = 0 } end
end, if on_exit then
} on_exit(result)
return {}
end end
return { return {
wait = function() wait = function()
return { stdout = '', code = 1 } return result
end, end,
} }
end end
vim.schedule = function(fn)
fn()
end
return function() return function()
vim.system = original vim.system = original_system
vim.schedule = original_schedule
end end
end end