Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable line breaks after infix operators and in ADT declarations #280

Open
1 task done
jonathanknowles opened this issue Dec 21, 2022 · 3 comments
Open
1 task done

Comments

@jonathanknowles
Copy link

jonathanknowles commented Dec 21, 2022

Is your feature request specific to Fourmolu?

  • Yes, this feature is not relevant to Ormolu (since Ormolu admits no configuration)

Context

  • We have a rather large code base (> 200,000 lines) that's formatted by hand, and we're interested in adopting fourmolu.
  • Our team coding standard encourages an 80-column limit. (We realise there's currently no option in fourmolu to do this (until Line length limit in fourmolu #71 is solved), but we're wondering if we can get by with a combination of the existing fourmolu and manual insertion of line breaks where necessary.)

Specific issue

We have sections of code that are formatted roughly like this:

test :: Spec
test =
  describe "An example specification" $
    it "An example test case" $
      [ someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      ]
        `shouldWithSomeHandwavingBeVeryApproximatelyEqualTo`
          [ someRatherLongExpressionToServeAsAnIlluminatingExample
          , someRatherLongExpressionToServeAsAnIlluminatingExample
          , someRatherLongExpressionToServeAsAnIlluminatingExample
          , someRatherLongExpressionToServeAsAnIlluminatingExample
          ]

Note that after the infix function:

  • we've inserted a line break.
  • we've used one level of indentation for the expression on the RHS.

If we run this through fourmolu, the RHS is converted from a "newline + indent" to a "hanging indent":

test :: Spec
test =
  describe "An example specification" $
    it "An example test case" $
      [ someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      , someRatherLongExpressionToServeAsAnIlluminatingExample
      ]
        `shouldWithSomeHandwavingBeVeryApproximatelyEqualTo` [ someRatherLongExpressionToServeAsAnIlluminatingExample
                                                             , someRatherLongExpressionToServeAsAnIlluminatingExample
                                                             , someRatherLongExpressionToServeAsAnIlluminatingExample
                                                             , someRatherLongExpressionToServeAsAnIlluminatingExample
                                                             ]

Obviously this is a rather extreme example 😄, but it does capture a general pattern.

Question 1

Is there some existing way to configure fourmolu so that it:

  • preserves newline characters, and arranges that subsequent lines are indented by one level; and/or
  • doesn't attempt to perform vertical alignment of multi-line expressions, except where it would be a part of normal indentation?

Question 2

If the above type of configuration is currently impossible, then in your opinion, would it be extremely difficult for us to create a configuration option that makes the above behaviour possible?

I'd be very happy to submit a PR, but I'm not entirely sure where in the fourmolu code base I should be looking.

I did notice the following type:

-- | Expression placement. This marks the places where expressions that
-- implement handing forms may use them.
data Placement
  = -- | Multi-line layout should cause
    -- insertion of a newline and indentation
    -- bump
    Normal
  | -- | Expressions that have hanging form
    -- should use it and avoid bumping one level
    -- of indentation
    Hanging

I've tried adjusting the code to use Normal instead of Hanging in certain places, but I'm not sure if it's relevant to the problem we're trying to solve, as it doesn't appear to affect the pretty-printing of the above source code snippet.

Many thanks for your help!
Jonathan

@jonathanknowles jonathanknowles changed the title More control over line breaking, indentation, and alignment. Control over line breaks, indentation, and alignment. Dec 21, 2022
@brandonchinn178
Copy link
Collaborator

So your specific example above is not specific to Fourmolu. Ormolu also hangs lists after infix operators + function calls, when it would be nicer (at least in this example, perhaps for all multiline RHS?) to put it on the next line. I think this is a worthwhile change that doesn't involve line length at all. Can you make an issue over at Ormolu and see what they think?

To your general point, yes, fourmolu has the respectful option, which we're aiming to respect manual line breaks more than Ormolu does. If Ormolu doesn't want to make this change, I think it's perfectly reasonable for respectful=true to respect newlines after infix function calls. Do you have other examples in mind?

@jonathanknowles
Copy link
Author

jonathanknowles commented Dec 22, 2022

Hi @brandonchinn178!

I think this is a worthwhile change that doesn't involve line length at all. Can you make an issue over at Ormolu and see what they think?

Done! I've raised the following issue:

Do you have other examples in mind?

This also appears to happen in data type declarations. I've created another issue here:

I think it's perfectly reasonable for respectful=true to respect newlines after infix function calls.

I think that sounds very reasonable. Though perhaps it's worth making this option more fine-grained? One of the things that seems useful about respectful=false is that it can coalesce all imports into a single contiguous section that's ordered alphabetically, if for some reason imports are separated by blank lines. Perhaps it'd be nice to be able to control this behaviour separately from the treatment of line breaks elsewhere?

@brandonchinn178
Copy link
Collaborator

brandonchinn178 commented Dec 23, 2022

RE: respectful, I think what you're saying makes sense. I've broken off a separate issue for that: #281. I'll leave this issue open to track those two specific issues you've opened on ormolu (and to implement them if ormolu rejects them)

@brandonchinn178 brandonchinn178 changed the title Control over line breaks, indentation, and alignment. Enable line breaks after infix operators and in ADT declarations Dec 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants