File: CodeLens\VisualBasicDisplayInfoService.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.Globalization
Imports Microsoft.CodeAnalysis.CodeLens
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeLens
    <ExportLanguageService(GetType(ICodeLensDisplayInfoService), LanguageNames.VisualBasic), [Shared]>
    Friend NotInheritable Class VisualBasicDisplayInfoService
        Implements ICodeLensDisplayInfoService
 
        Private Shared ReadOnly Format As SymbolDisplayFormat = New SymbolDisplayFormat(
                SymbolDisplayGlobalNamespaceStyle.Omitted,                                  ' Don't prepend VB namespaces with "Global."
                SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,    ' Show fully qualified names
                SymbolDisplayGenericsOptions.IncludeTypeParameters,
                SymbolDisplayMemberOptions.IncludeContainingType Or SymbolDisplayMemberOptions.IncludeParameters,
                SymbolDisplayDelegateStyle.NameOnly,
                SymbolDisplayExtensionMethodStyle.StaticMethod,
                SymbolDisplayParameterOptions.IncludeType,
                SymbolDisplayPropertyStyle.ShowReadWriteDescriptor,
                SymbolDisplayLocalOptions.IncludeType,
                SymbolDisplayKindOptions.None,
                SymbolDisplayMiscellaneousOptions.UseSpecialTypes)
 
        <ImportingConstructor>
        <Obsolete(MefConstruction.ImportingConstructorMessage, True)>
        Public Sub New()
        End Sub
 
        ''' <summary>
        ''' Returns the node that should be displayed
        ''' </summary>
        Public Function GetDisplayNode(node As SyntaxNode) As SyntaxNode Implements ICodeLensDisplayInfoService.GetDisplayNode
            Select Case node.Kind()
                ' A variable declarator can contain multiple symbols, for example "Private field2, field3 As Integer"
                ' In that case default to the first field name.
                Case SyntaxKind.VariableDeclarator
                    Dim variableNode = CType(node, VariableDeclaratorSyntax)
                    Return GetDisplayNode(variableNode.Names.First())
 
                ' A field declaration (global variable) can contain multiple symbols, for example "Private field2, field3 As Integer"
                ' In that case default to the first field name.
                Case SyntaxKind.FieldDeclaration
                    Dim fieldNode = CType(node, FieldDeclarationSyntax)
                    Return GetDisplayNode(fieldNode.Declarators.First())
 
                Case SyntaxKind.PredefinedType
                    Return GetDisplayNode(node.Parent)
 
                Case SyntaxKind.DocumentationCommentTrivia
                    If node.IsStructuredTrivia Then
                        Dim structuredTriviaSyntax = CType(node, StructuredTriviaSyntax)
                        Return GetDisplayNode(structuredTriviaSyntax.ParentTrivia.Token.Parent)
                    Else
                        Return node
                    End If
            End Select
 
            Return node
        End Function
 
        Private Shared Function SymbolToDisplayString(symbol As ISymbol) As String
            Return If(symbol Is Nothing, FeaturesResources.paren_Unknown_paren, symbol.ToDisplayString(Format))
        End Function
 
        Private Shared Function FormatPropertyAccessor(node As SyntaxNode, symbolName As String) As String
            Dim symbolNameWithNoParams As String = RemoveParameters(symbolName)
            If node.IsKind(SyntaxKind.GetAccessorBlock) Then
                symbolName = String.Format(CultureInfo.CurrentCulture, VBFeaturesResources.Property_getter_name, symbolNameWithNoParams)
            Else
                Debug.Assert(node.IsKind(SyntaxKind.SetAccessorBlock))
 
                symbolName = String.Format(CultureInfo.CurrentCulture, VBFeaturesResources.Property_setter_name, symbolNameWithNoParams)
            End If
 
            Return symbolName
        End Function
 
        Private Shared Function FormatEventHandler(node As SyntaxNode, symbolName As String) As String
            ' symbol name looks Like this at this point : Namespace.Class.Event(EventHandler)
            Dim symbolNameWithNoParams As String = RemoveParameters(symbolName)
            If node.IsKind(SyntaxKind.AddHandlerAccessorBlock) Then
                symbolName = String.Format(CultureInfo.CurrentCulture, VBFeaturesResources.Event_add_handler_name, symbolNameWithNoParams)
            Else
                Debug.Assert(node.IsKind(SyntaxKind.RemoveHandlerAccessorBlock))
 
                symbolName = String.Format(CultureInfo.CurrentCulture, VBFeaturesResources.Event_remove_handler_name, symbolNameWithNoParams)
            End If
 
            Return symbolName
        End Function
 
        Private Shared Function IsAccessorForDefaultProperty(symbol As ISymbol) As Boolean
            Dim methodSymbol = TryCast(symbol, IMethodSymbol) ' its really a SourcePropertyAccessorSymbol but it Is Not accessible 
            If methodSymbol IsNot Nothing Then
                Dim propertySymbol = TryCast(methodSymbol.AssociatedSymbol, IPropertySymbol)
                If propertySymbol IsNot Nothing Then
                    ' Applying the default modifier to a property allows it to be used Like a C# indexer
                    Return propertySymbol.IsDefault()
                End If
            End If
 
            Return False
        End Function
 
        Private Shared Function RemoveParameters(symbolName As String) As String
            Dim openParenIndex As Integer = symbolName.IndexOf("("c)
            Dim symbolNameWithNoParams As String = symbolName.Substring(0, openParenIndex)
            Return symbolNameWithNoParams
        End Function
 
        ''' <summary>
        ''' Gets the DisplayName for the given node.
        ''' </summary>
        Public Function GetDisplayName(semanticModel As SemanticModel, node As SyntaxNode) As String Implements ICodeLensDisplayInfoService.GetDisplayName
            If VisualBasicSyntaxFacts.Instance.IsGlobalAssemblyAttribute(node) Then
                Return node.ToString()
            End If
 
            Dim symbol As ISymbol = semanticModel.GetDeclaredSymbol(node)
            Dim symbolName As String
 
            Select Case node.Kind()
                Case SyntaxKind.GetAccessorBlock,
                     SyntaxKind.SetAccessorBlock
                    ' Indexer properties should Not include get And set
                    symbol = semanticModel.GetDeclaredSymbol(node)
                    If IsAccessorForDefaultProperty(symbol) AndAlso node.Parent.IsKind(SyntaxKind.PropertyBlock) Then
                        Return GetDisplayName(semanticModel, node.Parent)
                    Else
                        ' Append "get" Or "set" to property accessors
                        symbolName = SymbolToDisplayString(symbol)
                        symbolName = FormatPropertyAccessor(node, symbolName)
                    End If
 
                Case SyntaxKind.AddHandlerAccessorBlock,
                     SyntaxKind.RemoveHandlerAccessorBlock
                    ' Append "add" Or "remove" to event handlers
                    symbolName = SymbolToDisplayString(symbol)
                    symbolName = FormatEventHandler(node, symbolName)
 
                Case SyntaxKind.ImportsStatement
                    symbolName = "Imports"
 
                Case Else
                    symbolName = SymbolToDisplayString(symbol)
            End Select
 
            Return symbolName
        End Function
    End Class
End Namespace