File: SolutionExplorer\VisualBasicSolutionExplorerSymbolTreeItemProvider.vb
Web Access
Project: src\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Composition
Imports System.Text
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.FindSymbols
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.SolutionExplorer
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.SolutionExplorer
    <ExportLanguageService(GetType(ISolutionExplorerSymbolTreeItemProvider), LanguageNames.VisualBasic), [Shared]>
    Friend NotInheritable Class VisualBasicSolutionExplorerSymbolTreeItemProvider
        Inherits AbstractSolutionExplorerSymbolTreeItemProvider(Of
            CompilationUnitSyntax,
            StatementSyntax,
            NamespaceBlockSyntax,
            EnumBlockSyntax,
            TypeBlockSyntax)
 
        <ImportingConstructor>
        <Obsolete(MefConstruction.ImportingConstructorMessage, True)>
        Public Sub New()
        End Sub
 
        Protected Overrides Function GetMembers(root As CompilationUnitSyntax) As SyntaxList(Of StatementSyntax)
            Return root.Members
        End Function
 
        Protected Overrides Function GetMembers(baseNamespace As NamespaceBlockSyntax) As SyntaxList(Of StatementSyntax)
            Return baseNamespace.Members
        End Function
 
        Protected Overrides Function GetMembers(typeDeclaration As TypeBlockSyntax) As SyntaxList(Of StatementSyntax)
            Return typeDeclaration.Members
        End Function
 
        Protected Overrides Function TryAddType(documentId As DocumentId, member As StatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder) As Boolean
            Dim typeBlock = TryCast(member, TypeBlockSyntax)
            If typeBlock IsNot Nothing Then
                AddTypeBlock(documentId, typeBlock, items, nameBuilder)
                Return True
            End If
 
            Dim enumBlock = TryCast(member, EnumBlockSyntax)
            If enumBlock IsNot Nothing Then
                AddEnumBlock(documentId, enumBlock, items)
                Return True
            End If
 
            Dim delegateStatement = TryCast(member, DelegateStatementSyntax)
            If delegateStatement IsNot Nothing Then
                AddDelegateStatement(documentId, delegateStatement, items, nameBuilder)
                Return True
            End If
 
            Return False
        End Function
 
        Private Shared Sub AddTypeBlock(documentId As DocumentId, typeBlock As TypeBlockSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            Dim blockStatement As TypeStatementSyntax = typeBlock.BlockStatement
 
            nameBuilder.Append(blockStatement.Identifier.ValueText)
            AppendTypeParameterList(nameBuilder, blockStatement.TypeParameterList)
 
            Dim kind = GetDeclaredSymbolInfoKind(typeBlock)
            Dim accessibility = GetAccessibility(typeBlock.Parent, blockStatement, blockStatement.Modifiers)
 
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(kind, accessibility),
                hasItems:=typeBlock.Members.Count > 0,
                typeBlock,
                blockStatement.Identifier))
        End Sub
 
        Private Shared Sub AddEnumBlock(documentId As DocumentId, enumBlock As EnumBlockSyntax, items As ArrayBuilder(Of SymbolTreeItemData))
            Dim accessibility = GetAccessibility(enumBlock.Parent, enumBlock.EnumStatement, enumBlock.EnumStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                enumBlock.EnumStatement.Identifier.ValueText,
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Enum, accessibility),
                hasItems:=enumBlock.Members.Count > 0,
                enumBlock,
                enumBlock.EnumStatement.Identifier))
        End Sub
 
        Private Shared Sub AddDelegateStatement(documentId As DocumentId, delegateStatement As DelegateStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append(delegateStatement.Identifier.ValueText)
            AppendTypeParameterList(nameBuilder, delegateStatement.TypeParameterList)
            AppendParameterList(nameBuilder, delegateStatement.ParameterList)
            AppendAsClause(nameBuilder, delegateStatement.AsClause)
 
            Dim accessibility = GetAccessibility(delegateStatement.Parent, delegateStatement, delegateStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Delegate, accessibility),
                hasItems:=False,
                delegateStatement,
                delegateStatement.Identifier))
        End Sub
 
        Protected Overrides Sub AddEnumDeclarationMembers(documentId As DocumentId, enumDeclaration As EnumBlockSyntax, items As ArrayBuilder(Of SymbolTreeItemData), cancellationToken As CancellationToken)
            For Each member In enumDeclaration.Members
                Dim enumMember = TryCast(member, EnumMemberDeclarationSyntax)
                items.Add(New SymbolTreeItemData(
                    documentId,
                    enumMember.Identifier.ValueText,
                    Glyph.EnumMemberPublic,
                    hasItems:=False,
                    enumMember,
                    enumMember.Identifier))
            Next
        End Sub
 
        Protected Overrides Sub AddMemberDeclaration(documentId As DocumentId, member As StatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            Dim container = member.Parent
            Dim methodStatement = If(TryCast(member, MethodStatementSyntax), TryCast(member, MethodBlockSyntax)?.SubOrFunctionStatement)
            If methodStatement IsNot Nothing Then
                AddMethodStatement(documentId, container, methodStatement, items, nameBuilder)
                Return
            End If
 
            Dim constructorStatement = If(TryCast(member, SubNewStatementSyntax), TryCast(member, ConstructorBlockSyntax)?.SubNewStatement)
            If constructorStatement IsNot Nothing Then
                AddConstructorStatement(documentId, container, constructorStatement, items, nameBuilder)
                Return
            End If
 
            Dim operatorStatement = If(TryCast(member, OperatorStatementSyntax), TryCast(member, OperatorBlockSyntax)?.OperatorStatement)
            If operatorStatement IsNot Nothing Then
                AddOperatorStatement(documentId, container, operatorStatement, items, nameBuilder)
                Return
            End If
 
            Dim propertystatement = If(TryCast(member, PropertyStatementSyntax), TryCast(member, PropertyBlockSyntax)?.PropertyStatement)
            If propertystatement IsNot Nothing Then
                AddPropertyStatement(documentId, container, propertystatement, items, nameBuilder)
                Return
            End If
 
            Dim eventStatement = If(TryCast(member, EventStatementSyntax), TryCast(member, EventBlockSyntax)?.EventStatement)
            If eventStatement IsNot Nothing Then
                AddEventStatement(documentId, container, eventStatement, items, nameBuilder)
                Return
            End If
 
            Dim fieldDeclaration = TryCast(member, FieldDeclarationSyntax)
            If fieldDeclaration IsNot Nothing Then
                AddFieldDeclaration(documentId, container, fieldDeclaration, items, nameBuilder)
                Return
            End If
        End Sub
 
        Private Shared Sub AddMethodStatement(documentId As DocumentId, container As SyntaxNode, methodStatement As MethodStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append(methodStatement.Identifier.ValueText)
            AppendTypeParameterList(nameBuilder, methodStatement.TypeParameterList)
            AppendParameterList(nameBuilder, methodStatement.ParameterList)
            AppendAsClause(nameBuilder, methodStatement.AsClause)
 
            Dim isExtension = container.IsKind(SyntaxKind.ModuleBlock) AndAlso methodStatement.AttributeLists.Any(
                Function(list) list.Attributes.Any(
                    Function(attribute)
                        Dim name = attribute.Name.GetRightmostName().Identifier.ValueText
                        Return name = "Extension" OrElse name = "ExtensionAttribute"
                    End Function))
 
            Dim accesibility = GetAccessibility(container, methodStatement, methodStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(If(isExtension, DeclaredSymbolInfoKind.ExtensionMethod, DeclaredSymbolInfoKind.Method), accesibility),
                hasItems:=False,
                methodStatement,
                methodStatement.Identifier))
        End Sub
 
        Private Shared Sub AddConstructorStatement(documentId As DocumentId, container As SyntaxNode, constructorStatement As SubNewStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append("New")
            AppendParameterList(nameBuilder, constructorStatement.ParameterList)
 
            Dim accesibility = GetAccessibility(container, constructorStatement, constructorStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Constructor, accesibility),
                hasItems:=False,
                constructorStatement,
                constructorStatement.NewKeyword))
        End Sub
 
        Private Shared Sub AddOperatorStatement(documentId As DocumentId, container As SyntaxNode, operatorStatement As OperatorStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append("Operator ")
            nameBuilder.Append(operatorStatement.OperatorToken.ToString())
            AppendParameterList(nameBuilder, operatorStatement.ParameterList)
            AppendAsClause(nameBuilder, operatorStatement.AsClause, fallbackToObject:=True)
 
            Dim accesibility = GetAccessibility(container, operatorStatement, operatorStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Operator, accesibility),
                hasItems:=False,
                operatorStatement,
                operatorStatement.OperatorToken))
        End Sub
 
        Private Shared Sub AddPropertyStatement(documentId As DocumentId, container As SyntaxNode, propertystatement As PropertyStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append(propertystatement.Identifier.ValueText)
            AppendParameterList(nameBuilder, propertystatement.ParameterList)
            AppendAsClause(nameBuilder, propertystatement.AsClause, fallbackToObject:=True)
 
            Dim accesibility = GetAccessibility(container, propertystatement, propertystatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Property, accesibility),
                hasItems:=False,
                propertystatement,
                propertystatement.Identifier))
        End Sub
 
        Private Shared Sub AddEventStatement(documentId As DocumentId, container As SyntaxNode, eventStatement As EventStatementSyntax, items As ArrayBuilder(Of SymbolTreeItemData), nameBuilder As StringBuilder)
            nameBuilder.Append(eventStatement.Identifier.ValueText)
            AppendAsClause(nameBuilder, eventStatement.AsClause)
 
            Dim accesibility = GetAccessibility(container, eventStatement, eventStatement.Modifiers)
            items.Add(New SymbolTreeItemData(
                documentId,
                nameBuilder.ToStringAndClear(),
                GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Event, accesibility),
                hasItems:=False,
                eventStatement,
                eventStatement.Identifier))
        End Sub
 
        Private Shared Sub AddFieldDeclaration(
                documentId As DocumentId,
                container As SyntaxNode,
                fieldDeclaration As FieldDeclarationSyntax,
                items As ArrayBuilder(Of SymbolTreeItemData),
                nameBuilder As StringBuilder)
            For Each declarator In fieldDeclaration.Declarators
                For Each name In declarator.Names
                    nameBuilder.Append(name.Identifier.ValueText)
                    AppendAsClause(nameBuilder, declarator.AsClause, fallbackToObject:=True)
 
                    Dim accesibility = GetAccessibility(container, fieldDeclaration, fieldDeclaration.Modifiers)
                    items.Add(New SymbolTreeItemData(
                        documentId,
                        nameBuilder.ToStringAndClear(),
                        GlyphExtensions.GetGlyph(DeclaredSymbolInfoKind.Field, accesibility),
                        hasItems:=False,
                        fieldDeclaration,
                        name.Identifier))
                Next
            Next
        End Sub
 
        Private Shared Sub AppendTypeParameterList(
            builder As StringBuilder,
            typeParameterList As TypeParameterListSyntax)
 
            AppendCommaSeparatedList(
            builder, "(Of ", ")", typeParameterList,
            Function(list) list.Parameters,
            Sub(parameter, innherBuilder) innherBuilder.Append(parameter.Identifier.ValueText))
        End Sub
 
        Private Shared Sub AppendParameterList(
            builder As StringBuilder,
            parameterList As ParameterListSyntax)
 
            AppendCommaSeparatedList(
                builder, "(", ")", parameterList,
                Function(list) list.Parameters,
                Sub(parameter, innerBuilder) AppendType(parameter?.AsClause?.Type, builder))
        End Sub
 
        Private Shared Sub AppendAsClause(
                nameBuilder As StringBuilder,
                asClause As AsClauseSyntax,
                Optional fallbackToObject As Boolean = False)
            If asClause IsNot Nothing Then
 
                Dim simpleAsClause = TryCast(asClause, SimpleAsClauseSyntax)
                If simpleAsClause IsNot Nothing Then
                    nameBuilder.Append(" As ")
                    AppendType(simpleAsClause.Type, nameBuilder)
                    Return
                End If
 
                Dim asNewClause = TryCast(asClause, AsNewClauseSyntax)
                If asNewClause IsNot Nothing Then
                    Dim newObjectCreation = TryCast(asNewClause.NewExpression, ObjectCreationExpressionSyntax)
                    If newObjectCreation IsNot Nothing Then
                        nameBuilder.Append(" As ")
                        AppendType(newObjectCreation.Type, nameBuilder)
                        Return
                    End If
 
                    Dim newArrayCreation = TryCast(asNewClause.NewExpression, ArrayCreationExpressionSyntax)
                    If newArrayCreation IsNot Nothing Then
                        nameBuilder.Append(" As ")
                        AppendType(newArrayCreation.Type, nameBuilder)
                        nameBuilder.Append("()")
                        Return
                    End If
                End If
            ElseIf fallbackToObject Then
                nameBuilder.Append(" As Object")
            End If
        End Sub
 
        Private Shared Sub AppendType(typeSyntax As TypeSyntax, builder As StringBuilder)
            If typeSyntax Is Nothing Then
                Return
            End If
 
            Dim tupleType = TryCast(typeSyntax, TupleTypeSyntax)
            If tupleType IsNot Nothing Then
                AppendCommaSeparatedList(
                    builder, "(", ")", tupleType.Elements,
                    Sub(element, innerBuilder) AppendTupleElement(element, innerBuilder))
                Return
            End If
 
            Dim arrayType = TryCast(typeSyntax, ArrayTypeSyntax)
            If arrayType IsNot Nothing Then
                AppendType(arrayType.ElementType, builder)
                For Each rankSpecifier In arrayType.RankSpecifiers
                    builder.Append("("c)
                    builder.Append(","c, rankSpecifier.CommaTokens.Count)
                    builder.Append(")"c)
                Next
 
                Return
            End If
 
            Dim nullableType = TryCast(typeSyntax, NullableTypeSyntax)
            If nullableType IsNot Nothing Then
                AppendType(nullableType.ElementType, builder)
                builder.Append("?"c)
                Return
            End If
 
            Dim predefinedType = TryCast(typeSyntax, PredefinedTypeSyntax)
            If predefinedType IsNot Nothing Then
                builder.Append(predefinedType.ToString())
                Return
            End If
 
            Dim identifierName = TryCast(typeSyntax, IdentifierNameSyntax)
            If identifierName IsNot Nothing Then
                builder.Append(identifierName.Identifier.ValueText)
                Return
            End If
 
            Dim genericName = TryCast(typeSyntax, GenericNameSyntax)
            If genericName IsNot Nothing Then
                builder.Append(genericName.Identifier.ValueText)
                AppendCommaSeparatedList(
                    builder, "(Of ", ")", genericName.TypeArgumentList.Arguments,
                    Sub(typeArgument, innerBuilder) AppendType(typeArgument, innerBuilder))
                Return
            End If
 
            Dim qualifiedName = TryCast(typeSyntax, QualifiedNameSyntax)
            If qualifiedName IsNot Nothing Then
                AppendType(qualifiedName.Right, builder)
                Return
            End If
 
            Debug.Fail("Unhandled type: " + typeSyntax.GetType().FullName)
        End Sub
 
        Private Shared Sub AppendTupleElement(element As TupleElementSyntax, builder As StringBuilder)
            Dim typedTupleElement = TryCast(element, TypedTupleElementSyntax)
            If typedTupleElement IsNot Nothing Then
                AppendType(typedTupleElement.Type, builder)
                Return
            End If
 
            Dim namedTupleElement = TryCast(element, NamedTupleElementSyntax)
            If namedTupleElement IsNot Nothing Then
                builder.Append(namedTupleElement.Identifier.ValueText)
                AppendAsClause(builder, namedTupleElement.AsClause)
                Return
            End If
 
            Debug.Fail("Unhandled type: " + element.GetType().FullName)
 
        End Sub
    End Class
End Namespace