private void DescribeNestedAlignment()

in ReSharper.FSharp/src/FSharp/FSharp.Psi/src/CodeFormatter/FSharpCodeFormatterInfoProvider.cs [720:1100]


    private void DescribeNestedAlignment(string title, NodeType nodeType) =>
      Describe<IndentingRule>()
        .Name(title)
        .Where(Node().In(nodeType).Satisfies((node, _) => !IsNestedRefOrAppExpr(node.NodeOrNull)))
        .Return(IndentType.AlignThrough)
        .Build();

    private void DescribeChildrenAlignment<TParent>(IBuilderAction<IBlankWithSinglePattern> parentPattern,
      IBuilderAction<IBlankWithSinglePattern> nodeParent, Func<TParent, IEnumerable<ITreeNode>> childrenGetter) =>
      Describe<IndentingRule>()
        .Name("ListLikePatLikeAlignment")
        .Where(parentPattern, nodeParent)
        .CloseNodeGetter((node, context) => new VirtNode(context, childrenGetter((TParent) node.Parent.NodeOrNull).LastOrDefault()))
        .Return(IndentType.AlignThrough)
        .Build();

    private void DescribeChildrenAlignment<TParent>(NodeTypeSet parent, NodeTypeSet children,
      Func<TParent, IEnumerable<ITreeNode>> childrenGetter) =>
      DescribeChildrenAlignment(
        Parent().In(parent), Node().In(children).Satisfies(IsFirstNodeOfTypeSet(children, false)), childrenGetter);

    private void DescribeChildrenAlignment<TParent>(NodeType parent, NodeType children,
      Func<TParent, IEnumerable<ITreeNode>> childrenGetter) =>
      DescribeChildrenAlignment(
        Parent().In(parent), Node().In(children).Satisfies(IsFirstNodeOfItsType), childrenGetter);

    private void Formatting()
    {
      var nodesWithSpaces =
        new NodeTypeSet(
          ElementType.MATCH_EXPR,

          ElementType.CHAR_RANGE_PAT,
          ElementType.IS_INST_PAT,
          ElementType.LIST_CONS_PAT,
          ElementType.TYPED_PAT,

          ElementType.MATCH_CLAUSE,

          ElementType.F_SHARP_TYPE_DECLARATION,
          ElementType.PRIMARY_CONSTRUCTOR_DECLARATION,
          ElementType.SECONDARY_CONSTRUCTOR_DECLARATION,

          ElementType.ENUM_CASE_DECLARATION,
          ElementType.UNION_CASE_DECLARATION,
          ElementType.UNION_CASE_FIELD_DECLARATION_LIST,

          ElementType.FUNCTION_TYPE_USAGE,
          ElementType.TUPLE_TYPE_USAGE,

          ElementType.LOCAL_BINDING,
          ElementType.TOP_BINDING,

          ElementType.INTERFACE_IMPLEMENTATION);

      Describe<FormattingRule>()
        .Name("DeclarationsSpaces")
        .Group(SpaceRuleGroup)
        .Where(Parent().In(nodesWithSpaces))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Name("MultilineMatchExpr")
        .Group(SpaceRuleGroup)
        .Priority(100)
        .Where(
          Parent().In(ElementType.MATCH_EXPR, ElementType.MATCH_LAMBDA_EXPR),
          Right().In(ElementType.MATCH_CLAUSE)
        )
        .Return(IntervalFormatType.InsertSpace)
        .Build();


      Describe<FormattingRule>()
        .Name("GoodPlacesToWrap")
        .Group(WrapRuleGroup | LineBreaksRuleGroup)
        .Where(Parent().In(ElementType.PREFIX_APP_EXPR))
        .Return(IntervalFormatType.GoodPlaceToWrap)
        .Build();

      var membersBitset =
        ElementBitsets.MODULE_MEMBER_BIT_SET.Union(ElementBitsets.TYPE_BODY_MEMBER_DECLARATION_BIT_SET);

      Describe<FormattingRule>()
        .Name("LineBreaksBetweenMembers")
        .Group(LineBreaksRuleGroup)
        .Where(Right().In(membersBitset))
        .Return(IntervalFormatType.NewLine)
        .Build();

      // Describe<FormattingRule>()
      //   .Name("PipeNewLine")
      //   .Group(LineBreaksRuleGroup)
      //   .Where(
      //     Left().In(ElementBitsets.F_SHARP_EXPRESSION_BIT_SET),
      //     Right().In(FSharpTokenType.SYMBOLIC_OP), // todo: check pipe op
      //     If((context, formattingContext) => )
      //   ) 
      //   .Return(IntervalFormatType.NewLine)
      //   .Build();

      Describe<FormattingRule>()
        .Name("LineBreakBeforeBindingInExpr")
        .Group(LineBreaksRuleGroup)
        .Where(
          Parent().In(ElementType.LET_OR_USE_EXPR).Satisfies((node, _) => ((ILetOrUseExpr)node.Node).InKeyword == null),
          Right().In(ElementBitsets.F_SHARP_EXPRESSION_BIT_SET)
        )
        .Return(IntervalFormatType.NewLine)
        .Build();

      // todo: members
      Describe<FormattingRule>()
        .Name("GoodPlacesToWrap")
        .Group(WrapRuleGroup | LineBreaksRuleGroup)
        .Where(Parent().In(ElementBitsets.BINDING_BIT_SET), Left().In(FSharpTokenType.EQUALS))
        .Return(IntervalFormatType.GoodPlaceToWrap)
        .Build();

      Describe<FormattingRule>()
        .Name("ProhibitWrappingBeforeSemicolon")
        .Group(WrapRuleGroup | LineBreaksRuleGroup)
        .Where(Right().In(FSharpTokenType.SEMICOLON, FSharpTokenType.COMMA, FSharpTokenType.THEN))
        .Return(IntervalFormatType.NoWrap)
        .Build();


      Describe<FormattingRule>()
        .Name("PreferWrappingAfterSemicolon")
        .Group(WrapRuleGroup | LineBreaksRuleGroup)
        .Where(Left().In(FSharpTokenType.SEMICOLON, FSharpTokenType.COMMA))
        .Return(IntervalFormatType.ExcellentPlaceToWrap)
        .Build();

      Describe<FormattingRule>()
        .Name("SpaceAfterPunctuation")
        .Group(SpaceRuleGroup)
        .Where(Left().In(FSharpTokenType.COLON, FSharpTokenType.RARROW, ElementType.RETURN_TYPE_INFO))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Name("SpaceBeforeColon")
        .Group(SpaceRuleGroup)
        .Where(Right().In(ElementType.RETURN_TYPE_INFO, FSharpTokenType.COLON))
        .Switch(it => it.SpaceBeforeColon, SpaceOptionsBuilders)
        .Build();

      Describe<FormattingRule>()
        .Name("SpaceBeforeColon")
        .Group(SpaceRuleGroup)
        .Priority(100)
        .Where(
          Parent().In(ElementBitsets.BINDING_BIT_SET),
          Right().In(ElementType.RETURN_TYPE_INFO).Satisfies((node, _) => (node.Node.Parent as IBinding)?.HasParameters ?? false)
        )
        .Return(IntervalFormatType.Space)
        .Build();


      Describe<FormattingRule>()
        .Name("SpaceAfterComma")
        .Group(SpaceRuleGroup)
        .Where(Left().In(FSharpTokenType.COMMA))
        .Switch(it => it.SpaceAfterComma, SpaceOptionsBuilders)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceBeforeSeparators")
        .Group(SpaceRuleGroup)
        .Where(Right().In(FSharpTokenType.COMMA, FSharpTokenType.SEMICOLON, FSharpTokenType.SEMICOLON_SEMICOLON, FSharpTokenType.RPAREN))
        .Return(IntervalFormatType.Empty)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceBeforeSeparators")
        .Group(SpaceRuleGroup)
        .Where(Left().In(FSharpTokenType.LPAREN))
        .Return(IntervalFormatType.Empty)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceBeforePrimaryCtor")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementType.F_SHARP_TYPE_DECLARATION),
          Left().In(FSharpTokenType.IDENTIFIER, ElementType.POSTFIX_TYPE_PARAMETER_DECLARATION_LIST),
          Right().In(ElementType.PRIMARY_CONSTRUCTOR_DECLARATION, ElementType.POSTFIX_TYPE_PARAMETER_DECLARATION_LIST))
        .Return(IntervalFormatType.Empty)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceInsideIndexerLikeList")
        .Group(SpaceRuleGroup)
        .Priority(1000)
        .Where(
          Parent().In(ElementType.LIST_EXPR).Satisfies((node, _) =>
            node.NodeOrNull is IListExpr listExpr &&
            (DotLambdaExprNavigator.GetByExpression(listExpr) != null ||
             PrefixAppExprNavigator.GetByArgumentExpression(listExpr) is { IsIndexerLike: true })))
        .Return(IntervalFormatType.Empty)
        .StartAlternating()
        .Where(Left().In(FSharpTokenType.LBRACK))
        .Build()
        .Where(Right().In(FSharpTokenType.RBRACK))
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceBeforeTypeParams")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementBitsets.BINDING_BIT_SET),
          Left().In(ElementBitsets.F_SHARP_PATTERN_BIT_SET),
          Right().In(ElementType.POSTFIX_TYPE_PARAMETER_DECLARATION_LIST)
        )
        .Return(IntervalFormatType.Empty)
        .Build();

      DescribeEmptyOnlyFormatting(ElementType.UNIT_EXPR, FSharpTokenType.LPAREN, FSharpTokenType.RPAREN);

      DescribeEmptyOnlyFormatting(ElementType.ARRAY_PAT, FSharpTokenType.LBRACK_BAR, FSharpTokenType.BAR_RBRACK);
      DescribeEmptyOnlyFormatting(ElementType.LIST_PAT, FSharpTokenType.LBRACK, FSharpTokenType.RBRACK);
      DescribeEmptyOnlyFormatting(ElementType.UNIT_PAT, FSharpTokenType.LPAREN, FSharpTokenType.RPAREN);

      Describe<FormattingRule>()
        .Name("SpaceInLists")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementBitsets.ARRAY_OR_LIST_PAT_BIT_SET).Satisfies((node, _) =>
            !(node.NodeOrNull is IArrayOrListPat arrayOrListPat && arrayOrListPat.PatternsEnumerable.IsEmpty())))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceInArrayTypeUsage")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementType.ARRAY_TYPE_USAGE),
          Left().In(ElementBitsets.TYPE_USAGE_BIT_SET),
          Right().In(FSharpTokenType.LBRACK))
        .Return(IntervalFormatType.Empty)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceInListPat")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementType.LIST_PAT, ElementType.ARRAY_PAT),
          Left().In(FSharpTokenType.LBRACK, FSharpTokenType.LBRACE_BAR),
          Right().In(ElementBitsets.F_SHARP_PATTERN_BIT_SET))
        .Switch(it => it.SpaceAroundDelimiter, SpaceOptionsBuilders)
        .Build();

      Describe<FormattingRule>()
        .Name("NoSpaceInListPat")
        .Group(SpaceRuleGroup)
        .Where(
          Parent().In(ElementType.LIST_PAT, ElementType.ARRAY_PAT),
          Left().In(ElementBitsets.F_SHARP_PATTERN_BIT_SET),
          Right().In(FSharpTokenType.RBRACK, FSharpTokenType.BAR_RBRACK))
        .Switch(it => it.SpaceAroundDelimiter, SpaceOptionsBuilders)
        .Build();

      Describe<FormattingRule>()
        .Name("AttributeBrackets")
        .Group(SpaceRuleGroup | LineBreaksRuleGroup)
        .Where(
          Parent().In(ElementType.ATTRIBUTE_LIST))
        .Return(IntervalFormatType.OnlyEmpty)
        .StartAlternating()
        .Where(
          Left().In(FSharpTokenType.LBRACK_LESS),
          Right().In(ElementType.ATTRIBUTE))
        .Build()
        .Where(
          Left().In(ElementType.ATTRIBUTE),
          Right().In(FSharpTokenType.GREATER_RBRACK))
        .Build();

      Describe<FormattingRule>()
        .Name("ErrorNodes")
        .Group(AllRuleGroup)
        .Priority(1000)
        .Where(
          Right()
            .In(ElementType.FROM_ERROR_EXPR, ElementType.FROM_ERROR_PAT)
            .Or().In(ElementType.CHAMELEON_EXPRESSION).Satisfies((node, _) => node.IsZeroLength)
        )
        .Return(IntervalFormatType.ReallyDoNotChangeAnything)
        .Build()
        .AndViceVersa()
        .Build();

      Describe<FormattingRule>()
        .Group(LineBreaksRuleGroup)
        .Name("LineBreakAfterTypeReprAccessModifier")
        .Where(
          Parent()
            .In(ElementBitsets.SIMPLE_TYPE_REPRESENTATION_BIT_SET)
            .Satisfies((node, _) => ((ISimpleTypeRepresentation) node.Node).AccessModifier != null),
          Right().In(ElementBitsets.ENUM_CASE_LIKE_DECLARATION_BIT_SET).Satisfies(IsFirstNodeOfItsType))
        .Switch(settings => settings.LineBreakAfterTypeReprAccessModifier,
          When(true).Return(IntervalFormatType.NewLine))
        .Build();

      Describe<FormattingRule>()
        .Group(LineBreaksRuleGroup)
        .Name("NewLineBetweenMembers")
        .Return(IntervalFormatType.NewLine)
        .StartAlternating()
        .Where(
          Right().In(ElementBitsets.MODULE_MEMBER_BIT_SET.Union(ElementBitsets.TYPE_BODY_MEMBER_DECLARATION_BIT_SET)))
        .Build()
        .Where(
          Left().In(ElementBitsets.MODULE_MEMBER_BIT_SET.Union(ElementBitsets.TYPE_BODY_MEMBER_DECLARATION_BIT_SET)),
          Right().Not().In(FSharpTokenType.SEMICOLON))
        .Build();

      DescribeLineBreakInDeclarationWithEquals("TypeDeclaration",
        Node().In(ElementType.F_SHARP_TYPE_DECLARATION),
        Node().In(ElementBitsets.TYPE_REPRESENTATION_BIT_SET));

      DescribeLineBreakInDeclarationWithEquals("ModuleAbbreviation",
        Node().In(ElementType.MODULE_ABBREVIATION_DECLARATION),
        Node().In(ElementType.TYPE_REFERENCE_NAME));

      Describe<FormattingRule>()
        .Group(SpaceRuleGroup)
        .Name("SpaceAfterImplicitConstructorDecl")
        .Where(Left().HasType(ElementType.PRIMARY_CONSTRUCTOR_DECLARATION))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Group(SpaceRuleGroup)
        .Name("SpacesInMemberConstructorDecl")
        .Where(Parent().HasType(ElementType.SECONDARY_CONSTRUCTOR_DECLARATION))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Name("SpaceBetweenRecordBindings")
        .Where(
          Left()
            .HasType(ElementType.RECORD_FIELD_BINDING)
            .Satisfies((node, _) => ((IRecordFieldBinding) node.Node).Semicolon != null),
          Right().HasType(ElementType.RECORD_FIELD_BINDING))
        .Return(IntervalFormatType.Space)
        .Build();

      Describe<FormattingRule>()
        .Group(LineBreaksRuleGroup)
        .Name("LineBreaksBetweenRecordBindings")
        .Where(
          Left()
            .HasType(ElementType.RECORD_FIELD_BINDING)
            .Satisfies((node, _) => ((IRecordFieldBinding) node.Node).Semicolon == null),
          Right().HasType(ElementType.RECORD_FIELD_BINDING))
        .Return(IntervalFormatType.NewLine)
        .Build();

      Describe<FormattingRule>()
        .Group(LineBreaksRuleGroup)
        .Name("NewLineAfterPrecedingAttrList")
        .Where(
          Parent().In(ElementBitsets.MODULE_DECLARATION_BIT_SET.Union(ElementBitsets.F_SHARP_TYPE_OR_EXTENSION_DECLARATION_BIT_SET).Union(ElementBitsets.TYPE_BODY_MEMBER_DECLARATION_BIT_SET)),
          Left().In(ElementType.ATTRIBUTE_LIST),
          Right().In(ElementType.ATTRIBUTE_LIST, FSharpTokenType.MODULE, FSharpTokenType.TYPE, FSharpTokenType.AND, FSharpTokenType.OVERRIDE, FSharpTokenType.MEMBER))
        .Return(IntervalFormatType.NewLine)
        .Build();

      Describe<FormattingRule>()
        .Group(SpaceRuleGroup)
        .Name("SpacesAroundAttrList")
        .Where(Left().In(ElementType.ATTRIBUTE_LIST))
        .Return(IntervalFormatType.Space)
        .Build()
        .AndViceVersa()
        .Build();
    }