Skip to content

Commit b7ac528

Browse files
committed
Integrate FindSymbolsVisitor into ReferenceTable
We were missing the distinction between properties and enum members, as well as DSC configuration.
1 parent 54b6c01 commit b7ac528

File tree

3 files changed

+47
-201
lines changed

3 files changed

+47
-201
lines changed

src/PowerShellEditorServices/Services/Symbols/ReferenceTable.cs

+27-3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ public override AstVisitAction VisitCommand(CommandAst commandAst)
111111

112112
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
113113
{
114+
SymbolType symbolType = functionDefinitionAst.IsWorkflow
115+
? SymbolType.Workflow
116+
: SymbolType.Function;
117+
114118
// Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
115119
// This will not exclude nested functions as they have ScriptBlockAst as parent
116120
if (functionDefinitionAst.Parent is FunctionMemberAst)
@@ -119,9 +123,12 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
119123
}
120124

121125
// We only want the function name as the extent for highlighting (and so forth).
126+
//
127+
// TODO: After we replace the deprecated SymbolInformation usage with DocumentSymbol,
128+
// we'll want *both* the name extent and the full extent.
122129
IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst);
123130
_references.AddReference(
124-
SymbolType.Function,
131+
symbolType,
125132
functionDefinitionAst.Name,
126133
nameExtent,
127134
isDeclaration: true);
@@ -154,7 +161,9 @@ public override AstVisitAction VisitVariableExpression(VariableExpressionAst var
154161

155162
public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
156163
{
157-
SymbolType symbolType = typeDefinitionAst.IsEnum ? SymbolType.Enum : SymbolType.Class;
164+
SymbolType symbolType = typeDefinitionAst.IsEnum
165+
? SymbolType.Enum
166+
: SymbolType.Class;
158167

159168
IScriptExtent nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst);
160169
_references.AddReference(symbolType, typeDefinitionAst.Name, nameExtent);
@@ -196,13 +205,28 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem
196205

197206
public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
198207
{
208+
SymbolType symbolType =
209+
propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum
210+
? SymbolType.EnumMember : SymbolType.Property;
211+
199212
IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, false);
200213
_references.AddReference(
201-
SymbolType.Property,
214+
symbolType,
202215
VisitorUtils.GetMemberOverloadName(propertyMemberAst, false),
203216
nameExtent);
204217

205218
return AstVisitAction.Continue;
206219
}
220+
221+
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
222+
{
223+
IScriptExtent nameExtent = VisitorUtils.GetNameExtent(configurationDefinitionAst);
224+
_references.AddReference(
225+
SymbolType.Configuration,
226+
nameExtent.Text,
227+
nameExtent);
228+
229+
return AstVisitAction.Continue;
230+
}
207231
}
208232
}

src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs

-173
Original file line numberDiff line numberDiff line change
@@ -3,184 +3,11 @@
33

44
#nullable enable
55

6-
using Microsoft.PowerShell.EditorServices.Utility;
76
using System.Collections.Generic;
87
using System.Management.Automation.Language;
98

109
namespace Microsoft.PowerShell.EditorServices.Services.Symbols
1110
{
12-
/// <summary>
13-
/// The visitor used to find all the symbols (variables, functions and class defs etc) in the AST.
14-
/// </summary>
15-
internal class FindSymbolsVisitor : AstVisitor2
16-
{
17-
public List<SymbolReference> SymbolReferences { get; }
18-
19-
public FindSymbolsVisitor() => SymbolReferences = new List<SymbolReference>();
20-
21-
/// <summary>
22-
/// Adds each function definition to symbol reference list
23-
/// </summary>
24-
/// <param name="functionDefinitionAst">A FunctionDefinitionAst in the script's AST</param>
25-
/// <returns>A visit action that continues the search for references</returns>
26-
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
27-
{
28-
// Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
29-
// This will not exclude nested functions as they have ScriptBlockAst as parent
30-
if (functionDefinitionAst.Parent is FunctionMemberAst)
31-
{
32-
return AstVisitAction.Continue;
33-
}
34-
35-
(int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionDefinitionAst);
36-
IScriptExtent nameExtent = GetNewExtent(functionDefinitionAst, functionDefinitionAst.Name, startLine, startColumn);
37-
38-
SymbolType symbolType =
39-
functionDefinitionAst.IsWorkflow ?
40-
SymbolType.Workflow : SymbolType.Function;
41-
42-
SymbolReferences.Add(
43-
new SymbolReference(
44-
symbolType,
45-
nameExtent));
46-
47-
return AstVisitAction.Continue;
48-
}
49-
50-
/// <summary>
51-
/// Adds each script scoped variable assignment to symbol reference list
52-
/// </summary>
53-
/// <param name="variableExpressionAst">A VariableExpressionAst in the script's AST</param>
54-
/// <returns>A visit action that continues the search for references</returns>
55-
public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
56-
{
57-
if (!IsAssignedAtScriptScope(variableExpressionAst))
58-
{
59-
return AstVisitAction.Continue;
60-
}
61-
62-
SymbolReferences.Add(
63-
new SymbolReference(
64-
SymbolType.Variable,
65-
variableExpressionAst.Extent));
66-
67-
return AstVisitAction.Continue;
68-
}
69-
70-
private static bool IsAssignedAtScriptScope(VariableExpressionAst variableExpressionAst)
71-
{
72-
Ast parent = variableExpressionAst.Parent;
73-
if (parent is not AssignmentStatementAst)
74-
{
75-
return false;
76-
}
77-
78-
parent = parent.Parent;
79-
return parent is null || parent.Parent is null || parent.Parent.Parent is null;
80-
}
81-
82-
/// <summary>
83-
/// Adds class and enum AST to symbol reference list
84-
/// </summary>
85-
/// <param name="typeDefinitionAst">A TypeDefinitionAst in the script's AST</param>
86-
/// <returns>A visit action that continues the search for references</returns>
87-
public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
88-
{
89-
(int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(typeDefinitionAst);
90-
IScriptExtent nameExtent = GetNewExtent(typeDefinitionAst, typeDefinitionAst.Name, startLine, startColumn);
91-
92-
SymbolType symbolType =
93-
typeDefinitionAst.IsEnum ?
94-
SymbolType.Enum : SymbolType.Class;
95-
96-
SymbolReferences.Add(
97-
new SymbolReference(
98-
symbolType,
99-
nameExtent));
100-
101-
return AstVisitAction.Continue;
102-
}
103-
104-
/// <summary>
105-
/// Adds class method and constructor AST to symbol reference list
106-
/// </summary>
107-
/// <param name="functionMemberAst">A FunctionMemberAst in the script's AST</param>
108-
/// <returns>A visit action that continues the search for references</returns>
109-
public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
110-
{
111-
(int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionMemberAst);
112-
IScriptExtent nameExtent = GetNewExtent(functionMemberAst, VisitorUtils.GetMemberOverloadName(functionMemberAst, false, false), startLine, startColumn);
113-
114-
SymbolType symbolType =
115-
functionMemberAst.IsConstructor ?
116-
SymbolType.Constructor : SymbolType.Method;
117-
118-
SymbolReferences.Add(
119-
new SymbolReference(
120-
symbolType,
121-
nameExtent));
122-
123-
return AstVisitAction.Continue;
124-
}
125-
126-
/// <summary>
127-
/// Adds class property AST to symbol reference list
128-
/// </summary>
129-
/// <param name="propertyMemberAst">A PropertyMemberAst in the script's AST</param>
130-
/// <returns>A visit action that continues the search for references</returns>
131-
public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
132-
{
133-
SymbolType symbolType =
134-
propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ?
135-
SymbolType.EnumMember : SymbolType.Property;
136-
137-
bool isEnumMember = symbolType.Equals(SymbolType.EnumMember);
138-
(int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(propertyMemberAst, isEnumMember);
139-
IScriptExtent nameExtent = GetNewExtent(propertyMemberAst, propertyMemberAst.Name, startLine, startColumn);
140-
141-
SymbolReferences.Add(
142-
new SymbolReference(
143-
symbolType,
144-
nameExtent));
145-
146-
return AstVisitAction.Continue;
147-
}
148-
149-
/// <summary>
150-
/// Adds DSC configuration AST to symbol reference list
151-
/// </summary>
152-
/// <param name="configurationDefinitionAst">A ConfigurationDefinitionAst in the script's AST</param>
153-
/// <returns>A visit action that continues the search for references</returns>
154-
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
155-
{
156-
(int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(configurationDefinitionAst);
157-
IScriptExtent nameExtent = GetNewExtent(configurationDefinitionAst, configurationDefinitionAst.InstanceName.Extent.Text, startLine, startColumn);
158-
159-
SymbolReferences.Add(
160-
new SymbolReference(
161-
SymbolType.Configuration,
162-
nameExtent));
163-
164-
return AstVisitAction.Continue;
165-
}
166-
167-
/// <summary>
168-
/// Gets a new ScriptExtent for a given Ast with same range but modified Text
169-
/// </summary>
170-
private static ScriptExtent GetNewExtent(Ast ast, string text, int startLine, int startColumn)
171-
{
172-
return new ScriptExtent()
173-
{
174-
Text = text,
175-
StartLineNumber = startLine,
176-
EndLineNumber = ast.Extent.EndLineNumber,
177-
StartColumnNumber = startColumn,
178-
EndColumnNumber = ast.Extent.EndColumnNumber,
179-
File = ast.Extent.File
180-
};
181-
}
182-
}
183-
18411
/// <summary>
18512
/// Visitor to find all the keys in Hashtable AST
18613
/// </summary>

test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs

+20-25
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,7 @@ public async Task FindsDetailsWithSignatureForMethod()
559559
[Fact]
560560
public void FindsSymbolsInFile()
561561
{
562-
List<SymbolReference> symbolsResult =
563-
FindSymbolsInFile(
564-
FindSymbolsInMultiSymbolFile.SourceDetails);
562+
List<SymbolReference> symbolsResult = FindSymbolsInFile(FindSymbolsInMultiSymbolFile.SourceDetails);
565563

566564
Assert.Equal(4, symbolsResult.Count(symbolReference => symbolReference.SymbolType == SymbolType.Function));
567565
Assert.Equal(3, symbolsResult.Count(symbolReference => symbolReference.SymbolType == SymbolType.Variable));
@@ -622,50 +620,47 @@ public void FindsSymbolsInFile()
622620
[Fact]
623621
public void FindsSymbolsWithNewLineInFile()
624622
{
625-
List<SymbolReference> symbolsResult =
626-
FindSymbolsInFile(
627-
FindSymbolsInNewLineSymbolFile.SourceDetails);
623+
List<SymbolReference> symbols = FindSymbolsInFile(FindSymbolsInNewLineSymbolFile.SourceDetails);
628624

629-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Function));
630-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Class));
631-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Constructor));
632-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Property));
633-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Method));
634-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Enum));
635-
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.EnumMember));
636-
637-
SymbolReference firstFunctionSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Function);
625+
SymbolReference firstFunctionSymbol =
626+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Function));
638627
Assert.Equal("returnTrue", firstFunctionSymbol.SymbolName);
639628
Assert.Equal(2, firstFunctionSymbol.ScriptRegion.StartLineNumber);
640629
Assert.Equal(1, firstFunctionSymbol.ScriptRegion.StartColumnNumber);
641630

642-
SymbolReference firstClassSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Class);
631+
SymbolReference firstClassSymbol =
632+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Class));
643633
Assert.Equal("NewLineClass", firstClassSymbol.SymbolName);
644634
Assert.Equal(7, firstClassSymbol.ScriptRegion.StartLineNumber);
645635
Assert.Equal(1, firstClassSymbol.ScriptRegion.StartColumnNumber);
646636

647-
SymbolReference firstConstructorSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Constructor);
648-
Assert.Equal("NewLineClass()", firstConstructorSymbol.SymbolName);
637+
SymbolReference firstConstructorSymbol =
638+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Constructor));
639+
Assert.Equal("NewLineClass.NewLineClass()", firstConstructorSymbol.SymbolName);
649640
Assert.Equal(8, firstConstructorSymbol.ScriptRegion.StartLineNumber);
650641
Assert.Equal(5, firstConstructorSymbol.ScriptRegion.StartColumnNumber);
651642

652-
SymbolReference firstPropertySymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Property);
653-
Assert.Equal("SomePropWithDefault", firstPropertySymbol.SymbolName);
643+
SymbolReference firstPropertySymbol =
644+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Property));
645+
Assert.Equal("NewLineClass.SomePropWithDefault", firstPropertySymbol.SymbolName);
654646
Assert.Equal(15, firstPropertySymbol.ScriptRegion.StartLineNumber);
655647
Assert.Equal(5, firstPropertySymbol.ScriptRegion.StartColumnNumber);
656648

657-
SymbolReference firstMethodSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Method);
658-
Assert.Equal("MyClassMethod([MyNewLineEnum]$param1)", firstMethodSymbol.SymbolName);
649+
SymbolReference firstMethodSymbol =
650+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Method));
651+
Assert.Equal("NewLineClass.MyClassMethod([MyNewLineEnum]$param1)", firstMethodSymbol.SymbolName);
659652
Assert.Equal(20, firstMethodSymbol.ScriptRegion.StartLineNumber);
660653
Assert.Equal(5, firstMethodSymbol.ScriptRegion.StartColumnNumber);
661654

662-
SymbolReference firstEnumSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Enum);
655+
SymbolReference firstEnumSymbol =
656+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.Enum));
663657
Assert.Equal("MyNewLineEnum", firstEnumSymbol.SymbolName);
664658
Assert.Equal(26, firstEnumSymbol.ScriptRegion.StartLineNumber);
665659
Assert.Equal(1, firstEnumSymbol.ScriptRegion.StartColumnNumber);
666660

667-
SymbolReference firstEnumMemberSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.EnumMember);
668-
Assert.Equal("First", firstEnumMemberSymbol.SymbolName);
661+
SymbolReference firstEnumMemberSymbol =
662+
Assert.Single(symbols.Where(symbolReference => symbolReference.SymbolType == SymbolType.EnumMember));
663+
Assert.Equal("MyNewLineEnum.First", firstEnumMemberSymbol.SymbolName);
669664
Assert.Equal(27, firstEnumMemberSymbol.ScriptRegion.StartLineNumber);
670665
Assert.Equal(5, firstEnumMemberSymbol.ScriptRegion.StartColumnNumber);
671666
}

0 commit comments

Comments
 (0)