File: EndConstructGeneration\EndConstructStatementVisitor_IfStatement.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 Microsoft.CodeAnalysis.Text.Shared.Extensions
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.EndConstructGeneration
    Partial Friend Class EndConstructStatementVisitor
 
        Public Overrides Function VisitIfStatement(node As IfStatementSyntax) As AbstractEndConstructResult
            Dim needsEnd = node.GetAncestorsOrThis(Of MultiLineIfBlockSyntax)().Any(Function(block) block.EndIfStatement.IsMissing)
 
            If needsEnd Then
                Dim aligningWhitespace = _subjectBuffer.CurrentSnapshot.GetAligningWhitespace(node.SpanStart)
                Return New SpitLinesResult({"", aligningWhitespace & "End If"})
            Else
                Return Nothing
            End If
        End Function
 
        Public Overrides Function VisitSingleLineIfStatement(node As SingleLineIfStatementSyntax) As AbstractEndConstructResult
            Dim aligningWhitespace = _subjectBuffer.CurrentSnapshot.GetAligningWhitespace(node.SpanStart)
            Dim indentedWhitespace = aligningWhitespace & "    "
 
            Dim whitespaceTrivia = {SyntaxFactory.WhitespaceTrivia(aligningWhitespace)}.ToSyntaxTriviaList()
            Dim endOfLine = SyntaxFactory.EndOfLineTrivia(_state.NewLineCharacter)
 
            Dim elseBlock As ElseBlockSyntax = Nothing
 
            If node.ElseClause IsNot Nothing Then
                Dim trailingTrivia = If(node.ElseClause.ElseKeyword.HasTrailingTrivia AndAlso node.ElseClause.ElseKeyword.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia),
                                        node.ElseClause.ElseKeyword.TrailingTrivia,
                                        {endOfLine}.ToSyntaxTriviaList())
                elseBlock = SyntaxFactory.ElseBlock(SyntaxFactory.ElseStatement(SyntaxFactory.Token(whitespaceTrivia, SyntaxKind.ElseKeyword, trailingTrivia)),
                                           ConvertSingleLineStatementsToMultiLineStatements(node.ElseClause.Statements, indentedWhitespace))
            End If
 
            Dim ifBlock = SyntaxFactory.MultiLineIfBlock(
                                            SyntaxFactory.IfStatement(node.IfKeyword, node.Condition, node.ThenKeyword).WithTrailingTrivia(endOfLine),
                                            ConvertSingleLineStatementsToMultiLineStatements(node.Statements, indentedWhitespace),
                                            New SyntaxList(Of ElseIfBlockSyntax),
                                            elseBlock,
                                                  SyntaxFactory.EndIfStatement(
                                                      SyntaxFactory.Token(whitespaceTrivia, SyntaxKind.EndKeyword, {SyntaxFactory.WhitespaceTrivia(" ")}.ToSyntaxTriviaList(), "End"),
                                                      SyntaxFactory.Token(Nothing, SyntaxKind.IfKeyword, {endOfLine}.ToSyntaxTriviaList(), "If")))
 
            Dim position = If(ifBlock.Statements.Any(), ifBlock.Statements(0).SpanStart, ifBlock.IfStatement.Span.End + _state.NewLineCharacter.Length)
            Dim ifNodeToken As SyntaxNodeOrToken = ifBlock
            Return New ReplaceSpanResult(node.FullSpan.ToSnapshotSpan(_subjectBuffer.CurrentSnapshot), ifNodeToken.ToFullString(), position)
        End Function
 
        ''' <summary>
        ''' Given a separatedSyntaxList of statements separated by colons, converts them to a
        ''' separate syntax list of statements separated by newlines
        ''' </summary>
        ''' <param name="statements">The list of statements to convert.</param>
        ''' <param name="indentedWhitespace">The whitespace to indent with.</param>
        Private Function ConvertSingleLineStatementsToMultiLineStatements(statements As SyntaxList(Of StatementSyntax), indentedWhitespace As String) As SyntaxList(Of StatementSyntax)
            If statements = Nothing OrElse statements.Count = 0 Then
                ' Return an empty statement with a newline
                Return SyntaxFactory.List({DirectCast(SyntaxFactory.EmptyStatement(SyntaxFactory.Token(SyntaxKind.EmptyToken, SyntaxFactory.TriviaList(SyntaxFactory.EndOfLineTrivia(_state.NewLineCharacter)))), StatementSyntax)})
            End If
 
            Dim indentedWhitespaceTrivia = SpecializedCollections.SingletonEnumerable(SyntaxFactory.WhitespaceTrivia(indentedWhitespace))
            Dim newList As New List(Of StatementSyntax)(capacity:=statements.Count)
            Dim triviaLeftForNextStatement As IEnumerable(Of SyntaxTrivia) = New List(Of SyntaxTrivia)
 
            ' If the last statement itself is an End If statement, we should skip it
            Dim lastStatementToProcess = statements.Count - 1
 
            If statements.LastOrDefault().IsKind(SyntaxKind.EndIfStatement) Then
                lastStatementToProcess = statements.Count - 2
            End If
 
            For i = 0 To lastStatementToProcess
                Dim statement = statements(i)
 
                ' Add the new whitespace on the start of the statement
                If statement.Kind <> SyntaxKind.EmptyStatement OrElse statement.HasTrailingTrivia Then
                    Dim leadingTrivia = indentedWhitespaceTrivia.Concat(triviaLeftForNextStatement.Concat(statement.GetLeadingTrivia()).WithoutLeadingWhitespaceOrEndOfLine())
                    statement = statement.WithLeadingTrivia(leadingTrivia)
                End If
 
                ' We want to drop any whitespace trivia from the
                ' end
                Dim trailingTrivia = New List(Of SyntaxTrivia)
                Dim separator As SyntaxTrivia = Nothing
 
                Dim lastToken = statement.GetLastToken(includeZeroWidth:=True)
                For Each trivia In lastToken.TrailingTrivia
                    If trivia.Kind = SyntaxKind.ColonTrivia Then
                        separator = trivia
                        Exit For
                    End If
 
                    trailingTrivia.Add(trivia)
                Next
 
                Do While trailingTrivia.Count > 0 AndAlso trailingTrivia.Last().Kind = SyntaxKind.WhitespaceTrivia
                    trailingTrivia.RemoveAt(trailingTrivia.Count - 1)
                Loop
 
                If separator.Kind <> SyntaxKind.None OrElse Not trailingTrivia.Any Then
                    trailingTrivia.Add(SyntaxFactory.EndOfLineTrivia(_state.NewLineCharacter))
                End If
 
                statement = statement.WithTrailingTrivia(trailingTrivia)
                newList.Add(statement)
 
                triviaLeftForNextStatement = lastToken.TrailingTrivia.SkipWhile(Function(t) t <> separator).Where(Function(t) t.Kind <> SyntaxKind.ColonTrivia)
            Next
 
            Return SyntaxFactory.List(newList)
        End Function
 
    End Class
End Namespace