diff --git a/lua/git/blame.lua b/lua/git/blame.lua index 399406e..e36ff86 100644 --- a/lua/git/blame.lua +++ b/lua/git/blame.lua @@ -4,16 +4,9 @@ local util = require("git.core.util") local M = {} -local NS_INLINE = vim.api.nvim_create_namespace("ow.git.blame.inline") local NS_POPUP = vim.api.nvim_create_namespace("ow.git.blame.popup") -local NS_VIEW = vim.api.nvim_create_namespace("ow.git.blame.view") local ZERO_SHA = string.rep("0", 40) -local SHA_W = 8 -local AUTHOR_W = 20 -local DATE_W = 10 -local GAP = " " -local VIEW_WIDTH = SHA_W + #GAP + AUTHOR_W + #GAP + DATE_W ---@class ow.Git.Blame.Commit ---@field sha string @@ -41,8 +34,6 @@ local VIEW_WIDTH = SHA_W + #GAP + AUTHOR_W + #GAP + DATE_W ---@field tick integer? ---@field epoch integer ---@field pending fun()[] ----@field inline boolean ----@field autocmds integer[] ---@type table local states = {} @@ -59,41 +50,6 @@ function M.state(buf) return states[buf] end ----@param unix_ts integer ----@return string -local function relative_time(unix_ts) - local diff = os.time() - unix_ts - if diff < 0 then - diff = 0 - end - - local function fmt(n, unit) - return string.format("%d %s%s ago", n, unit, n == 1 and "" or "s") - end - - if diff < 45 then - return "just now" - elseif diff < 90 then - return "a minute ago" - elseif diff < 45 * 60 then - return fmt(math.floor(diff / 60 + 0.5), "minute") - elseif diff < 90 * 60 then - return "an hour ago" - elseif diff < 22 * 3600 then - return fmt(math.floor(diff / 3600 + 0.5), "hour") - elseif diff < 36 * 3600 then - return "a day ago" - elseif diff < 7 * 86400 then - return fmt(math.floor(diff / 86400 + 0.5), "day") - elseif diff < 30 * 86400 then - return fmt(math.floor(diff / (7 * 86400) + 0.5), "week") - elseif diff < 365 * 86400 then - return fmt(math.floor(diff / (30 * 86400) + 0.5), "month") - end - - return fmt(math.floor(diff / (365 * 86400) + 0.5), "year") -end - ---@param ts integer ---@param tz string ---@return string @@ -269,8 +225,6 @@ local function ensure_state(buf) tick = nil, epoch = 0, pending = {}, - inline = false, - autocmds = {}, } states[buf] = state return state @@ -327,327 +281,6 @@ local function run_blame(state, buf, done) end) end ----@param buf integer -local function render_inline(buf) - if not vim.api.nvim_buf_is_valid(buf) then - return - end - vim.api.nvim_buf_clear_namespace(buf, NS_INLINE, 0, -1) - local state = states[buf] - if not state or not state.inline then - return - end - local win = vim.api.nvim_get_current_buf() == buf - and vim.api.nvim_get_current_win() - or vim.fn.bufwinid(buf) - if win == -1 then - return - end - local lnum = vim.api.nvim_win_get_cursor(win)[1] - local sha = state.line_sha[lnum] - local commit = sha and state.commits[sha] - if not commit then - return - end - local text - if util.is_zero_sha(sha) then - text = " You - Not Committed Yet" - else - text = string.format( - " %s, %s - %s", - commit.author, - relative_time(commit.author_time), - commit.summary - ) - end - pcall(vim.api.nvim_buf_set_extmark, buf, NS_INLINE, lnum - 1, 0, { - virt_text = { { text, "GitBlameInline" } }, - virt_text_pos = "eol", - hl_mode = "combine", - }) -end - ----@param buf integer -local function reblame(buf) - local state = states[buf] - if not state or not state.inline then - return - end - run_blame(state, buf, function() - render_inline(buf) - end) -end - -local schedule, sched_handle = util.keyed_debounce(reblame, 150) - ----@param buf integer -function M._flush(buf) - sched_handle.flush(buf) -end - ----@param buf integer ----@param state ow.Git.Blame.BufState -local function attach_autocmds(buf, state) - if #state.autocmds > 0 then - return - end - local group = - vim.api.nvim_create_augroup("ow.git.blame." .. buf, { clear = true }) - state.autocmds = { - vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, { - group = group, - buffer = buf, - callback = function() - render_inline(buf) - end, - }), - vim.api.nvim_create_autocmd( - { "TextChanged", "TextChangedI", "BufWritePost" }, - { - group = group, - buffer = buf, - callback = function() - schedule(buf) - end, - } - ), - } -end - ----@param buf integer ----@param state ow.Git.Blame.BufState -local function detach_autocmds(buf, state) - for _, id in ipairs(state.autocmds) do - pcall(vim.api.nvim_del_autocmd, id) - end - state.autocmds = {} - pcall(vim.api.nvim_del_augroup_by_name, "ow.git.blame." .. buf) - sched_handle.cancel(buf) -end - ----@param buf integer? -function M.toggle_inline(buf) - buf = resolve_buf(buf) - local state = ensure_state(buf) - if not state then - util.warning("git blame: nothing to blame in this buffer") - return - end - state.inline = not state.inline - if state.inline then - attach_autocmds(buf, state) - run_blame(state, buf, function() - render_inline(buf) - end) - else - if vim.api.nvim_buf_is_valid(buf) then - vim.api.nvim_buf_clear_namespace(buf, NS_INLINE, 0, -1) - end - detach_autocmds(buf, state) - end -end - ----@class ow.Git.Blame.View ----@field source_buf integer ----@field source_win integer ----@field view_buf integer ----@field view_win integer ----@field group integer - ----@type table -local views = {} - ----@param s string ----@param w integer ----@return string -local function pad_w(s, w) - local sw = vim.api.nvim_strwidth(s) - if sw > w then - return vim.fn.strcharpart(s, 0, w) - end - return s .. string.rep(" ", w - sw) -end - ----@param state ow.Git.Blame.BufState ----@param line_count integer ----@return string[] -local function build_view_lines(state, line_count) - local lines = {} - for lnum = 1, line_count do - local sha = state.line_sha[lnum] - local commit = sha and state.commits[sha] - if commit and not util.is_zero_sha(sha) then - local author = pad_w(commit.author, AUTHOR_W) - local date = os.date("%Y-%m-%d", commit.author_time) - lines[lnum] = sha:sub(1, SHA_W) .. GAP .. author .. GAP .. date - else - lines[lnum] = pad_w("Uncommitted", VIEW_WIDTH) - end - end - return lines -end - ----@param view ow.Git.Blame.View ----@param state ow.Git.Blame.BufState -local function render_view(view, state) - if not vim.api.nvim_buf_is_valid(view.view_buf) then - return - end - local line_count = vim.api.nvim_buf_line_count(view.source_buf) - local lines = build_view_lines(state, line_count) - util.set_buf_lines(view.view_buf, 0, -1, lines) - vim.api.nvim_buf_clear_namespace(view.view_buf, NS_VIEW, 0, -1) - local author_col = SHA_W + #GAP - local date_col = author_col + AUTHOR_W + #GAP - for lnum = 1, #lines do - local sha = state.line_sha[lnum] - if sha and not util.is_zero_sha(sha) then - pcall(vim.api.nvim_buf_set_extmark, view.view_buf, NS_VIEW, lnum - 1, 0, { - end_col = SHA_W, - hl_group = "GitBlameSha", - }) - pcall(vim.api.nvim_buf_set_extmark, view.view_buf, NS_VIEW, lnum - 1, author_col, { - end_col = author_col + AUTHOR_W, - hl_group = "GitBlameAuthor", - }) - pcall(vim.api.nvim_buf_set_extmark, view.view_buf, NS_VIEW, lnum - 1, date_col, { - end_col = date_col + DATE_W, - hl_group = "GitBlameDate", - }) - end - end -end - ----@param source_buf integer -local function close_view(source_buf) - local view = views[source_buf] - if not view then - return - end - views[source_buf] = nil - pcall(vim.api.nvim_del_augroup_by_id, view.group) - if vim.api.nvim_win_is_valid(view.source_win) then - vim.wo[view.source_win].scrollbind = false - vim.wo[view.source_win].cursorbind = false - end - if vim.api.nvim_win_is_valid(view.view_win) then - pcall(vim.api.nvim_win_close, view.view_win, true) - end -end - ----@param source_buf integer ----@param state ow.Git.Blame.BufState ----@param source_win integer -local function open_view(source_buf, state, source_win) - local view_buf = vim.api.nvim_create_buf(false, true) - util.setup_scratch(view_buf, { buftype = "nofile", bufhidden = "wipe" }) - vim.bo[view_buf].filetype = "gitblame" - local view_win = vim.api.nvim_open_win(view_buf, false, { - split = "left", - width = VIEW_WIDTH, - win = source_win, - }) - vim.api.nvim_set_option_value("number", false, { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("relativenumber", false, { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("signcolumn", "no", { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("foldcolumn", "0", { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("wrap", false, { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("cursorline", true, { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("winfixwidth", true, { win = view_win, scope = "local" }) - vim.api.nvim_set_option_value("statuscolumn", "", { win = view_win, scope = "local" }) - - if not vim.tbl_contains(vim.opt.scrollopt:get(), "ver") then - vim.opt.scrollopt:append("ver") - end - vim.wo[source_win].scrollbind = true - vim.wo[source_win].cursorbind = true - vim.wo[view_win].scrollbind = true - vim.wo[view_win].cursorbind = true - - local group = vim.api.nvim_create_augroup( - "ow.git.blame.view." .. source_buf, - { clear = true } - ) - - ---@type ow.Git.Blame.View - local view = { - source_buf = source_buf, - source_win = source_win, - view_buf = view_buf, - view_win = view_win, - group = group, - } - views[source_buf] = view - render_view(view, state) - vim.api.nvim_win_call(view_win, function() - vim.cmd("syncbind") - end) - - vim.api.nvim_create_autocmd("WinClosed", { - group = group, - callback = function(args) - local closed = tonumber(args.match) - if closed == view_win or closed == source_win then - vim.schedule(function() - close_view(source_buf) - end) - end - end, - }) - vim.api.nvim_create_autocmd({ "BufWipeout", "BufDelete" }, { - group = group, - buffer = view_buf, - callback = function() - vim.schedule(function() - close_view(source_buf) - end) - end, - }) - - vim.keymap.set("n", "", function() - local lnum = vim.api.nvim_win_get_cursor(view_win)[1] - local sha = state.line_sha[lnum] - if not sha or util.is_zero_sha(sha) then - util.warning("git blame: line is not committed yet") - return - end - object.open(state.repo, sha, { split = false }) - end, { buffer = view_buf, silent = true, desc = "Open the commit" }) - vim.keymap.set("n", "q", function() - close_view(source_buf) - end, { buffer = view_buf, silent = true, desc = "Close the blame view" }) -end - ----@param buf integer? -function M.toggle_view(buf) - buf = resolve_buf(buf) - if views[buf] then - close_view(buf) - return - end - local state = ensure_state(buf) - if not state then - util.warning("git blame: nothing to blame in this buffer") - return - end - local source_win = vim.api.nvim_get_current_win() - if vim.api.nvim_win_get_buf(source_win) ~= buf then - source_win = vim.fn.bufwinid(buf) - end - if source_win == -1 then - return - end - run_blame(state, buf, function() - if - not vim.api.nvim_buf_is_valid(buf) - or not vim.api.nvim_win_is_valid(source_win) - then - return - end - open_view(buf, state, source_win) - end) -end - ---@param lines string[] ---@return integer width ---@return integer height @@ -884,34 +517,26 @@ end ---@param buf integer function M.detach(buf) - close_view(buf) local state = states[buf] if not state then return end - if vim.api.nvim_buf_is_valid(buf) then - vim.api.nvim_buf_clear_namespace(buf, NS_INLINE, 0, -1) - end - detach_autocmds(buf, state) state.epoch = state.epoch + 1 states[buf] = nil end -- The blame cache is keyed by `changedtick`, which a commit / checkout / --- rebase does not bump. Drop the cache for affected worktree buffers on --- a repo change so the next blame re-fetches; re-blame eagerly if a mode --- is showing. `git://` buffers blame a fixed revision and are skipped. +-- rebase does not bump. Drop the cache for affected worktree buffers on a +-- repo change so the next popup re-fetches. `git://` buffers blame a +-- fixed revision and are skipped. repo.on("change", function(r, change) - for buf, state in pairs(states) do + for _, state in pairs(states) do if state.repo == r and not state.revision and (change.paths[state.rel] or change.branch_changed) then state.tick = nil - if state.inline then - schedule(buf) - end end end end) diff --git a/plugin/git.lua b/plugin/git.lua index 4046029..e393909 100644 --- a/plugin/git.lua +++ b/plugin/git.lua @@ -45,7 +45,6 @@ local DEFAULT_HIGHLIGHTS = { GitBlameAuthor = "GitAuthor", GitBlameDate = "GitDate", - GitBlameInline = "Comment", GitBlameSha = "GitSha", } local STAGED_HUNK_HL = { @@ -322,12 +321,6 @@ vim.api.nvim_create_user_command("GitDiffOverlay", function() require("git.hunks").toggle_overlay() end, { desc = "Toggle the git diff overlay in the current buffer" }) -vim.keymap.set("n", "(git-blame-view)", function() - require("git.blame").toggle_view() -end, { silent = true, desc = "Toggle the git blame side window" }) -vim.keymap.set("n", "(git-blame-line)", function() - require("git.blame").toggle_inline() -end, { silent = true, desc = "Toggle inline git blame" }) vim.keymap.set("n", "(git-blame-popup)", function() require("git.blame").line_popup() end, { silent = true, desc = "Show git blame for the current line" }) @@ -344,9 +337,3 @@ end, { desc = "Open this file at the parent of the line's commit", }) -vim.api.nvim_create_user_command("GitBlame", function() - require("git.blame").toggle_view() -end, { desc = "Toggle the git blame side window" }) -vim.api.nvim_create_user_command("GitBlameLine", function() - require("git.blame").toggle_inline() -end, { desc = "Toggle inline git blame in the current buffer" }) diff --git a/test/git/blame_test.lua b/test/git/blame_test.lua index 4df3004..598b8d5 100644 --- a/test/git/blame_test.lua +++ b/test/git/blame_test.lua @@ -23,32 +23,6 @@ local function setup(committed, worktree, file) return dir, vim.api.nvim_get_current_buf() end ----@param buf integer ----@return ow.Git.Blame.BufState -local function enable_blame(buf) - blame.toggle_inline(buf) - t.wait_for(function() - local s = blame.state(buf) - return s ~= nil and s.tick ~= nil - end, "blame to populate the buffer state") - local s = assert(blame.state(buf)) - return s -end - ----@param buf integer ----@param ns_name string ----@return vim.api.keyset.get_extmark_item[] -local function marks(buf, ns_name) - local ns = vim.api.nvim_get_namespaces()[ns_name] - return vim.api.nvim_buf_get_extmarks(buf, ns, 0, -1, { details = true }) -end - ----@param buf integer ----@return vim.api.keyset.get_extmark_item[] -local function inline_marks(buf) - return marks(buf, "ow.git.blame.inline") -end - ---@return integer? local function find_float() for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do @@ -76,23 +50,24 @@ local function wait_buf_name(pat) end, "current buffer name to match " .. pat) end -t.test("inline annotation includes the relative time", function() - local _, buf = setup("alpha\nbeta\ngamma\n") +---@param buf integer +---@return ow.Git.Blame.BufState +local function populate_blame(buf) vim.api.nvim_set_current_buf(buf) vim.api.nvim_win_set_cursor(0, { 1, 0 }) - enable_blame(buf) + local tick = vim.api.nvim_buf_get_changedtick(buf) + blame.line_popup(buf) t.wait_for(function() - return #inline_marks(buf) == 1 - end, "an inline annotation on the current line") - local mark = assert(inline_marks(buf)[1]) - local details = assert(mark[4]) - local virt_text = assert(details.virt_text) - local chunk = assert(virt_text[1]) - t.truthy( - chunk[1]:find("just now", 1, true), - "a fresh commit reads as 'just now' in the annotation" - ) -end) + local s = blame.state(buf) + return s ~= nil and s.tick == tick + end, "blame to populate the buffer state") + for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_get_config(w).relative ~= "" then + pcall(vim.api.nvim_win_close, w, true) + end + end + return (assert(blame.state(buf))) +end t.test("line popup formats the datetime in the author timezone", function() local _, buf = setup("alpha\nbeta\n") @@ -119,7 +94,7 @@ end) t.test("porcelain parse of a committed file", function() local _, buf = setup("alpha\nbeta\ngamma\n") - local state = enable_blame(buf) + local state = populate_blame(buf) t.eq(vim.tbl_count(state.commits), 1, "one commit") local sha = state.line_sha[1] t.eq(state.line_sha[2], sha, "line 2 shares the commit") @@ -138,7 +113,7 @@ t.test("multiple line groups reuse one commit entry", function() h.git(dir, "commit", "-q", "-m", "change middle") vim.cmd.edit(dir .. "/a.txt") local buf = vim.api.nvim_get_current_buf() - local state = enable_blame(buf) + local state = populate_blame(buf) t.eq(vim.tbl_count(state.commits), 2, "two distinct commits") t.eq( state.line_sha[1], @@ -153,28 +128,29 @@ end) t.test("an edited line blames as the zero sha", function() local _, buf = setup("a\nb\nc\n") - local state = enable_blame(buf) + local state = populate_blame(buf) t.falsy(is_zero(state.line_sha[2]), "line 2 starts committed") vim.api.nvim_buf_set_lines(buf, 1, 2, false, { "EDITED" }) - vim.api.nvim_exec_autocmds("TextChanged", { buffer = buf }) - blame._flush(buf) - t.wait_for(function() - local s = assert(blame.state(buf)) - return s.line_sha[2] ~= nil and is_zero(s.line_sha[2]) - end, "the edited line to blame as uncommitted") + vim.api.nvim_win_set_cursor(0, { 2, 0 }) + local refreshed = populate_blame(buf) + t.truthy( + is_zero(refreshed.line_sha[2]), + "the edited line blames as uncommitted on the next fetch" + ) end) -t.test("blame refreshes after a git event", function() +t.test("blame picks up a commit amend on the next fetch", function() local dir, buf = setup("original\n") - local state = enable_blame(buf) + local state = populate_blame(buf) local sha1 = state.line_sha[1] h.git(dir, "commit", "--amend", "-m", "amended") local sha2 = h.git(dir, "rev-parse", "HEAD").stdout t.truthy(sha1 ~= sha2, "the amend produced a new commit") t.wait_for(function() - local s = blame.state(buf) - return s ~= nil and s.line_sha[1] == sha2 - end, "blame to pick up the amended commit", 2000) + return assert(blame.state(buf)).tick == nil + end, "the cache to be invalidated by the repo change") + local refreshed = populate_blame(buf) + t.eq(refreshed.line_sha[1], sha2, "blame picks up the amended sha") end) t.test("an untracked file blames every line as uncommitted", function() @@ -182,7 +158,7 @@ t.test("an untracked file blames every line as uncommitted", function() t.write(dir, "new.txt", "one\ntwo\nthree\n") vim.cmd.edit(dir .. "/new.txt") local buf = vim.api.nvim_get_current_buf() - local state = enable_blame(buf) + local state = populate_blame(buf) for i = 1, 3 do t.truthy(is_zero(state.line_sha[i]), "line " .. i .. " is uncommitted") end @@ -196,8 +172,6 @@ t.test("blame actions are no-ops off a worktree", function() end) t.quietly(function() blame.line_popup(buf) - blame.toggle_inline(buf) - blame.toggle_view(buf) end) t.eq(blame.state(buf), nil, "no state created for a non-worktree buffer") end) @@ -273,127 +247,6 @@ t.test("line popup works in a git:// object buffer", function() ) end) -t.test("inline toggle adds and removes the annotation", function() - local _, buf = setup("alpha\nbeta\ngamma\n") - vim.api.nvim_set_current_buf(buf) - vim.api.nvim_win_set_cursor(0, { 1, 0 }) - enable_blame(buf) - t.wait_for(function() - return #inline_marks(buf) == 1 - end, "an inline annotation on the current line") - t.eq(assert(inline_marks(buf)[1])[2], 0, "annotation on line 1") - blame.toggle_inline(buf) - t.eq(#inline_marks(buf), 0, "annotation cleared when toggled off") -end) - -t.test("inline annotation follows the cursor", function() - local _, buf = setup("alpha\nbeta\ngamma\n") - vim.api.nvim_set_current_buf(buf) - vim.api.nvim_win_set_cursor(0, { 1, 0 }) - enable_blame(buf) - t.wait_for(function() - return #inline_marks(buf) == 1 - end, "the initial annotation") - t.eq(assert(inline_marks(buf)[1])[2], 0) - vim.api.nvim_win_set_cursor(0, { 3, 0 }) - vim.api.nvim_exec_autocmds("CursorMoved", { buffer = buf }) - t.eq(#inline_marks(buf), 1, "still one annotation") - t.eq(assert(inline_marks(buf)[1])[2], 2, "annotation moved to line 3") -end) - ----@param buf integer ----@return integer view_win -local function wait_view(buf) - local win ---@type integer? - t.wait_for(function() - for _, w in ipairs(vim.api.nvim_list_wins()) do - local b = vim.api.nvim_win_get_buf(w) - if b ~= buf and vim.bo[b].filetype == "gitblame" then - win = w - return true - end - end - return false - end, "the blame side window to open") - return (assert(win)) -end - -t.test("toggle_view opens and closes a side split", function() - local _, buf = setup("alpha\nbeta\ngamma\n") - vim.api.nvim_set_current_buf(buf) - blame.toggle_view(buf) - local view_win = wait_view(buf) - t.defer(function() - if vim.api.nvim_win_is_valid(view_win) then - vim.api.nvim_win_close(view_win, true) - end - end) - blame.toggle_view(buf) - t.falsy( - vim.api.nvim_win_is_valid(view_win), - "toggling again closes the view" - ) -end) - -t.test("the side view shows sha, author and an absolute date", function() - local dir, buf = setup("alpha\nbeta\ngamma\n") - local sha = h.git(dir, "rev-parse", "HEAD").stdout - vim.api.nvim_set_current_buf(buf) - blame.toggle_view(buf) - local view_win = wait_view(buf) - t.defer(function() - if vim.api.nvim_win_is_valid(view_win) then - vim.api.nvim_win_close(view_win, true) - end - end) - local view_buf = vim.api.nvim_win_get_buf(view_win) - local row = vim.api.nvim_buf_get_lines(view_buf, 0, 1, false)[1] or "" - t.truthy( - vim.startswith(row, sha:sub(1, 8)), - "the row starts with the short sha" - ) - t.truthy(row:find("t", 1, true), "the row includes the author") - t.truthy( - row:match("%d%d%d%d%-%d%d%-%d%d$"), - "the row ends with a YYYY-MM-DD date" - ) -end) - -t.test(" on the view row opens the commit", function() - local _, buf = setup("alpha\nbeta\ngamma\n") - vim.api.nvim_set_current_buf(buf) - blame.toggle_view(buf) - local view_win = wait_view(buf) - t.defer(function() - if vim.api.nvim_win_is_valid(view_win) then - vim.api.nvim_win_close(view_win, true) - end - end) - vim.api.nvim_set_current_win(view_win) - vim.api.nvim_win_set_cursor(view_win, { 1, 0 }) - t.press("") - wait_buf_name("^git://%x+$") -end) - -t.test("closing the source window tears down the view", function() - local _, buf = setup("alpha\nbeta\ngamma\n") - vim.api.nvim_set_current_buf(buf) - local source_win = vim.api.nvim_get_current_win() - vim.cmd("split") - vim.api.nvim_set_current_win(source_win) - blame.toggle_view(buf) - local view_win = wait_view(buf) - t.defer(function() - if vim.api.nvim_win_is_valid(view_win) then - vim.api.nvim_win_close(view_win, true) - end - end) - vim.api.nvim_win_close(source_win, true) - t.wait_for(function() - return not vim.api.nvim_win_is_valid(view_win) - end, "the view to close when the source window closes") -end) - t.test("open_commit opens the commit that last touched the line", function() local _, buf = setup("alpha\nbeta\ngamma\n") vim.api.nvim_set_current_buf(buf) @@ -451,11 +304,9 @@ t.test("drilling chains through git:// buffers", function() wait_buf_name("^git://%x+$") end) -t.test("detach clears blame state and annotations", function() +t.test("detach drops the blame state", function() local _, buf = setup("a\nb\nc\n") - vim.api.nvim_set_current_buf(buf) - enable_blame(buf) + populate_blame(buf) blame.detach(buf) t.eq(blame.state(buf), nil, "state dropped on detach") - t.eq(#inline_marks(buf), 0, "inline annotation cleared") end)