Code Monkey home page Code Monkey logo

fourmolu's Introduction

Fourmolu

License BSD3 Hackage CI

Fourmolu is a formatter for Haskell source code. It is a fork of Ormolu, with upstream improvements continually merged.

We share all bar one of Ormolu's goals:

  • 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.
  • That 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.
  • Implementing one “true” formatting style which admits no configuration. We allow configuration of various parameters, via CLI options or config files. We encourage any contributions which add further flexibility.

Configuration

See https://fourmolu.github.io/config/

Installation

To install the latest release from Hackage, simply install with Cabal or Stack:

$ cabal install fourmolu
$ stack install fourmolu

Building from source

$ cabal build -fdev
$ stack build --flag fourmolu:dev

The dev flag may be omitted in your local workflow as you work, but CI may not pass if you only build without the dev flag.

Usage

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

$ fourmolu Module.hs

Add -i (or --mode inplace) to replace the contents of the input file with the formatted output.

$ fourmolu -i Module.hs

Specify a directory to recursively process all of its .hs files:

$ fourmolu -i src

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

$ fourmolu --mode inplace $(git ls-files '*.hs')
# Or to avoid hitting command line length limits and enable parallelism (12-way here):
$ git ls-files -z '*.hs' | xargs -P 12 -0 fourmolu --mode inplace

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

$ fourmolu --mode check src

⚡ Beware git's core.autocrlf on Windows ⚡

Fourmolu's output always uses LF line endings. In particular, fourmolu --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.

Web app

See https://fourmolu.github.io/ to try Fourmolu in your browser. This is re-deployed on every new commit to main, so will use the latest version of Fourmolu, potentially including unreleased changes.

Editor integration

Fourmolu can be integrated with your editor via the Haskell Language Server. Just set haskell.formattingProvider to fourmolu (instructions).

GitHub actions

[run-fourmolu][https://github.com/haskell-actions/run-fourmolu] is the recommended way to ensure that a project is formatted with Fourmolu.

Language extensions, dependencies, and fixities

Fourmolu 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 the fixities configuration option in fourmolu.yaml. When the input comes from stdin, one can pass --stdin-input-file which will give Fourmolu the location that should be used as the starting point for searching for .cabal files.

Here is an example of the fixities configuration:

fixities:
  - 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.

fourmolu.yaml can also contain instructions about module re-exports that Fourmolu should be aware of. This might be desirable because at the moment Fourmolu 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 Fourmolu. (You are welcome to open PRs to make Fourmolu aware of more re-exports by default.) However, when the fixity of an operator is not inferred correctly, making Fourmolu aware of a re-export may come in handy. Here is an example:

reexports:
  - module Control.Lens exports Control.Lens.At
  - module Control.Lens exports "lens" Control.Lens.Lens

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 files can be disabled by passing --no-cabal.

Magic comments

Fourmolu understands two magic comments:

{- FOURMOLU_DISABLE -}

and

{- FOURMOLU_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 {- FOURMOLU_DISABLE -} at the very top. Note that for Fourmolu to work the fragments where Fourmolu 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.

{- ORMOLU_DISABLE -} and {- ORMOLU_ENABLE -}, respectively, can be used to the same effect, and the two styles of magic comments can be mixed.

Regions

One can ask Fourmolu 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.

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)
400 Failed to load Fourmolu configuration file

Using as a library

The fourmolu 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.10.2.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.
  • Various minor idempotence issues, most of them are related to comments or column limits.
  • Fourmolu is in a fairly early stage of development. The implementation should be as stable as Ormolu, as it only makes minimal changes, and is extensively tested. But the default configuration style may change in some minor ways in the near future, as we make more options available. It will always be possible to replicate the old default behaviour with a suitable fourmolu.yaml.

Contributing

If there are any options you'd like to see, let us know. If it's not too complicated to implement (and especially if you implement it yourself!) then we'll probably add it.

See DEVELOPER.md for documentation.

License

See LICENSE.md.

Copyright © 2018–2020 Tweag I/O, 2020-present Matt Parsons

Acknowledgements

The vast majority of work here has been done by the Ormolu developers, and thus they deserve almost all of the credit. This project is simply intended as a haven for those of us who admire their work, but can't quite get on board with some of their decisions when it comes down to the details.

fourmolu's People

Contributors

3kyro avatar amesgen avatar avi-d-coder avatar basile-henry avatar brandonchinn178 avatar cblp avatar dependabot[bot] avatar ethercrow avatar facundominguez avatar gabrielelana avatar georgefst avatar gustavoavena avatar int-index avatar kindaro avatar kukimik avatar mboes avatar mitchellwrosen avatar mrkkrp avatar parsonsmatt avatar pbrisbin avatar robx avatar ruhatch avatar sol avatar sphaso avatar tbagrel1 avatar tomjaguarpaw avatar tomsmeding avatar utdemir avatar waddlaw avatar yumiova 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

fourmolu's Issues

Documentation

I think rather than duplicating the ormolu README, it would probably be clearer for anyone stumbling across this (and there will be more of them if haskell/haskell-language-server#161 gets merged), if we just linked to ormolu and explained the differences, along with why this project exists.

Preserve more user choice

In respectful mode, the following shouldn't be modified:

-- | a
a x = 1
-- | b
b = 2

For this, we need to be able to disable the isDocumented checks in Declaration.p_hsDecls'. But this has weird knock on effects - many previously separated declarations are now pushed together. The problem is that separatedByBlank doesn't really do what it says, because the SrcSpans we get from GHC can be rather odd, when the -haddock flag is passed (which, for us, is always). For example, for the above, we get these spans:

[ RealSrcSpan SrcSpanOneLine "tmp/Tmp.hs" 3 1 7
, RealSrcSpan SrcSpanMultiLine "tmp/Tmp.hs" 4 1 5 7
, RealSrcSpan SrcSpanOneLine "tmp/Tmp.hs" 5 1 7
, RealSrcSpan SrcSpanOneLine "tmp/Tmp.hs" 6 1 6
]

That SrcSpanMultiLine entry seems to be saying that the definition of a extends all the way to the end of the line containing the haddock comment for b (and this is the case even if we insert blank lines before that comment)...

I haven't yet determined for sure whether this can be considered a GHC bug, but it looks like one.

Option for not hanging characters in the margins.

Currently,

let
    expr1 = ....
    expr2 = ....

is reformatted to

let expr1 = ...
    expr2 = ...

This has at least three problems.

  1. Fourmolu ignores the configured indentation and always uses four spaces for such blocks.
  2. If you want to re-indent to a different width without using fourmolu, it's not enough to do a simple search and replace on consecutive whitespace, because space has to be inserted between let and expr1 when expanding, and it's not even possible to reduce the indentation below 4.
  3. It's font sensitive and looks staggered unless the font is fixed-width.

Does the code easily allow an option to always force the first kind of formatting? It seems like do blocks are already formatted this way.

no-layout

I am experimenting with a proof-of-concept for an option "no-layout" which uses explicit braces ({ ; }) in all where,let,do and case clauses.

My question: Would you be open to accepting a PR which implements that option?

Alternatives: Instead of "no-layout" there could also be a "preserve-layout" option which would use explicit braces exactly where the source-source uses them.


I nearly got it to work apart from some issues with bindings of the form

 where
  f 5 = a
  f x = x

somehow I don‘t get a semicolon behind the a, when I use txt ";" >> newline as separator for the where block.
Anyways I guess I`ll be able to figure that out.

Confusing docs for `diff-friendly-import-export`.

The docs for this option say "Whether to make use of extra commas in import/export lists (as opposed to Ormolu's style)", but Ormolu uses trailing commas on multi-line import and export lists (and I'm pretty sure it has since before fourmolu was forked), which would seem to be the "diff friendly" style.

In #19, tweag/ormolu#409 is referenced, but that issue isn't about commas, but rather the position of the parens.

So, I guess I'm asking for that documentation to be more explicit about how that option actually affects the formatting.

Line length limit in fourmolu

TL;DR: Would an option for max number of characters in a line be a welcome feature in fourmolu? Is anyone asking about or working on this?

Based on ormolu's design principle's and a couple of comments that I've seen in its repo (e.g. this), I know that setting a limit to the number of character in a line is not something that they would want to do.

FWIU, fourmolu was forked to provide more customization, so it now provides multiple options which I imagine wouldn't be considered in ormolu. With this in mind, I wanted to ask the maintainers about possibly adding an option for max number of characters in a line.

  • Would this be a welcome feature? Or is there a reason not to do this (e.g. increases the separation from ormolu, harder to maintain)?
  • Has anyone asked for this before? Is anyone working or planning to work on this already?

Thanks!

Make sure defaults don't slip out of sync

In Main.printerOptsParser, the default values are written as string constants, rather than coming from defaultPrinterOpts. We could easily forget to update these if we ever change defaults.

This is easy enough but should probably wait until after PRs adding options have been merged (#23, #26 etc.).

List of lists formatting

Tried to format list of lists:

x =
  [ [1, 2]
  , [2, 3]
  , [3, 4]
  ]

Result of formatting:

x =
  [
    [1, 2]
  ,
    [2, 3]
  ,
    [3, 4]
  ]

Configurable number of lines between top-level definitions

We use 2 newlines between top-level definitions in our codebase. eg.

f :: Int -> Int -> Int
f x y = square x + square y
  where
    square a = a * a


g :: Int -> Int
g z = f z z

as opposed to (single newline):

f :: Int -> Int -> Int
f x y = square x + square y
  where
    square a = a * a

g :: Int -> Int
g z = f z z

It would be great if fourmolu had a configurable number of newlines between top-level definitions, such that we could set it to 2 instead of 1!

Ormolu rejects

A list of features rejected by Tweag (not quite definitively in some cases) that we might want to have as options:

  • Prettier and more diff-friendly import/export lists: tweag/ormolu#409
  • Respect more newlines: tweag/ormolu#74 (parts of this have been implemented in Ormolu)
    • Import groups (it's worth thinking about how this plays along with tooling for auto-inserting imports e.g. HLS)
    • Not always one line between top-level definitions
      • Multiple blank lines can be useful for semantic grouping
      • Zero is nice for trivial definitions, especially instances
  • Break up long lines: tweag/ormolu#508
    • It's possible that this contradicts Ormolu's design enough to be a nightmare to implement fully
  • No space before record opening brace: tweag/ormolu#565
  • Keep multiline haddocks: tweag/ormolu#641
  • Set default GHC options: tweag/ormolu#519
    • Seeing as we already have support for config files, this shouldn't be difficult. Only issue is that these files only currently affect PrinterOpts.
  • Keep track of source locations: tweag/ormolu#433
  • Hang record constructors (and updates): tweag/ormolu#492
  • Keep end-of-line argument Haddocks: tweag/ormolu#389

fourmolu fails to build with current stack.yaml

In a clean environemnt (the haskell:8.8.3 image from dockerhub), running stack install with no modifications leads to this:

Error: While constructing the build plan, the following exceptions were encountered:
 In the dependencies for HsYAML-aeson-0.2.0.0:
     aeson-1.5.2.0 from stack configuration does not match ^>=1.4.0.0  (latest matching version
                   is 1.4.7.1)
 needed due to fourmolu-0.1.0.0 -> HsYAML-aeson-0.2.0.0
 Some different approaches to resolving this:
   * Set 'allow-newer: true'
     in /root/.stack/config.yaml to ignore all version constraints and build anyway.
   * Recommended action: try adding the following to your extra-deps in /fourmolu/stack.yaml:
 - aeson-1.4.7.1@sha256:6d8d2fd959b7122a1df9389cf4eca30420a053d67289f92cdc0dbc0dab3530ba,7098
 Plan construction failed.

Looking at the .cabal for HsYaml-aeson-0.2.0.0 this is indeed true. Interestingly, if I build once with allow-newer: true and then remove it I can still build afterwards (indicating some kind of caching bug in stack).

In any case, what would you like to do to fix this?

  • use allow-newer: true
  • use r2 for HsYAML-aeson which fixes this bound

Consistency between CLI and YAML config

The text representations of options are currently specified independently in FromJSON instances and ToCLIArgument instances. I discussed this with @kukimik at #44, and we determined that what we really want is a general-purpose config management library (some examples from other languages are linked in #44). But we could certainly do something simpler here for now, then maybe think about extracting that in to a library.

Support different comment types for module and function docstrings

I believe it's fairly standard for module docstrings to be of the form

{-|
Module: Foo.Bar
Description: ...
...
-}

and function docstrings to be of the form

-- | asdf
-- bar
-- baz

We should allow module docstrings to be different from function docstrings.

Also, as a bonus, it'd be great if the {-| syntax didn't add a space like {- |.

Formatting changes AST in header haddock

module M (
    -- | multi-line
    -- comment
    x,
) where
$ fourmolu M.hs --unsafe
module M (
    {- | multi-line
     comment
    -}
    x,
) where

The leading whitespace becomes part of the comment.

This will have been introduced by #30.

Haddock-like docs in non-top level functions are stripped

Fourmolu changes this:

module Foo where

foo :: Int
foo = 1
  where
    -- | asdf
    bar = 2

    -- This is important because:
    -- * Reason 1
    -- * Reason 2
    baz = 3

data Foo = Foo

instance Show Foo where
  -- | shows a foo
  show _ = "Foo"

to this:

module Foo where

foo :: Int
foo = 1
  where
    bar = 2

    -- This is important because:

    baz = 3

data Foo = Foo

instance Show Foo where
    show _ = "Foo"

Feature Request: import stanzas

The only thing preventing me from using this at this point is the fact that import reordering does not respect stanzas. This is important for 2 reasons.

  1. Internal vs External imports
  2. Separating your custom prelude (we all have one) from the rest of your imports.

The idea is maybe to prefix each stanza with some pragma, or infer it from whitespace.

This feature was rejected by the original ormolu team, but maybe this less dogmatic opinionated fork might consider it. It was rejected on the grounds that automatic import insertion would break, but I think it can be made to work if there is a convention around how stanzas get used, and encode common use cases in pragmas.

EDIT: If you use import stanzas in your projects, and you use them differently than the cases I outlined above, I'd love to hear your perspective.

Commas first

Since we have opened the pandoras box of configuration, would it be possible to add an option for having commas first in lists and records?

It is the one thing that annoys me whenever I use ormolu and it is the opposite of how all the rest of the Haskell community does ite.g. 1 2. The motivation seems fairly lacking, since Haskell afaik doesn't support trailing commas in most cases and now it forces you to manually type commas when appending a line to a list.

How tightly integrated is the choice of commas last in the design of the program? Would adding this configuration lead to a lot of extra complexity?

Testing all options

Until recently, all our output options have been binary choices*, with a neat split in to "the Ormolu version" and "the Fourmolu version". Our tests have thus tested our default options, and Ormolu's default options. This was never going to last. #48 is the first to introduce an option which is not used by default.

We have a few choices:

  • Run all the tests with all the combinations of options. This may take a long time to run (I haven't tried it yet), and it would start to become difficult to review all the outputs to check that everything looks sensible. But it does give us pretty comprehensive regression tests.
    • Obviously this grows exponentially as we add more options...
  • Only test each option on the examples where it should be relevant, e.g. there's no point running with and without record-brace-space on a file that doesn't contain any records. Setting this up would require reviewing each test case to determine which options are relevant. We could also miss bugs where an option affects something it shouldn't.
  • Consider which options are likely to interact with each other, and filter the set of combinations accordingly. This has some of the same drawbacks as the previous choice.

* Apart from indentation, but for the purposes of this discussion that's a choice between two and four. There doesn't seem much point testing with any more than that.

Config parse errors not displayed

Currently, if we hit a parse error on a fourmolu.yaml config file, it is silently ignored unless the -d debugging flag is on, in which case the error is printed to stderr. In either case, formatting will run without using the settings from the file, which is probably not what we want.

Indentation of 'where'

I believe I'm not alone in preferring:

x =
    y
  where
    y = 1

to:

x =
    y
    where
        y = 1

It would be nice to have the option to indent wheres half as much as everything else.

Magic comments don't completely disable formatting

I would expect adding {- FOURMOLU_DISABLE -} to the very top of the file and running fourmolu would not change anything in the file, but it does change this:

{- FOURMOLU_DISABLE -}

class Bar a where
  bar1 :: a  


  bar2 :: a

to this:

{- ORMOLU_DISABLE -}

class Bar a where
  bar1 :: a

  bar2 :: a

It collapses multiple newlines into one, changes FOURMOLU_DISABLE to ORMOLU_DISABLE, and (you can't see it here) removes trailing whitespace

Add a configuration file

It should be possible to configure fourmolu.

A great initial choice would be the amount of space in the indentation.

I'm not picky on the file format. Dhall might be cool? Yaml is a safe bet.

Not idempotent with comment in let block

Original:

foo :: IO ()
foo = let
  foo' = do
    putStrLn "hi"
  -- test
  in foo' >> putStrLn "bye"

First pass:

foo :: IO ()
foo =
    let foo' = do
            putStrLn "hi"
            -- test
     in foo' >> putStrLn "bye"

Second pass:

foo :: IO ()
foo =
    let foo' = do
            putStrLn "hi"
     in -- test
        foo' >> putStrLn "bye"

Allow more complex subexpressions to remain on a single line

This rejected commit demonstrates some of the remaining places in which I find Fourmolu's default style unpalatable.

Essentially, where an expression is only multiline because it ends with a particular multiline-friendly construct (this is a bit vague, but definitely includes record update and construction. case and \case statements, multi-way-if, and probably list literals), I'd like to format the preceding part of the expression in single-line style.

As an extreme case, we currently turn:

f = g 1 2 3 4 5 R
    { a = ()
    , b = ()
    }

in to:

f =
    g
        1
        2
        3
        4
        5
        R
            { a = ()
            , b = ()
            }

Which is unnecessarily indented (twice!), and has a lot of almost-empty lines. The only current sensible solution is to use a local binding. And that only solves the second issue.

Unfortunately I'm not entirely convinced this option could be implemented simply or efficiently.

Another example

It rather annoys me that, while this is accepted, adding some leading operation can cause a whole block below to be indented:

    items <- for [1 .. 5] \i ->
        print i
    items <-
        id <$> for [1 .. 5] \i -> -- note that `$` is special-cased - that wouldn't be reformatted
            print i

Comments that should not be moved between leading comma and element

We have comments that separate out elements in our list, like

[ foo1
  -- bars are below here
, bar1
, bar2
, bar3
  -- bazes below here
, baz1
, baz2
]

but Fourmolu reformats this to

[ foo1
, -- bars below here
  bar1
, bar2
, bar3
, -- bazes below here
  baz1
, baz2
]

which is weird because it seems like it's commenting that specific element rather than everything after that point. Ormolu doesn't have this problem because it doesn't have the leading commas

-- ormolu
[ foo1,
  -- bars below here
  bar1,
  bar2,
  -- bazes below here
  baz1,
  baz2
]

Please use tags and github releases before hackage ones

Hi, i know there is a maintenance burden that not always have an immediate advantage but been able to sync the repo with hackage releases quickly can help to start hacking the project for testing and bug fixing.

Thanks in advance!!

Allow trailing Haddock comments

This is mentioned in #32 (comment), but I figured it's worth breaking out into a separate issue.

It would be great to configure trailing Haddock comments, specifically for data types. Something like

data Foo
  = Foo1 -- ^ Foo1
  | Foo2 -- ^ Foo2

or even

data Foo
  = Foo1
    -- ^ Foo1
  | Foo2
    -- ^ Foo2

2 Spaces

My lines are much too long using this formatter, I suggest we use 2 space indentation.

Help text is ugly

Perhaps my fault for choosing some rather long field names... But really this is an optparse-applicative issue: pcapriotti/optparse-applicative#406

  --start-line START       Start line of the region to format (starts from 1)
  --end-line END           End line of the region to format (inclusive)
  --indentation WIDTH      Number of spaces per indentation step (default 4)
  --comma-style STYLE      How to place commas in multi-line lists, records etc: 'leading' (default) or 'trailing'
  --indent-wheres BOOL     Whether to indent 'where' bindings past the preceding body (rather than half-indenting the
                           'where' keyword) (default 'false')
  --record-brace-space BOOL
                           Whether to leave a space before an opening record brace (default 'false')
  --diff-friendly-import-export BOOL
                           Whether to make use of extra commas in import/export lists (as opposed to Ormolu's style)
                           (default 'true')
  --preserve-spacing BOOL  Give the programmer more choice on where to insert blank lines (default 'true')
  --haddock-style STYLE    How to print Haddock comments (default 'multi-line')
  FILE                     Haskell source files to format or stdin (default)

More consistent indentation

Logic similar to #37 should be applied in a few other places - at least constraint tuples and list/tuple patterns. But these are much less often multi-line. Besides, #37 uses a bit of a hack, and I'm still hoping a better solution may reveal itself once I've grown more accustomed to the code base.

Stackage?

Are there any plans to register this on Stackage?

GHC 9.0 Compatibility: TypeApplications and ticked promoted constructors

Currently, fourmolu formats type application with ticked promoted constructor with space inserted in-between:

before:

foo @'True

after:

foo @ 'True

This works fine with GHC <= 8.10.

However, as GHC 9.0 introduces whitespace-sensitive parsing of operators (1, 2), the above result makes ghc complaining that "Variable not in scope: (@)".

Dependently-typed programs suffer much from this behaviour, and hence a whitespace MUST NOT be inserted.

Setup some CI

Travis is easy and familiar to me but maybe it's a good time to try Circle CI

Place examples in a separate package

I'm packaging this project for Gentoo, and there doesn't seem to be a way to make the examples included in data-files optional. Considering how many there are, it would make sense to make a separate fourmolu-examples package so that these ~1100 examples don't have to be included.

Command line options

#10 introduced a PrinterOpts type, representing the space of all possible formatting configurations (at time of writing this is just indentation size). This should also be fully configurable from the command line, with the command line taking priority over config files.

Fix the test suite

Right now, the test suite is expecting everything to have 2 space indentation.

Reformatting it to have 4 space indentation would be great! Then we could publish to stackage.

Formatting changes AST in indented haddocks

The following module showcases valid Haddock documentation that Fourmolu can't parse:

module Foo (
  -- | BAD MULTILINE
  -- HERE
  foo
) where

foo :: Int
foo = 1

class Bar creds where
  -- | BAD MULTILINE
  -- HERE
  bar :: creds -> IO ()

`comma-style: leading` doesn't apply to import and export lists

I get:

module Logging (
  LogAction,
  logMessageStderr,

  -- * Messages
  Message (..),
  HasLog (..),
  WithLog,
  logDebug,
  logInfo,
  logWarning,
  logError,

  -- * Message Severity
  module Colog.Core.Severity,
) where

I want:

module Logging
  ( LogAction
  , logMessageStderr

  -- * Messages
  , Message(..)
  , HasLog(..)
  , WithLog
  , logDebug
  , logInfo
  , logWarning
  , logError

  -- * Message Severity
  , module Colog.Core.Severity
  ) where

This seems like a bug in the option, personally.

Rename 'preserve-spacing'

Thinking ahead a little, I can see us wanting a more general "preserve more user choice" flag, to make Fourmolu less keen to rearrange comments etc.

Unsure about a good name.

Feature request: support multiline comments in indented contexts

Related to #63

This PR solves the above issue by fixing a bug in detecting whether a comment is indented or not. When this bug is fixed, fourmolu will always use single line comments for indented haddocks, e.g.

class Foo a where
  -- | asdf
  -- asdf
  foo :: a

{-| asdf
    asdf
-}
bar :: Int

It would be nice if these haddocks could use the same style as non-indented functions, e.g.

class Foo a where
  {-| asdf
     asdf
  -}
  foo :: a

{-| asdf
    asdf
-}
bar :: Int

This seems to mostly be a one-line change:

    getPrinterOpt poHaddockStyle >>= \case
      HaddockSingleLine -> pure True
-     -- Use multiple single-line comments when the whole comment is indented
-     HaddockMultiLine -> maybe False ((> 1) . srcSpanStartCol) <$> getSrcSpan l
+     HaddockMultiLine -> pure False

but this fails when checking if the AST is the same as before, because of whitespace differences in the comment, e.g.

-- from
HsDocString " A multiline\n comment here"

-- to
HsDocString " A multiline\n   comment here\n  "

One possible solution is to also trim leading whitespace when doing the AST check (but not when printing out the haddock in p_hsDocString)

Merge upstream changes

ormolu-0.1 was recently released.

Once we've closed #1 and #2, it would be a good time to make a corresponding fourmolu-0.1 release.

Project description

The "About" field on GitHub should probably mention the expanded scope of the project (i.e. that it's no longer just about four spaces). Especially as this is reflected in the <title> element, so it's the first thing people see.

I think only @parsonsmatt has permissions to change that.

MultiWayIf

bar x y =
    if
            | x > y -> x
            | x < y ->
                y
            | otherwise -> x

This is obviously way too much indentation.

Somehow we've deviated from Ormolu's output. And seemingly during the period when we didn't have proper tests in place. Will need to do some git bisect-ing.

Ormolu compatibility

Currently, behaviour deviates from Ormolu (with this config) in the following ways:

  • #6: Changes a specific rare edge case (do block as the left operand of an infix operator), where Ormolu + 4-space indentation causes an unsafe formatting (modified AST).
  • #30: In single-line mode, we don't insert leading spaces:
    {- | haddock
    comment
    -}
    becomes:
    -- | haddock
    --comment
    rather than:
    -- | haddock
    -- comment
    Replicating Ormolu's behaviour here would be possible. The implementation would just get very messy. Besides, we still have the property that we won't modify code which has already been formatted by Ormolu. And I'm admittedly a bit ambivalent because I really can't see the argument for replacing multi-line comments with multiple single-line ones in the first place.
  • #37: The fact that we round down upon indenting means that some lines which wouldn't start in an even column with Ormolu do with Fourmolu. I'm yet to find an example of this which looks like code any human would actually write.

Each of these changes has elements which it may be worth at least attempting to upstream.

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.