private void FixPartialSelectionMemberIndentingRule()

in ReSharper.FSharp/src/FSharp/FSharp.Psi/src/CodeFormatter/FSharpCodeFormatterInfoProvider.cs [1297:1389]


    private void FixPartialSelectionMemberIndentingRule(NodeType parentType, NodeTypeSet memberTypes)
    {
      Describe<IndentingRule>()
        .Name("Partial selection indenting rule")
        .Where(
          Parent().In(parentType),
          Node() // the node is the starting node for the rule
            .In(memberTypes.Union(Comments))
            .Satisfies((node, _) =>
            {
              // This case should be handled by alignment, not this rule
              // type T() = member this.P = 1
              //            member this.P2 = 1
              if (!node.HasNewLineBefore())
                return false;
              
              // Find comment preceding a module member only (i.e. don't use comment before `=`)
              var parent = node.Parent;
              if (parent == null) return false;

              var foundComment = false;
              var ourNodeIsComment = false;

              for (var i = parent.FirstChild; i != null; i = i.NextSibling)
              {
                if (Comments[i.NodeType])
                {
                  if (i == node)
                  {
                    if (foundComment)
                      return false;
                    
                    ourNodeIsComment = true;
                  }

                  if (i.HasNewLineBefore())
                    foundComment = true;
                  
                  continue;
                }

                if (memberTypes[i.NodeType])
                  return i == node && !foundComment || ourNodeIsComment;

                if (!i.IsWhitespace) // looks like it's handling `=`
                {
                  foundComment = false;
                  if (ourNodeIsComment)
                    return false; // if the comment is before `=`/keyword/name, can't be the starting node for this rule 
                }
              }

              return false;
            }))
        .CloseNodeGetter((node, _) => GetLastNodeOfTypeSet(memberTypes, node))
        .Calculate((node, context) => // node is Left()/Node()
        {
          // Formatter engine passes nulls once for caching internal/external intervals as an optimization.
          if (node == null || context == null)
            return new ConstantOptionNode(new IndentOptionValue(IndentType.StartAtExternal | IndentType.EndAtExternal));

          var treeNode = (VirtNode)node;

          var closingNode = GetLastNodeOfTypeSet(memberTypes, treeNode);
          FormatTask lastTask = null;
          for (var i = context.FormatTasks.Length - 1; i >= 0; i--)
          {
            var it = context.FormatTasks[i];
            if (it.Profile != CodeFormatProfile.NO_REINDENT)
            {
              lastTask = it;
              break;
            }
          }

          var offsetLast = lastTask == null ? TreeOffset.Zero : new VirtNode(context, lastTask.LastElement).GetTreeStartOffset();

          if (closingNode.GetTreeStartOffset() > offsetLast)
            return
              new ConstantOptionNode(
                new IndentOptionValue(
                  IndentType.AbsoluteIndent | IndentType.StartAtExternal | IndentType.EndAtExternal |
                  IndentType.NonSticky | IndentType.NonAdjustable,
                  0, closingNode.CalcLineIndent(context.TabWidth, true)));

          // todo: try using the following for nodes without further indent:
          //     IndentType.NoIndentAtExternal
          //     or maybe startAtExt + multiplier 0

          return new ConstantOptionNode(
            new IndentOptionValue(IndentType.NoIndentAtExternal | IndentType.EndAtExternal | IndentType.NonSticky));
        }).Build();
    }