Code Monkey home page Code Monkey logo

ormolu's Introduction

Ormolu

License BSD3 Hackage Stackage Nightly Stackage LTS CI

Ormolu is a formatter for Haskell source code. The project was created with the following goals in mind:

  • Using GHC's own parser to avoid parsing problems caused by haskell-src-exts.
  • Let some whitespace be programmable. The layout of the input influences the layout choices in the output. This means that the choices between single-line/multi-line layouts in certain situations are made by the user, not by an algorithm. This makes the implementation simpler and leaves some control to the user while still guaranteeing that the formatted code is stylistically consistent.
  • Writing code in such a way so it's easy to modify and maintain.
  • Implementing one “true” formatting style which admits no configuration.
  • The formatting style aims to result in minimal diffs.
  • Choose a style compatible with modern dialects of Haskell. As new Haskell extensions enter broad use, we may change the style to accommodate them.
  • Idempotence: formatting already formatted code doesn't change it.
  • Be well-tested and robust so that the formatter can be used in large projects.

Try it out in your browser at https://ormolu-live.tweag.io! See Ormolu Live for more info.

Installation

The release page has binaries for Linux, macOS and Windows.

You can also install using cabal or stack:

$ cabal install ormolu
$ stack install ormolu

Ormolu is also included in several package repositories. E.g., on Arch Linux, one can use the package on AUR:

$ yay -S ormolu

Building from source

The easiest way to build the project is with Nix:

$ nix build

Make sure to accept the offered Nix caches (in particular the IOG cache), otherwise building may take a very long time.

Alternatively, stack could be used as follows:

$ stack build # to build
$ stack install # to install

To use Ormolu directly from GitHub with Nix flakes, this snippet may come in handy:

{
  inputs.ormolu.url = "github:tweag/ormolu";
  outputs = { ormolu, ... }: {
    # use ormolu.packages.${system}.default here
  };
}

Usage

The following will print the formatted output to the standard output.

$ ormolu Module.hs

Add --mode inplace to replace the contents of the input file with the formatted output.

$ ormolu --mode inplace Module.hs

Use find to format a tree recursively:

$ ormolu --mode inplace $(find . -name '*.hs')

Or find all files in a project with git ls-files:

$ ormolu --mode inplace $(git ls-files '*.hs')

To check if files are are already formatted (useful on CI):

$ ormolu --mode check $(find . -name '*.hs')

⚡ Beware git's core.autocrlf on Windows ⚡

Ormolu's output always uses LF line endings. In particular, ormolu --mode check will fail if its input is correctly formatted except that it has CRLF line endings. This situation can happen on Windows when checking out a git repository without having set core.autocrlf to false.

Ormolu Live

On every new commit to master, Ormolu Live is deployed to https://ormolu-live.tweag.io. Older versions are available at https://COMMITHASH--ormolu-live.netlify.app.

Editor integration

We know of the following editor integrations:

Haskell Language Server

Haskell Language Server has built-in support for using Ormolu as a formatter.

GitHub actions

run-ormolu is the recommended way to ensure that a project is formatted with Ormolu.

Language extensions, dependencies, and fixities

Ormolu automatically locates the Cabal file that corresponds to a given source code file. Cabal files are used to extract both default extensions and dependencies. Default extensions directly affect behavior of the GHC parser, while dependencies are used to figure out fixities of operators that appear in the source code. Fixities can also be overridden via an .ormolu file which should be located at a higher level in the file system hierarchy than the source file that is being formatted. When the input comes from stdin, one can pass --stdin-input-file which will give Ormolu the location that should be used as the starting point for searching for .cabal and .ormolu files.

Here is an example of .ormolu file:

infixr 9  .
infixr 5  ++
infixl 4  <$
infixl 1  >>, >>=
infixr 1  =<<
infixr 0  $, $!
infixl 4 <*>, <*, *>, <**>

It uses exactly the same syntax as usual Haskell fixity declarations to make it easier for Haskellers to edit and maintain.

As of Ormolu 0.7.0.0, .ormolu files can also contain instructions about module re-exports that Ormolu should be aware of. This might be desirable because at the moment Ormolu cannot know about all possible module re-exports in the ecosystem and only few of them are actually important when it comes to fixity deduction. In 99% of cases the user won't have to do anything, especially since most common re-exports are already programmed into Ormolu. (You are welcome to open PRs to make Ormolu aware of more re-exports by default.) However, when the fixity of an operator is not inferred correctly, making Ormolu aware of a re-export may come in handy. Here is an example:

module Control.Lens exports Control.Lens.At
module Control.Lens exports "lens" Control.Lens.Lens

Module re-export declarations can be mixed freely with fixity overrides, as long as each declaration is on its own line. As of Ormolu 0.7.1.0 explicit package names are allowed in re-export declarations (see the example above).

Finally, all of the above-mentioned parameters can be controlled from the command line:

  • Language extensions can be specified with the -o or --ghc-opt flag.
  • Dependencies can be specified with the -p or --package flag.
  • Fixities can be specified with the -f or --fixity flag.
  • Re-exports can be specified with the -r or --reexport flag.

Searching for .cabal and .ormolu files can be disabled by passing --no-cabal and --no-dot-ormolu respectively.

Magic comments

Ormolu understands two magic 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 for Ormolu to work the fragments where Ormolu is enabled must be parseable on their own. Because of that the magic comments cannot be placed arbitrarily, but rather must enclose independent top-level definitions.

Regions

One can ask Ormolu to format a region of input and leave the rest unformatted. This is accomplished by passing the --start-line and --end-line command line options. --start-line defaults to the beginning of the file, while --end-line defaults to the end.

Note that the selected region needs to be parseable Haskell code on its own.

Exit codes

Exit code Meaning
0 Success
1 General problem
2 CPP used (deprecated)
3 Parsing of original input failed
4 Parsing of formatted code failed
5 AST of original and formatted code differs
6 Formatting is not idempotent
7 Unrecognized GHC options
8 Cabal file parsing failed
9 Missing input file path when using stdin input and accounting for .cabal files
10 Parse error while parsing fixity overrides
100 In checking mode: unformatted files
101 Inplace mode does not work with stdin
102 Other issue (with multiple input files)

Using as a library

The ormolu package can also be depended upon from other Haskell programs. For these purposes only the top Ormolu module should be considered stable. It follows PVP starting from the version 0.5.3.0. Rely on other modules at your own risk.

Troubleshooting

Operators are being formatted weirdly!

This can happen when Ormolu doesn't know or can't determine the fixity of an operator.

  • If this is a custom operator, see the instructions in the Language extensions, dependencies, and fixities section to specify the correct fixities in a .ormolu file.

  • If this is a third-party operator (e.g. from base or some other package from Hackage), Ormolu probably doesn't recognize that the operator is the same as the third-party one.

    Some reasons this might be the case:

    • You might have a custom Prelude that re-exports things from Prelude
    • You might have -XNoImplicitPrelude turned on

    If any of these are true, make sure to specify the reexports correctly in a .ormolu file.

You can see how Ormolu decides the fixity of operators if you use --debug.

Limitations

  • CPP support is experimental. CPP is virtually impossible to handle correctly, so we process them as a sort of unchangeable snippets. This works only in simple cases when CPP conditionals surround top-level declarations. See the CPP section in the design notes for a discussion of the dangers.

Running on Hackage

It's possible to try Ormolu on arbitrary packages from Hackage. For that execute (from the root of the cloned repo):

$ nix build .#hackage.<package>

Then inspect result/log.txt for possible problems. The derivation will also contain formatted .hs files for inspection and original inputs with .hs-original extension (those are with CPP dropped, exactly what is fed into Ormolu).

Forks and modifications

We know of the following actively maintained forks:

  • Fourmolu, which uses 4-space indentation and allows arbitrary configuration.

Contributing

See CONTRIBUTING.md.

License

See LICENSE.md.

Copyright © 2018–present Tweag I/O

ormolu's People

Contributors

amesgen avatar avi-d-coder avatar basile-henry avatar brandonchinn178 avatar cblp avatar dependabot[bot] avatar ethercrow avatar expipiplus1 avatar facundominguez avatar felixonmars avatar fisx avatar gabrielelana avatar int-index avatar matthew-healy avatar mboes avatar mitchellwrosen avatar mrkkrp avatar panaeon avatar robx avatar ruhatch avatar sol avatar sphaso avatar srid avatar tbagrel1 avatar teofr avatar tomjaguarpaw avatar utdemir avatar waddlaw avatar yumiova avatar zimbatm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ormolu's Issues

Implement CLI

Now that we have something that works, albeit it only can handle module headers and export lists, it would be good to have proper CLI application so we can mess with the formater and check how it works on various files.

Improve handling of newline before functions bodies

Currently we use a "normal" breakpoint between equal sign = of function definitions and body functions. This means that there will be a space if the body is a one-liner and a newline otherwise. This leads to this sort of transformation:

-- input:
instance Stream BL.ByteString where
  reachOffset o pst =
    reachOffset' splitAtBL BL.foldl' BL8.unpack (chr . fromIntegral) (10, 9) o pst

-- output:
instance Stream BL.ByteString where
  reachOffset o pst = reachOffset' splitAtBL BL.foldl' BL8.unpack (chr . fromIntegral) (10, 9) o pst

Which is obviously wrong because now the line is too long. We should start respecting those newlines.

N.B. I guess this is applicable to any match groups, not only function definitions.

Support EmptyDataDecls

Currently

data Foo

fails with

Parsing of formatted code failed:
<rendered>:7:1
parse error (possibly incorrect indentation or mismatched brackets)                
Please, consider reporting the bug.

Extra layer of parentheses around operator sections

Input source file:

elP = EvalRelation <$> elP'
    where elP' = (,) <$> token termP
                     <*  symbol "->"
                     <*> token termP

gets this error.

λ ormolu A.hs
AST of input and AST of formatted code differ.
Please, consider reporting the bug.

The result of running with the unsafe option is:.

λ ormolu A.hs --unsafe
elP = EvalRelation <$> elP'
  where
    elP' =
      ((,)) <$> token termP <*
        symbol "->" <*>
        token termP

Do not try to support CPP

Here's a reason not to implement support for CPP. Let's say our solution blindly comments all CPP directives, it may produce an output that is invalid Haskell when the CPP directives are uncommented after formatting.

Input:

f = f1
  where
        f1 = g
#if C1
g = g1
  where
    g1 = g2
      where
        g2 = False
#else
        g = True
#end

After commenting CPP directives:

f = f1
  where
        f1 = g
{-[#if C1]-}
g = g1
  where
    g1 = g2
      where
        g2 = False
{-[#else]-}
        g = True
{-[#end]-}

After formatting:

f = f1
  where
    f1 = g
{-[#if C1]-}
g = g1
  where
    g1 = g2
      where
        g2 = False
{-[#else]-}
        g = True
{-[#end]-}

Output after uncommenting CPP directives:

f = f1
  where
    f1 = g
#if C1
g = g1
  where
    g1 = g2
      where
        g2 = False
#else
        g = True
#end

Now the definition of f is broken when C1 doesn't hold.

This is a problem if the formatter is applied automatically on changed files. It can be fixed by constraining where CPP directives are allowed to appear. Thus the formatter prescribes valid uses of CPP, and we probably are better off by internalizing conditional compilation as a plugin or with TH as we have been discussing elsewhere.

The alternative is to tell our users that the formatter shouldn't be applied automatically. I think I'd prefer not to do this. We should aim for the formatter to either fail with an error or produce an output equivalent to the input.

I suspect that it is possible to come up with examples where we produce valid Haskell syntax while changing the meaning of the code.

Cover entire GHC AST

This is the umbrella issue about covering various syntactic constructions in GHC AST. It should be closed when we cover everything.

  • HsModule Top level module thing
    • Module header and export list
    • Imports
    • Declarations
    • Deprecation messages
  • TyClD Type or class declaration
    • FamDecl Type family declaration
    • SynDecl Type declaration
    • DataDecl Data type declaration
    • ClassDecl Type class declaration
  • InstD Instance declaration
  • DerivD Deriving declaration
  • ValD Values (binds)
  • SigD Signature declaration
    • TypeSig Type signatures
    • PatSynSig Pattern-synonym signatures
    • ClassOpSig Signatures for class methods
    • FixSig Fixity declaration
    • InlineSig Inline pragmas
    • SpecSig Specialization pragmas
    • SpecInstSig A specialisation pragmas for instance declarations
    • MinimalSig Minimal complete definition pragmas
    • SCCFunSig A "set cost centre" pragmas for declarations
    • CompleteMatchSig A complete match pragma
  • DefD Default declaration
  • ForD Foreign declaration
    • ForeignImport
    • ForeignExport
  • WarningD Warning declaration
  • AnnD Annotation declaration
  • RuleD Rule declaration
  • SpliceD Splice declaration (Includes quasi-quotes)
  • RoleAnnotD Role annotation declaration

The overview is written according to ghc-8.6.4.

More precise handling of newlines between methods inside of a type class declaration

Type classes were implemented in #79, but there is still an issue related to white space between methods and data/type family declarations inside of class declarations. I think those should be grouped similar to what we do for top-level functions:

class Foo a where
  
  -- | Here goes the foo.
  foo : a -> String
  foo = default_implementation

  -- | Here goes bar.
  bar :: a -> String
  bar = ...

Note the newline between foo and bar and lack of thereof between e.g. type signature of foo and its default implementation.

Right now I think everything is joined without newlines, as one block of text.

Improve the way operators are chained

There are few issues with current formatting of chains of operators. First of all, I think we should revert to this style with operator on the second line before the second argument:

foo
  + bar

and in a sequence it should be:

foo
  + bar
  + baz

This will allow us to have reasonable rendering of e.g. applicative notation.

do-notation should be treated specially:

foo
  + bar
  + baz $ do

Also, things applied before do with ($) should still make the whole construction hanging:

foo = foo $ do
  ...

vs current:

foo =
  foo $ do
    ...

The change described in #94 should then still allow users to have a newline before such constructions involving do.

Error on functions with a single guard

Given input:

baz x | x == 5 = 10

Ormolu produces (with --unsafe flag):

baz x = | x == 5 = 10

Which is not valid Haskell.


I might be wrong, but the issue might be about this conditional, since it works fine if there are multiple guards:

unless (length grhssGRHSs > 1) $ do
case style of
Function _ -> txt " ="
PatternBind -> txt " ="
Case -> unless hasGuards (txt " ->")
_ -> txt " ->"

Running test suite is becoming rather slow

We have not thought about performance so far, yet test suite is rather slow to run as the number of examples grows. Even now it takes about 1 minute on my machine. I wonder if we could spend some time to identify the bottleneck and try to fix it. It would be good if there were an easy win that could give us, say, 2x faster execution.

Make embeddable within other GHC API-based tools

Given that parsing (and formatting) Haskell code tends to need information that is not lexically available, such as the fixity of operators imported from libraries or extensions enabled in cabal files or elsewhere, my natural inclination to have robust formatting would be to put it into intero, or ghc-mod, or haskell-ide-engine or whatever is the backend-du-jour.

Naturally, ormolu stands as a stand-alone tool like hindent and that's fine, but if it exposes a relatively simple Haskell API, then it can be imported by e.g. intero; Intero would parse the module itself using the proper flags and context, and it would pass the parsed AST over to ormolu.

Obviously, to be robust on "Real" code, you need to handle CPP to some degree like hindent tries to do. A user would want Ormolu to reformat with CPP taken into account, which is hard to do completely but doable in part. So that would be something to think about.

I've seen that the ormolu function already accepts a Located (HsModule GhcPs) via parseModule', so it might just be a case of exposing the right fragment of the ormolu function.

If you're in agreement with this as a side-goal, I could pretty easily take a crack at importing ormolu from intero with a :format command (or so) and then open a PR here for scrutiny of what'd be necessary to expose in ormolu for an IDE backend to use it. You guys wouldn't necessarily have to do any work in the end.

One soft-requirement for intero is light dependency footprint (so that it's easy and fairly fast to install for users), and ormolu's dependencies are quite light at the moment, which is good. yaml does bring a big footprint, but maybe that could be disabled with a flag. 🤔 Not sure. Technically people only need to install intero per GHC version nowadays, so it's less of a requirement. But I already regret adding the network dependency to intero, so... That's a delicate topic.

Error with source files containing Unicode

When I try to feed source files with Unicode symbols I get the infamous "invalid byte sequence" errors. Investigate what causes this and fix it.

Low-priority for now.

Type operator output with fully qualified name

Here is the diff from a type signature that replaced the ~ operator with its fully qualified version:

 mapBuilderJson
-  :: (Exts.IsList t, Exts.Item t ~ (k, v), Buildable k, Buildable v)
+  :: (Exts.IsList t, Exts.Item t Data.Type.Equality.~ (k, v), Buildable k, Buildable v)
   => t
   -> Builder

Type families with infix left-hand equations are reformatted in prefix style

type family (x :: N) + (y :: N) :: N where
  'Zero + y = y
  'Succ n + y = 'Succ (n + y)

type family (x :: N) `LEQ` (y :: N) :: Bool where
  'Zero `LEQ` y = 'True
  'Succ n `LEQ` 'Zero = 'False
  'Succ n `LEQ` 'Succ m = n `LEQ` m

is reformatted as

type family (+) (x :: N) (y :: N) :: N where
  (+) 'Zero y = y
  (+) 'Succ n y = 'Succ (n + y)

type family LEQ (x :: N) (y :: N) :: Bool where
  LEQ 'Zero y = 'True
  LEQ 'Succ n 'Zero = 'False
  LEQ 'Succ n 'Succ m = n LEQ m

I think the original style should be preserved in this case.

Review and expand the set of examples

There is a list of examples that I've contributed here. This will work as a specification for the implementer and as an evaluation tool when discussing which approach to use for writing the code.

This task is about expanding the list until it looks comprehensive enough to convey our expectations on the formatting output. Probably, we don't need to show every possible Haskell construct.

Build failure when building with Nix

Faced with problem when tried to build

nix-build --show-trace
copying path '/nix/store/aqr5jws27ira7mmi884r4n72yd2iqc34-happy-1.19.9' from 'https://cache.nixos.org'...
building '/nix/store/q325bf3n7y33nszmsn9pgcnxbp3dpava-python3-3.7.3.drv'...
unpacking sources
unpacking source archive /nix/store/r5ik1sbafp0kr3a7prnaca1lmmqmb0b6-Python-3.7.3.tar.xz
source root is Python-3.7.3
setting SOURCE_DATE_EPOCH to timestamp 1553547549 of file Python-3.7.3/Misc/NEWS
patching sources
applying patch /nix/store/gd8ycd6j3qhcq1qccaqz68zdq5l4c2wf-no-ldconfig.patch
patching file Lib/ctypes/util.py
applying patch /nix/store/ggk7z3dvydrkypxff7d7c6v1slvbriin-darwin-libutil.patch
patching file Modules/posixmodule.c
Hunk #1 succeeded at 5907 (offset 27 lines).
applying patch /nix/store/9kwzs3pplms8sijf55sdryypzvic4x1s-python-3.x-distutils-C++.patch
patching file Lib/_osx_support.py
patching file Lib/distutils/cygwinccompiler.py
patching file Lib/distutils/sysconfig.py
Hunk #1 succeeded at 183 (offset 13 lines).
Hunk #2 succeeded at 202 (offset 13 lines).
Hunk #3 succeeded at 231 (offset 13 lines).
patching file Lib/distutils/unixccompiler.py
Hunk #3 FAILED at 183.
1 out of 3 hunks FAILED -- saving rejects to file Lib/distutils/unixccompiler.py.rej
patching file Makefile.pre.in
Hunk #1 succeeded at 615 (offset 31 lines).
builder for '/nix/store/q325bf3n7y33nszmsn9pgcnxbp3dpava-python3-3.7.3.drv' failed with exit code 1
cannot build derivation '/nix/store/26rq8gd6dh6vzn9rzz56ljqdr1sq90wi-ghc-8.6.4.drv': 1 dependencies couldn't be built
cannot build derivation '/nix/store/6sxn76pka3xr30c1wg49q01qpkzy9jdh-cabal2nix-2.14.2.drv': 1 dependencies couldn't be built
cannot build derivation '/nix/store/49l07msc317w2gijh3mns7jr2wamaxii-cabal2nix-ormolu.drv': 1 dependencies couldn't be built
error: while evaluating the attribute 'ormolu' at ~/tmp/ormolu/default.nix:14:13:
while evaluating 'callCabal2nix' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:206:32, called from /Users/pachin/tmp/ormolu/default.nix:14:24:
while evaluating 'callCabal2nixWithOptions' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:191:66, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:206:38:
while evaluating 'overrideCabal' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/lib.nix:37:24, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:202:10:
while evaluating 'callPackageKeepDeriver' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:156:33, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:202:25:
while evaluating 'overrideCabal' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/lib.nix:37:24, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:157:5:
while evaluating 'callPackage' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:114:22, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:157:20:
while evaluating 'callPackageWithScope' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:74:37, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:114:28:
while evaluating 'makeOverridable' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/lib/customisation.nix:67:24, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:97:8:
while evaluating 'drvScope' at /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/pkgs/development/haskell-modules/make-package-set.nix:87:18, called from /nix/store/72vjs9dvrj8nzsavl0i6j3n6dv62i8jl-source/lib/customisation.nix:69:12:
build of '/nix/store/49l07msc317w2gijh3mns7jr2wamaxii-cabal2nix-ormolu.drv' failed

Improve handling of comments for imports

In #26 I had to drop all comments in import sections because comments for imports are associated incorrectly by ghc-exactprint and if we do sorting we get a total mess in the end and self-checking fails. There is no easy way around this except for dropping those comments right after parsing.

Leaving it here so we won't forget about this eventually.

Implement documentation and warning declarations

Extend p_hsDecl in Ormolu.Printer.Meat.Declaration in order to support WarningD and DocD declarations. I'm not sure what is the role of DocD and if it's at all necessary given that we render all comments automatically. Perhaps it could be skipped.

Implement detection of differences in ASTs disregarding span information

In order to understand if our tool preserves meaning of the code, we should check if it produces the same AST. Since span position will likely change because of re-formatting, we should ignore these differences though. We also should compare (some parts of) annotations because they contain information about comments, which we don't want to accidentally erase either. Comments may be a bit trickier because they verbatim contain indentation in them and indentation can easily change.

Replace SYB by Uniplate

It appears that Uniplate is a more modern, better maintained library compared to SYB. Let’s try to switch to Uniplate.

Things we may want to do below alignment/indentations

This is a bit of a hodgepodge issue, lest I forget.

  • Splitting the {-# LANGUAGE … #-} pragmas with list of extension into pragmas with a single extension
  • Sorting {-# LANGUAGE … #-} pragmas
  • Sorting import
    • Sorting import lists?

The rational is that none of these have structure, it's only to minimize diffs. And let tools insert these without fear.

Possibly, but maybe not a good idea, we could also change C => D => t into (C, D) => t. Again, it's for tools (such as Dante) which can insert constraints, but would have a hard time being too precise about layout.

Add missing CONTRIBUTING.md file

Hi,

I want to propose the idea of having CONTRIBUTING.md file so that developers (especially new Haskell developers) can get useful guide on how to build, run and test the project on their local machines.

Helpful pointers that could be added:

  • Whether to use cabal or stack?
  • If cabal, then do we use v1 commands or are cabal new-* commands recommended ?
  • If stack, then what should be the value of resolver?
  • How to run a particular test instead of all tests?
  • How to debug, make changes and see the changes in action?

I understand some of these things are quite basic and to some extent repetitive but adding these pointers are one way of learning about Haskell projects for beginners. This can definitely bring more developers to trying out new projects and bring obscure bugs to project maintainers notice.

I am currently facing problems in trying to setup the project locally and hence I have raised this issue. I tried to build the project using cabal new-build which produced the following error:

$ cabal new-build
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: ormolu-0.0.1.0 (user goal)
[__1] trying: gitrev-1.3.1 (dependency of ormolu)
[__2] next goal: template-haskell (dependency of gitrev)
[__2] rejecting: template-haskell-2.11.1.0/installed-2.1... (conflict: ormolu
=> ghc-boot-th>=8.4.3, template-haskell =>
ghc-boot-th==8.0.2/installed-8.0...)
[__2] rejecting: template-haskell-2.14.0.0, template-haskell-2.13.0.0,
template-haskell-2.12.0.0, template-haskell-2.11.1.0,
template-haskell-2.11.0.0, template-haskell-2.10.0.0,
template-haskell-2.9.0.0, template-haskell-2.8.0.0, template-haskell-2.7.0.0,
template-haskell-2.6.0.0, template-haskell-2.5.0.0, template-haskell-2.4.0.1,
template-haskell-2.4.0.0, template-haskell-2.3.0.1, template-haskell-2.3.0.0,
template-haskell-2.2.0.0 (constraint from non-upgradeable package requires
installed instance)
[__2] fail (backjumping, conflict set: gitrev, ormolu, template-haskell)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: template-haskell, ormolu, gitrev

I tried to remove ~./ghc and ~/.cabal directories, guessing that it could be due to conflict in the dependencies and re-ran cabal new-build but it failed.

Later I created stack.yaml and included resolver: lts-13.19 but ran into different errors this time. Here's the short snippet of error-log (complete log is in this link) :

$ stack build
Building all executables for `ormolu' once. After a successful build of all of them, only specified executables will be rebuilt.
ormolu-0.0.1.0: build (lib + exe)
Preprocessing library for ormolu-0.0.1.0..
Building library for ormolu-0.0.1.0..
[11 of 24] Compiling Ormolu.Printer.Meat.Type ( src/Ormolu/Printer/Meat/Type.hs, .stack-work/dist/x86_64-linux/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Type.o )

/home/abiduzair/Documents/uzair/ormolu/src/Ormolu/Printer/Meat/Type.hs:36:3: error:
    Not in scope: data constructor ‘HsAppsTy’
    Perhaps you meant ‘HsAppTy’ (imported from GHC)
   |
36 |   HsAppsTy apps ->
   |   ^^^^^^^^

/home/abiduzair/Documents/uzair/ormolu/src/Ormolu/Printer/Meat/Type.hs:53:3: error:
    Not in scope: data constructor ‘HsPArrTy’
    Perhaps you meant ‘HsParTy’ (imported from GHC)
   |
53 |   HsPArrTy t -> located t (bracketsPar . p_hsType)
   |   ^^^^^^^^
...
...
...
--  While building package ormolu-0.0.1.0 using:
      /home/abiduzair/.stack/setup-exe-cache/x86_64-linux/Cabal-simple_mPHDZzAJ_2.4.0.1_ghc-8.6.4 --builddir=.stack-work/dist/x86_64-linux/Cabal-2.4.0.1 build lib:ormolu exe:ormolu --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

I would like to know what are your thoughts on adding CONTRIBUTING.md file and how I could resolve the project setup issues mentioned above. I am happy to raise a PR for the same, if I can successfully run the project locally. Thank you :)

Multiple functions in single signature results in odd behaviour

I have two cases here, the first a bit less contrived than the second:

fst, snd :: Bool
(fst, snd) = (True, False)

fst, snd :: Bool
fst = True
snd = False

which gives:

fst, snd :: Bool

(fst, snd) = (True, False)

fst, snd :: Bool
fst = True

snd = False

The first should definitely not have the newline. The second is clearly more tricky, but I actually think we should keep them together if that's how people choose to write it.

Add instructions how to hack on the project

We need to add some instructions for hacking on the project. Just today I had to explain this verbally to the intern that arrived to the office and it'll be also useful when we bring the project to ZuriHac.

Support full variety of patterns

Extend Ormolu.Printer.Meat.Declaration.Pat to support fully variety of patterns. It's possible that some of the pattern types that I implemented lack tests, this also should be rectified.

case statement in do leads to parse error

The input,

module Main where

main :: IO ()
main = do
  case True of
    False -> pure ()
    True -> pure ()
  pure ()

, leads to the output,

module Main where

main :: IO ()
main = do
  case True of
  False -> pure (())
  True -> pure (())
  pure (())

, which has two issues:

  1. It doesn't parse, because the pattern matches have been lined up with case
  2. It seems to have added extra () for some reason 🤷‍♂️

Build failure when building with stack

Tried build with stack

stack build
mono-traversable-1.0.11.0: download
mono-traversable-1.0.11.0: configure
mono-traversable-1.0.11.0: build
mono-traversable-1.0.11.0: copy/register
conduit-1.3.1.1: download    
conduit-1.3.1.1: configure   
conduit-1.3.1.1: build       
conduit-1.3.1.1: copy/register
libyaml-0.1.1.0: download    
libyaml-0.1.1.0: configure   
libyaml-0.1.1.0: build       
libyaml-0.1.1.0: copy/register
yaml-0.11.0.0: download      
yaml-0.11.0.0: configure   
yaml-0.11.0.0: build       
yaml-0.11.0.0: copy/register
Building all executables for `ormolu' once. After a successful build of all of them, only specified executables will be rebuilt.
ormolu-0.0.1.0: configure (lib + exe)
Configuring ormolu-0.0.1.0...
ormolu-0.0.1.0: build (lib + exe)
Preprocessing library for ormolu-0.0.1.0..
Building library for ormolu-0.0.1.0..
[ 1 of 24] Compiling Ormolu.CommentStream ( src/Ormolu/CommentStream.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/CommentStream.o )
[ 2 of 24] Compiling Ormolu.Config    ( src/Ormolu/Config.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Config.o )
[ 3 of 24] Compiling Ormolu.Exception ( src/Ormolu/Exception.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Exception.o )
[ 4 of 24] Compiling Ormolu.Parser    ( src/Ormolu/Parser.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Parser.o )
[ 5 of 24] Compiling Ormolu.SpanStream ( src/Ormolu/SpanStream.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/SpanStream.o )
[ 6 of 24] Compiling Ormolu.Printer.Internal ( src/Ormolu/Printer/Internal.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Internal.o )
[ 7 of 24] Compiling Ormolu.Utils     ( src/Ormolu/Utils.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Utils.o )
[ 8 of 24] Compiling Ormolu.Printer.Comments ( src/Ormolu/Printer/Comments.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Comments.o )
[ 9 of 24] Compiling Ormolu.Printer.Combinators ( src/Ormolu/Printer/Combinators.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Combinators.o )
[10 of 24] Compiling Ormolu.Printer.Meat.Common ( src/Ormolu/Printer/Meat/Common.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Common.o )
[11 of 24] Compiling Ormolu.Printer.Meat.Type ( src/Ormolu/Printer/Meat/Type.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Type.o )
[12 of 24] Compiling Ormolu.Printer.Meat.ImportExport ( src/Ormolu/Printer/Meat/ImportExport.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/ImportExport.o )
[13 of 24] Compiling Ormolu.Printer.Meat.Declaration.TypeFamily ( src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/TypeFamily.o )
[14 of 24] Compiling Ormolu.Printer.Meat.Declaration.Type ( src/Ormolu/Printer/Meat/Declaration/Type.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/Type.o )
[15 of 24] Compiling Ormolu.Printer.Meat.Declaration.Signature ( src/Ormolu/Printer/Meat/Declaration/Signature.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/Signature.o )
[16 of 24] Compiling Ormolu.Printer.Meat.Declaration.Pat ( src/Ormolu/Printer/Meat/Declaration/Pat.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/Pat.o )
[17 of 24] Compiling Ormolu.Printer.Meat.Declaration.Value ( src/Ormolu/Printer/Meat/Declaration/Value.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/Value.o )
[18 of 24] Compiling Ormolu.Printer.Meat.Declaration.Data ( src/Ormolu/Printer/Meat/Declaration/Data.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration/Data.o )
[19 of 24] Compiling Ormolu.Printer.Meat.Declaration ( src/Ormolu/Printer/Meat/Declaration.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Declaration.o )
[20 of 24] Compiling Ormolu.Imports   ( src/Ormolu/Imports.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Imports.o )
[21 of 24] Compiling Ormolu.Printer.Meat.Module ( src/Ormolu/Printer/Meat/Module.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer/Meat/Module.o )
[22 of 24] Compiling Ormolu.Printer   ( src/Ormolu/Printer.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Printer.o )
[23 of 24] Compiling Ormolu.Diff      ( src/Ormolu/Diff.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/Diff.o )
[24 of 24] Compiling Ormolu           ( src/Ormolu.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu.o )
Preprocessing executable 'ormolu' for ormolu-0.0.1.0..
Building executable 'ormolu' for ormolu-0.0.1.0..
                            
~/tmp/ormolu/<built-in>:15:10: error:
     warning: non-portable path to file '".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"'; specified path differs in case from file name on disk [-Wnonportable-include-path]
#include ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/autogen/cabal_macros.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"
1 warning generated.        
                            
~/tmp/ormolu/<built-in>:15:10: error:
     warning: non-portable path to file '".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"'; specified path differs in case from file name on disk [-Wnonportable-include-path]
#include ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/autogen/cabal_macros.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"
1 warning generated.        
[1 of 2] Compiling Paths_ormolu     ( .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/autogen/Paths_ormolu.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/ormolu-tmp/Paths_ormolu.o )
[2 of 2] Compiling Main             ( app/Main.hs, .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/ormolu-tmp/Main.o )
                            
~/tmp/ormolu/<built-in>:15:10: error:
     warning: non-portable path to file '".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"'; specified path differs in case from file name on disk [-Wnonportable-include-path]
#include ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/autogen/cabal_macros.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"
1 warning generated.        
                            
~/tmp/ormolu/<built-in>:15:10: error:
     warning: non-portable path to file '".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"'; specified path differs in case from file name on disk [-Wnonportable-include-path]
#include ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/autogen/cabal_macros.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ".stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/Ormolu/autogen/cabal_macros.h"
1 warning generated.        
Linking .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/ormolu/ormolu ...
ormolu-0.0.1.0: copy/register
Installing library in~/tmp/ormolu/.stack-work/install/x86_64-osx/lts-13.19/8.6.4/lib/x86_64-osx-ghc-8.6.4/ormolu-0.0.1.0-7xmvlbJRMCu14uKxFQNUcA
Installing executable ormolu in /tmp/ormolu/.stack-work/install/x86_64-osx/lts-13.19/8.6.4/bin
Registering library for ormolu-0.0.1.0..
Completed 5 action(s). 

And when try to use

ormolu --mode inplace library/Exp/S.hs 
ormolu: panic! (the 'impossible' happened)
  (GHC version 8.6.4 for x86_64-apple-darwin):
        not implemented yet: ArithSeq
CallStack (from HasCallStack):
  error, called at src/Ormolu/Utils.hs:39:22 in ormolu-0.0.1.0-7xmvlbJRMCu14uKxFQNUcA:Ormolu.Utils

Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug

Combining where and do will fail

Input:

f :: Maybe Int
f = do
  return c
  where
    c = 0
λ ormolu A.hs
Parsing of formatted code failed:
<rendered>:3:1-5
parse error on input ‘where’
Please, consider reporting the bug.

Respect more newlines?

This is really a question about how ormolu wants to treat newlines in general. I like to be liberal with whitespace to increase readability. For example I:

  • Add newlines between groups of imports that I want to remain separate
  • Add multiple newlines between sections within a single file
  • Add newlines between expressions in a do block when each is a little complex
  • Add newlines between bindings in a where block

It seems that ormolu will strip most of these out right now and I'm wondering how this should be treated in the future. One of the most interesting features of ormolu, for me at least, is the ability for the programmer to influence the output by, for example, adding line-breaks in type signatures. Newlines seem like another place where we can transfer some control to the programmer.

Enable all language extensions that don't steal identifiers

This is an idea from @mboes.

If ormolu enables all extensions that don't steal syntax, it can then format a superset of Haskell.

It is then more debatable whether support for finding the default-extensions in cabal files (#56) is necessary. An extension that steals syntax is likely going to be activated per file, instead.

Are there any other factors we should consider when deciding which extensions to enable by default?

Add support for GHC 8.6

Right now we're working with GHC 8.4, but at some point we'll need to add support for more GHC versions.

Instance header formatting discussion

I'd like to use this issue to discuss various class instance layouting questions that arise as we test ormolu on more code. I'm currently running it over the various Cardano codebases to find nitpicks. I will label specific examples so we can refer to them more easily.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.