* 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.
229 lines
6.9 KiB
Lua
229 lines
6.9 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
|
|
-- selene: allow(incorrect_standard_library_use)
|
|
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
|
|
-- selene: allow(incorrect_standard_library_use)
|
|
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 true for config filetype in ghostty config dir', function()
|
|
local source = require('blink-cmp-ghostty')
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_set_current_buf(bufnr)
|
|
vim.api.nvim_set_option_value('filetype', 'config', { buf = bufnr })
|
|
local config_path = vim.fn.expand('$HOME/.config/ghostty/config')
|
|
vim.api.nvim_buf_set_name(bufnr, config_path)
|
|
local original_realpath = vim.uv.fs_realpath
|
|
vim.uv.fs_realpath = function(p)
|
|
if p == config_path then
|
|
return config_path
|
|
end
|
|
return original_realpath(p)
|
|
end
|
|
assert.is_true(source.enabled())
|
|
vim.uv.fs_realpath = original_realpath
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
|
|
it('returns false for config filetype outside ghostty dir', function()
|
|
local source = require('blink-cmp-ghostty')
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_set_current_buf(bufnr)
|
|
vim.api.nvim_set_option_value('filetype', 'config', { buf = bufnr })
|
|
vim.api.nvim_buf_set_name(bufnr, '/tmp/some-other/config')
|
|
local original_realpath = vim.uv.fs_realpath
|
|
vim.uv.fs_realpath = function(p)
|
|
if p == '/tmp/some-other/config' then
|
|
return '/tmp/some-other/config'
|
|
end
|
|
return original_realpath(p)
|
|
end
|
|
assert.is_false(source.enabled())
|
|
vim.uv.fs_realpath = original_realpath
|
|
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)
|