Exemplo n.º 1
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            // This provider is exported for all workspaces - so limit it to just our workspace.
            var(document, span, cancellationToken) = context;
            if (document.Project.Solution.Workspace.Kind != WorkspaceKind.AnyCodeRoslynWorkspace)
            {
                return;
            }

            var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient;

            if (lspClient == null)
            {
                return;
            }

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var diagnostics = await _diagnosticAnalyzerService.GetDiagnosticsForSpanAsync(document, span, cancellationToken : cancellationToken).ConfigureAwait(false);

            var diagnostic = diagnostics?.FirstOrDefault();

            if (diagnostic != null)
            {
                span = diagnostic.TextSpan;
            }

            var codeActionParams = new LSP.CodeActionParams
            {
                TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(document),
                Range        = ProtocolConversions.TextSpanToRange(span, text)
            };

            var commands = await lspClient.RequestAsync(LSP.Methods.TextDocumentCodeAction.ToLSRequest(), codeActionParams, cancellationToken).ConfigureAwait(false);

            if (commands == null)
            {
                return;
            }

            foreach (var command in commands)
            {
                if (LanguageServicesUtils.TryParseJson(command, out LSP.Command lspCommand))
                {
                    // The command can either wrap a Command or a CodeAction.
                    // If a Command, leave it unchanged; we want to dispatch it to the host to execute.
                    // If a CodeAction, unwrap the CodeAction so the guest can run it locally.
                    var commandArguments = lspCommand.Arguments.Single();

                    if (LanguageServicesUtils.TryParseJson(commandArguments, out LSP.CodeAction lspCodeAction))
                    {
                        context.RegisterRefactoring(new RoslynRemoteCodeAction(document, lspCodeAction.Command, lspCodeAction.Edit, lspCodeAction.Title, lspClient));
                    }
                    else
                    {
                        context.RegisterRefactoring(new RoslynRemoteCodeAction(document, lspCommand, lspCommand?.Title, lspClient));
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Return up to date diagnostics for the given span for the document
        /// <para>
        /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. If
        /// <paramref name="diagnosticId"/> is not null, it gets diagnostics only for this given <paramref
        /// name="diagnosticId"/> value.
        /// </para>
        /// </summary>
        public static Task <ImmutableArray <DiagnosticData> > GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, Document document, TextSpan?range, string?diagnosticId = null, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func <string, IDisposable?>?addOperationScope = null, CancellationToken cancellationToken = default)
        {
            Func <string, bool>?shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null;

            return(service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic,
                                                      includeSuppressedDiagnostics, priority, addOperationScope, cancellationToken));
        }
Exemplo n.º 3
0
 protected override Task <ImmutableArray <DiagnosticData> > GetDiagnosticsAsync(
     RequestContext context, Document document, DiagnosticMode diagnosticMode, CancellationToken cancellationToken)
 {
     // For open documents, directly use the IDiagnosticAnalyzerService.  This will use the actual snapshots
     // we're passing in.  If information is already cached for that snapshot, it will be returned.  Otherwise,
     // it will be computed on demand.  Because it is always accurate as per this snapshot, all spans are correct
     // and do not need to be adjusted.
     return(_analyzerService.GetDiagnosticsForSpanAsync(document, range: null, cancellationToken: cancellationToken));
 }
Exemplo n.º 4
0
        public async Task <ImmutableArray <CodeFixCollection> > GetFixesAsync(Document document, TextSpan range, bool includeSuppressionFixes, CancellationToken cancellationToken)
        {
            // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back
            // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information
            // (if it is up-to-date) or synchronously do the work at the spot.
            //
            // this design's weakness is that each side don't have enough information to narrow down works to do. it will most likely always do more works than needed.
            // sometimes way more than it is needed. (compilation)
            Dictionary <TextSpan, List <DiagnosticData> > aggregatedDiagnostics = null;

            foreach (var diagnostic in await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, cancellationToken: cancellationToken).ConfigureAwait(false))
            {
                if (diagnostic.IsSuppressed)
                {
                    continue;
                }

                cancellationToken.ThrowIfCancellationRequested();

                aggregatedDiagnostics = aggregatedDiagnostics ?? new Dictionary <TextSpan, List <DiagnosticData> >();
                aggregatedDiagnostics.GetOrAdd(diagnostic.TextSpan, _ => new List <DiagnosticData>()).Add(diagnostic);
            }

            if (aggregatedDiagnostics == null)
            {
                return(ImmutableArray <CodeFixCollection> .Empty);
            }

            var result = ArrayBuilder <CodeFixCollection> .GetInstance();

            foreach (var spanAndDiagnostic in aggregatedDiagnostics)
            {
                await AppendFixesAsync(
                    document, spanAndDiagnostic.Key, spanAndDiagnostic.Value,
                    result, cancellationToken).ConfigureAwait(false);
            }

            if (result.Count > 0)
            {
                // sort the result to the order defined by the fixers
                var priorityMap = _fixerPriorityMap[document.Project.Language].Value;
                result.Sort((d1, d2) => priorityMap.ContainsKey((CodeFixProvider)d1.Provider) ? (priorityMap.ContainsKey((CodeFixProvider)d2.Provider) ? priorityMap[(CodeFixProvider)d1.Provider] - priorityMap[(CodeFixProvider)d2.Provider] : -1) : 1);
            }

            // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive
            if (document.Project.Solution.Workspace.Kind != WorkspaceKind.Interactive && includeSuppressionFixes)
            {
                foreach (var spanAndDiagnostic in aggregatedDiagnostics)
                {
                    await AppendSuppressionsAsync(
                        document, spanAndDiagnostic.Key, spanAndDiagnostic.Value,
                        result, cancellationToken).ConfigureAwait(false);
                }
            }

            return(result.ToImmutableAndFree());
        }
            public override async Task <IEnumerable <Diagnostic> > GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken)
            {
                bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id);

                var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync(document, fixAllSpan, shouldIncludeDiagnostic, _includeSuppressedDiagnostics, cancellationToken : cancellationToken).ConfigureAwait(false);

                Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null));
                return(await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false));
            }
Exemplo n.º 6
0
        public async Task <IEnumerable <CodeFixCollection> > GetFixesAsync(Document document, TextSpan range, bool includeSuppressionFixes, CancellationToken cancellationToken)
        {
            // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back
            // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information
            // (if it is up-to-date) or synchronously do the work at the spot.
            //
            // this design's weakness is that each side don't have enough information to narrow down works to do. it will most likely always do more works than needed.
            // sometimes way more than it is needed. (compilation)
            Dictionary <TextSpan, List <DiagnosticData> > aggregatedDiagnostics = null;

            foreach (var diagnostic in await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, cancellationToken).ConfigureAwait(false))
            {
                cancellationToken.ThrowIfCancellationRequested();

                aggregatedDiagnostics = aggregatedDiagnostics ?? new Dictionary <TextSpan, List <DiagnosticData> >();
                aggregatedDiagnostics.GetOrAdd(diagnostic.TextSpan, _ => new List <DiagnosticData>()).Add(diagnostic);
            }

            var result = new List <CodeFixCollection>();

            if (aggregatedDiagnostics == null)
            {
                return(result);
            }

            foreach (var spanAndDiagnostic in aggregatedDiagnostics)
            {
                result = await AppendFixesAsync(document, spanAndDiagnostic.Key, spanAndDiagnostic.Value, result, cancellationToken).ConfigureAwait(false);
            }

            if (result.Any())
            {
                // sort the result to the order defined by the fixers
                var priorityMap = _fixerPriorityMap[document.Project.Language].Value;
                result.Sort((d1, d2) => priorityMap.ContainsKey((CodeFixProvider)d1.Provider) ? (priorityMap.ContainsKey((CodeFixProvider)d2.Provider) ? priorityMap[(CodeFixProvider)d1.Provider] - priorityMap[(CodeFixProvider)d2.Provider] : -1) : 1);
            }

            if (includeSuppressionFixes)
            {
                foreach (var spanAndDiagnostic in aggregatedDiagnostics)
                {
                    result = await AppendSuppressionsAsync(document, spanAndDiagnostic.Key, spanAndDiagnostic.Value, result, cancellationToken).ConfigureAwait(false);
                }
            }

            return(result);
        }
        private async Task <ImmutableArray <(string diagnosticId, string?title)> > GetThirdPartyDiagnosticIdsAndTitlesAsync(Document document, CancellationToken cancellationToken)
        {
            var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var range = new TextSpan(0, tree.Length);

            // Compute diagnostics for everything that is not an IDE analyzer
            var diagnostics = (await _diagnosticService.GetDiagnosticsForSpanAsync(document, range,
                                                                                   shouldIncludeDiagnostic: static diagnosticId => !(IDEDiagnosticIdToOptionMappingHelper.IsKnownIDEDiagnosticId(diagnosticId)),
                                                                                   includeCompilerDiagnostics: true, includeSuppressedDiagnostics: false,
                                                                                   cancellationToken: cancellationToken).ConfigureAwait(false));

            // ensure more than just known diagnostics were returned
            if (!diagnostics.Any())
            {
                return(ImmutableArray <(string diagnosticId, string?title)> .Empty);
            }

            return(diagnostics.SelectAsArray(static d => (d.Id, d.Title)).Distinct());
 public static Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, Document document, TextSpan range, string? diagnosticId = null, bool includeSuppressedDiagnostics = false, Func<string, IDisposable?>? addOperationScope = null, CancellationToken cancellationToken = default)
     => service.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics, CodeActionRequestPriority.None, addOperationScope, cancellationToken);
Exemplo n.º 9
0
        public async Task<ImmutableArray<CodeFixCollection>> GetFixesAsync(Document document, TextSpan range, bool includeConfigurationFixes, bool isBlocking, Func<string, IDisposable?> addOperationScope, CancellationToken cancellationToken)
        {
            // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back
            // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information
            // (if it is up-to-date) or synchronously do the work at the spot.
            //
            // this design's weakness is that each side don't have enough information to narrow down works to do. it will most likely always do more works than needed.
            // sometimes way more than it is needed. (compilation)

            // group diagnostics by their diagnostics span
            // invariant: later code gathers & runs CodeFixProviders for diagnostics with one identical diagnostics span (that gets set later as CodeFixCollection's TextSpan)
            // order diagnostics by span.
            SortedDictionary<TextSpan, List<DiagnosticData>>? aggregatedDiagnostics = null;
            foreach (var diagnostic in await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, diagnosticIdOpt: null, includeConfigurationFixes, addOperationScope, cancellationToken).ConfigureAwait(false))
            {
                if (diagnostic.IsSuppressed)
                {
                    continue;
                }

                cancellationToken.ThrowIfCancellationRequested();

                aggregatedDiagnostics ??= new SortedDictionary<TextSpan, List<DiagnosticData>>();
                aggregatedDiagnostics.GetOrAdd(diagnostic.GetTextSpan(), _ => new List<DiagnosticData>()).Add(diagnostic);
            }

            if (aggregatedDiagnostics == null)
            {
                return ImmutableArray<CodeFixCollection>.Empty;
            }

            // Order diagnostics by DiagnosticId so the fixes are in a deterministic order.
            foreach (var diagnosticsWithSpan in aggregatedDiagnostics.Values)
            {
                diagnosticsWithSpan.Sort(s_diagnosticDataComparisonById);
            }

            // append fixes for all diagnostics with the same diagnostics span
            using var resultDisposer = ArrayBuilder<CodeFixCollection>.GetInstance(out var result);
            foreach (var spanAndDiagnostic in aggregatedDiagnostics)
            {
                await AppendFixesAsync(
                    document, spanAndDiagnostic.Key, spanAndDiagnostic.Value, fixAllForInSpan: false, isBlocking,
                    result, addOperationScope, cancellationToken).ConfigureAwait(false);
            }

            if (result.Count > 0)
            {
                // sort the result to the order defined by the fixers
                ImmutableDictionary<CodeFixProvider, int> priorityMap = _fixerPriorityMap[document.Project.Language].Value;
                result.Sort((d1, d2) => GetValue(d1).CompareTo(GetValue(d2)));

                int GetValue(CodeFixCollection c)
                    => priorityMap.TryGetValue((CodeFixProvider)c.Provider, out var value) ? value : int.MaxValue;
            }

            // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive
            if (document.Project.Solution.Workspace.Kind != WorkspaceKind.Interactive && includeConfigurationFixes)
            {
                foreach (var spanAndDiagnostic in aggregatedDiagnostics)
                {
                    await AppendConfigurationsAsync(
                        document, spanAndDiagnostic.Key, spanAndDiagnostic.Value,
                        result, cancellationToken).ConfigureAwait(false);
                }
            }

            return result.ToImmutable();
        }