예제 #1
0
        /// <summary>
        /// Create a remote code action wrapping an LSP code action.
        /// </summary>
        public RoslynRemoteCodeAction(Document document, LSP.Command codeActionCommand, LSP.WorkspaceEdit codeActionWorkspaceEdit, string title, ILanguageServerClient lspClient)
        {
            _document  = document ?? throw new ArgumentNullException(nameof(document));
            _lspClient = lspClient ?? throw new ArgumentNullException(nameof(lspClient));

            _codeActionCommand       = codeActionCommand;
            _codeActionWorkspaceEdit = codeActionWorkspaceEdit ?? throw new ArgumentNullException(nameof(codeActionWorkspaceEdit));
            _title = title;

            _command = null;
        }
예제 #2
0
        internal static LSP.VSCodeAction CreateCodeAction(
            string title, LSP.CodeActionKind kind, LSP.VSCodeAction[] children,
            CodeActionResolveData data, LSP.Diagnostic[] diagnostics,
            LSP.WorkspaceEdit edit = null, LSP.Command command = null)
        {
            var action = new LSP.VSCodeAction
            {
                Title       = title,
                Kind        = kind,
                Children    = children,
                Data        = JToken.FromObject(data),
                Diagnostics = diagnostics,
                Edit        = edit,
                Command     = command
            };

            return(action);
        }
예제 #3
0
        public async Task <object[]> HandleRequestAsync(Solution solution, LSP.CodeActionParams request,
                                                        LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            var codeActions = await GetCodeActionsAsync(solution,
                                                        request.TextDocument.Uri,
                                                        request.Range,
                                                        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 commands = new ArrayBuilder <LSP.Command>();

            foreach (var codeAction in codeActions)
            {
                object[] remoteCommandArguments;
                // If we have a codeaction with a single applychangesoperation, we want to send the codeaction with the edits.
                var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false);

                var clientSupportsWorkspaceEdits = true;
                if (clientCapabilities?.Experimental is JObject clientCapabilitiesExtensions)
                {
                    clientSupportsWorkspaceEdits = clientCapabilitiesExtensions.SelectToken("supportsWorkspaceEdits")?.Value <bool>() ?? clientSupportsWorkspaceEdits;
                }

                if (clientSupportsWorkspaceEdits && operations.Length == 1 && operations.First() is ApplyChangesOperation applyChangesOperation)
                {
                    var workspaceEdit = new LSP.WorkspaceEdit {
                        Changes = new Dictionary <string, LSP.TextEdit[]>()
                    };
                    var changes          = applyChangesOperation.ChangedSolution.GetChanges(solution);
                    var changedDocuments = changes.GetProjectChanges().SelectMany(pc => pc.GetChangedDocuments());

                    foreach (var docId in changedDocuments)
                    {
                        var newDoc  = applyChangesOperation.ChangedSolution.GetDocument(docId);
                        var oldDoc  = solution.GetDocument(docId);
                        var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);

                        var textChanges = await newDoc.GetTextChangesAsync(oldDoc).ConfigureAwait(false);

                        var edits = textChanges.Select(tc => new LSP.TextEdit
                        {
                            NewText = tc.NewText,
                            Range   = ProtocolConversions.TextSpanToRange(tc.Span, oldText)
                        });

                        workspaceEdit.Changes.Add(newDoc.FilePath, edits.ToArray());
                    }

                    remoteCommandArguments = new object[] { new LSP.CodeAction {
                                                                Title = codeAction.Title, Edit = workspaceEdit
                                                            } };
                }
                // Otherwise, send the original request to be executed on the host.
                else
                {
                    // Note that we can pass through the params for this
                    // request (like range, filename) because between getcodeaction and runcodeaction there can be no
                    // changes on the IDE side (it will requery for codeactions if there are changes).
                    remoteCommandArguments = new object[]
                    {
                        new LSP.Command
                        {
                            CommandIdentifier = RunCodeActionCommandName,
                            Title             = codeAction.Title,
                            Arguments         = new object[]
                            {
                                new RunCodeActionParams
                                {
                                    CodeActionParams = request,
                                    Title            = codeAction.Title
                                }
                            }
                        }
                    };
                }

                // We need to return a command that is a generic wrapper that VS Code can execute.
                // The argument to this wrapper will either be a RunCodeAction command which will carry
                // enough information to run the command or a CodeAction with the edits.
                var command = new LSP.Command
                {
                    Title             = codeAction.Title,
                    CommandIdentifier = $"{RemoteCommandNamePrefix}.{ProviderName}",
                    Arguments         = remoteCommandArguments
                };

                commands.Add(command);
            }

            return(commands.ToArrayAndFree());
        }