From 072859ce04b30dfae063e33b6030672f311db8cb Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Fri, 20 Feb 2026 21:01:21 -0500 Subject: [PATCH] perf: async cache initialization and remove deepcopy (#8) * 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. * fix: revert blanket diagnostics.disable and selene comments Problem: .luarc.json blanket-disabled four diagnostic categories project-wide, and selene inline directives were added to suppress warnings on io.open monkey-patching in tests. Solution: revert .luarc.json to match main and remove selene comments. --- lua/blink-cmp-ghostty.lua | 46 ++++++++++++++++++++++++++++++--------- spec/ghostty_spec.lua | 25 ++++++++++++++++----- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/lua/blink-cmp-ghostty.lua b/lua/blink-cmp-ghostty.lua index 1110868..5787f24 100644 --- a/lua/blink-cmp-ghostty.lua +++ b/lua/blink-cmp-ghostty.lua @@ -5,6 +5,9 @@ local M = {} local keys_cache = nil ---@type table? local enums_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 }) @@ -37,14 +40,14 @@ function M.enabled() return false end +---@param stdout string ---@return blink.cmp.CompletionItem[] -local function parse_keys() +local function parse_keys(stdout) local Kind = require('blink.cmp.types').CompletionItemKind - local result = vim.system({ 'ghostty', '+show-config', '--docs' }):wait() local items = {} 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 local stripped = line:gsub('^# ?', '') doc_lines[#doc_lines + 1] = stripped @@ -101,13 +104,10 @@ end ---@param ctx blink.cmp.Context ---@param callback fun(response: blink.cmp.CompletionResponse) ----@return fun() -function M:get_completions(ctx, callback) +local function respond(ctx, callback) if not keys_cache or not enums_cache then - keys_cache = parse_keys() - enums_cache = parse_enums() + return end - local line = ctx.line local col = ctx.cursor[2] local eq_pos = line:find('=') @@ -130,16 +130,42 @@ function M:get_completions(ctx, callback) is_incomplete_backward = false, items = items, }) - return function() end + return end callback({ items = {} }) else callback({ is_incomplete_forward = false, is_incomplete_backward = false, - items = vim.deepcopy(keys_cache), + items = keys_cache, }) 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 end diff --git a/spec/ghostty_spec.lua b/spec/ghostty_spec.lua index 86cdd36..d0ad426 100644 --- a/spec/ghostty_spec.lua +++ b/spec/ghostty_spec.lua @@ -19,24 +19,39 @@ local BASH_COMPLETION = table.concat({ }, '\n') 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 - vim.system = function(cmd) + vim.system = function(cmd, _, on_exit) if cmd[1] == 'ghostty' then + local result = { stdout = CONFIG_DOCS, code = 0 } + if on_exit then + on_exit(result) + return {} + end return { wait = function() - return { stdout = CONFIG_DOCS, code = 0 } + return result end, } end + local result = { stdout = '', code = 1 } + if on_exit then + on_exit(result) + return {} + end return { wait = function() - return { stdout = '', code = 1 } + return result end, } end + vim.schedule = function(fn) + fn() + end return function() - vim.system = original + vim.system = original_system + vim.schedule = original_schedule end end