OverrideCompletionProvider.vb 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

Imports System.ComponentModel.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.Editor.Implementation.Intellisense.Completion.CompletionProviders
Imports Microsoft.CodeAnalysis.Options

Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion.CompletionProviders
    <ExportCompletionProvider("OverrideCompletionProvider", LanguageNames.VisualBasic)>
    Friend Class OverrideCompletionProvider
        Inherits AbstractOverrideCompletionProvider

        Private isFunction As Boolean
        Private isSub As Boolean
        Private isProperty As Boolean

        <ImportingConstructor()>
        Sub New(waitIndicator As IWaitIndicator)
            MyBase.New(waitIndicator)
        End Sub

        Protected Overrides Function GetSyntax(commonSyntaxToken As SyntaxToken) As SyntaxNode
            Dim token = CType(commonSyntaxToken, SyntaxToken)

            Dim propertyBlock = token.GetAncestor(Of PropertyBlockSyntax)()
            If propertyBlock IsNot Nothing Then
                Return propertyBlock
            End If

            Dim methodBlock = token.GetAncestor(Of MethodBlockBaseSyntax)()
            If methodBlock IsNot Nothing Then
                Return methodBlock
            End If

            Return token.GetAncestor(Of MethodStatementSyntax)()
        End Function

        Protected Overrides Function GetToken(completionItem As MemberInsertionCompletionItem, commonTree As SyntaxTree, cancellationToken As CancellationToken) As SyntaxToken
            Dim token = completionItem.Token
            Dim tree = DirectCast(commonTree, SyntaxTree)
            Return tree.FindTokenOnLeftOfPosition(token.Span.End, cancellationToken)
        End Function


        Public Overrides Function FindStartingToken(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As SyntaxToken
            Dim tree = DirectCast(syntaxTree, SyntaxTree)
            Dim token = tree.FindTokenOnLeftOfPosition(position, cancellationToken)
            Return token.GetPreviousTokenIfTouchingWord(position)
        End Function

        Protected Overrides Function GetTextChangeSpan(text As SourceText, position As Integer) As TextSpan
            Return CompletionUtilities.GetTextChangeSpan(text, position)
        End Function

        Public Overrides Function IsTriggerCharacter(text As SourceText, characterPosition As Integer, options As OptionSet) As Boolean
            Return CompletionUtilities.IsTriggerAfterSpaceOrStartOfWordCharacter(text, characterPosition, options)
        End Function

        Public Overrides Function TryDetermineModifiers(startToken As SyntaxToken,
                                                        text As SourceText, startLine As Integer,
                                                        ByRef seenAccessibility As Accessibility,
                                                        ByRef modifiers As DeclarationModifiers) As Boolean

            Dim token = CType(startToken, SyntaxToken)
            modifiers = New DeclarationModifiers()
            seenAccessibility = Accessibility.NotApplicable
            Dim overridesToken = New SyntaxToken()
            Dim isMustOverride = False
            Dim isNotOverridable = False
            Me.isSub = False
            Me.isFunction = False
            Me.isProperty = False

            Do While IsOnStartLine(token.SpanStart, text, startLine)
                Select Case token.Kind
                    Case SyntaxKind.OverridesKeyword
                        overridesToken = token
                    Case SyntaxKind.MustOverrideKeyword
                        isMustOverride = True
                    Case SyntaxKind.NotOverridableKeyword
                        isNotOverridable = True
                    Case SyntaxKind.FunctionKeyword
                        isFunction = True
                    Case SyntaxKind.PropertyKeyword
                        isProperty = True
                    Case SyntaxKind.SubKeyword
                        isSub = True

                        ' Filter on accessibility by keeping the first one that we see
                    Case SyntaxKind.PublicKeyword
                        If seenAccessibility = Accessibility.NotApplicable Then
                            seenAccessibility = Accessibility.Public
                        End If

                    Case SyntaxKind.FriendKeyword
                        If seenAccessibility = Accessibility.NotApplicable Then
                            seenAccessibility = Accessibility.Internal
                        End If

                        ' If we see Friend AND Protected, assume Friend Protected
                        If seenAccessibility = Accessibility.Protected Then
                            seenAccessibility = Accessibility.ProtectedOrInternal
                        End If

                    Case SyntaxKind.ProtectedKeyword
                        If seenAccessibility = Accessibility.NotApplicable Then
                            seenAccessibility = Accessibility.Protected
                        End If

                        ' If we see Protected and Friend, assume Protected Friend
                        If seenAccessibility = Accessibility.Internal Then
                            seenAccessibility = Accessibility.ProtectedOrInternal
                        End If

                    Case Else
                        ' If we see anything else, give up
                        Return False
                End Select

                Dim previousToken = token.GetPreviousToken()

                ' Consume only modifiers on the same line
                If previousToken.Kind = SyntaxKind.None OrElse Not IsOnStartLine(previousToken.SpanStart, text, startLine) Then
                    Exit Do
                End If

                token = previousToken
            Loop

            modifiers = New DeclarationModifiers(isAbstract:=isMustOverride, isOverride:=True, isSealed:=isNotOverridable)
            Return overridesToken.Kind = SyntaxKind.OverridesKeyword AndAlso IsOnStartLine(overridesToken.Parent.SpanStart, text, startLine)
        End Function

        Public Overrides Function TryDetermineReturnType(startToken As SyntaxToken,
                                                         semanticModel As SemanticModel,
                                                         cancellationToken As CancellationToken,
                                                         ByRef returnType As ITypeSymbol, ByRef nextToken As SyntaxToken) As Boolean
            nextToken = startToken
            returnType = Nothing

            Return True
        End Function

        Public Overrides Function FilterOverrides(members As ISet(Of ISymbol), returnType As ITypeSymbol) As ISet(Of ISymbol)
            ' Start by removing Finalize(), which we never want to show.
            Dim finalizeMethod = members.OfType(Of IMethodSymbol)().Where(Function(x) x.Name = "Finalize" AndAlso OverridesObjectMethod(x)).SingleOrDefault()
            If finalizeMethod IsNot Nothing Then
                members.Remove(finalizeMethod)
            End If

            If Me.isFunction Then
                ' Function: look for non-void return types
                Dim filteredMembers = members.OfType(Of IMethodSymbol)().Where(Function(m) Not m.ReturnsVoid)
                If filteredMembers.Any Then
                    Return New HashSet(Of ISymbol)(filteredMembers)
                End If
            ElseIf Me.isProperty Then
                ' Property: return properties
                Dim filteredMembers = members.Where(Function(m) m.Kind = SymbolKind.Property)
                If filteredMembers.Any Then
                    Return New HashSet(Of ISymbol)(filteredMembers)
                End If
            ElseIf Me.isSub Then
                ' Sub: look for void return types
                Dim filteredMembers = members.OfType(Of IMethodSymbol)().Where(Function(m) m.ReturnsVoid)
                If filteredMembers.Any Then
                    Return New HashSet(Of ISymbol)(filteredMembers)
                End If
            End If

180
            Return members.Where(Function(m) Not m.IsKind(SymbolKind.Event)).ToSet()
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        End Function

        Private Function OverridesObjectMethod(method As IMethodSymbol) As Boolean
            Dim overriddenMember = method
            Do While overriddenMember.OverriddenMethod IsNot Nothing
                overriddenMember = overriddenMember.OverriddenMethod
            Loop

            If overriddenMember.ContainingType.SpecialType = SpecialType.System_Object Then
                Return True
            End If

            Return False
        End Function

        Protected Overrides Function GetTargetCaretPosition(caretTarget As SyntaxNode) As Integer
            Dim node = DirectCast(caretTarget, SyntaxNode)

            ' MustOverride Sub | MustOverride Function: move to end of line
            Dim methodStatement = TryCast(node, MethodStatementSyntax)
            If methodStatement IsNot Nothing Then
                Return methodStatement.GetLocation().SourceSpan.End
            End If

            Dim methodBlock = TryCast(node, MethodBlockBaseSyntax)
            If methodBlock IsNot Nothing Then
                Dim lastStatement = methodBlock.Statements.LastOrDefault()
                If lastStatement IsNot Nothing Then
                    Return lastStatement.GetLocation().SourceSpan.End
                End If
            End If

            Dim propertyBlock = TryCast(node, PropertyBlockSyntax)
            If propertyBlock IsNot Nothing Then
                Dim firstAccessor = propertyBlock.Accessors.FirstOrDefault()
                If firstAccessor IsNot Nothing Then
                    Dim lastAccessorStatement = firstAccessor.Statements.LastOrDefault()
                    If lastAccessorStatement IsNot Nothing Then
                        Return lastAccessorStatement.GetLocation().SourceSpan.End
                    End If
                End If
            End If

            Return -1
        End Function
    End Class
End Namespace