-
Notifications
You must be signed in to change notification settings - Fork 288
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
Implement pull-model documentDiagnostics #746
Implement pull-model documentDiagnostics #746
Conversation
// in addition to the existing push-based publish notifications. | ||
// If the client supports pull diagnostics, we report the capability | ||
// and we should disable the publish notifications to avoid double-reporting. | ||
return clientCapabilities.textDocument?.diagnostic == nil |
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.
Should this rather be testing clientCapabilities.textDocument?.diagnostic?.dynamicRegistration != true
? I'm not sure if we would receive the pull requests if dynamic registration is not supported by the client.
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 is the right approach. Correct me if you think I’m wrong but checking for dynamicRegistration
would only check if the client supports dynamic registration of the capability. If, for some reason, it supported registration of the capability at server initialization but not dynamically after the fact, checking for dynamicRegistration
wouldn’t catch that.
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.
We have the same understanding of dynamicRegistration
. My read of SourceKitServer
is that it doesn't report supporting pull diagnostics at initialization. Later, when it is asked about a swift file, it starts SwiftLanguageServer
, initializes it, sees that it reports supporting pull diagnostics and at that point dynamically registers support for pull diagnostics to the LSP client. So, we depend on dynamicRegistration
being supported.
Or maybe we should unconditionally report support for pull diagnostics during SourceKitServer
's initialization, avoiding dynamic registration? If our underlying language server doesn't support the request, SourceKitServer
will return no diagnostics so I think there's no harm done.
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.
If you unconditionally supporting pull diagnostics are you then unconditionally disabling publish diagnostics? Given you are currently disabling publish diagnostics when pull diagnostics are enabled.
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.
That shouldn't be a problem. SourceKitServer.initialize()
can unconditionally report support for pull diagnostics, but SwiftLanguageServer
's logic is based on the ClientCapabilities
coming from the LSP Client, so it will only disable publish diagnostics if the LSP client supports pull diagnostics.
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.
Honestly, what I really want, would be to see how other language servers handle this. But I haven’t found a single major LSP server that supports both publishDiagnsotics
and textDocument/diagnostic
. Also, what surprises me, is that I can’t find a PR or issue on /~https://github.com/microsoft/language-server-protocol that mentions textDocument/diagnostics
, which kind of surprises me because I thought there would be some kind of discussion about these features.
Anyway, given the lack of precedence or clarification, I think we really need to make up our own and out of all the alternatives we have considered, I think not sending publishDiagnostics
notifications if both the server and the client support “Pull Diagnostics” still seems like the cleanest design to me.
It adds some complexity for determining if we should or not publish diagnostics, and it requires dynamic capability registration.
What’s the added complexity here? I think passing down the CapabilityRegistry
instead of ClientCapabilities
as you do it in your PR works rather nicely. I haven’t looked at it in too much detail though.
I'm thinking a better approach would be to stop publishing diagnostics in the language-specific server as soon as we receive a first pull diagnostics request, so we have proof that the client is using them.
Hmm, I’m not a huge fan because this means that sending a request silently changes the entire behavior of SourceKit-LSP, which is very confusing IMO. I think whether publishDiagnostic
notifications should be determined when the client and server agree on capabilities.
I also think it makes sense to unconditionally reporting pull diagnostics as supported in the SourceKitServer (independent of language-specific servers), rather than using dynamic registration when one language-specific server comes online and requires it. We have a well-defined response if we get a pull diagnostics request and the language-specific server doesn't support it (return an empty diagnostics array), and it won't stop the client from responding to publish diagnostics messages by language-specific servers who do not support pull diagnostics. Thoughts?
But wouldn’t that leave us with the following problem: clangd
publishes its diagnostics using publishDiagnostics
. Then the client requests diagnostics using textDocument/diagnostics
and SourceKit-LSP returns an empty array, clearing any diagnostics displayed on the client.
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.
Oh I think that your last point reveals our different assumptions. My assumption is that the namespacing of pull and publish diagnostics is separate and they don't interact because that would be hard to manage, but I'm still looking for some confirmation of this (specs or reference implementations).
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.
Oh, yes. If they are indeed in different namespaces, that would certainly change things.
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.
Let's see if this gets us more info: microsoft/language-server-protocol#1743
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.
In the meanwhile, I finished and pushed your suggested implementation, so we can make some progress. It's ready to review now.
@ahoppen Good news! I got this to work and it fixes some of the stale squiggly cases we knew of. Can you check the two comments I left on the PR as well? Thanks for the earlier pointers! |
// in addition to the existing push-based publish notifications. | ||
// If the client supports pull diagnostics, we report the capability | ||
// and we should disable the publish notifications to avoid double-reporting. | ||
return clientCapabilities.textDocument?.diagnostic == nil |
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 is the right approach. Correct me if you think I’m wrong but checking for dynamicRegistration
would only check if the client supports dynamic registration of the capability. If, for some reason, it supported registration of the capability at server initialization but not dynamically after the fact, checking for dynamicRegistration
wouldn’t catch that.
// Since LSP 3.17.0, diagnostics can be reported through pull-based requests, | ||
// in addition to the existing push-based publish notifications. | ||
// If the client supports pull diagnostics, we report the capability | ||
// and we should disable the publish notifications to avoid double-reporting. |
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.
Is it documented somewhere that LSP servers shouldn’t publish diagnostics if the client supports pullDiagnostics
? Or, if not, do you know if other LSP servers are also doing this as well or if VSCode is relying on it?
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 the two can be mix-and-matched if they use different provider names, but it wouldn't be desirable here because we would be reporting the same diagnostics twice, and the push-model ones would still have the same staleness issue being addressed by pull-model.
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 just worried that some LSP client says that it supports pull diagnostics but for some reason still expects that diagnostics get published. That’s why I’d like to know what other LSP servers do…
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 suggest a solution to this here: /~https://github.com/apple/sourcekit-lsp/pull/746/files#r1206839125
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.
Thank you for implementing support for the diagnostics request. The implementation is actually a lot shorter than I thought it would be, so great job! I’ve got a couple of questions, but overall, this looks great to me.
One request though: Could you also write a test case for diagnostic pulling?
I'll do my best. I was under the impression that the sourcekit-lsp tests weren't working on Windows and my macOS-fu is poor at best. :/ |
The tests worked (at one point) on Windows. They cannot be built currently due to the swift-syntax dependency and SPM miscompiling code. You should be able to roll back to prior to the swift-syntax adoption, port your code to that point and run the tests. Alternatively, you can enumerate all test cases into a manifest, add CMake rules, and build the tests locally with CMake. |
Or someone could ask swift-ci to build and test this PR for me? 😅 I have added a simple test mimicking the publish diagnostics test so there's a nonzero chance that it'll pass without me having to do much local build shenanigans... this time around. |
@swift-ci please test |
Sorry, I have meant to reply to this one and then forgot. I ran the test case locally and it had a few syntactic issues but I was able to fix them easily. Unfortunately, I forgot about the changes and discarded them. I can fix your test case again after you addressed #746 (comment). |
@ahoppen On it! Sorry I was pulled into other things momentarily but I aim to get this all wrapped up and merged by EOW. |
No worries, @tristanlabelle. Thanks for doing this work in the first place. |
@swift-ci test |
@swift-ci test windows |
@swift-ci test |
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.
Thanks. I think we have landed at a good design now and I’m curious to see if we’ll get a response from issue you filed to the LSP spec repo.
Also the refactoring of how we check for capabilities looks like a general improvement to me which usually shows that we’re on the right track. Thanks for your continued effort on this PR.
I’ve got two minor comments inline, otherwise, I think this is good to be merged.
// FIXME: cancellation | ||
_ = handle |
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 you should be able to implement cancellation similar to here
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.
Could we leave this to a PR that fixes all cancellation at once? This was copied from many places
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.
Sounds good to me as well
Co-authored-by: Alex Hoppen <alex@alexhoppen.de>
I would appreciate a ci kick. I'm still writing the unit test blindly due to current Windows build limitations. (@ahoppen if you get a chance to build locally that'd be super helpful) |
@swift-ci test |
I just ran the test locally and if you apply the following diff, they pass diff --git a/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift b/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift
index fa2b43f8..a28f23f4 100644
--- a/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift
+++ b/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift
@@ -16,6 +16,10 @@ import SKTestSupport
import XCTest
final class PullDiagnosticsTests: XCTestCase {
+ enum Error: Swift.Error {
+ case unexpectedDiagnsoticReport
+ }
+
/// Connection and lifetime management for the service.
var connection: TestSourceKitServer! = nil
@@ -61,8 +65,7 @@ final class PullDiagnosticsTests: XCTestCase {
}
guard case .full(let fullReport) = report else {
- XCTFail("Unexpected diagnostics report type: \(report)")
- return
+ throw Error.unexpectedDiagnsoticReport
}
return fullReport.items |
Thank you, @ahoppen ! I applied your diff. |
@swift-ci Please test |
Seems like the Windows build went out to lunch, but the test now passes on Linux and macOS! 🎉 |
@swift-ci test windows |
Windows builds need to be started separately, unfortunately. |
Woo! All green :D |
@tristanlabelle Can you also cherry-pick this PR to the |
Cherry-picked to #751 (after requashing - this PR was completed by merging so there's a bit of my dirty laundry in the history now) |
Implements LSP 3.17.0's pull-model documentDiagnostics request, which fix stale squiggly issues after fixing an error due to the relationship between code in two files. When using pull-model documentDiagnostics, we disable the push-model publishDiagnostics to avoid reporting them twice.
(this gif was using a temporary "pulldiag" source name to verify that it wasn't publish diagnostics in action)
Fixes #500