perf: async parallel cache initialization and remove deepcopy

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:18:18 -05:00
parent 7befe69ea1
commit ec1d9517a7
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
2 changed files with 79 additions and 41 deletions

View file

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