diff --git a/lua/gitsigns/hunks.lua b/lua/gitsigns/hunks.lua index 096785fbf..5eb17e4d3 100644 --- a/lua/gitsigns/hunks.lua +++ b/lua/gitsigns/hunks.lua @@ -225,6 +225,83 @@ function M.calc_signs(hunk, next, min_lnum, max_lnum, untracked) return signs end +--- Calculate signs needed to be applied from a hunk for a specified line range. +--- @param hunk Gitsigns.Hunk.Hunk +--- @param untracked boolean +--- @return Gitsigns.Sign[] +function M.calc_sign_ranges(hunk, untracked) + assert( + not untracked or hunk.type == 'add', + string.format('Invalid hunk with untracked=%s hunk="%s"', untracked, hunk.head) + ) + + local start = hunk.added.start + local added = hunk.added.count + local removed = hunk.removed.count + local end_lnum = change_end(hunk) + + if hunk.type == 'delete' and start == 0 then + -- topdelete signs get placed one row lower + return { { + type = 'topdelete', + count = removed, + lnum = 1 + } } + elseif hunk.type == 'delete' then + return { { + type = 'delete', + count = removed, + lnum = start, + end_lnum = start + } } + elseif untracked or hunk.type == 'add' then + return { { + type = untracked and 'untracked' or 'add', + count = added, + lnum = start, + end_lnum = end_lnum + } } + end + + --- @type Gitsigns.Sign[] + local signs = {} + + -- changedelete + if removed > added then + if end_lnum > start then + signs[#signs + 1] = { + type = 'change', + lnum = start, + end_lnum = end_lnum - 1 + } + end + + signs[#signs + 1] = { + type = 'changedelete', + count = removed, + lnum = end_lnum + } + else -- change + signs[#signs + 1] = { + type = 'change', + lnum = start, + end_lnum = end_lnum + } + + -- Added lines of a 'change' hunk + if added > removed then + signs[#signs + 1] = { + type = 'add', + count = added - removed, + lnum = end_lnum, + end_lnum = hunk.vend, + } + end + end + + return signs +end + --- @param relpath string --- @param hunks Gitsigns.Hunk.Hunk[] --- @param mode_bits string diff --git a/lua/gitsigns/signs.lua b/lua/gitsigns/signs.lua index 9a7bb844e..08a658319 100644 --- a/lua/gitsigns/signs.lua +++ b/lua/gitsigns/signs.lua @@ -6,6 +6,7 @@ local dprint = require('gitsigns.debug.log').dprint --- @field type Gitsigns.SignType --- @field count? integer --- @field lnum integer +--- @field end_lnum? integer --- @class Gitsigns.Signs --- @field hls table diff --git a/lua/gitsigns/signs/extmarks.lua b/lua/gitsigns/signs/extmarks.lua index f029c616c..e4124880c 100644 --- a/lua/gitsigns/signs/extmarks.lua +++ b/lua/gitsigns/signs/extmarks.lua @@ -7,6 +7,8 @@ local M = {} local group_base = 'gitsigns_extmark_signs_' +local use_invalidate_signs = vim.fn.has('nvim-0.10') > 0 + --- @param cfg Gitsigns.SignConfig --- @param hls table --- @param name string @@ -24,6 +26,9 @@ end --- @param last_orig? integer --- @param last_new? integer function M:on_lines(buf, _, last_orig, last_new) + if use_invalidate_signs then + return + end -- Remove extmarks on line deletions to mimic -- the behaviour of vim signs. if last_orig > last_new then @@ -65,11 +70,15 @@ function M:add(bufnr, signs) local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, self.ns, s.lnum - 1, -1, { id = s.lnum, + end_row = s.end_lnum and s.end_lnum - 1 or nil, sign_text = config.signcolumn and text or '', priority = config.sign_priority, sign_hl_group = hls.hl, number_hl_group = config.numhl and hls.numhl or nil, line_hl_group = config.linehl and hls.linehl or nil, + + undo_restore = not use_invalidate_signs, + invalidate = use_invalidate_signs, }) if not ok and config.debug_mode then