mirror of
https://github.com/lewis6991/gitsigns.nvim
synced 2025-02-20 22:47:23 +00:00
refactor(signs): simplify interface
This commit is contained in:
parent
f83a2e11cd
commit
9b3899a610
71
lua/gitsigns/hunks.lua
generated
71
lua/gitsigns/hunks.lua
generated
@ -3,6 +3,8 @@ local StatusObj = require('gitsigns.status').StatusObj
|
||||
|
||||
local util = require('gitsigns.util')
|
||||
|
||||
local min, max = math.min, math.max
|
||||
|
||||
local M = {Node = {}, Hunk = {}, Hunk_Public = {}, }
|
||||
|
||||
|
||||
@ -64,8 +66,8 @@ function M.create_hunk(start_a, count_a, start_b, count_b)
|
||||
hunk.type = "add"
|
||||
else
|
||||
|
||||
hunk.dend = added.start + math.min(added.count, removed.count) - 1
|
||||
hunk.vend = hunk.dend + math.max(added.count - removed.count, 0)
|
||||
hunk.dend = added.start + min(added.count, removed.count) - 1
|
||||
hunk.vend = hunk.dend + max(added.count - removed.count, 0)
|
||||
hunk.type = "change"
|
||||
end
|
||||
|
||||
@ -82,8 +84,8 @@ function M.create_partial_hunk(hunks, top, bot)
|
||||
|
||||
added_in_range = added_in_hunk
|
||||
else
|
||||
local added_above_bot = math.max(0, bot + 1 - (h.start + h.removed.count))
|
||||
local added_above_top = math.max(0, top - (h.start + h.removed.count))
|
||||
local added_above_bot = max(0, bot + 1 - (h.start + h.removed.count))
|
||||
local added_above_top = max(0, top - (h.start + h.removed.count))
|
||||
|
||||
if h.start >= top and h.start <= bot then
|
||||
|
||||
@ -146,35 +148,40 @@ function M.parse_diff_line(line)
|
||||
return hunk
|
||||
end
|
||||
|
||||
function M.calc_signs(hunk)
|
||||
|
||||
function M.calc_signs(hunk, min_lnum, max_lnum)
|
||||
local added, removed = hunk.added.count, hunk.removed.count
|
||||
|
||||
if hunk.type == 'delete' and hunk.start == 0 then
|
||||
if min_lnum <= 1 then
|
||||
|
||||
return { { type = 'topdelete', count = removed, lnum = 1 } }
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
local signs = {}
|
||||
local count = hunk.type == 'add' and hunk.added.count or
|
||||
hunk.removed.count
|
||||
for i = hunk.start, hunk.dend do
|
||||
local topdelete = hunk.type == 'delete' and i == 0
|
||||
local changedelete = hunk.type == 'change' and
|
||||
hunk.removed.count > hunk.added.count and
|
||||
i == hunk.dend
|
||||
|
||||
for lnum = max(hunk.start, min_lnum), min(hunk.dend, max_lnum) do
|
||||
local changedelete = hunk.type == 'change' and removed > added and lnum == hunk.dend
|
||||
|
||||
signs[#signs + 1] = {
|
||||
type = topdelete and 'topdelete' or
|
||||
changedelete and 'changedelete' or
|
||||
hunk.type,
|
||||
count = i == hunk.start and count,
|
||||
lnum = topdelete and 1 or i,
|
||||
type = changedelete and 'changedelete' or hunk.type,
|
||||
count = lnum == hunk.start and (hunk.type == 'add' and added or removed),
|
||||
lnum = lnum,
|
||||
}
|
||||
end
|
||||
if hunk.type == "change" then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
if add > remove then
|
||||
local count_diff = add - remove
|
||||
for i = 1, count_diff do
|
||||
signs[#signs + 1] = {
|
||||
type = 'add',
|
||||
count = i == 1 and count_diff,
|
||||
lnum = hunk.dend + i,
|
||||
}
|
||||
end
|
||||
|
||||
if hunk.type == "change" and added > removed and
|
||||
hunk.dend >= min_lnum and hunk.dend <= max_lnum then
|
||||
local count = added - removed
|
||||
for lnum = max(hunk.dend, min_lnum), min(hunk.dend + count, max_lnum) do
|
||||
signs[#signs + 1] = {
|
||||
type = 'add',
|
||||
count = lnum == hunk.dend and count,
|
||||
lnum = lnum,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@ -234,10 +241,10 @@ function M.get_summary(hunks)
|
||||
status.removed = status.removed + hunk.removed.count
|
||||
elseif hunk.type == 'change' then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
local min = math.min(add, remove)
|
||||
status.changed = status.changed + min
|
||||
status.added = status.added + add - min
|
||||
status.removed = status.removed + remove - min
|
||||
local delta = min(add, remove)
|
||||
status.changed = status.changed + delta
|
||||
status.added = status.added + add - delta
|
||||
status.removed = status.removed + remove - delta
|
||||
end
|
||||
end
|
||||
|
||||
|
10
lua/gitsigns/manager.lua
generated
10
lua/gitsigns/manager.lua
generated
@ -65,26 +65,26 @@ function M.apply_win_signs(bufnr, hunks, top, bot, clear)
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
if clear and i == 1 or
|
||||
top <= hunk.vend and bot >= hunk.start then
|
||||
signs:schedule(bufnr, gs_hunks.calc_signs(hunk))
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, top, bot))
|
||||
end
|
||||
if hunk.start > bot then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
signs:draw(bufnr, top, bot)
|
||||
end
|
||||
|
||||
M.on_lines = function(buf, first, _, last_new)
|
||||
M.on_lines = function(buf, first, last_orig, last_new)
|
||||
local bcache = cache[buf]
|
||||
if not bcache then
|
||||
dprint('Cache for buffer was nil. Detaching')
|
||||
return true
|
||||
end
|
||||
|
||||
signs:on_lines(buf, first, last_orig, last_new)
|
||||
|
||||
|
||||
if bcache.hunks and signs:need_redraw(buf, first, last_new) then
|
||||
|
||||
if bcache.hunks and signs:contains(buf, first, last_new) then
|
||||
|
||||
|
||||
bcache.hunks = nil
|
||||
|
1
lua/gitsigns/signs/base.lua
generated
1
lua/gitsigns/signs/base.lua
generated
@ -32,6 +32,7 @@ local M = {Sign = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return M
|
||||
|
65
lua/gitsigns/signs/extmarks.lua
generated
65
lua/gitsigns/signs/extmarks.lua
generated
@ -2,25 +2,11 @@ local api = vim.api
|
||||
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
local config = require('gitsigns.config').config
|
||||
local nvim = require('gitsigns.nvim')
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
local function attach(obj, bufnr)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
api.nvim_buf_attach(bufnr, false, {
|
||||
on_lines = function(_, buf, _, _, last_orig, last_new)
|
||||
if last_orig > last_new then
|
||||
obj:remove(buf, last_new + 1, last_orig)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local group_base = 'gitsigns_extmark_signs_'
|
||||
|
||||
function M.new(cfg, name)
|
||||
@ -28,29 +14,18 @@ function M.new(cfg, name)
|
||||
self.config = cfg
|
||||
self.group = group_base .. (name or '')
|
||||
self.ns = api.nvim_create_namespace(self.group)
|
||||
|
||||
nvim.augroup(self.group)
|
||||
nvim.autocmd('BufRead', {
|
||||
group = self.group,
|
||||
callback = vim.schedule_wrap(function()
|
||||
attach(self)
|
||||
end),
|
||||
})
|
||||
|
||||
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if api.nvim_buf_is_loaded(buf) and api.nvim_buf_get_name(buf) ~= '' then
|
||||
attach(self, buf)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function M.draw(_self, _bufnr, _top, _bot)
|
||||
function M:on_lines(buf, _, last_orig, last_new)
|
||||
|
||||
|
||||
if last_orig > last_new then
|
||||
self:remove(buf, last_new + 1, last_orig)
|
||||
end
|
||||
end
|
||||
|
||||
function M.remove(self, bufnr, start_lnum, end_lnum)
|
||||
function M:remove(bufnr, start_lnum, end_lnum)
|
||||
if start_lnum then
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, start_lnum - 1, end_lnum or start_lnum)
|
||||
else
|
||||
@ -58,17 +33,7 @@ function M.remove(self, bufnr, start_lnum, end_lnum)
|
||||
end
|
||||
end
|
||||
|
||||
local function placed(self, bufnr, start, last)
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr, self.ns,
|
||||
{ start - 1, 0 },
|
||||
{ last or start, 0 },
|
||||
{ limit = 1 })
|
||||
|
||||
return #marks > 0
|
||||
end
|
||||
|
||||
function M.schedule(self, bufnr, signs)
|
||||
function M:add(bufnr, signs)
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
|
||||
return
|
||||
@ -77,7 +42,7 @@ function M.schedule(self, bufnr, signs)
|
||||
local cfg = self.config
|
||||
|
||||
for _, s in ipairs(signs) do
|
||||
if not placed(self, bufnr, s.lnum) then
|
||||
if not self:contains(bufnr, s.lnum) then
|
||||
local cs = cfg[s.type]
|
||||
local text = cs.text
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
@ -99,15 +64,13 @@ function M.schedule(self, bufnr, signs)
|
||||
end
|
||||
end
|
||||
|
||||
function M.add(self, bufnr, signs)
|
||||
self:schedule(bufnr, signs)
|
||||
function M:contains(bufnr, start, last)
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr, self.ns, { start - 1, 0 }, { last or start, 0 }, { limit = 1 })
|
||||
return #marks > 0
|
||||
end
|
||||
|
||||
function M.need_redraw(self, bufnr, start, last)
|
||||
return placed(self, bufnr, start, last)
|
||||
end
|
||||
|
||||
function M.reset(self)
|
||||
function M:reset()
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
self:remove(buf)
|
||||
end
|
||||
|
112
lua/gitsigns/signs/vimfn.lua
generated
112
lua/gitsigns/signs/vimfn.lua
generated
@ -3,27 +3,12 @@ local fn = vim.fn
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
local setdefault = require('gitsigns.util').setdefault
|
||||
local emptytable = require('gitsigns.util').emptytable
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
local M = {}
|
||||
|
||||
local SignName = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local sign_map = {
|
||||
add = "GitSignsAdd",
|
||||
delete = "GitSignsDelete",
|
||||
change = "GitSignsChange",
|
||||
topdelete = "GitSignsTopDelete",
|
||||
changedelete = "GitSignsChangeDelete",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -32,52 +17,44 @@ local sign_map = {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function M.draw(self, bufnr, top, bot)
|
||||
local to_place = {}
|
||||
for i = top, bot do
|
||||
if self.scheduled[bufnr][i] then
|
||||
to_place[#to_place + 1] = self.scheduled[bufnr][i]
|
||||
self.placed[bufnr][i] = self.scheduled[bufnr][i]
|
||||
self.scheduled[bufnr][i] = nil
|
||||
end
|
||||
end
|
||||
if to_place[1] then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
local function capitalise_word(x)
|
||||
return x:sub(1, 1):upper() .. x:sub(2)
|
||||
end
|
||||
|
||||
local sign_define_cache = {}
|
||||
local function get_sign_name(obj, stype)
|
||||
local cache = obj.sign_name_cache
|
||||
if not cache[stype] then
|
||||
cache[stype] = string.format(
|
||||
'%s%s', 'GitSigns', capitalise_word(stype))
|
||||
end
|
||||
|
||||
local function sign_get(name)
|
||||
if not sign_define_cache[name] then
|
||||
return cache[stype]
|
||||
end
|
||||
|
||||
local function sign_get(obj, name)
|
||||
if not obj.sign_define_cache[name] then
|
||||
local s = fn.sign_getdefined(name)
|
||||
if not vim.tbl_isempty(s) then
|
||||
sign_define_cache[name] = s
|
||||
obj.sign_define_cache[name] = s
|
||||
end
|
||||
end
|
||||
return sign_define_cache[name]
|
||||
return obj.sign_define_cache[name]
|
||||
end
|
||||
|
||||
|
||||
local function define_sign(name, opts, redefine)
|
||||
local function define_sign(obj, name, opts, redefine)
|
||||
if redefine then
|
||||
sign_define_cache[name] = nil
|
||||
obj.sign_define_cache[name] = nil
|
||||
fn.sign_undefine(name)
|
||||
fn.sign_define(name, opts)
|
||||
elseif not sign_get(name) then
|
||||
elseif not sign_get(obj, name) then
|
||||
fn.sign_define(name, opts)
|
||||
end
|
||||
end
|
||||
|
||||
local function define_signs(obj, redefine)
|
||||
|
||||
for t, name in pairs(sign_map) do
|
||||
local cs = obj.config[t]
|
||||
define_sign(name, {
|
||||
for stype, cs in pairs(obj.config) do
|
||||
define_sign(obj, get_sign_name(obj, stype), {
|
||||
texthl = cs.hl,
|
||||
text = config.signcolumn and cs.text or nil,
|
||||
numhl = config.numhl and cs.numhl,
|
||||
@ -92,42 +69,43 @@ function M.new(cfg, name)
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.group = group_base .. (name or '')
|
||||
self.config = cfg
|
||||
self.placed = {}
|
||||
self.scheduled = {}
|
||||
|
||||
setdefault(self.placed)
|
||||
setdefault(self.scheduled)
|
||||
self.placed = emptytable()
|
||||
self.sign_name_cache = {}
|
||||
self.sign_define_cache = {}
|
||||
|
||||
define_signs(self, false)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function M.remove(self, bufnr, start_lnum, end_lnum)
|
||||
function M:on_lines(_, _, _, _)
|
||||
end
|
||||
|
||||
function M:remove(bufnr, start_lnum, end_lnum)
|
||||
end_lnum = end_lnum or start_lnum
|
||||
|
||||
if start_lnum then
|
||||
for lnum = start_lnum, end_lnum do
|
||||
self.placed[bufnr][lnum] = nil
|
||||
self.scheduled[bufnr][lnum] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr, id = lnum })
|
||||
end
|
||||
else
|
||||
self.placed[bufnr] = nil
|
||||
self.scheduled[bufnr] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr })
|
||||
end
|
||||
end
|
||||
|
||||
function M.schedule(self, bufnr, signs)
|
||||
function M:add(bufnr, signs)
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local to_place = {}
|
||||
|
||||
local cfg = self.config
|
||||
for _, s in ipairs(signs) do
|
||||
local stype = sign_map[s.type]
|
||||
local sign_name = get_sign_name(self, s.type)
|
||||
|
||||
local cs = cfg[s.type]
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
@ -135,8 +113,8 @@ function M.schedule(self, bufnr, signs)
|
||||
local cc = config.count_chars
|
||||
local count_suffix = cc[count] and tostring(count) or (cc['+'] and 'Plus') or ''
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
stype = stype .. count_suffix
|
||||
define_sign(stype, {
|
||||
sign_name = sign_name .. count_suffix
|
||||
define_sign(self, sign_name, {
|
||||
texthl = cs.hl,
|
||||
text = config.signcolumn and cs.text .. count_char or '',
|
||||
numhl = config.numhl and cs.numhl,
|
||||
@ -145,26 +123,25 @@ function M.schedule(self, bufnr, signs)
|
||||
end
|
||||
|
||||
if not self.placed[bufnr][s.lnum] then
|
||||
self.scheduled[bufnr][s.lnum] = {
|
||||
local sign = {
|
||||
id = s.lnum,
|
||||
group = self.group,
|
||||
name = stype,
|
||||
name = sign_name,
|
||||
buffer = bufnr,
|
||||
lnum = s.lnum,
|
||||
priority = config.sign_priority,
|
||||
}
|
||||
self.placed[bufnr][s.lnum] = sign
|
||||
to_place[#to_place + 1] = sign
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.add(self, bufnr, signs)
|
||||
self:schedule(bufnr, signs)
|
||||
for _, s in ipairs(signs) do
|
||||
self:draw(bufnr, s.lnum, s.lnum)
|
||||
if #to_place > 0 then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
end
|
||||
|
||||
function M.need_redraw(self, bufnr, start, last)
|
||||
function M:contains(bufnr, start, last)
|
||||
for i = start + 1, last + 1 do
|
||||
if self.placed[bufnr][i] then
|
||||
return true
|
||||
@ -173,11 +150,8 @@ function M.need_redraw(self, bufnr, start, last)
|
||||
return false
|
||||
end
|
||||
|
||||
function M.reset(self)
|
||||
self.placed = {}
|
||||
self.scheduled = {}
|
||||
setdefault(self.placed)
|
||||
setdefault(self.scheduled)
|
||||
function M:reset()
|
||||
self.placed = emptytable()
|
||||
fn.sign_unplace(self.group)
|
||||
define_signs(self, true)
|
||||
end
|
||||
|
4
lua/gitsigns/util.lua
generated
4
lua/gitsigns/util.lua
generated
@ -132,8 +132,8 @@ function M.calc_base(base)
|
||||
return base
|
||||
end
|
||||
|
||||
function M.setdefault(tbl)
|
||||
setmetatable(tbl, {
|
||||
function M.emptytable()
|
||||
return setmetatable({}, {
|
||||
__index = function(t, k)
|
||||
t[k] = {}
|
||||
return t[k]
|
||||
|
@ -3,6 +3,8 @@ local StatusObj = require('gitsigns.status').StatusObj
|
||||
|
||||
local util = require('gitsigns.util')
|
||||
|
||||
local min, max = math.min, math.max
|
||||
|
||||
local record M
|
||||
enum Type
|
||||
"add"
|
||||
@ -64,8 +66,8 @@ function M.create_hunk(start_a: integer, count_a: integer, start_b: integer, cou
|
||||
hunk.type = "add"
|
||||
else
|
||||
-- change
|
||||
hunk.dend = added.start + math.min(added.count, removed.count) - 1
|
||||
hunk.vend = hunk.dend + math.max(added.count - removed.count, 0)
|
||||
hunk.dend = added.start + min(added.count, removed.count) - 1
|
||||
hunk.vend = hunk.dend + max(added.count - removed.count, 0)
|
||||
hunk.type = "change"
|
||||
end
|
||||
|
||||
@ -82,8 +84,8 @@ function M.create_partial_hunk(hunks: {Hunk}, top: integer, bot: integer): Hunk
|
||||
-- Range contains hunk
|
||||
added_in_range = added_in_hunk
|
||||
else
|
||||
local added_above_bot = math.max(0, bot + 1 - (h.start + h.removed.count))
|
||||
local added_above_top = math.max(0, top - (h.start + h.removed.count))
|
||||
local added_above_bot = max(0, bot + 1 - (h.start + h.removed.count))
|
||||
local added_above_top = max(0, top - (h.start + h.removed.count))
|
||||
|
||||
if h.start >= top and h.start <= bot then
|
||||
-- Range top intersects hunk
|
||||
@ -146,35 +148,40 @@ function M.parse_diff_line(line: string): Hunk
|
||||
return hunk
|
||||
end
|
||||
|
||||
function M.calc_signs(hunk: Hunk): {Sign}
|
||||
-- Calculate signs needed to be applied from a hunk for a specified line range.
|
||||
function M.calc_signs(hunk: Hunk, min_lnum: integer, max_lnum: integer): {Sign}
|
||||
local added, removed = hunk.added.count, hunk.removed.count
|
||||
|
||||
if hunk.type == 'delete' and hunk.start == 0 then
|
||||
if min_lnum <= 1 then
|
||||
-- topdelete signs get placed one row lower
|
||||
return {{ type = 'topdelete', count = removed, lnum = 1 }}
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
local signs = {}
|
||||
local count = hunk.type == 'add' and hunk.added.count or
|
||||
hunk.removed.count
|
||||
for i = hunk.start, hunk.dend do
|
||||
local topdelete = hunk.type == 'delete' and i == 0
|
||||
local changedelete = hunk.type == 'change' and
|
||||
hunk.removed.count > hunk.added.count and
|
||||
i == hunk.dend
|
||||
-- topdelete signs get placed one row lower
|
||||
|
||||
for lnum = max(hunk.start, min_lnum), min(hunk.dend, max_lnum) do
|
||||
local changedelete = hunk.type == 'change' and removed > added and lnum == hunk.dend
|
||||
|
||||
signs[#signs+1] = {
|
||||
type = topdelete and 'topdelete' or
|
||||
changedelete and 'changedelete' or
|
||||
hunk.type,
|
||||
count = i == hunk.start and count,
|
||||
lnum = topdelete and 1 or i
|
||||
type = changedelete and 'changedelete' or hunk.type,
|
||||
count = lnum == hunk.start and (hunk.type == 'add' and added or removed),
|
||||
lnum = lnum
|
||||
}
|
||||
end
|
||||
if hunk.type == "change" then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
if add > remove then
|
||||
local count_diff = add - remove
|
||||
for i = 1, count_diff do
|
||||
signs[#signs+1] = {
|
||||
type = 'add',
|
||||
count = i == 1 and count_diff,
|
||||
lnum = hunk.dend + i
|
||||
}
|
||||
end
|
||||
|
||||
if hunk.type == "change" and added > removed and
|
||||
hunk.dend >= min_lnum and hunk.dend <= max_lnum then
|
||||
local count = added - removed
|
||||
for lnum = max(hunk.dend, min_lnum), min(hunk.dend + count, max_lnum) do
|
||||
signs[#signs+1] = {
|
||||
type = 'add',
|
||||
count = lnum == hunk.dend and count,
|
||||
lnum = lnum
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@ -234,10 +241,10 @@ function M.get_summary(hunks: {Hunk}): StatusObj
|
||||
status.removed = status.removed + hunk.removed.count
|
||||
elseif hunk.type == 'change' then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
local min = math.min(add, remove)
|
||||
status.changed = status.changed + min
|
||||
status.added = status.added + add - min
|
||||
status.removed = status.removed + remove - min
|
||||
local delta = min(add, remove)
|
||||
status.changed = status.changed + delta
|
||||
status.added = status.added + add - delta
|
||||
status.removed = status.removed + remove - delta
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,26 +65,26 @@ function M.apply_win_signs(bufnr: integer, hunks: {Hunk}, top: integer, bot: int
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
if clear and i == 1
|
||||
or top <= hunk.vend and bot >= hunk.start then
|
||||
signs:schedule(bufnr, gs_hunks.calc_signs(hunk))
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, top, bot))
|
||||
end
|
||||
if hunk.start > bot then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
signs:draw(bufnr, top, bot)
|
||||
end
|
||||
|
||||
M.on_lines = function(buf: integer, first: integer, _: integer, last_new: integer): boolean
|
||||
M.on_lines = function(buf: integer, first: integer, last_orig: integer, last_new: integer): boolean
|
||||
local bcache = cache[buf]
|
||||
if not bcache then
|
||||
dprint('Cache for buffer was nil. Detaching')
|
||||
return true
|
||||
end
|
||||
|
||||
signs:on_lines(buf, first, last_orig, last_new)
|
||||
|
||||
-- Signs in changed regions get invalidated so we need to force a redraw if
|
||||
-- any signs get removed.
|
||||
if bcache.hunks and signs:need_redraw(buf, first, last_new) then
|
||||
if bcache.hunks and signs:contains(buf, first, last_new) then
|
||||
-- invalidate hunks to force a sign redraw
|
||||
-- TODO(lewis6991): Force redraw without having to re-diff
|
||||
bcache.hunks = nil
|
||||
|
@ -24,14 +24,15 @@ local record M
|
||||
-- Used by signs/vimfn.tl
|
||||
placed: {integer:{integer:vim.fn.SignPlaceItem}}
|
||||
scheduled: {integer:{integer:vim.fn.SignPlaceItem}}
|
||||
sign_define_cache: {string:table}
|
||||
sign_name_cache: {string:string}
|
||||
|
||||
new : function(cfg: SignsConfig, name: string): M
|
||||
draw : function(M, bufnr: integer, top: integer, bot: integer): boolean
|
||||
remove : function(M, bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
schedule : function(M, bufnr: integer, signs: {M.Sign})
|
||||
add : function(M, bufnr: integer, signs: {M.Sign})
|
||||
need_redraw : function(M, bufnr: integer, start: integer, last: integer): boolean
|
||||
reset : function(M)
|
||||
new : function(cfg: SignsConfig, name: string): M
|
||||
remove : function(M, bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
add : function(M, bufnr: integer, signs: {M.Sign})
|
||||
contains : function(M, bufnr: integer, start: integer, last: integer): boolean
|
||||
on_lines : function(M, bufnr: integer, first: integer, last_orig: integer, last_new: integer)
|
||||
reset : function(M)
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -2,25 +2,11 @@ local api = vim.api
|
||||
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
local config = require('gitsigns.config').config
|
||||
local nvim = require('gitsigns.nvim')
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
local M: B = {}
|
||||
|
||||
-- Attach to each buffer in order to remove extmarks on line deletions to mimic
|
||||
-- the behaviour of vim signs.
|
||||
local function attach(obj: B, bufnr: integer)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
api.nvim_buf_attach(bufnr, false, {
|
||||
on_lines = function(_, buf: integer, _, _, last_orig: integer, last_new: integer)
|
||||
if last_orig > last_new then
|
||||
obj:remove(buf, last_new+1, last_orig)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local group_base = 'gitsigns_extmark_signs_'
|
||||
|
||||
function M.new(cfg: SignsConfig, name: string): B
|
||||
@ -28,29 +14,18 @@ function M.new(cfg: SignsConfig, name: string): B
|
||||
self.config = cfg
|
||||
self.group = group_base..(name or '')
|
||||
self.ns = api.nvim_create_namespace(self.group)
|
||||
|
||||
nvim.augroup(self.group)
|
||||
nvim.autocmd('BufRead', {
|
||||
group = self.group,
|
||||
callback = vim.schedule_wrap(function()
|
||||
attach(self)
|
||||
end)
|
||||
})
|
||||
|
||||
-- Attach to all open buffers
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if api.nvim_buf_is_loaded(buf) and api.nvim_buf_get_name(buf) ~= '' then
|
||||
attach(self, buf)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function M.draw(_self: B, _bufnr: integer, _top: integer, _bot: integer): boolean
|
||||
function M:on_lines(buf: integer, _: integer, last_orig: integer, last_new: integer)
|
||||
-- Remove extmarks on line deletions to mimic
|
||||
-- the behaviour of vim signs.
|
||||
if last_orig > last_new then
|
||||
self:remove(buf, last_new+1, last_orig)
|
||||
end
|
||||
end
|
||||
|
||||
function M.remove(self: B, bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
function M:remove(bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
if start_lnum then
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, start_lnum-1, end_lnum or start_lnum)
|
||||
else
|
||||
@ -58,17 +33,7 @@ function M.remove(self: B, bufnr: integer, start_lnum: integer, end_lnum: intege
|
||||
end
|
||||
end
|
||||
|
||||
local function placed(self: B, bufnr: integer, start: integer, last: integer): boolean
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr, self.ns,
|
||||
{start-1, 0},
|
||||
{last or start, 0},
|
||||
{limit=1}
|
||||
)
|
||||
return #marks > 0
|
||||
end
|
||||
|
||||
function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
function M:add(bufnr: integer, signs: {M.Sign})
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
@ -77,7 +42,7 @@ function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
local cfg = self.config
|
||||
|
||||
for _, s in ipairs(signs) do
|
||||
if not placed(self, bufnr, s.lnum) then
|
||||
if not self:contains(bufnr, s.lnum) then
|
||||
local cs = cfg[s.type]
|
||||
local text = cs.text
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
@ -99,15 +64,13 @@ function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
end
|
||||
end
|
||||
|
||||
function M.add(self: B, bufnr: integer, signs: {M.Sign})
|
||||
self:schedule(bufnr, signs)
|
||||
function M:contains(bufnr: integer, start: integer, last: integer): boolean
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr, self.ns, {start-1, 0}, {last or start, 0}, {limit=1})
|
||||
return #marks > 0
|
||||
end
|
||||
|
||||
function M.need_redraw(self: B, bufnr: integer, start: integer, last: integer): boolean
|
||||
return placed(self, bufnr, start, last)
|
||||
end
|
||||
|
||||
function M.reset(self: B)
|
||||
function M:reset()
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
self:remove(buf)
|
||||
end
|
||||
|
@ -3,81 +3,58 @@ local fn = vim.fn
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
local setdefault = require('gitsigns.util').setdefault
|
||||
local emptytable = require('gitsigns.util').emptytable
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
local M: B = {}
|
||||
|
||||
local enum SignName
|
||||
"GitSignsAdd"
|
||||
"GitSignsDelete"
|
||||
"GitSignsChange"
|
||||
"GitSignsTopDelete"
|
||||
"GitSignsChangeDelete"
|
||||
end
|
||||
|
||||
local sign_map: {B.SignType:SignName} = {
|
||||
add = "GitSignsAdd",
|
||||
delete = "GitSignsDelete",
|
||||
change = "GitSignsChange",
|
||||
topdelete = "GitSignsTopDelete",
|
||||
changedelete = "GitSignsChangeDelete",
|
||||
}
|
||||
|
||||
-- The internal representation of signs in Neovim is a linked list which is
|
||||
-- slow to index. Also when a sign is added/removed, it causes the sign column
|
||||
-- width to re-calculated which requires a full scan of the signs list.
|
||||
-- The internal representation of signs in Neovim is a linked list which is slow
|
||||
-- to index. To improve efficiency we add an abstraction layer to the signs API
|
||||
-- which keeps track of which signs have already been placed in the buffer.
|
||||
--
|
||||
-- To workaround these shortcomings, we add an abstraction layer to the signs
|
||||
-- API which keeps track of which signs have already been placed in the buffer.
|
||||
-- This allows us to:
|
||||
-- - efficiently query placed signs.
|
||||
-- - skip adding a sign if it has already been placed.
|
||||
|
||||
|
||||
function M.draw(self: B, bufnr: integer, top: integer, bot: integer): boolean
|
||||
local to_place = {}
|
||||
for i = top, bot do
|
||||
if self.scheduled[bufnr][i] then
|
||||
to_place[#to_place+1] = self.scheduled[bufnr][i]
|
||||
self.placed[bufnr][i] = self.scheduled[bufnr][i]
|
||||
self.scheduled[bufnr][i]= nil
|
||||
end
|
||||
end
|
||||
if to_place[1] then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
local function capitalise_word(x: string): string
|
||||
return x:sub(1, 1):upper()..x:sub(2)
|
||||
end
|
||||
|
||||
local sign_define_cache = {}
|
||||
local function get_sign_name(obj: B, stype: string): string
|
||||
local cache = obj.sign_name_cache
|
||||
if not cache[stype] then
|
||||
cache[stype] = string.format(
|
||||
'%s%s', 'GitSigns', capitalise_word(stype))
|
||||
end
|
||||
|
||||
local function sign_get(name: string): table
|
||||
if not sign_define_cache[name] then
|
||||
return cache[stype]
|
||||
end
|
||||
|
||||
local function sign_get(obj: B, name: string): table
|
||||
if not obj.sign_define_cache[name] then
|
||||
local s = fn.sign_getdefined(name)
|
||||
if not vim.tbl_isempty(s) then
|
||||
sign_define_cache[name] = s
|
||||
obj.sign_define_cache[name] = s
|
||||
end
|
||||
end
|
||||
return sign_define_cache[name]
|
||||
return obj.sign_define_cache[name]
|
||||
end
|
||||
|
||||
|
||||
local function define_sign(name: string, opts: {string:any}, redefine: boolean)
|
||||
local function define_sign(obj: B, name: string, opts: {string:any}, redefine: boolean)
|
||||
if redefine then
|
||||
sign_define_cache[name] = nil
|
||||
obj.sign_define_cache[name] = nil
|
||||
fn.sign_undefine(name)
|
||||
fn.sign_define(name, opts)
|
||||
elseif not sign_get(name) then
|
||||
elseif not sign_get(obj, name) then
|
||||
fn.sign_define(name, opts)
|
||||
end
|
||||
end
|
||||
|
||||
local function define_signs(obj: B, redefine: boolean)
|
||||
-- Define signs
|
||||
for t, name in pairs(sign_map) do
|
||||
local cs = obj.config[t]
|
||||
define_sign(name, {
|
||||
for stype, cs in pairs(obj.config) do
|
||||
define_sign(obj, get_sign_name(obj, stype), {
|
||||
texthl = cs.hl,
|
||||
text = config.signcolumn and cs.text or nil,
|
||||
numhl = config.numhl and cs.numhl,
|
||||
@ -92,42 +69,43 @@ function M.new(cfg: SignsConfig, name: string): B
|
||||
local self = setmetatable({} as B, {__index = M})
|
||||
self.group = group_base..(name or '')
|
||||
self.config = cfg
|
||||
self.placed = {}
|
||||
self.scheduled = {}
|
||||
|
||||
setdefault(self.placed)
|
||||
setdefault(self.scheduled)
|
||||
self.placed = emptytable()
|
||||
self.sign_name_cache = {}
|
||||
self.sign_define_cache = {}
|
||||
|
||||
define_signs(self, false)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function M.remove(self: B, bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
function M:on_lines(_: integer, _: integer, _: integer, _: integer)
|
||||
end
|
||||
|
||||
function M:remove(bufnr: integer, start_lnum: integer, end_lnum: integer)
|
||||
end_lnum = end_lnum or start_lnum
|
||||
|
||||
if start_lnum then
|
||||
for lnum = start_lnum, end_lnum do
|
||||
self.placed[bufnr][lnum] = nil
|
||||
self.scheduled[bufnr][lnum] = nil
|
||||
fn.sign_unplace(self.group, {buffer = bufnr, id = lnum})
|
||||
end
|
||||
else
|
||||
self.placed[bufnr] = nil
|
||||
self.scheduled[bufnr] = nil
|
||||
fn.sign_unplace(self.group, {buffer = bufnr})
|
||||
end
|
||||
end
|
||||
|
||||
function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
function M:add(bufnr: integer, signs: {M.Sign})
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
end
|
||||
|
||||
local to_place = {}
|
||||
|
||||
local cfg = self.config
|
||||
for _, s in ipairs(signs) do
|
||||
local stype: string = sign_map[s.type]
|
||||
local sign_name = get_sign_name(self, s.type)
|
||||
|
||||
local cs = cfg[s.type]
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
@ -135,8 +113,8 @@ function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
local cc = config.count_chars
|
||||
local count_suffix = cc[count] and tostring(count) or (cc['+'] and 'Plus') or ''
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
stype = stype..count_suffix
|
||||
define_sign(stype, {
|
||||
sign_name = sign_name..count_suffix
|
||||
define_sign(self, sign_name, {
|
||||
texthl = cs.hl,
|
||||
text = config.signcolumn and cs.text..count_char or '',
|
||||
numhl = config.numhl and cs.numhl,
|
||||
@ -145,26 +123,25 @@ function M.schedule(self: B, bufnr: integer, signs: {M.Sign})
|
||||
end
|
||||
|
||||
if not self.placed[bufnr][s.lnum] then
|
||||
self.scheduled[bufnr][s.lnum] = {
|
||||
local sign = {
|
||||
id = s.lnum,
|
||||
group = self.group,
|
||||
name = stype,
|
||||
name = sign_name,
|
||||
buffer = bufnr,
|
||||
lnum = s.lnum,
|
||||
priority = config.sign_priority
|
||||
}
|
||||
self.placed[bufnr][s.lnum] = sign
|
||||
to_place[#to_place+1] = sign
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.add(self: B, bufnr: integer, signs: {M.Sign})
|
||||
self:schedule(bufnr, signs)
|
||||
for _, s in ipairs(signs) do
|
||||
self:draw(bufnr, s.lnum, s.lnum)
|
||||
if #to_place > 0 then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
end
|
||||
|
||||
function M.need_redraw(self: B, bufnr: integer, start: integer, last: integer): boolean
|
||||
function M:contains(bufnr: integer, start: integer, last: integer): boolean
|
||||
for i = start+1, last+1 do
|
||||
if self.placed[bufnr][i] then
|
||||
return true
|
||||
@ -173,11 +150,8 @@ function M.need_redraw(self: B, bufnr: integer, start: integer, last: integer):
|
||||
return false
|
||||
end
|
||||
|
||||
function M.reset(self: B)
|
||||
self.placed = {}
|
||||
self.scheduled = {}
|
||||
setdefault(self.placed)
|
||||
setdefault(self.scheduled)
|
||||
function M:reset()
|
||||
self.placed = emptytable()
|
||||
fn.sign_unplace(self.group)
|
||||
define_signs(self, true)
|
||||
end
|
||||
|
@ -132,8 +132,8 @@ function M.calc_base(base: string): string
|
||||
return base
|
||||
end
|
||||
|
||||
function M.setdefault(tbl: table)
|
||||
setmetatable(tbl, {
|
||||
function M.emptytable<T>(): T
|
||||
return setmetatable({} as T, {
|
||||
__index = function(t: table, k: any): any
|
||||
t[k] = {}
|
||||
return t[k]
|
||||
|
@ -91,7 +91,7 @@ end
|
||||
|
||||
function M.expectf(cond, interval)
|
||||
local duration = 0
|
||||
interval = interval or 5
|
||||
interval = interval or 1
|
||||
while duration < timeout do
|
||||
if pcall(cond) then
|
||||
return
|
||||
@ -276,8 +276,8 @@ function M.check(attrs, interval)
|
||||
if s.name == "GitSignsAdd" then act.added = act.added + 1
|
||||
elseif s.name == "GitSignsChange" then act.changed = act.changed + 1
|
||||
elseif s.name == "GitSignsDelete" then act.delete = act.delete + 1
|
||||
elseif s.name == "GitSignsChangeDelete" then act.changedelete = act.changedelete + 1
|
||||
elseif s.name == "GitSignsTopDelete" then act.topdelete = act.topdelete + 1
|
||||
elseif s.name == "GitSignsChangedelete" then act.changedelete = act.changedelete + 1
|
||||
elseif s.name == "GitSignsTopdelete" then act.topdelete = act.topdelete + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user