From 9fb994117ee07975dd7af93b7fda1a72e63dcbe0 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Thu, 7 Jul 2022 17:02:01 +0200 Subject: [PATCH] Refactor keep indent in branch (#2342) * Determine KeepIndentInBranch by original code. * Fix nested match inside let bindings. --- CHANGELOG.md | 3 + .../KeepIndentInBranchTests.fs | 340 ++++++++++-------- .../KeepIndentInBranchExpressionTests.fs | 74 ++-- src/Fantomas.Core/CodePrinter.fs | 326 +++-------------- src/Fantomas.Core/SourceParser.fs | 64 ---- 5 files changed, 292 insertions(+), 515 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31121b7499..a9534221d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ ### Added * Add setting `fsharp_max_if_then_short_width`. [#2299](https://github.com/fsprojects/fantomas/issues/2299) +### Fixed +* KeepIndentInBranch not respected inside a let and match. [1825](https://github.com/fsprojects/fantomas/issues/1825) + ## [5.0.0-alpha-010] - 2022-06-27 ### Changed diff --git a/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs b/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs index 830987f613..16cea91cde 100644 --- a/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs +++ b/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs @@ -16,12 +16,12 @@ let foo () = if someCondition then 0 else - let config = Configuration.Read "/myfolder/myfile.xml" - let result = Process.main config otherArg - if result.IsOk then - 0 - else - -1 + let config = Configuration.Read "/myfolder/myfile.xml" + let result = Process.main config otherArg + if result.IsOk then + 0 + else + -1 """ config |> prepend newline @@ -57,10 +57,10 @@ let main argv = printfn "Would execute actions, but --dry-run was supplied: %+A" instructions 0 else - // proceed with main method - let output = Library.execute instructions - // do more stuff - 0 + // proceed with main method + let output = Library.execute instructions + // do more stuff + 0 """ config |> prepend newline @@ -108,10 +108,10 @@ let main argv = printfn "Would execute actions, but --dry-run was supplied: %+A" instructions 0 | RunMode.Wet -> - // proceed with main method - let output = Library.execute instructions - // do more stuff - 0 + // proceed with main method + let output = Library.execute instructions + // do more stuff + 0 """ config |> prepend newline @@ -160,10 +160,10 @@ let main argv = printfn "Would execute actions, but --dry-run was supplied: %+A" instructions 0 | RunMode.Wet -> - printfn "here it comes" - let output = Library.execute instructions - // do more stuff - 0 + printfn "here it comes" + let output = Library.execute instructions + // do more stuff + 0 """ config |> prepend newline @@ -213,10 +213,10 @@ let main argv = printfn "Would execute actions, but --dry-run was supplied: %+A" instructions 0 | RunMode.Wet -> - // proceed with main method - let output = Library.execute instructions - // do more stuff - 0 + // proceed with main method + let output = Library.execute instructions + // do more stuff + 0 """ config |> prepend newline @@ -268,8 +268,8 @@ module Foo = None | _ -> - doEvenMore () - Some 3 + doEvenMore () + Some 3 } """ config @@ -324,8 +324,8 @@ module Foo = None | _ -> - doEvenMore () - Some 3 + doEvenMore () + Some 3 } """ config @@ -368,9 +368,9 @@ let sum a b = | _, Negative _ -> None | a, b -> - logMessage "a and b are both positive" - // some grand explainer about the code - Some (a + b) + logMessage "a and b are both positive" + // some grand explainer about the code + Some (a + b) """ config |> prepend newline @@ -397,12 +397,12 @@ let sum a b = match a with | Negative -> None | _ -> - match b with - | Negative -> None - | _ -> - logMessage "a and b are both positive" - // some grand explainer about the code - Some(a + b) + match b with + | Negative -> None + | _ -> + logMessage "a and b are both positive" + // some grand explainer about the code + Some(a + b) """ config |> prepend newline @@ -432,13 +432,13 @@ let sum a b = if a < 0 then None else - logMessage "a is positive" - if b < 0 then - None - else - logMessage "a and b are both positive" - // some grand explainer about the code - Some(a + b) + logMessage "a is positive" + if b < 0 then + None + else + logMessage "a and b are both positive" + // some grand explainer about the code + Some(a + b) """ config |> prepend newline @@ -470,13 +470,13 @@ let sum a b = if a < 0 then None else - logMessage "a is positive" - match b with - | Negative -> None - | _ -> - logMessage "a and b are both positive" - // some grand explainer about the code - Some (a + b) + logMessage "a is positive" + match b with + | Negative -> None + | _ -> + logMessage "a and b are both positive" + // some grand explainer about the code + Some (a + b) """ config |> prepend newline @@ -555,7 +555,7 @@ if baz then printfn "Aborting." 1 else -0 + 0 """ config |> prepend newline @@ -1075,25 +1075,25 @@ and [] Bar<'context, 'a> = member this.InnerEquals<'innerContextLongLongLong, 'd, 'e> (a : Foo<'innerContextLongLongLong, 'd>) (b : Foo<'innerContext, 'd>) (cont : bool -> 'e) : 'e = if a.Hash <> b.Hash then cont false else - match a.Foo, b.Foo with - | Foo.Apply a, Foo.Apply b -> - a.Apply { new ApplyEval<_, _, _> with - member __.Eval<'bb> (a : Foo<'innerContextLongLongLong, 'bb -> 'b> * Foo<'innerContextLongLongLong, 'bb>) = - let (af, av) = a - b.Apply { new ApplyEval<_, _, _> with - member __.Eval<'cb> (b : Foo<'innerContextLongLongLong, 'cb -> 'b> * Foo<'innerContextLongLongLong, 'bc>) = - let (bf, bv) = b - if typeof<'bb> = typeof<'cb> then - let bv = unbox> bv - this.InnerEquals av bv (fun inner -> - if inner then - let bv = unbox 'b>> bf - this.InnerEquals af bf cont - else cont false - ) - else cont false - } - } + match a.Foo, b.Foo with + | Foo.Apply a, Foo.Apply b -> + a.Apply { new ApplyEval<_, _, _> with + member __.Eval<'bb> (a : Foo<'innerContextLongLongLong, 'bb -> 'b> * Foo<'innerContextLongLongLong, 'bb>) = + let (af, av) = a + b.Apply { new ApplyEval<_, _, _> with + member __.Eval<'cb> (b : Foo<'innerContextLongLongLong, 'cb -> 'b> * Foo<'innerContextLongLongLong, 'bc>) = + let (bf, bv) = b + if typeof<'bb> = typeof<'cb> then + let bv = unbox> bv + this.InnerEquals av bv (fun inner -> + if inner then + let bv = unbox 'b>> bf + this.InnerEquals af bf cont + else cont false + ) + else cont false + } + } """ { config with MaxLineLength = 100 @@ -1510,16 +1510,15 @@ let x y = """ let x y = if - not - ( - result.HasResultsFor( - [ "label" - "ipv4" - "macAddress" - "medium" - "manufacturer" ] - ) + not ( + result.HasResultsFor( + [ "label" + "ipv4" + "macAddress" + "medium" + "manufacturer" ] ) + ) then None else @@ -1558,16 +1557,15 @@ let x = """ let x = if - not - ( - result.HasResultsFor( - [ "label" - "ipv4" - "macAddress" - "medium" - "manufacturer" ] - ) + not ( + result.HasResultsFor( + [ "label" + "ipv4" + "macAddress" + "medium" + "manufacturer" ] ) + ) then None else @@ -1687,11 +1685,11 @@ let mapOperationToWebPart (operation: OpenApiOperationDescription) : SynExpr = let route = match operation with | _ -> // no route parameters - let route = mkAppNonAtomicExpr (mkIdentExpr "route") (mkStringExprConst operation.Path) - let responseHttpFunc = - mkLambdaExpr [ ] unitExpr - |> mkParenExpr - infixFish route responseHttpFunc + let route = mkAppNonAtomicExpr (mkIdentExpr "route") (mkStringExprConst operation.Path) + let responseHttpFunc = + mkLambdaExpr [ ] unitExpr + |> mkParenExpr + infixFish route responseHttpFunc infixFish verb route """ @@ -1725,16 +1723,16 @@ let updateModuleInImpl (ast: ParsedInput) (mdl: SynModuleOrNamespace) : ParsedIn match ast with | ParsedInput.SigFile _ -> ast | ParsedInput.ImplFile _ -> - ParsedImplFileInput( - fileName, - isScript, - qualifiedNameOfFile, - scopedPragmas, - hashDirectives, - [ mdl ], - isLastAndCompiled - ) - |> ParsedInput.ImplFile + ParsedImplFileInput( + fileName, + isScript, + qualifiedNameOfFile, + scopedPragmas, + hashDirectives, + [ mdl ], + isLastAndCompiled + ) + |> ParsedInput.ImplFile """ config |> prepend newline @@ -1767,25 +1765,25 @@ let private parseModel (modelSrc: string) : Result = if not (File.Exists(modelSrc)) then Error [ sprintf "Provided modelSrc \"%s\" does not exist" modelSrc ] else - let graph = new QueryableGraph() - graph.LoadFromFile(modelSrc, TurtleParser()) + let graph = new QueryableGraph() + graph.LoadFromFile(modelSrc, TurtleParser()) - let xsdPath = - Path.Combine(Directory.GetCurrentDirectory(), "Some.xsd") + let xsdPath = + Path.Combine(Directory.GetCurrentDirectory(), "Some.xsd") - let ontologyPath = - Path.Combine(Directory.GetCurrentDirectory(), "Some.ttl") + let ontologyPath = + Path.Combine(Directory.GetCurrentDirectory(), "Some.ttl") - let ontoGraph = new OntologyGraph() - FileLoader.Load(ontoGraph, ontologyPath) + let ontoGraph = new OntologyGraph() + FileLoader.Load(ontoGraph, ontologyPath) - let validationErrors = - GraphValidation.validate xsdPath ontoGraph "http://company.com/meh" graph + let validationErrors = + GraphValidation.validate xsdPath ontoGraph "http://company.com/meh" graph - if List.isEmpty validationErrors then - Ok graph - else - Error validationErrors + if List.isEmpty validationErrors then + Ok graph + else + Error validationErrors """ config |> prepend newline @@ -1829,10 +1827,10 @@ let ``tuple is consider as short branch, 1800`` () = // look it's a tuple nextModel, objectsRemoved | Some subjectToRemove -> - let a = 5 - let b = 6 - someFunctionApp a b |> ignore - acc) + let a = 5 + let b = 6 + someFunctionApp a b |> ignore + acc) state [] """ @@ -1874,10 +1872,10 @@ let ``parenthesis tuple is consider as short branch`` () = // look it's a tuple but wrapped in parenthesis (nextModel, objectsRemoved) | Some subjectToRemove -> - let a = 5 - let b = 6 - someFunctionApp a b |> ignore - acc) + let a = 5 + let b = 6 + someFunctionApp a b |> ignore + acc) state [] """ @@ -1949,11 +1947,10 @@ module Foo = if output.Exists - && not - ( - output.EnumerateFiles () |> Seq.isEmpty - && onlyContainsFoobar output - ) + && not ( + output.EnumerateFiles () |> Seq.isEmpty + && onlyContainsFoobar output + ) then Error (FooBarBazError.ErrorCase output) else @@ -2009,12 +2006,11 @@ module Foo = match output.Exists - && not - ( - output.EnumerateFiles () |> Seq.isEmpty - && onlyContainsFoobar output - ) - with + && not ( + output.EnumerateFiles () |> Seq.isEmpty + && onlyContainsFoobar output + ) + with | true -> Error (FooBarBazError.ErrorCase output) | false -> @@ -2038,9 +2034,9 @@ module Foo = if foo = bar then () else - let leftSet = HashSet (FooBarBaz.keys leftThings) - leftSet.SymmetricExceptWith (FooBarBaz.keys rightThings) - |> ignore + let leftSet = HashSet (FooBarBaz.keys leftThings) + leftSet.SymmetricExceptWith (FooBarBaz.keys rightThings) + |> ignore """ { config with MaxLineLength = 100 @@ -2053,7 +2049,9 @@ module Foo = """ module Foo = let assertConsistent () : unit = - if veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong then + if + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong + then () else if foo = bar then () @@ -2079,8 +2077,8 @@ module TestThing = match Stream.peel rest with | None -> failwith "oh no" | Some longName -> - longName - |> Map.map (fun _ -> TypedTerm.force) + longName + |> Map.map (fun _ -> TypedTerm.force) """ config |> prepend newline @@ -2114,9 +2112,9 @@ module Foo = if foo = bar then () else - let leftSet = HashSet (FooBarBaz.keys leftThings) - leftSet.SymmetricExceptWith (FooBarBaz.keys rightThings) - |> ignore + let leftSet = HashSet (FooBarBaz.keys leftThings) + leftSet.SymmetricExceptWith (FooBarBaz.keys rightThings) + |> ignore """ { config with MaxLineLength = 100 @@ -2129,7 +2127,9 @@ module Foo = """ module Foo = let assertConsistent () : unit = - if veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong then + if + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong + then () else if foo = bar then () @@ -2160,7 +2160,8 @@ longName equal """ match // comment - Stream.peel rest with + Stream.peel rest +with | None -> failwith "oh no" | Some longName -> @@ -2187,10 +2188,55 @@ longName equal """ match! // comment - Stream.peel rest with + Stream.peel rest +with | None -> failwith "oh no" | Some longName -> longName |> Map.map (fun _ -> TypedTerm.force) """ + +[] +let ``nested match inside let bindings, 1825`` () = + formatSourceString + false + """ +module Foo = + + let blah = + + let a = + match true with + | false -> + match result with + | Error _ -> failwith "" + | Ok _ -> + printfn "hi" + failwith "blah blah blah blah" + | true -> failwith "" + + failwith "" +""" + config + |> prepend newline + |> should + equal + """ +module Foo = + + let blah = + + let a = + match true with + | false -> + match result with + | Error _ -> failwith "" + | Ok _ -> + + printfn "hi" + failwith "blah blah blah blah" + | true -> failwith "" + + failwith "" +""" diff --git a/src/Fantomas.Core.Tests/Stroustrup/KeepIndentInBranchExpressionTests.fs b/src/Fantomas.Core.Tests/Stroustrup/KeepIndentInBranchExpressionTests.fs index 511e7bc019..d0df7d1b46 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/KeepIndentInBranchExpressionTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/KeepIndentInBranchExpressionTests.fs @@ -4,6 +4,8 @@ open NUnit.Framework open FsUnit open Fantomas.Core.Tests.TestHelper +// ExperimentalKeepIndentInBranch has precedence over ExperimentalStroustrupStyle + let config = { config with MultilineBlockBracketsOnSameColumn = true @@ -30,11 +32,12 @@ match x with equal """ match x with -| _ -> { - A = longTypeName - B = someOtherVariable - C = ziggyBarX -} +| _ -> + { + A = longTypeName + B = someOtherVariable + C = ziggyBarX + } """ [] @@ -75,11 +78,12 @@ match x with equal """ match x with -| _ -> {| - A = longTypeName - B = someOtherVariable - C = ziggyBarX -|} +| _ -> + {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} """ [] @@ -100,11 +104,12 @@ match x with equal """ match x with -| _ -> struct {| - A = longTypeName - B = someOtherVariable - C = ziggyBarX -|} +| _ -> + struct {| + A = longTypeName + B = someOtherVariable + C = ziggyBarX + |} """ [] @@ -125,10 +130,11 @@ match x with equal """ match x with -| _ -> task { - // some computation here - () -} +| _ -> + task { + // some computation here + () + } """ [] @@ -150,13 +156,14 @@ match x with equal """ match x with -| _ -> [ - itemOne - itemTwo - itemThree - itemFour - itemFive -] +| _ -> + [ + itemOne + itemTwo + itemThree + itemFour + itemFive + ] """ [] @@ -178,11 +185,12 @@ match x with equal """ match x with -| _ -> [| - itemOne - itemTwo - itemThree - itemFour - itemFive -|] +| _ -> + [| + itemOne + itemTwo + itemThree + itemFour + itemFive + |] """ diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 8d1b186104..f1cd6cab93 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -332,7 +332,7 @@ and genModuleDecl astContext (node: SynModuleDecl) = |> snd attributesExpr ctx - | DeclExpr e -> genExprKeepIndentInBranch astContext e + | DeclExpr e -> genExpr astContext e | Exception ex -> genException astContext ex | HashDirective p -> genParsedHashDirective p | Extern (ats, px, ao, t, sli, ps) -> @@ -1329,7 +1329,7 @@ and genExpr astContext synExpr ctx = +> genExpr astContext e2 | Paren (lpr, Lambda (pats, arrowRange, expr, lambdaRange), rpr, pr) -> fun (ctx: Context) -> - let body = genExprKeepIndentInBranch astContext + let body = genExpr astContext let expr = let triviaOfLambda f before (ctx: Context) = @@ -1933,7 +1933,7 @@ and genExpr astContext synExpr ctx = let hasMultipleClausesWhereOneHasRagnarok = hasMultipleClausesWhereOneHasStroustrup ctx.Config.ExperimentalStroustrupStyle cs - col sepNln cs (genClause astContext hasMultipleClausesWhereOneHasRagnarok) ctx) + col sepNln cs (genClause astContext false hasMultipleClausesWhereOneHasRagnarok) ctx) ) | TryFinally (tryKeyword, e1, finallyKeyword, e2) -> @@ -2000,7 +2000,7 @@ and genExpr astContext synExpr ctx = +> unindent +> sepNln +> genElse - +> indent + +> genKeepIdent elseKw elseExpr +> sepNln +> genExpr astContext elseExpr +> unindent @@ -2104,7 +2104,7 @@ and genExpr astContext synExpr ctx = (fun (elseKw, elseExpr) -> sepNln +> genTriviaFor SynExpr_IfThenElse_Else elseKw !- "else" - +> indent + +> genKeepIdent elseKw elseExpr +> sepNln +> genExpr astContext elseExpr +> unindent) @@ -2586,7 +2586,7 @@ and genMultilineFunctionApplicationArguments astContext argExpr = (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange + +> genLambdaArrowWithTrivia (genExpr astContext) body arrowRange |> genTriviaFor SynExpr_Lambda range) +> sepNln +> sepCloseTFor rpr) @@ -2960,7 +2960,7 @@ and genApp astContext e es ctx = leadingExpressionIsMultiline (sepOpenTFor lpr -- "fun " +> pats - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) bodyExpr arrowRange) + +> genLambdaArrowWithTrivia (genExpr astContext) bodyExpr arrowRange) (fun isMultiline -> onlyIf isMultiline sepNln +> sepCloseTFor rpr) |> genTriviaFor SynExpr_Paren pr @@ -3006,7 +3006,7 @@ and genLambdaMultiLineClosingNewline leadingExpressionIsMultiline (sepOpenTFor lpr -- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) bodyExpr arrowRange + +> genLambdaArrowWithTrivia (genExpr astContext) bodyExpr arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) (fun isMultiline -> onlyIf isMultiline sepNln +> sepCloseTFor rpr) |> genTriviaFor SynExpr_Paren pr @@ -3074,7 +3074,7 @@ and genAppWithLambda astContext sep (e, es, lpr, lambda, rpr, pr) = sepArrow |> genTriviaFor SynExpr_Lambda_Arrow arrowRange) arrowRange - +> genExprKeepIndentInBranch astContext body + +> genExpr astContext body |> genTriviaFor SynExpr_Lambda lambdaRange | Choice2Of2 (keywordRange, cs, range) -> (!- "function " @@ -3098,7 +3098,7 @@ and genAppWithLambda astContext sep (e, es, lpr, lambda, rpr, pr) = sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) bodyExpr arrowRange + +> genLambdaArrowWithTrivia (genExpr astContext) bodyExpr arrowRange |> genTriviaFor SynExpr_Lambda range) +> sepNln +> sepCloseTFor rpr @@ -3166,7 +3166,7 @@ and genAppWithLambda astContext sep (e, es, lpr, lambda, rpr, pr) = +> (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange + +> genLambdaArrowWithTrivia (genExpr astContext) body arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) +> sepNlnWhenWriteBeforeNewlineNotEmpty sepNone +> sepCloseTFor rpr @@ -3181,7 +3181,7 @@ and genAppWithLambda astContext sep (e, es, lpr, lambda, rpr, pr) = +> (sepOpenTFor lpr +> (!- "fun " +> col sepSpace pats (genPat astContext) - +> genLambdaArrowWithTrivia (genExprKeepIndentInBranch astContext) body arrowRange + +> genLambdaArrowWithTrivia (genExpr astContext) body arrowRange |> genTriviaFor SynExpr_Lambda lambdaRange) +> sepCloseTFor rpr |> genTriviaFor SynExpr_Paren pr) @@ -3293,7 +3293,6 @@ and genControlExpressionStart let enterStartKeyword = enterNodeFor startKeywordType startKeywordRange let genStartKeyword = !-startKeywordText let leaveStartKeyword = leaveNodeFor startKeywordType startKeywordRange - let enterEndKeyword = enterNodeFor endKeywordType endKeywordRange let genEndKeyword = !-endKeywordText let leaveEndKeyword = leaveNodeFor endKeywordType endKeywordRange @@ -3379,113 +3378,6 @@ and genMatchBangWith astContext (matchKeyword: range) (matchExpr: SynExpr) (with withKeyword "with" -// TODO: remove when revisiting KeepIndentInBranch -and genExprInIfOrMatch astContext (e: SynExpr) (ctx: Context) : Context = - let short = - sepNlnWhenWriteBeforeNewlineNotEmpty sepSpace - +> genExpr astContext e - - let long = - let hasCommentBeforeExpr (e: SynExpr) = - match Map.tryFindOrEmptyList (synExprToFsAstType e |> fst) ctx.TriviaBefore with - | [] -> false - | triviaInstructions -> - List.exists - (fun ti -> - match ti.Trivia.Item with - | Comment (CommentOnSingleLine _) -> RangeHelpers.rangeEq ti.Range e.Range - | _ -> false) - triviaInstructions - - let indentNlnUnindentNln f = indentSepNlnUnindent f +> sepNln - - let fallback = - if hasCommentBeforeExpr e then - genExpr astContext e |> indentNlnUnindentNln - else - sepNlnWhenWriteBeforeNewlineNotEmpty sepNone - +> genExpr astContext e - - match e with - | App (SynExpr.DotGet _, [ (Paren _) ]) -> atCurrentColumn (genExpr astContext e) - | Paren (lpr, (AppSingleParenArg _ as ate), rpr, _pr) -> - sepOpenTFor lpr - +> atCurrentColumnIndent (genExpr astContext ate) - +> sepCloseTFor rpr - | AppParenArg app -> - genAlternativeAppWithParenthesis app astContext - |> indentNlnUnindentNln - | InfixApp (s, sli, (AppParenArg app as e1), e2, _) -> - (expressionFitsOnRestOfLine (genExpr astContext e1) (genAlternativeAppWithParenthesis app astContext) - +> ifElse (noBreakInfixOps.Contains(s)) sepSpace sepNln - +> genSynLongIdent false sli - +> sepSpace - +> genExpr astContext e2) - |> indentNlnUnindentNln - | InfixApp (s, sli, e1, (AppParenArg app as e2), _) -> - (genExpr astContext e1 - +> sepNln - +> genSynLongIdent false sli - +> sepSpace - +> expressionFitsOnRestOfLine (genExpr astContext e2) (genAlternativeAppWithParenthesis app astContext)) - |> indentNlnUnindentNln - // very specific fix for 1380 - | SameInfixApps (Paren (lpr, AppParenArg e, rpr, _pr), es) -> - (sepOpenTFor lpr - +> genAlternativeAppWithParenthesis e astContext - +> sepCloseTFor rpr - +> sepNln - +> col sepNln es (fun (opText, opExpr, e) -> - genSynLongIdent false opExpr - +> sepSpace - +> (match e with - | Paren (lpr, AppParenArg app, rpr, _pr) -> - sepOpenTFor lpr - +> genAlternativeAppWithParenthesis app astContext - +> sepCloseTFor rpr - | _ -> genExpr astContext e))) - |> indentNlnUnindentNln - | InfixApp _ -> fallback - | App (SynExpr.Ident _, _) - | App (SynExpr.LongIdent _, _) -> - indent - +> sepNln - +> genExpr astContext e - +> unindent - | SynExpr.Match _ - | SynExpr.MatchBang _ - | SynExpr.TryWith _ - | SynExpr.TryFinally _ -> genExpr astContext e |> indentNlnUnindentNln - | DotGetAppParen (DotGetAppParen (e1, px1, lids1), px2, lids2) -> - genExpr astContext e1 - +> genExpr astContext px1 - +> indent - +> sepNln - +> genSynLongIdentMultiline true lids1 - +> genExpr astContext px2 - +> sepNln - +> genSynLongIdentMultiline true lids2 - +> unindent - |> genTriviaFor SynExpr_DotGet e.Range - |> indentNlnUnindentNln - | _ -> fallback - - expressionFitsOnRestOfLine short long ctx - -and genWithAfterMatch (astType: FsAstType) (withRange: Range) = - genTriviaFor astType withRange (fun ctx -> - let hasContentOnLastLine = - List.tryHead ctx.WriterModel.Lines - |> Option.map String.isNotNullOrWhitespace - |> Option.defaultValue false - - if hasContentOnLastLine then - // add a space if there is no newline right after the expression - (!- " with") ctx - else - // add the indentation in spaces if there is no content on the current line - (rep ctx.Config.IndentSize (!- " ") +> !- "with") ctx) - and genAlternativeAppWithParenthesis app astContext = match app with | Choice1Of2 t -> genAlternativeAppWithTupledArgument t astContext @@ -4555,6 +4447,7 @@ and genInterfaceImpl astContext (InterfaceImpl (t, withKeywordRange, bs, members and genClause (astContext: ASTContext) + (isLastItem: bool) (hasMultipleClausesWhereOneHasRagnarok: bool) (Clause (barRange, p, eo, arrowRange, e, clauseRange)) = @@ -4599,7 +4492,23 @@ and genClause sepArrow |> genTriviaFor SynMatchClause_Arrow arrowRange) arrowRange - +> autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e) + +> (if ctx.Config.ExperimentalKeepIndentInBranch + && isLastItem then + let short = genExpr astContext e + + let long = + match barRange with + | None -> + autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e + | Some barRange -> + genKeepIdent barRange e + +> sepNln + +> genExpr astContext e + +> unindent + + expressionFitsOnRestOfLine short long + else + autoIndentAndNlnIfExpressionExceedsPageWidthUnlessRagnarok (genExpr astContext) e)) ctx) let genBar = @@ -4631,10 +4540,19 @@ and genClause |> genTriviaFor SynMatchClause_ clauseRange) and genClauses astContext cs (ctx: Context) = - col + let lastIndex = cs.Length - 1 + + coli sepNln cs - (genClause astContext (hasMultipleClausesWhereOneHasStroustrup ctx.Config.ExperimentalStroustrupStyle cs)) + (fun idx clause -> + let isLastItem = lastIndex = idx + + genClause + astContext + isLastItem + (hasMultipleClausesWhereOneHasStroustrup ctx.Config.ExperimentalStroustrupStyle cs) + clause) ctx /// Each multiline member definition has a pre and post new line. @@ -5171,8 +5089,7 @@ and genSynBindingFunction expressionFitsOnRestOfLine short long - let body (ctx: Context) = - genExprKeepIndentInBranch astContext e ctx + let body (ctx: Context) = genExpr astContext e ctx let genExpr isMultiline = if isMultiline then @@ -5181,7 +5098,7 @@ and genSynBindingFunction let short = sepSpace +> body let long = - autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExprKeepIndentInBranch astContext e) e + autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExpr astContext e) e isShortExpression ctx.Config.MaxFunctionBindingWidth short long @@ -5282,7 +5199,7 @@ and genSynBindingFunctionWithReturnType expressionFitsOnRestOfLine short long - let body = genExprKeepIndentInBranch astContext e + let body = genExpr astContext e let genExpr isMultiline = if isMultiline then @@ -5291,7 +5208,7 @@ and genSynBindingFunctionWithReturnType let short = sepSpace +> body let long = - autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExprKeepIndentInBranch astContext e) e + autoIndentAndNlnExpressUnlessRagnarok (fun e -> sepSpace +> genExpr astContext e) e isShortExpression ctx.Config.MaxFunctionBindingWidth short long @@ -5408,11 +5325,11 @@ and genSynBindingValue +> genValueName +> genReturnType - let short = prefix +> genExprKeepIndentInBranch astContext e + let short = prefix +> genExpr astContext e let long = prefix - +> autoIndentAndNlnExpressUnlessRagnarok (genExprKeepIndentInBranch astContext) e + +> autoIndentAndNlnExpressUnlessRagnarok (genExpr astContext) e isShortExpression ctx.Config.MaxValueBindingWidth short long ctx) @@ -5449,147 +5366,6 @@ and genParenTupleWithIndentAndNewlines +> genTriviaFor SynPat_Paren_ClosingParenthesis rpr sepCloseT |> genTriviaFor SynPat_Paren pr -and collectMultilineItemForSynExprKeepIndent (astContext: ASTContext) (e: SynExpr) : ColMultilineItem list = - match e with - | LetOrUses (bs, e) -> - collectMultilineItemForLetOrUses astContext bs (collectMultilineItemForSynExprKeepIndent astContext e) - | Sequentials es -> - let lastIndex = es.Length - 1 - - es - |> List.mapi (fun idx e -> - if idx = lastIndex then - collectMultilineItemForSynExprKeepIndent astContext e - else - collectMultilineItemForSynExpr astContext e) - |> List.collect id - | KeepIndentMatch (matchKeywordRange, me, withRange, clauses, range, matchTriviaType) -> - ColMultilineItem( - genKeepIndentMatch astContext matchKeywordRange me withRange clauses range matchTriviaType, - sepNlnConsideringTriviaContentBeforeForMainNode matchTriviaType range - ) - |> List.singleton - | KeepIndentIfThenElse (branches, elseBranch, ifElseRange) -> - ColMultilineItem( - genKeepIdentIf astContext branches elseBranch ifElseRange, - sepNlnConsideringTriviaContentBeforeForMainNode SynExpr_IfThenElse ifElseRange - ) - |> List.singleton - | _ -> - let t, r = synExprToFsAstType e - [ ColMultilineItem(genExpr astContext e, sepNlnConsideringTriviaContentBeforeForMainNode t r) ] - -and genExprKeepIndentInBranch (astContext: ASTContext) (e: SynExpr) : Context -> Context = - let keepIndentExpr (ctx: Context) = - let items = collectMultilineItemForSynExprKeepIndent astContext e - - colWithNlnWhenItemIsMultilineUsingConfig items ctx - - ifElseCtx (fun ctx -> ctx.Config.ExperimentalKeepIndentInBranch) keepIndentExpr (genExpr astContext e) - -and genKeepIndentMatch - (astContext: ASTContext) - (matchKeyword: range) - (e: SynExpr) - (withRange: range) - (clauses: SynMatchClause list) - (range: Range) - (triviaType: FsAstType) - : Context -> Context = - let lastClauseIndex = clauses.Length - 1 - let isMatchBang = triviaType = SynExpr_MatchBang - - ifElse - isMatchBang - (genTriviaFor SynExpr_MatchBang_Match matchKeyword !- "match! ") - (genTriviaFor SynExpr_Match_Match matchKeyword !- "match ") - +> autoIndentAndNlnWhenWriteBeforeNewlineNotEmpty ( - genExprInIfOrMatch astContext e - +> genWithAfterMatch - (if isMatchBang then - SynExpr_MatchBang_With - else - SynExpr_Match_With) - withRange - ) - +> sepNln - +> (fun ctx -> - let hasMultipleClausesWhereOneHasRagnarok = - hasMultipleClausesWhereOneHasStroustrup ctx.Config.ExperimentalStroustrupStyle clauses - - coli - sepNln - clauses - (fun idx -> - if idx < lastClauseIndex then - genClause astContext hasMultipleClausesWhereOneHasRagnarok - else - genLastClauseKeepIdent astContext) - ctx) - |> genTriviaFor triviaType range - -and genLastClauseKeepIdent (astContext: ASTContext) (Clause (_, pat, whenExpr, arrowRange, expr, _)) = - sepBar - +> genPat astContext pat - +> sepSpace - +> optSingle (genExpr astContext) whenExpr - +> optSingle - (fun arrowRange -> - sepArrowFixed - |> genTriviaFor SynMatchClause_Arrow arrowRange) - arrowRange - +> sepNln - +> (let t, r = synExprToFsAstType expr in sepNlnConsideringTriviaContentBeforeForMainNode t r) - +> genExprKeepIndentInBranch astContext expr - -and genKeepIdentIf - (astContext: ASTContext) - (branches: (range option * range * bool * SynExpr * range * SynExpr) list) - (elseExpr: SynExpr) - (ifElseRange: Range) - = - col sepNln branches (fun (elseKw, ifKw, isElif, ifExpr, thenKw, thenExpr) -> - let genIf = - let genKeywordStart = - optSingle - (fun r -> - !- "else " - |> genTriviaFor SynExpr_IfThenElse_Else r) - elseKw - +> (ifElse isElif (!- "elif ") (!- "if ") - |> genTriviaFor - (if isElif then - SynExpr_IfThenElse_Elif - else - SynExpr_IfThenElse_If) - ifKw) - - let short = - genKeywordStart - +> genExpr astContext ifExpr - +> (!- " then" - |> genTriviaFor SynExpr_IfThenElse_Then thenKw) - - let long = - genKeywordStart - +> genExprInIfOrMatch astContext ifExpr - +> sepSpace - +> !- "then" - - expressionFitsOnRestOfLine short long - - genIf - +> indent - +> sepNln - +> genExpr astContext thenExpr - +> unindent) - +> sepNln - +> !- "else" - +> sepNln - +> (let t, r = synExprToFsAstType elseExpr in sepNlnConsideringTriviaContentBeforeForMainNode t r) - +> genExprKeepIndentInBranch astContext elseExpr - |> genTriviaFor SynExpr_IfThenElse ifElseRange - and genConst (c: SynConst) (r: Range) = match c with | SynConst.Unit -> @@ -5787,3 +5563,11 @@ and genMeasure (measure: SynMeasure) = | SynMeasure.Paren (m, _) -> sepOpenT +> loop m +> sepCloseT !- "<" +> loop measure +> !- ">" + +and genKeepIdent startRange (e: SynExpr) ctx = + if ctx.Config.ExperimentalKeepIndentInBranch + && (startRange.StartColumn = e.Range.StartColumn) then + let t, r = synExprToFsAstType e + sepNlnConsideringTriviaContentBeforeForMainNode t r ctx + else + indent ctx diff --git a/src/Fantomas.Core/SourceParser.fs b/src/Fantomas.Core/SourceParser.fs index d6882c75ad..d2ce7693d9 100644 --- a/src/Fantomas.Core/SourceParser.fs +++ b/src/Fantomas.Core/SourceParser.fs @@ -1804,70 +1804,6 @@ let (|AppParenArg|_|) e = | AppParenSingleArg s -> Choice2Of2 s |> Some | _ -> None -let private shouldNotIndentBranch e es = - let isShortIfBranch e = - match e with - | SimpleExpr _ - | Sequential (_, _, true) - | App _ - | Tuple _ - | Paren (_, Tuple _, _, _) -> true - | _ -> false - - let isLongElseBranch e = - match e with - | LetOrUses _ - | Sequential _ - | Match _ - | TryWith _ - | App (_, [ ObjExpr _ ]) - | NewlineInfixApp (_, _, AppParenTupleArg _, _) - | NewlineInfixApp (_, _, _, App (_, [ Paren (_, Lambda _, _, _) ])) -> true - | _ -> false - - List.forall isShortIfBranch es - && isLongElseBranch e - -let (|KeepIndentMatch|_|) (e: SynExpr) = - let mapClauses matchKeyword matchExpr withKeyword clauses range t = - match clauses with - | [] -> None - | [ (Clause (_, _, _, _, lastClause, _)) ] -> - if shouldNotIndentBranch lastClause [] then - Some(matchKeyword, matchExpr, withKeyword, clauses, range, t) - else - None - | clauses -> - let firstClauses = - clauses - |> List.take (clauses.Length - 1) - |> List.map (fun (Clause (_, _, _, _, expr, _)) -> expr) - - let (Clause (_, _, _, _, lastClause, _)) = List.last clauses - - if shouldNotIndentBranch lastClause firstClauses then - Some(matchKeyword, matchExpr, withKeyword, clauses, range, t) - else - None - - match e with - | Match (matchKeyword, matchExpr, withKeyword, clauses) -> - mapClauses matchKeyword matchExpr withKeyword clauses e.Range SynExpr_Match - | MatchBang (matchKeyword, matchExpr, withKeyword, clauses) -> - mapClauses matchKeyword matchExpr withKeyword clauses e.Range SynExpr_MatchBang - | _ -> None - -let (|KeepIndentIfThenElse|_|) (e: SynExpr) = - match e with - | ElIf (branches, Some (_, elseExpr), _) -> - let branchBodies = branches |> List.map (fun (_, _, _, e, _, _) -> e) - - if shouldNotIndentBranch elseExpr branchBodies then - Some(branches, elseExpr, e.Range) - else - None - | _ -> None - let (|StroustrupStyleExpr|_|) (isStroustrupStyleEnabled: bool) (e: SynExpr) = if not isStroustrupStyleEnabled then None