File: Venus\ContainedLanguageStaticEventBinding.vb
Web Access
Project: src\src\VisualStudio\VisualBasic\Impl\Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj (Microsoft.VisualStudio.LanguageServices.VisualBasic)
' 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.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Venus
 
Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Venus
    Friend Module ContainedLanguageStaticEventBinding
 
        ''' <summary>
        ''' Find all the methods that handle events (though "Handles" clauses).
        ''' </summary>
        ''' <returns></returns>
        Public Function GetStaticEventBindings(document As Document,
                                               className As String,
                                               objectName As String,
                                               cancellationToken As CancellationToken) As IEnumerable(Of Tuple(Of String, String, String))
            Dim type = document.Project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken).GetTypeByMetadataName(className)
            Dim methods = type.GetMembers().
                Where(Function(m) m.CanBeReferencedByName AndAlso m.Kind = SymbolKind.Method).
                Cast(Of IMethodSymbol)()
 
            Dim methodAndMethodSyntaxesWithHandles = methods.
                Select(Function(m) Tuple.Create(m, GetMethodStatement(m))).
                Where(Function(t) t.Item2.HandlesClause IsNot Nothing).
                ToArray()
 
            If Not methodAndMethodSyntaxesWithHandles.Any() Then
                Return SpecializedCollections.EmptyEnumerable(Of Tuple(Of String, String, String))()
            End If
 
            Dim result As New List(Of Tuple(Of String, String, String))()
            For Each methodAndMethodSyntax In methodAndMethodSyntaxesWithHandles
                For Each handleClauseItem In methodAndMethodSyntax.Item2.HandlesClause.Events
                    If handleClauseItem.EventContainer.ToString() = objectName OrElse
                        (String.IsNullOrEmpty(objectName) AndAlso handleClauseItem.EventContainer.IsKind(SyntaxKind.MeKeyword, SyntaxKind.MyBaseKeyword, SyntaxKind.MyClassKeyword)) Then
                        result.Add(Tuple.Create(handleClauseItem.EventMember.Identifier.ToString(),
                                                methodAndMethodSyntax.Item2.Identifier.ToString(),
                                                ContainedLanguageCodeSupport.ConstructMemberId(methodAndMethodSyntax.Item1)))
                    End If
                Next
            Next
 
            Return result
        End Function
 
        Public Sub AddStaticEventBinding(document As Document,
                                         visualStudioWorkspace As VisualStudioWorkspace,
                                         className As String,
                                         memberId As String,
                                         objectName As String,
                                         nameOfEvent As String,
                                         cancellationToken As CancellationToken)
 
            Dim type = document.Project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken).GetTypeByMetadataName(className)
            Dim memberSymbol = ContainedLanguageCodeSupport.LookupMemberId(type, memberId)
            Dim targetDocument = document.Project.Solution.GetDocument(memberSymbol.Locations.First().SourceTree)
 
            If HandlesEvent(GetMethodStatement(memberSymbol), objectName, nameOfEvent) Then
                Return
            End If
 
            Dim textBuffer = targetDocument.GetTextSynchronously(cancellationToken).Container.TryGetTextBuffer()
            If textBuffer Is Nothing Then
                Using visualStudioWorkspace.OpenInvisibleEditor(targetDocument.Id)
                    targetDocument = visualStudioWorkspace.CurrentSolution.GetDocument(targetDocument.Id)
                    AddStaticEventBinding(targetDocument, visualStudioWorkspace, className, memberId, objectName, nameOfEvent, cancellationToken)
                End Using
            Else
                Dim memberStatement = GetMemberBlockOrBegin(memberSymbol)
                Dim codeModel = targetDocument.Project.Services.GetService(Of ICodeModelService)()
                codeModel.AddHandlesClause(targetDocument, objectName & "." & nameOfEvent, memberStatement, cancellationToken)
            End If
        End Sub
 
        Public Sub RemoveStaticEventBinding(document As Document,
                                            visualStudioWorkspace As VisualStudioWorkspace,
                                            className As String,
                                            memberId As String,
                                            objectName As String,
                                            nameOfEvent As String,
                                            cancellationToken As CancellationToken)
            Dim type = document.Project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken).GetTypeByMetadataName(className)
            Dim memberSymbol = ContainedLanguageCodeSupport.LookupMemberId(type, memberId)
            Dim targetDocument = document.Project.Solution.GetDocument(memberSymbol.Locations.First().SourceTree)
 
            If Not HandlesEvent(GetMethodStatement(memberSymbol), objectName, nameOfEvent) Then
                Return
            End If
 
            Dim textBuffer = targetDocument.GetTextSynchronously(cancellationToken).Container.TryGetTextBuffer()
            If textBuffer Is Nothing Then
                Using visualStudioWorkspace.OpenInvisibleEditor(targetDocument.Id)
                    targetDocument = visualStudioWorkspace.CurrentSolution.GetDocument(targetDocument.Id)
                    RemoveStaticEventBinding(targetDocument, visualStudioWorkspace, className, memberId, objectName, nameOfEvent, cancellationToken)
                End Using
            Else
                Dim memberStatement = GetMemberBlockOrBegin(memberSymbol)
                Dim codeModel = targetDocument.Project.Services.GetService(Of ICodeModelService)()
                codeModel.RemoveHandlesClause(targetDocument, objectName & "." & nameOfEvent, memberStatement, cancellationToken)
            End If
 
        End Sub
 
        Public Function HandlesEvent(methodStatement As MethodStatementSyntax, objectName As String, eventName As String) As Boolean
            If methodStatement.HandlesClause Is Nothing Then
                Return False
            End If
 
            For Each handlesClauseItem In methodStatement.HandlesClause.Events
                If handlesClauseItem.EventMember.ToString() = eventName Then
 
                    If String.IsNullOrEmpty(objectName) AndAlso handlesClauseItem.EventContainer.IsKind(SyntaxKind.MeKeyword, SyntaxKind.MyBaseKeyword, SyntaxKind.MyClassKeyword) Then
                        Return True
                    ElseIf handlesClauseItem.EventContainer.ToString() = objectName Then
                        Return True
                    End If
                End If
            Next
 
            Return False
        End Function
 
        Private Function GetMemberBlockOrBegin(member As ISymbol) As SyntaxNode
            Return member.DeclaringSyntaxReferences.Select(Function(r) r.GetSyntax()).FirstOrDefault()
        End Function
 
        Private Function GetMethodStatement(member As ISymbol) As MethodStatementSyntax
            Dim node = GetMemberBlockOrBegin(member)
            If node.Kind = SyntaxKind.SubBlock OrElse node.Kind = SyntaxKind.FunctionBlock Then
                Return DirectCast(DirectCast(node, MethodBlockSyntax).BlockStatement, MethodStatementSyntax)
            ElseIf node.Kind = SyntaxKind.SubStatement OrElse node.Kind = SyntaxKind.FunctionStatement Then
                Return DirectCast(node, MethodStatementSyntax)
            Else
                Throw New InvalidOperationException()
            End If
        End Function
    End Module
End Namespace