Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RenameProvider for variable/function renaming #2152

Open
wants to merge 138 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
a98ae1b
adding in rename symbol service
Razmo99 Mar 26, 2023
6661980
switched to using a task not sure if there is a better way
Razmo99 Mar 26, 2023
30c182d
completed rename function
Razmo99 Mar 26, 2023
02fbd45
slight refactoring
Razmo99 Mar 26, 2023
139bfca
Adding in unit teste for refactoring functions
Razmo99 Mar 29, 2023
95f67cf
test case for a function that is flat or inline
Razmo99 Mar 29, 2023
9221ca7
added new test case
Razmo99 Sep 13, 2023
5a4b381
initial commit
Razmo99 Sep 18, 2023
afdc72b
converted visitor class from powershell to C#
Razmo99 Sep 18, 2023
cb564bc
Updated to using ps1 file with a renamed varient
Razmo99 Sep 19, 2023
3c5a879
Bug Fixes now passing all tests
Razmo99 Sep 19, 2023
baafc42
Stripped un-needed functions now using FunctionRename class
Razmo99 Sep 19, 2023
3f0332b
small clean up of init of class
Razmo99 Sep 25, 2023
915cad4
unneeded init of class var
Razmo99 Sep 25, 2023
1b06af2
init commit of variable visitor class
Razmo99 Sep 25, 2023
8447b2c
piping for vscode rename symbol
Razmo99 Sep 25, 2023
eca9f59
initial tests for variable visitor class
Razmo99 Sep 25, 2023
47a174d
fixing typo
Razmo99 Sep 25, 2023
c0465c0
adjusting scopestack to store Ast objects
Razmo99 Sep 25, 2023
06784a0
added additional tests for variablerename visitor
Razmo99 Sep 26, 2023
60b81e9
adjusting so it finds the first variable definition within scope
Razmo99 Sep 26, 2023
b3e8f9c
added function to get variable top assignment
Razmo99 Sep 26, 2023
6a0fb41
renamed scriptfile to scriptast
Razmo99 Sep 26, 2023
ce77226
can visit binary expressions now
Razmo99 Sep 26, 2023
6dc4ac3
can visit dountil and dowhile
Razmo99 Sep 26, 2023
7f1d7bc
logic to stop start shouldrename
Razmo99 Sep 26, 2023
d5d73ae
can visit scriptexpressions now
Razmo99 Sep 26, 2023
ac9f552
start stop logic for if a redefinition is found
Razmo99 Sep 26, 2023
fab8477
formatting
Razmo99 Sep 26, 2023
9cbc336
implemented visithastable
Razmo99 Sep 27, 2023
7e9a803
function to determine if a node is within a targets scope
Razmo99 Sep 27, 2023
99c731b
adjusted get variable top assignment for better detection
Razmo99 Sep 27, 2023
329c86a
additional test cases
Razmo99 Sep 27, 2023
d7df0f1
additional tests
Razmo99 Sep 27, 2023
52fdd4f
added break for finding the top variable assignment
Razmo99 Sep 27, 2023
0bc1202
implemented visit command parameter
Razmo99 Sep 27, 2023
7994264
implemented visit member expression
Razmo99 Sep 27, 2023
a743bb7
implemented visitparentexpression
Razmo99 Sep 27, 2023
96e71b5
altered logic for variable renamed to check operator is equals
Razmo99 Sep 27, 2023
066ba98
removing examples file
Razmo99 Sep 27, 2023
9f46e27
formatting and additional test
Razmo99 Sep 28, 2023
5b90bd3
formatting and proper sorting for gettestscript
Razmo99 Sep 28, 2023
8262e4d
additional test data
Razmo99 Sep 28, 2023
f6b0b16
reworked class so that oldname is no longer needed
Razmo99 Sep 28, 2023
3e8871b
implemented some new visitors
Razmo99 Sep 28, 2023
b889fb0
early start on commandparameter renaming
Razmo99 Sep 28, 2023
da08199
more implementations and some formatting
Razmo99 Sep 28, 2023
526990f
logic to determin if we are renaming a var or parameter
Razmo99 Sep 28, 2023
cd05eaa
additional test
Razmo99 Sep 28, 2023
656dc12
additional tests for parameters
Razmo99 Sep 30, 2023
54136f4
adjusting checks for parameters
Razmo99 Sep 30, 2023
01a22de
case insensitive compare & adjustment for get ast parent scope to fav…
Razmo99 Sep 30, 2023
04b586c
initial implentation of prepare rename provider
Razmo99 Oct 13, 2023
44fa10c
new test to handle detection for if the target function is a param ast
Razmo99 Oct 13, 2023
0e97bd5
new exception for when dot sourcing is detected
Razmo99 Oct 13, 2023
c6da57c
added more detection and errors for prepare rename symbol
Razmo99 Oct 13, 2023
c1260b6
new exception for when the function definition cannot be found
Razmo99 Oct 13, 2023
d0d87c6
no longer using trygetsymbolatposition as it doesnt detect parameterA…
Razmo99 Oct 13, 2023
eb61e05
further adjustments to detection
Razmo99 Oct 13, 2023
3d16517
switched to processing using iteration to avoid stack overflow
Razmo99 Oct 14, 2023
b6d57b0
Fixing typo
Razmo99 Oct 14, 2023
cc72715
Switching tests to use iterative class
Razmo99 Oct 14, 2023
31c55e7
init version of the variable rename iterative
Razmo99 Oct 14, 2023
ed6bb7e
switched tests and vscode to use iterative class
Razmo99 Oct 14, 2023
0f6b1e3
new test to check for method with the same parameter name
Razmo99 Oct 14, 2023
9e6a08d
fixing up tests for VariableParameterCommandWithSameName
Razmo99 Oct 14, 2023
8839871
fixing up tests
Razmo99 Oct 14, 2023
d7cec11
adjusting tests for more complexity
Razmo99 Oct 14, 2023
26f51a4
now adds Alias on commandParameterRenaming
Razmo99 Oct 14, 2023
f61893e
refactored alias creation for readability
Razmo99 Oct 15, 2023
720bd32
updated prepeare rename symbol to use iterative and added msg for if …
Razmo99 Oct 15, 2023
2876946
renamed renamevariableiterative to IterativeVariableRename
Razmo99 Oct 15, 2023
552e2a7
using switch instead of else if
Razmo99 Oct 15, 2023
768e1b2
formatting for rename symbol
Razmo99 Oct 15, 2023
b76ee0b
moved Function shared test content into its own folder
Razmo99 Oct 15, 2023
9490d8b
New Test for splatted variable parameter renaming
Razmo99 Oct 15, 2023
2c20d30
first stage of supporting symbol renaming for splatted command Ast calls
Razmo99 Oct 15, 2023
ef22ad1
split out exceptions into generic file
Razmo99 Oct 15, 2023
85f8beb
updated to use its own internal version
Razmo99 Oct 15, 2023
65fcc56
added functionality to reverse lookup the top variable from a splat
Razmo99 Oct 15, 2023
37dd1ac
Detter symbol detection was timing out on larger files
Razmo99 Oct 15, 2023
bc0ebcb
added utilities for common renaming functions updated tests
Razmo99 Oct 15, 2023
2c0247f
adjusted rename to use utilities
Razmo99 Oct 15, 2023
a349bb3
added comments to NewSplattedModification
Razmo99 Oct 24, 2023
9e0f092
adjusted test to use -username param instead of -password due to Alia…
Razmo99 Oct 24, 2023
0e696a2
extracted method of LookForParentOfType from GetFuncDecFromCommAst
Razmo99 Oct 24, 2023
aa188db
adjusted LookForParent so it accepts multiple types to look for
Razmo99 Oct 24, 2023
0abd97b
adjusting iterative functions to use LookForParentOfType method
Razmo99 Oct 24, 2023
7849d6b
Moved GetAstNodeByLineAndColumn to a generic method
Razmo99 Oct 24, 2023
c94ffa3
updated visitors to use generic GetAstNodeByLineAndColumn
Razmo99 Oct 24, 2023
8222620
formatting moved GetFunctionDefByCommandAst to Utilities
Razmo99 Oct 24, 2023
4fb4685
Refactoring to use Utilities class for generic methods
Razmo99 Oct 24, 2023
fad7669
Renaming methods to be clearer
Razmo99 Oct 24, 2023
24d37af
reworked GetAst for better detection
Razmo99 Oct 27, 2023
6aeb6e5
Added a test to get a function definition ast
Razmo99 Mar 21, 2024
3d28315
additional checks in getast for namedblock detection
Razmo99 Mar 21, 2024
d0e94e2
formatting an new test for finding a function
Razmo99 Oct 27, 2023
25bb53a
additional changes to getast utility method
Razmo99 Mar 21, 2024
e5cc303
reverting changes from bad merge request pull
Razmo99 Mar 21, 2024
f23b476
removing unused properties of the class
Razmo99 Mar 22, 2024
d331641
migrated FunctionDefinitionNotFoundException to exceptions.cs
Razmo99 Mar 22, 2024
3089fb6
removed original recursive visitor classes
Razmo99 Mar 22, 2024
ea1ecdc
removed unsued class properties from function visitor
Razmo99 Mar 22, 2024
05dcbb3
condensing if statements
Razmo99 Mar 22, 2024
89ffeaf
Broke up Process node into 3 sub functions for readability
Razmo99 Mar 22, 2024
4d8829a
fixing comment grammar
Razmo99 Mar 22, 2024
3353df0
New Method and tests to check if a script ast contains dot sourcing
Razmo99 Mar 24, 2024
da4897e
finalised dot source detection and notification
Razmo99 Mar 24, 2024
5f51977
fixing spelling / naming mistakes
Razmo99 Mar 24, 2024
5b71cc9
removing .vscode files
Razmo99 Mar 25, 2024
66506fe
deleting package-lock.json
Razmo99 Mar 25, 2024
d3d5738
cleaning up comments and removed unused code
Razmo99 Mar 25, 2024
6009456
Adjusted refactoring Tests to use IAsyncLifetime instead of IDisposable
Razmo99 Jun 2, 2024
939d568
Fix Path.Combine to be cross platform
JustinGrote Jun 3, 2024
107230a
Fixing an odd edge case, of not being to rename a variable directly u…
Razmo99 Jun 5, 2024
96a693c
added tests and logic for duplicate assignment cases for; foreach and…
Razmo99 Jun 5, 2024
234d9e3
Adding in out of scope $i to test case which shouldnt be renamed
Razmo99 Jun 5, 2024
515483d
.net 8 requires a newline at the start of the script to recognise the…
Razmo99 Jun 6, 2024
783d8c9
fixing type in test name
Razmo99 Jun 6, 2024
6fcacce
CommandAst input was being sent to variable renamer not function rena…
Razmo99 Jun 6, 2024
ffadfe6
additional condition so that a function CommandAst will not be touche…
Razmo99 Jun 7, 2024
d3b8229
Making RenameSymbolParams public for xunit serializer
Razmo99 Jun 7, 2024
dd8e70a
Renamed Test .ps1 to match class property name, reworked test cases t…
Razmo99 Jun 7, 2024
6a76bdc
consolidated tests as their are no special requirements
Razmo99 Jun 7, 2024
1a1168c
moved serializer and getmodifiedscriptcontent seperate file as it wil…
Razmo99 Jun 7, 2024
01c5e9f
Adding missing test case renamed.ps1 varient
Razmo99 Jun 7, 2024
3c125c5
removing unused test case data
Razmo99 Jun 7, 2024
4a0247f
updated GetModifiedScript with changes from the VariableRenameTests, …
Razmo99 Jun 7, 2024
0dd88cd
modified variable test cases to use parameterized test cases
Razmo99 Jun 7, 2024
987e2b4
Added a new test case for renaming an inner variable leaking out the …
Razmo99 Jun 7, 2024
1714312
reworked applicable utilities tests to be parameterized
Razmo99 Jun 7, 2024
763dbf8
Added test case for simple function parameter rename, added clause fo…
Razmo99 Jun 7, 2024
3093101
adding plumbling for shouldgenerateAlias on server side
Razmo99 Jun 7, 2024
28761bf
Passing through shouldGenerateAlias to VariableVisitor Class
Razmo99 Jun 7, 2024
956dbea
added new test cases for functions with variables defined outside of …
Razmo99 Jun 7, 2024
386b707
renaming ShouldGenerateAlias to create CreateAlias
Razmo99 Jun 10, 2024
4148425
Add Missing Disclaimer
JustinGrote Sep 12, 2024
b5838e8
Explicitly Show Unsaved Files as currently unsupported. It's probably…
JustinGrote Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/PowerShellEditorServices/Server/PsesLanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public async Task StartAsync()
.WithHandler<ExpandAliasHandler>()
.WithHandler<PsesSemanticTokensHandler>()
.WithHandler<DidChangeWatchedFilesHandler>()
.WithHandler<PrepareRenameSymbolHandler>()
.WithHandler<RenameSymbolHandler>()
// NOTE: The OnInitialize delegate gets run when we first receive the
// _Initialize_ request:
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using MediatR;
using System.Management.Automation.Language;
using OmniSharp.Extensions.JsonRpc;
using Microsoft.PowerShell.EditorServices.Services;
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
using Microsoft.PowerShell.EditorServices.Refactoring;
using Microsoft.PowerShell.EditorServices.Services.Symbols;

namespace Microsoft.PowerShell.EditorServices.Handlers
{
[Serial, Method("powerShell/PrepareRenameSymbol")]
internal interface IPrepareRenameSymbolHandler : IJsonRpcRequestHandler<PrepareRenameSymbolParams, PrepareRenameSymbolResult> { }

internal class PrepareRenameSymbolParams : IRequest<PrepareRenameSymbolResult>
{
public string FileName { get; set; }
public int Line { get; set; }
public int Column { get; set; }
public string RenameTo { get; set; }
}
internal class PrepareRenameSymbolResult
{
public string message;
}

internal class PrepareRenameSymbolHandler : IPrepareRenameSymbolHandler
{
private readonly WorkspaceService _workspaceService;

public PrepareRenameSymbolHandler(WorkspaceService workspaceService) => _workspaceService = workspaceService;

public async Task<PrepareRenameSymbolResult> Handle(PrepareRenameSymbolParams request, CancellationToken cancellationToken)
{
if (!_workspaceService.TryGetFile(request.FileName, out ScriptFile scriptFile))
{
// TODO: Unsaved file support. We need to find the unsaved file in the text documents synced to the LSP and use that as our Ast Base.
throw new HandlerErrorException($"File {request.FileName} not found in workspace. Unsaved files currently do not support the rename symbol feature.");
}
return await Task.Run(() =>
{
PrepareRenameSymbolResult result = new()
{
message = ""
};
// ast is FunctionDefinitionAst or CommandAst or VariableExpressionAst or StringConstantExpressionAst &&
SymbolReference symbol = scriptFile.References.TryGetSymbolAtPosition(request.Line + 1, request.Column + 1);
Ast token = Utilities.GetAst(request.Line + 1, request.Column + 1, scriptFile.ScriptAst);

if (token == null)
{
result.message = "Unable to find symbol";
return result;
}
if (Utilities.AssertContainsDotSourced(scriptFile.ScriptAst))
{
result.message = "Dot Source detected, this is currently not supported operation aborted";
return result;
}

bool IsFunction = false;
string tokenName = "";

switch (token)
{

case FunctionDefinitionAst FuncAst:
IsFunction = true;
tokenName = FuncAst.Name;
break;
case VariableExpressionAst or CommandParameterAst or ParameterAst:
IsFunction = false;
tokenName = request.RenameTo;
break;
case StringConstantExpressionAst:

if (token.Parent is CommandAst CommAst)
{
IsFunction = true;
tokenName = CommAst.GetCommandName();
}
else
{
IsFunction = false;
}
break;
}

if (IsFunction)
{
try
{
IterativeFunctionRename visitor = new(tokenName,
request.RenameTo,
token.Extent.StartLineNumber,
token.Extent.StartColumnNumber,
scriptFile.ScriptAst);
}
catch (FunctionDefinitionNotFoundException)
{
result.message = "Failed to Find function definition within current file";
}
}
else
{
IterativeVariableRename visitor = new(tokenName,
token.Extent.StartLineNumber,
token.Extent.StartColumnNumber,
scriptFile.ScriptAst);
if (visitor.TargetVariableAst == null)
{
result.message = "Failed to find variable definition within the current file";
}
}
return result;
}).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using System.Management.Automation.Language;
using OmniSharp.Extensions.JsonRpc;
using Microsoft.PowerShell.EditorServices.Services;
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
using Microsoft.PowerShell.EditorServices.Refactoring;
using System;
namespace Microsoft.PowerShell.EditorServices.Handlers
{
[Serial, Method("powerShell/renameSymbol")]
internal interface IRenameSymbolHandler : IJsonRpcRequestHandler<RenameSymbolParams, RenameSymbolResult> { }

public class RenameSymbolOptions
{
public bool CreateAlias { get; set; }
}

public class RenameSymbolParams : IRequest<RenameSymbolResult>
{
public string FileName { get; set; }
public int Line { get; set; }
public int Column { get; set; }
public string RenameTo { get; set; }
public RenameSymbolOptions Options { get; set; }
}
public class TextChange
{
public string NewText { get; set; }
public int StartLine { get; set; }
public int StartColumn { get; set; }
public int EndLine { get; set; }
public int EndColumn { get; set; }
}
public class ModifiedFileResponse
{
public string FileName { get; set; }
public List<TextChange> Changes { get; set; }
public ModifiedFileResponse(string fileName)
{
FileName = fileName;
Changes = new List<TextChange>();
}

public void AddTextChange(Ast Symbol, string NewText)
{
Changes.Add(
new TextChange
{
StartColumn = Symbol.Extent.StartColumnNumber - 1,
StartLine = Symbol.Extent.StartLineNumber - 1,
EndColumn = Symbol.Extent.EndColumnNumber - 1,
EndLine = Symbol.Extent.EndLineNumber - 1,
NewText = NewText
}
);
}
}
public class RenameSymbolResult
{
public RenameSymbolResult() => Changes = new List<ModifiedFileResponse>();
public List<ModifiedFileResponse> Changes { get; set; }
}

internal class RenameSymbolHandler : IRenameSymbolHandler
{
private readonly WorkspaceService _workspaceService;

public RenameSymbolHandler(WorkspaceService workspaceService) => _workspaceService = workspaceService;

internal static ModifiedFileResponse RenameFunction(Ast token, Ast scriptAst, RenameSymbolParams request)
{
string tokenName = "";
if (token is FunctionDefinitionAst funcDef)
{
tokenName = funcDef.Name;
}
else if (token.Parent is CommandAst CommAst)
{
tokenName = CommAst.GetCommandName();
}
IterativeFunctionRename visitor = new(tokenName,
request.RenameTo,
token.Extent.StartLineNumber,
token.Extent.StartColumnNumber,
scriptAst);
visitor.Visit(scriptAst);
ModifiedFileResponse FileModifications = new(request.FileName)
{
Changes = visitor.Modifications
};
return FileModifications;
}

internal static ModifiedFileResponse RenameVariable(Ast symbol, Ast scriptAst, RenameSymbolParams request)
{
if (symbol is VariableExpressionAst or ParameterAst or CommandParameterAst or StringConstantExpressionAst)
{

IterativeVariableRename visitor = new(
request.RenameTo,
symbol.Extent.StartLineNumber,
symbol.Extent.StartColumnNumber,
scriptAst,
request.Options ?? null
);
visitor.Visit(scriptAst);
ModifiedFileResponse FileModifications = new(request.FileName)
{
Changes = visitor.Modifications
};
return FileModifications;

}
return null;
}

public async Task<RenameSymbolResult> Handle(RenameSymbolParams request, CancellationToken cancellationToken)
{
if (!_workspaceService.TryGetFile(request.FileName, out ScriptFile scriptFile))
{
throw new InvalidOperationException("This should not happen as PrepareRename should have already checked for viability. File an issue if you see this.");
}

return await Task.Run(() =>
{
Ast token = Utilities.GetAst(request.Line + 1, request.Column + 1, scriptFile.ScriptAst);
if (token == null) { return null; }

ModifiedFileResponse FileModifications = (token is FunctionDefinitionAst || token.Parent is CommandAst)
? RenameFunction(token, scriptFile.ScriptAst, request)
: RenameVariable(token, scriptFile.ScriptAst, request);

RenameSymbolResult result = new();

result.Changes.Add(FileModifications);

return result;
}).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
namespace Microsoft.PowerShell.EditorServices.Refactoring
{

public class TargetSymbolNotFoundException : Exception
{
public TargetSymbolNotFoundException()
{
}

public TargetSymbolNotFoundException(string message)
: base(message)
{
}

public TargetSymbolNotFoundException(string message, Exception inner)
: base(message, inner)
{
}
}

public class FunctionDefinitionNotFoundException : Exception
{
public FunctionDefinitionNotFoundException()
{
}

public FunctionDefinitionNotFoundException(string message)
: base(message)
{
}

public FunctionDefinitionNotFoundException(string message, Exception inner)
: base(message, inner)
{
}
}
}
Loading