Previously ‘RdrName’s were sorted by simple comparison of their names.
However, in ASCII not all “operator-like” symbols go after alpha-numeric
characters. This resulted in this sort of output:
import Linear.Vector ((*^), Additive (..), (^*))
The new ordering scheme allows us to separate operators and other names:
import Linear.Vector (Additive (..), (*^), (^*))
Comments that have more than one consecutive blank line will be rendered
with just a single blank line due to how our rendering engine works. I think
that the engine work correctly in this case and we want to generally
normalize blank lines. So what should be fixed is just the check. For that
we remove extra consecutive blank lines when we construct ‘Comment’ values.
This resolves a class of idempotence issues when the equality sign happens
to be inserted between an element and its comment that follows on the same
line. I had to special-case equality sign for this, because all alternative
approaches (changing comment association logic or trying to find a more
general rule) did not work or fixed this issue yet made other things worse.
There is nothing special about the equality sign per se, but it (always?)
starts definitions which have their own ‘Located’ wrappers and it is those
spans interfere with the logic of comment association (they are detected as
AST elements between the “host” element and its comment) on subsequent
passes. This results in non-idempotent formatting.
The solution is OKish in that it fixes 99% of problems that one will
encounter in practice, but I see how an input can be crafted to show that
there is still an issue with idempotence.
Ormolu can be turned on and off via the special comments:
{- ORMOLU_DISABLE -}
and
{- ORMOLU_ENABLE -}
This allows us to disable formatting selectively for code between these
markers or disable it for the entire file. To achieve the latter, just put
{- ORMOLU_DISABLE -} at the very top. Note that the source code should still
be parseable even without the “excluded” part. Because of that the magic
comments cannot be placed arbitrary, but should rather enclose independent
top-level definitions.
It is tempting to enable ‘ImportQualifiedPost’ automatically for all files,
but in that case we won't be able to tell when to format in the
classical (prefix) style vs the new postfix style. Checking the list of
pragmas on per-file basis is not good enough because the extension can be
enabled in other ways, such as via the --ghc-opt flag.
GHC 8.10.1 comes with some changes to the AST, which works great for
Ormolu, but causes this commit to be a bit large:
* Trees That Grow extension points for new constructors are now statically
proven to be uninhabited, via noExtCon :: NoExtCon -> a. Thanks to this
change I got rid of many notImplemented calls.
* LPat constructor is now a lot more usable, so we don't need to use
the locatedPat combinator and can remove some boilerplate code.
Also it comes with ImportQualifiedPost and StandaloneKindSignatures
we should support. I did not implement them in this commit, they'll
be merged in later on.
It causes one behaviour change, where the ordering of qualified and
non-qualified imports of the same module is changed. This is due to
our usage of gcompare resulting a different ordering because of the
AST change caused by the ImportQualifiedPost extension. I think this is
acceptable and we shouldn't try to keep backwards compatibility there.
Another behaviour change is that previously HsExpr had a few extra
constructors for arrows and patterns used in expression context. Those
programs were syntactically incorrect, but refused on a later stage. But
we nonetheless formatted those constructs so Ormolu didn't fail there
while keeping the source code intact. However, now those constructors
are removed, so Ormolu fails with a parse error in this case (same as
GHC). I also removed some tests exhibiting this behaviour.