Skip to content

Commit ba826dc

Browse files
authored
Merge pull request #3440 from savpek/feature/fix-all-providers
Fix all providers support
2 parents bc3ecb3 + 3769538 commit ba826dc

File tree

7 files changed

+214
-2
lines changed

7 files changed

+214
-2
lines changed

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,21 @@
854854
"title": "Select Project",
855855
"category": "OmniSharp"
856856
},
857+
{
858+
"command": "o.fixAll.solution",
859+
"title": "Fix all code issues in solution",
860+
"category": "OmniSharp"
861+
},
862+
{
863+
"command": "o.fixAll.project",
864+
"title": "Fix all code issues in project",
865+
"category": "OmniSharp"
866+
},
867+
{
868+
"command": "o.fixAll.document",
869+
"title": "Fix all code issues in document",
870+
"category": "OmniSharp"
871+
},
857872
{
858873
"command": "o.reanalyze.allProjects",
859874
"title": "Analyze all projects",

src/features/commands.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
2929
disposable.add(vscode.commands.registerCommand('o.restart', async () => restartOmniSharp(context, server, optionProvider)));
3030
disposable.add(vscode.commands.registerCommand('o.pickProjectAndStart', async () => pickProjectAndStart(server, optionProvider)));
3131
disposable.add(vscode.commands.registerCommand('o.showOutput', () => eventStream.post(new ShowOmniSharpChannel())));
32+
3233
disposable.add(vscode.commands.registerCommand('dotnet.restore.project', async () => pickProjectAndDotnetRestore(server, eventStream)));
3334
disposable.add(vscode.commands.registerCommand('dotnet.restore.all', async () => dotnetRestoreAllProjects(server, eventStream)));
3435

@@ -43,10 +44,8 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
4344
let attachItemsProvider = DotNetAttachItemsProviderFactory.Get();
4445
let attacher = new AttachPicker(attachItemsProvider);
4546
disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => attacher.ShowAttachEntries()));
46-
4747
// Register command for generating tasks.json and launch.json assets.
4848
disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
49-
5049
// Register command for remote process picker for attach
5150
disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', async (args) => RemoteAttachPicker.ShowAttachEntries(args, platformInfo)));
5251

src/features/fixAll.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import AbstractProvider from "./abstractProvider";
7+
8+
export class FixAll extends AbstractProvider
9+
{
10+
}

src/features/fixAllProvider.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import * as serverUtils from '../omnisharp/utils';
8+
import * as protocol from '../omnisharp/protocol';
9+
import { OmniSharpServer } from '../omnisharp/server';
10+
import { FixAllScope, FixAllItem, FileModificationType } from '../omnisharp/protocol';
11+
import { Uri } from 'vscode';
12+
import CompositeDisposable from '../CompositeDisposable';
13+
import AbstractProvider from './abstractProvider';
14+
import { toRange2 } from '../omnisharp/typeConversion';
15+
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
16+
17+
export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider {
18+
public constructor(private server: OmniSharpServer, languageMiddlewareFeature: LanguageMiddlewareFeature) {
19+
super(server, languageMiddlewareFeature);
20+
let disposable = new CompositeDisposable();
21+
disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => this.fixAllMenu(server, protocol.FixAllScope.Solution)));
22+
disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => this.fixAllMenu(server, protocol.FixAllScope.Project)));
23+
disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => this.fixAllMenu(server, protocol.FixAllScope.Document)));
24+
this.addDisposables(disposable);
25+
}
26+
27+
public async provideCodeActions(
28+
document: vscode.TextDocument,
29+
_range: vscode.Range | vscode.Selection,
30+
context: vscode.CodeActionContext,
31+
_token: vscode.CancellationToken,
32+
): Promise<vscode.CodeAction[]> {
33+
console.log(context);
34+
if (!context.only) {
35+
return [];
36+
}
37+
38+
if (context.only.value === "source.fixAll.csharp") {
39+
await this.applyFixes(document.fileName, FixAllScope.Document, undefined);
40+
}
41+
42+
return [];
43+
}
44+
45+
private async fixAllMenu(server: OmniSharpServer, scope: protocol.FixAllScope): Promise<void> {
46+
let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope });
47+
48+
let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`);
49+
50+
if (scope === protocol.FixAllScope.Document) {
51+
targets = ["Fix all issues", ...targets];
52+
}
53+
54+
return vscode.window.showQuickPick(targets, {
55+
matchOnDescription: true,
56+
placeHolder: `Select fix all action`
57+
}).then(async selectedAction => {
58+
let filter: FixAllItem[] = undefined;
59+
60+
if (selectedAction === undefined) {
61+
return;
62+
}
63+
64+
if (selectedAction !== "Fix all issues") {
65+
let actionTokens = selectedAction.split(":");
66+
filter = [{ Id: actionTokens[0], Message: actionTokens[1] }];
67+
}
68+
69+
await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter);
70+
});
71+
}
72+
73+
private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise<boolean | string | {}> {
74+
let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter, WantsAllCodeActionOperations: true, WantsTextChanges: true });
75+
76+
if (response && Array.isArray(response.Changes)) {
77+
let edit = new vscode.WorkspaceEdit();
78+
79+
let fileToOpen: Uri = null;
80+
let renamedFiles: Uri[] = [];
81+
82+
for (let change of response.Changes) {
83+
if (change.ModificationType == FileModificationType.Renamed)
84+
{
85+
// The file was renamed. Omnisharp has already persisted
86+
// the right changes to disk. We don't need to try to
87+
// apply text changes (and will skip this file if we see an edit)
88+
renamedFiles.push(Uri.file(change.FileName));
89+
}
90+
}
91+
92+
for (let change of response.Changes) {
93+
if (change.ModificationType == FileModificationType.Opened)
94+
{
95+
// The CodeAction requested that we open a file.
96+
// Record that file name and keep processing CodeActions.
97+
// If a CodeAction requests that we open multiple files
98+
// we only open the last one (what would it mean to open multiple files?)
99+
fileToOpen = vscode.Uri.file(change.FileName);
100+
}
101+
102+
if (change.ModificationType == FileModificationType.Modified)
103+
{
104+
let uri = vscode.Uri.file(change.FileName);
105+
if (renamedFiles.some(r => r == uri))
106+
{
107+
// This file got renamed. Omnisharp has already
108+
// persisted the new file with any applicable changes.
109+
continue;
110+
}
111+
112+
let edits: vscode.TextEdit[] = [];
113+
for (let textChange of change.Changes) {
114+
edits.push(vscode.TextEdit.replace(toRange2(textChange), textChange.NewText));
115+
}
116+
117+
edit.set(uri, edits);
118+
}
119+
}
120+
121+
let applyEditPromise = vscode.workspace.applyEdit(edit);
122+
123+
// Unfortunately, the textEditor.Close() API has been deprecated
124+
// and replaced with a command that can only close the active editor.
125+
// If files were renamed that weren't the active editor, their tabs will
126+
// be left open and marked as "deleted" by VS Code
127+
let next = applyEditPromise;
128+
if (renamedFiles.some(r => r.fsPath == vscode.window.activeTextEditor.document.uri.fsPath))
129+
{
130+
next = applyEditPromise.then(_ =>
131+
{
132+
return vscode.commands.executeCommand("workbench.action.closeActiveEditor");
133+
});
134+
}
135+
136+
return fileToOpen != null
137+
? next.then(_ =>
138+
{
139+
return vscode.commands.executeCommand("vscode.open", fileToOpen);
140+
})
141+
: next;
142+
}
143+
}
144+
}

src/omnisharp/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import trackVirtualDocuments from '../features/virtualDocumentTracker';
3838
import { StructureProvider } from '../features/structureProvider';
3939
import { OmniSharpMonoResolver } from './OmniSharpMonoResolver';
4040
import { getMonoVersion } from '../utils/getMonoVersion';
41+
import { FixAllProvider } from '../features/fixAllProvider';
4142
import { LanguageMiddlewareFeature } from './LanguageMiddlewareFeature';
4243
import SemanticTokensProvider from '../features/semanticTokensProvider';
4344

@@ -95,6 +96,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
9596
const codeActionProvider = new CodeActionProvider(server, optionProvider, languageMiddlewareFeature);
9697
localDisposables.add(codeActionProvider);
9798
localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider));
99+
localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, new FixAllProvider(server, languageMiddlewareFeature)));
98100
localDisposables.add(reportDiagnostics(server, advisor, languageMiddlewareFeature));
99101
localDisposables.add(forwardChanges(server));
100102
localDisposables.add(trackVirtualDocuments(server, eventStream));

src/omnisharp/protocol.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export module Requests {
2828
export const TypeLookup = '/typelookup';
2929
export const UpdateBuffer = '/updatebuffer';
3030
export const Metadata = '/metadata';
31+
export const RunFixAll = '/runfixall';
32+
export const GetFixAll = '/getfixall';
3133
export const ReAnalyze = '/reanalyze';
3234
export const QuickInfo = '/quickinfo';
3335
}
@@ -254,6 +256,20 @@ export interface GetCodeActionsResponse {
254256
CodeActions: string[];
255257
}
256258

259+
export interface RunFixAllActionResponse {
260+
Text: string;
261+
Changes: ModifiedFileResponse[];
262+
}
263+
264+
export interface FixAllItem {
265+
Id: string;
266+
Message: string;
267+
}
268+
269+
export interface GetFixAllResponse {
270+
Items: FixAllItem[];
271+
}
272+
257273
export interface SyntaxFeature {
258274
Name: string;
259275
Data: string;
@@ -474,6 +490,24 @@ export enum FileChangeType {
474490
DirectoryDelete = "DirectoryDelete"
475491
}
476492

493+
export enum FixAllScope {
494+
Document = "Document",
495+
Project = "Project",
496+
Solution = "Solution"
497+
}
498+
499+
export interface GetFixAllRequest extends FileBasedRequest {
500+
Scope: FixAllScope;
501+
FixAllFilter?: FixAllItem[];
502+
}
503+
504+
export interface RunFixAllRequest extends FileBasedRequest {
505+
Scope: FixAllScope;
506+
FixAllFilter?: FixAllItem[];
507+
WantsTextChanges: boolean;
508+
WantsAllCodeActionOperations: boolean;
509+
}
510+
477511
export interface QuickInfoRequest extends Request {
478512
}
479513

src/omnisharp/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ export async function findSymbols(server: OmniSharpServer, request: protocol.Fin
4343
return server.makeRequest<protocol.FindSymbolsResponse>(protocol.Requests.FindSymbols, request, token);
4444
}
4545

46+
export async function runFixAll(server: OmniSharpServer, request: protocol.RunFixAllRequest): Promise<protocol.RunFixAllActionResponse> {
47+
return server.makeRequest<protocol.RunFixAllActionResponse>(protocol.Requests.RunFixAll, request);
48+
}
49+
50+
export async function getFixAll(server: OmniSharpServer, request: protocol.GetFixAllRequest): Promise<protocol.GetFixAllResponse> {
51+
return server.makeRequest<protocol.GetFixAllResponse>(protocol.Requests.GetFixAll, request);
52+
}
53+
4654
export async function findUsages(server: OmniSharpServer, request: protocol.FindUsagesRequest, token: vscode.CancellationToken) {
4755
return server.makeRequest<protocol.QuickFixResponse>(protocol.Requests.FindUsages, request, token);
4856
}

0 commit comments

Comments
 (0)