diff --git a/CHANGELOG.md b/CHANGELOG.md index 116a0d6813..0857f474a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ ## Unreleased ### Fixes -* Stroustrup: Two lists given directly as parameters, break code [#2681](https://github.com/fsprojects/fantomas/issues/2681) +* Stroustrup: Two lists given directly as parameters, break code [#2681](https://github.com/fsprojects/fantomas/issues/2681) +* fsharp_experimental_stroustrup_style=true breaks on types with nested anonymous records. [#2413](https://github.com/fsprojects/fantomas/issues/2413) +* Stroustrup style breaks on nested records. [#2587](https://github.com/fsprojects/fantomas/issues/2587) ## [5.2.0-beta-001] - 2023-01-02 diff --git a/src/Fantomas.Core.Tests/Stroustrup/SynBindingValueExpressionTests.fs b/src/Fantomas.Core.Tests/Stroustrup/SynBindingValueExpressionTests.fs index 6ae2845d85..84e4040459 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/SynBindingValueExpressionTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/SynBindingValueExpressionTests.fs @@ -398,3 +398,197 @@ let fooDto = |> Option.toObj |} """ + +[] +let ``let binding with nested anonymous records, 2413`` () = + formatSourceString + false + """ +let foo = + {| Data = + {| Name = "Isaac" + Age = 43 + Day = "Monday" + Colour = "Blue" |} |} +""" + config + |> prepend newline + |> should + equal + """ +let foo = {| + Data = {| + Name = "Isaac" + Age = 43 + Day = "Monday" + Colour = "Blue" + |} +|} +""" + +[] +let ``list expression inside anonymous record, 2413`` () = + formatSourceString + false + """ +let foo = {| + Data = + {| + Name = "Isaac" + Age = 43 + Day = "Monday" + Colours = + [ + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + ] + |} +|} +""" + config + |> prepend newline + |> should + equal + """ +let foo = {| + Data = {| + Name = "Isaac" + Age = 43 + Day = "Monday" + Colours = [ + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + ] + |} +|} +""" + +[] +let ``list expression inside regular record, 2413`` () = + formatSourceString + false + """ +let foo = { + Data = + { + Name = "Isaac" + Age = 43 + Day = "Monday" + Colours = + [ + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + ] + } +} +""" + config + |> prepend newline + |> should + equal + """ +let foo = { + Data = { + Name = "Isaac" + Age = 43 + Day = "Monday" + Colours = [ + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + "Red" + "Blue" + "White" + "Orange" + ] + } +} +""" + +[] +let ``nested records, 2587`` () = + formatSourceString + false + """ +let myRecord = { + Property1 = { + Value1 = 20 + Value2 = 30 + Value3 = 40 + } + Property2 = { + Value1 = 20 + Value2 = 30 + Value3 = 40 + } +} +""" + config + |> prepend newline + |> should + equal + """ +let myRecord = { + Property1 = { + Value1 = 20 + Value2 = 30 + Value3 = 40 + } + Property2 = { + Value1 = 20 + Value2 = 30 + Value3 = 40 + } +} +""" diff --git a/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs b/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs index b68d7c2bdc..351b1050a7 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs @@ -206,3 +206,32 @@ module OutdentingProblem = let withSetting2 value configuration = { configuration with Setting2 = value } """ + +[] +let ``nested anonymous record in type definition, 2413`` () = + formatSourceString + false + """ +type MangaDexAtHomeResponse = { + baseUrl: string + chapter: {| + hash: string + data: string[] + otherThing: int + |} +} +""" + config + |> prepend newline + |> should + equal + """ +type MangaDexAtHomeResponse = { + baseUrl: string + chapter: {| + hash: string + data: string[] + otherThing: int + |} +} +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 1a1355a7de..329986d0a8 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -1,4 +1,4 @@ -module internal rec Fantomas.Core.CodePrinter +module internal rec Fantomas.Core.CodePrinter open System open Fantomas.Core.Context @@ -436,7 +436,7 @@ let genExpr (e: Expr) = genIdentListNode node.FieldName +> sepSpace +> genSingleTextNode node.Equals - +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genExpr node.Expr) + +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidthUnlessStroustrup genExpr node.Expr |> genNode node let fieldsExpr = col sepNln node.Fields genRecordFieldName @@ -551,7 +551,7 @@ let genExpr (e: Expr) = genSingleTextNode node.Ident +> sepSpace +> genSingleTextNode node.Equals - +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genExpr node.Expr) + +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidthUnlessStroustrup genExpr node.Expr |> genNode node let smallExpression = @@ -609,19 +609,19 @@ let genExpr (e: Expr) = onlyIf node.IsStruct !- "struct " +> recordExpr let genMultilineAnonRecordAlignBrackets = - let copyExpr fieldsExpr e = - atCurrentColumnIndent (genExpr e) - +> (!- " with" - +> indent - +> whenShortIndent indent - +> sepNln - +> fieldsExpr - +> whenShortIndent unindent - +> unindent) - let genAnonRecord = match node.CopyInfo with | Some ci -> + let copyExpr fieldsExpr e = + atCurrentColumnIndent (genExpr e) + +> (!- " with" + +> indent + +> whenShortIndent indent + +> sepNln + +> fieldsExpr + +> whenShortIndent unindent + +> unindent) + genSingleTextNodeSuffixDelimiter node.OpeningBrace +> sepNlnWhenWriteBeforeNewlineNotEmpty // comment after curly brace +> copyExpr fieldsExpr ci @@ -3036,8 +3036,8 @@ let genType (t: Type) = | None -> sepOpenAnonRecdFixed +> addSpaceIfSpaceAroundDelimiter | Some n -> genSingleTextNode n +> addSpaceIfSpaceAroundDelimiter - let genAnonRecordFieldType (i, t) = - genSingleTextNode i + let genAnonRecordFieldType (identifier, t) = + genSingleTextNode identifier +> sepColon +> autoIndentAndNlnIfExpressionExceedsPageWidth (genType t) @@ -3049,27 +3049,19 @@ let genType (t: Type) = +> genSingleTextNode node.Closing let longExpression = - let genFields = col sepNln node.Fields genAnonRecordFieldType - - let genMultilineAnonRecordTypeAlignBrackets = - let genRecord = - sepOpenAnonRecdFixed - +> indentSepNlnUnindent (atCurrentColumnIndent genFields) - +> sepNln - +> genSingleTextNode node.Closing - - genStruct +> genRecord - - let genMultilineAnonRecordType = - let genRecord = - genOpening - +> atCurrentColumn genFields - +> addSpaceIfSpaceAroundDelimiter - +> genSingleTextNode node.Closing - - genStruct +> genRecord + let genAnonRecordFields = col sepNln node.Fields genAnonRecordFieldType - ifAlignOrStroustrupBrackets genMultilineAnonRecordTypeAlignBrackets genMultilineAnonRecordType + ifAlignOrStroustrupBrackets + (genStruct + +> sepOpenAnonRecdFixed + +> indentSepNlnUnindent (atCurrentColumnIndent genAnonRecordFields) + +> sepNln + +> genSingleTextNode node.Closing) + (genStruct + +> genOpening + +> atCurrentColumn genAnonRecordFields + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.Closing) fun (ctx: Context) -> let size = getRecordSize ctx node.Fields @@ -3462,7 +3454,7 @@ let genField (node: FieldNode) = | Some name -> genSingleTextNode name +> sepColon - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genType node.Type)) + +> autoIndentAndNlnTypeUnlessStroustrup genType node.Type) |> genNode node let genUnionCase (hasVerticalBar: bool) (node: UnionCaseNode) = diff --git a/src/Fantomas.Core/Context.fs b/src/Fantomas.Core/Context.fs index 0cef699785..d356cf670f 100644 --- a/src/Fantomas.Core/Context.fs +++ b/src/Fantomas.Core/Context.fs @@ -927,6 +927,18 @@ let autoIndentAndNlnExpressUnlessStroustrup (f: Expr -> Context -> Context) (e: else indentSepNlnUnindent (f e) ctx +let autoIndentAndNlnTypeUnlessStroustrup (f: Type -> Context -> Context) (t: Type) (ctx: Context) = + let shouldUseStroustrup = + ctx.Config.ExperimentalStroustrupStyle + && t.IsStroustrupStyleType + && let node = Type.Node t in + Seq.isEmpty node.ContentBefore + + if shouldUseStroustrup then + f t ctx + else + autoIndentAndNlnIfExpressionExceedsPageWidth (f t) ctx + let autoIndentAndNlnIfExpressionExceedsPageWidthUnlessStroustrup (f: Expr -> Context -> Context) (e: Expr) diff --git a/src/Fantomas.Core/Context.fsi b/src/Fantomas.Core/Context.fsi index 11d6b14a4b..4f934e14e2 100644 --- a/src/Fantomas.Core/Context.fsi +++ b/src/Fantomas.Core/Context.fsi @@ -254,6 +254,7 @@ val sepSpaceUnlessWriteBeforeNewlineNotEmpty: ctx: Context -> Context val autoIndentAndNlnWhenWriteBeforeNewlineNotEmpty: f: (Context -> Context) -> ctx: Context -> Context val addParenIfAutoNln: expr: Expr -> f: (Expr -> Context -> Context) -> (Context -> Context) val autoIndentAndNlnExpressUnlessStroustrup: f: (Expr -> Context -> Context) -> e: Expr -> ctx: Context -> Context +val autoIndentAndNlnTypeUnlessStroustrup: f: (Type -> Context -> Context) -> t: Type -> ctx: Context -> Context val autoIndentAndNlnIfExpressionExceedsPageWidthUnlessStroustrup: f: (Expr -> Context -> Context) -> e: Expr -> ctx: Context -> Context diff --git a/src/Fantomas.Core/SyntaxOak.fs b/src/Fantomas.Core/SyntaxOak.fs index f25bdc4d15..98affa418d 100644 --- a/src/Fantomas.Core/SyntaxOak.fs +++ b/src/Fantomas.Core/SyntaxOak.fs @@ -370,6 +370,11 @@ type Type = | Or n -> n | LongIdentApp n -> n + member e.IsStroustrupStyleType: bool = + match e with + | AnonRecord _ -> true + | _ -> false + /// A pattern composed from a left hand-side pattern, a single text token/operator and a right hand-side pattern. type PatLeftMiddleRight(lhs: Pattern, middle: Choice, rhs: Pattern, range) = inherit NodeBase(range)