on 03e6c70 help sign in
47 files changed+107−299
since splitsnapshots
unifiedsplit
.buildkite/pipeline.yml+2−2 .github/workflows/binaries.yml−39 .github/workflows/build.sh−7 CHANGELOG.md+2−21 README.md+11−20 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/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+21−67 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+2−21
@@ -1,27 +1,8 @@
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−
193* Imports in a import lists are now normalized: duplicate imports are
204 combined/eliminated intelligently.
215
22−* Import declarations that can be merged are now automatically merged.
23− [Issue 414](https://github.com/tweag/ormolu/issues/414).
24−
256* The magic comments for disabling and enabling Ormolu now can encompass any
267 fragment of code provided that the remaining code after exclusion of the
278 disabled part is still syntactically correct. [Issue
@@ -42,8 +23,8 @@
4223* Blank lines between definitions in `let` and `while` bindings are now
4324 preserved. [Issue 554](https://github.com/tweag/ormolu/issues/554).
4425
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).
26+* Fixed the bug when type applications glued to TH splices that followed
27+ them. [Issue 613](https://github.com/tweag/ormolu/issues/613).
4728
4829* Improved region formatting so that indented fragments—such as definitions
4930 inside of `where` clauses—can be formatted. [Issue
README.md+11−20
@@ -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.
@@ -49,8 +49,8 @@ resolver: lts-16.0
4949packages:
5050- '.'
5151
52−$ stack build # to build
53−$ stack install # to install
52+$ stack build -- to build
53+$ stack install -- to install
5454```
5555
5656To use Ormolu directly from GitHub with Nix, this snippet may come in handy:
@@ -130,6 +130,14 @@ independent top-level definitions.
130130 criterion than just being valid Haskell modules.
131131* Various minor idempotence issues, most of them are related to comments.
132132
133+## Editor integration
134+
135+We know of the following editor integrations:
136+
137+* [Emacs][emacs-package]
138+* [VS Code][vs-code-plugin]
139+* vim: [neoformat][neoformat], [vim-ormolu][vim-ormolu]
140+
133141## Running on Hackage
134142
135143It's possible to try Ormolu on arbitrary packages from Hackage. For that
@@ -144,22 +152,6 @@ also contain formatted `.hs` files for inspection and original inputs with
144152`.hs-original` extension (those are with CPP dropped, exactly what is fed
145153into Ormolu).
146154
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−
163155## Contributing
164156
165157See [CONTRIBUTING.md][contributing].
@@ -178,4 +170,3 @@ Copyright © 2018–present Tweag I/O
178170[vs-code-plugin]: https://marketplace.visualstudio.com/items?itemName=sjurmillidahl.ormolu-vscode
179171[vim-ormolu]: https://github.com/sdiehl/vim-ormolu
180172[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/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+21−67
@@ -4,94 +4,48 @@
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.Generics (gcompare)
1415import Data.List (foldl', nubBy, sortBy, sortOn)
1516import Data.Map.Strict (Map)
1617import qualified Data.Map.Strict as M
17−import FastString (FastString)
1818import GHC hiding (GhcPs, IE)
1919import GHC.Hs.Extension
2020import GHC.Hs.ImpExp (IE (..))
2121import Ormolu.Utils (notImplemented, showOutputable)
2222
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))
23+-- | Sort imports by module name. This also sorts and normalizes explicit
24+-- import lists for each declaration.
25+sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
26+sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
3027 where
31− g (L l ImportDecl {..}) =
32− L
33− l
28+ sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
29+ sortImportLists = \case
30+ ImportDecl {..} ->
3431 ImportDecl
3532 { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
3633 ..
3734 }
38− g _ = notImplemented "XImportDecl"
35+ XImportDecl x -> noExtCon x
3936
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"
57−
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)
73−
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− }
37+-- | Compare two @'LImportDecl' 'GhcPs'@ things.
38+compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
39+compareIdecl (L _ m0) (L _ m1) =
40+ case (isPrelude n0, isPrelude n1) of
41+ (False, False) -> n0 `compare` n1
42+ (True, False) -> GT
43+ (False, True) -> LT
44+ (True, True) -> m0 `gcompare` m1
9145 where
92− isPrelude = moduleNameString moduleName == "Prelude"
93− moduleName = unLoc ideclName
94−importId _ = notImplemented "XImportDecl"
46+ n0 = unLoc (ideclName m0)
47+ n1 = unLoc (ideclName m1)
48+ isPrelude = (== "Prelude") . moduleNameString
9549
9650-- | Normalize a collection of import\/export items.
9751normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]
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