Merge #30595 from justinmk/fixwatch

This commit is contained in:
Justin M. Keyes 2024-10-02 08:05:03 -07:00 committed by GitHub
commit d3b4772ddc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 91 additions and 31 deletions

View File

@ -30,6 +30,8 @@ M.FileChangeType = {
--- @class vim._watch.watch.Opts : vim._watch.Opts
--- @field uvflags? uv.fs_event_start.flags
--- Decides if `path` should be skipped.
---
--- @param path string
--- @param opts? vim._watch.Opts
local function skip(path, opts)
@ -69,7 +71,7 @@ function M.watch(path, opts, callback)
local uvflags = opts and opts.uvflags or {}
local handle = assert(uv.new_fs_event())
local _, start_err = handle:start(path, uvflags, function(err, filename, events)
local _, start_err, start_errname = handle:start(path, uvflags, function(err, filename, events)
assert(not err, err)
local fullpath = path
if filename then
@ -96,7 +98,15 @@ function M.watch(path, opts, callback)
callback(fullpath, change_type)
end)
assert(not start_err, start_err)
if start_err then
if start_errname == 'ENOENT' then
-- Server may send "workspace/didChangeWatchedFiles" with nonexistent `baseUri` path.
-- This is mostly a placeholder until we have `nvim_log` API.
vim.notify_once(('watch.watch: %s'):format(start_err), vim.log.levels.INFO)
end
-- TODO(justinmk): log important errors once we have `nvim_log` API.
return function() end
end
return function()
local _, stop_err = handle:stop()
@ -193,7 +203,18 @@ function M.watchdirs(path, opts, callback)
local root_handle = assert(uv.new_fs_event())
handles[path] = root_handle
root_handle:start(path, {}, create_on_change(path))
local _, start_err, start_errname = root_handle:start(path, {}, create_on_change(path))
if start_err then
if start_errname == 'ENOENT' then
-- Server may send "workspace/didChangeWatchedFiles" with nonexistent `baseUri` path.
-- This is mostly a placeholder until we have `nvim_log` API.
vim.notify_once(('watch.watchdirs: %s'):format(start_err), vim.log.levels.INFO)
end
-- TODO(justinmk): log important errors once we have `nvim_log` API.
-- Continue. vim.fs.dir() will return nothing, so the code below is harmless.
end
--- "640K ought to be enough for anyone"
--- Who has folders this deep?

View File

@ -21,19 +21,78 @@ describe('vim._watch', function()
end)
local function run(watchfunc)
it('detects file changes (watchfunc=' .. watchfunc .. '())', function()
-- Monkey-patches vim.notify_once so we can "spy" on it.
local function spy_notify_once()
exec_lua [[
_G.__notify_once_msgs = {}
vim.notify_once = (function(overridden)
return function(msg, level, opts)
table.insert(_G.__notify_once_msgs, msg)
return overridden(msg, level, opts)
end
end)(vim.notify_once)
]]
end
local function last_notify_once_msg()
return exec_lua 'return _G.__notify_once_msgs[#_G.__notify_once_msgs]'
end
local function do_watch(root_dir, watchfunc_)
exec_lua(
[[
local root_dir, watchfunc = ...
_G.events = {}
_G.stop_watch = vim._watch[watchfunc](root_dir, {
debounce = 100,
include_pattern = vim.lpeg.P(root_dir) * vim.lpeg.P("/file") ^ -1,
exclude_pattern = vim.lpeg.P(root_dir .. '/file.unwatched'),
}, function(path, change_type)
table.insert(_G.events, { path = path, change_type = change_type })
end)
]],
root_dir,
watchfunc_
)
end
it(watchfunc .. '() ignores nonexistent paths', function()
if watchfunc == 'inotify' then
skip(n.fn.executable('inotifywait') == 0, 'inotifywait not found')
skip(is_os('bsd'), 'inotifywait on bsd CI seems to expect path to exist?')
end
local msg = ('watch.%s: ENOENT: no such file or directory'):format(watchfunc)
spy_notify_once()
do_watch('/i am /very/funny.go', watchfunc)
if watchfunc ~= 'inotify' then -- watch.inotify() doesn't (currently) call vim.notify_once.
t.retry(nil, 2000, function()
t.eq(msg, last_notify_once_msg())
end)
end
eq(0, exec_lua [[return #_G.events]])
exec_lua [[_G.stop_watch()]]
end)
it(watchfunc .. '() detects file changes', function()
if watchfunc == 'inotify' then
skip(is_os('win'), 'not supported on windows')
skip(is_os('mac'), 'flaky test on mac')
skip(
not is_ci() and n.fn.executable('inotifywait') == 0,
'inotify-tools not installed and not on CI'
)
skip(not is_ci() and n.fn.executable('inotifywait') == 0, 'inotifywait not found')
end
-- Note: because this is not `elseif`, BSD is skipped for *all* cases...?
if watchfunc == 'watch' then
skip(is_os('mac'), 'flaky test on mac')
skip(is_os('bsd'), 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38')
elseif watchfunc == 'watchdirs' and is_os('mac') then
-- Bump this (or fix the bug) if CI continues to fail in future versions of macos CI.
skip(is_ci() and vim.uv.os_uname().release == '23.6.0', 'weird failure for macOS arm 14 CI')
else
skip(
is_os('bsd'),
@ -41,10 +100,8 @@ describe('vim._watch', function()
)
end
local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
local expected_events = 0
--- Waits for a new event, or fails if no events are triggered.
local function wait_for_event()
expected_events = expected_events + 1
exec_lua(
@ -65,26 +122,11 @@ describe('vim._watch', function()
)
end
local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
local unwatched_path = root_dir .. '/file.unwatched'
local watched_path = root_dir .. '/file'
exec_lua(
[[
local root_dir, watchfunc = ...
_G.events = {}
_G.stop_watch = vim._watch[watchfunc](root_dir, {
debounce = 100,
include_pattern = vim.lpeg.P(root_dir) * vim.lpeg.P("/file") ^ -1,
exclude_pattern = vim.lpeg.P(root_dir .. '/file.unwatched'),
}, function(path, change_type)
table.insert(_G.events, { path = path, change_type = change_type })
end)
]],
root_dir,
watchfunc
)
do_watch(root_dir, watchfunc)
if watchfunc ~= 'watch' then
vim.uv.sleep(200)
@ -92,16 +134,13 @@ describe('vim._watch', function()
touch(watched_path)
touch(unwatched_path)
wait_for_event()
os.remove(watched_path)
os.remove(unwatched_path)
wait_for_event()
exec_lua [[_G.stop_watch()]]
-- No events should come through anymore
vim.uv.sleep(100)