private ImmutableArray <DiagnosticData> AdjustInitialDiagnostics( Solution solution, UpdatedEventArgs args, CancellationToken cancellationToken) { // we only reach here if there is the document var document = solution.GetDocument(args.DocumentId); // if there is no source text for this document, we don't populate the initial tags. this behavior is equivalent of existing // behavior in OnDiagnosticsUpdated. if (!document.TryGetText(out var text)) { return(ImmutableArray <DiagnosticData> .Empty); } // GetDiagnostics returns whatever cached diagnostics in the service which can be stale ones. for example, build error will be most likely stale // diagnostics. so here we make sure we filter out any diagnostics that is not in the text range. var builder = ArrayBuilder <DiagnosticData> .GetInstance(); var fullSpan = new TextSpan(0, text.Length); foreach (var diagnostic in diagService.GetDiagnostics( args.Workspace, args.ProjectId, args.DocumentId, args.Id, includeSuppressedDiagnostics: false, cancellationToken: cancellationToken)) { if (fullSpan.Contains(diagnostic.GetExistingOrCalculatedTextSpan(text))) { builder.Add(diagnostic); } } return(builder.ToImmutableAndFree()); }
private void ProduceTags( TaggerContext <TTag> context, DocumentSnapshotSpan spanToTag, Workspace workspace, Document document, SourceText sourceText, NormalizedSnapshotSpanCollection suppressedDiagnosticsSpans, UpdatedEventArgs updateArgs, CancellationToken cancellationToken) { try { var id = updateArgs.Id; var diagnostics = _diagnosticService.GetDiagnostics( workspace, document.Project.Id, document.Id, id, false, cancellationToken); var isLiveUpdate = id is ISupportLiveUpdate; var requestedSpan = spanToTag.SnapshotSpan; var editorSnapshot = requestedSpan.Snapshot; foreach (var diagnosticData in diagnostics) { if (this.IncludeDiagnostic(diagnosticData)) { // We're going to be retrieving the diagnostics against the last time the engine // computed them against this document *id*. That might have been a different // version of the document vs what we're looking at now. But that's ok: // // 1) GetExistingOrCalculatedTextSpan will ensure that the diagnostics spans are // contained within 'editorSnapshot'. // 2) We'll eventually hear about an update to the diagnostics for this document // for whatever edits happened between the last time and this current snapshot. // So we'll eventually reach a point where the diagnostics exactly match the // editorSnapshot. var diagnosticSpan = diagnosticData.GetExistingOrCalculatedTextSpan(sourceText) .ToSnapshotSpan(editorSnapshot); if (diagnosticSpan.IntersectsWith(requestedSpan) && !IsSuppressed(suppressedDiagnosticsSpans, diagnosticSpan)) { var tagSpan = this.CreateTagSpan(isLiveUpdate, diagnosticSpan, diagnosticData); if (tagSpan != null) { context.AddTag(tagSpan); } } } } } catch (ArgumentOutOfRangeException ex) when(FatalError.ReportWithoutCrash(ex)) { // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=428328&_a=edit&triage=false // explicitly report NFW to find out what is causing us for out of range. // stop crashing on such occations return; } }
private async Task <LSP.Diagnostic[]> GetDiagnosticsAsync(Solution solution, Document document, CancellationToken cancellationToken) { var diagnostics = _diagnosticService.GetDiagnostics(solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(diagnostics.Select(diag => new RoslynDiagnostic { Code = diag.Id, Message = diag.Message, Severity = ProtocolConversions.DiagnosticSeverityToLspDiagnositcSeverity(diag.Severity), Range = ProtocolConversions.TextSpanToRange(diag.GetExistingOrCalculatedTextSpan(text), text), Tags = diag.CustomTags.Where(s => s == "Unnecessary").ToArray() }).ToArray()); }
private async Task <LanguageServer.Protocol.Diagnostic[]> GetDiagnosticsAsync(Solution solution, Document document, CancellationToken cancellationToken) { var diagnostics = _diagnosticService.GetDiagnostics(solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(diagnostics.Select(diagnostic => new LanguageServer.Protocol.Diagnostic { Code = diagnostic.Id, Message = diagnostic.Message, Severity = ProtocolConversions.DiagnosticSeverityToLspDiagnositcSeverity(diagnostic.Severity), Range = ProtocolConversions.TextSpanToRange(DiagnosticData.GetExistingOrCalculatedTextSpan(diagnostic.DataLocation, text), text), // Only the unnecessary diagnostic tag is currently supported via LSP. Tags = diagnostic.CustomTags.Contains("Unnecessary") ? new DiagnosticTag[] { DiagnosticTag.Unnecessary } : Array.Empty <DiagnosticTag>() }).ToArray()); }
public static ImmutableArray <DiagnosticData> GetDiagnostics(this IDiagnosticService service, Document document, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) { var project = document.Project; var workspace = project.Solution.Workspace; using var _ = ArrayBuilder <DiagnosticData> .GetInstance(out var result); foreach (var bucket in service.GetDiagnosticBuckets(workspace, project.Id, document.Id, cancellationToken)) { Contract.ThrowIfFalse(workspace.Equals(bucket.Workspace)); Contract.ThrowIfFalse(document.Id.Equals(bucket.DocumentId)); var diagnostics = service.GetDiagnostics(bucket, includeSuppressedDiagnostics, cancellationToken); result.AddRange(diagnostics); } return(result.ToImmutable()); }
private async Task <Dictionary <Uri, ImmutableArray <LanguageServer.Protocol.Diagnostic> > > GetDiagnosticsAsync(CodeAnalysis.Document document, CancellationToken cancellationToken) { var diagnostics = _diagnosticService.GetDiagnostics(document.Project.Solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken) .Where(IncludeDiagnostic); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // Retrieve diagnostics for the document. These diagnostics could be for the current document, or they could map // to a different location in a different file. We need to publish the diagnostics for the mapped locations as well. // An example of this is razor imports where the generated C# document maps to many razor documents. // https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-3.1#importing-shared-directives // https://docs.microsoft.com/en-us/aspnet/core/blazor/layouts?view=aspnetcore-3.1#centralized-layout-selection // So we get the diagnostics and group them by the actual mapped path so we can publish notifications // for each mapped file's diagnostics. var fileUriToDiagnostics = diagnostics.GroupBy(diagnostic => GetDiagnosticUri(document, diagnostic)).ToDictionary( group => group.Key, group => group.Select(diagnostic => ConvertToLspDiagnostic(diagnostic, text)).ToImmutableArray()); return(fileUriToDiagnostics);
public static ImmutableArray <DiagnosticData> GetDiagnostics(this IDiagnosticService service, DiagnosticBucket bucket, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) => service.GetDiagnostics(bucket.Workspace, bucket.ProjectId, bucket.DocumentId, bucket.Id, includeSuppressedDiagnostics, cancellationToken);