From 41e26b5cb9d4996304cb6799dd98743285ce895e Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sat, 13 Aug 2022 21:47:26 +0000 Subject: [PATCH 01/18] add symbols for classes --- .../Services/Symbols/SymbolType.cs | 27 +++- .../Symbols/Vistors/FindSymbolsVisitor.cs | 138 +++++++++++++++++- .../Handlers/DocumentSymbolHandler.cs | 16 +- .../Handlers/WorkspaceSymbolsHandler.cs | 25 +++- 4 files changed, 195 insertions(+), 11 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs index 02778b106..207038107 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs @@ -41,6 +41,31 @@ 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 class property + /// + Property, + + /// + /// The symbol is a class method + /// + Method, + + /// + /// The symbol is a class constructor + /// + Constructor } } diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs index 970acb4e7..4d2d3a076 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs @@ -7,12 +7,9 @@ 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; } @@ -26,6 +23,12 @@ internal class FindSymbolsVisitor : AstVisitor /// or a decision to continue if it wasn't found public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { + // 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) { + return AstVisitAction.Continue; + } + IScriptExtent nameExtent = new ScriptExtent() { Text = functionDefinitionAst.Name, @@ -49,7 +52,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun } /// - /// 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, @@ -80,6 +83,129 @@ 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 AST to symbol reference list + /// + public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) + { + IScriptExtent 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 + }; + + 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 + /// + public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) + { + IScriptExtent nameExtent = new ScriptExtent() + { + Text = GetMethodOverloadName(functionMemberAst), + StartLineNumber = functionMemberAst.Extent.StartLineNumber, + EndLineNumber = functionMemberAst.Extent.EndLineNumber, + StartColumnNumber = functionMemberAst.Extent.StartColumnNumber, + EndColumnNumber = functionMemberAst.Extent.EndColumnNumber, + File = functionMemberAst.Extent.File + }; + + SymbolType symbolType = + functionMemberAst.IsConstructor ? + SymbolType.Constructor : SymbolType.Method; + + SymbolReferences.Add( + new SymbolReference( + symbolType, + nameExtent)); + + return AstVisitAction.Continue; + } + + /// + /// Gets the method or constructor name with parameters for current overload. + /// + /// A FunctionMemberAst object in the script's AST + /// Function member name with parameter types and names + private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) { + if (functionMemberAst.Parameters.Count > 0) + { + List parameters = new(functionMemberAst.Parameters.Count); + foreach (ParameterAst param in functionMemberAst.Parameters) + { + parameters.Add(param.Extent.Text); + } + + string paramString = string.Join(", ", parameters); + return string.Concat(functionMemberAst.Name, "(", paramString, ")"); + } + else + { + return string.Concat(functionMemberAst.Name, "()"); + } + } + + /// + /// Adds class property AST to symbol reference list + /// + public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst) + { + IScriptExtent nameExtent = new ScriptExtent() + { + Text = propertyMemberAst.Name, + StartLineNumber = propertyMemberAst.Extent.StartLineNumber, + EndLineNumber = propertyMemberAst.Extent.EndLineNumber, + StartColumnNumber = propertyMemberAst.Extent.StartColumnNumber, + EndColumnNumber = propertyMemberAst.Extent.EndColumnNumber, + File = propertyMemberAst.Extent.File + }; + + SymbolReferences.Add( + new SymbolReference( + SymbolType.Property, + nameExtent)); + + return AstVisitAction.Continue; + } + + /// + /// Adds DSC configuration AST to symbol reference list + /// + 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, + File = configurationDefinitionAst.Extent.File + }; + + 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..01d544f93 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs @@ -128,7 +128,12 @@ 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, _ => SymbolKind.Variable, }; } @@ -137,8 +142,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 } } From 0d6fc671c43b75d31eed825e41abf3269e478179 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sat, 13 Aug 2022 22:07:33 +0000 Subject: [PATCH 02/18] remove code used for supporting PSv3 and v4 --- .../Symbols/ScriptDocumentSymbolProvider.cs | 11 --- .../Services/Symbols/Vistors/AstOperations.cs | 12 --- .../Symbols/Vistors/FindSymbolsVisitor2.cs | 74 ------------------- 3 files changed, 97 deletions(-) delete mode 100644 src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor2.cs 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/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index e14b2051a..6334b48b1 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -228,18 +228,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/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; - // } - //} -} From 4974f0d70c9e7cc8ab7c872d07812057d809b98b Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sun, 14 Aug 2022 16:04:35 +0000 Subject: [PATCH 03/18] support hover --- .../Services/Symbols/SymbolDetails.cs | 26 ++++ .../Symbols/Vistors/FindSymbolVisitor.cs | 143 +++++++++++++++++- 2 files changed, 167 insertions(+), 2 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs index 80a1dd8b4..fa19e0289 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs @@ -82,6 +82,32 @@ 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.Constructor: + // TODO: constructor Class(parameters) + symbolDetails.DisplayString = "constructor " + symbolReference.SymbolName; + return symbolDetails; + + case SymbolType.Method: + // TODO: method ReturnType Class.MethodName(parameters) + symbolDetails.DisplayString = "method " + symbolReference.SymbolName; + return symbolDetails; + + case SymbolType.Property: + symbolDetails.DisplayString = "(property) " + symbolReference.SymbolName; + return symbolDetails; + + case SymbolType.Configuration: + symbolDetails.DisplayString = "configuration " + symbolReference.SymbolName; + return symbolDetails; + default: return symbolDetails; } diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index bf2520a3c..adb1d6b48 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -9,7 +9,7 @@ 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; @@ -116,7 +116,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, @@ -147,5 +147,144 @@ 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) + { + // Show only method/ctor name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + functionMemberAst.Extent.StartColumnNumber + + functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionMemberAst.Name, + StartLineNumber = functionMemberAst.Extent.StartLineNumber, + EndLineNumber = functionMemberAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, + File = functionMemberAst.Extent.File + }; + + 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) + { + // Show only type name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + typeDefinitionAst.Extent.StartColumnNumber + + typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = typeDefinitionAst.Name, + StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, + EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, + File = typeDefinitionAst.Extent.File + }; + + if (IsPositionInExtent(nameExtent)) + { + SymbolType symbolType = + typeDefinitionAst.IsEnum ? + SymbolType.Enum : SymbolType.Class; + + FoundSymbolReference = + new SymbolReference( + symbolType, + 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) + { + string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; + + // Show only configuration name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + configurationDefinitionAst.Extent.StartColumnNumber + + configurationDefinitionAst.Extent.Text.IndexOf(configurationName); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = configurationName, + StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + configurationName.Length, + File = configurationDefinitionAst.Extent.File + }; + + if (IsPositionInExtent(nameExtent)) + { + FoundSymbolReference = + new SymbolReference( + SymbolType.Configuration, + nameExtent); + + return AstVisitAction.StopVisit; + } + + return AstVisitAction.Continue; + } + + /// + /// 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 VisitPropertyMember(PropertyMemberAst propertyMemberAst) + { + if (IsPositionInExtent(propertyMemberAst.Extent)) + { + FoundSymbolReference = + new SymbolReference( + SymbolType.Property, + propertyMemberAst.Extent); + + return AstVisitAction.StopVisit; + } + + return AstVisitAction.Continue; + } } } From 221c1f771da9e497fd4afc7fe145171439f5add6 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Tue, 16 Aug 2022 20:14:58 +0000 Subject: [PATCH 04/18] add type reference symboltype --- .../Services/Symbols/SymbolDetails.cs | 4 ++++ .../Services/Symbols/SymbolType.cs | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs index fa19e0289..fc85acd30 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs @@ -90,6 +90,10 @@ await CommandHelpers.GetCommandSynopsisAsync( symbolDetails.DisplayString = "enum " + symbolReference.SymbolName; return symbolDetails; + case SymbolType.Type: + symbolDetails.DisplayString = "type " + symbolReference.SymbolName; + return symbolDetails; + case SymbolType.Constructor: // TODO: constructor Class(parameters) symbolDetails.DisplayString = "constructor " + symbolReference.SymbolName; diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs index 207038107..84756661c 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs @@ -66,6 +66,11 @@ internal enum SymbolType /// /// The symbol is a class constructor /// - Constructor + Constructor, + + /// + /// The symbol is a type reference + /// + Type, } } From b8f2c05e36e7b0e64b581566b7306f7ad8d89e5c Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Tue, 16 Aug 2022 20:20:54 +0000 Subject: [PATCH 05/18] support references and definitions --- .../Symbols/Vistors/FindDeclarationVisitor.cs | 153 +++++++++++++++- .../Symbols/Vistors/FindReferencesVisitor.cs | 163 +++++++++++++++++- .../Symbols/Vistors/FindSymbolVisitor.cs | 68 ++++++++ 3 files changed, 381 insertions(+), 3 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index 5c3071451..60d82eeac 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -9,7 +9,7 @@ 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; @@ -68,6 +68,157 @@ 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)) + { + // Show only type name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + typeDefinitionAst.Extent.StartColumnNumber + + typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = typeDefinitionAst.Name, + StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, + EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, + File = typeDefinitionAst.Extent.File + }; + + 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) && + functionMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + // Show only method/ctor name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + functionMemberAst.Extent.StartColumnNumber + + functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionMemberAst.Name, + StartLineNumber = functionMemberAst.Extent.StartLineNumber, + EndLineNumber = functionMemberAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, + File = functionMemberAst.Extent.File + }; + + 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 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) + { + if (symbolRef.SymbolType.Equals(SymbolType.Property) && + propertyMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + FoundDeclaration = + new SymbolReference( + SymbolType.Property, + propertyMemberAst.Extent); + + return AstVisitAction.StopVisit; + } + + return AstVisitAction.Continue; + } + + /// + /// Decides if the current configuration definition is the right definition + /// for the symbol being searched for. The definition of the symbol will be a of type + /// SymbolType.Configuration and have the same name as the symbol + /// + /// A ConfigurationDefinitionAst in the script's AST + /// A decision to stop searching if the right ConfigurationDefinitionAst was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) + { + string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; + + if (symbolRef.SymbolType.Equals(SymbolType.Configuration) && + configurationName.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + // Show only configuration name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + configurationDefinitionAst.Extent.StartColumnNumber + + configurationDefinitionAst.Extent.Text.IndexOf(configurationName); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = configurationName, + StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + configurationName.Length, + File = configurationDefinitionAst.Extent.File + }; + + FoundDeclaration = + new SymbolReference( + SymbolType.Configuration, + 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..bbae9ccaf 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -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; @@ -168,5 +168,164 @@ 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)) + { + // Show only type name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + typeDefinitionAst.Extent.StartColumnNumber + + typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = typeDefinitionAst.Name, + StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, + EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, + File = typeDefinitionAst.Extent.File + }; + + 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) && + functionMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + // Show only method/ctor name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + functionMemberAst.Extent.StartColumnNumber + + functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionMemberAst.Name, + StartLineNumber = functionMemberAst.Extent.StartLineNumber, + EndLineNumber = functionMemberAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, + File = functionMemberAst.Extent.File + }; + + 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 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) + { + if (_symbolRef.SymbolType.Equals(SymbolType.Property) && + propertyMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + FoundReferences.Add(new SymbolReference(SymbolType.Property, propertyMemberAst.Extent)); + } + return AstVisitAction.Continue; + } + + /// + /// Decides if the current configuration definition is a reference of the symbol being searched for. + /// A reference of the symbol will be a of type SymbolType.Configuration and have the same name as the symbol + /// + /// A ConfigurationDefinitionAst in the script's AST + /// A visit action that continues the search for references + public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) + { + string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; + + if (_symbolRef.SymbolType.Equals(SymbolType.Configuration) && + configurationName.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + { + // Show only configuration name. Offset by StartColumn to include indentation etc. + int startColumnNumber = + configurationDefinitionAst.Extent.StartColumnNumber + + configurationDefinitionAst.Extent.Text.IndexOf(configurationName); + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = configurationName, + StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + configurationName.Length, + File = configurationDefinitionAst.Extent.File + }; + + FoundReferences.Add(new SymbolReference(SymbolType.Configuration, 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 adb1d6b48..f40b44fd1 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -228,6 +228,74 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit 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. Offset by StartColumn to include indentation etc. + int startColumnNumber = + typeExpressionAst.Extent.StartColumnNumber + + typeExpressionAst.Extent.Text.IndexOf(typeExpressionAst.TypeName.Name); + + 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. Offset by StartColumn to include indentation etc. + int startColumnNumber = + typeConstraintAst.Extent.StartColumnNumber + + typeConstraintAst.Extent.Text.IndexOf(typeConstraintAst.TypeName.Name); + + 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. /// From 5774edc0fd852e7baeac5e8cdc974fd4bc99f971 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Tue, 16 Aug 2022 20:51:45 +0000 Subject: [PATCH 06/18] add codelens for class and enum --- .../CodeLens/ReferencesCodeLensProvider.cs | 9 +++-- .../Services/Symbols/SymbolsService.cs | 6 ++-- .../Services/Symbols/Vistors/AstOperations.cs | 6 ++-- .../Symbols/Vistors/FindSymbolVisitor.cs | 33 ++++++++++++------- 4 files changed, 34 insertions(+), 20 deletions(-) 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/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index f052520f0..336495bd8 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) { diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index 6334b48b1..6fcbd62c9 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -147,19 +147,19 @@ 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. /// SymbolReference of found symbol public static SymbolReference FindSymbolAtPosition( Ast scriptAst, int lineNumber, int columnNumber, - bool includeFunctionDefinitions = false) + bool includeDefinitions = false) { FindSymbolVisitor symbolVisitor = new( lineNumber, columnNumber, - includeFunctionDefinitions); + includeDefinitions); scriptAst.Visit(symbolVisitor); diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index f40b44fd1..41cd2be00 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -13,18 +13,18 @@ internal class FindSymbolVisitor : AstVisitor2 { private readonly int lineNumber; private readonly int columnNumber; - private readonly bool includeFunctionDefinitions; + private readonly bool includeDefinitions; public SymbolReference FoundSymbolReference { get; private set; } public FindSymbolVisitor( int lineNumber, int columnNumber, - bool includeFunctionDefinitions) + bool includeDefinitions) { this.lineNumber = lineNumber; this.columnNumber = columnNumber; - this.includeFunctionDefinitions = includeFunctionDefinitions; + this.includeDefinitions = includeDefinitions; } /// @@ -63,7 +63,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun int endLineNumber = functionDefinitionAst.Extent.EndLineNumber; int endColumnNumber = functionDefinitionAst.Extent.EndColumnNumber; - if (!includeFunctionDefinitions) + if (!includeDefinitions) { // We only want the function name (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst); @@ -196,18 +196,29 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem /// or a decision to continue if it wasn't found public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { - // Show only type name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - typeDefinitionAst.Extent.StartColumnNumber + - typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); + int startLineNumber = typeDefinitionAst.Extent.StartLineNumber; + int startColumnNumber = typeDefinitionAst.Extent.StartColumnNumber; + int endLineNumber = typeDefinitionAst.Extent.EndLineNumber; + int endColumnNumber = typeDefinitionAst.Extent.EndColumnNumber; + + if (!includeDefinitions) + { + // We only want the function name + startColumnNumber = + typeDefinitionAst.Extent.StartColumnNumber + + typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); + startLineNumber = typeDefinitionAst.Extent.StartLineNumber; + endColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length; + endLineNumber = typeDefinitionAst.Extent.StartLineNumber; + } IScriptExtent nameExtent = new ScriptExtent() { Text = typeDefinitionAst.Name, - StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, - EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, + StartLineNumber = startLineNumber, + EndLineNumber = endLineNumber, StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, + EndColumnNumber = endColumnNumber, File = typeDefinitionAst.Extent.File }; From 7bc1d3d5fe490e75bc06798298ceb2b74b21b93c Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Tue, 16 Aug 2022 21:38:09 +0000 Subject: [PATCH 07/18] skip function ast visit for FunctionMemberAst --- .../Services/Symbols/Vistors/FindDeclarationVisitor.cs | 7 +++++++ .../Services/Symbols/Vistors/FindReferencesVisitor.cs | 7 +++++++ .../Services/Symbols/Vistors/FindSymbolVisitor.cs | 7 +++++++ .../Services/Symbols/Vistors/FindSymbolsVisitor.cs | 3 ++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index 60d82eeac..aa587003d 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -36,6 +36,13 @@ public FindDeclarationVisitor(SymbolReference symbolRef) /// or a decision to continue if it wasn't found public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { + // 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) + { + return AstVisitAction.Continue; + } + // 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 = diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index bbae9ccaf..7438482ce 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -117,6 +117,13 @@ public override AstVisitAction VisitCommand(CommandAst commandAst) /// A visit action that continues the search for references public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { + // 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) + { + return AstVisitAction.Continue; + } + (int startColumnNumber, int startLineNumber) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst); IScriptExtent nameExtent = new ScriptExtent() diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index 41cd2be00..71a773c0e 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -58,6 +58,13 @@ public override AstVisitAction VisitCommand(CommandAst commandAst) /// or a decision to continue if it wasn't found public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { + // 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) + { + return AstVisitAction.Continue; + } + int startLineNumber = functionDefinitionAst.Extent.StartLineNumber; int startColumnNumber = functionDefinitionAst.Extent.StartColumnNumber; int endLineNumber = functionDefinitionAst.Extent.EndLineNumber; diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs index 4d2d3a076..d7fe87db1 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs @@ -25,7 +25,8 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun { // 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) { + if (functionDefinitionAst.Parent is FunctionMemberAst) + { return AstVisitAction.Continue; } From ab34105447b590f63994be86492e9d3bfc3cab32 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Wed, 17 Aug 2022 19:05:10 +0000 Subject: [PATCH 08/18] drop reference-support for ConfigurationDefinition --- .../Symbols/Vistors/FindDeclarationVisitor.cs | 41 ------------------- .../Symbols/Vistors/FindReferencesVisitor.cs | 33 --------------- 2 files changed, 74 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index aa587003d..18aafef21 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -185,47 +185,6 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem return AstVisitAction.Continue; } - /// - /// Decides if the current configuration definition is the right definition - /// for the symbol being searched for. The definition of the symbol will be a of type - /// SymbolType.Configuration and have the same name as the symbol - /// - /// A ConfigurationDefinitionAst in the script's AST - /// A decision to stop searching if the right ConfigurationDefinitionAst was found, - /// or a decision to continue if it wasn't found - public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) - { - string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; - - if (symbolRef.SymbolType.Equals(SymbolType.Configuration) && - configurationName.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) - { - // Show only configuration name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - configurationDefinitionAst.Extent.StartColumnNumber + - configurationDefinitionAst.Extent.Text.IndexOf(configurationName); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = configurationName, - StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + configurationName.Length, - File = configurationDefinitionAst.Extent.File - }; - - FoundDeclaration = - new SymbolReference( - SymbolType.Configuration, - 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 7438482ce..858ee8479 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -296,39 +296,6 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem return AstVisitAction.Continue; } - /// - /// Decides if the current configuration definition is a reference of the symbol being searched for. - /// A reference of the symbol will be a of type SymbolType.Configuration and have the same name as the symbol - /// - /// A ConfigurationDefinitionAst in the script's AST - /// A visit action that continues the search for references - public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) - { - string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; - - if (_symbolRef.SymbolType.Equals(SymbolType.Configuration) && - configurationName.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) - { - // Show only configuration name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - configurationDefinitionAst.Extent.StartColumnNumber + - configurationDefinitionAst.Extent.Text.IndexOf(configurationName); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = configurationName, - StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + configurationName.Length, - File = configurationDefinitionAst.Extent.File - }; - - FoundReferences.Add(new SymbolReference(SymbolType.Configuration, nameExtent)); - } - return AstVisitAction.Continue; - } - /// /// Tests if symbol type is a type (class/enum) definition or type reference. /// From 829480d91e84cf6a801d02644481ba9dc23bc554 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Wed, 17 Aug 2022 19:07:04 +0000 Subject: [PATCH 09/18] update document symbols test --- .../Symbols/DSCFile.ps1 | 4 ++ .../Symbols/FindSymbolsInDSCFile.cs | 21 ++++++++ .../Symbols/MultipleSymbols.ps1 | 18 +++++-- .../Language/SymbolsServiceTests.cs | 51 ++++++++++++++++--- 4 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 test/PowerShellEditorServices.Test.Shared/Symbols/DSCFile.ps1 create mode 100644 test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInDSCFile.cs 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/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/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index e7938c287..b6552c0fb 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() { @@ -287,6 +289,11 @@ 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.Equal(2, symbolsResult.Count(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)); SymbolReference firstFunctionSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Function); Assert.Equal("AFunction", firstFunctionSymbol.SymbolName); @@ -303,12 +310,44 @@ public void FindsSymbolsInFile() 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); + SymbolReference firstClassSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Class); + Assert.Equal("AClass", firstClassSymbol.SymbolName); + Assert.Equal(25, firstClassSymbol.ScriptRegion.StartLineNumber); + Assert.Equal(1, 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(5, 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(5, 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(1, firstEnumSymbol.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(1, firstConfigurationSymbol.ScriptRegion.StartColumnNumber); } [Fact] From 1827700e477fbea50122ab704ae0f5638b81d726 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Wed, 17 Aug 2022 23:24:56 +0000 Subject: [PATCH 10/18] refactor and cleanup --- .../Symbols/Vistors/FindDeclarationVisitor.cs | 60 ++------ .../Symbols/Vistors/FindReferencesVisitor.cs | 66 +++------ .../Symbols/Vistors/FindSymbolVisitor.cs | 128 ++++++------------ .../Symbols/Vistors/FindSymbolsVisitor.cs | 95 ++++++------- .../Utility/VisitorUtils.cs | 120 +++++++++++++++- 5 files changed, 230 insertions(+), 239 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index 18aafef21..71fd99905 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -3,6 +3,7 @@ using System; using System.Management.Automation.Language; +using Microsoft.PowerShell.EditorServices.Utility; namespace Microsoft.PowerShell.EditorServices.Services.Symbols { @@ -43,27 +44,15 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun return AstVisitAction.Continue; } - // 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() - { - Text = functionDefinitionAst.Name, - StartLineNumber = functionDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndLineNumber = functionDefinitionAst.Extent.StartLineNumber, - EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length, - File = functionDefinitionAst.Extent.File - }; - // 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, @@ -92,20 +81,8 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit if ((symbolRef.SymbolType is SymbolType.Type || symbolRef.SymbolType.Equals(symbolType)) && typeDefinitionAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - // Show only type name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - typeDefinitionAst.Extent.StartColumnNumber + - typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = typeDefinitionAst.Name, - StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, - EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, - File = typeDefinitionAst.Extent.File - }; + // We only want the type name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst); FoundDeclaration = new SymbolReference( @@ -135,20 +112,8 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem if (symbolRef.SymbolType.Equals(symbolType) && functionMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - // Show only method/ctor name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - functionMemberAst.Extent.StartColumnNumber + - functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = functionMemberAst.Name, - StartLineNumber = functionMemberAst.Extent.StartLineNumber, - EndLineNumber = functionMemberAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, - File = functionMemberAst.Extent.File - }; + // We only want the method/ctor name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); FoundDeclaration = new SymbolReference( @@ -174,10 +139,13 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem if (symbolRef.SymbolType.Equals(SymbolType.Property) && propertyMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { + // We only want the property name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); + FoundDeclaration = new SymbolReference( SymbolType.Property, - propertyMemberAst.Extent); + nameExtent); return AstVisitAction.StopVisit; } diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index 858ee8479..81fca6331 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -113,7 +113,7 @@ 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) { @@ -124,31 +124,21 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun return AstVisitAction.Continue; } - (int startColumnNumber, int startLineNumber) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = functionDefinitionAst.Name, - StartLineNumber = startLineNumber, - EndLineNumber = startLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length, - File = functionDefinitionAst.Extent.File - }; - 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) { @@ -161,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) { @@ -191,21 +181,8 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit if ((_symbolRef.SymbolType is SymbolType.Type || _symbolRef.SymbolType.Equals(symbolType)) && typeDefinitionAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - // Show only type name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - typeDefinitionAst.Extent.StartColumnNumber + - typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = typeDefinitionAst.Name, - StartLineNumber = typeDefinitionAst.Extent.StartLineNumber, - EndLineNumber = typeDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length, - File = typeDefinitionAst.Extent.File - }; - + // 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; @@ -260,21 +237,8 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem if (_symbolRef.SymbolType.Equals(symbolType) && functionMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - // Show only method/ctor name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - functionMemberAst.Extent.StartColumnNumber + - functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = functionMemberAst.Name, - StartLineNumber = functionMemberAst.Extent.StartLineNumber, - EndLineNumber = functionMemberAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, - File = functionMemberAst.Extent.File - }; - + // We only want the method/ctor name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); FoundReferences.Add(new SymbolReference(symbolType, nameExtent)); } return AstVisitAction.Continue; @@ -291,7 +255,9 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem if (_symbolRef.SymbolType.Equals(SymbolType.Property) && propertyMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - FoundReferences.Add(new SymbolReference(SymbolType.Property, propertyMemberAst.Extent)); + // We only want the property name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); + FoundReferences.Add(new SymbolReference(SymbolType.Property, nameExtent)); } return AstVisitAction.Continue; } diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index 71a773c0e..697a92041 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -65,30 +65,25 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun return AstVisitAction.Continue; } - int startLineNumber = functionDefinitionAst.Extent.StartLineNumber; - int startColumnNumber = functionDefinitionAst.Extent.StartColumnNumber; - int endLineNumber = functionDefinitionAst.Extent.EndLineNumber; - int endColumnNumber = functionDefinitionAst.Extent.EndColumnNumber; + IScriptExtent nameExtent; - if (!includeDefinitions) + if (includeDefinitions) { - // We only want the function name - (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst); - startLineNumber = startLine; - startColumnNumber = startColumn; - endLineNumber = startLine; - endColumnNumber = startColumn + functionDefinitionAst.Name.Length; + 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 + }; } - - IScriptExtent nameExtent = new ScriptExtent() + else { - Text = functionDefinitionAst.Name, - StartLineNumber = startLineNumber, - EndLineNumber = endLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = endColumnNumber, - File = functionDefinitionAst.Extent.File - }; + // We only want the function name + nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst); + } if (IsPositionInExtent(nameExtent)) { @@ -163,20 +158,8 @@ private bool IsPositionInExtent(IScriptExtent extent) /// or a decision to continue if it wasn't found public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) { - // Show only method/ctor name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - functionMemberAst.Extent.StartColumnNumber + - functionMemberAst.Extent.Text.IndexOf(functionMemberAst.Name); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = functionMemberAst.Name, - StartLineNumber = functionMemberAst.Extent.StartLineNumber, - EndLineNumber = functionMemberAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + functionMemberAst.Name.Length, - File = functionMemberAst.Extent.File - }; + // We only want the method/ctor name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); if (IsPositionInExtent(nameExtent)) { @@ -203,31 +186,25 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem /// or a decision to continue if it wasn't found public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { - int startLineNumber = typeDefinitionAst.Extent.StartLineNumber; - int startColumnNumber = typeDefinitionAst.Extent.StartColumnNumber; - int endLineNumber = typeDefinitionAst.Extent.EndLineNumber; - int endColumnNumber = typeDefinitionAst.Extent.EndColumnNumber; + IScriptExtent nameExtent; - if (!includeDefinitions) + if (includeDefinitions) { - // We only want the function name - startColumnNumber = - typeDefinitionAst.Extent.StartColumnNumber + - typeDefinitionAst.Extent.Text.IndexOf(typeDefinitionAst.Name); - startLineNumber = typeDefinitionAst.Extent.StartLineNumber; - endColumnNumber = startColumnNumber + typeDefinitionAst.Name.Length; - endLineNumber = typeDefinitionAst.Extent.StartLineNumber; + 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 + }; } - - IScriptExtent nameExtent = new ScriptExtent() + else { - Text = typeDefinitionAst.Name, - StartLineNumber = startLineNumber, - EndLineNumber = endLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = endColumnNumber, - File = typeDefinitionAst.Extent.File - }; + // We only want the type name + nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst); + } if (IsPositionInExtent(nameExtent)) { @@ -254,10 +231,8 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit /// or a decision to continue if it wasn't found public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpressionAst) { - // Show only type name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - typeExpressionAst.Extent.StartColumnNumber + - typeExpressionAst.Extent.Text.IndexOf(typeExpressionAst.TypeName.Name); + // Show only type name (skip leading '['). Offset by StartColumn to include indentation etc. + int startColumnNumber = typeExpressionAst.Extent.StartColumnNumber + 1; IScriptExtent nameExtent = new ScriptExtent() { @@ -288,10 +263,8 @@ public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpress /// or a decision to continue if it wasn't found public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { - // Show only type name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - typeConstraintAst.Extent.StartColumnNumber + - typeConstraintAst.Extent.Text.IndexOf(typeConstraintAst.TypeName.Name); + // Show only type name (skip leading '['). Offset by StartColumn to include indentation etc. + int startColumnNumber = typeConstraintAst.Extent.StartColumnNumber + 1; IScriptExtent nameExtent = new ScriptExtent() { @@ -322,22 +295,8 @@ public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstra /// or a decision to continue if it wasn't found public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { - string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; - - // Show only configuration name. Offset by StartColumn to include indentation etc. - int startColumnNumber = - configurationDefinitionAst.Extent.StartColumnNumber + - configurationDefinitionAst.Extent.Text.IndexOf(configurationName); - - IScriptExtent nameExtent = new ScriptExtent() - { - Text = configurationName, - StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - EndLineNumber = configurationDefinitionAst.Extent.StartLineNumber, - StartColumnNumber = startColumnNumber, - EndColumnNumber = startColumnNumber + configurationName.Length, - File = configurationDefinitionAst.Extent.File - }; + // We only want the configuration name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(configurationDefinitionAst); if (IsPositionInExtent(nameExtent)) { @@ -353,19 +312,22 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit } /// - /// Checks to see if this variable expression is the symbol we are looking for. + /// Checks to see if this property member is the symbol we are looking for. /// - /// A VariableExpressionAst object in the script's AST + /// 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) { - if (IsPositionInExtent(propertyMemberAst.Extent)) + // We only want the property name. Get start-location for name + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); + + if (IsPositionInExtent(nameExtent)) { FoundSymbolReference = new SymbolReference( SymbolType.Property, - propertyMemberAst.Extent); + nameExtent); return AstVisitAction.StopVisit; } diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs index d7fe87db1..b66b6f265 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs @@ -16,11 +16,10 @@ internal class FindSymbolsVisitor : AstVisitor2 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) { // Extent for constructors and method trigger both this and VisitFunctionMember(). Covered in the latter. @@ -30,15 +29,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun return AstVisitAction.Continue; } - IScriptExtent 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 - }; + IScriptExtent nameExtent = GetNewExtent(functionDefinitionAst, functionDefinitionAst.Name); SymbolType symbolType = functionDefinitionAst.IsWorkflow ? @@ -53,11 +44,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)) @@ -86,19 +76,13 @@ private static bool IsAssignedAtScriptScope(VariableExpressionAst variableExpres } /// - /// Adds class and AST to symbol reference list + /// 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) { - IScriptExtent 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 - }; + IScriptExtent nameExtent = GetNewExtent(typeDefinitionAst, typeDefinitionAst.Name); SymbolType symbolType = typeDefinitionAst.IsEnum ? @@ -115,17 +99,11 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit /// /// 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) { - IScriptExtent nameExtent = new ScriptExtent() - { - Text = GetMethodOverloadName(functionMemberAst), - StartLineNumber = functionMemberAst.Extent.StartLineNumber, - EndLineNumber = functionMemberAst.Extent.EndLineNumber, - StartColumnNumber = functionMemberAst.Extent.StartColumnNumber, - EndColumnNumber = functionMemberAst.Extent.EndColumnNumber, - File = functionMemberAst.Extent.File - }; + IScriptExtent nameExtent = GetNewExtent(functionMemberAst, GetMethodOverloadName(functionMemberAst)); SymbolType symbolType = functionMemberAst.IsConstructor ? @@ -144,7 +122,8 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem /// /// A FunctionMemberAst object in the script's AST /// Function member name with parameter types and names - private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) { + private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) + { if (functionMemberAst.Parameters.Count > 0) { List parameters = new(functionMemberAst.Parameters.Count); @@ -165,17 +144,11 @@ private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) /// /// 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) { - IScriptExtent nameExtent = new ScriptExtent() - { - Text = propertyMemberAst.Name, - StartLineNumber = propertyMemberAst.Extent.StartLineNumber, - EndLineNumber = propertyMemberAst.Extent.EndLineNumber, - StartColumnNumber = propertyMemberAst.Extent.StartColumnNumber, - EndColumnNumber = propertyMemberAst.Extent.EndColumnNumber, - File = propertyMemberAst.Extent.File - }; + IScriptExtent nameExtent = GetNewExtent(propertyMemberAst, propertyMemberAst.Name); SymbolReferences.Add( new SymbolReference( @@ -188,17 +161,11 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem /// /// 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) { - 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, - File = configurationDefinitionAst.Extent.File - }; + IScriptExtent nameExtent = GetNewExtent(configurationDefinitionAst, configurationDefinitionAst.InstanceName.Extent.Text); SymbolReferences.Add( new SymbolReference( @@ -207,6 +174,22 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit 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) + { + return new ScriptExtent() + { + Text = text, + StartLineNumber = ast.Extent.StartLineNumber, + EndLineNumber = ast.Extent.EndLineNumber, + StartColumnNumber = ast.Extent.StartColumnNumber, + EndColumnNumber = ast.Extent.EndColumnNumber, + File = ast.Extent.File + }; + } } /// @@ -227,6 +210,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/Utility/VisitorUtils.cs b/src/PowerShellEditorServices/Utility/VisitorUtils.cs index 861d04acc..3456c1920 100644 --- a/src/PowerShellEditorServices/Utility/VisitorUtils.cs +++ b/src/PowerShellEditorServices/Utility/VisitorUtils.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Management.Automation.Language; +using PSESSymbols = Microsoft.PowerShell.EditorServices.Services.Symbols; namespace Microsoft.PowerShell.EditorServices.Utility { @@ -11,15 +12,16 @@ 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 offset specifying where to begin searching in the first line of the AST's extent text + /// A tuple with start column and line of the symbol name + private static (int startColumn, int startLine) GetNameStartColumnAndLineNumbersFromAst(Ast ast, int firstLineColumnOffset) { int startColumnNumber = ast.Extent.StartColumnNumber; int startLineNumber = ast.Extent.StartLineNumber; - int astOffset = ast.IsFilter ? "filter".Length : ast.IsWorkflow ? "workflow".Length : "function".Length; + int astOffset = firstLineColumnOffset; 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. @@ -47,5 +49,113 @@ internal static (int startColumn, int startLine) GetNameStartColumnAndLineNumber return (startColumnNumber + lineOffset, startLineNumber); } + + /// + /// 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 astOffset = functionDefinitionAst.IsFilter ? "filter".Length : functionDefinitionAst.IsWorkflow ? "workflow".Length : "function".Length; + (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst, astOffset); + + 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 astOffset = typeDefinitionAst.IsEnum ? "enum".Length : "class".Length; + (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(typeDefinitionAst, astOffset); + + 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 ScriptExtent with for the symbol name only + internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functionMemberAst) + { + // offset by [type] if return type is specified + int astOffset = functionMemberAst.ReturnType?.Extent.Text.Length ?? 0; + (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(functionMemberAst, astOffset); + + return new PSESSymbols.ScriptExtent() + { + Text = functionMemberAst.Name, + 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 ScriptExtent with for the symbol name only + internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propertyMemberAst) + { + // offset by [type] if type is specified + int astOffset = propertyMemberAst.PropertyType?.Extent.Text.Length ?? 0; + (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(propertyMemberAst, astOffset); + + return new PSESSymbols.ScriptExtent() + { + Text = propertyMemberAst.Name, + StartLineNumber = startLine, + EndLineNumber = startLine, + StartColumnNumber = startColumn, + EndColumnNumber = startColumn + propertyMemberAst.Name.Length + 1, + 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; + const int astOffset = 13; // "configuration".Length + (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(configurationDefinitionAst, astOffset); + + return new PSESSymbols.ScriptExtent() + { + Text = configurationName, + StartLineNumber = startLine, + EndLineNumber = startLine, + StartColumnNumber = startColumn, + EndColumnNumber = startColumn + configurationName.Length, + File = configurationDefinitionAst.Extent.File + }; + } } } From f587eededad7b709d15141aca3f0be7ec4b78473 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sat, 20 Aug 2022 02:50:17 +0000 Subject: [PATCH 11/18] fix Contains extension method for same line extent --- .../Services/Symbols/Vistors/FindSymbolVisitor.cs | 10 ++++++---- src/PowerShellEditorServices/Utility/Extensions.cs | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index 697a92041..8269eb2d9 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation.Language; @@ -85,7 +85,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun nameExtent = VisitorUtils.GetNameExtent(functionDefinitionAst); } - if (IsPositionInExtent(nameExtent)) + if (nameExtent.Contains(lineNumber, columnNumber)) { FoundSymbolReference = new SymbolReference( @@ -139,7 +139,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 @@ -206,7 +208,7 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit nameExtent = VisitorUtils.GetNameExtent(typeDefinitionAst); } - if (IsPositionInExtent(nameExtent)) + if (nameExtent.Contains(lineNumber, columnNumber)) { SymbolType symbolType = typeDefinitionAst.IsEnum ? 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) From 301258672cedc4fa751799a35e09f8b955b28370 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sat, 20 Aug 2022 02:54:35 +0000 Subject: [PATCH 12/18] add EnumMember, signature and fix detection --- .../Services/Symbols/SymbolDetails.cs | 11 +- .../Services/Symbols/SymbolType.cs | 5 + .../Services/Symbols/SymbolsService.cs | 3 +- .../Services/Symbols/Vistors/AstOperations.cs | 7 +- .../Symbols/Vistors/FindDeclarationVisitor.cs | 12 +- .../Symbols/Vistors/FindReferencesVisitor.cs | 13 +- .../Symbols/Vistors/FindSymbolVisitor.cs | 24 ++- .../Symbols/Vistors/FindSymbolsVisitor.cs | 53 ++---- .../Handlers/DocumentSymbolHandler.cs | 1 + .../Utility/VisitorUtils.cs | 179 +++++++++++++++--- 10 files changed, 222 insertions(+), 86 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs index fc85acd30..84d0cef29 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolDetails.cs @@ -95,17 +95,10 @@ await CommandHelpers.GetCommandSynopsisAsync( return symbolDetails; case SymbolType.Constructor: - // TODO: constructor Class(parameters) - symbolDetails.DisplayString = "constructor " + symbolReference.SymbolName; - return symbolDetails; - case SymbolType.Method: - // TODO: method ReturnType Class.MethodName(parameters) - symbolDetails.DisplayString = "method " + symbolReference.SymbolName; - return symbolDetails; - + case SymbolType.EnumMember: case SymbolType.Property: - symbolDetails.DisplayString = "(property) " + symbolReference.SymbolName; + symbolDetails.DisplayString = symbolReference.SymbolName; return symbolDetails; case SymbolType.Configuration: diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs index 84756661c..7f5850f45 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolType.cs @@ -53,6 +53,11 @@ internal enum SymbolType /// Enum, + /// + /// The symbol is a enum member/value + /// + EnumMember, + /// /// The symbol is a class property /// diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 336495bd8..2e22ac1dc 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -332,7 +332,8 @@ public Task FindSymbolDetailsAtLocationAsync( AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, - columnNumber); + columnNumber, + returnMemberSignature: true); if (symbolReference == null) { diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index 6fcbd62c9..623fc02ba 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -148,18 +148,21 @@ await executionService.ExecuteDelegateAsync( /// The line number of the cursor for the given script /// The column number of the cursor for the given script /// Includes full symbol definition ranges in the search. + /// Includes return type and class in symbol name. /// SymbolReference of found symbol public static SymbolReference FindSymbolAtPosition( Ast scriptAst, int lineNumber, int columnNumber, - bool includeDefinitions = false) + bool includeDefinitions = false, + bool returnMemberSignature = false) { FindSymbolVisitor symbolVisitor = new( lineNumber, columnNumber, - includeDefinitions); + includeDefinitions, + returnMemberSignature); scriptAst.Visit(symbolVisitor); diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index 71fd99905..e3d111923 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -110,7 +110,7 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem SymbolType.Constructor : SymbolType.Method; if (symbolRef.SymbolType.Equals(symbolType) && - functionMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + VisitorUtils.GetMemberOverloadName(functionMemberAst).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { // We only want the method/ctor name. Get start-location for name IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); @@ -129,15 +129,19 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem /// /// 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 and have the same name as the symbol + /// 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) { - if (symbolRef.SymbolType.Equals(SymbolType.Property) && - propertyMemberAst.Name.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + SymbolType symbolType = + propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ? + SymbolType.EnumMember : SymbolType.Property; + + if (symbolRef.SymbolType.Equals(symbolType) && + VisitorUtils.GetMemberOverloadName(propertyMemberAst).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { // We only want the property name. Get start-location for name IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index 81fca6331..6bdab55bd 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -235,7 +235,7 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem SymbolType.Constructor : SymbolType.Method; if (_symbolRef.SymbolType.Equals(symbolType) && - functionMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + VisitorUtils.GetMemberOverloadName(functionMemberAst).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { // We only want the method/ctor name. Get start-location for name IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); @@ -246,14 +246,19 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem /// /// 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 and have the same name as the symbol + /// 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) { - if (_symbolRef.SymbolType.Equals(SymbolType.Property) && - propertyMemberAst.Name.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + SymbolType symbolType = + propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ? + SymbolType.EnumMember : SymbolType.Property; + + if (_symbolRef.SymbolType.Equals(symbolType) && + VisitorUtils.GetMemberOverloadName(propertyMemberAst).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { // We only want the property name. Get start-location for name IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index 8269eb2d9..5442fb681 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation.Language; @@ -14,17 +14,20 @@ internal class FindSymbolVisitor : AstVisitor2 private readonly int lineNumber; private readonly int columnNumber; private readonly bool includeDefinitions; + private readonly bool returnMemberSignature; public SymbolReference FoundSymbolReference { get; private set; } public FindSymbolVisitor( int lineNumber, int columnNumber, - bool includeDefinitions) + bool includeDefinitions, + bool returnMemberSignature) { this.lineNumber = lineNumber; this.columnNumber = columnNumber; this.includeDefinitions = includeDefinitions; + this.returnMemberSignature = returnMemberSignature; } /// @@ -161,7 +164,7 @@ private bool IsPositionInExtent(IScriptExtent extent) public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) { // We only want the method/ctor name. Get start-location for name - IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, returnMemberSignature); if (IsPositionInExtent(nameExtent)) { @@ -265,8 +268,11 @@ public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpress /// or a decision to continue if it wasn't found public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { - // Show only type name (skip leading '['). Offset by StartColumn to include indentation etc. - int startColumnNumber = typeConstraintAst.Extent.StartColumnNumber + 1; + // 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() { @@ -322,13 +328,17 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst) { // We only want the property name. Get start-location for name - IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, returnMemberSignature); if (IsPositionInExtent(nameExtent)) { + SymbolType symbolType = + propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ? + SymbolType.EnumMember : SymbolType.Property; + FoundSymbolReference = new SymbolReference( - SymbolType.Property, + symbolType, nameExtent); return AstVisitAction.StopVisit; diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs index b66b6f265..f2b78abe3 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.PowerShell.EditorServices.Utility; using System.Collections.Generic; using System.Management.Automation.Language; @@ -29,7 +30,8 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun return AstVisitAction.Continue; } - IScriptExtent nameExtent = GetNewExtent(functionDefinitionAst, functionDefinitionAst.Name); + (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionDefinitionAst); + IScriptExtent nameExtent = GetNewExtent(functionDefinitionAst, functionDefinitionAst.Name, startLine, startColumn); SymbolType symbolType = functionDefinitionAst.IsWorkflow ? @@ -82,7 +84,8 @@ private static bool IsAssignedAtScriptScope(VariableExpressionAst variableExpres /// A visit action that continues the search for references public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { - IScriptExtent nameExtent = GetNewExtent(typeDefinitionAst, typeDefinitionAst.Name); + (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(typeDefinitionAst); + IScriptExtent nameExtent = GetNewExtent(typeDefinitionAst, typeDefinitionAst.Name, startLine, startColumn); SymbolType symbolType = typeDefinitionAst.IsEnum ? @@ -103,7 +106,8 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit /// A visit action that continues the search for references public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) { - IScriptExtent nameExtent = GetNewExtent(functionMemberAst, GetMethodOverloadName(functionMemberAst)); + (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionMemberAst); + IScriptExtent nameExtent = GetNewExtent(functionMemberAst, VisitorUtils.GetMemberOverloadName(functionMemberAst), startLine, startColumn); SymbolType symbolType = functionMemberAst.IsConstructor ? @@ -117,30 +121,6 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem return AstVisitAction.Continue; } - /// - /// Gets the method or constructor name with parameters for current overload. - /// - /// A FunctionMemberAst object in the script's AST - /// Function member name with parameter types and names - private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) - { - if (functionMemberAst.Parameters.Count > 0) - { - List parameters = new(functionMemberAst.Parameters.Count); - foreach (ParameterAst param in functionMemberAst.Parameters) - { - parameters.Add(param.Extent.Text); - } - - string paramString = string.Join(", ", parameters); - return string.Concat(functionMemberAst.Name, "(", paramString, ")"); - } - else - { - return string.Concat(functionMemberAst.Name, "()"); - } - } - /// /// Adds class property AST to symbol reference list /// @@ -148,11 +128,17 @@ private static string GetMethodOverloadName(FunctionMemberAst functionMemberAst) /// A visit action that continues the search for references public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst) { - IScriptExtent nameExtent = GetNewExtent(propertyMemberAst, propertyMemberAst.Name); + 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.Property, + symbolType, nameExtent)); return AstVisitAction.Continue; @@ -165,7 +151,8 @@ public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMem /// A visit action that continues the search for references public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { - IScriptExtent nameExtent = GetNewExtent(configurationDefinitionAst, configurationDefinitionAst.InstanceName.Extent.Text); + (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(configurationDefinitionAst); + IScriptExtent nameExtent = GetNewExtent(configurationDefinitionAst, configurationDefinitionAst.InstanceName.Extent.Text, startLine, startColumn); SymbolReferences.Add( new SymbolReference( @@ -178,14 +165,14 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit /// /// Gets a new ScriptExtent for a given Ast with same range but modified Text /// - private static ScriptExtent GetNewExtent(Ast ast, string text) + private static ScriptExtent GetNewExtent(Ast ast, string text, int startLine, int startColumn) { return new ScriptExtent() { Text = text, - StartLineNumber = ast.Extent.StartLineNumber, + StartLineNumber = startLine, EndLineNumber = ast.Extent.EndLineNumber, - StartColumnNumber = ast.Extent.StartColumnNumber, + StartColumnNumber = startColumn, EndColumnNumber = ast.Extent.EndColumnNumber, File = ast.Extent.File }; diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs index 01d544f93..5071cc745 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs @@ -134,6 +134,7 @@ private static SymbolKind GetSymbolKind(SymbolType symbolType) SymbolType.Constructor => SymbolKind.Constructor, SymbolType.Method => SymbolKind.Method, SymbolType.Property => SymbolKind.Property, + SymbolType.EnumMember => SymbolKind.EnumMember, _ => SymbolKind.Variable, }; } diff --git a/src/PowerShellEditorServices/Utility/VisitorUtils.cs b/src/PowerShellEditorServices/Utility/VisitorUtils.cs index 3456c1920..3c959f35a 100644 --- a/src/PowerShellEditorServices/Utility/VisitorUtils.cs +++ b/src/PowerShellEditorServices/Utility/VisitorUtils.cs @@ -1,7 +1,10 @@ // 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 @@ -15,39 +18,94 @@ internal static class VisitorUtils /// Calculates the start line and column of the actual symbol name in a AST. /// /// An Ast object in the script's AST - /// An offset specifying where to begin searching in the first line of the AST's extent text + /// 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) GetNameStartColumnAndLineNumbersFromAst(Ast ast, int firstLineColumnOffset) + private static (int startColumn, int startLine) GetNameStartColumnAndLineFromAst(Ast ast, int nameStartIndex) { int startColumnNumber = ast.Extent.StartColumnNumber; int startLineNumber = ast.Extent.StartLineNumber; - int astOffset = firstLineColumnOffset; 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); } /// @@ -57,8 +115,7 @@ private static (int startColumn, int startLine) GetNameStartColumnAndLineNumbers /// A ScriptExtent with for the symbol name only internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionDefinitionAst functionDefinitionAst) { - int astOffset = functionDefinitionAst.IsFilter ? "filter".Length : functionDefinitionAst.IsWorkflow ? "workflow".Length : "function".Length; - (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(functionDefinitionAst, astOffset); + (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(functionDefinitionAst); return new PSESSymbols.ScriptExtent() { @@ -78,8 +135,7 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionDefinitionAst fun /// A ScriptExtent with for the symbol name only internal static PSESSymbols.ScriptExtent GetNameExtent(TypeDefinitionAst typeDefinitionAst) { - int astOffset = typeDefinitionAst.IsEnum ? "enum".Length : "class".Length; - (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(typeDefinitionAst, astOffset); + (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(typeDefinitionAst); return new PSESSymbols.ScriptExtent() { @@ -96,16 +152,15 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(TypeDefinitionAst typeDef /// 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 return type and class should be included /// A ScriptExtent with for the symbol name only - internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functionMemberAst) + internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functionMemberAst, bool includeSignature = false) { - // offset by [type] if return type is specified - int astOffset = functionMemberAst.ReturnType?.Extent.Text.Length ?? 0; - (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(functionMemberAst, astOffset); + (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(functionMemberAst); return new PSESSymbols.ScriptExtent() { - Text = functionMemberAst.Name, + Text = GetMemberOverloadName(functionMemberAst, includeSignature), StartLineNumber = startLine, EndLineNumber = startLine, StartColumnNumber = startColumn, @@ -118,20 +173,25 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functio /// Gets a new ScriptExtent for a given Ast for the property name only /// /// A PropertyMemberAst in the script's AST + /// A bool indicating if property-type and name of class/enum should be included /// A ScriptExtent with for the symbol name only - internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propertyMemberAst) + internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propertyMemberAst, bool includeSignature = false) { - // offset by [type] if type is specified - int astOffset = propertyMemberAst.PropertyType?.Extent.Text.Length ?? 0; - (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(propertyMemberAst, astOffset); + 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 = propertyMemberAst.Name, + Text = GetMemberOverloadName(propertyMemberAst, includeSignature), StartLineNumber = startLine, EndLineNumber = startLine, StartColumnNumber = startColumn, - EndColumnNumber = startColumn + propertyMemberAst.Name.Length + 1, + EndColumnNumber = endColumnNumber, File = propertyMemberAst.Extent.File }; } @@ -144,8 +204,7 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propert internal static PSESSymbols.ScriptExtent GetNameExtent(ConfigurationDefinitionAst configurationDefinitionAst) { string configurationName = configurationDefinitionAst.InstanceName.Extent.Text; - const int astOffset = 13; // "configuration".Length - (int startColumn, int startLine) = GetNameStartColumnAndLineNumbersFromAst(configurationDefinitionAst, astOffset); + (int startColumn, int startLine) = GetNameStartColumnAndLineFromAst(configurationDefinitionAst); return new PSESSymbols.ScriptExtent() { @@ -157,5 +216,73 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(ConfigurationDefinitionAs 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 return type and class should be included + /// Function member name with return type (optional) and parameters + internal static string GetMemberOverloadName(FunctionMemberAst functionMemberAst, bool includeSignature = false) + { + StringBuilder sb = new(); + + // Prepend return type and class. Used for symbol details (hover) + if (includeSignature) + { + if (!functionMemberAst.IsConstructor) + { + sb.Append(functionMemberAst.ReturnType?.TypeName.Name ?? "void").Append(' '); + } + + if (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 property-type and name of class/enum should be included + /// Property name with type (optional) and class/enum + internal static string GetMemberOverloadName(PropertyMemberAst propertyMemberAst, bool includeSignature = false) + { + StringBuilder sb = new(); + + // Prepend return type and class. Used for symbol details (hover) + if (includeSignature && propertyMemberAst.Parent is TypeDefinitionAst typeAst) + { + if (!typeAst.IsEnum) + { + sb.Append(propertyMemberAst.PropertyType?.TypeName.Name ?? "object").Append(' '); + } + + sb.Append(typeAst.Name).Append('.'); + } + + sb.Append(propertyMemberAst.Name); + return sb.ToString(); + } } } From 4c0327455420499d59924d3e2c5cce3a1f7a0333 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sun, 21 Aug 2022 15:00:58 +0000 Subject: [PATCH 13/18] update symbols test with name position --- .../LanguageServerProtocolMessageTests.cs | 6 +++--- .../Language/SymbolsServiceTests.cs | 20 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 0ca4bb2e3..5011d5245 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); }); @@ -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); diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index b6552c0fb..27e0e8893 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -290,15 +290,16 @@ public void FindsSymbolsInFile() 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.Equal(2, symbolsResult.Count(symbolReference => symbolReference.SymbolType == SymbolType.Property)); + 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); @@ -308,17 +309,17 @@ 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); + 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(1, firstClassSymbol.ScriptRegion.StartColumnNumber); + 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(5, firstPropertySymbol.ScriptRegion.StartColumnNumber); + Assert.Equal(13, firstPropertySymbol.ScriptRegion.StartColumnNumber); SymbolReference firstConstructorSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Constructor); Assert.Equal("AClass([string]$AParameter)", firstConstructorSymbol.SymbolName); @@ -328,12 +329,17 @@ public void FindsSymbolsInFile() 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(5, firstMethodSymbol.ScriptRegion.StartColumnNumber); + 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(1, firstEnumSymbol.ScriptRegion.StartColumnNumber); + 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); } [SkippableFact] From b3eb6d69cdb4682028ab686362f5dd5266c56258 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Sun, 21 Aug 2022 16:47:18 +0000 Subject: [PATCH 14/18] check type for property and function members --- .../Services/Symbols/SymbolsService.cs | 2 +- .../Services/Symbols/Vistors/AstOperations.cs | 6 +-- .../Symbols/Vistors/FindDeclarationVisitor.cs | 8 ++-- .../Symbols/Vistors/FindReferencesVisitor.cs | 8 ++-- .../Symbols/Vistors/FindSymbolVisitor.cs | 10 ++--- .../Symbols/Vistors/FindSymbolsVisitor.cs | 2 +- .../Utility/VisitorUtils.cs | 44 ++++++++++--------- 7 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 2e22ac1dc..294cbb986 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -333,7 +333,7 @@ public Task FindSymbolDetailsAtLocationAsync( scriptFile.ScriptAst, lineNumber, columnNumber, - returnMemberSignature: true); + returnFullSignature: true); if (symbolReference == null) { diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index 623fc02ba..e843128d2 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -148,21 +148,21 @@ await executionService.ExecuteDelegateAsync( /// The line number of the cursor for the given script /// The column number of the cursor for the given script /// Includes full symbol definition ranges in the search. - /// Includes return type and class in symbol name. + /// Includes return or property type in symbol name. /// SymbolReference of found symbol public static SymbolReference FindSymbolAtPosition( Ast scriptAst, int lineNumber, int columnNumber, bool includeDefinitions = false, - bool returnMemberSignature = false) + bool returnFullSignature = false) { FindSymbolVisitor symbolVisitor = new( lineNumber, columnNumber, includeDefinitions, - returnMemberSignature); + returnFullSignature); scriptAst.Visit(symbolVisitor); diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs index e3d111923..8c8e1842a 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindDeclarationVisitor.cs @@ -110,10 +110,10 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem SymbolType.Constructor : SymbolType.Method; if (symbolRef.SymbolType.Equals(symbolType) && - VisitorUtils.GetMemberOverloadName(functionMemberAst).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + 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); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, false); FoundDeclaration = new SymbolReference( @@ -141,10 +141,10 @@ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ? SymbolType.EnumMember : SymbolType.Property; if (symbolRef.SymbolType.Equals(symbolType) && - VisitorUtils.GetMemberOverloadName(propertyMemberAst).Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + 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); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, false); FoundDeclaration = new SymbolReference( diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index 6bdab55bd..7f6014a2a 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -235,10 +235,10 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem SymbolType.Constructor : SymbolType.Method; if (_symbolRef.SymbolType.Equals(symbolType) && - VisitorUtils.GetMemberOverloadName(functionMemberAst).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + 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); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, false); FoundReferences.Add(new SymbolReference(symbolType, nameExtent)); } return AstVisitAction.Continue; @@ -258,10 +258,10 @@ propertyMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsEnum ? SymbolType.EnumMember : SymbolType.Property; if (_symbolRef.SymbolType.Equals(symbolType) && - VisitorUtils.GetMemberOverloadName(propertyMemberAst).Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + 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); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, false); FoundReferences.Add(new SymbolReference(SymbolType.Property, nameExtent)); } return AstVisitAction.Continue; diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs index 5442fb681..b45ee03eb 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolVisitor.cs @@ -14,7 +14,7 @@ internal class FindSymbolVisitor : AstVisitor2 private readonly int lineNumber; private readonly int columnNumber; private readonly bool includeDefinitions; - private readonly bool returnMemberSignature; + private readonly bool returnFullSignature; public SymbolReference FoundSymbolReference { get; private set; } @@ -22,12 +22,12 @@ public FindSymbolVisitor( int lineNumber, int columnNumber, bool includeDefinitions, - bool returnMemberSignature) + bool returnFullSignature) { this.lineNumber = lineNumber; this.columnNumber = columnNumber; this.includeDefinitions = includeDefinitions; - this.returnMemberSignature = returnMemberSignature; + this.returnFullSignature = returnFullSignature; } /// @@ -164,7 +164,7 @@ private bool IsPositionInExtent(IScriptExtent extent) public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) { // We only want the method/ctor name. Get start-location for name - IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, returnMemberSignature); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(functionMemberAst, true, returnFullSignature); if (IsPositionInExtent(nameExtent)) { @@ -328,7 +328,7 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst) { // We only want the property name. Get start-location for name - IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, returnMemberSignature); + IScriptExtent nameExtent = VisitorUtils.GetNameExtent(propertyMemberAst, returnFullSignature); if (IsPositionInExtent(nameExtent)) { diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs index f2b78abe3..55c90b4fd 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindSymbolsVisitor.cs @@ -107,7 +107,7 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst) { (int startColumn, int startLine) = VisitorUtils.GetNameStartColumnAndLineFromAst(functionMemberAst); - IScriptExtent nameExtent = GetNewExtent(functionMemberAst, VisitorUtils.GetMemberOverloadName(functionMemberAst), startLine, startColumn); + IScriptExtent nameExtent = GetNewExtent(functionMemberAst, VisitorUtils.GetMemberOverloadName(functionMemberAst, false, false), startLine, startColumn); SymbolType symbolType = functionMemberAst.IsConstructor ? diff --git a/src/PowerShellEditorServices/Utility/VisitorUtils.cs b/src/PowerShellEditorServices/Utility/VisitorUtils.cs index 3c959f35a..3fc3f7ea1 100644 --- a/src/PowerShellEditorServices/Utility/VisitorUtils.cs +++ b/src/PowerShellEditorServices/Utility/VisitorUtils.cs @@ -152,15 +152,16 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(TypeDefinitionAst typeDef /// 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 return type and class should be included + /// 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 includeSignature = false) + 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, includeSignature), + Text = GetMemberOverloadName(functionMemberAst, useQualifiedName, includeReturnType), StartLineNumber = startLine, EndLineNumber = startLine, StartColumnNumber = startColumn, @@ -173,9 +174,9 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(FunctionMemberAst functio /// Gets a new ScriptExtent for a given Ast for the property name only /// /// A PropertyMemberAst in the script's AST - /// A bool indicating if property-type and name of class/enum should be included + /// 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 includeSignature = false) + 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); @@ -187,7 +188,7 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(PropertyMemberAst propert return new PSESSymbols.ScriptExtent() { - Text = GetMemberOverloadName(propertyMemberAst, includeSignature), + Text = GetMemberOverloadName(propertyMemberAst, includePropertyType), StartLineNumber = startLine, EndLineNumber = startLine, StartColumnNumber = startColumn, @@ -221,24 +222,24 @@ internal static PSESSymbols.ScriptExtent GetNameExtent(ConfigurationDefinitionAs /// Gets the method or constructor name with parameters for current overload. /// /// A FunctionMemberAst object in the script's AST - /// A bool indicating if return type and class should be included + /// 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 includeSignature = false) + 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 (includeSignature) + if (includeReturnType && !functionMemberAst.IsConstructor) { - if (!functionMemberAst.IsConstructor) - { - sb.Append(functionMemberAst.ReturnType?.TypeName.Name ?? "void").Append(' '); - } + sb.Append(functionMemberAst.ReturnType?.TypeName.Name ?? "void").Append(' '); + } - if (functionMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsClass) - { - sb.Append(typeAst.Name).Append('.'); - } + if (useQualifiedName && functionMemberAst.Parent is TypeDefinitionAst typeAst && typeAst.IsClass) + { + sb.Append(typeAst.Name).Append('.'); } sb.Append(functionMemberAst.Name); @@ -264,16 +265,17 @@ internal static string GetMemberOverloadName(FunctionMemberAst functionMemberAst /// Gets the property name with type and class/enum. /// /// A PropertyMemberAst object in the script's AST - /// A bool indicating if property-type and name of class/enum should be included + /// 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 includeSignature = false) + internal static string GetMemberOverloadName(PropertyMemberAst propertyMemberAst, + bool includePropertyType = false) { StringBuilder sb = new(); // Prepend return type and class. Used for symbol details (hover) - if (includeSignature && propertyMemberAst.Parent is TypeDefinitionAst typeAst) + if (propertyMemberAst.Parent is TypeDefinitionAst typeAst) { - if (!typeAst.IsEnum) + if (includePropertyType && !typeAst.IsEnum) { sb.Append(propertyMemberAst.PropertyType?.TypeName.Name ?? "object").Append(' '); } From 849bd57aa7f2bdf089c0c9bf46a800c40d1a37db Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Thu, 25 Aug 2022 19:12:04 +0000 Subject: [PATCH 15/18] add symbolsservice tests --- .../Definition/FindsTypeSymbolsDefinition.cs | 90 ++++++ .../FindsOccurrencesOnTypeSymbols.cs | 90 ++++++ .../Occurrences/FindsOccurrencesOnVariable.cs | 20 ++ .../FindsReferencesOnTypeSymbols.cs | 90 ++++++ .../References/TypeAndClassesFile.ps1 | 46 +++ .../FindsDetailsForTypeSymbols.cs | 50 ++++ .../SymbolDetails/TypeSymbolDetails.ps1 | 23 ++ .../Language/SymbolsServiceTests.cs | 269 ++++++++++++++++++ 8 files changed, 678 insertions(+) create mode 100644 test/PowerShellEditorServices.Test.Shared/Definition/FindsTypeSymbolsDefinition.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnTypeSymbols.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnVariable.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnTypeSymbols.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/References/TypeAndClassesFile.ps1 create mode 100644 test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForTypeSymbols.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/SymbolDetails/TypeSymbolDetails.ps1 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/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index 27e0e8893..c3de81c8d 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -225,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() { @@ -253,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() { @@ -279,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() { From a20cf9945b4e404068a635de173a9fa224af4008 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Thu, 25 Aug 2022 19:12:50 +0000 Subject: [PATCH 16/18] add codelens tests --- .../LanguageServerProtocolMessageTests.cs | 106 +++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 5011d5245..d990ebe09 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -841,7 +841,7 @@ public async Task NoMessageIfPesterCodeLensDisabled() } [Fact] - public async Task CanSendReferencesCodeLensRequestAsync() + public async Task CanSendFunctionReferencesCodeLensRequestAsync() { string filePath = NewTestFile(@" function CanSendReferencesCodeLensRequest { @@ -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() { From 2aeca75f5b77adfe8f6c6ab1b79c3a77135d26d7 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Thu, 25 Aug 2022 19:34:02 +0000 Subject: [PATCH 17/18] add symbol tests using newline after keyword --- .../Symbols/FindSymbolsInNewLineSymbolFile.cs | 21 ++++++++ .../Symbols/NewLineSymbols.ps1 | 28 ++++++++++ .../Language/SymbolsServiceTests.cs | 51 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNewLineSymbolFile.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/Symbols/NewLineSymbols.ps1 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/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 c3de81c8d..26754f92b 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -611,6 +611,57 @@ public void FindsSymbolsInFile() 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() { From 1171939a385fd53cb93e7a56420a0f338f0dd7c8 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Thu, 25 Aug 2022 19:45:19 +0000 Subject: [PATCH 18/18] fix DSC symbol test --- .../Language/SymbolsServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index 26754f92b..0f30eccde 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -673,7 +673,7 @@ public void FindsSymbolsInDSCFile() SymbolReference firstConfigurationSymbol = symbolsResult.First(r => r.SymbolType == SymbolType.Configuration); Assert.Equal("AConfiguration", firstConfigurationSymbol.SymbolName); Assert.Equal(2, firstConfigurationSymbol.ScriptRegion.StartLineNumber); - Assert.Equal(1, firstConfigurationSymbol.ScriptRegion.StartColumnNumber); + Assert.Equal(15, firstConfigurationSymbol.ScriptRegion.StartColumnNumber); } [Fact]