Skip to content

Commit 4a8c82d

Browse files
Razmo99andyleejordanJustinGroteCopilotCopilot
authored
RenameProvider for variable/function renaming (#2152)
Adds "F2" Rename Support via a provider that parses the AST for the variables. A disclaimer is present that this is only scoped for one script at a time. --------- Co-authored-by: Andy Jordan <2226434+andyleejordan@users.noreply.github.com> Co-authored-by: Justin Grote <JustinGrote@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <copilot@github.com>
1 parent caa9d28 commit 4a8c82d

108 files changed

Lines changed: 3141 additions & 35 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-test.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
ci:
1111
name: dotnet
1212
strategy:
13+
fail-fast: false
1314
matrix:
1415
os: [ windows-latest, macos-latest, ubuntu-latest ]
1516
runs-on: ${{ matrix.os }}
@@ -43,16 +44,21 @@ jobs:
4344
shell: pwsh
4445
run: ./pwsh/tools/install-powershell.ps1 -Preview -Destination ./preview
4546

46-
- name: If debugging, start upterm for interactive pipeline troubleshooting
47-
if: ${{ runner.debug == 1 }}
48-
uses: owenthereal/action-upterm@v1
49-
with:
50-
wait-timeout-minutes: 1
51-
5247
- name: Build and test
5348
shell: pwsh
5449
run: Invoke-Build -Configuration Release TestFull
5550

51+
# - name: Start VS Code Tunnel if failed or in debug mode
52+
# if: ${{ failure() || runner.debug == '1' }}
53+
# #v0.0.2 - Pinned for security
54+
# uses: justingrote/vscode-action@5d495ef6156c20f1f9bb21031c15776d476c10c3
55+
# with:
56+
# #All these settings are optional
57+
# tunnel-name: 'pses-action-tunnel'
58+
# connection-timeout: 2
59+
# session-timeout: 30
60+
# no-cache-cli-auth: true
61+
5662
- name: Upload build artifacts
5763
if: always()
5864
uses: actions/upload-artifact@v5

.github/workflows/vim-test.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ jobs:
6262
repository: thinca/vim-themis
6363
path: vim-themis
6464

65-
# - name: Debug if run with debugging enabled
66-
# uses: owenthereal/action-upterm@v1
67-
6865
- name: Run Themis with full CLI
6966
env:
7067
THEMIS_VIM: ${{ steps.vim.outputs.executable }}
@@ -74,3 +71,14 @@ jobs:
7471
env:
7572
THEMIS_VIM: ${{ steps.vim.outputs.executable }}
7673
run: ./vim-themis/bin/themis ./test/vim-simple-test.vim
74+
75+
# - name: Start VS Code Tunnel if failed or in debug mode
76+
# if: ${{ failure() || runner.debug == '1' }}
77+
# #v0.0.2 - Pinned for security
78+
# uses: justingrote/vscode-action@5d495ef6156c20f1f9bb21031c15776d476c10c3
79+
# with:
80+
# #All these settings are optional
81+
# tunnel-name: 'pses-action-tunnel'
82+
# connection-timeout: 2
83+
# session-timeout: 30
84+
# no-cache-cli-auth: true

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ This release contains a logging overhaul which purposely removes our
5454
dependency on Serilog and should lead to improved stability with
5555
PowerShell 5.1 (by avoiding a major GAC assembly conflict).
5656

57+
## v3.30.0 (mistakenly released as v3.3.0)
58+
### Friday, November 15, 2024
59+
60+
See more details at the GitHub Release for [v3.3.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.3.0).
61+
62+
Logging updates and dropped EOL PowerShell
63+
5764
## v3.21.0
5865
### Wednesday, October 30, 2024
5966

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
functionality needed to enable a consistent and robust PowerShell development
99
experience in almost any editor or integrated development environment (IDE).
1010

11-
## [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) clients using PowerShell Editor Services:
11+
## [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) clients using PowerShell Editor Services
1212

1313
- [PowerShell for Visual Studio Code](https://github.com/PowerShell/vscode-powershell)
1414
> [!NOTE]
@@ -143,6 +143,34 @@ The debugging functionality in PowerShell Editor Services is available in the fo
143143
- [powershell.nvim for Neovim](https://github.com/TheLeoP/powershell.nvim)
144144
- [intellij-powershell](https://github.com/ant-druha/intellij-powershell)
145145

146+
### Rename Disclaimer
147+
148+
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.
149+
150+
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.
151+
152+
The focus of the rename support is on quick updates to variables or functions within a self-contained script file. It is not intended for module developers to find and rename a symbol across multiple files, which is very difficult to do as the relationships are primarily only computed at runtime and not possible to be statically analyzed.
153+
154+
#### 👍 [Implemented and Tested Rename Scenarios](https://github.com/PowerShell/PowerShellEditorServices/blob/main/test/PowerShellEditorServices.Test.Shared/Refactoring)
155+
156+
#### 🤚 Unsupported Scenarios
157+
158+
- ❌ Renaming can only be done within a single file. Renaming symbols across multiple files is not supported, even if those are dotsourced from the source file.
159+
- ❌ Functions or variables must have a corresponding definition within their scope or above to be renamed. If we cannot find the original definition of a variable or function, the rename will not be supported.
160+
- ❌ Dynamic Parameters are not supported
161+
- ❌ Dynamically constructed splat parameters will not be renamed/updated (e.g. `$splat = @{};$splat.a = 5;Do-Thing @a`)
162+
- ❌ Scoped variables (e.g. $SCRIPT:test) are not currently supported
163+
- ❌ Renaming a variable inside of a scriptblock that is used in unscoped operations like `Foreach-Parallel` or `Start-Job` and the variable is not defined within the scriptblock is not supported and can have unexpected results.
164+
- ❌ Scriptblocks part of an assignment are considered isolated scopes. For example `$a = 5; $x = {$a}; & $x` does not consider the two $a to be related, even though in execution this reference matches.
165+
- ❌ Scriptblocks that are part of a parameter are assumed to not be executing in a different runspace. For example, the renaming behavior will treat `ForEach-Object -Parallel {$x}` the same as `Foreach-Object {$x}` for purposes of finding scope definitions. To avoid unexpected renaming, define/redefine all your variables in the scriptblock using a param block.
166+
- ❌ A lot of the logic relies on the position of items, so for example, defining a variable in a `begin` block and placing it after a `process` block, while technically correct in PowerShell, will not rename as expected.
167+
- ❌ Similarly, defining a function, and having the function rely on a variable that is assigned outside the function and after the function definition, will not find the outer variable reference.
168+
-`Get-Variable` and `Set-Variable` are not considered and not currently searched for renames
169+
170+
#### 📄 Filing a Rename Issue
171+
172+
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.
173+
146174
## API Usage
147175

148176
Please note that we only consider the following as stable APIs that can be relied on:

src/PowerShellEditorServices/Server/PsesLanguageServer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public async Task StartAsync()
9191
.ClearProviders()
9292
.AddPsesLanguageServerLogging()
9393
.SetMinimumLevel(_minimumLogLevel))
94+
// TODO: Consider replacing all WithHandler with AddSingleton
95+
.WithConfigurationSection("powershell.rename")
9496
.WithHandler<PsesWorkspaceSymbolsHandler>()
9597
.WithHandler<PsesTextDocumentHandler>()
9698
.WithHandler<GetVersionHandler>()
@@ -123,6 +125,8 @@ public async Task StartAsync()
123125
.WithHandler<ExpandAliasHandler>()
124126
.WithHandler<PsesSemanticTokensHandler>()
125127
.WithHandler<DidChangeWatchedFilesHandler>()
128+
.WithHandler<PrepareRenameHandler>()
129+
.WithHandler<RenameHandler>()
126130
// NOTE: The OnInitialize delegate gets run when we first receive the
127131
// _Initialize_ request:
128132
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize

src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public static IServiceCollection AddPsesLanguageServices(
5050
extensionService.InitializeAsync();
5151
return extensionService;
5252
})
53-
.AddSingleton<AnalysisService>();
53+
.AddSingleton<AnalysisService>()
54+
.AddSingleton<RenameService>();
5455
}
5556

5657
public static IServiceCollection AddPsesDebugServices(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using OmniSharp.Extensions.JsonRpc;
5+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
6+
7+
namespace Microsoft.PowerShell.EditorServices.Handlers;
8+
9+
/// <summary>
10+
/// A convenience exception for handlers to throw when a request fails for a normal reason,
11+
/// and to communicate that reason to the user without a full internal stacktrace.
12+
/// </summary>
13+
/// <param name="message">The message describing the reason for the request failure.</param>
14+
/// <param name="logDetails">Additional details to be logged regarding the failure. It should be serializable to JSON.</param>
15+
/// <param name="severity">The severity level of the message. This is only shown in internal logging.</param>
16+
internal class HandlerErrorException
17+
(
18+
string message,
19+
object logDetails = null,
20+
MessageType severity = MessageType.Error
21+
) : RpcErrorException(0, logDetails!, message)
22+
{
23+
internal MessageType Severity { get; } = severity;
24+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
namespace Microsoft.PowerShell.EditorServices.Refactoring
6+
{
7+
internal class TargetSymbolNotFoundException : Exception
8+
{
9+
public TargetSymbolNotFoundException()
10+
{
11+
}
12+
13+
public TargetSymbolNotFoundException(string message)
14+
: base(message)
15+
{
16+
}
17+
18+
public TargetSymbolNotFoundException(string message, Exception inner)
19+
: base(message, inner)
20+
{
21+
}
22+
}
23+
24+
internal class FunctionDefinitionNotFoundException : Exception
25+
{
26+
public FunctionDefinitionNotFoundException()
27+
{
28+
}
29+
30+
public FunctionDefinitionNotFoundException(string message)
31+
: base(message)
32+
{
33+
}
34+
35+
public FunctionDefinitionNotFoundException(string message, Exception inner)
36+
: base(message, inner)
37+
{
38+
}
39+
}
40+
}

src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public override async Task<TextEditContainer> Handle(DocumentFormattingParams re
9090
return s_emptyTextEditContainer;
9191
}
9292

93-
return new TextEditContainer(new TextEdit
93+
return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit
9494
{
9595
NewText = formattedScript,
9696
Range = editRange
@@ -184,7 +184,7 @@ public override async Task<TextEditContainer> Handle(DocumentRangeFormattingPara
184184
return s_emptyTextEditContainer;
185185
}
186186

187-
return new TextEditContainer(new TextEdit
187+
return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit
188188
{
189189
NewText = formattedScript,
190190
Range = editRange
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#nullable enable
4+
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Microsoft.PowerShell.EditorServices.Services;
8+
9+
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
10+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
11+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
12+
13+
namespace Microsoft.PowerShell.EditorServices.Handlers;
14+
15+
/// <summary>
16+
/// A handler for <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareRename">textDocument/prepareRename</a>
17+
/// </summary>
18+
internal class PrepareRenameHandler
19+
(
20+
RenameService renameService
21+
) : IPrepareRenameHandler
22+
{
23+
public RenameRegistrationOptions GetRegistrationOptions(RenameCapability capability, ClientCapabilities clientCapabilities) => capability.PrepareSupport ? new() { PrepareProvider = true } : new();
24+
25+
public async Task<RangeOrPlaceholderRange?> Handle(PrepareRenameParams request, CancellationToken cancellationToken)
26+
=> await renameService.PrepareRenameSymbol(request, cancellationToken).ConfigureAwait(false);
27+
}
28+
29+
/// <summary>
30+
/// A handler for <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rename">textDocument/rename</a>
31+
/// </summary>
32+
internal class RenameHandler(
33+
RenameService renameService
34+
) : IRenameHandler
35+
{
36+
// RenameOptions may only be specified if the client states that it supports prepareSupport in its initial initialize request.
37+
public RenameRegistrationOptions GetRegistrationOptions(RenameCapability capability, ClientCapabilities clientCapabilities) => capability.PrepareSupport ? new() { PrepareProvider = true } : new();
38+
39+
public async Task<WorkspaceEdit?> Handle(RenameParams request, CancellationToken cancellationToken)
40+
=> await renameService.RenameSymbol(request, cancellationToken).ConfigureAwait(false);
41+
}

0 commit comments

Comments
 (0)