diff --git a/lua/blink-cmp-ghostty.lua b/lua/blink-cmp-ghostty.lua index 9eb94ce..7044953 100644 --- a/lua/blink-cmp-ghostty.lua +++ b/lua/blink-cmp-ghostty.lua @@ -5,9 +5,6 @@ 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 }) @@ -40,14 +37,14 @@ function M.enabled() return false end ----@param stdout string ---@return blink.cmp.CompletionItem[] -local function parse_keys(stdout) +local function parse_keys() local Kind = require('blink.cmp.types').CompletionItemKind + local result = vim.system({ 'ghostty', '+show-config', '--docs' }):wait() local items = {} local doc_lines = {} - for line in (stdout .. '\n'):gmatch('(.-)\n') do + for line in ((result.stdout or '') .. '\n'):gmatch('(.-)\n') do if line:match('^#') then local stripped = line:gsub('^# ?', '') doc_lines[#doc_lines + 1] = stripped @@ -67,25 +64,28 @@ local function parse_keys(stdout) return items end ----@return string? -function M.bash_completion_path() +---@return table +local function parse_enums() local bin = vim.fn.exepath('ghostty') if bin == '' then - return nil + return {} end local real = vim.uv.fs_realpath(bin) if not real then - return nil + return {} end local prefix = real:match('(.*)/bin/ghostty$') if not prefix then - return nil + return {} end - return prefix .. '/share/bash-completion/completions/ghostty.bash' -end + local path = prefix .. '/share/bash-completion/completions/ghostty.bash' + local fd = io.open(path, 'r') + if not fd then + return {} + end + local content = fd:read('*a') + fd:close() ----@return table -local function parse_enums(content) local enums = {} for key, values in content:gmatch('%-%-([a-z][a-z0-9-]*)%) [^\n]* compgen %-W "([^"]+)"') do local vals = {} @@ -101,10 +101,13 @@ end ---@param ctx blink.cmp.Context ---@param callback fun(response: blink.cmp.CompletionResponse) -local function respond(ctx, callback) - if not keys_cache or not enums_cache then - return +---@return fun() +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 col = ctx.cursor[2] local eq_pos = line:find('=') @@ -127,81 +130,16 @@ local function respond(ctx, callback) is_incomplete_backward = false, items = items, }) - return + return function() end end callback({ items = {} }) else callback({ is_incomplete_forward = false, is_incomplete_backward = false, - items = keys_cache, + items = vim.deepcopy(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 - local config_out, enums_content - local remaining = 2 - - local function on_all_done() - remaining = remaining - 1 - if remaining > 0 then - return - end - vim.schedule(function() - keys_cache = parse_keys(config_out) - enums_cache = parse_enums(enums_content) - loading = false - for _, p in ipairs(pending) do - respond(p.ctx, p.callback) - end - pending = {} - end) - end - - vim.system({ 'ghostty', '+show-config', '--docs' }, {}, function(result) - config_out = result.stdout or '' - on_all_done() - end) - - local path = M.bash_completion_path() - if not path then - enums_content = '' - on_all_done() - else - vim.uv.fs_open(path, 'r', 438, function(err, fd) - if err or not fd then - enums_content = '' - on_all_done() - return - end - vim.uv.fs_fstat(fd, function(err2, stat) - if err2 or not stat then - vim.uv.fs_close(fd) - enums_content = '' - on_all_done() - return - end - vim.uv.fs_read(fd, stat.size, 0, function(_, data) - vim.uv.fs_close(fd) - enums_content = data or '' - on_all_done() - end) - end) - end) - end - end return function() end end diff --git a/lua/blink-cmp-ghostty/health.lua b/lua/blink-cmp-ghostty/health.lua deleted file mode 100644 index 5db565d..0000000 --- a/lua/blink-cmp-ghostty/health.lua +++ /dev/null @@ -1,47 +0,0 @@ -local M = {} - -function M.check() - vim.health.start('blink-cmp-ghostty') - - local ok = pcall(require, 'blink.cmp') - if ok then - vim.health.ok('blink.cmp is installed') - else - vim.health.error('blink.cmp is not installed') - end - - local bin = vim.fn.exepath('ghostty') - if bin ~= '' then - vim.health.ok('ghostty executable found: ' .. bin) - else - vim.health.error('ghostty executable not found') - return - end - - local result = vim.system({ 'ghostty', '+show-config', '--docs' }):wait() - if result.code == 0 and result.stdout and result.stdout ~= '' then - vim.health.ok('ghostty +show-config --docs produces output') - else - vim.health.warn( - 'ghostty +show-config --docs failed (config key documentation will be unavailable)' - ) - end - - local source = require('blink-cmp-ghostty') - local path = source.bash_completion_path() - if not path then - vim.health.warn('could not resolve bash completion path (enum completions will be unavailable)') - return - end - local fd = io.open(path, 'r') - if fd then - fd:close() - vim.health.ok('bash completion file found: ' .. path) - else - vim.health.warn( - 'bash completion file not found at ' .. path .. ' (enum completions will be unavailable)' - ) - end -end - -return M diff --git a/lua/blink-cmp-ghostty/types.lua b/lua/blink-cmp-ghostty/types.lua deleted file mode 100644 index d311414..0000000 --- a/lua/blink-cmp-ghostty/types.lua +++ /dev/null @@ -1,16 +0,0 @@ ----@class blink.cmp.Source - ----@class blink.cmp.CompletionItem ----@field label string ----@field kind? integer ----@field documentation? {kind: string, value: string} ----@field filterText? string - ----@class blink.cmp.Context ----@field line string ----@field cursor integer[] - ----@class blink.cmp.CompletionResponse ----@field is_incomplete_forward? boolean ----@field is_incomplete_backward? boolean ----@field items blink.cmp.CompletionItem[] diff --git a/spec/ghostty_spec.lua b/spec/ghostty_spec.lua index 5ef7a2a..cfac929 100644 --- a/spec/ghostty_spec.lua +++ b/spec/ghostty_spec.lua @@ -19,51 +19,31 @@ local BASH_COMPLETION = table.concat({ }, '\n') local function mock_system() - local original_system = vim.system - local original_schedule = vim.schedule + local original = vim.system ---@diagnostic disable-next-line: duplicate-set-field - vim.system = function(cmd, _, on_exit) + vim.system = function(cmd) 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 + return { stdout = CONFIG_DOCS, code = 0 } end, } end - local result = { stdout = '', code = 1 } - if on_exit then - on_exit(result) - return {} - end return { wait = function() - return result + return { stdout = '', code = 1 } end, } end - vim.schedule = function(fn) - fn() - end return function() - vim.system = original_system - vim.schedule = original_schedule + vim.system = original end end -local MOCK_FD = 99 - local function mock_enums() local original_exepath = vim.fn.exepath local original_realpath = vim.uv.fs_realpath - local original_fs_open = vim.uv.fs_open - local original_fs_fstat = vim.uv.fs_fstat - local original_fs_read = vim.uv.fs_read - local original_fs_close = vim.uv.fs_close + local original_open = io.open vim.fn.exepath = function(name) if name == 'ghostty' then @@ -77,41 +57,22 @@ local function mock_enums() end return original_realpath(path) end - vim.uv.fs_open = function(path, flags, mode, callback) + io.open = function(path, mode) if path:match('ghostty%.bash$') then - callback(nil, MOCK_FD) - return + return { + read = function() + return BASH_COMPLETION + end, + close = function() end, + } end - return original_fs_open(path, flags, mode, callback) - end - vim.uv.fs_fstat = function(fd, callback) - if fd == MOCK_FD then - callback(nil, { size = #BASH_COMPLETION }) - return - end - return original_fs_fstat(fd, callback) - end - vim.uv.fs_read = function(fd, size, offset, callback) - if fd == MOCK_FD then - callback(nil, BASH_COMPLETION) - return - end - return original_fs_read(fd, size, offset, callback) - end - vim.uv.fs_close = function(fd, ...) - if fd == MOCK_FD then - return true - end - return original_fs_close(fd, ...) + return original_open(path, mode) end return function() vim.fn.exepath = original_exepath vim.uv.fs_realpath = original_realpath - vim.uv.fs_open = original_fs_open - vim.uv.fs_fstat = original_fs_fstat - vim.uv.fs_read = original_fs_read - vim.uv.fs_close = original_fs_close + io.open = original_open end end