1

Refresh generated neovim config

This commit is contained in:
2024-08-15 13:01:03 +02:00
parent 64b51cf53a
commit f5af8e2b28
1836 changed files with 38979 additions and 31094 deletions

View File

@ -1,16 +1,14 @@
local async = require('gitsigns.async')
local config = require('gitsigns.config').config
local mk_repeatable = require('gitsigns.repeat').mk_repeatable
local git = require('gitsigns.git')
local Hunks = require('gitsigns.hunks')
local manager = require('gitsigns.manager')
local popup = require('gitsigns.popup')
local util = require('gitsigns.util')
local manager = require('gitsigns.manager')
local git = require('gitsigns.git')
local run_diff = require('gitsigns.diff')
local gs_cache = require('gitsigns.cache')
local cache = gs_cache.cache
local Hunks = require('gitsigns.hunks')
local config = require('gitsigns.config').config
local mk_repeatable = require('gitsigns.repeat').mk_repeatable
local cache = require('gitsigns.cache').cache
local api = vim.api
local current_buf = api.nvim_get_current_buf
@ -416,7 +414,6 @@ end)
--- {async}
M.stage_buffer = async.create(function()
local bufnr = current_buf()
local bcache = cache[bufnr]
if not bcache then
return
@ -977,8 +974,6 @@ end
--- Display full commit message with hunk.
--- • {ignore_whitespace}: (boolean)
--- Ignore whitespace when running blame.
--- • {rev}: (string)
--- Revision to blame against.
--- • {extra_opts}: (string[])
--- Extra options passed to `git-blame`.
M.blame_line = async.create(1, function(opts)
@ -1014,16 +1009,14 @@ M.blame_line = async.create(1, function(opts)
return
end
assert(result)
result = util.convert_blame_info(result)
result = util.convert_blame_info(assert(result))
local is_committed = result.sha and tonumber('0x' .. result.sha) ~= 0
local blame_linespec = create_blame_fmt(is_committed, opts.full)
if is_committed and opts.full then
local body = bcache.git_obj:command(
local body = bcache.git_obj.repo:command(
{ 'show', '-s', '--format=%B', result.sha },
{ text = true }
)
@ -1055,9 +1048,13 @@ end
--- Run git-blame on the current file and open the results
--- in a scroll-bound vertical split.
---
--- <CR> is mapped to open a menu with the actions:
--- - [Show commit] in a vertical split.
--- - [Reblame at commit]
--- Mappings:
--- <CR> is mapped to open a menu with the other mappings
--- Note: <Alt> must be held to activate the mappings whilst the menu is
--- open.
--- s [Show commit] in a vertical split.
--- S [Show commit] in a new tab.
--- r [Reblame at commit]
---
--- Attributes: ~
--- {async}
@ -1243,6 +1240,10 @@ M.show = function(revision, callback)
diffthis.show(bufnr, revision, callback)
end
C.show = function(args, _)
M.show(args[1])
end
CP.show = complete_heads
--- @param buf_or_filename string|integer
@ -1287,17 +1288,24 @@ local function buildqflist(target)
end
end
local repo = git.Repo.new(assert(vim.loop.cwd()))
if not repos[repo.gitdir] then
local repo = git.Repo.get(assert(vim.loop.cwd()))
if repo and not repos[repo.gitdir] then
repos[repo.gitdir] = repo
end
for _, r in pairs(repos) do
for _, f in ipairs(r:files_changed()) do
for _, f in ipairs(r:files_changed(config.base)) do
local f_abs = r.toplevel .. '/' .. f
local stat = vim.loop.fs_stat(f_abs)
if stat and stat.type == 'file' then
local a = r:get_show_text(':0:' .. f)
---@type string
local obj
if config.base and config.base ~= ':0' then
obj = config.base .. ':' .. f
else
obj = ':0:' .. f
end
local a = r:get_show_text(obj)
async.scheduler()
local hunks = run_diff(a, util.file_lines(f_abs))
hunks_to_qflist(f_abs, hunks, qflist)

View File

@ -181,6 +181,7 @@ function M.create(argc_or_func, func)
return function(...)
local callback = argc and select(argc + 1, ...) or nil
assert(not callback or type(callback) == 'function')
return run(func, callback, unpack({ ... }, 1, argc))
end
end

View File

@ -1,20 +1,15 @@
local Status = require('gitsigns.status')
local async = require('gitsigns.async')
local git = require('gitsigns.git')
local manager = require('gitsigns.manager')
local Cache = require('gitsigns.cache')
local log = require('gitsigns.debug.log')
local dprintf = log.dprintf
local dprint = log.dprint
local gs_cache = require('gitsigns.cache')
local cache = gs_cache.cache
local Status = require('gitsigns.status')
local config = require('gitsigns.config').config
local manager = require('gitsigns.manager')
local util = require('gitsigns.util')
local cache = Cache.cache
local config = require('gitsigns.config').config
local dprint = log.dprint
local dprintf = log.dprintf
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
local api = vim.api
@ -46,13 +41,25 @@ end
--- @return string buffer
--- @return string? commit
local function parse_gitsigns_uri(name)
-- TODO(lewis6991): Support submodules
--- @type any, any, string?, string?, string
local _, _, root_path, commit, rel_path = name:find([[^gitsigns://(.*)/%.git/(.*):(.*)]])
local _proto, head, tail = unpack(vim.split(name, '//'))
--- @type any, any, string?, string?
local _, _, root_path, sub_path = head:find([[(.*)/%.git(.*)]])
--- @type any, any, string?, string?
local _, _, commit, rel_path = tail:find([[(.*):(.*)]])
commit = util.norm_base(commit)
if root_path then
name = root_path .. '/' .. rel_path
if sub_path then
sub_path = sub_path:gsub('^/modules/', '')
name = string.format('%s/%s/%s', root_path, sub_path, rel_path)
else
name = string.format('%s/%s', root_path, rel_path)
end
end
return name, commit
end
@ -71,7 +78,7 @@ local function get_buf_path(bufnr)
dprintf("Fugitive buffer for file '%s' from path '%s'", path, file)
if path then
local realpath = uv.fs_realpath(path)
if realpath then
if realpath and vim.fn.isdirectory(realpath) == 0 then
return realpath, commit, true
end
end
@ -323,7 +330,7 @@ local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd)
return
end
cache[cbuf] = gs_cache.new({
cache[cbuf] = Cache.new({
bufnr = cbuf,
file = file,
git_obj = git_obj,
@ -357,6 +364,10 @@ local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd)
-- Initial update
manager.update(cbuf)
if config.current_line_blame then
require('gitsigns.current_line_blame').update(cbuf)
end
end)
--- Detach Gitsigns from all buffers it is attached to.
@ -391,7 +402,7 @@ function M.detach(bufnr, _keep_signs)
-- Clear status variables
Status:clear(bufnr)
gs_cache.destroy(bufnr)
Cache.destroy(bufnr)
end
--- Attach Gitsigns to the buffer.

View File

@ -22,9 +22,12 @@ local M = {
--- @field blame? table<integer,Gitsigns.BlameInfo?>
local CacheEntry = M.CacheEntry
function CacheEntry:get_rev_bufname(rev)
function CacheEntry:get_rev_bufname(rev, nofile)
rev = rev or self.git_obj.revision or ':0'
return string.format('gitsigns://%s/%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
if nofile then
return string.format('gitsigns://%s//%s', self.git_obj.repo.gitdir, rev)
end
return string.format('gitsigns://%s//%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
end
--- Invalidate any state dependent on the buffer content.
@ -53,6 +56,7 @@ local sleep = async.wrap(2, function(duration, cb)
vim.defer_fn(cb, duration)
end)
--- @async
--- @private
function CacheEntry:wait_for_hunks()
local loop_protect = 0
@ -65,71 +69,61 @@ end
-- If a file contains has up to this amount of lines, then
-- always blame the whole file, otherwise only blame one line
-- at a time.
local BLAME_THRESHOLD_LEN = 1000000
local BLAME_THRESHOLD_LEN = 10000
--- @async
--- @private
--- @param lnum? integer
--- @param opts Gitsigns.BlameOpts
--- @return table<integer,Gitsigns.BlameInfo?>?
--- @param opts? Gitsigns.BlameOpts
--- @return table<integer,Gitsigns.BlameInfo?>
--- @return boolean? full
function CacheEntry:run_blame(lnum, opts)
local bufnr = self.bufnr
local blame_cache --- @type table<integer,Gitsigns.BlameInfo?>?
local blame --- @type table<integer,Gitsigns.BlameInfo?>?
local lnum0 --- @type integer?
repeat
local buftext = util.buf_lines(bufnr, true)
local tick = vim.b[bufnr].changedtick
local lnum0 = #buftext > BLAME_THRESHOLD_LEN and lnum or nil
lnum0 = #buftext > BLAME_THRESHOLD_LEN and lnum or nil
-- TODO(lewis6991): Cancel blame on changedtick
blame_cache = self.git_obj:run_blame(buftext, lnum0, opts)
blame = self.git_obj:run_blame(buftext, lnum0, self.git_obj.revision, opts)
async.scheduler()
if not vim.api.nvim_buf_is_valid(bufnr) then
return
return {}
end
until vim.b[bufnr].changedtick == tick
return blame_cache
return blame, lnum0 == nil
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_committed(file),
filename = file,
}
end
--- @param lnum integer
--- @param opts Gitsigns.BlameOpts
--- If lnum is nil then run blame for the entire buffer.
--- @async
--- @param lnum? integer
--- @param opts? Gitsigns.BlameOpts
--- @return Gitsigns.BlameInfo?
function CacheEntry:get_blame(lnum, opts)
if opts.rev then
local buftext = util.buf_lines(self.bufnr)
return self.git_obj:run_blame(buftext, lnum, opts)[lnum]
end
local blame = self.blame
local blame_cache = self.blame
if not blame_cache or not blame_cache[lnum] then
if not blame or (lnum and not blame[lnum]) then
self:wait_for_hunks()
blame = blame or {}
local Hunks = require('gitsigns.hunks')
if Hunks.find_hunk(lnum, self.hunks) then
if lnum and 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)
local Blame = require('gitsigns.git.blame')
blame[lnum] = Blame.get_blame_nc(self.git_obj.relpath, lnum)
else
-- Refresh cache
blame_cache = self:run_blame(lnum, opts)
-- Refresh/update cache
local b, full = self:run_blame(lnum, opts)
if lnum and not full then
blame[lnum] = b[lnum]
else
blame = b
end
end
self.blame = blame_cache
self.blame = blame
end
if blame_cache then
return blame_cache[lnum]
end
return blame[lnum]
end
function CacheEntry:destroy()
@ -137,9 +131,10 @@ function CacheEntry:destroy()
if w and not w:is_closing() then
w:close()
end
self.git_obj.repo:unref()
end
---@type table<integer,Gitsigns.CacheEntry>
---@type table<integer,Gitsigns.CacheEntry?>
M.cache = {}
--- @param bufnr integer

View File

@ -1,20 +1,16 @@
local async = require('gitsigns.async')
local log = require('gitsigns.debug.log')
local dprintf = log.dprintf
local message = require('gitsigns.message')
local parse_args = require('gitsigns.cli.argparse').parse_args
local actions = require('gitsigns.actions')
local argparse = require('gitsigns.cli.argparse')
local async = require('gitsigns.async')
local attach = require('gitsigns.attach')
local gs_debug = require('gitsigns.debug')
local Debug = require('gitsigns.debug')
local log = require('gitsigns.debug.log')
local message = require('gitsigns.message')
--- @type table<table<string,function>,boolean>
local sources = {
[actions] = true,
[attach] = false,
[gs_debug] = false,
[Debug] = false,
}
-- try to parse each argument as a lua boolean, nil or number, if fails then
@ -64,7 +60,7 @@ end
M.run = async.create(1, function(params)
local __FUNC__ = 'cli.run'
local pos_args_raw, named_args_raw = parse_args(params.args)
local pos_args_raw, named_args_raw = argparse.parse_args(params.args)
local func = pos_args_raw[1]
@ -79,7 +75,7 @@ M.run = async.create(1, function(params)
local named_args = vim.tbl_map(parse_to_lua, named_args_raw)
local args = vim.tbl_extend('error', pos_args, named_args)
dprintf(
log.dprintf(
"Running action '%s' with arguments %s",
func,
vim.inspect(args, { newline = ' ', indent = '' })

View File

@ -25,6 +25,7 @@
--- @field text string
--- @field numhl string
--- @field linehl string
--- @field culhl string
--- @alias Gitsigns.SignType
--- | 'add'
@ -34,7 +35,7 @@
--- | 'changedelete'
--- | 'untracked'
--- @alias Gitsigns.CurrentLineBlameFmtFun fun(user: string, info: table<string,any>): {[1]:string,[2]:string}[]
--- @alias Gitsigns.CurrentLineBlameFmtFun fun(user: string, info: table<string,any>): [string,string][]
--- @class (exact) Gitsigns.CurrentLineBlameOpts : Gitsigns.BlameOpts
--- @field virt_text? boolean
@ -44,7 +45,6 @@
--- @class (exact) Gitsigns.BlameOpts
--- @field ignore_whitespace? boolean
--- @field rev? string
--- @field extra_opts? string[]
--- @class (exact) Gitsigns.LineBlameOpts : Gitsigns.BlameOpts
@ -61,6 +61,7 @@
--- @field signcolumn boolean
--- @field numhl boolean
--- @field linehl boolean
--- @field culhl boolean
--- @field show_deleted boolean
--- @field sign_priority integer
--- @field _on_attach_pre fun(bufnr: integer, callback: fun(_: table))
@ -184,10 +185,13 @@ local function validate_signs(x)
if x[kind] and x[kind][ty] and vim.endswith(ty, 'hl') then
warnings = warnings or {}
local w = string.format(
"'signs.%s.%s' is now deprecated, please define highlight '%s'",
"'signs.%s.%s' is now deprecated, please define highlight '%s' e.g:\n"
.. " vim.api.nvim_set_hl(0, '%s', { link = '%s' })",
kind,
ty,
v
v,
v,
x[kind][ty]
)
warnings[w] = true
end
@ -210,36 +214,47 @@ M.schema = {
type = validate_signs,
deep_extend = true,
default = {
add = { hl = 'GitSignsAdd', text = '', numhl = 'GitSignsAddNr', linehl = 'GitSignsAddLn' },
add = {
hl = 'GitSignsAdd',
text = '',
numhl = 'GitSignsAddNr',
linehl = 'GitSignsAddLn',
culhl = 'GitSignsAddCul',
},
change = {
hl = 'GitSignsChange',
text = '',
numhl = 'GitSignsChangeNr',
linehl = 'GitSignsChangeLn',
culhl = 'GitSignsChangeCul',
},
delete = {
hl = 'GitSignsDelete',
text = '',
numhl = 'GitSignsDeleteNr',
linehl = 'GitSignsDeleteLn',
culhl = 'GitSignsDeleteCul',
},
topdelete = {
hl = 'GitSignsTopdelete',
text = '',
numhl = 'GitSignsTopdeleteNr',
linehl = 'GitSignsTopdeleteLn',
culhl = 'GitSignsTopdeleteCul',
},
changedelete = {
hl = 'GitSignsChangedelete',
text = '~',
numhl = 'GitSignsChangedeleteNr',
linehl = 'GitSignsChangedeleteLn',
culhl = 'GitSignsChangedeleteCul',
},
untracked = {
hl = 'GitSignsUntracked',
text = '',
numhl = 'GitSignsUntrackedNr',
linehl = 'GitSignsUntrackedLn',
culhl = 'GitSignsUntrackedCul',
},
},
default_help = [[{
@ -261,6 +276,7 @@ M.schema = {
• `GitSignsAdd` (for normal text signs)
• `GitSignsAddNr` (for signs when `config.numhl == true`)
• `GitSignsAddLn `(for signs when `config.linehl == true`)
• `GitSignsAddCul `(for signs when `config.culhl == true`)
See |gitsigns-highlight-groups|.
]],
@ -275,30 +291,35 @@ M.schema = {
text = '',
numhl = 'GitSignsStagedAddNr',
linehl = 'GitSignsStagedAddLn',
culhl = 'GitSignsStagedAddCul',
},
change = {
hl = 'GitSignsStagedChange',
text = '',
numhl = 'GitSignsStagedChangeNr',
linehl = 'GitSignsStagedChangeLn',
culhl = 'GitSignsStagedChangeCul',
},
delete = {
hl = 'GitSignsStagedDelete',
text = '',
numhl = 'GitSignsStagedDeleteNr',
linehl = 'GitSignsStagedDeleteLn',
culhl = 'GitSignsStagedDeleteCul',
},
topdelete = {
hl = 'GitSignsStagedTopdelete',
text = '',
numhl = 'GitSignsStagedTopdeleteNr',
linehl = 'GitSignsStagedTopdeleteLn',
culhl = 'GitSignsStagedTopdeleteCul',
},
changedelete = {
hl = 'GitSignsStagedChangedelete',
text = '~',
numhl = 'GitSignsStagedChangedeleteNr',
linehl = 'GitSignsStagedChangedeleteLn',
culhl = 'GitSignsStagedChangedeleteCul',
},
},
default_help = [[{
@ -456,6 +477,19 @@ M.schema = {
]],
},
culhl = {
type = 'boolean',
default = false,
description = [[
Enable/disable highlights for the sign column when the cursor is on
the same line.
When enabled the highlights defined in `signs.*.culhl` are used. If
the highlight group does not exist, then it is automatically defined
and linked to the corresponding highlight group in `signs.*.hl`.
]],
},
show_deleted = {
type = 'boolean',
default = false,

View File

@ -1,11 +1,13 @@
local async = require('gitsigns.async')
local cache = require('gitsigns.cache').cache
local config = require('gitsigns.config').config
local debounce = require('gitsigns.debounce')
local util = require('gitsigns.util')
local api = vim.api
local cache = require('gitsigns.cache').cache
local config = require('gitsigns.config').config
local schema = require('gitsigns.config').schema
local error_once = require('gitsigns.message').error_once
local debounce = require('gitsigns.debounce')
local api = vim.api
local namespace = api.nvim_create_namespace('gitsigns_blame')
@ -72,19 +74,31 @@ end
---@param bufnr integer
---@param blame_info Gitsigns.BlameInfoPublic
---@return {[1]: string, [2]:string}[]
---@return [string, string][]
local function get_blame_virt_text(bufnr, blame_info)
local git_obj = assert(cache[bufnr]).git_obj
local use_nc = blame_info.author == 'Not Committed Yet'
local clb_formatter = blame_info.author == 'Not Committed Yet'
and config.current_line_blame_formatter_nc
local clb_formatter = use_nc and config.current_line_blame_formatter_nc
or config.current_line_blame_formatter
if type(clb_formatter) == 'string' then
clb_formatter = default_formatter(clb_formatter)
if type(clb_formatter) == 'function' then
local ok, res = pcall(clb_formatter, git_obj.repo.username, blame_info)
if ok then
return res
end
local nc_sfx = use_nc and '_nc' or ''
error_once(
'Failed running config.current_line_blame_formatter%s, using default:\n %s',
nc_sfx,
res
)
--- @type string
clb_formatter = schema.current_line_blame_formatter.default
end
return clb_formatter(git_obj.repo.username, blame_info)
return default_formatter(clb_formatter)(git_obj.repo.username, blame_info)
end
--- @param bufnr integer
@ -192,50 +206,53 @@ end
local update = async.create(1, debounce.throttle_by_id(update0))
--- @type fun(bufnr: integer)
local update_debounced
M.update = nil
function M.setup()
local group = api.nvim_create_augroup('gitsigns_blame', {})
local opts = config.current_line_blame_opts
update_debounced = debounce.debounce_trailing(opts.delay, update)
for k, _ in pairs(cache) do
for k in pairs(cache) do
reset(k)
end
if config.current_line_blame then
local events = { 'FocusGained', 'BufEnter', 'CursorMoved', 'CursorMovedI' }
if vim.fn.exists('#WinResized') == 1 then
-- For nvim 0.9+
events[#events + 1] = 'WinResized'
end
local group = api.nvim_create_augroup('gitsigns_blame', {})
api.nvim_create_autocmd(events, {
group = group,
callback = function(args)
reset(args.buf)
update_debounced(args.buf)
end,
})
api.nvim_create_autocmd({ 'InsertEnter', 'FocusLost', 'BufLeave' }, {
group = group,
callback = function(args)
reset(args.buf)
end,
})
api.nvim_create_autocmd('OptionSet', {
group = group,
pattern = { 'fileformat', 'bomb', 'eol' },
callback = function(args)
reset(args.buf)
end,
})
update_debounced(api.nvim_get_current_buf())
if not config.current_line_blame then
return
end
local opts = config.current_line_blame_opts
M.update = debounce.debounce_trailing(opts.delay, update)
-- show current buffer line blame immediately
M.update(api.nvim_get_current_buf())
local events = { 'FocusGained', 'BufEnter', 'CursorMoved', 'CursorMovedI' }
if vim.fn.exists('#WinResized') == 1 then
-- For nvim 0.9+
events[#events + 1] = 'WinResized'
end
api.nvim_create_autocmd(events, {
group = group,
callback = function(args)
reset(args.buf)
M.update(args.buf)
end,
})
api.nvim_create_autocmd({ 'InsertEnter', 'FocusLost', 'BufLeave' }, {
group = group,
callback = function(args)
reset(args.buf)
end,
})
api.nvim_create_autocmd('OptionSet', {
group = group,
pattern = { 'fileformat', 'bomb', 'eol' },
callback = function(args)
reset(args.buf)
end,
})
end
return M

View File

@ -1,4 +1,4 @@
local uv = vim.loop
local uv = vim.uv or vim.loop
local M = {}

View File

@ -1,10 +1,10 @@
local Hunks = require('gitsigns.hunks')
local util = require('gitsigns.util')
local scheduler = require('gitsigns.async').scheduler
local config = require('gitsigns.config').config
local git_diff = require('gitsigns.git').diff
local gs_hunks = require('gitsigns.hunks')
local util = require('gitsigns.util')
local scheduler = require('gitsigns.async').scheduler
local M = {}
-- Async function
@ -61,7 +61,7 @@ function M.run_diff(text_cmp, text_buf)
for _, line in ipairs(out) do
if vim.startswith(line, '@@') then
results[#results + 1] = gs_hunks.parse_diff_line(line)
results[#results + 1] = Hunks.parse_diff_line(line)
elseif #results > 0 then
local r = results[#results]
if line:sub(1, 1) == '-' then

View File

@ -1,6 +1,7 @@
local async = require('gitsigns.async')
local create_hunk = require('gitsigns.hunks').create_hunk
local config = require('gitsigns.config').config
local async = require('gitsigns.async')
local M = {}

View File

@ -1,15 +1,15 @@
local api = vim.api
local async = require('gitsigns.async')
local cache = require('gitsigns.cache').cache
local util = require('gitsigns.util')
local manager = require('gitsigns.manager')
local message = require('gitsigns.message')
local util = require('gitsigns.util')
local Status = require('gitsigns.status')
local dprint = require('gitsigns.debug.log').dprint
local cache = require('gitsigns.cache').cache
local log = require('gitsigns.debug.log')
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
local api = vim.api
local M = {}
--- @async
@ -17,7 +17,7 @@ local M = {}
--- @param dbufnr integer
--- @param base string?
local function bufread(bufnr, dbufnr, base)
local bcache = cache[bufnr]
local bcache = assert(cache[bufnr])
base = util.norm_base(base)
local text --- @type string[]
if base == bcache.git_obj.revision then
@ -52,8 +52,9 @@ end
--- @param bufnr integer
--- @param dbufnr integer
--- @param base string?
local bufwrite = async.create(3, function(bufnr, dbufnr, base)
local bcache = cache[bufnr]
--- @param _callback? fun()
local bufwrite = async.create(3, function(bufnr, dbufnr, base, _callback)
local bcache = assert(cache[bufnr])
local buftext = util.buf_lines(dbufnr)
base = util.norm_base(base)
bcache.git_obj:stage_lines(buftext)
@ -151,14 +152,17 @@ end
--- @param base string?
--- @param opts Gitsigns.DiffthisOpts
M.diffthis = async.create(2, function(base, opts)
--- @param _callback? fun()
M.diffthis = async.create(2, function(base, opts, _callback)
if vim.wo.diff then
log.dprint('diff is disabled')
return
end
local bufnr = api.nvim_get_current_buf()
local bcache = cache[bufnr]
if not bcache then
log.dprintf('buffer %d is not attached', bufnr)
return
end
@ -176,16 +180,24 @@ end)
--- @param bufnr integer
--- @param base string
M.show = async.create(2, function(bufnr, base)
--- @param _callback? fun()
M.show = async.create(2, function(bufnr, base, _callback)
__FUNC__ = 'show'
local bufname = create_show_buf(bufnr, base)
if not bufname then
dprint('No bufname for revision ' .. base)
log.dprint('No bufname for revision ' .. base)
return
end
dprint('bufname ' .. bufname)
log.dprint('bufname ' .. bufname)
vim.cmd.edit(bufname)
-- Wait for the buffer to attach in case the user passes a callback that
-- requires the buffer to be attached.
local sbufnr = api.nvim_get_current_buf()
vim.wait(2000, function()
return cache[sbufnr] ~= nil
end)
end)
--- @param bufnr integer
@ -205,16 +217,15 @@ end
-- This function needs to be throttled as there is a call to vim.ui.input
--- @param bufnr integer
M.update = throttle_by_id(async.create(1, function(bufnr)
--- @param _callback? fun()
M.update = throttle_by_id(async.create(1, function(bufnr, _callback)
if not vim.wo.diff then
return
end
local bcache = cache[bufnr]
-- Note this will be the bufname for the currently set base
-- which are the only ones we want to update
local bufname = bcache:get_rev_bufname()
local bufname = assert(cache[bufnr]):get_rev_bufname()
for _, w in ipairs(api.nvim_list_wins()) do
if api.nvim_win_is_valid(w) then

View File

@ -1,25 +1,12 @@
local async = require('gitsigns.async')
local scheduler = require('gitsigns.async').scheduler
local log = require('gitsigns.debug.log')
local util = require('gitsigns.util')
local system = require('gitsigns.system').system
local gs_config = require('gitsigns.config')
local config = gs_config.config
local uv = vim.uv or vim.loop
local dprint = log.dprint
local dprintf = log.dprintf
local error_once = require('gitsigns.message').error_once
local Repo = require('gitsigns.git.repo')
local check_version = require('gitsigns.git.version').check
local M = {}
--- @type fun(cmd: string[], opts?: vim.SystemOpts): vim.SystemCompleted
local asystem = async.wrap(3, system)
M.Repo = Repo
--- @param file string
--- @return boolean
@ -48,78 +35,7 @@ local Obj = {}
M.Obj = Obj
--- @class Gitsigns.RepoInfo
--- @field gitdir string
--- @field toplevel string
--- @field detached boolean
--- @field abbrev_head string
--- @class Gitsigns.Repo : Gitsigns.RepoInfo
--- @field username string
local Repo = {}
M.Repo = Repo
--- @class Gitsigns.Git.JobSpec : vim.SystemOpts
--- @field command? string
--- @field ignore_error? boolean
--- @async
--- @param args string[]
--- @param spec? Gitsigns.Git.JobSpec
--- @return string[] stdout, string? stderr
local function git_command(args, spec)
spec = spec or {}
local cmd = {
spec.command or 'git',
'--no-pager',
'--no-optional-locks',
'--literal-pathspecs',
'-c',
'gc.auto=0', -- Disable auto-packing which emits messages to stderr
unpack(args),
}
if spec.text == nil then
spec.text = true
end
-- Fix #895. Only needed for Nvim 0.9 and older
spec.clear_env = true
--- @type vim.SystemCompleted
local obj = asystem(cmd, spec)
local stdout = obj.stdout
local stderr = obj.stderr
if not spec.ignore_error and obj.code > 0 then
local cmd_str = table.concat(cmd, ' ')
log.eprintf("Received exit code %d when running command\n'%s':\n%s", obj.code, cmd_str, stderr)
end
local stdout_lines = vim.split(stdout or '', '\n')
if spec.text then
-- If stdout ends with a newline, then remove the final empty string after
-- the split
if stdout_lines[#stdout_lines] == '' then
stdout_lines[#stdout_lines] = nil
end
end
if log.verbose then
log.vprintf('%d lines:', #stdout_lines)
for i = 1, math.min(10, #stdout_lines) do
log.vprintf('\t%s', stdout_lines[i])
end
end
if stderr == '' then
stderr = nil
end
return stdout_lines, stderr
end
local git_command = require('gitsigns.git.cmd')
--- @async
--- @param file_cmp string
@ -145,224 +61,9 @@ function M.diff(file_cmp, file_buf, indent_heuristic, diff_algo)
})
end
--- @async
--- @param gitdir string
--- @param head_str string
--- @param cwd string
--- @param cmd? string
--- @return string
local function process_abbrev_head(gitdir, head_str, cwd, cmd)
if not gitdir then
return head_str
end
if head_str == 'HEAD' then
local short_sha = git_command({ 'rev-parse', '--short', 'HEAD' }, {
command = cmd,
ignore_error = true,
cwd = cwd,
})[1] or ''
if log.debug_mode and short_sha ~= '' then
short_sha = 'HEAD'
end
if
util.path_exists(gitdir .. '/rebase-merge')
or util.path_exists(gitdir .. '/rebase-apply')
then
return short_sha .. '(rebasing)'
end
return short_sha
end
return head_str
end
local has_cygpath = jit and jit.os == 'Windows' and vim.fn.executable('cygpath') == 1
local cygpath_convert ---@type fun(path: string): string
if has_cygpath then
cygpath_convert = function(path)
--- @type vim.SystemCompleted
local obj = asystem({ 'cygpath', '-aw', path })
return obj.stdout
end
end
--- @param path string
--- @return string
local function normalize_path(path)
if path and has_cygpath and not uv.fs_stat(path) then
-- If on windows and path isn't recognizable as a file, try passing it
-- through cygpath
path = cygpath_convert(path)
end
return path
end
--- @async
--- @param cwd string
--- @param cmd? string
--- @param gitdir? string
--- @param toplevel? string
--- @return Gitsigns.RepoInfo
function M.get_repo_info(cwd, cmd, gitdir, toplevel)
-- Does git rev-parse have --absolute-git-dir, added in 2.13:
-- https://public-inbox.org/git/20170203024829.8071-16-szeder.dev@gmail.com/
local has_abs_gd = check_version({ 2, 13 })
local git_dir_opt = has_abs_gd and '--absolute-git-dir' or '--git-dir'
-- Wait for internal scheduler to settle before running command (#215)
scheduler()
local args = {}
if gitdir then
vim.list_extend(args, { '--git-dir', gitdir })
end
if toplevel then
vim.list_extend(args, { '--work-tree', toplevel })
end
vim.list_extend(args, {
'rev-parse',
'--show-toplevel',
git_dir_opt,
'--abbrev-ref',
'HEAD',
})
local results = git_command(args, {
command = cmd,
ignore_error = true,
cwd = toplevel or cwd,
})
local toplevel_r = normalize_path(results[1])
local gitdir_r = normalize_path(results[2])
if gitdir_r and not has_abs_gd then
gitdir_r = assert(uv.fs_realpath(gitdir_r))
end
return {
toplevel = toplevel_r,
gitdir = gitdir_r,
abbrev_head = process_abbrev_head(gitdir_r, results[3], cwd, cmd),
detached = toplevel_r and gitdir_r ~= toplevel_r .. '/.git',
}
end
--------------------------------------------------------------------------------
-- Git repo object methods
--------------------------------------------------------------------------------
--- Run git command the with the objects gitdir and toplevel
--- @async
--- @param args string[]
--- @param spec? Gitsigns.Git.JobSpec
--- @return string[] stdout, string? stderr
function Repo:command(args, spec)
spec = spec or {}
spec.cwd = self.toplevel
local args1 = {
'--git-dir',
self.gitdir,
}
if self.detached then
vim.list_extend(args1, { '--work-tree', self.toplevel })
end
vim.list_extend(args1, args)
return git_command(args1, spec)
end
--- @return string[]
function Repo:files_changed()
--- @type string[]
local results = self:command({ 'status', '--porcelain', '--ignore-submodules' })
local ret = {} --- @type string[]
for _, line in ipairs(results) do
if line:sub(1, 2):match('^.M') then
ret[#ret + 1] = line:sub(4, -1)
end
end
return ret
end
--- @param encoding string
--- @return boolean
local function iconv_supported(encoding)
-- TODO(lewis6991): needs https://github.com/neovim/neovim/pull/21924
if vim.startswith(encoding, 'utf-16') then
return false
elseif vim.startswith(encoding, 'utf-32') then
return false
end
return true
end
--- Get version of file in the index, return array lines
--- @param object string
--- @param encoding? string
--- @return string[] stdout, string? stderr
function Repo:get_show_text(object, encoding)
local stdout, stderr = self:command({ 'show', object }, { text = false, ignore_error = true })
if encoding and encoding ~= 'utf-8' and iconv_supported(encoding) then
for i, l in ipairs(stdout) do
--- @diagnostic disable-next-line:param-type-mismatch
stdout[i] = vim.iconv(l, encoding, 'utf-8')
end
end
return stdout, stderr
end
--- @async
function Repo:update_abbrev_head()
self.abbrev_head = M.get_repo_info(self.toplevel).abbrev_head
end
--- @async
--- @param dir string
--- @param gitdir? string
--- @param toplevel? string
--- @return Gitsigns.Repo
function Repo.new(dir, gitdir, toplevel)
local self = setmetatable({}, { __index = Repo })
self.username = git_command({ 'config', 'user.name' }, { ignore_error = true })[1]
local info = M.get_repo_info(dir, nil, gitdir, toplevel)
for k, v in
pairs(info --[[@as table<string,any>]])
do
---@diagnostic disable-next-line:no-unknown
self[k] = v
end
return self
end
--------------------------------------------------------------------------------
-- Git object methods
--------------------------------------------------------------------------------
--- Run git command the with the objects gitdir and toplevel
--- @param args string[]
--- @param spec? Gitsigns.Git.JobSpec
--- @return string[] stdout, string? stderr
function Obj:command(args, spec)
return self.repo:command(args, spec)
end
--- @param revision? string
function Obj:update_revision(revision)
revision = util.norm_base(revision)
self.revision = revision
self.revision = util.norm_base(revision)
self:update()
end
@ -434,7 +135,7 @@ function Obj:file_info_index(file, silent)
cmd[#cmd + 1] = file or self.file
local results, stderr = self:command(cmd, { ignore_error = true })
local results, stderr = self.repo:command(cmd, { ignore_error = true })
if stderr and not silent then
-- ignore_error for the cases when we run:
@ -481,7 +182,7 @@ end
--- @param silent? boolean
--- @return Gitsigns.FileInfo
function Obj:file_info_tree(file, silent)
local results, stderr = self:command({
local results, stderr = self.repo:command({
'-c',
'core.quotepath=off',
'ls-tree',
@ -516,14 +217,14 @@ end
--- @return string[] stdout, string? stderr
function Obj:get_show_text(revision)
if revision and not self.relpath then
dprint('no relpath')
log.dprint('no relpath')
return {}
end
local object = revision and (revision .. ':' .. self.relpath) or self.object_name
if not object then
dprint('no revision or object_name')
log.dprint('no revision or object_name')
return { '' }
end
@ -551,215 +252,17 @@ local function autocmd_changed(file)
end
function Obj:unstage_file()
self:command({ 'reset', self.file })
self.repo:command({ 'reset', self.file })
autocmd_changed(self.file)
end
--- @class Gitsigns.CommitInfo
--- @field author string
--- @field author_mail string
--- @field author_time integer
--- @field author_tz string
--- @field committer string
--- @field committer_mail string
--- @field committer_time integer
--- @field committer_tz string
--- @field summary string
--- @field sha string
--- @field abbrev_sha string
--- @field boundary? true
--- @class Gitsigns.BlameInfoPublic: Gitsigns.BlameInfo, Gitsigns.CommitInfo
--- @field body? string[]
--- @field hunk_no? integer
--- @field num_hunks? integer
--- @field hunk? string[]
--- @field hunk_head? string
--- @class Gitsigns.BlameInfo
--- @field orig_lnum integer
--- @field final_lnum integer
--- @field commit Gitsigns.CommitInfo
--- @field filename string
--- @field previous_filename? string
--- @field previous_sha? string
local NOT_COMMITTED = {
author = 'Not Committed Yet',
author_mail = '<not.committed.yet>',
committer = 'Not Committed Yet',
committer_mail = '<not.committed.yet>',
}
--- @param file string
--- @return Gitsigns.CommitInfo
function M.not_committed(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
---@return integer
local function asinteger(x)
return assert(tonumber(x))
end
--- @param lines string[]
--- @param lnum? integer
--- @param revision? string
--- @param opts? Gitsigns.BlameOpts
--- @return table<integer,Gitsigns.BlameInfo?>?
function Obj:run_blame(lines, lnum, opts)
local ret = {} --- @type table<integer,Gitsigns.BlameInfo>
if not self.object_name or self.repo.abbrev_head == '' then
-- As we support attaching to untracked files we need to return something if
-- the file isn't isn't tracked in git.
-- If abbrev_head is empty, then assume the repo has no commits
local commit = M.not_committed(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
local args = { 'blame', '--contents', '-', '--incremental' }
opts = opts or {}
if opts.ignore_whitespace then
args[#args + 1] = '-w'
end
if lnum then
vim.list_extend(args, { '-L', lnum .. ',+1' })
end
if opts.extra_opts then
vim.list_extend(args, opts.extra_opts)
end
local ignore_file = self.repo.toplevel .. '/.git-blame-ignore-revs'
if uv.fs_stat(ignore_file) then
vim.list_extend(args, { '--ignore-revs-file', ignore_file })
end
args[#args + 1] = opts.rev
args[#args + 1] = '--'
args[#args + 1] = self.file
local results, stderr = self:command(args, { stdin = lines, ignore_error = true })
if stderr then
error_once('Error running git-blame: ' .. stderr)
return
end
if #results == 0 then
return
end
local commits = {} --- @type table<string,Gitsigns.CommitInfo>
local i = 1
while i <= #results do
--- @param pat? string
--- @return string
local function get(pat)
local l = assert(results[i])
i = i + 1
if pat then
return l:match(pat)
end
return l
end
local function peek(pat)
local l = results[i]
if l and pat then
return l:match(pat)
end
return l
end
local sha, orig_lnum_str, final_lnum_str, size_str = get('(%x+) (%d+) (%d+) (%d+)')
local orig_lnum = asinteger(orig_lnum_str)
local final_lnum = asinteger(final_lnum_str)
local size = asinteger(size_str)
if peek():match('^author ') then
--- @type table<string,string|true>
local commit = {
sha = sha,
abbrev_sha = sha:sub(1, 8),
}
-- filename terminates the entry
while peek() and not (peek():match('^filename ') or peek():match('^previous ')) do
local l = get()
local key, value = l:match('^([^%s]+) (.*)')
if key then
if vim.endswith(key, '_time') then
value = tonumber(value)
end
key = key:gsub('%-', '_') --- @type string
commit[key] = value
else
commit[l] = true
if l ~= 'boundary' then
dprintf("Unknown tag: '%s'", l)
end
end
end
-- New in git 2.41:
-- The output given by "git blame" that attributes a line to contents
-- taken from the file specified by the "--contents" option shows it
-- differently from a line attributed to the working tree file.
if
commit.author_mail == '<external.file>'
or commit.author_mail == 'External file (--contents)'
then
commit = vim.tbl_extend('force', commit, NOT_COMMITTED)
end
commits[sha] = commit
end
local previous_sha, previous_filename = peek():match('^previous (%x+) (.*)')
if previous_sha then
get()
end
local filename = assert(get():match('^filename (.*)'))
for j = 0, size - 1 do
ret[final_lnum + j] = {
final_lnum = final_lnum + j,
orig_lnum = orig_lnum + j,
commit = commits[sha],
filename = filename,
previous_filename = previous_filename,
previous_sha = previous_sha,
}
end
end
return ret
--- @return table<integer,Gitsigns.BlameInfo?>
function Obj:run_blame(lines, lnum, revision, opts)
return require('gitsigns.git.blame').run_blame(self, lines, lnum, revision, opts)
end
--- @param obj Gitsigns.GitObj
@ -770,12 +273,12 @@ local function ensure_file_in_index(obj)
if not obj.object_name then
-- If there is no object_name then it is not yet in the index so add it
obj:command({ 'add', '--intent-to-add', obj.file })
obj.repo:command({ 'add', '--intent-to-add', obj.file })
else
-- Update the index with the common ancestor (stage 1) which is what bcache
-- stores
local info = string.format('%s,%s,%s', obj.mode_bits, obj.object_name, obj.relpath)
obj:command({ 'update-index', '--add', '--cacheinfo', info })
obj.repo:command({ 'update-index', '--add', '--cacheinfo', info })
end
obj:update()
@ -784,17 +287,15 @@ end
--- Stage 'lines' as the entire contents of the file
--- @param lines string[]
function Obj:stage_lines(lines)
local stdout = self:command({
local new_object = self.repo:command({
'hash-object',
'-w',
'--path',
self.relpath,
'--stdin',
}, { stdin = lines })
}, { stdin = lines })[1]
local new_object = stdout[1]
self:command({
self.repo:command({
'update-index',
'--cacheinfo',
string.format('%s,%s,%s', self.mode_bits, new_object, self.relpath),
@ -808,9 +309,7 @@ end
function Obj:stage_hunks(hunks, invert)
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 = require('gitsigns.hunks').create_patch(self.relpath, hunks, self.mode_bits, invert)
if not self.i_crlf and self.w_crlf then
-- Remove cr
@ -819,7 +318,7 @@ function Obj:stage_hunks(hunks, invert)
end
end
self:command({
self.repo:command({
'apply',
'--whitespace=nowarn',
'--cached',
@ -834,7 +333,7 @@ end
--- @return string?
function Obj:has_moved()
local out = self:command({ 'diff', '--name-status', '-C', '--cached' })
local out = self.repo:command({ 'diff', '--name-status', '-C', '--cached' })
local orig_relpath = self.orig_relpath or self.relpath
for _, l in ipairs(out) do
local parts = vim.split(l, '%s+')
@ -858,24 +357,25 @@ end
--- @return Gitsigns.GitObj?
function Obj.new(file, revision, encoding, gitdir, toplevel)
if in_git_dir(file) then
dprint('In git dir')
log.dprint('In git dir')
return nil
end
local self = setmetatable({}, { __index = Obj })
if not vim.startswith(file, '/') and toplevel then
file = toplevel .. util.path_sep .. file
end
local repo = Repo.get(util.dirname(file), gitdir, toplevel)
if not repo then
log.dprint('Not in git repo')
return
end
local self = setmetatable({}, { __index = Obj })
self.repo = repo
self.file = file
self.revision = util.norm_base(revision)
self.encoding = encoding
self.repo = Repo.new(util.dirname(file), gitdir, toplevel)
if not self.repo.gitdir then
dprint('Not in git repo')
return nil
end
-- When passing gitdir and toplevel, suppress stderr when resolving the file
local silent = gitdir ~= nil and toplevel ~= nil

View File

@ -135,6 +135,48 @@ M.hls = {
},
},
{
GitSignsAddCul = {
'GitSignsAdd',
desc = "Used for the text of 'add' signs when the cursor is on the same line as the sign.",
},
},
{
GitSignsChangeCul = {
'GitSignsChange',
desc = "Used for the text of 'change' signs when the cursor is on the same line as the sign.",
},
},
{
GitSignsDeleteCul = {
'GitSignsDelete',
desc = "Used for the text of 'delete' signs when the cursor is on the same line as the sign.",
},
},
{
GitSignsChangedeleteCul = {
'GitSignsChangeCul',
desc = "Used for the text of 'changedelete' signs when the cursor is on the same line as the sign.",
},
},
{
GitSignsTopdeleteCul = {
'GitSignsDeleteCul',
desc = "Used for the text of 'topdelete' signs when the cursor is on the same line as the sign.",
},
},
{
GitSignsUntrackedCul = {
'GitSignsAddCul',
desc = "Used for the text of 'untracked' signs when the cursor is on the same line as the sign.",
},
},
-- Don't set GitSignsDeleteLn by default
-- {GitSignsDeleteLn = {}},
@ -153,6 +195,11 @@ M.hls = {
{ GitSignsStagedDeleteLn = { 'GitSignsDeleteLn', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedChangedeleteLn = { 'GitSignsChangedeleteLn', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedTopdeleteLn = { 'GitSignsTopdeleteLn', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedAddCul = { 'GitSignsAddCul', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedChangeCul = { 'GitSignsChangeCul', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedDeleteCul = { 'GitSignsDeleteCul', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedChangedeleteCul = { 'GitSignsChangedeleteCul', fg_factor = 0.5, hidden = true } },
{ GitSignsStagedTopdeleteCul = { 'GitSignsTopdeleteCul', fg_factor = 0.5, hidden = true } },
{
GitSignsAddPreview = {

View File

@ -1,22 +1,15 @@
local async = require('gitsigns.async')
local gs_cache = require('gitsigns.cache')
local cache = gs_cache.cache
local log = require('gitsigns.debug.log')
local util = require('gitsigns.util')
local run_diff = require('gitsigns.diff')
local Hunks = require('gitsigns.hunks')
local Signs = require('gitsigns.signs')
local Status = require('gitsigns.status')
local debounce_trailing = require('gitsigns.debounce').debounce_trailing
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
local log = require('gitsigns.debug.log')
local dprint = log.dprint
local util = require('gitsigns.util')
local run_diff = require('gitsigns.diff')
local gs_hunks = require('gitsigns.hunks')
local cache = require('gitsigns.cache').cache
local config = require('gitsigns.config').config
local api = vim.api
@ -31,7 +24,7 @@ local M = {}
--- @param hunks Gitsigns.Hunk.Hunk[]
--- @param top integer
--- @param bot integer
--- @param clear boolean
--- @param clear? boolean
--- @param untracked boolean
local function apply_win_signs0(bufnr, signs, hunks, top, bot, clear, untracked)
if clear then
@ -47,14 +40,11 @@ local function apply_win_signs0(bufnr, signs, hunks, top, bot, clear, untracked)
-- least one sign. Only do this on the first call after an update when we all
-- the signs have been cleared.
if clear and i == 1 then
signs:add(
bufnr,
gs_hunks.calc_signs(hunk, next, hunk.added.start, hunk.added.start, untracked)
)
signs:add(bufnr, Hunks.calc_signs(hunk, next, hunk.added.start, hunk.added.start, untracked))
end
if top <= hunk.vend and bot >= hunk.added.start then
signs:add(bufnr, gs_hunks.calc_signs(hunk, next, top, bot, untracked))
signs:add(bufnr, Hunks.calc_signs(hunk, next, top, bot, untracked))
end
if hunk.added.start > bot then
break
@ -65,13 +55,9 @@ end
--- @param bufnr integer
--- @param top integer
--- @param bot integer
--- @param clear boolean
--- @param clear? boolean
local function apply_win_signs(bufnr, top, bot, clear)
local bcache = cache[bufnr]
if not bcache then
return
end
local bcache = assert(cache[bufnr])
local untracked = bcache.git_obj.object_name == nil
apply_win_signs0(bufnr, signs_normal, bcache.hunks, top, bot, clear, untracked)
if signs_staged then
@ -88,15 +74,13 @@ local function on_lines_blame(blame, first, last_orig, last_new)
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
if last_new < last_orig then
util.list_remove(blame, last_new + 1, last_orig)
elseif last_new > last_orig then
util.list_insert(blame, last_orig + 1, last_new)
end
for i = math.min(first + 1, last_new), math.max(first + 1, last_new) do
for i = first + 1, last_new do
blame[i] = nil
end
end
@ -109,7 +93,7 @@ end
function M.on_lines(buf, first, last_orig, last_new)
local bcache = cache[buf]
if not bcache then
dprint('Cache for buffer was nil. Detaching')
log.dprint('Cache for buffer was nil. Detaching')
return true
end
@ -161,7 +145,7 @@ local function apply_word_diff(bufnr, row)
local lnum = row + 1
local hunk = gs_hunks.find_hunk(lnum, bcache.hunks)
local hunk = Hunks.find_hunk(lnum, bcache.hunks)
if not hunk then
-- No hunk at line
return
@ -320,7 +304,7 @@ function M.show_deleted_in_float(bufnr, nsd, hunk, staged)
virt_lines_leftcol = true,
})
local bcache = cache[bufnr]
local bcache = assert(cache[bufnr])
local pbufnr = api.nvim_create_buf(false, true)
local text = staged and bcache.compare_text_head or bcache.compare_text
api.nvim_buf_set_lines(pbufnr, 0, -1, false, assert(text))
@ -422,7 +406,7 @@ end
--- @param bufnr integer
local function update_show_deleted(bufnr)
local bcache = cache[bufnr]
local bcache = assert(cache[bufnr])
clear_deleted(bufnr)
if config.show_deleted then
@ -440,15 +424,15 @@ end
function M.schedule(bufnr, check_compare_text)
async.scheduler()
if not api.nvim_buf_is_valid(bufnr) then
dprint('Buffer not valid, aborting')
log.dprint('Buffer not valid, aborting')
return false
end
if not cache[bufnr] then
dprint('Has detached, aborting')
log.dprint('Has detached, aborting')
return false
end
if check_compare_text and not cache[bufnr].compare_text then
dprint('compare_text was invalid, aborting')
log.dprint('compare_text was invalid, aborting')
return false
end
return true
@ -463,7 +447,7 @@ M.update = throttle_by_id(function(bufnr)
if not M.schedule(bufnr) then
return
end
local bcache = cache[bufnr]
local bcache = assert(cache[bufnr])
local old_hunks, old_hunks_staged = bcache.hunks, bcache.hunks_staged
bcache.hunks, bcache.hunks_staged = nil, nil
@ -500,15 +484,15 @@ M.update = throttle_by_id(function(bufnr)
if not M.schedule(bufnr) then
return
end
bcache.hunks_staged = gs_hunks.filter_common(hunks_head, bcache.hunks)
bcache.hunks_staged = Hunks.filter_common(hunks_head, bcache.hunks)
end
-- Note the decoration provider may have invalidated bcache.hunks at this
-- point
if
bcache.force_next_update
or gs_hunks.compare_heads(bcache.hunks, old_hunks)
or gs_hunks.compare_heads(bcache.hunks_staged, old_hunks_staged)
or Hunks.compare_heads(bcache.hunks, old_hunks)
or Hunks.compare_heads(bcache.hunks_staged, old_hunks_staged)
then
-- Apply signs to the window. Other signs will be added by the decoration
-- provider as they are drawn.
@ -517,7 +501,7 @@ M.update = throttle_by_id(function(bufnr)
update_show_deleted(bufnr)
bcache.force_next_update = false
local summary = gs_hunks.get_summary(bcache.hunks)
local summary = Hunks.get_summary(bcache.hunks)
summary.head = git_obj.repo.abbrev_head
Status:update(bufnr, summary)
end
@ -558,7 +542,7 @@ local function on_win(_cb, _winid, bufnr, topline, botline_guess)
end
local botline = math.min(botline_guess, api.nvim_buf_line_count(bufnr))
apply_win_signs(bufnr, topline + 1, botline + 1, false)
apply_win_signs(bufnr, topline + 1, botline + 1)
if not (config.word_diff and config.diff_opts.internal) then
return false

View File

@ -1,7 +1,7 @@
local M = {}
local api = vim.api
local M = {}
--- @param bufnr integer
--- @param lines string[]
--- @return integer

View File

@ -65,6 +65,7 @@ function M:add(bufnr, signs)
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,
cursorline_hl_group = config.culhl and hls.culhl or nil,
})
if not ok and config.debug_mode then

View File

@ -327,7 +327,7 @@ end
---@param first integer
---@param last integer
function M.list_remove(t, first, last)
local n = #t
local n = table.maxn(t)
for i = 0, n - first do
t[first + i] = t[last + 1 + i]
t[last + 1 + i] = nil
@ -347,7 +347,7 @@ end
---@param last integer
---@param v any
function M.list_insert(t, first, last, v)
local n = #t
local n = table.maxn(t)
-- Shift table forward
for i = n - first, 0, -1 do

View File

@ -1,10 +1,10 @@
local api = vim.api
local uv = vim.loop
local Status = require('gitsigns.status')
local async = require('gitsigns.async')
local log = require('gitsigns.debug.log')
local util = require('gitsigns.util')
local Status = require('gitsigns.status')
local cache = require('gitsigns.cache').cache
local config = require('gitsigns.config').config
@ -18,7 +18,7 @@ local dprintf = log.dprintf
--- @param bufnr integer
--- @param old_relpath string
local function handle_moved(bufnr, old_relpath)
local bcache = cache[bufnr]
local bcache = assert(cache[bufnr])
local git_obj = bcache.git_obj
local new_name = git_obj:has_moved()
@ -73,6 +73,7 @@ local function watcher_handler0(bufnr)
-- Avoid cache hit for detached buffer
-- ref: https://github.com/lewis6991/gitsigns.nvim/issues/956
if not manager.schedule(bufnr) then
dprint('buffer invalid (1)')
return
end
@ -81,6 +82,7 @@ local function watcher_handler0(bufnr)
git_obj.repo:update_abbrev_head()
if not manager.schedule(bufnr) then
dprint('buffer invalid (2)')
return
end
@ -91,6 +93,7 @@ local function watcher_handler0(bufnr)
git_obj:update()
if not manager.schedule(bufnr) then
dprint('buffer invalid (3)')
return
end
@ -99,6 +102,7 @@ local function watcher_handler0(bufnr)
-- moved. Check if it was moved and switch to it.
handle_moved(bufnr, old_relpath)
if not manager.schedule(bufnr) then
dprint('buffer invalid (4)')
return
end
end
@ -108,9 +112,12 @@ local function watcher_handler0(bufnr)
require('gitsigns.manager').update(bufnr)
end
--- Debounce and throttle the handler.
--- We also throttle in case the debounce delay is not enough and to prevent
--- too many handlers from being launched (and interleaved).
--- Debounce to:
--- - wait for all changes to the gitdir to complete.
--- Throttle to:
--- - ensure handler is only triggered once per git operation.
--- - prevent updates to the same buffer from interleaving as the handler is
--- async.
local watcher_handler =
debounce_trailing(200, async.create(1, throttle_by_id(watcher_handler0, true)), 1)
@ -123,11 +130,6 @@ end
local M = {}
local WATCH_IGNORE = {
ORIG_HEAD = true,
FETCH_HEAD = true,
}
--- @param bufnr integer
--- @param gitdir string
--- @return uv.uv_fs_event_t
@ -141,17 +143,15 @@ function M.watch_gitdir(bufnr, gitdir)
return
end
local info = string.format("Git dir update: '%s' %s", filename, inspect(events))
-- The luv docs say filename is passed as a string but it has been observed
-- to sometimes be nil.
-- https://github.com/lewis6991/gitsigns.nvim/issues/848
if filename == nil or WATCH_IGNORE[filename] or vim.endswith(filename, '.lock') then
dprintf('%s (ignoring)', info)
if not filename then
log.eprint('No filename')
return
end
dprint(info)
dprintf("Git dir update: '%s' %s", filename, inspect(events))
watcher_handler(bufnr)
end)