diff --git a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs
index eb50ce2d5..5f9b6d958 100644
--- a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs
+++ b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs
@@ -57,14 +57,17 @@ public ReferencesCodeLensProvider(WorkspaceService workspaceService, SymbolsServ
///
/// The PowerShell script file to get code lenses for.
///
- /// An array of CodeLenses describing all functions in the given script file.
+ /// An array of CodeLenses describing all functions, classes and enums in the given script file.
public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
{
List acc = new();
foreach (SymbolReference sym in _symbolProvider.ProvideDocumentSymbols(scriptFile))
{
cancellationToken.ThrowIfCancellationRequested();
- if (sym.SymbolType == SymbolType.Function)
+ if (sym.SymbolType is
+ SymbolType.Function or
+ SymbolType.Class or
+ SymbolType.Enum)
{
acc.Add(new CodeLens
{
@@ -96,7 +99,7 @@ public async Task ResolveCodeLens(
ScriptFile[] references = _workspaceService.ExpandScriptReferences(
scriptFile);
- SymbolReference foundSymbol = SymbolsService.FindFunctionDefinitionAtLocation(
+ SymbolReference foundSymbol = SymbolsService.FindSymbolDefinitionAtLocation(
scriptFile,
codeLens.Range.Start.Line + 1,
codeLens.Range.Start.Character + 1);
diff --git a/src/PowerShellEditorServices/Services/Symbols/ScriptDocumentSymbolProvider.cs b/src/PowerShellEditorServices/Services/Symbols/ScriptDocumentSymbolProvider.cs
index 92b33c8de..0b40a5cc1 100644
--- a/src/PowerShellEditorServices/Services/Symbols/ScriptDocumentSymbolProvider.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/ScriptDocumentSymbolProvider.cs
@@ -33,17 +33,6 @@ IEnumerable IDocumentSymbolProvider.ProvideDocumentSymbols(
/// A collection of SymbolReference objects
public static IEnumerable FindSymbolsInDocument(Ast scriptAst)
{
- // TODO: Restore this when we figure out how to support multiple
- // PS versions in the new PSES-as-a-module world (issue #276)
- // if (powerShellVersion >= new Version(5,0))
- // {
- //#if PowerShell v5
- // FindSymbolsVisitor2 findSymbolsVisitor = new FindSymbolsVisitor2();
- // scriptAst.Visit(findSymbolsVisitor);
- // symbolReferences = findSymbolsVisitor.SymbolReferences;
- //#endif
- // }
- // else
FindSymbolsVisitor findSymbolsVisitor = new();
scriptAst.Visit(findSymbolsVisitor);
return findSymbolsVisitor.SymbolReferences;
diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs
index 80a1dd8b4..84d0cef29 100644
--- a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs
@@ -82,6 +82,29 @@ await CommandHelpers.GetCommandSynopsisAsync(
symbolDetails.DisplayString = symbolReference.SymbolName;
return symbolDetails;
+ case SymbolType.Class:
+ symbolDetails.DisplayString = "class " + symbolReference.SymbolName;
+ return symbolDetails;
+
+ case SymbolType.Enum:
+ symbolDetails.DisplayString = "enum " + symbolReference.SymbolName;
+ return symbolDetails;
+
+ case SymbolType.Type:
+ symbolDetails.DisplayString = "type " + symbolReference.SymbolName;
+ return symbolDetails;
+
+ case SymbolType.Constructor:
+ case SymbolType.Method:
+ case SymbolType.EnumMember:
+ case SymbolType.Property:
+ symbolDetails.DisplayString = symbolReference.SymbolName;
+ return symbolDetails;
+
+ case SymbolType.Configuration:
+ symbolDetails.DisplayString = "configuration " + symbolReference.SymbolName;
+ return symbolDetails;
+
default:
return symbolDetails;
}
diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs
index 02778b106..7f5850f45 100644
--- a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs
@@ -41,6 +41,41 @@ internal enum SymbolType
///
/// The symbol is a hashtable key
///
- HashtableKey
+ HashtableKey,
+
+ ///
+ /// The symbol is a class
+ ///
+ Class,
+
+ ///
+ /// The symbol is a enum
+ ///
+ Enum,
+
+ ///
+ /// The symbol is a enum member/value
+ ///
+ EnumMember,
+
+ ///
+ /// The symbol is a class property
+ ///
+ Property,
+
+ ///
+ /// The symbol is a class method
+ ///
+ Method,
+
+ ///
+ /// The symbol is a class constructor
+ ///
+ Constructor,
+
+ ///
+ /// The symbol is a type reference
+ ///
+ Type,
}
}
diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs
index f052520f0..294cbb986 100644
--- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs
@@ -288,7 +288,7 @@ public static IReadOnlyList FindOccurrencesInFile(
}
///
- /// Finds a function definition in the script given a file location
+ /// Finds a function, class or enum definition in the script given a file location
///
/// The details and contents of a open script file
/// The line number of the cursor for the given script
@@ -296,7 +296,7 @@ public static IReadOnlyList FindOccurrencesInFile(
/// A SymbolReference of the symbol found at the given location
/// or null if there is no symbol at that location
///
- public static SymbolReference FindFunctionDefinitionAtLocation(
+ public static SymbolReference FindSymbolDefinitionAtLocation(
ScriptFile scriptFile,
int lineNumber,
int columnNumber)
@@ -306,7 +306,7 @@ public static SymbolReference FindFunctionDefinitionAtLocation(
scriptFile.ScriptAst,
lineNumber,
columnNumber,
- includeFunctionDefinitions: true);
+ includeDefinitions: true);
if (symbolReference != null)
{
@@ -332,7 +332,8 @@ public Task FindSymbolDetailsAtLocationAsync(
AstOperations.FindSymbolAtPosition(
scriptFile.ScriptAst,
lineNumber,
- columnNumber);
+ columnNumber,
+ returnFullSignature: true);
if (symbolReference == null)
{
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs
index e14b2051a..e843128d2 100644
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs
@@ -147,19 +147,22 @@ await executionService.ExecuteDelegateAsync(
/// The abstract syntax tree of the given script
/// The line number of the cursor for the given script
/// The column number of the cursor for the given script
- /// Includes full function definition ranges in the search.
+ /// Includes full symbol definition ranges in the search.
+ /// Includes return or property type in symbol name.
/// SymbolReference of found symbol
public static SymbolReference FindSymbolAtPosition(
Ast scriptAst,
int lineNumber,
int columnNumber,
- bool includeFunctionDefinitions = false)
+ bool includeDefinitions = false,
+ bool returnFullSignature = false)
{
FindSymbolVisitor symbolVisitor =
new(
lineNumber,
columnNumber,
- includeFunctionDefinitions);
+ includeDefinitions,
+ returnFullSignature);
scriptAst.Visit(symbolVisitor);
@@ -228,18 +231,6 @@ public static SymbolReference FindDefinitionOfSymbol(
/// A collection of SymbolReference objects
public static IEnumerable FindSymbolsInDocument(Ast scriptAst)
{
- // TODO: Restore this when we figure out how to support multiple
- // PS versions in the new PSES-as-a-module world (issue #276)
- // if (powerShellVersion >= new Version(5,0))
- // {
- //#if PowerShell v5
- // FindSymbolsVisitor2 findSymbolsVisitor = new FindSymbolsVisitor2();
- // scriptAst.Visit(findSymbolsVisitor);
- // symbolReferences = findSymbolsVisitor.SymbolReferences;
- //#endif
- // }
- // else
-
FindSymbolsVisitor findSymbolsVisitor = new();
scriptAst.Visit(findSymbolsVisitor);
return findSymbolsVisitor.SymbolReferences;
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs
index 5c3071451..8c8e1842a 100644
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs
@@ -3,13 +3,14 @@
using System;
using System.Management.Automation.Language;
+using Microsoft.PowerShell.EditorServices.Utility;
namespace Microsoft.PowerShell.EditorServices.Services.Symbols
{
///
/// The visitor used to find the definition of a symbol
///
- internal class FindDeclarationVisitor : AstVisitor
+ internal class FindDeclarationVisitor : AstVisitor2
{
private readonly SymbolReference symbolRef;
private readonly string variableName;
@@ -36,27 +37,22 @@ public FindDeclarationVisitor(SymbolReference symbolRef)
/// or a decision to continue if it wasn't found
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
- // 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(
- functionDefinitionAst.Name, StringComparison.OrdinalIgnoreCase) + 1;
-
- IScriptExtent nameExtent = new ScriptExtent()
+ // Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
+ // This will not exclude nested functions as they have ScriptBlockAst as parent
+ if (functionDefinitionAst.Parent is FunctionMemberAst)
{
- Text = functionDefinitionAst.Name,
- StartLineNumber = functionDefinitionAst.Extent.StartLineNumber,
- StartColumnNumber = startColumnNumber,
- EndLineNumber = functionDefinitionAst.Extent.StartLineNumber,
- EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length,
- File = functionDefinitionAst.Extent.File
- };
+ return AstVisitAction.Continue;
+ }
// We compare to the SymbolName instead of its text because it may have been resolved
// from an alias.
if (symbolRef.SymbolType.Equals(SymbolType.Function) &&
- nameExtent.Text.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ functionDefinitionAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
{
+ // Get the start column number of the function name,
+ // instead of the the start column of 'function' and create new extent for the functionName
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst);
+
FoundDeclaration =
new SymbolReference(
SymbolType.Function,
@@ -68,6 +64,99 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
return base.VisitFunctionDefinition(functionDefinitionAst);
}
+ ///
+ /// Decides if the current type definition is the right definition
+ /// for the symbol being searched for. The definition of the symbol will be a of type
+ /// SymbolType.Enum or SymbolType.Class and have the same name as the symbol
+ ///
+ /// A TypeDefinitionAst in the script's AST
+ /// A decision to stop searching if the right TypeDefinitionAst was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
+ {
+ SymbolType symbolType =
+ typeDefinitionAst.IsEnum ?
+ SymbolType.Enum : SymbolType.Class;
+
+ if ((symbolRef.SymbolType is SymbolType.Type || symbolRef.SymbolType.Equals(symbolType)) &&
+ typeDefinitionAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the type name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst);
+
+ FoundDeclaration =
+ new SymbolReference(
+ symbolType,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current function member is the right definition
+ /// for the symbol being searched for. The definition of the symbol will be a of type
+ /// SymbolType.Constructor or SymbolType.Method and have the same name as the symbol
+ ///
+ /// A FunctionMemberAst in the script's AST
+ /// A decision to stop searching if the right FunctionMemberAst was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
+ {
+ SymbolType symbolType =
+ functionMemberAst.IsConstructor ?
+ SymbolType.Constructor : SymbolType.Method;
+
+ if (symbolRef.SymbolType.Equals(symbolType) &&
+ VisitorUtils.GetMemberOverloadName(functionMemberAst, true, false).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the method/ctor name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, false);
+
+ FoundDeclaration =
+ new SymbolReference(
+ symbolType,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current property member is the right definition
+ /// for the symbol being searched for. The definition of the symbol will be a of type
+ /// SymbolType.Property or SymbolType.EnumMember and have the same name as the symbol
+ ///
+ /// A PropertyMemberAst in the script's AST
+ /// A decision to stop searching if the right PropertyMemberAst was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
+ {
+ SymbolType symbolType =
+ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ?
+ SymbolType.EnumMember : SymbolType.Property;
+
+ if (symbolRef.SymbolType.Equals(symbolType) &&
+ VisitorUtils.GetMemberOverloadName(propertyMemberAst, false).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the property name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, false);
+
+ FoundDeclaration =
+ new SymbolReference(
+ SymbolType.Property,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
///
/// Check if the left hand side of an assignmentStatementAst is a VariableExpressionAst
/// with the same name as that of symbolRef.
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs
index 44b64c8f5..7f6014a2a 100644
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs
@@ -11,7 +11,7 @@ namespace Microsoft.PowerShell.EditorServices.Services.Symbols
///
/// The visitor used to find the references of a symbol in a script's AST
///
- internal class FindReferencesVisitor : AstVisitor
+ internal class FindReferencesVisitor : AstVisitor2
{
private readonly SymbolReference _symbolRef;
private readonly IDictionary> _cmdletToAliasDictionary;
@@ -113,35 +113,32 @@ public override AstVisitAction VisitCommand(CommandAst commandAst)
/// Decides if the current function definition is a reference of the symbol being searched for.
/// A reference of the symbol will be a of type SymbolType.Function and have the same name as the symbol
///
- /// A functionDefinitionAst in the script's AST
+ /// A FunctionDefinitionAst in the script's AST
/// A visit action that continues the search for references
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
- (int startColumnNumber, int startLineNumber) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst);
-
- IScriptExtent nameExtent = new ScriptExtent()
+ // Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
+ // This will not exclude nested functions as they have ScriptBlockAst as parent
+ if (functionDefinitionAst.Parent is FunctionMemberAst)
{
- Text = functionDefinitionAst.Name,
- StartLineNumber = startLineNumber,
- EndLineNumber = startLineNumber,
- StartColumnNumber = startColumnNumber,
- EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length,
- File = functionDefinitionAst.Extent.File
- };
+ return AstVisitAction.Continue;
+ }
if (_symbolRef.SymbolType.Equals(SymbolType.Function) &&
- nameExtent.Text.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ functionDefinitionAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
{
+ // We only want the function name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst);
FoundReferences.Add(new SymbolReference(SymbolType.Function, nameExtent));
}
return base.VisitFunctionDefinition(functionDefinitionAst);
}
///
- /// Decides if the current function definition is a reference of the symbol being searched for.
+ /// Decides if the current command parameter is a reference of the symbol being searched for.
/// A reference of the symbol will be a of type SymbolType.Parameter and have the same name as the symbol
///
- /// A commandParameterAst in the script's AST
+ /// A CommandParameterAst in the script's AST
/// A visit action that continues the search for references
public override AstVisitAction VisitCommandParameter(CommandParameterAst commandParameterAst)
{
@@ -154,10 +151,10 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
}
///
- /// Decides if the current function definition is a reference of the symbol being searched for.
+ /// Decides if the current variable expression is a reference of the symbol being searched for.
/// A reference of the symbol will be a of type SymbolType.Variable and have the same name as the symbol
///
- /// A variableExpressionAst in the script's AST
+ /// A VariableExpressionAst in the script's AST
/// A visit action that continues the search for references
public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
{
@@ -168,5 +165,112 @@ public override AstVisitAction VisitVariableExpression(VariableExpressionAst var
}
return AstVisitAction.Continue;
}
+
+ ///
+ /// Decides if the current type definition is a reference of the symbol being searched for.
+ /// A reference of the symbol will be a of type SymbolType.Class or SymbolType.Enum and have the same name as the symbol
+ ///
+ /// A TypeDefinitionAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
+ {
+ SymbolType symbolType =
+ typeDefinitionAst.IsEnum ?
+ SymbolType.Enum : SymbolType.Class;
+
+ if ((_symbolRef.SymbolType is SymbolType.Type || _symbolRef.SymbolType.Equals(symbolType)) &&
+ typeDefinitionAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the type name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst);
+ FoundReferences.Add(new SymbolReference(symbolType, nameExtent));
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current type expression is a reference of the symbol being searched for.
+ /// A reference of the symbol will be a of type SymbolType.Type and have the same name as the symbol
+ ///
+ /// A TypeExpressionAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpressionAst)
+ {
+ // We don't know if we're looking at a class or enum, but name is likely unique
+ if (IsTypeSymbol(_symbolRef.SymbolType) &&
+ typeExpressionAst.TypeName.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ FoundReferences.Add(new SymbolReference(SymbolType.Type, typeExpressionAst.Extent));
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current type constraint is a reference of the symbol being searched for.
+ /// A reference of the symbol will be a of type SymbolType.Type and have the same name as the symbol
+ ///
+ /// A TypeConstraintAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst)
+ {
+ // We don't know if we're looking at a class or enum, but name is likely unique
+ if (IsTypeSymbol(_symbolRef.SymbolType) &&
+ typeConstraintAst.TypeName.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ FoundReferences.Add(new SymbolReference(SymbolType.Type, typeConstraintAst.Extent));
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current function member is a reference of the symbol being searched for.
+ /// A reference of the symbol will be a of type SymbolType.Constructor or SymbolType.Method and have the same name as the symbol
+ ///
+ /// A FunctionMemberAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
+ {
+ SymbolType symbolType =
+ functionMemberAst.IsConstructor ?
+ SymbolType.Constructor : SymbolType.Method;
+
+ if (_symbolRef.SymbolType.Equals(symbolType) &&
+ VisitorUtils.GetMemberOverloadName(functionMemberAst, true, false).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the method/ctor name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, false);
+ FoundReferences.Add(new SymbolReference(symbolType, nameExtent));
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Decides if the current property member is a reference of the symbol being searched for.
+ /// A reference of the symbol will be a of type SymbolType.Property or SymbolType.EnumMember
+ /// and have the same name as the symbol.
+ ///
+ /// A PropertyMemberAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
+ {
+ SymbolType symbolType =
+ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ?
+ SymbolType.EnumMember : SymbolType.Property;
+
+ if (_symbolRef.SymbolType.Equals(symbolType) &&
+ VisitorUtils.GetMemberOverloadName(propertyMemberAst, false).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // We only want the property name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, false);
+ FoundReferences.Add(new SymbolReference(SymbolType.Property, nameExtent));
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Tests if symbol type is a type (class/enum) definition or type reference.
+ ///
+ private static bool IsTypeSymbol(SymbolType symbolType)
+ => symbolType is SymbolType.Class or SymbolType.Enum or SymbolType.Type;
}
}
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs
index bf2520a3c..b45ee03eb 100644
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs
@@ -9,22 +9,25 @@ namespace Microsoft.PowerShell.EditorServices.Services.Symbols
///
/// The visitor used to find the symbol at a specific location in the AST
///
- internal class FindSymbolVisitor : AstVisitor
+ internal class FindSymbolVisitor : AstVisitor2
{
private readonly int lineNumber;
private readonly int columnNumber;
- private readonly bool includeFunctionDefinitions;
+ private readonly bool includeDefinitions;
+ private readonly bool returnFullSignature;
public SymbolReference FoundSymbolReference { get; private set; }
public FindSymbolVisitor(
int lineNumber,
int columnNumber,
- bool includeFunctionDefinitions)
+ bool includeDefinitions,
+ bool returnFullSignature)
{
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
- this.includeFunctionDefinitions = includeFunctionDefinitions;
+ this.includeDefinitions = includeDefinitions;
+ this.returnFullSignature = returnFullSignature;
}
///
@@ -58,32 +61,34 @@ public override AstVisitAction VisitCommand(CommandAst commandAst)
/// or a decision to continue if it wasn't found
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
- int startLineNumber = functionDefinitionAst.Extent.StartLineNumber;
- int startColumnNumber = functionDefinitionAst.Extent.StartColumnNumber;
- int endLineNumber = functionDefinitionAst.Extent.EndLineNumber;
- int endColumnNumber = functionDefinitionAst.Extent.EndColumnNumber;
-
- if (!includeFunctionDefinitions)
+ // Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
+ // This will not exclude nested functions as they have ScriptBlockAst as parent
+ if (functionDefinitionAst.Parent is FunctionMemberAst)
{
- // We only want the function name
- (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst);
- startLineNumber = startLine;
- startColumnNumber = startColumn;
- endLineNumber = startLine;
- endColumnNumber = startColumn + functionDefinitionAst.Name.Length;
+ return AstVisitAction.Continue;
}
- IScriptExtent nameExtent = new ScriptExtent()
+ IScriptExtent nameExtent;
+
+ if (includeDefinitions)
{
- Text = functionDefinitionAst.Name,
- StartLineNumber = startLineNumber,
- EndLineNumber = endLineNumber,
- StartColumnNumber = startColumnNumber,
- EndColumnNumber = endColumnNumber,
- File = functionDefinitionAst.Extent.File
- };
+ nameExtent = new ScriptExtent()
+ {
+ Text = functionDefinitionAst.Name,
+ StartLineNumber = functionDefinitionAst.Extent.StartLineNumber,
+ EndLineNumber = functionDefinitionAst.Extent.EndLineNumber,
+ StartColumnNumber = functionDefinitionAst.Extent.StartColumnNumber,
+ EndColumnNumber = functionDefinitionAst.Extent.EndColumnNumber,
+ File = functionDefinitionAst.Extent.File
+ };
+ }
+ else
+ {
+ // We only want the function name
+ nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst);
+ }
- if (IsPositionInExtent(nameExtent))
+ if (nameExtent.Contains(lineNumber, columnNumber))
{
FoundSymbolReference =
new SymbolReference(
@@ -116,7 +121,7 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
}
///
- /// Checks to see if this variable expression is the symbol we are looking for.
+ /// Checks to see if this variable expression is the symbol we are looking for.
///
/// A VariableExpressionAst object in the script's AST
/// A decision to stop searching if the right symbol was found,
@@ -137,7 +142,9 @@ public override AstVisitAction VisitVariableExpression(VariableExpressionAst var
}
///
- /// Is the position of the given location is in the ast's extent
+ /// Is the position of the given location is in the ast's extent.
+ /// Only works with single-line extents like name extents.
+ /// Use extension for definition extents.
///
/// The script extent of the element
/// True if the given position is in the range of the element's extent
@@ -147,5 +154,197 @@ private bool IsPositionInExtent(IScriptExtent extent)
extent.StartColumnNumber <= columnNumber &&
extent.EndColumnNumber >= columnNumber;
}
+
+ ///
+ /// Checks to see if this function member is the symbol we are looking for.
+ ///
+ /// A FunctionMemberAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
+ {
+ // We only want the method/ctor name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, returnFullSignature);
+
+ if (IsPositionInExtent(nameExtent))
+ {
+ SymbolType symbolType =
+ functionMemberAst.IsConstructor ?
+ SymbolType.Constructor : SymbolType.Method;
+
+ FoundSymbolReference =
+ new SymbolReference(
+ symbolType,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Checks to see if this type definition is the symbol we are looking for.
+ ///
+ /// A TypeDefinitionAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
+ {
+ IScriptExtent nameExtent;
+
+ if (includeDefinitions)
+ {
+ nameExtent = new ScriptExtent()
+ {
+ Text = typeDefinitionAst.Name,
+ StartLineNumber = typeDefinitionAst.Extent.StartLineNumber,
+ EndLineNumber = typeDefinitionAst.Extent.EndLineNumber,
+ StartColumnNumber = typeDefinitionAst.Extent.StartColumnNumber,
+ EndColumnNumber = typeDefinitionAst.Extent.EndColumnNumber,
+ File = typeDefinitionAst.Extent.File
+ };
+ }
+ else
+ {
+ // We only want the type name
+ nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst);
+ }
+
+ if (nameExtent.Contains(lineNumber, columnNumber))
+ {
+ SymbolType symbolType =
+ typeDefinitionAst.IsEnum ?
+ SymbolType.Enum : SymbolType.Class;
+
+ FoundSymbolReference =
+ new SymbolReference(
+ symbolType,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Checks to see if this type expression is the symbol we are looking for.
+ ///
+ /// A TypeExpressionAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpressionAst)
+ {
+ // Show only type name (skip leading '['). Offset by StartColumn to include indentation etc.
+ int startColumnNumber = typeExpressionAst.Extent.StartColumnNumber + 1;
+
+ IScriptExtent nameExtent = new ScriptExtent()
+ {
+ Text = typeExpressionAst.TypeName.Name,
+ StartLineNumber = typeExpressionAst.Extent.StartLineNumber,
+ EndLineNumber = typeExpressionAst.Extent.StartLineNumber,
+ StartColumnNumber = startColumnNumber,
+ EndColumnNumber = startColumnNumber + typeExpressionAst.TypeName.Name.Length,
+ File = typeExpressionAst.Extent.File
+ };
+
+ if (IsPositionInExtent(nameExtent))
+ {
+ FoundSymbolReference =
+ new SymbolReference(
+ SymbolType.Type,
+ nameExtent);
+ return AstVisitAction.StopVisit;
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Checks to see if this type constraint is the symbol we are looking for.
+ ///
+ /// A TypeConstraintAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst)
+ {
+ // Show only type name (skip leading '[' if present). It's not present for inherited types
+ // Offset by StartColumn to include indentation etc.
+ int startColumnNumber =
+ typeConstraintAst.Extent.Text[0] == '[' ?
+ typeConstraintAst.Extent.StartColumnNumber + 1 : typeConstraintAst.Extent.StartColumnNumber;
+
+ IScriptExtent nameExtent = new ScriptExtent()
+ {
+ Text = typeConstraintAst.TypeName.Name,
+ StartLineNumber = typeConstraintAst.Extent.StartLineNumber,
+ EndLineNumber = typeConstraintAst.Extent.StartLineNumber,
+ StartColumnNumber = startColumnNumber,
+ EndColumnNumber = startColumnNumber + typeConstraintAst.TypeName.Name.Length,
+ File = typeConstraintAst.Extent.File
+ };
+
+ if (IsPositionInExtent(nameExtent))
+ {
+ FoundSymbolReference =
+ new SymbolReference(
+ SymbolType.Type,
+ nameExtent);
+ return AstVisitAction.StopVisit;
+ }
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Checks to see if this configuration definition is the symbol we are looking for.
+ ///
+ /// A ConfigurationDefinitionAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
+ {
+ // We only want the configuration name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(configurationDefinitionAst);
+
+ if (IsPositionInExtent(nameExtent))
+ {
+ FoundSymbolReference =
+ new SymbolReference(
+ SymbolType.Configuration,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Checks to see if this property member is the symbol we are looking for.
+ ///
+ /// A PropertyMemberAst object in the script's AST
+ /// A decision to stop searching if the right symbol was found,
+ /// or a decision to continue if it wasn't found
+ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
+ {
+ // We only want the property name. Get start-location for name
+ IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, returnFullSignature);
+
+ if (IsPositionInExtent(nameExtent))
+ {
+ SymbolType symbolType =
+ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ?
+ SymbolType.EnumMember : SymbolType.Property;
+
+ FoundSymbolReference =
+ new SymbolReference(
+ symbolType,
+ nameExtent);
+
+ return AstVisitAction.StopVisit;
+ }
+
+ return AstVisitAction.Continue;
+ }
}
}
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs
index 970acb4e7..55c90b4fd 100644
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs
+++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs
@@ -1,40 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using Microsoft.PowerShell.EditorServices.Utility;
using System.Collections.Generic;
using System.Management.Automation.Language;
namespace Microsoft.PowerShell.EditorServices.Services.Symbols
{
///
- /// The visitor used to find all the symbols (function and class defs) in the AST.
+ /// The visitor used to find all the symbols (variables, functions and class defs etc) in the AST.
///
- ///
- /// Requires PowerShell v3 or higher
- ///
- internal class FindSymbolsVisitor : AstVisitor
+ internal class FindSymbolsVisitor : AstVisitor2
{
public List SymbolReferences { get; }
public FindSymbolsVisitor() => SymbolReferences = new List();
///
- /// Adds each function definition as a
+ /// Adds each function definition to symbol reference list
///
- /// A functionDefinitionAst object in the script's AST
- /// A decision to stop searching if the right symbol was found,
- /// or a decision to continue if it wasn't found
+ /// A FunctionDefinitionAst in the script's AST
+ /// A visit action that continues the search for references
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
- IScriptExtent nameExtent = new ScriptExtent()
+ // Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter.
+ // This will not exclude nested functions as they have ScriptBlockAst as parent
+ if (functionDefinitionAst.Parent is FunctionMemberAst)
{
- Text = functionDefinitionAst.Name,
- StartLineNumber = functionDefinitionAst.Extent.StartLineNumber,
- EndLineNumber = functionDefinitionAst.Extent.EndLineNumber,
- StartColumnNumber = functionDefinitionAst.Extent.StartColumnNumber,
- EndColumnNumber = functionDefinitionAst.Extent.EndColumnNumber,
- File = functionDefinitionAst.Extent.File
- };
+ return AstVisitAction.Continue;
+ }
+
+ (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionDefinitionAst);
+ IScriptExtent nameExtent = GetNewExtent(functionDefinitionAst, functionDefinitionAst.Name, startLine, startColumn);
SymbolType symbolType =
functionDefinitionAst.IsWorkflow ?
@@ -49,11 +46,10 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
}
///
- /// Checks to see if this variable expression is the symbol we are looking for.
+ /// Adds each script scoped variable assignment to symbol reference list
///
- /// A VariableExpressionAst object in the script's AST
- /// A decision to stop searching if the right symbol was found,
- /// or a decision to continue if it wasn't found
+ /// A VariableExpressionAst in the script's AST
+ /// A visit action that continues the search for references
public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
{
if (!IsAssignedAtScriptScope(variableExpressionAst))
@@ -80,6 +76,107 @@ private static bool IsAssignedAtScriptScope(VariableExpressionAst variableExpres
parent = parent.Parent;
return parent is null || parent.Parent is null || parent.Parent.Parent is null;
}
+
+ ///
+ /// Adds class and enum AST to symbol reference list
+ ///
+ /// A TypeDefinitionAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
+ {
+ (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(typeDefinitionAst);
+ IScriptExtent nameExtent = GetNewExtent(typeDefinitionAst, typeDefinitionAst.Name, startLine, startColumn);
+
+ SymbolType symbolType =
+ typeDefinitionAst.IsEnum ?
+ SymbolType.Enum : SymbolType.Class;
+
+ SymbolReferences.Add(
+ new SymbolReference(
+ symbolType,
+ nameExtent));
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Adds class method and constructor AST to symbol reference list
+ ///
+ /// A FunctionMemberAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
+ {
+ (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionMemberAst);
+ IScriptExtent nameExtent = GetNewExtent(functionMemberAst, VisitorUtils.GetMemberOverloadName(functionMemberAst, false, false), startLine, startColumn);
+
+ SymbolType symbolType =
+ functionMemberAst.IsConstructor ?
+ SymbolType.Constructor : SymbolType.Method;
+
+ SymbolReferences.Add(
+ new SymbolReference(
+ symbolType,
+ nameExtent));
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Adds class property AST to symbol reference list
+ ///
+ /// A PropertyMemberAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
+ {
+ SymbolType symbolType =
+ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ?
+ SymbolType.EnumMember : SymbolType.Property;
+
+ bool isEnumMember = symbolType.Equals(SymbolType.EnumMember);
+ (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(propertyMemberAst, isEnumMember);
+ IScriptExtent nameExtent = GetNewExtent(propertyMemberAst, propertyMemberAst.Name, startLine, startColumn);
+
+ SymbolReferences.Add(
+ new SymbolReference(
+ symbolType,
+ nameExtent));
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Adds DSC configuration AST to symbol reference list
+ ///
+ /// A ConfigurationDefinitionAst in the script's AST
+ /// A visit action that continues the search for references
+ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
+ {
+ (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(configurationDefinitionAst);
+ IScriptExtent nameExtent = GetNewExtent(configurationDefinitionAst, configurationDefinitionAst.InstanceName.Extent.Text, startLine, startColumn);
+
+ SymbolReferences.Add(
+ new SymbolReference(
+ SymbolType.Configuration,
+ nameExtent));
+
+ return AstVisitAction.Continue;
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast with same range but modified Text
+ ///
+ private static ScriptExtent GetNewExtent(Ast ast, string text, int startLine, int startColumn)
+ {
+ return new ScriptExtent()
+ {
+ Text = text,
+ StartLineNumber = startLine,
+ EndLineNumber = ast.Extent.EndLineNumber,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = ast.Extent.EndColumnNumber,
+ File = ast.Extent.File
+ };
+ }
}
///
@@ -100,6 +197,8 @@ internal class FindHashtableSymbolsVisitor : AstVisitor
///
/// Adds keys in the input hashtable to the symbol reference
///
+ /// A HashtableAst in the script's AST
+ /// A visit action that continues the search for references
public override AstVisitAction VisitHashtable(HashtableAst hashtableAst)
{
if (hashtableAst.KeyValuePairs == null)
diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor2.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor2.cs
deleted file mode 100644
index 15f5e49db..000000000
--- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor2.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-namespace Microsoft.PowerShell.EditorServices.Services.Symbols
-{
- // TODO: Restore this when we figure out how to support multiple
- // PS versions in the new PSES-as-a-module world (issue #276)
-
- /////
- ///// The visitor used to find all the symbols (function and class defs) in the AST.
- /////
- /////
- ///// Requires PowerShell v5 or higher
- /////
- /////
- //internal class FindSymbolsVisitor2 : AstVisitor2
- //{
- // private FindSymbolsVisitor findSymbolsVisitor;
-
- // public List SymbolReferences
- // {
- // get
- // {
- // return this.findSymbolsVisitor.SymbolReferences;
- // }
- // }
-
- // public FindSymbolsVisitor2()
- // {
- // this.findSymbolsVisitor = new FindSymbolsVisitor();
- // }
-
- // ///
- // /// Adds each function definition as a
- // ///
- // /// A functionDefinitionAst object in the script's AST
- // /// A decision to stop searching if the right symbol was found,
- // /// or a decision to continue if it wasn't found
- // public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
- // {
- // return this.findSymbolsVisitor.VisitFunctionDefinition(functionDefinitionAst);
- // }
-
- // ///
- // /// Checks to see if this variable expression is the symbol we are looking for.
- // ///
- // /// A VariableExpressionAst object in the script's AST
- // /// A decision to stop searching if the right symbol was found,
- // /// or a decision to continue if it wasn't found
- // public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
- // {
- // return this.findSymbolsVisitor.VisitVariableExpression(variableExpressionAst);
- // }
-
- // public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
- // {
- // IScriptExtent nameExtent = new ScriptExtent()
- // {
- // Text = configurationDefinitionAst.InstanceName.Extent.Text,
- // StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber,
- // EndLineNumber = configurationDefinitionAst.Extent.EndLineNumber,
- // StartColumnNumber = configurationDefinitionAst.Extent.StartColumnNumber,
- // EndColumnNumber = configurationDefinitionAst.Extent.EndColumnNumber
- // };
-
- // this.findSymbolsVisitor.SymbolReferences.Add(
- // new SymbolReference(
- // SymbolType.Configuration,
- // nameExtent));
-
- // return AstVisitAction.Continue;
- // }
- //}
-}
diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs
index f0973a7f0..5071cc745 100644
--- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs
+++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs
@@ -128,7 +128,13 @@ private static SymbolKind GetSymbolKind(SymbolType symbolType)
{
return symbolType switch
{
- SymbolType.Configuration or SymbolType.Function or SymbolType.Workflow => SymbolKind.Function,
+ SymbolType.Function or SymbolType.Configuration or SymbolType.Workflow => SymbolKind.Function,
+ SymbolType.Enum => SymbolKind.Enum,
+ SymbolType.Class => SymbolKind.Class,
+ SymbolType.Constructor => SymbolKind.Constructor,
+ SymbolType.Method => SymbolKind.Method,
+ SymbolType.Property => SymbolKind.Property,
+ SymbolType.EnumMember => SymbolKind.EnumMember,
_ => SymbolKind.Variable,
};
}
@@ -137,8 +143,15 @@ private static string GetDecoratedSymbolName(ISymbolReference symbolReference)
{
string name = symbolReference.SymbolName;
- if (symbolReference.SymbolType is SymbolType.Configuration or
+ // Append { } for symbols with scriptblock
+ // Constructors and Methods have overloaded names already
+ if (symbolReference.SymbolType is
SymbolType.Function or
+ SymbolType.Enum or
+ SymbolType.Class or
+ SymbolType.Constructor or
+ SymbolType.Method or
+ SymbolType.Configuration or
SymbolType.Workflow)
{
name += " { }";
diff --git a/src/PowerShellEditorServices/Services/Workspace/Handlers/WorkspaceSymbolsHandler.cs b/src/PowerShellEditorServices/Services/Workspace/Handlers/WorkspaceSymbolsHandler.cs
index 8a9aaa815..3fa830f99 100644
--- a/src/PowerShellEditorServices/Services/Workspace/Handlers/WorkspaceSymbolsHandler.cs
+++ b/src/PowerShellEditorServices/Services/Workspace/Handlers/WorkspaceSymbolsHandler.cs
@@ -72,7 +72,7 @@ public override async Task> Handle(WorkspaceSymbolP
symbols.Add(new SymbolInformation
{
ContainerName = containerName,
- Kind = foundOccurrence.SymbolType == SymbolType.Variable ? SymbolKind.Variable : SymbolKind.Function,
+ Kind = GetSymbolKind(foundOccurrence.SymbolType),
Location = location,
Name = GetDecoratedSymbolName(foundOccurrence)
});
@@ -107,8 +107,15 @@ private static string GetDecoratedSymbolName(SymbolReference symbolReference)
{
string name = symbolReference.SymbolName;
- if (symbolReference.SymbolType is SymbolType.Configuration or
+ // Append { } for symbols with scriptblock
+ // Constructors and Methods have overloaded names already
+ if (symbolReference.SymbolType is
SymbolType.Function or
+ SymbolType.Enum or
+ SymbolType.Class or
+ SymbolType.Constructor or
+ SymbolType.Method or
+ SymbolType.Configuration or
SymbolType.Workflow)
{
name += " { }";
@@ -117,6 +124,20 @@ SymbolType.Function or
return name;
}
+ private static SymbolKind GetSymbolKind(SymbolType symbolType)
+ {
+ return symbolType switch
+ {
+ SymbolType.Function or SymbolType.Configuration or SymbolType.Workflow => SymbolKind.Function,
+ SymbolType.Enum => SymbolKind.Enum,
+ SymbolType.Class => SymbolKind.Class,
+ SymbolType.Constructor => SymbolKind.Constructor,
+ SymbolType.Method => SymbolKind.Method,
+ SymbolType.Property => SymbolKind.Property,
+ _ => SymbolKind.Variable,
+ };
+ }
+
#endregion
}
}
diff --git a/src/PowerShellEditorServices/Utility/Extensions.cs b/src/PowerShellEditorServices/Utility/Extensions.cs
index c280f1b14..22148e8b3 100644
--- a/src/PowerShellEditorServices/Utility/Extensions.cs
+++ b/src/PowerShellEditorServices/Utility/Extensions.cs
@@ -135,7 +135,14 @@ public static bool Contains(this IScriptExtent scriptExtent, int line, int colum
if (scriptExtent.StartLineNumber == line)
{
- return scriptExtent.StartColumnNumber <= column;
+ if (scriptExtent.StartLineNumber == scriptExtent.EndLineNumber)
+ {
+ return scriptExtent.StartColumnNumber <= column && scriptExtent.EndColumnNumber >= column;
+ }
+ else
+ {
+ return scriptExtent.StartColumnNumber <= column;
+ }
}
if (scriptExtent.EndLineNumber == line)
diff --git a/src/PowerShellEditorServices/Utility/VisitorUtils.cs b/src/PowerShellEditorServices/Utility/VisitorUtils.cs
index 861d04acc..3fc3f7ea1 100644
--- a/src/PowerShellEditorServices/Utility/VisitorUtils.cs
+++ b/src/PowerShellEditorServices/Utility/VisitorUtils.cs
@@ -1,7 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System;
+using System.Collections.Generic;
using System.Management.Automation.Language;
+using System.Text;
+using PSESSymbols = Microsoft.PowerShell.EditorServices.Services.Symbols;
namespace Microsoft.PowerShell.EditorServices.Utility
{
@@ -11,41 +15,276 @@ namespace Microsoft.PowerShell.EditorServices.Utility
internal static class VisitorUtils
{
///
- /// Calculates the start line and column of the actual function name in a function definition AST.
+ /// Calculates the start line and column of the actual symbol name in a AST.
///
- /// A FunctionDefinitionAst object in the script's AST
- /// A tuple with start column and line for the function name
- internal static (int startColumn, int startLine) GetNameStartColumnAndLineNumbersFromAst(FunctionDefinitionAst ast)
+ /// An Ast object in the script's AST
+ /// An int specifying start index of name in the AST's extent text
+ /// A tuple with start column and line of the symbol name
+ private static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(Ast ast, int nameStartIndex)
{
int startColumnNumber = ast.Extent.StartColumnNumber;
int startLineNumber = ast.Extent.StartLineNumber;
- int astOffset = ast.IsFilter ? "filter".Length : ast.IsWorkflow ? "workflow".Length : "function".Length;
string astText = ast.Extent.Text;
- // The line offset represents the offset on the line that we're on where as
// astOffset is the offset on the entire text of the AST.
- int lineOffset = astOffset;
- for (; astOffset < astText.Length; astOffset++, lineOffset++)
+ for (int astOffset = 0; astOffset <= ast.Extent.Text.Length; astOffset++, startColumnNumber++)
{
if (astText[astOffset] == '\n')
{
// reset numbers since we are operating on a different line and increment the line number.
startColumnNumber = 0;
startLineNumber++;
- lineOffset = 0;
}
else if (astText[astOffset] == '\r')
{
// Do nothing with carriage returns... we only look for line feeds since those
// are used on every platform.
}
- else if (!char.IsWhiteSpace(astText[astOffset]))
+ else if (astOffset >= nameStartIndex && !char.IsWhiteSpace(astText[astOffset]))
{
// This is the start of the function name so we've found our start column and line number.
break;
}
}
- return (startColumnNumber + lineOffset, startLineNumber);
+ return (startColumnNumber, startLineNumber);
+ }
+
+ ///
+ /// Calculates the start line and column of the actual function name in a function definition AST.
+ ///
+ /// A FunctionDefinitionAst object in the script's AST
+ /// A tuple with start column and line for the function name
+ internal static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(FunctionDefinitionAst functionDefinitionAst)
+ {
+ int startOffset = functionDefinitionAst.IsFilter ? "filter".Length : functionDefinitionAst.IsWorkflow ? "workflow".Length : "function".Length;
+ return GetNameStartColumnAndLineFromAst(functionDefinitionAst, startOffset);
+ }
+
+ ///
+ /// Calculates the start line and column of the actual class/enum name in a type definition AST.
+ ///
+ /// A TypeDefinitionAst object in the script's AST
+ /// A tuple with start column and line for the type name
+ internal static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(TypeDefinitionAst typeDefinitionAst)
+ {
+ int startOffset = typeDefinitionAst.IsEnum ? "enum".Length : "class".Length;
+ return GetNameStartColumnAndLineFromAst(typeDefinitionAst, startOffset);
+ }
+
+ ///
+ /// Calculates the start line and column of the actual method/constructor name in a function member AST.
+ ///
+ /// A FunctionMemberAst object in the script's AST
+ /// A tuple with start column and line for the method/constructor name
+ internal static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(FunctionMemberAst functionMemberAst)
+ {
+ // find name index to get offset even with attributes, static, hidden ++
+ int nameStartIndex = functionMemberAst.Extent.Text.LastIndexOf(string.Concat(functionMemberAst.Name, '('), StringComparison.OrdinalIgnoreCase);
+ return GetNameStartColumnAndLineFromAst(functionMemberAst, nameStartIndex);
+ }
+
+ ///
+ /// Calculates the start line and column of the actual property name in a property member AST.
+ ///
+ /// A PropertyMemberAst object in the script's AST
+ /// A bool indicating this is a enum member
+ /// A tuple with start column and line for the property name
+ internal static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(PropertyMemberAst propertyMemberAst, bool isEnumMember)
+ {
+ // find name index to get offset even with attributes, static, hidden ++
+ string searchString = isEnumMember ? propertyMemberAst.Name : string.Concat('$', propertyMemberAst.Name);
+ int nameStartIndex = propertyMemberAst.Extent.Text.LastIndexOf(searchString, StringComparison.OrdinalIgnoreCase);
+ return GetNameStartColumnAndLineFromAst(propertyMemberAst, nameStartIndex);
+ }
+
+ ///
+ /// Calculates the start line and column of the actual configuration name in a configuration definition AST.
+ ///
+ /// A ConfigurationDefinitionAst object in the script's AST
+ /// A tuple with start column and line for the configuration name
+ internal static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(ConfigurationDefinitionAst configurationDefinitionAst)
+ {
+ const int startOffset = 13; // "configuration".Length
+ return GetNameStartColumnAndLineFromAst(configurationDefinitionAst, startOffset);
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast for the symbol name only (variable)
+ ///
+ /// A FunctionDefinitionAst in the script's AST
+ /// A ScriptExtent with for the symbol name only
+ internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionDefinitionAst functionDefinitionAst)
+ {
+ (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(functionDefinitionAst);
+
+ return new PSESSymbols.ScriptExtent()
+ {
+ Text = functionDefinitionAst.Name,
+ StartLineNumber = startLine,
+ EndLineNumber = startLine,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = startColumn + functionDefinitionAst.Name.Length,
+ File = functionDefinitionAst.Extent.File
+ };
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast for the symbol name only (variable)
+ ///
+ /// A TypeDefinitionAst in the script's AST
+ /// A ScriptExtent with for the symbol name only
+ internal static PSESSymbols.ScriptExtent GetNameExtent(TypeDefinitionAst typeDefinitionAst)
+ {
+ (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(typeDefinitionAst);
+
+ return new PSESSymbols.ScriptExtent()
+ {
+ Text = typeDefinitionAst.Name,
+ StartLineNumber = startLine,
+ EndLineNumber = startLine,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = startColumn + typeDefinitionAst.Name.Length,
+ File = typeDefinitionAst.Extent.File
+ };
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast for the symbol name only (variable)
+ ///
+ /// A FunctionMemberAst in the script's AST
+ /// A bool indicating if class/enum name should be prepended
+ /// A bool indicating if return type should be included for methods
+ /// A ScriptExtent with for the symbol name only
+ internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functionMemberAst, bool useQualifiedName = true, bool includeReturnType = false)
+ {
+ (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(functionMemberAst);
+
+ return new PSESSymbols.ScriptExtent()
+ {
+ Text = GetMemberOverloadName(functionMemberAst, useQualifiedName, includeReturnType),
+ StartLineNumber = startLine,
+ EndLineNumber = startLine,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = startColumn + functionMemberAst.Name.Length,
+ File = functionMemberAst.Extent.File
+ };
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast for the property name only
+ ///
+ /// A PropertyMemberAst in the script's AST
+ /// A bool indicating if type should be included for class property
+ /// A ScriptExtent with for the symbol name only
+ internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propertyMemberAst, bool includePropertyType = false)
+ {
+ bool isEnumMember = propertyMemberAst.Parent is TypeDefinitionAst typeDef && typeDef.IsEnum;
+ (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(propertyMemberAst, isEnumMember);
+
+ // +1 when class property to as start includes $
+ int endColumnNumber = isEnumMember ?
+ startColumn + propertyMemberAst.Name.Length :
+ startColumn + propertyMemberAst.Name.Length + 1;
+
+ return new PSESSymbols.ScriptExtent()
+ {
+ Text = GetMemberOverloadName(propertyMemberAst, includePropertyType),
+ StartLineNumber = startLine,
+ EndLineNumber = startLine,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = endColumnNumber,
+ File = propertyMemberAst.Extent.File
+ };
+ }
+
+ ///
+ /// Gets a new ScriptExtent for a given Ast for the configuration instance name only
+ ///
+ /// A ConfigurationDefinitionAst in the script's AST
+ /// A ScriptExtent with for the symbol name only
+ internal static PSESSymbols.ScriptExtent GetNameExtent(ConfigurationDefinitionAst configurationDefinitionAst)
+ {
+ string configurationName = configurationDefinitionAst.InstanceName.Extent.Text;
+ (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(configurationDefinitionAst);
+
+ return new PSESSymbols.ScriptExtent()
+ {
+ Text = configurationName,
+ StartLineNumber = startLine,
+ EndLineNumber = startLine,
+ StartColumnNumber = startColumn,
+ EndColumnNumber = startColumn + configurationName.Length,
+ File = configurationDefinitionAst.Extent.File
+ };
+ }
+
+ ///
+ /// Gets the method or constructor name with parameters for current overload.
+ ///
+ /// A FunctionMemberAst object in the script's AST
+ /// A bool indicating if class/enum name should be prepended
+ /// A bool indicating if return type should be included for methods
+ /// Function member name with return type (optional) and parameters
+ internal static string GetMemberOverloadName(FunctionMemberAst functionMemberAst,
+ bool useQualifiedName = true,
+ bool includeReturnType = false)
+ {
+ StringBuilder sb = new();
+
+ // Prepend return type and class. Used for symbol details (hover)
+ if (includeReturnType && !functionMemberAst.IsConstructor)
+ {
+ sb.Append(functionMemberAst.ReturnType?.TypeName.Name ?? "void").Append(' ');
+ }
+
+ if (useQualifiedName && functionMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsClass)
+ {
+ sb.Append(typeAst.Name).Append('.');
+ }
+
+ sb.Append(functionMemberAst.Name);
+
+ // Add parameters
+ sb.Append('(');
+ if (functionMemberAst.Parameters.Count > 0)
+ {
+ List parameters = new(functionMemberAst.Parameters.Count);
+ foreach (ParameterAst param in functionMemberAst.Parameters)
+ {
+ parameters.Add(param.Extent.Text);
+ }
+
+ sb.Append(string.Join(", ", parameters));
+ }
+ sb.Append(')');
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Gets the property name with type and class/enum.
+ ///
+ /// A PropertyMemberAst object in the script's AST
+ /// A bool indicating if type should be included for class property
+ /// Property name with type (optional) and class/enum
+ internal static string GetMemberOverloadName(PropertyMemberAst propertyMemberAst,
+ bool includePropertyType = false)
+ {
+ StringBuilder sb = new();
+
+ // Prepend return type and class. Used for symbol details (hover)
+ if (propertyMemberAst.Parent is TypeDefinitionAst typeAst)
+ {
+ if (includePropertyType && !typeAst.IsEnum)
+ {
+ sb.Append(propertyMemberAst.PropertyType?.TypeName.Name ?? "object").Append(' ');
+ }
+
+ sb.Append(typeAst.Name).Append('.');
+ }
+
+ sb.Append(propertyMemberAst.Name);
+ return sb.ToString();
}
}
}
diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs
index 0ca4bb2e3..d990ebe09 100644
--- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs
+++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation.
+// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
@@ -442,7 +442,7 @@ await PsesLanguageClient
Range range = symInfoOrDocSym.SymbolInformation.Location.Range;
Assert.Equal(1, range.Start.Line);
- Assert.Equal(0, range.Start.Character);
+ Assert.Equal(9, range.Start.Character);
Assert.Equal(3, range.End.Line);
Assert.Equal(1, range.End.Character);
});
@@ -841,7 +841,7 @@ public async Task NoMessageIfPesterCodeLensDisabled()
}
[Fact]
- public async Task CanSendReferencesCodeLensRequestAsync()
+ public async Task CanSendFunctionReferencesCodeLensRequestAsync()
{
string filePath = NewTestFile(@"
function CanSendReferencesCodeLensRequest {
@@ -867,7 +867,7 @@ function CanSendReferencesCodeLensRequest {
Range range = codeLens.Range;
Assert.Equal(1, range.Start.Line);
- Assert.Equal(0, range.Start.Character);
+ Assert.Equal(9, range.Start.Character);
Assert.Equal(3, range.End.Line);
Assert.Equal(1, range.End.Character);
@@ -878,6 +878,110 @@ function CanSendReferencesCodeLensRequest {
Assert.Equal("1 reference", codeLensResolveResult.Command.Title);
}
+ [Fact]
+ public async Task CanSendClassReferencesCodeLensRequestAsync()
+ {
+ string filePath = NewTestFile(@"
+param(
+ [MyBaseClass]$enumValue
+)
+
+class MyBaseClass {
+
+}
+
+class ChildClass : MyBaseClass, System.IDisposable {
+
+}
+
+$o = [MyBaseClass]::new()
+$o -is [MyBaseClass]
+");
+
+ CodeLensContainer codeLenses = await PsesLanguageClient
+ .SendRequest(
+ "textDocument/codeLens",
+ new CodeLensParams
+ {
+ TextDocument = new TextDocumentIdentifier
+ {
+ Uri = new Uri(filePath)
+ }
+ })
+ .Returning(CancellationToken.None).ConfigureAwait(true);
+
+ Assert.Collection(codeLenses,
+ codeLens =>
+ {
+ Range range = codeLens.Range;
+ Assert.Equal(5, range.Start.Line);
+ Assert.Equal(6, range.Start.Character);
+ Assert.Equal(7, range.End.Line);
+ Assert.Equal(1, range.End.Character);
+ },
+ codeLens =>
+ {
+ Range range = codeLens.Range;
+ Assert.Equal(9, range.Start.Line);
+ Assert.Equal(6, range.Start.Character);
+ Assert.Equal(11, range.End.Line);
+ Assert.Equal(1, range.End.Character);
+ }
+ );
+
+ CodeLens baseClassCodeLens = codeLenses.First();
+ CodeLens codeLensResolveResult = await PsesLanguageClient
+ .SendRequest("codeLens/resolve", baseClassCodeLens)
+ .Returning(CancellationToken.None).ConfigureAwait(true);
+
+ Assert.Equal("4 references", codeLensResolveResult.Command.Title);
+ }
+
+ [Fact]
+ public async Task CanSendEnumReferencesCodeLensRequestAsync()
+ {
+ string filePath = NewTestFile(@"
+param(
+ [MyEnum]$enumValue
+)
+
+enum MyEnum {
+ First = 1
+ Second
+ Third
+}
+
+[MyEnum]::First
+'First' -is [MyEnum]
+");
+
+ CodeLensContainer codeLenses = await PsesLanguageClient
+ .SendRequest(
+ "textDocument/codeLens",
+ new CodeLensParams
+ {
+ TextDocument = new TextDocumentIdentifier
+ {
+ Uri = new Uri(filePath)
+ }
+ })
+ .Returning(CancellationToken.None).ConfigureAwait(true);
+
+ CodeLens codeLens = Assert.Single(codeLenses);
+
+ Range range = codeLens.Range;
+ Assert.Equal(5, range.Start.Line);
+ Assert.Equal(5, range.Start.Character);
+ Assert.Equal(9, range.End.Line);
+ Assert.Equal(1, range.End.Character);
+
+ CodeLens codeLensResolveResult = await PsesLanguageClient
+ .SendRequest("codeLens/resolve", codeLens)
+ .Returning(CancellationToken.None).ConfigureAwait(true);
+
+ Assert.Equal("3 references", codeLensResolveResult.Command.Title);
+ }
+
[SkippableFact]
public async Task CanSendCodeActionRequestAsync()
{
diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsTypeSymbolsDefinition.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsTypeSymbolsDefinition.cs
new file mode 100644
index 000000000..a0b5ae61c
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsTypeSymbolsDefinition.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.Definition
+{
+ public static class FindsTypeSymbolsDefinitionData
+ {
+ public static readonly ScriptRegion ClassSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 8,
+ startColumnNumber: 14,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 39,
+ startColumnNumber: 10,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeExpressionSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 45,
+ startColumnNumber: 5,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeConstraintSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 25,
+ startColumnNumber: 24,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion ConstructorSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 9,
+ startColumnNumber: 14,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion MethodSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 19,
+ startColumnNumber: 25,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion PropertySourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 15,
+ startColumnNumber: 32,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumMemberSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 41,
+ startColumnNumber: 11,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnTypeSymbols.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnTypeSymbols.cs
new file mode 100644
index 000000000..7c012bc7c
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnTypeSymbols.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.Occurrences
+{
+ public static class FindsOccurrencesOnTypeSymbolsData
+ {
+ public static readonly ScriptRegion ClassSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 8,
+ startColumnNumber: 16,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 39,
+ startColumnNumber: 7,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeExpressionSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 34,
+ startColumnNumber: 16,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeConstraintSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 8,
+ startColumnNumber: 24,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion ConstructorSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 13,
+ startColumnNumber: 14,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion MethodSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 28,
+ startColumnNumber: 22,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion PropertySourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 15,
+ startColumnNumber: 18,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumMemberSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 40,
+ startColumnNumber: 6,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnVariable.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnVariable.cs
new file mode 100644
index 000000000..c01db0591
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnVariable.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.Occurrences
+{
+ public static class FindsOccurrencesOnVariableData
+ {
+ public static readonly ScriptRegion SourceDetails = new(
+ file: TestUtilities.NormalizePath("References/SimpleFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 8,
+ startColumnNumber: 3,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnTypeSymbols.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnTypeSymbols.cs
new file mode 100644
index 000000000..d9cbcf434
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnTypeSymbols.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.References
+{
+ public static class FindsReferencesOnTypeSymbolsData
+ {
+ public static readonly ScriptRegion ClassSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 8,
+ startColumnNumber: 12,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 39,
+ startColumnNumber: 8,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion ConstructorSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 9,
+ startColumnNumber: 8,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion MethodSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 19,
+ startColumnNumber: 20,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion PropertySourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 17,
+ startColumnNumber: 15,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion EnumMemberSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 41,
+ startColumnNumber: 8,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeExpressionSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 34,
+ startColumnNumber: 12,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion TypeConstraintSourceDetails = new(
+ file: TestUtilities.NormalizePath("References/TypeAndClassesFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 25,
+ startColumnNumber: 22,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/References/TypeAndClassesFile.ps1 b/test/PowerShellEditorServices.Test.Shared/References/TypeAndClassesFile.ps1
new file mode 100644
index 000000000..4fe54505d
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/References/TypeAndClassesFile.ps1
@@ -0,0 +1,46 @@
+Get-ChildItem ./file1.ps1
+$myScriptVar = 123
+
+class BaseClass {
+
+}
+
+class SuperClass : BaseClass {
+ SuperClass([string]$name) {
+
+ }
+
+ SuperClass() { }
+
+ [string]$SomePropWithDefault = 'this is a default value'
+
+ [int]$SomeProp
+
+ [string]MyClassMethod([string]$param1, $param2, [int]$param3) {
+ $this.SomePropWithDefault = 'something happend'
+ return 'finished'
+ }
+
+ [string]
+ MyClassMethod([MyEnum]$param1) {
+ return 'hello world'
+ }
+ [string]MyClassMethod() {
+ return 'hello world'
+ }
+}
+
+New-Object SuperClass
+$o = [SuperClass]::new()
+$o.SomeProp
+$o.MyClassMeth
+
+
+enum MyEnum {
+ First
+ Second
+ Third
+}
+
+[MyEnum]::First
+'First' -is [MyEnum]
diff --git a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForTypeSymbols.cs b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForTypeSymbols.cs
new file mode 100644
index 000000000..ff78a2c5a
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForTypeSymbols.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.SymbolDetails
+{
+ public static class FindsDetailsForTypeSymbolsData
+ {
+ public static readonly ScriptRegion EnumMemberSourceDetails = new(
+ file: TestUtilities.NormalizePath("SymbolDetails/TypeSymbolDetails.ps1"),
+ text: string.Empty,
+ startLineNumber: 20,
+ startColumnNumber: 6,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion PropertySourceDetails = new(
+ file: TestUtilities.NormalizePath("SymbolDetails/TypeSymbolDetails.ps1"),
+ text: string.Empty,
+ startLineNumber: 6,
+ startColumnNumber: 18,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion ConstructorSourceDetails = new(
+ file: TestUtilities.NormalizePath("SymbolDetails/TypeSymbolDetails.ps1"),
+ text: string.Empty,
+ startLineNumber: 2,
+ startColumnNumber: 11,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+
+ public static readonly ScriptRegion MethodSourceDetails = new(
+ file: TestUtilities.NormalizePath("SymbolDetails/TypeSymbolDetails.ps1"),
+ text: string.Empty,
+ startLineNumber: 10,
+ startColumnNumber: 20,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/TypeSymbolDetails.ps1 b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/TypeSymbolDetails.ps1
new file mode 100644
index 000000000..fd4a10a46
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/TypeSymbolDetails.ps1
@@ -0,0 +1,23 @@
+class SuperClass {
+ SuperClass([string]$name) {
+
+ }
+
+ [string]$SomePropWithDefault = 'this is a default value'
+
+ [int]$SomeProp
+
+ [string]MyClassMethod([string]$param1, $param2, [int]$param3) {
+ $this.SomePropWithDefault = 'something happend'
+ return 'finished'
+ }
+}
+
+New-Object SuperClass
+$o = [SuperClass]::new()
+
+enum MyEnum {
+ First
+ Second
+ Third
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/DSCFile.ps1 b/test/PowerShellEditorServices.Test.Shared/Symbols/DSCFile.ps1
new file mode 100644
index 000000000..defec6863
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Symbols/DSCFile.ps1
@@ -0,0 +1,4 @@
+# This file represents a script with a DSC configuration
+configuration AConfiguration {
+ Node "TEST-PC" {}
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInDSCFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInDSCFile.cs
new file mode 100644
index 000000000..6e3d45ff2
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInDSCFile.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.Symbols
+{
+ public static class FindSymbolsInDSCFile
+ {
+ public static readonly ScriptRegion SourceDetails =
+ new(
+ file: TestUtilities.NormalizePath("Symbols/DSCFile.ps1"),
+ text: string.Empty,
+ startLineNumber: 0,
+ startColumnNumber: 0,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNewLineSymbolFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNewLineSymbolFile.cs
new file mode 100644
index 000000000..0be43f8d1
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNewLineSymbolFile.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.EditorServices.Services.TextDocument;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Shared.Symbols
+{
+ public static class FindSymbolsInNewLineSymbolFile
+ {
+ public static readonly ScriptRegion SourceDetails =
+ new(
+ file: TestUtilities.NormalizePath("Symbols/NewLineSymbols.ps1"),
+ text: string.Empty,
+ startLineNumber: 0,
+ startColumnNumber: 0,
+ startOffset: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0,
+ endOffset: 0);
+ }
+}
diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/MultipleSymbols.ps1 b/test/PowerShellEditorServices.Test.Shared/Symbols/MultipleSymbols.ps1
index f234fed03..db53a6c1a 100644
--- a/test/PowerShellEditorServices.Test.Shared/Symbols/MultipleSymbols.ps1
+++ b/test/PowerShellEditorServices.Test.Shared/Symbols/MultipleSymbols.ps1
@@ -22,10 +22,22 @@ function AnAdvancedFunction {
workflow AWorkflow {}
-Configuration AConfiguration {
- Node "TEST-PC" {}
+class AClass {
+ [string]$AProperty
+
+ AClass([string]$AParameter) {
+
+ }
+
+ [void]AMethod([string]$param1, [int]$param2, $param3) {
+
+ }
+}
+
+enum AEnum {
+ AValue = 0
}
AFunction
1..3 | AFilter
-AnAdvancedFunction
\ No newline at end of file
+AnAdvancedFunction
diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/NewLineSymbols.ps1 b/test/PowerShellEditorServices.Test.Shared/Symbols/NewLineSymbols.ps1
new file mode 100644
index 000000000..5ca44f02a
--- /dev/null
+++ b/test/PowerShellEditorServices.Test.Shared/Symbols/NewLineSymbols.ps1
@@ -0,0 +1,28 @@
+function
+returnTrue {
+ $true
+}
+
+class
+NewLineClass {
+ NewLineClass() {
+
+ }
+
+ static
+ hidden
+ [string]
+ $SomePropWithDefault = 'some value'
+
+ static
+ hidden
+ [string]
+ MyClassMethod([MyNewLineEnum]$param1) {
+ return 'hello world $param1'
+ }
+}
+
+enum
+MyNewLineEnum {
+ First
+}
diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs
index e7938c287..0f30eccde 100644
--- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs
+++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Management.Automation;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
@@ -32,6 +33,7 @@ public class SymbolsServiceTests : IDisposable
private readonly PsesInternalHost psesHost;
private readonly WorkspaceService workspace;
private readonly SymbolsService symbolsService;
+ private static readonly bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public SymbolsServiceTests()
{
@@ -223,6 +225,15 @@ public async Task FindsReferencesOnVariable()
Assert.Equal(13, referencesResult[referencesResult.Count - 1].ScriptRegion.StartColumnNumber);
}
+ [Fact]
+ public void FindsOccurrencesOnVariable()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnVariableData.SourceDetails);
+ Assert.Equal(3, occurrencesResult.Count);
+ Assert.Equal(10, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ Assert.Equal(13, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartColumnNumber);
+ }
+
[Fact]
public void FindsOccurrencesOnFunction()
{
@@ -251,6 +262,222 @@ public async Task FindsReferencesOnCommandWithAlias()
Assert.Equal("Get-ChildItem", referencesResult[referencesResult.Count - 1].SymbolName);
}
+ [Fact]
+ public async Task FindsClassDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.ClassSourceDetails).ConfigureAwait(true);
+ Assert.Equal(8, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(7, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("SuperClass", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnClass()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.ClassSourceDetails).ConfigureAwait(true);
+ Assert.Equal(2, referencesResult.Count);
+ Assert.Equal(8, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(7, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnClass()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.ClassSourceDetails);
+ Assert.Equal(2, occurrencesResult.Count);
+ Assert.Equal("[SuperClass]", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(34, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsEnumDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.EnumSourceDetails).ConfigureAwait(true);
+ Assert.Equal(39, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(6, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("MyEnum", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnEnum()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.EnumSourceDetails).ConfigureAwait(true);
+ Assert.Equal(4, referencesResult.Count);
+ Assert.Equal(25, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(19, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnEnum()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.EnumSourceDetails);
+ Assert.Equal(4, occurrencesResult.Count);
+ Assert.Equal("[MyEnum]", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(46, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsTypeExpressionDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.TypeExpressionSourceDetails).ConfigureAwait(true);
+ Assert.Equal(39, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(6, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("MyEnum", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnTypeExpression()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.TypeExpressionSourceDetails).ConfigureAwait(true);
+ Assert.Equal(2, referencesResult.Count);
+ Assert.Equal(8, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(7, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnTypeExpression()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.TypeExpressionSourceDetails);
+ Assert.Equal(2, occurrencesResult.Count);
+ Assert.Equal("SuperClass", occurrencesResult[0].SymbolName);
+ Assert.Equal(8, occurrencesResult[0].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsTypeConstraintDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.TypeConstraintSourceDetails).ConfigureAwait(true);
+ Assert.Equal(39, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(6, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("MyEnum", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnTypeConstraint()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.TypeConstraintSourceDetails).ConfigureAwait(true);
+ Assert.Equal(4, referencesResult.Count);
+ Assert.Equal(25, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(19, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnTypeConstraint()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.TypeConstraintSourceDetails);
+ Assert.Equal(2, occurrencesResult.Count);
+ Assert.Equal("BaseClass", occurrencesResult[0].SymbolName);
+ Assert.Equal(4, occurrencesResult[0].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsConstructorDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.ConstructorSourceDetails).ConfigureAwait(true);
+ Assert.Equal(9, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("SuperClass.SuperClass([string]$name)", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnConstructor()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.ConstructorSourceDetails).ConfigureAwait(true);
+ Assert.Single(referencesResult);
+ Assert.Equal(9, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(5, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnConstructor()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.ConstructorSourceDetails);
+ Assert.Single(occurrencesResult);
+ Assert.Equal("SuperClass.SuperClass()", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(13, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsMethodDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.MethodSourceDetails).ConfigureAwait(true);
+ Assert.Equal(19, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(13, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("SuperClass.MyClassMethod([string]$param1, $param2, [int]$param3)", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnMethod()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.MethodSourceDetails).ConfigureAwait(true);
+ Assert.Single(referencesResult);
+ Assert.Equal(19, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(13, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnMethod()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.MethodSourceDetails);
+ Assert.Single(occurrencesResult);
+ Assert.Equal("SuperClass.MyClassMethod()", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(28, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsPropertyDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.PropertySourceDetails).ConfigureAwait(true);
+ Assert.Equal(15, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(13, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("SuperClass.SomePropWithDefault", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnProperty()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.PropertySourceDetails).ConfigureAwait(true);
+ Assert.Single(referencesResult);
+ Assert.Equal(17, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(10, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnProperty()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.PropertySourceDetails);
+ Assert.Equal(1, occurrencesResult.Count);
+ Assert.Equal("SuperClass.SomePropWithDefault", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(15, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
+ [Fact]
+ public async Task FindsEnumMemberDefinition()
+ {
+ SymbolReference definitionResult = await GetDefinition(FindsTypeSymbolsDefinitionData.EnumMemberSourceDetails).ConfigureAwait(true);
+ Assert.Equal(41, definitionResult.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, definitionResult.ScriptRegion.StartColumnNumber);
+ Assert.Equal("MyEnum.Second", definitionResult.SymbolName);
+ }
+
+ [Fact]
+ public async Task FindsReferencesOnEnumMember()
+ {
+ List referencesResult = await GetReferences(FindsReferencesOnTypeSymbolsData.EnumMemberSourceDetails).ConfigureAwait(true);
+ Assert.Single(referencesResult);
+ Assert.Equal(41, referencesResult[0].ScriptRegion.StartLineNumber);
+ Assert.Equal(5, referencesResult[0].ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsOccurrencesOnEnumMember()
+ {
+ IReadOnlyList occurrencesResult = GetOccurrences(FindsOccurrencesOnTypeSymbolsData.EnumMemberSourceDetails);
+ Assert.Single(occurrencesResult);
+ Assert.Equal("MyEnum.First", occurrencesResult[occurrencesResult.Count - 1].SymbolName);
+ Assert.Equal(40, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber);
+ }
+
[Fact]
public async Task FindsReferencesOnFileWithReferencesFileB()
{
@@ -277,6 +504,50 @@ public async Task FindsDetailsForBuiltInCommand()
Assert.NotEqual("", symbolDetails.Documentation);
}
+ [Fact]
+ public async Task FindsDetailsWithSignatureForEnumMember()
+ {
+ SymbolDetails symbolDetails = await symbolsService.FindSymbolDetailsAtLocationAsync(
+ GetScriptFile(FindsDetailsForTypeSymbolsData.EnumMemberSourceDetails),
+ FindsDetailsForTypeSymbolsData.EnumMemberSourceDetails.StartLineNumber,
+ FindsDetailsForTypeSymbolsData.EnumMemberSourceDetails.StartColumnNumber).ConfigureAwait(true);
+
+ Assert.Equal("MyEnum.First", symbolDetails.DisplayString);
+ }
+
+ [Fact]
+ public async Task FindsDetailsWithSignatureForProperty()
+ {
+ SymbolDetails symbolDetails = await symbolsService.FindSymbolDetailsAtLocationAsync(
+ GetScriptFile(FindsDetailsForTypeSymbolsData.PropertySourceDetails),
+ FindsDetailsForTypeSymbolsData.PropertySourceDetails.StartLineNumber,
+ FindsDetailsForTypeSymbolsData.PropertySourceDetails.StartColumnNumber).ConfigureAwait(true);
+
+ Assert.Equal("string SuperClass.SomePropWithDefault", symbolDetails.DisplayString);
+ }
+
+ [Fact]
+ public async Task FindsDetailsWithSignatureForConstructor()
+ {
+ SymbolDetails symbolDetails = await symbolsService.FindSymbolDetailsAtLocationAsync(
+ GetScriptFile(FindsDetailsForTypeSymbolsData.ConstructorSourceDetails),
+ FindsDetailsForTypeSymbolsData.ConstructorSourceDetails.StartLineNumber,
+ FindsDetailsForTypeSymbolsData.ConstructorSourceDetails.StartColumnNumber).ConfigureAwait(true);
+
+ Assert.Equal("SuperClass.SuperClass([string]$name)", symbolDetails.DisplayString);
+ }
+
+ [Fact]
+ public async Task FindsDetailsWithSignatureForMethod()
+ {
+ SymbolDetails symbolDetails = await symbolsService.FindSymbolDetailsAtLocationAsync(
+ GetScriptFile(FindsDetailsForTypeSymbolsData.MethodSourceDetails),
+ FindsDetailsForTypeSymbolsData.MethodSourceDetails.StartLineNumber,
+ FindsDetailsForTypeSymbolsData.MethodSourceDetails.StartColumnNumber).ConfigureAwait(true);
+
+ Assert.Equal("string SuperClass.MyClassMethod([string]$param1, $param2, [int]$param3)", symbolDetails.DisplayString);
+ }
+
[Fact]
public void FindsSymbolsInFile()
{
@@ -287,11 +558,17 @@ public void FindsSymbolsInFile()
Assert.Equal(4, symbolsResult.Count(symbolReference => symbolReference.SymbolType == SymbolType.Function));
Assert.Equal(3, symbolsResult.Count(symbolReference => symbolReference.SymbolType == SymbolType.Variable));
Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Workflow));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Class));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Property));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Constructor));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Method));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Enum));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.EnumMember));
SymbolReference firstFunctionSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Function);
Assert.Equal("AFunction", firstFunctionSymbol.SymbolName);
Assert.Equal(7, firstFunctionSymbol.ScriptRegion.StartLineNumber);
- Assert.Equal(1, firstFunctionSymbol.ScriptRegion.StartColumnNumber);
+ Assert.Equal(10, firstFunctionSymbol.ScriptRegion.StartColumnNumber);
SymbolReference lastVariableSymbol = symbolsResult.Last(r => r.SymbolType == SymbolType.Variable);
Assert.Equal("$Script:ScriptVar2", lastVariableSymbol.SymbolName);
@@ -301,14 +578,102 @@ public void FindsSymbolsInFile()
SymbolReference firstWorkflowSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Workflow);
Assert.Equal("AWorkflow", firstWorkflowSymbol.SymbolName);
Assert.Equal(23, firstWorkflowSymbol.ScriptRegion.StartLineNumber);
- Assert.Equal(1, firstWorkflowSymbol.ScriptRegion.StartColumnNumber);
-
- // TODO: Bring this back when we can use AstVisitor2 again (#276)
- //Assert.Equal(1, symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Configuration).Count());
- //SymbolReference firstConfigurationSymbol = symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Configuration).First();
- //Assert.Equal("AConfiguration", firstConfigurationSymbol.SymbolName);
- //Assert.Equal(25, firstConfigurationSymbol.ScriptRegion.StartLineNumber);
- //Assert.Equal(1, firstConfigurationSymbol.ScriptRegion.StartColumnNumber);
+ Assert.Equal(10, firstWorkflowSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstClassSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Class);
+ Assert.Equal("AClass", firstClassSymbol.SymbolName);
+ Assert.Equal(25, firstClassSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(7, firstClassSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstPropertySymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Property);
+ Assert.Equal("AProperty", firstPropertySymbol.SymbolName);
+ Assert.Equal(26, firstPropertySymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(13, firstPropertySymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstConstructorSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Constructor);
+ Assert.Equal("AClass([string]$AParameter)", firstConstructorSymbol.SymbolName);
+ Assert.Equal(28, firstConstructorSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstConstructorSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstMethodSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Method);
+ Assert.Equal("AMethod([string]$param1, [int]$param2, $param3)", firstMethodSymbol.SymbolName);
+ Assert.Equal(32, firstMethodSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(11, firstMethodSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstEnumSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Enum);
+ Assert.Equal("AEnum", firstEnumSymbol.SymbolName);
+ Assert.Equal(37, firstEnumSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(6, firstEnumSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstEnumMemberSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.EnumMember);
+ Assert.Equal("AValue", firstEnumMemberSymbol.SymbolName);
+ Assert.Equal(38, firstEnumMemberSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstEnumMemberSymbol.ScriptRegion.StartColumnNumber);
+ }
+
+ [Fact]
+ public void FindsSymbolsWithNewLineInFile()
+ {
+ List symbolsResult =
+ FindSymbolsInFile(
+ FindSymbolsInNewLineSymbolFile.SourceDetails);
+
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Function));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Class));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Constructor));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Property));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Method));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Enum));
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.EnumMember));
+
+ SymbolReference firstFunctionSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Function);
+ Assert.Equal("returnTrue", firstFunctionSymbol.SymbolName);
+ Assert.Equal(2, firstFunctionSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(1, firstFunctionSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstClassSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Class);
+ Assert.Equal("NewLineClass", firstClassSymbol.SymbolName);
+ Assert.Equal(7, firstClassSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(1, firstClassSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstConstructorSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Constructor);
+ Assert.Equal("NewLineClass()", firstConstructorSymbol.SymbolName);
+ Assert.Equal(8, firstConstructorSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstConstructorSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstPropertySymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Property);
+ Assert.Equal("SomePropWithDefault", firstPropertySymbol.SymbolName);
+ Assert.Equal(15, firstPropertySymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstPropertySymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstMethodSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Method);
+ Assert.Equal("MyClassMethod([MyNewLineEnum]$param1)", firstMethodSymbol.SymbolName);
+ Assert.Equal(20, firstMethodSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstMethodSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstEnumSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Enum);
+ Assert.Equal("MyNewLineEnum", firstEnumSymbol.SymbolName);
+ Assert.Equal(26, firstEnumSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(1, firstEnumSymbol.ScriptRegion.StartColumnNumber);
+
+ SymbolReference firstEnumMemberSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.EnumMember);
+ Assert.Equal("First", firstEnumMemberSymbol.SymbolName);
+ Assert.Equal(27, firstEnumMemberSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(5, firstEnumMemberSymbol.ScriptRegion.StartColumnNumber);
+ }
+
+ [SkippableFact]
+ public void FindsSymbolsInDSCFile()
+ {
+ Skip.If(!s_isWindows, "DSC only works properly on Windows.");
+
+ List symbolsResult = FindSymbolsInFile(FindSymbolsInDSCFile.SourceDetails);
+
+ Assert.Single(symbolsResult.Where(symbolReference => symbolReference.SymbolType == SymbolType.Configuration));
+ SymbolReference firstConfigurationSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Configuration);
+ Assert.Equal("AConfiguration", firstConfigurationSymbol.SymbolName);
+ Assert.Equal(2, firstConfigurationSymbol.ScriptRegion.StartLineNumber);
+ Assert.Equal(15, firstConfigurationSymbol.ScriptRegion.StartColumnNumber);
}
[Fact]