refactor(signs): simplify interface

This commit is contained in:
Lewis Russell 2022-04-21 18:20:18 +01:00 committed by Lewis Russell
parent f83a2e11cd
commit 9b3899a610
13 changed files with 222 additions and 332 deletions

71
lua/gitsigns/hunks.lua generated
View File

@ -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

View File

@ -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

View File

@ -32,6 +32,7 @@ local M = {Sign = {}, }
return M

View File

@ -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

View File

@ -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
View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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