aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/_watch.lua
Commit message (Collapse)AuthorAge
* refactor(watch): extract watchdirs into a classLewis Russell4 days
| | | | | | | | | | | | Problem: watchdirs() keeps its state in large nested callbacks in _watch.lua, which makes its directory tracking logic harder to change. Solution: Move the backend into its own module and store its state in a WatchDirs instance. Split setup, event handling and cleanup into methods. Extend the existing test to cover cancellation while a directory stat is pending. AI-assisted
* perf(watch): skip directories outside include glob prefixesLewis Russell2026-10-05
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: watchdirs() scans and watches every directory that is not excluded, even when a glob only matches files in one subtree. Some language servers register absolute dependency filenames separately. Neovim uses the workspace root for these string globs, so each extra registration can scan and allocate libuv handles across the whole tree. Solution: Keep each glob's literal directory prefix with its compiled matcher. A directory must be allowed by both the include filter and at least one watch rule. Skip unrelated subtrees, including new directories created later. Keep ancestors watched so matching directories can still be found. Report events only when they match the full patterns. For example, watching /project for /project/src/**/*.lua only needs the src tree and /project itself: /project/ watched |-- src/ watched | `-- lib/ watched |-- tests/ skipped | `-- fixtures/ skipped `-- vendor/ skipped `-- pkg/ skipped With no exclusions, this uses three watches instead of seven. The gain can be large, but depends on the server's patterns and the workspace layout. Separate watches for absolute dependency filenames can skip most directories. For example, a test workspace has 213 directories, including 201 under build/. The two dependency files in this example are under packages/ and styles/, so their watches can skip build/. Before this change, each watch starts at the workspace root, scans all 213 directories and creates 213 libuv handles. With this change: - Haskell Language Server: a separate watch for /project/packages/demo/demo.cabal watches four directories: the project root, packages/, demo/ and its src/ child. This cuts that watch's scans and handles by 98%. With one broad source watch and this dependency watch, the total falls by 49%. - Tailwind CSS v4: a separate watch for the imported /project/styles/themes/colors.css watches three directories: the project root, styles/ and themes/. This cuts that watch's scans and handles by 99%. With two broad watches and this dependency watch, the total falls by 33%. The broad watches still cover all 213 directories. The saving comes from avoiding repeated scans and extra handles for each dependency watch. Most servers with watch registrations in the survey do not benefit. Their patterns match throughout the watch root, or their watches already start at the relevant directory. In both cases, the glob prefix cannot exclude more directories. AI-assisted
* fix(lua): enable param-type-mismatch diagnosticLewis Russell2026-09-28
| | | | | | | | | Problem: Argument type mismatches are excluded from EmmyLua checks. Solution: Correct runtime annotations and calls, document local checker workarounds, and enable the diagnostic. AI-assisted
* fix(lua): enable assignment type checking in runtime codeLewis Russell2026-09-25
| | | | | | | | | | | Enable EmmyLua's assign-type-mismatch diagnostic after correcting the runtime annotations and assignments it flags. Correct option, tuple, and return types, and use separate locals when a value changes representation. Add guards and narrow casts where needed so the runtime passes the stricter check. Regenerate the affected docs. AI-assisted
* fix(lua): enable nil-safety diagnostics in runtime codeLewis Russell2026-09-25
| | | | | | | | | | | | Enable EmmyLua's need-check-nil diagnostic and resolve nullable values through more precise types, explicit invariants, and guards for missing runtime state. Keep analyzer-specific workarounds local and update the affected API documentation. Exclude the bundled re.lua from analysis so its nil-check warnings do not fail CI. AI-assisted
* perf(lua): share compatible file watchersLewis Russell2026-09-24
| | | | | | | | | | | | | | | | | | Problem: LSP servers can register identical file watchers before removing old registrations. Each registration creates another watcher tree, making setup and teardown expensive in large workspaces. Solution: Move glob compilation into vim._watch and accept rules for multiple roots alongside existing single-path and LPeg inputs. Share compatible backends until their last subscription is cancelled. Ref: #23291 AI-assisted
* feat(watch): add on_error callbackLewis Russell2026-09-09
| | | | | | | | | | | | | | Problem: Watcher failures are reported inconsistently, and callers cannot detect when a watcher is no longer usable. Solution: Route startup and event failures and unexpected inotifywait exits through an optional on_error callback. Log to nvim-watch.log by default. Ignore disappearing child directories and exits caused by cancellation. Let LSP log failures and notify once per message, using INFO for missing roots and ERROR otherwise, without interrupting registration. AI-assisted
* build: enable more EmmyLua checksLewis Russell2026-09-08
| | | | | | | | | | | | | | | | - call-non-callable - missing-fields - redefined-label - redefined-local - undefined-field - unreachable-code Promote redefined-local and unreachable-code to warnings. Fix DOS line-ending handling for multiline semantic tokens, and support intersection types in the help parser. AI-assisted
* perf(watch): prune excluded subtrees during setupLewis Russell2026-09-07
| | | | | | | | | | | | | | | | | | | | | | | | | | Problem: The LSP client excludes paths such as Git objects and installed dependencies from file watching. However, watchdirs() still traverses these subtrees before filtering which directories receive watchers, paying the filesystem and Lua traversal cost for excluded content. Solution: Prune excluded subdirectories during the initial walk, and document that excluding a directory also excludes its descendants. Using the LSP client's existing **/node_modules/*/** exclusion on a synthetic macOS tree with 100 installed packages and 4,000 files: Before After Directory scans 503 103 Entries inspected 4502 202 Directory watchers 103 103 The measurements use real filesystem calls and watcher handles, with identical watcher paths for this exclusion. Ref: #23291 AI-assisted
* feat(fswatch): report filewatchers in :checkhealthJustin M. Keyes2026-05-20
|
* fix(watch): invalid joined path #37973Oleksandr Chekhovskyi2026-02-23
| | | | | | | | | | | | | Problem: When vim._watch.watch() is used to watch a single file, libuv returns the basename as the filename argument in the callback. The code joins this with the watched path, producing a nonsensical path like "/path/to/file.lua/file.lua", which causes ENOTDIR errors on subsequent fs_stat calls. Solution: Check whether the watched path is a directory before joining the filename. When watching a file, ignore the filename from libuv and use the watched path directly.
* fix(lua): don't leak timer when vim._watch.watch() fails (#35768)zeertzjq2025-09-15
| | | | | | | | This fixes the following warning in tests with ASAN or TSAN: -------- Running tests from test/functional/lua/watch_spec.lua RUN T4253 vim._watch watch() ignores nonexistent paths: 29.00 ms OK nvim took 2006 milliseconds to exit after last test This indicates a likely problem with the test even if it passed!
* feat(vim.validate): improve fast form and deprecate spec formLewis Russell2024-10-21
| | | | | | | | | | | | | | Problem: `vim.validate()` takes two forms when it only needs one. Solution: - Teach the fast form all the features of the spec form. - Deprecate the spec form. - General optimizations for both forms. - Add a `message` argument which can be used alongside or in place of the `optional` argument.
* perf(validate): use lighter versionLewis Russell2024-10-17
| | | | | - Also fix `vim.validate()` for PUC Lua when showing errors for values that aren't string or number.
* fix(watch): ignore nonexistent paths (ENOENT)Justin M. Keyes2024-10-02
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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
* fix(watch): exclude .git when using inotifywait (#29914)Manuel2024-08-01
| | | | | | inotifywait man page specifies: The file must be specified with a relative or absolute path according to whether a relative or absolute path is given for watched directories. So it would only work this way in case the path is relative (which at least for gopls it is not)
* feat(lsp): drop fswatch, use inotifywait (#29374)Andreas Schneider2024-07-06
| | | | | | | | | | | | | | 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>
* perf(lsp): only joinpath for dirs in watchdirsMathias Fussenegger2024-05-14
| | | | | | | | | | | | | | | | | | | | | | | | Doesn't have a huge impact, but showed up in profile output using `require("jit.p").start("i1", "/tmp/profile")` before: 31% joinpath 25% fs.lua:0 13% normalize 13% skip 8% _watchfunc 5% gsplit 3% spairs after: 34% skip 29% fs.lua:0 12% joinpath 7% normalize 5% _watchfunc 5% spairs
* fix: move fswatch linux check inside of vim.schedule (#27824)Tomas Slusny2024-03-12
| | | | | | Fixes issue reported in the original PR: https://github.com/neovim/neovim/pull/27810 Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
* docs: adjust fswatch overflow message to mention docs with infoTomas Slusny2024-03-11
| | | | | | | | | - Add :h fswatch-limitations that notifies user about default inotify limitations on linux and how to adjust them - Check for Event queue overflow message from fswatch and refer user to new documentation Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
* fix(fswatch): --latency is locale dependentOscar Creator2024-03-10
|
* feat(lsp): report fswatch errorsLewis Russell2024-03-06
| | | | | | Resolves #27713 Co-authored-by: Tomasz N <przepompownia@users.noreply.github.com>
* feat(lsp): add fswatch watchfunc backendLewis Russell2024-03-01
| | | | | | | | | | | Problem: vim._watch.watchdirs has terrible performance. Solution: - On linux use fswatch as a watcher backend if available. - Add File watcher section to health:vim.lsp. Warn if watchfunc is libuv-poll.
* refactor(watch): simplify filechanges processingLewis Russell2024-03-01
|
* refactor(watch): general tidy upLewis Russell2024-03-01
| | | | | | - Rename watch.poll to watch.watchdirs - Unify how include and exclude is applied - Improve type hints
* refactor: fix luals warningsdundargoc2023-12-30
|
* perf(lsp): use async fs_stat for file watching on linux (#26123)Mathias Fußenegger2023-11-21
|
* perf(lsp): replace file polling on linux with per dir watcher (#26108)Mathias Fußenegger2023-11-19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Should help with https://github.com/neovim/neovim/issues/23291 On linux `new_fs_event` doesn't support recursive watching, but we can still use it to watch folders. The downside of this approach is that we may end up sending some false `Deleted` events. For example, if you save a file named `foo` there will be a intermediate `foo~` due to the save mechanism of neovim. The events we get from vim.uv in that case are: - rename: foo~ - rename: foo~ - rename: foo - rename: foo - change: foo - change: foo The mechanism in this PR uses a debounce to reduce this to: - deleted: foo~ - changed: foo `foo~` will be the false positive. I suspect that for the LSP case this is good enough. If not, we may need to follow up on this and keep a table in memory that tracks available files.
* fix(lua): improve annotations for stricter luals diagnostics (#24609)Christian Clason2023-08-09
| | | | | | | | | | | | | | | Problem: luals returns stricter diagnostics with bundled luarc.json Solution: Improve some function and type annotations: * use recognized uv.* types * disable diagnostic for global `vim` in shared.lua * docs: don't start comment lines with taglink (otherwise LuaLS will interpret it as a type) * add type alias for lpeg pattern * fix return annotation for `vim.secure.trust` * rename local Range object in vim.version (shadows `Range` in vim.treesitter) * fix some "missing fields" warnings * add missing required fields for test functions in eval.lua * rename lsp meta files for consistency
* docs(lua): more improvements (#24387)Lewis Russell2023-07-18
| | | | | | | | | | | | | | | | | * docs(lua): teach lua2dox how to table * docs(lua): teach gen_vimdoc.py about local functions No more need to mark local functions with @private * docs(lua): mention @nodoc and @meta in dev-lua-doc * fixup! Co-authored-by: Justin M. Keyes <justinkz@gmail.com> --------- Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* fix(lint): lint warnings #24226Raphael2023-07-10
|
* perf(lsp): reduce polling handles for workspace/didChangeWatchedFiles (#23500)Jon Huhn2023-06-14
| | | Co-authored-by: Lewis Russell <lewis6991@gmail.com>
* feat(lua): rename vim.loop -> vim.uv (#22846)Lewis Russell2023-06-03
|
* fix(watchfiles): skip Created events when poll starts (#23139)Jon Huhn2023-04-17
|
* feat(lsp): implement workspace/didChangeWatchedFiles (#22405)Jon Huhn2023-03-05
|
* Revert "feat(lsp): implement workspace/didChangeWatchedFiles (#21293)"Mathias Fussenegger2023-02-25
| | | | | | This reverts commit 5732aa706c639b3d775573d91d1139f24624629c. Causes editor to freeze in projects with many watcher registrations
* feat(lsp): implement workspace/didChangeWatchedFiles (#21293)Jon Huhn2023-02-25