File: src\Analyzers\CSharp\CodeFixes\UseConditionalExpression\MultiLineConditionalExpressionFormattingRule.cs
Web Access
Project: src\src\CodeStyle\CSharp\CodeFixes\Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes)
// 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.Generic;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Shared.Extensions;
using static Microsoft.CodeAnalysis.UseConditionalExpression.UseConditionalExpressionCodeFixHelpers;
 
namespace Microsoft.CodeAnalysis.CSharp.UseConditionalExpression;
 
/// <summary>
/// Special formatting rule that will convert a conditional expression into the following
/// form if it has the <see cref="SpecializedFormattingAnnotation"/> on it:
/// 
/// <code>
///     var v = expr
///         ? whenTrue
///         : whenFalse
/// </code>
/// 
/// i.e. both branches will be on a newline, indented once from the parent indentation.
/// </summary>
internal class MultiLineConditionalExpressionFormattingRule : AbstractFormattingRule
{
    public static readonly AbstractFormattingRule Instance = new MultiLineConditionalExpressionFormattingRule();
 
    private MultiLineConditionalExpressionFormattingRule()
    {
    }
 
    private static bool IsQuestionOrColonOfNewConditional(SyntaxToken token)
        => token.Kind() is SyntaxKind.QuestionToken or SyntaxKind.ColonToken && token.GetRequiredParent().HasAnnotation(SpecializedFormattingAnnotation);
 
    public override AdjustNewLinesOperation? GetAdjustNewLinesOperation(
        in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustNewLinesOperation nextOperation)
    {
        if (IsQuestionOrColonOfNewConditional(currentToken))
        {
            // We want to force the ? and : to each be put onto the following line.
            return FormattingOperations.CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.ForceLines);
        }
 
        return nextOperation.Invoke(in previousToken, in currentToken);
    }
 
    public override void AddIndentBlockOperations(
        List<IndentBlockOperation> list, SyntaxNode node, in NextIndentBlockOperationAction nextOperation)
    {
        if (node.HasAnnotation(SpecializedFormattingAnnotation) &&
            node is ConditionalExpressionSyntax conditional)
        {
            var statement = conditional.FirstAncestorOrSelf<StatementSyntax>();
            if (statement != null)
            {
                var baseToken = statement.GetFirstToken();
 
                // we want to indent the ? and : in one level from the containing statement.
                list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(
                    baseToken, conditional.QuestionToken, conditional.WhenTrue.GetLastToken(),
                    indentationDelta: 1, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine));
                list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(
                    baseToken, conditional.ColonToken, conditional.WhenFalse.GetLastToken(),
                    indentationDelta: 1, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine));
                return;
            }
        }
 
        nextOperation.Invoke();
    }
}