Skip to content

Fix finding variable definitions in workspace #458

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

Merged
merged 4 commits into from
May 19, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
42 changes: 24 additions & 18 deletions src/PowerShellEditorServices/Language/FindDeclartionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,31 @@ namespace Microsoft.PowerShell.EditorServices
internal class FindDeclartionVisitor : AstVisitor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind fixing the typo in the name of this class? FindDeclarationVisitor. That's bugged me for a while :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed!

{
private SymbolReference symbolRef;
private string variableName;

public SymbolReference FoundDeclartion{ get; private set; }

public FindDeclartionVisitor(SymbolReference symbolRef)
{
this.symbolRef = symbolRef;
if (this.symbolRef.SymbolType == SymbolType.Variable)
{
// converts `$varName` to `varName` or of the form ${varName} to varName
variableName = symbolRef.SymbolName.TrimStart('$').Trim('{', '}');
}
}

/// <summary>
/// Decides if the current function defintion is the right defition
/// for the symbol being searched for. The defintion of the symbol will be a of type
/// for the symbol being searched for. The defintion of the symbol will be a of type
/// SymbolType.Function and have the same name as the symbol
/// </summary>
/// <param name="functionDefinitionAst">A FunctionDefinitionAst in the script's AST</param>
/// <returns>A descion to stop searching if the right FunctionDefinitionAst was found,
/// <returns>A descion to stop searching if the right FunctionDefinitionAst was found,
/// or a decision to continue if it wasn't found</returns>
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
// Get the start column number of the function name,
// Get the start column number of the function name,
// instead of the the start column of 'function' and create new extent for the functionName
int startColumnNumber =
functionDefinitionAst.Extent.Text.IndexOf(
Expand Down Expand Up @@ -62,27 +68,27 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
}

/// <summary>
/// Decides if the current variable expression is the right defition for
/// the symbol being searched for. The defintion of the symbol will be a of type
/// SymbolType.Variable and have the same name as the symbol
/// Check if the left hand side of an assignmentStatementAst is a VariableExpressionAst
/// with the same name as that of symbolRef.
/// </summary>
/// <param name="variableExpressionAst">A FunctionDefinitionAst in the script's AST</param>
/// <returns>A descion to stop searching if the right VariableExpressionAst was found,
/// <param name="assignmentStatementAst">An AssignmentStatementAst/param>
/// <returns>A descion to stop searching if the right VariableExpressionAst was found,
/// or a decision to continue if it wasn't found</returns>
public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)
{
if(symbolRef.SymbolType.Equals(SymbolType.Variable) &&
variableExpressionAst.Extent.Text.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
var variableExprAst = assignmentStatementAst.Left as VariableExpressionAst;
if (variableExprAst == null ||
variableName == null ||
!variableExprAst.VariablePath.UserPath.Equals(
variableName,
StringComparison.OrdinalIgnoreCase))
{
this.FoundDeclartion =
new SymbolReference(
SymbolType.Variable,
variableExpressionAst.Extent);

return AstVisitAction.StopVisit;
return AstVisitAction.Continue;
}

return AstVisitAction.Continue;
// TODO also find instances of set-variable
FoundDeclartion = new SymbolReference(SymbolType.Variable, variableExprAst.Extent);
return AstVisitAction.StopVisit;
}
}
}
1 change: 1 addition & 0 deletions src/PowerShellEditorServices/Language/SymbolReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System;
using System.Diagnostics;
using System.Management.Automation.Language;

Expand Down
60 changes: 60 additions & 0 deletions test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,66 @@ await this.SendRequest(
Assert.Equal(7, locations[0].Range.End.Character);
}

[Fact]
public async Task FindsDefinitionOfVariableInOtherFile()
{
await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1");

Location[] locations =
await this.SendRequest(
DefinitionRequest.Type,
new TextDocumentPositionParams
{
TextDocument = new TextDocumentIdentifier
{
Uri = "TestFiles\\FindReferences.ps1"
},
Position = new Position
{
Line = 15,
Character = 20,
}
});

Assert.NotNull(locations);
Assert.Equal(1, locations.Length);
Assert.EndsWith("VariableDefinition.ps1", locations[0].Uri);
Assert.Equal(0, locations[0].Range.Start.Line);
Assert.Equal(0, locations[0].Range.Start.Character);
Assert.Equal(0, locations[0].Range.End.Line);
Assert.Equal(20, locations[0].Range.End.Character);
}

[Fact]
public async Task FindDefinitionOfVariableWithSpecialChars()
{
await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1");

Location[] locations =
await this.SendRequest(
DefinitionRequest.Type,
new TextDocumentPositionParams
{
TextDocument = new TextDocumentIdentifier
{
Uri = "TestFiles\\FindReferences.ps1"
},
Position = new Position
{
Line = 18,
Character = 24,
}
});

Assert.NotNull(locations);
Assert.Equal(1, locations.Length);
Assert.EndsWith("FindReferences.ps1", locations[0].Uri);
Assert.Equal(17, locations[0].Range.Start.Line);
Assert.Equal(0, locations[0].Range.Start.Character);
Assert.Equal(17, locations[0].Range.End.Line);
Assert.Equal(27, locations[0].Range.End.Character);
}

[Fact]
public async Task FindsOccurencesOnFunctionDefinition()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ My-Function $things

Write-Output "Hi";

Write-Output ""
Write-Output ""

. .\VariableDefinition.ps1
Write-Output $variableInOtherFile

${variable-with-weird-name} = "this variable has special characters"
Write-Output ${variable-with-weird-name}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$variableInOtherFile = "some value"