Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
8bb6a81d1f
perf: async cache initialization and remove deepcopy
Problem: first completion request blocked the UI with a synchronous
vim.system():wait() call, and every subsequent key completion
unnecessarily deep-copied the entire cache.

Solution: use vim.system with an async callback to initialize the cache
without blocking. Queue pending completion requests during loading and
serve them once parsing finishes. Return cached keys directly instead
of deep-copying.
2026-02-20 20:16:33 -05:00
2 changed files with 55 additions and 25 deletions

View file

@ -5,6 +5,9 @@ local M = {}
local keys_cache = nil local keys_cache = nil
---@type table<string, string[]>? ---@type table<string, string[]>?
local enums_cache = nil local enums_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 })
@ -15,14 +18,14 @@ function M.enabled()
return vim.bo.filetype == 'ghostty' return vim.bo.filetype == 'ghostty'
end end
---@param stdout string
---@return blink.cmp.CompletionItem[] ---@return blink.cmp.CompletionItem[]
local function parse_keys() local function parse_keys(stdout)
local Kind = require('blink.cmp.types').CompletionItemKind local Kind = require('blink.cmp.types').CompletionItemKind
local result = vim.system({ 'ghostty', '+show-config', '--docs' }):wait()
local items = {} local items = {}
local doc_lines = {} local doc_lines = {}
for line in ((result.stdout or '') .. '\n'):gmatch('(.-)\n') do for line in (stdout .. '\n'):gmatch('(.-)\n') do
if line:match('^#') then if line:match('^#') then
local stripped = line:gsub('^# ?', '') local stripped = line:gsub('^# ?', '')
doc_lines[#doc_lines + 1] = stripped doc_lines[#doc_lines + 1] = stripped
@ -79,13 +82,7 @@ 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 keys_cache then
keys_cache = parse_keys()
enums_cache = parse_enums()
end
local line = ctx.line local line = ctx.line
local col = ctx.cursor[2] local col = ctx.cursor[2]
local eq_pos = line:find('=') local eq_pos = line:find('=')
@ -108,16 +105,42 @@ function M:get_completions(ctx, callback)
is_incomplete_backward = false, is_incomplete_backward = false,
items = items, items = items,
}) })
return function() end return
end end
callback({ items = {} }) callback({ items = {} })
else else
callback({ callback({
is_incomplete_forward = false, is_incomplete_forward = false,
is_incomplete_backward = false, is_incomplete_backward = false,
items = vim.deepcopy(keys_cache), items = keys_cache,
}) })
end end
end
---@param ctx blink.cmp.Context
---@param callback fun(response: blink.cmp.CompletionResponse)
---@return fun()
function M:get_completions(ctx, callback)
if keys_cache then
respond(ctx, callback)
return function() end
end
pending[#pending + 1] = { ctx = ctx, callback = callback }
if not loading then
loading = true
vim.system({ 'ghostty', '+show-config', '--docs' }, {}, function(result)
vim.schedule(function()
keys_cache = parse_keys(result.stdout or '')
enums_cache = parse_enums()
loading = false
for _, p in ipairs(pending) do
respond(p.ctx, p.callback)
end
pending = {}
end)
end)
end
return function() end return function() end
end end

View file

@ -19,24 +19,31 @@ local BASH_COMPLETION = 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)
if cmd[1] == 'ghostty' then if cmd[1] == 'ghostty' then
return { local result = { stdout = CONFIG_DOCS, code = 0 }
wait = function() if on_exit then
return { stdout = CONFIG_DOCS, code = 0 } on_exit(result)
end, return {}
} end
return { wait = function() return result end }
end end
return { local result = { stdout = '', code = 1 }
wait = function() if on_exit then
return { stdout = '', code = 1 } on_exit(result)
end, return {}
} end
return { wait = function() return result end }
end
vim.schedule = function(fn)
fn()
end end
return function() return function()
vim.system = original vim.system = original_system
vim.schedule = original_schedule
end end
end end