diff --git a/lua/gitsigns/async.lua b/lua/gitsigns/async.lua index 1bd125cf3..60180acd7 100644 --- a/lua/gitsigns/async.lua +++ b/lua/gitsigns/async.lua @@ -182,8 +182,11 @@ function Task:_finish(err, result) self._result = result threads[self._thread] = nil for _, cb in pairs(self._callbacks) do - -- Needs to be pcall as step() (who calls this function) cannot error - pcall(cb, err, result) + -- Run the callbacks in the main loop so we can drain the callbacks + -- and not interrupt step() + vim.schedule(function() + cb(err, result) + end) end end @@ -399,7 +402,7 @@ end --- @generic F: function --- @param argc integer --- @param func F ---- @return F +--- @return fun(...): Gitsigns.async.Task function M.create(argc, func) assert(type(argc) == 'number') assert(type(func) == 'function') @@ -409,7 +412,14 @@ function M.create(argc, func) return function(...) local task = Task._new(func) - local callback = argc and select(argc + 1, ...) or nil + local callback = argc and select(argc + 1, ...) + or function(err) + -- Default continuation is to raise any errors + if err then + error(task:traceback(err)) + end + end + if callback and type(callback) == 'function' then task:await(callback) end diff --git a/lua/gitsigns/attach.lua b/lua/gitsigns/attach.lua index 33071d4a1..5be219d2c 100644 --- a/lua/gitsigns/attach.lua +++ b/lua/gitsigns/attach.lua @@ -44,7 +44,7 @@ local function parse_git_path(name) rel_path = tail:match('^[^/]+/(.*)') end - dprintf("%s buffer for file '%s' from path '%s' on commit '%s'", plugin, rel_path, file, commit) + dprintf("%s buffer for file '%s' from path '%s' on commit '%s'", plugin, rel_path, name, commit) return rel_path, commit, gitdir end @@ -155,10 +155,16 @@ local function get_buf_context(bufnr) end local gitdir_oap, toplevel_oap = on_attach_pre(bufnr) + local gitdir = gitdir_oap or gitdir_from_bufname + + if rel_path and gitdir then + local toplevel = toplevel_oap or util.dirname(gitdir) + file = util.joinpath(toplevel, rel_path) + end return { - file = rel_path or file, - gitdir = gitdir_oap or gitdir_from_bufname, + file = file, + gitdir = gitdir, toplevel = toplevel_oap, -- Stage buffers always compare against the common ancestor (':1') -- :0: index diff --git a/lua/gitsigns/blame.lua b/lua/gitsigns/blame.lua index 0d7c5ad74..157419f8b 100644 --- a/lua/gitsigns/blame.lua +++ b/lua/gitsigns/blame.lua @@ -155,7 +155,7 @@ end --- @param win integer --- @param revision? string --- @param parent? boolean -local function reblame(blame, win, revision, parent) +local reblame = async.create(4, function(blame, win, revision, parent) local blm_win = api.nvim_get_current_win() local lnum = unpack(api.nvim_win_get_cursor(blm_win)) local sha = blame[lnum].commit.sha @@ -168,21 +168,17 @@ local function reblame(blame, win, revision, parent) vim.cmd.quit() api.nvim_set_current_win(win) + local bufnr = api.nvim_win_get_buf(win) - require('gitsigns').show( - sha, - vim.schedule_wrap(function() - local bufnr = api.nvim_get_current_buf() - local ok = vim.wait(1000, function() - return cache[bufnr] ~= nil - end) - if not ok then - error('Timeout waiting for attach') - end - async.arun(M.blame) - end) - ) -end + async.await(require('gitsigns.diffthis').show(bufnr, sha)) + local ok = vim.wait(1000, function() + return cache[bufnr] ~= nil + end) + if not ok then + error('Timeout waiting for attach') + end + M.blame() +end) --- @param win integer --- @param open 'vsplit'|'tabnew' diff --git a/lua/gitsigns/debug/log.lua b/lua/gitsigns/debug/log.lua index dfd4ac9b4..9aba54d9f 100644 --- a/lua/gitsigns/debug/log.lua +++ b/lua/gitsigns/debug/log.lua @@ -3,7 +3,7 @@ local uv = vim.uv or vim.loop local start_time = uv.hrtime() --- @class Gitsigns.log ---- @field private messages [number, string, string, string][] +--- @field private messages [number, string, string, thread?, string][] local M = { debug_mode = false, verbose = false, @@ -94,7 +94,8 @@ local function cprint(kind, lvl, ...) if ctx.bufnr then ctx1 = string.format('%s(%s)', ctx1, ctx.bufnr) end - table.insert(M.messages, { time, kind, ctx1, msg }) + local thread = coroutine.running() + table.insert(M.messages, { time, kind, ctx1, thread, msg }) end function M.dprint(...) @@ -131,7 +132,8 @@ local function eprint(msg, level) local info = debug.getinfo(level + 2, 'Sl') local ctx = info and string.format('%s<%d>', info.short_src, info.currentline) or '???' local time = (uv.hrtime() - start_time) / 1e6 - table.insert(M.messages, { time, 'error', ctx, debug.traceback(msg) }) + local thread = coroutine.running() + table.insert(M.messages, { time, 'error', ctx, thread, debug.traceback(msg) }) if M.debug_mode then error(msg, 3) end @@ -167,18 +169,39 @@ function M.clear() M.messages = {} end ---- @param m [number, string, string, string] +--- @param thread thread +--- @return string +local function fmt_thread(thread) + local threads = _G.tostring(thread) + local thread1 = threads:sub(#threads - 1) + return '' +end + +--- @param m [number, string, string, thread, string] --- @return [string,string][] local function build_msg(m) - local time, kind, ctx, msg = m[1], m[2], m[3], m[4] + local time, kind, ctx, thread, msg = m[1], m[2], m[3], m[4], m[5] local hl = sev_to_hl[kind] - return { + + local r = { { string.format('%.2f ', time), 'Comment' }, + } + + if thread then + vim.list_extend(r, { + { fmt_thread(thread), 'Comment' }, + { ' ' }, + }) + end + + vim.list_extend(r, { { kind:upper():sub(1, 1), hl }, { string.format(' %s:', ctx), 'Tag' }, { ' ' }, { msg }, - } + }) + + return r end function M.show() diff --git a/lua/gitsigns/system/compat.lua b/lua/gitsigns/system/compat.lua index 7e7c09560..ce304ce97 100644 --- a/lua/gitsigns/system/compat.lua +++ b/lua/gitsigns/system/compat.lua @@ -275,7 +275,7 @@ end --- @param on_exit? fun(out: vim.SystemCompleted) --- @return vim.SystemObj local function system(cmd, opts, on_exit) - local __FUNC__ = 'run_job' + local __FUNC__ = 'system' vim.validate({ cmd = { cmd, 'table' }, opts = { opts, 'table', true }, diff --git a/lua/gitsigns/util.lua b/lua/gitsigns/util.lua index c7af4fab6..16bf5c544 100644 --- a/lua/gitsigns/util.lua +++ b/lua/gitsigns/util.lua @@ -24,6 +24,16 @@ function M.dirname(file) return file:match(string.format('^(.+)%s[^%s]+', M.path_sep, M.path_sep)) end +---@param ... string +---@return string +function M.joinpath(...) + local path = table.concat({ ... }, '/') + if not is_unix then + path = path:gsub('\\', '/') + end + return (path:gsub('//+', '/')) +end + --- @param path string --- @return string[] function M.file_lines(path)