Conversation
|
👋 @cblp Reviewer: Please verify the following things have been done, if applicable.
|
Port of tweag/ormolu#457 Original issue tweag/ormolu#299 Closes fourmolu#80
|
Added unsolved questions to the PR description. Please read. |
if cond1
|| cond2 then
something
else
something elseThe multiline condition makes the overall expression four lines. I think it's fine that the indentation in the condition matches the block below, we already do this in a few places like instances with multiline constraints. |
brandonchinn178
left a comment
There was a problem hiding this comment.
Looks great! Just let me know when you've resolved the open questions + added tests for them
|
I think I solved problems 1 and 2. Sorry, I find merged condition and then-block with Unfortunately, I don't like the code I wrote, I don't know Fourmolu code good enough. The style is great, but implementation sucks a little. I hope somebody will refactor it after me. |
|
Also, I'm not comfortable with the option name. |
brandonchinn178
left a comment
There was a problem hiding this comment.
Left some comments, take a look?
Also, I'm not comfortable with the option name. one-level-ifs came from original patch, but I think indent-then-else :: Bool is better.
indent-then-else doesn't quite communicate it to me either. How about compact-ifs?
| thenBodySpan = getLocA then' | ||
| elseBodySpan = getLocA else' | ||
| isWholeOneLine = isOneLineSpanFromTo conditionSpan elseBodySpan | ||
| if isOneLineSpan conditionSpan then space else breakpoint |
There was a problem hiding this comment.
Use switchLayout with all the spans (condition/then/else), and that will set the context to be "SingleLine" or "MultiLine". Once this context is set, breakpoint will switch between space and newline automatically
There was a problem hiding this comment.
Sorry, I can't. Could you help me?
Which version is more compact? The proposed feature makes code narrower but sometimes taller. E.g. it may reformat 3-line block into 4-line. |
|
I was thinking yours is more compact because it puts the if/then/else keywords on two lines instesd of three (in a multiline context) Could also do
|
Shift from what? Do you assume one of these style "old" or "default"? From my taste, "then" on the same line with "if" is the neutral position, and "then" placed on a separate line and indented is shifted, because simply "indented" == "shifted". |
|
Yes, the default Fourmolu format is the default because that's how 90% of Haskell code is formatted in the wild. So the option should be describing how we're transforming from that default style |
|
Oh, from that perspective, ok. I'm still not sure about the naming. I'd like leave the decision for the mainteiner. |
|
I noticed there are no test cases covering multiple chained if foo then
doFoo
else if bar then
doBar
else
doBazThis style ^ is what I would love to use with |
|
@cblp I am the maintainer 🙂 Let's go with Also, can you apply this diff? The key thing here is that we should avoid changing the code too much, to minimize conflicts when merging Ormolu changes back. Expand to see diffdiff --git a/src/Ormolu/Printer/Meat/Declaration/Value.hs b/src/Ormolu/Printer/Meat/Declaration/Value.hs
index cc29dad9..0480a462 100644
--- a/src/Ormolu/Printer/Meat/Declaration/Value.hs
+++ b/src/Ormolu/Printer/Meat/Declaration/Value.hs
@@ -1101,6 +1101,8 @@ p_if ::
LocatedA body ->
R ()
p_if placer render anns if' then' else' = do
+ oneLevelIfs <- getPrinterOpt poOneLevelIfs
+
txt "if"
space
located if' p_hsExpr
@@ -1109,6 +1111,9 @@ p_if placer render anns if' then' else' = do
where
AnnsIf {aiThen, aiElse} = anns
+ locatedToken tokenSpan token =
+ located (L tokenSpan ()) $ \_ -> txt token
+
betweenSpans spanA spanB s = spanA < s && s < spanB
placeHangingLocated tokenSpan bodyLoc@(L _ body) = do
@@ -1121,33 +1126,23 @@ p_if placer render anns if' then' else' = do
switchLayout [tokenSpan, bodySpan] $
placeHanging placement (located bodyLoc render)
- oneLevelIfs <- getPrinterOpt poOneLevelIfs
+ placeBranch tokenSpan body =
+ if oneLevelIfs && isOneLineSpan (getLocA body)
+ then placeHanging Normal (located body render)
+ else placeHangingLocated tokenSpan body
- let placeBranch tokenSpan token body = do
- located (L tokenSpan ()) $ \_ -> txt token
+ let hangIf m =
if oneLevelIfs
- then do
- if isOneLineSpan $ getLocA body
- then do
- breakpoint
- inci (located body render)
- else placeHangingLocated tokenSpan body
- else do
- space
- placeHangingLocated tokenSpan body
-
- branches = do
- placeBranch thenSpan "then" then'
- breakpoint
- placeBranch elseSpan "else" else'
-
- if oneLevelIfs
- then do
- if isOneLineSpan $ getLocA if' then space else breakpoint
- branches
- else do
- breakpoint
- inci branches
+ then switchLayout [getLocA if'] breakpoint >> m
+ else breakpoint >> inci m
+ hangIf $ do
+ locatedToken thenSpan "then"
+ space
+ placeBranch thenSpan then'
+ breakpoint
+ locatedToken elseSpan "else"
+ space
+ placeBranch elseSpan else'
p_let ::
-- | True if in do-blockCan you also add @evanrelf's suggestion to the test suite? |
|
@evanrelf I think if-else-if chain is a good feature, but should be implemented separately |
|
@cblp Can you ateast add a test so the behavior is documented? |
|
@brandonchinn178 I've added the patch. |
brandonchinn178
left a comment
There was a problem hiding this comment.
I think one TODO that was accidentally left in, but looks good otherwise!
| expr_chain = | ||
| if a then | ||
| b | ||
| else | ||
| if c then | ||
| d | ||
| else | ||
| if e then | ||
| f | ||
| else | ||
| g |
There was a problem hiding this comment.
Yeah, this looks fine for now. We can merge, but if someone else wants to put up a PR collapsing this, that would be welcome
| poShiftedIfs = pure shiftedIfs | ||
| }, | ||
| showTestCase = \(shiftedIfs, indent) -> | ||
| [ "if=" ++ renderPrinterOpt shiftedIfs, |
There was a problem hiding this comment.
Nit: shifted=?
Requires regenerating output files
|
@evanrelf I'm not sure aligning nested (chained) ifs is useful since we have |
|
Thanks for adding those example cases! Hopefully we can collapse those nested ifs in the future 😄
You're right that they both achieve the same semantic result, but syntactically they're meaningfully different. Nested ifs look like every other programming language, so they read more naturally for me. Also
|
Original issue tweag/ormolu#299
Closes #80
Based on @wldhx's work
Solved questions
1. Should we allow 2-line "if-then-break-else" layout?
My personal opinion: No. Either one line or minimum 4.
2. What do we do with multiline conditions
Unsolved questions
3. Option name
Maybe
one-level-ifsis a bad name, but that was in the original patch. I'd preferindent-then-else :: Bool.