-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix issues with pull diagnostics in VSCode #73248
Changes from 3 commits
192c83a
bbc2d2e
bd61fd5
e9b7afd
1251636
7814344
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,11 +45,11 @@ public DiagnosticSourceManager([ImportMany] IEnumerable<IDiagnosticSourceProvide | |
.ToImmutableDictionary(kvp => kvp.Name, kvp => kvp); | ||
} | ||
|
||
public ImmutableArray<string> GetDocumentSourceProviderNames() | ||
=> _nameToDocumentProviderMap.Keys.ToImmutableArray(); | ||
public ImmutableArray<string> GetDocumentSourceProviderNames(ClientCapabilities clientCapabilities) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allows us to filter the diagnostic sources based on the client (e.g. no task diagnostics in vscode). |
||
=> _nameToDocumentProviderMap.Where(kvp => kvp.Value.IsEnabled(clientCapabilities)).SelectAsArray(kvp => kvp.Key); | ||
|
||
public ImmutableArray<string> GetWorkspaceSourceProviderNames() | ||
=> _nameToWorkspaceProviderMap.Keys.ToImmutableArray(); | ||
public ImmutableArray<string> GetWorkspaceSourceProviderNames(ClientCapabilities clientCapabilities) | ||
=> _nameToWorkspaceProviderMap.Where(kvp => kvp.Value.IsEnabled(clientCapabilities)).SelectAsArray(kvp => kvp.Key); | ||
|
||
public ValueTask<ImmutableArray<IDiagnosticSource>> CreateDocumentDiagnosticSourcesAsync(RequestContext context, string? providerName, CancellationToken cancellationToken) | ||
=> CreateDiagnosticSourcesAsync(context, providerName, _nameToDocumentProviderMap, isDocument: true, cancellationToken); | ||
|
@@ -69,7 +69,10 @@ private static async ValueTask<ImmutableArray<IDiagnosticSource>> CreateDiagnost | |
// VS does not distinguish between document and workspace sources. Thus it can request | ||
// document diagnostics with workspace source name. We need to handle this case. | ||
if (nameToProviderMap.TryGetValue(providerName, out var provider)) | ||
{ | ||
Contract.ThrowIfFalse(provider.IsEnabled(context.GetRequiredClientCapabilities())); | ||
return await provider.CreateDiagnosticSourcesAsync(context, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
return []; | ||
} | ||
|
@@ -79,12 +82,13 @@ private static async ValueTask<ImmutableArray<IDiagnosticSource>> CreateDiagnost | |
using var _ = ArrayBuilder<IDiagnosticSource>.GetInstance(out var sourcesBuilder); | ||
foreach (var (name, provider) in nameToProviderMap) | ||
{ | ||
// Exclude Task diagnostics from the aggregated sources. | ||
if (name != PullDiagnosticCategories.Task) | ||
if (!provider.IsEnabled(context.GetRequiredClientCapabilities())) | ||
{ | ||
var namedSources = await provider.CreateDiagnosticSourcesAsync(context, cancellationToken).ConfigureAwait(false); | ||
sourcesBuilder.AddRange(namedSources); | ||
continue; | ||
} | ||
|
||
var namedSources = await provider.CreateDiagnosticSourcesAsync(context, cancellationToken).ConfigureAwait(false); | ||
sourcesBuilder.AddRange(namedSources); | ||
} | ||
|
||
var sources = sourcesBuilder.ToImmutableAndClear(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Roslyn.LanguageServer.Protocol; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Public; | ||
// A document diagnostic partial report is defined as having the first literal send = DocumentDiagnosticReport (aka changed / unchanged) followed | ||
|
@@ -17,15 +18,29 @@ internal sealed partial class PublicDocumentPullDiagnosticsHandler : IOnInitiali | |
{ | ||
public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) | ||
{ | ||
// Dynamically register for all of our document diagnostic sources. | ||
// Dynamically register for all relevant diagnostic sources. | ||
if (clientCapabilities?.TextDocument?.Diagnostic?.DynamicRegistration is true) | ||
{ | ||
// TODO: Hookup an option changed handler for changes to BackgroundAnalysisScopeOption | ||
// to dynamically register/unregister the non-local document diagnostic source. | ||
|
||
// Task diagnostics shouldn't be reported through VSCode (it has its own task stuff). Additional cleanup needed. | ||
var sources = DiagnosticSourceManager.GetDocumentSourceProviderNames().Where(source => source != PullDiagnosticCategories.Task); | ||
var registrations = sources.Select(FromSourceName).ToArray(); | ||
var documentSources = DiagnosticSourceManager.GetDocumentSourceProviderNames(clientCapabilities); | ||
var workspaceSources = DiagnosticSourceManager.GetWorkspaceSourceProviderNames(clientCapabilities); | ||
|
||
// All diagnostic sources have to be registered under the document pull method name, | ||
// See https://github.com/microsoft/language-server-protocol/issues/1723 | ||
// | ||
// Additionally if a source name is used by both document and workspace pull (e.g. enc) | ||
// we don't want to send two registrations, instead we should send a single registration | ||
// that also sets the workspace pull option. | ||
// | ||
// So we build up a unique set of source names and mark if each one is also a workspace source. | ||
var allSources = documentSources | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. previously the doc handler registered doc sources and the workspace handler registered workspace sources. However this meant that sources using the same identifer for doc and workspace would overwrite each other - because vscode requires us to register all identifiers as document sources (even if they are workspace only). now we register them all once (and set the workspace option appropriately if it also participates in workspace requests). |
||
.AddRange(workspaceSources) | ||
.ToSet() | ||
.Select(name => (Name: name, IsWorkspaceSource: workspaceSources.Contains(name))); | ||
|
||
var registrations = allSources.Select(FromSourceName).ToArray(); | ||
await _clientLanguageServerManager.SendRequestAsync( | ||
methodName: Methods.ClientRegisterCapabilityName, | ||
@params: new RegistrationParams() | ||
|
@@ -35,12 +50,14 @@ await _clientLanguageServerManager.SendRequestAsync( | |
cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
Registration FromSourceName(string sourceName) | ||
=> new() | ||
static Registration FromSourceName((string Name, bool IsWorkspaceSource) source) | ||
{ | ||
return new() | ||
{ | ||
Id = Guid.NewGuid().ToString(), | ||
Method = Methods.TextDocumentDiagnosticName, | ||
RegisterOptions = new DiagnosticRegistrationOptions { Identifier = sourceName } | ||
RegisterOptions = new DiagnosticRegistrationOptions { Identifier = source.Name, InterFileDependencies = true, WorkspaceDiagnostics = source.IsWorkspaceSource, WorkDoneProgress = source.IsWorkspaceSource } | ||
}; | ||
} | ||
} | ||
} |
This file was deleted.
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.
this was the root cause of duplicate diagnostics in vscode - we were registering for requests with no identifier and also for requests with an identifier later on in dynamic registration