mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge #30595 from justinmk/fixwatch
This commit is contained in:
commit
d3b4772ddc
@ -30,6 +30,8 @@ M.FileChangeType = {
|
|||||||
--- @class vim._watch.watch.Opts : vim._watch.Opts
|
--- @class vim._watch.watch.Opts : vim._watch.Opts
|
||||||
--- @field uvflags? uv.fs_event_start.flags
|
--- @field uvflags? uv.fs_event_start.flags
|
||||||
|
|
||||||
|
--- Decides if `path` should be skipped.
|
||||||
|
---
|
||||||
--- @param path string
|
--- @param path string
|
||||||
--- @param opts? vim._watch.Opts
|
--- @param opts? vim._watch.Opts
|
||||||
local function skip(path, opts)
|
local function skip(path, opts)
|
||||||
@ -69,7 +71,7 @@ function M.watch(path, opts, callback)
|
|||||||
local uvflags = opts and opts.uvflags or {}
|
local uvflags = opts and opts.uvflags or {}
|
||||||
local handle = assert(uv.new_fs_event())
|
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)
|
assert(not err, err)
|
||||||
local fullpath = path
|
local fullpath = path
|
||||||
if filename then
|
if filename then
|
||||||
@ -96,7 +98,15 @@ function M.watch(path, opts, callback)
|
|||||||
callback(fullpath, change_type)
|
callback(fullpath, change_type)
|
||||||
end)
|
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()
|
return function()
|
||||||
local _, stop_err = handle:stop()
|
local _, stop_err = handle:stop()
|
||||||
@ -193,7 +203,18 @@ function M.watchdirs(path, opts, callback)
|
|||||||
|
|
||||||
local root_handle = assert(uv.new_fs_event())
|
local root_handle = assert(uv.new_fs_event())
|
||||||
handles[path] = root_handle
|
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"
|
--- "640K ought to be enough for anyone"
|
||||||
--- Who has folders this deep?
|
--- Who has folders this deep?
|
||||||
|
@ -21,19 +21,78 @@ describe('vim._watch', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
local function run(watchfunc)
|
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
|
if watchfunc == 'inotify' then
|
||||||
skip(is_os('win'), 'not supported on windows')
|
skip(is_os('win'), 'not supported on windows')
|
||||||
skip(is_os('mac'), 'flaky test on mac')
|
skip(is_os('mac'), 'flaky test on mac')
|
||||||
skip(
|
skip(not is_ci() and n.fn.executable('inotifywait') == 0, 'inotifywait not found')
|
||||||
not is_ci() and n.fn.executable('inotifywait') == 0,
|
|
||||||
'inotify-tools not installed and not on CI'
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Note: because this is not `elseif`, BSD is skipped for *all* cases...?
|
||||||
if watchfunc == 'watch' then
|
if watchfunc == 'watch' then
|
||||||
skip(is_os('mac'), 'flaky test on mac')
|
skip(is_os('mac'), 'flaky test on mac')
|
||||||
skip(is_os('bsd'), 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38')
|
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
|
else
|
||||||
skip(
|
skip(
|
||||||
is_os('bsd'),
|
is_os('bsd'),
|
||||||
@ -41,10 +100,8 @@ describe('vim._watch', function()
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
|
|
||||||
|
|
||||||
local expected_events = 0
|
local expected_events = 0
|
||||||
|
--- Waits for a new event, or fails if no events are triggered.
|
||||||
local function wait_for_event()
|
local function wait_for_event()
|
||||||
expected_events = expected_events + 1
|
expected_events = expected_events + 1
|
||||||
exec_lua(
|
exec_lua(
|
||||||
@ -65,26 +122,11 @@ describe('vim._watch', function()
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
|
||||||
local unwatched_path = root_dir .. '/file.unwatched'
|
local unwatched_path = root_dir .. '/file.unwatched'
|
||||||
local watched_path = root_dir .. '/file'
|
local watched_path = root_dir .. '/file'
|
||||||
|
|
||||||
exec_lua(
|
do_watch(root_dir, watchfunc)
|
||||||
[[
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
if watchfunc ~= 'watch' then
|
if watchfunc ~= 'watch' then
|
||||||
vim.uv.sleep(200)
|
vim.uv.sleep(200)
|
||||||
@ -92,16 +134,13 @@ describe('vim._watch', function()
|
|||||||
|
|
||||||
touch(watched_path)
|
touch(watched_path)
|
||||||
touch(unwatched_path)
|
touch(unwatched_path)
|
||||||
|
|
||||||
wait_for_event()
|
wait_for_event()
|
||||||
|
|
||||||
os.remove(watched_path)
|
os.remove(watched_path)
|
||||||
os.remove(unwatched_path)
|
os.remove(unwatched_path)
|
||||||
|
|
||||||
wait_for_event()
|
wait_for_event()
|
||||||
|
|
||||||
exec_lua [[_G.stop_watch()]]
|
exec_lua [[_G.stop_watch()]]
|
||||||
|
|
||||||
-- No events should come through anymore
|
-- No events should come through anymore
|
||||||
|
|
||||||
vim.uv.sleep(100)
|
vim.uv.sleep(100)
|
||||||
|
Loading…
Reference in New Issue
Block a user