mirror of
https://github.com/lewis6991/gitsigns.nvim
synced 2025-02-20 22:47:23 +00:00
- Also make relative time the default time format for current_line_blame_formatter - When `current_line_blame_formatter` is passed as a function it is no longer passed an opts argument.
242 lines
5.8 KiB
Lua
242 lines
5.8 KiB
Lua
local async = require('gitsigns.async')
|
|
local cache = require('gitsigns.cache').cache
|
|
local config = require('gitsigns.config').config
|
|
local util = require('gitsigns.util')
|
|
|
|
local api = vim.api
|
|
|
|
local debounce = require('gitsigns.debounce')
|
|
|
|
local namespace = api.nvim_create_namespace('gitsigns_blame')
|
|
|
|
local M = {}
|
|
|
|
--- @param bufnr integer
|
|
local function reset(bufnr)
|
|
if not api.nvim_buf_is_valid(bufnr) then
|
|
return
|
|
end
|
|
api.nvim_buf_del_extmark(bufnr, namespace, 1)
|
|
vim.b[bufnr].gitsigns_blame_line_dict = nil
|
|
end
|
|
|
|
--- @param fmt string
|
|
--- @param name string
|
|
--- @param info Gitsigns.BlameInfoPublic
|
|
--- @return string
|
|
local function expand_blame_format(fmt, name, info)
|
|
if info.author == name then
|
|
info.author = 'You'
|
|
end
|
|
return util.expand_format(fmt, info)
|
|
end
|
|
|
|
--- @param virt_text {[1]: string, [2]: string}[]
|
|
--- @return string
|
|
local function flatten_virt_text(virt_text)
|
|
local res = {} ---@type string[]
|
|
for _, part in ipairs(virt_text) do
|
|
res[#res + 1] = part[1]
|
|
end
|
|
return table.concat(res)
|
|
end
|
|
|
|
--- @return integer
|
|
local function win_width()
|
|
local winid = api.nvim_get_current_win()
|
|
local wininfo = vim.fn.getwininfo(winid)[1]
|
|
local textoff = wininfo and wininfo.textoff or 0
|
|
return api.nvim_win_get_width(winid) - textoff
|
|
end
|
|
|
|
--- @param bufnr integer
|
|
--- @param lnum integer
|
|
--- @return integer
|
|
local function line_len(bufnr, lnum)
|
|
local line = api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1]
|
|
return api.nvim_strwidth(line)
|
|
end
|
|
|
|
--- @param fmt string
|
|
--- @return Gitsigns.CurrentLineBlameFmtFun
|
|
local function default_formatter(fmt)
|
|
return function(username, blame_info)
|
|
return {
|
|
{
|
|
expand_blame_format(fmt, username, blame_info),
|
|
'GitSignsCurrentLineBlame',
|
|
},
|
|
}
|
|
end
|
|
end
|
|
|
|
---@param bufnr integer
|
|
---@param blame_info Gitsigns.BlameInfoPublic
|
|
---@return {[1]: string, [2]:string}[]
|
|
local function get_blame_virt_text(bufnr, blame_info)
|
|
local git_obj = assert(cache[bufnr]).git_obj
|
|
|
|
local clb_formatter = blame_info.author == 'Not Committed Yet'
|
|
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)
|
|
end
|
|
|
|
return clb_formatter(git_obj.repo.username, blame_info)
|
|
end
|
|
|
|
--- @param bufnr integer
|
|
--- @param lnum integer
|
|
--- @param blame_info Gitsigns.BlameInfo
|
|
--- @param opts Gitsigns.CurrentLineBlameOpts
|
|
local function handle_blame_info(bufnr, lnum, blame_info, opts)
|
|
blame_info = util.convert_blame_info(blame_info)
|
|
|
|
local virt_text = get_blame_virt_text(bufnr, blame_info)
|
|
local virt_text_str = flatten_virt_text(virt_text)
|
|
|
|
vim.b[bufnr].gitsigns_blame_line_dict = blame_info
|
|
vim.b[bufnr].gitsigns_blame_line = virt_text_str
|
|
|
|
if opts.virt_text then
|
|
local virt_text_pos = opts.virt_text_pos
|
|
if virt_text_pos == 'right_align' then
|
|
if api.nvim_strwidth(virt_text_str) > (win_width() - line_len(bufnr, lnum)) then
|
|
virt_text_pos = 'eol'
|
|
end
|
|
end
|
|
api.nvim_buf_set_extmark(bufnr, namespace, lnum - 1, 0, {
|
|
id = 1,
|
|
virt_text = virt_text,
|
|
virt_text_pos = virt_text_pos,
|
|
priority = opts.virt_text_priority,
|
|
hl_mode = 'combine',
|
|
})
|
|
end
|
|
end
|
|
|
|
--- @param winid integer
|
|
--- @return integer lnum
|
|
local function get_lnum(winid)
|
|
return api.nvim_win_get_cursor(winid)[1]
|
|
end
|
|
|
|
--- @param winid integer
|
|
--- @param lnum integer
|
|
--- @return boolean
|
|
local function foldclosed(winid, lnum)
|
|
---@return boolean
|
|
return api.nvim_win_call(winid, function()
|
|
return vim.fn.foldclosed(lnum) ~= -1
|
|
end)
|
|
end
|
|
|
|
---@return boolean
|
|
local function insert_mode()
|
|
return api.nvim_get_mode().mode == 'i'
|
|
end
|
|
|
|
--- Update function, must be called in async context
|
|
--- @param bufnr integer
|
|
local function update0(bufnr)
|
|
async.scheduler()
|
|
if not api.nvim_buf_is_valid(bufnr) then
|
|
return
|
|
end
|
|
|
|
if insert_mode() then
|
|
return
|
|
end
|
|
|
|
local winid = api.nvim_get_current_win()
|
|
|
|
if bufnr ~= api.nvim_win_get_buf(winid) then
|
|
return
|
|
end
|
|
|
|
local lnum = get_lnum(winid)
|
|
|
|
-- Can't show extmarks on folded lines so skip
|
|
if foldclosed(winid, lnum) then
|
|
return
|
|
end
|
|
|
|
local bcache = cache[bufnr]
|
|
if not bcache or not bcache.git_obj.object_name then
|
|
return
|
|
end
|
|
|
|
local opts = config.current_line_blame_opts
|
|
|
|
local blame_info = bcache:get_blame(lnum, opts)
|
|
|
|
if not api.nvim_win_is_valid(winid) or bufnr ~= api.nvim_win_get_buf(winid) then
|
|
return
|
|
end
|
|
|
|
if not blame_info then
|
|
return
|
|
end
|
|
|
|
if lnum ~= get_lnum(winid) then
|
|
-- Cursor has moved during events; abort and tr-trigger another update
|
|
update0(bufnr)
|
|
return
|
|
end
|
|
|
|
handle_blame_info(bufnr, lnum, blame_info, opts)
|
|
end
|
|
|
|
local update = async.create(1, debounce.throttle_by_id(update0))
|
|
|
|
--- @type fun(bufnr: integer)
|
|
local update_debounced
|
|
|
|
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
|
|
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
|
|
|
|
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())
|
|
end
|
|
end
|
|
|
|
return M
|