feat: interleave attaches on startup
This commit is contained in:
parent
59bd933faa
commit
5fc573f2d2
|
@ -1,5 +1,4 @@
|
|||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
local async = require('gitsigns.async')
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local config = gs_config.config
|
||||
|
@ -15,7 +14,7 @@ local M = {}
|
|||
|
||||
local cwd_watcher ---@type uv.uv_fs_event_t?
|
||||
|
||||
local update_cwd_head = void(function()
|
||||
local update_cwd_head = async.void(function()
|
||||
local paths = vim.fs.find('.git', {
|
||||
limit = 1,
|
||||
upward = true,
|
||||
|
@ -56,7 +55,7 @@ local update_cwd_head = void(function()
|
|||
head = info.abbrev_head
|
||||
end
|
||||
|
||||
scheduler()
|
||||
async.scheduler()
|
||||
vim.g.gitsigns_head = head
|
||||
|
||||
if not gitdir then
|
||||
|
@ -74,9 +73,9 @@ local update_cwd_head = void(function()
|
|||
|
||||
local update_head = debounce_trailing(
|
||||
100,
|
||||
void(function()
|
||||
async.void(function()
|
||||
local new_head = git.get_repo_info(cwd).abbrev_head
|
||||
scheduler()
|
||||
async.scheduler()
|
||||
vim.g.gitsigns_head = new_head
|
||||
end)
|
||||
)
|
||||
|
@ -85,7 +84,7 @@ local update_cwd_head = void(function()
|
|||
cwd_watcher:start(
|
||||
towatch,
|
||||
{},
|
||||
void(function(err)
|
||||
async.void(function(err)
|
||||
local __FUNC__ = 'cwd_watcher_cb'
|
||||
if err then
|
||||
dprintf('Git dir update error: %s', err)
|
||||
|
@ -116,27 +115,31 @@ local function setup_debug()
|
|||
log.verbose = config._verbose
|
||||
end
|
||||
|
||||
--- @async
|
||||
local function setup_attach()
|
||||
scheduler()
|
||||
async.scheduler()
|
||||
|
||||
api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'BufWritePost' }, {
|
||||
group = 'gitsigns',
|
||||
callback = function(data)
|
||||
M.attach(nil, nil, data.event)
|
||||
require('gitsigns.attach').attach(data.buf, nil, data.event)
|
||||
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
|
||||
M.attach(buf, nil, 'setup')
|
||||
scheduler()
|
||||
-- Make sure to run each attach in its on async context in case one of the
|
||||
-- attaches is aborted.
|
||||
local attach = require('gitsigns.attach')
|
||||
async.run(attach.attach, buf, nil, 'setup')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @async
|
||||
local function setup_cwd_head()
|
||||
scheduler()
|
||||
async.scheduler()
|
||||
update_cwd_head()
|
||||
-- Need to debounce in case some plugin changes the cwd too often
|
||||
-- (like vim-grepper)
|
||||
|
@ -156,7 +159,7 @@ end
|
|||
---
|
||||
--- @param cfg table|nil Configuration for Gitsigns.
|
||||
--- See |gitsigns-usage| for more details.
|
||||
M.setup = void(function(cfg)
|
||||
M.setup = async.void(function(cfg)
|
||||
gs_config.build(cfg)
|
||||
|
||||
if vim.fn.executable('git') == 0 then
|
||||
|
|
|
@ -191,8 +191,6 @@ end
|
|||
--- @param staged? boolean
|
||||
--- @return Gitsigns.Hunk.Hunk[]? hunks
|
||||
local function get_hunks(bufnr, bcache, greedy, staged)
|
||||
local hunks --- @type Gitsigns.Hunk.Hunk[]
|
||||
|
||||
if greedy then
|
||||
-- Re-run the diff without linematch
|
||||
local buftext = util.buf_lines(bufnr)
|
||||
|
@ -205,8 +203,8 @@ local function get_hunks(bufnr, bcache, greedy, staged)
|
|||
if not text then
|
||||
return
|
||||
end
|
||||
hunks = run_diff(text, buftext, false)
|
||||
async.scheduler()
|
||||
local hunks = run_diff(text, buftext, false)
|
||||
manager.buf_check(bufnr)
|
||||
return hunks
|
||||
end
|
||||
|
||||
|
@ -225,20 +223,20 @@ end
|
|||
local function get_hunk(bufnr, range, greedy, staged)
|
||||
local bcache = cache[bufnr]
|
||||
local hunks = get_hunks(bufnr, bcache, greedy, staged)
|
||||
local hunk --- @type Gitsigns.Hunk.Hunk?
|
||||
if range then
|
||||
table.sort(range)
|
||||
local top, bot = range[1], range[2]
|
||||
hunk = Hunks.create_partial_hunk(hunks or {}, top, bot)
|
||||
hunk.added.lines = api.nvim_buf_get_lines(bufnr, top - 1, bot, false)
|
||||
hunk.removed.lines = vim.list_slice(
|
||||
bcache.compare_text,
|
||||
hunk.removed.start,
|
||||
hunk.removed.start + hunk.removed.count - 1
|
||||
)
|
||||
else
|
||||
hunk = get_cursor_hunk(bufnr, hunks)
|
||||
|
||||
if not range then
|
||||
return get_cursor_hunk(bufnr, hunks)
|
||||
end
|
||||
|
||||
table.sort(range)
|
||||
local top, bot = range[1], range[2]
|
||||
local hunk = Hunks.create_partial_hunk(hunks or {}, top, bot)
|
||||
hunk.added.lines = api.nvim_buf_get_lines(bufnr, top - 1, bot, false)
|
||||
hunk.removed.lines = vim.list_slice(
|
||||
bcache.compare_text,
|
||||
hunk.removed.start,
|
||||
hunk.removed.start + hunk.removed.count - 1
|
||||
)
|
||||
return hunk
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
-- Order by highest number of return types
|
||||
|
||||
local M = {}
|
||||
|
||||
--- @class Gitsigns.Async_T
|
||||
--- @field _current Gitsigns.Async_T
|
||||
local Async_T = {}
|
||||
|
||||
-- Handle for an object currently running on the event loop.
|
||||
|
@ -20,9 +20,9 @@ local Async_T = {}
|
|||
--
|
||||
-- We need to handle both.
|
||||
|
||||
-- Store all the async threads in a weak table so we don't prevent them from
|
||||
-- being garbage collected
|
||||
--- @type table<thread,uv.uv_handle_t>
|
||||
--- Store all the async threads in a weak table so we don't prevent them from
|
||||
--- being garbage collected
|
||||
--- @type table<thread,Gitsigns.Async_T>
|
||||
local handles = setmetatable({}, { __mode = 'k' })
|
||||
|
||||
--- Returns whether the current execution context is async.
|
||||
|
@ -31,6 +31,7 @@ function M.running()
|
|||
if current and handles[current] then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_Async_T(handle)
|
||||
|
@ -44,7 +45,8 @@ local function is_Async_T(handle)
|
|||
end
|
||||
end
|
||||
|
||||
-- Analogous to uv.close
|
||||
--- Analogous to uv.close
|
||||
--- @param cb function
|
||||
function Async_T:cancel(cb)
|
||||
-- Cancel anything running on the event loop
|
||||
if self._current and not self._current:is_cancelled() then
|
||||
|
@ -52,17 +54,24 @@ function Async_T:cancel(cb)
|
|||
end
|
||||
end
|
||||
|
||||
--- @param co thread
|
||||
--- @return Gitsigns.Async_T
|
||||
function Async_T.new(co)
|
||||
local handle = setmetatable({}, { __index = Async_T })
|
||||
handles[co] = handle
|
||||
return handle
|
||||
end
|
||||
|
||||
-- Analogous to uv.is_closing
|
||||
--- Analogous to uv.is_closing
|
||||
--- @return boolean
|
||||
function Async_T:is_cancelled()
|
||||
return self._current and self._current:is_cancelled()
|
||||
end
|
||||
|
||||
--- @param func function
|
||||
--- @param callback? fun(...: any)
|
||||
--- @param ... any
|
||||
--- @return Gitsigns.Async_T
|
||||
local function run(func, callback, ...)
|
||||
local co = coroutine.create(func)
|
||||
local handle = Async_T.new(co)
|
||||
|
@ -85,7 +94,7 @@ local function run(func, callback, ...)
|
|||
return
|
||||
end
|
||||
|
||||
--- @type integer, function
|
||||
--- @type integer, fun(...: any): any
|
||||
local nargs, fn = ret[2], ret[3]
|
||||
|
||||
assert(type(fn) == 'function', 'type error :: expected func')
|
||||
|
@ -95,6 +104,7 @@ local function run(func, callback, ...)
|
|||
|
||||
local r = fn(unpack(args, 1, nargs))
|
||||
if is_Async_T(r) then
|
||||
--- @cast r Gitsigns.Async_T
|
||||
handle._current = r
|
||||
end
|
||||
end
|
||||
|
@ -103,6 +113,10 @@ local function run(func, callback, ...)
|
|||
return handle
|
||||
end
|
||||
|
||||
--- @param argc integer
|
||||
--- @param func function
|
||||
--- @param ... any
|
||||
--- @return any ...
|
||||
function M.wait(argc, func, ...)
|
||||
-- Always run the wrapped functions in xpcall and re-raise the error in the
|
||||
-- coroutine. This makes pcall work as normal.
|
||||
|
@ -121,17 +135,17 @@ function M.wait(argc, func, ...)
|
|||
|
||||
local ok = ret[1]
|
||||
if not ok then
|
||||
local _, err, traceback = unpack(ret)
|
||||
local err, traceback = ret[2], ret[3]
|
||||
error(string.format('Wrapped function failed: %s\n%s', err, traceback))
|
||||
end
|
||||
|
||||
return unpack(ret, 2, table.maxn(ret))
|
||||
end
|
||||
|
||||
---Creates an async function with a callback style function.
|
||||
---@param func function: A callback style function to be converted. The last argument must be the callback.
|
||||
---@param argc number: The number of arguments of func. Must be included.
|
||||
---@return function: Returns an async function
|
||||
--- Creates an async function with a callback style function.
|
||||
--- @param func function: A callback style function to be converted. The last argument must be the callback.
|
||||
--- @param argc number: The number of arguments of func. Must be included.
|
||||
--- @return function: Returns an async function
|
||||
function M.wrap(func, argc)
|
||||
assert(argc)
|
||||
return function(...)
|
||||
|
@ -142,13 +156,13 @@ function M.wrap(func, argc)
|
|||
end
|
||||
end
|
||||
|
||||
---Use this to create a function which executes in an async context but
|
||||
---called from a non-async context. Inherently this cannot return anything
|
||||
---since it is non-blocking
|
||||
---@generic F: function
|
||||
---@param func F
|
||||
---@param argc integer
|
||||
---@return F
|
||||
--- Use this to create a function which executes in an async context but
|
||||
--- called from a non-async context. Inherently this cannot return anything
|
||||
--- since it is non-blocking
|
||||
--- @generic F: function
|
||||
--- @param func F
|
||||
--- @param argc integer
|
||||
--- @return F
|
||||
function M.create(func, argc)
|
||||
argc = argc or 0
|
||||
return function(...)
|
||||
|
@ -160,12 +174,12 @@ function M.create(func, argc)
|
|||
end
|
||||
end
|
||||
|
||||
---Use this to create a function which executes in an async context but
|
||||
---called from a non-async context. Inherently this cannot return anything
|
||||
---since it is non-blocking
|
||||
---@generic F: function
|
||||
---@param func F
|
||||
---@return async F
|
||||
--- Use this to create a function which executes in an async context but
|
||||
--- called from a non-async context. Inherently this cannot return anything
|
||||
--- since it is non-blocking
|
||||
--- @generic F: function
|
||||
--- @param func F
|
||||
--- @return async F
|
||||
function M.void(func)
|
||||
return function(...)
|
||||
if M.running() then
|
||||
|
@ -175,11 +189,20 @@ function M.void(func)
|
|||
end
|
||||
end
|
||||
|
||||
---An async function that when called will yield to the Neovim scheduler to be
|
||||
---able to call the API.
|
||||
--- @generic F: function
|
||||
--- @param func F
|
||||
--- @param ... any
|
||||
--- @return async F
|
||||
function M.run(func, ...)
|
||||
return run(func, nil, ...)
|
||||
end
|
||||
|
||||
--- An async function that when called will yield to the Neovim scheduler to be
|
||||
--- able to call the API.
|
||||
M.scheduler = M.wrap(vim.schedule, 1)
|
||||
|
||||
--- @param buf? integer
|
||||
--- @param cb function
|
||||
M.scheduler_if_buf_valid = M.wrap(function(buf, cb)
|
||||
vim.schedule(function()
|
||||
if not buf or vim.api.nvim_buf_is_valid(buf) then
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
local async = require('gitsigns.async')
|
||||
local git = require('gitsigns.git')
|
||||
|
||||
local manager = require('gitsigns.manager')
|
||||
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
local manager = require('gitsigns.manager')
|
||||
local hl = require('gitsigns.highlight')
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local Status = require('gitsigns.status')
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local config = gs_config.config
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
local void = require('gitsigns.async').void
|
||||
local util = require('gitsigns.util')
|
||||
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
@ -165,33 +162,9 @@ local function try_worktrees(_bufnr, file, encoding)
|
|||
end
|
||||
end
|
||||
|
||||
local done_setup = false
|
||||
|
||||
function M._setup()
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
|
||||
done_setup = true
|
||||
|
||||
manager.setup()
|
||||
|
||||
hl.setup_highlights()
|
||||
api.nvim_create_autocmd('ColorScheme', {
|
||||
group = 'gitsigns',
|
||||
callback = hl.setup_highlights,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('OptionSet', {
|
||||
group = 'gitsigns',
|
||||
pattern = { 'fileformat', 'bomb', 'eol' },
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end,
|
||||
})
|
||||
|
||||
-- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
-- waste lots of resource and even slow down vimgrep.
|
||||
--- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
--- waste lots of resource and even slow down vimgrep.
|
||||
local function setup_vimgrep_autocmds()
|
||||
api.nvim_create_autocmd('QuickFixCmdPre', {
|
||||
group = 'gitsigns',
|
||||
pattern = '*vimgrep*',
|
||||
|
@ -207,6 +180,29 @@ function M._setup()
|
|||
attach_disabled = false
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local done_setup = false
|
||||
|
||||
function M._setup()
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
|
||||
done_setup = true
|
||||
|
||||
manager.setup()
|
||||
require('gitsigns.highlight').setup()
|
||||
|
||||
api.nvim_create_autocmd('OptionSet', {
|
||||
group = 'gitsigns',
|
||||
pattern = { 'fileformat', 'bomb', 'eol' },
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end,
|
||||
})
|
||||
|
||||
setup_vimgrep_autocmds()
|
||||
|
||||
require('gitsigns.current_line_blame').setup()
|
||||
|
||||
|
@ -223,7 +219,7 @@ end
|
|||
--- @field commit string
|
||||
--- @field base string
|
||||
|
||||
--- Ensure attaches cannot be interleaved.
|
||||
--- Ensure attaches cannot be interleaved for the same buffer.
|
||||
--- Since attaches are asynchronous we need to make sure an attach isn't
|
||||
--- performed whilst another one is in progress.
|
||||
--- @param cbuf integer
|
||||
|
@ -435,7 +431,7 @@ end
|
|||
--- • {base}: (string|nil)
|
||||
--- The git revision that the file should be compared to.
|
||||
--- @param _trigger? string
|
||||
M.attach = void(function(bufnr, ctx, _trigger)
|
||||
M.attach = async.void(function(bufnr, ctx, _trigger)
|
||||
attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger)
|
||||
end)
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ local function check_version(version)
|
|||
return true
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- @param version string
|
||||
function M._set_version(version)
|
||||
if version ~= 'auto' then
|
||||
|
|
|
@ -331,4 +331,12 @@ function M.setup_highlights()
|
|||
end
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
M.setup_highlights()
|
||||
api.nvim_create_autocmd('ColorScheme', {
|
||||
group = 'gitsigns',
|
||||
callback = M.setup_highlights,
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
Loading…
Reference in New Issue