chore(watchers): Watcher shares single fs_event from Event, node watchers use unique path prefixed debounce context (#1453)

This commit is contained in:
Alexander Courtis
2022-07-26 18:43:58 +10:00
committed by GitHub
parent e5222970d9
commit eff1db341c
4 changed files with 156 additions and 84 deletions

View File

@@ -45,21 +45,23 @@ function M.create_watcher(absolute_path)
return nil
end
log.line("watcher", "node start '%s'", absolute_path)
return Watcher.new {
absolute_path = absolute_path,
on_event = function(opts)
log.line("watcher", "node event scheduled '%s'", opts.absolute_path)
utils.debounce("explorer:watch:" .. opts.absolute_path, M.debounce_delay, function()
refresh_path(opts.absolute_path)
end)
end,
}
local function callback(watcher)
log.line("watcher", "node event scheduled %s", watcher.context)
utils.debounce(watcher.context, M.debounce_delay, function()
refresh_path(watcher._path)
end)
end
M.uid = M.uid + 1
return Watcher:new(absolute_path, callback, {
context = "explorer:watch:" .. absolute_path .. ":" .. M.uid,
})
end
function M.setup(opts)
M.enabled = opts.filesystem_watchers.enable
M.debounce_delay = opts.filesystem_watchers.debounce_delay
M.uid = 0
end
return M

View File

@@ -143,16 +143,17 @@ function M.load_project_status(cwd)
local watcher = nil
if M.config.filesystem_watchers.enable then
log.line("watcher", "git start")
watcher = Watcher.new {
absolute_path = utils.path_join { project_root, ".git" },
local callback = function(w)
log.line("watcher", "git event scheduled '%s'", w.project_root)
utils.debounce("git:watcher:" .. w.project_root, M.config.filesystem_watchers.debounce_delay, function()
reload_tree_at(w.project_root)
end)
end
watcher = Watcher:new(utils.path_join { project_root, ".git" }, callback, {
project_root = project_root,
on_event = function(opts)
log.line("watcher", "git event scheduled '%s'", opts.project_root)
utils.debounce("git:watcher:" .. opts.project_root, M.config.filesystem_watchers.debounce_delay, function()
reload_tree_at(opts.project_root)
end)
end,
}
})
end
M.projects[project_root] = {

View File

@@ -169,15 +169,17 @@ end
---Matching executable files in Windows.
---@param ext string
---@return boolean
local PATHEXT = vim.env.PATHEXT or ""
local wexe = vim.split(PATHEXT:gsub("%.", ""), ";")
local pathexts = {}
for _, v in pairs(wexe) do
pathexts[v] = true
end
function M.is_windows_exe(ext)
return pathexts[ext:upper()]
if not M.pathexts then
local PATHEXT = vim.env.PATHEXT or ""
local wexe = vim.split(PATHEXT:gsub("%.", ""), ";")
M.pathexts = {}
for _, v in pairs(wexe) do
M.pathexts[v] = true
end
end
return M.pathexts[ext:upper()]
end
function M.rename_loaded_buffers(old_path, new_path)
@@ -379,4 +381,23 @@ function M.clear_prompt()
end
end
-- return a new table with values from array
function M.array_shallow_clone(array)
local to = {}
for _, v in ipairs(array) do
table.insert(to, v)
end
return to
end
-- remove item from array if it exists
function M.array_remove(array, item)
for i, v in ipairs(array) do
if v == item then
table.remove(array, i)
break
end
end
end
return M

View File

@@ -3,10 +3,16 @@ local uv = vim.loop
local log = require "nvim-tree.log"
local utils = require "nvim-tree.utils"
local M = {
local M = {}
local Event = {
_events = {},
}
Event.__index = Event
local Watcher = {
_watchers = {},
}
local Watcher = {}
Watcher.__index = Watcher
local FS_EVENT_FLAGS = {
@@ -16,87 +22,129 @@ local FS_EVENT_FLAGS = {
recursive = false,
}
function Watcher.new(opts)
for _, existing in ipairs(M._watchers) do
if existing._opts.absolute_path == opts.absolute_path then
log.line("watcher", "Watcher:new using existing '%s'", opts.absolute_path)
return existing
end
function Event:new(path)
log.line("watcher", "Event:new '%s'", path)
local e = setmetatable({
_path = path,
_fs_event = nil,
_listeners = {},
}, Event)
if e:start() then
Event._events[path] = e
return e
else
return nil
end
log.line("watcher", "Watcher:new '%s'", opts.absolute_path)
local watcher = setmetatable({
_opts = opts,
}, Watcher)
watcher = watcher:start()
table.insert(M._watchers, watcher)
return watcher
end
function Watcher:start()
log.line("watcher", "Watcher:start '%s'", self._opts.absolute_path)
function Event:start()
log.line("watcher", "Event:start '%s'", self._path)
local rc, _, name
self._e, _, name = uv.new_fs_event()
if not self._e then
self._e = nil
utils.notify.warn(
string.format("Could not initialize an fs_event watcher for path %s : %s", self._opts.absolute_path, name)
)
return nil
self._fs_event, _, name = uv.new_fs_event()
if not self._fs_event then
self._fs_event = nil
utils.notify.warn(string.format("Could not initialize an fs_event watcher for path %s : %s", self._path, name))
return false
end
local event_cb = vim.schedule_wrap(function(err, filename, events)
local event_cb = vim.schedule_wrap(function(err, filename)
if err then
log.line("watcher", "event_cb for %s fail : %s", self._opts.absolute_path, err)
log.line("watcher", "event_cb for %s fail : %s", self._path, err)
else
log.line("watcher", "event_cb '%s' '%s' %s", self._opts.absolute_path, filename, vim.inspect(events))
self._opts.on_event(self._opts)
log.line("watcher", "event_cb '%s' '%s'", self._path, filename)
for _, listener in ipairs(self._listeners) do
listener()
end
end
end)
rc, _, name = self._e:start(self._opts.absolute_path, FS_EVENT_FLAGS, event_cb)
rc, _, name = self._fs_event:start(self._path, FS_EVENT_FLAGS, event_cb)
if rc ~= 0 then
utils.notify.warn(
string.format("Could not start the fs_event watcher for path %s : %s", self._opts.absolute_path, name)
)
utils.notify.warn(string.format("Could not start the fs_event watcher for path %s : %s", self._path, name))
return false
end
return true
end
function Event:add(listener)
table.insert(self._listeners, listener)
end
function Event:remove(listener)
utils.array_remove(self._listeners, listener)
if #self._listeners == 0 then
self:destroy()
end
end
function Event:destroy()
log.line("watcher", "Event:destroy '%s'", self._path)
if self._fs_event then
local rc, _, name = self._fs_event:stop()
if rc ~= 0 then
utils.notify.warn(string.format("Could not stop the fs_event watcher for path %s : %s", self._path, name))
end
self._fs_event = nil
end
Event._events[self._path] = nil
end
function Watcher:new(path, callback, data)
log.line("watcher", "Watcher:new '%s'", path)
local w = setmetatable(data, Watcher)
w._event = Event._events[path] or Event:new(path)
w._listener = nil
w._path = path
w._callback = callback
if not w._event then
return nil
end
return self
w:start()
table.insert(Watcher._watchers, w)
return w
end
function Watcher:start()
self._listener = function()
self._callback(self)
end
self._event:add(self._listener)
end
function Watcher:destroy()
log.line("watcher", "Watcher:destroy '%s'", self._opts.absolute_path)
if self._e then
local rc, _, name = self._e:stop()
if rc ~= 0 then
utils.notify.warn(
string.format("Could not stop the fs_event watcher for path %s : %s", self._opts.absolute_path, name)
)
end
self._e = nil
end
for i, w in ipairs(M._watchers) do
if w == self then
table.remove(M._watchers, i)
break
end
end
log.line("watcher", "Watcher:destroy '%s'", self._path)
self._event:remove(self._listener)
utils.array_remove(Watcher._watchers, self)
end
M.Watcher = Watcher
function M.purge_watchers()
for _, watcher in pairs(M._watchers) do
watcher:destroy()
log.line("watcher", "purge_watchers")
for _, w in ipairs(utils.array_shallow_clone(Watcher._watchers)) do
w:destroy()
end
for _, e in pairs(Event._events) do
e:destroy()
end
M._watchers = {}
end
return M