Skip to content

Commit

Permalink
Merge pull request #4988 from 50Wliu/modernize-code-action-provider
Browse files Browse the repository at this point in the history
Modernize code action provider
  • Loading branch information
JoeRobich authored Jan 10, 2022
2 parents d20cd03 + 67b1e02 commit 201a495
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 60 deletions.
84 changes: 30 additions & 54 deletions src/features/codeActionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,73 +13,47 @@ import OptionProvider from '../observers/OptionProvider';
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
import { buildEditForResponse } from '../omnisharp/fileOperationsResponseEditBuilder';

export default class CodeActionProvider extends AbstractProvider implements vscode.CodeActionProvider {

export default class CodeActionProvider extends AbstractProvider implements vscode.CodeActionProvider<vscode.CodeAction> {
private _commandId: string;

constructor(server: OmniSharpServer, private optionProvider: OptionProvider, languageMiddlewareFeature: LanguageMiddlewareFeature) {
super(server, languageMiddlewareFeature);
this._commandId = 'omnisharp.runCodeAction';
let registerCommandDisposable = vscode.commands.registerCommand(this._commandId, this._runCodeAction, this);
const registerCommandDisposable = vscode.commands.registerCommand(this._commandId, this._runCodeAction, this);
this.addDisposables(new CompositeDisposable(registerCommandDisposable));
}

public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.Command[]> {
let options = this.optionProvider.GetLatestOptions();
public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[]> {
const options = this.optionProvider.GetLatestOptions();
if (options.disableCodeActions) {
return;
}

let line: number;
let column: number;
let selection: protocol.V2.Range;

// VS Code will pass the range of the word at the editor caret, even if there isn't a selection.
// To ensure that we don't suggest selection-based refactorings when there isn't a selection, we first
// find the text editor for this document and verify that there is a selection.
let editor = vscode.window.visibleTextEditors.find(e => e.document === document);
if (editor) {
if (editor.selection.isEmpty) {
// The editor does not have a selection. Use the active position of the selection (i.e. the caret).
let active = editor.selection.active;
const line = range.start.line;
const column = range.start.character;

line = active.line;
column = active.character;
}
else {
// The editor has a selection. Use it.
let start = editor.selection.start;
let end = editor.selection.end;
const request: protocol.V2.GetCodeActionsRequest = {
FileName: document.fileName,
Line: line,
Column: column,
};

selection = {
Start: { Line: start.line, Column: start.character },
End: { Line: end.line, Column: end.character }
};
}
}
else {
// We couldn't find the editor, so just use the range we were provided.
selection = {
// Only suggest selection-based refactorings when a selection exists.
// If there is no selection and the editor isn't focused,
// VS Code will pass us an empty Selection rather than a Range,
// hence the extra range.isEmpty check.
if (range instanceof vscode.Selection && !range.isEmpty) {
request.Selection = {
Start: { Line: range.start.line, Column: range.start.character },
End: { Line: range.end.line, Column: range.end.character }
};
}

let request: protocol.V2.GetCodeActionsRequest = {
FileName: document.fileName,
Line: line,
Column: column,
Selection: selection
};

try {
let response = await serverUtils.getCodeActions(this._server, request, token);
const response = await serverUtils.getCodeActions(this._server, request, token);
return response.CodeActions.map(codeAction => {
let runRequest: protocol.V2.RunCodeActionRequest = {
FileName: document.fileName,
Line: line,
Column: column,
Selection: selection,
const runRequest: protocol.V2.RunCodeActionRequest = {
...request,
Identifier: codeAction.Identifier,
WantsTextChanges: true,
WantsAllCodeActionOperations: true,
Expand All @@ -88,24 +62,26 @@ export default class CodeActionProvider extends AbstractProvider implements vsco

return {
title: codeAction.Name,
command: this._commandId,
arguments: [runRequest, token]
command: {
title: codeAction.Name,
command: this._commandId,
arguments: [runRequest, token]
},
};
});
}
catch (error) {
} catch (error) {
return Promise.reject(`Problem invoking 'GetCodeActions' on OmniSharp server: ${error}`);
}
}

private async _runCodeAction(req: protocol.V2.RunCodeActionRequest, token: vscode.CancellationToken): Promise<boolean | string | {}> {

return serverUtils.runCodeAction(this._server, req).then(async response => {
try {
const response = await serverUtils.runCodeAction(this._server, req);
if (response) {
return buildEditForResponse(response.Changes, this._languageMiddlewareFeature, token);
}
}, async (error) => {
} catch (error) {
return Promise.reject(`Problem invoking 'RunCodeAction' on OmniSharp server: ${error}`);
});
}
}
}
10 changes: 4 additions & 6 deletions test/integrationTests/codeActionRename.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ suite(`Code Action Rename ${testAssetWorkspace.description}`, function () {

test("Code actions can rename and open files", async () => {
await vscode.commands.executeCommand("vscode.open", fileUri);
let c = await vscode.commands.executeCommand("vscode.executeCodeActionProvider", fileUri, new vscode.Range(0, 7, 0, 7)) as { command: string, title: string, arguments: string[] }[];
let command = c.find(
(s) => { return s.title == "Rename file to C.cs"; }
);
expect(command, "Didn't find rename class command");
const codeActions = await vscode.commands.executeCommand<vscode.CodeAction[]>("vscode.executeCodeActionProvider", fileUri, new vscode.Range(0, 7, 0, 7));
const codeAction = codeActions.find(codeAction => codeAction.title == "Rename file to C.cs");
expect(codeAction, "Didn't find rename class code action");

await vscode.commands.executeCommand(command.command, ...command.arguments);
await vscode.commands.executeCommand(codeAction.command.command, ...codeAction.command.arguments);

await assertWithPoll(() => { }, 15 * 1000, 500, _ => expect(vscode.window.activeTextEditor.document.fileName).contains("C.cs"));
});
Expand Down

0 comments on commit 201a495

Please sign in to comment.