in ReSharper.FSharp/src/FSharp/FSharp.Psi/src/CodeFormatter/FSharpCodeFormatterInfoProvider.cs [560:715]
private void Aligning()
{
var aligningNodes =
new NodeTypeSet(
ElementType.BINARY_APP_EXPR,
ElementType.MATCH_EXPR,
ElementType.SEQUENTIAL_EXPR,
ElementType.IF_THEN_ELSE_EXPR,
ElementType.TUPLE_PAT,
ElementType.PARAMETERS_OWNER_PAT,
ElementType.ARRAY_TYPE_USAGE,
ElementType.NAMED_TYPE_USAGE,
ElementType.TUPLE_TYPE_USAGE,
ElementType.EXPRESSION_REFERENCE_NAME,
ElementType.TYPE_REFERENCE_NAME,
ElementType.RECORD_FIELD_BINDING_LIST,
ElementType.RECORD_FIELD_DECLARATION_LIST,
ElementType.UNION_CASE_FIELD_DECLARATION,
ElementType.ENUM_REPRESENTATION,
ElementType.UNION_REPRESENTATION);
Describe<IndentingRule>()
.Name("SimpleAlignment")
.Where(Node().In(aligningNodes).Satisfies((node, _) => !aligningNodes[node.Parent.NodeType] || node.Parent.FirstChild != node))
.Return(IndentType.AlignThrough)
.Build();
Describe<IndentingRule>()
.Name("LetExprAlignment")
.Where(Node().In(ElementType.LET_OR_USE_EXPR))
.Return(IndentType.AlignThrough)
.Build();
// todo: tuple patterns
//
// Describe<IndentingRule>()
// .Name("TupleAlignment")
// .Where(Node().In(ElementType.TUPLE_EXPR).Satisfies((node, _) => ((ITupleExpr)node.NodeOrNull)?.Expressions.LastOrDefault() is not ILambdaExpr or IMatchLambdaExpr))
// .Return(IndentType.AlignThrough)
// .Build();
var expressionsExceptLambda =
ElementBitsets.F_SHARP_EXPRESSION_BIT_SET.Except(ElementType.LAMBDA_EXPR, ElementType.MATCH_LAMBDA_EXPR);
Describe<IndentingRule>()
.Name("TupleAlignment")
.Where(Parent().In(ElementType.TUPLE_EXPR),
Node().In(expressionsExceptLambda).Satisfies(IsFirstNodeOfItsType)).CloseNodeGetter((node, _) => GetLastNodeOfTypeSet(expressionsExceptLambda, node))
.Return(IndentType.AlignThrough)
.Build();
Describe<IndentingRule>()
.Name("Todo123")
.Where(Parent().In(ElementType.NAMED_UNION_CASE_FIELDS_PAT),
Node().In(ElementType.FIELD_PAT).Satisfies(IsFirstNodeOfItsType)).CloseNodeGetter(GetLastNodeWithSameType)
.Return(IndentType.AlignThrough)
.Build();
Describe<IndentingRule>()
.Name("PrefixAppAlignmentForNonComputationExpressionArg")
.Where(Node().In(ElementType.PREFIX_APP_EXPR).Satisfies((node, _) =>
!IsNestedRefOrAppExpr(node.NodeOrNull) && IsPrefixAppWithNoComputationExprArg(node)))
.Return(IndentType.AlignThrough)
.Build();
Describe<IndentingRule>()
.Name("PrefixAppNoAlignmentForComputationExpressionArg")
.Where(Node().In(ElementType.PREFIX_APP_EXPR).Satisfies((node, _) =>
!IsNestedRefOrAppExpr(node.NodeOrNull) && !IsPrefixAppWithNoComputationExprArg(node)))
.Return(IndentType.ExternalNoIndent)
.Build();
DescribeNestedAlignment("RefExprAppAlignment", ElementType.REFERENCE_EXPR);
DescribeNestedAlignment("FunctionTypeUsageAlignment", ElementType.FUNCTION_TYPE_USAGE);
DescribeNestedAlignment("ArrayTypeUsageAlignment", ElementType.ARRAY_TYPE_USAGE);
DescribeChildrenAlignment<IArrayOrListPat>(
ElementBitsets.ARRAY_OR_LIST_PAT_BIT_SET,
ElementBitsets.F_SHARP_PATTERN_BIT_SET,
pat => pat.PatternsEnumerable);
DescribeChildrenAlignment<IMatchClauseListOwnerExpr>(
ElementType.MATCH_EXPR,
ElementType.MATCH_CLAUSE,
pat => pat.ClausesEnumerable);
DescribeChildrenAlignment<IAttributeList>(
ElementType.ATTRIBUTE_LIST,
ElementType.ATTRIBUTE,
attrList => attrList.AttributesEnumerable);
Describe<IndentingRule>().Name("Function deindent alignment")
.Where(
Parent().In(ElementType.PAREN_EXPR)
.Satisfies((node, _) =>
{
var innerExpr = ((IParenExpr)node.Node).InnerExpression;
if (innerExpr is IMatchLambdaExpr or ILambdaExpr) return true;
if (innerExpr is ITupleExpr tupleExpr &&
tupleExpr.ExpressionsEnumerable.LastOrDefault() is IMatchLambdaExpr or ILambdaExpr)
return true;
return false;
}),
Left().In(FSharpTokenType.LPAREN), Right().In(FSharpTokenType.RPAREN))
.Return(IndentType.OverrideAlignment | IndentType.ExternalNoIndent | IndentType.Internal)
.Build();
Describe<IndentingRule>().Name("EnumCaseLikeDeclarations")
.Where(Parent().In(ElementBitsets.SIMPLE_TYPE_REPRESENTATION_BIT_SET),
Left().In(ElementBitsets.ENUM_CASE_LIKE_DECLARATION_BIT_SET).Satisfies(IsFirstNodeOfItsType))
.CloseNodeGetter((node, _) => node.Parent.LastChild)
.Return(IndentType.AlignThrough) // through => including the last node (till => without the last one)
.Build();
Describe<IndentingRule>()
.Name("OutdentBinaryOperators")
.Priority(100100)
.Where(
Parent().HasType(ElementType.BINARY_APP_EXPR),
Node().HasRole(BinaryAppExpr.OP_REF_EXPR).Satisfies((node, context) => !IsPipeOperator(node, context)))
.Switch(settings => settings.OutdentBinaryOperators,
When(true).Return(IndentType.Outdent | IndentType.External))
.Build();
Describe<IndentingRule>()
.Name("OutdentPipeOperators")
.Priority(100100)
.Where(
Parent().HasType(ElementType.BINARY_APP_EXPR),
Node().HasRole(BinaryAppExpr.OP_REF_EXPR).Satisfies(IsPipeOperator))
.Switch(settings => settings.OutdentBinaryOperators,
When(true).Switch(settings => settings.NeverOutdentPipeOperators,
When(false).Return(IndentType.Outdent | IndentType.External)))
.Build();
Describe<IndentingRule>()
.Name("RecordReprAccessibility")
.Where(Node().In(ElementType.RECORD_REPRESENTATION).Satisfies((node, context) =>
((RecordRepresentation)node.Node).AccessModifier != null &&
new VirtNode(context, ((RecordRepresentation)node.Node).LeftBrace).HasNewLineBefore()))
.StartAlternating()
.Return(IndentType.AlignThrough)
.Build()
.Return(IndentType.StartAfterFirstToken | IndentType.EndAtExternal)
.Build();
}