File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\Formatting\Rules\SpacingFormattingRule.cs
Web Access
Project: src\src\CodeStyle\CSharp\Analyzers\Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle)
// 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;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Formatting;
 
internal sealed class SpacingFormattingRule : BaseFormattingRule
{
    private readonly CSharpSyntaxFormattingOptions _options;
 
    public SpacingFormattingRule()
        : this(CSharpSyntaxFormattingOptions.Default)
    {
    }
 
    private SpacingFormattingRule(CSharpSyntaxFormattingOptions options)
    {
        _options = options;
    }
 
    public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions options)
    {
        var newOptions = options as CSharpSyntaxFormattingOptions ?? CSharpSyntaxFormattingOptions.Default;
 
        if (_options.Spacing == newOptions.Spacing &&
            _options.SpacingAroundBinaryOperator == newOptions.SpacingAroundBinaryOperator)
        {
            return this;
        }
 
        return new SpacingFormattingRule(newOptions);
    }
 
    public override AdjustSpacesOperation? GetAdjustSpacesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustSpacesOperation nextOperation)
    {
        RoslynDebug.Assert(previousToken.Parent != null && currentToken.Parent != null);
 
        var previousKind = previousToken.Kind();
        var currentKind = currentToken.Kind();
        var previousParentKind = previousToken.Parent.Kind();
        var currentParentKind = currentToken.Parent.Kind();
 
        // For Method Declaration
        if (currentToken.IsOpenParenInParameterList() && previousKind == SyntaxKind.IdentifierToken)
        {
            // Parenthesized lambda with explicit return type.
            if (currentToken.IsOpenParenInParameterListOfParenthesizedLambdaExpression())
            {
                return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodDeclarationName));
        }
 
        // For Generic Method Declaration
        if (currentToken.IsOpenParenInParameterList() && previousKind == SyntaxKind.GreaterThanToken)
        {
            // Parenthesized lambda with explicit generic return type.
            if (currentToken.IsOpenParenInParameterListOfParenthesizedLambdaExpression() && previousParentKind == SyntaxKind.TypeArgumentList)
            {
                return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            if (previousParentKind == SyntaxKind.TypeParameterList)
            {
                return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodDeclarationName));
            }
        }
 
        // Case: public static implicit operator string(Program p) { return null; }
        // Case: public static implicit operator int?(Program p) { return null; }
        // Case: public static implicit operator int*(Program p) { return null; }
        // Case: public static implicit operator int[](Program p) { return null; }
        // Case: public static implicit operator (int, int)(Program p) { return null; }
        // Case: public static implicit operator Action<int>(Program p) { return null; }
        if ((previousToken.IsKeyword() || previousToken.Kind() is SyntaxKind.QuestionToken or SyntaxKind.AsteriskToken or SyntaxKind.CloseBracketToken or SyntaxKind.CloseParenToken or SyntaxKind.GreaterThanToken)
            && currentToken.IsOpenParenInParameterListOfAConversionOperatorDeclaration())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodDeclarationName));
        }
 
        // Case: public static Program operator !(Program p) { return null; }
        if (previousToken.Parent.IsKind(SyntaxKind.OperatorDeclaration) && currentToken.IsOpenParenInParameterListOfAOperationDeclaration())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodDeclarationName));
        }
 
        if (previousToken.IsOpenParenInParameterList() && currentToken.IsCloseParenInParameterList())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BetweenEmptyMethodDeclarationParentheses));
        }
 
        if (previousToken.IsOpenParenInParameterList())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodDeclarationParenthesis));
        }
 
        if (currentToken.IsCloseParenInParameterList())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodDeclarationParenthesis));
        }
 
        // For Method Call
        //   MethodName ( args )
        // Or Positional Pattern
        //   x is TypeName ( args )
        if (currentToken.IsOpenParenInArgumentListOrPositionalPattern())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodCallName));
        }
 
        if (previousToken.IsOpenParenInArgumentListOrPositionalPattern() && currentToken.IsCloseParenInArgumentListOrPositionalPattern())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BetweenEmptyMethodCallParentheses));
        }
 
        if (previousToken.IsOpenParenInArgumentListOrPositionalPattern())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodCallParentheses));
        }
 
        if (currentToken.IsCloseParenInArgumentListOrPositionalPattern())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodCallParentheses));
        }
 
        // For spacing around: typeof, default, and sizeof; treat like a Method Call
        if (currentKind == SyntaxKind.OpenParenToken && IsFunctionLikeKeywordExpressionKind(currentParentKind))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterMethodCallName));
        }
 
        if (previousKind == SyntaxKind.OpenParenToken && IsFunctionLikeKeywordExpressionKind(previousParentKind))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodCallParentheses));
        }
 
        if (currentKind == SyntaxKind.CloseParenToken && IsFunctionLikeKeywordExpressionKind(currentParentKind))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodCallParentheses));
        }
 
        // For Spacing b/n control flow keyword and paren. Parent check not needed.
        if (currentKind == SyntaxKind.OpenParenToken &&
            (previousKind == SyntaxKind.IfKeyword || previousKind == SyntaxKind.WhileKeyword || previousKind == SyntaxKind.SwitchKeyword ||
            previousKind == SyntaxKind.ForKeyword || previousKind == SyntaxKind.ForEachKeyword || previousKind == SyntaxKind.CatchKeyword ||
            previousKind == SyntaxKind.UsingKeyword || previousKind == SyntaxKind.WhenKeyword || previousKind == SyntaxKind.LockKeyword ||
            previousKind == SyntaxKind.FixedKeyword))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterControlFlowStatementKeyword));
        }
 
        // For spacing between parenthesis and expression
        if ((previousToken.Parent.Kind() is SyntaxKind.ParenthesizedExpression or SyntaxKind.ParenthesizedPattern && previousKind == SyntaxKind.OpenParenToken) ||
            (currentToken.Parent.Kind() is SyntaxKind.ParenthesizedExpression or SyntaxKind.ParenthesizedPattern && currentKind == SyntaxKind.CloseParenToken))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinExpressionParentheses));
        }
 
        // For spacing between the parenthesis and the cast expression
        if ((previousParentKind == SyntaxKind.CastExpression && previousKind == SyntaxKind.OpenParenToken) ||
            (currentParentKind == SyntaxKind.CastExpression && currentKind == SyntaxKind.CloseParenToken))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinCastParentheses));
        }
 
        // Semicolons in an empty for statement.  i.e.   for(;;)
        if (previousParentKind == SyntaxKind.ForStatement
            && IsEmptyForStatement((ForStatementSyntax)previousToken.Parent!))
        {
            if (currentKind == SyntaxKind.SemicolonToken
                && (previousKind != SyntaxKind.SemicolonToken
                    || _options.Spacing.HasFlag(SpacePlacement.BeforeSemicolonsInForStatement)))
            {
                return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeSemicolonsInForStatement));
            }
 
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterSemicolonsInForStatement));
        }
 
        // For spacing between the parenthesis and the expression inside the control flow expression
        if (previousKind == SyntaxKind.OpenParenToken && IsControlFlowLikeKeywordStatementKind(previousParentKind))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinOtherParentheses));
        }
 
        if (currentKind == SyntaxKind.CloseParenToken && IsControlFlowLikeKeywordStatementKind(currentParentKind))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinOtherParentheses));
        }
 
        // For spacing after the cast
        if (previousParentKind == SyntaxKind.CastExpression && previousKind == SyntaxKind.CloseParenToken)
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterCast));
        }
 
        // List patterns
        if (currentKind == SyntaxKind.OpenBracketToken && currentToken.Parent.Kind() is SyntaxKind.ListPattern or SyntaxKind.CollectionExpression)
        {
            // For the space after the middle comma in ([1, 2], [1, 2])
            if (previousKind == SyntaxKind.CommaToken)
            {
                return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterComma));
            }
 
            // For "is [", "and [", but not "([" or "[["
            if (previousKind is not (SyntaxKind.OpenParenToken or SyntaxKind.OpenBracketToken))
            {
                return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
        }
 
        // For spacing Before Square Braces
        if (currentKind == SyntaxKind.OpenBracketToken && HasFormattableBracketParent(currentToken) && !previousToken.IsOpenBraceOrCommaOfObjectInitializer())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeOpenSquareBracket));
        }
 
        // For spacing empty square braces, also treat [,] as empty
        if (((currentKind == SyntaxKind.CloseBracketToken && previousKind == SyntaxKind.OpenBracketToken)
            || currentKind == SyntaxKind.OmittedArraySizeExpressionToken)
            && HasFormattableBracketParent(previousToken))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BetweenEmptySquareBrackets));
        }
 
        // For spacing square brackets within
        if (previousKind == SyntaxKind.OpenBracketToken && HasFormattableBracketParent(previousToken))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinSquareBrackets));
        }
 
        if (currentKind == SyntaxKind.CloseBracketToken && previousKind != SyntaxKind.OmittedArraySizeExpressionToken && HasFormattableBracketParent(currentToken))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinSquareBrackets));
        }
 
        // attribute case ] *
        if (previousKind == SyntaxKind.CloseBracketToken && previousToken.Parent.IsKind(SyntaxKind.AttributeList))
        {
            // [Attribute1]$$[Attribute2]
            if (currentToken.IsKind(SyntaxKind.OpenBracketToken) &&
                currentToken.Parent.IsKind(SyntaxKind.AttributeList))
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            // [Attribute1]$${EOF}
            if (currentToken.IsKind(SyntaxKind.EndOfFileToken))
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            // [Attribute]$$ int Prop { ... }
            return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
        }
 
        // For spacing delimiters - after colon
        if (previousToken.IsColonInTypeBaseList())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterColonInBaseTypeDeclaration));
        }
 
        // For spacing delimiters - before colon
        if (currentToken.IsColonInTypeBaseList())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeColonInBaseTypeDeclaration));
        }
 
        // For spacing delimiters - after comma
        if ((previousToken.IsCommaInArgumentOrParameterList() && currentKind != SyntaxKind.OmittedTypeArgumentToken)
            || previousToken.IsCommaInInitializerExpression()
            || previousToken.IsCommaInCollectionExpression()
            || (previousKind == SyntaxKind.CommaToken
                && currentKind != SyntaxKind.OmittedArraySizeExpressionToken
                && HasFormattableBracketParent(previousToken)))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterComma));
        }
 
        // For spacing delimiters - before comma
        if ((currentToken.IsCommaInArgumentOrParameterList() && previousKind != SyntaxKind.OmittedTypeArgumentToken)
            || currentToken.IsCommaInInitializerExpression()
            || previousToken.IsCommaInCollectionExpression()
            || (currentKind == SyntaxKind.CommaToken
                && previousKind != SyntaxKind.OmittedArraySizeExpressionToken
                && HasFormattableBracketParent(currentToken)))
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeComma));
        }
 
        // For Spacing delimiters - after Dot
        if (previousToken.IsDotInMemberAccessOrQualifiedName())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterDot));
        }
 
        // For spacing delimiters - before Dot
        if (currentToken.IsDotInMemberAccessOrQualifiedName())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeDot));
        }
 
        // For spacing delimiters - after semicolon
        if (previousToken.IsSemicolonInForStatement() && currentKind != SyntaxKind.CloseParenToken)
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterSemicolonsInForStatement));
        }
 
        // For spacing delimiters - before semicolon
        if (currentToken.IsSemicolonInForStatement())
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.BeforeSemicolonsInForStatement));
        }
 
        // For spacing around the binary operators
        if (currentToken.Parent is BinaryExpressionSyntax ||
            previousToken.Parent is BinaryExpressionSyntax ||
            currentToken.Parent is AssignmentExpressionSyntax ||
            previousToken.Parent is AssignmentExpressionSyntax ||
            currentToken.Parent.Kind() is SyntaxKind.AndPattern or SyntaxKind.OrPattern or SyntaxKind.RelationalPattern ||
            previousToken.Parent.Kind() is SyntaxKind.AndPattern or SyntaxKind.OrPattern or SyntaxKind.RelationalPattern)
        {
            switch (_options.SpacingAroundBinaryOperator)
            {
                case BinaryOperatorSpacingOptions.Single:
                    return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                case BinaryOperatorSpacingOptions.Remove:
                    if (currentKind == SyntaxKind.IsKeyword ||
                        currentKind == SyntaxKind.AsKeyword ||
                        currentKind == SyntaxKind.AndKeyword ||
                        currentKind == SyntaxKind.OrKeyword ||
                        previousKind == SyntaxKind.IsKeyword ||
                        previousKind == SyntaxKind.AsKeyword ||
                        previousKind == SyntaxKind.AndKeyword ||
                        previousKind == SyntaxKind.OrKeyword)
                    {
                        // User want spaces removed but at least one is required for the "as" & "is" keyword
                        return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                    }
                    else
                    {
                        return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                    }
                case BinaryOperatorSpacingOptions.Ignore:
                    return CreateAdjustSpacesOperation(0, AdjustSpacesOption.PreserveSpaces);
                default:
                    System.Diagnostics.Debug.Assert(false, "Invalid BinaryOperatorSpacingOptions");
                    break;
            }
        }
 
        // Function pointer type adjustments
        if (previousParentKind == SyntaxKind.FunctionPointerType)
        {
            // No spacing between delegate and *
            if (currentKind == SyntaxKind.AsteriskToken && previousKind == SyntaxKind.DelegateKeyword)
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            // Force a space between * and the calling convention
            if (previousKind == SyntaxKind.AsteriskToken && currentParentKind == SyntaxKind.FunctionPointerCallingConvention)
            {
                switch (currentKind)
                {
                    case SyntaxKind.IdentifierToken:
                    case SyntaxKind.ManagedKeyword:
                    case SyntaxKind.UnmanagedKeyword:
                        return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                }
            }
        }
 
        if (currentParentKind == SyntaxKind.FunctionPointerParameterList && currentKind == SyntaxKind.LessThanToken)
        {
            switch (previousKind)
            {
                // No spacing between the * and < tokens if there is no calling convention
                case SyntaxKind.AsteriskToken:
                // No spacing between the calling convention and opening angle bracket of function pointer types:
                // delegate* managed<
                case SyntaxKind.ManagedKeyword:
                case SyntaxKind.UnmanagedKeyword:
                // No spacing between the calling convention specifier and the opening angle
                // delegate* unmanaged[Cdecl]<
                case SyntaxKind.CloseBracketToken when previousParentKind == SyntaxKind.FunctionPointerUnmanagedCallingConventionList:
                    return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
        }
 
        // No space between unmanaged and the [
        // delegate* unmanaged[
        if (previousParentKind == SyntaxKind.FunctionPointerCallingConvention && currentParentKind == SyntaxKind.FunctionPointerUnmanagedCallingConventionList && currentKind == SyntaxKind.OpenBracketToken)
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
        }
 
        // Function pointer calling convention adjustments
        if (currentParentKind == SyntaxKind.FunctionPointerUnmanagedCallingConventionList && previousParentKind == SyntaxKind.FunctionPointerUnmanagedCallingConventionList)
        {
            if (currentKind == SyntaxKind.IdentifierToken)
            {
                // No space after the [
                // unmanaged[Cdecl
                if (previousKind == SyntaxKind.OpenBracketToken)
                {
                    return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                }
                // Space after the ,
                // unmanaged[Cdecl, Thiscall
                else if (previousKind == SyntaxKind.CommaToken)
                {
                    return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
                }
            }
 
            // No space between identifier and comma
            // unmanaged[Cdecl,
            if (currentKind == SyntaxKind.CommaToken)
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
 
            // No space before the ]
            // unmanaged[Cdecl]
            if (currentKind == SyntaxKind.CloseBracketToken)
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
            }
        }
 
        // Respect spacing setting for after the < in function pointer parameter lists
        // delegate*<void
        if (previousKind == SyntaxKind.LessThanToken && previousParentKind == SyntaxKind.FunctionPointerParameterList)
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodDeclarationParenthesis));
        }
 
        // Respect spacing setting for before the > in function pointer parameter lists
        // delegate*<void>
        if (currentKind == SyntaxKind.GreaterThanToken && currentParentKind == SyntaxKind.FunctionPointerParameterList)
        {
            return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.WithinMethodDeclarationParenthesis));
        }
 
        // For spacing after the 'not' pattern operator
        if (previousToken.Parent.IsKind(SyntaxKind.NotPattern))
        {
            return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
        }
 
        // Slice pattern:
        // .. var x
        if (previousKind == SyntaxKind.DotDotToken && previousParentKind == SyntaxKind.SlicePattern)
        {
            return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine);
        }
 
        // No space after $" and $@" and @$" at the start of an interpolated string
        if (previousKind is SyntaxKind.InterpolatedStringStartToken or
                            SyntaxKind.InterpolatedVerbatimStringStartToken or
                            SyntaxKind.InterpolatedSingleLineRawStringStartToken or
                            SyntaxKind.InterpolatedMultiLineRawStringStartToken)
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // No space before " at the end of an interpolated string
        if (currentKind is SyntaxKind.InterpolatedStringEndToken or
                           SyntaxKind.InterpolatedRawStringEndToken)
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // No space before { or after } in interpolations
        if ((currentKind == SyntaxKind.OpenBraceToken && currentToken.Parent is InterpolationSyntax) ||
            (previousKind == SyntaxKind.CloseBraceToken && previousToken.Parent is InterpolationSyntax))
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // No space after { in interpolations (i.e. between the braces and the expression)
        if (previousKind == SyntaxKind.OpenBraceToken && previousToken.Parent is InterpolationSyntax)
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // Handle space before } in interpolations (i.e. between the braces and the expression)
        if (currentKind == SyntaxKind.CloseBraceToken && currentToken.Parent is InterpolationSyntax interpolation)
        {
            // If there is no format specifier (i.e. a colon) remove spaces
            if (interpolation.FormatClause is null)
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
 
            // If there is a format specifier then whitespace is significant so preserve it
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.PreserveSpaces);
        }
 
        // No space before or after , in interpolation alignment clause
        if ((previousKind == SyntaxKind.CommaToken && previousToken.Parent is InterpolationAlignmentClauseSyntax) ||
            (currentKind == SyntaxKind.CommaToken && currentToken.Parent is InterpolationAlignmentClauseSyntax))
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // No space before or after : in interpolation format clause
        if ((previousKind == SyntaxKind.ColonToken && previousToken.Parent is InterpolationFormatClauseSyntax) ||
            (currentKind == SyntaxKind.ColonToken && currentToken.Parent is InterpolationFormatClauseSyntax))
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // Always put a space in the var form of deconstruction-declaration
        if (currentToken.IsOpenParenInVarDeconstructionDeclaration())
        {
            return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpaces);
        }
 
        // Index expressions
        if (previousKind == SyntaxKind.CaretToken && previousParentKind == SyntaxKind.IndexExpression)
        {
            return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
        }
 
        // Right of Range expressions
        if (previousKind == SyntaxKind.DotDotToken && previousParentKind == SyntaxKind.RangeExpression)
        {
            var rangeExpression = (RangeExpressionSyntax)previousToken.Parent!;
            var hasRightOperand = rangeExpression.RightOperand != null;
            if (hasRightOperand)
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        // Left of Range expressions
        if (currentKind == SyntaxKind.DotDotToken && currentParentKind == SyntaxKind.RangeExpression)
        {
            var rangeExpression = (RangeExpressionSyntax)currentToken.Parent!;
            var hasLeftOperand = rangeExpression.LeftOperand != null;
            if (hasLeftOperand)
            {
                return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpaces);
            }
        }
 
        return nextOperation.Invoke(in previousToken, in currentToken);
    }
 
    public override void AddSuppressOperations(ArrayBuilder<SuppressOperation> list, SyntaxNode node, in NextSuppressOperationAction nextOperation)
    {
        nextOperation.Invoke();
 
        SuppressVariableDeclaration(list, node);
    }
 
    private static bool IsEmptyForStatement(ForStatementSyntax forStatement)
        => forStatement.Initializers.Count == 0
        && forStatement.Declaration == null
        && forStatement.Condition == null
        && forStatement.Incrementors.Count == 0;
 
    private void SuppressVariableDeclaration(ArrayBuilder<SuppressOperation> list, SyntaxNode node)
    {
        if (node.Kind()
                is SyntaxKind.FieldDeclaration
                or SyntaxKind.EventDeclaration
                or SyntaxKind.EventFieldDeclaration
                or SyntaxKind.LocalDeclarationStatement
                or SyntaxKind.EnumMemberDeclaration)
        {
            if (_options.Spacing.HasFlag(SpacePlacement.IgnoreAroundVariableDeclaration))
            {
                var firstToken = node.GetFirstToken(includeZeroWidth: true);
                var lastToken = node.GetLastToken(includeZeroWidth: true);
 
                list.Add(FormattingOperations.CreateSuppressOperation(firstToken, lastToken, SuppressOption.NoSpacing));
            }
        }
    }
 
    private static AdjustSpacesOperation AdjustSpacesOperationZeroOrOne(bool option, AdjustSpacesOption explicitOption = AdjustSpacesOption.ForceSpacesIfOnSingleLine)
    {
        if (option)
        {
            return CreateAdjustSpacesOperation(1, explicitOption);
        }
        else
        {
            return CreateAdjustSpacesOperation(0, explicitOption);
        }
    }
 
    private static bool HasFormattableBracketParent(SyntaxToken token)
        => token.Parent is (kind: SyntaxKind.ArrayRankSpecifier or SyntaxKind.BracketedArgumentList or SyntaxKind.BracketedParameterList or SyntaxKind.ImplicitArrayCreationExpression or SyntaxKind.ListPattern or SyntaxKind.CollectionExpression);
 
    private static bool IsFunctionLikeKeywordExpressionKind(SyntaxKind syntaxKind)
        => (syntaxKind is SyntaxKind.TypeOfExpression or SyntaxKind.DefaultExpression or SyntaxKind.SizeOfExpression);
 
    private static bool IsControlFlowLikeKeywordStatementKind(SyntaxKind syntaxKind)
    {
        return syntaxKind is SyntaxKind.IfStatement or SyntaxKind.WhileStatement or SyntaxKind.SwitchStatement or
            SyntaxKind.ForStatement or SyntaxKind.ForEachStatement or SyntaxKind.ForEachVariableStatement or
            SyntaxKind.DoStatement or
            SyntaxKind.CatchDeclaration or SyntaxKind.UsingStatement or SyntaxKind.LockStatement or
            SyntaxKind.FixedStatement or SyntaxKind.CatchFilterClause;
    }
}