From caece402d1ef77538fc41896cb83096c1c54a1bc Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 6 Dec 2022 23:10:44 -0800 Subject: [PATCH 1/8] Multiline pattern proof of concept. --- .../Fantomas.Core.Tests.fsproj | 1 + src/Fantomas.Core.Tests/MultilinePatterns.fs | 388 ++++++++++++++++++ .../PatternMatchingTests.fs | 10 +- src/Fantomas.Core/CodePrinter.fs | 47 ++- 4 files changed, 433 insertions(+), 13 deletions(-) create mode 100644 src/Fantomas.Core.Tests/MultilinePatterns.fs diff --git a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj index 621f333f3c..90ca88d1dc 100644 --- a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj +++ b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj @@ -128,6 +128,7 @@ + diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs new file mode 100644 index 0000000000..68630f1a2c --- /dev/null +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -0,0 +1,388 @@ +module Fantomas.Core.Tests.MultilinePatterns + +open NUnit.Framework +open FsUnit +open Fantomas.Core.Tests.TestHelper + +[] +let ``long ident tuple pattern`` () = + formatSourceString + false + """ +match a with +| X ( // comment + y, + // other comment + z + ) -> + do () + 1 +""" + config + |> prepend newline + |> should + equal + """ +match a with +| X( // comment + y, + // other comment + z + ) -> + do () + 1 +""" + +[] +let ``multiple clauses with long ident app`` () = + formatSourceString + false + """ +match a with +| X( // comment + y, + // other comment + z) -> + do () + 1 +| Z( + [| 1 ; 2 // comment + 3 |] + ) -> + do() + 2 +""" + config + |> prepend newline + |> should + equal + """ +match a with +| X( // comment + y, + // other comment + z + ) -> + do () + 1 +| Z( + [| 1 + 2 // comment + 3 |] + ) -> + do () + 2 +""" + +[] +let ``multiple clauses with long ident app in or pattern`` () = + formatSourceString + false + """ +match a with +| X( // comment + y, + // other comment + z) +| Z( + [| 1 ; 2 // comment + 3 |] + ) -> + do() + 2 +""" + config + |> prepend newline + |> should + equal + """ +match a with +| X( // comment + y, + // other comment + z + ) +| Z( + [| 1 + 2 // comment + 3 |] + ) -> + do () + 2 +""" + +[] +let ``single named pair pattern`` () = + formatSourceString + false + """ +match a with +| Xray ( y = + Zulu ( + a, + // comment + b, c + ) + ) -> + do () + "meh" +""" + config + |> prepend newline + |> should + equal + """ +match a with +| Xray(y = + Zulu( + a, + // comment + b, + c + ) + ) -> + do () + "meh" +""" + +[] +let ``multiple short name pairs`` () = + formatSourceString + false + """ +match x with +| Alfa ( + bravo = + Bravo(x, y, z) + charlie = + Charlie(x, y, z) + delta = + Delta(x, y, z) + ) -> + do () + "meh" +""" + config + |> prepend newline + |> should + equal + """ +match x with +| Alfa(bravo = Bravo(x, y, z); charlie = Charlie(x, y, z); delta = Delta(x, y, z)) -> + do () + "meh" +""" + +[] +let ``multiple short name pairs that don't fit on a single line`` () = + formatSourceString + false + """ +match x with +| Alfa ( + bravo = + Bravo(x, y, z) + charlie = + Charlie(x, y, z) + delta = + Delta(x, y, z) + echo = + Echo (x, y, z) + foxtrot = + Foxtrot(x, y, z) + golf = + Golf(x, y, z) + ) -> + do () + "meh" +""" + config + |> prepend newline + |> should + equal + """ +match x with +| Alfa( + bravo = Bravo(x, y, z) + charlie = Charlie(x, y, z) + delta = Delta(x, y, z) + echo = Echo(x, y, z) + foxtrot = Foxtrot(x, y, z) + golf = Golf(x, y, z) + ) -> + do () + "meh" +""" + +[] +let ``a mix of multiple name pairs that don't fit on a single line`` () = + formatSourceString + false + " +match x with +| Kilo ( + lima = Lima(a, b ,c ) ; mike = mickey; + november = \"\"\" +Nothin' lasts forever +And we both know hearts can change +And it's hard to hold a candle +In the cold November rain + \"\"\" + ) -> + do () + \"meh\" +" + config + |> prepend newline + |> should + equal + " +match x with +| Kilo( + lima = Lima(a, b, c) + mike = mickey + november = + \"\"\" +Nothin' lasts forever +And we both know hearts can change +And it's hard to hold a candle +In the cold November rain + \"\"\" + ) -> + do () + \"meh\" +" + +[] +let ``long ident app with constants`` () = + formatSourceString + false + """ +match foo with +| SomeVeryLongMatchCase (1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890) -> bar () +| _ -> () +""" + { config with + MaxLineLength = 80 + SpaceBeforeUppercaseInvocation = true } + |> prepend newline + |> should + equal + """ +match foo with +| SomeVeryLongMatchCase ( + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890, + 1234567890 + ) -> bar () +| _ -> () +""" + +[] +let ``syntax tree example one`` () = + formatSourceString + false + """ +match parseResults with +| ParsedInput.ImplFile (ParsedImplFileInput (contents = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types(typeDefns = [SynTypeDefn(typeRepr = SynTypeDefnRepr.ObjectModel(members = [ _; SynMemberDefn.Member(memberDefn = SynBinding(trivia={ EqualsRange = Some mEquals }))]))]) +]) ])) -> + assertRange (3, 18) (3, 19) mEquals +| _ -> Assert.Fail "Could not get valid AST" +""" + config + |> prepend newline + |> fun output -> + printfn $"%s{output}" + output + |> should + equal + """ +match parseResults with +| ParsedInput.ImplFile( + ParsedImplFileInput(contents = + [ SynModuleOrNamespace.SynModuleOrNamespace(decls = + [ SynModuleDecl.Types(typeDefns = + [ SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = + [ _; SynMemberDefn.Member(memberDefn = SynBinding(trivia = { EqualsRange = Some mEquals })) ] + ) + ) ] + ) ] + ) ] + ) + ) -> assertRange (3, 18) (3, 19) mEquals +| _ -> Assert.Fail "Could not get valid AST" +""" + +[] +let ``syntax tree example two `` () = + formatSourceString + false + """ +match parseResults with +| ParsedInput.ImplFile(ParsedImplFileInput( + modules = [ SynModuleOrNamespace.SynModuleOrNamespace( + decls = [ SynModuleDecl.Types( + typeDefns = [ SynTypeDefn( + typeRepr = SynTypeDefnRepr.ObjectModel( + members = [ SynMemberDefn.ImplicitCtor _ + SynMemberDefn.GetSetMember(Some(SynBinding( + headPat = SynPat.LongIdent( + extraId = Some getIdent))), + Some(SynBinding( + headPat = SynPat.LongIdent( + extraId = Some setIdent))), + m, + { WithKeyword = mWith + GetKeyword = Some mGet + AndKeyword = Some mAnd + SetKeyword = Some mSet }) ])) ]) ]) ])) -> + () +""" + config + |> prepend newline + |> fun output -> + printfn $"%s{output}" + output + |> should + equal + """ +match parseResults with +| ParsedInput.ImplFile( + ParsedImplFileInput(modules = + [ SynModuleOrNamespace.SynModuleOrNamespace(decls = + [ SynModuleDecl.Types(typeDefns = + [ SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = + [ SynMemberDefn.ImplicitCtor _ + SynMemberDefn.GetSetMember( + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))), + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))), + m, + { WithKeyword = mWith + GetKeyword = Some mGet + AndKeyword = Some mAnd + SetKeyword = Some mSet } + ) ] + ) + ) ] + ) ] + ) ] + ) + ) -> () +""" diff --git a/src/Fantomas.Core.Tests/PatternMatchingTests.fs b/src/Fantomas.Core.Tests/PatternMatchingTests.fs index d8830d73bd..07873420f2 100644 --- a/src/Fantomas.Core.Tests/PatternMatchingTests.fs +++ b/src/Fantomas.Core.Tests/PatternMatchingTests.fs @@ -1179,8 +1179,9 @@ type Thing = | Foo of msg: string override this.ToString() = match this with - | Foo(ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) -> - "" + | Foo( + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) -> "" """ [] @@ -1211,8 +1212,9 @@ type Thing = | Foo of msg : string override this.ToString() : string = match this with - | Foo(ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) -> - "" + | Foo( + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) -> "" """ [] diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 6bc22d831e..c0d4427d39 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2524,19 +2524,32 @@ let genPat (p: Pattern) = genSingleTextNode node.Ident +> sepSpace +> genSingleTextNode node.Equals - +> sepSpace - +> genPat node.Pattern + +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genPat node.Pattern) + |> genNode node let pats = - expressionFitsOnRestOfLine - (atCurrentColumn (col sepSemi node.Pairs genPatWithIdent)) - (atCurrentColumn (col sepNln node.Pairs genPatWithIdent)) + match node.Pairs with + | [ singlePair ] -> + expressionFitsOnRestOfLine + (genPatWithIdent singlePair) + (genSingleTextNode singlePair.Ident + +> sepSpace + +> genSingleTextNode singlePair.Equals + +> sepNln + +> genPat singlePair.Pattern) + | _ -> + expressionFitsOnRestOfLine + (col sepSemi node.Pairs genPatWithIdent) + (col sepNln node.Pairs genPatWithIdent) + |> autoNlnIfExpressionExceedsPageWidth genIdentListNode node.Identifier +> optSingle genTyparDecls node.TyparDecls +> addSpaceBeforeParenInPattern node.Identifier +> genSingleTextNode node.OpeningParen - +> autoIndentAndNlnIfExpressionExceedsPageWidth (sepNlnWhenWriteBeforeNewlineNotEmpty +> pats) + +> leadingExpressionIsMultiline + (indent +> sepNlnWhenWriteBeforeNewlineNotEmpty +> pats +> unindent) + (fun isMultiline -> onlyIf isMultiline sepNln) +> genSingleTextNode node.ClosingParen |> genNode node @@ -2549,8 +2562,21 @@ let genPat (p: Pattern) = let genParameters = match node.Parameters with | [] -> sepNone - | [ Pattern.Paren _ | Pattern.Unit _ as parameter ] -> - addSpaceBeforeParenInPattern node.Identifier +> genPat parameter + | [ Pattern.Unit _ as parameter ] -> addSpaceBeforeParenInPattern node.Identifier +> genPat parameter + | [ Pattern.Paren parenNode as parameter ] -> + + let short = genPat parameter + + let long = + genSingleTextNode parenNode.OpeningParen + +> indentSepNlnUnindent (genPat parenNode.Pattern) + +> sepNln + +> genSingleTextNode parenNode.ClosingParen + |> genNode parenNode + + addSpaceBeforeParenInPattern node.Identifier + +> expressionFitsOnRestOfLine short long + | ps -> sepSpace +> atCurrentColumn (col sepSpace ps genPat) genName +> genParameters |> genNode node @@ -2633,7 +2659,10 @@ let genPatInClause (pat: Pattern) = | Choice1Of2 barNode -> genSingleTextNode barNode +> sepSpace | Choice2Of2 _ -> sepBar - genPatMultiline p.LeftHandSide +> sepNln +> genBar +> genPat p.RightHandSide + genPatMultiline p.LeftHandSide + +> sepNln + +> genBar + +> atCurrentColumn (genPat p.RightHandSide) | Pattern.As p -> let genAs = From 634528589c1a7b07143525f5903c0369b344bcb9 Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 7 Dec 2022 20:20:18 -0800 Subject: [PATCH 2/8] Add additional tests for other pattern combinations. --- src/Fantomas.Core.Tests/MultilinePatterns.fs | 230 +++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 30 ++- 2 files changed, 251 insertions(+), 9 deletions(-) diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index 68630f1a2c..cd2ccf6844 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -386,3 +386,233 @@ match parseResults with ) ) -> () """ + +[] +let ``as pattern`` () = + formatSourceString + false + """ +match x with +| ABC(Y( + itemOne = OhSomeActivePatternThing(a, + b) as foo)) -> + () +""" + { config with MaxLineLength = 30 } + |> prepend newline + |> should + equal + """ +match x with +| ABC( + Y(itemOne = + OhSomeActivePatternThing( + a, b + ) as foo + ) + ) -> () +""" + +[] +let ``or pattern`` () = + formatSourceString + false + """ +match x with +| ABC( Y(itemOne = OhSomeActivePatternThing(a,b) | S(one, two, three, four))) -> () +""" + { config with MaxLineLength = 30 } + |> prepend newline + |> should + equal + """ +match x with +| ABC( + Y(itemOne = + OhSomeActivePatternThing( + a, b + ) | S( + one, + two, + three, + four + ) + ) + ) -> () +""" + +[] +let ``cons pattern`` () = + formatSourceString + false + """ +match x with +| ABC( Y(itemOne = OhSomeActivePatternThing(a,b) :: S(one, two, three, four))) -> () +""" + { config with MaxLineLength = 30 } + |> prepend newline + |> should + equal + """ +match x with +| ABC( + Y(itemOne = + OhSomeActivePatternThing( + a, b + ) :: S( + one, + two, + three, + four + ) + ) + ) -> () +""" + +[] +let ``array pattern`` () = + formatSourceString + false + """ +match x with +| [| Y( + itemOne = OhSomeActivePatternThing(a, + b)) + S(one, + two, + three, + four, + five) |] -> () +""" + { config with MaxLineLength = 30 } + |> prepend newline + |> should + equal + """ +match x with +| [| Y(itemOne = + OhSomeActivePatternThing( + a, b + ) + ) + S( + one, + two, + three, + four, + five + ) |] -> () +""" + +[] +let ``array pattern, alt`` () = + formatSourceString + false + """ +match x with +| [| Y( + itemOne = OhSomeActivePatternThing(a, + b)) + S(one, + two, + three, + four, + five) |] -> () +""" + { config with + MaxLineLength = 30 + MultilineBlockBracketsOnSameColumn = true } + |> prepend newline + |> should + equal + """ +match x with +| [| + Y(itemOne = + OhSomeActivePatternThing( + a, b + ) + ) + S( + one, + two, + three, + four, + five + ) + |] -> () +""" + +[] +let ``record pattern`` () = + formatSourceString + false + """ +match x with +| { Y = Y( + itemOne = OhSomeActivePatternThing(a, + b)) + V = S(one, + two, + three, + four, + five) } -> () +""" + { config with MaxLineLength = 30 } + |> prepend newline + |> should + equal + """ +match x with +| { Y = Y(itemOne = + OhSomeActivePatternThing( + a, b + ) + ) + V = S( + one, + two, + three, + four, + five + ) } -> () +""" + +[] +let ``record pattern, alt`` () = + formatSourceString + false + """ +match x with +| { Y = Y( + itemOne = OhSomeActivePatternThing(a, + b)) + V = S(one, + two, + three, + four, + five) } -> () +""" + { config with + MaxLineLength = 30 + MultilineBlockBracketsOnSameColumn = true } + |> prepend newline + |> should + equal + """ +match x with +| { + Y = Y(itemOne = + OhSomeActivePatternThing( + a, b + ) + ) + V = S( + one, + two, + three, + four, + five + ) + } -> () +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index c0d4427d39..b26abbb603 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2606,15 +2606,27 @@ let genPat (p: Pattern) = expressionFitsOnRestOfLine short long - ifElse - node.Patterns.IsEmpty - (genSingleTextNode node.OpenToken +> genSingleTextNode node.CloseToken) - (genSingleTextNode node.OpenToken - +> addSpaceIfSpaceAroundDelimiter - +> atCurrentColumn genPats - +> addSpaceIfSpaceAroundDelimiter - +> genSingleTextNode node.CloseToken) - |> genNode node + let emptyPattern = + genSingleTextNode node.OpenToken +> genSingleTextNode node.CloseToken + + let small = + genSingleTextNode node.OpenToken + +> addSpaceIfSpaceAroundDelimiter + +> atCurrentColumn genPats + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.CloseToken + + let multilineAlignBrackets = + genSingleTextNode node.OpenToken + +> indentSepNlnUnindent genPats + +> sepNln + +> genSingleTextNode node.CloseToken + + let nonEmpty ctx = + let size = getRecordSize ctx node.Patterns + isSmallExpression size small (ifAlignBrackets multilineAlignBrackets small) ctx + + ifElse node.Patterns.IsEmpty emptyPattern nonEmpty |> genNode node | Pattern.Record node -> let smallRecordExpr = genSingleTextNode node.OpeningNode From 4dfd6f8524d821ea22de632c703ba28aee12b204 Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 17 Dec 2022 19:38:37 +0100 Subject: [PATCH 3/8] Use max_line_length for array and record patterns. --- src/Fantomas.Core.Tests/MultilinePatterns.fs | 47 ++++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 9 ++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index cd2ccf6844..008b891006 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -4,6 +4,10 @@ open NUnit.Framework open FsUnit open Fantomas.Core.Tests.TestHelper +let tap s = + printfn "%s" s + s + [] let ``long ident tuple pattern`` () = formatSourceString @@ -616,3 +620,46 @@ match x with ) } -> () """ + +[] +let ``keep array on one line`` () = + formatSourceString + false + """ +match ast with +| ParsedInput.ImplFile(ParsedImplFileInput( + contents = [ SynModuleOrNamespace.SynModuleOrNamespace( + decls = [ SynModuleDecl.Types([ SynTypeDefn.SynTypeDefn( + typeRepr = SynTypeDefnRepr.Simple( + simpleRepr = SynTypeDefnSimpleRepr.Enum( + cases = [ SynEnumCase.SynEnumCase( + trivia = { BarRange = None + EqualsRange = mEquals }) ]))) ], + _) ]) ])) -> assertRange (2, 15) (2, 16) mEquals +| _ -> Assert.Fail "Could not get valid AST" +""" + config + |> tap + |> prepend newline + |> should + equal + """ +match ast with +| ParsedInput.ImplFile( + ParsedImplFileInput(contents = + [ SynModuleOrNamespace.SynModuleOrNamespace(decls = + [ SynModuleDecl.Types( + [ SynTypeDefn.SynTypeDefn(typeRepr = + SynTypeDefnRepr.Simple(simpleRepr = + SynTypeDefnSimpleRepr.Enum(cases = + [ SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) ] + ) + ) + ) ], + _ + ) ] + ) ] + ) + ) -> assertRange (2, 15) (2, 16) mEquals +| _ -> Assert.Fail "Could not get valid AST" +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index b26abbb603..741579987d 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2622,9 +2622,8 @@ let genPat (p: Pattern) = +> sepNln +> genSingleTextNode node.CloseToken - let nonEmpty ctx = - let size = getRecordSize ctx node.Patterns - isSmallExpression size small (ifAlignBrackets multilineAlignBrackets small) ctx + let nonEmpty = + expressionFitsOnRestOfLine small (ifAlignBrackets multilineAlignBrackets small) ifElse node.Patterns.IsEmpty emptyPattern nonEmpty |> genNode node | Pattern.Record node -> @@ -2655,9 +2654,7 @@ let genPat (p: Pattern) = let multilineExpressionIfAlignBrackets = ifAlignOrStroustrupBrackets multilineRecordExprAlignBrackets multilineRecordExpr - fun ctx -> - let size = getRecordSize ctx node.Fields - genNode node (isSmallExpression size smallRecordExpr multilineExpressionIfAlignBrackets) ctx + genNode node (expressionFitsOnRestOfLine smallRecordExpr multilineExpressionIfAlignBrackets) | Pattern.Const c -> genConstant c | Pattern.IsInst node -> genSingleTextNode node.Token +> sepSpace +> genType node.Type |> genNode node | Pattern.QuoteExpr node -> genQuoteExpr node From a0738b55b2c5c73ddfaaa13e306cbde1c47cd1c6 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 19 Dec 2022 11:59:07 +0100 Subject: [PATCH 4/8] Use some Stroustrup for patterns where the child pattern is array/list/record inside an application. --- .../AlignedMultilineBracketStyleTests.fs | 2 +- .../CrampedMultilineBracketStyleTests.fs | 16 +- src/Fantomas.Core.Tests/LambdaTests.fs | 2 +- src/Fantomas.Core.Tests/MultilinePatterns.fs | 273 +++++++++++++----- src/Fantomas.Core/CodePrinter.fs | 80 +++-- 5 files changed, 268 insertions(+), 105 deletions(-) diff --git a/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs b/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs index 824d394b8e..c3adb97095 100644 --- a/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs +++ b/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs @@ -1009,7 +1009,7 @@ let ``record destructuring in let binding`` () = printfn "Last Name: %s" ln printfn "Age: %i" age """ - config + { config with MaxLineLength = 50 } |> prepend newline |> should equal diff --git a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs index 25beb3a28c..2cacf9bb17 100644 --- a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs +++ b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs @@ -1453,7 +1453,7 @@ let internal sepSemi (ctx: Context) = | true, true -> str " ; " <| ctx """ - config + { config with MaxLineLength = 80 } |> prepend newline |> should equal @@ -1578,7 +1578,7 @@ match entities with Type = Elephant } ] -> () | _ -> () """ - config + { config with MaxLineLength = 40 } |> prepend newline |> should equal @@ -1607,7 +1607,7 @@ match entities with Type = Elephant } ] -> () | _ -> () """ - config + { config with MaxLineLength = 40 } |> prepend newline |> should equal @@ -1636,7 +1636,7 @@ match entities with Type = Elephant } |] -> () | _ -> () """ - config + { config with MaxLineLength = 40 } |> prepend newline |> should equal @@ -1644,10 +1644,10 @@ match entities with match entities with | [| { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d1" Type = Elephant } - { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d2" - Type = Elephant } - { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d3" - Type = Elephant } |] -> () + { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d2" + Type = Elephant } + { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d3" + Type = Elephant } |] -> () | _ -> () """ diff --git a/src/Fantomas.Core.Tests/LambdaTests.fs b/src/Fantomas.Core.Tests/LambdaTests.fs index 720f7d5cee..9f500d0b02 100644 --- a/src/Fantomas.Core.Tests/LambdaTests.fs +++ b/src/Fantomas.Core.Tests/LambdaTests.fs @@ -1108,7 +1108,7 @@ let g = revisionNumber = r processName = pn } -> p, r, pn) """ - config + { config with MaxLineLength = 60 } |> prepend newline |> should equal diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index 008b891006..42e3dcb276 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -4,9 +4,10 @@ open NUnit.Framework open FsUnit open Fantomas.Core.Tests.TestHelper -let tap s = - printfn "%s" s - s +let formatSourceString isFsi input config = + let formatted = formatSourceString isFsi input config + printfn "%s" formatted + formatted [] let ``long ident tuple pattern`` () = @@ -69,11 +70,11 @@ match a with ) -> do () 1 -| Z( - [| 1 - 2 // comment - 3 |] - ) -> +| Z([| + 1 + 2 // comment + 3 + |]) -> do () 2 """ @@ -106,11 +107,11 @@ match a with // other comment z ) -| Z( - [| 1 - 2 // comment - 3 |] - ) -> +| Z([| + 1 + 2 // comment + 3 + |]) -> do () 2 """ @@ -310,25 +311,23 @@ match parseResults with """ config |> prepend newline - |> fun output -> - printfn $"%s{output}" - output |> should equal """ match parseResults with | ParsedInput.ImplFile( - ParsedImplFileInput(contents = - [ SynModuleOrNamespace.SynModuleOrNamespace(decls = - [ SynModuleDecl.Types(typeDefns = - [ SynTypeDefn(typeRepr = - SynTypeDefnRepr.ObjectModel(members = - [ _; SynMemberDefn.Member(memberDefn = SynBinding(trivia = { EqualsRange = Some mEquals })) ] - ) - ) ] - ) ] - ) ] - ) + ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types(typeDefns = [ + SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = [ + _ + SynMemberDefn.Member(memberDefn = SynBinding(trivia = { EqualsRange = Some mEquals })) + ]) + ) + ]) + ]) + ]) ) -> assertRange (3, 18) (3, 19) mEquals | _ -> Assert.Fail "Could not get valid AST" """ @@ -360,34 +359,31 @@ match parseResults with """ config |> prepend newline - |> fun output -> - printfn $"%s{output}" - output |> should equal """ match parseResults with | ParsedInput.ImplFile( - ParsedImplFileInput(modules = - [ SynModuleOrNamespace.SynModuleOrNamespace(decls = - [ SynModuleDecl.Types(typeDefns = - [ SynTypeDefn(typeRepr = - SynTypeDefnRepr.ObjectModel(members = - [ SynMemberDefn.ImplicitCtor _ - SynMemberDefn.GetSetMember( - Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))), - Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))), - m, - { WithKeyword = mWith - GetKeyword = Some mGet - AndKeyword = Some mAnd - SetKeyword = Some mSet } - ) ] - ) - ) ] - ) ] - ) ] - ) + ParsedImplFileInput(modules = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types(typeDefns = [ + SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = [ + SynMemberDefn.ImplicitCtor _ + SynMemberDefn.GetSetMember( + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))), + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))), + m, + { WithKeyword = mWith + GetKeyword = Some mGet + AndKeyword = Some mAnd + SetKeyword = Some mSet } + ) + ]) + ) + ]) + ]) + ]) ) -> () """ @@ -495,17 +491,17 @@ match x with """ match x with | [| Y(itemOne = - OhSomeActivePatternThing( - a, b - ) - ) - S( - one, - two, - three, - four, - five - ) |] -> () + OhSomeActivePatternThing( + a, b + ) + ) + S( + one, + two, + three, + four, + five + ) |] -> () """ [] @@ -639,27 +635,158 @@ match ast with | _ -> Assert.Fail "Could not get valid AST" """ config - |> tap |> prepend newline |> should equal """ match ast with | ParsedInput.ImplFile( - ParsedImplFileInput(contents = - [ SynModuleOrNamespace.SynModuleOrNamespace(decls = - [ SynModuleDecl.Types( + ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types( [ SynTypeDefn.SynTypeDefn(typeRepr = - SynTypeDefnRepr.Simple(simpleRepr = - SynTypeDefnSimpleRepr.Enum(cases = - [ SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) ] + SynTypeDefnRepr.Simple(simpleRepr = + SynTypeDefnSimpleRepr.Enum(cases = [ + SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) + ]) ) - ) - ) ], + ) ], _ - ) ] - ) ] - ) + ) + ]) + ]) ) -> assertRange (2, 15) (2, 16) mEquals | _ -> Assert.Fail "Could not get valid AST" """ + +[] +let ``single named identifier with array could go stroustrup??`` () = + formatSourceString + false + """ +match parseResults with +| ModuleName.PatternName( + identifier = [ SomethingElse(a, + // comment + b, + c) ]) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" + +""" + config + |> prepend newline + |> should + equal + """ +match parseResults with +| ModuleName.PatternName(identifier = [ + SomethingElse( + a, + // comment + b, + c + ) + ]) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" + +[] +let ``single named identifier with records could go stroustrup??`` () = + formatSourceString + false + """ +match parseResults with +| ModuleName.PatternName( + identifier = { FieldName1 = 9 + SomethingElse = fooBar + LastField = "some string" + VeryLastFieldWithQuiteTheLongIdentifierName = "and also quite the lengthy string value" }) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" + config + |> prepend newline + |> should + equal + """ +match parseResults with +| ModuleName.PatternName(identifier = { + FieldName1 = 9 + SomethingElse = fooBar + LastField = "some string" + VeryLastFieldWithQuiteTheLongIdentifierName = "and also quite the lengthy string value" }) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" + +[] +let ``application pattern with single array could go stroustrup??`` () = + formatSourceString + false + """ +match parseResults with +| ModuleName.PatternName( + [ SomethingElse(a, + // comment + b, + c) ]) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" + +""" + config + |> prepend newline + |> should + equal + """ +match parseResults with +| ModuleName.PatternName([ + SomethingElse( + a, + // comment + b, + c + ) + ]) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" + +[] +let ``application pattern with single record could go stroustrup??`` () = + formatSourceString + false + """ +match parseResults with +| ModuleName.PatternName( + { FieldName1 = 9 + SomethingElse = fooBar + LastField = "some string" + VeryLastFieldWithQuiteTheLongIdentifierName = "and also quite the lengthy string value" }) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" + config + |> prepend newline + |> should + equal + """ +match parseResults with +| ModuleName.PatternName({ + FieldName1 = 9 + SomethingElse = fooBar + LastField = "some string" + VeryLastFieldWithQuiteTheLongIdentifierName = "and also quite the lengthy string value" }) -> + assertRange (2, 21) (2, 22) mSlash + assertRange (2, 21) (2, 29) mTuple +| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 741579987d..85122e3a3d 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2520,36 +2520,49 @@ let genPat (p: Pattern) = | Pattern.As node | Pattern.ListCons node -> genPatLeftMiddleRight node | Pattern.NamePatPairs node -> + let genName (node: NamePatPair) = + genSingleTextNode node.Ident +> sepSpace +> genSingleTextNode node.Equals + let genPatWithIdent (node: NamePatPair) = - genSingleTextNode node.Ident - +> sepSpace - +> genSingleTextNode node.Equals + genName node +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genPat node.Pattern) |> genNode node let pats = + let noContentAfterOpeningParen = (node.OpeningParen :> Node).HasContentAfter |> not + match node.Pairs with - | [ singlePair ] -> - expressionFitsOnRestOfLine - (genPatWithIdent singlePair) - (genSingleTextNode singlePair.Ident - +> sepSpace - +> genSingleTextNode singlePair.Equals - +> sepNln - +> genPat singlePair.Pattern) + | [ singlePair ] when noContentAfterOpeningParen -> + match singlePair.Pattern with + | Pattern.ArrayOrList arrayOrListNode -> + expressionFitsOnRestOfLine + (genPatWithIdent singlePair) + (genName singlePair + +> sepSpace + +> genMultilineArrayOrListPatternInApplication arrayOrListNode) + | Pattern.Record recordNode -> + expressionFitsOnRestOfLine + (genPatWithIdent singlePair) + (genName singlePair + +> sepSpace + +> genMultilineRecordPatternInApplication recordNode) + | _ -> + expressionFitsOnRestOfLine + (genPatWithIdent singlePair) + (genName singlePair +> indentSepNlnUnindent (genPat singlePair.Pattern) +> sepNln) | _ -> expressionFitsOnRestOfLine (col sepSemi node.Pairs genPatWithIdent) - (col sepNln node.Pairs genPatWithIdent) - |> autoNlnIfExpressionExceedsPageWidth + (indentSepNlnUnindent (col sepNln node.Pairs genPatWithIdent) +> sepNln) genIdentListNode node.Identifier +> optSingle genTyparDecls node.TyparDecls +> addSpaceBeforeParenInPattern node.Identifier +> genSingleTextNode node.OpeningParen - +> leadingExpressionIsMultiline - (indent +> sepNlnWhenWriteBeforeNewlineNotEmpty +> pats +> unindent) - (fun isMultiline -> onlyIf isMultiline sepNln) + +> pats + // +> leadingExpressionIsMultiline + // pats // (indent +> sepNlnWhenWriteBeforeNewlineNotEmpty +> pats +> unindent) + // (fun isMultiline -> onlyIf isMultiline sepNln) +> genSingleTextNode node.ClosingParen |> genNode node @@ -2568,9 +2581,15 @@ let genPat (p: Pattern) = let short = genPat parameter let long = + let body = + match parenNode.Pattern with + | Pattern.ArrayOrList arrayOrListNode -> + genMultilineArrayOrListPatternInApplication arrayOrListNode + | Pattern.Record recordNode -> genMultilineRecordPatternInApplication recordNode + | _ -> indentSepNlnUnindent (genPat parenNode.Pattern) +> sepNln + genSingleTextNode parenNode.OpeningParen - +> indentSepNlnUnindent (genPat parenNode.Pattern) - +> sepNln + +> body +> genSingleTextNode parenNode.ClosingParen |> genNode parenNode @@ -2602,20 +2621,23 @@ let genPat (p: Pattern) = | [ Pattern.Or _ ] -> let column = ctx.Column + 1 atIndentLevel false column (col sepNln node.Patterns genPatInClause) ctx - | _ -> col sepNln node.Patterns genPatInClause ctx + | _ -> col sepNln node.Patterns genPat ctx expressionFitsOnRestOfLine short long let emptyPattern = genSingleTextNode node.OpenToken +> genSingleTextNode node.CloseToken - let small = + let short = genSingleTextNode node.OpenToken +> addSpaceIfSpaceAroundDelimiter - +> atCurrentColumn genPats + +> indent + +> genPats + +> unindent +> addSpaceIfSpaceAroundDelimiter +> genSingleTextNode node.CloseToken + // Note that we deliberately are not take the setting fsharp_multiline_block_brackets_on_same_column into account. let multilineAlignBrackets = genSingleTextNode node.OpenToken +> indentSepNlnUnindent genPats @@ -2623,7 +2645,7 @@ let genPat (p: Pattern) = +> genSingleTextNode node.CloseToken let nonEmpty = - expressionFitsOnRestOfLine small (ifAlignBrackets multilineAlignBrackets small) + expressionFitsOnRestOfLine short (ifAlignBrackets multilineAlignBrackets short) ifElse node.Patterns.IsEmpty emptyPattern nonEmpty |> genNode node | Pattern.Record node -> @@ -2659,6 +2681,20 @@ let genPat (p: Pattern) = | Pattern.IsInst node -> genSingleTextNode node.Token +> sepSpace +> genType node.Type |> genNode node | Pattern.QuoteExpr node -> genQuoteExpr node +let genMultilineRecordPatternInApplication (node: PatRecordNode) = + genSingleTextNode node.OpeningNode + +> indentSepNlnUnindent (col sepNln node.Fields genPatRecordFieldName) + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.ClosingNode + |> genNode node + +let genMultilineArrayOrListPatternInApplication (node: PatArrayOrListNode) = + genSingleTextNode node.OpenToken + +> indentSepNlnUnindent (col sepNln node.Patterns genPat) + +> sepNln + +> genSingleTextNode node.CloseToken + |> genNode node + let genPatInClause (pat: Pattern) = let rec genPatMultiline p = match p with From dc4dc91f9f669d55335db1b51eccb0c8785d0d28 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 10 Feb 2023 10:29:58 +0100 Subject: [PATCH 5/8] Produce valid code for multiline array patterns. --- .../CrampedMultilineBracketStyleTests.fs | 5 ++-- src/Fantomas.Core.Tests/MultilinePatterns.fs | 13 ++++---- .../PatternMatchingTests.fs | 7 +++-- src/Fantomas.Core/CodePrinter.fs | 30 ++++++++++++++----- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs index 2cacf9bb17..7d27c5cb1f 100644 --- a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs +++ b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs @@ -1642,8 +1642,9 @@ match entities with equal """ match entities with -| [| { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d1" - Type = Elephant } +| [| + { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d1" + Type = Elephant } { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d2" Type = Elephant } { Key = "0031ff53-e59b-49e3-8e0f-53f72a3890d3" diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index 42e3dcb276..ac3cf224fb 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -2,13 +2,9 @@ open NUnit.Framework open FsUnit +open Fantomas.Core open Fantomas.Core.Tests.TestHelper -let formatSourceString isFsi input config = - let formatted = formatSourceString isFsi input config - printfn "%s" formatted - formatted - [] let ``long ident tuple pattern`` () = formatSourceString @@ -490,7 +486,8 @@ match x with equal """ match x with -| [| Y(itemOne = +| [| + Y(itemOne = OhSomeActivePatternThing( a, b ) @@ -521,7 +518,7 @@ match x with """ { config with MaxLineLength = 30 - MultilineBlockBracketsOnSameColumn = true } + MultilineBracketStyle = Aligned } |> prepend newline |> should equal @@ -595,7 +592,7 @@ match x with """ { config with MaxLineLength = 30 - MultilineBlockBracketsOnSameColumn = true } + MultilineBracketStyle = Aligned } |> prepend newline |> should equal diff --git a/src/Fantomas.Core.Tests/PatternMatchingTests.fs b/src/Fantomas.Core.Tests/PatternMatchingTests.fs index 07873420f2..e4f7e0fdf3 100644 --- a/src/Fantomas.Core.Tests/PatternMatchingTests.fs +++ b/src/Fantomas.Core.Tests/PatternMatchingTests.fs @@ -1530,9 +1530,10 @@ let args = """ let args = match args with - | [| LongPatIndentifierOne - | LongPatIndentifierTwo - | LongPatIndentifierThree |] -> args + | [| + LongPatIndentifierOne + | LongPatIndentifierTwo + | LongPatIndentifierThree |] -> args | _ -> failwith "meh" """ diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 85122e3a3d..e50891d347 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2637,15 +2637,29 @@ let genPat (p: Pattern) = +> addSpaceIfSpaceAroundDelimiter +> genSingleTextNode node.CloseToken - // Note that we deliberately are not take the setting fsharp_multiline_block_brackets_on_same_column into account. - let multilineAlignBrackets = - genSingleTextNode node.OpenToken - +> indentSepNlnUnindent genPats - +> sepNln - +> genSingleTextNode node.CloseToken + let multiline = + let cramped = + if node.OpenToken.Text.Length = 1 then + short + else + // pattern is an array, which can lead to some offset problems. + // It is better to play it safe and indent the patterns on the next line. + genSingleTextNode node.OpenToken + +> addSpaceIfSpaceAroundDelimiter + +> indentSepNlnUnindent genPats + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.CloseToken + + // Note that we deliberately are not take the setting fsharp_multiline_block_brackets_on_same_column into account. + let multilineAlignBrackets = + genSingleTextNode node.OpenToken + +> indentSepNlnUnindent genPats + +> sepNln + +> genSingleTextNode node.CloseToken + + ifAlignOrStroustrupBrackets multilineAlignBrackets cramped - let nonEmpty = - expressionFitsOnRestOfLine short (ifAlignBrackets multilineAlignBrackets short) + let nonEmpty = expressionFitsOnRestOfLine short multiline ifElse node.Patterns.IsEmpty emptyPattern nonEmpty |> genNode node | Pattern.Record node -> From 4b3e064ada336a93ae9f99aea21c6bf2c98eda56 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 30 Jun 2023 15:13:15 +0200 Subject: [PATCH 6/8] Start SingleNamedPatPair on same line in Pattern.LongIdent with Pattern.Paren. --- src/Fantomas.Core.Tests/MultilinePatterns.fs | 159 ++++++++++--------- src/Fantomas.Core/CodePrinter.fs | 9 ++ 2 files changed, 91 insertions(+), 77 deletions(-) diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index ac3cf224fb..7597f0e219 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -3,7 +3,7 @@ open NUnit.Framework open FsUnit open Fantomas.Core -open Fantomas.Core.Tests.TestHelper +open Fantomas.Core.Tests.TestHelpers [] let ``long ident tuple pattern`` () = @@ -311,20 +311,18 @@ match parseResults with equal """ match parseResults with -| ParsedInput.ImplFile( - ParsedImplFileInput(contents = [ - SynModuleOrNamespace.SynModuleOrNamespace(decls = [ - SynModuleDecl.Types(typeDefns = [ - SynTypeDefn(typeRepr = - SynTypeDefnRepr.ObjectModel(members = [ - _ - SynMemberDefn.Member(memberDefn = SynBinding(trivia = { EqualsRange = Some mEquals })) - ]) - ) - ]) +| ParsedInput.ImplFile(ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types(typeDefns = [ + SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = [ + _ + SynMemberDefn.Member(memberDefn = SynBinding(trivia = { EqualsRange = Some mEquals })) + ]) + ) ]) ]) - ) -> assertRange (3, 18) (3, 19) mEquals + ])) -> assertRange (3, 18) (3, 19) mEquals | _ -> Assert.Fail "Could not get valid AST" """ @@ -359,28 +357,23 @@ match parseResults with equal """ match parseResults with -| ParsedInput.ImplFile( - ParsedImplFileInput(modules = [ - SynModuleOrNamespace.SynModuleOrNamespace(decls = [ - SynModuleDecl.Types(typeDefns = [ - SynTypeDefn(typeRepr = - SynTypeDefnRepr.ObjectModel(members = [ - SynMemberDefn.ImplicitCtor _ - SynMemberDefn.GetSetMember( - Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))), - Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))), - m, - { WithKeyword = mWith - GetKeyword = Some mGet - AndKeyword = Some mAnd - SetKeyword = Some mSet } - ) - ]) - ) - ]) +| ParsedInput.ImplFile(ParsedImplFileInput(modules = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types(typeDefns = [ + SynTypeDefn(typeRepr = + SynTypeDefnRepr.ObjectModel(members = [ + SynMemberDefn.ImplicitCtor _ + SynMemberDefn.GetSetMember( + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))), + Some(SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))), + m, + { WithKeyword = mWith; GetKeyword = Some mGet; AndKeyword = Some mAnd; SetKeyword = Some mSet } + ) + ]) + ) ]) ]) - ) -> () + ])) -> () """ [] @@ -400,13 +393,11 @@ match x with equal """ match x with -| ABC( - Y(itemOne = - OhSomeActivePatternThing( - a, b - ) as foo - ) - ) -> () +| ABC(Y(itemOne = + OhSomeActivePatternThing( + a, b + ) as foo + )) -> () """ [] @@ -423,18 +414,13 @@ match x with equal """ match x with -| ABC( - Y(itemOne = - OhSomeActivePatternThing( - a, b - ) | S( - one, - two, - three, - four - ) +| ABC(Y(itemOne = + OhSomeActivePatternThing( + a, b + ) | S( + one, two, three, four ) - ) -> () + )) -> () """ [] @@ -451,18 +437,13 @@ match x with equal """ match x with -| ABC( - Y(itemOne = - OhSomeActivePatternThing( - a, b - ) :: S( - one, - two, - three, - four - ) +| ABC(Y(itemOne = + OhSomeActivePatternThing( + a, b + ) :: S( + one, two, three, four ) - ) -> () + )) -> () """ [] @@ -637,22 +618,20 @@ match ast with equal """ match ast with -| ParsedInput.ImplFile( - ParsedImplFileInput(contents = [ - SynModuleOrNamespace.SynModuleOrNamespace(decls = [ - SynModuleDecl.Types( - [ SynTypeDefn.SynTypeDefn(typeRepr = - SynTypeDefnRepr.Simple(simpleRepr = - SynTypeDefnSimpleRepr.Enum(cases = [ - SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) - ]) - ) - ) ], - _ - ) - ]) +| ParsedInput.ImplFile(ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Types( + [ SynTypeDefn.SynTypeDefn(typeRepr = + SynTypeDefnRepr.Simple(simpleRepr = + SynTypeDefnSimpleRepr.Enum(cases = [ + SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) + ]) + ) + ) ], + _ + ) ]) - ) -> assertRange (2, 15) (2, 16) mEquals + ])) -> assertRange (2, 15) (2, 16) mEquals | _ -> Assert.Fail "Could not get valid AST" """ @@ -787,3 +766,29 @@ match parseResults with assertRange (2, 21) (2, 29) mTuple | _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" """ + +[] +let ``single named pattern pair should start inside parent pat long ident parentheses`` () = + formatSourceString + false + """ +match ast with +| ParsedInput.ImplFile (ParsedImplFileInput(contents = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = + [ SynModuleDecl.Attributes(attributes = [ { Attributes = [ { Range = mAttribute } ] } ]) ; SynModuleDecl.Expr _ ] ) ])) -> + assertRange (2, 2) (2, 25) mAttribute +| _ -> Assert.Fail $"Could not get valid AST, got {ast}" +""" + config + |> prepend newline + |> should + equal + """ +match ast with +| ParsedInput.ImplFile(ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + SynModuleDecl.Attributes(attributes = [ { Attributes = [ { Range = mAttribute } ] } ]) + SynModuleDecl.Expr _ + ]) + ])) -> assertRange (2, 2) (2, 25) mAttribute +| _ -> Assert.Fail $"Could not get valid AST, got {ast}" +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index e50891d347..80711fdaaf 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2492,6 +2492,14 @@ let genTuplePat (node: PatTupleNode) = atCurrentColumn (expressionFitsOnRestOfLine short (atCurrentColumn (genTuplePatLong node))) |> genNode node +let (|SingleNamedPatPair|_|) (p: Pattern) = + match p with + | Pattern.NamePatPairs namePatPairsNode -> + match namePatPairsNode.Pairs with + | [ _singlePat ] -> Some() + | _ -> None + | _ -> None + let genPat (p: Pattern) = match p with | Pattern.OptionalVal n -> genSingleTextNode n @@ -2586,6 +2594,7 @@ let genPat (p: Pattern) = | Pattern.ArrayOrList arrayOrListNode -> genMultilineArrayOrListPatternInApplication arrayOrListNode | Pattern.Record recordNode -> genMultilineRecordPatternInApplication recordNode + | SingleNamedPatPair _ as pat -> genPat pat | _ -> indentSepNlnUnindent (genPat parenNode.Pattern) +> sepNln genSingleTextNode parenNode.OpeningParen From b68cc1938fdfa9933c16293596fbb40f60f836d6 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 30 Jun 2023 17:05:37 +0200 Subject: [PATCH 7/8] Format applications with a single named argument different. --- .../Fantomas.Core.Tests.fsproj | 1 + .../MultilineNamedExpressions.fs | 72 +++++++ ...foreMultilineComputationExpressionTests.fs | 10 +- src/Fantomas.Core.Tests/StringTests.fs | 5 +- .../NamedArgumentExpressionTests.fs | 84 ++++---- .../Stroustrup/NamedParameterTests.fs | 73 +++---- src/Fantomas.Core/CodePrinter.fs | 201 +++++++++++++----- 7 files changed, 293 insertions(+), 153 deletions(-) create mode 100644 src/Fantomas.Core.Tests/MultilineNamedExpressions.fs diff --git a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj index 90ca88d1dc..d09032a391 100644 --- a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj +++ b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj @@ -129,6 +129,7 @@ + diff --git a/src/Fantomas.Core.Tests/MultilineNamedExpressions.fs b/src/Fantomas.Core.Tests/MultilineNamedExpressions.fs new file mode 100644 index 0000000000..2603878f9d --- /dev/null +++ b/src/Fantomas.Core.Tests/MultilineNamedExpressions.fs @@ -0,0 +1,72 @@ +module Fantomas.Core.Tests.MultilineNamedExpressions + +open NUnit.Framework +open FsUnit +open Fantomas.Core +open Fantomas.Core.Tests.TestHelpers + +[] +let ``single named item expression`` () = + formatSourceString + false + """ +let x = + ParsedInput.ImplFile( + ParsedImplFileInput( + contents = + [ SynModuleOrNamespace.SynModuleOrNamespace( + decls = + [ a + // + b ] + ) ] + ) + ) +""" + { config with MaxLineLength = 80 } + |> prepend newline + |> should + equal + """ +let x = + ParsedInput.ImplFile( + ParsedImplFileInput(contents = [ + SynModuleOrNamespace.SynModuleOrNamespace(decls = [ + a + // + b + ]) + ]) + ) +""" + +// TODO: there is currently no check whether `(a =` fits on the remainder of the line. + +[] +let ``multiline long identifier in named argument function call`` () = + formatSourceString + false + """ +XXXXXXXXXXXXXXXX + // Some comment + .YYYYYYYYYYYYYY + .ZZZZZZZZZZZZZZZ( + a = + try b () with ex -> c +) +""" + config + |> prepend newline + |> should + equal + """ +XXXXXXXXXXXXXXXX + // Some comment + .YYYYYYYYYYYYYY + .ZZZZZZZZZZZZZZZ(a = + try + b () + with ex -> + c + ) +""" diff --git a/src/Fantomas.Core.Tests/NewlineBeforeMultilineComputationExpressionTests.fs b/src/Fantomas.Core.Tests/NewlineBeforeMultilineComputationExpressionTests.fs index 6c0b6a71fd..2d241028cb 100644 --- a/src/Fantomas.Core.Tests/NewlineBeforeMultilineComputationExpressionTests.fs +++ b/src/Fantomas.Core.Tests/NewlineBeforeMultilineComputationExpressionTests.fs @@ -327,12 +327,10 @@ let v = equal """ let v = - SomeConstructor( - v = task { - // some computation here - () - } - ) + SomeConstructor(v = task { + // some computation here + () + }) """ [] diff --git a/src/Fantomas.Core.Tests/StringTests.fs b/src/Fantomas.Core.Tests/StringTests.fs index fdb0e38f42..15ab1bbb91 100644 --- a/src/Fantomas.Core.Tests/StringTests.fs +++ b/src/Fantomas.Core.Tests/StringTests.fs @@ -193,9 +193,8 @@ let main argv = let main argv = use fun1 = R.eval ( - R.parse ( - text = - \"\"\" + R.parse (text = + \"\"\" function(i) { x <- rnorm(1000) y <- rnorm(1000) diff --git a/src/Fantomas.Core.Tests/Stroustrup/NamedArgumentExpressionTests.fs b/src/Fantomas.Core.Tests/Stroustrup/NamedArgumentExpressionTests.fs index f7fb860d3f..51b91e380e 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/NamedArgumentExpressionTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/NamedArgumentExpressionTests.fs @@ -29,13 +29,11 @@ let v = equal """ let v = - SomeConstructor( - v = { - A = longTypeName - B = someOtherVariable - C = ziggyBarX - } - ) + SomeConstructor(v = { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + }) """ [] @@ -58,15 +56,13 @@ let v = equal """ let v = - SomeConstructor( - v = { - astContext with - IsInsideMatchClausePattern = true - A = longTypeName - B = someOtherVariable - C = ziggyBarX - } - ) + SomeConstructor(v = { + astContext with + IsInsideMatchClausePattern = true + A = longTypeName + B = someOtherVariable + C = ziggyBarX + }) """ [] @@ -88,13 +84,11 @@ let v = equal """ let v = - SomeConstructor( - v = {| - A = longTypeName - B = someOtherVariable - C = ziggyBarX - |} - ) + SomeConstructor(v = {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) """ [] @@ -116,13 +110,11 @@ let v = equal """ let v = - SomeConstructor( - v = struct {| - A = longTypeName - B = someOtherVariable - C = ziggyBarX - |} - ) + SomeConstructor(v = struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |}) """ [] @@ -146,15 +138,13 @@ let v = equal """ let v = - SomeConstructor( - v = [ - itemOne - itemTwo - itemThree - itemFour - itemFive - ] - ) + SomeConstructor(v = [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ]) """ [] @@ -178,15 +168,13 @@ let v = equal """ let v = - SomeConstructor( - v = [| - itemOne - itemTwo - itemThree - itemFour - itemFive - |] - ) + SomeConstructor(v = [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |]) """ [] diff --git a/src/Fantomas.Core.Tests/Stroustrup/NamedParameterTests.fs b/src/Fantomas.Core.Tests/Stroustrup/NamedParameterTests.fs index 50b4a97e75..443decdfe2 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/NamedParameterTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/NamedParameterTests.fs @@ -110,46 +110,41 @@ let loginPage = View.ContentPage( title = "Fabulous Demo", content = - View.ScrollView( - content = - View.StackLayout( - padding = 30.0, - children = [ - View.Frame( - verticalOptions = LayoutOptions.CenterAndExpand, - content = - View.StackLayout( - children = [ - View.Entry( - placeholder = "User name", - isEnabled = (not model.IsSigningIn), - textChanged = - (fun args -> (dispatch (UserNameChanged args.NewTextValue))) - ) - View.Entry( - placeholder = "Password", - isPassword = true, - isEnabled = (not model.IsSigningIn), - textChanged = - (fun args -> (dispatch (PasswordChanged args.NewTextValue))) - ) - View.Button( - text = "Sign in", - heightRequest = 30.0, - isVisible = (not model.IsSigningIn), - command = (fun () -> dispatch SignIn), - canExecute = model.IsCredentialsProvided - ) - View.ActivityIndicator( - isRunning = true, - heightRequest = 30.0, - isVisible = model.IsSigningIn - ) - ] + View.ScrollView(content = + View.StackLayout( + padding = 30.0, + children = [ + View.Frame( + verticalOptions = LayoutOptions.CenterAndExpand, + content = + View.StackLayout(children = [ + View.Entry( + placeholder = "User name", + isEnabled = (not model.IsSigningIn), + textChanged = (fun args -> (dispatch (UserNameChanged args.NewTextValue))) + ) + View.Entry( + placeholder = "Password", + isPassword = true, + isEnabled = (not model.IsSigningIn), + textChanged = (fun args -> (dispatch (PasswordChanged args.NewTextValue))) ) - ) - ] - ) + View.Button( + text = "Sign in", + heightRequest = 30.0, + isVisible = (not model.IsSigningIn), + command = (fun () -> dispatch SignIn), + canExecute = model.IsCredentialsProvided + ) + View.ActivityIndicator( + isRunning = true, + heightRequest = 30.0, + isVisible = model.IsSigningIn + ) + ]) + ) + ] + ) ) ) """ diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 80711fdaaf..85750213db 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -321,6 +321,17 @@ let isLambdaOrIfThenElse (e: Expr) = let (|IsLambdaOrIfThenElse|_|) (e: Expr) = if isLambdaOrIfThenElse e then Some e else None +let (|ParenArgWithSingleNamedExpr|_|) (parenExpr: Expr) = + match parenExpr with + | Expr.Paren parenNode -> + match parenNode.Expr with + | Expr.InfixApp infixApp when infixApp.Operator.Text = "=" -> + match infixApp.LeftHandSide with + | Expr.Ident nameIdent -> Some(parenNode, nameIdent, infixApp) + | _ -> None + | _ -> None + | _ -> None + let genExpr (e: Expr) = match e with | Expr.Lazy node -> @@ -996,52 +1007,76 @@ let genExpr (e: Expr) = node.ArgExpr let shortLids = genIdentListNode node.FunctionName - let short = shortLids +> addSpace +> genExpr node.ArgExpr - let long = - let args = + match node.ArgExpr with + // x.y.fn (a = expr) + | ParenArgWithSingleNamedExpr(parenNode, nameIdent, infixApp) -> + genParenArgWithSingleNamedExpr + (expressionFitsOnRestOfLine shortLids (genFunctionNameWithMultilineLids id node.FunctionName)) addSpace - +> expressionFitsOnRestOfLine - (genExpr node.ArgExpr) - (genMultilineFunctionApplicationArguments node.ArgExpr) + parenNode + nameIdent + infixApp + node + | _ -> + let short = shortLids +> addSpace +> genExpr node.ArgExpr - ifElseCtx - (futureNlnCheck shortLids) - (genFunctionNameWithMultilineLids args node.FunctionName) - (shortLids +> args) + let long = + let args = + addSpace + +> expressionFitsOnRestOfLine + (genExpr node.ArgExpr) + (genMultilineFunctionApplicationArguments node.ArgExpr) + + ifElseCtx + (futureNlnCheck shortLids) + (genFunctionNameWithMultilineLids args node.FunctionName) + (shortLids +> args) + + expressionFitsOnRestOfLine short long |> genNode node - expressionFitsOnRestOfLine short long |> genNode node // fn (a, b, c) | Expr.AppSingleParenArg node -> - let short = - genExpr node.FunctionExpr - +> sepSpaceBeforeParenInFuncInvocation node.FunctionExpr node.ArgExpr - +> genExpr node.ArgExpr - - let long ctx = - let genDefaultLong = + match node.ArgExpr with + // fn (a = expr) + | ParenArgWithSingleNamedExpr(parenNode, nameIdent, infixApp) -> + genParenArgWithSingleNamedExpr + (genExpr node.FunctionExpr) + (sepSpaceBeforeParenInFuncInvocation node.FunctionExpr node.ArgExpr) + parenNode + nameIdent + infixApp + node + | _ -> + let short = genExpr node.FunctionExpr +> sepSpaceBeforeParenInFuncInvocation node.FunctionExpr node.ArgExpr - +> genMultilineFunctionApplicationArguments node.ArgExpr + +> genExpr node.ArgExpr - match node.ArgExpr with - | Expr.Paren parenNode when parenNode.HasContentBefore -> - // We make a copy of the parenthesis argument (without the trivia being copied). - // Then we check if that is was multiline or not. - let parenNode' = - mkExprParenNode parenNode.OpeningParen parenNode.Expr parenNode.ClosingParen parenNode.Range + let long ctx = + let genDefaultLong = + genExpr node.FunctionExpr + +> sepSpaceBeforeParenInFuncInvocation node.FunctionExpr node.ArgExpr + +> genMultilineFunctionApplicationArguments node.ArgExpr - let isSingleLineWithoutTriviaBefore = futureNlnCheck (genExpr parenNode') ctx + match node.ArgExpr with + | Expr.Paren parenNode when parenNode.HasContentBefore -> + // We make a copy of the parenthesis argument (without the trivia being copied). + // Then we check if that is was multiline or not. + let parenNode' = + mkExprParenNode parenNode.OpeningParen parenNode.Expr parenNode.ClosingParen parenNode.Range - if not isSingleLineWithoutTriviaBefore then - (genExpr node.FunctionExpr +> indentSepNlnUnindent (genExpr node.ArgExpr)) ctx - else - (genExpr node.FunctionExpr - +> indentSepNlnUnindent (genMultilineFunctionApplicationArguments node.ArgExpr)) - ctx - | _ -> genDefaultLong ctx + let isSingleLineWithoutTriviaBefore = futureNlnCheck (genExpr parenNode') ctx - expressionFitsOnRestOfLine short long |> genNode node + if not isSingleLineWithoutTriviaBefore then + (genExpr node.FunctionExpr +> indentSepNlnUnindent (genExpr node.ArgExpr)) ctx + else + (genExpr node.FunctionExpr + +> indentSepNlnUnindent (genMultilineFunctionApplicationArguments node.ArgExpr)) + ctx + | _ -> genDefaultLong ctx + + expressionFitsOnRestOfLine short long |> genNode node // functionName arg1 arg2 (fun x y z -> ...) | Expr.AppWithLambda node -> @@ -1647,6 +1682,28 @@ let genSmallRecordNode (node: ExprRecordNode) = | None -> sepNone) node +/// Caller needs to invoke `genNode` +let genMultilineRecordAlignBrackets (node: ExprRecordNode) (ctx: Context) = + let fieldsExpr = genMultilineRecordFieldsExpr genRecordFieldNameAligned node + + match node.CopyInfo with + | Some ci -> + let additionalIndent = ctx.Config.IndentSize < 3 + + (genSingleTextNodeSuffixDelimiter node.OpeningBrace + +> ifElseCtx (fun ctx -> ctx.Config.IsStroustrupStyle) (indent +> sepNln) sepNlnWhenWriteBeforeNewlineNotEmpty // comment after curly brace + +> genMultilineRecordCopyExpr additionalIndent fieldsExpr ci + +> onlyIfCtx (fun ctx -> ctx.Config.IsStroustrupStyle) unindent + +> sepNln + +> genSingleTextNode node.ClosingBrace) + ctx + | None -> + (genSingleTextNode node.OpeningBrace + +> indentSepNlnUnindent fieldsExpr + +> ifElseCtx lastWriteEventIsNewline sepNone sepNln + +> genSingleTextNode node.ClosingBrace) + ctx + /// /// Print a multiline ExprRecordNode. /// @@ -1668,28 +1725,6 @@ let genMultilineRecord (node: ExprRecordNode) (ctx: Context) = else openBraceLength) - let genMultilineAlignBrackets = - let fieldsExpr = genMultilineRecordFieldsExpr genRecordFieldNameAligned node - - match node.CopyInfo with - | Some ci -> - let additionalIndent = ctx.Config.IndentSize < 3 - - genSingleTextNodeSuffixDelimiter node.OpeningBrace - +> ifElseCtx - (fun ctx -> ctx.Config.IsStroustrupStyle) - (indent +> sepNln) - sepNlnWhenWriteBeforeNewlineNotEmpty // comment after curly brace - +> genMultilineRecordCopyExpr additionalIndent fieldsExpr ci - +> onlyIfCtx (fun ctx -> ctx.Config.IsStroustrupStyle) unindent - +> sepNln - +> genSingleTextNode node.ClosingBrace - | None -> - genSingleTextNode node.OpeningBrace - +> indentSepNlnUnindent fieldsExpr - +> ifElseCtx lastWriteEventIsNewline sepNone sepNln - +> genSingleTextNode node.ClosingBrace - let genMultilineCramped = let genFields = match node.CopyInfo with @@ -1738,7 +1773,7 @@ let genMultilineRecord (node: ExprRecordNode) (ctx: Context) = ifElseCtx lastWriteEventIsNewline brace (addSpaceIfSpaceAroundDelimiter +> brace) ctx) ) - ifAlignOrStroustrupBrackets genMultilineAlignBrackets genMultilineCramped ctx + ifAlignOrStroustrupBrackets (genMultilineRecordAlignBrackets node) genMultilineCramped ctx let genRecord smallRecordExpr multilineRecordExpr (node: ExprRecordBaseNode) ctx = let size = getRecordSize ctx node.Fields @@ -1978,6 +2013,58 @@ let genLambdaAux (includeClosingParen: bool) (node: ExprLambdaNode) = let genLambda = genLambdaAux false let genLambdaWithParen = genLambdaAux true +let genParenArgWithSingleNamedExpr + (genIdentifier: Context -> Context) + (sepSpaceBeforeParen: Context -> Context) + (parenNode: ExprParenNode) + (nameIdent: SingleTextNode) + (infixAppNode: ExprInfixAppNode) + (node: Node) + = + let short = genIdentifier +> sepSpaceBeforeParen +> genExpr (Expr.Paren parenNode) + + let long = + let genArg leadingIdentifierIsMultiline = + let genArgLong = + match infixAppNode.RightHandSide with + // fn (a = [ + // b + // c + // ]) + | Expr.ArrayOrList arrayOrList -> sepSpace +> genArrayOrList false arrayOrList + | Expr.Record record -> sepSpace +> (genMultilineRecordAlignBrackets record |> genNode record) + | Expr.AnonStructRecord anonStructRecord -> + sepSpace + +> genSingleTextNodeWithSpaceSuffix sepSpace anonStructRecord.Struct + +> (genMultilineRecordAlignBrackets anonStructRecord |> genNode anonStructRecord) + | Expr.NamedComputation _ as e -> sepSpace +> genExpr e + | e -> indentSepNlnUnindent (genExpr e) +> sepNln + + // Maybe the entire application is only long due to the identifier + if not leadingIdentifierIsMultiline then + genArgLong + else + // If this is the case, we should try and have the argument in one line first + expressionFitsOnRestOfLine (sepSpace +> genExpr infixAppNode.RightHandSide) genArgLong + + leadingExpressionIsMultiline + (genIdentifier + +> sepSpaceBeforeParen + +> enterNode parenNode + +> genSingleTextNode parenNode.OpeningParen + +> genSingleTextNode nameIdent + +> sepSpace + +> genSingleTextNode infixAppNode.Operator) + (fun isMultiline -> + if not isMultiline then + genArg isMultiline + else + indent +> genArg isMultiline +> unindent) + +> genSingleTextNode parenNode.ClosingParen + +> leaveNode parenNode + + expressionFitsOnRestOfLine short long |> genNode node + let genClauses (clauses: MatchClauseNode list) = let lastIndex = clauses.Length - 1 From 7d23284f668f2403c55bd7e66541bdf867445afa Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 30 Jun 2023 18:10:39 +0200 Subject: [PATCH 8/8] Weird cramped ArrayOrList patterns. --- src/Fantomas.Core.Tests/MultilinePatterns.fs | 43 ++++++++++++++++++- .../PatternMatchingTests.fs | 7 ++- src/Fantomas.Core/CodePrinter.fs | 29 ++++++++----- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/Fantomas.Core.Tests/MultilinePatterns.fs b/src/Fantomas.Core.Tests/MultilinePatterns.fs index 7597f0e219..3c36c66b7d 100644 --- a/src/Fantomas.Core.Tests/MultilinePatterns.fs +++ b/src/Fantomas.Core.Tests/MultilinePatterns.fs @@ -621,7 +621,7 @@ match ast with | ParsedInput.ImplFile(ParsedImplFileInput(contents = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = [ SynModuleDecl.Types( - [ SynTypeDefn.SynTypeDefn(typeRepr = + [ SynTypeDefn.SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(simpleRepr = SynTypeDefnSimpleRepr.Enum(cases = [ SynEnumCase.SynEnumCase(trivia = { BarRange = None; EqualsRange = mEquals }) @@ -792,3 +792,44 @@ match ast with ])) -> assertRange (2, 2) (2, 25) mAttribute | _ -> Assert.Fail $"Could not get valid AST, got {ast}" """ + +[] +let ``cramped list in multiline pattern`` () = + formatSourceString + false + """ +match parseResults with +| + SynTypeDefnSimpleRepr.TypeAbbrev(rhsType = + SynType.Tuple( + false, + [ SynTupleTypeSegment.Type(TypeName "Y") + SynTupleTypeSegment.Star mStar + SynTupleTypeSegment.Type(TypeName "Z") ], + mTuple + ) + ) + -> () +""" + { config with MaxLineLength = 20 } + |> prepend newline + |> should + equal + """ +match + parseResults +with +| SynTypeDefnSimpleRepr.TypeAbbrev(rhsType = + SynType.Tuple( + false, + [ SynTupleTypeSegment.Type( + TypeName "Y" + ) + SynTupleTypeSegment.Star mStar + SynTupleTypeSegment.Type( + TypeName "Z" + ) ], + mTuple + ) + ) -> () +""" diff --git a/src/Fantomas.Core.Tests/PatternMatchingTests.fs b/src/Fantomas.Core.Tests/PatternMatchingTests.fs index e4f7e0fdf3..43398be5ec 100644 --- a/src/Fantomas.Core.Tests/PatternMatchingTests.fs +++ b/src/Fantomas.Core.Tests/PatternMatchingTests.fs @@ -1530,10 +1530,9 @@ let args = """ let args = match args with - | [| - LongPatIndentifierOne - | LongPatIndentifierTwo - | LongPatIndentifierThree |] -> args + | [| LongPatIndentifierOne + | LongPatIndentifierTwo + | LongPatIndentifierThree |] -> args | _ -> failwith "meh" """ diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 85750213db..8a4d01973e 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2735,16 +2735,25 @@ let genPat (p: Pattern) = let multiline = let cramped = - if node.OpenToken.Text.Length = 1 then - short - else - // pattern is an array, which can lead to some offset problems. - // It is better to play it safe and indent the patterns on the next line. - genSingleTextNode node.OpenToken - +> addSpaceIfSpaceAroundDelimiter - +> indentSepNlnUnindent genPats - +> addSpaceIfSpaceAroundDelimiter - +> genSingleTextNode node.CloseToken + genSingleTextNode node.OpenToken + +> (fun ctx -> + let column = ctx.WriterModel.Column + + let rest = + if column <= ctx.Config.IndentSize then + ctx.Config.IndentSize - column + else + ctx.Config.IndentSize - (column % ctx.Config.IndentSize) + + if rest = 0 then + (indent +> sepNln) ctx + else + // Insert additional spaces so that the first element starts on the next indent. + ((rep rest (!- " ")) +> indent) ctx) + +> genPats + +> unindent + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.CloseToken // Note that we deliberately are not take the setting fsharp_multiline_block_brackets_on_same_column into account. let multilineAlignBrackets =