From 8d0c023a698651c42f6bba2c92a4636ccab6dd33 Mon Sep 17 00:00:00 2001 From: glepnir Date: Thu, 9 Jan 2025 20:27:35 +0800 Subject: [PATCH] refactor: make executable check async executable is sync version when one of formatter is not executable it will cause nvim block, make executable check async and move to FileType callback we don't need check all when config. just lazied check when need. --- lua/guard/events.lua | 46 ++++++++++++++++++++++++++++++++++---------- lua/guard/format.lua | 23 +++++++++++++--------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/lua/guard/events.lua b/lua/guard/events.lua index 64f7eec..2884a43 100644 --- a/lua/guard/events.lua +++ b/lua/guard/events.lua @@ -175,22 +175,48 @@ end ---@param ft string ---@param formatters FmtConfig[] function M.fmt_on_filetype(ft, formatters) - -- check if all cmds executable before registering formatter - iter(formatters):any(function(config) - if type(config) == 'table' and config.cmd and vim.fn.executable(config.cmd) ~= 1 then - report_error(config.cmd .. ' not executable') - return false - end - return true - end) + -- safely check do we need? + if #formatters == 0 then + return + end au('FileType', { group = M.group, pattern = ft, callback = function(args) - M.try_attach_fmt_to_buf(args.buf) + coroutine.resume(coroutine.create(function() + local co = assert(coroutine.running()) + local it = iter(formatters) + local ok = true + while true do + local config = it:next() + if not config then + break + end + if type(config) == 'table' and config.cmd then + vim.uv.fs_stat(vim.fn.exepath(config.cmd), function(err, stat) + if err or not stat or stat.type ~= 'file' then + ok = false + end + coroutine.resume(co) + end) + coroutine.yield() + if not ok then + vim.schedule(function() + report_error(('%s not executable'):format(config.cmd)) + api.nvim_del_autocmd(args.id) + end) + return + end + end + end + + vim.schedule(function() + M.try_attach_fmt_to_buf(args.buf) + end) + end)) end, - desc = 'guard', + desc = '[Guard] register BufWritePre for auto format when save', }) end diff --git a/lua/guard/format.lua b/lua/guard/format.lua index 6d4dc69..78dd5a6 100644 --- a/lua/guard/format.lua +++ b/lua/guard/format.lua @@ -51,6 +51,18 @@ local function fail(msg) vim.notify('[Guard]: ' .. msg, vim.log.levels.WARN) end +--- Error handler +---@param errno table +local function err_handler(errno) + if errno.reason:match('exited with errors$') then + fail(('%s exited with code %d\n%s'):format(errno.cmd, errno.code, errno.stderr)) + elseif errno.reason == 'buf changed' then + fail('buffer changed during formatting') + else + fail(errno.reason) + end +end + local function do_fmt(buf) buf = buf or api.nvim_get_current_buf() local ft_conf = filetype[vim.bo[buf].filetype] @@ -169,18 +181,11 @@ local function do_fmt(buf) vim.schedule(function() -- handle errors if errno then - if errno.reason:match('exited with errors$') then - fail(('%s exited with code %d\n%s'):format(errno.cmd, errno.code, errno.stderr)) - elseif errno.reason == 'buf changed' then - fail('buffer changed during formatting') - else - fail(errno.reason) - end - return + return err_handler(errno) end -- check buffer one last time if api.nvim_buf_get_changedtick(buf) ~= changedtick then - fail('buffer changed during formatting') + return fail('buffer changed during formatting') end if not api.nvim_buf_is_valid(buf) then fail('buffer no longer valid')