feat: Add configurable symlink following and depth limit for file watchers#4988
Open
feat: Add configurable symlink following and depth limit for file watchers#4988
Conversation
- Introduce lsp-file-watch-follow-symlinks to control symlink handling - Add lsp-file-watch-max-depth to limit recursion during file watching - Update docs and changelog for new symlink and depth options - Change default: symlinks outside workspace root are not followed - Extend and revise tests for symlink, depth, and error handling BREAKING CHANGE: Symlinks pointing outside the workspace root are no longer followed by default, preventing Emacs freezes. Set lsp-file-watch-follow-symlinks to t to restore previous behavior.
75b03fd to
3c72282
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lsp-file-watch-follow-symlinks(nil/'within-root/t) to control whether symlinks are followed during file watching. Default'within-rootfollows symlinks only when the resolved target is inside the workspace root, preventing Emacs freezes caused by symlinks to large external directories (e.g.,/usr/include).lsp-file-watch-max-depth(default100) to cap recursion depth during watcher setup, preventing stack overflow in deeply nested directory structures.lsp--all-watchable-directoriesinto a policy-dispatched function with cycle detection via a visited hash table keyed on truenames.lsp--folder-watch-callback) to resolve event paths to truenames before file enumeration, ensuring thewithin-rootfilter compares paths in the same namespace.lsp-watchroot-directoryat the registration site (lsp--server-register-capability), eliminating redundant re-resolution in the callback.nconcwithappendinlsp--watchable-directories-recursefor long-term safety.Breaking change
Symlinks pointing outside the workspace root are no longer followed by default. To restore previous behavior:
Additionally,
lsp-watch-root-directorynow always returns a truename-resolved path (previously it could return a raw path with unresolved symlink components).Motivation
Implementation details
Callback path-type consistency
The runtime callback
lsp--folder-watch-callbackenumerates files in newly-created directories viadirectory-files-recursively. Under thewithin-rootpolicy, these file paths are filtered usingstring-prefix-pagainst the workspace root.Previously,
directory-files-recursivelywas called on the raw event path (file-name), which could contain unresolved symlink segments. The filter compared these raw paths against a truename-resolved root — a namespace mismatch that could silently drop legitimate file events when the workspace was accessed through a symlink alias.The fix resolves
file-nameto its truename before enumeration, ensuring all paths share the same resolved prefix.Truename storage in
lsp-watchlsp--server-register-capabilitypreviously stored the raw folder path inlsp-watch :root-directory. The callback compensated by calling(file-truename (lsp-watch-root-directory watch))on every invocation. Now the truename is stored at construction time, and the callback reads it directly.Note: The
created-watcheshash table key remains the raw folder path (intentional — it must matchfolder->serverskey convention forcl-set-differenceandlsp--cleanup-hanging-watches).Test plan
lsp-watch-root-foldertruename storage verificationfile-in-directory-preplaced withstring-prefix-pon pre-resolved truenames