feat(explorer): add filesystem watchers (#1304)
* feat(explorer): add experimental watchers This commit introduces watchers to update the tree. This behavior is introduced behind an "filesystem_watchers" option which should prevent instabilities. It will become the default at some point. Co-authored-by: Alexander Courtis <alex@courtis.org>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
local uv = vim.loop
|
||||
|
||||
local git = require "nvim-tree.git"
|
||||
local watch = require "nvim-tree.explorer.watch"
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -15,6 +16,8 @@ function Explorer.new(cwd)
|
||||
local explorer = setmetatable({
|
||||
absolute_path = cwd,
|
||||
nodes = {},
|
||||
watcher = watch.create_watcher(cwd),
|
||||
open = true,
|
||||
}, Explorer)
|
||||
explorer:_load(explorer)
|
||||
return explorer
|
||||
@@ -30,11 +33,30 @@ function Explorer:expand(node)
|
||||
self:_load(node)
|
||||
end
|
||||
|
||||
function Explorer.clear_watchers_for(root_node)
|
||||
local function iterate(node)
|
||||
if node.watcher then
|
||||
node.watcher:stop()
|
||||
for _, child in pairs(node.nodes) do
|
||||
if child.watcher then
|
||||
iterate(child)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
iterate(root_node)
|
||||
end
|
||||
|
||||
function Explorer:_clear_watchers()
|
||||
Explorer.clear_watchers_for(self)
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
require("nvim-tree.explorer.explore").setup(opts)
|
||||
require("nvim-tree.explorer.filters").setup(opts)
|
||||
require("nvim-tree.explorer.sorters").setup(opts)
|
||||
require("nvim-tree.explorer.reload").setup(opts)
|
||||
require("nvim-tree.explorer.watch").setup(opts)
|
||||
end
|
||||
|
||||
M.Explorer = Explorer
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local uv = vim.loop
|
||||
local utils = require "nvim-tree.utils"
|
||||
local watch = require "nvim-tree.explorer.watch"
|
||||
|
||||
local M = {
|
||||
is_windows = vim.fn.has "win32" == 1,
|
||||
@@ -18,6 +19,7 @@ function M.folder(parent, absolute_path, name)
|
||||
nodes = {},
|
||||
open = false,
|
||||
parent = parent,
|
||||
watcher = watch.create_watcher(absolute_path),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -49,12 +51,13 @@ end
|
||||
function M.link(parent, absolute_path, name)
|
||||
--- I dont know if this is needed, because in my understanding, there isnt hard links in windows, but just to be sure i changed it.
|
||||
local link_to = uv.fs_realpath(absolute_path)
|
||||
local open, nodes, has_children
|
||||
local open, nodes, has_children, watcher
|
||||
if (link_to ~= nil) and uv.fs_stat(link_to).type == "directory" then
|
||||
local handle = uv.fs_scandir(link_to)
|
||||
has_children = handle and uv.fs_scandir_next(handle) ~= nil
|
||||
open = false
|
||||
nodes = {}
|
||||
watcher = watch.create_watcher(link_to)
|
||||
end
|
||||
|
||||
return {
|
||||
@@ -67,6 +70,7 @@ function M.link(parent, absolute_path, name)
|
||||
nodes = nodes,
|
||||
open = open,
|
||||
parent = parent,
|
||||
watcher = watcher,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ function M.reload(node, status)
|
||||
local node_ignored = node.git_status == "!!"
|
||||
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
|
||||
while true do
|
||||
local name, t = uv.fs_scandir_next(handle)
|
||||
if not name then
|
||||
local ok, name, t = pcall(uv.fs_scandir_next, handle)
|
||||
if not ok or not name then
|
||||
break
|
||||
end
|
||||
|
||||
@@ -48,12 +48,17 @@ function M.reload(node, status)
|
||||
child_names[abs] = true
|
||||
if not nodes_by_path[abs] then
|
||||
if t == "directory" and uv.fs_access(abs, "R") then
|
||||
table.insert(node.nodes, builders.folder(node, abs, name))
|
||||
local folder = builders.folder(node, abs, name)
|
||||
nodes_by_path[abs] = folder
|
||||
table.insert(node.nodes, folder)
|
||||
elseif t == "file" then
|
||||
table.insert(node.nodes, builders.file(node, abs, name))
|
||||
local file = builders.file(node, abs, name)
|
||||
nodes_by_path[abs] = file
|
||||
table.insert(node.nodes, file)
|
||||
elseif t == "link" then
|
||||
local link = builders.link(node, abs, name)
|
||||
if link.link_to ~= nil then
|
||||
nodes_by_path[abs] = link
|
||||
table.insert(node.nodes, link)
|
||||
end
|
||||
end
|
||||
|
||||
59
lua/nvim-tree/explorer/watch.lua
Normal file
59
lua/nvim-tree/explorer/watch.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
local log = require "nvim-tree.log"
|
||||
local utils = require "nvim-tree.utils"
|
||||
local git = require "nvim-tree.git"
|
||||
local Watcher = require("nvim-tree.watcher").Watcher
|
||||
|
||||
local M = {}
|
||||
|
||||
local function reload_and_get_git_project(path)
|
||||
local project_root = git.get_project_root(path)
|
||||
git.reload_project(project_root)
|
||||
return project_root, git.get_project(project_root) or {}
|
||||
end
|
||||
|
||||
local function update_parent_statuses(node, project, root)
|
||||
while project and node and node.absolute_path ~= root do
|
||||
require("nvim-tree.explorer.common").update_git_status(node, false, project)
|
||||
node = node.parent
|
||||
end
|
||||
end
|
||||
|
||||
local function is_git(path)
|
||||
return path:match "%.git$" ~= nil or path:match(utils.path_add_trailing ".git") ~= nil
|
||||
end
|
||||
|
||||
function M.create_watcher(absolute_path)
|
||||
if not M.enabled then
|
||||
return nil
|
||||
end
|
||||
if is_git(absolute_path) then
|
||||
return nil
|
||||
end
|
||||
|
||||
log.line("watcher", "node start '%s'", absolute_path)
|
||||
Watcher.new {
|
||||
absolute_path = absolute_path,
|
||||
interval = M.interval,
|
||||
on_event = function(path)
|
||||
local n = utils.get_node_from_path(absolute_path)
|
||||
if not n then
|
||||
return
|
||||
end
|
||||
log.line("watcher", "node event '%s'", path)
|
||||
|
||||
local node = utils.get_parent_of_group(n)
|
||||
local project_root, project = reload_and_get_git_project(path)
|
||||
require("nvim-tree.explorer.reload").reload(node, project)
|
||||
update_parent_statuses(node, project, project_root)
|
||||
|
||||
require("nvim-tree.renderer").draw()
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
M.enabled = opts.filesystem_watchers.enable
|
||||
M.interval = opts.filesystem_watchers.interval
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user