mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 12:45:17 -07:00
feat(lsp): drop fswatch, use inotifywait (#29374)
This patch replaces fswatch with inotifywait from inotify-toools: https://github.com/inotify-tools/inotify-tools fswatch takes ~1min to set up recursively for the Samba source code directory. inotifywait needs less than a second to do the same thing. https://github.com/emcrisostomo/fswatch/issues/321 Also it fswatch seems to be unmaintained in the meantime. Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
parent
91e5dcae3d
commit
55e4301036
2
.github/scripts/install_deps.sh
vendored
2
.github/scripts/install_deps.sh
vendored
@ -30,7 +30,7 @@ if [[ $os == Linux ]]; then
|
||||
fi
|
||||
|
||||
if [[ -n $TEST ]]; then
|
||||
sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb fswatch
|
||||
sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb inotify-tools
|
||||
|
||||
# Use default CC to avoid compilation problems when installing Python modules
|
||||
CC=cc python3 -m pip -q install --user --upgrade pynvim
|
||||
|
@ -543,16 +543,19 @@ Example: File-change detection *watch-file*
|
||||
vim.api.nvim_command(
|
||||
"command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))")
|
||||
<
|
||||
*fswatch-limitations*
|
||||
When on Linux and using fswatch, you may need to increase the maximum number
|
||||
of `inotify` watches and queued events as the default limit can be too low. To
|
||||
increase the limit, run: >sh
|
||||
sysctl fs.inotify.max_user_watches=100000
|
||||
sysctl fs.inotify.max_queued_events=100000
|
||||
*inotify-limitations*
|
||||
When on Linux you may need to increase the maximum number of `inotify` watches
|
||||
and queued events as the default limit can be too low. To increase the limit,
|
||||
run: >sh
|
||||
sysctl fs.inotify.max_user_watches=494462
|
||||
<
|
||||
This will increase the limit to 100000 watches and queued events. These lines
|
||||
This will increase the limit to 494462 watches and queued events. These lines
|
||||
can be added to `/etc/sysctl.conf` to make the changes persistent.
|
||||
|
||||
Note that each watch is a structure in the Kernel, thus available memory is
|
||||
also a bottleneck for using inotify. In fact, a watch can take up to 1KB of
|
||||
space. This means a million watches could result in 1GB of extra RAM usage.
|
||||
|
||||
Example: TCP echo-server *tcp-server*
|
||||
1. Save this code to a file.
|
||||
2. Execute it with ":luafile %".
|
||||
|
@ -227,11 +227,12 @@ end
|
||||
--- @param data string
|
||||
--- @param opts vim._watch.Opts?
|
||||
--- @param callback vim._watch.Callback
|
||||
local function fswatch_output_handler(data, opts, callback)
|
||||
local function on_inotifywait_output(data, opts, callback)
|
||||
local d = vim.split(data, '%s+')
|
||||
|
||||
-- only consider the last reported event
|
||||
local fullpath, event = d[1], d[#d]
|
||||
local path, event, file = d[1], d[2], d[#d]
|
||||
local fullpath = vim.fs.joinpath(path, file)
|
||||
|
||||
if skip(fullpath, opts) then
|
||||
return
|
||||
@ -240,20 +241,16 @@ local function fswatch_output_handler(data, opts, callback)
|
||||
--- @type integer
|
||||
local change_type
|
||||
|
||||
if event == 'Created' then
|
||||
if event == 'CREATE' then
|
||||
change_type = M.FileChangeType.Created
|
||||
elseif event == 'Removed' then
|
||||
elseif event == 'DELETE' then
|
||||
change_type = M.FileChangeType.Deleted
|
||||
elseif event == 'Updated' then
|
||||
elseif event == 'MODIFY' then
|
||||
change_type = M.FileChangeType.Changed
|
||||
elseif event == 'Renamed' then
|
||||
local _, staterr, staterrname = uv.fs_stat(fullpath)
|
||||
if staterrname == 'ENOENT' then
|
||||
change_type = M.FileChangeType.Deleted
|
||||
else
|
||||
assert(not staterr, staterr)
|
||||
change_type = M.FileChangeType.Created
|
||||
end
|
||||
elseif event == 'MOVED_FROM' then
|
||||
change_type = M.FileChangeType.Deleted
|
||||
elseif event == 'MOVED_TO' then
|
||||
change_type = M.FileChangeType.Created
|
||||
end
|
||||
|
||||
if change_type then
|
||||
@ -265,24 +262,22 @@ end
|
||||
--- @param opts vim._watch.Opts?
|
||||
--- @param callback vim._watch.Callback Callback for new events
|
||||
--- @return fun() cancel Stops the watcher
|
||||
function M.fswatch(path, opts, callback)
|
||||
-- debounce isn't the same as latency but close enough
|
||||
local latency = 0.5 -- seconds
|
||||
if opts and opts.debounce then
|
||||
latency = opts.debounce / 1000
|
||||
end
|
||||
|
||||
function M.inotify(path, opts, callback)
|
||||
local obj = vim.system({
|
||||
'fswatch',
|
||||
'--event=Created',
|
||||
'--event=Removed',
|
||||
'--event=Updated',
|
||||
'--event=Renamed',
|
||||
'--event-flags',
|
||||
'inotifywait',
|
||||
'--quiet', -- suppress startup messages
|
||||
'--no-dereference', -- don't follow symlinks
|
||||
'--monitor', -- keep listening for events forever
|
||||
'--recursive',
|
||||
'--latency=' .. tostring(latency),
|
||||
'--exclude',
|
||||
'/.git/',
|
||||
'--event',
|
||||
'create',
|
||||
'--event',
|
||||
'delete',
|
||||
'--event',
|
||||
'modify',
|
||||
'--event',
|
||||
'move',
|
||||
'@.git', -- ignore git directory
|
||||
path,
|
||||
}, {
|
||||
stderr = function(err, data)
|
||||
@ -292,11 +287,11 @@ function M.fswatch(path, opts, callback)
|
||||
|
||||
if data and #vim.trim(data) > 0 then
|
||||
vim.schedule(function()
|
||||
if vim.fn.has('linux') == 1 and vim.startswith(data, 'Event queue overflow') then
|
||||
data = 'inotify(7) limit reached, see :h fswatch-limitations for more info.'
|
||||
if vim.fn.has('linux') == 1 and vim.startswith(data, 'Failed to watch') then
|
||||
data = 'inotify(7) limit reached, see :h inotify-limitations for more info.'
|
||||
end
|
||||
|
||||
vim.notify('fswatch: ' .. data, vim.log.levels.ERROR)
|
||||
vim.notify('inotify: ' .. data, vim.log.levels.ERROR)
|
||||
end)
|
||||
end
|
||||
end,
|
||||
@ -306,7 +301,7 @@ function M.fswatch(path, opts, callback)
|
||||
end
|
||||
|
||||
for line in vim.gsplit(data or '', '\n', { plain = true, trimempty = true }) do
|
||||
fswatch_output_handler(line, opts, callback)
|
||||
on_inotifywait_output(line, opts, callback)
|
||||
end
|
||||
end,
|
||||
-- --latency is locale dependent but tostring() isn't and will always have '.' as decimal point.
|
||||
|
@ -9,8 +9,8 @@ local M = {}
|
||||
|
||||
if vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1 then
|
||||
M._watchfunc = watch.watch
|
||||
elseif vim.fn.executable('fswatch') == 1 then
|
||||
M._watchfunc = watch.fswatch
|
||||
elseif vim.fn.executable('inotifywait') == 1 then
|
||||
M._watchfunc = watch.inotify
|
||||
else
|
||||
M._watchfunc = watch.watchdirs
|
||||
end
|
||||
|
@ -90,8 +90,8 @@ local function check_watcher()
|
||||
watchfunc_name = 'libuv-watch'
|
||||
elseif watchfunc == vim._watch.watchdirs then
|
||||
watchfunc_name = 'libuv-watchdirs'
|
||||
elseif watchfunc == vim._watch.fswatch then
|
||||
watchfunc_name = 'fswatch'
|
||||
elseif watchfunc == vim._watch.inotifywait then
|
||||
watchfunc_name = 'inotifywait'
|
||||
else
|
||||
local nm = debug.getinfo(watchfunc, 'S').source
|
||||
watchfunc_name = string.format('Custom (%s)', nm)
|
||||
@ -99,7 +99,7 @@ local function check_watcher()
|
||||
|
||||
report_info('File watch backend: ' .. watchfunc_name)
|
||||
if watchfunc_name == 'libuv-watchdirs' then
|
||||
report_warn('libuv-watchdirs has known performance issues. Consider installing fswatch.')
|
||||
report_warn('libuv-watchdirs has known performance issues. Consider installing inotify-tools.')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,10 +23,13 @@ describe('vim._watch', function()
|
||||
|
||||
local function run(watchfunc)
|
||||
it('detects file changes (watchfunc=' .. watchfunc .. '())', function()
|
||||
if watchfunc == 'fswatch' then
|
||||
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('fswatch') == 0, 'fswatch not installed and not on CI')
|
||||
skip(
|
||||
not is_ci() and n.fn.executable('inotifywait') == 0,
|
||||
'inotify-tools not installed and not on CI'
|
||||
)
|
||||
end
|
||||
|
||||
if watchfunc == 'watch' then
|
||||
@ -123,5 +126,5 @@ describe('vim._watch', function()
|
||||
|
||||
run('watch')
|
||||
run('watchdirs')
|
||||
run('fswatch')
|
||||
run('inotify')
|
||||
end)
|
||||
|
@ -5128,12 +5128,12 @@ describe('LSP', function()
|
||||
it(
|
||||
string.format('sends notifications when files change (watchfunc=%s)', watchfunc),
|
||||
function()
|
||||
if watchfunc == 'fswatch' then
|
||||
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 fn.executable('fswatch') == 0,
|
||||
'fswatch not installed and not on CI'
|
||||
not is_ci() and fn.executable('inotifywait') == 0,
|
||||
'inotify-tools not installed and not on CI'
|
||||
)
|
||||
end
|
||||
|
||||
@ -5265,7 +5265,7 @@ describe('LSP', function()
|
||||
|
||||
test_filechanges('watch')
|
||||
test_filechanges('watchdirs')
|
||||
test_filechanges('fswatch')
|
||||
test_filechanges('inotify')
|
||||
|
||||
it('correctly registers and unregisters', function()
|
||||
local root_dir = '/some_dir'
|
||||
|
Loading…
Reference in New Issue
Block a user