Skip to content

Commit dde0eaf

Browse files
authored
Merge pull request #4133 from 333fred/rename-not-apply
Don't apply edits on server
2 parents f97ba36 + 1ca331b commit dde0eaf

File tree

6 files changed

+95
-151
lines changed

6 files changed

+95
-151
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## 1.23.5 (Not yet released)
1515
* Set meaning of UseGlobalMono "auto" to "never" since Mono 6.12.0 still ships with MSBuild 16.7 (PR: [#4130](https://github.com/OmniSharp/omnisharp-vscode/pull/4130))
16+
* Ensure that the rename identifier and run code action providers do not apply changes twice PR: [4133](https://github.com/OmniSharp/omnisharp-vscode/pull/4133)
1617

1718
## 1.23.4 (October, 19, 2020)
1819
* Use incremental changes to update language server (PR: [#4088](https://github.com/OmniSharp/omnisharp-vscode/pull/4088))

src/features/codeActionProvider.ts

Lines changed: 9 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ import * as vscode from 'vscode';
77
import { OmniSharpServer } from '../omnisharp/server';
88
import AbstractProvider from './abstractProvider';
99
import * as protocol from '../omnisharp/protocol';
10-
import { toRange2 } from '../omnisharp/typeConversion';
1110
import * as serverUtils from '../omnisharp/utils';
12-
import { FileModificationType } from '../omnisharp/protocol';
13-
import { Uri } from 'vscode';
1411
import CompositeDisposable from '../CompositeDisposable';
1512
import OptionProvider from '../observers/OptionProvider';
1613
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
14+
import { buildEditForResponse } from '../omnisharp/fileOperationsResponseEditBuilder';
1715

1816
export default class CodeActionProvider extends AbstractProvider implements vscode.CodeActionProvider {
1917

@@ -84,7 +82,8 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
8482
Selection: selection,
8583
Identifier: codeAction.Identifier,
8684
WantsTextChanges: true,
87-
WantsAllCodeActionOperations: true
85+
WantsAllCodeActionOperations: true,
86+
ApplyTextChanges: false
8887
};
8988

9089
return {
@@ -101,80 +100,12 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
101100

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

104-
return serverUtils.runCodeAction(this._server, req).then(async response => {
105-
if (response && Array.isArray(response.Changes)) {
106-
107-
let edit = new vscode.WorkspaceEdit();
108-
109-
let fileToOpen: Uri = null;
110-
let renamedFiles: Uri[] = [];
111-
112-
for (let change of response.Changes) {
113-
if (change.ModificationType == FileModificationType.Renamed)
114-
{
115-
// The file was renamed. Omnisharp has already persisted
116-
// the right changes to disk. We don't need to try to
117-
// apply text changes (and will skip this file if we see an edit)
118-
renamedFiles.push(Uri.file(change.FileName));
119-
}
120-
}
121-
122-
for (let change of response.Changes) {
123-
if (change.ModificationType == FileModificationType.Opened)
124-
{
125-
// The CodeAction requested that we open a file.
126-
// Record that file name and keep processing CodeActions.
127-
// If a CodeAction requests that we open multiple files
128-
// we only open the last one (what would it mean to open multiple files?)
129-
fileToOpen = vscode.Uri.file(change.FileName);
130-
}
131-
132-
if (change.ModificationType == FileModificationType.Modified)
133-
{
134-
let uri = vscode.Uri.file(change.FileName);
135-
if (renamedFiles.some(r => r == uri))
136-
{
137-
// This file got renamed. OmniSharp has already
138-
// persisted the new file with any applicable changes.
139-
continue;
140-
}
141-
142-
let edits: vscode.TextEdit[] = [];
143-
for (let textChange of change.Changes) {
144-
edits.push(vscode.TextEdit.replace(toRange2(textChange), textChange.NewText));
145-
}
146-
147-
edit.set(uri, edits);
148-
}
149-
}
150-
151-
// Allow language middlewares to re-map its edits if necessary.
152-
edit = await this._languageMiddlewareFeature.remap("remapWorkspaceEdit", edit, token);
153-
154-
let applyEditPromise = vscode.workspace.applyEdit(edit);
155-
156-
// Unfortunately, the textEditor.Close() API has been deprecated
157-
// and replaced with a command that can only close the active editor.
158-
// If files were renamed that weren't the active editor, their tabs will
159-
// be left open and marked as "deleted" by VS Code
160-
let next = applyEditPromise;
161-
if (renamedFiles.some(r => r.fsPath == vscode.window.activeTextEditor.document.uri.fsPath))
162-
{
163-
next = applyEditPromise.then(_ =>
164-
{
165-
return vscode.commands.executeCommand("workbench.action.closeActiveEditor");
166-
});
167-
}
168-
169-
return fileToOpen != null
170-
? next.then(_ =>
171-
{
172-
return vscode.commands.executeCommand("vscode.open", fileToOpen);
173-
})
174-
: next;
175-
}
176-
}, async (error) => {
103+
return serverUtils.runCodeAction(this._server, req).then(response => {
104+
if (response) {
105+
return buildEditForResponse(response.Changes, this._languageMiddlewareFeature, token);
106+
}
107+
}, async (error) => {
177108
return Promise.reject(`Problem invoking 'RunCodeAction' on OmniSharp server: ${error}`);
178109
});
179110
}
180-
}
111+
}

src/features/fixAllProvider.ts

Lines changed: 5 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import * as vscode from 'vscode';
77
import * as serverUtils from '../omnisharp/utils';
88
import * as protocol from '../omnisharp/protocol';
99
import { OmniSharpServer } from '../omnisharp/server';
10-
import { FixAllScope, FixAllItem, FileModificationType } from '../omnisharp/protocol';
11-
import { Uri } from 'vscode';
10+
import { FixAllScope, FixAllItem } from '../omnisharp/protocol';
1211
import CompositeDisposable from '../CompositeDisposable';
1312
import AbstractProvider from './abstractProvider';
14-
import { toRange2 } from '../omnisharp/typeConversion';
1513
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
14+
import { buildEditForResponse } from '../omnisharp/fileOperationsResponseEditBuilder';
15+
import { CancellationToken } from 'vscode-languageserver-protocol';
1616

1717
export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider {
1818
public constructor(private server: OmniSharpServer, languageMiddlewareFeature: LanguageMiddlewareFeature) {
@@ -80,72 +80,8 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio
8080
ApplyChanges: false
8181
});
8282

83-
if (response && Array.isArray(response.Changes)) {
84-
let edit = new vscode.WorkspaceEdit();
85-
86-
let fileToOpen: Uri = null;
87-
let renamedFiles: Uri[] = [];
88-
89-
for (let change of response.Changes) {
90-
if (change.ModificationType == FileModificationType.Renamed)
91-
{
92-
// The file was renamed. Omnisharp has already persisted
93-
// the right changes to disk. We don't need to try to
94-
// apply text changes (and will skip this file if we see an edit)
95-
renamedFiles.push(Uri.file(change.FileName));
96-
}
97-
}
98-
99-
for (let change of response.Changes) {
100-
if (change.ModificationType == FileModificationType.Opened)
101-
{
102-
// The CodeAction requested that we open a file.
103-
// Record that file name and keep processing CodeActions.
104-
// If a CodeAction requests that we open multiple files
105-
// we only open the last one (what would it mean to open multiple files?)
106-
fileToOpen = vscode.Uri.file(change.FileName);
107-
}
108-
109-
if (change.ModificationType == FileModificationType.Modified)
110-
{
111-
let uri = vscode.Uri.file(change.FileName);
112-
if (renamedFiles.some(r => r == uri))
113-
{
114-
// This file got renamed. Omnisharp has already
115-
// persisted the new file with any applicable changes.
116-
continue;
117-
}
118-
119-
let edits: vscode.TextEdit[] = [];
120-
for (let textChange of change.Changes) {
121-
edits.push(vscode.TextEdit.replace(toRange2(textChange), textChange.NewText));
122-
}
123-
124-
edit.set(uri, edits);
125-
}
126-
}
127-
128-
let applyEditPromise = vscode.workspace.applyEdit(edit);
129-
130-
// Unfortunately, the textEditor.Close() API has been deprecated
131-
// and replaced with a command that can only close the active editor.
132-
// If files were renamed that weren't the active editor, their tabs will
133-
// be left open and marked as "deleted" by VS Code
134-
let next = applyEditPromise;
135-
if (renamedFiles.some(r => r.fsPath == vscode.window.activeTextEditor.document.uri.fsPath))
136-
{
137-
next = applyEditPromise.then(_ =>
138-
{
139-
return vscode.commands.executeCommand("workbench.action.closeActiveEditor");
140-
});
141-
}
142-
143-
return fileToOpen != null
144-
? next.then(_ =>
145-
{
146-
return vscode.commands.executeCommand("vscode.open", fileToOpen);
147-
})
148-
: next;
83+
if (response) {
84+
return buildEditForResponse(response.Changes, this._languageMiddlewareFeature, CancellationToken.None);
14985
}
15086
}
15187
}

src/features/renameProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default class OmnisharpRenameProvider extends AbstractSupport implements
1616
let req = createRequest<protocol.RenameRequest>(document, position);
1717
req.WantsTextChanges = true;
1818
req.RenameTo = newName;
19+
req.ApplyTextChanges = false;
1920

2021
try {
2122
let response = await serverUtils.rename(this._server, req, token);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 { Uri } from 'vscode';
8+
import { LanguageMiddlewareFeature } from './LanguageMiddlewareFeature';
9+
import { FileModificationType, FileOperationResponse, ModifiedFileResponse, RenamedFileResponse } from "./protocol";
10+
import { toRange2 } from './typeConversion';
11+
12+
export async function buildEditForResponse(changes: FileOperationResponse[], languageMiddlewareFeature: LanguageMiddlewareFeature, token: vscode.CancellationToken): Promise<boolean> {
13+
let edit = new vscode.WorkspaceEdit();
14+
15+
let fileToOpen: Uri = null;
16+
17+
if (!changes || !Array.isArray(changes) || !changes.length) {
18+
return true;
19+
}
20+
21+
for (const change of changes) {
22+
if (change.ModificationType == FileModificationType.Opened) {
23+
// The CodeAction requested that we open a file.
24+
// Record that file name and keep processing CodeActions.
25+
// If a CodeAction requests that we open multiple files
26+
// we only open the last one (what would it mean to open multiple files?)
27+
fileToOpen = vscode.Uri.file(change.FileName);
28+
}
29+
30+
if (change.ModificationType == FileModificationType.Modified) {
31+
const modifiedChange = <ModifiedFileResponse>change;
32+
const uri = vscode.Uri.file(modifiedChange.FileName);
33+
let edits: vscode.TextEdit[] = [];
34+
for (let textChange of modifiedChange.Changes) {
35+
edits.push(vscode.TextEdit.replace(toRange2(textChange), textChange.NewText));
36+
}
37+
38+
edit.set(uri, edits);
39+
}
40+
}
41+
42+
for (const change of changes) {
43+
if (change.ModificationType == FileModificationType.Renamed) {
44+
const renamedChange = <RenamedFileResponse>change;
45+
edit.renameFile(vscode.Uri.file(renamedChange.FileName), vscode.Uri.file(renamedChange.NewFileName));
46+
}
47+
}
48+
49+
// Allow language middlewares to re-map its edits if necessary.
50+
edit = await languageMiddlewareFeature.remap("remapWorkspaceEdit", edit, token);
51+
52+
const applyEditPromise = vscode.workspace.applyEdit(edit);
53+
54+
// Unfortunately, the textEditor.Close() API has been deprecated
55+
// and replaced with a command that can only close the active editor.
56+
// If files were renamed that weren't the active editor, their tabs will
57+
// be left open and marked as "deleted" by VS Code
58+
return fileToOpen != null
59+
? applyEditPromise.then(_ => {
60+
return vscode.commands.executeCommand("vscode.open", fileToOpen);
61+
})
62+
: applyEditPromise;
63+
}

src/omnisharp/protocol.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export interface GetCodeActionsResponse {
262262

263263
export interface RunFixAllActionResponse {
264264
Text: string;
265-
Changes: ModifiedFileResponse[];
265+
Changes: FileOperationResponse[];
266266
}
267267

268268
export interface FixAllItem {
@@ -371,13 +371,24 @@ export interface DotNetFramework {
371371
export interface RenameRequest extends Request {
372372
RenameTo: string;
373373
WantsTextChanges?: boolean;
374+
ApplyTextChanges: boolean;
374375
}
375376

376-
export interface ModifiedFileResponse {
377+
export interface FileOperationResponse {
377378
FileName: string;
379+
ModificationType: FileModificationType;
380+
}
381+
382+
export interface ModifiedFileResponse extends FileOperationResponse {
378383
Buffer: string;
379384
Changes: TextChange[];
380-
ModificationType: FileModificationType;
385+
}
386+
387+
export interface RenamedFileResponse extends FileOperationResponse {
388+
NewFileName: string;
389+
}
390+
391+
export interface OpenFileResponse extends FileOperationResponse {
381392
}
382393

383394
export enum FileModificationType {
@@ -596,10 +607,11 @@ export namespace V2 {
596607
Selection?: Range;
597608
WantsTextChanges: boolean;
598609
WantsAllCodeActionOperations: boolean;
610+
ApplyTextChanges: boolean;
599611
}
600612

601613
export interface RunCodeActionResponse {
602-
Changes: ModifiedFileResponse[];
614+
Changes: FileOperationResponse[];
603615
}
604616

605617
export interface MSBuildProjectDiagnostics {

0 commit comments

Comments
 (0)