File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\Formatting\Rules\NewLineUserSettingFormattingRule.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces)
// 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 Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Formatting;
 
internal sealed class NewLineUserSettingFormattingRule : BaseFormattingRule
{
    private readonly CSharpSyntaxFormattingOptions _options;
 
    public NewLineUserSettingFormattingRule()
        : this(CSharpSyntaxFormattingOptions.Default)
    {
    }
 
    private NewLineUserSettingFormattingRule(CSharpSyntaxFormattingOptions options)
    {
        _options = options;
    }
 
    public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions options)
    {
        var newOptions = options as CSharpSyntaxFormattingOptions ?? CSharpSyntaxFormattingOptions.Default;
 
        if (_options.NewLines == newOptions.NewLines &&
            _options.WrappingKeepStatementsOnSingleLine == newOptions.WrappingKeepStatementsOnSingleLine)
        {
            return this;
        }
 
        return new NewLineUserSettingFormattingRule(newOptions);
    }
 
    private static bool IsControlBlock(SyntaxNode node)
    {
        RoslynDebug.Assert(node != null);
 
        if (node.IsKind(SyntaxKind.SwitchStatement))
        {
            return true;
        }
 
        var parentKind = node.Parent?.Kind();
 
        switch (parentKind.GetValueOrDefault())
        {
            case SyntaxKind.IfStatement:
            case SyntaxKind.ElseClause:
            case SyntaxKind.WhileStatement:
            case SyntaxKind.DoStatement:
            case SyntaxKind.ForEachStatement:
            case SyntaxKind.ForEachVariableStatement:
            case SyntaxKind.UsingStatement:
            case SyntaxKind.ForStatement:
            case SyntaxKind.TryStatement:
            case SyntaxKind.CatchClause:
            case SyntaxKind.FinallyClause:
            case SyntaxKind.LockStatement:
            case SyntaxKind.CheckedStatement:
            case SyntaxKind.UncheckedStatement:
            case SyntaxKind.SwitchSection:
            case SyntaxKind.FixedStatement:
            case SyntaxKind.UnsafeStatement:
                return true;
            default:
                return false;
        }
    }
 
    public override AdjustSpacesOperation? GetAdjustSpacesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustSpacesOperation nextOperation)
    {
        RoslynDebug.AssertNotNull(currentToken.Parent);
 
        var operation = nextOperation.Invoke(in previousToken, in currentToken);
 
        // } else in the if else context
        if (previousToken.IsKind(SyntaxKind.CloseBraceToken)
            && currentToken.IsKind(SyntaxKind.ElseKeyword)
            && previousToken.Parent!.Parent == currentToken.Parent.Parent)
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeElse))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * catch in the try catch context
        if (currentToken.IsKind(SyntaxKind.CatchKeyword))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeCatch))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * finally
        if (currentToken.IsKind(SyntaxKind.FinallyKeyword))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeFinally))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { in the type declaration context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent is BaseTypeDeclarationSyntax or NamespaceDeclarationSyntax)
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInTypes))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // new { - Anonymous object creation
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAnonymousTypes))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // new { - Object Initialization, or with { - Record with initializer, or is { - property pattern clauses
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken))
        {
            if (currentToken.Parent.Kind() is SyntaxKind.ObjectInitializerExpression
                or SyntaxKind.CollectionInitializerExpression
                or SyntaxKind.ArrayInitializerExpression
                or SyntaxKind.ImplicitArrayCreationExpression
                or SyntaxKind.WithInitializerExpression)
            {
                if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers))
                {
                    operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
                }
            }
            else if (currentToken.Parent.IsKind(SyntaxKind.PropertyPatternClause))
            {
                if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers))
                {
                    // Allow property patterns in switch expressions to start on their own line:
                    //
                    // var x = y switch {
                    //    { Value: true } => false,    ⬅️ This line starts with an open brace
                    //    _ => true,
                    // };
                    var isFirstTokenOfSwitchArm = currentToken.Parent.IsParentKind(SyntaxKind.RecursivePattern, out RecursivePatternSyntax? recursivePattern)
                        && recursivePattern.IsParentKind(SyntaxKind.SwitchExpressionArm, out SwitchExpressionArmSyntax? switchExpressionArm)
                        && switchExpressionArm.GetFirstToken() == currentToken;
 
                    var spacesOption = isFirstTokenOfSwitchArm
                        ? AdjustSpacesOption.ForceSpacesIfOnSingleLine
                        : AdjustSpacesOption.ForceSpaces;
                    operation = CreateAdjustSpacesOperation(1, spacesOption);
                }
            }
        }
 
        var currentTokenParentParent = currentToken.Parent.Parent;
 
        // * { - in the member declaration context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent is MemberDeclarationSyntax)
        {
            var option = currentTokenParentParent is BasePropertyDeclarationSyntax
                ? _options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInProperties)
                : _options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInMethods);
 
            if (!option)
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent is AccessorDeclarationSyntax)
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAccessors))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { - in the anonymous Method context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent.IsKind(SyntaxKind.AnonymousMethodExpression))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAnonymousMethods))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { - in the local function context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent.IsKind(SyntaxKind.LocalFunctionStatement))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInMethods))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { - in the Lambda context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) &&
           currentTokenParentParent is (kind: SyntaxKind.SimpleLambdaExpression or SyntaxKind.ParenthesizedLambdaExpression))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInLambdaExpressionBody))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { - in the switch expression context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent.IsKind(SyntaxKind.SwitchExpression))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // * { - in the control statement context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && IsControlBlock(currentToken.Parent))
        {
            if (!_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInControlBlocks))
            {
                operation = CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        return operation;
    }
 
    public override AdjustNewLinesOperation? GetAdjustNewLinesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustNewLinesOperation nextOperation)
    {
        RoslynDebug.AssertNotNull(currentToken.Parent);
 
        var operation = nextOperation.Invoke(in previousToken, in currentToken);
 
        // else condition is actually handled in the GetAdjustSpacesOperation()
 
        // For Object Initialization Expression
        if (previousToken.IsKind(SyntaxKind.CommaToken) && previousToken.Parent.IsKind(SyntaxKind.ObjectInitializerExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeMembersInObjectInitializers))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                // we never force it to move up unless it is already on same line
                return CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines);
            }
        }
 
        // For Anonymous Object Creation Expression
        if (previousToken.IsKind(SyntaxKind.CommaToken) && previousToken.Parent.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeMembersInAnonymousTypes))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                // we never force it to move up unless it is already on same line
                return CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines);
            }
        }
 
        // } else in the if else context
        if (previousToken.IsKind(SyntaxKind.CloseBraceToken) && currentToken.IsKind(SyntaxKind.ElseKeyword))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeElse)
                || previousToken.Parent!.Parent != currentToken.Parent.Parent)
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * catch in the try catch context
        if (currentToken.IsKind(SyntaxKind.CatchKeyword))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeCatch))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * Finally
        if (currentToken.IsKind(SyntaxKind.FinallyKeyword))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeFinally))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the type declaration context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent is BaseTypeDeclarationSyntax or NamespaceDeclarationSyntax)
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInTypes))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // new { - Anonymous object creation
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAnonymousTypes))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // new MyObject { - Object Initialization
        // new List<int> { - Collection Initialization
        // with { - Record with initializer
        // is { - property pattern clauses
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) &&
            currentToken.Parent.Kind() is SyntaxKind.ObjectInitializerExpression or SyntaxKind.CollectionInitializerExpression or SyntaxKind.WithInitializerExpression or SyntaxKind.PropertyPatternClause)
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // Array Initialization Expression
        // int[] arr = new int[] {
        //             new[] {
        //             { - Implicit Array
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) &&
            currentToken.Parent.Kind() is SyntaxKind.ArrayInitializerExpression or SyntaxKind.ImplicitArrayCreationExpression)
        {
            return null;
        }
 
        var currentTokenParentParent = currentToken.Parent.Parent;
 
        // * { - in the member declaration context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent is MemberDeclarationSyntax)
        {
            var option = currentTokenParentParent is BasePropertyDeclarationSyntax
                ? _options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInProperties)
                : _options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInMethods);
 
            if (option)
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the property accessor context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent is AccessorDeclarationSyntax)
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAccessors))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the anonymous Method context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent.IsKind(SyntaxKind.AnonymousMethodExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInAnonymousMethods))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.ForceLinesIfOnSingleLine);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the local function context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentTokenParentParent.IsKind(SyntaxKind.LocalFunctionStatement))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInMethods))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the simple Lambda context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) &&
           currentTokenParentParent is (kind: SyntaxKind.SimpleLambdaExpression or SyntaxKind.ParenthesizedLambdaExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInLambdaExpressionBody))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.ForceLinesIfOnSingleLine);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the switch expression context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && currentToken.Parent.IsKind(SyntaxKind.SwitchExpression))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // * { - in the control statement context
        if (currentToken.IsKind(SyntaxKind.OpenBraceToken) && IsControlBlock(currentToken.Parent))
        {
            if (_options.NewLines.HasFlag(NewLinePlacement.BeforeOpenBraceInControlBlocks))
            {
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
            else
            {
                return null;
            }
        }
 
        // Wrapping - Leave statements on same line (false):
        // Insert a newline between the previous statement and this one.
        // ; *
        if (previousToken.IsKind(SyntaxKind.SemicolonToken)
            && (previousToken.Parent is StatementSyntax && !previousToken.Parent.IsKind(SyntaxKind.ForStatement))
            && !_options.WrappingKeepStatementsOnSingleLine)
        {
            return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
        }
 
        return operation;
    }
}