Skip to content

Commit

Permalink
Extend LetOrUseBang to deal with short expression followed by a multi…
Browse files Browse the repository at this point in the history
…line expression. Fixes fsprojects#838
  • Loading branch information
nojaf committed May 16, 2020
1 parent f94bbd1 commit 0cde9d2
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 7 deletions.
64 changes: 63 additions & 1 deletion src/Fantomas.Tests/ComputationExpressionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -445,4 +445,66 @@ async {
return (a + b + c)
}
"""
"""

[<Test>]
let ``add new line between one-liner and multiline expression, 838`` () =
formatSourceString false """
let AddEvents (req: HttpRequest, log: ILogger) =
task {
let! user = req.Authenticate(log)
let! response =
user
|> Result.mapError sendUnAuthorizedRequest
|> Result.bind (decodeEvents req.Body)
|> Result.bind (validateEvents)
|> Result.bind (authenticateEvents)
|> Result.map (persistEvents)
|> Result.either
return response
}
let AddEventsX (req: HttpRequest, log: ILogger) =
let user = req.Authenticate(log)
let response =
user
|> Result.mapError sendUnAuthorizedRequest
|> Result.bind (decodeEvents req.Body)
|> Result.bind (validateEvents)
|> Result.bind (authenticateEvents)
|> Result.map (persistEvents)
|> Result.either
response
""" config
|> prepend newline
|> should equal """
let AddEvents (req: HttpRequest, log: ILogger) =
task {
let! user = req.Authenticate(log)
let! response =
user
|> Result.mapError sendUnAuthorizedRequest
|> Result.bind (decodeEvents req.Body)
|> Result.bind (validateEvents)
|> Result.bind (authenticateEvents)
|> Result.map (persistEvents)
|> Result.either
return response
}
let AddEventsX (req: HttpRequest, log: ILogger) =
let user = req.Authenticate(log)
let response =
user
|> Result.mapError sendUnAuthorizedRequest
|> Result.bind (decodeEvents req.Body)
|> Result.bind (validateEvents)
|> Result.bind (authenticateEvents)
|> Result.map (persistEvents)
|> Result.either
response
"""
14 changes: 11 additions & 3 deletions src/Fantomas/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,15 @@ and genExpr astContext synExpr =
genTrivia pat.Range (!- "and! " +> genPat astContext pat) -- " = "
+> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext expr)

let genReturn = genExpr astContext e2
let genReturn =
match e2 with
| SynExpr.LetOrUse(_,_, bindings, body, _) ->
let bindings =
bindings
|> List.map (fun b -> genLetBinding astContext "let " b, b.RangeOfBindingAndRhs)
[ yield! bindings; yield (genExpr astContext body, body.Range) ]
| _ -> [(genExpr astContext e2, e2.Range)]

let andLength = List.length ands

let andNodes =
Expand All @@ -1587,10 +1595,10 @@ and genExpr astContext synExpr =
let (_,_,_,pat,_,_) = ands.[idx + 1]
(genAnd astContext a, pat.Range)) ands

colWithNlnWhenLeadingWasMultiline
colWithNlnWhenItemIsMultiline
[ yield (genLetBang, e1.Range)
yield! andNodes
yield (genReturn, e2.Range)]
yield! genReturn ]

| ParsingError r ->
raise <| FormatException (sprintf "Parsing error(s) between line %i column %i and line %i column %i"
Expand Down
42 changes: 39 additions & 3 deletions src/Fantomas/Context.fs
Original file line number Diff line number Diff line change
Expand Up @@ -911,16 +911,52 @@ let internal sepNlnTypeAndMembers (firstMemberRange: range option) ctx =
| _ ->
ctx

let internal autoNlnConsideringTriviaIfExpressionExceedsPageWidth expr range (ctx: Context) =
expressionExceedsPageWidth
sepNone sepNone // before and after for short expressions
(sepNlnConsideringTriviaContentBefore range) sepNone // before and after for long expressions
expr ctx

let internal addExtraNewlineIfLeadingWasMultiline leading continuation continuationRange =
leadingExpressionIsMultiline
leading
(fun ml -> sepNln +> onlyIf ml (sepNlnConsideringTriviaContentBefore continuationRange) +> continuation)

let internal colWithNlnWhenLeadingWasMultiline items =
/// This helper function takes a list of expressions and ranges.
/// If the expression is multiline it will add a newline before and after the expression.
/// Unless it is the first expression in the list, that will never have a leading new line.
/// F.ex.
/// let a = AAAA
/// let b =
/// BBBB
/// BBBB
/// let c = CCCC
///
/// will be formatted as:
/// let a = AAAA
///
/// let b =
/// BBBB
/// BBBBB
///
/// let c = CCCC
///
/// The range in the tuple is the range of expression
let internal colWithNlnWhenItemIsMultiline items =
let firstItemRange = List.tryHead items |> Option.map snd
let rec impl items =
match items with
| (f1,_)::(_,r2)::_ -> addExtraNewlineIfLeadingWasMultiline f1 (impl (List.skip 1 items)) r2
| [(f,_)] -> f
| (f1,r1)::(_,r2)::_ ->
let f1Expr =
match firstItemRange with
| Some (fr1) when (fr1 = r1) -> f1
| _ -> ifElseCtx (lastWriteEventIsNewline) f1 (autoNlnConsideringTriviaIfExpressionExceedsPageWidth f1 r1)
addExtraNewlineIfLeadingWasMultiline f1Expr (impl (List.skip 1 items)) r2
| [(f,r)] ->
match firstItemRange with
| Some (fr1) when (fr1 = r) -> f
| _ -> autoNlnConsideringTriviaIfExpressionExceedsPageWidth f r
| [] -> sepNone
impl items

Expand Down

0 comments on commit 0cde9d2

Please sign in to comment.