Skip to content

Commit 2e2916f

Browse files
author
jghauser
committed
feat(fs-watcher): add debounce and deferred handling
1 parent f9d1e72 commit 2e2916f

File tree

1 file changed

+58
-45
lines changed

1 file changed

+58
-45
lines changed

lua/papis/fs-watcher.lua

+58-45
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ local file_read_timer
2727
local autocmd_id
2828
local handles = {}
2929
local fs_watching_stopped = false
30+
local event_timestamps = {}
3031

3132
---Uses libuv to start file system watchers
3233
---@param path string #The path to watch
@@ -78,55 +79,67 @@ local function init_fs_watcher(dir_to_watch, is_library_root)
7879
local info_path
7980
local do_unwatch = false
8081
local do_update = true
81-
if is_library_root then
82-
log.debug("Filesystem event in the library root directory")
83-
entry_dir = Path(dir_to_watch, filename)
84-
info_path = entry_dir / info_name
85-
local entry_dir_str = tostring(entry_dir)
86-
local info_path_str = tostring(info_path)
87-
if entry_dir:exists() and entry_dir:is_dir() then
88-
log.debug(string.format("Filesystem event: path '%s' added", entry_dir_str))
89-
init_fs_watcher(entry_dir_str)
90-
if info_path:exists() then
91-
mtime = fs_stat(info_path_str).mtime.sec
82+
local current_time = uv.hrtime() / 1e6 -- get current time in milliseconds
83+
local last_event_time = event_timestamps[filename]
84+
85+
if last_event_time and current_time - last_event_time < 200 then
86+
log.debug("Debouncing: skipping filesystem event")
87+
-- If the last event for this file was less than 200ms ago, discard this event
88+
return
89+
end
90+
91+
-- Update the timestamp for this file
92+
event_timestamps[filename] = current_time
93+
94+
vim.defer_fn(function()
95+
if is_library_root then
96+
log.debug("Filesystem event in the library root directory")
97+
entry_dir = Path(dir_to_watch, filename)
98+
info_path = entry_dir / info_name
99+
local entry_dir_str = tostring(entry_dir)
100+
local info_path_str = tostring(info_path)
101+
if entry_dir:exists() and entry_dir:is_dir() then
102+
log.debug(string.format("Filesystem event: path '%s' added", entry_dir_str))
103+
init_fs_watcher(entry_dir_str)
104+
if info_path:exists() then
105+
mtime = fs_stat(info_path_str).mtime.sec
106+
end
107+
elseif entry_dir:is_dir() then
108+
log.debug(string.format("Filesystem event: path '' removed", entry_dir_str))
109+
-- don't update here, because we'll catch it below under entry events
110+
do_update = false
111+
else
112+
-- it's a file (not a directory). ignore
113+
do_update = false
92114
end
93-
elseif entry_dir:is_dir() then
94-
log.debug(string.format("Filesystem event: path '' removed", entry_dir_str))
95-
-- don't update here, because we'll catch it below under entry events
96-
do_update = false
97115
else
98-
-- it's a file (not a directory). ignore
99-
do_update = false
100-
end
101-
else
102-
log.debug("Filesystem event in entry directory")
103-
entry_dir = Path(dir_to_watch)
104-
info_path = entry_dir / info_name
105-
local info_path_str = tostring(info_path)
106-
if info_path:exists() then
107-
-- info file exists, update with new info
108-
log.debug(string.format("Filesystem event: '%s' changed", info_path_str))
109-
mtime = fs_stat(tostring(info_path)).mtime.sec
110-
elseif not entry_dir:exists() then
111-
-- info file and entry dir don't exist. delete entry (mtime = nil) and remove watcher
112-
log.debug(string.format("Filesystem event: '%s' removed", info_path_str))
113-
do_unwatch = true
114-
else
115-
-- info file doesn't exist but entry dir does. delete entry but keep watcher
116-
log.debug(string.format("Filesystem event: '%s' removed", info_path_str))
116+
log.debug("Filesystem event in entry directory")
117+
entry_dir = Path(dir_to_watch)
118+
info_path = entry_dir / info_name
119+
local info_path_str = tostring(info_path)
120+
if info_path:exists() then
121+
-- info file exists, update with new info
122+
log.debug(string.format("Filesystem event: '%s' changed", info_path_str))
123+
mtime = fs_stat(tostring(info_path)).mtime.sec
124+
elseif not entry_dir:exists() then
125+
-- info file and entry dir don't exist. delete entry (mtime = nil) and remove watcher
126+
log.debug(string.format("Filesystem event: '%s' removed", info_path_str))
127+
do_unwatch = true
128+
else
129+
-- info file doesn't exist but entry dir does. delete entry but keep watcher
130+
log.debug(string.format("Filesystem event: '%s' removed", info_path_str))
131+
end
117132
end
118-
end
119-
if do_update then
120-
local info_path_str = tostring(info_path)
121-
log.debug("Update database for this fs event...")
122-
log.debug("Updating: " .. vim.inspect({ path = info_path_str, mtime = mtime }))
123-
vim.defer_fn(function()
133+
if do_update then
134+
local info_path_str = tostring(info_path)
135+
log.debug("Update database for this fs event...")
136+
log.debug("Updating: " .. vim.inspect({ path = info_path_str, mtime = mtime }))
124137
data.update_db({ path = info_path_str, mtime = mtime })
125-
end, 200)
126-
elseif do_unwatch then
127-
log.debug("Removing watcher")
128-
unwatch_cb()
129-
end
138+
elseif do_unwatch then
139+
log.debug("Removing watcher")
140+
unwatch_cb()
141+
end
142+
end, 200)
130143
end
131144

132145
-- start the file watcher

0 commit comments

Comments
 (0)