-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
File watcher implementation & LSP DidChangeWatchedFiles notification #2653
base: master
Are you sure you want to change the base?
Conversation
// A list of directory names to ignore when found in the root of | ||
// a workspace. These are directories that would otherwise generate | ||
// a very large amount of events which could cause stuttering. | ||
// i.e. `rust-analyzer` calls `cargo check` on every write | ||
// which generates a large amount of fingerprint file changes in the | ||
// `target` directory. | ||
const IGNORE: &[&str] = &["target"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... ignoring those matching some default list of build/cache directories ala Rust's target directory
I think we can just use the .gitignore (or other .ignore files) to filter relevant files/folders.
They are used anyway already for the file-picker etc.
I think there's rarely a case where no git or something a like is used...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to agree that .gitignore
is the better approach now that I've thought about it some more. Originally I imagined that could lead to some surprising behaviour in edge cases depending on what the user's chosen to add to their .gitignore
, but thinking about it now I can't really imagine many sane scenarios where something bad could happen.
For Rust and other languages that use such an integrated project/build system it should work well in the vast majority of cases, since of course cargo new
automatically sets up a repo and gitignores target
. Other languages I'm not so sure, but we can hope that the amount of cases where people would have an extremely active build directory that isn't present in .gitignore
would be comparatively low.
This would also eliminate some edge cases: like if a Rust project were configured to use a different name than target
for its build directory.
Ok(serde_json::Value::Null) | ||
} | ||
_ => Err(anyhow::anyhow!( | ||
"attempt to register unsupported capability" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should say "unregister"?
"attempt to register unsupported capability" | |
"attempt to unregister unsupported capability" |
// TODO: This could be replaced with just the filter closure, moving glob logic | ||
// to the LSP side. Not sure what's preferable. Conversely, currently filtering | ||
// which event kinds are actually wanted is done on the LSP side, which is a bit | ||
// weird. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even leaving aside moving the glob logic to the LSP side, a filter closure subsumes it anyway, right?
@kyrime Sorry for the delay on this! I've been meaning to review this for a while.
Hmm isn't this how watchman would handle this internally? A recursive watch, but a way to drop unwanted notifications very early to avoid sending it through the notification system.
I'd start with a simple recursive watch and implement something more complicated only if it becomes necessary.
We could use an
We should watch from the root directory down, then if a filepath is also inside the LSP workspace, we can also trigger a LSP notification.
I think we could use a global ignore list. The problem with per-language setting is that in a monorepo we might have a mix of different languages. Just because I'm editing a rust file doesn't mean there isn't a |
yeah this is annoying. I wish there was an editor-independent (in LSP or outside) way of marking paths as "not containing sources". Would be nice to piggy-back on gitignore but that's not exactly the same thing.
I agree. This is also what kak-lsp does because I couldn't reproduce performance problems. |
Any updates recently? |
This feature is the only one preventing me from switching over to helix completely. I love helix, but being out of sync with the state of the file on the disk is a little jarring sometimes. What needs to be done for this to work? |
On a quick glance this seems reasonable apart from the needlessly-complex recursive watch logic. You should know that kakoune-lsp recently disabled the file watcher by default because it caused very high latency for |
part of the work done here has already been merged with #7665 |
Closes #2479 and is a prerequisite for #1125. Also probably supersedes #588.
I don't think this is quite ready to merge but it's definitely due for some reviewing. Functionality works fine for closing the intended issue but I've got concerns when it comes to other issues that depend on file watching.
notify
is the main dependency added. I've reusedglobset
as a dependency to parse the LSP spec's glob patterns, it's already included throughignore
.There are a couple different choices with regard to what directories to set
notify
watching:The issue with (1) is that there's no way to outright ignore directories: it seems likely that the amount of file changes
rust-analyzer
generates in thetarget
directory on every write throughcargo check
could cause stuttering on lower-specced machines. The issue with (2) is that it can't detect new files as it's not watching for them, and the LSP may be interested in events from files that aren't open in the editor. So I've gone with (3) here: non-recursively watching the root directory, ignoring those matching some default list of build/cache directories ala Rust'starget
directory, and recursively watching the rest. This approach is taken from emacs-lsp, which has a seemingly pretty comprehensive list of ignored directories. The downside is that this adds some complexity with keeping track of directory creations/deletions/renames in the root directory, but it's not bad.File watching works as follows: Watcher sets up
notify
watches on open LSP workspaces. Interested parties register a callback to be called when the watcher receives events matching a path filter in a specific workspace.Some considerations:
DidChangeWatchedFiles
can only be registered dynamically, so supporting it requires handlingRegisterCapability
andUnregisterCapability
. I haven't yet added support for unregistering theDidChangeWatchedFiles
capability as I'm not sure where to store the required state: it seems to belong with the LSP client, but mutating the client seems to require interior mutability right now which gives me pause.target
. Happy to add others fromemacs-lsp
's list in if maintainers are fine with this approach. Something nagging me is that since these directories are per-language it would also make sense to me if these were configured per-language, rather than one big list.notify
which has been around for a while and seems pretty stable. It doesn't support debouncing yet but it seems like it's not too far off either. I haven't added debouncing to this PR yet either, it's possible it's best to just leave it as is and wait untilnotify
's debounce support hits if the lack of debouncing isn't causing any issues.That's all for now off the top of my head, but there's probably other stuff lurking around in the PR.