diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 6aadaf3dc27..f15962225e8 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -569,6 +569,7 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua filesystem_watchers = { enable = true, debounce_delay = 50, + max_outstanding_events = 100, ignore_dirs = { "/.ccls-cache", "/build", @@ -1450,6 +1451,15 @@ Enable / disable the feature. Idle milliseconds between filesystem change and action. Type: `number`, Default: `50` (ms) +*nvim-tree.filesystem_watchers.max_outstanding_events* +Maximum number of consecutive file system events for a single directory with +an interval < |nvim-tree.filesystem_watchers.debounce_delay| +When this is exceeded a warning notification will be shown and the file system +watcher disabled for the directory. +This has been observed when running on Windows PowerShell. +Consider adding this directory to |filesystem_watchers.ignore_dirs| + Type: `number`, Default: `100` + *nvim-tree.filesystem_watchers.ignore_dirs* List of vim regex for absolute directory paths that will not be watched or function returning whether a path should be ignored. @@ -3203,6 +3213,7 @@ highlight group is not, hard linking as follows: > |nvim-tree.filesystem_watchers.debounce_delay| |nvim-tree.filesystem_watchers.enable| |nvim-tree.filesystem_watchers.ignore_dirs| +|nvim-tree.filesystem_watchers.max_outstanding_events| |nvim-tree.filters.custom| |nvim-tree.filters.dotfiles| |nvim-tree.filters.enable| diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index dc0178b4a18..2ccb31faed9 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -444,6 +444,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS filesystem_watchers = { enable = true, debounce_delay = 50, + max_outstanding_events = 100, ignore_dirs = { "/.ccls-cache", "/build", diff --git a/lua/nvim-tree/explorer/watch.lua b/lua/nvim-tree/explorer/watch.lua index 06eb429ba9d..aa7ece0eaed 100644 --- a/lua/nvim-tree/explorer/watch.lua +++ b/lua/nvim-tree/explorer/watch.lua @@ -1,6 +1,7 @@ local log = require("nvim-tree.log") local git = require("nvim-tree.git") local utils = require("nvim-tree.utils") +local notify = require("nvim-tree.notify") local Watcher = require("nvim-tree.watcher").Watcher local M = { @@ -69,10 +70,30 @@ function M.create_watcher(node) ---@param watcher Watcher local function callback(watcher) log.line("watcher", "node event scheduled refresh %s", watcher.data.context) + + -- event is awaiting debouncing and handling + watcher.data.outstanding_events = watcher.data.outstanding_events + 1 + + -- disable watcher when outstanding exceeds max + if watcher.data.outstanding_events > M.config.filesystem_watchers.max_outstanding_events then + notify.error(string.format( + "Observed %d consecutive file system events with interval < %dms, exceeding filesystem_watchers.max_events=%s. Disabling watcher for directory '%s'. Consider adding this directory to filesystem_watchers.ignore_dirs", + watcher.data.outstanding_events, + M.config.filesystem_watchers.max_outstanding_events, + M.config.filesystem_watchers.debounce_delay, + node.absolute_path + )) + node:destroy_watcher() + end + utils.debounce(watcher.data.context, M.config.filesystem_watchers.debounce_delay, function() if watcher.destroyed then return end + + -- event has been handled + watcher.data.outstanding_events = 0 + if node.link_to then log.line("watcher", "node event executing refresh '%s' -> '%s'", node.link_to, node.absolute_path) else @@ -87,7 +108,8 @@ function M.create_watcher(node) path = path, callback = callback, data = { - context = "explorer:watch:" .. path .. ":" .. M.uid + context = "explorer:watch:" .. path .. ":" .. M.uid, + outstanding_events = 0, -- unprocessed events that have not been debounced } }) end diff --git a/lua/nvim-tree/node/directory.lua b/lua/nvim-tree/node/directory.lua index 0965e4ab9fc..540299c578f 100644 --- a/lua/nvim-tree/node/directory.lua +++ b/lua/nvim-tree/node/directory.lua @@ -36,10 +36,7 @@ function DirectoryNode:new(args) end function DirectoryNode:destroy() - if self.watcher then - self.watcher:destroy() - self.watcher = nil - end + self:destroy_watcher() if self.nodes then for _, node in pairs(self.nodes) do @@ -50,6 +47,14 @@ function DirectoryNode:destroy() Node.destroy(self) end +---Halt and remove the watcher for this node +function DirectoryNode:destroy_watcher() + if self.watcher then + self.watcher:destroy() + self.watcher = nil + end +end + ---Update the git_status of the directory ---@param parent_ignored boolean ---@param project GitProject?