File: AutomaticCompletion\AutomaticLineEnderCommandHandler_Helpers.cs
Web Access
Project: src\src\EditorFeatures\CSharp\Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion;
 
using static CSharpSyntaxTokens;
using static SyntaxFactory;
 
internal partial class AutomaticLineEnderCommandHandler
{
    #region NodeReplacementHelpers
 
    private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwnerAndInsertStatement(
        SolutionServices services,
        SyntaxNode root,
        SyntaxNode oldNode,
        SyntaxNode newNode,
        SyntaxNode anchorNode,
        ImmutableArray<StatementSyntax> nodesToInsert,
        SyntaxFormattingOptions formattingOptions,
        CancellationToken cancellationToken)
    {
        var rootEditor = new SyntaxEditor(root, services);
 
        // 1. Insert the node before anchor node
        rootEditor.InsertAfter(anchorNode, nodesToInsert);
 
        // 2. Replace the old node with newNode. (new node is the node with correct braces)
        rootEditor.ReplaceNode(oldNode, newNode.WithAdditionalAnnotations(s_replacementNodeAnnotation));
        var newRoot = rootEditor.GetChangedRoot();
 
        // 4. Format the new node so that the inserted braces/blocks would have correct indentation and formatting.
        var newNodeAfterInsertion = newRoot.GetAnnotatedNodes(s_replacementNodeAnnotation).Single();
 
        var formattedNewRoot = Formatter.Format(
            newRoot,
            newNodeAfterInsertion.Span,
            services,
            formattingOptions,
            cancellationToken);
 
        // 4. Use the annotation to find the end of the open brace, it would be the new caret position
        var nextCaretPosition = formattedNewRoot.GetAnnotatedTokens(s_openBracePositionAnnotation).Single().Span.End;
        return (formattedNewRoot, nextCaretPosition);
    }
 
    private static SyntaxNode ReplaceNodeAndFormat(
        SolutionServices services,
        SyntaxNode root,
        SyntaxNode oldNode,
        SyntaxNode newNode,
        SyntaxFormattingOptions formattingOptions,
        CancellationToken cancellationToken)
    {
        // 1. Tag the new node so that it could be found later.
        var annotatedNewNode = newNode.WithAdditionalAnnotations(s_replacementNodeAnnotation);
 
        // 2. Replace the old node with newNode. (new node is the node with correct braces)
        var newRoot = root.ReplaceNode(
            oldNode,
            annotatedNewNode);
 
        // 3. Find the newNode in the new syntax root.
        var newNodeAfterInsertion = newRoot.GetAnnotatedNodes(s_replacementNodeAnnotation).Single();
 
        // 4. Format the new node so that the inserted braces/blocks would have correct indentation and formatting.
        var formattedNewRoot = Formatter.Format(
            newRoot,
            newNodeAfterInsertion.Span,
            services,
            formattingOptions,
            cancellationToken);
 
        return formattedNewRoot;
    }
 
    #endregion
 
    #region EmbeddedStatementModificationHelpers
 
    private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToEmbeddedStatementOwner(
        SolutionServices services,
        SyntaxNode root,
        SyntaxNode embeddedStatementOwner,
        SyntaxFormattingOptions formattingOptions,
        CancellationToken cancellationToken)
    {
        // If there is no inner statement, just add an empty block to it.
        // e.g.
        // class Bar
        // {
        //    if (true)$$
        // }
        // =>
        // class Bar
        // {
        //    if (true)
        //    {
        //    }
        // }
        var statement = embeddedStatementOwner.GetEmbeddedStatement();
        if (statement == null || statement.IsMissing)
        {
            var newRoot = ReplaceNodeAndFormat(
                services,
                root,
                embeddedStatementOwner,
                WithBraces(embeddedStatementOwner, formattingOptions),
                formattingOptions,
                cancellationToken);
 
            // Locate the open brace token, and move the caret after it.
            var nextCaretPosition = GetOpenBraceSpanEnd(newRoot);
            return (newRoot, nextCaretPosition);
        }
 
        // There is an inner statement, it needs to be handled differently in addition to adding the block,
 
        // For while, ForEach, Lock and Using statement,
        // If there is an statement in the embeddedStatementOwner,
        // move the old statement next to the statementOwner,
        // and insert a empty block into the statementOwner,
        // e.g.
        // before:
        // whi$$le(true)
        // var i = 1;
        // for this case 'var i = 1;' is thought as the inner statement,
        //
        // after:
        // while(true)
        // {
        //      $$
        // }
        // var i = 1;
        return embeddedStatementOwner switch
        {
            WhileStatementSyntax or ForEachStatementSyntax or ForStatementSyntax or LockStatementSyntax or UsingStatementSyntax
                => ReplaceStatementOwnerAndInsertStatement(
                      services,
                      root,
                      oldNode: embeddedStatementOwner,
                      newNode: AddBlockToEmbeddedStatementOwner(embeddedStatementOwner, formattingOptions),
                      anchorNode: embeddedStatementOwner,
                      nodesToInsert: [statement],
                      formattingOptions,
                      cancellationToken),
            DoStatementSyntax doStatementNode => AddBraceToDoStatement(services, root, doStatementNode, formattingOptions, statement, cancellationToken),
            IfStatementSyntax ifStatementNode => AddBraceToIfStatement(services, root, ifStatementNode, formattingOptions, statement, cancellationToken),
            ElseClauseSyntax elseClauseNode => AddBraceToElseClause(services, root, elseClauseNode, formattingOptions, statement, cancellationToken),
            _ => throw ExceptionUtilities.UnexpectedValue(embeddedStatementOwner),
        };
    }
 
    private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement(
        SolutionServices services,
        SyntaxNode root,
        DoStatementSyntax doStatementNode,
        SyntaxFormattingOptions formattingOptions,
        StatementSyntax innerStatement,
        CancellationToken cancellationToken)
    {
        // If this do statement doesn't end with the 'while' parts
        // e.g:
        // before:
        // d$$o
        // Print("hello");
        // after:
        // do
        // {
        //     $$
        // }
        // Print("hello");
        if (doStatementNode.WhileKeyword.IsMissing
            && doStatementNode.SemicolonToken.IsMissing
            && doStatementNode.OpenParenToken.IsMissing
            && doStatementNode.CloseParenToken.IsMissing)
        {
            return ReplaceStatementOwnerAndInsertStatement(
                services,
                root,
                oldNode: doStatementNode,
                newNode: AddBlockToEmbeddedStatementOwner(doStatementNode, formattingOptions),
                anchorNode: doStatementNode,
                nodesToInsert: [innerStatement],
                formattingOptions,
                cancellationToken);
        }
 
        // if the do statement has 'while' as an end
        // e.g:
        // before:
        // d$$o
        // Print("hello");
        // while (true);
        // after:
        // do
        // {
        //     $$
        //     Print("hello");
        // } while(true);
        var newRoot = ReplaceNodeAndFormat(
            services,
            root,
            doStatementNode,
            AddBlockToEmbeddedStatementOwner(doStatementNode, formattingOptions, innerStatement),
            formattingOptions,
            cancellationToken);
 
        var nextCaretPosition = GetOpenBraceSpanEnd(newRoot);
        return (newRoot, nextCaretPosition);
    }
 
    private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement(
        SolutionServices services,
        SyntaxNode root,
        IfStatementSyntax ifStatementNode,
        SyntaxFormattingOptions formattingOptions,
        StatementSyntax innerStatement,
        CancellationToken cancellationToken)
    {
        // This ifStatement doesn't have an else clause, and its parent is a Block.
        // Insert the innerStatement next to the ifStatement
        // e.g.
        // if ($$a)
        // Print();
        // =>
        // if (a)
        // {
        //     $$
        // }
        // Print();
        if (ifStatementNode.Else == null && ifStatementNode.Parent is BlockSyntax)
        {
            return ReplaceStatementOwnerAndInsertStatement(
                services,
                root,
                ifStatementNode,
                AddBlockToEmbeddedStatementOwner(ifStatementNode, formattingOptions),
                ifStatementNode,
                [innerStatement],
                formattingOptions,
                cancellationToken);
        }
 
        // If this IfStatement has an else statement after
        // e.g.
        // before:
        // if $$(true)
        //     print("Hello");
        // else {}
        // after:
        // if (true)
        // {
        //     $$
        //     print("Hello");
        // }
        // else {}
        var newRoot = ReplaceNodeAndFormat(
            services,
            root,
            ifStatementNode,
            AddBlockToEmbeddedStatementOwner(ifStatementNode, formattingOptions, innerStatement),
            formattingOptions,
            cancellationToken);
 
        var nextCaretPosition = GetOpenBraceSpanEnd(newRoot);
        return (newRoot, nextCaretPosition);
    }
 
    private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause(
        SolutionServices services,
        SyntaxNode root,
        ElseClauseSyntax elseClauseNode,
        SyntaxFormattingOptions formattingOptions,
        StatementSyntax innerStatement,
        CancellationToken cancellationToken)
    {
        // If this is an 'els$$e if(true)' statement,
        // then treat it as the selected node is the nested if statement
        if (elseClauseNode.Statement is IfStatementSyntax)
        {
            return AddBraceToEmbeddedStatementOwner(services, root, elseClauseNode.Statement, formattingOptions, cancellationToken);
        }
 
        // Otherwise, it is just an ending else clause.
        // if its parent is an ifStatement and the parent of ifStatement is a block, insert the innerStatement after the ifStatement
        // e.g. before:
        // if (true)
        // {
        // } els$$e
        // Print();
        // after:
        // if (true)
        // {
        // } els$$e
        // {
        //      $$
        // }
        // Print();
        if (elseClauseNode.Parent is IfStatementSyntax { Parent: BlockSyntax })
        {
            return ReplaceStatementOwnerAndInsertStatement(
                services,
                root,
                elseClauseNode,
                WithBraces(elseClauseNode, formattingOptions),
                elseClauseNode.Parent!,
                [innerStatement],
                formattingOptions,
                cancellationToken);
        }
 
        // For all the other cases,
        // Put the innerStatement into the block
        // e.g.
        // if (a)
        //     if (true)
        //     {
        //     }
        //     else
        //     {
        //         $$
        //         Print();
        //     }
        // =>
        // if (a)
        //     if (true)
        //     {
        //     }
        //     els$$e
        //         Print();
        var formattedNewRoot = ReplaceNodeAndFormat(
            services,
            root,
            elseClauseNode,
            AddBlockToEmbeddedStatementOwner(elseClauseNode, formattingOptions, innerStatement),
            formattingOptions,
            cancellationToken);
 
        var nextCaretPosition = formattedNewRoot.GetAnnotatedTokens(s_openBracePositionAnnotation).Single().Span.End;
        return (formattedNewRoot, nextCaretPosition);
    }
 
    #endregion
 
    #region ObjectCreationExpressionModificationHelpers
 
    private static (SyntaxNode newNode, SyntaxNode oldNode) ModifyObjectCreationExpressionNode(
        BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode,
        bool addOrRemoveInitializer,
        SyntaxFormattingOptions formattingOptions)
    {
        // 1. Add '()' after the type or new keyword.
        // e.g.
        // case 1: 'var c = new Bar' becomes 'var c = new Bar()'
        // case 2: 'Bar b = new' becomes 'Bar b = new()'
        var objectCreationNodeWithArgumentList = WithArgumentListIfNeeded(baseObjectCreationExpressionNode);
 
        // 2. Add or remove initializer
        // e.g. var c = new Bar() => var c = new Bar() { }
        var objectCreationNodeWithCorrectInitializer = addOrRemoveInitializer
            ? WithBraces(objectCreationNodeWithArgumentList, formattingOptions)
            : WithoutBraces(objectCreationNodeWithArgumentList);
 
        // 3. Handle the semicolon.
        // If the next token is a semicolon, e.g.
        // var l = new Ba$$r() { }  => var l = new Ba$$r() { };
        var nextToken = baseObjectCreationExpressionNode.GetLastToken(includeZeroWidth: true).GetNextToken(includeZeroWidth: true);
        if (nextToken.IsKind(SyntaxKind.SemicolonToken)
            && nextToken.Parent != null
            && nextToken.Parent.Contains(baseObjectCreationExpressionNode))
        {
            var objectCreationNodeContainer = nextToken.Parent;
            // Replace the old object creation node and add the semicolon token.
            // Note: need to move the trailing trivia of the objectCreationExpressionNode after the semicolon token
            // e.g.
            // var l = new Bar() {} // I am some comments
            // =>
            // var l = new Bar() {}; // I am some comments
            var replacementContainerNode = objectCreationNodeContainer.ReplaceSyntax(
                nodes: [baseObjectCreationExpressionNode],
                (_, _) => objectCreationNodeWithCorrectInitializer.WithoutTrailingTrivia(),
                tokens: [nextToken],
                computeReplacementToken: (_, _) =>
                    SemicolonToken.WithTrailingTrivia(objectCreationNodeWithCorrectInitializer.GetTrailingTrivia()),
                trivia: [],
                computeReplacementTrivia: (_, syntaxTrivia) => syntaxTrivia);
            return (replacementContainerNode, objectCreationNodeContainer);
        }
        else
        {
            // No need to change the semicolon, just return the objectCreationExpression with correct initializer
            return (objectCreationNodeWithCorrectInitializer, baseObjectCreationExpressionNode);
        }
    }
 
    /// <summary>
    /// Add argument list to the objectCreationExpression if needed.
    /// e.g. new Bar; => new Bar();
    /// </summary>
    private static BaseObjectCreationExpressionSyntax WithArgumentListIfNeeded(BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode)
    {
        var argumentList = baseObjectCreationExpressionNode.ArgumentList;
        if (argumentList is { IsMissing: false })
        {
            return baseObjectCreationExpressionNode;
        }
 
        RoslynDebug.Assert(!baseObjectCreationExpressionNode.NewKeyword.IsMissing);
        if (baseObjectCreationExpressionNode is ObjectCreationExpressionSyntax objectCreationExpressionNode)
        {
            var typeNode = objectCreationExpressionNode.Type;
            if (typeNode.IsMissing)
            {
                // There is only 'new' keyword in the object creation expression. Treat it as an ImplicitObjectCreationExpression.
                // This could happen because when only type 'new', parser would think it is an ObjectCreationExpression.
                var newKeywordToken = baseObjectCreationExpressionNode.NewKeyword;
                var newArgumentList = ArgumentList().WithTrailingTrivia(newKeywordToken.TrailingTrivia);
                return ImplicitObjectCreationExpression(newKeywordToken.WithoutTrailingTrivia(), newArgumentList, baseObjectCreationExpressionNode.Initializer);
            }
            else
            {
                // Make sure the trailing trivia is passed to the argument list
                // like var l = new List\r\n =>
                // var l = new List()\r\r
                var newArgumentList = ArgumentList().WithTrailingTrivia(typeNode.GetTrailingTrivia());
                var newTypeNode = typeNode.WithoutTrivia();
                return objectCreationExpressionNode.WithType(newTypeNode).WithArgumentList(newArgumentList);
            }
        }
 
        if (baseObjectCreationExpressionNode is ImplicitObjectCreationExpressionSyntax implicitObjectCreationExpressionNode)
        {
            var newKeywordToken = implicitObjectCreationExpressionNode.NewKeyword;
            var newArgumentList = ArgumentList().WithTrailingTrivia(newKeywordToken.TrailingTrivia);
            return ImplicitObjectCreationExpression(newKeywordToken.WithoutTrailingTrivia(), newArgumentList, baseObjectCreationExpressionNode.Initializer);
        }
 
        RoslynDebug.Assert(false, $"New derived type of {nameof(BaseObjectCreationExpressionSyntax)} is added");
        return baseObjectCreationExpressionNode;
    }
 
    #endregion
 
    #region ShouldAddBraceCheck
 
    private static bool ShouldAddBraces(SyntaxNode node, int caretPosition)
        => node switch
        {
            NamespaceDeclarationSyntax namespaceDeclarationNode => ShouldAddBraceForNamespaceDeclaration(namespaceDeclarationNode, caretPosition),
            BaseTypeDeclarationSyntax baseTypeDeclarationNode => ShouldAddBraceForBaseTypeDeclaration(baseTypeDeclarationNode, caretPosition),
            BaseMethodDeclarationSyntax baseMethodDeclarationNode => ShouldAddBraceForBaseMethodDeclaration(baseMethodDeclarationNode, caretPosition),
            LocalFunctionStatementSyntax localFunctionStatementNode => ShouldAddBraceForLocalFunctionStatement(localFunctionStatementNode, caretPosition),
            ObjectCreationExpressionSyntax objectCreationExpressionNode => ShouldAddBraceForObjectCreationExpression(objectCreationExpressionNode),
            BaseFieldDeclarationSyntax baseFieldDeclarationNode => ShouldAddBraceForBaseFieldDeclaration(baseFieldDeclarationNode),
            AccessorDeclarationSyntax accessorDeclarationNode => ShouldAddBraceForAccessorDeclaration(accessorDeclarationNode),
            IndexerDeclarationSyntax indexerDeclarationNode => ShouldAddBraceForIndexerDeclaration(indexerDeclarationNode, caretPosition),
            SwitchStatementSyntax switchStatementNode => ShouldAddBraceForSwitchStatement(switchStatementNode),
            TryStatementSyntax tryStatementNode => ShouldAddBraceForTryStatement(tryStatementNode, caretPosition),
            CatchClauseSyntax catchClauseNode => ShouldAddBraceForCatchClause(catchClauseNode, caretPosition),
            FinallyClauseSyntax finallyClauseNode => ShouldAddBraceForFinallyClause(finallyClauseNode, caretPosition),
            DoStatementSyntax doStatementNode => ShouldAddBraceForDoStatement(doStatementNode, caretPosition),
            CommonForEachStatementSyntax commonForEachStatementNode => ShouldAddBraceForCommonForEachStatement(commonForEachStatementNode, caretPosition),
            ForStatementSyntax forStatementNode => ShouldAddBraceForForStatement(forStatementNode, caretPosition),
            IfStatementSyntax ifStatementNode => ShouldAddBraceForIfStatement(ifStatementNode, caretPosition),
            ElseClauseSyntax elseClauseNode => ShouldAddBraceForElseClause(elseClauseNode, caretPosition),
            LockStatementSyntax lockStatementNode => ShouldAddBraceForLockStatement(lockStatementNode, caretPosition),
            UsingStatementSyntax usingStatementNode => ShouldAddBraceForUsingStatement(usingStatementNode, caretPosition),
            WhileStatementSyntax whileStatementNode => ShouldAddBraceForWhileStatement(whileStatementNode, caretPosition),
            CheckedStatementSyntax checkedStatementNode => ShouldAddBraceForCheckedStatement(checkedStatementNode, caretPosition),
            _ => false,
        };
 
    /// <summary>
    /// For namespace, make sure it has name there is no braces
    /// </summary>
    private static bool ShouldAddBraceForNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclarationNode, int caretPosition)
        => !namespaceDeclarationNode.Name.IsMissing
           && HasNoBrace(namespaceDeclarationNode)
           && !WithinAttributeLists(namespaceDeclarationNode, caretPosition)
           && !WithinBraces(namespaceDeclarationNode, caretPosition);
 
    /// <summary>
    /// For class/struct/enum ..., make sure it has name and there is no braces.
    /// </summary>
    private static bool ShouldAddBraceForBaseTypeDeclaration(BaseTypeDeclarationSyntax baseTypeDeclarationNode, int caretPosition)
        => !baseTypeDeclarationNode.Identifier.IsMissing
           && HasNoBrace(baseTypeDeclarationNode)
           && !WithinAttributeLists(baseTypeDeclarationNode, caretPosition)
           && !WithinBraces(baseTypeDeclarationNode, caretPosition);
 
    /// <summary>
    /// For method, make sure it has a ParameterList, because later braces would be inserted after the Parameterlist
    /// </summary>
    private static bool ShouldAddBraceForBaseMethodDeclaration(BaseMethodDeclarationSyntax baseMethodDeclarationNode, int caretPosition)
        => baseMethodDeclarationNode.ExpressionBody == null
           && baseMethodDeclarationNode.Body == null
           && !baseMethodDeclarationNode.ParameterList.IsMissing
           && baseMethodDeclarationNode.SemicolonToken.IsMissing
           && !WithinAttributeLists(baseMethodDeclarationNode, caretPosition)
           && !WithinMethodBody(baseMethodDeclarationNode, caretPosition)
           // Make sure we don't insert braces for method in Interface.
           && !baseMethodDeclarationNode.IsParentKind(SyntaxKind.InterfaceDeclaration);
 
    /// <summary>
    /// For local Function, make sure it has a ParameterList, because later braces would be inserted after the Parameterlist
    /// </summary>
    private static bool ShouldAddBraceForLocalFunctionStatement(LocalFunctionStatementSyntax localFunctionStatementNode, int caretPosition)
        => localFunctionStatementNode.ExpressionBody == null
           && localFunctionStatementNode.Body == null
           && !localFunctionStatementNode.ParameterList.IsMissing
           && !WithinAttributeLists(localFunctionStatementNode, caretPosition)
           && !WithinMethodBody(localFunctionStatementNode, caretPosition);
 
    /// <summary>
    /// Add brace for ObjectCreationExpression if it doesn't have initializer
    /// </summary>
    private static bool ShouldAddBraceForObjectCreationExpression(ObjectCreationExpressionSyntax objectCreationExpressionNode)
        => objectCreationExpressionNode.Initializer == null;
 
    /// <summary>
    /// Add braces for field and event field if they only have one variable, semicolon is missing and don't have readonly keyword
    /// Example:
    /// public int Bar$$ =>
    /// public int Bar
    /// {
    ///      $$
    /// }
    /// This would change field to property, and change event field to event declaration.
    /// </summary>
    private static bool ShouldAddBraceForBaseFieldDeclaration(BaseFieldDeclarationSyntax baseFieldDeclarationNode)
        => baseFieldDeclarationNode is { Declaration.Variables: [{ Initializer: null }], SemicolonToken.IsMissing: true }
           && !baseFieldDeclarationNode.Modifiers.Any(SyntaxKind.ReadOnlyKeyword);
 
    private static bool ShouldAddBraceForAccessorDeclaration(AccessorDeclarationSyntax accessorDeclarationNode)
    {
        if (accessorDeclarationNode.Body == null
            && accessorDeclarationNode.ExpressionBody == null
            && accessorDeclarationNode.SemicolonToken.IsMissing)
        {
            // If the accessor doesn't have body, expression body and semicolon, let's check this case
            // for both event and property,
            // e.g.
            // int Bar
            // {
            //     get;
            //     se$$t
            // }
            // because if the getter doesn't have a body then setter also shouldn't have any body.
            // Don't check for indexer because the accessor for indexer should have body.
            var parent = accessorDeclarationNode.Parent;
            var parentOfParent = parent?.Parent;
            if (parent is AccessorListSyntax accessorListNode
                && parentOfParent is PropertyDeclarationSyntax)
            {
                var otherAccessors = accessorListNode.Accessors
                    .Except([accessorDeclarationNode])
                    .ToImmutableArray();
                if (!otherAccessors.IsEmpty)
                {
                    return !otherAccessors.Any(
                        static accessor => accessor.Body == null
                                    && accessor.ExpressionBody == null
                                    && !accessor.SemicolonToken.IsMissing);
                }
            }
 
            return true;
        }
 
        return false;
    }
 
    /// <summary>
    /// For indexer, switch, try and catch syntax node without braces, if it is the last child of its parent, it would
    /// use its parent's close brace as its own.
    /// Example:
    /// class Bar
    /// {
    ///      int th$$is[int i]
    /// }
    /// In this case, parser would think the last '}' belongs to the indexer, not the class.
    /// Therefore, only check if the open brace is missing for these 4 types of SyntaxNode
    /// </summary>
    private static bool ShouldAddBraceForIndexerDeclaration(IndexerDeclarationSyntax indexerDeclarationNode, int caretPosition)
    {
        if (WithinAttributeLists(indexerDeclarationNode, caretPosition) ||
            WithinBraces(indexerDeclarationNode.AccessorList, caretPosition))
        {
            return false;
        }
 
        // Make sure it has brackets
        var (openBracket, closeBracket) = indexerDeclarationNode.ParameterList.GetBrackets();
        if (openBracket.IsMissing || closeBracket.IsMissing)
        {
            return false;
        }
 
        // If both accessorList and body is empty
        if ((indexerDeclarationNode.AccessorList == null || indexerDeclarationNode.AccessorList.IsMissing)
            && indexerDeclarationNode.ExpressionBody == null)
        {
            return true;
        }
 
        return indexerDeclarationNode.AccessorList != null
           && indexerDeclarationNode.AccessorList.OpenBraceToken.IsMissing;
    }
 
    // For the Switch, Try, Catch, Finally node
    // e.g.
    // class Bar
    // {
    //      void Main()
    //      {
    //          tr$$y
    //      }
    // }
    // In this case, the last close brace of 'void Main()' would be thought as a part of the try statement,
    // and the last close brace of 'Bar' would be thought as a part of Main()
    // So for these case, just check if the open brace is missing.
    private static bool ShouldAddBraceForSwitchStatement(SwitchStatementSyntax switchStatementNode)
        => !switchStatementNode.SwitchKeyword.IsMissing && switchStatementNode.OpenBraceToken.IsMissing;
 
    private static bool ShouldAddBraceForTryStatement(TryStatementSyntax tryStatementNode, int caretPosition)
        => !tryStatementNode.TryKeyword.IsMissing
           && tryStatementNode.Block.OpenBraceToken.IsMissing
           && !tryStatementNode.Block.Span.Contains(caretPosition);
 
    private static bool ShouldAddBraceForCatchClause(CatchClauseSyntax catchClauseSyntax, int caretPosition)
        => !catchClauseSyntax.CatchKeyword.IsMissing
           && catchClauseSyntax.Block.OpenBraceToken.IsMissing
           && !catchClauseSyntax.Block.Span.Contains(caretPosition);
 
    private static bool ShouldAddBraceForFinallyClause(FinallyClauseSyntax finallyClauseNode, int caretPosition)
        => !finallyClauseNode.FinallyKeyword.IsMissing
           && finallyClauseNode.Block.OpenBraceToken.IsMissing
           && !finallyClauseNode.Block.Span.Contains(caretPosition);
 
    private static bool ShouldAddBraceForCheckedStatement(CheckedStatementSyntax checkedStatementNode, int caretPosition)
        => checkedStatementNode.Block.OpenBraceToken.IsMissing
           && !checkedStatementNode.Block.Span.Contains(caretPosition);
 
    // For all the embeddedStatementOwners,
    // if the embeddedStatement is not block, insert the the braces if its statement is not block.
    private static bool ShouldAddBraceForDoStatement(DoStatementSyntax doStatementNode, int caretPosition)
        => !doStatementNode.DoKeyword.IsMissing
           && doStatementNode.Statement is not BlockSyntax
           && doStatementNode.DoKeyword.FullSpan.Contains(caretPosition);
 
    private static bool ShouldAddBraceForCommonForEachStatement(CommonForEachStatementSyntax commonForEachStatementNode, int caretPosition)
        => commonForEachStatementNode.Statement is not BlockSyntax
           && !commonForEachStatementNode.OpenParenToken.IsMissing
           && !commonForEachStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(commonForEachStatementNode, caretPosition);
 
    private static bool ShouldAddBraceForForStatement(ForStatementSyntax forStatementNode, int caretPosition)
        => forStatementNode.Statement is not BlockSyntax
           && !forStatementNode.OpenParenToken.IsMissing
           && !forStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(forStatementNode, caretPosition);
 
    private static bool ShouldAddBraceForIfStatement(IfStatementSyntax ifStatementNode, int caretPosition)
        => ifStatementNode.Statement is not BlockSyntax
           && !ifStatementNode.OpenParenToken.IsMissing
           && !ifStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(ifStatementNode, caretPosition);
 
    private static bool ShouldAddBraceForElseClause(ElseClauseSyntax elseClauseNode, int caretPosition)
    {
        // In case it is an else-if clause, if the statement is IfStatement, use its insertion statement
        // otherwise, use the end of the else keyword
        // Example:
        // Before: if (a)
        //         {
        //         } else i$$f (b)
        // After: if (a)
        //        {
        //        } else if (b)
        //        {
        //            $$
        //        }
        if (elseClauseNode.Statement is IfStatementSyntax ifStatementNode)
        {
            return ShouldAddBraceForIfStatement(ifStatementNode, caretPosition);
        }
        else
        {
            // Here it should be an elseClause
            // like:
            // if (a)
            // {
            // } els$$e {
            // }
            // So only check the its statement
            return elseClauseNode.Statement is not BlockSyntax && !WithinEmbeddedStatement(elseClauseNode, caretPosition);
        }
    }
 
    private static bool ShouldAddBraceForLockStatement(LockStatementSyntax lockStatementNode, int caretPosition)
        => lockStatementNode.Statement is not BlockSyntax
           && !lockStatementNode.OpenParenToken.IsMissing
           && !lockStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(lockStatementNode, caretPosition);
 
    private static bool ShouldAddBraceForUsingStatement(UsingStatementSyntax usingStatementNode, int caretPosition)
        => usingStatementNode.Statement is not BlockSyntax
           && !usingStatementNode.OpenParenToken.IsMissing
           && !usingStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(usingStatementNode, caretPosition);
 
    private static bool ShouldAddBraceForWhileStatement(WhileStatementSyntax whileStatementNode, int caretPosition)
        => whileStatementNode.Statement is not BlockSyntax
           && !whileStatementNode.OpenParenToken.IsMissing
           && !whileStatementNode.CloseParenToken.IsMissing
           && !WithinEmbeddedStatement(whileStatementNode, caretPosition);
 
    private static bool WithinAttributeLists(SyntaxNode node, int caretPosition)
    {
        var attributeLists = node.GetAttributeLists();
        return attributeLists.Span.Contains(caretPosition);
    }
 
    private static bool WithinBraces(SyntaxNode? node, int caretPosition)
    {
        var (openBrace, closeBrace) = node.GetBraces();
        return TextSpan.FromBounds(openBrace.SpanStart, closeBrace.Span.End).Contains(caretPosition);
    }
 
    private static bool WithinMethodBody(SyntaxNode node, int caretPosition)
    {
        if (node is BaseMethodDeclarationSyntax { Body: { } baseMethodBody })
        {
            return baseMethodBody.Span.Contains(caretPosition);
        }
 
        if (node is LocalFunctionStatementSyntax { Body: { } localFunctionBody })
        {
            return localFunctionBody.Span.Contains(caretPosition);
        }
 
        return false;
    }
 
    private static bool HasNoBrace(SyntaxNode node)
    {
        var (openBrace, closeBrace) = node.GetBraces();
        return openBrace.IsKind(SyntaxKind.None) && closeBrace.IsKind(SyntaxKind.None)
            || openBrace.IsMissing && closeBrace.IsMissing;
    }
 
    private static bool WithinEmbeddedStatement(SyntaxNode node, int caretPosition)
        => node.GetEmbeddedStatement()?.Span.Contains(caretPosition) ?? false;
 
    #endregion
 
    #region ShouldRemoveBraceCheck
 
    private static bool ShouldRemoveBraces(SyntaxNode node, int caretPosition)
        => node switch
        {
            BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode => ShouldRemoveBraceForObjectCreationExpression(baseObjectCreationExpressionNode),
            AccessorDeclarationSyntax accessorDeclarationNode => ShouldRemoveBraceForAccessorDeclaration(accessorDeclarationNode, caretPosition),
            PropertyDeclarationSyntax propertyDeclarationNode => ShouldRemoveBraceForPropertyDeclaration(propertyDeclarationNode, caretPosition),
            EventDeclarationSyntax eventDeclarationNode => ShouldRemoveBraceForEventDeclaration(eventDeclarationNode, caretPosition),
            _ => false,
        };
 
    /// <summary>
    /// Remove the braces if the BaseObjectCreationExpression has an empty Initializer.
    /// </summary>
    private static bool ShouldRemoveBraceForObjectCreationExpression(BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode)
    {
        var initializer = baseObjectCreationExpressionNode.Initializer;
        return initializer != null && initializer.Expressions.IsEmpty();
    }
 
    // Only do this when it is an accessor in property
    // Since it is illegal to have something like
    // int this[int i] { get; set;}
    // event EventHandler Bar {add; remove;}
    private static bool ShouldRemoveBraceForAccessorDeclaration(AccessorDeclarationSyntax accessorDeclarationNode, int caretPosition)
        => accessorDeclarationNode.Body != null
           && accessorDeclarationNode.Body.Statements.IsEmpty()
           && accessorDeclarationNode.ExpressionBody == null
           && accessorDeclarationNode.Parent != null
           && accessorDeclarationNode.Parent.IsParentKind(SyntaxKind.PropertyDeclaration)
           && accessorDeclarationNode.Body.Span.Contains(caretPosition);
 
    private static bool ShouldRemoveBraceForPropertyDeclaration(PropertyDeclarationSyntax propertyDeclarationNode, int caretPosition)
    {
        // If a property just has an empty accessorList, like
        // int i $${ }
        // then remove the braces and change it to a field
        // int i;
        if (propertyDeclarationNode.AccessorList != null
            && propertyDeclarationNode.ExpressionBody == null)
        {
            var accessorList = propertyDeclarationNode.AccessorList;
            return accessorList.Span.Contains(caretPosition) && accessorList.Accessors.IsEmpty();
        }
 
        return false;
    }
 
    private static bool ShouldRemoveBraceForEventDeclaration(EventDeclarationSyntax eventDeclarationNode, int caretPosition)
    {
        // If an event declaration just has an empty accessorList,
        // like
        // event EventHandler e$$  { }
        // then change it to a event field declaration
        // event EventHandler e;
        var accessorList = eventDeclarationNode.AccessorList;
        return accessorList != null
               && accessorList.Span.Contains(caretPosition)
               && accessorList.Accessors.IsEmpty();
    }
 
    #endregion
 
    #region AddBrace
 
    private static AccessorListSyntax GetAccessorListNode(SyntaxFormattingOptions formattingOptions)
        => AccessorList().WithOpenBraceToken(GetOpenBrace(formattingOptions)).WithCloseBraceToken(GetCloseBrace(formattingOptions));
 
    private static InitializerExpressionSyntax GetInitializerExpressionNode(SyntaxFormattingOptions formattingOptions)
        => InitializerExpression(SyntaxKind.ObjectInitializerExpression)
            .WithOpenBraceToken(GetOpenBrace(formattingOptions));
 
    private static BlockSyntax GetBlockNode(SyntaxFormattingOptions formattingOptions)
        => Block().WithOpenBraceToken(GetOpenBrace(formattingOptions)).WithCloseBraceToken(GetCloseBrace(formattingOptions));
 
    private static SyntaxToken GetOpenBrace(SyntaxFormattingOptions formattingOptions)
        => Token(
                leading: SyntaxTriviaList.Empty,
                kind: SyntaxKind.OpenBraceToken,
                trailing: [GetNewLineTrivia(formattingOptions)])
            .WithAdditionalAnnotations(s_openBracePositionAnnotation);
 
    private static SyntaxToken GetCloseBrace(SyntaxFormattingOptions formattingOptions)
        => Token(
            leading: SyntaxTriviaList.Empty,
            kind: SyntaxKind.CloseBraceToken,
            trailing: [GetNewLineTrivia(formattingOptions)]);
 
    private static SyntaxTrivia GetNewLineTrivia(SyntaxFormattingOptions formattingOptions)
    {
        var newLineString = formattingOptions.NewLine;
        return EndOfLine(newLineString);
    }
 
    /// <summary>
    /// Add braces to the <param name="node"/>.
    /// For FieldDeclaration and EventFieldDeclaration, it will change them to PropertyDeclaration and EventDeclaration
    /// </summary>
    private static SyntaxNode WithBraces(SyntaxNode node, SyntaxFormattingOptions formattingOptions)
        => node switch
        {
            BaseTypeDeclarationSyntax baseTypeDeclarationNode => WithBracesForBaseTypeDeclaration(baseTypeDeclarationNode, formattingOptions),
            BaseObjectCreationExpressionSyntax objectCreationExpressionNode => GetObjectCreationExpressionWithInitializer(objectCreationExpressionNode, formattingOptions),
            BaseMethodDeclarationSyntax baseMethodDeclarationNode => AddBlockToBaseMethodDeclaration(baseMethodDeclarationNode, formattingOptions),
            LocalFunctionStatementSyntax localFunctionStatementNode => AddBlockToLocalFunctionDeclaration(localFunctionStatementNode, formattingOptions),
            AccessorDeclarationSyntax accessorDeclarationNode => AddBlockToAccessorDeclaration(accessorDeclarationNode, formattingOptions),
            _ when node.IsEmbeddedStatementOwner() => AddBlockToEmbeddedStatementOwner(node, formattingOptions),
            _ => throw ExceptionUtilities.UnexpectedValue(node),
        };
 
    /// <summary>
    /// Add braces to <param name="baseTypeDeclarationNode"/>.
    /// </summary>
    private static BaseTypeDeclarationSyntax WithBracesForBaseTypeDeclaration(
        BaseTypeDeclarationSyntax baseTypeDeclarationNode,
        SyntaxFormattingOptions formattingOptions)
        => baseTypeDeclarationNode.WithOpenBraceToken(GetOpenBrace(formattingOptions))
            .WithCloseBraceToken(CloseBraceToken);
 
    /// <summary>
    /// Add an empty initializer to <param name="objectCreationExpressionNode"/>.
    /// </summary>
    private static BaseObjectCreationExpressionSyntax GetObjectCreationExpressionWithInitializer(
        BaseObjectCreationExpressionSyntax objectCreationExpressionNode,
        SyntaxFormattingOptions formattingOptions)
        => objectCreationExpressionNode.WithInitializer(GetInitializerExpressionNode(formattingOptions));
 
    /// <summary>
    /// Add an empty block to <param name="baseMethodDeclarationNode"/>.
    /// </summary>
    private static BaseMethodDeclarationSyntax AddBlockToBaseMethodDeclaration(
        BaseMethodDeclarationSyntax baseMethodDeclarationNode,
        SyntaxFormattingOptions formattingOptions)
        => baseMethodDeclarationNode.WithBody(GetBlockNode(formattingOptions))
            // When the method declaration with no body is parsed, it has an invisible trailing semicolon. Make sure it is removed.
            .WithSemicolonToken(Token(SyntaxKind.None));
 
    /// <summary>
    /// Add an empty block to <param name="localFunctionStatementNode"/>.
    /// </summary>
    private static LocalFunctionStatementSyntax AddBlockToLocalFunctionDeclaration(
        LocalFunctionStatementSyntax localFunctionStatementNode,
        SyntaxFormattingOptions formattingOptions)
        => localFunctionStatementNode.WithBody(GetBlockNode(formattingOptions))
            // When the local method declaration with no body is parsed, it has an invisible trailing semicolon. Make sure it is removed.
            .WithSemicolonToken(Token(SyntaxKind.None));
 
    /// <summary>
    /// Add an empty block to <param name="accessorDeclarationNode"/>.
    /// </summary>
    private static AccessorDeclarationSyntax AddBlockToAccessorDeclaration(
        AccessorDeclarationSyntax accessorDeclarationNode,
        SyntaxFormattingOptions formattingOptions)
        => accessorDeclarationNode.WithBody(GetBlockNode(formattingOptions))
            // When the accessor with no body is parsed, it has an invisible trailing semicolon. Make sure it is removed.
            .WithSemicolonToken(Token(SyntaxKind.None));
 
    /// <summary>
    /// Add a block with <param name="extraNodeInsertedBetweenBraces"/> to <param name="embeddedStatementOwner"/>
    /// </summary>
    private static SyntaxNode AddBlockToEmbeddedStatementOwner(
        SyntaxNode embeddedStatementOwner,
        SyntaxFormattingOptions formattingOptions,
        StatementSyntax? extraNodeInsertedBetweenBraces = null)
    {
        var block = extraNodeInsertedBetweenBraces != null
            ? GetBlockNode(formattingOptions).WithStatements(new SyntaxList<StatementSyntax>(extraNodeInsertedBetweenBraces))
            : GetBlockNode(formattingOptions);
 
        return embeddedStatementOwner switch
        {
            DoStatementSyntax doStatementNode => doStatementNode.WithStatement(block),
            CommonForEachStatementSyntax forEachStatementNode => forEachStatementNode.WithStatement(block),
            ForStatementSyntax forStatementNode => forStatementNode.WithStatement(block),
            IfStatementSyntax ifStatementNode => ifStatementNode.WithStatement(block),
            ElseClauseSyntax elseClauseNode => elseClauseNode.WithStatement(block),
            WhileStatementSyntax whileStatementNode => whileStatementNode.WithStatement(block),
            UsingStatementSyntax usingStatementNode => usingStatementNode.WithStatement(block),
            LockStatementSyntax lockStatementNode => lockStatementNode.WithStatement(block),
            _ => throw ExceptionUtilities.UnexpectedValue(embeddedStatementOwner)
        };
    }
 
    #endregion
 
    #region RemoveBrace
 
    /// <summary>
    /// Remove the brace for the input syntax node
    /// For ObjectCreationExpressionSyntax, it would remove the initializer
    /// For PropertyDeclarationSyntax, it would change it to a FieldDeclaration
    /// For EventDeclarationSyntax, it would change it to eventFieldDeclaration
    /// For Accessor, it would change it to the empty version ending with semicolon.
    /// e.g get {} => get;
    /// </summary>
    private static SyntaxNode WithoutBraces(SyntaxNode node)
        => node switch
        {
            BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode => RemoveInitializerForBaseObjectCreationExpression(baseObjectCreationExpressionNode),
            PropertyDeclarationSyntax propertyDeclarationNode => ConvertPropertyDeclarationToFieldDeclaration(propertyDeclarationNode),
            EventDeclarationSyntax eventDeclarationNode => ConvertEventDeclarationToEventFieldDeclaration(eventDeclarationNode),
            AccessorDeclarationSyntax accessorDeclarationNode => RemoveBodyForAccessorDeclarationNode(accessorDeclarationNode),
            _ => throw ExceptionUtilities.UnexpectedValue(node),
        };
 
    /// <summary>
    /// Remove the initializer for <param name="baseObjectCreationExpressionNode"/>.
    /// </summary>
    private static BaseObjectCreationExpressionSyntax RemoveInitializerForBaseObjectCreationExpression(
        BaseObjectCreationExpressionSyntax baseObjectCreationExpressionNode)
    {
        var objectCreationNodeWithoutInitializer = baseObjectCreationExpressionNode.WithInitializer(null);
        // Filter the non-comments trivia
        // e.g.
        // Bar(new Foo() // I am some comments
        // {
        //      $$
        // });
        // =>
        // Bar(new Foo() // I am some comments);
        // In this case, 'I am somme comments' has an end of line triva, if not removed, it would make
        // the final result becomes
        // Bar(new Foo() // I am some comments
        // );
        var trivia = objectCreationNodeWithoutInitializer.GetTrailingTrivia().Where(trivia => trivia.IsSingleOrMultiLineComment());
        return objectCreationNodeWithoutInitializer.WithTrailingTrivia(trivia);
    }
 
    /// <summary>
    /// Convert <param name="propertyDeclarationNode"/> to fieldDeclaration.
    /// </summary>
    private static FieldDeclarationSyntax ConvertPropertyDeclarationToFieldDeclaration(
        PropertyDeclarationSyntax propertyDeclarationNode)
        => FieldDeclaration(
            propertyDeclarationNode.AttributeLists,
            propertyDeclarationNode.Modifiers,
            VariableDeclaration(
                propertyDeclarationNode.Type,
                [VariableDeclarator(propertyDeclarationNode.Identifier)]),
            SemicolonToken);
 
    /// <summary>
    /// Convert <param name="eventDeclarationNode"/> to EventFieldDeclaration.
    /// </summary>
    private static EventFieldDeclarationSyntax ConvertEventDeclarationToEventFieldDeclaration(
        EventDeclarationSyntax eventDeclarationNode)
        => EventFieldDeclaration(
            eventDeclarationNode.AttributeLists,
            eventDeclarationNode.Modifiers,
            VariableDeclaration(
                eventDeclarationNode.Type,
                [VariableDeclarator(eventDeclarationNode.Identifier)]));
 
    /// <summary>
    /// Remove the body of <param name="accessorDeclarationNode"/>.
    /// </summary>
    private static AccessorDeclarationSyntax RemoveBodyForAccessorDeclarationNode(AccessorDeclarationSyntax accessorDeclarationNode)
        => accessorDeclarationNode
            .WithBody(null).WithoutTrailingTrivia().WithSemicolonToken(
                Token(SyntaxTriviaList.Empty, SyntaxKind.SemicolonToken, SyntaxTriviaList.Empty));
 
    #endregion
}