mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
fix(watch): ignore nonexistent paths (ENOENT)
Problem: The `_watch.watch()` strategy may fail if the given path does not exist: …/vim/_watch.lua:101: ENOENT: no such file or directory stack traceback: [C]: in function 'assert' …/vim/_watch.lua:101: in function <…/vim/_watch.lua:61> [string "<nvim>"]:5: in main chunk - `_watch.watch()` actively asserts any error returned by `handle:start()`. - whereas `_watch.watchdirs()` just ignores the result of `root_handle:start()`. Servers may send "client/registerCapability" with "workspace/didChangeWatchedFiles" item(s) (`baseUri`) which do not actually exist on the filesystem: https://github.com/neovim/neovim/issues/28058#issuecomment-2189929424 { method = "client/registerCapability", params = { registrations = { { method = "workspace/didChangeWatchedFiles", registerOptions = { watchers = { { globPattern = { baseUri = "file:///Users/does/not/exist", pattern = "**/*.{ts,js,mts,mjs,cjs,cts,json,svelte}" } }, ... } Solution: - Remove the assert in `_watch.watch()`. - Show a once-only message for both cases. - More detailed logging is blocked until we have `nvim_log` / `vim.log`. fix #28058
This commit is contained in:
parent
391c5084d1
commit
9e23b4e185
@ -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?
|
||||
|
@ -21,16 +21,72 @@ describe('vim._watch', function()
|
||||
end)
|
||||
|
||||
local function run(watchfunc)
|
||||
it('detects file changes (watchfunc=' .. watchfunc .. '())', 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'
|
||||
-- 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, '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')
|
||||
@ -41,10 +97,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 +119,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 +131,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)
|
||||
|
Loading…
Reference in New Issue
Block a user