on e09fcc0 help sign in
49 files changed+145−382
since splitsnapshots
unifiedsplit
.buildkite/pipeline.yml+2−2 .github/workflows/binaries.yml−39 .github/workflows/build.sh−7 CHANGELOG.md+6−23 README.md+11−21 app/Main.hs+13−23 data/examples/declaration/signature/type/infix-promoted-type-constructor-out.hs+1 data/examples/declaration/signature/type/unrelated-out.hs−4 data/examples/declaration/signature/type/unrelated.hs−4 data/examples/declaration/value/function/implicit-params-out.hs−1 data/examples/declaration/value/function/implicit-params.hs−2 data/examples/import/deduplication-bug-out.hs−4 data/examples/import/deduplication-bug.hs−4 data/examples/import/merging-0-out.hs−3 data/examples/import/merging-0.hs−4 data/examples/import/merging-1-out.hs−2 data/examples/import/merging-1.hs−3 data/examples/import/merging-2-out.hs−2 data/examples/import/merging-2.hs−4 data/examples/import/qualified-post-out.hs+1−1 data/examples/import/qualified-prelude-out.hs+1−1 data/examples/import/simple-out.hs+5−2 data/examples/other/argument-comment-out.hs+1 data/examples/other/comment-glued-together-out.hs−7 data/examples/other/comment-glued-together.hs−6 data/examples/other/cpp/separation-2b-out.hs+1 default.nix+10−7 expected-failures/Agda.txt+1−1 expected-failures/esqueleto.txt+1−1 expected-failures/hlint.txt−11 expected-failures/idris.txt+4 expected-failures/intero.txt+1−1 expected-failures/leksah.txt+1−1 expected-failures/pandoc.txt+1−1 expected-failures/postgrest.txt−5 expected-failures/purescript.txt+2−2 expected-failures/tensorflow.txt−7 nix/nixpkgs/default.nix+3−7 ormolu.cabal+4−4 shell.nix+1−3 src/Ormolu/Diff.hs+3−3 src/Ormolu/Imports.hs+55−139 src/Ormolu/Printer/Comments.hs+2−2 src/Ormolu/Printer/Meat/Declaration.hs+4−14 src/Ormolu/Printer/Meat/Declaration.hs-boot+1 src/Ormolu/Printer/Meat/Declaration/Value.hs-boot+4 src/Ormolu/Printer/Meat/Module.hs+2−2 src/Ormolu/Processing/Preprocess.hs+2−1 src/Ormolu/Utils.hs+1−1
.buildkite/pipeline.yml+2−2
@@ -3,9 +3,9 @@ steps:
33 command: |
44 nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc865
55 timeout: 100
6− - label: Build and test with GHC 8.8.4
6+ - label: Build and test with GHC 8.8.3
77 command: |
8− nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc884
8+ nix-build --keep-going --no-out-link --argstr ormoluCompiler ghc883
99 timeout: 100
1010 - label: Build and test with GHC 8.10.1
1111 command: |
.github/workflows/binaries.ymldeleted−39
@@ -1,39 +0,0 @@
1−name: Build binaries
2−on:
3− release:
4− types: [published]
5−jobs:
6− build:
7− strategy:
8− fail-fast: false
9− matrix:
10− os: [ubuntu-latest, macOS-latest, windows-latest]
11− runs-on: ${{ matrix.os }}
12− steps:
13− - uses: actions/checkout@v2
14− - uses: actions/setup-haskell@v1
15− if: runner.os != 'Linux'
16− with:
17− ghc-version: '8.10.1'
18− cabal-version: '3.2'
19− - name: Build Linux binary
20− if: runner.os == 'Linux'
21− run: |
22− docker run \
23− -v $(pwd):/mnt -w /mnt \
24− utdemir/ghc-musl:v14-ghc8101 \
25− bash .github/workflows/build.sh
26− - name: Build ${{ runner.os }} binary
27− if: runner.os != 'Linux'
28− shell: bash
29− run: |
30− cabal build exe:ormolu
31− cp $(find dist-newstyle \( -name ormolu -o -name ormolu.exe \) -type f) ormolu
32− - uses: actions/upload-release-asset@v1
33− env:
34− GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35− with:
36− upload_url: ${{ github.event.release.upload_url }}
37− asset_path: ormolu
38− asset_name: ormolu-${{ runner.os }}
39− asset_content_type: application/octet-stream
.github/workflows/build.shdeleted−7
@@ -1,7 +0,0 @@
1−set -e
2−
3−cabal update
4−cabal build exe:ormolu --enable-executable-static --ghc-options="-split-sections"
5−
6−cp $(find dist-newstyle -name ormolu -type f) .
7−strip -s ormolu
CHANGELOG.md+6−23
@@ -1,26 +1,6 @@
11## Unreleased
22
3−* Ormolu no longer overwrites already formatted files. [PR
4− 649](https://github.com/tweag/ormolu/pull/649).
5−
6−## Ormolu 0.1.2.0
7−
8−* Fixed the bug when comments in different styles got glued together after
9− formatting. [Issue 589](https://github.com/tweag/ormolu/issues/589).
10−
11−* Added `-i` as a shortcut for `--mode inplace`. [Issue
12− 467](https://github.com/tweag/ormolu/issues/467).
13−
14−* Improved grouping of top-level declarations. [Issue
15− 466](https://github.com/tweag/ormolu/issues/466).
16−
17−## Ormolu 0.1.1.0
18−
19−* Imports in a import lists are now normalized: duplicate imports are
20− combined/eliminated intelligently.
21−
22−* Import declarations that can be merged are now automatically merged.
23− [Issue 414](https://github.com/tweag/ormolu/issues/414).
3+* Duplicate imports in a single import list are now eliminated.
244
255* The magic comments for disabling and enabling Ormolu now can encompass any
266 fragment of code provided that the remaining code after exclusion of the
@@ -42,13 +22,16 @@
4222* Blank lines between definitions in `let` and `while` bindings are now
4323 preserved. [Issue 554](https://github.com/tweag/ormolu/issues/554).
4424
45−* Fixed the bug when type applications stuck to the `$` of TH splices that
46− followed them. [Issue 613](https://github.com/tweag/ormolu/issues/613).
25+* Fixed the bug when type applications glued to TH splices that followed
26+ them. [Issue 613](https://github.com/tweag/ormolu/issues/613).
4727
4828* Improved region formatting so that indented fragments—such as definitions
4929 inside of `where` clauses—can be formatted. [Issue
5030 572](https://github.com/tweag/ormolu/issues/572).
5131
32+* Fixed the bug in the de-duplicating algorithm for import lists. [Issue
33+ 620](https://github.com/tweag/ormolu/issues/620).
34+
5235* Fixed the bug related to the de-association of pragma comments. [Issue
5336 619](https://github.com/tweag/ormolu/issues/619).
5437
README.md+11−21
@@ -22,7 +22,7 @@ the following goals in mind:
2222* That formatting style aims to result in minimal diffs while still
2323 remaining very close to “conventional” Haskell formatting people use.
2424* Choose a style compatible with modern dialects of Haskell. As new Haskell
25− extensions enter broad use, we may change the style to accommodate them.
25+ extensions enter broad use, we may change the style to accomodate them.
2626* Idempotence: formatting already formatted code doesn't change it.
2727* Be well-tested and robust to the point that it can be used in large
2828 projects without exposing unfortunate, disappointing bugs here and there.
@@ -45,12 +45,11 @@ Alternatively, `stack` could be used with a `stack.yaml` file as follows.
4545
4646```console
4747$ cat stack.yaml
48−resolver: lts-16.0
48+resolver: lts-14.3
4949packages:
5050- '.'
5151
52−$ stack build # to build
53−$ stack install # to install
52+$ stack build
5453```
5554
5655To use Ormolu directly from GitHub with Nix, this snippet may come in handy:
@@ -130,6 +129,14 @@ independent top-level definitions.
130129 criterion than just being valid Haskell modules.
131130* Various minor idempotence issues, most of them are related to comments.
132131
132+## Editor integration
133+
134+We know of the following editor integrations:
135+
136+* [Emacs][emacs-package]
137+* [VS Code][vs-code-plugin]
138+* vim: [neoformat][neoformat], [vim-ormolu][vim-ormolu]
139+
133140## Running on Hackage
134141
135142It's possible to try Ormolu on arbitrary packages from Hackage. For that
@@ -144,22 +151,6 @@ also contain formatted `.hs` files for inspection and original inputs with
144151`.hs-original` extension (those are with CPP dropped, exactly what is fed
145152into Ormolu).
146153
147−## Editor integration
148−
149−We know of the following editor integrations:
150−
151−* [Emacs][emacs-package]
152−* [VS Code][vs-code-plugin]
153−* vim: [neoformat][neoformat], [vim-ormolu][vim-ormolu]
154−
155−## Arch Linux
156−
157−To install Ormolu on Arch Linux, one can use [the package on AUR][aur]:
158−
159−```console
160−yay -S ormolu
161−```
162−
163154## Contributing
164155
165156See [CONTRIBUTING.md][contributing].
@@ -178,4 +169,3 @@ Copyright © 2018–present Tweag I/O
178169[vs-code-plugin]: https://marketplace.visualstudio.com/items?itemName=sjurmillidahl.ormolu-vscode
179170[vim-ormolu]: https://github.com/sdiehl/vim-ormolu
180171[neoformat]: https://github.com/sbdchd/neoformat
181−[aur]: https://aur.archlinux.org/packages/ormolu
app/Main.hs+13−23
@@ -8,7 +8,6 @@ module Main (main) where
88
99import Control.Exception (SomeException, displayException, try)
1010import Control.Monad
11−import Data.Bool (bool)
1211import Data.Either (lefts)
1312import Data.List (intercalate, sort)
1413import qualified Data.Text.IO as TIO
@@ -61,19 +60,15 @@ formatOne mode config = \case
6160 -- 101 is different from all the other exit codes we already use.
6261 exitWith (ExitFailure 101)
6362 Just inputFile -> do
64− originalInput <- TIO.readFile inputFile
65− formattedInput <- ormoluFile config inputFile
63+ r <- ormoluFile config inputFile
6664 case mode of
6765 Stdout ->
68− TIO.putStr formattedInput
69− InPlace -> do
70− -- Only write when the contents have changed, in order to avoid
71− -- updating the modified timestamp if the file was already correctly
72− -- formatted.
73− when (formattedInput /= originalInput) $
74− TIO.writeFile inputFile formattedInput
66+ TIO.putStr r
67+ InPlace ->
68+ TIO.writeFile inputFile r
7569 Check -> do
76− when (formattedInput /= originalInput) $
70+ r' <- TIO.readFile inputFile
71+ when (r /= r') $
7772 -- 100 is different to all the other exit code that are emitted
7873 -- either from an 'OrmoluException' or from 'error' and
7974 -- 'notImplemented'.
@@ -139,18 +134,13 @@ optsParserInfo =
139134optsParser :: Parser Opts
140135optsParser =
141136 Opts
142− <$> ( (fmap (bool Stdout InPlace) . switch . mconcat)
143− [ short 'i',
144− help "A shortcut for --mode inplace"
145− ]
146− <|> (option parseMode . mconcat)
147− [ long "mode",
148− short 'm',
149− metavar "MODE",
150− value Stdout,
151− help "Mode of operation: 'stdout' (default), 'inplace', or 'check'"
152− ]
153− )
137+ <$> (option parseMode . mconcat)
138+ [ long "mode",
139+ short 'm',
140+ metavar "MODE",
141+ value Stdout,
142+ help "Mode of operation: 'stdout' (default), 'inplace', or 'check'"
143+ ]
154144 <*> configParser
155145 <*> (many . strArgument . mconcat)
156146 [ metavar "FILE",
data/examples/declaration/signature/type/infix-promoted-type-constructor-out.hs+1
@@ -1,2 +1,3 @@
11fun1 :: Def ('[Ref s (Stored Uint32), IBool] 'T.:-> IBool)
2+
23fun2 :: Def ('[Ref s (Stored Uint32), IBool] ':-> IBool)
data/examples/declaration/signature/type/unrelated-out.hsdeleted−4
@@ -1,4 +0,0 @@
1−clientFunc1 :: SomeType1
2−clientFunc2 :: SomeType2
3−clientFunc3 :: SomeType3
4−clientFunc1 :<|> clientFunc2 :<|> clientFunc3 = hoistClient foo bar baz
data/examples/declaration/signature/type/unrelated.hsdeleted−4
@@ -1,4 +0,0 @@
1−clientFunc1 :: SomeType1
2−clientFunc2 :: SomeType2
3−clientFunc3 :: SomeType3
4−clientFunc1 :<|> clientFunc2 :<|> clientFunc3 = hoistClient foo bar baz
data/examples/declaration/value/function/implicit-params-out.hs−1
@@ -1,7 +1,6 @@
11{-# LANGUAGE ImplicitParams #-}
22
33sortBy :: (a -> a -> Bool) -> [a] -> [a]
4−sortBy = undefined
54
65sort :: (?cmp :: a -> a -> Bool) => [a] -> [a]
76sort = sortBy ?cmp
data/examples/declaration/value/function/implicit-params.hs−2
@@ -1,8 +1,6 @@
11{-# LANGUAGE ImplicitParams #-}
22sortBy :: (a -> a -> Bool) -> [a] -> [a]
33
4−sortBy = undefined
5−
64sort :: (?cmp :: a -> a -> Bool) => [a] -> [a]
75sort = sortBy ?cmp
86
data/examples/import/deduplication-bug-out.hs−4
@@ -1,6 +1,2 @@
11import Foo1 (Bar1 (..), Baz1)
22import Foo2 (Bar2 (..), Baz2)
3−import Foo3 (Bar3 (x1, x2, x3))
4−import Foo4 (Bar4 (x1, x2))
5−import Foo5 (Bar5 (x1))
6−import Foo6 (Bar6 (..))
data/examples/import/deduplication-bug.hs−4
@@ -1,6 +1,2 @@
11import Foo1 (Bar1, Baz1, Bar1(..))
22import Foo2 (Bar2(..), Baz2, Bar2)
3−import Foo3 (Bar3(x1,x3), Bar3(x1, x2))
4−import Foo4 (Bar4(x1), Bar4(x2))
5−import Foo5 (Bar5, Bar5(x1))
6−import Foo6 (Bar6(x1), Bar6(..))
data/examples/import/merging-0-out.hsdeleted−3
@@ -1,3 +0,0 @@
1−import Foo
2−import Foo (bar, foo)
3−import Foo as F
data/examples/import/merging-0.hsdeleted−4
@@ -1,4 +0,0 @@
1−import Foo
2−import Foo (foo)
3−import Foo (bar)
4−import Foo as F
data/examples/import/merging-1-out.hsdeleted−2
@@ -1,2 +0,0 @@
1−import "bar" Foo (bar)
2−import "foo" Foo (baz, foo)
data/examples/import/merging-1.hsdeleted−3
@@ -1,3 +0,0 @@
1−import "foo" Foo (foo)
2−import "bar" Foo (bar)
3−import "foo" Foo (baz)
data/examples/import/merging-2-out.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Foo hiding (bar4, foo2)
2−import qualified Foo (bar3, foo1)
data/examples/import/merging-2.hsdeleted−4
@@ -1,4 +0,0 @@
1−import qualified Foo (foo1)
2−import Foo hiding (foo2)
3−import qualified Foo (bar3)
4−import Foo hiding (bar4)
data/examples/import/qualified-post-out.hs+1−1
@@ -1,5 +1,5 @@
11{-# LANGUAGE ImportQualifiedPost #-}
22
3+import Data.Text qualified as T
34import Data.Text qualified (a, b, c)
45import Data.Text qualified hiding (a, b, c)
5−import Data.Text qualified as T
data/examples/import/qualified-prelude-out.hs+1−1
@@ -1,4 +1,4 @@
11module P where
22
3−import Prelude hiding (id, (.))
43import qualified Prelude
4+import Prelude hiding (id, (.))
data/examples/import/simple-out.hs+5−2
@@ -1,5 +1,8 @@
11import Data.Text
2+import Data.Text
3+import qualified Data.Text as T
4+import qualified Data.Text (a, b, c)
5+import Data.Text (a, b, c)
6+import Data.Text hiding (a, b, c)
27import Data.Text (a, b, c)
38import Data.Text hiding (a, b, c)
4−import qualified Data.Text (a, b, c)
5−import qualified Data.Text as T
data/examples/other/argument-comment-out.hs+1
@@ -9,6 +9,7 @@ foo ::
99 -- | Foo
1010 Int ->
1111 Int
12+
1213foo ::
1314 Foo a =>
1415 -- | Foo
data/examples/other/comment-glued-together-out.hsdeleted−7
@@ -1,7 +0,0 @@
1−module Main (main) where
2−
3−-- | Foo.
4−
5−-- Bar
6−main :: IO ()
7−main = return ()
data/examples/other/comment-glued-together.hsdeleted−6
@@ -1,6 +0,0 @@
1−module Main (main) where
2−
3−{- | Foo. -}
4−-- Bar
5−main :: IO ()
6−main = return ()
data/examples/other/cpp/separation-2b-out.hs+1
@@ -1,4 +1,5 @@
11ffff, ffffffff :: Natural
2+
23#ifdef HASKELL_ZIP_DEV_MODE
34ffff = 200
45ffffffff = 5000
default.nix+10−7
@@ -1,6 +1,5 @@
1−{ pkgs ? (import ./nix/nixpkgs { inherit system; })
2−, system ? builtins.currentSystem
3−, ormoluCompiler ? "ghc884" # the shell doesn't work with ghc8101 yet
1+{ pkgs ? (import ./nix/nixpkgs)
2+, ormoluCompiler ? "ghc883" # the shell doesn't work with ghc8101 yet
43}:
54
65let
@@ -17,6 +16,12 @@ let
1716 };
1817 ormoluOverlay = self: super: {
1918 "ormolu" = super.callCabal2nixWithOptions "ormolu" source "-fdev" { };
19+ "ghc-lib-parser" = pkgs.haskell.lib.dontHaddock
20+ (super.callHackageDirect {
21+ pkg = "ghc-lib-parser";
22+ ver = "8.10.1.20200412";
23+ sha256 = "sha256-EjMzp8xRT3xVFKDI1fAfopkylGB0hv35Av2+uZeETRU=";
24+ } {});
2025 };
2126 ormolize = import ./nix/ormolize {
2227 inherit pkgs;
@@ -28,16 +33,14 @@ let
2833 "distributed-process"
2934 "esqueleto"
3035 "fay"
31− "hlint"
3236 "idris"
3337 "intero"
3438 "leksah"
39+ "lens"
3540 "pandoc"
3641 "pipes"
37− "postgrest"
3842 "purescript"
39− "tensorflow"
40− ] ++ (if ormoluCompiler == "ghc8101" then [] else ["lens"]);
43+ ];
4144 ormolizedPackages = doCheck:
4245 pkgs.lib.mapAttrs (name: p: ormolize {
4346 package = p;
expected-failures/Agda.txt+1−1
@@ -1,5 +1,5 @@
11Formatting is not idempotent:
2− dist/build/Agda/Syntax/Parser/Lexer.hs<rendered>:1602:17
2+ dist/build/Agda/Syntax/Parser/Lexer.hs<rendered>:1597:17
33 before: " = (check_ac"
44 after: " =\n "
55Please, consider reporting the bug.
expected-failures/esqueleto.txt+1−1
@@ -3,5 +3,5 @@ The GHC parser (in Haddock mode) failed:
33 lexical error in string/character literal at character 's'
44
55The GHC parser (in Haddock mode) failed:
6− test/PostgreSQL/Test.hs:562:9-41
6+ test/PostgreSQL/Test.hs:510:9-41
77 parse error on input `-- | Check the result is not null'
expected-failures/hlint.txtdeleted−11
@@ -1,11 +0,0 @@
1−Formatting is not idempotent:
2− src/Extension.hs<rendered>:19:22
3− before: "DoRec , -} Recursive"
4− after: "DoRec , -}\n "
5−Please, consider reporting the bug.
6−
7−Formatting is not idempotent:
8− src/Hint/Bracket.hs<rendered>:249:50
9− before: "\" x y [r]) {ideaSpan"
10− after: "\" x y [r])\n "
11−Please, consider reporting the bug.
expected-failures/idris.txt+4
@@ -2,6 +2,10 @@ The GHC parser (in Haddock mode) failed:
22 src/Idris/Parser.hs:1052:1
33 parse error on input `@'
44
5+AST of input and AST of formatted code differ.
6+ at src/Idris/Parser/Data.hs:281:1-29
7+Please, consider reporting the bug.
8+
59The GHC parser (in Haddock mode) failed:
610 src/Idris/Parser/Expr.hs:75:1
711 parse error on input `@'
expected-failures/intero.txt+1−1
@@ -1,5 +1,5 @@
11Formatting is not idempotent:
2− src/InteractiveUI.hs<rendered>:3748:33
2+ src/InteractiveUI.hs<rendered>:3757:33
33 before: "text \"Try\" <+> doWha"
44 after: "text \"Try\"\n "
55Please, consider reporting the bug.
expected-failures/leksah.txt+1−1
@@ -1,5 +1,5 @@
11Formatting is not idempotent:
2− src/IDE/Pane/Modules.hs<rendered>:1184:7
2+ src/IDE/Pane/Modules.hs<rendered>:1189:7
33 before: "cr\n -- show"
44 after: "cr\n in -- show"
55Please, consider reporting the bug.
expected-failures/pandoc.txt+1−1
@@ -3,7 +3,7 @@ The GHC parser (in Haddock mode) failed:
33 parse error on input `import'
44
55Formatting is not idempotent:
6− src/Text/Pandoc/Readers/Vimwiki.hs<rendered>:613:19
6+ src/Text/Pandoc/Readers/Vimwiki.hs<rendered>:615:19
77 before: ".softbreak <$ endlin"
88 after: ".softbreak\n <$ en"
99Please, consider reporting the bug.
expected-failures/postgrest.txtdeleted−5
@@ -1,5 +0,0 @@
1−Formatting is not idempotent:
2− src/PostgREST/DbRequestBuilder.hs<rendered>:149:24
3− before: " || -- clients"
4− after: " || ( origin ="
5−Please, consider reporting the bug.
expected-failures/purescript.txt+2−2
@@ -1,10 +1,10 @@
11The GHC parser (in Haddock mode) failed:
2− src/Language/PureScript/AST/Declarations.hs:479:1-17
2+ src/Language/PureScript/AST/Declarations.hs:478:1-17
33 Invalid type signature: pattern ValueDecl :: ...
44Perhaps you meant to use PatternSynonyms?
55
66The GHC parser (in Haddock mode) failed:
7− src/Language/PureScript/AST/SourcePos.hs:99:1-22
7+ src/Language/PureScript/AST/SourcePos.hs:101:1-22
88 Invalid type signature: pattern NullSourceSpan :: ...
99Perhaps you meant to use PatternSynonyms?
1010
expected-failures/tensorflow.txtdeleted−7
@@ -1,7 +0,0 @@
1−The GHC parser (in Haddock mode) failed:
2− tensorflow-mnist/app/Main.hs:50:13-14
3− parse error on input `->'
4−
5−The GHC parser (in Haddock mode) failed:
6− tensorflow-ops/tests/RegressionTest.hs:3:1-6
7− parse error on input `import'
nix/nixpkgs/default.nix+3−7
@@ -1,13 +1,9 @@
1−{ system ? builtins.currentSystem }:
21let
3− rev = "32b46dd897ab2143a609988a04d87452f0bbef59";
4− sha256 = "1gzfrpjnr1bz9zljsyg3a4zrhk8r927sz761mrgcg56dwinkhpjk";
2+ rev = "807ca93fadd5197c2260490de0c76e500562dc05";
3+ sha256 = "10yq8bnls77fh3pk5chkkb1sv5lbdgyk1rr2v9xn71rr1k2x563p";
54 nixpkgs = builtins.fetchTarball {
65 url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
76 inherit sha256;
87 };
9− pkgs = import nixpkgs {
10− inherit system;
11− config.allowUnfree = true;
12− };
8+ pkgs = import nixpkgs { config.allowUnfree = true; };
139in pkgs
ormolu.cabal+4−4
@@ -1,10 +1,10 @@
11cabal-version: 1.18
22name: ormolu
3−version: 0.1.2.0
3+version: 0.1.0.0
44license: BSD3
55license-file: LICENSE.md
66maintainer: Mark Karpov <mark.karpov@tweag.io>
7−tested-with: ghc ==8.6.5 ghc ==8.8.4 ghc ==8.10.1
7+tested-with: ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1
88homepage: https://github.com/tweag/ormolu
99bug-reports: https://github.com/tweag/ormolu/issues
1010synopsis: A formatter for Haskell source code
@@ -119,7 +119,7 @@ library
119119 base >=4.12 && <5.0,
120120 bytestring >=0.2 && <0.11,
121121 containers >=0.5 && <0.7,
122− dlist >=0.8 && <2.0,
122+ dlist >=0.8 && <0.9,
123123 exceptions >=0.6 && <0.11,
124124 ghc-lib-parser >=8.10 && <8.11,
125125 mtl >=2.0 && <3.0,
@@ -144,7 +144,7 @@ executable ormolu
144144 base >=4.12 && <5.0,
145145 ghc-lib-parser >=8.10 && <8.11,
146146 gitrev >=1.3 && <1.4,
147− optparse-applicative >=0.14 && <0.17,
147+ optparse-applicative >=0.14 && <0.16,
148148 ormolu -any,
149149 text >=0.2 && <1.3
150150
shell.nix+1−3
@@ -1,5 +1,3 @@
1−{ pkgs ? (import ./nix/nixpkgs { inherit system; })
2−, system ? builtins.currentSystem
3−}:
1+{ pkgs ? (import ./nix/nixpkgs) }:
42
53(import ./default.nix { inherit pkgs; }).dev.ormoluShell
src/Ormolu/Diff.hs+3−3
@@ -14,7 +14,7 @@ import Data.Text (Text)
1414import qualified Data.Text as T
1515import qualified FastString as GHC
1616import GHC
17−import Ormolu.Imports (normalizeImports)
17+import Ormolu.Imports (sortImports)
1818import Ormolu.Parser.CommentStream
1919import Ormolu.Parser.Result
2020import Ormolu.Utils
@@ -47,8 +47,8 @@ diffParseResult
4747 } =
4848 matchIgnoringSrcSpans cstream0 cstream1
4949 <> matchIgnoringSrcSpans
50− hs0 {hsmodImports = normalizeImports (hsmodImports hs0)}
51− hs1 {hsmodImports = normalizeImports (hsmodImports hs1)}
50+ hs0 {hsmodImports = sortImports (hsmodImports hs0)}
51+ hs1 {hsmodImports = sortImports (hsmodImports hs1)}
5252
5353-- | Compare two values for equality disregarding differences in 'SrcSpan's
5454-- and the ordering of import lists.
src/Ormolu/Imports.hs+55−139
@@ -4,176 +4,92 @@
44
55-- | Manipulations on import lists.
66module Ormolu.Imports
7− ( normalizeImports,
7+ ( sortImports,
88 )
99where
1010
1111import Data.Bifunctor
1212import Data.Char (isAlphaNum)
1313import Data.Function (on)
14−import Data.List (foldl', nubBy, sortBy, sortOn)
15−import Data.Map.Strict (Map)
16−import qualified Data.Map.Strict as M
17−import FastString (FastString)
14+import Data.Generics (gcompare)
15+import Data.List (nubBy, sortBy)
1816import GHC hiding (GhcPs, IE)
1917import GHC.Hs.Extension
2018import GHC.Hs.ImpExp (IE (..))
2119import Ormolu.Utils (notImplemented, showOutputable)
2220
23−-- | Sort and normalize imports.
24−normalizeImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
25−normalizeImports =
26− fmap snd
27− . M.toAscList
28− . M.fromListWith combineImports
29− . fmap (\x -> (importId x, g x))
21+-- | Sort imports by module name. This also sorts explicit import lists for
22+-- each declaration.
23+sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
24+sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
3025 where
31− g (L l ImportDecl {..}) =
32− L
33− l
26+ sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
27+ sortImportLists = \case
28+ ImportDecl {..} ->
3429 ImportDecl
35− { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
30+ { ideclHiding = second (fmap sortLies) <$> ideclHiding,
3631 ..
3732 }
38− g _ = notImplemented "XImportDecl"
33+ XImportDecl x -> noExtCon x
3934
40−-- | Combine two import declarations. It should be assumed that 'ImportId's
41−-- are equal.
42−combineImports ::
43− LImportDecl GhcPs ->
44− LImportDecl GhcPs ->
45− LImportDecl GhcPs
46−combineImports (L lx ImportDecl {..}) (L _ y) =
47− L
48− lx
49− ImportDecl
50− { ideclHiding = case (ideclHiding, GHC.ideclHiding y) of
51− (Just (hiding, L l' xs), Just (_, L _ ys)) ->
52− Just (hiding, (L l' (normalizeLies (xs ++ ys))))
53− _ -> Nothing,
54− ..
55− }
56−combineImports _ _ = notImplemented "XImportDecl"
35+-- | Compare two @'LImportDecl' 'GhcPs'@ things.
36+compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
37+compareIdecl (L _ m0) (L _ m1) =
38+ case (isPrelude n0, isPrelude n1) of
39+ (False, False) -> n0 `compare` n1
40+ (True, False) -> GT
41+ (False, True) -> LT
42+ (True, True) -> m0 `gcompare` m1
43+ where
44+ n0 = unLoc (ideclName m0)
45+ n1 = unLoc (ideclName m1)
46+ isPrelude = (== "Prelude") . moduleNameString
5747
58−-- | Import id, a collection of all things that justify having a separate
59−-- import entry. This is used for merging of imports. If two imports have
60−-- the same 'ImportId' they can be merged.
61−data ImportId = ImportId
62− { importIsPrelude :: Bool,
63− importIdName :: ModuleName,
64− importPkgQual :: Maybe FastString,
65− importSource :: Bool,
66− importSafe :: Bool,
67− importQualified :: Bool,
68− importImplicit :: Bool,
69− importAs :: Maybe ModuleName,
70− importHiding :: Maybe Bool
71− }
72− deriving (Eq, Ord)
48+-- | Sort located import or export, dropping duplicates.
49+sortLies :: [LIE GhcPs] -> [LIE GhcPs]
50+sortLies =
51+ nubBy (\x y -> compareLIE x y == EQ)
52+ . sortBy compareLIEAll
53+ . fmap (fmap sortThings)
7354
74−-- | Obtain an 'ImportId' for a given import.
75−importId :: LImportDecl GhcPs -> ImportId
76−importId (L _ ImportDecl {..}) =
77− ImportId
78− { importIsPrelude = isPrelude,
79− importIdName = moduleName,
80− importPkgQual = sl_fs <$> ideclPkgQual,
81− importSource = ideclSource,
82− importSafe = ideclSafe,
83− importQualified = case ideclQualified of
84− QualifiedPre -> True
85− QualifiedPost -> True
86− NotQualified -> False,
87− importImplicit = ideclImplicit,
88− importAs = unLoc <$> ideclAs,
89− importHiding = fst <$> ideclHiding
90− }
91− where
92− isPrelude = moduleNameString moduleName == "Prelude"
93− moduleName = unLoc ideclName
94−importId _ = notImplemented "XImportDecl"
55+-- | Sort imports\/exports inside of 'IEThingWith'.
56+sortThings :: IE GhcPs -> IE GhcPs
57+sortThings = \case
58+ IEThingWith NoExtField x w xs fl ->
59+ IEThingWith NoExtField x w (sortBy (compareIewn `on` unLoc) xs) fl
60+ other -> other
9561
96−-- | Normalize a collection of import\/export items.
97−normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]
98−normalizeLies = sortOn (getIewn . unLoc) . M.elems . foldl' combine M.empty
62+-- | Compare a pair of located imports or exports taking into account
63+-- wildcards.
64+compareLIEAll :: LIE GhcPs -> LIE GhcPs -> Ordering
65+compareLIEAll = f `on` getIewn . unLoc
9966 where
100− combine ::
101− Map IEWrappedNameOrd (LIE GhcPs) ->
102− LIE GhcPs ->
103− Map IEWrappedNameOrd (LIE GhcPs)
104− combine m (L new_l new) =
105− let wname = getIewn new
106− normalizeWNames =
107− nubBy (\x y -> compareLIewn x y == EQ) . sortBy compareLIewn
108− alter = \case
109− Nothing -> Just . L new_l $
110− case new of
111− IEThingWith NoExtField n wildcard g flbl ->
112− IEThingWith NoExtField n wildcard (normalizeWNames g) flbl
113− other -> other
114− Just old ->
115− let f = \case
116− IEVar NoExtField n -> IEVar NoExtField n
117− IEThingAbs NoExtField _ -> new
118− IEThingAll NoExtField n -> IEThingAll NoExtField n
119− IEThingWith NoExtField n wildcard g flbl ->
120− case new of
121− IEVar NoExtField _ ->
122− error "Ormolu.Imports broken presupposition"
123− IEThingAbs NoExtField _ ->
124− IEThingWith NoExtField n wildcard g flbl
125− IEThingAll NoExtField n' ->
126− IEThingAll NoExtField n'
127− IEThingWith NoExtField n' wildcard' g' flbl' ->
128− let combinedWildcard =
129− case (wildcard, wildcard') of
130− (IEWildcard _, _) -> IEWildcard 0
131− (_, IEWildcard _) -> IEWildcard 0
132− _ -> NoIEWildcard
133− in IEThingWith
134− NoExtField
135− n'
136− combinedWildcard
137− (normalizeWNames (g <> g'))
138− flbl'
139− IEModuleContents NoExtField _ -> notImplemented "IEModuleContents"
140− IEGroup NoExtField _ _ -> notImplemented "IEGroup"
141− IEDoc NoExtField _ -> notImplemented "IEDoc"
142− IEDocNamed NoExtField _ -> notImplemented "IEDocNamed"
143− XIE x -> noExtCon x
144− IEModuleContents NoExtField _ -> notImplemented "IEModuleContents"
145− IEGroup NoExtField _ _ -> notImplemented "IEGroup"
146− IEDoc NoExtField _ -> notImplemented "IEDoc"
147− IEDocNamed NoExtField _ -> notImplemented "IEDocNamed"
148− XIE x -> noExtCon x
149− in Just (f <$> old)
150− in M.alter alter wname m
67+ f (x, xall) (y, yall) =
68+ case x `compareIewn` y of
69+ EQ -> xall `compare` yall
70+ ordering -> ordering
15171
152−-- | A wrapper for @'IEWrappedName' 'RdrName'@ that allows us to define an
153−-- 'Ord' instance for it.
154−newtype IEWrappedNameOrd = IEWrappedNameOrd (IEWrappedName RdrName)
155− deriving (Eq)
72+-- | The same as 'compareLIEAll'.
73+compareLIE :: LIE GhcPs -> LIE GhcPs -> Ordering
74+compareLIE = compareIewn `on` fst . getIewn . unLoc
15675
157−instance Ord IEWrappedNameOrd where
158− compare (IEWrappedNameOrd x) (IEWrappedNameOrd y) = compareIewn x y
76+-- | Indication whether or not a wildcard is used by an import item.
77+data IEWild = All | NoWild
78+ deriving (Eq, Ord)
15979
16080-- | Project @'IEWrappedName' 'RdrName'@ from @'IE' 'GhcPs'@.
161−getIewn :: IE GhcPs -> IEWrappedNameOrd
81+getIewn :: IE GhcPs -> (IEWrappedName RdrName, IEWild)
16282getIewn = \case
163− IEVar NoExtField x -> IEWrappedNameOrd (unLoc x)
164− IEThingAbs NoExtField x -> IEWrappedNameOrd (unLoc x)
165− IEThingAll NoExtField x -> IEWrappedNameOrd (unLoc x)
166− IEThingWith NoExtField x _ _ _ -> IEWrappedNameOrd (unLoc x)
83+ IEVar NoExtField x -> (unLoc x, NoWild)
84+ IEThingAbs NoExtField x -> (unLoc x, NoWild)
85+ IEThingAll NoExtField x -> (unLoc x, All)
86+ IEThingWith NoExtField x _ _ _ -> (unLoc x, NoWild)
16787 IEModuleContents NoExtField _ -> notImplemented "IEModuleContents"
16888 IEGroup NoExtField _ _ -> notImplemented "IEGroup"
16989 IEDoc NoExtField _ -> notImplemented "IEDoc"
17090 IEDocNamed NoExtField _ -> notImplemented "IEDocNamed"
17191 XIE x -> noExtCon x
17292
173−-- | Like 'compareIewn' for located wrapped names.
174−compareLIewn :: LIEWrappedName RdrName -> LIEWrappedName RdrName -> Ordering
175−compareLIewn = compareIewn `on` unLoc
176−
17793-- | Compare two @'IEWrapppedName' 'RdrName'@ things.
17894compareIewn :: IEWrappedName RdrName -> IEWrappedName RdrName -> Ordering
17995compareIewn (IEName x) (IEName y) = unLoc x `compareRdrName` unLoc y
src/Ormolu/Printer/Comments.hs+2−2
@@ -154,7 +154,6 @@ needsNewlineBefore ::
154154 -- | Last printed comment span
155155 Maybe SpanMark ->
156156 Bool
157−needsNewlineBefore _ (Just (HaddockSpan _ _)) = True
158157needsNewlineBefore l mlastMark =
159158 case spanMarkSpan <$> mlastMark of
160159 Nothing -> False
@@ -239,7 +238,8 @@ commentFollowsElt ref mnSpn meSpn mlastMark (L l comment) =
239238 -- lexemes in front of it and goes right after the previous comment.
240239 not (hasAtomsBefore comment)
241240 && ( case mlastMark of
242− Just (HaddockSpan _ _) -> False
241+ Just (HaddockSpan _ spn) ->
242+ srcSpanEndLine spn + 1 == srcSpanStartLine l
243243 Just (CommentSpan spn) ->
244244 srcSpanEndLine spn + 1 == srcSpanStartLine l
245245 _ -> False
src/Ormolu/Printer/Meat/Declaration.hs+4−14
@@ -85,6 +85,9 @@ isDocumented = any (isHaddock . unLoc)
8585 isHaddock _ = False
8686
8787-- | Group relevant declarations together.
88+--
89+-- Add a declaration to a group iff it is relevant to either the first or
90+-- the preceding declaration in the group.
8891groupDecls :: [LHsDecl GhcPs] -> [NonEmpty (LHsDecl GhcPs)]
8992groupDecls [] = []
9093groupDecls (l@(L _ DocNext) : xs) =
@@ -97,8 +100,7 @@ groupDecls (header : xs) =
97100 let (grp, rest) = flip span (zip (header : xs) xs) $ \(previous, current) ->
98101 let relevantToHdr = groupedDecls header current
99102 relevantToPrev = groupedDecls previous current
100− isDeclSeries = declSeries previous current
101− in isDeclSeries || relevantToHdr || relevantToPrev
103+ in relevantToHdr || relevantToPrev
102104 in (header :| map snd grp) : groupDecls (map snd rest)
103105
104106p_hsDecl :: FamilyStyle -> HsDecl GhcPs -> R ()
@@ -195,18 +197,6 @@ groupedDecls (L l_x x') (L l_y y') =
195197 (_, DocPrev) -> True
196198 _ -> False
197199
198−-- | Detect declaration series that should not have blanks between them.
199−declSeries ::
200− LHsDecl GhcPs ->
201− LHsDecl GhcPs ->
202− Bool
203−declSeries (L _ x) (L _ y) =
204− case (x, y) of
205− ( SigD NoExtField (TypeSig NoExtField _ _),
206− SigD NoExtField (TypeSig NoExtField _ _)
207− ) -> True
208− _ -> False
209−
210200intersects :: Ord a => [a] -> [a] -> Bool
211201intersects a b = go (sort a) (sort b)
212202 where
src/Ormolu/Printer/Meat/Declaration.hs-boot+1
@@ -9,4 +9,5 @@ import Ormolu.Printer.Combinators
99import Ormolu.Printer.Meat.Common
1010
1111p_hsDecls :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
12+
1213p_hsDeclsRespectGrouping :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
src/Ormolu/Printer/Meat/Declaration/Value.hs-boot+4
@@ -11,7 +11,11 @@ import GHC
1111import Ormolu.Printer.Combinators
1212
1313p_valDecl :: HsBindLR GhcPs GhcPs -> R ()
14+
1415p_pat :: Pat GhcPs -> R ()
16+
1517p_hsExpr :: HsExpr GhcPs -> R ()
18+
1619p_hsSplice :: HsSplice GhcPs -> R ()
20+
1721p_stringLit :: String -> R ()
src/Ormolu/Printer/Meat/Module.hs+2−2
@@ -11,7 +11,7 @@ where
1111import Control.Monad
1212import qualified Data.Text as T
1313import GHC
14−import Ormolu.Imports (normalizeImports)
14+import Ormolu.Imports
1515import Ormolu.Parser.CommentStream
1616import Ormolu.Parser.Pragma
1717import Ormolu.Parser.Shebang
@@ -69,7 +69,7 @@ p_hsModule mstackHeader shebangs pragmas qualifiedPost HsModule {..} = do
6969 txt "where"
7070 newline
7171 newline
72− forM_ (normalizeImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
72+ forM_ (sortImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
7373 newline
7474 switchLayout (getLoc <$> hsmodDecls) $ do
7575 p_hsDecls Free hsmodDecls
src/Ormolu/Processing/Preprocess.hs+2−1
@@ -11,7 +11,8 @@ where
1111import Control.Monad
1212import Data.Char (isSpace)
1313import qualified Data.List as L
14−import Data.Maybe (isJust, maybeToList)
14+import Data.Maybe (isJust)
15+import Data.Maybe (maybeToList)
1516import FastString
1617import Ormolu.Config (RegionDeltas (..))
1718import Ormolu.Parser.Shebang (isShebang)
src/Ormolu/Utils.hs+1−1
@@ -22,8 +22,8 @@ where
2222
2323import Data.Char (isSpace)
2424import Data.List (dropWhileEnd)
25−import Data.List.NonEmpty (NonEmpty (..))
2625import qualified Data.List.NonEmpty as NE
26+import Data.List.NonEmpty (NonEmpty (..))
2727import Data.Maybe (fromMaybe)
2828import Data.Text (Text)
2929import qualified Data.Text as T