diff --git a/docs/ragnarok.md b/docs/ragnarok.md new file mode 100644 index 0000000000..8dc0b151d2 --- /dev/null +++ b/docs/ragnarok.md @@ -0,0 +1,544 @@ +# The Ragnarok Feature + +I'm not sure who this document is for right now. These are some thoughts and some research about a certain feature that people want in Fantomas. +The feature is about how the code is printed back to source and is a variation on what the style guide advices today. + +I will go in great lengths why this is feature does not bring much value to the overall mission of Fantomas. +Why I consider it inconsistent and why the implementation is far from trivial. +Throughout this document there will be a negative tone toward this and for the initial draft I'm ok with this. +Again, there are no plans to publish this as is. +Oh and typos all over the place. + +## Introduction + +The feature has been request multiple times: +- https://github.com/fsprojects/fantomas/issues/1408 +- https://github.com/fsprojects/fantomas/issues/1225 +- https://github.com/fsprojects/fantomas/issues/453 (in comments) + +The gist is that some multiline expressions should start on the same line to save some space: + +```fsharp +let v = { + X = x + Y = y +} +``` + +The style guides deal with this by putting the entire expression on the next line: + +```fsharp +let v = + { X = x + Y = y } + +// or + +let v = + { + X = x + Y = y + } +``` + +## The inconsistency + +When you dissect the initial sample in AST you get something like: + +```fsharp +ImplFile + (ParsedImplFileInput + ("tmp.fsx", true, QualifiedNameOfFile Tmp$fsx, [], [], + [SynModuleOrNamespace + ([Tmp], false, AnonModule, + [Let + (false, + [SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((1,4), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (None, SynValInfo ([], SynArgInfo ([], false, None)), None), + Named (v, false, None, tmp.fsx (1,4--1,5)), None, + Record + (None, None, + [((LongIdentWithDots ([X], []), true), Some (Ident x), + Some (tmp.fsx (2,10--3,4), None)); + ((LongIdentWithDots ([Y], []), true), Some (Ident y), None)], + tmp.fsx (1,8--4,1)), tmp.fsx (1,4--1,5), + Yes tmp.fsx (1,0--4,1))], tmp.fsx (1,0--4,1))], PreXmlDocEmpty, + [], None, tmp.fsx (1,0--4,1))], (true, true))) +``` + +or simplified: + +`SynBinding(pat = pat; expr = expr)` + +The `pat` represents the `v` and the `expr` everything after the equals sign. +In Fantomas we adhere to a simple rule, we tried an put everything on one line `let v = { X = ...` and if that crossed a certain threshold (based on a setting), we put the expression on the next line indented. + +Now the ask for the ragnarok setting, is to not do this for a handful of [SynExpr](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html). +There are about 65 union cases for `SynExpr` and for maybe 5 cases, people want to deviate from our currently consistent behavior. + +Of course, the thing is that the formatting of these `SynExpr` depends on the context of where the nodes are in. +Example: + +```fsharp +let v = { + X = x + Y = y +} + +// versus + +let vlist = [ + { X = x + Y = y } + // or + { + X = x + Y = y + } +] +``` + +If `SynExpr.Record` is the `expr` in a `SynBinding`, if would not require a newline after the `=` to start. +If it is part of `SynExpr.ArrayOrListComputed`, it would be following the default rules I guess. +Point is that, the combination of two syntax nodes would lead to a different style and that will occur all over the SyntaxTree. + +## The subjectivity + +As a long term Fantomas user, over time you stop caring about how the code looks like. You accept what is does and letting go of your past habits leads to a world of freedom. +People that do not use Fantomas, cannot cope with the fact that the formatted code does differ from their original source. +That is the idea thought, you follow a style guide and your code looks like how the rest of the world does it. + +In any case, as a maintainer, I'm always caught in between giving the people what they want and giving them what they need. +The point I'm trying to make is that there is no right or wrong in the style of code. If you prefer your own handwriting that is fine, but using a typewriter works just as well to bring your story. + +So, asking for a new style without any solid arguments really is a hard sell. People mentioned that this is a popular style and all that jazz but never bring up any numbers. +Nor, do they understand the technical nature of what their preferred style implies. +And lastly, not a single person has engaged the discussion in the MS style guide. This really rubs me the wrong way. +People want something, don't have a solid case, are clueless and don't put in the proper legwork to get somewhere. + +## Scope + +There are many syntax nodes in play for this feature. +Most people only list one example when they ask for this feature, but the realm of the SyntaxTree can be a quite large one. + +### SynExpr + +The `SynExpr` I believe that should be included in this would be: + +- [SynExpr.Record](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#Record) +- [SynExpr.AnonRecd](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#AnonRecd) +- [SynExpr.ComputationExpr](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#ComputationExpr) +- [SynExpr.ArrayOrListComputed](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#ArrayOrListComputed) + +I'm not sure that this list is completed. +Some people might also include [SynExpr.MatchLambda](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#MatchLambda). +And I also wonder about tuples, [SynExpr.Tuple](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#Tuple). + +Maybe multiline strings ([SynConst.String](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synconst.html#String) in [SynExpr.Const](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#Const)) should also be included: + +```fsharp +let json = """ +{ + "foo":"bar" +} +""" +``` + +Note that depending on the information stored in these nodes, they are formatted somewhat differently. + +### SynPat + +The `SynPat` cases might be: +- [SynPat.Record](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synpat.html#Record) +- [SynPat.ArrayOrList](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synpat.html#ArrayOrList) + +### SynType + +The `SynType` cases might be: +- [SynType.AnonRecd](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-syntype.html#AnonRecd) + +### SynBinding + +SynBinding is used for let bindings and members: + +```fsharp +let x a b = async { + return a + b +} + +type Foo() = + member this.Bar = {| + bar with X = x + |} // this is quite interesting how the closing brace is indented. +``` + +Fantomas has different rules depending on the details of the SynBinding. + +```fsharp +let a = { + X = x +} + +// different code path in CodePrinter +let b c = { + X = x +} + +// also a different code path in CodePrinter +let d e : MyRecord = { + X = x +} +``` + +### LetOrUseBang + +Note that not every time the `let` keyword is used, it leads to a `SynBinding`. +[LetBang](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#LetOrUseBang) for example has a different way of storing information. + +```fsharp +async { + let! a = { + X = x + } + () +} +``` + +### YieldOrReturn + +[YieldOrReturn](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#YieldOrReturn) + +```fsharp +myComp { + yield { + X = y + } + return { + Y = y + } +} +``` + +### YieldReturnFrom + +[YieldReturnFrom](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#YieldOrReturnFrom) + +```fsharp +myComp { + yield! { + X = y + } + return! { + Y = y + } +} +``` + +### SynExprAndBang + +[SynExprAndBang](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexprandbang.html) + +```fsharp +async { + let! x = y + and! z = async { + return! meh + } + () +} +``` + +### LongIdentSet + +[LongIdentSet](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#LongIdentSet) + +```fsharp +myMutable <- { + X = x +} +``` + +### DotIndexedSet + +[DotIndexedSet](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#DotIndexedSet) + +```fsharp +myMutable.[x] <- { + X = x +} +``` + +### Set + +[Set](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#Set) + +```fsharp +myMutable[x] <- { + X = x +} +``` + +New F# 6 syntax. + +### DotSet + +[DotSet](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#DotSet) + +```fsharp +myMutable().foo <- { + X = x +} +``` + +### Lambda + +[Lambda](https://fsprojects.github.io/fantomas-tools/#/ast?data=N4KABGBEAmCmBmBLAdrAzpAXFSAacUiaAYmolmPAIYA2as%2BEkAxgPZwWTwCuyYAHmAC0APjBU0AT2TMwwADoAneXwhhFsAC7dFffkpUBfSCENA) + +```fsharp +fun x -> async { + return x +} +``` + +This is an interesting one as there are quite some rules to format lambda in Fantomas. +There is the raw lambda as you see it above but it is often capture in more elaborate patterns: + +```fsharp +myTasks +|> List.map (fun p -> task { + return p +}) +|> Task.WhenAll +``` + +### SynMatchClause + +Used as part of [SynExpr.Match](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#Match) and [SynExpr.MatchBang](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#MatchBang). +[SynMatchClause](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synmatchclause.html). + +```fsharp +match v with +| () -> async { + return FooBar() +} +``` + +Keep in mind that [SynExpr.TryWith](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#TryWith) has this as well: + +```fsharp +try x with +| ex -> async { + () +} +``` + +### App + +Another very interesting case, where do you draw the line with [SynExpr.App](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#App) + +```fsharp +let v = + foo [ + a + b + c + ] +``` + +This is partially already implemented in the Elmish settings. +However, there are again a lot of possibilities there: + +```fsharp +let v = + foo "string" [ + a + b + c + ] +``` + +this is currently not supported. +When do you draw the line and go over to: + +```fsharp +let v = + foo + "string" + [ a + b + c ] +``` + +? + +#### Named arguments + +Another interesting edge case is named arguments inside applications: + +```fsharp +let v = + SomeConstructor(v = [ + A + B + C + ]) +``` + +Note that the AST for `v = [ ... ]` is something like + +```fsharp +App + (NonAtomic, false, + App + (NonAtomic, true, Ident op_Equality, Ident v, + tmp.fsx (2,20--2,23)), + ArrayOrListComputed + (false, + Sequential + (SuppressNeither, true, Ident A, + Sequential + (SuppressNeither, true, Ident B, Ident C, + tmp.fsx (4,8--5,9)), tmp.fsx (3,8--5,9)), + tmp.fsx (2,24--6,5)), tmp.fsx (2,20--6,5)), +tmp.fsx (2,19--2,20), Some tmp.fsx (6,5--6,6), +tmp.fsx (2,19--6,6) +``` + +so this is hard to detect in the first place. + +### SynExprRecordField + +[SynExprRecordField](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexprrecordfield.html) + +```fsharp +let v = { + X = { + Y = y + } +} +``` + +### Fields in AnonRecords + +[recordFields in AnonRecd](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synexpr.html#AnonRecd) + +```fsharp +let v = {| + X = {| + Y = y + |} +|} +``` + +### SynTypeDefnSimpleRepr.Record + +[SynTypeDefnSimpleRepr.Record](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-syntypedefnsimplerepr.html#Record) + +```fsharp +type V = { + X :int + Y: int +} +``` + +Note, access modifiers: + +```fsharp +type V = internal { + X :int + Y: int +} +``` + +Members need the `with` keyword: + +```fsharp +type V = { + X :int + Y: int +} with + member this.XY = X + Y +``` + +The current style does not use the `with` keyword, here is would be a requirement. + +This is also being used in [SynTypeDefnSigRepr.Simple](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-syntypedefnsigrepr.html). + +```fsharp +namespace Meh + +type V = { + X :int + Y: int +} with + member XY : int +``` + +### TypeAbbrev + +[SynType.AnonRecd](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-syntype.html#AnonRecd) in [SynTypeDefnSimpleRepr.TypeAbbrev](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-syntypedefnsimplerepr.html#TypeAbbrev). + +```fsharp +type V = {| + x :int +|} +``` + +### SynArgPats.NamePatPairs + +[SynArgPats.NamePatPairs](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synargpats.html#NamePatPairs) + +```fsharp +match x with +| Foo(x = { + Y = y + }) -> + () +``` + +### SynPat.Record + +[fieldPats in SynPat.Record](https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax-synpat.html#Record) + +```fsharp +match x with +| { Y = { + X = y + }} -> + () + +// no idea if this looks ok but you get the idea, after the `Y =` you have the scenario. +``` + +### Even more nodes + +I'm quite certain that the list above is not complete. + +## Implementation + +The impact will be huge in CodePrinter, there are numerous locations where some clever helper function will need to be called in order not to newline. +I do believe that not placing the newline will not be the only thing that is required to make all these examples work. +Having an entirely new implementation for all the impacted nodes is also not recommended. + +Some re-use might be possible on the record side by turning on [fsharp_multiline_block_brackets_on_same_column](https://github.com/fsprojects/fantomas/blob/master/docs/Documentation.md#fsharp_multiline_block_brackets_on_same_column). +However, by doing this, a new precedent will be introduced. Two settings need to be combined in order for a valid code to be outputted. +This is unseen for the tool. + +And no battle plan survives first contact. Even if everything above is implemented and it produces no warnings whatsoever, the will more definitely be a case that pops up once this is released in the wild. + +## The twist + +Even though this whole thing is a bad idea, like a really bad one, I might be open to it in the future. +There are two things I still wish to achieve in the Fantomas project: +- A better Syntax tree: improvements on the compiler side to simplify Fantomas +- Parallel formatting: formatting certain syntax tree nodes in parallel to speed up things for large files. + +After that, I'm willing to open a bit to what the community wants out of this project. +I might even agreed to the ragnarok feature under very strict conditions. +These obviously would be that the feature is not breaking any existing tests and is not impacting anything else whatsoever. + +I'm also pretty much not going to do this implementation myself unless I'm properly paid for it. +Again, I don't care and this is a bad idea. \ No newline at end of file diff --git a/src/Fantomas.Tests/Fantomas.Tests.fsproj b/src/Fantomas.Tests/Fantomas.Tests.fsproj index a93bd910ea..b6f3e4ebb3 100644 --- a/src/Fantomas.Tests/Fantomas.Tests.fsproj +++ b/src/Fantomas.Tests/Fantomas.Tests.fsproj @@ -92,6 +92,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Fantomas.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs b/src/Fantomas.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs index 4c5d6fbb60..dd11023b13 100644 --- a/src/Fantomas.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs +++ b/src/Fantomas.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs @@ -62,7 +62,7 @@ let ``no extra newline after record type with no members`` () = To : float Name: string } """ - config + { config with MaxRecordWidth = 39 } |> prepend newline |> should equal diff --git a/src/Fantomas.Tests/Ragnarok/DotIndexedSetExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/DotIndexedSetExpressionTests.fs new file mode 100644 index 0000000000..8a119b484b --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/DotIndexedSetExpressionTests.fs @@ -0,0 +1,506 @@ +module Fantomas.Tests.Ragnarok.DotIndexedSetExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``dotIndexedSet with record instance `` () = + formatSourceString + false + """ +myMutable.[x] <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``dotIndexedSet with update record`` () = + formatSourceString + false + """ +myMutable.[x] <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``dotIndexedSet with anonymous record instance`` () = + formatSourceString + false + """ +myMutable.[x] <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``dotIndexedSet with anonymous record instance struct`` () = + formatSourceString + false + """ +myMutable.[x] <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``dotIndexedSet with computation expression`` () = + formatSourceString + false + """ +myMutable.[x] <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- task { + // some computation here + () +} +""" + +[] +let ``dotIndexedSet with list`` () = + formatSourceString + false + """ +myMutable.[x] <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``dotIndexedSet with array`` () = + formatSourceString + false + """ +myMutable.[x] <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +myMutable.[x] <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``application unit dotIndexedSet with record instance `` () = + formatSourceString + false + """ +app().[x] <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``application unit dotIndexedSet with update record`` () = + formatSourceString + false + """ +app().[x] <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``application unit dotIndexedSet with anonymous record instance`` () = + formatSourceString + false + """ +app().[x] <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``application unit dotIndexedSet with anonymous record instance struct`` () = + formatSourceString + false + """ +app().[x] <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``application unit dotIndexedSet with computation expression`` () = + formatSourceString + false + """ +app().[x] <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- task { + // some computation here + () +} +""" + +[] +let ``application unit dotIndexedSet with list`` () = + formatSourceString + false + """ +app().[x] <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``application unit dotIndexedSet with array`` () = + formatSourceString + false + """ +app().[x] <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +app().[x] <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +// See https://github.com/fsprojects/fantomas/issues/1999 + +[] +let ``application parenthesis expr dotIndexedSet with record instance `` () = + formatSourceString + false + """ +app(meh).[x] <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``application parenthesis expr dotIndexedSet with update record`` () = + formatSourceString + false + """ +app(meh).[x] <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``application parenthesis expr dotIndexedSet with anonymous record instance`` () = + formatSourceString + false + """ +app(meh).[x] <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``application parenthesis expr dotIndexedSet with anonymous record instance struct`` () = + formatSourceString + false + """ +app(meh).[x] <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``application parenthesis expr dotIndexedSet with computation expression`` () = + formatSourceString + false + """ +app(meh).[x] <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- task { + // some computation here + () +} +""" + +[] +let ``application parenthesis expr dotIndexedSet with list`` () = + formatSourceString + false + """ +app(meh).[x] <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``application parenthesis expr dotIndexedSet with array`` () = + formatSourceString + false + """ +app(meh).[x] <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +app( + meh +).[x] <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" diff --git a/src/Fantomas.Tests/Ragnarok/DotSetExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/DotSetExpressionTests.fs new file mode 100644 index 0000000000..d2d03783d0 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/DotSetExpressionTests.fs @@ -0,0 +1,170 @@ +module Fantomas.Tests.Ragnarok.DotSetExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``dotSet with record instance `` () = + formatSourceString + false + """ +App().foo <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``dotSet with update record`` () = + formatSourceString + false + """ +App().foo <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``dotSet with anonymous record instance`` () = + formatSourceString + false + """ +App().foo <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``dotSet with anonymous record instance struct`` () = + formatSourceString + false + """ +App().foo <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``dotSet with computation expression`` () = + formatSourceString + false + """ +App().foo <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- task { + // some computation here + () +} +""" + +[] +let ``dotSet with list`` () = + formatSourceString + false + """ +App().foo <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``dotSet with array`` () = + formatSourceString + false + """ +App().foo <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +App().foo <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" diff --git a/src/Fantomas.Tests/Ragnarok/KeepIndentInBranchExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/KeepIndentInBranchExpressionTests.fs new file mode 100644 index 0000000000..418bfe17c6 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/KeepIndentInBranchExpressionTests.fs @@ -0,0 +1,188 @@ +module Fantomas.Tests.Ragnarok.KeepIndentInBranchExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + KeepIndentInBranch = true + Ragnarok = true } + +// There currently is no conflict with this setting, but I'm guessing the case was never brought up. +// I would conclude that will never clash. + +[] +let ``synMatchClause in match expression with record instance `` () = + formatSourceString + false + """ +match x with +| _ -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synMatchClause in match expression with update record`` () = + formatSourceString + false + """ +match x with +| _ -> + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synMatchClause in match expression with anonymous record instance`` () = + formatSourceString + false + """ +match x with +| _ -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in match expression with anonymous record instance struct`` () = + formatSourceString + false + """ +match x with +| _ -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in match expression with computation expression`` () = + formatSourceString + false + """ +match x with +| _ -> + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> task { + // some computation here + () +} +""" + +[] +let ``synMatchClause in match expression with list`` () = + formatSourceString + false + """ +match x with +| _ -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synMatchClause in match expression with array`` () = + formatSourceString + false + """ +match x with +| _ -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" diff --git a/src/Fantomas.Tests/Ragnarok/LambdaExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/LambdaExpressionTests.fs new file mode 100644 index 0000000000..73b3ee97b4 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/LambdaExpressionTests.fs @@ -0,0 +1,856 @@ +module Fantomas.Tests.Ragnarok.LambdaExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``lambda with record instance `` () = + formatSourceString + false + """ +fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``lambda with update record`` () = + formatSourceString + false + """ +fun x -> + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +fun x -> + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``lambda with anonymous record instance`` () = + formatSourceString + false + """ +fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``lambda with computation expression`` () = + formatSourceString + false + """ +fun x -> + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +fun x -> task { + // some computation here + () +} +""" + +[] +let ``lambda with list`` () = + formatSourceString + false + """ +fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``lambda with array`` () = + formatSourceString + false + """ +fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``paren lambda with record instance `` () = + formatSourceString + false + """ +(fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +}) +""" + +[] +let ``paren lambda with update record`` () = + formatSourceString + false + """ +(fun x -> + { astContext with IsInsideMatchClausePattern = true }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> + { astContext with + IsInsideMatchClausePattern = true + }) +""" + +[] +let ``paren lambda with anonymous record instance`` () = + formatSourceString + false + """ +(fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|}) +""" + +[] +let ``paren lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +(fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|}) +""" + +[] +let ``paren lambda with computation expression`` () = + formatSourceString + false + """ +(fun x -> + task { + // some computation here + () + }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> task { + // some computation here + () +}) +""" + +[] +let ``paren lambda with list`` () = + formatSourceString + false + """ +(fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +]) +""" + +[] +let ``paren lambda with array`` () = + formatSourceString + false + """ +(fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|]) +""" + +[] +let ``app paren lambda with record instance `` () = + formatSourceString + false + """ +List.map (fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +}) +""" + +[] +let ``app paren lambda with update record`` () = + formatSourceString + false + """ +List.map (fun x -> + { astContext with IsInsideMatchClausePattern = true }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> + { astContext with + IsInsideMatchClausePattern = true + }) +""" + +[] +let ``app paren lambda with anonymous record instance`` () = + formatSourceString + false + """ +List.map (fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|}) +""" + +[] +let ``app paren lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +List.map (fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|}) +""" + +[] +let ``app paren lambda with computation expression`` () = + formatSourceString + false + """ +List.map (fun x -> + task { + // some computation here + () + }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> task { + // some computation here + () +}) +""" + +[] +let ``app paren lambda with list`` () = + formatSourceString + false + """ +List.map (fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +]) +""" + +[] +let ``app paren lambda with array`` () = + formatSourceString + false + """ +List.map (fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|]) +""" + +[] +let ``app paren lambda with record instance and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + }) + b + c +""" + +[] +let ``app paren lambda with update record and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + { astContext with IsInsideMatchClausePattern = true }) b c +""" + { config with MaxLineLength = 60 } + |> prepend newline + |> should + equal + """ +List.map + (fun x -> + { astContext with + IsInsideMatchClausePattern = true + }) + b + c +""" + +[] +let ``app paren lambda with anonymous record instance and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) + b + c +""" + +[] +let ``app paren lambda with anonymous record instance struct and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) + b + c +""" + +[] +let ``app paren lambda with computation expression and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + task { + // some computation here + () + }) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> task { + // some computation here + () + }) + b + c +""" + +[] +let ``app paren lambda with list and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ]) + b + c +""" + +[] +let ``app paren lambda with array and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |]) + b + c +""" + +[] +let ``dotGetApp with lambda with record instance`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> { A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry }).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry + }) + .Bar() +""" + +[] +let ``dotGetApp with lambda with update record`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> { other with + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry }).Bar() +""" + { config with MaxLineLength = 60 } + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> + { other with + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry + }) + .Bar() +""" + +[] +let ``dotGetApp with lambda with anonymous record instance`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) + .Bar() +""" + +[] +let ``dotGetApp with lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) + .Bar() +""" + +[] +let ``dotGetApp with lambda with computation expression`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + task { + // some computation here + () + }).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> task { + // some computation here + () + }) + .Bar() +""" + +[] +let ``dotGetApp with lambda with list`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ]) + .Bar() +""" + +[] +let ``dotGetApp with lambda with array`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |]) + .Bar() +""" diff --git a/src/Fantomas.Tests/Ragnarok/LetOrUseBangExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/LetOrUseBangExpressionTests.fs new file mode 100644 index 0000000000..dfea4679d6 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/LetOrUseBangExpressionTests.fs @@ -0,0 +1,226 @@ +module Fantomas.Tests.Ragnarok.LetOrUseBangExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``letOrUseBang with record instance`` () = + formatSourceString + false + """ +opt { + let! foo = + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! foo = { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + () +} +""" + +[] +let ``letOrUseBang with update record`` () = + formatSourceString + false + """ +opt { + let! foo = + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! foo = + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + () +} +""" + +[] +let ``letOrUseBang with anonymous record instance`` () = + formatSourceString + false + """ +opt { + let! foo = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! foo = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + () +} +""" + +[] +let ``letOrUseBang with anonymous record instance struct`` () = + formatSourceString + false + """ +opt { + let! foo = + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! foo = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + () +} +""" + +[] +let ``letOrUseBang with computation expression`` () = + formatSourceString + false + """ +task { + let! meh = + task { + // comment + return 42 + } + () +} +""" + config + |> prepend newline + |> should + equal + """ +task { + let! meh = task { + // comment + return 42 + } + + () +} +""" + +[] +let ``letOrUseBang with list`` () = + formatSourceString + false + """ +collect { + let! items = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + return items +} +""" + config + |> prepend newline + |> should + equal + """ +collect { + let! items = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + + return items +} +""" + +[] +let ``letOrUseBang with array`` () = + formatSourceString + false + """ +collect { + let! items = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + return items +} +""" + config + |> prepend newline + |> should + equal + """ +collect { + let! items = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + + return items +} +""" diff --git a/src/Fantomas.Tests/Ragnarok/LongIdentSetExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/LongIdentSetExpressionTests.fs new file mode 100644 index 0000000000..4d403f96f5 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/LongIdentSetExpressionTests.fs @@ -0,0 +1,170 @@ +module Fantomas.Tests.Ragnarok.LongIdentSetExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``longIdentSet with record instance `` () = + formatSourceString + false + """ +myMutable <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``longIdentSet with update record`` () = + formatSourceString + false + """ +myMutable <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``longIdentSet with anonymous record instance`` () = + formatSourceString + false + """ +myMutable <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``longIdentSet with anonymous record instance struct`` () = + formatSourceString + false + """ +myMutable <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``longIdentSet with computation expression`` () = + formatSourceString + false + """ +myMutable <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- task { + // some computation here + () +} +""" + +[] +let ``longIdentSet with list`` () = + formatSourceString + false + """ +myMutable <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``longIdentSet with array`` () = + formatSourceString + false + """ +myMutable <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +myMutable <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" diff --git a/src/Fantomas.Tests/Ragnarok/MultiLineLambdaClosingNewlineExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/MultiLineLambdaClosingNewlineExpressionTests.fs new file mode 100644 index 0000000000..85dfe5983a --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/MultiLineLambdaClosingNewlineExpressionTests.fs @@ -0,0 +1,728 @@ +module Fantomas.Tests.Ragnarok.MultiLineLambdaClosingNewlineExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + MultiLineLambdaClosingNewline = true + Ragnarok = true } + +// TODO: figure out what should happen when you mix MultiLineLambdaClosingNewline and Ragnarok +// From a technical point of view, this is correct behavior but having `})` at the end seems sensible as well. + +[] +let ``paren lambda with record instance`` () = + formatSourceString + false + """ +(fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +) +""" + +[] +let ``paren lambda with update record`` () = + formatSourceString + false + """ +(fun x -> + { astContext with IsInsideMatchClausePattern = true }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> + { astContext with + IsInsideMatchClausePattern = true + } +) +""" + +[] +let ``paren lambda with anonymous record instance`` () = + formatSourceString + false + """ +(fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +) +""" + +[] +let ``paren lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +(fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +) +""" + +[] +let ``paren lambda with computation expression`` () = + formatSourceString + false + """ +(fun x -> + task { + // some computation here + () + }) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> task { + // some computation here + () +} +) +""" + +[] +let ``paren lambda with list`` () = + formatSourceString + false + """ +(fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +) +""" + +[] +let ``paren lambda with array`` () = + formatSourceString + false + """ +(fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) +""" + config + |> prepend newline + |> should + equal + """ +(fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +) +""" + +[] +let ``app paren lambda with record instance `` () = + formatSourceString + false + """ +List.map (fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +) +""" + +[] +let ``app paren lambda with update record`` () = + formatSourceString + false + """ +List.map (fun x -> + { astContext with IsInsideMatchClausePattern = true }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> + { astContext with + IsInsideMatchClausePattern = true + } +) +""" + +[] +let ``app paren lambda with anonymous record instance`` () = + formatSourceString + false + """ +List.map (fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +) +""" + +[] +let ``app paren lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +List.map (fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +) +""" + +[] +let ``app paren lambda with computation expression`` () = + formatSourceString + false + """ +List.map (fun x -> + task { + // some computation here + () + }) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> task { + // some computation here + () +} +) +""" + +[] +let ``app paren lambda with list`` () = + formatSourceString + false + """ +List.map (fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +) +""" + +[] +let ``app paren lambda with array`` () = + formatSourceString + false + """ +List.map (fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) +""" + config + |> prepend newline + |> should + equal + """ +List.map (fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +) +""" + +[] +let ``app paren lambda with record instance and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX }) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } + ) + b + c +""" + +[] +let ``app paren lambda with update record and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + { astContext with IsInsideMatchClausePattern = true }) b c +""" + { config with MaxLineLength = 60 } + |> prepend newline + |> should + equal + """ +List.map + (fun x -> + { astContext with + IsInsideMatchClausePattern = true + } + ) + b + c +""" + +[] +let ``app paren lambda with anonymous record instance and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) + b + c +""" + +[] +let ``app paren lambda with anonymous record instance struct and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) + b + c +""" + +[] +let ``app paren lambda with computation expression and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + task { + // some computation here + () + }) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> task { + // some computation here + () + } + ) + b + c +""" + +[] +let ``app paren lambda with list and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) + b + c +""" + +[] +let ``app paren lambda with array and other args`` () = + formatSourceString + false + """ +List.map (fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]) b c +""" + config + |> prepend newline + |> should + equal + """ +List.map + (fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + ) + b + c +""" + +[] +let ``dotGetApp with lambda with record instance`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> { A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry }).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry + } + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with update record`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> { other with + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry }).Bar() +""" + { config with MaxLineLength = 60 } + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> + { other with + A = longTypeName + B = someOtherVariable + C = ziggyBarX + D = evenMoreZigBarry + } + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with anonymous record instance`` () = + formatSourceString + false + """ +Bar.Foo(fun x -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with anonymous record instance struct`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |}).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with computation expression`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + task { + // some computation here + () + }).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> task { + // some computation here + () + } + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with list`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ]).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) + .Bar() +""" + +[] +let ``dotGetApp with lambda with array`` () = + formatSourceString + false + """ +Bar + .Foo(fun x -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |]).Bar() +""" + config + |> prepend newline + |> should + equal + """ +Bar + .Foo(fun x -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + ) + .Bar() +""" diff --git a/src/Fantomas.Tests/Ragnarok/NamedArgumentExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/NamedArgumentExpressionTests.fs new file mode 100644 index 0000000000..7d61fc2fa9 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/NamedArgumentExpressionTests.fs @@ -0,0 +1,518 @@ +module Fantomas.Tests.Ragnarok.NamedArgumentExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``synExprApp with named argument with record instance`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } + ) +""" + +[] +let ``synExprApp with named argument with update record`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + { astContext with IsInsideMatchClausePattern = true + A = longTypeName + B = someOtherVariable + C = ziggyBarX } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = + { astContext with + IsInsideMatchClausePattern = true + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } + ) +""" + +[] +let ``synExprApp with named argument with anonymous record instance`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) +""" + +[] +let ``synExprApp with named argument with anonymous record instance struct`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) +""" + +[] +let ``synExprApp with named argument with computation expression`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + task { + // some computation here + () + } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = task { + // some computation here + () + } + ) +""" + +[] +let ``synExprApp with named argument with list`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" + +[] +let ``synExprApp with named argument with array`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + v = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + v = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + ) +""" + +[] +let ``synExprApp with multiple named arguments`` () = + formatSourceString + false + """ +let v = + SomeConstructor( + x = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |], + y = + [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + SomeConstructor( + x = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |], + y = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" + +[] +let ``synExprNew with named argument with record instance`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } + ) +""" + +[] +let ``synExprNew with named argument with update record`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + { astContext with IsInsideMatchClausePattern = true + A = longTypeName + B = someOtherVariable + C = ziggyBarX } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = + { astContext with + IsInsideMatchClausePattern = true + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } + ) +""" + +[] +let ``synExprNew with named argument with anonymous record instance`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) +""" + +[] +let ``synExprNew with named argument with anonymous record instance struct`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + ) +""" + +[] +let ``synExprNew with named argument with computation expression`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + task { + // some computation here + () + } + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = task { + // some computation here + () + } + ) +""" + +[] +let ``synExprNew with named argument with list`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" + +[] +let ``synExprNew with named argument with array`` () = + formatSourceString + false + """ +let v = + new FooBar( + v = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + v = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + ) +""" + +[] +let ``synExprNew with multiple named arguments`` () = + formatSourceString + false + """ +let v = + new FooBar( + x = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |], + y = + [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" + config + |> prepend newline + |> should + equal + """ +let v = + new FooBar( + x = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |], + y = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + ) +""" diff --git a/src/Fantomas.Tests/Ragnarok/SetExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SetExpressionTests.fs new file mode 100644 index 0000000000..39fe7c888f --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SetExpressionTests.fs @@ -0,0 +1,170 @@ +module Fantomas.Tests.Ragnarok.SetExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``set with record instance `` () = + formatSourceString + false + """ +myMutable[x] <- + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``set with update record`` () = + formatSourceString + false + """ +myMutable[x] <- + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``set with anonymous record instance`` () = + formatSourceString + false + """ +myMutable[x] <- + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``set with anonymous record instance struct`` () = + formatSourceString + false + """ +myMutable[x] <- + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``set with computation expression`` () = + formatSourceString + false + """ +myMutable[x] <- + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- task { + // some computation here + () +} +""" + +[] +let ``set with list`` () = + formatSourceString + false + """ +myMutable[x] <- + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``set with array`` () = + formatSourceString + false + """ +myMutable[x] <- + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +myMutable[x] <- [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynBindingFunctionExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionExpressionTests.fs new file mode 100644 index 0000000000..a64a94476e --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionExpressionTests.fs @@ -0,0 +1,320 @@ +module Fantomas.Tests.Ragnarok.SynBindingFunctionExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``synbinding function with record instance `` () = + formatSourceString + false + """ +let x y = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +let x y = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synbinding function with update record`` () = + formatSourceString + false + """ +let x y = + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +let x y = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synbinding function with anonymous record instance `` () = + formatSourceString + false + """ +let x y = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +let x y = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synbinding function with computation expression`` () = + formatSourceString + false + """ +let x y = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +let x y = task { + // some computation here + () +} +""" + +[] +let ``synbinding function with list`` () = + formatSourceString + false + """ +let x y = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +let x y = [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synbinding function with array`` () = + formatSourceString + false + """ +let x y = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +let x y = [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``type member function with record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + +[] +let ``type member function with update record`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``type member function with anonymous record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with anonymous record instance struct`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with computation expression`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = task { + // some computation here + () + } +""" + +[] +let ``type member function with list`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +""" + +[] +let ``type member function with array`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynBindingFunctionLongPatternExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionLongPatternExpressionTests.fs new file mode 100644 index 0000000000..0fd8034c34 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionLongPatternExpressionTests.fs @@ -0,0 +1,518 @@ +module Fantomas.Tests.Ragnarok.SynBindingFunctionLongPatternExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MaxLineLength = 80 + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +// TODO: conclude on what should happen here +// This one feels very weird to have `= {` because the pattern is already multiline +[] +let ``synbinding function with record`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + +[] +let ``synbinding function with update record`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { astContext with + IsInsideMatchClausePattern = true + } +""" + + +[] +let ``synbinding function with anonymous record`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``synbinding function with computation expression`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + task { + // some computation here + () + } +""" + +[] +let ``synbinding function with list`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +""" + +[] +let ``synbinding function with array`` () = + formatSourceString + false + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +let private addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +""" + + +[] +let ``type member function with record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + +[] +let ``type member function with update record`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``type member function with anonymous record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with anonymous record instance struct`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with computation expression`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + task { + // some computation here + () + } +""" + +[] +let ``type member function with list`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +""" + +[] +let ``type member function with array`` () = + formatSourceString + false + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.addTaskToScheduler + (scheduler: IScheduler) + taskName + taskCron + prio + (task: unit -> unit) + groupName + = + [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynBindingFunctionWithReturnTypeExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionWithReturnTypeExpressionTests.fs new file mode 100644 index 0000000000..fc32ac78db --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynBindingFunctionWithReturnTypeExpressionTests.fs @@ -0,0 +1,320 @@ +module Fantomas.Tests.Ragnarok.SynBindingFunctionWithReturnTypeExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``synbinding function with record instance `` () = + formatSourceString + false + """ +let x y : MyRecord = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +let x y : MyRecord = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synbinding function with update record`` () = + formatSourceString + false + """ +let x y : MyRecord = + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +let x y : MyRecord = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synbinding function with anonymous record instance `` () = + formatSourceString + false + """ +let x y : {| A:int; B:int; C:int |} = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +let x y : {| A: int; B: int; C: int |} = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synbinding function with computation expression`` () = + formatSourceString + false + """ +let x y: Task = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +let x y : Task = task { + // some computation here + () +} +""" + +[] +let ``synbinding function with list`` () = + formatSourceString + false + """ +let x y : int list = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +let x y : int list = [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synbinding function with array`` () = + formatSourceString + false + """ +let x y : int array = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +let x y : int array = [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``type member function with record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : MyRecord = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : MyRecord = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + +[] +let ``type member function with update record`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : MyRecord = { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : MyRecord = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``type member function with anonymous record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : {| A:int; B:int; C:int |} = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : {| A: int; B: int; C: int |} = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with anonymous record instance struct`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : {| A:int; B:int; C:int |} = + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : {| A: int; B: int; C: int |} = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member function with computation expression`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : Task = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : Task = task { + // some computation here + () + } +""" + +[] +let ``type member function with list`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : int list = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : int list = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +""" + +[] +let ``type member function with array`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar x : int array = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar x : int array = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynBindingValueExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynBindingValueExpressionTests.fs new file mode 100644 index 0000000000..ab455d6e2e --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynBindingValueExpressionTests.fs @@ -0,0 +1,370 @@ +module Fantomas.Tests.Ragnarok.SynBindingValueExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``synbinding value with record instance `` () = + formatSourceString + false + """ +let x = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +let x = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synbinding value with update record`` () = + formatSourceString + false + """ +let astCtx = + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +let astCtx = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synbinding value with anonymous record instance`` () = + formatSourceString + false + """ +let x = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +let x = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synbinding value with anonymous record instance struct`` () = + formatSourceString + false + """ +let x = + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +let x = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synbinding value with computation expression`` () = + formatSourceString + false + """ +let t = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +let t = task { + // some computation here + () +} +""" + +[] +let ``synbinding value with list`` () = + formatSourceString + false + """ +let t = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +let t = [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synbinding value with array`` () = + formatSourceString + false + """ +let t = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +let t = [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``nested synbinding value with record`` () = + formatSourceString + false + """ +let outer = + let inner = + { + X = someGreatXValue + Y = someRatherSmallYValue + } + () +""" + config + |> prepend newline + |> should + equal + """ +let outer = + let inner = { + X = someGreatXValue + Y = someRatherSmallYValue + } + + () +""" + +[] +let ``type member value with record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + +[] +let ``type member value with update record`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``type member value with anonymous record instance`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member value with anonymous record instance struct`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +""" + +[] +let ``type member value with computation expression`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = task { + // some computation here + () + } +""" + +[] +let ``type member value with list`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +""" + +[] +let ``type member value with array`` () = + formatSourceString + false + """ +type Foo() = + member this.Bar = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +type Foo() = + member this.Bar = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynExprAndBangExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynExprAndBangExpressionTests.fs new file mode 100644 index 0000000000..e57ecc9ca8 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynExprAndBangExpressionTests.fs @@ -0,0 +1,248 @@ +module Fantomas.Tests.Ragnarok.SynExprAndBangExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``andBang with record instance`` () = + formatSourceString + false + """ +opt { + let! abc = def () + and! foo = + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! abc = def () + + and! foo = { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + () +} +""" + +[] +let ``andBang with update record`` () = + formatSourceString + false + """ +opt { + let! abc = def () + and! foo = + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! abc = def () + + and! foo = + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + () +} +""" + +[] +let ``andBang with anonymous record instance`` () = + formatSourceString + false + """ +opt { + let! abc = def () + and! foo = + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! abc = def () + + and! foo = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + () +} +""" + +[] +let ``andBang with anonymous record instance struct`` () = + formatSourceString + false + """ +opt { + let! abc = def () + and! foo = + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + + () +} +""" + config + |> prepend newline + |> should + equal + """ +opt { + let! abc = def () + + and! foo = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + () +} +""" + +[] +let ``andBang with computation expression`` () = + formatSourceString + false + """ +task { + let! abc = def () + and! meh = + task { + // comment + return 42 + } + () +} +""" + config + |> prepend newline + |> should + equal + """ +task { + let! abc = def () + + and! meh = task { + // comment + return 42 + } + + () +} +""" + +[] +let ``andBang with list`` () = + formatSourceString + false + """ +collect { + let! abc = def () + and! items = + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + return items +} +""" + config + |> prepend newline + |> should + equal + """ +collect { + let! abc = def () + + and! items = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + + return items +} +""" + +[] +let ``andBang with array`` () = + formatSourceString + false + """ +collect { + let! abc = def () + + and! items = + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + return items +} +""" + config + |> prepend newline + |> should + equal + """ +collect { + let! abc = def () + + and! items = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + + return items +} +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynMatchClauseExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/SynMatchClauseExpressionTests.fs new file mode 100644 index 0000000000..c77b39c7f1 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynMatchClauseExpressionTests.fs @@ -0,0 +1,454 @@ +module Fantomas.Tests.Ragnarok.SynMatchClauseExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``synMatchClause in match expression with record instance `` () = + formatSourceString + false + """ +match x with +| _ -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synMatchClause in match expression with update record`` () = + formatSourceString + false + """ +match x with +| _ -> + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synMatchClause in match expression with anonymous record instance`` () = + formatSourceString + false + """ +match x with +| _ -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in match expression with anonymous record instance struct`` () = + formatSourceString + false + """ +match x with +| _ -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in match expression with computation expression`` () = + formatSourceString + false + """ +match x with +| _ -> + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> task { + // some computation here + () +} +""" + +[] +let ``synMatchClause in match expression with list`` () = + formatSourceString + false + """ +match x with +| _ -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synMatchClause in match expression with array`` () = + formatSourceString + false + """ +match x with +| _ -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +// TODO: Here, I again feel this is fitting not to have ragnarok. +// Similar to long patterns in synbinding functions. + +[] +let ``synMatchClause in match expression with long when expression with record instance `` () = + formatSourceString + false + """ +match x with +| _ when (try + somethingDangerous () + true + with | ex -> false) + -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +match x with +| _ when + (try + somethingDangerous () + true + with + | ex -> false) + -> + { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } +""" + + +[] +let ``synMatchClause in try/with expression with record instance `` () = + formatSourceString + false + """ +try + foo() +with ex -> + { A = longTypeName + B = someOtherVariable + C = ziggyBarX } +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> { + A = longTypeName + B = someOtherVariable + C = ziggyBarX +} +""" + +[] +let ``synMatchClause in try/with expression with update record`` () = + formatSourceString + false + """ +try + foo() +with ex -> + { astContext with IsInsideMatchClausePattern = true } +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> + { astContext with + IsInsideMatchClausePattern = true + } +""" + +[] +let ``synMatchClause in try/with expression with anonymous record instance`` () = + formatSourceString + false + """ +try + foo() +with ex -> + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in try/with expression with anonymous record instance struct`` () = + formatSourceString + false + """ +try + foo() +with ex -> + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX +|} +""" + +[] +let ``synMatchClause in try/with expression with computation expression`` () = + formatSourceString + false + """ +try + foo() +with +| ex -> + task { + // some computation here + () + } +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> task { + // some computation here + () +} +""" + +[] +let ``synMatchClause in try/with expression with list`` () = + formatSourceString + false + """ +try + foo () +with +| ex -> + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> [ + itemOne + itemTwo + itemThree + itemFour + itemFive +] +""" + +[] +let ``synMatchClause in try/with expression with array`` () = + formatSourceString + false + """ +try + foo () +with +| ex -> + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +""" + config + |> prepend newline + |> should + equal + """ +try + foo () +with +| ex -> [| + itemOne + itemTwo + itemThree + itemFour + itemFive +|] +""" + +[] +let ``multiple clauses with lists`` () = + formatSourceString + false + """ +match x with +| SynMemberDefn.ImplicitCtor (_, attrs, ctorArgs, _, _xmlDoc, range) -> + [ yield mkNode SynMemberDefn_ImplicitCtor range + yield! (visitSynAttributeLists attrs) + yield! visitSynSimplePats ctorArgs ] +| SynMemberDefn.ImplicitInherit (inheritType, inheritArgs, _, range) -> + [ yield mkNode SynMemberDefn_ImplicitInherit range + yield! visitSynType inheritType + yield! visitSynExpr inheritArgs ] +""" + config + |> prepend newline + |> should + equal + """ +match x with +| SynMemberDefn.ImplicitCtor (_, attrs, ctorArgs, _, _xmlDoc, range) -> [ + yield mkNode SynMemberDefn_ImplicitCtor range + yield! (visitSynAttributeLists attrs) + yield! visitSynSimplePats ctorArgs + ] +| SynMemberDefn.ImplicitInherit (inheritType, inheritArgs, _, range) -> [ + yield mkNode SynMemberDefn_ImplicitInherit range + yield! visitSynType inheritType + yield! visitSynExpr inheritArgs + ] +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynTypeDefnSigReprSimpleTests.fs b/src/Fantomas.Tests/Ragnarok/SynTypeDefnSigReprSimpleTests.fs new file mode 100644 index 0000000000..4bca70defd --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynTypeDefnSigReprSimpleTests.fs @@ -0,0 +1,96 @@ +module Fantomas.Tests.Ragnarok.SynTypeDefnSigReprSimpleTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``record type definition`` () = + formatSourceString + true + """ +namespace Foo + +type V = + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } +""" + config + |> prepend newline + |> should + equal + """ +namespace Foo + +type V = { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName +} +""" + +[] +[] +let ``record type definition with comment after equals`` () = + formatSourceString + true + """ +namespace Foo + +type V = // comment + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } +""" + config + |> prepend newline + |> should + equal + """ +namespace Foo + +type V = // comment + { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName + } +""" + +// TODO: I feel like ragnarok should not work when there are members involved +// Having members would require the `with` keyword which is not recommended by the style guide: https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#formatting-record-declarations + +[] +let ``record type definition with members`` () = + formatSourceString + true + """ +namespace Foo + +type V = + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } + member Coordinate : SomeFieldType * OhSomethingElse * ALongTypeName +""" + config + |> prepend newline + |> should + equal + """ +namespace Foo + +type V = + { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName + } + member Coordinate: SomeFieldType * OhSomethingElse * ALongTypeName +""" diff --git a/src/Fantomas.Tests/Ragnarok/SynTypeDefnSimpleReprRecordTests.fs b/src/Fantomas.Tests/Ragnarok/SynTypeDefnSimpleReprRecordTests.fs new file mode 100644 index 0000000000..f491ad8254 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/SynTypeDefnSimpleReprRecordTests.fs @@ -0,0 +1,84 @@ +module Fantomas.Tests.Ragnarok.SynTypeDefnSimpleReprRecordTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``record type definition`` () = + formatSourceString + false + """ +type V = + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } +""" + config + |> prepend newline + |> should + equal + """ +type V = { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName +} +""" + +[] +[] +let ``record type definition with comment after equals`` () = + formatSourceString + false + """ +type V = // comment + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } +""" + config + |> prepend newline + |> should + equal + """ +type V = // comment + { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName + } +""" + +// TODO: I feel like ragnarok should not work when there are members involved +// Having members would require the `with` keyword which is not recommended by the style guide: https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#formatting-record-declarations + +[] +let ``record type definition with members`` () = + formatSourceString + false + """ +type V = + { X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName } + member this.Coordinate = (this.X, this.Y, this.Z) +""" + config + |> prepend newline + |> should + equal + """ +type V = + { + X: SomeFieldType + Y: OhSomethingElse + Z: ALongTypeName + } + member this.Coordinate = (this.X, this.Y, this.Z) +""" diff --git a/src/Fantomas.Tests/Ragnarok/YieldOrReturnBangExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/YieldOrReturnBangExpressionTests.fs new file mode 100644 index 0000000000..e0e3427fd9 --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/YieldOrReturnBangExpressionTests.fs @@ -0,0 +1,281 @@ +module Fantomas.Tests.Ragnarok.YieldOrReturnBangExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``yieldOrReturnBang with record instance`` () = + formatSourceString + false + """ +myComp { + yield! + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + return! + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + return! { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } +} +""" + +[] +let ``yieldOrReturnBang with update record`` () = + formatSourceString + false + """ +myComp { + yield! + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + return! + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + return! + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } +} +""" + +[] +let ``yieldOrReturnBang with anonymous record instance`` () = + formatSourceString + false + """ +myComp { + yield! + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + return! + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + return! {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +} +""" + +[] +let ``yieldOrReturnBang with anonymous record instance struct`` () = + formatSourceString + false + """ +myComp { + yield! + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + return! + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + return! struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +} +""" + +[] +let ``yieldOrReturnBang with computation expression`` () = + formatSourceString + false + """ +myComp { + yield! + seq { + // meh + return 0 .. 2 + } + return! + seq { + // meh + return 0 .. 2 + } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! seq { + // meh + return 0..2 + } + + return! seq { + // meh + return 0..2 + } +} +""" + +[] +let ``yieldOrReturnBang with list`` () = + formatSourceString + false + """ +myComp { + yield! + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + return! + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + + return! [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +} +""" + +[] +let ``yieldOrReturnBang with array`` () = + formatSourceString + false + """ +myComp { + yield! + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + return! + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield! [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + + return! [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +} +""" diff --git a/src/Fantomas.Tests/Ragnarok/YieldOrReturnExpressionTests.fs b/src/Fantomas.Tests/Ragnarok/YieldOrReturnExpressionTests.fs new file mode 100644 index 0000000000..7818f6827c --- /dev/null +++ b/src/Fantomas.Tests/Ragnarok/YieldOrReturnExpressionTests.fs @@ -0,0 +1,281 @@ +module Fantomas.Tests.Ragnarok.YieldOrReturnExpressionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +let config = + { config with + MultilineBlockBracketsOnSameColumn = true + Ragnarok = true } + +[] +let ``yieldOrReturn with record instance`` () = + formatSourceString + false + """ +myComp { + yield + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + return + { X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + return { + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } +} +""" + +[] +let ``yieldOrReturn with update record`` () = + formatSourceString + false + """ +myComp { + yield + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } + return + { bar with X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } + + return + { bar with + X = xFieldValueOne + Y = yFieldValueTwo + Z = zFieldValueThree + } +} +""" + +[] +let ``yieldOrReturn with anonymous record instance`` () = + formatSourceString + false + """ +myComp { + yield + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + return + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + return {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +} +""" + +[] +let ``yieldOrReturn with anonymous record instance struct`` () = + formatSourceString + false + """ +myComp { + yield + struct {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} + return + struct + {| A = longTypeName + B = someOtherVariable + C = ziggyBarX |} +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} + + return struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} +} +""" + +[] +let ``yieldOrReturn with computation expression`` () = + formatSourceString + false + """ +myComp { + yield + seq { + // meh + return 0 .. 2 + } + return + seq { + // meh + return 0 .. 2 + } +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield seq { + // meh + return 0..2 + } + + return seq { + // meh + return 0..2 + } +} +""" + +[] +let ``yieldOrReturn with list`` () = + formatSourceString + false + """ +myComp { + yield + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] + return + [ itemOne + itemTwo + itemThree + itemFour + itemFive ] +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] + + return [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] +} +""" + +[] +let ``yieldOrReturn with array`` () = + formatSourceString + false + """ +myComp { + yield + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] + return + [| itemOne + itemTwo + itemThree + itemFour + itemFive |] +} +""" + config + |> prepend newline + |> should + equal + """ +myComp { + yield [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] + + return [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] +} +""" diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 319aef4372..bce34b7ded 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -1003,10 +1003,7 @@ and genNamedArgumentExpr (astContext: ASTContext) operatorExpr e1 e2 appRange = genExpr astContext e1 +> sepSpace +> genInfixOperator "=" operatorExpr - +> indent - +> sepNln - +> genExpr astContext e2 - +> unindent + +> autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExpr astContext e) e2 expressionFitsOnRestOfLine short long |> genTriviaFor SynExpr_App appRange @@ -1220,6 +1217,16 @@ and genExpr astContext synExpr ctx = +> ifElse isInfixExpr genInfixExpr genNonInfixExpr | SingleExpr (kind, e) -> + let mapping = + (match kind with + | YieldFrom _ + | Yield _ + | Return _ + | ReturnFrom _ + | Do _ + | DoBang _ -> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e + | _ -> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e)) + match kind with | InferredDowncast downcastKeyword -> genTriviaFor SynExpr_InferredDowncast_Downcast downcastKeyword !- "downcast " @@ -1236,7 +1243,8 @@ and genExpr astContext synExpr ctx = | Do doKeyword -> genTriviaFor SynExpr_Do_Do doKeyword !- "do " | DoBang doBangKeyword -> genTriviaFor SynExpr_DoBang_DoBang doBangKeyword !- "do! " | Fixed fixedKeyword -> genTriviaFor SynExpr_Fixed_Fixed fixedKeyword !- "fixed " - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e) + +> mapping + | ConstExpr (c, r) -> genConst c r | NullExpr -> !- "null" // Not sure about the role of e1 @@ -1477,13 +1485,13 @@ and genExpr astContext synExpr ctx = +> genPat astContext pat +> genEq SynExpr_LetOrUseBang_Equals equalsRange +> sepSpace - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext expr) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) expr | AndBangStatement (pat, equalsRange, expr, range) -> !- "and! " +> genPat astContext pat +> genEq SynExprAndBang_Equals (Some equalsRange) +> sepSpace - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext expr) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) expr |> genTriviaFor SynExprAndBang_ range | OtherStatement expr -> genExpr astContext expr @@ -1515,9 +1523,9 @@ and genExpr astContext synExpr ctx = | JoinIn (e1, e2) -> genExpr astContext e1 -- " in " +> genExpr astContext e2 - | Paren (lpr, Lambda (pats, arrowRange, expr, lambdaRange), rpr, _pr) -> + | Paren (lpr, Lambda (pats, arrowRange, expr, lambdaRange), rpr, pr) -> fun (ctx: Context) -> - let body = genExprKeepIndentInBranch astContext expr + let body = genExprKeepIndentInBranch astContext let expr = let triviaOfLambda f (ctx: Context) = @@ -1533,18 +1541,22 @@ and genExpr astContext synExpr ctx = +> (fun ctx -> if not ctx.Config.MultiLineLambdaClosingNewline then genLambdaArrowWithTrivia - (body - +> triviaOfLambda printContentAfter - +> sepNlnWhenWriteBeforeNewlineNotEmpty id - +> sepCloseTFor rpr) + (fun e -> + body e + +> triviaOfLambda printContentAfter + +> sepNlnWhenWriteBeforeNewlineNotEmpty id + +> sepCloseTFor rpr) + expr arrowRange ctx else leadingExpressionIsMultiline (genLambdaArrowWithTrivia - (body - +> triviaOfLambda printContentAfter - +> sepNlnWhenWriteBeforeNewlineNotEmpty id) + (fun e -> + body e + +> triviaOfLambda printContentAfter + +> sepNlnWhenWriteBeforeNewlineNotEmpty id) + expr arrowRange) (fun isMultiline -> onlyIf isMultiline sepNln +> sepCloseTFor rpr) ctx) @@ -1561,7 +1573,7 @@ and genExpr astContext synExpr ctx = sepArrow |> genTriviaFor SynExpr_Lambda_Arrow arrowRange) arrowRange - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext expr) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) expr ) | MatchLambda (keywordRange, cs) -> (!- "function " @@ -2040,7 +2052,8 @@ and genExpr astContext synExpr ctx = +> (!- "fun " +> col sepSpace pats (genPat astContext) +> genLambdaArrowWithTrivia - (genExprKeepIndentInBranch astContext bodyExpr) + (genExprKeepIndentInBranch astContext) + bodyExpr arrowRange |> genTriviaFor SynExpr_Lambda range) +> sepNln @@ -2117,7 +2130,7 @@ and genExpr astContext synExpr ctx = +> (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext body) arrowRange + +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) +> sepNlnWhenWriteBeforeNewlineNotEmpty sepNone +> sepCloseTFor rpr @@ -2132,7 +2145,7 @@ and genExpr astContext synExpr ctx = +> (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext body) arrowRange + +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) +> sepCloseTFor rpr |> genTriviaFor SynExpr_Paren pr) @@ -2219,7 +2232,11 @@ and genExpr astContext synExpr ctx = +> genTriviaFor SynExpr_TryWith_With withKeyword (!- "with") +> indentOnWith +> sepNln - +> col sepNln cs (genClause astContext true) + +> (fun ctx -> + let hasMultipleClausesWhereOneHasRagnarok = + hasMultipleClausesWhereOneHasRagnarok ctx.Config.Ragnarok cs + + col sepNln cs (genClause astContext true hasMultipleClausesWhereOneHasRagnarok) ctx) +> unindentOnWith ) @@ -2400,7 +2417,7 @@ and genExpr astContext synExpr ctx = +> opt id lastRange (leaveNodeFor Ident_) | LongIdentSet (s, e, _) -> !-(sprintf "%s <- " s) - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e | DotIndexedGet (App (e, [ ConstExpr (SynConst.Unit, _) as ux ]), indexArgs) -> genExpr astContext e +> genExpr astContext ux @@ -2442,7 +2459,7 @@ and genExpr astContext synExpr ctx = (appExpr +> idx +> genExpr astContext valueExpr) (appExpr +> idx - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext valueExpr)) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) valueExpr) | DotIndexedSet (AppSingleParenArg (a, px), indexArgs, valueExpr) -> let short = genExpr astContext a +> genExpr astContext px @@ -2461,14 +2478,14 @@ and genExpr astContext synExpr ctx = (short +> idx +> genExpr astContext valueExpr) (long +> idx - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext valueExpr)) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) valueExpr) | DotIndexedSet (objectExpr, indexArgs, valueExpr) -> addParenIfAutoNln objectExpr (genExpr astContext) -- ".[" +> genExpr astContext indexArgs -- "] <- " - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext valueExpr) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) valueExpr | NamedIndexedPropertySet (ident, e1, e2) -> !-ident +> genExpr astContext e1 -- " <- " +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e2) @@ -2494,12 +2511,12 @@ and genExpr astContext synExpr ctx = | DotSet (e1, s, e2) -> addParenIfAutoNln e1 (genExpr astContext) -- sprintf ".%s <- " s - +> genExpr astContext e2 + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e2 | SynExpr.Set (e1, e2, _) -> addParenIfAutoNln e1 (genExpr astContext) -- sprintf " <- " - +> genExpr astContext e2 + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e2 | ParsingError r -> raise @@ -2871,7 +2888,7 @@ and genMultilineFunctionApplicationArguments astContext argExpr = (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext body) arrowRange + +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange |> genTriviaFor SynExpr_Lambda range) +> sepNln +> sepCloseTFor rpr) @@ -3025,7 +3042,6 @@ and genMultilineRecordInstanceAlignBrackets +> unindent +> ifElseCtx lastWriteEventIsNewline sepNone sepNln +> genTriviaFor SynExpr_Record_ClosingBrace closingBrace sepCloseSFixed) - |> atCurrentColumnIndent and genMultilineAnonRecord (isStruct: bool) fields copyInfo (astContext: ASTContext) = let recordExpr = @@ -3101,7 +3117,7 @@ and genMultilineAnonRecordAlignBrackets (isStruct: bool) fields copyInfo astCont +> sepCloseAnonRecdFixed ifElse isStruct !- "struct " sepNone - +> atCurrentColumnIndent genAnonRecord + +> genAnonRecord and genObjExpr t eio withKeyword bd members ims range (astContext: ASTContext) = // Check the role of the second part of eio @@ -3243,7 +3259,7 @@ and genApp astContext e es ctx = leadingExpressionIsMultiline (sepOpenTFor lpr -- "fun " +> pats - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext bodyExpr) arrowRange) + +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) bodyExpr arrowRange) (fun isMultiline -> onlyIf isMultiline sepNln +> sepCloseTFor rpr) |> genTriviaFor SynExpr_Paren pr @@ -3289,7 +3305,7 @@ and genLambdaMultiLineClosingNewline leadingExpressionIsMultiline (sepOpenTFor lpr -- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext bodyExpr) arrowRange + +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) bodyExpr arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) (fun isMultiline -> onlyIf isMultiline sepNln +> sepCloseTFor rpr) |> genTriviaFor SynExpr_Paren pr @@ -3613,9 +3629,7 @@ and genTypeDefn closingBrace) (genMultilineSimpleRecordTypeDefn astContext openingBrace withKeyword ms ao' fs closingBrace) - let bodyExpr ctx = - let size = getRecordSize ctx fs - + let bodyExpr size ctx = if (List.isEmpty ms) then (isSmallExpression size smallExpression multilineExpression +> leaveNodeFor SynTypeDefnSimpleRepr_Record tdr.Range // this will only print something when there is trivia after } in the short expression @@ -3625,11 +3639,22 @@ and genTypeDefn else multilineExpression ctx + let genTypeDefinition (ctx: Context) = + let size = getRecordSize ctx fs + + let short = + enterNodeFor SynTypeDefnSimpleRepr_Record tdr.Range + +> bodyExpr size + +> leaveNodeFor SynTypeDefnSimpleRepr_Record tdr.Range + + if ctx.Config.Ragnarok && ms.IsEmpty then + (sepSpace +> short) ctx + else + isSmallExpression size short (indent +> sepNln +> short +> unindent) ctx + typeName +> genEq SynTypeDefn_Equals equalsRange - +> indent - +> genTriviaFor SynTypeDefnSimpleRepr_Record tdr.Range bodyExpr - +> unindent + +> genTypeDefinition | Simple TDSRNone -> typeName | Simple (TDSRTypeAbbrev t) -> @@ -3791,8 +3816,7 @@ and genMultilineSimpleRecordTypeDefn astContext openingBrace withKeyword ms ao' and genMultilineSimpleRecordTypeDefnAlignBrackets astContext openingBrace withKeyword ms ao' fs closingBrace = // the typeName is already printed - sepNlnUnlessLastEventIsNewline - +> opt (indent +> sepNln) ao' genAccess + opt (indent +> sepNln) ao' genAccess +> enterNodeFor SynTypeDefnSimpleRepr_Record_OpeningBrace openingBrace +> sepOpenSFixed +> indent @@ -3906,9 +3930,7 @@ and genSigTypeDefn (genSigSimpleRecordAlignBrackets astContext openingBrace withKeyword ms ao' fs closingBrace) (genSigSimpleRecord astContext openingBrace withKeyword ms ao' fs closingBrace) - let bodyExpr ctx = - let size = getRecordSize ctx fs - + let bodyExpr size ctx = if (List.isEmpty ms) then (isSmallExpression size smallExpression multilineExpression +> leaveNodeFor SynTypeDefnSimpleRepr_Record tdr.Range // this will only print something when there is trivia after } in the short expression @@ -3918,11 +3940,20 @@ and genSigTypeDefn else multilineExpression ctx - typeName - +> genEq SynTypeDefnSig_Equals equalsRange - +> indent - +> genTriviaFor SynTypeDefnSimpleRepr_Record tdr.Range bodyExpr - +> unindent + let genTypeDefinition (ctx: Context) = + let size = getRecordSize ctx fs + + let short = + enterNodeFor SynTypeDefnSimpleRepr_Record tdr.Range + +> bodyExpr size + +> leaveNodeFor SynTypeDefnSimpleRepr_Record tdr.Range + + if ctx.Config.Ragnarok && ms.IsEmpty then + (sepSpace +> short) ctx + else + isSmallExpression size short (indent +> sepNln +> short +> unindent) ctx + + typeName +> sepEq +> genTypeDefinition | SigSimple TDSRNone -> let genMembers = @@ -4009,10 +4040,7 @@ and genSigTypeDefn |> genTriviaFor SynTypeDefnSig_ fullRange and genSigSimpleRecord astContext openingBrace withKeyword ms ao' fs closingBrace = - // the typeName is already printed - sepNlnUnlessLastEventIsNewline - +> opt (indent +> sepNln) ao' genAccess - +> enterNodeFor SynTypeDefnSimpleRepr_Record_OpeningBrace openingBrace + opt (indent +> sepNln) ao' genAccess +> sepOpenS +> atCurrentColumn ( leaveNodeFor SynTypeDefnSimpleRepr_Record_OpeningBrace openingBrace @@ -4025,10 +4053,7 @@ and genSigSimpleRecord astContext openingBrace withKeyword ms ao' fs closingBrac +> colPre sepNln sepNln ms (genMemberSig astContext) and genSigSimpleRecordAlignBrackets astContext openingBrace withKeyword ms ao' fs closingBrace = - // the typeName is already printed - sepNlnUnlessLastEventIsNewline - +> opt (indent +> sepNln) ao' genAccess - +> enterNodeFor SynTypeDefnSimpleRepr_Record_OpeningBrace openingBrace + opt (indent +> sepNln) ao' genAccess +> sepOpenSFixed +> indent +> sepNln @@ -4519,7 +4544,12 @@ and genInterfaceImpl astContext (InterfaceImpl (t, withKeywordRange, bs, members +> genMemberDefnList astContext members +> unindent -and genClause astContext hasBar (Clause (p, eo, arrowRange, e) as ce) = +and genClause + (astContext: ASTContext) + (hasBar: bool) + (hasMultipleClausesWhereOneHasRagnarok: bool) + (Clause (p, eo, arrowRange, e) as ce) + = let astCtx = { astContext with IsInsideMatchClausePattern = true } let patAndBody = @@ -4561,14 +4591,34 @@ and genClause astContext hasBar (Clause (p, eo, arrowRange, e) as ce) = sepArrow |> genTriviaFor SynMatchClause_Arrow arrowRange) arrowRange - +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e)) + +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e) ctx) - (onlyIf hasBar sepBar +> patAndBody + (onlyIf hasBar sepBar + +> (fun ctx -> + if hasMultipleClausesWhereOneHasRagnarok then + // avoid edge case + (* + match x with + | y -> [ + 1 + 2 + 3 + ] + | z -> [ + 4 + 5 + 6 + ] + *) + // ] and | cannot align, otherwise you get a parser error + atCurrentColumn patAndBody ctx + else + patAndBody ctx) |> genTriviaFor SynMatchClause_ ce.Range) -and genClauses astContext cs = - col sepNln cs (genClause astContext true) +and genClauses astContext cs (ctx: Context) = + col sepNln cs (genClause astContext true (hasMultipleClausesWhereOneHasRagnarok ctx.Config.Ragnarok cs)) ctx /// Each multiline member definition has a pre and post new line. and genMemberDefnList astContext nodes = @@ -5109,7 +5159,12 @@ and genSynBindingFunction if isMultiline then (indent +> sepNln +> body +> unindent) else - sepSpaceIfShortExpressionOrAddIndentAndNewline ctx.Config.MaxFunctionBindingWidth body + let short = sepSpace +> body + + let long = + autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExprKeepIndentInBranch astContext e) e + + isShortExpression ctx.Config.MaxFunctionBindingWidth short long (genPreXmlDoc px +> genAttrIsFirstChild @@ -5217,7 +5272,12 @@ and genSynBindingFunctionWithReturnType if isMultiline then (indent +> sepNln +> body +> unindent) else - sepSpaceIfShortExpressionOrAddIndentAndNewline ctx.Config.MaxFunctionBindingWidth body + let short = sepSpace +> body + + let long = + autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExprKeepIndentInBranch astContext e) e + + isShortExpression ctx.Config.MaxFunctionBindingWidth short long (genPreXmlDoc px +> genAttrIsFirstChild @@ -5338,10 +5398,7 @@ and genSynBindingValue let long = prefix - +> indent - +> sepNln - +> genExprKeepIndentInBranch astContext e - +> unindent + +> autoIndentAndNlnExpressUnlessRagnarok (genExprKeepIndentInBranch astContext) e isShortExpression ctx.Config.MaxValueBindingWidth short long ctx) @@ -5425,11 +5482,19 @@ and genKeepIndentMatch withRange ) +> sepNln - +> coli sepNln clauses (fun idx -> - if idx < lastClauseIndex then - genClause astContext true - else - genLastClauseKeepIdent astContext) + +> (fun ctx -> + let hasMultipleClausesWhereOneHasRagnarok = + hasMultipleClausesWhereOneHasRagnarok ctx.Config.Ragnarok clauses + + coli + sepNln + clauses + (fun idx -> + if idx < lastClauseIndex then + genClause astContext true hasMultipleClausesWhereOneHasRagnarok + else + genLastClauseKeepIdent astContext) + ctx) |> genTriviaFor triviaType range and genLastClauseKeepIdent (astContext: ASTContext) (Clause (pat, whenExpr, arrowRange, expr)) = @@ -5674,7 +5739,11 @@ and genTriviaForOption (mainNodeName: FsAstType) (range: range option) f ctx = | None -> ctx | Some range -> genTriviaFor mainNodeName range f ctx -and genLambdaArrowWithTrivia (bodyExpr: Context -> Context) (arrowRange: Range option) = +and genLambdaArrowWithTrivia + (bodyExpr: SynExpr -> Context -> Context) + (body: SynExpr) + (arrowRange: Range option) + : Context -> Context = optSingle (fun arrowRange -> sepArrow @@ -5682,9 +5751,9 @@ and genLambdaArrowWithTrivia (bodyExpr: Context -> Context) (arrowRange: Range o arrowRange +> (fun ctx -> if String.isNotNullOrEmpty ctx.WriterModel.WriteBeforeNewline then - (indent +> sepNln +> bodyExpr +> unindent) ctx + (indent +> sepNln +> (bodyExpr body) +> unindent) ctx else - (autoIndentAndNlnIfExpressionExceedsPageWidth bodyExpr) ctx) + autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok bodyExpr body ctx) and infixOperatorFromTrivia range fallback (ctx: Context) = // by specs, section 3.4 https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf#page=24&zoom=auto,-137,312 diff --git a/src/Fantomas/Context.fs b/src/Fantomas/Context.fs index 476c8ffb6b..bdd76e1d2a 100644 --- a/src/Fantomas/Context.fs +++ b/src/Fantomas/Context.fs @@ -631,6 +631,10 @@ let internal getRecordSize ctx fields = let internal ifElse b (f1: Context -> Context) f2 (ctx: Context) = if b then f1 ctx else f2 ctx let internal ifElseCtx cond (f1: Context -> Context) f2 (ctx: Context) = if cond ctx then f1 ctx else f2 ctx +let internal ifRagnarokElse = ifElseCtx (fun ctx -> ctx.Config.Ragnarok) + +let internal ifRagnarok (f1: Context -> Context) = + ifElseCtx (fun ctx -> ctx.Config.Ragnarok) f1 id /// apply f only when cond is true let internal onlyIf cond f ctx = if cond then f ctx else ctx @@ -680,6 +684,12 @@ let internal sepNlnUnlessLastEventIsNewline (ctx: Context) = else sepNln ctx +let internal sepNlnUnlessLastEventIsNewlineOrRagnarok (ctx: Context) = + if lastWriteEventIsNewline ctx || ctx.Config.Ragnarok then + ctx + else + sepNln ctx + let internal sepStar = !- " * " let internal sepStarFixed = !- "* " let internal sepEq = !- " =" @@ -1311,6 +1321,20 @@ let internal addExtraNewlineIfLeadingWasMultiline leading sepNlnConsideringTrivi +> onlyIf ml sepNlnConsideringTriviaContentBefore +> continuation) +let internal autoIndentAndNlnExpressUnlessRagnarok (f: SynExpr -> Context -> Context) (e: SynExpr) (ctx: Context) = + match e with + | SourceParser.RagnarokExpr e when ctx.Config.Ragnarok -> f e ctx + | _ -> (indent +> sepNln +> f e +> unindent) ctx + +let internal autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok + (f: SynExpr -> Context -> Context) + (e: SynExpr) + (ctx: Context) + = + match e with + | SourceParser.RagnarokExpr e when ctx.Config.Ragnarok -> f e ctx + | _ -> autoIndentAndNlnIfExpressionExceedsPageWidth (f e) ctx + type internal ColMultilineItem = | ColMultilineItem of // current expression diff --git a/src/Fantomas/FormatConfig.fs b/src/Fantomas/FormatConfig.fs index b34d600534..d0715bfebb 100644 --- a/src/Fantomas/FormatConfig.fs +++ b/src/Fantomas/FormatConfig.fs @@ -218,6 +218,10 @@ type FormatConfig = [] BarBeforeDiscriminatedUnionDeclaration: bool + [] + [] + Ragnarok: bool + [] [] [] @@ -263,4 +267,5 @@ type FormatConfig = KeepIndentInBranch = false BlankLinesAroundNestedMultilineExpressions = true BarBeforeDiscriminatedUnionDeclaration = false + Ragnarok = false StrictMode = false } diff --git a/src/Fantomas/SourceParser.fs b/src/Fantomas/SourceParser.fs index 412d1c553c..31ad76fa61 100644 --- a/src/Fantomas/SourceParser.fs +++ b/src/Fantomas/SourceParser.fs @@ -1857,3 +1857,24 @@ let (|KeepIndentIfThenElse|_|) (e: SynExpr) = else None | _ -> None + +let (|RagnarokExpr|_|) (e: SynExpr) = + match e with + // { foo with Bar = bar } + | SynExpr.Record(copyInfo = Some _) -> None + | SynExpr.Record _ + | SynExpr.AnonRecd _ + // task { ... } + | SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.Ident _, SynExpr.ComputationExpr _, _) + | ArrayOrList _ -> Some e + | _ -> None + +let hasMultipleClausesWhereOneHasRagnarok (ragnarokEnabled) (cs: SynMatchClause list) : bool = + ragnarokEnabled + && List.moreThanOne cs + && List.exists + (fun (SynMatchClause (resultExpr = e)) -> + match e with + | RagnarokExpr _ -> true + | _ -> false) + cs