-
-
Notifications
You must be signed in to change notification settings - Fork 625
chore(watchers): Watcher shares single fs_event from Event, node watchers use unique path prefixed debounce context #1453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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()] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not related to the change, however this hurt my eyes and I could not help tidying it. |
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 = {}, | ||
kyazdani42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
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 | ||
end | ||
|
||
log.line("watcher", "Watcher:new '%s'", opts.absolute_path) | ||
function Event:new(path) | ||
log.line("watcher", "Event:new '%s'", path) | ||
|
||
local watcher = setmetatable({ | ||
_opts = opts, | ||
}, Watcher) | ||
local e = setmetatable({ | ||
_path = path, | ||
_fs_event = nil, | ||
_listeners = {}, | ||
}, Event) | ||
|
||
watcher = watcher:start() | ||
|
||
table.insert(M._watchers, watcher) | ||
|
||
return watcher | ||
if e:start() then | ||
Event._events[path] = e | ||
return e | ||
else | ||
return nil | ||
end | ||
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) | ||
) | ||
return nil | ||
utils.notify.warn(string.format("Could not start the fs_event watcher for path %s : %s", self._path, name)) | ||
return false | ||
end | ||
|
||
return self | ||
return true | ||
end | ||
|
||
function Watcher:destroy() | ||
log.line("watcher", "Watcher:destroy '%s'", self._opts.absolute_path) | ||
if self._e then | ||
local rc, _, name = self._e:stop() | ||
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._opts.absolute_path, name) | ||
) | ||
utils.notify.warn(string.format("Could not stop the fs_event watcher for path %s : %s", self._path, name)) | ||
end | ||
self._e = nil | ||
self._fs_event = nil | ||
end | ||
for i, w in ipairs(M._watchers) do | ||
if w == self then | ||
table.remove(M._watchers, i) | ||
break | ||
end | ||
|
||
Event._events[self._path] = nil | ||
end | ||
|
||
function Watcher:new(path, callback, data) | ||
kyazdani42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
|
||
w:start() | ||
|
||
table.insert(Watcher._watchers, w) | ||
|
||
return w | ||
end | ||
|
||
function Watcher:start() | ||
self._listener = function() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to call back to a member function like |
||
self._callback(self) | ||
end | ||
|
||
self._event:add(self._listener) | ||
end | ||
|
||
function Watcher:destroy() | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure i understand why a shallow clone is needed here ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
w:destroy() | ||
end | ||
|
||
for _, e in pairs(Event._events) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be necessary however it does not hurt. |
||
e:destroy() | ||
end | ||
M._watchers = {} | ||
end | ||
|
||
return M |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unique debouncer context added so that the real/link jobs do not clobber each other.