lsp: Override virtual file system#793
Conversation
2c83aa4 to
e36b938
Compare
|
I really like the dependent updates, that seems like a really useful feature. However the VFS implementation seems more complex than I thought would have been necessary when I designed the abstraction. |
This is to figure out which files that file depends on: The VFS records which files are read, and that is the basis for the data in the DependencyMap. I saw no other way to get hold of this information (especially if the Parser produces Diagnostics instead of an AST). |
|
I'm a bit reluctant to merge this because the complexity in the LSP increased somewhat substantially, and the impact on performance is probably not neglect able. In your implementation you first compile/check the file that's open and then all other files that depend on it that are also open. For large base files (like rv3264im) parsing and checking it takes 300ms (on my machine without native built) but with these changes we now parse and check it 5 more times as part of the dependents if all files that depend on it are also open, resulting in 1.8s total (at least). To be fair the ergonomics of it could be worth it. Also we could parallelize it so that it doesn't block new updates on the base file (I'm not sure if that's already done). I fear there are also some shortcommings in the implementation, that make it even more complex (If I understand it correctly, I'm not totally sure). 1) Diagnostics in dependencies aren't published 2) The importer can break the importee |
|
Again I'm still not too sure about the dependency implementation/apporach but in case you feel really passionate about it, I started the review. During the review I had an idea about a different approach for the VFS. |
e36b938 to
86815e2
Compare
|
Please note my new
In my testing it was substantially faster than that: Pushing diagnostics for all 9 files that (directly or indirectly) depend on
You're right, that's weird behavior for the user. I've added this improvement: If the compiler reports a diagnostic in an imported file, then a new LSP diagnostic is placed right at the top of the current file (the one we're preparing diagnostics for) stating in which file an error occurred. It's not a perfect solution but better than nothing; it can certainly be improved (e.g. by including a code action to jump to the file in question), but that is out-of-scope for this PR.
As you say, this is a niche problem; I've never seen an example for this mechanism. It is something to tackle in another PR/issue if it is actually a problem - but maybe my solution for 1) above also helps here?
That is somewhat similar to my approach of tying the VFS to the Document Snapshot. Instead of having a snapshot for a file that is based on that file's version, you want to create a new Snapshot for a set of files. I believe that is overkill. I think that race conditions are not a problem: All in all, I wouldn't worry too much about which version of an imported file is used in compilation. Eventually, the latest edit (in a series of fast-paced edits) would override all others, because it either is an edit of a dependent file, thus it takes the latest changes of its dependencies into account, or it is an edit of a dependency, upon which diagnostics are recomputed for all dependent files. As for the reference to TODO: I just noticed that the VFS will also provide the most current content for the Document it is attached to, instead of the Snapshot's version. I will fix that tomorrow. I believe I have addressed all your concerns, please let me know if you're happy to go ahead with my approach. |
|
Oh, one more thing: I've added the first test class to the vadl-lsp module, but it seems to me that the CI didn't run these tests - how to configure that? |
|
Ok the performance sounds good, let's move forward with this approach, but let's still try to get complexity down, I think this will pay of massively in the long run. One more think, I don't see how the whole snapshot system makes any sense at the moment. Because the contents of the snapshots aren't used at all for compiling, instead the newest version will be used. I can see why the raceconditions might not be a good enough reason to implement the whole-world-snapshot-VFS™ but I think this is mostly about improvement in complexity. Creating it should be pretty cheap because snapshots are cached and even though many files are open, only a few (most likely one) will have changed. If you can please try this approach and let's see if my assumption is correct that this also improves the readability of the program. |
4b94007 to
4922a60
Compare
Yes, as I've mentioned above, that was a mistake on my part. Please see my
I don't see any advantages at the moment. Yes, the language server is highly parallelized, but in practice everything is driven by user input - i.e. files are edited one after another, at human speed, not at the same time. I don't think there's gonna be a problem with multiple threads interfering with / deadlocking each other.
I'm not sure how there would be readability improvements. The difference to the currently implemented Snapshot is that several files would be snapshot instead - but in any case the VadlTextDocumentService has to work with some sort of snapshot, so there's no difference there. All the complexity regarding other files is neatly tucked away in the VFS now. The reason for introducing per-Document Snapshots is so that version, text, and textLines are in sync (for things like checking if version is outdated or transforming positions to UTF16 positions for LSP) - this is actually already a simplification from the old implementation. So far I see no reason to include other files in that snapshot, and so I didn't. I propose to postpone multi-file snapshots for now, and only implement that if we actually need it. I would very much like to move forward with merging this. |
|
@flofriday and I had a talk about this PR. We agreed to:
Additionally, both points make it easier to reason about state in this multi-threaded environment - immutable data is much more straight-forward than mutable data. |
50b1ff8 to
887d0e4
Compare
|
@flofriday You can finish your review now. |
flofriday
left a comment
There was a problem hiding this comment.
This is way better now, just minor, non-blocking things.
The language server provides its own VirtualFileSystem in order to intercept reads of files that are currently managed by the LSP client. Additionally, parsing dependencies between files are tracked in order to publish diagnostics for dependent files too. Updated LICENSE.md with lsp4j information.
- VadlTextDocumentService: - Use Frontend.compileToAst() - Report if there are errors in imported files - Publish diagnostics for dependent files in parallel - Add DependencyMap Test
- VadlTextDocumentService: Do NOT use Frontend.compileToAst() - We want to use the snapshot's text for the parsed file instead of whatever the current version is.
- Immutable Document (i.e. always snapshot) - Use snapshots of files in VFS instead of most recent version
- Document: - Do not store text (it's used very rarely) - Do not create new ArrayList if fixed-size list is fine - throw IllegalStateException instead of RuntimeException
4932e60 to
522e626
Compare
|
@flofriday Please approve again, it's just miner changes. |
The language server provides its own VirtualFileSystem in order to intercept reads of files that are currently managed by the LSP client.
Additionally, parsing dependencies between files are tracked in order to publish diagnostics for dependent files too.
Updated LICENSE.md with lsp4j information.