Implement formatting logic (without comments/linebreak support)#117
Implement formatting logic (without comments/linebreak support)#117robinheghan merged 39 commits intogren-lang:mainfrom
Conversation
This reverts commit 6931d87.
…e track the newlines from the input file)
Now it's
A
-> B
-> C
-> D
instead of
A
-> B
-> C
-> D
This primarily affected `exposing` clauses, but also was sometiems visible for record expressions and possibly other places.
robinheghan
left a comment
There was a problem hiding this comment.
Overall, this looks good to me. I have a few comments, but nothing major.
| then | ||
| let deps = map Src.getImportName imports | ||
| local = Details.Local path time deps (any isMain values) lastChange buildID | ||
| local = Details.Local path time deps (any isMain (fmap snd values)) lastChange buildID |
There was a problem hiding this comment.
Any reason you're using fmap here instead of the (isMain . snd) approach you use further down?
| canonicalize pkg ifaces modul@(Src.Module _ exports docs imports values _ _ binops effects) = | ||
| canonicalize pkg ifaces modul@(Src.Module _ exports docs imports values' _ _ binops effects) = | ||
| do | ||
| let values = fmap snd values' |
There was a problem hiding this comment.
I know the prime char isn't considered bad practice in Haskell, but could we call this ordredValues or something to that effect?
There was a problem hiding this comment.
I saw you were using the prime convention elsewhere as well. I struggle a bit with reading those functions, as the name doesn't really give me a clue about what's different from the non-prime version.
Consider using full names where possible. Makes it much easier to read and understand the code.
| _aliases :: [A.Located Alias], | ||
| _values :: [(SourceOrder, A.Located Value)], | ||
| _unions :: [(SourceOrder, A.Located Union)], | ||
| _aliases :: [(SourceOrder, A.Located Alias)], |
There was a problem hiding this comment.
Would it be better to use a new type A.SourceOrderWithLocation a instead of using (SourceOrder, A.Located a)? That would get rid of having to do fmap snd everywhere, wouldn't it? 🤔
There was a problem hiding this comment.
I think the fmap snd's you're talking about are fmap'ing a List, and have type [(sourceOrder, A.Located a)] -> A.Located a, so having a A.SourceOrderWithLocation a type would still need an fmap someNewFunction with someNewFunction : A.SourceOrderWithLocation a -> A.Located a, or maybe in some places where A.toValue is used later on, we'd have someOtherNewFunction : A.SourceOrderWithLocation a -> a.
So I think the result would just change fmap snd into fmap someNewFunction, which I think does have different trade-offs, but doesn't seem strictly better to me. But if you think having some more named functions would be worth it to avoid using more tuples, then I can do that. What do you think?
There was a problem hiding this comment.
Just keep it as is.
A part of me is thinking "there's got to be a way to do this without adding fmaps all over", but I'm sure you're right in your assessment that it would include different trade offs.
The code is easy enough to understand, so just keep it.
There was a problem hiding this comment.
maybe the fmap's are a smell that the SourceOrder's are being passed down further than they need to be? I'll keep an eye out for that when I fix the other feedback.
| spaceOrIndent' :: Bool -> NonEmpty Block -> Block | ||
| spaceOrIndent' forceMultiline = Block.rowOrIndent' forceMultiline (Just Block.space) | ||
|
|
||
| {-# INLINE group #-} |
There was a problem hiding this comment.
Does this make a big difference, performance wise?
There was a problem hiding this comment.
Probably not. My estimation is there's maybe a 60% chance this does improve performance at the cost of increased binary size, since this is a large enough function that ghc might not inline it without the hint. But also the improvement is certainly going to be tiny and probably not noticeable.
There was a problem hiding this comment.
Would you mind removing the pragmas, then?
Don't mind keeping it if we know for certain that it makes an important difference in performance, but until we know that to be true I'd rather just trust the compiler to do its thing.
|
Should've mentioned: this is a real impressive piece of work 👏 |
|
Comments addressed. I think this should be working now, and should preserve the meaning of any input code when formatting. Comments will be lost, and linebreak sensitivity isn't implemented in this PR (with a preference toward splitting things onto multiple lines), so it should be enough for people to play around with while #58 gets worked on. If you have any preferences about the format itself, I'm happy to hear that here, or address it in a later PR. |
|
Just tested out this branch by formatting The code in this PR looks good to me, just want to know why certain files fail before an eventual merge. |
Yeah, that's because the parser allows different things depending on if the project is a package and if it's a special package that can have kernel features. Currently when I parse for formatting, I just tell it it's an application project. I'll make that work better in a future PR. For now, that means that modules that have infix operator definitions, and effect manager modules won't be able to parse. |
|
Thank you for explaining it. LGTM. |
This is the base formatting logic. This is one part of #42. Handling non-documentation comments and linebreak sensitivity are not part of this PR.
Run with
gren formatfrom inside a project with agren.json, or rungren format <files and/or directories...>.This is based on the learnings from elm-format, but is rewritten and greatly simplified, and also should be more performant.
TODO:
exposinglistings{ f1, f2 }(instead of{ f1 = f1, f2 = f2 }Will be done in a future PR (tracked in #42):