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));
                    }
                }
            }
        }
Esempio n. 2
0
        public async Task TestCodeActionHasCorrectDiagnostics()
        {
            var markup =
                @"class A
{
    void M()
    {
        {|caret:|}Task.Delay(1);
    }
}";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            testLspServer.InitializeDiagnostics(BackgroundAnalysisScope.ActiveFile, DiagnosticMode.Default,
                                                new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()));
            await testLspServer.WaitForDiagnosticsAsync();

            var caret            = testLspServer.GetLocations("caret").Single();
            var codeActionParams = new LSP.CodeActionParams
            {
                TextDocument = CreateTextDocumentIdentifier(caret.Uri),
                Range        = caret.Range,
                Context      = new LSP.CodeActionContext
                {
                    Diagnostics = new[]
                    {
                        new LSP.Diagnostic
                        {
                            Code = AddImportDiagnosticIds.CS0103
                        },
                        new LSP.Diagnostic
                        {
                            Code = "SomeCode"
                        }
                    }
                }
            };

            var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams);

            var addImport = results.FirstOrDefault(r => r.Title.Contains($"using System.Threading.Tasks"));

            Assert.Equal(1, addImport.Diagnostics.Length);
            Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code.Value);
        }
Esempio n. 3
0
        public async Task TestCodeActionHasCorrectDiagnostics()
        {
            var markup =
                @"class A
{
    void M()
    {
        {|caret:|}Task.Delay(1);
    }
}";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var caret            = testLspServer.GetLocations("caret").Single();
            var codeActionParams = new LSP.CodeActionParams
            {
                TextDocument = CreateTextDocumentIdentifier(caret.Uri),
                Range        = caret.Range,
                Context      = new LSP.CodeActionContext
                {
                    Diagnostics = new[]
                    {
                        new LSP.Diagnostic
                        {
                            Code = AddImportDiagnosticIds.CS0103
                        },
                        new LSP.Diagnostic
                        {
                            Code = "SomeCode"
                        }
                    }
                }
            };

            var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams);

            var addImport = results.FirstOrDefault(r => r.Title.Contains($"using System.Threading.Tasks"));

            Assert.Equal(1, addImport.Diagnostics.Length);
            Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code.Value);
        }
Esempio n. 4
0
 /// <summary>
 /// Answers a code action request by returning the code actions for the document and range.
 /// https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction
 /// </summary>
 /// <param name="solution">the solution containing the document.</param>
 /// <param name="request">the document and range to get code actions for.</param>
 /// <param name="clientCapabilities">the client capabilities for the request.</param>
 /// <param name="cancellationToken">a cancellation token.</param>
 /// <returns>a list of commands representing code actions.</returns>
 public Task <object[]> GetCodeActionsAsync(Solution solution, LSP.CodeActionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
 => ExecuteRequestAsync <LSP.CodeActionParams, object[]>(LSP.Methods.TextDocumentCodeActionName, solution, request, clientCapabilities, cancellationToken);
Esempio n. 5
0
        public async Task <LSP.SumType <LSP.Command, LSP.CodeAction>[]> HandleRequestAsync(Solution solution, LSP.CodeActionParams request,
                                                                                           LSP.ClientCapabilities clientCapabilities, string?clientName, CancellationToken cancellationToken)
        {
            var codeActions = await GetCodeActionsAsync(solution,
                                                        request.TextDocument,
                                                        request.Range,
                                                        clientName, cancellationToken).ConfigureAwait(false);

            // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options.
            codeActions = codeActions.Where(c => !(c is CodeActionWithOptions));

            var result = new ArrayBuilder <LSP.SumType <LSP.Command, LSP.CodeAction> >();

            foreach (var codeAction in codeActions)
            {
                // Always return the Command instead of a precalculated set of workspace edits.
                // The edits will be calculated when the code action is either previewed or
                // invoked.

                // It's safe for the client to pass back the range/filename in the command to run
                // on the server because the client will always re-issue a get code actions request
                // before invoking a preview or running the command on the server.

                result.Add(
                    new LSP.Command
                {
                    CommandIdentifier = RunCodeActionCommandName,
                    Title             = codeAction.Title,
                    Arguments         = new object[]
                    {
                        new RunCodeActionParams
                        {
                            CodeActionParams = request,
                            Title            = codeAction.Title
                        }
                    }
                });
            }

            return(result.ToArrayAndFree());
        }