mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-06 11:48:23 +00:00
feat(explorer): git index watcher
This commit is contained in:
parent
268aa41e55
commit
4c12475e80
4 changed files with 85 additions and 45 deletions
|
@ -20,6 +20,15 @@ function M.refresh(path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param cwd string
|
||||||
|
function M.is_dirty(cwd)
|
||||||
|
local root = Snacks.git.get_root(cwd)
|
||||||
|
if not root then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return M.state[root] == nil
|
||||||
|
end
|
||||||
|
|
||||||
---@param cwd string
|
---@param cwd string
|
||||||
---@param opts? {on_update?: fun(), ttl?: number, force?: boolean}
|
---@param opts? {on_update?: fun(), ttl?: number, force?: boolean}
|
||||||
function M.update(cwd, opts)
|
function M.update(cwd, opts)
|
||||||
|
|
|
@ -220,6 +220,9 @@ end
|
||||||
---@param opts? {hidden?: boolean, ignored?: boolean}
|
---@param opts? {hidden?: boolean, ignored?: boolean}
|
||||||
function Tree:is_dirty(cwd, opts)
|
function Tree:is_dirty(cwd, opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
|
if require("snacks.explorer.git").is_dirty(cwd) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
local dirty = false
|
local dirty = false
|
||||||
self:get(cwd, function(n)
|
self:get(cwd, function(n)
|
||||||
if n.dir and n.open and not n.expanded then
|
if n.dir and n.open and not n.expanded then
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
local Git = require("snacks.explorer.git")
|
||||||
|
local Tree = require("snacks.explorer.tree")
|
||||||
|
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
|
|
||||||
---@alias snacks.explorer.Watcher fun(path:string, opts:vim._watch.watch.Opts?, callback:vim._watch.Callback):fun()
|
---@alias snacks.explorer.Watcher fun(path:string, opts:vim._watch.watch.Opts?, callback:vim._watch.Callback):fun()
|
||||||
|
@ -27,23 +30,51 @@ function M.abort()
|
||||||
running = {}
|
running = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local timer = assert(uv.new_timer())
|
||||||
|
|
||||||
|
function M.refresh()
|
||||||
|
-- batch updates and give explorer the time to update before the watcher
|
||||||
|
timer:start(500, 0, function()
|
||||||
|
local picker = Snacks.picker.get({ source = "explorer" })[1]
|
||||||
|
if picker and not picker.closed and Tree:is_dirty(picker:cwd(), picker.opts) then
|
||||||
|
if not picker.list.target then
|
||||||
|
picker.list:set_target()
|
||||||
|
end
|
||||||
|
picker:find()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
---@param cwd string
|
---@param cwd string
|
||||||
function M.watch(cwd)
|
function M.watch_git(cwd)
|
||||||
if running[cwd] then
|
local root = Snacks.git.get_root(cwd)
|
||||||
|
if not root then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local Tree = require("snacks.explorer.tree")
|
local handle = assert(vim.uv.new_fs_event())
|
||||||
|
handle:start(root .. "/.git", {}, function(_, file)
|
||||||
|
if file == "index" then
|
||||||
|
Git.refresh(root)
|
||||||
|
M.refresh()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return function()
|
||||||
|
if handle and not handle:is_closing() then
|
||||||
|
handle:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param cwd string
|
||||||
|
function M.watch_files(cwd)
|
||||||
local watch = M.watcher()
|
local watch = M.watcher()
|
||||||
if not watch then
|
if not watch then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
M.abort()
|
|
||||||
|
|
||||||
pcall(function()
|
return watch(cwd, {
|
||||||
local timer = assert(uv.new_timer())
|
|
||||||
local cancel = watch(cwd, {
|
|
||||||
uvflags = { recursive = true },
|
uvflags = { recursive = true },
|
||||||
}, function(path)
|
}, function(path, changes)
|
||||||
-- handle deletes
|
-- handle deletes
|
||||||
while not uv.fs_stat(path) do
|
while not uv.fs_stat(path) do
|
||||||
local p = vim.fs.dirname(path)
|
local p = vim.fs.dirname(path)
|
||||||
|
@ -53,25 +84,30 @@ function M.watch(cwd)
|
||||||
path = p
|
path = p
|
||||||
end
|
end
|
||||||
Tree:refresh(path)
|
Tree:refresh(path)
|
||||||
|
M.refresh()
|
||||||
-- batch updates and give explorer the time to update before the watcher
|
|
||||||
timer:start(100, 0, function()
|
|
||||||
local picker = Snacks.picker.get({ source = "explorer" })[1]
|
|
||||||
if picker and Tree:is_dirty(picker:cwd(), picker.opts) then
|
|
||||||
if not picker.list.target then
|
|
||||||
picker.list:set_target()
|
|
||||||
end
|
|
||||||
picker:find()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
running[cwd] = function()
|
|
||||||
if not timer:is_closing() then
|
|
||||||
timer:close()
|
|
||||||
end
|
|
||||||
cancel()
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param cwd string
|
||||||
|
function M.watch(cwd)
|
||||||
|
if running[cwd] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local watchers = { M.watch_git, M.watch_files }
|
||||||
|
local cancel = {} ---@type (fun())[]
|
||||||
|
|
||||||
|
for _, watch in ipairs(watchers) do
|
||||||
|
local ok, c = pcall(watch, cwd)
|
||||||
|
if ok and c then
|
||||||
|
cancel[#cancel + 1] = c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
running[cwd] = function()
|
||||||
|
vim.tbl_map(pcall, cancel)
|
||||||
|
running[cwd] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
@ -49,14 +49,6 @@ function State.new(picker)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
picker.list.win:on("TermClose", function()
|
|
||||||
local p = ref()
|
|
||||||
if p then
|
|
||||||
Tree:refresh(p:cwd())
|
|
||||||
Actions.update(p)
|
|
||||||
end
|
|
||||||
end, { pattern = "*lazygit" })
|
|
||||||
|
|
||||||
picker.list.win:on("BufWritePost", function(_, ev)
|
picker.list.win:on("BufWritePost", function(_, ev)
|
||||||
local p = ref()
|
local p = ref()
|
||||||
if p then
|
if p then
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue