private void DescribeEmptyOnlyFormatting()

in ReSharper.FSharp/src/FSharp/FSharp.Psi/src/CodeFormatter/FSharpCodeFormatterInfoProvider.cs [1102:1210]


    private void DescribeEmptyOnlyFormatting(IBuilderAction<IBlankWithSinglePattern> parent, NodeType left,
      NodeType right) =>
      Describe<FormattingRule>()
        .Name("SpaceInLists")
        .Group(SpaceRuleGroup)
        .Where(parent, Left().In(left), Right().In(right))
        .Return(IntervalFormatType.OnlyEmpty)
        .Build();

    private void DescribeEmptyOnlyFormatting(NodeType parent, NodeType left, NodeType right) =>
      DescribeEmptyOnlyFormatting(Parent().In(parent), left, right);

    private void BlankLines()
    {
      var declarations =
        ElementBitsets.MODULE_MEMBER_BIT_SET
          .Union(ElementBitsets.BINDING_BIT_SET)
          .Union(ElementBitsets.ENUM_CASE_LIKE_DECLARATION_BIT_SET)
          .Union(ElementType.F_SHARP_TYPE_DECLARATION)
          .Union(ElementBitsets.MODULE_MEMBER_BIT_SET)
          .Union(ElementBitsets.F_SHARP_TYPE_MEMBER_DECLARATION_BIT_SET)
          .Union(ElementBitsets.TYPE_REPRESENTATION_BIT_SET);

      var noBlankLineAfterNodeTypes =
        new NodeTypeSet(
          FSharpTokenType.EQUALS,
          FSharpTokenType.IDENTIFIER,
          FSharpTokenType.CLASS,
          FSharpTokenType.INTERFACE,
          FSharpTokenType.STRUCT,
          FSharpTokenType.WITH,
          FSharpTokenType.PRIVATE,
          FSharpTokenType.INTERNAL,
          FSharpTokenType.PUBLIC
        );

      var noBlankLineBeforeNodeTypes =
        ElementBitsets.TYPE_REPRESENTATION_BIT_SET
          .Union(
            FSharpTokenType.RBRACE
          );

      // todo: group member kinds, check same group instead
      bool AllowsNoBlankLine(NodeType nodeType, NodeType nextNodeType)
      {
        return nodeType == ElementType.LET_BINDINGS_DECLARATION && nextNodeType == ElementType.EXPRESSION_STATEMENT ||
               nodeType == ElementType.EXPRESSION_STATEMENT&& nextNodeType == ElementType.LET_BINDINGS_DECLARATION ||
               nodeType == ElementType.ABSTRACT_MEMBER_DECLARATION && nextNodeType == ElementType.MEMBER_DECLARATION;
      }

      Describe<BlankLinesAroundNodeRule>()
        .AddNodesToGroupBefore(Node().In(Comments))
        .AddNodesToGroupAfter(Node().In(Comments))
        .AllowedNodesBefore(Node().Satisfies((node, _) => !noBlankLineAfterNodeTypes[node.NodeType]))
        .AllowedNodesAfter(Node().Satisfies((node, _) => !noBlankLineBeforeNodeTypes[node.NodeType]))
        .Priority(1)
        .StartAlternating()

        .Name("BlankLinesAroundDeclarations")
        .Where(Node().In(declarations))
        .MinBlankLines(it => it.BlankLinesAroundMultilineModuleMembers)
        .MinBlankLinesForSingleLine(it => it.BlankLinesAroundSingleLineModuleMember)
        .Build()

        .Name("BlankLinesAroundDifferentModuleMemberKinds")
        .Where(Node().In(declarations))
        .MinBlankLines(it => it.BlankLinesAroundDifferentModuleMemberKinds)
        .AdditionalCheckForBlankLineAfter((node, _) =>
          node.GetNextMeaningfulSibling().NodeType is var nextNodeType &&
          nextNodeType != node.NodeType && 
          !AllowsNoBlankLine(node.NodeType, nextNodeType) && 
          declarations[nextNodeType])
        .AdditionalCheckForBlankLineBefore((node, _) =>
          node.GetPreviousMeaningfulSibling().NodeType is var prevNodeType &&
          !AllowsNoBlankLine(prevNodeType, node.NodeType) &&
          prevNodeType != node.NodeType && declarations[prevNodeType])
        .Build()

        .AllowedNodesBefore(Node().Satisfies((_, _) => true))
        .Name("BlankLinesBeforeFirstTopLevelModuleMember")
        .Where(
          Parent().In(ElementBitsets.TOP_LEVEL_MODULE_LIKE_DECLARATION_BIT_SET),
          Node().In(ElementBitsets.MODULE_MEMBER_BIT_SET)
            .Satisfies(IsFirstNodeOfTypeSet(ElementBitsets.MODULE_MEMBER_BIT_SET, false)))
        .MinBlankLinesBefore(it => it.BlankLinesBeforeFirstModuleMemberInTopLevelModule)
        .Build()

        .AllowedNodesBefore(Node().Satisfies((_, _) => true))
        .Name("BlankLinesBeforeFirstNestedModuleMember")
        .Where(
          Parent().In(ElementType.NESTED_MODULE_DECLARATION),
          Node().In(ElementBitsets.MODULE_MEMBER_BIT_SET)
            .Satisfies(IsFirstNodeOfTypeSet(ElementBitsets.MODULE_MEMBER_BIT_SET, false)))
        .MinBlankLinesBefore(it => it.BlankLinesBeforeFirstModuleMemberInNestedModule)
        .Build()

        .Name("BlankLinesAroundModules")
        .Where(Node().In(ElementBitsets.TOP_LEVEL_MODULE_LIKE_DECLARATION_BIT_SET))
        .MinBlankLines(it => it.BlankLineAroundTopLevelModules)
        .Build();

      // todo: test sig files
      Describe<BlankLinesRule>()
        .Name("BlankLinesInDecls")
        .Group(MaxLineBreaks)
        .Where(Parent().In(ElementBitsets.MODULE_LIKE_DECLARATION_BIT_SET.Union(ElementBitsets.F_SHARP_FILE_BIT_SET)))
        .SwitchBlankLines(it => it.KeepMaxBlankLineAroundModuleMembers, true, BlankLineLimitKind.LimitMaximumMild)
        .Build();
    }