-
Notifications
You must be signed in to change notification settings - Fork 235
Go To Definition works with different Ast types #706
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
// | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Management.Automation.Language; | ||
|
||
namespace Microsoft.PowerShell.EditorServices | ||
|
@@ -76,19 +78,68 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun | |
/// or a decision to continue if it wasn't found</returns> | ||
public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) | ||
{ | ||
var variableExprAst = assignmentStatementAst.Left as VariableExpressionAst; | ||
if (variableExprAst == null || | ||
variableName == null || | ||
!variableExprAst.VariablePath.UserPath.Equals( | ||
if (variableName == null) | ||
{ | ||
return AstVisitAction.Continue; | ||
} | ||
|
||
// The AssignmentStatementAst could contain either of the following Ast types: | ||
// VariableExpressionAst, ArrayLiteralAst, ConvertExpressionAst, AttributedExpressionAst | ||
// We might need to recurse down the tree to find the VariableExpressionAst we're looking for | ||
List<VariableExpressionAst> asts = FindMatchingAsts(assignmentStatementAst.Left, variableName); | ||
if (asts.Count > 0) | ||
{ | ||
FoundDeclaration = new SymbolReference(SymbolType.Variable, asts.FirstOrDefault().Extent); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this only check the first variable when the lhs is an array literal? If so we should enumerate until we find a match or the list is completed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be worth looking at this method (in typical fashion, it's private, but still may be helpful): There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @SeeminglyScience that will grab the first in an array literal, I guess. But we probably want the LAST hit from an array literal.
FindMatchingAsts for we want @rjmholt looks like they went iterative. I like that, but it's a little extra code when we could just continue to recurse (since we're already doing it). Thanks for the reference though :) let me know if you feel strongly otherwise There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer the clarity of recursion actually. But thought it was worth noting that an implementation already exists There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tylerl0706 Initially I thought the method was just returning all That said, I would stick with getting the first assignment. Take this example $a, [string] $b, $b = 1, 2, 3 Here we know with certainty what the value of the RHS is. So we can assume that the value of It'll also save a little bit of processing time in some (very) fringe cases (assuming the switch to the |
||
return AstVisitAction.StopVisit; | ||
} | ||
return AstVisitAction.Continue; | ||
} | ||
|
||
/// <summary> | ||
/// Takes in an ExpressionAst and recurses until it finds all VariableExpressionAst and returns the list of them. | ||
/// We expect this ExpressionAst to be either: VariableExpressionAst, ArrayLiteralAst, ConvertExpressionAst, AttributedExpressionAst | ||
/// </summary> | ||
/// <param name="ast">An ExpressionAst</param> | ||
/// <param name="variableName">The name of the variable we are trying to find</param> | ||
/// <returns>A list of ExpressionAsts that match the variable name provided</returns> | ||
private static List<VariableExpressionAst> FindMatchingAsts (ExpressionAst ast, string variableName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May want to remove the space between the method name and the opening paren here |
||
|
||
// VaraibleExpressionAst case - aka base case. This will return a list with the variableExpressionAst in it or an empty list if it's not the right one. | ||
var variableExprAst = ast as VariableExpressionAst; | ||
if (variableExprAst != null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the variable will only be used within the Or possibly an assignment switch switch (ast)
{
case ConvertExpressionAst convertExpression:
{
FindMatchingAsts(convertExpressionAst);
break;
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I reckon a type switch is probably clearest here -- it's probably how the PowerShell compiler itself would work if it were written today. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw a bunch of articles that said this wasn't possible hmm - I'll give it a go There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MS Docs seem to call it pattern matching. (Which is guess is approximately true...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohhhh, but this may not be possible in all the .NET versions we support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Historical note: We think this should work) |
||
{ | ||
if (variableExprAst.VariablePath.UserPath.Equals( | ||
variableName, | ||
StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return new List<VariableExpressionAst>{ variableExprAst }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neat! I didn't know you could omit the parenthesis 😃 |
||
} | ||
return new List<VariableExpressionAst>(); | ||
} | ||
|
||
// VariableExpressionAsts could be an element of an ArrayLiteralAst. This adds all the elements to the call stack. | ||
var arrayLiteralAst = ast as ArrayLiteralAst; | ||
if (arrayLiteralAst != null) | ||
{ | ||
return AstVisitAction.Continue; | ||
return (arrayLiteralAst.Elements.SelectMany(e => FindMatchingAsts(e, variableName)).ToList()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't super important but this method might be a lot more efficient in assignments with a lot of decoration if it used yield. If you change the return type to foreach (var foundVariableAst in FindMatchingAsts(convertExprAst.Child))
{
yield return foundVariableAst;
}
yield break; Then you could remove the variable name parameter and make this a more general utility function. You could then do the actual test in the caller while still iterating only as much as you need to. |
||
} | ||
|
||
// TODO also find instances of set-variable | ||
FoundDeclaration = new SymbolReference(SymbolType.Variable, variableExprAst.Extent); | ||
return AstVisitAction.StopVisit; | ||
// The ConvertExpressionAst (static casting for example `[string]$foo = "asdf"`) could contain a VariableExpressionAst so we recurse down | ||
var convertExprAst = ast as ConvertExpressionAst; | ||
if (convertExprAst != null && convertExprAst.Child != null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally we're moving to the switch, but the null check here could be compressed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can actually skip the null on Incidentally, I didn't realize There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh but apparently this works ($a, $b) = 1, 2 So replace it with |
||
{ | ||
return FindMatchingAsts(convertExprAst.Child, variableName); | ||
} | ||
|
||
// The AttributedExpressionAst (any attribute for example `[NotNull()]$foo = "asdf"`) could contain a VariableExpressionAst so we recurse down | ||
var attributedExprAst = ast as AttributedExpressionAst; | ||
if (attributedExprAst != null && attributedExprAst.Child != null) | ||
{ | ||
return FindMatchingAsts(attributedExprAst.Child, variableName); | ||
}; | ||
|
||
// We shouldn't ever get here, but in case a new Ast type is added to PowerShell, this fails gracefully | ||
return new List<VariableExpressionAst>(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gah comment is wrong