Add support for distinct LSP diagnostic categories in LSP Pull Diagnostics.#65985
Conversation
| /// </summary> | ||
| Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, ImmutableHashSet<string>? diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default); | ||
| Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync( | ||
| Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, ImmutableHashSet<string>? diagnosticIds = null, bool includeSuppressedDiagnostics = false, DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default); |
| AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); | ||
|
|
||
| if (includeAll) | ||
| AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); |
There was a problem hiding this comment.
need eyes on this.
There was a problem hiding this comment.
actually, i just realized there's a simpler way to do this most likely.
…actPullDiagnosticHandler.cs
| internal abstract record AbstractDocumentDiagnosticSource<TDocument>(TDocument Document) : IDiagnosticSource | ||
| where TDocument : TextDocument | ||
| { | ||
| private static readonly ImmutableArray<string> s_todoCommentCustomTags = ImmutableArray.Create(PullDiagnosticConstants.TaskItemCustomTag); |
There was a problem hiding this comment.
these types got much simpler as task-list handling was extracted into a dedicated diagnostic source.
| var allSpanDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForSpanAsync( | ||
| Document, range: null, diagnosticKind: this.DiagnosticKind, cancellationToken: cancellationToken).ConfigureAwait(false); | ||
| return allSpanDiagnostics; | ||
| } |
There was a problem hiding this comment.
this part was extremely clean, and fits onto the current diagnostic service API without any issue.
|
|
||
| namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; | ||
|
|
||
| internal sealed record class TaskListDiagnosticSource(Document Document) : AbstractDocumentDiagnosticSource<Document>(Document) |
There was a problem hiding this comment.
this is just extracting the logic for task list diagnostics into a dedicate source. it is otherwise unchanged.
| // However we can include them as a part of workspace pull when FSA is on. | ||
| var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync(Document.Project.Solution, Document.Project.Id, Document.Id, cancellationToken: cancellationToken).ConfigureAwait(false); | ||
| var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( | ||
| Document.Project.Solution, Document.Project.Id, Document.Id, cancellationToken: cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
this part was a little messy. specifically, GetDiagnosticsForIdsAsync doesn't support fine-grained diagnostic requests (@mavasani ). So for workspace-document-pull requests, we just wait on getting all the diagnostics. this means that there's no speedup on getting diagnostics for closed files. However, i think that's ok as you only get those for FSA, and FSA is notoriously slow anyways.
If we do want to support this, it would be possible. We'd just need GetDiagnosticsForIdsAsync to support passing in a diagnostickind. I initially attempted to do that, but i was uncertain about the precise semantics of how that would work in the impl.
If you are interested in adding support for that @mavasani i can update this side of things.
There was a problem hiding this comment.
I think it is fine to wait for adding this support as it does not seem to be a mainline scenario.
| protected override async ValueTask<ImmutableArray<IDiagnosticSource>> GetOrderedDiagnosticSourcesAsync( | ||
| VSInternalWorkspaceDiagnosticsParams diagnosticsParams, | ||
| RequestContext context, | ||
| CancellationToken cancellationToken) |
There was a problem hiding this comment.
helper to walk projects in priority order. used by both teh normal diagnostic walking code, and the task-list code.
| // diagnostics will be handled by razor itself, which will operate by calling into Roslyn and asking for | ||
| // document-diagnostics instead. | ||
| if (context.ServerKind == WellKnownLspServerKinds.RazorLspServer) | ||
| return ImmutableArray<IDiagnosticSource>.Empty; |
There was a problem hiding this comment.
@dibarbet we both check this and below check if (document.IsRazorDocument()) to bail out. Do we need both? Or can we skip the second check if the server is the razor server?
There was a problem hiding this comment.
I believe we need both - there are two cases
- The razor C# server is being asked for workspace diagnostics (this check here). The razor C# server shouldn't give anything back
- The normal C# server is being asked for workspace diagnostics, and it sees a Razor document. It should not return diagnostics for that document because we rely on razor to do a document pull for all razor documents.
| } | ||
|
|
||
| private static ImmutableArray<IDiagnosticSource> GetTaskListDiagnosticSources( | ||
| RequestContext context, IGlobalOptionService globalOptions) |
There was a problem hiding this comment.
dedicated helpers to get the sources for task-list vs diagnsotics.
| { | ||
| serverCapabilities.SupportsDiagnosticRequests = true; | ||
| serverCapabilities.MultipleContextSupportProvider = new VSInternalMultipleContextFeatures { SupportsMultipleContextsDiagnostics = true }; | ||
| serverCapabilities.DiagnosticProvider ??= new(); |
There was a problem hiding this comment.
could use initializer syntax here? This should always be null here I believe
| protected static (DiagnosticKind diagnosticKind, bool taskList, bool isProject) GetDiagnosticKindInfo(VSInternalDiagnosticKind? queryingDiagnosticKind) | ||
| { | ||
| if (queryingDiagnosticKind?.Value == PullDiagnosticConstants.Project) | ||
| return (default, taskList: false, isProject: true); |
There was a problem hiding this comment.
maybe an explicit kind instead of default?
| /// </summary> | ||
| protected abstract ValueTask<ImmutableArray<IDiagnosticSource>> GetOrderedDiagnosticSourcesAsync(RequestContext context, CancellationToken cancellationToken); | ||
| protected abstract ValueTask<ImmutableArray<IDiagnosticSource>> GetOrderedDiagnosticSourcesAsync( | ||
| TDiagnosticsParams diagnosticsParams, RequestContext context, CancellationToken cancellationToken); |
There was a problem hiding this comment.
as discussed offline, we'll need a different cache per kind
There was a problem hiding this comment.
yup. has been done.
| // diagnostics will be handled by razor itself, which will operate by calling into Roslyn and asking for | ||
| // document-diagnostics instead. | ||
| if (context.ServerKind == WellKnownLspServerKinds.RazorLspServer) | ||
| return ImmutableArray<IDiagnosticSource>.Empty; |
There was a problem hiding this comment.
I believe we need both - there are two cases
- The razor C# server is being asked for workspace diagnostics (this check here). The razor C# server shouldn't give anything back
- The normal C# server is being asked for workspace diagnostics, and it sees a Razor document. It should not return diagnostics for that document because we rely on razor to do a document pull for all razor documents.
| Assert.Equal("todo: goo", results.Single().Diagnostics.Single().Message); | ||
| Assert.Empty(results.Single().Diagnostics); | ||
| //Assert.Equal("TODO", results.Single().Diagnostics.Single().Code); | ||
| //Assert.Equal("todo: goo", results.Single().Diagnostics.Single().Message); |
This allows us to coordinate with the client to support categories like "Compiler Syntax", "Compiler Semantic", "Task List", etc. categories. This will greatly speed up LSP pull diagnostics as these requests can operate concurrently, and can be displayed to the user once individually complete, as opposed to waiting for us to compute all of them.