예제 #1
0
        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;
            }
        }
예제 #3
0
        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());
        }
예제 #4
0
        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());
        }
예제 #5
0
        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());
        }
예제 #6
0
        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);
예제 #7
0
 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);