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

Implemtation of VSCode RenameProvider for variable/function renaming #2152

Open
wants to merge 117 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
16f6a34
adding in rename symbol service
Razmo99 Mar 26, 2023
790669a
switched to using a task not sure if there is a better way
Razmo99 Mar 26, 2023
99995b6
completed rename function
Razmo99 Mar 26, 2023
7006c6e
slight refactoring
Razmo99 Mar 26, 2023
f63661d
Adding in unit teste for refactoring functions
Razmo99 Mar 29, 2023
66c8af2
test case for a function that is flat or inline
Razmo99 Mar 29, 2023
1e8de30
added new test case
Razmo99 Sep 13, 2023
377a6b0
initial commit
Razmo99 Sep 18, 2023
002d13c
converted visitor class from powershell to C#
Razmo99 Sep 18, 2023
1178cd8
Updated to using ps1 file with a renamed varient
Razmo99 Sep 19, 2023
ec50b53
Bug Fixes now passing all tests
Razmo99 Sep 19, 2023
03c77ae
Stripped un-needed functions now using FunctionRename class
Razmo99 Sep 19, 2023
f688404
Merge branch 'PowerShell:main' into rename-function-customvisitor
Razmo99 Sep 22, 2023
4769c02
small clean up of init of class
Razmo99 Sep 25, 2023
6f5b8ca
unneeded init of class var
Razmo99 Sep 25, 2023
79bc41b
init commit of variable visitor class
Razmo99 Sep 25, 2023
20b0d94
piping for vscode rename symbol
Razmo99 Sep 25, 2023
804076b
initial tests for variable visitor class
Razmo99 Sep 25, 2023
c3cddc7
fixing typo
Razmo99 Sep 25, 2023
5a2f50a
adjusting scopestack to store Ast objects
Razmo99 Sep 25, 2023
8ec8d10
added additional tests for variablerename visitor
Razmo99 Sep 26, 2023
102affe
adjusting so it finds the first variable definition within scope
Razmo99 Sep 26, 2023
057edd1
added function to get variable top assignment
Razmo99 Sep 26, 2023
6265381
renamed scriptfile to scriptast
Razmo99 Sep 26, 2023
d1f5eaf
can visit binary expressions now
Razmo99 Sep 26, 2023
c8bae09
can visit dountil and dowhile
Razmo99 Sep 26, 2023
2376082
logic to stop start shouldrename
Razmo99 Sep 26, 2023
2ada3b5
can visit scriptexpressions now
Razmo99 Sep 26, 2023
64971c2
start stop logic for if a redefinition is found
Razmo99 Sep 26, 2023
a1133a2
formatting
Razmo99 Sep 26, 2023
e9642f3
implemented visithastable
Razmo99 Sep 27, 2023
b1a807f
function to determine if a node is within a targets scope
Razmo99 Sep 27, 2023
07c13c7
adjusted get variable top assignment for better detection
Razmo99 Sep 27, 2023
0010b40
additional test cases
Razmo99 Sep 27, 2023
8c8576b
additional tests
Razmo99 Sep 27, 2023
50bf971
added break for finding the top variable assignment
Razmo99 Sep 27, 2023
fa53a7b
implemented visit command parameter
Razmo99 Sep 27, 2023
97e5420
implemented visit member expression
Razmo99 Sep 27, 2023
0b3c124
implemented visitparentexpression
Razmo99 Sep 27, 2023
e84ce61
altered logic for variable renamed to check operator is equals
Razmo99 Sep 27, 2023
9d1bbf8
removing examples file
Razmo99 Sep 27, 2023
acb9794
formatting and additional test
Razmo99 Sep 28, 2023
49b3bb6
formatting and proper sorting for gettestscript
Razmo99 Sep 28, 2023
599f981
additional test data
Razmo99 Sep 28, 2023
3bc2352
reworked class so that oldname is no longer needed
Razmo99 Sep 28, 2023
8b0d85b
implemented some new visitors
Razmo99 Sep 28, 2023
50dc815
early start on commandparameter renaming
Razmo99 Sep 28, 2023
772b6b7
more implementations and some formatting
Razmo99 Sep 28, 2023
b42ef12
logic to determin if we are renaming a var or parameter
Razmo99 Sep 28, 2023
09a439a
additional test
Razmo99 Sep 28, 2023
97f2768
additional tests for parameters
Razmo99 Sep 30, 2023
c057cc5
adjusting checks for parameters
Razmo99 Sep 30, 2023
448f808
case insensitive compare & adjustment for get ast parent scope to fav…
Razmo99 Sep 30, 2023
e355326
initial implentation of prepare rename provider
Razmo99 Oct 13, 2023
28dc7d6
new test to handle detection for if the target function is a param ast
Razmo99 Oct 13, 2023
5e9a364
new exception for when dot sourcing is detected
Razmo99 Oct 13, 2023
98f0e1d
added more detection and errors for prepare rename symbol
Razmo99 Oct 13, 2023
a08fd1d
new exception for when the function definition cannot be found
Razmo99 Oct 13, 2023
5bc90d7
no longer using trygetsymbolatposition as it doesnt detect parameterA…
Razmo99 Oct 13, 2023
9cb1252
further adjustments to detection
Razmo99 Oct 13, 2023
1726405
switched to processing using iteration to avoid stack overflow
Razmo99 Oct 14, 2023
58b2a02
Fixing typo
Razmo99 Oct 14, 2023
064ff88
Switching tests to use iterative class
Razmo99 Oct 14, 2023
a8e05d6
init version of the variable rename iterative
Razmo99 Oct 14, 2023
ab6d48c
switched tests and vscode to use iterative class
Razmo99 Oct 14, 2023
30c9dbc
new test to check for method with the same parameter name
Razmo99 Oct 14, 2023
82c8120
fixing up tests for VariableParameterCommandWithSameName
Razmo99 Oct 14, 2023
cb987bd
fixing up tests
Razmo99 Oct 14, 2023
2e093ad
adjusting tests for more complexity
Razmo99 Oct 14, 2023
648be18
now adds Alias on commandParameterRenaming
Razmo99 Oct 14, 2023
45d5993
refactored alias creation for readability
Razmo99 Oct 15, 2023
b20a5cf
updated prepeare rename symbol to use iterative and added msg for if …
Razmo99 Oct 15, 2023
6e563b6
renamed renamevariableiterative to IterativeVariableRename
Razmo99 Oct 15, 2023
5818e4c
using switch instead of else if
Razmo99 Oct 15, 2023
2825d4c
formatting for rename symbol
Razmo99 Oct 15, 2023
b7d141c
moved Function shared test content into its own folder
Razmo99 Oct 15, 2023
66b56b9
New Test for splatted variable parameter renaming
Razmo99 Oct 15, 2023
b2beb22
first stage of supporting symbol renaming for splatted command Ast calls
Razmo99 Oct 15, 2023
33e4cda
split out exceptions into generic file
Razmo99 Oct 15, 2023
0ca7824
updated to use its own internal version
Razmo99 Oct 15, 2023
9ecc372
added functionality to reverse lookup the top variable from a splat
Razmo99 Oct 15, 2023
d08775d
Detter symbol detection was timing out on larger files
Razmo99 Oct 15, 2023
15ecd2b
added utilities for common renaming functions updated tests
Razmo99 Oct 15, 2023
1dc0c45
adjusted rename to use utilities
Razmo99 Oct 15, 2023
1a6e2a2
added comments to NewSplattedModification
Razmo99 Oct 24, 2023
b4a5647
adjusted test to use -username param instead of -password due to Alia…
Razmo99 Oct 24, 2023
3c3d96c
extracted method of LookForParentOfType from GetFuncDecFromCommAst
Razmo99 Oct 24, 2023
e620586
adjusted LookForParent so it accepts multiple types to look for
Razmo99 Oct 24, 2023
563e98d
adjusting iterative functions to use LookForParentOfType method
Razmo99 Oct 24, 2023
9eeba30
Moved GetAstNodeByLineAndColumn to a generic method
Razmo99 Oct 24, 2023
c8a96bc
updated visitors to use generic GetAstNodeByLineAndColumn
Razmo99 Oct 24, 2023
f39fd47
formatting moved GetFunctionDefByCommandAst to Utilities
Razmo99 Oct 24, 2023
1b7246f
Refactoring to use Utilities class for generic methods
Razmo99 Oct 24, 2023
bc1ba49
Renaming methods to be clearer
Razmo99 Oct 24, 2023
05dac0b
formatting an new test for finding a function
Razmo99 Oct 27, 2023
ca3d230
reworked GetAst for better detection
Razmo99 Oct 27, 2023
271b67f
Added a test to get a function definition ast
Razmo99 Mar 21, 2024
c5bebe3
additional checks in getast for namedblock detection
Razmo99 Mar 21, 2024
5d32932
Merge branch 'rename-function-customvisitor' of https://github.com/Ra…
Razmo99 Mar 21, 2024
6c1605a
additional changes to getast utility method
Razmo99 Mar 21, 2024
e7c1e87
reverting changes from bad merge request pull
Razmo99 Mar 21, 2024
76f1e7f
removing unused properties of the class
Razmo99 Mar 22, 2024
8fa9b51
migrated FunctionDefinitionNotFoundException to exceptions.cs
Razmo99 Mar 22, 2024
8d72e12
removed original recursive visitor classes
Razmo99 Mar 22, 2024
c416024
removed unsued class properties from function visitor
Razmo99 Mar 22, 2024
9a74a1b
condensing if statements
Razmo99 Mar 22, 2024
92ae8f4
Broke up Process node into 3 sub functions for readability
Razmo99 Mar 22, 2024
86e6287
fixing comment grammar
Razmo99 Mar 22, 2024
545bd0c
New Method and tests to check if a script ast contains dot sourcing
Razmo99 Mar 24, 2024
6f5e416
finalised dot source detection and notification
Razmo99 Mar 24, 2024
2c9a8e0
Merge branch 'PowerShell:main' into rename-function-customvisitor
Razmo99 Mar 24, 2024
a5296ab
fixing spelling / naming mistakes
Razmo99 Mar 24, 2024
914ffb3
Merge branch 'rename-function-customvisitor' of https://github.com/Ra…
Razmo99 Mar 24, 2024
e269d1e
removing .vscode files
Razmo99 Mar 25, 2024
119d0c6
deleting package-lock.json
Razmo99 Mar 25, 2024
2647b91
cleaning up comments and removed unused code
Razmo99 Mar 25, 2024
effbbbc
Merge branch 'main' into rename-function-customvisitor
JustinGrote May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
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,109 @@
// 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.Extensions.Logging;
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 ILogger _logger;
private readonly WorkspaceService _workspaceService;

public PrepareRenameSymbolHandler(ILoggerFactory loggerFactory, WorkspaceService workspaceService)
{
_logger = loggerFactory.CreateLogger<RenameSymbolHandler>();
_workspaceService = workspaceService;
}

public async Task<PrepareRenameSymbolResult> Handle(PrepareRenameSymbolParams request, CancellationToken cancellationToken)
{
if (!_workspaceService.TryGetFile(request.FileName, out ScriptFile scriptFile))
{
_logger.LogDebug("Failed to open file!");
return await Task.FromResult<PrepareRenameSymbolResult>(null).ConfigureAwait(false);
}
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;
}
switch (token)
{
case FunctionDefinitionAst funcDef:
{
try
{

IterativeFunctionRename visitor = new(funcDef.Name,
request.RenameTo,
funcDef.Extent.StartLineNumber,
funcDef.Extent.StartColumnNumber,
scriptFile.ScriptAst);
}
catch (FunctionDefinitionNotFoundException)
{

result.message = "Failed to Find function definition within current file";
}

break;
}

case VariableExpressionAst or CommandAst or CommandParameterAst or ParameterAst or StringConstantExpressionAst:
{
IterativeVariableRename visitor = new(request.RenameTo,
token.Extent.StartLineNumber,
token.Extent.StartColumnNumber,
scriptFile.ScriptAst);
if (visitor.TargetVariableAst == null)
{
result.message = "Failed to find variable definition within the current file";
}
break;
}
}
return result;
}).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// 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.Extensions.Logging;
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
using Microsoft.PowerShell.EditorServices.Refactoring;
namespace Microsoft.PowerShell.EditorServices.Handlers
{
[Serial, Method("powerShell/renameSymbol")]
internal interface IRenameSymbolHandler : IJsonRpcRequestHandler<RenameSymbolParams, RenameSymbolResult> { }

internal class RenameSymbolParams : IRequest<RenameSymbolResult>
{
public string FileName { get; set; }
public int Line { get; set; }
public int Column { get; set; }
public string RenameTo { get; set; }
}
internal 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; }
}
internal 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
}
);
}
}
internal class RenameSymbolResult
{
public RenameSymbolResult() => Changes = new List<ModifiedFileResponse>();
public List<ModifiedFileResponse> Changes { get; set; }
}

internal class RenameSymbolHandler : IRenameSymbolHandler
{
private readonly ILogger _logger;
private readonly WorkspaceService _workspaceService;

public RenameSymbolHandler(ILoggerFactory loggerFactory, WorkspaceService workspaceService)
{
_logger = loggerFactory.CreateLogger<RenameSymbolHandler>();
_workspaceService = workspaceService;
}
internal static ModifiedFileResponse RenameFunction(Ast token, Ast scriptAst, RenameSymbolParams request)
{
if (token is FunctionDefinitionAst funcDef)
{
IterativeFunctionRename visitor = new(funcDef.Name,
request.RenameTo,
funcDef.Extent.StartLineNumber,
funcDef.Extent.StartColumnNumber,
scriptAst);
visitor.Visit(scriptAst);
ModifiedFileResponse FileModifications = new(request.FileName)
{
Changes = visitor.Modifications
};
return FileModifications;

}
return null;

}
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);
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))
{
_logger.LogDebug("Failed to open file!");
return await Task.FromResult<RenameSymbolResult>(null).ConfigureAwait(false);
}

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
? 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)
{
}
}
}