Skip to content

Commit

Permalink
Properly collect ColMultilineItems for nested LetOrUse in Sequential. F…
Browse files Browse the repository at this point in the history
…ixes #1671. (#1678)
  • Loading branch information
nojaf committed Apr 26, 2021
1 parent 0d8d7c7 commit cb92329
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 59 deletions.
38 changes: 38 additions & 0 deletions src/Fantomas.Tests/ColMultilineItemTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,41 @@ printFn ()
open Foo
open Bar
"""

[<Test>]
let ``items should be collected from nested let-or-use in sequentials`` () =
formatSourceString
false
"""
let blah<'a> config : Type =
//#if DEBUG
failwith ""
//#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
()
"""
{ config with
SpaceBeforeUppercaseInvocation = true
SpaceBeforeClassConstructor = true
SpaceBeforeMember = true
SpaceBeforeColon = true
SpaceBeforeSemicolon = true
MultilineBlockBracketsOnSameColumn = true
NewlineBetweenTypeDefinitionAndMembers = true
KeepIfThenInSameLine = true
AlignFunctionSignatureToIndentation = true
AlternativeLongMemberDefinitions = true
MultiLineLambdaClosingNewline = true }
|> prepend newline
|> should
equal
"""
let blah<'a> config : Type =
//#if DEBUG
failwith ""
//#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
()
"""
138 changes: 138 additions & 0 deletions src/Fantomas.Tests/KeepIndentInBranchTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1176,3 +1176,141 @@ and [<CustomEquality ; NoComparison>] Bar<'context, 'a> =
}
}
"""

[<Test>]
let ``ifdef trivia should not influence outcome, 1646`` () =
formatSourceString
false
"""
module Foo =
[<Foo>]
let blah<'a> config : Type =
#if DEBUG
failwith ""
#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
if successful |> List.isEmpty then
result
else
let errors =
unsuccessful
|> List.filter (fun report ->
not report.BuildResult.IsBuildSuccess
|| not report.BuildResult.IsGenerateSuccess
)
|> List.map (fun report -> report.BuildResult.ErrorMessage)
failwith ""
"""
{ config with
SpaceBeforeUppercaseInvocation = true
SpaceBeforeClassConstructor = true
SpaceBeforeMember = true
SpaceBeforeColon = true
SpaceBeforeSemicolon = true
MultilineBlockBracketsOnSameColumn = true
NewlineBetweenTypeDefinitionAndMembers = true
KeepIfThenInSameLine = true
AlignFunctionSignatureToIndentation = true
AlternativeLongMemberDefinitions = true
MultiLineLambdaClosingNewline = true
KeepIndentInBranch = true }
|> prepend newline
|> should
equal
"""
module Foo =
[<Foo>]
let blah<'a> config : Type =
#if DEBUG
failwith ""
#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
if successful |> List.isEmpty then
result
else
let errors =
unsuccessful
|> List.filter (fun report ->
not report.BuildResult.IsBuildSuccess
|| not report.BuildResult.IsGenerateSuccess
)
|> List.map (fun report -> report.BuildResult.ErrorMessage)
failwith ""
"""

[<Test>]
let ``ifdef trivia should not influence outcome, idempotent`` () =
formatSourceString
false
"""
module Foo =
[<Foo>]
let blah<'a> config : Type =
#if DEBUG
failwith ""
#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
if successful |> List.isEmpty then
result
else
let errors =
unsuccessful
|> List.filter (fun report ->
not report.BuildResult.IsBuildSuccess
|| not report.BuildResult.IsGenerateSuccess
)
|> List.map (fun report -> report.BuildResult.ErrorMessage)
failwith ""
"""
{ config with
SpaceBeforeUppercaseInvocation = true
SpaceBeforeClassConstructor = true
SpaceBeforeMember = true
SpaceBeforeColon = true
SpaceBeforeSemicolon = true
MultilineBlockBracketsOnSameColumn = true
NewlineBetweenTypeDefinitionAndMembers = true
KeepIfThenInSameLine = true
AlignFunctionSignatureToIndentation = true
AlternativeLongMemberDefinitions = true
MultiLineLambdaClosingNewline = true
KeepIndentInBranch = true }
|> prepend newline
|> should
equal
"""
module Foo =
[<Foo>]
let blah<'a> config : Type =
#if DEBUG
failwith ""
#endif
DoThing.doIt ()
let result = Runner.Run<'a> config
if successful |> List.isEmpty then
result
else
let errors =
unsuccessful
|> List.filter (fun report ->
not report.BuildResult.IsBuildSuccess
|| not report.BuildResult.IsGenerateSuccess
)
|> List.map (fun report -> report.BuildResult.ErrorMessage)
failwith ""
"""
120 changes: 61 additions & 59 deletions src/Fantomas/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1993,65 +1993,26 @@ and genExpr astContext synExpr ctx =
&& not (futureNlnCheck (genExpr astContext e) ctx)
| _ -> false

let genInKeyword (binding: SynBinding) (ctx: Context) =
match inKeyWordTrivia binding ctx with
| Some (_, tn) ->
(printContentBefore tn
+> !- " in "
+> printContentAfter tn)
ctx
| None -> sepNone ctx

fun ctx ->
if isInSameLine ctx then
// short expression with in keyword
// f.ex. let a in ()
atCurrentColumn
(optSingle
(fun (p, x) ->
(fun (p, b) ->
genLetBinding
{ astContext with
IsFirstChild = p <> "and" }
p
x
+> genInKeyword x)
b
+> genInKeyword b e)
(List.tryHead bs)
+> genExpr astContext e)
ctx
else
let letBindings (bs: (string * SynBinding) list) =
bs
|> List.map
(fun (p, x) ->
let expr =
enterNodeFor (synBindingToFsAstType x) x.RangeOfBindingAndRhs
+> genLetBinding
{ astContext with
IsFirstChild = p <> "and" }
p
x
+> genInKeyword x

let range = x.RangeOfBindingAndRhs

let sepNln =
sepNlnConsideringTriviaContentBeforeForMainNode (synBindingToFsAstType x) range

ColMultilineItem(expr, sepNln))

let rec synExpr e =
match e with
| LetOrUses (bs, e) -> letBindings bs @ synExpr e
| Sequentials s -> s |> List.collect synExpr
| _ ->
let t, r = synExprToFsAstType e

[ ColMultilineItem(
genExpr astContext e,
sepNlnConsideringTriviaContentBeforeForMainNode t r
) ]

let items = letBindings bs @ synExpr e
let items =
collectMultilineItemForLetOrUses astContext bs e
@ collectMultilineItemForSynExpr astContext e

atCurrentColumn (colWithNlnWhenItemIsMultilineUsingConfig items) ctx

Expand Down Expand Up @@ -2117,20 +2078,7 @@ and genExpr astContext synExpr ctx =
| SequentialSimple es
| Sequentials es ->
let items =
es
|> List.map
(fun e ->
let expr = genExpr astContext e

let fsAstType, r =
match e with
| LetOrUses ((_, fb) :: _, _) -> (synBindingToFsAstType fb), fb.RangeOfBindingAndRhs
| _ -> synExprToFsAstType e

let sepNln =
sepConsideringTriviaContentBeforeForMainNode sepSemiNln fsAstType r

ColMultilineItem(expr, sepNln))
List.collect (collectMultilineItemForSynExpr astContext) es

atCurrentColumn (colWithNlnWhenItemIsMultilineUsingConfig items)

Expand Down Expand Up @@ -3283,6 +3231,60 @@ and genAlternativeAppWithParenthesis app astContext =
| Choice1Of2 t -> genAlternativeAppWithTupledArgument t astContext
| Choice2Of2 s -> genAlternativeAppWithSingleParenthesisArgument s astContext

and collectMultilineItemForSynExpr (astContext: ASTContext) (e: SynExpr) : ColMultilineItem list =
match e with
| LetOrUses (bs, e) ->
collectMultilineItemForLetOrUses astContext bs e
@ collectMultilineItemForSynExpr astContext e
| Sequentials s ->
s
|> List.collect (collectMultilineItemForSynExpr astContext)
| _ ->
let t, r = synExprToFsAstType e

[ ColMultilineItem(genExpr astContext e, sepNlnConsideringTriviaContentBeforeForMainNode t r) ]

and collectMultilineItemForLetOrUses
(astContext: ASTContext)
(bs: (string * SynBinding) list)
(e: SynExpr)
: ColMultilineItem list =
bs
|> List.map
(fun (p, x) ->
let expr =
enterNodeFor (synBindingToFsAstType x) x.RangeOfBindingAndRhs
+> genLetBinding
{ astContext with
IsFirstChild = p <> "and" }
p
x
+> genInKeyword x e

let range = x.RangeOfBindingAndRhs

let sepNln =
sepNlnConsideringTriviaContentBeforeForMainNode (synBindingToFsAstType x) range

ColMultilineItem(expr, sepNln))

and genInKeyword (binding: SynBinding) (e: SynExpr) (ctx: Context) =
let inKeyWordTrivia (binding: SynBinding) =
let inRange =
ctx.MkRange binding.RangeOfBindingAndRhs.End e.Range.Start

Map.tryFindOrEmptyList IN ctx.TriviaTokenNodes
|> TriviaHelpers.``keyword token after start column and on same line`` inRange
|> List.tryHead

match inKeyWordTrivia binding with
| Some (_, tn) ->
(printContentBefore tn
+> !- " in "
+> printContentAfter tn)
ctx
| None -> sepNone ctx

and sepNlnBetweenTypeAndMembers (tdr: SynTypeDefnRepr) (ms: SynMemberDefn list) =
match List.tryHead ms with
| Some m ->
Expand Down

0 comments on commit cb92329

Please sign in to comment.