From b02df6fd375ccab0841a8c2e704946b676ab5e5a Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:02:58 +1100 Subject: [PATCH 01/20] registering new rename provider feature --- src/extension.ts | 7 ++++ src/features/RenameSymbol.ts | 81 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/features/RenameSymbol.ts diff --git a/src/extension.ts b/src/extension.ts index 01a4ee4a1c..355f31c6c4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,6 +25,7 @@ import { SessionManager } from "./session"; import { LogLevel, getSettings } from "./settings"; import { PowerShellLanguageId } from "./utils"; import { LanguageClientConsumer } from "./languageClientConsumer"; +import { RenameSymbolFeature } from "./features/RenameSymbol"; // The most reliable way to get the name and version of the current extension. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-var-requires @@ -35,6 +36,7 @@ const PackageJSON: any = require("../package.json"); const TELEMETRY_KEY = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255"; let languageConfigurationDisposable: vscode.Disposable; +let languageRenameProvider:vscode.Disposable; let logger: Logger; let sessionManager: SessionManager; let languageClientConsumers: LanguageClientConsumer[] = []; @@ -56,6 +58,9 @@ export async function activate(context: vscode.ExtensionContext): Promise { await telemetryReporter.dispose(); languageConfigurationDisposable.dispose(); + languageRenameProvider.dispose(); } diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts new file mode 100644 index 0000000000..b15cfa0764 --- /dev/null +++ b/src/features/RenameSymbol.ts @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import vscode = require("vscode"); +import { RequestType } from "vscode-languageclient"; +import { LanguageClientConsumer } from "../languageClientConsumer"; +import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range } from "vscode"; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface IRenameSymbolRequestArguments { + FileName?:string + Line?:number + Column?:number + RenameTo:string +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface TextChange { + + newText: string; + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; +} +interface ModifiedFileResponse{ + fileName: string; + changes : TextChange[] +} + +interface IRenameSymbolRequestResponse { + changes : ModifiedFileResponse[] +} + +export const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); + +export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { + private command: vscode.Disposable; + + constructor() { + super(); + this.command = vscode.commands.registerCommand("PowerShell.RenameSymbol", () => { + throw new Error("Not implemented"); + + }); + } + public dispose() { + this.command.dispose(); + } + public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { + + const req:IRenameSymbolRequestArguments = { + FileName : document.fileName, + Line: position.line, + Column : position.character, + RenameTo : newName, + }; + + try { + const response = await this.languageClient?.sendRequest(RenameSymbolRequestType, req); + + if (!response) { + return undefined; + } + + const edit = new WorkspaceEdit(); + response.changes.forEach(change => { + const uri = Uri.file(change.fileName); + + change.changes.forEach(change => { + edit.replace(uri, + new Range(change.startLine, change.startColumn, change.endLine, change.endColumn), + change.newText); + }); + }); + return edit; + }catch (error) { + return undefined; + } + } + +} From d26601ead077d15346bd29d2bef7f6fde4aa73b4 Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:07:34 +0300 Subject: [PATCH 02/20] hints for dispose --- src/features/RenameSymbol.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index b15cfa0764..2b5c62ba2e 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -43,7 +43,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam }); } - public dispose() { + public dispose() :void{ this.command.dispose(); } public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { From d533c2b8c6c27307f5e0dc3bf5d6df32abb4115f Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:53:39 +0300 Subject: [PATCH 03/20] implemented prepar rename symbol --- src/features/RenameSymbol.ts | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 2b5c62ba2e..eaf5ae8a50 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -10,7 +10,13 @@ interface IRenameSymbolRequestArguments { FileName?:string Line?:number Column?:number - RenameTo:string + RenameTo?:string +} +interface IPrepareRenameSymbolRequestArguments { + FileName?:string + Line?:number + Column?:number + RenameTo?:string } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -31,7 +37,12 @@ interface IRenameSymbolRequestResponse { changes : ModifiedFileResponse[] } +interface IPrepareRenameSymbolRequestResponse { + message : string +} + export const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); +export const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { private command: vscode.Disposable; @@ -77,5 +88,28 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam return undefined; } } + public async prepareRename(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { + + const req:IRenameSymbolRequestArguments = { + FileName : document.fileName, + Line: position.line, + Column : position.character, + }; + + try { + const response = await this.languageClient?.sendRequest(PrepareRenameSymbolRequestType, req); + + if (!response) { + return undefined; + } + return Promise.reject(response.message); + + }catch (error) { + return undefined; + } + } } From 056cb4be74d2811b88b12e5b97569e162a6d1c0a Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:58:47 +0300 Subject: [PATCH 04/20] added more detection and handeling for preparerenamesymbol --- src/features/RenameSymbol.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index eaf5ae8a50..6865738458 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import vscode = require("vscode"); -import { RequestType } from "vscode-languageclient"; +import { RequestType } from "vscode-languageclient"; import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range } from "vscode"; // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -89,9 +89,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam } } public async prepareRename(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { + range: vscode.Range; placeholder: string;} | null> { const req:IRenameSymbolRequestArguments = { FileName : document.fileName, @@ -103,12 +101,25 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam const response = await this.languageClient?.sendRequest(PrepareRenameSymbolRequestType, req); if (!response) { - return undefined; + return null; + } + const wordRange = document.getWordRangeAtPosition(position); + if (!wordRange) { + return Promise.reject("Not a valid location for renaming."); + } - return Promise.reject(response.message); + const wordText = document.getText(wordRange); + if (response.message) { + return Promise.reject(response.message); + } + + return { + range: wordRange, + placeholder: wordText.substring(1) + }; }catch (error) { - return undefined; + return null; } } From 1c1a38e8b1236906e235967d300f3ecf69c13542 Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:57:40 +0300 Subject: [PATCH 05/20] removed substring to avoid confusion --- src/features/RenameSymbol.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 6865738458..a40198257e 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -116,7 +116,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam return { range: wordRange, - placeholder: wordText.substring(1) + placeholder: wordText }; }catch (error) { return null; From e56a794fe2bc33693653d4084932e181035e82ae Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:42:16 +1100 Subject: [PATCH 06/20] updating rename sumbol to new languageclient format --- src/features/RenameSymbol.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index a40198257e..b2835d6d01 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -5,6 +5,7 @@ import vscode = require("vscode"); import { RequestType } from "vscode-languageclient"; import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range } from "vscode"; +import type { LanguageClient } from "vscode-languageclient/node"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IRenameSymbolRequestArguments { FileName?:string @@ -45,6 +46,9 @@ export const RenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { + public override onLanguageClientSet(_languageClient: LanguageClient): void { + throw new Error("Method not implemented."); + } private command: vscode.Disposable; constructor() { @@ -67,9 +71,10 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam }; try { - const response = await this.languageClient?.sendRequest(RenameSymbolRequestType, req); + const client = await LanguageClientConsumer.getLanguageClient(); + const response = await client.sendRequest(RenameSymbolRequestType, req); - if (!response) { + if (!response.changes.length) { return undefined; } @@ -98,9 +103,10 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam }; try { - const response = await this.languageClient?.sendRequest(PrepareRenameSymbolRequestType, req); + const client = await LanguageClientConsumer.getLanguageClient(); + const response = await client.sendRequest(PrepareRenameSymbolRequestType, req); - if (!response) { + if (!response.message) { return null; } const wordRange = document.getWordRangeAtPosition(position); From 6ee4eb566fc5b67325225db05585be2622f690ff Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:03:08 +1000 Subject: [PATCH 07/20] removed command register and not implemented method --- src/features/RenameSymbol.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index b2835d6d01..23f74865c7 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -5,7 +5,6 @@ import vscode = require("vscode"); import { RequestType } from "vscode-languageclient"; import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range } from "vscode"; -import type { LanguageClient } from "vscode-languageclient/node"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IRenameSymbolRequestArguments { FileName?:string @@ -46,21 +45,7 @@ export const RenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { - public override onLanguageClientSet(_languageClient: LanguageClient): void { - throw new Error("Method not implemented."); - } - private command: vscode.Disposable; - - constructor() { - super(); - this.command = vscode.commands.registerCommand("PowerShell.RenameSymbol", () => { - throw new Error("Not implemented"); - }); - } - public dispose() :void{ - this.command.dispose(); - } public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { const req:IRenameSymbolRequestArguments = { From 6a27fd87dcee944bdb599c2ed9cdf1d2bfee4139 Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:20:26 +1000 Subject: [PATCH 08/20] implemented renamesymbol as a feature and structured under languageClientConsumers --- src/extension.ts | 7 +------ src/features/RenameSymbol.ts | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 355f31c6c4..593a6308d3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -36,7 +36,6 @@ const PackageJSON: any = require("../package.json"); const TELEMETRY_KEY = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255"; let languageConfigurationDisposable: vscode.Disposable; -let languageRenameProvider:vscode.Disposable; let logger: Logger; let sessionManager: SessionManager; let languageClientConsumers: LanguageClientConsumer[] = []; @@ -58,9 +57,6 @@ export async function activate(context: vscode.ExtensionContext): Promise { await telemetryReporter.dispose(); languageConfigurationDisposable.dispose(); - languageRenameProvider.dispose(); } diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 23f74865c7..84914f1e81 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -4,7 +4,8 @@ import vscode = require("vscode"); import { RequestType } from "vscode-languageclient"; import { LanguageClientConsumer } from "../languageClientConsumer"; -import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range } from "vscode"; +import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range, DocumentSelector } from "vscode"; +import { LanguageClient } from "vscode-languageclient/node"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IRenameSymbolRequestArguments { FileName?:string @@ -45,7 +46,14 @@ export const RenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { + private languageRenameProvider:vscode.Disposable; + constructor(documentSelector:DocumentSelector){ + super(); + this.languageRenameProvider = vscode.languages.registerRenameProvider(documentSelector,this); + } + // eslint-disable-next-line @typescript-eslint/no-empty-function + public override onLanguageClientSet(_languageClient: LanguageClient): void {} public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { const req:IRenameSymbolRequestArguments = { @@ -113,5 +121,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam return null; } } - + public dispose(): void { + this.languageRenameProvider.dispose(); + } } From 6f2812363e383c1ef7d0a5fac4959dbbee613134 Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:49:25 +1000 Subject: [PATCH 09/20] added logging. Now using forof. throw instead of promise.reject. removed unused exports --- src/extension.ts | 2 +- src/features/RenameSymbol.ts | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 593a6308d3..f41629b971 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -152,7 +152,7 @@ export async function activate(context: vscode.ExtensionContext): Promise("powerShell/renameSymbol"); -export const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); +const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); +const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { private languageRenameProvider:vscode.Disposable; - constructor(documentSelector:DocumentSelector){ + constructor(documentSelector:DocumentSelector,private logger: ILogger){ super(); + this.languageRenameProvider = vscode.languages.registerRenameProvider(documentSelector,this); } // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -72,15 +74,16 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam } const edit = new WorkspaceEdit(); - response.changes.forEach(change => { - const uri = Uri.file(change.fileName); + for (const file of response.changes) { + + const uri = Uri.file(file.fileName); - change.changes.forEach(change => { + for (const change of file.changes) { edit.replace(uri, new Range(change.startLine, change.startColumn, change.endLine, change.endColumn), change.newText); - }); - }); + } + } return edit; }catch (error) { return undefined; @@ -104,13 +107,12 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam } const wordRange = document.getWordRangeAtPosition(position); if (!wordRange) { - return Promise.reject("Not a valid location for renaming."); + throw new Error("Not a valid location for renaming."); } const wordText = document.getText(wordRange); if (response.message) { - return Promise.reject(response.message); - + throw new Error(response.message); } return { @@ -118,7 +120,9 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam placeholder: wordText }; }catch (error) { - return null; + const msg = `RenameSymbol unhandled error: ${error}`; + this.logger.writeError(msg); + throw new Error(msg); } } public dispose(): void { From 9245c93920a62dc76649ae5e30eaac6af0a6f4ff Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:19:46 +1000 Subject: [PATCH 10/20] adjusting unhandled error preifx --- src/features/RenameSymbol.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 419f82db74..0c1e81e207 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -120,7 +120,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam placeholder: wordText }; }catch (error) { - const msg = `RenameSymbol unhandled error: ${error}`; + const msg = `RenameSymbol: ${error}`; this.logger.writeError(msg); throw new Error(msg); } From a48c88e640bfa267089d04dbbe3c9c77a45784aa Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:21:18 +1000 Subject: [PATCH 11/20] added new option to specifiy if an Alias should be generated when a function parameter is renamed --- package.json | 7 ++++++- src/features/RenameSymbol.ts | 8 ++++++++ src/settings.ts | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 57cbd1b3b7..b5e09e0846 100644 --- a/package.json +++ b/package.json @@ -1011,7 +1011,12 @@ "verbose" ], "default": "off", - "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services language server. **This setting is only meant for extension developers!**" + "description": "Traces the communication between VS Code and the PowerShell Editor Services language server. **This setting is only meant for extension developers!**" + }, + "powershell.renameSymbol.shouldGenerateAlias": { + "type": "boolean", + "default": true, + "description": "Should an Alias be genereted when renaming a function parameter" } } }, diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 0c1e81e207..fd6b4f432f 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -8,11 +8,15 @@ import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Positio import { LanguageClient } from "vscode-languageclient/node"; import { ILogger } from "../logging"; // eslint-disable-next-line @typescript-eslint/no-empty-interface +interface RenameSymbolOptions { + ShouldGenerateAlias?:boolean +} interface IRenameSymbolRequestArguments { FileName?:string Line?:number Column?:number RenameTo?:string + Options?:RenameSymbolOptions } interface IPrepareRenameSymbolRequestArguments { FileName?:string @@ -64,6 +68,10 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam Column : position.character, RenameTo : newName, }; + const config = vscode.workspace.getConfiguration(); + req.Options = { + ShouldGenerateAlias: config.get("powershell.renameSymbol.shouldGenerateAlias") + }; try { const client = await LanguageClientConsumer.getLanguageClient(); diff --git a/src/settings.ts b/src/settings.ts index 878cac9036..05052dc782 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -39,6 +39,7 @@ export class Settings extends PartialSettings { cwd = ""; // NOTE: use validateCwdSetting() instead of this directly! enableReferencesCodeLens = true; analyzeOpenDocumentsOnly = false; + renameSymbol = new RenameSymbolSettings(); // TODO: Add (deprecated) useX86Host (for testing) } @@ -155,6 +156,10 @@ class ButtonSettings extends PartialSettings { showPanelMovementButtons = false; } +class RenameSymbolSettings extends PartialSettings { + shouldGenerateAlias = true; +} + // This is a recursive function which unpacks a WorkspaceConfiguration into our settings. function getSetting(key: string | undefined, value: TSetting, configuration: vscode.WorkspaceConfiguration): TSetting { // Base case where we're looking at a primitive type (or our special record). From 0b497f6232622b5bfc7f50566c3adcb75e63a810 Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Mon, 10 Jun 2024 17:21:12 +1000 Subject: [PATCH 12/20] renaming ShouldGenerateAlias to create CreateAlias --- package.json | 4 ++-- src/features/RenameSymbol.ts | 4 ++-- src/settings.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b5e09e0846..ba7053547a 100644 --- a/package.json +++ b/package.json @@ -1013,10 +1013,10 @@ "default": "off", "description": "Traces the communication between VS Code and the PowerShell Editor Services language server. **This setting is only meant for extension developers!**" }, - "powershell.renameSymbol.shouldGenerateAlias": { + "powershell.renameSymbol.createAlias": { "type": "boolean", "default": true, - "description": "Should an Alias be genereted when renaming a function parameter" + "description": "Should an Alias be created when renaming a function parameter" } } }, diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index fd6b4f432f..71e43607b0 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -9,7 +9,7 @@ import { LanguageClient } from "vscode-languageclient/node"; import { ILogger } from "../logging"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RenameSymbolOptions { - ShouldGenerateAlias?:boolean + CreateAlias?:boolean } interface IRenameSymbolRequestArguments { FileName?:string @@ -70,7 +70,7 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam }; const config = vscode.workspace.getConfiguration(); req.Options = { - ShouldGenerateAlias: config.get("powershell.renameSymbol.shouldGenerateAlias") + CreateAlias: config.get("powershell.renameSymbol.createAlias") }; try { diff --git a/src/settings.ts b/src/settings.ts index 05052dc782..a397df1a43 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -157,7 +157,7 @@ class ButtonSettings extends PartialSettings { } class RenameSymbolSettings extends PartialSettings { - shouldGenerateAlias = true; + createAlias = true; } // This is a recursive function which unpacks a WorkspaceConfiguration into our settings. From 4a2196cfa2e927b624c2d4f315c0c583b0df5672 Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 18:46:10 -0700 Subject: [PATCH 13/20] Move RenameSymbolSettings to RenameSymbol.ts I know this differs from the existing style of the codebase but it makes sense to keep the separation of concerns local. Ideally PartialSettings is also updated to a stricter type like Record or something. --- src/features/RenameSymbol.ts | 6 ++++++ src/settings.ts | 7 +++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 71e43607b0..b293f5ad9a 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -7,6 +7,7 @@ import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range, DocumentSelector } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { ILogger } from "../logging"; +import { PartialSettings } from "../settings"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RenameSymbolOptions { CreateAlias?:boolean @@ -47,6 +48,11 @@ interface IPrepareRenameSymbolRequestResponse { message : string } +export class RenameSymbolSettings extends PartialSettings { + createAlias = true; + acceptRenameDisclaimer = false; +} + const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); diff --git a/src/settings.ts b/src/settings.ts index a397df1a43..8a149aa773 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -7,6 +7,7 @@ import os = require("os"); import { ILogger } from "./logging"; import untildify from "untildify"; import path = require("path"); +import { RenameSymbolSettings } from "./features/RenameSymbol"; // TODO: Quite a few of these settings are unused in the client and instead // exist just for the server. Those settings do not need to be represented in @@ -17,7 +18,7 @@ import path = require("path"); // Perhaps we just get rid of this entirely? // eslint-disable-next-line @typescript-eslint/no-extraneous-class -class PartialSettings { } +export class PartialSettings { } export class Settings extends PartialSettings { powerShellAdditionalExePaths: PowerShellAdditionalExePathSettings = {}; @@ -156,9 +157,7 @@ class ButtonSettings extends PartialSettings { showPanelMovementButtons = false; } -class RenameSymbolSettings extends PartialSettings { - createAlias = true; -} + // This is a recursive function which unpacks a WorkspaceConfiguration into our settings. function getSetting(key: string | undefined, value: TSetting, configuration: vscode.WorkspaceConfiguration): TSetting { From 8b3b660265ebc9bf7fb9d798b57a3dcdece4a2ec Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 19:42:56 -0700 Subject: [PATCH 14/20] Revert Settings, I was wrong and created a circular dependency, oops --- src/features/RenameSymbol.ts | 5 ----- src/settings.ts | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index b293f5ad9a..0c1322559b 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -7,7 +7,6 @@ import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range, DocumentSelector } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { ILogger } from "../logging"; -import { PartialSettings } from "../settings"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RenameSymbolOptions { CreateAlias?:boolean @@ -48,10 +47,6 @@ interface IPrepareRenameSymbolRequestResponse { message : string } -export class RenameSymbolSettings extends PartialSettings { - createAlias = true; - acceptRenameDisclaimer = false; -} const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); diff --git a/src/settings.ts b/src/settings.ts index 8a149aa773..f5a9054117 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -7,7 +7,6 @@ import os = require("os"); import { ILogger } from "./logging"; import untildify from "untildify"; import path = require("path"); -import { RenameSymbolSettings } from "./features/RenameSymbol"; // TODO: Quite a few of these settings are unused in the client and instead // exist just for the server. Those settings do not need to be represented in @@ -157,6 +156,10 @@ class ButtonSettings extends PartialSettings { showPanelMovementButtons = false; } +class RenameSymbolSettings extends PartialSettings { + createAlias = true; + acceptRenameDisclaimer = false; +} // This is a recursive function which unpacks a WorkspaceConfiguration into our settings. From df907ecd836ffe0a822c702b3b9a4705e98a5144 Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 19:56:21 -0700 Subject: [PATCH 15/20] Add Rename Disclaimer. Fully detailed disclaimer to come next --- package.json | 7 +++- src/features/RenameSymbol.ts | 72 ++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index ba7053547a..21667da9f0 100644 --- a/package.json +++ b/package.json @@ -1016,7 +1016,12 @@ "powershell.renameSymbol.createAlias": { "type": "boolean", "default": true, - "description": "Should an Alias be created when renaming a function parameter" + "description": "If set, an [Alias] attribute will be added when renaming a function parameter so that the previous parameter name still works. This helps avoid breaking changes." + }, + "powershell.renameSymbol.acceptRenameDisclaimer": { + "type": "boolean", + "default": false, + "description": "Accepts the disclaimer about risks and limitations to the PowerShell rename functionality" } } }, diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 0c1322559b..15bcb54347 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -53,15 +53,22 @@ const PrepareRenameSymbolRequestType = new RequestType; constructor(documentSelector:DocumentSelector,private logger: ILogger){ super(); this.languageRenameProvider = vscode.languages.registerRenameProvider(documentSelector,this); } + // eslint-disable-next-line @typescript-eslint/no-empty-function public override onLanguageClientSet(_languageClient: LanguageClient): void {} - public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { + + public async provideRenameEdits(document: TextDocument, position: Position, newName: string, _token: CancellationToken): Promise { + + const disclaimerAccepted = await this.acknowledgeDisclaimer(); + if (!disclaimerAccepted) {return undefined;} const req:IRenameSymbolRequestArguments = { FileName : document.fileName, @@ -84,22 +91,29 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam const edit = new WorkspaceEdit(); for (const file of response.changes) { - const uri = Uri.file(file.fileName); - for (const change of file.changes) { - edit.replace(uri, + edit.replace( + uri, new Range(change.startLine, change.startColumn, change.endLine, change.endColumn), - change.newText); + change.newText + ); } } return edit; - }catch (error) { + } catch (error) { return undefined; } } - public async prepareRename(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { + + public async prepareRename( + document: vscode.TextDocument, + position: vscode.Position, + _token: vscode.CancellationToken + ): Promise { + + const disclaimerAccepted = await this.acknowledgeDisclaimer(); + if (!disclaimerAccepted) {return undefined;} const req:IRenameSymbolRequestArguments = { FileName : document.fileName, @@ -134,7 +148,49 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam throw new Error(msg); } } + + + /** Prompts the user to acknowledge the risks inherent with the rename provider and does not proceed until it is accepted */ + async acknowledgeDisclaimer(): Promise { + if (!this.disclaimerPromise) { + this.disclaimerPromise = this.acknowledgeDisclaimerImpl(); + } + return this.disclaimerPromise; + } + + /** This is a separate function so that it only runs once as a singleton and the promise only resolves once */ + async acknowledgeDisclaimerImpl(): Promise + { + const config = vscode.workspace.getConfiguration(); + const acceptRenameDisclaimer = config.get("powershell.renameSymbol.acceptRenameDisclaimer", false); + + if (!acceptRenameDisclaimer) { + const result = await vscode.window.showWarningMessage( + //TODO: Provide a link to a markdown document that appears in the editor window, preferably one hosted with the extension itself. + "The PowerShell Rename functionality has limitations. Do you accept the limitations and risks?", + "Yes", + "Workspace Only", + "No" + ); + + switch (result) { + case "Yes": + await config.update("powershell.renameSymbol.acceptRenameDisclaimer", true, vscode.ConfigurationTarget.Global); + break; + case "Workspace Only": + await config.update("powershell.renameSymbol.acceptRenameDisclaimer", true, vscode.ConfigurationTarget.Workspace); + break; + default: + void vscode.window.showInformationMessage("Rename operation cancelled and rename has been disabled until the extension is restarted."); + break; + } + } + + return config.get("powershell.renameSymbol.acceptRenameDisclaimer", false); + } + public dispose(): void { this.languageRenameProvider.dispose(); } + } From 81e2e993228775fc471a110492322b1710dccc24 Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 20:13:37 -0700 Subject: [PATCH 16/20] Add First-Run rename disclaimer --- media/RenameDisclaimer.txt | 9 +++++++++ src/features/RenameSymbol.ts | 13 ++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 media/RenameDisclaimer.txt diff --git a/media/RenameDisclaimer.txt b/media/RenameDisclaimer.txt new file mode 100644 index 0000000000..57e309e508 --- /dev/null +++ b/media/RenameDisclaimer.txt @@ -0,0 +1,9 @@ +### PowerShell Extension Rename Disclaimer ### + +PowerShell is not a statically typed language. As such, the renaming of functions, parameters, and other symbols can only be done on a best effort basis. While this is sufficient for the majority of use cases, it cannot be relied upon to find all instances of a symbol and rename them across an entire code base such as in C# or TypeScript. + +There are several edge case scenarios which may exist where rename is difficult or impossible, or unable to be determined due to the dynamic scoping nature of PowerShell. + +# Known Rename Limitations + +- Renaming can only be done within a single file. Renaming symbols across multiple files is not supported. diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 15bcb54347..c01b418145 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -165,19 +165,22 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam const acceptRenameDisclaimer = config.get("powershell.renameSymbol.acceptRenameDisclaimer", false); if (!acceptRenameDisclaimer) { + const extensionPath = vscode.extensions.getExtension("ms-vscode.PowerShell")?.extensionPath; + const disclaimerPath = vscode.Uri.file(`${extensionPath}/media/RenameDisclaimer.txt`); + const result = await vscode.window.showWarningMessage( //TODO: Provide a link to a markdown document that appears in the editor window, preferably one hosted with the extension itself. - "The PowerShell Rename functionality has limitations. Do you accept the limitations and risks?", - "Yes", - "Workspace Only", + `The PowerShell Rename functionality has limitations and risks, please [review the disclaimer](${disclaimerPath}).`, + "I Accept", + "I Accept [Workspace]", "No" ); switch (result) { - case "Yes": + case "I Accept": await config.update("powershell.renameSymbol.acceptRenameDisclaimer", true, vscode.ConfigurationTarget.Global); break; - case "Workspace Only": + case "I Accept [Workspace]": await config.update("powershell.renameSymbol.acceptRenameDisclaimer", true, vscode.ConfigurationTarget.Workspace); break; default: From 8e456e1dd6406a2f185b1f751a231ca27090256d Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 20:24:38 -0700 Subject: [PATCH 17/20] Refresh config when fetching after update --- src/features/RenameSymbol.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index c01b418145..7a2e80b26c 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -189,7 +189,8 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam } } - return config.get("powershell.renameSymbol.acceptRenameDisclaimer", false); + // Refresh the config to ensure it was set + return vscode.workspace.getConfiguration().get("powershell.renameSymbol.acceptRenameDisclaimer", false); } public dispose(): void { From 76ba1ddf4f6932fce1574e9a782966d687008ef0 Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 21:39:13 -0700 Subject: [PATCH 18/20] Refine format to use Unicode --- media/RenameDisclaimer.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/media/RenameDisclaimer.txt b/media/RenameDisclaimer.txt index 57e309e508..374c614ec2 100644 --- a/media/RenameDisclaimer.txt +++ b/media/RenameDisclaimer.txt @@ -1,9 +1,18 @@ -### PowerShell Extension Rename Disclaimer ### +⚠️⚠️⚠️ PowerShell Extension Rename Disclaimer ⚠️⚠️⚠️ PowerShell is not a statically typed language. As such, the renaming of functions, parameters, and other symbols can only be done on a best effort basis. While this is sufficient for the majority of use cases, it cannot be relied upon to find all instances of a symbol and rename them across an entire code base such as in C# or TypeScript. There are several edge case scenarios which may exist where rename is difficult or impossible, or unable to be determined due to the dynamic scoping nature of PowerShell. -# Known Rename Limitations +🤚🤚 Unsupported Scenarios -- Renaming can only be done within a single file. Renaming symbols across multiple files is not supported. +❌ Renaming can only be done within a single file. Renaming symbols across multiple files is not supported. + +👍👍 Implemented and Tested Rename Scenaiors + +See the supported rename scenarios we are currently testing at: +https://github.com/PowerShell/PowerShellEditorServices/blob/main/test/PowerShellEditorServices.Test.Shared/Refactoring + +📄📄 Filing a Rename Issue + +If there is a rename scenario you feel can be reasonably supported in PowerShell, please file a bug report in the PowerShellEditorServices repository with the "Expected" and "Actual" being the before and after rename. We will evaluate it and accept or reject it and give reasons why. Items that fall under the Unsupported Scenarios above will be summarily rejected, however that does not mean that they may not be supported in the future if we come up with a reasonably safe way to implement a scenario. From fb60faf974a147c9eed17f140d13d8ef2aa23970 Mon Sep 17 00:00:00 2001 From: Justin Grote Date: Wed, 11 Sep 2024 23:30:55 -0700 Subject: [PATCH 19/20] Add note about unsaved files --- media/RenameDisclaimer.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/media/RenameDisclaimer.txt b/media/RenameDisclaimer.txt index 374c614ec2..13009fac46 100644 --- a/media/RenameDisclaimer.txt +++ b/media/RenameDisclaimer.txt @@ -7,6 +7,7 @@ There are several edge case scenarios which may exist where rename is difficult 🤚🤚 Unsupported Scenarios ❌ Renaming can only be done within a single file. Renaming symbols across multiple files is not supported. +❌ Unsaved/Virtual files are currently not supported, you must save a file to disk before you can rename values. 👍👍 Implemented and Tested Rename Scenaiors From 627ec9b959a67195b76818df54c03924f1f00ade Mon Sep 17 00:00:00 2001 From: Razmo99 <3089087+Razmo99@users.noreply.github.com> Date: Fri, 27 Sep 2024 21:30:20 +1000 Subject: [PATCH 20/20] Adjusted VScode renameSymbol requests arguments to support new LSP framework --- src/features/RenameSymbol.ts | 42 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/features/RenameSymbol.ts b/src/features/RenameSymbol.ts index 7a2e80b26c..071071ca8e 100644 --- a/src/features/RenameSymbol.ts +++ b/src/features/RenameSymbol.ts @@ -2,27 +2,22 @@ // Licensed under the MIT License. import vscode = require("vscode"); -import { RequestType } from "vscode-languageclient"; +import { RequestType, TextDocumentIdentifier } from "vscode-languageclient"; import { LanguageClientConsumer } from "../languageClientConsumer"; import { RenameProvider, WorkspaceEdit, TextDocument, CancellationToken, Position,Uri,Range, DocumentSelector } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { ILogger } from "../logging"; // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface RenameSymbolOptions { - CreateAlias?:boolean -} + interface IRenameSymbolRequestArguments { - FileName?:string - Line?:number - Column?:number - RenameTo?:string - Options?:RenameSymbolOptions + TextDocument:TextDocumentIdentifier + Position:Position + NewName:string } interface IPrepareRenameSymbolRequestArguments { - FileName?:string - Line?:number - Column?:number - RenameTo?:string + TextDocument:TextDocumentIdentifier + Position:Position + NewName:string } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -48,8 +43,8 @@ interface IPrepareRenameSymbolRequestResponse { } -const RenameSymbolRequestType = new RequestType("powerShell/renameSymbol"); -const PrepareRenameSymbolRequestType = new RequestType("powerShell/PrepareRenameSymbol"); +const RenameSymbolRequestType = new RequestType("textDocument/rename"); +const PrepareRenameSymbolRequestType = new RequestType("textDocument/prepareRename"); export class RenameSymbolFeature extends LanguageClientConsumer implements RenameProvider { private languageRenameProvider:vscode.Disposable; @@ -71,14 +66,9 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam if (!disclaimerAccepted) {return undefined;} const req:IRenameSymbolRequestArguments = { - FileName : document.fileName, - Line: position.line, - Column : position.character, - RenameTo : newName, - }; - const config = vscode.workspace.getConfiguration(); - req.Options = { - CreateAlias: config.get("powershell.renameSymbol.createAlias") + TextDocument : {uri:document.uri.toString()}, + Position : position, + NewName : newName }; try { @@ -116,9 +106,9 @@ export class RenameSymbolFeature extends LanguageClientConsumer implements Renam if (!disclaimerAccepted) {return undefined;} const req:IRenameSymbolRequestArguments = { - FileName : document.fileName, - Line: position.line, - Column : position.character, + TextDocument : {uri:document.uri.toString()}, + Position : position, + NewName : "" }; try {