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();
}