From b03447babd7e57dd4f0002f246c8900b86170d2a Mon Sep 17 00:00:00 2001 From: Savpek Date: Tue, 9 Jul 2019 20:54:52 +0300 Subject: [PATCH 01/13] First e2e version of fix all. --- package.json | 10 ++++++++++ src/features/commands.ts | 10 ++++++++++ src/omnisharp/protocol.ts | 1 + src/omnisharp/utils.ts | 4 ++++ 4 files changed, 25 insertions(+) diff --git a/package.json b/package.json index b53da6613..3744985ab 100644 --- a/package.json +++ b/package.json @@ -793,6 +793,16 @@ "title": "Select Project", "category": "OmniSharp" }, + { + "command": "o.fixAll.solution", + "title": "Fix all code issues in solution", + "category": "OmniSharp" + }, + { + "command": "o.fixAll.project", + "title": "Fix all code issues in project", + "category": "OmniSharp" + }, { "command": "dotnet.generateAssets", "title": "Generate Assets for Build and Debug", diff --git a/src/features/commands.ts b/src/features/commands.ts index 2d8802e93..492e7c05d 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -29,6 +29,11 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('o.restart', () => restartOmniSharp(server))); disposable.add(vscode.commands.registerCommand('o.pickProjectAndStart', async () => pickProjectAndStart(server, optionProvider))); disposable.add(vscode.commands.registerCommand('o.showOutput', () => eventStream.post(new ShowOmniSharpChannel()))); + + // Todo these should really open new menu that lists correct options... + disposable.add(vscode.commands.registerCommand('o.fixAll.solution', () => fixAllTemporary(server))); + disposable.add(vscode.commands.registerCommand('o.fixAll.project', () => fixAllTemporary(server))); + disposable.add(vscode.commands.registerCommand('dotnet.restore.project', async () => pickProjectAndDotnetRestore(server, eventStream))); disposable.add(vscode.commands.registerCommand('dotnet.restore.all', async () => dotnetRestoreAllProjects(server, eventStream))); @@ -57,6 +62,11 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: return new CompositeDisposable(disposable); } +// This should be replaced with method that opens menu. +async function fixAllTemporary(server: OmniSharpServer): Promise { + await serverUtils.fixAll(server, {}); +} + function restartOmniSharp(server: OmniSharpServer) { if (server.isRunning()) { server.restart(); diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index a34957001..2538d8a63 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -28,6 +28,7 @@ export module Requests { export const TypeLookup = '/typelookup'; export const UpdateBuffer = '/updatebuffer'; export const Metadata = '/metadata'; + export const FixAll = '/fixall'; } export namespace WireProtocol { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index a488083dd..2590df51c 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -35,6 +35,10 @@ export async function findSymbols(server: OmniSharpServer, request: protocol.Fin return server.makeRequest(protocol.Requests.FindSymbols, request, token); } +export async function fixAll(server: OmniSharpServer, request: any) { + return server.makeRequest(protocol.Requests.FixAll, request); +} + export async function findUsages(server: OmniSharpServer, request: protocol.FindUsagesRequest, token: vscode.CancellationToken) { return server.makeRequest(protocol.Requests.FindUsages, request, token); } From cb213b47b56f7f95af125995ae8a1b936e4c6f11 Mon Sep 17 00:00:00 2001 From: Savpek Date: Tue, 16 Jul 2019 20:55:05 +0300 Subject: [PATCH 02/13] On very very basic level fix all works. --- package-lock.json | 41 +++++++++++---------------------------- package.json | 22 ++++++++++----------- src/features/commands.ts | 16 ++++++++++++++- src/features/fixAll.ts | 10 ++++++++++ src/omnisharp/protocol.ts | 8 +++++++- src/omnisharp/utils.ts | 4 ++-- 6 files changed, 56 insertions(+), 45 deletions(-) create mode 100644 src/features/fixAll.ts diff --git a/package-lock.json b/package-lock.json index b1c9213c1..6c1814de5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3608,8 +3608,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -3630,14 +3629,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3652,20 +3649,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3782,8 +3776,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3795,7 +3788,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3810,7 +3802,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3818,14 +3809,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3844,7 +3833,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3925,8 +3913,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3938,7 +3925,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4024,8 +4010,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -4061,7 +4046,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4081,7 +4065,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4125,14 +4108,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/package.json b/package.json index 3744985ab..a80b725b4 100644 --- a/package.json +++ b/package.json @@ -486,12 +486,12 @@ "items": { "type": "string" }, - "description": "Array of symbol server URLs (example: http\u200b://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", + "description": "Array of symbol server URLs (example: http​://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", "default": [] }, "searchMicrosoftSymbolServer": { "type": "boolean", - "description": "If 'true' the Microsoft Symbol server (https\u200b://msdl.microsoft.com\u200b/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", + "description": "If 'true' the Microsoft Symbol server (https​://msdl.microsoft.com​/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", "default": false }, "cachePath": { @@ -1360,12 +1360,12 @@ "items": { "type": "string" }, - "description": "Array of symbol server URLs (example: http\u200b://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", + "description": "Array of symbol server URLs (example: http​://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", "default": [] }, "searchMicrosoftSymbolServer": { "type": "boolean", - "description": "If 'true' the Microsoft Symbol server (https\u200b://msdl.microsoft.com\u200b/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", + "description": "If 'true' the Microsoft Symbol server (https​://msdl.microsoft.com​/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", "default": false }, "cachePath": { @@ -1771,12 +1771,12 @@ "items": { "type": "string" }, - "description": "Array of symbol server URLs (example: http\u200b://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", + "description": "Array of symbol server URLs (example: http​://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", "default": [] }, "searchMicrosoftSymbolServer": { "type": "boolean", - "description": "If 'true' the Microsoft Symbol server (https\u200b://msdl.microsoft.com\u200b/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", + "description": "If 'true' the Microsoft Symbol server (https​://msdl.microsoft.com​/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", "default": false }, "cachePath": { @@ -2428,12 +2428,12 @@ "items": { "type": "string" }, - "description": "Array of symbol server URLs (example: http\u200b://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", + "description": "Array of symbol server URLs (example: http​://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", "default": [] }, "searchMicrosoftSymbolServer": { "type": "boolean", - "description": "If 'true' the Microsoft Symbol server (https\u200b://msdl.microsoft.com\u200b/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", + "description": "If 'true' the Microsoft Symbol server (https​://msdl.microsoft.com​/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", "default": false }, "cachePath": { @@ -2839,12 +2839,12 @@ "items": { "type": "string" }, - "description": "Array of symbol server URLs (example: http\u200b://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", + "description": "Array of symbol server URLs (example: http​://MyExampleSymbolServer) or directories (example: /build/symbols) to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to.", "default": [] }, "searchMicrosoftSymbolServer": { "type": "boolean", - "description": "If 'true' the Microsoft Symbol server (https\u200b://msdl.microsoft.com\u200b/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", + "description": "If 'true' the Microsoft Symbol server (https​://msdl.microsoft.com​/download/symbols) is added to the symbols search path. If unspecified, this option defaults to 'false'.", "default": false }, "cachePath": { @@ -2979,4 +2979,4 @@ ] } } -} \ No newline at end of file +} diff --git a/src/features/commands.ts b/src/features/commands.ts index 492e7c05d..4f7970670 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -23,6 +23,7 @@ import reportIssue from './reportIssue'; import setNextStatement from '../coreclr-debug/setNextStatement'; import { IMonoResolver } from '../constants/IMonoResolver'; import { getDotnetInfo } from '../utils/getDotnetInfo'; +import { WorkspaceEdit } from 'vscode'; export default function registerCommands(server: OmniSharpServer, platformInfo: PlatformInformation, eventStream: EventStream, optionProvider: OptionProvider, monoResolver: IMonoResolver, packageJSON: any, extensionPath: string): CompositeDisposable { let disposable = new CompositeDisposable(); @@ -64,7 +65,20 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: // This should be replaced with method that opens menu. async function fixAllTemporary(server: OmniSharpServer): Promise { - await serverUtils.fixAll(server, {}); + let response = await serverUtils.fixAll(server, { FileName: "not_used_yet"}); + + response.Changes.forEach(change => { + const uri = vscode.Uri.file(change.FileName); + + let edits: WorkspaceEdit = new WorkspaceEdit(); + change.Changes.forEach(change => { + edits.replace(uri, + new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), + change.NewText); + }); + + vscode.workspace.applyEdit(edits); + }); } function restartOmniSharp(server: OmniSharpServer) { diff --git a/src/features/fixAll.ts b/src/features/fixAll.ts new file mode 100644 index 000000000..ee3caf613 --- /dev/null +++ b/src/features/fixAll.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import AbstractProvider from "./abstractProvider"; + +export class FixAll extends AbstractProvider +{ +} \ No newline at end of file diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 2538d8a63..5abfb200e 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -28,7 +28,8 @@ export module Requests { export const TypeLookup = '/typelookup'; export const UpdateBuffer = '/updatebuffer'; export const Metadata = '/metadata'; - export const FixAll = '/fixall'; + export const RunFixAll = '/runfixall'; + export const GetFixAll = '/getfixall'; } export namespace WireProtocol { @@ -253,6 +254,11 @@ export interface GetCodeActionsResponse { CodeActions: string[]; } +export interface RunFixAllActionResponse { + Text: string; + Changes: ModifiedFileResponse[]; +} + export interface SyntaxFeature { Name: string; Data: string; diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 2590df51c..27a3bfbbf 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -35,8 +35,8 @@ export async function findSymbols(server: OmniSharpServer, request: protocol.Fin return server.makeRequest(protocol.Requests.FindSymbols, request, token); } -export async function fixAll(server: OmniSharpServer, request: any) { - return server.makeRequest(protocol.Requests.FixAll, request); +export async function fixAll(server: OmniSharpServer, request: protocol.FileBasedRequest): Promise { + return server.makeRequest(protocol.Requests.RunFixAll, request); } export async function findUsages(server: OmniSharpServer, request: protocol.FindUsagesRequest, token: vscode.CancellationToken) { From 96eb096ab55aa3a044dcdf3af32f00bc80d9f0ae Mon Sep 17 00:00:00 2001 From: Savpek Date: Fri, 2 Aug 2019 12:41:11 +0300 Subject: [PATCH 03/13] Very simple version that fetches actions and executes selected. --- src/features/commands.ts | 37 ++++++++++++++++++++++++------------- src/omnisharp/protocol.ts | 25 +++++++++++++++++++++++++ src/omnisharp/utils.ts | 6 +++++- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/features/commands.ts b/src/features/commands.ts index 4f7970670..8b172d07b 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -32,8 +32,8 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('o.showOutput', () => eventStream.post(new ShowOmniSharpChannel()))); // Todo these should really open new menu that lists correct options... - disposable.add(vscode.commands.registerCommand('o.fixAll.solution', () => fixAllTemporary(server))); - disposable.add(vscode.commands.registerCommand('o.fixAll.project', () => fixAllTemporary(server))); + disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => fixAllTemporary(server))); + disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => fixAllTemporary(server))); disposable.add(vscode.commands.registerCommand('dotnet.restore.project', async () => pickProjectAndDotnetRestore(server, eventStream))); disposable.add(vscode.commands.registerCommand('dotnet.restore.all', async () => dotnetRestoreAllProjects(server, eventStream))); @@ -65,19 +65,30 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: // This should be replaced with method that opens menu. async function fixAllTemporary(server: OmniSharpServer): Promise { - let response = await serverUtils.fixAll(server, { FileName: "not_used_yet"}); - - response.Changes.forEach(change => { - const uri = vscode.Uri.file(change.FileName); + let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: protocol.FixAllScope.Solution }); + let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); + + return vscode.window.showQuickPick(targets, { + matchOnDescription: true, + placeHolder: `Select fix all action` + }).then(async selectedAction => { + // action comes in form like "CS0000: Description message" + let actionTokens = selectedAction.split(":"); + + let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: protocol.FixAllScope.Solution, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); + + response.Changes.forEach(change => { + const uri = vscode.Uri.file(change.FileName); + + let edits: WorkspaceEdit = new WorkspaceEdit(); + change.Changes.forEach(change => { + edits.replace(uri, + new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), + change.NewText); + }); - let edits: WorkspaceEdit = new WorkspaceEdit(); - change.Changes.forEach(change => { - edits.replace(uri, - new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), - change.NewText); + vscode.workspace.applyEdit(edits); }); - - vscode.workspace.applyEdit(edits); }); } diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 5abfb200e..348f2c27c 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -259,6 +259,20 @@ export interface RunFixAllActionResponse { Changes: ModifiedFileResponse[]; } +export interface RunFixAllActionResponse { + Text: string; + Changes: ModifiedFileResponse[]; +} + +export interface FixAllItem { + Id: string; + Message: string; +} + +export interface GetFixAllResponse { + Items: FixAllItem[]; +} + export interface SyntaxFeature { Name: string; Data: string; @@ -461,6 +475,17 @@ export enum FileChangeType { Delete = "Delete" } +export enum FixAllScope { + Document = "Document", + Project = "Project", + Solution = "Solution" +} + +export interface FixAllRequest extends FileBasedRequest { + Scope: FixAllScope; + FixAllFilter?: FixAllItem[]; +} + export namespace V2 { export module Requests { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 27a3bfbbf..f25d6cbed 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -35,10 +35,14 @@ export async function findSymbols(server: OmniSharpServer, request: protocol.Fin return server.makeRequest(protocol.Requests.FindSymbols, request, token); } -export async function fixAll(server: OmniSharpServer, request: protocol.FileBasedRequest): Promise { +export async function runFixAll(server: OmniSharpServer, request: protocol.FixAllRequest): Promise { return server.makeRequest(protocol.Requests.RunFixAll, request); } +export async function getFixAll(server: OmniSharpServer, request: protocol.FixAllRequest): Promise { + return server.makeRequest(protocol.Requests.GetFixAll, request); +} + export async function findUsages(server: OmniSharpServer, request: protocol.FindUsagesRequest, token: vscode.CancellationToken) { return server.makeRequest(protocol.Requests.FindUsages, request, token); } From 09d93331713268a9a895dbc04d607d8cd90f7c01 Mon Sep 17 00:00:00 2001 From: Savpek Date: Sat, 3 Aug 2019 15:42:41 +0300 Subject: [PATCH 04/13] Support for scopes in fix all operations. --- package.json | 5 +++++ src/features/commands.ts | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a80b725b4..615a47de3 100644 --- a/package.json +++ b/package.json @@ -803,6 +803,11 @@ "title": "Fix all code issues in project", "category": "OmniSharp" }, + { + "command": "o.fixAll.document", + "title": "Fix all code issues in document", + "category": "OmniSharp" + }, { "command": "dotnet.generateAssets", "title": "Generate Assets for Build and Debug", diff --git a/src/features/commands.ts b/src/features/commands.ts index 8b172d07b..5443907ae 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -32,8 +32,9 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('o.showOutput', () => eventStream.post(new ShowOmniSharpChannel()))); // Todo these should really open new menu that lists correct options... - disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => fixAllTemporary(server))); - disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => fixAllTemporary(server))); + disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => fixAllTemporary(server, protocol.FixAllScope.Solution))); + disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => fixAllTemporary(server, protocol.FixAllScope.Project))); + disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => fixAllTemporary(server, protocol.FixAllScope.Document))); disposable.add(vscode.commands.registerCommand('dotnet.restore.project', async () => pickProjectAndDotnetRestore(server, eventStream))); disposable.add(vscode.commands.registerCommand('dotnet.restore.all', async () => dotnetRestoreAllProjects(server, eventStream))); @@ -63,8 +64,8 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: return new CompositeDisposable(disposable); } -// This should be replaced with method that opens menu. -async function fixAllTemporary(server: OmniSharpServer): Promise { +// This should be replaced with method that opens menu etc. +async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: protocol.FixAllScope.Solution }); let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); @@ -72,10 +73,14 @@ async function fixAllTemporary(server: OmniSharpServer): Promise { matchOnDescription: true, placeHolder: `Select fix all action` }).then(async selectedAction => { + if (selectedAction === undefined) { + return; + } + // action comes in form like "CS0000: Description message" let actionTokens = selectedAction.split(":"); - let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: protocol.FixAllScope.Solution, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); + let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); response.Changes.forEach(change => { const uri = vscode.Uri.file(change.FileName); From 8704413ca9541bdb3c7c5793759a644645ef7935 Mon Sep 17 00:00:00 2001 From: Savpek Date: Sat, 21 Sep 2019 12:40:11 +0300 Subject: [PATCH 05/13] Fix for scoping when fix all actions are get --- src/features/commands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/commands.ts b/src/features/commands.ts index 5443907ae..60c34392e 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -55,7 +55,7 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', async (args) => RemoteAttachPicker.ShowAttachEntries(args, platformInfo))); disposable.add(vscode.commands.registerCommand('csharp.setNextStatement', async () => setNextStatement())); - + // Register command for adapter executable command. disposable.add(vscode.commands.registerCommand('csharp.coreclrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); disposable.add(vscode.commands.registerCommand('csharp.clrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); @@ -66,7 +66,7 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: // This should be replaced with method that opens menu etc. async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { - let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: protocol.FixAllScope.Solution }); + let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); return vscode.window.showQuickPick(targets, { From 609a690b37466667675b2120e0eae68edbe7fa94 Mon Sep 17 00:00:00 2001 From: Savpek Date: Sun, 29 Sep 2019 13:13:39 +0300 Subject: [PATCH 06/13] Fix all command support --- src/features/commands.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/features/commands.ts b/src/features/commands.ts index 60c34392e..08c115dbc 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -55,7 +55,7 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', async (args) => RemoteAttachPicker.ShowAttachEntries(args, platformInfo))); disposable.add(vscode.commands.registerCommand('csharp.setNextStatement', async () => setNextStatement())); - + // Register command for adapter executable command. disposable.add(vscode.commands.registerCommand('csharp.coreclrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); disposable.add(vscode.commands.registerCommand('csharp.clrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); @@ -69,7 +69,9 @@ async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllSc let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); - return vscode.window.showQuickPick(targets, { + const fixAllIssuesSelectItem = "Fix all issues"; + + return vscode.window.showQuickPick([fixAllIssuesSelectItem, ...targets], { matchOnDescription: true, placeHolder: `Select fix all action` }).then(async selectedAction => { @@ -77,10 +79,15 @@ async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllSc return; } - // action comes in form like "CS0000: Description message" - let actionTokens = selectedAction.split(":"); + let response = undefined; - let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); + if (selectedAction == fixAllIssuesSelectItem) { + response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: undefined }); + } else { + // action comes in form like "CS0000: Description message" + let actionTokens = selectedAction.split(":"); + response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); + } response.Changes.forEach(change => { const uri = vscode.Uri.file(change.FileName); From d110c07696a4bd1b3f061de48e9f96c27c60ba73 Mon Sep 17 00:00:00 2001 From: Savpek Date: Fri, 4 Oct 2019 15:58:51 +0300 Subject: [PATCH 07/13] Version that works with fix all on save --- package-lock.json | 41 ++++++++++++++----- src/features/commands.ts | 21 +++++++--- src/features/fixAllProvider.ts | 40 ++++++++++++++++++ src/omnisharp/extension.ts | 2 + .../codeActionRename.integration.test.ts | 2 +- .../hoverProvider.integration.test.ts | 2 +- 6 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 src/features/fixAllProvider.ts diff --git a/package-lock.json b/package-lock.json index 6c1814de5..b1c9213c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3608,7 +3608,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3629,12 +3630,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3649,17 +3652,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3776,7 +3782,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3788,6 +3795,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3802,6 +3810,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3809,12 +3818,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3833,6 +3844,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3913,7 +3925,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3925,6 +3938,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4010,7 +4024,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4046,6 +4061,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4065,6 +4081,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4108,12 +4125,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/features/commands.ts b/src/features/commands.ts index 60c34392e..3afbde61f 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -47,15 +47,14 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: let attachItemsProvider = DotNetAttachItemsProviderFactory.Get(); let attacher = new AttachPicker(attachItemsProvider); disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => attacher.ShowAttachEntries())); - +vscode.CodeAction // Register command for generating tasks.json and launch.json assets. disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex))); - // Register command for remote process picker for attach disposable.add(vscode.commands.registerCommand('csharp.listRemoteProcess', async (args) => RemoteAttachPicker.ShowAttachEntries(args, platformInfo))); disposable.add(vscode.commands.registerCommand('csharp.setNextStatement', async () => setNextStatement())); - + // Register command for adapter executable command. disposable.add(vscode.commands.registerCommand('csharp.coreclrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); disposable.add(vscode.commands.registerCommand('csharp.clrAdapterExecutableCommand', async (args) => getAdapterExecutionCommand(platformInfo, eventStream, packageJSON, extensionPath))); @@ -67,20 +66,30 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: // This should be replaced with method that opens menu etc. async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); + let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); + if (scope === protocol.FixAllScope.Document) { + targets = ["Fix all issues", ...targets]; + } + return vscode.window.showQuickPick(targets, { matchOnDescription: true, placeHolder: `Select fix all action` }).then(async selectedAction => { + let filter = undefined; + if (selectedAction === undefined) { return; } - // action comes in form like "CS0000: Description message" - let actionTokens = selectedAction.split(":"); + if (selectedAction !== "Fix all issues") { + let actionTokens = selectedAction.split(":"); + filter = [{ Id: actionTokens[0], Message: actionTokens[1] }] + } - let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: [{ Id: actionTokens[0], Message: actionTokens[1] }] }); + // action comes in form like "CS0000: Description message" + let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: filter }); response.Changes.forEach(change => { const uri = vscode.Uri.file(change.FileName); diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts new file mode 100644 index 000000000..ea8a73a59 --- /dev/null +++ b/src/features/fixAllProvider.ts @@ -0,0 +1,40 @@ +import * as vscode from 'vscode'; +import * as serverUtils from '../omnisharp/utils'; +import { OmniSharpServer } from '../omnisharp/server'; +import { FixAllScope } from '../omnisharp/protocol'; +import { WorkspaceEdit } from 'vscode'; + +export class FixAllProvider implements vscode.CodeActionProvider { + public constructor(private server: OmniSharpServer) { + } + + public async provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range | vscode.Selection, + context: vscode.CodeActionContext, + _token: vscode.CancellationToken, + ): Promise { + console.log(context); + if (!context.only) { + return []; + } + + if (context.only.value === "source.fixAll.csharp") { + let response = await serverUtils.runFixAll(this.server, { FileName: document.fileName, Scope: FixAllScope.Document, FixAllFilter: undefined }); + + response.Changes.forEach(change => { + const uri = vscode.Uri.file(change.FileName); + + let edits: WorkspaceEdit = new WorkspaceEdit(); + change.Changes.forEach(change => { + edits.replace(uri, + new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), + change.NewText); + }); + + vscode.workspace.applyEdit(edits); + }); + } + return undefined; + } +} \ No newline at end of file diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts index f2691a744..a7f2e185f 100644 --- a/src/omnisharp/extension.ts +++ b/src/omnisharp/extension.ts @@ -38,6 +38,7 @@ import trackVirtualDocuments from '../features/virtualDocumentTracker'; import { StructureProvider } from '../features/structureProvider'; import { OmniSharpMonoResolver } from './OmniSharpMonoResolver'; import { getMonoVersion } from '../utils/getMonoVersion'; +import { FixAllProvider } from '../features/fixAllProvider'; export interface ActivationResult { readonly server: OmniSharpServer; @@ -84,6 +85,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an const codeActionProvider = new CodeActionProvider(server, optionProvider); localDisposables.add(codeActionProvider); localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider)); + localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, new FixAllProvider(server))); localDisposables.add(reportDiagnostics(server, advisor)); localDisposables.add(forwardChanges(server)); localDisposables.add(trackVirtualDocuments(server, eventStream)); diff --git a/test/integrationTests/codeActionRename.integration.test.ts b/test/integrationTests/codeActionRename.integration.test.ts index 7685f3dfd..ef3d1437f 100644 --- a/test/integrationTests/codeActionRename.integration.test.ts +++ b/test/integrationTests/codeActionRename.integration.test.ts @@ -25,7 +25,7 @@ suite(`Code Action Rename ${testAssetWorkspace.description}`, function () { let fileName = 'A.cs'; let projectDirectory = testAssetWorkspace.projects[0].projectDirectoryPath; let filePath = path.join(projectDirectory, fileName); - fileUri = vscode.Uri.file(filePath) + fileUri = vscode.Uri.file(filePath); }); suiteTeardown(async () => { diff --git a/test/integrationTests/hoverProvider.integration.test.ts b/test/integrationTests/hoverProvider.integration.test.ts index c1bd5b739..1221e2c8d 100644 --- a/test/integrationTests/hoverProvider.integration.test.ts +++ b/test/integrationTests/hoverProvider.integration.test.ts @@ -34,7 +34,7 @@ suite(`Hover Provider: ${testAssetWorkspace.description}`, function () { await vscode.commands.executeCommand("vscode.open", fileUri); let c = (await vscode.commands.executeCommand("vscode.executeHoverProvider", fileUri, new vscode.Position(10, 29))); let answer: string = - `Checks if object is tagged with the tag.` + `Checks if object is tagged with the tag.`; expect((<{ language: string; value: string }>c[0].contents[1]).value).to.equal(answer); }); From 48b4e36ecc61bff5a1d1cb6330dfd993e94312b1 Mon Sep 17 00:00:00 2001 From: Savpek Date: Tue, 15 Oct 2019 12:35:44 +0300 Subject: [PATCH 08/13] Moved to providers --- src/features/commands.ts | 47 ---------------------- src/features/fixAllProvider.ts | 72 +++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 61 deletions(-) diff --git a/src/features/commands.ts b/src/features/commands.ts index 3afbde61f..54e78a013 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -23,7 +23,6 @@ import reportIssue from './reportIssue'; import setNextStatement from '../coreclr-debug/setNextStatement'; import { IMonoResolver } from '../constants/IMonoResolver'; import { getDotnetInfo } from '../utils/getDotnetInfo'; -import { WorkspaceEdit } from 'vscode'; export default function registerCommands(server: OmniSharpServer, platformInfo: PlatformInformation, eventStream: EventStream, optionProvider: OptionProvider, monoResolver: IMonoResolver, packageJSON: any, extensionPath: string): CompositeDisposable { let disposable = new CompositeDisposable(); @@ -31,11 +30,6 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: disposable.add(vscode.commands.registerCommand('o.pickProjectAndStart', async () => pickProjectAndStart(server, optionProvider))); disposable.add(vscode.commands.registerCommand('o.showOutput', () => eventStream.post(new ShowOmniSharpChannel()))); - // Todo these should really open new menu that lists correct options... - disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => fixAllTemporary(server, protocol.FixAllScope.Solution))); - disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => fixAllTemporary(server, protocol.FixAllScope.Project))); - disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => fixAllTemporary(server, protocol.FixAllScope.Document))); - disposable.add(vscode.commands.registerCommand('dotnet.restore.project', async () => pickProjectAndDotnetRestore(server, eventStream))); disposable.add(vscode.commands.registerCommand('dotnet.restore.all', async () => dotnetRestoreAllProjects(server, eventStream))); @@ -63,48 +57,7 @@ vscode.CodeAction return new CompositeDisposable(disposable); } -// This should be replaced with method that opens menu etc. -async function fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { - let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); - - let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); - - if (scope === protocol.FixAllScope.Document) { - targets = ["Fix all issues", ...targets]; - } - - return vscode.window.showQuickPick(targets, { - matchOnDescription: true, - placeHolder: `Select fix all action` - }).then(async selectedAction => { - let filter = undefined; - - if (selectedAction === undefined) { - return; - } - - if (selectedAction !== "Fix all issues") { - let actionTokens = selectedAction.split(":"); - filter = [{ Id: actionTokens[0], Message: actionTokens[1] }] - } - - // action comes in form like "CS0000: Description message" - let response = await serverUtils.runFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope, FixAllFilter: filter }); - - response.Changes.forEach(change => { - const uri = vscode.Uri.file(change.FileName); - - let edits: WorkspaceEdit = new WorkspaceEdit(); - change.Changes.forEach(change => { - edits.replace(uri, - new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), - change.NewText); - }); - vscode.workspace.applyEdit(edits); - }); - }); -} function restartOmniSharp(server: OmniSharpServer) { if (server.isRunning()) { diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts index ea8a73a59..80f60182f 100644 --- a/src/features/fixAllProvider.ts +++ b/src/features/fixAllProvider.ts @@ -1,11 +1,20 @@ import * as vscode from 'vscode'; import * as serverUtils from '../omnisharp/utils'; +import * as protocol from '../omnisharp/protocol'; import { OmniSharpServer } from '../omnisharp/server'; -import { FixAllScope } from '../omnisharp/protocol'; +import { FixAllScope, FixAllItem } from '../omnisharp/protocol'; import { WorkspaceEdit } from 'vscode'; +import CompositeDisposable from '../CompositeDisposable'; +import AbstractProvider from './abstractProvider'; -export class FixAllProvider implements vscode.CodeActionProvider { +export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider { public constructor(private server: OmniSharpServer) { + super(server); + let disposable = new CompositeDisposable(); + disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => this.fixAllTemporary(server, protocol.FixAllScope.Solution))); + disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => this.fixAllTemporary(server, protocol.FixAllScope.Project))); + disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => this.fixAllTemporary(server, protocol.FixAllScope.Document))); + this.addDisposables(disposable); } public async provideCodeActions( @@ -20,21 +29,56 @@ export class FixAllProvider implements vscode.CodeActionProvider { } if (context.only.value === "source.fixAll.csharp") { - let response = await serverUtils.runFixAll(this.server, { FileName: document.fileName, Scope: FixAllScope.Document, FixAllFilter: undefined }); + await this.applyFixes(document.fileName, FixAllScope.Document, undefined); + } + + return []; + } - response.Changes.forEach(change => { - const uri = vscode.Uri.file(change.FileName); + // This should be replaced with method that opens menu etc. + private async fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { + let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); - let edits: WorkspaceEdit = new WorkspaceEdit(); - change.Changes.forEach(change => { - edits.replace(uri, - new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), - change.NewText); - }); + let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); - vscode.workspace.applyEdit(edits); - }); + if (scope === protocol.FixAllScope.Document) { + targets = ["Fix all issues", ...targets]; + } + + return vscode.window.showQuickPick(targets, { + matchOnDescription: true, + placeHolder: `Select fix all action` + }).then(async selectedAction => { + let filter: FixAllItem[] = undefined; + + if (selectedAction === undefined) { + return; } - return undefined; + + if (selectedAction !== "Fix all issues") { + let actionTokens = selectedAction.split(":"); + filter = [{ Id: actionTokens[0], Message: actionTokens[1] }] + } + + await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter); + }); +} + + private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise + { + let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter }); + + response.Changes.forEach(change => { + const uri = vscode.Uri.file(change.FileName); + + let edits: WorkspaceEdit = new WorkspaceEdit(); + change.Changes.forEach(change => { + edits.replace(uri, + new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), + change.NewText); + }); + + vscode.workspace.applyEdit(edits); + }); } } \ No newline at end of file From 4a93dde11d05b3464465aa9bdb2c2fb5d7e12278 Mon Sep 17 00:00:00 2001 From: Savpek Date: Thu, 5 Dec 2019 12:24:12 +0200 Subject: [PATCH 09/13] Merge and linting fixes --- src/features/commands.ts | 1 - src/features/fixAllProvider.ts | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/features/commands.ts b/src/features/commands.ts index 56b688e09..c967d59f4 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -40,7 +40,6 @@ export default function registerCommands(server: OmniSharpServer, platformInfo: let attachItemsProvider = DotNetAttachItemsProviderFactory.Get(); let attacher = new AttachPicker(attachItemsProvider); disposable.add(vscode.commands.registerCommand('csharp.listProcess', async () => attacher.ShowAttachEntries())); -vscode.CodeAction // Register command for generating tasks.json and launch.json assets. disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex))); // Register command for remote process picker for attach diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts index 80f60182f..ae2f991b9 100644 --- a/src/features/fixAllProvider.ts +++ b/src/features/fixAllProvider.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + import * as vscode from 'vscode'; import * as serverUtils from '../omnisharp/utils'; import * as protocol from '../omnisharp/protocol'; @@ -57,7 +62,7 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio if (selectedAction !== "Fix all issues") { let actionTokens = selectedAction.split(":"); - filter = [{ Id: actionTokens[0], Message: actionTokens[1] }] + filter = [{ Id: actionTokens[0], Message: actionTokens[1] }]; } await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter); From be7218ad13e5e4fe0560b01607aedde46a411cf3 Mon Sep 17 00:00:00 2001 From: Savpek Date: Thu, 5 Dec 2019 12:59:16 +0200 Subject: [PATCH 10/13] Naming fix and tested version that merges changes to file from existing actions --- src/features/fixAllProvider.ts | 137 +++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 41 deletions(-) diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts index ae2f991b9..f31295564 100644 --- a/src/features/fixAllProvider.ts +++ b/src/features/fixAllProvider.ts @@ -7,18 +7,19 @@ import * as vscode from 'vscode'; import * as serverUtils from '../omnisharp/utils'; import * as protocol from '../omnisharp/protocol'; import { OmniSharpServer } from '../omnisharp/server'; -import { FixAllScope, FixAllItem } from '../omnisharp/protocol'; -import { WorkspaceEdit } from 'vscode'; +import { FixAllScope, FixAllItem, FileModificationType } from '../omnisharp/protocol'; +import { Uri } from 'vscode'; import CompositeDisposable from '../CompositeDisposable'; import AbstractProvider from './abstractProvider'; +import { toRange2 } from '../omnisharp/typeConversion'; export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider { public constructor(private server: OmniSharpServer) { super(server); let disposable = new CompositeDisposable(); - disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => this.fixAllTemporary(server, protocol.FixAllScope.Solution))); - disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => this.fixAllTemporary(server, protocol.FixAllScope.Project))); - disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => this.fixAllTemporary(server, protocol.FixAllScope.Document))); + disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => this.fixAllMenu(server, protocol.FixAllScope.Solution))); + disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => this.fixAllMenu(server, protocol.FixAllScope.Project))); + disposable.add(vscode.commands.registerCommand('o.fixAll.document', async () => this.fixAllMenu(server, protocol.FixAllScope.Document))); this.addDisposables(disposable); } @@ -40,50 +41,104 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio return []; } - // This should be replaced with method that opens menu etc. - private async fixAllTemporary(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { - let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); + private async fixAllMenu(server: OmniSharpServer, scope: protocol.FixAllScope): Promise { + let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope }); - let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); + let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`); - if (scope === protocol.FixAllScope.Document) { - targets = ["Fix all issues", ...targets]; - } - - return vscode.window.showQuickPick(targets, { - matchOnDescription: true, - placeHolder: `Select fix all action` - }).then(async selectedAction => { - let filter: FixAllItem[] = undefined; - - if (selectedAction === undefined) { - return; + if (scope === protocol.FixAllScope.Document) { + targets = ["Fix all issues", ...targets]; } - if (selectedAction !== "Fix all issues") { - let actionTokens = selectedAction.split(":"); - filter = [{ Id: actionTokens[0], Message: actionTokens[1] }]; - } + return vscode.window.showQuickPick(targets, { + matchOnDescription: true, + placeHolder: `Select fix all action` + }).then(async selectedAction => { + let filter: FixAllItem[] = undefined; - await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter); - }); -} + if (selectedAction === undefined) { + return; + } - private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise - { - let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter }); + if (selectedAction !== "Fix all issues") { + let actionTokens = selectedAction.split(":"); + filter = [{ Id: actionTokens[0], Message: actionTokens[1] }]; + } - response.Changes.forEach(change => { - const uri = vscode.Uri.file(change.FileName); + await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter); + }); + } - let edits: WorkspaceEdit = new WorkspaceEdit(); - change.Changes.forEach(change => { - edits.replace(uri, - new vscode.Range(change.StartLine - 1, change.StartColumn - 1, change.EndLine - 1, change.EndColumn - 1), - change.NewText); - }); + private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise { + let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter }); - vscode.workspace.applyEdit(edits); - }); + if (response && Array.isArray(response.Changes)) { + + let edit = new vscode.WorkspaceEdit(); + + let fileToOpen: Uri = null; + let renamedFiles: Uri[] = []; + + for (let change of response.Changes) { + if (change.ModificationType == FileModificationType.Renamed) + { + // The file was renamed. Omnisharp has already persisted + // the right changes to disk. We don't need to try to + // apply text changes (and will skip this file if we see an edit) + renamedFiles.push(Uri.file(change.FileName)); + } + } + + for (let change of response.Changes) { + if (change.ModificationType == FileModificationType.Opened) + { + // The CodeAction requested that we open a file. + // Record that file name and keep processing CodeActions. + // If a CodeAction requests that we open multiple files + // we only open the last one (what would it mean to open multiple files?) + fileToOpen = vscode.Uri.file(change.FileName); + } + + if (change.ModificationType == FileModificationType.Modified) + { + let uri = vscode.Uri.file(change.FileName); + if (renamedFiles.some(r => r == uri)) + { + // This file got renamed. Omnisharp has already + // persisted the new file with any applicable changes. + continue; + } + + let edits: vscode.TextEdit[] = []; + for (let textChange of change.Changes) { + edits.push(vscode.TextEdit.replace(toRange2(textChange), textChange.NewText)); + } + + edit.set(uri, edits); + } + } + + let applyEditPromise = vscode.workspace.applyEdit(edit); + + // Unfortunately, the textEditor.Close() API has been deprecated + // and replaced with a command that can only close the active editor. + // If files were renamed that weren't the active editor, their tabs will + // be left open and marked as "deleted" by VS Code + let next = applyEditPromise; + if (renamedFiles.some(r => r.fsPath == vscode.window.activeTextEditor.document.uri.fsPath)) + { + next = applyEditPromise.then(_ => + { + return vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + }); + } + + return fileToOpen != null + ? next.then(_ => + { + return vscode.commands.executeCommand("vscode.open", fileToOpen); + }) + : next; + } } } \ No newline at end of file From a5b4861b6dc1cedb0c320e1cd73ad2e8ff5996ef Mon Sep 17 00:00:00 2001 From: Savpek Date: Thu, 5 Dec 2019 19:11:12 +0200 Subject: [PATCH 11/13] Update to support wantsTextChanges and actions flags --- src/features/fixAllProvider.ts | 5 ++--- src/omnisharp/protocol.ts | 14 ++++++++------ src/omnisharp/utils.ts | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts index f31295564..f61114de6 100644 --- a/src/features/fixAllProvider.ts +++ b/src/features/fixAllProvider.ts @@ -70,10 +70,9 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio } private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise { - let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter }); + let response = await serverUtils.runFixAll(this.server, { FileName: fileName, Scope: scope, FixAllFilter: fixAllFilter, WantsAllCodeActionOperations: true, WantsTextChanges: true }); if (response && Array.isArray(response.Changes)) { - let edit = new vscode.WorkspaceEdit(); let fileToOpen: Uri = null; @@ -92,7 +91,7 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio for (let change of response.Changes) { if (change.ModificationType == FileModificationType.Opened) { - // The CodeAction requested that we open a file. + // The CodeAction requested that we open a file. // Record that file name and keep processing CodeActions. // If a CodeAction requests that we open multiple files // we only open the last one (what would it mean to open multiple files?) diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 348f2c27c..450b76046 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -259,11 +259,6 @@ export interface RunFixAllActionResponse { Changes: ModifiedFileResponse[]; } -export interface RunFixAllActionResponse { - Text: string; - Changes: ModifiedFileResponse[]; -} - export interface FixAllItem { Id: string; Message: string; @@ -481,9 +476,16 @@ export enum FixAllScope { Solution = "Solution" } -export interface FixAllRequest extends FileBasedRequest { +export interface GetFixAllRequest extends FileBasedRequest { + Scope: FixAllScope; + FixAllFilter?: FixAllItem[]; +} + +export interface RunFixAllRequest extends FileBasedRequest { Scope: FixAllScope; FixAllFilter?: FixAllItem[]; + WantsTextChanges: boolean; + WantsAllCodeActionOperations: boolean; } export namespace V2 { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 38e469687..767e837f6 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -35,11 +35,11 @@ export async function findSymbols(server: OmniSharpServer, request: protocol.Fin return server.makeRequest(protocol.Requests.FindSymbols, request, token); } -export async function runFixAll(server: OmniSharpServer, request: protocol.FixAllRequest): Promise { +export async function runFixAll(server: OmniSharpServer, request: protocol.RunFixAllRequest): Promise { return server.makeRequest(protocol.Requests.RunFixAll, request); } -export async function getFixAll(server: OmniSharpServer, request: protocol.FixAllRequest): Promise { +export async function getFixAll(server: OmniSharpServer, request: protocol.GetFixAllRequest): Promise { return server.makeRequest(protocol.Requests.GetFixAll, request); } From bf80498241e2386b3e2d33f3818b553404e0f75a Mon Sep 17 00:00:00 2001 From: Savpek Date: Fri, 6 Dec 2019 08:21:35 +0200 Subject: [PATCH 12/13] Mergefixes --- package.json | 6 +++--- src/features/fixAllProvider.ts | 5 +++-- src/omnisharp/extension.ts | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e540630da..129b12a80 100644 --- a/package.json +++ b/package.json @@ -800,7 +800,6 @@ "category": "OmniSharp" }, { -<<<<<<< HEAD "command": "o.fixAll.solution", "title": "Fix all code issues in solution", "category": "OmniSharp" @@ -813,7 +812,9 @@ { "command": "o.fixAll.document", "title": "Fix all code issues in document", -======= + "category": "OmniSharp" + }, + { "command": "o.reanalyze.allProjects", "title": "Analyze all projects", "category": "OmniSharp" @@ -821,7 +822,6 @@ { "command": "o.reanalyze.currentProject", "title": "Analyze current project", ->>>>>>> upstream/master "category": "OmniSharp" }, { diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts index f61114de6..f96737c06 100644 --- a/src/features/fixAllProvider.ts +++ b/src/features/fixAllProvider.ts @@ -12,10 +12,11 @@ import { Uri } from 'vscode'; import CompositeDisposable from '../CompositeDisposable'; import AbstractProvider from './abstractProvider'; import { toRange2 } from '../omnisharp/typeConversion'; +import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature'; export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider { - public constructor(private server: OmniSharpServer) { - super(server); + public constructor(private server: OmniSharpServer, languageMiddlewareFeature: LanguageMiddlewareFeature) { + super(server, languageMiddlewareFeature); let disposable = new CompositeDisposable(); disposable.add(vscode.commands.registerCommand('o.fixAll.solution', async () => this.fixAllMenu(server, protocol.FixAllScope.Solution))); disposable.add(vscode.commands.registerCommand('o.fixAll.project', async () => this.fixAllMenu(server, protocol.FixAllScope.Project))); diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts index 1183eb06e..1fb2183fc 100644 --- a/src/omnisharp/extension.ts +++ b/src/omnisharp/extension.ts @@ -89,7 +89,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an const codeActionProvider = new CodeActionProvider(server, optionProvider, languageMiddlewareFeature); localDisposables.add(codeActionProvider); localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider)); - localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, new FixAllProvider(server))); + localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, new FixAllProvider(server, languageMiddlewareFeature))); localDisposables.add(reportDiagnostics(server, advisor, languageMiddlewareFeature)); localDisposables.add(forwardChanges(server)); localDisposables.add(trackVirtualDocuments(server, eventStream)); From a236607bf2c5e45bafc137ea6c5f37a961ebd577 Mon Sep 17 00:00:00 2001 From: Savpek Date: Fri, 6 Dec 2019 08:22:45 +0200 Subject: [PATCH 13/13] Fix for protocol.ts merge --- src/omnisharp/protocol.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 0216c0c35..692411367 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -28,12 +28,9 @@ export module Requests { export const TypeLookup = '/typelookup'; export const UpdateBuffer = '/updatebuffer'; export const Metadata = '/metadata'; -<<<<<<< HEAD export const RunFixAll = '/runfixall'; export const GetFixAll = '/getfixall'; -======= export const ReAnalyze = '/reanalyze'; ->>>>>>> upstream/master } export namespace WireProtocol {