diff --git a/lua/gitsigns/debounce.lua b/lua/gitsigns/debounce.lua index 1699f1ca..56639b5a 100644 --- a/lua/gitsigns/debounce.lua +++ b/lua/gitsigns/debounce.lua @@ -7,14 +7,21 @@ local M = {} --- @generic F: function --- @param ms number Timeout in ms --- @param fn F Function to debounce +--- @param hash? fun(...): any Function that determines id from arguments to fn --- @return F Debounced function. -function M.debounce_trailing(ms, fn) - local timer = assert(uv.new_timer()) +function M.debounce_trailing(ms, fn, hash) + local running = {} --- @type table return function(...) + local id = hash and hash(...) or true + if running[id] == nil then + running[id] = assert(uv.new_timer()) + end + local timer = running[id] local argv = { ... } timer:start(ms, 0, function() timer:stop() - fn(unpack(argv)) + running[id] = nil + fn(unpack(argv, 1, table.maxn(argv))) end) end end diff --git a/lua/gitsigns/watcher.lua b/lua/gitsigns/watcher.lua index 7626a3e0..f00da6ba 100644 --- a/lua/gitsigns/watcher.lua +++ b/lua/gitsigns/watcher.lua @@ -59,7 +59,9 @@ local function handle_moved(bufnr, old_relpath) dprintf('%s buffer %d from %s to %s', msg, bufnr, old_name, bcache.file) end -local watch_gitdir_handler = async.void(function(bufnr) +local handler = async.void(function(bufnr, file) + local __FUNC__ = 'watcher_handler' + dprint(file) buf_check(bufnr) local git_obj = cache[bufnr].git_obj @@ -83,11 +85,28 @@ local watch_gitdir_handler = async.void(function(bufnr) buf_check(bufnr) end - cache[bufnr]:invalidate() + local bcache = cache[bufnr] + + if not bcache.base and file == 'index' then + bcache.compare_text = nil + end + + if file == 'HEAD' then + bcache.compare_text_head = nil + end + + bcache.hunks = nil + bcache.hunks_staged = nil require('gitsigns.manager').update(bufnr) end) +local function handler_hash(bufnr, filename) + return string.format('%d:%s', bufnr, filename) +end + +local handler_db = debounce_trailing(200, handler, handler_hash) + -- vim.inspect but on one line --- @param x any --- @return string @@ -97,14 +116,15 @@ end local M = {} +local WATCH_IGNORE = { + ORIG_HEAD = true, + FETCH_HEAD = true +} + --- @param bufnr integer --- @param gitdir string --- @return uv.uv_fs_event_t function M.watch_gitdir(bufnr, gitdir) - -- Setup debounce as we create the luv object so the debounce is independent - -- to each watcher - local watch_gitdir_handler_db = debounce_trailing(200, watch_gitdir_handler) - dprintf('Watching git dir') local w = assert(uv.new_fs_event()) w:start(gitdir, {}, function(err, filename, events) @@ -119,14 +139,14 @@ function M.watch_gitdir(bufnr, gitdir) -- 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 vim.endswith(filename, '.lock') then + if filename == nil or WATCH_IGNORE[filename] or vim.endswith(filename, '.lock') then dprintf('%s (ignoring)', info) return end dprint(info) - watch_gitdir_handler_db(bufnr) + handler_db(bufnr, filename) end) return w end