File: QuickInfo\VisualBasicSemanticQuickInfoProvider.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.Collections.Immutable
Imports System.Composition
Imports System.Diagnostics.CodeAnalysis
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.QuickInfo
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators
 
Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo
    <ExportQuickInfoProvider(QuickInfoProviderNames.Semantic, LanguageNames.VisualBasic), [Shared]>
    Friend Class VisualBasicSemanticQuickInfoProvider
        Inherits CommonSemanticQuickInfoProvider
 
        <ImportingConstructor>
        <Obsolete(MefConstruction.ImportingConstructorMessage, True)>
        Public Sub New()
        End Sub
 
        Protected Overrides Async Function BuildQuickInfoAsync(
                context As QuickInfoContext,
                token As SyntaxToken) As Task(Of QuickInfoItem)
            Dim semanticModel = Await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(False)
            Dim services = context.Document.Project.Solution.Services
            Dim info = Await BuildQuickInfoAsync(services, semanticModel, token, context.Options, context.CancellationToken).ConfigureAwait(False)
            If info IsNot Nothing Then
                Return info
            End If
 
            Return Await MyBase.BuildQuickInfoAsync(context, token).ConfigureAwait(False)
        End Function
 
        Protected Overrides Async Function BuildQuickInfoAsync(
                context As CommonQuickInfoContext,
                token As SyntaxToken) As Task(Of QuickInfoItem)
            Dim info = Await BuildQuickInfoAsync(context.Services, context.SemanticModel, token, context.Options, context.CancellationToken).ConfigureAwait(False)
            If info IsNot Nothing Then
                Return info
            End If
 
            Return Await MyBase.BuildQuickInfoAsync(context, token).ConfigureAwait(False)
        End Function
 
        Private Overloads Shared Async Function BuildQuickInfoAsync(
                services As SolutionServices,
                semanticModel As SemanticModel,
                token As SyntaxToken,
                options As SymbolDescriptionOptions,
                cancellationToken As CancellationToken) As Task(Of QuickInfoItem)
 
            Dim parent = token.Parent
 
            Dim predefinedCastExpression = TryCast(parent, PredefinedCastExpressionSyntax)
            If predefinedCastExpression IsNot Nothing AndAlso token = predefinedCastExpression.Keyword Then
                Dim documentation = New PredefinedCastExpressionDocumentation(predefinedCastExpression.Keyword.Kind, semanticModel.Compilation)
                Return BuildContentForIntrinsicOperator(semanticModel, token, parent, documentation, Glyph.MethodPublic, cancellationToken)
            End If
 
            Select Case token.Kind
                Case SyntaxKind.AddHandlerKeyword
                    If TypeOf parent Is AddRemoveHandlerStatementSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New AddHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken)
                    End If
 
                Case SyntaxKind.DimKeyword
                    If TypeOf parent Is FieldDeclarationSyntax Then
                        Return Await BuildContentAsync(services, semanticModel, token, DirectCast(parent, FieldDeclarationSyntax).Declarators, options, cancellationToken).ConfigureAwait(False)
                    ElseIf TypeOf parent Is LocalDeclarationStatementSyntax Then
                        Return Await BuildContentAsync(services, semanticModel, token, DirectCast(parent, LocalDeclarationStatementSyntax).Declarators, options, cancellationToken).ConfigureAwait(False)
                    End If
 
                Case SyntaxKind.CTypeKeyword
                    If TypeOf parent Is CTypeExpressionSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New CTypeCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.DirectCastKeyword
                    If TypeOf parent Is DirectCastExpressionSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New DirectCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.GetTypeKeyword
                    If TypeOf parent Is GetTypeExpressionSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New GetTypeExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.GetXmlNamespaceKeyword
                    If TypeOf parent Is GetXmlNamespaceExpressionSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New GetXmlNamespaceExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.IfKeyword
                    If parent.Kind = SyntaxKind.BinaryConditionalExpression Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New BinaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    ElseIf parent.Kind = SyntaxKind.TernaryConditionalExpression Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New TernaryConditionalExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.RemoveHandlerKeyword
                    If TypeOf parent Is AddRemoveHandlerStatementSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New RemoveHandlerStatementDocumentation(), Glyph.Keyword, cancellationToken)
                    End If
 
                Case SyntaxKind.TryCastKeyword
                    If TypeOf parent Is TryCastExpressionSyntax Then
                        Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New TryCastExpressionDocumentation(), Glyph.MethodPublic, cancellationToken)
                    End If
 
                Case SyntaxKind.IdentifierToken
                    If SyntaxFacts.GetContextualKeywordKind(token.ToString()) = SyntaxKind.MidKeyword Then
                        If parent.Kind = SyntaxKind.MidExpression Then
                            Return BuildContentForIntrinsicOperator(semanticModel, token, parent, New MidAssignmentDocumentation(), Glyph.MethodPublic, cancellationToken)
                        End If
                    End If
            End Select
 
            Return Nothing
        End Function
 
        ''' <summary>
        ''' If the token is a 'Sub' or 'Function' in a lambda, returns the syntax for the whole lambda
        ''' </summary>
        Protected Overrides Function GetBindableNodeForTokenIndicatingLambda(token As SyntaxToken, <Out> ByRef found As SyntaxNode) As Boolean
            If token.IsKind(SyntaxKind.SubKeyword, SyntaxKind.FunctionKeyword) AndAlso token.Parent.IsKind(SyntaxKind.SubLambdaHeader, SyntaxKind.FunctionLambdaHeader) Then
                found = token.Parent.Parent
                Return True
            End If
 
            found = Nothing
            Return False
        End Function
 
        Protected Overrides Function GetBindableNodeForTokenIndicatingPossibleIndexerAccess(token As SyntaxToken, ByRef found As SyntaxNode) As Boolean
            If token.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken) AndAlso
                token.Parent?.Parent.IsKind(SyntaxKind.InvocationExpression) = True Then
                found = token.Parent.Parent
                Return True
            End If
 
            found = Nothing
            Return False
        End Function
 
        Protected Overrides Function GetBindableNodeForTokenIndicatingMemberAccess(token As SyntaxToken, ByRef found As SyntaxToken) As Boolean
            If token.IsKind(SyntaxKind.DotToken) AndAlso
                token.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
 
                found = DirectCast(token.Parent, MemberAccessExpressionSyntax).Name.Identifier
                Return True
            End If
 
            found = Nothing
            Return False
        End Function
 
        Private Overloads Shared Async Function BuildContentAsync(
                services As SolutionServices,
                semanticModel As SemanticModel,
                token As SyntaxToken,
                declarators As SeparatedSyntaxList(Of VariableDeclaratorSyntax),
                options As SymbolDescriptionOptions,
                cancellationToken As CancellationToken) As Task(Of QuickInfoItem)
 
            If declarators.Count = 0 Then
                Return Nothing
            End If
 
            Dim types = declarators.SelectMany(Function(d) d.Names).Select(
                Function(n) As ISymbol
                    Dim symbol = semanticModel.GetDeclaredSymbol(n, cancellationToken)
                    If symbol Is Nothing Then
                        Return Nothing
                    End If
 
                    If TypeOf symbol Is ILocalSymbol Then
                        Return DirectCast(symbol, ILocalSymbol).Type
                    ElseIf TypeOf symbol Is IFieldSymbol Then
                        Return DirectCast(symbol, IFieldSymbol).Type
                    Else
                        Return Nothing
                    End If
                End Function).WhereNotNull().Distinct().ToImmutableArray()
 
            If types.Length = 0 Then
                Return Nothing
            End If
 
            If types.Length > 1 Then
                Return QuickInfoItem.Create(token.Span, sections:=ImmutableArray.Create(QuickInfoSection.Create(QuickInfoSectionKinds.Description, ImmutableArray.Create(New TaggedText(TextTags.Text, VBFeaturesResources.Multiple_Types)))))
            End If
 
            Return Await CreateContentAsync(services, semanticModel, token, New TokenInformation(types), supportedPlatforms:=Nothing, options, onTheFlyDocsInfo:=Nothing, cancellationToken).ConfigureAwait(False)
        End Function
 
        Private Shared Function BuildContentForIntrinsicOperator(
                semanticModel As SemanticModel,
                token As SyntaxToken,
                expression As SyntaxNode,
                documentation As AbstractIntrinsicOperatorDocumentation,
                glyph As Glyph,
                cancellationToken As CancellationToken) As QuickInfoItem
            Dim builder = New List(Of SymbolDisplayPart)
 
            builder.AddRange(documentation.PrefixParts)
 
            Dim position = expression.SpanStart
 
            For i = 0 To documentation.ParameterCount - 1
                If i <> 0 Then
                    builder.AddPunctuation(",")
                    builder.AddSpace()
                End If
 
                Dim typeNameToBind = documentation.TryGetTypeNameParameter(expression, i)
 
                If typeNameToBind IsNot Nothing Then
                    ' We'll try to bind the type name
                    Dim typeInfo = semanticModel.GetTypeInfo(typeNameToBind, cancellationToken)
 
                    If typeInfo.Type IsNot Nothing Then
                        builder.AddRange(typeInfo.Type.ToMinimalDisplayParts(semanticModel, position))
                        Continue For
                    End If
                End If
 
                builder.AddRange(documentation.GetParameterDisplayParts(i))
            Next
 
            builder.AddRange(documentation.GetSuffix(semanticModel, position, expression, cancellationToken))
 
            Return QuickInfoItem.Create(
                token.Span,
                tags:=GlyphTags.GetTags(glyph),
                sections:=ImmutableArray.Create(
                    QuickInfoSection.Create(QuickInfoSectionKinds.Description, builder.ToTaggedText()),
                    QuickInfoSection.Create(QuickInfoSectionKinds.DocumentationComments, ImmutableArray.Create(New TaggedText(TextTags.Text, documentation.DocumentationText)))))
        End Function
    End Class
End Namespace