-
Notifications
You must be signed in to change notification settings - Fork 128
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
refactor: decouple diagnostics from handlers into a hook #714
Conversation
6d16708
to
84cee5d
Compare
When a (1) capped channel has more than one consumer, one of them will effectively remain blocking as the other consumes the message from the channel. Closing the channel unblocks any/all consumers. Here we also unpublish previously public ModuleOperation.Done() to further prevent deadlocks in the future, i.e. effectively to prevent any place that schedules tasks to wait. The only reason we keep the channel there for now is to make testing *within the package* easier.
84cee5d
to
806088e
Compare
This is no longer true (fixed by sublimelsp/LSP#1881). ST LSP still hides diagnostics for files that are outside of the workspace but that's also not entirely wanted so might change in the future. |
@rchl Thanks for the note, that further supports the theory that people do want to see diagnostics even for unopened files then 👍🏻 |
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.
Previously we would (mistakenly) not parse module in walker, only when it's opened (as part of didOpen or didChange). This fixes that to help publish diagnostics from all modules at startup time.
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.
7611112 fixes the before mentioned strange behavior 🎉
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
Background
Originally we would only publish diagnostics directly from the relevant (RPC request) handlers, such as from
textDocument/didOpen
,textDocument/didChange
ortextDocument/didClose
.This in principle introduced the requirement on the task scheduler to support blocking/synchronous execution as we didn't want to be publishing stale diagnostics before the parsing and decoding is actually finished. However, this execution combined with the amount of operations we need to run in different contexts makes this a complex implementation hard to reason about and harder to maintain. Consequently there is at least one bug 🐛 where duplicate operations are attempted to be scheduled and one of them will remain blocking forever, effectively causing a deadlock 🙀 . This bug was first discovered in #700 which adds more operations to ensure cross-module references are discovered and therefore makes the deadlock situation more likely to occur.
This PR therefore aims to remove the whole concept of blocking/sync execution and assume that all operations will be executed asynchronously.
UX Implications
This comes with a (subtle?) UX difference where we would publish diagnostics for all files as they are indexed (and changed), not just the ones which are open on the client. We could still provide the existing UX if we wanted to, but there's no clear benefit we'd gain by the additional complexity.
Also we now avoid publishing any diagnostics for non-autoloaded tfvars files, which more or less aligns the behaviour with gopls ' treatment of Go build tags. I will create a separate issue to track this problem.
See #715
LSP
There is currently no clear guidance in LSP spec around whether servers should publish diagnostics for unopened files. The only related part of the spec talks about clearing of diagnostics on close:
Language Clients & gopls
Although LSP doesn't provide clear guidance, it is clear that sending diagnostics for unopened files is a common practice in gopls. Clients account for this possibility differently, probably catering different user bases with different expectations:
Future TODOs
Versioned diagnostics
LSP allows us to send diagnostics with document versions, which presumably aids the client as it can just drop the ones which are associated with an old version of the document and effectively prevent ever publishing a stale diagnostic.
Attaching the document version at this point would not be trivial as we'd probably need to start tracking document versions in memdb alongside diagnostics. This PR therefore does increase the likelihood of us publishing outdated diagnostics, but they would always get updated/cleared as part of another module change hook execution anyway, so it's rather a "temporary blip".
See #716
Only publishing changed diagnostics
In an ideal scenario we would only publish diagnostics when there are different ones to publish and avoid publishing the same ones multiple times. In order to achieve this however we would need to check whether all diagnostics are equal. Currently we use the upstream
hcl.Diagnostic
to represent a diagnostic and the upstream implementation does not have an equality function itself. We could implement all this relatively easily downstream, but we can't actually take advantage of it until we figure out a better way of publishing and clearingvalidate
diagnostics, which currently get just cleared whenever other diagnostics are published, to avoid stale diagnostics.See #717