[ty] Track open files in the server#19264
Conversation
6f768c0 to
1f21e75
Compare
|
| // TODO: Why do we require this? Because this doesn't do anything as `File` doesn't | ||
| // exists for the system path and this event will not create it either. |
There was a problem hiding this comment.
I think we need it to bump the file revision. Before the event: the file's revision is the timestamp on disk. After opening it, the revision is the document version.
This comment was marked as resolved.
This comment was marked as resolved.
1cc96b9 to
bf3212e
Compare
bf3212e to
8d5ef98
Compare
|
crates/ty_project/src/lib.rs
Outdated
| Indexed(files::Indexed<'a>), | ||
| Indexed { | ||
| files: files::Indexed<'a>, | ||
| virtual_files: Option<Box<[File]>>, |
There was a problem hiding this comment.
This does not seem useful at all because VS Code also sends us the textDocument/diagnostic request for open files and virtual files are always open. So, we could possibly just remove this field and all should still be fine.
There was a problem hiding this comment.
Hmm, that's interesting. It would certainly simplify things a bit. Can you try if skipping over the virtual files still works as expected?
There was a problem hiding this comment.
I've removed this special handling for virtual files.
I tested out Zed which also supports virtual files but it seems that it doesn't send any notification to the client about it's existence.
Screen.Recording.2025-07-16.at.17.32.44.mov
Zed seems to be sending a didClose for the new other.py and then a didOpen which is what caused the error in the above video. I'm not sure why though. But, it does send a FileChangeType::Created which seems fine but then why is it sending a didClose event?
// Send:
{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///Users/dhruv/playground/ty-server/src/ty_server/other.py","type":1}]}}
// Send:
{"jsonrpc":"2.0","method":"textDocument/didClose","params":{"textDocument":{"uri":"file:///Users/dhruv/playground/ty-server/src/ty_server/other.py"}}}
// Send:
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/dhruv/playground/ty-server/src/ty_server/other.py","languageId":"python","version":0,"text":"x"}}}
Neovim also has virtual file support but it has it's own issues: #15392
c14535c to
993d565
Compare
854decd to
e649866
Compare
## Summary Previously, the virtual files were being added to the default database that's present on the session. This is wrong because the default database is for any files that don't belong to any project i.e., they're outside of any projects managed by the server. Virtual files are neither part of the project nor it is outside the projects. This was not the intention as in the initial version, virtual files were being added to the only project database managed by the server. This PR fixes this by reverting back to the original behavior where virtual files will be added to the only project database present. When support for multiple workspace and project is added, this will require updating (astral-sh/ty#794). This is required for #19264 because workspace diagnostics doesn't check the default project database yet. Ideally, the default db should be checked as well. The implementation of this PR means that virtual files are now being included for workspace diagnostics but it doesn't work completely e.g., if I save an untitled file the diagnostics disappears but it doesn't appear back for the (now) saved file on disk as shown in the following video demonstration: https://github.com/user-attachments/assets/123e8d20-1e95-4c7d-b7eb-eb65be8c476e
e649866 to
93a44b3
Compare
MichaReiser
left a comment
There was a problem hiding this comment.
Thank you. I think the should_check_file implementation is currently incorrect as it also returns true for third party files, which we don't want. It might be worth adding a CLI test for it.
I also think that we should remove the implicit behavior where setting open_files to Some changes the check mode. I think this is confusing now where we also have an explicit check mode. Instead, check should always do whatever is configured by CheckMode. This may require updating the benchmarks and ty_wasm to explicitly set CheckMode
crates/ty_project/src/lib.rs
Outdated
| Indexed(files::Indexed<'a>), | ||
| Indexed { | ||
| files: files::Indexed<'a>, | ||
| virtual_files: Option<Box<[File]>>, |
There was a problem hiding this comment.
Hmm, that's interesting. It would certainly simplify things a bit. Can you try if skipping over the virtual files still works as expected?
Post review changes
|
- Update `should_check_file` to check whether the file exists in the indexed file set in `AllFiles` check mode. This will avoid any third party files to be checked in this mode - Remove the `check_mode` field from the `ProjectMetadata`, instead expose a method `set_check_mode` from `ProjectDatabase` that updates the `Project`'s check mode - Update the default check mode to be `AllFiles` and update the server code to set the check mode as per the diagnostic mode, update the wasm and benchmark code to always use the `OpenFiles` check mode - Update the logic to check if a file is open or not to only check whether it's explicitly in the open file set and avoid checking the indexed file set - Expand comments
398bbb7 to
b4d826b
Compare
29831c6 to
4d0df12
Compare
Test cases
Screen.Recording.2025-07-18.at.15.24.30.mov |
MichaReiser
left a comment
There was a problem hiding this comment.
Uff, that was tricky. Thank you for seeing this through!
Summary
This PR updates the server to keep track of open files both system and virtual files.
This is done by updating the project by adding the file in the open file set in
didOpennotification and removing it indidClosenotification.This does mean that for workspace diagnostics, ty will only check open files because the behavior of different diagnostic builder is to first check
is_file_openand only add diagnostics for open files. So, this required updating theis_file_openmodel to beshould_check_filemodel which validates whether the file needs to be checked based on theCheckMode. If the check mode is open files only then it will check whether the file is open. If it's all files then it'll returntrueby default.Closes: astral-sh/ty#619
Test Plan
Before
There are two files in the project:
__init__.pyanddiagnostics.py.In the video, I'm demonstrating the old behavior where making changes to the (open)
diagnostics.pyfile results in re-parsing the file:Screen.Recording.2025-07-15.at.11.07.01.mov
After
Same setup as above.
In the video, I'm demonstrating the new behavior where making changes to the (open)
diagnostics.pyfile doesn't result in re-parting the file:Screen.Recording.2025-07-15.at.11.07.44.mov