Skip to content

Commit c07ce43

Browse files
authored
fix(#3198): add filesystem_watchers.max_events to handle runaway filesystem events on PowerShell (#3232)
* fix(#3198): add filesystem_watchers.max_outstanding_events which disables filesystem watchers on a directory when runaway events are received * fix(#3198): document filesystem_watchers.max_events * fix(#3198): document filesystem_watchers.max_events
1 parent 89d9621 commit c07ce43

File tree

5 files changed

+58
-7
lines changed

5 files changed

+58
-7
lines changed

doc/nvim-tree-lua.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,10 @@ Windows WSL and PowerShell
17071707
freeze Nvim
17081708
- Some filesystem watcher error related to permissions will not be reported
17091709

1710+
Powershell
1711+
- Observed Nvim hanging after a runaway (infinite) number of events on a
1712+
single directory. See |nvim_tree.config.filesystem_watchers| {max_events}
1713+
17101714
==============================================================================
17111715
netrw *nvim-tree-netrw*
17121716

@@ -2511,12 +2515,21 @@ Config: filesystem_watchers *nvim-tree-config-filesystem-watchers*
25112515
This may be useful when a path is not in `.gitignore` or git integration
25122516
is disabled.
25132517

2518+
After {max_events} consecutive filesystem events on a single directory
2519+
with an interval < {debounce_delay}:
2520+
• The filesystem watcher will be disabled for that directory.
2521+
• A warning notification will be shown.
2522+
• Consider adding this directory to {ignore_dirs}
2523+
25142524
Fields: ~
25152525
{enable}? (`boolean`) (default: `true`)
25162526
• {debounce_delay}? (`integer`, default: `50`) Idle milliseconds
25172527
between filesystem change and tree update.
25182528
• {ignore_dirs}? (`string[]|(fun(path: string): boolean)`, default: `{ "/.ccls-cache", "/build", "/node_modules", "/target", }`)
2519-
Disable for directories.
2529+
Disable for specific directories.
2530+
• {max_events}? (`integer`, default: `100`) Disable for a single
2531+
directory after {max_events} consecutive events
2532+
with an interval < {debounce_delay}.
25202533

25212534

25222535

@@ -2987,6 +3000,7 @@ Following is the default configuration, see |nvim_tree.config| for details. >lua
29873000
filesystem_watchers = {
29883001
enable = true,
29893002
debounce_delay = 50,
3003+
max_events = 100,
29903004
ignore_dirs = {
29913005
"/.ccls-cache",
29923006
"/build",

lua/nvim-tree.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ local DEFAULT_OPTS = { -- default-config-start
462462
filesystem_watchers = {
463463
enable = true,
464464
debounce_delay = 50,
465+
max_events = 100,
465466
ignore_dirs = {
466467
"/.ccls-cache",
467468
"/build",

lua/nvim-tree/_meta/config/filesystem_watchers.lua

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ error("Cannot require a meta file")
1212
--- - A function that is passed an absolute path and returns `true` to disable
1313
---This may be useful when a path is not in `.gitignore` or git integration is disabled.
1414
---
15+
---After {max_events} consecutive filesystem events on a single directory with an interval < {debounce_delay}:
16+
---- The filesystem watcher will be disabled for that directory.
17+
---- A warning notification will be shown.
18+
---- Consider adding this directory to {ignore_dirs}
19+
---
1520
---@class nvim_tree.config.filesystem_watchers
1621
---
1722
---(default: `true`)
@@ -21,6 +26,10 @@ error("Cannot require a meta file")
2126
---(default: `50`)
2227
---@field debounce_delay? integer
2328
---
24-
---Disable for directories.
29+
---Disable for specific directories.
2530
---(default: `{ "/.ccls-cache", "/build", "/node_modules", "/target", }`)
2631
---@field ignore_dirs? string[]|(fun(path: string): boolean)
32+
---
33+
---Disable for a single directory after {max_events} consecutive events with an interval < {debounce_delay}.
34+
---(default: `100`)
35+
---@field max_events? integer

lua/nvim-tree/explorer/watch.lua

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local log = require("nvim-tree.log")
22
local git = require("nvim-tree.git")
33
local utils = require("nvim-tree.utils")
4+
local notify = require("nvim-tree.notify")
45
local Watcher = require("nvim-tree.watcher").Watcher
56

67
local M = {
@@ -69,10 +70,30 @@ function M.create_watcher(node)
6970
---@param watcher Watcher
7071
local function callback(watcher)
7172
log.line("watcher", "node event scheduled refresh %s", watcher.data.context)
73+
74+
-- event is awaiting debouncing and handling
75+
watcher.data.outstanding_events = watcher.data.outstanding_events + 1
76+
77+
-- disable watcher when outstanding exceeds max
78+
if watcher.data.outstanding_events > M.config.filesystem_watchers.max_events then
79+
notify.error(string.format(
80+
"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",
81+
watcher.data.outstanding_events,
82+
M.config.filesystem_watchers.max_events,
83+
M.config.filesystem_watchers.debounce_delay,
84+
node.absolute_path
85+
))
86+
node:destroy_watcher()
87+
end
88+
7289
utils.debounce(watcher.data.context, M.config.filesystem_watchers.debounce_delay, function()
7390
if watcher.destroyed then
7491
return
7592
end
93+
94+
-- event has been handled
95+
watcher.data.outstanding_events = 0
96+
7697
if node.link_to then
7798
log.line("watcher", "node event executing refresh '%s' -> '%s'", node.link_to, node.absolute_path)
7899
else
@@ -87,7 +108,8 @@ function M.create_watcher(node)
87108
path = path,
88109
callback = callback,
89110
data = {
90-
context = "explorer:watch:" .. path .. ":" .. M.uid
111+
context = "explorer:watch:" .. path .. ":" .. M.uid,
112+
outstanding_events = 0, -- unprocessed events that have not been debounced
91113
}
92114
})
93115
end

lua/nvim-tree/node/directory.lua

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ function DirectoryNode:new(args)
3737
end
3838

3939
function DirectoryNode:destroy()
40-
if self.watcher then
41-
self.watcher:destroy()
42-
self.watcher = nil
43-
end
40+
self:destroy_watcher()
4441

4542
if self.nodes then
4643
for _, node in pairs(self.nodes) do
@@ -51,6 +48,14 @@ function DirectoryNode:destroy()
5148
Node.destroy(self)
5249
end
5350

51+
---Halt and remove the watcher for this node
52+
function DirectoryNode:destroy_watcher()
53+
if self.watcher then
54+
self.watcher:destroy()
55+
self.watcher = nil
56+
end
57+
end
58+
5459
---Update the git_status of the directory
5560
---@param parent_ignored boolean
5661
---@param project GitProject?

0 commit comments

Comments
 (0)