blink-cmp-ghostty/spec/ghostty_spec.lua
Barrett Ruth 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

182 lines
5.3 KiB
Lua

local helpers = require('spec.helpers')
local CONFIG_DOCS = table.concat({
'# The font family to use.',
'# This can be a comma-separated list of font families.',
'font-family = default',
'',
'# The font size in points.',
'font-size = 12',
'',
'# The cursor style.',
'cursor-style = block',
'',
}, '\n')
local BASH_COMPLETION = table.concat({
' --cursor-style) mapfile -t COMPREPLY < <( compgen -W "block bar underline" -- "$cur" ); _add_spaces ;;',
' --font-style) mapfile -t COMPREPLY < <( compgen -W "normal italic" -- "$cur" ); _add_spaces ;;',
}, '\n')
local function mock_system()
local original_system = vim.system
local original_schedule = vim.schedule
---@diagnostic disable-next-line: duplicate-set-field
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 result end }
end
local result = { stdout = '', code = 1 }
if on_exit then
on_exit(result)
return {}
end
return { wait = function() return result end }
end
vim.schedule = function(fn)
fn()
end
return function()
vim.system = original_system
vim.schedule = original_schedule
end
end
local function mock_enums()
local original_exepath = vim.fn.exepath
local original_realpath = vim.uv.fs_realpath
local original_open = io.open
vim.fn.exepath = function(name)
if name == 'ghostty' then
return '/mock/bin/ghostty'
end
return original_exepath(name)
end
vim.uv.fs_realpath = function(path)
if path == '/mock/bin/ghostty' then
return '/mock/bin/ghostty'
end
return original_realpath(path)
end
io.open = function(path, mode)
if path:match('ghostty%.bash$') then
return {
read = function()
return BASH_COMPLETION
end,
close = function() end,
}
end
return original_open(path, mode)
end
return function()
vim.fn.exepath = original_exepath
vim.uv.fs_realpath = original_realpath
io.open = original_open
end
end
describe('blink-cmp-ghostty', function()
local restores = {}
before_each(function()
package.loaded['blink-cmp-ghostty'] = nil
end)
after_each(function()
for _, fn in ipairs(restores) do
fn()
end
restores = {}
end)
describe('enabled', function()
it('returns true for ghostty filetype', function()
local bufnr = helpers.create_buffer({}, 'ghostty')
local source = require('blink-cmp-ghostty')
assert.is_true(source.enabled())
helpers.delete_buffer(bufnr)
end)
it('returns false for other filetypes', function()
local bufnr = helpers.create_buffer({}, 'lua')
local source = require('blink-cmp-ghostty')
assert.is_false(source.enabled())
helpers.delete_buffer(bufnr)
end)
end)
describe('get_completions', function()
it('returns config keys before =', function()
restores[#restores + 1] = mock_system()
restores[#restores + 1] = mock_enums()
local source = require('blink-cmp-ghostty').new()
local items
source:get_completions({ line = 'font', cursor = { 1, 4 } }, function(response)
items = response.items
end)
assert.is_not_nil(items)
assert.equals(3, #items)
for _, item in ipairs(items) do
assert.equals(10, item.kind)
end
end)
it('includes documentation from config docs', function()
restores[#restores + 1] = mock_system()
restores[#restores + 1] = mock_enums()
local source = require('blink-cmp-ghostty').new()
local items
source:get_completions({ line = '', cursor = { 1, 0 } }, function(response)
items = response.items
end)
local font_family = vim.iter(items):find(function(item)
return item.label == 'font-family'
end)
assert.is_not_nil(font_family)
assert.is_not_nil(font_family.documentation)
assert.is_truthy(font_family.documentation.value:find('font family'))
end)
it('returns enum values after =', function()
restores[#restores + 1] = mock_system()
restores[#restores + 1] = mock_enums()
local source = require('blink-cmp-ghostty').new()
local items
source:get_completions({ line = 'cursor-style = ', cursor = { 1, 15 } }, function(response)
items = response.items
end)
assert.is_not_nil(items)
assert.equals(3, #items)
for _, item in ipairs(items) do
assert.equals(20, item.kind)
end
end)
it('returns empty after = for unknown key', function()
restores[#restores + 1] = mock_system()
restores[#restores + 1] = mock_enums()
local source = require('blink-cmp-ghostty').new()
local items
source:get_completions({ line = 'font-family = ', cursor = { 1, 14 } }, function(response)
items = response.items
end)
assert.equals(0, #items)
end)
it('returns a cancel function', function()
restores[#restores + 1] = mock_system()
restores[#restores + 1] = mock_enums()
local source = require('blink-cmp-ghostty').new()
local cancel = source:get_completions({ line = '', cursor = { 1, 0 } }, function() end)
assert.is_function(cancel)
end)
end)
end)