perf(blame): better cache invalidation
The blame cache is now maintained in the CacheEntry object and invalidated incrementally on buffer updates. In addition git-blame is bypassed if the cursor line is within a hunk.
This commit is contained in:
parent
9bec6e1ef5
commit
bdeba1cec3
|
@ -288,7 +288,7 @@ M.stage_hunk = mk_repeatable(async.void(function(range, opts)
|
||||||
|
|
||||||
table.insert(bcache.staged_diffs, hunk)
|
table.insert(bcache.staged_diffs, hunk)
|
||||||
|
|
||||||
bcache:invalidate()
|
bcache:invalidate(true)
|
||||||
update(bufnr)
|
update(bufnr)
|
||||||
end))
|
end))
|
||||||
|
|
||||||
|
@ -371,7 +371,7 @@ M.undo_stage_hunk = async.void(function()
|
||||||
end
|
end
|
||||||
|
|
||||||
bcache.git_obj:stage_hunks({ hunk }, true)
|
bcache.git_obj:stage_hunks({ hunk }, true)
|
||||||
bcache:invalidate()
|
bcache:invalidate(true)
|
||||||
update(bufnr)
|
update(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@ M.stage_buffer = async.void(function()
|
||||||
|
|
||||||
-- Only process files with existing hunks
|
-- Only process files with existing hunks
|
||||||
local hunks = bcache.hunks
|
local hunks = bcache.hunks
|
||||||
if #hunks == 0 then
|
if not hunks or #hunks == 0 then
|
||||||
print('No unstaged changes in file to stage')
|
print('No unstaged changes in file to stage')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -405,7 +405,7 @@ M.stage_buffer = async.void(function()
|
||||||
table.insert(bcache.staged_diffs, hunk)
|
table.insert(bcache.staged_diffs, hunk)
|
||||||
end
|
end
|
||||||
|
|
||||||
bcache:invalidate()
|
bcache:invalidate(true)
|
||||||
update(bufnr)
|
update(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ M.reset_buffer_index = async.void(function()
|
||||||
|
|
||||||
bcache.git_obj:unstage_file()
|
bcache.git_obj:unstage_file()
|
||||||
|
|
||||||
bcache:invalidate()
|
bcache:invalidate(true)
|
||||||
update(bufnr)
|
update(bufnr)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -893,17 +893,16 @@ M.blame_line = async.void(function(opts)
|
||||||
end, 1000)
|
end, 1000)
|
||||||
|
|
||||||
async.scheduler_if_buf_valid()
|
async.scheduler_if_buf_valid()
|
||||||
local buftext = util.buf_lines(bufnr)
|
|
||||||
local fileformat = vim.bo[bufnr].fileformat
|
local fileformat = vim.bo[bufnr].fileformat
|
||||||
local lnum = api.nvim_win_get_cursor(0)[1]
|
local lnum = api.nvim_win_get_cursor(0)[1]
|
||||||
local results = bcache.git_obj:run_blame(buftext, lnum, opts.ignore_whitespace)
|
local result = bcache:get_blame(lnum, opts)
|
||||||
pcall(function()
|
pcall(function()
|
||||||
loading:close()
|
loading:close()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert(results and results[lnum])
|
assert(result)
|
||||||
|
|
||||||
local result = util.convert_blame_info(results[lnum])
|
result = util.convert_blame_info(result)
|
||||||
|
|
||||||
local is_committed = result.sha and tonumber('0x' .. result.sha) ~= 0
|
local is_committed = result.sha and tonumber('0x' .. result.sha) ~= 0
|
||||||
|
|
||||||
|
@ -934,10 +933,12 @@ C.blame_line = function(args, _)
|
||||||
M.blame_line(args)
|
M.blame_line(args)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_buf_base(buf, bcache, base)
|
---@param bcache Gitsigns.CacheEntry
|
||||||
|
---@param base string?
|
||||||
|
local function update_buf_base(bcache, base)
|
||||||
bcache.base = base
|
bcache.base = base
|
||||||
bcache:invalidate()
|
bcache:invalidate(true)
|
||||||
update(buf)
|
update(bcache.bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Change the base revision to diff against. If {base} is not
|
--- Change the base revision to diff against. If {base} is not
|
||||||
|
@ -978,8 +979,8 @@ M.change_base = async.void(function(base, global)
|
||||||
if global then
|
if global then
|
||||||
config.base = base
|
config.base = base
|
||||||
|
|
||||||
for bufnr, bcache in pairs(cache) do
|
for _, bcache in pairs(cache) do
|
||||||
update_buf_base(bufnr, bcache, base)
|
update_buf_base(bcache, base)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local bufnr = current_buf()
|
local bufnr = current_buf()
|
||||||
|
@ -988,7 +989,7 @@ M.change_base = async.void(function(base, global)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
update_buf_base(bufnr, bcache, base)
|
update_buf_base(bcache, base)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -1317,7 +1318,7 @@ M.refresh = async.void(function()
|
||||||
require('gitsigns.highlight').setup_highlights()
|
require('gitsigns.highlight').setup_highlights()
|
||||||
require('gitsigns.current_line_blame').setup()
|
require('gitsigns.current_line_blame').setup()
|
||||||
for k, v in pairs(cache) do
|
for k, v in pairs(cache) do
|
||||||
v:invalidate()
|
v:invalidate(true)
|
||||||
manager.update(k)
|
manager.update(k)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -10,7 +10,6 @@ local hl = require('gitsigns.highlight')
|
||||||
|
|
||||||
local gs_cache = require('gitsigns.cache')
|
local gs_cache = require('gitsigns.cache')
|
||||||
local cache = gs_cache.cache
|
local cache = gs_cache.cache
|
||||||
local CacheEntry = gs_cache.CacheEntry
|
|
||||||
local Status = require('gitsigns.status')
|
local Status = require('gitsigns.status')
|
||||||
|
|
||||||
local gs_config = require('gitsigns.config')
|
local gs_config = require('gitsigns.config')
|
||||||
|
@ -111,6 +110,7 @@ end
|
||||||
--- @param bufnr integer
|
--- @param bufnr integer
|
||||||
local function on_reload(_, bufnr)
|
local function on_reload(_, bufnr)
|
||||||
local __FUNC__ = 'on_reload'
|
local __FUNC__ = 'on_reload'
|
||||||
|
cache[bufnr]:invalidate()
|
||||||
dprint('Reload')
|
dprint('Reload')
|
||||||
manager.update_debounced(bufnr)
|
manager.update_debounced(bufnr)
|
||||||
end
|
end
|
||||||
|
@ -336,7 +336,7 @@ local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
cache[cbuf] = CacheEntry.new({
|
cache[cbuf] = gs_cache.new({
|
||||||
bufnr = cbuf,
|
bufnr = cbuf,
|
||||||
base = ctx and ctx.base or config.base,
|
base = ctx and ctx.base or config.base,
|
||||||
file = file,
|
file = file,
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
|
local async = require('gitsigns.async')
|
||||||
local config = require('gitsigns.config').config
|
local config = require('gitsigns.config').config
|
||||||
|
local util = require('gitsigns.util')
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
CacheEntry = {},
|
CacheEntry = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Timer object watching the gitdir
|
--- @class (exact) Gitsigns.CacheEntry
|
||||||
|
|
||||||
--- @class Gitsigns.CacheEntry
|
|
||||||
--- @field bufnr integer
|
--- @field bufnr integer
|
||||||
--- @field file string
|
--- @field file string
|
||||||
--- @field base? string
|
--- @field base? string
|
||||||
--- @field compare_text? string[]
|
--- @field compare_text? string[]
|
||||||
--- @field hunks Gitsigns.Hunk.Hunk[]
|
--- @field hunks? Gitsigns.Hunk.Hunk[]
|
||||||
--- @field force_next_update? boolean
|
--- @field force_next_update? boolean
|
||||||
---
|
---
|
||||||
--- @field compare_text_head? string[]
|
--- @field compare_text_head? string[]
|
||||||
--- @field hunks_staged? Gitsigns.Hunk.Hunk[]
|
--- @field hunks_staged? Gitsigns.Hunk.Hunk[]
|
||||||
---
|
---
|
||||||
--- @field staged_diffs Gitsigns.Hunk.Hunk[]
|
--- @field staged_diffs? Gitsigns.Hunk.Hunk[]
|
||||||
--- @field gitdir_watcher? uv.uv_fs_event_t
|
--- @field gitdir_watcher? uv.uv_fs_event_t
|
||||||
--- @field git_obj Gitsigns.GitObj
|
--- @field git_obj Gitsigns.GitObj
|
||||||
--- @field commit? string
|
--- @field commit? string
|
||||||
|
--- @field blame? table<integer,Gitsigns.BlameInfo?>
|
||||||
local CacheEntry = M.CacheEntry
|
local CacheEntry = M.CacheEntry
|
||||||
|
|
||||||
function CacheEntry:get_compare_rev(base)
|
function CacheEntry:get_compare_rev(base)
|
||||||
|
@ -47,20 +48,95 @@ function CacheEntry:get_rev_bufname(rev)
|
||||||
return string.format('gitsigns://%s/%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
|
return string.format('gitsigns://%s/%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
|
||||||
end
|
end
|
||||||
|
|
||||||
function CacheEntry:invalidate()
|
--- Invalidate any state dependent on the buffer content.
|
||||||
self.compare_text = nil
|
--- If 'all' is passed, then invalidate everything.
|
||||||
self.compare_text_head = nil
|
--- @param all? boolean
|
||||||
|
function CacheEntry:invalidate(all)
|
||||||
self.hunks = nil
|
self.hunks = nil
|
||||||
self.hunks_staged = nil
|
self.hunks_staged = nil
|
||||||
|
self.blame = nil
|
||||||
|
if all then
|
||||||
|
-- The below doesn't need to be invalidated
|
||||||
|
-- if the buffer changes
|
||||||
|
self.compare_text = nil
|
||||||
|
self.compare_text_head = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param o Gitsigns.CacheEntry
|
--- @param o Gitsigns.CacheEntry
|
||||||
--- @return Gitsigns.CacheEntry
|
--- @return Gitsigns.CacheEntry
|
||||||
function CacheEntry.new(o)
|
function M.new(o)
|
||||||
o.staged_diffs = o.staged_diffs or {}
|
o.staged_diffs = o.staged_diffs or {}
|
||||||
return setmetatable(o, { __index = CacheEntry })
|
return setmetatable(o, { __index = CacheEntry })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local sleep = async.wrap(function(duration, cb)
|
||||||
|
vim.defer_fn(cb, duration)
|
||||||
|
end, 2)
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
function CacheEntry:wait_for_hunks()
|
||||||
|
local loop_protect = 0
|
||||||
|
while not self.hunks and loop_protect < 10 do
|
||||||
|
loop_protect = loop_protect + 1
|
||||||
|
sleep(100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
--- @param opts Gitsigns.CurrentLineBlameOpts
|
||||||
|
--- @return table<integer,Gitsigns.BlameInfo?>?
|
||||||
|
function CacheEntry:run_blame(opts)
|
||||||
|
local blame_cache --- @type table<integer,Gitsigns.BlameInfo?>?
|
||||||
|
repeat
|
||||||
|
local buftext = util.buf_lines(self.bufnr)
|
||||||
|
local tick = vim.b[self.bufnr].changedtick
|
||||||
|
-- TODO(lewis6991): Cancel blame on changedtick
|
||||||
|
blame_cache = self.git_obj:run_blame(buftext, nil, opts.ignore_whitespace)
|
||||||
|
async.scheduler_if_buf_valid(self.bufnr)
|
||||||
|
until vim.b[self.bufnr].changedtick == tick
|
||||||
|
return blame_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param file string
|
||||||
|
--- @param lnum integer
|
||||||
|
--- @return Gitsigns.BlameInfo
|
||||||
|
local function get_blame_nc(file, lnum)
|
||||||
|
local Git = require('gitsigns.git')
|
||||||
|
|
||||||
|
return {
|
||||||
|
orig_lnum = 0,
|
||||||
|
final_lnum = lnum,
|
||||||
|
commit = Git.not_commited(file),
|
||||||
|
filename = file,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param lnum integer
|
||||||
|
--- @param opts Gitsigns.CurrentLineBlameOpts
|
||||||
|
--- @return Gitsigns.BlameInfo?
|
||||||
|
function CacheEntry:get_blame(lnum, opts)
|
||||||
|
local blame_cache = self.blame
|
||||||
|
|
||||||
|
if not blame_cache or not blame_cache[lnum] then
|
||||||
|
self:wait_for_hunks()
|
||||||
|
local Hunks = require('gitsigns.hunks')
|
||||||
|
if Hunks.find_hunk(lnum, self.hunks) then
|
||||||
|
--- Bypass running blame (which can be expensive) if we know lnum is in a hunk
|
||||||
|
blame_cache = blame_cache or {}
|
||||||
|
blame_cache[lnum] = get_blame_nc(self.git_obj.relpath, lnum)
|
||||||
|
else
|
||||||
|
-- Refresh cache
|
||||||
|
blame_cache = self:run_blame(opts)
|
||||||
|
end
|
||||||
|
self.blame = blame_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
if blame_cache then
|
||||||
|
return blame_cache[lnum]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function CacheEntry:destroy()
|
function CacheEntry:destroy()
|
||||||
local w = self.gitdir_watcher
|
local w = self.gitdir_watcher
|
||||||
if w and not w:is_closing() then
|
if w and not w:is_closing() then
|
||||||
|
|
|
@ -70,7 +70,6 @@
|
||||||
--- @field trouble boolean
|
--- @field trouble boolean
|
||||||
--- -- Undocumented
|
--- -- Undocumented
|
||||||
--- @field _refresh_staged_on_update boolean
|
--- @field _refresh_staged_on_update boolean
|
||||||
--- @field _blame_cache boolean
|
|
||||||
--- @field _threaded_diff boolean
|
--- @field _threaded_diff boolean
|
||||||
--- @field _inline2 boolean
|
--- @field _inline2 boolean
|
||||||
--- @field _extmark_signs boolean
|
--- @field _extmark_signs boolean
|
||||||
|
@ -760,14 +759,6 @@ M.schema = {
|
||||||
]],
|
]],
|
||||||
},
|
},
|
||||||
|
|
||||||
_blame_cache = {
|
|
||||||
type = 'boolean',
|
|
||||||
default = true,
|
|
||||||
description = [[
|
|
||||||
Cache blame results for current_line_blame
|
|
||||||
]],
|
|
||||||
},
|
|
||||||
|
|
||||||
_threaded_diff = {
|
_threaded_diff = {
|
||||||
type = 'boolean',
|
type = 'boolean',
|
||||||
default = true,
|
default = true,
|
||||||
|
|
|
@ -20,13 +20,6 @@ local function reset(bufnr)
|
||||||
vim.b[bufnr].gitsigns_blame_line_dict = nil
|
vim.b[bufnr].gitsigns_blame_line_dict = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @class (exact) Gitsigns.BlameCache
|
|
||||||
--- @field cache Gitsigns.BlameInfo[]?
|
|
||||||
--- @field tick integer
|
|
||||||
|
|
||||||
--- @type table<integer,Gitsigns.BlameCache>
|
|
||||||
local blame_cache = {}
|
|
||||||
|
|
||||||
--- @param fmt string
|
--- @param fmt string
|
||||||
--- @param name string
|
--- @param name string
|
||||||
--- @param info Gitsigns.BlameInfoPublic
|
--- @param info Gitsigns.BlameInfoPublic
|
||||||
|
@ -48,36 +41,6 @@ local function flatten_virt_text(virt_text)
|
||||||
return table.concat(res)
|
return table.concat(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param bufnr integer
|
|
||||||
--- @param lnum integer
|
|
||||||
--- @param opts Gitsigns.CurrentLineBlameOpts
|
|
||||||
--- @return Gitsigns.BlameInfo?
|
|
||||||
local function run_blame(bufnr, lnum, opts)
|
|
||||||
-- init and invalidate
|
|
||||||
local tick = vim.b[bufnr].changedtick
|
|
||||||
if not blame_cache[bufnr] or blame_cache[bufnr].tick ~= tick then
|
|
||||||
blame_cache[bufnr] = { tick = tick }
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = blame_cache[bufnr].cache
|
|
||||||
|
|
||||||
if result then
|
|
||||||
return result[lnum]
|
|
||||||
end
|
|
||||||
|
|
||||||
local buftext = util.buf_lines(bufnr)
|
|
||||||
local bcache = cache[bufnr]
|
|
||||||
result = bcache.git_obj:run_blame(buftext, nil, opts.ignore_whitespace)
|
|
||||||
|
|
||||||
if not result then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
blame_cache[bufnr].cache = result
|
|
||||||
|
|
||||||
return result[lnum]
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param winid integer
|
--- @param winid integer
|
||||||
--- @return integer
|
--- @return integer
|
||||||
local function win_width(winid)
|
local function win_width(winid)
|
||||||
|
@ -203,14 +166,12 @@ local function update0(bufnr)
|
||||||
|
|
||||||
local opts = config.current_line_blame_opts
|
local opts = config.current_line_blame_opts
|
||||||
|
|
||||||
local blame_info = run_blame(bufnr, lnum, opts)
|
local blame_info = bcache:get_blame(lnum, opts)
|
||||||
|
|
||||||
if not blame_info then
|
if not blame_info then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
async.scheduler_if_buf_valid(bufnr)
|
|
||||||
|
|
||||||
if lnum ~= get_lnum(winid) then
|
if lnum ~= get_lnum(winid) then
|
||||||
-- Cursor has moved during events; abort and tr-trigger another update
|
-- Cursor has moved during events; abort and tr-trigger another update
|
||||||
update0(bufnr)
|
update0(bufnr)
|
||||||
|
|
|
@ -8,8 +8,6 @@ local subprocess = require('gitsigns.subprocess')
|
||||||
local gs_config = require('gitsigns.config')
|
local gs_config = require('gitsigns.config')
|
||||||
local config = gs_config.config
|
local config = gs_config.config
|
||||||
|
|
||||||
local gs_hunks = require('gitsigns.hunks')
|
|
||||||
|
|
||||||
local uv = vim.loop
|
local uv = vim.loop
|
||||||
local startswith = vim.startswith
|
local startswith = vim.startswith
|
||||||
|
|
||||||
|
@ -569,11 +567,30 @@ end
|
||||||
|
|
||||||
local NOT_COMMITTED = {
|
local NOT_COMMITTED = {
|
||||||
author = 'Not Committed Yet',
|
author = 'Not Committed Yet',
|
||||||
['author_mail'] = '<not.committed.yet>',
|
author_mail = '<not.committed.yet>',
|
||||||
committer = 'Not Committed Yet',
|
committer = 'Not Committed Yet',
|
||||||
['committer_mail'] = '<not.committed.yet>',
|
committer_mail = '<not.committed.yet>',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- @param file string
|
||||||
|
--- @return Gitsigns.CommitInfo
|
||||||
|
function M.not_commited(file)
|
||||||
|
local time = os.time()
|
||||||
|
return {
|
||||||
|
sha = string.rep('0', 40),
|
||||||
|
abbrev_sha = string.rep('0', 8),
|
||||||
|
author = 'Not Committed Yet',
|
||||||
|
author_mail = '<not.committed.yet>',
|
||||||
|
author_tz = '+0000',
|
||||||
|
author_time = time,
|
||||||
|
committer = 'Not Committed Yet',
|
||||||
|
committer_time = time,
|
||||||
|
committer_mail = '<not.committed.yet>',
|
||||||
|
committer_tz = '+0000',
|
||||||
|
summary = 'Version of ' .. file,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
---@param x any
|
---@param x any
|
||||||
---@return integer
|
---@return integer
|
||||||
local function asinteger(x)
|
local function asinteger(x)
|
||||||
|
@ -585,11 +602,22 @@ end
|
||||||
--- @param ignore_whitespace? boolean
|
--- @param ignore_whitespace? boolean
|
||||||
--- @return table<integer,Gitsigns.BlameInfo?>?
|
--- @return table<integer,Gitsigns.BlameInfo?>?
|
||||||
function Obj:run_blame(lines, lnum, ignore_whitespace)
|
function Obj:run_blame(lines, lnum, ignore_whitespace)
|
||||||
|
local ret = {} --- @type table<integer,Gitsigns.BlameInfo>
|
||||||
|
|
||||||
if not self.object_name or self.repo.abbrev_head == '' then
|
if not self.object_name or self.repo.abbrev_head == '' then
|
||||||
-- As we support attaching to untracked files we need to return something if
|
-- As we support attaching to untracked files we need to return something if
|
||||||
-- the file isn't isn't tracked in git.
|
-- the file isn't isn't tracked in git.
|
||||||
-- If abbrev_head is empty, then assume the repo has no commits
|
-- If abbrev_head is empty, then assume the repo has no commits
|
||||||
return NOT_COMMITTED
|
local commit = M.not_commited(self.file)
|
||||||
|
for i in ipairs(lines) do
|
||||||
|
ret[i] = {
|
||||||
|
orig_lnum = 0,
|
||||||
|
final_lnum = i,
|
||||||
|
commit = commit,
|
||||||
|
filename = self.file,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
local args = { 'blame', '--contents', '-', '--incremental' }
|
local args = { 'blame', '--contents', '-', '--incremental' }
|
||||||
|
@ -619,7 +647,6 @@ function Obj:run_blame(lines, lnum, ignore_whitespace)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ret = {} --- @type Gitsigns.BlameInfo[]
|
|
||||||
local commits = {} --- @type table<string,Gitsigns.CommitInfo>
|
local commits = {} --- @type table<string,Gitsigns.CommitInfo>
|
||||||
local i = 1
|
local i = 1
|
||||||
|
|
||||||
|
@ -660,6 +687,9 @@ function Obj:run_blame(lines, lnum, ignore_whitespace)
|
||||||
local l = get()
|
local l = get()
|
||||||
local key, value = l:match('^([^%s]+) (.*)')
|
local key, value = l:match('^([^%s]+) (.*)')
|
||||||
if key then
|
if key then
|
||||||
|
if vim.endswith(key, '_time') then
|
||||||
|
value = tonumber(value)
|
||||||
|
end
|
||||||
key = key:gsub('%-', '_') --- @type string
|
key = key:gsub('%-', '_') --- @type string
|
||||||
commit[key] = value
|
commit[key] = value
|
||||||
else
|
else
|
||||||
|
@ -749,6 +779,8 @@ end
|
||||||
function Obj.stage_hunks(self, hunks, invert)
|
function Obj.stage_hunks(self, hunks, invert)
|
||||||
ensure_file_in_index(self)
|
ensure_file_in_index(self)
|
||||||
|
|
||||||
|
local gs_hunks = require('gitsigns.hunks')
|
||||||
|
|
||||||
local patch = gs_hunks.create_patch(self.relpath, hunks, self.mode_bits, invert)
|
local patch = gs_hunks.create_patch(self.relpath, hunks, self.mode_bits, invert)
|
||||||
|
|
||||||
if not self.i_crlf and self.w_crlf then
|
if not self.i_crlf and self.w_crlf then
|
||||||
|
|
|
@ -75,6 +75,28 @@ local function apply_win_signs(bufnr, top, bot, clear)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param blame table<integer,Gitsigns.BlameInfo?>?
|
||||||
|
--- @param first integer
|
||||||
|
--- @param last_orig integer
|
||||||
|
--- @param last_new integer
|
||||||
|
local function on_lines_blame(blame, first, last_orig, last_new)
|
||||||
|
if not blame then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_new ~= last_orig then
|
||||||
|
if last_new < last_orig then
|
||||||
|
util.list_remove(blame, last_new, last_orig)
|
||||||
|
else
|
||||||
|
util.list_insert(blame, last_orig, last_new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = math.min(first + 1, last_new), math.max(first + 1, last_new) do
|
||||||
|
blame[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- @param buf integer
|
--- @param buf integer
|
||||||
--- @param first integer
|
--- @param first integer
|
||||||
--- @param last_orig integer
|
--- @param last_orig integer
|
||||||
|
@ -87,6 +109,8 @@ function M.on_lines(buf, first, last_orig, last_new)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
on_lines_blame(bcache.blame, first, last_orig, last_new)
|
||||||
|
|
||||||
signs_normal:on_lines(buf, first, last_orig, last_new)
|
signs_normal:on_lines(buf, first, last_orig, last_new)
|
||||||
if signs_staged then
|
if signs_staged then
|
||||||
signs_staged:on_lines(buf, first, last_orig, last_new)
|
signs_staged:on_lines(buf, first, last_orig, last_new)
|
||||||
|
|
|
@ -191,19 +191,6 @@ function M.get_relative_time(timestamp)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @generic T
|
|
||||||
--- @param x T[]
|
|
||||||
--- @return T[]
|
|
||||||
function M.copy_array(x)
|
|
||||||
local r = {}
|
|
||||||
--- @diagnostic disable-next-line:no-unknown
|
|
||||||
for i, e in ipairs(x) do
|
|
||||||
--- @diagnostic disable-next-line:no-unknown
|
|
||||||
r[i] = e
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Strip '\r' from the EOL of each line only if all lines end with '\r'
|
--- Strip '\r' from the EOL of each line only if all lines end with '\r'
|
||||||
--- @param xs0 string[]
|
--- @param xs0 string[]
|
||||||
--- @return string[]
|
--- @return string[]
|
||||||
|
@ -304,4 +291,49 @@ function M.convert_blame_info(x)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Efficiently remove items from middle of a list a list.
|
||||||
|
---
|
||||||
|
--- Calling table.remove() in a loop will re-index the tail of the table on
|
||||||
|
--- every iteration, instead this function will re-index the table exactly
|
||||||
|
--- once.
|
||||||
|
---
|
||||||
|
--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
|
||||||
|
---
|
||||||
|
---@param t any[]
|
||||||
|
---@param first integer
|
||||||
|
---@param last integer
|
||||||
|
function M.list_remove(t, first, last)
|
||||||
|
local n = #t
|
||||||
|
for i = 0, n - first do
|
||||||
|
t[first + i] = t[last + 1 + i]
|
||||||
|
t[last + 1 + i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Efficiently insert items into the middle of a list.
|
||||||
|
---
|
||||||
|
--- Calling table.insert() in a loop will re-index the tail of the table on
|
||||||
|
--- every iteration, instead this function will re-index the table exactly
|
||||||
|
--- once.
|
||||||
|
---
|
||||||
|
--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
|
||||||
|
---
|
||||||
|
---@param t any[]
|
||||||
|
---@param first integer
|
||||||
|
---@param last integer
|
||||||
|
---@param v any
|
||||||
|
function M.list_insert(t, first, last, v)
|
||||||
|
local n = #t
|
||||||
|
|
||||||
|
-- Shift table forward
|
||||||
|
for i = n - first, 0, -1 do
|
||||||
|
t[last + 1 + i] = t[first + i]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fill in new values
|
||||||
|
for i = first, last do
|
||||||
|
t[i] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
@ -87,7 +87,7 @@ local handler = debounce_trailing(
|
||||||
buf_check(bufnr)
|
buf_check(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
cache[bufnr]:invalidate()
|
cache[bufnr]:invalidate(true)
|
||||||
|
|
||||||
require('gitsigns.manager').update(bufnr)
|
require('gitsigns.manager').update(bufnr)
|
||||||
end),
|
end),
|
||||||
|
|
Loading…
Reference in New Issue