Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add symbols for class-types and configuration #1886

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,17 @@ public ReferencesCodeLensProvider(WorkspaceService workspaceService, SymbolsServ
/// </summary>
/// <param name="scriptFile">The PowerShell script file to get code lenses for.</param>
/// <param name="cancellationToken"></param>
/// <returns>An array of CodeLenses describing all functions in the given script file.</returns>
/// <returns>An array of CodeLenses describing all functions, classes and enums in the given script file.</returns>
public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
{
List<CodeLens> 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
{
Expand Down Expand Up @@ -96,7 +99,7 @@ public async Task<CodeLens> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,6 @@ IEnumerable<ISymbolReference> IDocumentSymbolProvider.ProvideDocumentSymbols(
/// <returns>A collection of SymbolReference objects</returns>
public static IEnumerable<SymbolReference> 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;
Expand Down
23 changes: 23 additions & 0 deletions src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
37 changes: 36 additions & 1 deletion src/PowerShellEditorServices/Services/Symbols/SymbolType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,41 @@ internal enum SymbolType
/// <summary>
/// The symbol is a hashtable key
/// </summary>
HashtableKey
HashtableKey,

/// <summary>
/// The symbol is a class
/// </summary>
Class,

/// <summary>
/// The symbol is a enum
/// </summary>
Enum,

/// <summary>
/// The symbol is a enum member/value
/// </summary>
EnumMember,

/// <summary>
/// The symbol is a class property
/// </summary>
Property,

/// <summary>
/// The symbol is a class method
/// </summary>
Method,

/// <summary>
/// The symbol is a class constructor
/// </summary>
Constructor,

/// <summary>
/// The symbol is a type reference
/// </summary>
Type,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,15 @@ public static IReadOnlyList<SymbolReference> FindOccurrencesInFile(
}

/// <summary>
/// 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
/// </summary>
/// <param name="scriptFile">The details and contents of a open script file</param>
/// <param name="lineNumber">The line number of the cursor for the given script</param>
/// <param name="columnNumber">The column number of the cursor for the given script</param>
/// <returns>A SymbolReference of the symbol found at the given location
/// or null if there is no symbol at that location
/// </returns>
public static SymbolReference FindFunctionDefinitionAtLocation(
public static SymbolReference FindSymbolDefinitionAtLocation(
ScriptFile scriptFile,
int lineNumber,
int columnNumber)
Expand All @@ -306,7 +306,7 @@ public static SymbolReference FindFunctionDefinitionAtLocation(
scriptFile.ScriptAst,
lineNumber,
columnNumber,
includeFunctionDefinitions: true);
includeDefinitions: true);

if (symbolReference != null)
{
Expand All @@ -332,7 +332,8 @@ public Task<SymbolDetails> FindSymbolDetailsAtLocationAsync(
AstOperations.FindSymbolAtPosition(
scriptFile.ScriptAst,
lineNumber,
columnNumber);
columnNumber,
returnFullSignature: true);

if (symbolReference == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,22 @@ await executionService.ExecuteDelegateAsync(
/// <param name="scriptAst">The abstract syntax tree of the given script</param>
/// <param name="lineNumber">The line number of the cursor for the given script</param>
/// <param name="columnNumber">The column number of the cursor for the given script</param>
/// <param name="includeFunctionDefinitions">Includes full function definition ranges in the search.</param>
/// <param name="includeDefinitions">Includes full symbol definition ranges in the search.</param>
/// <param name="returnFullSignature">Includes return or property type in symbol name.</param>
/// <returns>SymbolReference of found symbol</returns>
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);

Expand Down Expand Up @@ -228,18 +231,6 @@ public static SymbolReference FindDefinitionOfSymbol(
/// <returns>A collection of SymbolReference objects</returns>
public static IEnumerable<SymbolReference> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

using System;
using System.Management.Automation.Language;
using Microsoft.PowerShell.EditorServices.Utility;

namespace Microsoft.PowerShell.EditorServices.Services.Symbols
{
/// <summary>
/// The visitor used to find the definition of a symbol
/// </summary>
internal class FindDeclarationVisitor : AstVisitor
internal class FindDeclarationVisitor : AstVisitor2
{
private readonly SymbolReference symbolRef;
private readonly string variableName;
Expand All @@ -36,27 +37,22 @@ public FindDeclarationVisitor(SymbolReference symbolRef)
/// or a decision to continue if it wasn't found</returns>
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
{
// Get the start column number of the function name,
// 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,
Expand All @@ -68,6 +64,99 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
return base.VisitFunctionDefinition(functionDefinitionAst);
}

/// <summary>
/// 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
/// </summary>
/// <param name="typeDefinitionAst">A TypeDefinitionAst in the script's AST</param>
/// <returns>A decision to stop searching if the right TypeDefinitionAst was found,
/// or a decision to continue if it wasn't found</returns>
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;
}

/// <summary>
/// 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
/// </summary>
/// <param name="functionMemberAst">A FunctionMemberAst in the script's AST</param>
/// <returns>A decision to stop searching if the right FunctionMemberAst was found,
/// or a decision to continue if it wasn't found</returns>
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;
}

/// <summary>
/// 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
/// </summary>
/// <param name="propertyMemberAst">A PropertyMemberAst in the script's AST</param>
/// <returns>A decision to stop searching if the right PropertyMemberAst was found,
/// or a decision to continue if it wasn't found</returns>
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;
}

/// <summary>
/// Check if the left hand side of an assignmentStatementAst is a VariableExpressionAst
/// with the same name as that of symbolRef.
Expand Down
Loading