File: NavigationBar\VisualBasicEditorNavigationBarItemService_CodeGeneration.vb
Web Access
Project: src\src\EditorFeatures\VisualBasic\Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj (Microsoft.CodeAnalysis.VisualBasic.EditorFeatures)
' 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.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.Utilities
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Formatting.Rules
Imports Microsoft.CodeAnalysis.NavigationBar
Imports Microsoft.CodeAnalysis.NavigationBar.RoslynNavigationBarItem
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.VisualStudio.Text.Editor
 
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar
    Partial Friend Class VisualBasicEditorNavigationBarItemService
        Private Async Function GenerateCodeForItemAsync(document As Document, generateCodeItem As AbstractGenerateCodeItem, textView As ITextView, cancellationToken As CancellationToken) As Task
            ' We'll compute everything up front before we go mutate state
            Dim text = Await document.GetValueTextAsync(cancellationToken).ConfigureAwait(False)
            Dim newDocument = Await GetGeneratedDocumentAsync(document, generateCodeItem, cancellationToken).ConfigureAwait(False)
            Dim generatedTree = Await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
 
            Dim generatedNode = generatedTree.GetAnnotatedNodes(GeneratedSymbolAnnotation).Single().FirstAncestorOrSelf(Of MethodBlockBaseSyntax)
            Dim formattingOptions = Await document.GetLineFormattingOptionsAsync(cancellationToken).ConfigureAwait(False)
            Dim indentSize = formattingOptions.IndentationSize
 
            Dim navigationPoint = NavigationPointHelpers.GetNavigationPoint(generatedTree.GetText(text.Encoding), indentSize, generatedNode)
 
            ' switch back to ui thread to actually perform the application and navigation
            Await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken)
 
            Using transaction = New CaretPreservingEditTransaction(VBEditorResources.Generate_Member, textView, _textUndoHistoryRegistry, _editorOperationsFactoryService)
                Dim solution = newDocument.Project.Solution
                Await solution.Workspace.ApplyDocumentChangesAsync(
                    ThreadingContext, newDocument, cancellationToken).ConfigureAwait(True)
 
                Await NavigateToPositionAsync(
                    solution.Workspace, solution.GetRequiredDocument(navigationPoint.Tree).Id,
                    navigationPoint.Position, navigationPoint.VirtualSpaces, cancellationToken).ConfigureAwait(True)
 
                transaction.Complete()
            End Using
        End Function
 
        Public Shared Async Function GetGeneratedDocumentAsync(document As Document, generateCodeItem As RoslynNavigationBarItem, cancellationToken As CancellationToken) As Task(Of Document)
            Dim syntaxTree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False)
            Dim contextLocation = syntaxTree.GetLocation(New TextSpan(0, 0))
 
            Dim codeGenerationContext = New CodeGenerationContext(contextLocation, generateMethodBodies:=True)
 
            Dim newDocument = Await GetGeneratedDocumentCoreAsync(document, generateCodeItem, codeGenerationContext, cancellationToken).ConfigureAwait(False)
            If newDocument Is Nothing Then
                Return document
            End If
 
            Dim simplifierOptions = Await newDocument.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(False)
            Dim formattingOptions = Await newDocument.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(False)
 
            newDocument = Await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, simplifierOptions, cancellationToken).ConfigureAwait(False)
 
            Dim formatterRules = Formatter.GetDefaultFormattingRules(newDocument)
            If ShouldApplyLineAdjustmentFormattingRule(generateCodeItem) Then
                formatterRules = ImmutableArray.Create(Of AbstractFormattingRule)(LineAdjustmentFormattingRule.Instance).AddRange(formatterRules)
            End If
 
            Return Await Formatter.FormatAsync(
                newDocument,
                Formatter.Annotation,
                options:=formattingOptions,
                cancellationToken:=cancellationToken,
                rules:=formatterRules).ConfigureAwait(False)
        End Function
 
        Private Shared Function ShouldApplyLineAdjustmentFormattingRule(generateCodeItem As RoslynNavigationBarItem) As Boolean
            Return generateCodeItem.Kind <> RoslynNavigationBarItemKind.GenerateFinalizer
        End Function
 
        Private Shared Function GetGeneratedDocumentCoreAsync(
                document As Document,
                generateCodeItem As RoslynNavigationBarItem,
                codeGenerationContext As CodeGenerationContext,
                cancellationToken As CancellationToken) As Task(Of Document)
 
            Select Case generateCodeItem.Kind
                Case RoslynNavigationBarItemKind.GenerateDefaultConstructor
                    Return GenerateDefaultConstructorAsync(document, DirectCast(generateCodeItem, GenerateDefaultConstructor), codeGenerationContext, cancellationToken)
 
                Case RoslynNavigationBarItemKind.GenerateEventHandler
                    Return GenerateEventHandlerAsync(document, DirectCast(generateCodeItem, GenerateEventHandler), codeGenerationContext, cancellationToken)
 
                Case RoslynNavigationBarItemKind.GenerateFinalizer
                    Return GenerateFinalizerAsync(document, DirectCast(generateCodeItem, GenerateFinalizer), codeGenerationContext, cancellationToken)
 
                Case RoslynNavigationBarItemKind.GenerateMethod
                    Return GenerateMethodAsync(document, DirectCast(generateCodeItem, GenerateMethod), codeGenerationContext, cancellationToken)
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(generateCodeItem.Kind)
            End Select
        End Function
 
        Private Shared Async Function GenerateDefaultConstructorAsync(
                document As Document,
                generateCodeItem As GenerateDefaultConstructor,
                codeGenerationContext As CodeGenerationContext,
                cancellationToken As CancellationToken) As Task(Of Document)
 
            Dim compilation = Await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(False)
            Dim destinationType = TryCast(generateCodeItem.DestinationTypeSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).Symbol, INamedTypeSymbol)
 
            If destinationType Is Nothing Then
                Return Nothing
            End If
 
            Dim statements As New ArrayBuilder(Of SyntaxNode)
 
            If destinationType.IsDesignerGeneratedTypeWithInitializeComponent(compilation) Then
                Dim statement = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName("InitializeComponent"), SyntaxFactory.ArgumentList()))
                Dim endOfLineTrivia = SyntaxFactory.EndOfLineTrivia(vbCrLf)
 
                ' When sticking on the comments, we don't want the ' in the localized string
                ' lest we try localizing the comment character itself
                statement = statement.WithLeadingTrivia(endOfLineTrivia, SyntaxFactory.CommentTrivia("' " & VBEditorResources.This_call_is_required_by_the_designer), endOfLineTrivia)
                statement = statement.WithTrailingTrivia(endOfLineTrivia, endOfLineTrivia, SyntaxFactory.CommentTrivia("' " & VBEditorResources.Add_any_initialization_after_the_InitializeComponent_call), endOfLineTrivia, endOfLineTrivia)
                statements.Add(statement)
            End If
 
            Dim methodSymbol = CodeGenerationSymbolFactory.CreateConstructorSymbol(
                attributes:=Nothing,
                accessibility:=Accessibility.Public,
                modifiers:=New DeclarationModifiers(),
                typeName:=destinationType.Name,
                parameters:=ImmutableArray(Of IParameterSymbol).Empty,
                statements:=statements.ToImmutableAndFree())
            methodSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(methodSymbol)
 
            Return Await CodeGenerator.AddMethodDeclarationAsync(
                New CodeGenerationSolutionContext(
                    document.Project.Solution,
                    codeGenerationContext),
                destinationType,
                methodSymbol,
                cancellationToken).ConfigureAwait(False)
        End Function
 
        Private Shared Async Function GenerateEventHandlerAsync(
                document As Document,
                generateCodeItem As GenerateEventHandler,
                codeGenerationContext As CodeGenerationContext,
                cancellationToken As CancellationToken) As Task(Of Document)
 
            Dim compilation = Await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(False)
            Dim eventSymbol = TryCast(generateCodeItem.EventSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).GetAnySymbol(), IEventSymbol)
            Dim destinationType = TryCast(generateCodeItem.DestinationTypeSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).GetAnySymbol(), INamedTypeSymbol)
 
            If eventSymbol Is Nothing OrElse destinationType Is Nothing Then
                Return Nothing
            End If
 
            Dim delegateInvokeMethod = DirectCast(eventSymbol.Type, INamedTypeSymbol).DelegateInvokeMethod
 
            If delegateInvokeMethod Is Nothing Then
                Return Nothing
            End If
 
            Dim containerSyntax As ExpressionSyntax
            Dim methodName As String
 
            If generateCodeItem.ContainerName IsNot Nothing Then
                containerSyntax = SyntaxFactory.IdentifierName(generateCodeItem.ContainerName)
                methodName = generateCodeItem.ContainerName + "_" + eventSymbol.Name
            Else
                containerSyntax = SyntaxFactory.KeywordEventContainer(SyntaxFactory.Token(SyntaxKind.MeKeyword))
                methodName = destinationType.Name + "_" + eventSymbol.Name
            End If
 
            Dim handlesSyntax = SyntaxFactory.SimpleMemberAccessExpression(containerSyntax, SyntaxFactory.Token(SyntaxKind.DotToken), eventSymbol.Name.ToIdentifierName())
 
            Dim methodSymbol = CodeGenerationSymbolFactory.CreateMethodSymbol(
                attributes:=Nothing,
                accessibility:=Accessibility.Private,
                modifiers:=New DeclarationModifiers(),
                returnType:=delegateInvokeMethod.ReturnType,
                refKind:=delegateInvokeMethod.RefKind,
                explicitInterfaceImplementations:=Nothing,
                name:=methodName,
                typeParameters:=Nothing,
                parameters:=delegateInvokeMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(destinationType).Parameters,
                handlesExpressions:=ImmutableArray.Create(Of SyntaxNode)(handlesSyntax))
            methodSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(methodSymbol)
 
            Return Await CodeGenerator.AddMethodDeclarationAsync(
                New CodeGenerationSolutionContext(
                    document.Project.Solution,
                    codeGenerationContext),
                destinationType,
                methodSymbol,
                cancellationToken).ConfigureAwait(False)
        End Function
 
        Private Shared Async Function GenerateFinalizerAsync(
                document As Document,
                generateCodeItem As GenerateFinalizer,
                codeGenerationContext As CodeGenerationContext,
                cancellationToken As CancellationToken) As Task(Of Document)
 
            Dim compilation = Await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(False)
            Dim destinationType = TryCast(generateCodeItem.DestinationTypeSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).Symbol, INamedTypeSymbol)
 
            If destinationType Is Nothing Then
                Return Nothing
            End If
 
            Dim syntaxFactory = document.GetLanguageService(Of SyntaxGenerator)()
            Dim finalizeCall =
                syntaxFactory.ExpressionStatement(
                    syntaxFactory.InvocationExpression(
                        syntaxFactory.MemberAccessExpression(
                            syntaxFactory.BaseExpression(),
                            syntaxFactory.IdentifierName(WellKnownMemberNames.DestructorName))))
 
            Dim finalizerMethodSymbol = CodeGenerationSymbolFactory.CreateMethodSymbol(
                attributes:=Nothing,
                accessibility:=Accessibility.Protected,
                modifiers:=New DeclarationModifiers(isOverride:=True),
                returnType:=compilation.GetSpecialType(SpecialType.System_Void),
                refKind:=RefKind.None,
                explicitInterfaceImplementations:=Nothing,
                name:=WellKnownMemberNames.DestructorName,
                typeParameters:=Nothing,
                parameters:=ImmutableArray(Of IParameterSymbol).Empty,
                statements:=ImmutableArray.Create(finalizeCall))
 
            finalizerMethodSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(finalizerMethodSymbol)
 
            Return Await CodeGenerator.AddMethodDeclarationAsync(
                New CodeGenerationSolutionContext(
                    document.Project.Solution,
                    codeGenerationContext),
                destinationType,
                finalizerMethodSymbol,
                cancellationToken).ConfigureAwait(False)
        End Function
 
        Private Shared Async Function GenerateMethodAsync(
                document As Document,
                generateCodeItem As GenerateMethod,
                codeGenerationContext As CodeGenerationContext,
                cancellationToken As CancellationToken) As Task(Of Document)
 
            Dim compilation = Await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(False)
            Dim destinationType = TryCast(generateCodeItem.DestinationTypeSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).Symbol, INamedTypeSymbol)
            Dim methodToReplicate = TryCast(generateCodeItem.MethodToReplicateSymbolKey.Resolve(compilation, cancellationToken:=cancellationToken).Symbol, IMethodSymbol)
 
            If destinationType Is Nothing OrElse methodToReplicate Is Nothing Then
                Return Nothing
            End If
 
            Dim codeGenerationSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(
                CodeGenerationSymbolFactory.CreateMethodSymbol(
                    methodToReplicate.RemoveInaccessibleAttributesAndAttributesOfTypes(destinationType)))
 
            Return Await CodeGenerator.AddMethodDeclarationAsync(
                 New CodeGenerationSolutionContext(
                    document.Project.Solution,
                    codeGenerationContext),
                destinationType,
                codeGenerationSymbol,
                cancellationToken).ConfigureAwait(False)
        End Function
    End Class
End Namespace